covers with logo
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@@ -49,7 +49,7 @@ exports.generatePicturePrompt = (project = {}) => {
|
|||||||
|
|
||||||
const prompt = `
|
const prompt = `
|
||||||
<BRIEF>
|
<BRIEF>
|
||||||
<OBJECTIF>Créer une pochette d'album abstraite et lumineuse.</OBJECTIF>
|
<OBJECTIF>Créer une pochette lumineuse.</OBJECTIF>
|
||||||
<TITRE>${title || "Sans titre"}</TITRE>
|
<TITRE>${title || "Sans titre"}</TITRE>
|
||||||
<STYLE_MUSICAL>${tagsLine}</STYLE_MUSICAL>
|
<STYLE_MUSICAL>${tagsLine}</STYLE_MUSICAL>
|
||||||
<EXTRAITS_PAROLES>${lyricsLine}</EXTRAITS_PAROLES>
|
<EXTRAITS_PAROLES>${lyricsLine}</EXTRAITS_PAROLES>
|
||||||
@@ -58,7 +58,6 @@ exports.generatePicturePrompt = (project = {}) => {
|
|||||||
<CONTEXTES_VISUELS>
|
<CONTEXTES_VISUELS>
|
||||||
<FORMAT>Image carrée 1024x1024 pixels.</FORMAT>
|
<FORMAT>Image carrée 1024x1024 pixels.</FORMAT>
|
||||||
<STYLE>Abstrait, graphique, formes géométriques et dégradés.</STYLE>
|
<STYLE>Abstrait, graphique, formes géométriques et dégradés.</STYLE>
|
||||||
<PALETTE>${paletteHint}</PALETTE>
|
|
||||||
<COMPOSITION>Remplis toute la surface sans laisser de bordures ou zones vides.</COMPOSITION>
|
<COMPOSITION>Remplis toute la surface sans laisser de bordures ou zones vides.</COMPOSITION>
|
||||||
</CONTEXTES_VISUELS>
|
</CONTEXTES_VISUELS>
|
||||||
|
|
||||||
@@ -66,7 +65,7 @@ exports.generatePicturePrompt = (project = {}) => {
|
|||||||
<INTERDIT>Texte ou typographie.</INTERDIT>
|
<INTERDIT>Texte ou typographie.</INTERDIT>
|
||||||
<INTERDIT>Personnes ou visages.</INTERDIT>
|
<INTERDIT>Personnes ou visages.</INTERDIT>
|
||||||
<INTERDIT>Fond blanc ou bordures.</INTERDIT>
|
<INTERDIT>Fond blanc ou bordures.</INTERDIT>
|
||||||
<INTERDIT>Filigrane ou logo.</INTERDIT>
|
<INTERDIT>Autre logo ou filigrane que celui prévu en bas à droite.</INTERDIT>
|
||||||
</CONTRAINTES>
|
</CONTRAINTES>
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
|
|||||||
Generated
+435
-5
@@ -15,7 +15,8 @@
|
|||||||
"firebase-admin": "^12.7.0",
|
"firebase-admin": "^12.7.0",
|
||||||
"firebase-functions": "^6.4.0",
|
"firebase-functions": "^6.4.0",
|
||||||
"fluent-ffmpeg": "^2.1.3",
|
"fluent-ffmpeg": "^2.1.3",
|
||||||
"genkit": "^1.18.0"
|
"genkit": "^1.18.0",
|
||||||
|
"sharp": "^0.33.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.15.0",
|
"eslint": "^8.15.0",
|
||||||
@@ -62,6 +63,16 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@emnapi/runtime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.8.0",
|
"version": "4.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz",
|
||||||
@@ -799,6 +810,367 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/@img/sharp-darwin-arm64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-darwin-arm64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-darwin-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-darwin-x64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-arm": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-arm": "1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-arm64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-arm64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-s390x": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-s390x": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-x64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-wasm32": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
|
||||||
|
"cpu": [
|
||||||
|
"wasm32"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/runtime": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-win32-ia32": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-win32-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@js-sdsl/ordered-map": {
|
"node_modules/@js-sdsl/ordered-map": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
|
||||||
@@ -3097,7 +3469,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "^1.0.0",
|
"color-name": "^1.0.0",
|
||||||
"simple-swizzle": "^0.2.2"
|
"simple-swizzle": "^0.2.2"
|
||||||
@@ -3296,6 +3667,15 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-libc": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/doctrine": {
|
"node_modules/doctrine": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||||
@@ -4842,8 +5222,7 @@
|
|||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.16.1",
|
"version": "2.16.1",
|
||||||
@@ -6205,6 +6584,58 @@
|
|||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/sharp": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"color": "^4.2.3",
|
||||||
|
"detect-libc": "^2.0.3",
|
||||||
|
"semver": "^7.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-darwin-arm64": "0.33.5",
|
||||||
|
"@img/sharp-darwin-x64": "0.33.5",
|
||||||
|
"@img/sharp-libvips-darwin-arm64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-darwin-x64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linux-arm": "1.0.5",
|
||||||
|
"@img/sharp-libvips-linux-arm64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linux-s390x": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linux-x64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64": "1.0.4",
|
||||||
|
"@img/sharp-linux-arm": "0.33.5",
|
||||||
|
"@img/sharp-linux-arm64": "0.33.5",
|
||||||
|
"@img/sharp-linux-s390x": "0.33.5",
|
||||||
|
"@img/sharp-linux-x64": "0.33.5",
|
||||||
|
"@img/sharp-linuxmusl-arm64": "0.33.5",
|
||||||
|
"@img/sharp-linuxmusl-x64": "0.33.5",
|
||||||
|
"@img/sharp-wasm32": "0.33.5",
|
||||||
|
"@img/sharp-win32-ia32": "0.33.5",
|
||||||
|
"@img/sharp-win32-x64": "0.33.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sharp/node_modules/color": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1",
|
||||||
|
"color-string": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@@ -6311,7 +6742,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-arrayish": "^0.3.1"
|
"is-arrayish": "^0.3.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
"firebase-admin": "^12.7.0",
|
"firebase-admin": "^12.7.0",
|
||||||
"firebase-functions": "^6.4.0",
|
"firebase-functions": "^6.4.0",
|
||||||
"fluent-ffmpeg": "^2.1.3",
|
"fluent-ffmpeg": "^2.1.3",
|
||||||
"genkit": "^1.18.0"
|
"genkit": "^1.18.0",
|
||||||
|
"sharp": "^0.33.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.15.0",
|
"eslint": "^8.15.0",
|
||||||
|
|||||||
+91
-54
@@ -3,11 +3,68 @@ const admin = require("firebase-admin");
|
|||||||
const logger = require("firebase-functions/logger");
|
const logger = require("firebase-functions/logger");
|
||||||
const {
|
const {
|
||||||
generateImageV2,
|
generateImageV2,
|
||||||
CombineCoverAndPicture,
|
|
||||||
} = require("../helpers/gemini");
|
} = require("../helpers/gemini");
|
||||||
const { generatePicturePrompt } = require("../helpers/prompts");
|
const { generatePicturePrompt } = require("../helpers/prompts");
|
||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
const axios = require("axios");
|
||||||
|
const sharp = require("sharp");
|
||||||
|
|
||||||
const db = admin.firestore();
|
const db = admin.firestore();
|
||||||
|
const bucket = admin.storage().bucket();
|
||||||
|
const LOGO_PATH = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"../assets/musicLandProduction.png",
|
||||||
|
);
|
||||||
|
|
||||||
|
async function buildCoverWithLogo(backgroundUrl, targetPath) {
|
||||||
|
logger.info("🖼️ [Cover] Adding logo to generated background");
|
||||||
|
const [{ data: backgroundBuffer }, logoBuffer] = await Promise.all([
|
||||||
|
axios.get(backgroundUrl, { responseType: "arraybuffer" }),
|
||||||
|
fs.promises.readFile(LOGO_PATH),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const baseImage = sharp(backgroundBuffer);
|
||||||
|
const { width = 1024, height = 1024 } = await baseImage.metadata();
|
||||||
|
|
||||||
|
const desiredWidth = Math.round(width * 0.32);
|
||||||
|
const margin = Math.round(width * 0.04);
|
||||||
|
|
||||||
|
const { data: resizedLogo, info: logoInfo } = await sharp(logoBuffer)
|
||||||
|
.resize({ width: desiredWidth })
|
||||||
|
.png()
|
||||||
|
.toBuffer({ resolveWithObject: true });
|
||||||
|
|
||||||
|
const left = Math.max(width - logoInfo.width - margin, 0);
|
||||||
|
const top = Math.max(height - logoInfo.height - margin, 0);
|
||||||
|
|
||||||
|
const stampedBuffer = await baseImage
|
||||||
|
.ensureAlpha()
|
||||||
|
.composite([
|
||||||
|
{
|
||||||
|
input: resizedLogo,
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
blend: "over",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const token = require("crypto").randomUUID();
|
||||||
|
await bucket.file(targetPath).save(stampedBuffer, {
|
||||||
|
resumable: false,
|
||||||
|
metadata: {
|
||||||
|
contentType: "image/png",
|
||||||
|
cacheControl: "public, max-age=31536000",
|
||||||
|
metadata: { firebaseStorageDownloadTokens: token },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o/${encodeURIComponent(
|
||||||
|
targetPath,
|
||||||
|
)}?alt=media&token=${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Shared core for generating and saving the cover, and updating the project
|
// Shared core for generating and saving the cover, and updating the project
|
||||||
async function performCoverGeneration(project) {
|
async function performCoverGeneration(project) {
|
||||||
@@ -19,12 +76,13 @@ async function performCoverGeneration(project) {
|
|||||||
promptPreview: String(prompt).slice(0, 160),
|
promptPreview: String(prompt).slice(0, 160),
|
||||||
});
|
});
|
||||||
let coverUrl = "";
|
let coverUrl = "";
|
||||||
|
const timestamp = Date.now();
|
||||||
try {
|
try {
|
||||||
// Utiliser une taille fixe de 1024 pixels
|
// Utiliser une taille fixe de 1024 pixels
|
||||||
coverUrl = await generateImageV2(
|
coverUrl = await generateImageV2(
|
||||||
prompt,
|
prompt,
|
||||||
1024,
|
1024,
|
||||||
`users/${project.userId}/projects/${project.id}/generated-${Date.now()}.png`,
|
`users/${project.userId}/projects/${project.id}/generated-${timestamp}.png`,
|
||||||
);
|
);
|
||||||
console.log("coverUrl", coverUrl);
|
console.log("coverUrl", coverUrl);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -38,6 +96,19 @@ async function performCoverGeneration(project) {
|
|||||||
throw new Error("Génération d'image échouée (URL vide)");
|
throw new Error("Génération d'image échouée (URL vide)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let finalCoverUrl = "";
|
||||||
|
try {
|
||||||
|
const stampedPath = `users/${project.userId}/projects/${project.id}/cover-${timestamp}-stamped.png`;
|
||||||
|
finalCoverUrl = await buildCoverWithLogo(coverUrl, stampedPath);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("❌ [Cover] Logo overlay failed", {
|
||||||
|
projectId: project.id,
|
||||||
|
error: e?.message || String(e),
|
||||||
|
});
|
||||||
|
// En cas d'échec, on retient au moins la cover générée
|
||||||
|
finalCoverUrl = coverUrl;
|
||||||
|
}
|
||||||
|
|
||||||
// Mettre à jour le projet avec l'URL et le statut
|
// Mettre à jour le projet avec l'URL et le statut
|
||||||
await db
|
await db
|
||||||
.collection("projects")
|
.collection("projects")
|
||||||
@@ -46,6 +117,7 @@ async function performCoverGeneration(project) {
|
|||||||
{
|
{
|
||||||
cover: {
|
cover: {
|
||||||
generatedBackground: coverUrl,
|
generatedBackground: coverUrl,
|
||||||
|
result: finalCoverUrl,
|
||||||
},
|
},
|
||||||
coverStatus: "GENERATED",
|
coverStatus: "GENERATED",
|
||||||
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
||||||
@@ -56,56 +128,10 @@ async function performCoverGeneration(project) {
|
|||||||
logger.info("✅ [Cover] Saved", {
|
logger.info("✅ [Cover] Saved", {
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
coverUrl,
|
coverUrl,
|
||||||
|
finalCoverUrl,
|
||||||
ms: Date.now() - t0,
|
ms: Date.now() - t0,
|
||||||
});
|
});
|
||||||
return coverUrl;
|
return finalCoverUrl;
|
||||||
}
|
|
||||||
|
|
||||||
async function performCombineGeneration(project) {
|
|
||||||
const t0 = Date.now();
|
|
||||||
const background = project?.cover?.generatedBackground || null;
|
|
||||||
const foreground = project?.cover?.userForeground || null;
|
|
||||||
|
|
||||||
if (!background || !foreground) {
|
|
||||||
throw new Error(
|
|
||||||
"Images manquantes: cover.generatedBackground et cover.userForground sont requis",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("🧩 [Combine] Inputs detected", {
|
|
||||||
bgPreview: String(background).slice(0, 80),
|
|
||||||
fgPreview: String(foreground).slice(0, 80),
|
|
||||||
});
|
|
||||||
|
|
||||||
const combinedUrl = await CombineCoverAndPicture(
|
|
||||||
background,
|
|
||||||
foreground,
|
|
||||||
`users/${project.userId}/projects/${project.id}/combine-${Date.now()}.png`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Sauvegarder le résultat et marquer comme généré
|
|
||||||
await db
|
|
||||||
.collection("projects")
|
|
||||||
.doc(project.id)
|
|
||||||
.set(
|
|
||||||
{
|
|
||||||
cover: {
|
|
||||||
...project.cover,
|
|
||||||
result: combinedUrl,
|
|
||||||
},
|
|
||||||
coverStatus: "GENERATED",
|
|
||||||
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
||||||
},
|
|
||||||
{ merge: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.info("✅ [Cover] Combined image saved", {
|
|
||||||
projectId: project.id,
|
|
||||||
combinedUrl,
|
|
||||||
ms: Date.now() - t0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return combinedUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firestore trigger: création d'une tâche de génération de cover
|
// Firestore trigger: création d'une tâche de génération de cover
|
||||||
@@ -132,6 +158,21 @@ exports.onTaskCreateGenerateCover = onDocumentCreated(
|
|||||||
type,
|
type,
|
||||||
projectId,
|
projectId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (type === "combine") {
|
||||||
|
logger.warn("⛔ [Task] Combine task ignored (feature disabled)", {
|
||||||
|
projectId,
|
||||||
|
taskId: event?.params?.taskId,
|
||||||
|
});
|
||||||
|
await event.data.ref.update({
|
||||||
|
status: "CANCELLED",
|
||||||
|
error:
|
||||||
|
"La personnalisation de la pochette avec une photo n'est plus disponible.",
|
||||||
|
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await db.collection("projects").doc(projectId).update({
|
await db.collection("projects").doc(projectId).update({
|
||||||
coverStatus: "GENERATING",
|
coverStatus: "GENERATING",
|
||||||
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
|
||||||
@@ -154,10 +195,6 @@ exports.onTaskCreateGenerateCover = onDocumentCreated(
|
|||||||
logger.info("🎨 [Task] Start cover generation", { projectId });
|
logger.info("🎨 [Task] Start cover generation", { projectId });
|
||||||
coverUrl = await performCoverGeneration(project);
|
coverUrl = await performCoverGeneration(project);
|
||||||
logger.info("✅ [Task] Cover generated", { projectId });
|
logger.info("✅ [Task] Cover generated", { projectId });
|
||||||
} else if (type === "combine") {
|
|
||||||
logger.info("🧩 [Task] Start combine (cover + photo)", { projectId });
|
|
||||||
coverUrl = await performCombineGeneration(project);
|
|
||||||
logger.info("✅ [Task] Combine generated", { projectId, coverUrl });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await event.data.ref.update({
|
await event.data.ref.update({
|
||||||
|
|||||||
@@ -1,30 +1,21 @@
|
|||||||
import * as ImagePicker from "expo-image-picker";
|
|
||||||
import { BlurView } from "expo-blur";
|
import { BlurView } from "expo-blur";
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import { Image, Modal, Pressable, StyleSheet, Text, View } from "react-native";
|
||||||
Image,
|
|
||||||
Modal,
|
|
||||||
Pressable,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import useMinuit from "react-native-minuit/src/hooks/useMinuit";
|
import useMinuit from "react-native-minuit/src/hooks/useMinuit";
|
||||||
import { ai, background } from "../../assets";
|
import { ai, background } from "../../assets";
|
||||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||||
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
|
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
|
||||||
import GradientButton from "../../components/GradientButton";
|
import GradientButton from "../../components/GradientButton";
|
||||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
|
||||||
import { Input } from "../../components/Input";
|
import { Input } from "../../components/Input";
|
||||||
|
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||||
import firebase, { projectsRef, usersRef } from "../../config/firebase";
|
import firebase, { projectsRef, usersRef } from "../../config/firebase";
|
||||||
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
|
|
||||||
import Page from "../../layouts/Page";
|
import Page from "../../layouts/Page";
|
||||||
import { Routes } from "../../navigation";
|
import { Routes } from "../../navigation";
|
||||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||||
import { useUserData } from "../../providers/UserDataProvider";
|
import { useUserData } from "../../providers/UserDataProvider";
|
||||||
import { buildFullName } from "../../utils/artistName";
|
|
||||||
import { gutters, Palette } from "../../styles";
|
import { gutters, Palette } from "../../styles";
|
||||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||||
|
import { buildFullName } from "../../utils/artistName";
|
||||||
|
|
||||||
export const ChooseCoverType = () => {
|
export const ChooseCoverType = () => {
|
||||||
const [showIntro, setShowIntro] = useState(true);
|
const [showIntro, setShowIntro] = useState(true);
|
||||||
@@ -52,11 +43,14 @@ export const ChooseCoverType = () => {
|
|||||||
return pref === "REAL_NAME" || pref === "CUSTOM";
|
return pref === "REAL_NAME" || pref === "CUSTOM";
|
||||||
}, [currentUserData?.artistNamePreference, currentUserData?.userName]);
|
}, [currentUserData?.artistNamePreference, currentUserData?.userName]);
|
||||||
|
|
||||||
const realName = useMemo(() => buildFullName(currentUserData), [
|
const realName = useMemo(
|
||||||
|
() => buildFullName(currentUserData),
|
||||||
|
[
|
||||||
currentUserData?.firstName,
|
currentUserData?.firstName,
|
||||||
currentUserData?.lastName,
|
currentUserData?.lastName,
|
||||||
currentUserData?.displayName,
|
currentUserData?.displayName,
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pseudoVisible) {
|
if (pseudoVisible) {
|
||||||
@@ -122,48 +116,48 @@ export const ChooseCoverType = () => {
|
|||||||
[currentUID, projectId]
|
[currentUID, projectId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const pickUserImage = useCallback(async () => {
|
// const pickUserImage = useCallback(async () => {
|
||||||
try {
|
// try {
|
||||||
await setIsLoading(true);
|
// await setIsLoading(true);
|
||||||
const result = await ImagePicker.launchImageLibraryAsync({
|
// const result = await ImagePicker.launchImageLibraryAsync({
|
||||||
mediaTypes: ["images"],
|
// mediaTypes: ["images"],
|
||||||
allowsEditing: true,
|
// allowsEditing: true,
|
||||||
aspect: [1, 1],
|
// aspect: [1, 1],
|
||||||
quality: 1,
|
// quality: 1,
|
||||||
});
|
// });
|
||||||
|
|
||||||
const uri = result?.assets?.[0]?.uri || null;
|
// const uri = result?.assets?.[0]?.uri || null;
|
||||||
if (!uri) return;
|
// if (!uri) return;
|
||||||
|
|
||||||
if (!projectId) return;
|
// if (!projectId) return;
|
||||||
const path = `musics/${projectId}/userSelectedCover.png`;
|
// const path = `musics/${projectId}/userSelectedCover.png`;
|
||||||
|
|
||||||
const { resultURI } = await uploadFileToFirebase({
|
// const { resultURI } = await uploadFileToFirebase({
|
||||||
uri,
|
// uri,
|
||||||
path,
|
// path,
|
||||||
shouldCompress: true,
|
// shouldCompress: true,
|
||||||
fileType: "IMAGE",
|
// fileType: "IMAGE",
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!resultURI) {
|
// if (!resultURI) {
|
||||||
throw new Error("Erreur lors de l'upload");
|
// throw new Error("Erreur lors de l'upload");
|
||||||
}
|
// }
|
||||||
await updateProjectData({
|
// await updateProjectData({
|
||||||
cover: {
|
// cover: {
|
||||||
result: resultURI,
|
// result: resultURI,
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
navigate(Routes.ValidateCover);
|
// navigate(Routes.ValidateCover);
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.log("onPickUserImage error", e?.message);
|
// console.log("onPickUserImage error", e?.message);
|
||||||
} finally {
|
// } finally {
|
||||||
await setIsLoading(false);
|
// await setIsLoading(false);
|
||||||
}
|
// }
|
||||||
}, [projectId, setIsLoading, updateProjectData]);
|
// }, [projectId, setIsLoading, updateProjectData]);
|
||||||
|
|
||||||
const handlePickUserImage = useCallback(() => {
|
// const handlePickUserImage = useCallback(() => {
|
||||||
ensureArtistPreference(pickUserImage);
|
// ensureArtistPreference(pickUserImage);
|
||||||
}, [ensureArtistPreference, pickUserImage]);
|
// }, [ensureArtistPreference, pickUserImage]);
|
||||||
|
|
||||||
const handleGenerateCover = useCallback(() => {
|
const handleGenerateCover = useCallback(() => {
|
||||||
ensureArtistPreference(() => navigate(Routes.PouchReady));
|
ensureArtistPreference(() => navigate(Routes.PouchReady));
|
||||||
@@ -184,8 +178,7 @@ export const ChooseCoverType = () => {
|
|||||||
if (!realName) {
|
if (!realName) {
|
||||||
setTooltip({
|
setTooltip({
|
||||||
type: "error",
|
type: "error",
|
||||||
text:
|
text: "Renseigne ton prénom et nom dans ton profil ou crée un nom d'artiste.",
|
||||||
"Renseigne ton prénom et nom dans ton profil ou crée un nom d'artiste.",
|
|
||||||
});
|
});
|
||||||
setChoiceVisible(false);
|
setChoiceVisible(false);
|
||||||
setPseudoVisible(true);
|
setPseudoVisible(true);
|
||||||
@@ -298,10 +291,10 @@ export const ChooseCoverType = () => {
|
|||||||
gap: 12,
|
gap: 12,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BorderGradientButton
|
{/* <BorderGradientButton
|
||||||
title="Choisir une image"
|
title="Choisir une image"
|
||||||
onPress={handlePickUserImage}
|
onPress={handlePickUserImage}
|
||||||
/>
|
/> */}
|
||||||
<GradientButton
|
<GradientButton
|
||||||
title="Générer une pochette"
|
title="Générer une pochette"
|
||||||
onPress={handleGenerateCover}
|
onPress={handleGenerateCover}
|
||||||
@@ -333,9 +326,7 @@ export const ChooseCoverType = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
<View style={styles.modalButtons}>
|
<View style={styles.modalButtons}>
|
||||||
<BorderGradientButton
|
<BorderGradientButton
|
||||||
title={
|
title={savingChoice ? "Chargement..." : "Utiliser prénom/nom"}
|
||||||
savingChoice ? "Chargement..." : "Utiliser prénom/nom"
|
|
||||||
}
|
|
||||||
onPress={handleUseRealName}
|
onPress={handleUseRealName}
|
||||||
disabled={savingChoice}
|
disabled={savingChoice}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
import { Image as ExpoImage } from "expo-image";
|
import { Image as ExpoImage } from "expo-image";
|
||||||
import * as ImagePicker from "expo-image-picker";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ActivityIndicator, Text, View } from "react-native";
|
import { ActivityIndicator, Text, View } from "react-native";
|
||||||
import useMinuit from "react-native-minuit/src/hooks/useMinuit";
|
|
||||||
import { background } from "../../assets";
|
import { background } from "../../assets";
|
||||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||||
import GradientButton from "../../components/GradientButton";
|
import GradientButton from "../../components/GradientButton";
|
||||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||||
import firebase, { tasksRef } from "../../config/firebase";
|
|
||||||
import loaderMessages from "../../config/loaderMessages";
|
import loaderMessages from "../../config/loaderMessages";
|
||||||
import { uploadFileToFirebase } from "../../helpers/uploadToFirebase";
|
|
||||||
import { isWeb } from "../../hooks/useLayoutType";
|
import { isWeb } from "../../hooks/useLayoutType";
|
||||||
import Page from "../../layouts/Page";
|
import Page from "../../layouts/Page";
|
||||||
import { Routes } from "../../navigation";
|
import { Routes } from "../../navigation";
|
||||||
@@ -19,69 +15,30 @@ import { gutters, Palette, Style } from "../../styles";
|
|||||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||||
|
|
||||||
const PhotoCover = () => {
|
const PhotoCover = () => {
|
||||||
const { selectedProject, selectedProjectId, updateProjectData } =
|
const { selectedProject } = useUserData();
|
||||||
useUserData();
|
|
||||||
const { setIsLoading } = useMinuit();
|
|
||||||
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
||||||
const coverGenerationMessage = isWeb
|
const coverGenerationMessage = isWeb
|
||||||
? loaderMessages.photoCoverGenerationWeb
|
? loaderMessages.photoCoverGenerationWeb
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const onChooseForegroundImage = async () => {
|
const coverUrl =
|
||||||
try {
|
selectedProject?.cover?.result ||
|
||||||
await setIsLoading(true);
|
selectedProject?.cover?.generatedBackground ||
|
||||||
const result = await ImagePicker.launchImageLibraryAsync({
|
null;
|
||||||
mediaTypes: ["images"],
|
|
||||||
allowsEditing: true,
|
|
||||||
aspect: [1, 1],
|
|
||||||
quality: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const uri = result?.assets?.[0]?.uri || null;
|
|
||||||
if (!uri) return;
|
|
||||||
|
|
||||||
const projectId = selectedProject?.id || selectedProjectId;
|
|
||||||
const path = `musics/${projectId}/userForeground.png`;
|
|
||||||
|
|
||||||
const { resultURI } = await uploadFileToFirebase({
|
|
||||||
uri,
|
|
||||||
path,
|
|
||||||
shouldCompress: true,
|
|
||||||
fileType: "IMAGE",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!resultURI) {
|
|
||||||
throw new Error("Erreur lors de l'upload");
|
|
||||||
}
|
|
||||||
await updateProjectData({
|
|
||||||
cover: {
|
|
||||||
...selectedProject.cover,
|
|
||||||
userForeground: resultURI,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await tasksRef.add({
|
|
||||||
type: "combine",
|
|
||||||
projectId: selectedProjectId,
|
|
||||||
status: "PENDING",
|
|
||||||
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log("onPickUserImage error", e?.message);
|
|
||||||
} finally {
|
|
||||||
await setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||||||
<MusicLandHeader onPressBack={goBack} progress={81} />
|
<MusicLandHeader onPressBack={goBack} progress={81} />
|
||||||
<View style={{ flex: 1, marginTop: 16 }}>
|
<View style={{ flex: 1, marginTop: 16 }}>
|
||||||
<CreateLyricsHeader title="Souhaites-tu rajouter une photo de toi sur la pochette?" />
|
<CreateLyricsHeader
|
||||||
|
title="Ta pochette MusicLand Production"
|
||||||
|
subTitle="Le logo est automatiquement ajouté en bas à droite."
|
||||||
|
/>
|
||||||
<View style={{ flex: 1, ...Style.containerCenter }}>
|
<View style={{ flex: 1, ...Style.containerCenter }}>
|
||||||
<View style={{ width: "80%", position: "relative" }}>
|
<View style={{ width: "80%", position: "relative" }}>
|
||||||
{selectedProject?.cover?.result && !isGenerating ? (
|
{coverUrl && !isGenerating ? (
|
||||||
<ExpoImage
|
<ExpoImage
|
||||||
source={{ uri: selectedProject?.cover?.result }}
|
source={{ uri: coverUrl }}
|
||||||
cachePolicy="memory-disk"
|
cachePolicy="memory-disk"
|
||||||
priority="high"
|
priority="high"
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
@@ -103,11 +60,11 @@ const PhotoCover = () => {
|
|||||||
...Style.containerCenter,
|
...Style.containerCenter,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isGenerating && (
|
|
||||||
<View style={{ alignItems: "center", gap: 8 }}>
|
<View style={{ alignItems: "center", gap: 8 }}>
|
||||||
|
{isGenerating ? (
|
||||||
|
<>
|
||||||
<ActivityIndicator color={Palette.white} />
|
<ActivityIndicator color={Palette.white} />
|
||||||
{isWeb ? (
|
{isWeb && coverGenerationMessage ? (
|
||||||
coverGenerationMessage ? (
|
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
color: Palette.white,
|
color: Palette.white,
|
||||||
@@ -118,7 +75,6 @@ const PhotoCover = () => {
|
|||||||
>
|
>
|
||||||
{coverGenerationMessage}
|
{coverGenerationMessage}
|
||||||
</Text>
|
</Text>
|
||||||
) : null
|
|
||||||
) : (
|
) : (
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
@@ -131,9 +87,22 @@ const PhotoCover = () => {
|
|||||||
Génération en cours.
|
Génération en cours.
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</>
|
||||||
|
) : (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: Palette.white,
|
||||||
|
marginTop: 6,
|
||||||
|
textAlign: "center",
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
La pochette sera disponible une fois la génération
|
||||||
|
terminée.
|
||||||
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -147,18 +116,14 @@ const PhotoCover = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BorderGradientButton
|
<BorderGradientButton
|
||||||
onPress={() => navigate(Routes.ValidateCover)}
|
onPress={() => navigate(Routes.PouchReady)}
|
||||||
title={
|
title="Regénérer la pochette"
|
||||||
isGenerating ? "Génération en cours..." : "Valider la pochette"
|
|
||||||
}
|
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
/>
|
/>
|
||||||
<GradientButton
|
<GradientButton
|
||||||
title={
|
title="Valider la pochette"
|
||||||
isGenerating ? "Génération en cours..." : "Oui choisir une image"
|
onPress={() => navigate(Routes.ValidateCover)}
|
||||||
}
|
disabled={isGenerating || !coverUrl}
|
||||||
onPress={() => onChooseForegroundImage()}
|
|
||||||
disabled={isGenerating}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { gutters, Palette, Style } from "../../styles";
|
|||||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||||
|
|
||||||
const PouchReady = () => {
|
const PouchReady = () => {
|
||||||
const { selectedProjectId, selectedProject, updateProjectData } = useUser();
|
const { selectedProjectId, selectedProject } = useUser();
|
||||||
const { setIsLoading } = useMinuit();
|
const { setIsLoading } = useMinuit();
|
||||||
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
const isGenerating = selectedProject?.coverStatus === "GENERATING";
|
||||||
const isFocused = useIsFocused();
|
const isFocused = useIsFocused();
|
||||||
@@ -44,22 +44,9 @@ const PouchReady = () => {
|
|||||||
}
|
}
|
||||||
}, [selectedProjectId, setIsLoading]);
|
}, [selectedProjectId, setIsLoading]);
|
||||||
|
|
||||||
async function validateCover() {
|
const validateCover = () => {
|
||||||
try {
|
navigate(Routes.ValidateCover);
|
||||||
await setIsLoading(true);
|
};
|
||||||
await updateProjectData({
|
|
||||||
cover: {
|
|
||||||
...selectedProject.cover,
|
|
||||||
result: selectedProject.cover.generatedBackground,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
navigate(Routes.PhotoCover);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
} finally {
|
|
||||||
await setIsLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
@@ -84,6 +71,11 @@ const PouchReady = () => {
|
|||||||
}
|
}
|
||||||
}, [generateCover, isFocused, selectedProject]);
|
}, [generateCover, isFocused, selectedProject]);
|
||||||
|
|
||||||
|
const coverPreviewUrl =
|
||||||
|
selectedProject?.cover?.result ||
|
||||||
|
selectedProject?.cover?.generatedBackground ||
|
||||||
|
null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||||||
<MusicLandHeader onPressBack={goBack} progress={72} />
|
<MusicLandHeader onPressBack={goBack} progress={72} />
|
||||||
@@ -98,9 +90,9 @@ const PouchReady = () => {
|
|||||||
/>
|
/>
|
||||||
<View style={{ flex: 1, ...Style.containerCenter }}>
|
<View style={{ flex: 1, ...Style.containerCenter }}>
|
||||||
<View style={{ width: "80%", position: "relative" }}>
|
<View style={{ width: "80%", position: "relative" }}>
|
||||||
{selectedProject?.cover?.generatedBackground && !isGenerating ? (
|
{coverPreviewUrl && !isGenerating ? (
|
||||||
<ExpoImage
|
<ExpoImage
|
||||||
source={{ uri: selectedProject?.cover?.generatedBackground }}
|
source={{ uri: coverPreviewUrl }}
|
||||||
cachePolicy="memory-disk"
|
cachePolicy="memory-disk"
|
||||||
priority="high"
|
priority="high"
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
@@ -215,7 +207,7 @@ const PouchReady = () => {
|
|||||||
/>
|
/>
|
||||||
<GradientButton
|
<GradientButton
|
||||||
title={isGenerating ? "Veuillez patienter..." : "Valider"}
|
title={isGenerating ? "Veuillez patienter..." : "Valider"}
|
||||||
disabled={isGenerating}
|
disabled={isGenerating || !coverPreviewUrl}
|
||||||
onPress={() => validateCover()}
|
onPress={() => validateCover()}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ export const getStageAction = (key, project) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hasFinalCover) {
|
if (!hasFinalCover) {
|
||||||
return { route: Routes.PhotoCover };
|
return { route: Routes.ValidateCover };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { route: Routes.ChooseCoverType };
|
return { route: Routes.ChooseCoverType };
|
||||||
|
|||||||
Reference in New Issue
Block a user