diff --git a/.github/readme/imgs/plugin_music_playlist.png b/.github/readme/imgs/plugin_music_playlist.png
new file mode 100644
index 00000000..32df6d3c
Binary files /dev/null and b/.github/readme/imgs/plugin_music_playlist.png differ
diff --git a/.github/readme/imgs/plugin_music_playlist_apple.png b/.github/readme/imgs/plugin_music_playlist_apple.png
new file mode 100644
index 00000000..1a22d5b1
Binary files /dev/null and b/.github/readme/imgs/plugin_music_playlist_apple.png differ
diff --git a/.github/readme/imgs/plugin_music_playlist_spotify.png b/.github/readme/imgs/plugin_music_playlist_spotify.png
new file mode 100644
index 00000000..369f7164
Binary files /dev/null and b/.github/readme/imgs/plugin_music_playlist_spotify.png differ
diff --git a/.github/readme/imgs/plugin_music_recent.png b/.github/readme/imgs/plugin_music_recent.png
new file mode 100644
index 00000000..6a313b7a
Binary files /dev/null and b/.github/readme/imgs/plugin_music_recent.png differ
diff --git a/.github/readme/imgs/plugin_music_recent_spotify_token_0.png b/.github/readme/imgs/plugin_music_recent_spotify_token_0.png
new file mode 100644
index 00000000..4052ca7c
Binary files /dev/null and b/.github/readme/imgs/plugin_music_recent_spotify_token_0.png differ
diff --git a/.github/readme/imgs/plugin_music_recent_spotify_token_1.png b/.github/readme/imgs/plugin_music_recent_spotify_token_1.png
new file mode 100644
index 00000000..5a1c2f3d
Binary files /dev/null and b/.github/readme/imgs/plugin_music_recent_spotify_token_1.png differ
diff --git a/.github/readme/imgs/plugin_music_recent_spotify_token_2.png b/.github/readme/imgs/plugin_music_recent_spotify_token_2.png
new file mode 100644
index 00000000..9add7f5e
Binary files /dev/null and b/.github/readme/imgs/plugin_music_recent_spotify_token_2.png differ
diff --git a/README.md b/README.md
index 293bb9cc..5b43b2ae 100644
--- a/README.md
+++ b/README.md
@@ -120,10 +120,13 @@ jobs:
# Skip commits flagged with [Skip GitHub Action] from commits count
plugin_selfskip: no
-
+
+ # Enable music plugin (checkout documentation for option list)
+ plugin_music: no
+
# Optimize SVG image
optimize: yes
-
+
# Number of repositories to use to compute metrics
repositories: 100
@@ -171,7 +174,7 @@ Since GitHub API has rate limitations, the shared instance has a few limitations
* Your generated metrics won't be updated during this amount of time when queried
* The rate limiter is enabled, although it won't affect already cached users metrics
* Plugins which consume additional requests or require elevated token rights are disabled.
-
+
To ensure maximum availability, consider deploying your own instance or use the GitHub Action.
@@ -286,6 +289,13 @@ Open and edit `settings.json` to configure your instance using a text editor of
"enabled":true,
//Number of events used to compute coding habits (capped at 100 by GitHub API)
"from":100,
+ },
+ //Music plugin
+ "music":{
+ //Enable or disable this plugin. Pass "?music=1" in url to generate music metrics
+ "enabled":true,
+ //Music provider token, optionally required depending on provider
+ "token":"****************************************"
}
}
}
@@ -366,7 +376,7 @@ The following errors code can be encountered if on a server instance :
### 🖼️ Templates
Templates allows you to style your metrics.
-The default is the classic one, but you can change it for something more stylish.
+The default is the classic one, but you can change it for something more stylish.
Some metrics may be displayed differently, and it is possible that not all plugins are supported or behave the same from one template to another.
@@ -410,7 +420,6 @@ Choose the parts you want to keep and update your url query. For exemple, to kee
-
#### ⏱️ PageSpeed
The *pagespeed* plugin allows you to add the performances of the website attached to the GitHub user account :
@@ -452,6 +461,235 @@ Add the following to your `settings.json` and pass `?pagespeed=1` in url when ge
+#### 🎼 Music
+
+The *music* plugin can work in the following modes :
+
+##### Playlist mode
+
+Select randomly a few tracks from a given playlist so you can suggest your favorite tracks to your visitors.
+
+
+
+
+💬 About
+
+Select a music provider below for instructions.
+
+
+Apple Music
+
+You will need to extract the *embed* url of the playlist you want to share.
+
+Connect to [music.apple.com](https://music.apple.com/) and select the playlist you want to share.
+From the `...` menu, select `Share` and `Copy embed code`.
+
+
+
+Paste the code in your clipboard and extract the source link from it :
+```html
+
+```
+
+Once you've extracted the embed url you can finish the setup :
+
+##### Setup with GitHub actions
+
+Add the following to your workflow :
+```yaml
+- uses: lowlighter/metrics@latest
+ with:
+ # ... other options
+ plugin_music: yes
+ plugin_music_provider: apple
+ plugin_music_mode: playlist
+ plugin_music_playlist: https://********
+ plugin_music_limit: 4 # Set the number of tracks you want to display
+```
+
+##### Setup in your own instance
+
+Add the following to your `settings.json`.
+```json
+ "plugins":{
+ "music":{
+ "enabled":true
+ }
+ }
+```
+
+Pass `?music=1&music.provider=apple&music.mode=playlist&music.playlist=https%3A%2F%2F********` in url when generating metrics.
+Note that given url must be escaped (you can use `encodeURIComponent` from your browser console if needed).
+
+You can optionally pass `?music.limit=` parameter to configure the number of tracks to display.
+
+
+
+
+Spotify
+
+You will need to extract the *embed* url of the playlist you want to share.
+
+Open Spotify and select the playlist you want to share.
+From the `...` menu, select `Share` and `Copy embed code`.
+
+
+
+Paste the code in your clipboard and extract the source link from it :
+```html
+
+```
+Once you've extracted the embed url you can finish the setup :
+
+##### Setup with GitHub actions
+
+Add the following to your workflow :
+```yaml
+- uses: lowlighter/metrics@latest
+ with:
+ # ... other options
+ plugin_music: yes
+ plugin_music_provider: spotify
+ plugin_music_mode: playlist
+ plugin_music_playlist: https://********
+ plugin_music_limit: 4 # Set the number of tracks you want to display
+```
+
+##### Setup in your own instance
+
+Add the following to your `settings.json`.
+```json
+ "plugins":{
+ "music":{
+ "enabled":true
+ }
+ }
+```
+
+Pass `?music=1&music.provider=spotify&music.mode=playlist&music.playlist=https%3A%2F%2F********` in url when generating metrics.
+Note that given url must be escaped (you can use `encodeURIComponent` from your browser console if needed).
+
+You can optionally pass `?music.limit=` parameter to configure the number of tracks to display.
+
+
+
+
+
+##### Recently played mode
+
+Display the track you played recently.
+
+
+
+
+💬 About
+
+Select a music provider below for additional instructions.
+
+
+Apple Music
+
+This mode is not supported for now.
+
+I tried to find a way with *smart playlists*, *shortcuts* and other stuff but could not figure a workaround to do it without paying the 99$ fee for developper program.
+
+So unfortunately this isn't available for now.
+
+
+
+
+Spotify
+
+Spotify does not have *personal tokens*, so it makes the processus a bit longer because you're required to follow the [authorization workflow](https://developer.spotify.com/documentation/general/guides/authorization-guide/)... Follow the instructions below for *TL;DR* and obtain the `refresh_token`.
+
+Sign-in to the [developer dashboard](https://developer.spotify.com/dashboard/) and create a new app.
+Keep your `client_id` and `client_secret` and keep this tab open for now.
+
+
+
+Then open the settings and add a new *Redirect url*. Normally you use it to setup callbacks for your apps, but since we don't have one and it is mandatory as per the [authorization guide](https://developer.spotify.com/documentation/general/guides/authorization-guide/), just put `https://localhost`.
+
+Next forge the url for authorization with your `client_id` and the encoded `redirect_uri` you whitelisted, and access it from your browser.
+
+```
+https://accounts.spotify.com/authorize?client_id=********&response_type=code&scope=user-read-recently-played&redirect_uri=https%3A%2F%2Flocalhost
+```
+When prompted, authorize your application.
+
+
+
+Next you'll be redirected to `redirect_uri`. Extract the generated authorization `code` from your url bar.
+
+
+
+Then go back to the developer dashboard tab, open the web console of your browser and paste the following JavaScript code, with your own `client_id`, `client_secret`, authorization `code` and `redirect_uri`.
+
+```js
+(async () => {
+ console.log(await (await fetch("https://accounts.spotify.com/api/token", {
+ method:"POST",
+ headers:{"Content-Type":"application/x-www-form-urlencoded"},
+ body:new URLSearchParams({
+ grant_type:"authorization_code",
+ redirect_uri:"https://localhost",
+ client_id:"********",
+ client_secret:"********",
+ code:"********",
+ })
+ })).json())
+})()
+```
+
+It should return a JSON response with the following content :
+```json
+{
+ "access_token":"********",
+ "expires_in": 3600,
+ "scope":"user-read-recently-played",
+ "token_type":"Bearer",
+ "refresh_token":"********"
+}
+```
+
+Now that you've got your `client_id`, `client_secret` and `refresh_token` you can finish the setup :
+
+##### Setup with GitHub actions
+
+Add the following to your workflow :
+```yaml
+- uses: lowlighter/metrics@latest
+ with:
+ # ... other options
+ plugin_music: yes
+ plugin_music_provider: spotify
+ plugin_music_token: "${{ secrets.SPOTIFY_CLIENT_ID }}, ${{ secrets.SPOTIFY_CLIENT_SECRET }}, ${{ secrets.SPOTIFY_REFRESH_TOKEN }}"
+ plugin_music_mode: recent
+ plugin_music_limit: 4 # Set the number of tracks you want to display
+```
+
+##### Setup in your own instance
+
+Add the following to your `settings.json`.
+```json
+ "plugins":{
+ "music":{
+ "enabled":true,
+ "token":"****************************************"
+ }
+ }
+```
+
+Pass `?music=1&music.provider=spotify&music.mode=recent` in url when generating metrics.
+Note that given url must be escaped (you can use `encodeURIComponent` from your browser console if needed).
+
+You can optionally pass `?music.limit=` parameter to configure the number of tracks to display.
+
+*Note : As you can see, currently this plugin is only designed for a single user, as you can only put one token. But a feature release may allow multiple users.*
+
+
+
+
+
#### 👨💻 Lines
The *lines* of code plugin allows you to compute the number of lines of code you added and removed across all of your repositories.
diff --git a/src/metrics.mjs b/src/metrics.mjs
index 6e433be3..f92ba547 100644
--- a/src/metrics.mjs
+++ b/src/metrics.mjs
@@ -6,6 +6,7 @@
import Plugins from "./plugins/index.mjs"
import Templates from "./templates/index.mjs"
import puppeteer from "puppeteer"
+ import url from "url"
//Setup
export default async function metrics({login, q}, {graphql, rest, plugins, conf}) {
@@ -43,7 +44,7 @@
//Template
console.debug(`metrics/compute/${login} > compute`)
const computer = Templates[template].default || Templates[template]
- await computer({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports:{plugins:Plugins, imgb64, axios, puppeteer, format, shuffle}})
+ await computer({login, q}, {conf, data, rest, graphql, plugins}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, format, shuffle}})
await Promise.all(pending)
console.debug(`metrics/compute/${login} > compute > success`)
@@ -84,7 +85,9 @@
/** Array shuffler */
function shuffle(array) {
- for (let i = array.length-1, j = Math.floor(Math.random()*(i+1)); i > 0; i--)
- [array[i], array[j]] = [array[j], array[i]]
+ for (let i = array.length-1; i > 0; i--) {
+ const j = Math.floor(Math.random()*(i+1))
+ ;[array[i], array[j]] = [array[j], array[i]]
+ }
return array
}
diff --git a/src/plugins/music/index.mjs b/src/plugins/music/index.mjs
index e89fddae..b8893dcf 100644
--- a/src/plugins/music/index.mjs
+++ b/src/plugins/music/index.mjs
@@ -113,23 +113,37 @@
switch (provider) {
//Spotify
case "spotify":{
+ //Prepare credentials
+ const [client_id, client_secret, refresh_token] = token.split(",").map(part => part.trim())
+ if ((!client_id)||(!client_secret)||(!refresh_token))
+ throw {status:`Spotify token must contain client id/secret and refresh token`}
//API call and parse tracklist
try {
- tracks = (await imports.axios(`https://api.spotify.com/v1/me/player/recently-played?limit=${limit}&after=${timestamp}`, {headers:{
- "Accept":"application/json",
- "Content-Type":"application/json",
- "Authorization":`Bearer ${token}`}
- })).data.items.map(({track}) => ({
- name:track.name,
- artist:track.artists[0].name,
- artwork:track.album.images[0].url,
- }))
+ //Request access token
+ console.debug(`metrics/compute/${login}/plugins > music > requesting access token with refresh token for spotify`)
+ const {data:{access_token:access}} = await imports.axios.post("https://accounts.spotify.com/api/token",
+ `${new imports.url.URLSearchParams({grant_type:"refresh_token", refresh_token, client_id, client_secret})}`,
+ {headers:{"Content-Type":"application/x-www-form-urlencoded"}},
+ )
+ console.log(access)
+ console.debug(`metrics/compute/${login}/plugins > music > got new access token`)
+ //Retriev tracks
+ tracks = (await imports.axios(`https://api.spotify.com/v1/me/player/recently-played?limit=${limit}&after=${timestamp}`, {headers:{
+ "Accept":"application/json",
+ "Content-Type":"application/json",
+ "Authorization":`Bearer ${access}`}
+ })).data.items.map(({track}) => ({
+ name:track.name,
+ artist:track.artists[0].name,
+ artwork:track.album.images[0].url,
+ }))
}
//Handle errors
catch (error) {
console.debug(error)
if ((error.response)&&(error.response.status))
throw {status:`API call returned ${error.response.status}`}
+ throw error
}
break
}