Um MVP local que transforma uma lista de músicas em texto simples (uma música
por linha, por exemplo Artista - Título) em uma playlist no Spotify. Ele
procura no catálogo do Spotify, pontua cada resultado em relação ao que você
digitou e, então, seleciona automaticamente a melhor correspondência ou a marca
para revisão.
- Node.js >= 20
- Para o Spotify (padrão): uma conta do Spotify e um Client ID de aplicativo do Spotify do Spotify Developer Dashboard. Nenhum client secret é necessário — a autenticação usa o fluxo de Código de Autorização com PKCE.
- Para o YouTube (opcional): um projeto no
Google Cloud Console com a YouTube
Data API v3 habilitada e um OAuth 2.0 Client ID do tipo confidential.
O suporte ao YouTube só é ativado quando
GOOGLE_CLIENT_IDeGOOGLE_CLIENT_SECRETestão definidos; caso contrário, o app roda apenas com Spotify.
-
Instale as dependências:
npm install
-
Copie o arquivo de exemplo de configuração e preencha seu Client ID:
cp .env.example .env
A URI de redirecionamento deve corresponder exatamente à que você cadastrou no Dashboard:
http://127.0.0.1:3000/auth/callback -
Inicie o servidor de API e a interface web:
npm run dev
A API ouve em
http://127.0.0.1:3000e a interface web emhttp://127.0.0.1:5173. Abra a interface, clique em Login, conclua a tela de consentimento do Spotify e volte ao app.
O YouTube/Google é um segundo provedor, à frente de um dropdown de Platform na interface. É ativado somente quando as credenciais do Google estão presentes:
-
No Google Cloud Console, crie (ou selecione) um projeto.
-
Em APIs & Services → Library, ative a YouTube Data API v3 (necessária para a busca/criação de playlist).
-
Em APIs & Services → OAuth consent screen, configure o app: escolha External (ou Internal para conta de trabalho/educação), preencha os dados e, enquanto estiver em Testing, adicione seu e-mail como tester — senão a autorização do Google é recusada.
-
Em APIs & Services → Credentials → Create credentials → OAuth client ID, crie uma credencial do tipo Web application e registre:
-
Authorized JavaScript origins — a origem da interface web:
- desenvolvimento:
http://127.0.0.1:5173 - produção: a origem onde a SPA é servida (ex.:
http://127.0.0.1:3000)
- desenvolvimento:
-
Authorized redirect URIs — a URI que o servidor usa:
http://127.0.0.1:3000/api/auth/youtube/callback
A redirect URI cadastrada deve casar exatamente com a que o servidor envia. Se você sobrescrever
GOOGLE_REDIRECT_URI(ou rodar em outra porta/host), cadastre essa mesma URI aqui, ou o Google recusa o callback. -
-
No arquivo baixado, copie
client_ideclient_secretpara o.env(oproject_id/client_emailnão são usados):GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET=
-
Reinicie o app. A aba YouTube passa a mostrar o botão Connect YouTube; conclua o consentimento do Google e volte.
Quando GOOGLE_CLIENT_ID e GOOGLE_CLIENT_SECRET não estão definidos, o card do
YouTube exibe "Not configured on this server" e o app continua funcionando
com Spotify. Quando apenas GOOGLE_CLIENT_ID está presente (sem o secret), o
servidor emite um aviso e o suporte ao YouTube fica indisponível.
Erro "Este aplicativo não foi publicado" / não consegue autorizar. Isso é esperado enquanto a OAuth consent screen está no estado Testing. Duas formas de resolver:
- Ficar no estado Testing (recomendado para uso local). Em APIs & Services → OAuth consent screen → Audience, clique em Add test users e adicione exatamente o e-mail da conta com a qual você faz login no Google, aguarde ~1-2 min para propagar e tente de novo. Em Testing o app expira em 30 dias e a data é renovada a cada edição da tela.
- Publicar o app. Clique em Publish app no canto superior direito da mesma tela. Para a YouTube Data API v3 o Google também pode exigir verificação do app (um formulário que pode levar dias); caso isso bloqueie, prefira ficar no estado Testing com test users.
A Authorized JavaScript origin cadastrada deve casar exatamente com a origem de onde a página faz o login:
http://127.0.0.1:5173no desenvolvimento (Vite) ouhttp://127.0.0.1:3000na produção. Uma origem diferente faz o Google recusar o callback.
Todas são opcionais, exceto SPOTIFY_CLIENT_ID, que é obrigatória para
autenticar.
| Variável | Padrão | Descrição |
|---|---|---|
SPOTIFY_CLIENT_ID |
— | O Client ID do seu aplicativo do Spotify. |
SPOTIFY_REDIRECT_URI |
http://127.0.0.1:3000/auth/callback |
Deve corresponder a uma URI de redirecionamento do Dashboard. |
PORT |
3000 |
Porta do servidor de API. |
WEB_PORT |
5173 |
Porta do servidor de desenvolvimento Vite. |
WEB_BASE_URL |
http://127.0.0.1:${WEB_PORT} |
URL para a qual a API redireciona após a autenticação. |
TOKEN_FILE |
spotify-token.json |
Onde o token de autenticação é persistido. |
GOOGLE_CLIENT_ID |
(opcional) | Client ID do Google OAuth 2.0. Habilita o YouTube. |
GOOGLE_CLIENT_SECRET |
(opcional) | Secret do Google OAuth 2.0. Necessário com o ID. |
GOOGLE_REDIRECT_URI |
http://127.0.0.1:3000/api/auth/youtube/callback |
URI de redirect registrada no Google Cloud. |
YOUTUBE_TOKEN_FILE |
youtube-token.json |
Onde o token do YouTube/Google é persistido. |
| Comando | Descrição |
|---|---|
npm run dev |
Executa o servidor de API e a interface web juntos. |
npm run dev:server |
Executa apenas o servidor de API (modo de observação). |
npm run dev:web |
Executa apenas o servidor de desenvolvimento Vite. |
npm run build |
Compila o servidor (dist-server) e a web (dist-web). |
npm run start |
Executa o servidor compilado, que serve o build da web. |
npm run typecheck |
Verifica os tipos do projeto inteiro. |
npm test |
Executa a suíte do Vitest uma vez. |
npm run test:watch |
Executa o Vitest em modo de observação. |
O código é dividido de modo que a lógica de domínio fique independente de framework e testável:
HTTP/API → PlaylistService → PlaylistOrchestrator → SpotifyService → SpotifyClient
(Express) (caso de uso) (pipeline de busca) (chamadas à Web API) (fetch + token)
server/spotify— a borda do Spotify.spotifyClientexecuta as chamadasfetchreais e renova o token.spotifyServiceencapsula o contrato da Web API (busca com limite de 10 itens, criação de playlist, adição de itens em blocos de 100) e normaliza as respostas.spotifyAuthimplementa o fluxo OAuth 2.0 com PKCE;tokenManagerfaz o cache e renova o token em disco.
server/services— o domínio.parserdivide o texto de entrada emParsedTrackLines e monta uma query de busca.matcherpontua uma linha analisada em relação a uma música candidata e escolhe a melhor.dedupremove músicas duplicadas por URI.orchestratorexecuta as buscas com um limite de concorrencia, escolhe a melhor correspondência por linha e descarta duplicatas.playlistServiceé o caso de uso que une analisar → buscar → pontuar → deduplicar → criar/adicionar.
server/routes— roteadores Express finos sobre o serviço, protegidos por uma verificação de token.web— um pequeno SPA React + Vite que conversa com a API.
Cada linha analisada é pontuada contra cada música candidata usando correspondência componentizada ponderada:
| Componente | Peso |
|---|---|
| Artista | 0,35 |
| Título | 0,45 |
| Query completa | 0,20 |
As pontuações se consolidam em vereditos:
| Veredito | Faixa de pontuação |
|---|---|
excellent |
90–100 |
good |
80–89 |
review |
60–79 |
reject |
abaixo de 60 |
Por padrão, uma correspondência é selecionada automaticamente quando sua
pontuação é igual ou superior a autoThreshold (padrão 90). Qualquer valor
inferior é retornado para revisão, em vez de ser adicionado automaticamente. O
orquestrador ignora linhas cuja melhor correspondência não atinja a faixa de
revisão (60) e conta linhas duplicadas, de modo que cada música seja adicionada
apenas uma vez.
POST /playlists/{id}/itemsaceita no máximo 100 itens por requisição; o serviço faz a divisão em blocos (chunking) para adições maiores.- Os resultados de busca são limitados a 10 itens.
- O campo
popularityfoi removido da API; ele é mantido opcional para que respostas em cache continuem sendo interpretadas. - O campo
tracksde uma playlist pode aparecer comoitems, dependendo da versão da API.
A suíte do Vitest fica em test/ e cobre o parser, as utilidades de texto, a
pontuação/correspondência, a deduplicação e o serviço de playlist de ponta a
ponta, com um SpotifyService falso (sem chamadas de rede).
npm testEste projeto foi um exercício de uso de IA local. Ele foi concebido,
implementado e depurado inteiramente a partir do opencode (CLI interativa
de engenharia de software) acoplado a um modelo do Ollama:
qwen3.8:27b-mlx com janela de contexto de 32k
(modelo exato ollama/qwen3.8:27b-mlx-32k). Nada foi enviado a um provedor
da nuvem — toda a inferência roda na máquina local.