Como construir um gerenciador de música com Nuxt.js e Express.js

Publicados: 2022-03-10
Resumo rápido ↬ Este artigo apresenta como o Multer simplifica o processo de manipulação de uploads de arquivos. Ele também apresenta como usar o Mongoose para interagir com nosso banco de dados criando um aplicativo gerenciador de música usando Express.js junto com Multer para o upload de música e Nuxt.js (estrutura Vue) para nosso frontend.

O manuseio de ativos de mídia digital como áudio e vídeo em seu aplicativo pode ser complicado devido às considerações que devem ser feitas no lado do servidor (por exemplo, rede, armazenamento e a natureza assíncrona do manuseio de uploads de arquivos). No entanto, podemos usar bibliotecas como Multer e Express.js para simplificar nosso fluxo de trabalho no back-end enquanto usamos o Nuxt.js (estrutura Vue) para criar as interações de front-end.

Sempre que um cliente da Web faz upload de um arquivo para um servidor, ele geralmente é enviado por meio de um formulário e codificado como multipart/form-data . Multer é um middleware para Express.js e Node.js que facilita o manuseio dos chamados multipart/form-data sempre que seus usuários fazem upload de arquivos. Neste tutorial, explicarei como você pode criar um aplicativo gerenciador de música usando Express.js com Multer para fazer upload de música e Nuxt.js (estrutura Vue) para nosso frontend.

Pré-requisitos

  • Familiaridade com HTML, CSS e JavaScript (ES6+);
  • Node.js, npm e MongoDB instalados em sua máquina de desenvolvimento;
  • Código VS ou qualquer editor de código de sua escolha;
  • Conhecimento básico de Express.js.
Mais depois do salto! Continue lendo abaixo ↓

Construindo o serviço de back-end

Vamos começar criando um diretório para nosso projeto navegando no diretório e emitindo npm init -y em seu terminal para criar um arquivo package.json que gerencia todas as dependências de nosso aplicativo.

 mkdir serverside && cd serverside npm init -y

Em seguida, instale multer , express e as outras dependências necessárias para inicializar um aplicativo Express.js.

 npm install express multer nodemon mongoose cors morgan body-parser --save

Em seguida, crie um arquivo index.js :

 touch index.js

Em seguida, no arquivo index.js , inicializaremos todos os módulos, criaremos um aplicativo Express.js e criaremos um servidor para conexão com navegadores:

 const express = require("express"); const PORT = process.env.PORT || 4000; const morgan = require("morgan"); const cors = require("cors"); const bodyParser = require("body-parser"); const mongoose = require("mongoose"); const config = require("./config/db"); const app = express(); //configure database and mongoose mongoose.set("useCreateIndex", true); mongoose .connect(config.database, { useNewUrlParser: true }) .then(() => { console.log("Database is connected"); }) .catch(err => { console.log({ database_error: err }); }); // db configuaration ends here //registering cors app.use(cors()); //configure body parser app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); //configure body-parser ends here app.use(morgan("dev")); // configire morgan // define first route app.get("/", (req, res) => { res.json("Hola MEVN devs...Assemble"); }); app.listen(PORT, () => { console.log(`App is running on ${PORT}`); });

Nós, em primeiro lugar, trazemos o Express.js para o projeto e, em seguida, definimos uma porta na qual nosso aplicativo será executado. Em seguida, trazemos as dependências body-parser , morgan , mongoose e cors .

Em seguida, salvamos a instância expressa em uma variável chamada app . Podemos usar a instância do app para configurar o middleware em nosso aplicativo, assim como configuramos o middleware cors . Também usamos a instância do app para configurar a rota raiz que será executada na porta que definimos.

Vamos agora criar uma pasta /config para nossa configuração de banco de dados e config de multer :

 mkdir config and cd config touch multer.js && touch db.js

Em seguida, abra config/db.js e adicione o seguinte código para configurar nosso banco de dados:

 module.exports = { database: "mongodb://localhost:27017/", secret: "password" };

(Na verdade, este é um objeto que contém a URL do banco de dados e o segredo do banco de dados.)

Executar nodemon e navegar até localhost:4000 no seu navegador deve fornecer esta mensagem:

 "Hola MEVN devs...Assemble"

