nodecdtheque => MyMusicLibrary & new UI for adding album

This commit is contained in:
Damien Broqua 2022-02-17 09:37:25 +01:00
parent a35c8899ac
commit a502fe9088
14 changed files with 231 additions and 37 deletions

View file

@ -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

View file

@ -1,8 +1,8 @@
version: "2.4"
services:
nodecdtheque-www:
container_name: nodecdtheque-www
mymusiclibrary-www:
container_name: mymusiclibrary-www
image: "node:16"
restart: always
user: "node"
@ -17,21 +17,21 @@ services:
ports:
- 3001:3001
depends_on:
- nodecdtheque-db
- mymusiclibrary-db
environment:
NODE_ENV: ${NODE_ENV}
DISCOGS_TOKEN: ${DISCOGS_TOKEN}
networks:
- nodecdtheque
nodecdtheque-db:
container_name: nodecdtheque-db
- mymusiclibrary
mymusiclibrary-db:
container_name: mymusiclibrary-db
image: mongo:latest
restart: always
ports:
- 27617:27017
networks:
- nodecdtheque
- mymusiclibrary
networks:
nodecdtheque:
mymusiclibrary:
driver: bridge

View file

@ -1,5 +1,5 @@
{
"name": "nodecdtheque",
"name": "mymusiclibrary",
"version": "1.0.0",
"description": "Simple application to manage your CD/Vinyl collection",
"scripts": {
@ -18,7 +18,7 @@
},
"repository": {
"type": "git",
"url": "git@git.darkou.fr:dbroqua/nodecdtheque.git"
"url": "git@git.darkou.fr:dbroqua/MyMusicLibrary.git"
},
"author": {
"name": "Damien Broqua",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

4
sass/bulma.scss vendored
View file

@ -1 +1,5 @@
@use '../node_modules/bulma/bulma.sass';
.modal-content {
width: 80%;
}

View file

@ -13,8 +13,9 @@ import { isXhr } from "./helpers";
import indexRouter from "./routes";
import addAlbumRouter from "./routes/addAlbum";
import importRouterApiV1 from "./routes/api/v1";
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
import importSearchRouterApiV1 from "./routes/api/v1/search";
// Mongoose schema init
require("./models/users");
@ -81,8 +82,8 @@ app.use(
app.use("/", indexRouter);
app.use("/ajouter-un-album", addAlbumRouter);
app.use("/api/v1", importRouterApiV1);
app.use("/api/v1/albums", importAlbumRouterApiV1);
app.use("/api/v1/search", importSearchRouterApiV1);
// Handle 404
app.use((req, res) => {

View file

@ -9,7 +9,7 @@ import http from "http";
import app from "../app";
import { port } from "../config";
const debug = debugLib("nodecdtheque:server");
const debug = debugLib("mymusiclibrary:server");
const server = http.createServer(app);
/**

View file

@ -1,7 +1,8 @@
module.exports = {
nodeEnv: process.env.NODE_ENV || "development",
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",
discogsToken: process.env.DISCOGS_TOKEN,
};

View file

@ -38,6 +38,10 @@ class Pages {
this.pageContent.page[field] = value;
}
getPageContent(field) {
return this.pageContent.page[field];
}
/**
* Rendu de la page
* @return {Object}

View file

@ -2,14 +2,12 @@ import express from "express";
import { ensureLoggedIn } from "connect-ensure-login";
import { sendResponse } from "../../../libs/format";
import { searchSong } from "../../../helpers";
import { searchSong, getAlbumDetails } from "../../../helpers";
// eslint-disable-next-line new-cap
const router = express.Router();
router
.route("/search")
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
const data = await searchSong(req.query.q);
@ -19,4 +17,16 @@ router
}
});
router
.route("/:discogsId")
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
const data = await getAlbumDetails(req.params.discogsId);
sendResponse(req, res, data);
} catch (err) {
next(err);
}
});
export default router;

View file

@ -12,15 +12,12 @@ import render from "../libs/format";
const router = express.Router();
router.route("/").get((req, res, next) => {
if (req.user) {
return res.redirect("/ma-collection");
}
try {
const page = new Pages(req, "home");
return render(res, page);
render(res, page);
} catch (err) {
return next(err);
next(err);
}
});
@ -88,7 +85,11 @@ router
await page.loadMyCollection();
if (page.getPageContent("artists").length > 0) {
render(res, page);
} else {
res.redirect("/ajouter-un-album");
}
} catch (err) {
next(err);
}

View file

@ -4,7 +4,7 @@
<meta charset="utf-8">
<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="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">
<div class="navbar-brand">
<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 role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
@ -87,7 +88,12 @@
<% if ( user ) { %>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
Mon compte
<span class="icon">
<i class="fa-solid fa-user"></i>
</span>
<span>
<%= user.username %>
</span>
</a>
<div class="navbar-dropdown">
@ -165,7 +171,7 @@
<footer class="footer">
<div class="content has-text-centered">
<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.
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>

View file

@ -36,7 +36,8 @@
<img :src="item.thumb" :alt="item.title" style="max-width: 120px;"/>
</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>{{ item.year }}</td>
<td>{{ item.country }}</td>
@ -58,6 +59,120 @@
</tr>
</tbody>
</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>
<script>
Vue.createApp({
@ -66,6 +181,8 @@
q: '',
loading: false,
items: [],
details: {},
modalIsVisible: false,
}
},
methods: {
@ -116,7 +233,38 @@
.finally(() => {
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')
</script>

View file

@ -1,16 +1,35 @@
<section class="section">
<div class="container">
<div class="header"></div>
<h1 class="title">
Ma CDThèque
<h1 class="title is-1">
My Music Library
</h1>
<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>
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 />
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>
<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>
</section>