nodecdtheque => MyMusicLibrary & new UI for adding album
This commit is contained in:
parent
a35c8899ac
commit
a502fe9088
14 changed files with 231 additions and 37 deletions
|
@ -1,6 +1,6 @@
|
||||||
# nodecdtheque
|
# My Music Library
|
||||||
|
|
||||||
NodeCDThèque (non temporaire faute de mieux haha) est une application Web permettant de lister votre collection de CD ou vinyles.
|
My Music Library est une application Web permettant de lister votre collection de CD ou vinyles.
|
||||||
|
|
||||||
## Prérequis
|
## Prérequis
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
version: "2.4"
|
version: "2.4"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nodecdtheque-www:
|
mymusiclibrary-www:
|
||||||
container_name: nodecdtheque-www
|
container_name: mymusiclibrary-www
|
||||||
image: "node:16"
|
image: "node:16"
|
||||||
restart: always
|
restart: always
|
||||||
user: "node"
|
user: "node"
|
||||||
|
@ -17,21 +17,21 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- 3001:3001
|
- 3001:3001
|
||||||
depends_on:
|
depends_on:
|
||||||
- nodecdtheque-db
|
- mymusiclibrary-db
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: ${NODE_ENV}
|
NODE_ENV: ${NODE_ENV}
|
||||||
DISCOGS_TOKEN: ${DISCOGS_TOKEN}
|
DISCOGS_TOKEN: ${DISCOGS_TOKEN}
|
||||||
networks:
|
networks:
|
||||||
- nodecdtheque
|
- mymusiclibrary
|
||||||
nodecdtheque-db:
|
mymusiclibrary-db:
|
||||||
container_name: nodecdtheque-db
|
container_name: mymusiclibrary-db
|
||||||
image: mongo:latest
|
image: mongo:latest
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 27617:27017
|
- 27617:27017
|
||||||
networks:
|
networks:
|
||||||
- nodecdtheque
|
- mymusiclibrary
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
nodecdtheque:
|
mymusiclibrary:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "nodecdtheque",
|
"name": "mymusiclibrary",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Simple application to manage your CD/Vinyl collection",
|
"description": "Simple application to manage your CD/Vinyl collection",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git@git.darkou.fr:dbroqua/nodecdtheque.git"
|
"url": "git@git.darkou.fr:dbroqua/MyMusicLibrary.git"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Damien Broqua",
|
"name": "Damien Broqua",
|
||||||
|
|
BIN
public/logo.png
BIN
public/logo.png
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.3 KiB |
4
sass/bulma.scss
vendored
4
sass/bulma.scss
vendored
|
@ -1 +1,5 @@
|
||||||
@use '../node_modules/bulma/bulma.sass';
|
@use '../node_modules/bulma/bulma.sass';
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
width: 80%;
|
||||||
|
}
|
|
@ -13,8 +13,9 @@ import { isXhr } from "./helpers";
|
||||||
|
|
||||||
import indexRouter from "./routes";
|
import indexRouter from "./routes";
|
||||||
import addAlbumRouter from "./routes/addAlbum";
|
import addAlbumRouter from "./routes/addAlbum";
|
||||||
import importRouterApiV1 from "./routes/api/v1";
|
|
||||||
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
|
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
|
||||||
|
import importSearchRouterApiV1 from "./routes/api/v1/search";
|
||||||
|
|
||||||
// Mongoose schema init
|
// Mongoose schema init
|
||||||
require("./models/users");
|
require("./models/users");
|
||||||
|
@ -81,8 +82,8 @@ app.use(
|
||||||
|
|
||||||
app.use("/", indexRouter);
|
app.use("/", indexRouter);
|
||||||
app.use("/ajouter-un-album", addAlbumRouter);
|
app.use("/ajouter-un-album", addAlbumRouter);
|
||||||
app.use("/api/v1", importRouterApiV1);
|
|
||||||
app.use("/api/v1/albums", importAlbumRouterApiV1);
|
app.use("/api/v1/albums", importAlbumRouterApiV1);
|
||||||
|
app.use("/api/v1/search", importSearchRouterApiV1);
|
||||||
|
|
||||||
// Handle 404
|
// Handle 404
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import http from "http";
|
||||||
import app from "../app";
|
import app from "../app";
|
||||||
import { port } from "../config";
|
import { port } from "../config";
|
||||||
|
|
||||||
const debug = debugLib("nodecdtheque:server");
|
const debug = debugLib("mymusiclibrary:server");
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
nodeEnv: process.env.NODE_ENV || "development",
|
nodeEnv: process.env.NODE_ENV || "development",
|
||||||
port: parseInt(process.env.PORT || "3001", 10),
|
port: parseInt(process.env.PORT || "3001", 10),
|
||||||
mongoDbUri: process.env.MONGODB_URI || "mongodb://nodecdtheque-db/cdtheque",
|
mongoDbUri:
|
||||||
|
process.env.MONGODB_URI || "mongodb://mymusiclibrary-db/mymusiclibrary",
|
||||||
secret: process.env.SECRET || "waemaeMe5ahc6ce1chaeKohKa6Io8Eik",
|
secret: process.env.SECRET || "waemaeMe5ahc6ce1chaeKohKa6Io8Eik",
|
||||||
discogsToken: process.env.DISCOGS_TOKEN,
|
discogsToken: process.env.DISCOGS_TOKEN,
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,6 +38,10 @@ class Pages {
|
||||||
this.pageContent.page[field] = value;
|
this.pageContent.page[field] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPageContent(field) {
|
||||||
|
return this.pageContent.page[field];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rendu de la page
|
* Rendu de la page
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
|
|
|
@ -2,17 +2,27 @@ import express from "express";
|
||||||
import { ensureLoggedIn } from "connect-ensure-login";
|
import { ensureLoggedIn } from "connect-ensure-login";
|
||||||
|
|
||||||
import { sendResponse } from "../../../libs/format";
|
import { sendResponse } from "../../../libs/format";
|
||||||
import { searchSong } from "../../../helpers";
|
import { searchSong, getAlbumDetails } from "../../../helpers";
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
// eslint-disable-next-line new-cap
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router
|
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
.route("/search")
|
|
||||||
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
|
||||||
try {
|
try {
|
||||||
const data = await searchSong(req.query.q);
|
const data = await searchSong(req.query.q);
|
||||||
|
|
||||||
|
sendResponse(req, res, data);
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router
|
||||||
|
.route("/:discogsId")
|
||||||
|
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const data = await getAlbumDetails(req.params.discogsId);
|
||||||
|
|
||||||
sendResponse(req, res, data);
|
sendResponse(req, res, data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err);
|
|
@ -12,15 +12,12 @@ import render from "../libs/format";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.route("/").get((req, res, next) => {
|
router.route("/").get((req, res, next) => {
|
||||||
if (req.user) {
|
|
||||||
return res.redirect("/ma-collection");
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const page = new Pages(req, "home");
|
const page = new Pages(req, "home");
|
||||||
|
|
||||||
return render(res, page);
|
render(res, page);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return next(err);
|
next(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -88,7 +85,11 @@ router
|
||||||
|
|
||||||
await page.loadMyCollection();
|
await page.loadMyCollection();
|
||||||
|
|
||||||
|
if (page.getPageContent("artists").length > 0) {
|
||||||
render(res, page);
|
render(res, page);
|
||||||
|
} else {
|
||||||
|
res.redirect("/ajouter-un-album");
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<title><% if (page.title) { %><%= page.title %> <% } else { %> DarKou - Ma CDThèque <% } %></title>
|
<title><% if (page.title) { %><%= page.title %> <% } else { %> DarKou - My Music Library <% } %></title>
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||||
|
@ -55,7 +55,8 @@
|
||||||
<nav class="navbar is-fixed-top is-light" role="navigation" aria-label="main navigation">
|
<nav class="navbar is-fixed-top is-light" role="navigation" aria-label="main navigation">
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="navbar-item" href="/">
|
<a class="navbar-item" href="/">
|
||||||
<img src="/logo.png" alt="Ma CDThèque">
|
<img src="/logo.png" alt="Logo">
|
||||||
|
<h1 class="title ml-2">My Music Library</h1>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||||
|
@ -87,7 +88,12 @@
|
||||||
<% if ( user ) { %>
|
<% if ( user ) { %>
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
<a class="navbar-link">
|
<a class="navbar-link">
|
||||||
Mon compte
|
<span class="icon">
|
||||||
|
<i class="fa-solid fa-user"></i>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<%= user.username %>
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="navbar-dropdown">
|
<div class="navbar-dropdown">
|
||||||
|
@ -165,7 +171,7 @@
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="content has-text-centered">
|
<div class="content has-text-centered">
|
||||||
<p>
|
<p>
|
||||||
<strong>Ma CDThèque</strong> par <a href="https://www.darkou.fr">Damien Broqua</a>.
|
<strong title="Merci Brunus ! 😜">My Music Library</strong> par <a href="https://www.darkou.fr">Damien Broqua</a>.
|
||||||
Fait avec ❤ à Bordeaux.
|
Fait avec ❤ à Bordeaux.
|
||||||
Le code source est sous licence <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html">GNU GPL-3.0-or-later</a>.
|
Le code source est sous licence <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html">GNU GPL-3.0-or-later</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
<img :src="item.thumb" :alt="item.title" style="max-width: 120px;"/>
|
<img :src="item.thumb" :alt="item.title" style="max-width: 120px;"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a :href="'/ajouter-un-album/' + item.id">{{ item.title }}</a>
|
<a @click="loadDetails(item.id)">{{ item.title }}</a>
|
||||||
|
<!-- <a :href="'/ajouter-un-album/' + item.id">{{ item.title }}</a> -->
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.year }}</td>
|
<td>{{ item.year }}</td>
|
||||||
<td>{{ item.country }}</td>
|
<td>{{ item.country }}</td>
|
||||||
|
@ -58,6 +59,120 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="modal" :class="{'is-active': modalIsVisible}" id="addAlbum">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">{{details.artists_sort}} - {{details.title}}</p>
|
||||||
|
<button class="delete" aria-label="close" @click="toggleModal"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column is-12-mobile is-4-desktop">
|
||||||
|
<div class="has-text-centered">
|
||||||
|
<img :src="details.thumb %>" alt="Miniature" />
|
||||||
|
<hr />
|
||||||
|
<img v-for="image in details.images" :src="image.uri150" alt="Miniature" style="max-width: 60px;" />
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<ol class="ml-4">
|
||||||
|
<li v-for="track in details.tracklist">{{ track.title }} ({{track.duration}})</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-8-desktop">
|
||||||
|
<div class="column is-12">
|
||||||
|
<div class="columns is-mobile is-multiline">
|
||||||
|
<div class="column is-12-mobile is-6-desktop" v-for="genre in details.genres">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="company">Genre</label>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" id="genres" name="genres" class="input" v-model="genre" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-6-desktop" v-for="style in details.styles">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="company">Style</label>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" id="style" name="style" class="input" v-model="style" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column is-12-mobile is-6-desktop">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="year">Année</label>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" id="year" name="year" class="input" v-model="details.year" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-6-desktop">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="released">Date de sortie</label>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" id="released" name="released" class="input" v-model="details.released" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column is-12-mobile is-6-desktop">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="country">Pays</label>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" id="country" name="country" class="input" v-model="details.country" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-6-desktop" v-for="format in details.formats">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="format">Format</label>
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" id="format" name="format" class="input" v-model="format.name" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column is-12-mobile is-6-desktop">
|
||||||
|
<span class="label">Codes barres</span>
|
||||||
|
<ol>
|
||||||
|
<li v-for="identifier in details.identifiers">
|
||||||
|
{{identifier.value}} ({{identifier.type}})
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div class="column is-12-mobile is-6-desktop">
|
||||||
|
<span class="label">Label</span>
|
||||||
|
<div class="field" v-for="label in details.labels">
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" name="label" class="input" v-model="label.name" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="label">Société</span>
|
||||||
|
<div class="field" v-for="company in details.companies">
|
||||||
|
<div class="control">
|
||||||
|
<input type="text" name="company" class="input" v-model="company.name" disabled />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<button class="button is-success" @click="add">Ajouter</button>
|
||||||
|
<button class="button" @click="toggleModal">Annuler</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<button class="modal-close is-large" aria-label="close" @click="toggleModal"></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
Vue.createApp({
|
Vue.createApp({
|
||||||
|
@ -66,6 +181,8 @@
|
||||||
q: '',
|
q: '',
|
||||||
loading: false,
|
loading: false,
|
||||||
items: [],
|
items: [],
|
||||||
|
details: {},
|
||||||
|
modalIsVisible: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -116,7 +233,38 @@
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
toggleModal() {
|
||||||
|
this.modalIsVisible = !this.modalIsVisible;
|
||||||
|
},
|
||||||
|
loadDetails(discogsId) {
|
||||||
|
console.log('discogsId:', discogsId);
|
||||||
|
|
||||||
|
axios.get(`/api/v1/search/${discogsId}`)
|
||||||
|
.then( response => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
} = response;
|
||||||
|
|
||||||
|
this.details = data;
|
||||||
|
this.toggleModal();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToastr(err.response?.data?.message || "Impossible de charger les détails de cet album");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
add() {
|
||||||
|
axios.post('/api/v1/albums', this.details)
|
||||||
|
.then(() => {
|
||||||
|
window.location.href = '/ma-collection';
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showToastr(err.response?.data?.message || "Impossible d'ajouter ce album pour le moment…");
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}).mount('#app')
|
}).mount('#app')
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,16 +1,35 @@
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header"></div>
|
<div class="header"></div>
|
||||||
<h1 class="title">
|
<h1 class="title is-1">
|
||||||
Ma CDThèque
|
My Music Library
|
||||||
</h1>
|
</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
Retrouvez votre CDThèque partout depuis votre PC ou votre smartphone.
|
Retrouvez votre cdthèque partout depuis votre PC ou votre smartphone.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Ma CDThèque est un site web permettant de sauvegarder votre liste des CDs ou Vinyles et de la retrouver facilement et n'importe ou !
|
<strong>My Music Library</strong> est un site web permettant de sauvegarder votre liste des CDs ou Vinyles et de la retrouver facilement et n'importe ou !
|
||||||
<br />
|
<br />
|
||||||
Le code source est publiée sous licence <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html">GNU GPL-3.0-or-later</a>. Le code source est librement accessible sur <a href="https://git.darkou.fr/dbroqua/nodecdtheque">git.darkou.fr</a>.
|
Le code source est publiée sous licence <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html" target="_blank" rel="noopener noreferrer">GNU GPL-3.0-or-later</a>. Le code source est librement accessible sur <a href="https://git.darkou.fr/dbroqua/MyMusicLibrary" target="_blank">git.darkou.fr</a>.
|
||||||
</p>
|
</p>
|
||||||
|
<h2 class="title is-2">
|
||||||
|
Pourquoi ?
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
<strong>My Music Library</strong> est né d'un besoin personnel lorsque ma collecion de CD est devenu un peu trop grosse pour m'en souvenir et après avoir acheté par accident plusieurs fois le même CD…
|
||||||
|
<br />
|
||||||
|
Il était donc temps de trouver une solution ! Il existe plusieurs logiciel de gestion de librairies musicales mais, à ma connaissance, aucun facilement accessible via internet (genre calé sous la couette ou dans un magasin de musique 😜).
|
||||||
|
</p>
|
||||||
|
<h2 class="title is-2">
|
||||||
|
Comment ?
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Alors là clairement j'ai pas trop réfléchi… <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer">NodeJS</a> forcément, déformation pro 😅.
|
||||||
|
<br />
|
||||||
|
Pour la base de données je suis parti sur <a href="https://www.mongodb.com/" target="_blank" rel="noopener noreferrer">MongoDB</a> qui me semblait être le plus logique pour stocker une telle collection sans avoir plein de relations dans tous les sens (artiste, pays, genre, style etc…).
|
||||||
|
<br />
|
||||||
|
Pour le front j'ai voulu innover en n'utilisant pas <a href="https://jquery.com/" target="_blank" rel="noopener noreferrer">jQuery</a> et <a href="https://getbootstrap.com/" target="_blank" rel="noopener noreferrer">Boostrap</a> mais <a href="https://vuejs.org/" target="_blank" rel="noopener noreferrer">VueJS</a> et <a href="https://bulma.io/" target="_blank" rel="noopener noreferrer">Bulma</a> !
|
||||||
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
Loading…
Reference in a new issue