Além disso, é assim que seu terminal deve se parecer agora:

Executando o Nodemon usando o Terminal
Visualização do terminal (visualização grande)

Configurando modelo, rotas e controladores

Vamos configurar uma estrutura de arquivos digitando o seguinte:

 mkdir api && cd api mkdir model && cd model && touch Music.js cd .. mkdir controller && cd controller && touch musicController.js cd .. mkdir routes && cd routes && touch music.js

Em nosso terminal, usamos mkdir para criar um novo diretório e, em seguida, cd para mover para um diretório. Então começamos criando um diretório chamado api e depois passamos para o diretório api .

O comando touch é usado para criar um novo arquivo dentro de um diretório usando o terminal, enquanto o comando cd é usado para sair de um diretório.

Agora vamos para o nosso arquivo api/model/Music.js para criar um esquema de música. Um modelo é uma classe com a qual construímos documentos. Nesse caso, cada documento será uma música com propriedades e comportamentos conforme declarado em nosso esquema:

 let mongoose = require("mongoose"); let musicSchema = mongoose.Schema({ title: { type: String, required: true }, music: { type: Object, required: true }, artist: { type: String, required: true }, created: { type: Date, default: Date.now() } }); let Music = mongoose.model("Music", musicSchema); module.exports = Music;

Vamos para config/multer para configurar o Multer:

 let multer = require("multer"); const path = require("path"); const storage = multer.diskStorage({ destination: (req, res, cb) => { cb(null, "./uploads"); }, filename: (req, file, cb) => { cb(null, new Date().toISOString() + file.originalname); } }); const fileFilter = (req, file, cb) => { if ( file.mimetype === "audio/mpeg" || file.mimetype === "audio/wave" || file.mimetype === "audio/wav" || file.mimetype === "audio/mp3" ) { cb(null, true); } else { cb(null, false); } }; exports.upload = multer({ storage: storage, limits: { fileSize: 1024 * 1024 * 5 }, fileFilter: fileFilter });

No arquivo multer.js , começamos configurando uma pasta onde serão enviados todos os arquivos de música enviados. Precisamos tornar este arquivo estático definindo-o no arquivo index.js :

 app.use('/uploads', express.static('uploads'));

Depois disso, escrevemos um validador simples que verificará o tipo MIME do arquivo antes de fazer o upload. Em seguida, definimos a instância do multer adicionando o local de armazenamento, os limites de cada arquivo e o validador que criamos.

Crie as Rotas Necessárias

Agora vamos criar nossas rotas. Abaixo está a lista de endpoints que estaremos criando.

HTTP POST /music Adicionar nova música
HTTP GET /music Obter todas as músicas
HTTP DELETE /music/:blogId Excluir uma música

Vamos começar criando a rota do blog. Vá até api/routes/music.js e escreva o seguinte código:

 const express = require("express"); const router = express.Router(); const musicController = require("../controller/musicController"); const upload = require("../../config/multer"); router.get("/", musicController.getAllMusics); router.post("/", upload.upload.single("music"), musicController.addNewMusic); router.delete("/:musicId", musicController.deleteMusic); module.exports = router;

Nota : Agora, sempre que fizermos uma solicitação get para /music . a rota chama a função getAllMusic que está localizada no arquivo 'controllers'.

Vamos passar para api/controllers/musicController para definir os controladores. Começamos escrevendo uma função para obter todas as músicas em nosso banco de dados usando o método mangusto db.collection.find que retornará todos os itens dessa coleção.

Depois de fazer isso, escrevemos outra função que criará uma nova música no banco de dados. Precisamos criar uma nova instância de música usando a palavra-chave new e depois definir o objeto de música. Depois de fazer isso, usaremos o método de save do mangusto para adicionar novas músicas ao banco de dados.

