Use a docker image to support puppeteer (#7)
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Based on https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-in-docker
|
||||||
|
|
||||||
|
# Base image
|
||||||
|
FROM node:15-slim
|
||||||
|
|
||||||
|
# Install latest chrome dev package and fonts to support major charsets
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y wget gnupg ca-certificates libgconf-2-4 \
|
||||||
|
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||||
|
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
|
||||||
|
--no-install-recommends \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Uncomment to skip the chromium download when installing puppeteer
|
||||||
|
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
|
||||||
|
ENV PUPPETEER_BROWSER_PATH "google-chrome-stable"
|
||||||
|
|
||||||
|
# Copy action
|
||||||
|
COPY action/dist/index.js /index.js
|
||||||
|
RUN chmod +x /index.js
|
||||||
|
|
||||||
|
# Execute action
|
||||||
|
ENTRYPOINT node /index.js
|
||||||
11
action.yml
11
action.yml
@@ -6,8 +6,8 @@ branding:
|
|||||||
icon: user-check
|
icon: user-check
|
||||||
color: gray-dark
|
color: gray-dark
|
||||||
runs:
|
runs:
|
||||||
using: node12
|
using: docker
|
||||||
main: action/dist/index.js
|
image: Dockerfile
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
inputs:
|
inputs:
|
||||||
@@ -125,7 +125,8 @@ inputs:
|
|||||||
default: no
|
default: no
|
||||||
|
|
||||||
# Music provider
|
# Music provider
|
||||||
# This is required when you enable the music plugin
|
# This is required in "recent" mode
|
||||||
|
# This is optional in "playlist" mode, in this case it will be deduced from "plugin_music_playlist" url
|
||||||
# Supported values are :
|
# Supported values are :
|
||||||
# - "apple" for Apple Music
|
# - "apple" for Apple Music
|
||||||
# - "spotify" for Spotify
|
# - "spotify" for Spotify
|
||||||
@@ -147,11 +148,11 @@ inputs:
|
|||||||
# - "recent" : display recently played tracks
|
# - "recent" : display recently played tracks
|
||||||
plugin_music_mode:
|
plugin_music_mode:
|
||||||
description: Use "recent" to display recently played music and "playlist" to display tracks randomly from a given playlist (*required if music plugin is enabled)
|
description: Use "recent" to display recently played music and "playlist" to display tracks randomly from a given playlist (*required if music plugin is enabled)
|
||||||
default: ""
|
default: "recent"
|
||||||
|
|
||||||
# Music playlist
|
# Music playlist
|
||||||
# Required when using "plugin_music_mode" as "playlist"
|
|
||||||
# The embed playlist url (the one used for music player iframe)
|
# The embed playlist url (the one used for music player iframe)
|
||||||
|
# Will default mode to "playlist" when set
|
||||||
plugin_music_playlist:
|
plugin_music_playlist:
|
||||||
description: Embed playlist url
|
description: Embed playlist url
|
||||||
default: ""
|
default: ""
|
||||||
|
|||||||
94473
action/dist/index.js
vendored
94473
action/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -35,7 +35,11 @@
|
|||||||
"base.repositories":"Repositories metrics",
|
"base.repositories":"Repositories metrics",
|
||||||
"base.metadata":"Metadata",
|
"base.metadata":"Metadata",
|
||||||
},
|
},
|
||||||
options:{},
|
options:{
|
||||||
|
"habits.from":100,
|
||||||
|
"music.playlist":"",
|
||||||
|
"music.mode":"playlist",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
templates:{
|
templates:{
|
||||||
list:templates,
|
list:templates,
|
||||||
@@ -66,6 +70,10 @@
|
|||||||
.flatMap(([key, value]) => key === "base" ? Object.entries(value).map(([key, value]) => [`base.${key}`, value]) : [[key, value]])
|
.flatMap(([key, value]) => key === "base" ? Object.entries(value).map(([key, value]) => [`base.${key}`, value]) : [[key, value]])
|
||||||
.filter(([key, value]) => /^base[.]\w+$/.test(key) ? !value : value)
|
.filter(([key, value]) => /^base[.]\w+$/.test(key) ? !value : value)
|
||||||
.map(([key, value]) => `${key}=${+value}`)
|
.map(([key, value]) => `${key}=${+value}`)
|
||||||
|
//Plugins options
|
||||||
|
const options = Object.entries(this.plugins.options)
|
||||||
|
.filter(([key, value]) => `${value}`.length)
|
||||||
|
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
||||||
//Template
|
//Template
|
||||||
const template = (this.templates.selected !== templates[0]) ? [`template=${this.templates.selected}`] : []
|
const template = (this.templates.selected !== templates[0]) ? [`template=${this.templates.selected}`] : []
|
||||||
//Generated url
|
//Generated url
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<section class="steps">
|
<section class="steps">
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<h2>1. Enter your GitHub username</h2>
|
<h2>1. Enter your GitHub username</h2>
|
||||||
<input type="text" v-model="user" maxlength="39" placeholder="GitHub username" :disabled="generated.pending">
|
<input type="text" name="user" v-model="user" maxlength="39" placeholder="GitHub username" :disabled="generated.pending">
|
||||||
</div>
|
</div>
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<h2>2. Select a template</h2>
|
<h2>2. Select a template</h2>
|
||||||
@@ -48,18 +48,20 @@
|
|||||||
{{ plugins.descriptions[plugin] || plugin }}
|
{{ plugins.descriptions[plugin] || plugin }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="(plugins.enabled.habits)||(plugins.enabled.music)">
|
||||||
|
<h3>2.3 Configure additional plugins</h3>
|
||||||
|
<div class="options">
|
||||||
|
<label v-if="(plugins.enabled.music)&&(plugins.options['music.mode'] === 'playlist')">
|
||||||
|
Playlist embed link
|
||||||
|
<input type="text" v-model="plugins.options['music.playlist']" placeholder="https://embed.music.apple.com/en/playlist/">
|
||||||
|
</label>
|
||||||
|
<label v-if="plugins.enabled.habits">
|
||||||
|
Number of events for habits
|
||||||
|
<input type="number" v-model="plugins.options['habits.from']" min="1" max="100">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<div class="palette">
|
|
||||||
Generated metrics use transparency and colors which can be read on both light and dark modes, so everyone can see your stats whatever their preferred color scheme !
|
|
||||||
<div class="palettes">
|
|
||||||
<label>
|
|
||||||
<input type="radio" v-model="palette" value="light"> ☀️ Light mode
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="radio" v-model="palette" value="dark"> 🌙 Night mode
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<h2>3. Generate your metrics</h2>
|
<h2>3. Generate your metrics</h2>
|
||||||
@@ -78,6 +80,17 @@
|
|||||||
<template v-if="user">
|
<template v-if="user">
|
||||||
<button @click="generate" :disabled="generated.pending">{{ generated.pending ? "Working on it :)" : "Generate your metrics !" }}</button>
|
<button @click="generate" :disabled="generated.pending">{{ generated.pending ? "Working on it :)" : "Generate your metrics !" }}</button>
|
||||||
</template>
|
</template>
|
||||||
|
<div class="palette">
|
||||||
|
Generated metrics use transparency and colors which can be read on both light and dark modes, so everyone can see your stats whatever their preferred color scheme !
|
||||||
|
<div class="palettes">
|
||||||
|
<label>
|
||||||
|
<input type="radio" v-model="palette" value="light"> ☀️ Light mode
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" v-model="palette" value="dark"> 🌙 Night mode
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<h2>4. Embed these metrics on your GitHub profile</h2>
|
<h2>4. Embed these metrics on your GitHub profile</h2>
|
||||||
|
|||||||
@@ -56,9 +56,11 @@
|
|||||||
input:focus {
|
input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
input[name=user] {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
input[type=text], select, button {
|
input[type=text], select, button {
|
||||||
min-width: 50%;
|
min-width: 50%;
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
}
|
||||||
option {
|
option {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -133,6 +135,10 @@
|
|||||||
.plugins label, .palettes label {
|
.plugins label, .palettes label {
|
||||||
margin: 0 1rem;
|
margin: 0 1rem;
|
||||||
}
|
}
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
/* Code snippets */
|
/* Code snippets */
|
||||||
.code {
|
.code {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//Setup
|
//Setup
|
||||||
export default function ({login, imports, rest, computed, pending, q}, {enabled = false, from:_from = 100} = {}) {
|
export default function ({login, imports, rest, computed, pending, q}, {enabled = false, from:defaults = 100} = {}) {
|
||||||
//Check if plugin is enabled and requirements are met
|
//Check if plugin is enabled and requirements are met
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return computed.plugins.habits = null
|
return computed.plugins.habits = null
|
||||||
@@ -8,9 +8,11 @@
|
|||||||
console.debug(`metrics/compute/${login}/plugins > habits`)
|
console.debug(`metrics/compute/${login}/plugins > habits`)
|
||||||
|
|
||||||
//Parameters override
|
//Parameters override
|
||||||
|
let {"habits.from":from = defaults.from ?? 100} = q
|
||||||
//Events
|
//Events
|
||||||
const from = Math.max(1, Math.min(100, "habits.from" in q ? Number(q["habits.from"])||0 : _from))
|
from = Math.max(1, Math.min(100, Number(from)))
|
||||||
console.debug(`metrics/compute/${login}/plugins > habits > events = ${from}`)
|
//Debug
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > habits > ${JSON.stringify({from})}`)
|
||||||
|
|
||||||
//Plugin execution
|
//Plugin execution
|
||||||
pending.push(new Promise(async solve => {
|
pending.push(new Promise(async solve => {
|
||||||
@@ -27,7 +29,7 @@
|
|||||||
//Compute commit hours
|
//Compute commit hours
|
||||||
const hours = commits.map(({created_at}) => (new Date(created_at)).getHours())
|
const hours = commits.map(({created_at}) => (new Date(created_at)).getHours())
|
||||||
for (const hour of hours)
|
for (const hour of hours)
|
||||||
habits.commits.hours[hour] = (habits.commits.hours[hour] || 0) + 1
|
habits.commits.hours[hour] = (habits.commits.hours[hour] ?? 0) + 1
|
||||||
//Compute hour with most commits
|
//Compute hour with most commits
|
||||||
habits.commits.hour = hours.length ? Object.entries(habits.commits.hours).sort(([an, a], [bn, b]) => b - a).map(([hour, occurence]) => hour)[0] : NaN
|
habits.commits.hour = hours.length ? Object.entries(habits.commits.hours).sort(([an, a], [bn, b]) => b - a).map(([hour, occurence]) => hour)[0] : NaN
|
||||||
}
|
}
|
||||||
@@ -42,7 +44,7 @@
|
|||||||
edited
|
edited
|
||||||
.filter(({status}) => status === "fulfilled")
|
.filter(({status}) => status === "fulfilled")
|
||||||
.map(({value}) => value)
|
.map(({value}) => value)
|
||||||
.flatMap(files => files.flatMap(file => (file.patch||"").match(/(?<=^[+])((?:\t)|(?: )) /gm)||[]))
|
.flatMap(files => files.flatMap(file => (file.patch ?? "").match(/(?<=^[+])((?:\t)|(?: )) /gm) ?? []))
|
||||||
.forEach(indent => habits.indents[/^\t/.test(indent) ? "tabs" : "spaces"]++)
|
.forEach(indent => habits.indents[/^\t/.test(indent) ? "tabs" : "spaces"]++)
|
||||||
//Compute indent style
|
//Compute indent style
|
||||||
habits.indents.style = habits.indents.spaces > habits.indents.tabs ? "spaces" : habits.indents.tabs > habits.indents.spaces ? "tabs" : ""
|
habits.indents.style = habits.indents.spaces > habits.indents.tabs ? "spaces" : habits.indents.tabs > habits.indents.spaces ? "tabs" : ""
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
//Supported providers
|
//Supported providers
|
||||||
const providers = {
|
const providers = {
|
||||||
apple:"Apple Music",
|
apple:{
|
||||||
spotify:"Spotify",
|
name:"Apple Music",
|
||||||
|
embed:/^https:..embed.music.apple.com.\w+.playlist/,
|
||||||
|
},
|
||||||
|
spotify:{
|
||||||
|
name:"Spotify",
|
||||||
|
embed:/^https:..open.spotify.com.embed.playlist/,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
//Supported modes
|
//Supported modes
|
||||||
@@ -18,32 +24,39 @@
|
|||||||
if (!q.music)
|
if (!q.music)
|
||||||
return computed.plugins.music = null
|
return computed.plugins.music = null
|
||||||
console.debug(`metrics/compute/${login}/plugins > music`)
|
console.debug(`metrics/compute/${login}/plugins > music`)
|
||||||
|
const raw = {
|
||||||
|
get provider() { return providers[provider]?.name ?? "" },
|
||||||
|
get mode() { return modes[mode] ?? "Unconfigured music plugin"},
|
||||||
|
}
|
||||||
|
|
||||||
//Parameters override and checks
|
//Parameters override and checks
|
||||||
|
let {"music.provider":provider = null, "music.mode":mode = null, "music.playlist":playlist = null, "music.limit":limit = 4} = q
|
||||||
|
//Auto-guess parameters
|
||||||
|
if ((playlist)&&(mode === null))
|
||||||
|
mode = "playlist"
|
||||||
|
if ((playlist)&&(provider === null))
|
||||||
|
for (const [name, {embed}] of Object.entries(providers))
|
||||||
|
if (embed.test(playlist))
|
||||||
|
provider = name
|
||||||
|
if (!mode)
|
||||||
|
mode = "recent"
|
||||||
//Provider
|
//Provider
|
||||||
const provider = q["music.provider"]||""
|
|
||||||
if (!(provider in providers))
|
if (!(provider in providers))
|
||||||
return computed.plugins.music = {error:provider ? `Unsupported provider "${provider}"` : `Missing provider`, mode:"Unconfigured music plugin"}
|
return computed.plugins.music = {...raw, error:provider ? `Unsupported provider "${provider}"` : `Missing provider`}
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > provider "${provider}"`)
|
|
||||||
//Mode
|
//Mode
|
||||||
const mode = q["music.mode"]||""
|
|
||||||
if (!(mode in modes))
|
if (!(mode in modes))
|
||||||
return computed.plugins.music = {error:mode ? `Unsupported mode "${mode}"` : `Missing mode`, provider:providers[provider], mode:"Unconfigured music plugin"}
|
return computed.plugins.music = {...raw, error:`Unsupported mode "${mode}"`}
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > mode "${mode}"`)
|
|
||||||
//Playlist mode
|
//Playlist mode
|
||||||
const playlist = q["music.playlist"]||""
|
|
||||||
if (mode === "playlist") {
|
if (mode === "playlist") {
|
||||||
if (!playlist)
|
if (!playlist)
|
||||||
return computed.plugins.music = {error:`Missing playlist url`, provider:providers[provider], mode:modes[mode]}
|
return computed.plugins.music = {...raw, error:`Missing playlist url`}
|
||||||
if ((provider === "spotify")&&(!/^https:..open.spotify.com.embed.playlist/.test(playlist)))
|
if (!providers[provider].embed.test(playlist))
|
||||||
return computed.plugins.music = {error:`Unsupported playlist url format`, provider:providers[provider], mode:modes[mode]}
|
return computed.plugins.music = {...raw, error:`Unsupported playlist url format`}
|
||||||
if ((provider === "apple")&&(!/^https:..embed.music.apple.com.\w+.playlist/.test(playlist)))
|
|
||||||
return computed.plugins.music = {error:`Unsupported playlist url format`, provider:providers[provider], mode:modes[mode]}
|
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > playlist = ${playlist}`)
|
|
||||||
}
|
}
|
||||||
//Limit
|
//Limit
|
||||||
const limit = Math.max(1, Math.min(100, "music.limit" in q ? Number(q["music.limit"])||0 : 4))
|
limit = Math.max(1, Math.min(100, Number(limit)))
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > limit = ${limit}`)
|
//Debug
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > habits > ${JSON.stringify({provider, mode, playlist, limit})}`)
|
||||||
|
|
||||||
//Plugin execution
|
//Plugin execution
|
||||||
pending.push(new Promise(async solve => {
|
pending.push(new Promise(async solve => {
|
||||||
@@ -57,7 +70,8 @@
|
|||||||
case "playlist":{
|
case "playlist":{
|
||||||
//Start puppeteer and navigate to playlist
|
//Start puppeteer and navigate to playlist
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > starting browser`)
|
console.debug(`metrics/compute/${login}/plugins > music > starting browser`)
|
||||||
const browser = await imports.puppeteer.launch()
|
const browser = await imports.puppeteer.launch({headless:true, executablePath:process.env.PUPPETEER_BROWSER_PATH, args:["--no-sandbox", "--disable-extensions", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]})
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > music > loaded ${await browser.version()}`)
|
||||||
const page = await browser.newPage()
|
const page = await browser.newPage()
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > loading page`)
|
console.debug(`metrics/compute/${login}/plugins > music > loading page`)
|
||||||
await page.goto(playlist)
|
await page.goto(playlist)
|
||||||
@@ -174,7 +188,7 @@
|
|||||||
}
|
}
|
||||||
//Save results
|
//Save results
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > success`)
|
console.debug(`metrics/compute/${login}/plugins > music > success`)
|
||||||
computed.plugins.music = {provider:providers[provider], mode:modes[mode], tracks}
|
computed.plugins.music = {...raw, tracks}
|
||||||
solve()
|
solve()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -184,12 +198,12 @@
|
|||||||
catch (error) {
|
catch (error) {
|
||||||
//Plugin error
|
//Plugin error
|
||||||
if (error.status) {
|
if (error.status) {
|
||||||
computed.plugins.music = {provider:providers[provider], mode:modes[mode], error:error.status}
|
computed.plugins.music = {...raw, error:error.status}
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > error > ${error.status}`)
|
console.debug(`metrics/compute/${login}/plugins > music > error > ${error.status}`)
|
||||||
return solve()
|
return solve()
|
||||||
}
|
}
|
||||||
//Generic error
|
//Generic error
|
||||||
computed.plugins.music = {provider:providers[provider], mode:modes[mode], error:`An error occured`}
|
computed.plugins.music = {...raw, error:`An error occured`}
|
||||||
console.debug(`metrics/compute/${login}/plugins > music > error`)
|
console.debug(`metrics/compute/${login}/plugins > music > error`)
|
||||||
console.debug(error)
|
console.debug(error)
|
||||||
solve()
|
solve()
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
+ (!!computed.plugins.habits)*68
|
+ (!!computed.plugins.habits)*68
|
||||||
+ (!!computed.plugins.languages)*96
|
+ (!!computed.plugins.languages)*96
|
||||||
+ (!!computed.plugins.music)*64 + (computed.plugins.music ? computed.plugins.music.tracks ? 14+Math.max(0, computed.plugins.music.tracks.length-1)*36 : 0 : 0)
|
+ (!!computed.plugins.music)*64 + (computed.plugins.music ? computed.plugins.music.tracks ? 14+Math.max(0, computed.plugins.music.tracks.length-1)*36 : 0 : 0)
|
||||||
|
+ Math.max(0, (((!!base.metadata)+(!!base.header)+((!!base.activity)||(!!base.community))+(!!base.repositories)+((!!computed.plugins.habits))+(!!computed.plugins.pagespeed)+(!!computed.plugins.languages)+(!!computed.plugins.music))-1))*4
|
||||||
%>">
|
%>">
|
||||||
|
|
||||||
<defs><style><%= fonts %></style></defs>
|
<defs><style><%= fonts %></style></defs>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@@ -39,7 +39,12 @@
|
|||||||
{traffic:1},
|
{traffic:1},
|
||||||
{selfskip:1},
|
{selfskip:1},
|
||||||
{pagespeed:1},
|
{pagespeed:1},
|
||||||
{followup:1, languages:1, habits:1, "habits.events":1, lines:1, traffic:1, selfskip:1, pagespeed:1},
|
{music:1},
|
||||||
|
{music:1, "music.mode":"recent"},
|
||||||
|
{music:1, "music.mode":"recent", "music.provider":"apple"},
|
||||||
|
{music:1, "music.mode":"recent", "music.provider":"spotify"},
|
||||||
|
{music:1, "music.mode":"playlist"},
|
||||||
|
{followup:1, languages:1, habits:1, "habits.events":1, lines:1, traffic:1, selfskip:1, pagespeed:1, music:1},
|
||||||
]) {
|
]) {
|
||||||
await test.metrics({graphql, rest, q:{template, repositories:1, ...q}})
|
await test.metrics({graphql, rest, q:{template, repositories:1, ...q}})
|
||||||
}
|
}
|
||||||
@@ -58,6 +63,7 @@
|
|||||||
selfskip:{enabled:true},
|
selfskip:{enabled:true},
|
||||||
languages:{enabled:true},
|
languages:{enabled:true},
|
||||||
followup:{enabled:true},
|
followup:{enabled:true},
|
||||||
|
musit:{enabled:true},
|
||||||
}
|
}
|
||||||
|
|
||||||
//Compute render
|
//Compute render
|
||||||
|
|||||||
Reference in New Issue
Block a user