ok we got somewhere
build Details
--> Linted: DOCKERFILE_HADOLINT No errors were found in the linting process Details
--> Linted: EDITORCONFIG No errors were found in the linting process Details
--> Linted: GITHUB_ACTIONS No errors were found in the linting process Details
--> Linted: GITLEAKS No errors were found in the linting process Details
--> Linted: JSON No errors were found in the linting process Details
--> Linted: YAML No errors were found in the linting process Details
lint Details

This commit is contained in:
Chase 2023-08-20 23:06:42 -05:00
parent e5eee07434
commit e979ceb10d
Signed by: chase
GPG Key ID: 9EC29E797878008C
9 changed files with 1267 additions and 30 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
**/.git
**/.node_modules
.env

2
.env.example Normal file
View File

@ -0,0 +1,2 @@
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=

28
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,28 @@
---
name: "📦 Publish Docker Container"
on:
push:
branches: [master, main]
env:
PAT: ${{ secrets.PAT }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Build the Docker Image
run: docker build . --file Dockerfile --tag chase/aaa-spotify:latest --tag git.chse.dev/chase/aaa-spotify:latest
- name: Login to Container Registry
uses: docker/login-action@v2
with:
registry: git.chse.dev
username: chase
password: ${{ secrets.PAT }}
- name: Push the Docker Image to Container Registry
run: docker push git.chse.dev/chase/aaa-spotify:latest

3
.gitignore vendored
View File

@ -2,9 +2,8 @@
node_modules/
# Private Data
config.json
.env
*.db
doNotShareInfo.txt
# Misc
*.bak

6
Dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD [ "node", "." ]

View File

@ -4,3 +4,17 @@
</h1></div>
Automatically Add New Releases from Followed Artists to your Liked Songs on Spotify.
```bash
git clone https://git.chse.dev/chase/aaa-spotify.git
cd aaa-spotify
npm i
cp .env.example .env # add creds.
node .
```
To get credentials:
1. Go to [the Spotify Developer Dashboard](https://developer.spotify.com/dashboard/applications).
2. Create an app.
3. Add `http://127.0.0.1:43019` as a redirect URI.
4. Copy the Client ID and Client Secret into the `.env` file.

1130
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,11 @@
"url": "https://github.com/chase/aaa-spotify/issues"
},
"homepage": "https://github.com/chase/aaa-spotify#readme",
"dependencies": {},
"dependencies": {
"dotenv": "16.3.1",
"express": "4.18.2",
"node-fetch": "3.3.2"
},
"devDependencies": {
"eslint": "8.47.0",
"eslint-config-chase": "1.0.11",

View File

@ -1,2 +1,103 @@
/* eslint-disable unicorn/no-empty-file */
// Todo.
/* eslint-disable import/no-extraneous-dependencies */
import express from "express";
import fetch from "node-fetch";
import { config } from "dotenv";
import fs from "node:fs";
config();
const port = 43_019;
let scope = `user-follow-read user-library-modify`;
scope = encodeURIComponent(scope);
const app = express();
app.get(`/`, async (request, response) =>
{
if (request.query)
{
const { code } = request.query;
if (code)
{
// use the code to get the access and refresh tokens
const request = await fetch(`https://accounts.spotify.com/api/token`, {
method: `POST`,
headers: {
Authorization: `Basic ${ Buffer.from(`${ process.env.SPOTIFY_CLIENT_ID }:${ process.env.SPOTIFY_CLIENT_SECRET }`).toString(`base64`) }`,
"Content-Type": `application/x-www-form-urlencoded`
},
body: `grant_type=authorization_code&code=${ code }&redirect_uri=http://127.0.0.1:${ port }`,
});
const json = await request.json();
const { access_token: accessToken, refresh_token: refreshToken } = json;
// file deepcode ignore XSS: We trust Spotify.
return response.send(`<script>location.replace("token?access_token=${ accessToken }&refresh_token=${ refreshToken }");</script><noscript>Please enable JavaScript.</noscript>`);
}
}
return response.send(`<script>location.replace("token?" + location.hash.slice(1));</script><noscript>Please enable JavaScript.</noscript>`); // We got a fragment instead.
});
app.get(`/token`, async (request, response) =>
{
const { access_token: accessToken, refresh_token: refreshToken } = request.query;
let { lastRun } = request.query;
if (lastRun === undefined)
lastRun = Date.now();
response.send(`Thanks! You may now close this window.`);
fs.writeFileSync(`doNotShareInfo.txt`, `${ refreshToken }\n${ Date.now() }\n`, `utf8`);
const followedArtists = [];
const following = await fetch(`https://api.spotify.com/v1/me/following?type=artist&limit=50`, { headers: { Authorization: `Bearer ${ accessToken }` } });
const followingJson = await following.json();
if (followingJson.artists.total > 0)
{
for (const artist of followingJson.artists.items)
followedArtists.push(artist.id);
let next = followingJson.artists.next;
while (next)
{
// file deepcode ignore Ssrf: We trust Spotify.
const nextResponse = await fetch(next, { headers: { Authorization: `Bearer ${ accessToken }` } });
const nextJson = await nextResponse.json();
next = nextJson.artists.next;
for (const artist of nextJson.artists.items)
followedArtists.push(artist.id);
}
console.log(`Found ${ followedArtists.length } artists you follow.`);
console.log(`Looking for new releases (since ${ new Date(Number(lastRun)) })...`);
// TODO: Look at each artist and their latest releases, and if it's newer than lastRun, add it to their liked songs.
}
else
{
console.log(`No artists found.`);
process.exit(0);
}
});
app.listen(port, async () =>
{
const urlToAuth = `https://accounts.spotify.com/authorize?response_type=code&client_id=${ process.env.SPOTIFY_CLIENT_ID }&scope=${ scope }&redirect_uri=http://127.0.0.1:${ port }`;
if (!fs.existsSync(`doNotShareInfo.txt`))
{
console.log(`Please visit ${ urlToAuth } to authenticate with Spotify.`);
return;
}
// Not fresh run (aka we have a refresh token)
const secretInfo = fs.readFileSync(`doNotShareInfo.txt`, `utf8`);
const refreshToken = secretInfo.split(`\n`)[0];
const lastRun = secretInfo.split(`\n`)[1];
const request = await fetch(`https://accounts.spotify.com/api/token`, {
method: `POST`,
headers: {
Authorization: `Basic ${ Buffer.from(`${ process.env.SPOTIFY_CLIENT_ID }:${ process.env.SPOTIFY_CLIENT_SECRET }`).toString(`base64`) }`,
"Content-Type": `application/x-www-form-urlencoded`
},
body: `grant_type=refresh_token&refresh_token=${ refreshToken }&redirect_uri=http://127.0.0.1:${ port }`,
});
const json = await request.json();
const { access_token: accessToken } = json;
await fetch(`http://127.0.0.1:${ port }/token?access_token=${ accessToken }&refresh_token=${ refreshToken }&lastRun=${ lastRun }`);
});