Para excluir uma música, precisamos usar o método de remove do mangusto simplesmente passando o ID da música como parâmetro na instância de remove . Isso resulta em mangusto olhando para a coleção de música que tem esse ID específico e, em seguida, removendo-o dessa coleção.

 let mongoose = require("mongoose"); const Music = require("../model/Music"); exports.getAllMusics = async (req, res) => { try { let music = await Music.find(); res.status(200).json(music); } catch (err) { res.status(500).json(err); } }; exports.addNewMusic = async (req, res) => { try { const music = new Music({ title:req.body.title, artist:req.body.artist, music:req.file }); let newMusic = await music.save(); res.status(200).json({ data: newMusic }); } catch (err) { res.status(500).json({ error: err }); } }; exports.deleteMusic = async (req, res) => { try { const id = req.params.musicId; let result = await Music.remove({ _id: id }); res.status(200).json(result); } catch (err) { res.status(500).json(err); } };

Por último, mas não menos importante, para testar as rotas, precisamos registrar as rotas de música em nosso arquivo index.js :

 const userRoutes = require("./api/user/route/user"); //bring in our user routes app.use("/user", userRoutes);

Testando os pontos finais

Para testar nossos endpoints, usaremos POSTMAN.

Adicionando novas músicas

Para testar a funcionalidade Add Music , defina o método da solicitação clicando no menu suspenso de métodos. Depois de fazer isso, digite a URL do endpoint e clique na guia body para selecionar como deseja enviar seus dados. (No nosso caso, usaremos o método form-data.)

Então clique nos dados do formulário e configure sua chave de modelo. Ao configurá-lo, atribua algum valor às chaves, conforme mostrado na imagem abaixo:

Testando Adicionando nova API de música usando o Postman
Testando Adicionando nova API de música no painel do Postman (visualização grande)

Após fazer isso, clique em 'Enviar' para fazer a solicitação.

Listando todas as músicas

Para listar todas as músicas em nosso banco de dados, temos que digitar o URL do endpoint na seção URL fornecida. Após fazer isso, clique no botão 'Enviar' para fazer a solicitação.

Testando a API de listagem usando o Postman
Testando a API de listagem no painel do Postman (visualização grande)

Excluindo música

Para excluir uma música, precisamos passar o music id como parâmetro.

Testando a API de exclusão usando o Postman
Testando o painel Excluir API Postman (visualização grande)

É isso!

Construindo o Front-end

Para nosso frontend, usaremos um framework Vue: Nuxt.js.

“Nuxt é um framework progressivo baseado em Vue.js para criar aplicações web modernas. É baseado em bibliotecas oficiais Vue.js (vue, vue-router e vuex) e poderosas ferramentas de desenvolvimento (webpack, Babel e PostCSS).”

— Guia NuxtJS

Para criar um novo aplicativo Nuxt.js, abra seu terminal e digite o seguinte (com musicapp como o nome do aplicativo que iremos construir):

 $ npx create-nuxt-app musicapp

Durante o processo de instalação, serão feitas algumas perguntas sobre a configuração do projeto:

Project name aplicativo de música
project description Um aplicativo gerenciador de música simples
Author name <seu nome>
Package manager npm
UI framework Bootstrap vue
custom ui framework Nenhum
Nuxt modules Axios,pwa (use a barra de espaço do teclado para selecionar itens)
Linting tool Mais bonito
test framework Nenhum
Rendering Mode Universal (SSR)
development tool Jsonconfig.json

Depois de selecionar tudo isso, temos que esperar um pouco para que o projeto seja configurado. Quando estiver pronto, vá para a pasta /project e veicule o projeto da seguinte forma:

 cd musicapp && npm run dev

Abra o projeto em qualquer editor de código de sua escolha e depois abra o projeto no navegador acessando localhost:3000 .

Visualização do projeto Nuxt.js
Visualização do projeto Nuxt.js (visualização grande)

Configurando o Axios

Usaremos axios para fazer uma solicitação HTTP ao nosso servidor back-end. O Axios já está instalado em nosso projeto, então só precisamos configurar o baseURL - para nosso servidor backend.

Para fazer isso, abra o arquivo nuxt.config.js no diretório root e inclua a baseURL no objeto axios .

 axios: { baseURL:'https://localhost:4000' },

Construindo o Gerenciador de Música

Configurando a IU

Vamos começar limpando a interface do usuário. Abra o arquivo pages/index.vue e remova todo o código com o seguinte:

 <template> <div>Hello</div> </template>

Depois de fazer isso, você só poderá ver um “Olá” no navegador.

