Update readme
BIN
.github/readme/imgs/plugin_music_playlist.png
vendored
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
.github/readme/imgs/plugin_music_playlist_apple.png
vendored
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
.github/readme/imgs/plugin_music_playlist_spotify.png
vendored
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
.github/readme/imgs/plugin_music_recent.png
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
.github/readme/imgs/plugin_music_recent_spotify_token_0.png
vendored
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
.github/readme/imgs/plugin_music_recent_spotify_token_1.png
vendored
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
.github/readme/imgs/plugin_music_recent_spotify_token_2.png
vendored
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
240
README.md
@@ -121,6 +121,9 @@ jobs:
|
|||||||
# Skip commits flagged with [Skip GitHub Action] from commits count
|
# Skip commits flagged with [Skip GitHub Action] from commits count
|
||||||
plugin_selfskip: no
|
plugin_selfskip: no
|
||||||
|
|
||||||
|
# Enable music plugin (checkout documentation for option list)
|
||||||
|
plugin_music: no
|
||||||
|
|
||||||
# Optimize SVG image
|
# Optimize SVG image
|
||||||
optimize: yes
|
optimize: yes
|
||||||
|
|
||||||
@@ -286,6 +289,13 @@ Open and edit `settings.json` to configure your instance using a text editor of
|
|||||||
"enabled":true,
|
"enabled":true,
|
||||||
//Number of events used to compute coding habits (capped at 100 by GitHub API)
|
//Number of events used to compute coding habits (capped at 100 by GitHub API)
|
||||||
"from":100,
|
"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":"****************************************"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,7 +420,6 @@ Choose the parts you want to keep and update your url query. For exemple, to kee
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
#### ⏱️ PageSpeed
|
#### ⏱️ PageSpeed
|
||||||
|
|
||||||
The *pagespeed* plugin allows you to add the performances of the website attached to the GitHub user account :
|
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
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
#### 🎼 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>💬 About</summary>
|
||||||
|
|
||||||
|
Select a music provider below for instructions.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Apple Music</summary>
|
||||||
|
|
||||||
|
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
|
||||||
|
<iframe allow="" frameborder="" height="" style="" sandbox="" src="https://embed.music.apple.com/**/playlist/********"></iframe>
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Spotify</summary>
|
||||||
|
|
||||||
|
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
|
||||||
|
<iframe src="https://open.spotify.com/embed/playlist/********" width="" height="" frameborder="0" allowtransparency="" allow=""></iframe>
|
||||||
|
```
|
||||||
|
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.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
##### Recently played mode
|
||||||
|
|
||||||
|
Display the track you played recently.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>💬 About</summary>
|
||||||
|
|
||||||
|
Select a music provider below for additional instructions.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Apple Music</summary>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Spotify</summary>
|
||||||
|
|
||||||
|
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.*
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
#### 👨💻 Lines
|
#### 👨💻 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.
|
The *lines* of code plugin allows you to compute the number of lines of code you added and removed across all of your repositories.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import Plugins from "./plugins/index.mjs"
|
import Plugins from "./plugins/index.mjs"
|
||||||
import Templates from "./templates/index.mjs"
|
import Templates from "./templates/index.mjs"
|
||||||
import puppeteer from "puppeteer"
|
import puppeteer from "puppeteer"
|
||||||
|
import url from "url"
|
||||||
|
|
||||||
//Setup
|
//Setup
|
||||||
export default async function metrics({login, q}, {graphql, rest, plugins, conf}) {
|
export default async function metrics({login, q}, {graphql, rest, plugins, conf}) {
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
//Template
|
//Template
|
||||||
console.debug(`metrics/compute/${login} > compute`)
|
console.debug(`metrics/compute/${login} > compute`)
|
||||||
const computer = Templates[template].default || Templates[template]
|
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)
|
await Promise.all(pending)
|
||||||
console.debug(`metrics/compute/${login} > compute > success`)
|
console.debug(`metrics/compute/${login} > compute > success`)
|
||||||
|
|
||||||
@@ -84,7 +85,9 @@
|
|||||||
|
|
||||||
/** Array shuffler */
|
/** Array shuffler */
|
||||||
function shuffle(array) {
|
function shuffle(array) {
|
||||||
for (let i = array.length-1, j = Math.floor(Math.random()*(i+1)); i > 0; i--)
|
for (let i = array.length-1; i > 0; i--) {
|
||||||
[array[i], array[j]] = [array[j], array[i]]
|
const j = Math.floor(Math.random()*(i+1))
|
||||||
|
;[array[i], array[j]] = [array[j], array[i]]
|
||||||
|
}
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,23 +113,37 @@
|
|||||||
switch (provider) {
|
switch (provider) {
|
||||||
//Spotify
|
//Spotify
|
||||||
case "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
|
//API call and parse tracklist
|
||||||
try {
|
try {
|
||||||
tracks = (await imports.axios(`https://api.spotify.com/v1/me/player/recently-played?limit=${limit}&after=${timestamp}`, {headers:{
|
//Request access token
|
||||||
"Accept":"application/json",
|
console.debug(`metrics/compute/${login}/plugins > music > requesting access token with refresh token for spotify`)
|
||||||
"Content-Type":"application/json",
|
const {data:{access_token:access}} = await imports.axios.post("https://accounts.spotify.com/api/token",
|
||||||
"Authorization":`Bearer ${token}`}
|
`${new imports.url.URLSearchParams({grant_type:"refresh_token", refresh_token, client_id, client_secret})}`,
|
||||||
})).data.items.map(({track}) => ({
|
{headers:{"Content-Type":"application/x-www-form-urlencoded"}},
|
||||||
name:track.name,
|
)
|
||||||
artist:track.artists[0].name,
|
console.log(access)
|
||||||
artwork:track.album.images[0].url,
|
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
|
//Handle errors
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.debug(error)
|
console.debug(error)
|
||||||
if ((error.response)&&(error.response.status))
|
if ((error.response)&&(error.response.status))
|
||||||
throw {status:`API call returned ${error.response.status}`}
|
throw {status:`API call returned ${error.response.status}`}
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||