Skip to content
17 changes: 17 additions & 0 deletions blueprints/nodelink/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
services:
nodelink:
image: performanc/nodelink:latest
volumes:
- ../files/config.js:/app/config.js
ports:
- "3000:3000"
restart: unless-stopped
environment:
NODELINK_SERVER_PASSWORD: "${NODELINK_SERVER_PASSWORD:-youshallnotpass}"
NODELINK_SERVER_PORT: "${NODELINK_SERVER_PORT:-3000}"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:2333/version"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Binary file added blueprints/nodelink/nodelink.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
379 changes: 379 additions & 0 deletions blueprints/nodelink/template.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,379 @@
[variables]
main_domain = "${domain}"
server_port = "3000"
nodelink_server_password = "${password}"

[config]
[[config.mounts]]
filePath = "./config.js"
content = """
export default {
server: {
host: '0.0.0.0',
port: 3000,
password: 'youshallnotpass',
useBunServer: false // set to true to use Bun.serve websocket (experimental)
},
cluster: {
enabled: true, // active cluster (or use env CLUSTER_ENABLED)
workers: 0, // 0 => uses os.cpus().length, or specify a number (1 = 2 processes total: master + 1 worker)
minWorkers: 1, // Minimum workers to keep alive (improves availability during bursts)
specializedSourceWorker: {
enabled: true, // If true, source loading (search, lyrics, etc.) is delegated to dedicated workers to prevent voice worker lag
count: 1, // Number of separate process clusters for source operations
microWorkers: 2, // Number of worker threads per process cluster
tasksPerWorker: 32, // Number of parallel tasks each micro-worker can handle before queuing
silentLogs: true // If true, micro-workers will only log warnings and errors
},
commandTimeout: 6000, // Timeout for heavy operations like loadTracks (6s)
fastCommandTimeout: 4000, // Timeout for player commands like play/pause (4s)
maxRetries: 2, // Number of retry attempts on timeout or worker failure
hibernation: {
enabled: true,
timeoutMs: 1200000
},
scaling: {
//scaling configurations
maxPlayersPerWorker: 20, // Reference capacity for utilization calculation
targetUtilization: 0.7, // Target utilization for scaling up/down
scaleUpThreshold: 0.75, // Utilization threshold to scale up
scaleDownThreshold: 0.3, // Utilization threshold to scale down
checkIntervalMs: 5000, // Interval to check for scaling needs
idleWorkerTimeoutMs: 60000, // Time in ms an idle worker should wait before being removed
queueLengthScaleUpFactor: 5, // How many commands in queue per active worker trigger scale up
lagPenaltyLimit: 60, // Event loop lag threshold (ms) to penalize worker cost
cpuPenaltyLimit: 0.85 // CPU usage threshold (85% of a core) to force scale up
},
endpoint: {
patchEnabled: true,
allowExternalPatch: false,
code: 'CAPYBARA'
}
},
logging: {
level: 'debug',
file: {
enabled: false,
path: 'logs',
rotation: 'daily',
ttlDays: 7
},
debug: {
all: false,
request: true,
session: true,
player: true,
filters: true,
sources: true,
lyrics: true,
youtube: true,
'youtube-cipher': true
}
},
connection: {
logAllChecks: false,
interval: 300000, // 5 minutes
timeout: 10000, // 10 seconds
thresholds: {
bad: 1, // Mbps
average: 5 // Mbps
}
},
maxSearchResults: 10,
maxAlbumPlaylistLength: 100,
playerUpdateInterval: 2000,
statsUpdateInterval: 30000,
trackStuckThresholdMs: 10000,
zombieThresholdMs: 60000,
enableHoloTracks: false,
enableTrackStreamEndpoint: false,
enableLoadStreamEndpoint: false,
resolveExternalLinks: false,
fetchChannelInfo: false,
filters: {
enabled: {
tremolo: true,
vibrato: true,
lowpass: true,
highpass: true,
rotation: true,
karaoke: true,
distortion: true,
channelMix: true,
equalizer: true,
chorus: true,
compressor: true,
echo: true,
phaser: true,
timescale: true
}
},
defaultSearchSource: ['youtube', 'soundcloud'],
unifiedSearchSources: ['youtube', 'soundcloud'],
sources: {
vkmusic: {
enabled: true,
userToken: '', // (optional) get from vk in browser devtools -> reqs POST /?act=web_token HTTP/2 - headers -> response -> access_token
userCookie: '' // (required without userToken) get from vk in browser devtools -> reqs POST /?act=web_token HTTP/2 - headers -> request -> cookie (copy full cookie header)
},
amazonmusic: {
enabled: true
},
mixcloud: {
enabled: true
},
audiomack: {
enabled: true
},
deezer: {
// arl: '',
// decryptionKey: '',
enabled: true
},
bandcamp: {
enabled: true
},
soundcloud: {
enabled: true,
// clientId: ""
},
local: {
enabled: true,
basePath: './local-music/'
},
http: {
enabled: true
},
vimeo: {
// Note: not 100% of the songs are currently working (but most should.), because i need to code a different extractor for every year (2010, 2011, etc. not all are done)
enabled: true,
},
telegram: {
enabled: true
},
shazam: {
enabled: true,
allowExplicit: true
},
bilibili: {
enabled: true,
sessdata: '' // Optional, improves access to some videos (premium and 4k+)
},
genius: {
enabled: true
},
pinterest: {
enabled: true
},
flowery: {
enabled: true,
voice: 'Salli',
translate: false,
silence: 0,
speed: 1.0,
enforceConfig: false
},
jiosaavn: {
enabled: true,
playlistLoadLimit: 50,
artistLoadLimit: 20
// "secretKey": "38346591" // Optional, defaults to standard key
},
gaana: {
enabled: true,
apiUrl: 'https://gaana.1lucas1apk.fun/api', // if you want to host your server https://github.com/notdeltaxd/Gaana-API
streamQuality: 'high',
playlistLoadLimit: 100,
albumLoadLimit: 100,
artistLoadLimit: 100
},
"google-tts": {
enabled: true,
language: 'en-US'
},
youtube: {
enabled: true,
allowItag: [], // additional itags for audio streams, e.g., [140, 141]
targetItag: null, // force a specific itag for audio streams, overriding the quality option
getOAuthToken: false,
hl: 'en',
gl: 'US',
clients: {
search: ['Android'], // Clients used for searching tracks
playback: ['AndroidVR', 'TV', 'TVEmbedded', 'IOS'], // Clients used for playback/streaming
resolve: ['AndroidVR', 'TV', 'TVEmbedded', 'IOS', 'Web'], // Clients used for resolving detailed track information (channel, external links, etc.)
settings: {
TV: {
refreshToken: [""] // You can use a string "token" or an array ["token1", "token2"] for rotation/fallback
}
}
},
cipher: {
url: 'https://cipher.kikkia.dev/api',
token: null
}
},
instagram: {
enabled: true
},
kwai: {
enabled: true
},
twitch: {
enabled: true
},
spotify: {
enabled: true,
clientId: '',
clientSecret: '',
externalAuthUrl: 'http://get.1lucas1apk.fun/spotify/gettoken', // URL to external token provider (e.g. http://localhost:8080/api/token - use https://github.com/topi314/spotify-tokener or https://github.com/1Lucas1apk/gettoken)
market: 'US',
playlistLoadLimit: 1, // 0 means no limit (loads all tracks), 1 = 100 tracks, 2 = 100 and so on!
playlistPageLoadConcurrency: 10, // How many pages to load simultaneously
albumLoadLimit: 1, // 0 means no limit (loads all tracks), 1 = 50 tracks, 2 = 100 tracks, etc.
albumPageLoadConcurrency: 5, // How many pages to load simultaneously
allowExplicit: true // If true plays the explicit version of the song, If false plays the Non-Explicit version of the song. Normal songs are not affected.
},
applemusic: {
enabled: true,
mediaApiToken: 'token_here', //manually | or "token_here" to get a token automatically
market: 'US',
playlistLoadLimit: 0,
albumLoadLimit: 0,
playlistPageLoadConcurrency: 5,
albumPageLoadConcurrency: 5,
allowExplicit: true
},
tidal: {
enabled: true,
token: 'token_here', //manually | or "token_here" to get a token automatically, get from tidal web player devtools; using login google account
countryCode: 'US',
playlistLoadLimit: 2, // 0 = no limit, 1 = 50 tracks, 2 = 100 tracks, etc.
playlistPageLoadConcurrency: 5 // How many pages to load simultaneously
},
pandora: {
enabled: true,
// Optional, setting this manually can help unblocking countries (since pandora is US only.). May need to be updated periodically.
// fetching manually: use a vpn connected to US, go on pandora.com, open devtools, Network tab, first request to appear and copy the 2nd csrfToken= value.
// csrfToken: '',
remoteTokenUrl: 'https://get.1lucas1apk.fun/pandora/gettoken' // URL to a remote provider that returns { success: true, authToken: "...", csrfToken: "...", expires_in_seconds: ... } //https://github.com/1Lucas1apk/gettoken
},
nicovideo: {
enabled: true
},
reddit: {
enabled: true
},
lastfm: {
enabled: true
}
},
lyrics: {
fallbackSource: 'genius',
youtube: {
enabled: true
},
genius: {
enabled: true
},
musixmatch: {
enabled: true
// signatureSecret: ''
},
lrclib: {
enabled: true
},
bilibili: {
enabled: true
},
applemusic: {
enabled: true,
advanceSearch: true // Uses YTMusic to fetch the correct title and artists instead of relying on messy YouTube video titles, improving lyrics accuracy
}
},
audio: {
quality: 'high', // high, medium, low, lowest
encryption: 'aead_aes256_gcm_rtpsize',
resamplingQuality: 'best' // best, medium, fastest, zero order holder, linear
},
voiceReceive: {
enabled: false,
format: 'opus' // pcm_s16le, opus
},
routePlanner: {
strategy: 'RotateOnBan', // RotateOnBan, RoundRobin, LoadBalance
bannedIpCooldown: 600000, // 10 minutes
ipBlocks: []
},
rateLimit: {
enabled: true,
global: {
maxRequests: 1000,
timeWindowMs: 60000 // 1 minute
},
perIp: {
maxRequests: 100,
timeWindowMs: 10000 // 10 seconds
},
perUserId: {
maxRequests: 50,
timeWindowMs: 5000 // 5 seconds
},
perGuildId: {
maxRequests: 20,
timeWindowMs: 5000 // 5 seconds
},
ignorePaths: [],
ignore: {
userIds: [],
guildIds: [],
ips: []
}
},
dosProtection: {
enabled: true,
thresholds: {
burstRequests: 50,
timeWindowMs: 10000 // 10 seconds
},
mitigation: {
delayMs: 500,
blockDurationMs: 300000 // 5 minutes
},
ignore: {
userIds: [],
guildIds: [],
ips: []
}
},
metrics: {
enabled: true,
authorization: {
type: 'Bearer', // Bearer or Basic.
password: '' // If empty, uses server.password
}
},
mix: {
enabled: true,
defaultVolume: 0.8,
maxLayersMix: 5,
autoCleanup: true
},
plugins: [
/* {
name: 'nodelink-sample-plugin',
source: 'local'
} */
],
pluginConfig: {}
}
"""

[[config.domains]]
serviceName = "nodelink"
port = 3_000
domain = "${main_domain}"

[config.env]
NODELINK_SERVER_PASSWORD = "${nodelink_server_password}"
NODELINK_SERVER_PORT = "${server_port}"
Loading
Loading