No diretório root , crie uma pasta /partials . Dentro da pasta /partials partials, crie um arquivo navbar.vue e adicione o seguinte código:

 <template> <header> <nav class="navbar navbar-expand-lg navbar-light bg-info"> <div class="container"> <a class="navbar-brand" href="#">Music App</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse justify-content-end"> <ul class="navbar-nav"> <li class="nav-item active"> <a class="nav-link" href="#">Player</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Manager</a> </li> </ul> </div> </div> </nav> </header> </template> <style scoped> .nav-link, .navbar-brand { color: #ffff !important; } </style>

Nota : Usaremos o componente para navegar pelas páginas em nosso aplicativo. Este será apenas um componente simples feito de Bootstrap navbar . Confira a documentação oficial do Bootstrap para mais referência.

Em seguida, vamos definir um layout personalizado para o aplicativo. Abra a pasta /layouts , substitua o código no arquivo default.vue pelo código abaixo.

 <template> <div> <navbar /> <nuxt /> </div> </template> <script> import navbar from '@/partial/navbar' export default { components: { navbar } } </script>

Importamos a barra de navbar para este layout, o que significa que todas as páginas em nosso aplicativo terão esse componente de navbar de navegação nela. (Este será o componente que todos os outros componentes em nosso aplicativo serão montados.)

Depois disso, você poderá ver isso no seu navegador:

Componente Nuxt.js Navbar após modificação
Componente Nuxt.js Navbar (visualização grande)

Agora vamos configurar a interface do usuário para nosso gerente. Para fazer isso, precisamos criar uma pasta /manager dentro da pasta de componentes e adicionar um arquivo na pasta chamada manager.vue .

Nesse arquivo, adicione o seguinte código:

 <template> <section class="mt-5"> <div class="container mb-4"> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-body"> <div class="card-title mb-4"> <h4>Add Music</h4> </div> <form> <div class="form-group"> <label for="title">Title</label> <input type="text" class="form-control" /> </div> <div class="form-group"> <label for="artist">Artist</label> <input type="text" class="form-control" /> </div> <div class="form-group"> <label for="artist">Music</label> <div class="custom-file"> <input type="file" class="custom-file-input" /> <label class="custom-file-label" for="customFile">Choose file</label> </div> </div> <div class="form-group"> <button class="btn btn-primary">Submit</button> </div> </form> </div> </div> </div> </div> </div> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="card bg-light p-1 showdow-sm"> <div class="card-title"> <button class="btn btn-info m-3">Add Music</button> </div> <div class="card-body"> <table class="table"> <thead> <tr> <th scope="col">#</th> <th scope="col">Title</th> <th scope="col">Artist</th> <th scope="col">Date created</th> <th scope="col">Action</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Demo Title</td> <td>Wisdom.vue</td> <td>12/23/13</td> <td> <button class="btn btn-info">Delete</button> </td> </tr> </tbody> </table> </div> </div> </div> </div> </div> </section> </template>

Nota : Este é apenas um modelo simples de Bootstrap para adicionar música em nosso aplicativo. O formulário definirá um modelo de tabela que listará todas as músicas que podem ser encontradas em nosso banco de dados.

Após definir este componente, precisamos registrá-lo na pasta /pages para inicializar o roteamento.

O Nuxt.js não tem um arquivo 'router.js' como o Vue.js. Ele usa a pasta pages para roteamento. Para obter mais detalhes, visite o site Nuxt.js.

Para registrar o componente, crie uma pasta /manager dentro da pasta /pages e crie um arquivo index.vue . Em seguida, coloque o seguinte código dentro do arquivo:

 <template> <div> <manager /> </div> </template> <script> import manager from '@/components/manager/manager' export default { components: { manager } } </script>

Este é o componente que irá renderizar em nossa rota de pages .

Depois de fazer isso, vá para o seu navegador e navegue até /manager - você deve estar vendo isso:

IU do gerenciador de música
IU do gerenciador de música (visualização grande)

Listando todas as músicas

Vamos continuar criando uma função que irá buscar todas as músicas. Esta função será registrada no gancho de ciclo de vida criado, de forma que sempre que o componente for criado, a função será chamada.

Vamos começar criando uma variável na instância vue que conterá todas as músicas:

 allmusic = []; musicLoading: false,

