diff --git a/README.md b/README.md index 1da497b..4e40c47 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Le site est accessible sur [http://localhost:PORT](http://localhost:PORT). #### Standalone -Pour la version standalone je vous conseille de faire un script embarquant les variables d'environnement que vous souhaitez modifier : +Pour la version standalone je vous conseille de faire un script embarquant les variables d'environnement que vous souhaitez modifier ([voir à la fin pour la liste des variables](#env-file)) : ```bash #! /bin/bash @@ -198,10 +198,15 @@ FORMSPREE_ID # Id du formulaire formspree pour la page "nous-contacter" MATOMO_URL # Url vers l'instance matomo (exemple: https://analytics.darkou.fr/) MATOMO_ID # Id du site sur votre instance matomo (exemple: 1) SITE_NAME # Nom du site (utilisé dans le titre des pages) +AWS_ACCESS_KEY_ID # Clé d'accès AWS +AWS_SECRET_ACCESS_KEY # Clé secrète AWS +S3_ENDPOINT # Url de l'instance aws (s3.fr-par.scw.cloud pour scaleway france par exemple) +S3_SIGNATURE # Version de la signature AWS (s3v4 pour scaleway par exemple) +S3_BASEFOLDER # Nom du sous dossier dans lequel seront mis les pochettes des albums +S3_BUCKET # Nom du bucket ``` ## Contributeurs - Damien Broqua (développeur principal du projet) - Brunus (Logo et fournisseur d'idées :wink: ) - diff --git a/docker-compose.yml.dev b/docker-compose.yml.dev index 9391624..2f7e80e 100644 --- a/docker-compose.yml.dev +++ b/docker-compose.yml.dev @@ -28,6 +28,12 @@ services: MATOMO_URL: ${MATOMO_URL} MATOMO_ID: ${MATOMO_ID} SITE_NAME: ${SITE_NAME} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + S3_BASEFOLDER: ${S3_BASEFOLDER} + S3_BUCKET: ${S3_BUCKET} + S3_ENDPOINT: ${S3_ENDPOINT} + S3_SIGNATURE: ${S3_SIGNATURE} networks: - musictopus musictopus-db: diff --git a/docker-compose.yml.prod b/docker-compose.yml.prod index 9077877..59823cb 100644 --- a/docker-compose.yml.prod +++ b/docker-compose.yml.prod @@ -28,6 +28,12 @@ services: MATOMO_URL: ${MATOMO_URL} MATOMO_ID: ${MATOMO_ID} SITE_NAME: ${SITE_NAME} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + S3_BASEFOLDER: ${S3_BASEFOLDER} + S3_BUCKET: ${S3_BUCKET} + S3_ENDPOINT: ${S3_ENDPOINT} + S3_SIGNATURE: ${S3_SIGNATURE} networks: - musictopus musictopus-db: diff --git a/package.json b/package.json index b5aea27..d346f25 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@babel/cli": "^7.17.0", "@babel/core": "^7.17.2", "@babel/preset-env": "^7.16.11", + "aws-sdk": "^2.1110.0", "axios": "^0.26.0", "connect-ensure-login": "^0.1.1", "connect-flash": "^0.1.1", @@ -64,6 +65,7 @@ "passport-local": "^1.0.0", "rimraf": "^3.0.2", "sass": "^1.49.7", + "uuid": "^8.3.2", "vue": "^3.2.31" }, "nodemonConfig": { diff --git a/src/app.js b/src/app.js index 6d1e507..a3e80bd 100644 --- a/src/app.js +++ b/src/app.js @@ -46,10 +46,10 @@ const sess = { const app = express(); -app.use(express.json()); -app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(flash()); +app.use(express.json({ limit: "50mb" })); +app.use(express.urlencoded({ extended: false, limit: "50mb" })); app.use(session(sess)); diff --git a/src/config/index.js b/src/config/index.js index 19f16ea..8070698 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -8,4 +8,10 @@ module.exports = { matomoUrl: process.env.MATOMO_URL || "", matomoId: process.env.MATOMO_ID || "", siteName: process.env.SITE_NAME || "MusicTopus", + awsAccessKeyId: process.env.AWS_ACCESS_KEY_ID, + awsSecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + s3BaseFolder: process.env.S3_BASEFOLDER || "dev", + s3Bucket: process.env.S3_BUCKET || "musictopus", + s3Endpoint: process.env.S3_ENDPOINT || "s3.fr-par.scw.cloud", + s3Signature: process.env.S3_SIGNATURE || "s3v4", }; diff --git a/src/libs/aws.js b/src/libs/aws.js new file mode 100644 index 0000000..f65521b --- /dev/null +++ b/src/libs/aws.js @@ -0,0 +1,72 @@ +import AWS from "aws-sdk"; +import fs from "fs"; +import path from "path"; +import axios from "axios"; +import { v4 as uuid } from "uuid"; + +import { + awsAccessKeyId, + awsSecretAccessKey, + s3BaseFolder, + s3Endpoint, + s3Bucket, + s3Signature, +} from "../config"; + +AWS.config.update({ + accessKeyId: awsAccessKeyId, + secretAccessKey: awsSecretAccessKey, +}); +/** + * Fonction permettant de stocker un fichier local sur S3 + * @param {String} filename + * @param {String} file + * @param {Boolean} deleteFile + * + * @return {String} + */ +export const uploadFromFile = async (filename, file, deleteFile = false) => { + const data = await fs.readFileSync(file); + + const base64data = Buffer.from(data, "binary"); + const dest = path.join(s3BaseFolder, filename); + + const s3 = new AWS.S3({ + endpoint: s3Endpoint, + signatureVersion: s3Signature, + }); + + await s3 + .putObject({ + Bucket: s3Bucket, + Key: dest, + Body: base64data, + ACL: "public-read", + }) + .promise(); + + if (deleteFile) { + fs.unlinkSync(file); + } + + return `https://${s3Bucket}.${s3Endpoint}/${dest}`; +}; + +/** + * Fonction permettant de stocker un fichier provenant d'une URL sur S3 + * @param {String} url + * + * @return {String} + */ +export const uploadFromUrl = async (url) => { + const filename = `${uuid()}.jpg`; + const file = `/tmp/${filename}`; + + const { data } = await axios.get(url, { responseType: "arraybuffer" }); + + fs.writeFileSync(file, data); + + return uploadFromFile(filename, file, true); + + // return s3Object; +}; diff --git a/src/middleware/Albums.js b/src/middleware/Albums.js index 6caa29b..a0ead43 100644 --- a/src/middleware/Albums.js +++ b/src/middleware/Albums.js @@ -1,468 +1,19 @@ +/* eslint-disable no-await-in-loop */ import moment from "moment"; -import momenttz from "moment-timezone"; -import xl from "excel4node"; import Pages from "./Pages"; +import Export from "./Export"; import AlbumsModel from "../models/albums"; +import CronModel from "../models/cron"; import UsersModel from "../models/users"; import ErrorEvent from "../libs/error"; +// import { uploadFromUrl } from "../libs/aws"; /** * Classe permettant la gestion des albums d'un utilisateur */ class Albums extends Pages { - /** - * Méthode permettant de remplacer certains cartactères par leur équivalents html - * @param {String} str - * - * @return {String} - */ - static replaceSpecialChars(str) { - if (!str) { - return ""; - } - let final = str.toString(); - const find = ["&", "<", ">"]; - const replace = ["&", "<", ">"]; - - for (let i = 0; i < find.length; i += 1) { - final = final.replace(new RegExp(find[i], "g"), replace[i]); - } - - return final; - } - - /** - * Méthode permettant de convertir les rows en csv - * @param {Array} rows - * - * @return {string} - */ - static async convertToCsv(rows) { - let data = - "Artiste;Titre;Genre;Styles;Pays;Année;Date de sortie;Format\n\r"; - - for (let i = 0; i < rows.length; i += 1) { - const { - artists_sort, - title, - genres, - styles, - country, - year, - released, - formats, - } = rows[i]; - - let format = ""; - for (let j = 0; j < formats.length; j += 1) { - format += `${format !== "" ? ", " : ""}${formats[j].name}`; - } - - data += `${artists_sort};${title};${genres.join()};${styles.join()};${country};${year};${released};${format}\n\r`; - } - - return data; - } - - /** - * Méthode permettant de convertir les rows en fichier xls - * @param {Array} rows - * - * @return {Object} - */ - static async convertToXls(rows) { - const wb = new xl.Workbook(); - const ws = wb.addWorksheet("MusicTopus"); - - const headerStyle = wb.createStyle({ - font: { - color: "#FFFFFF", - size: 11, - }, - fill: { - type: "pattern", - patternType: "solid", - bgColor: "#595959", - fgColor: "#595959", - }, - }); - const style = wb.createStyle({ - font: { - color: "#000000", - size: 11, - }, - numberFormat: "0000", - }); - - const header = [ - "Artiste", - "Titre", - "Genre", - "Styles", - "Pays", - "Année", - "Date de sortie", - "Format", - ]; - for (let i = 0; i < header.length; i += 1) { - ws.cell(1, i + 1) - .string(header[i]) - .style(headerStyle); - } - - for (let i = 0; i < rows.length; i += 1) { - const currentRow = i + 2; - const { - artists_sort, - title, - genres, - styles, - country, - year, - released, - formats, - } = rows[i]; - - let format = ""; - for (let j = 0; j < formats.length; j += 1) { - format += `${format !== "" ? ", " : ""}${formats[j].name}`; - } - - ws.cell(currentRow, 1).string(artists_sort).style(style); - ws.cell(currentRow, 2).string(title).style(style); - ws.cell(currentRow, 3).string(genres.join()).style(style); - ws.cell(currentRow, 4).string(styles.join()).style(style); - if (country) { - ws.cell(currentRow, 5).string(country).style(style); - } - if (year) { - ws.cell(currentRow, 6).number(year).style(style); - } - if (released) { - ws.cell(currentRow, 7) - .date(momenttz.tz(released, "Europe/Paris").hour(12)) - .style({ numberFormat: "dd/mm/yyyy" }); - } - ws.cell(currentRow, 8).string(format).style(style); - } - - return wb; - } - - /** - * Méthode permettant de convertir les rows en csv pour importer dans MusicTopus - * @param {Array} rows - * - * @return {string} - */ - static async convertToXml(rows) { - let data = '\n\r'; - - for (let i = 0; i < rows.length; i += 1) { - const { - discogsId, - year, - released, - uri, - artists, - artists_sort, - labels, - series, - companies, - formats, - title, - country, - notes, - identifiers, - videos, - genres, - styles, - tracklist, - extraartists, - images, - thumb, - } = rows[i]; - - let artistsList = ""; - let labelList = ""; - let serieList = ""; - let companiesList = ""; - let formatsList = ""; - let identifiersList = ""; - let videosList = ""; - let genresList = ""; - let stylesList = ""; - let tracklistList = ""; - let extraartistsList = ""; - let imagesList = ""; - - for (let j = 0; j < artists.length; j += 1) { - artistsList += ` - ${Albums.replaceSpecialChars(artists[j].name)} - ${Albums.replaceSpecialChars(artists[j].anv)} - ${Albums.replaceSpecialChars(artists[j].join)} - ${Albums.replaceSpecialChars(artists[j].role)} - ${Albums.replaceSpecialChars( - artists[j].tracks - )} - ${Albums.replaceSpecialChars(artists[j].id)} - ${Albums.replaceSpecialChars( - artists[j].resource_url - )} - ${Albums.replaceSpecialChars( - artists[j].thumbnail_url - )} - `; - } - - for (let j = 0; j < labels.length; j += 1) { - labelList += ` - `; - } - - for (let j = 0; j < series.length; j += 1) { - serieList += ` - ${Albums.replaceSpecialChars(series[j].name)} - ${Albums.replaceSpecialChars(series[j].catno)} - ${Albums.replaceSpecialChars( - series[j].entity_type - )} - ${Albums.replaceSpecialChars( - series[j].entity_type_name - )} - ${Albums.replaceSpecialChars(series[j].id)} - ${Albums.replaceSpecialChars( - series[j].resource_url - )} - ${Albums.replaceSpecialChars( - series[j].thumbnail_url - )} - - `; - } - - for (let j = 0; j < companies.length; j += 1) { - companiesList += ` - ${Albums.replaceSpecialChars(companies[j].name)} - ${Albums.replaceSpecialChars(companies[j].catno)} - ${Albums.replaceSpecialChars( - companies[j].entity_type - )} - ${Albums.replaceSpecialChars( - companies[j].entity_type_name - )} - ${Albums.replaceSpecialChars(companies[j].id)} - ${Albums.replaceSpecialChars( - companies[j].resource_url - )} - ${Albums.replaceSpecialChars( - companies[j].thumbnail_url - )} - - `; - } - - for (let j = 0; j < formats.length; j += 1) { - let descriptions = ""; - if (formats[j].descriptions) { - for ( - let k = 0; - k < formats[j].descriptions.length; - k += 1 - ) { - descriptions += `${formats[j].descriptions[k]} - `; - } - } - formatsList += ` - ${Albums.replaceSpecialChars(formats[j].name)} - ${Albums.replaceSpecialChars(formats[j].qty)} - ${Albums.replaceSpecialChars(formats[j].text)} - - ${descriptions} - - - `; - } - - for (let j = 0; j < identifiers.length; j += 1) { - identifiersList += ` - ${Albums.replaceSpecialChars(identifiers[j].type)} - ${Albums.replaceSpecialChars( - identifiers[j].value - )} - ${Albums.replaceSpecialChars( - identifiers[j].description - )} - - `; - } - - for (let j = 0; j < videos.length; j += 1) { - videosList += ` - `; - } - - for (let j = 0; j < genres.length; j += 1) { - genresList += `${Albums.replaceSpecialChars( - genres[j] - )} - `; - } - - for (let j = 0; j < styles.length; j += 1) { - stylesList += ` - `; - } - - for (let j = 0; j < tracklist.length; j += 1) { - tracklistList += ` - ${Albums.replaceSpecialChars(tracklist[j].title)} - - `; - } - - for (let j = 0; j < extraartists.length; j += 1) { - extraartistsList += ` - ${Albums.replaceSpecialChars(extraartists[j].name)} - ${Albums.replaceSpecialChars(extraartists[j].anv)} - ${Albums.replaceSpecialChars(extraartists[j].join)} - ${Albums.replaceSpecialChars(extraartists[j].role)} - ${Albums.replaceSpecialChars( - extraartists[j].tracks - )} - ${Albums.replaceSpecialChars(extraartists[j].id)} - ${Albums.replaceSpecialChars( - extraartists[j].resource_url - )} - ${Albums.replaceSpecialChars( - extraartists[j].thumbnail_url - )} - - `; - } - - for (let j = 0; j < images.length; j += 1) { - imagesList += ` - ${Albums.replaceSpecialChars(images[j].uri)} - ${Albums.replaceSpecialChars( - images[j].resource_url - )} - ${Albums.replaceSpecialChars( - images[j].resource_url - )} - - `; - } - - data += ` - - ${discogsId} - ${Albums.replaceSpecialChars(title)} - ${Albums.replaceSpecialChars(artists_sort)} - - ${artistsList} - - ${year} - ${Albums.replaceSpecialChars(country)} - ${released} - ${uri} - ${thumb} - - ${labelList} - - - ${serieList} - - - ${companiesList} - - - ${formatsList} - - ${Albums.replaceSpecialChars(notes)} - - ${identifiersList} - - - ${videosList} - - - ${genresList} - - - ${stylesList} - - - ${tracklistList} - - - ${extraartistsList} - - - ${imagesList} - - `; - } - - return `${data}`; - } - - /** - * Méthode permettant de convertir les rows en csv pour importer dans MusicTopus - * @param {Array} rows - * - * @return {string} - */ - static async convertToMusicTopus(rows) { - let data = "itemId;createdAt;updatedAt\n\r"; - - for (let i = 0; i < rows.length; i += 1) { - const { discogsId, createdAt, updatedAt } = rows[i]; - - data += `${discogsId};${createdAt};${updatedAt}\n\r`; - } - - data += "v1.0"; - - return data; - } - /** * Méthode permettant d'ajouter un album dans une collection * @param {Object} req @@ -480,9 +31,36 @@ class Albums extends Pages { : null; delete data.id; + // INFO: {POC} Pour chaque image on récupère une version que l'on stocke localement + // Utiliser un cron qui check la librairie pour mettre à jour les urls des images + // Mettre en cron l'id du nouvel élément créé pour me pas parser toute la bibliothèque à chaque fois + // if (data.thumb) { + // data.thumb = await uploadFromUrl(data.thumb); + // data.thumbType = "local"; + // } + // if (data.images && data.images.length > 0) { + // for (let i = 0; i < data.images.length; i += 1) { + // data.images[i].uri150 = await uploadFromUrl( + // data.images[i].uri150 + // ); + // data.images[i].uri = await uploadFromUrl(data.images[i].uri); + // } + // } + const album = new AlbumsModel(data); - return album.save(); + await album.save(); + + const cronData = { + model: "Albums", + id: album._id, + }; + + const cron = new CronModel(cronData); + + cron.save(); + + return album; } /** @@ -593,13 +171,13 @@ class Albums extends Pages { switch (exportFormat) { case "csv": - return Albums.convertToCsv(rows); + return Export.convertToCsv(rows); case "xls": - return Albums.convertToXls(rows); + return Export.convertToXls(rows); case "xml": - return Albums.convertToXml(rows); + return Export.convertToXml(rows); case "musictopus": - return Albums.convertToMusicTopus(rows); + return Export.convertToMusicTopus(rows); case "json": default: return { diff --git a/src/middleware/Export.js b/src/middleware/Export.js new file mode 100644 index 0000000..380f025 --- /dev/null +++ b/src/middleware/Export.js @@ -0,0 +1,453 @@ +import momenttz from "moment-timezone"; +import xl from "excel4node"; + +class Export { + /** + * Méthode permettant de remplacer certains cartactères par leur équivalents html + * @param {String} str + * + * @return {String} + */ + static replaceSpecialChars(str) { + if (!str) { + return ""; + } + let final = str.toString(); + const find = ["&", "<", ">"]; + const replace = ["&", "<", ">"]; + + for (let i = 0; i < find.length; i += 1) { + final = final.replace(new RegExp(find[i], "g"), replace[i]); + } + + return final; + } + + /** + * Méthode permettant de convertir les rows en csv + * @param {Array} rows + * + * @return {string} + */ + static async convertToCsv(rows) { + let data = + "Artiste;Titre;Genre;Styles;Pays;Année;Date de sortie;Format\n\r"; + + for (let i = 0; i < rows.length; i += 1) { + const { + artists_sort, + title, + genres, + styles, + country, + year, + released, + formats, + } = rows[i]; + + let format = ""; + for (let j = 0; j < formats.length; j += 1) { + format += `${format !== "" ? ", " : ""}${formats[j].name}`; + } + + data += `${artists_sort};${title};${genres.join()};${styles.join()};${country};${year};${released};${format}\n\r`; + } + + return data; + } + + /** + * Méthode permettant de convertir les rows en fichier xls + * @param {Array} rows + * + * @return {Object} + */ + static async convertToXls(rows) { + const wb = new xl.Workbook(); + const ws = wb.addWorksheet("MusicTopus"); + + const headerStyle = wb.createStyle({ + font: { + color: "#FFFFFF", + size: 11, + }, + fill: { + type: "pattern", + patternType: "solid", + bgColor: "#595959", + fgColor: "#595959", + }, + }); + const style = wb.createStyle({ + font: { + color: "#000000", + size: 11, + }, + numberFormat: "0000", + }); + + const header = [ + "Artiste", + "Titre", + "Genre", + "Styles", + "Pays", + "Année", + "Date de sortie", + "Format", + ]; + for (let i = 0; i < header.length; i += 1) { + ws.cell(1, i + 1) + .string(header[i]) + .style(headerStyle); + } + + for (let i = 0; i < rows.length; i += 1) { + const currentRow = i + 2; + const { + artists_sort, + title, + genres, + styles, + country, + year, + released, + formats, + } = rows[i]; + + let format = ""; + for (let j = 0; j < formats.length; j += 1) { + format += `${format !== "" ? ", " : ""}${formats[j].name}`; + } + + ws.cell(currentRow, 1).string(artists_sort).style(style); + ws.cell(currentRow, 2).string(title).style(style); + ws.cell(currentRow, 3).string(genres.join()).style(style); + ws.cell(currentRow, 4).string(styles.join()).style(style); + if (country) { + ws.cell(currentRow, 5).string(country).style(style); + } + if (year) { + ws.cell(currentRow, 6).number(year).style(style); + } + if (released) { + ws.cell(currentRow, 7) + .date(momenttz.tz(released, "Europe/Paris").hour(12)) + .style({ numberFormat: "dd/mm/yyyy" }); + } + ws.cell(currentRow, 8).string(format).style(style); + } + + return wb; + } + + /** + * Méthode permettant de convertir les rows en csv pour importer dans MusicTopus + * @param {Array} rows + * + * @return {string} + */ + static async convertToXml(rows) { + let data = '\n\r'; + + for (let i = 0; i < rows.length; i += 1) { + const { + discogsId, + year, + released, + uri, + artists, + artists_sort, + labels, + series, + companies, + formats, + title, + country, + notes, + identifiers, + videos, + genres, + styles, + tracklist, + extraartists, + images, + thumb, + } = rows[i]; + + let artistsList = ""; + let labelList = ""; + let serieList = ""; + let companiesList = ""; + let formatsList = ""; + let identifiersList = ""; + let videosList = ""; + let genresList = ""; + let stylesList = ""; + let tracklistList = ""; + let extraartistsList = ""; + let imagesList = ""; + + for (let j = 0; j < artists.length; j += 1) { + artistsList += ` + ${Export.replaceSpecialChars(artists[j].name)} + ${Export.replaceSpecialChars(artists[j].anv)} + ${Export.replaceSpecialChars(artists[j].join)} + ${Export.replaceSpecialChars(artists[j].role)} + ${Export.replaceSpecialChars(artists[j].tracks)} + ${Export.replaceSpecialChars(artists[j].id)} + ${Export.replaceSpecialChars( + artists[j].resource_url + )} + ${Export.replaceSpecialChars( + artists[j].thumbnail_url + )} + `; + } + + for (let j = 0; j < labels.length; j += 1) { + labelList += ` + `; + } + + for (let j = 0; j < series.length; j += 1) { + serieList += ` + ${Export.replaceSpecialChars(series[j].name)} + ${Export.replaceSpecialChars(series[j].catno)} + ${Export.replaceSpecialChars( + series[j].entity_type + )} + ${Export.replaceSpecialChars( + series[j].entity_type_name + )} + ${Export.replaceSpecialChars(series[j].id)} + ${Export.replaceSpecialChars( + series[j].resource_url + )} + ${Export.replaceSpecialChars( + series[j].thumbnail_url + )} + + `; + } + + for (let j = 0; j < companies.length; j += 1) { + companiesList += ` + ${Export.replaceSpecialChars(companies[j].name)} + ${Export.replaceSpecialChars(companies[j].catno)} + ${Export.replaceSpecialChars( + companies[j].entity_type + )} + ${Export.replaceSpecialChars( + companies[j].entity_type_name + )} + ${Export.replaceSpecialChars(companies[j].id)} + ${Export.replaceSpecialChars( + companies[j].resource_url + )} + ${Export.replaceSpecialChars( + companies[j].thumbnail_url + )} + + `; + } + + for (let j = 0; j < formats.length; j += 1) { + let descriptions = ""; + if (formats[j].descriptions) { + for ( + let k = 0; + k < formats[j].descriptions.length; + k += 1 + ) { + descriptions += `${formats[j].descriptions[k]} + `; + } + } + formatsList += ` + ${Export.replaceSpecialChars(formats[j].name)} + ${Export.replaceSpecialChars(formats[j].qty)} + ${Export.replaceSpecialChars(formats[j].text)} + + ${descriptions} + + + `; + } + + for (let j = 0; j < identifiers.length; j += 1) { + identifiersList += ` + ${Export.replaceSpecialChars(identifiers[j].type)} + ${Export.replaceSpecialChars(identifiers[j].value)} + ${Export.replaceSpecialChars( + identifiers[j].description + )} + + `; + } + + for (let j = 0; j < videos.length; j += 1) { + videosList += ` + `; + } + + for (let j = 0; j < genres.length; j += 1) { + genresList += `${Export.replaceSpecialChars( + genres[j] + )} + `; + } + + for (let j = 0; j < styles.length; j += 1) { + stylesList += ` + `; + } + + for (let j = 0; j < tracklist.length; j += 1) { + tracklistList += ` + ${Export.replaceSpecialChars(tracklist[j].title)} + + `; + } + + for (let j = 0; j < extraartists.length; j += 1) { + extraartistsList += ` + ${Export.replaceSpecialChars(extraartists[j].name)} + ${Export.replaceSpecialChars(extraartists[j].anv)} + ${Export.replaceSpecialChars(extraartists[j].join)} + ${Export.replaceSpecialChars(extraartists[j].role)} + ${Export.replaceSpecialChars( + extraartists[j].tracks + )} + ${Export.replaceSpecialChars(extraartists[j].id)} + ${Export.replaceSpecialChars( + extraartists[j].resource_url + )} + ${Export.replaceSpecialChars( + extraartists[j].thumbnail_url + )} + + `; + } + + for (let j = 0; j < images.length; j += 1) { + imagesList += ` + ${Export.replaceSpecialChars(images[j].uri)} + ${Export.replaceSpecialChars( + images[j].resource_url + )} + ${Export.replaceSpecialChars( + images[j].resource_url + )} + + `; + } + + data += ` + + ${discogsId} + ${Export.replaceSpecialChars(title)} + ${Export.replaceSpecialChars(artists_sort)} + + ${artistsList} + + ${year} + ${Export.replaceSpecialChars(country)} + ${released} + ${uri} + ${thumb} + + ${labelList} + + + ${serieList} + + + ${companiesList} + + + ${formatsList} + + ${Export.replaceSpecialChars(notes)} + + ${identifiersList} + + + ${videosList} + + + ${genresList} + + + ${stylesList} + + + ${tracklistList} + + + ${extraartistsList} + + + ${imagesList} + +`; + } + + return `${data}`; + } + + /** + * Méthode permettant de convertir les rows en csv pour importer dans MusicTopus + * @param {Array} rows + * + * @return {string} + */ + static async convertToMusicTopus(rows) { + let data = "itemId;createdAt;updatedAt\n\r"; + + for (let i = 0; i < rows.length; i += 1) { + const { discogsId, createdAt, updatedAt } = rows[i]; + + data += `${discogsId};${createdAt};${updatedAt}\n\r`; + } + + data += "v1.0"; + + return data; + } +} + +export default Export; diff --git a/src/models/albums.js b/src/models/albums.js index 0180dad..e08b7ad 100644 --- a/src/models/albums.js +++ b/src/models/albums.js @@ -29,6 +29,7 @@ const AlbumSchema = new mongoose.Schema( extraartists: Array, images: Array, thumb: String, + thumbType: String, }, { timestamps: true } ); diff --git a/src/models/cron.js b/src/models/cron.js new file mode 100644 index 0000000..6e782cc --- /dev/null +++ b/src/models/cron.js @@ -0,0 +1,24 @@ +import mongoose from "mongoose"; + +const { Schema } = mongoose; + +const CronSchema = new mongoose.Schema( + { + model: String, + id: Schema.Types.ObjectId, + state: { + type: String, + enum: ["NEW", "ERROR", "SUCCESS"], + default: "NEW", + }, + lastTry: Date, + lastErrorMessage: String, + tries: { + type: Number, + default: 0, + }, + }, + { timestamps: true } +); + +export default mongoose.model("Cron", CronSchema);