Em seguida, defina uma função getAllMusics e adicione o seguinte código:

 async getAllMusics() { this.musicLoading = true try { let data = await this.$axios.$get('/music') this.allmusic = data this.musicLoading = false } catch (err) { this.musicLoading = false swal('Error', 'Error Fetting Musics', 'error') } }

Em seguida, registre-se no gancho do ciclo de vida criado:

 created() { this.getAllMusics() }

Emitindo os dados

Agora é hora de produzir todas as músicas na mesa que criamos anteriormente:

 <table class="table"> <thead> <tr> <th scope="col">#</th> <th scope="col">Title</th> <th scope="col">Artist</th> <th scope="col">Date created</th> <th scope="col">Action</th> </tr> </thead> <div v-if="musicLoading" class="spinner-border" role="status" > <span class="sr-only">Loading...</span> </div> <tbody v-else> <tr v-for="(music, index) in allmusic" :key="index"> <td>{{ index + 1 }}</td> <td>{{ music.title }}</td> <td>{{ music.artist }}</td> <td>{{ music.created }}</td> <td> <button class="btn btn-info" @click="deleteMusic(music._id)">Delete</button> </td> </tr> </tbody> </table>

Lembra daquela tabela que criamos anteriormente? Bem, precisaremos percorrer a resposta que recebemos de nosso back-end para listar todas as músicas recebidas de volta do banco de dados.

Adicionando música

Para adicionar uma nova música, precisamos fazer uma solicitação HTTP ao servidor back-end com os detalhes da música. Para fazer isso, vamos começar modificando o formulário e o tratamento dos uploads de arquivos.

No formulário, precisamos adicionar um ouvinte de event que ouvirá o formulário quando ele for enviado. No campo de input , adicionamos um v- -model para vincular o valor ao campo de entrada.

 <form @submit.prevent="addNewMusic"> <div class="form-group"> <label for="title">Title</label> <input type="text" v-model="musicDetails.title" class="form-control" /> </div> <div class="form-group"> <label for="artist">Artist</label> <input type="text" v-model="musicDetails.artist" class="form-control" /> </div> <div class="form-group"> <label for="artist">Music</label> <div class="custom-file"> <input type="file" ref="file" v-on:change="handleFileUpload()" class="custom-file-input" /> <label class="custom-file-label" for="customFile">Choose file</label> </div> </div> <div class="form-group"> <button class="btn btn-primary" :disabled="isDisabled"> <span class="spinner-border spinner-border-sm" v-if="addLoading" role="status" aria-hidden="true" ></span>Submit </button> </div> </form>

E a seção de script deve ficar assim:

 <script> export default { data() { return { musicDetails: { title: '', artist: '', music: '' }, allmusic = [], musicLoading: false, isValid: false; addLoading: false, } }, computed: { isDisabled: function() { if ( this.musicDetails.title === '' || this.musicDetails.artist === '' || this.musicDetails.music === '' ) { return !this.isValid } } }, methods: { handleFileUpload() { this.musicDetails.music = this.$refs.file.files[0] console.log(this.musicDetails.music.type) }, addNewMusic() { let types = /(\.|\/)(mp3|mp4)$/i if ( types.test(this.musicDetails.music.type) || types.test(this.musicDetails.music.name) ) { console.log('erjkb') } else { alert('Invalid file type') return !this.isValid } } } } </script>

Definiremos uma função que enviará uma solicitação ao nosso serviço de back-end para criar qualquer nova música que tenha sido adicionada à lista. Também. precisamos escrever uma função de validação simples que verificará o tipo de arquivo para que os usuários só possam fazer upload de arquivos com uma extensão de .mp3 e .mp4 .

É importante definir uma propriedade computada para garantir que nosso campo de entrada não esteja vazio. Também precisamos adicionar um validador simples que garantirá que o arquivo que estamos tentando enviar seja realmente um arquivo de música.

Vamos continuar editando a função addMusic para fazer uma solicitação ao nosso serviço de back-end. Mas antes de fazermos isso, vamos primeiro instalar o sweetalert que nos fornecerá uma boa janela modal. Para fazer isso, abra seu terminal e digite o seguinte:

 npm i sweetalert

Depois de instalar o pacote, crie um arquivo sweetalert.js na pasta /plugins e adicione isto:

 import Vue from 'vue'; import swal from 'sweetalert'; Vue.prototype.$swal = swal;

Em seguida, registre o plugin no arquivo nuxt.config.js dentro da instância do plugin assim:

 plugins: [ { src: '~/plugins/sweetalert' } ],

Agora configuramos com sucesso sweetalert em nosso aplicativo, para que possamos seguir em frente e editar a função addmusic para isso:

 addNewMusic() { let types = /(\.|\/)(mp3|mp4)$/i if ( types.test(this.musicDetails.music.type) || types.test(this.musicDetails.music.name) ) { let formData = new FormData() formData.append('title', this.musicDetails.title) formData.append('artist', this.musicDetails.artist) formData.append('music', this.musicDetails.music) this.addLoading = true this.$axios .$post('/music', formData) .then(response => { console.log(response) this.addLoading = false this.musicDetails = {} this.getAllMusics() // we will create this function later swal('Success', 'New Music Added', 'success') }) .catch(err => { this.addLoading = false swal('Error', 'Something Went wrong', 'error') console.log(err) }) } else { swal('Error', 'Invalid file type', 'error') return !this.isValid } },

Vamos escrever um script simples que alterne o formulário, ou seja, ele só deve ser exibido quando quisermos adicionar novas músicas.

Podemos fazer isso editando o botão 'Adicionar música' na tabela que exibe todas as músicas que podem ser encontradas:

 <button class="btn btn-info m-3" @click="initForm"> {{addState?"Cancel":"Add New Music"}} </button>

Em seguida, adicione um estado que manterá o estado do formulário na propriedade data :

 addState: false

Depois de fazer isso, vamos definir a função initForm :

 initForm() { this.addState = !this.addState },

E, em seguida, adicione v-if="addState" ao div que contém o formulário:

 <div class="card" v-if="addState">

Excluindo música

Para excluir música, precisamos chamar o endpoint delete e passar o music id como um parâmetro. Vamos adicionar um evento de click ao botão 'Excluir' que acionará a função para excluir uma função:

 <button class="btn btn-info" @click="deleteMusic(music._id)">Delete</button>

A função delete fará uma solicitação HTTP para nosso serviço de back-end. Depois de obter o ID da música do parâmetro da função deleteMusic , adicionaremos o ID na URL que estamos usando para enviar a solicitação. Isso especifica a música exata que deve ser removida do banco de dados.

 deleteMusic(id) { swal({ title: 'Are you sure?', text: 'Once deleted, you will not be able to recover this Music!', icon: 'warning', buttons: true, dangerMode: true }).then(willDelete => { if (willDelete) { this.$axios .$delete('/music/' + id) .then(response => { this.getAllMusics() swal('Poof! Your Music file has been deleted!', { icon: 'success' }) }) .catch(err => { swal('Error', 'Somethimg went wrong', 'error') }) } else { swal('Your Music file is safe!') } }) }

Com tudo isso, acabamos de construir nosso gerenciador de música. Agora é hora de construir o player de música.

Vamos começar criando uma nova pasta na pasta de componentes chamada /player . Em seguida, crie um arquivo player.vue dentro desta pasta e adicione isto:

 <template> <section> <div class="container"> <div class="row"> <div class="col-md-12"> <h3 class="text-center">Player</h3> </div> </div> </div> </section> </template> <script> export default { data() { return {} } } </script> <style scoped> </style>

Em seguida, vamos importar este componente para o arquivo index.vue na pasta /pages . Substitua o código no arquivo index.vue por este:

 <template> <div> <player /> </div> </template> <script> import player from '@/components/player/player' export default { components: { player } } </script>

Vamos configurar o roteamento em nosso componente navbar para habilitar o roteamento entre nossas páginas.

Para rotear em um aplicativo Nuxt.js, o nuxt-link é usado após o qual você especificou a página dessa rota para uma instância específica. Então, vamos editar o código no componente partials/navbar para isso:

 <template> <header> <nav class="navbar navbar-expand-lg navbar-light bg-info"> <div class="container"> <nuxt-link to="/" class="navbar-brand">Music App</nuxt-link> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse justify-content-end"> <ul class="navbar-nav"> <li class="nav-item active"> <nuxt-link to="/" class="nav-link">Player</nuxt-link> </li> <li class="nav-item"> <nuxt-link to="/manager" class="nav-link">Manager</nuxt-link> </li> </ul> </div> </div> </nav> </header> </template> <style scoped> .nav-link, .navbar-brand { color: #ffff !important; } </style>

Com isso, podemos navegar pelas nossas páginas usando a barra de navegação.

Construindo o jogador

Antes de começarmos, precisamos estender o Webpack para carregar arquivos de áudio. Os arquivos de áudio devem ser processados ​​pelo file-loader . Este carregador já está incluído na configuração padrão do Webpack, mas não está configurado para lidar com arquivos de áudio.

Para fazer isso, acesse o arquivo nuxt.config.js e modifique o objeto de build para este:

 build: { extend(config, ctx) { config.module.rules.push({ test: /\.(ogg|mp3|mp4|wav|mpe?g)$/i, loader: 'file-loader', options: { name: '\[path\][name].[ext]' } }) } }

Em seguida, vamos escrever uma função que obterá todas as músicas e, em seguida, usar o construtor Audio para reproduzir a primeira música no array allMusic .

Para começar, vamos modificar nosso arquivo player.vue para este:

 <template> <section v-if="allMusic"> <div class="container"> <div class="row"> <div class="col-md-12"> <h3 class="text-center">Player</h3> </div> </div> <div class="row"> <div class="col-md-6"> <span>{{this.current.title}} - {{this.current.artist}}</span> </div> </div> </div> </section> </template> <script> export default { data() { return { current: { title: '', artist: '' }, song: true, isplaying: false, allMusic: null, index: 0, player: '' } }, methods: { async initPlayer() { if (this.allMusic !== []) { this.current = await this.allMusic[this.index] this.player.src = `https://localhost:4000/${this.current.music.path}` } else { this.song = true } }, async getAllSongs() { try { let response = await this.$axios.$get('/music') console.log(response) if (response === []) { this.song = true this.current = null } else { this.song = false this.allMusic = response } await this.initPlayer() } catch (err) { this.current = null console.log(err) } } }, created() { if (process.client) { this.player = new Audio() } this.getAllSongs() } } </script> <style scoped> </style>

Depois que o arquivo for servido, a música será reproduzida em segundo plano e você poderá ver isso no seu navegador:

IU do leitor de música
IU do player de música (visualização grande)

Para parar a música, tudo que você precisa fazer é comentar o await await player.play() na função initPlayer .

Criando a IU do Player

Vamos agora definir nossa interface de usuário do music player substituindo o modelo em nosso arquivo player.vue pelo seguinte:

 <template> <section v-if="allMusic"> <div class="container"> <div class="row mb-5"> <div class="col-md-12"> <h3 class="text-center">Player</h3> </div> </div> <div class="row mt-5"> <div class="col-md-6"> <img src="https://images.pexels.com/photos/3624281/pexels-photo-3624281.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" class="image" /> <div class="card player_card"> <div class="card-body"> <h6 class="card-title"> <b>{{this.current.title}} - {{this.current.artist}}</b> </h6> <div> <i class="fas fa-backward control mr-4"></i> <i class="fas fa-play play"></i> <i class="fas fa-pause play"></i> <i class="fas fa-forward control ml-4"></i> </div> </div> </div> </div> <div class="col-md-6"> <div class="card shadow"> <table class="table"> <thead> <tr> <th scope="col">#</th> <th scope="col">Title</th> <th scope="col">Artist</th> <th scope="col">Action</th> </tr> </thead> <tbody> <tr> <th scope="row">1</th> <td>Mark</td> <td>Otto</td> <td> <button class="btn btn-primary">Play</button> </td> </tr> </tbody> </table> </div> </div> </div> </div> </section> </template>

Em seguida, adicione o seguinte estilo na seção de style :

 <style scoped> .image { border-radius: 5px !important; position: relative; height: 300px; width: 100%; } .player_card { text-align: center; bottom: 20px; margin: 0px 40px; } .text-muted { font-size: 15px; } .play { font-size: 40px; } .control { font-size: 25px; } </style>

Depois de modificar isso, o player deve ficar assim:

IU final do player de música
IU final do player de música (visualização grande)

Adicionando a função de reprodução

Continuaremos exibindo a descrição da música na mesa. Para isso, substitua a tabela pelo código abaixo:

 <table class="table"> <thead> <tr> <th scope="col">#</th> <th scope="col">Title</th> <th scope="col">Artist</th> <th scope="col">Action</th> </tr> </thead> <tbody> <tr v-for="(music,index) in allMusic" :key="index"> <th scope="row">{{index+1}}</th> <td>{{music.title}}</td> <td>{{music.artist}}</td> <td> <button class="btn btn-primary">Play</button> </td> </tr> </tbody> </table>

Não queremos exibir os ícones 'Reproduzir' e 'Pausar' ao mesmo tempo. Em vez disso, queremos uma situação em que, quando a música estiver tocando, o ícone 'Pause' seja exibido. Além disso, quando a música é pausada, o ícone de reprodução deve ser exibido.

Para conseguir isso, precisamos definir um estado isPlaying para a instância false e, em seguida, usar essa instância para alternar os ícones. Depois disso, adicionaremos uma função ao nosso ícone 'Play'.

 isplaying:false

Depois de fazer isso, modifique seu ícone 'Play' e 'Pause' para este:

 <i class="fas fa-play play" v-if="!isplaying" @click="play"></i> <i class="fas fa-pause play" v-else></i>

Com tudo isso vamos definir o método play :

 play(song) { console.log(song) if (song) { this.current = song this.player.src = `https://localhost:4000/${this.current.music.path}` } this.player.play() this.isplaying = true },

Nós, em primeiro lugar, pegamos a música atual e a passamos para o parâmetro da function . Em seguida, definimos a instância JavaScript Audio() . Em seguida, verificamos se a música é nula: se não for, configuramos this.current para a música que passamos no parâmetro e, em seguida, chamamos a instância do player de Audio . (Além disso, não esqueça que temos que definir o estado isPlaying como true quando a música estiver tocando.)

Adicionando a função de pausa

Para pausar uma música, usaremos o método de pausa de Audio . Precisamos adicionar um evento de click ao ícone de pausa:

 <i class="fas fa-pause play" @click="pause" v-else></i>

E então defina a função na instância de methods :

pause() { this.player.pause() this.isplaying = false },

Tocando uma música da lista de músicas

Isso é bem simples de implementar. Tudo o que precisamos fazer é adicionar um evento de click que mudará o parâmetro da song no método de play da música que acabamos de criar.

Basta modificar o botão play na tabela da lista de músicas para isto:

 <button class="btn btn-primary" @click="play(music)">Play</button>

E aí está!

Adicionando a próxima função

Para adicionar a próxima função, precisamos incrementar o índice em um. Para fazer isso, adicione um evento de click ao próximo ícone:

 @click="next"

E então defina a função prev na instância de methods :

 next() { this.index++ if (this.index > this.allMusic.length - 1) { this.index = 0 } this.current = this.allMusic[this.index] this.play(this.current) },

Essa condicional é responsável por reproduzir todas as músicas sempre que a última música da lista for reproduzida.

Adicionando a função previous

Na verdade, isso é o oposto da próxima função, então vamos adicionar um evento de click à função anterior:

 @click="prev"

Em seguida, definimos a função anterior:

 prev() { this.index-- if (this.index < 0) { this.index = this.allMusic.length - 1 } this.current = this.allMusic[this.index] this.play(this.current) },

Nosso aplicativo de player de música agora está completo!

Conclusão

Neste artigo, vimos como podemos criar um gerenciador de música com Nuxt.js e Express.js. Ao longo do caminho, vimos como o Multer agiliza o processo de manipulação de uploads de arquivos e como usar o Mongoose para interagir sem um banco de dados. Por fim, usamos o Nuxt.js para criar o aplicativo cliente, o que lhe dá uma sensação rápida e ágil.

Ao contrário de outros frameworks, construir um aplicativo com Nuxt.js e Express.js é bastante fácil e rápido. A parte legal do Nuxt.js é a maneira como ele gerencia suas rotas e faz com que você estruture melhor seus aplicativos.

  • Você pode acessar mais informações sobre Nuxt.js aqui.
  • Você pode acessar o código-fonte no Github aqui