[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
0

Configure Feed

Select the types of activity you want to include in your feed.

docs: Add discord local/headless docs

FoxxMD (Feb 23, 2026, 10:31 PM UTC) b587d834 9b63e022

+295 -4
+11
config/discord-local.json.example
··· 1 + [ 2 + { 3 + "name": "MS", 4 + "enable": true, 5 + "data": { 6 + "applicationId": "8190211179716453570", 7 + "ipcLocations": ["192.168.0.105:6655"] 8 + } 9 + } 10 + ] 11 +
+3 -2
docsite/docs/configuration/clients/discord.mdx docsite/docs/configuration/clients/discord/discord-headless.mdx
··· 1 1 --- 2 - title: Discord 2 + title: Discord (Headless) 3 3 toc_min_heading_level: 2 4 4 toc_max_heading_level: 5 5 5 --- 6 6 7 7 import Tabs from '@theme/Tabs'; 8 8 import TabItem from '@theme/TabItem'; 9 - import CodeBlock from '@theme/CodeBlock'; 10 9 import JsonConfig from '!!raw-loader!@site/../config/discord.json.example'; 11 10 12 11 This scrobbler uses **[Now Playing](/configuration/clients#now-playing)** functionality to set your Discord [Rich Presence](https://docs.discord.com/developers/rich-presence/overview) to the music you are currently monitoring with multi-scrobbler. 13 12 14 13 <img src={require('/img/discord_presence_art.png').default} height="127"/> 14 + 15 + This integration is **headless**, it does not depend on any specific Discord application to be running. 15 16 16 17 :::warning[Discord TOS Violation] 17 18
+234
docsite/docs/configuration/clients/discord/discord-local.mdx
··· 1 + --- 2 + title: Discord (Local) 3 + toc_min_heading_level: 2 4 + toc_max_heading_level: 5 5 + --- 6 + 7 + import Tabs from '@theme/Tabs'; 8 + import TabItem from '@theme/TabItem'; 9 + import JsonConfig from '!!raw-loader!@site/../config/discord-local.json.example'; 10 + 11 + This scrobbler uses **[Now Playing](/configuration/clients#now-playing)** functionality to set your Discord [Rich Presence](https://docs.discord.com/developers/rich-presence/overview) to the music you are currently monitoring with multi-scrobbler. 12 + 13 + <img src={require('/img/discord_presence_art.png').default} height="127"/> 14 + 15 + This integration uses an **existing Discord application** to set your Rich Presence. 16 + 17 + ## Required Setup 18 + 19 + ### Discord Application 20 + 21 + You must create a [Discord App](https://docs.discord.com/developers/quick-start/overview-of-apps) in order to use this integration. Creating an App is free and simple. 22 + 23 + Go to the [Discord Developer Portal](https://discord.com/developers/applications) 24 + 25 + * Click on "New Application" and give it a name 26 + * Copy the **Application Id** (Client Id) shown after creation 27 + * Add this Id to your Multi-Scrobbler config 28 + * Env Config => `DISCORD_APPLICATION_ID=12345678` 29 + * File Config => `"applicationId": "12345678"` 30 + 31 + ### Discord Connection 32 + 33 + This integration uses an existing, running Discord application to communicate with Discord and set Rich Presence. Multi-scrobbler must be able to communicate with the Discord application, either on the same machine or over a network. 34 + 35 + <Tabs groupId="hostType" queryString> 36 + 37 + <TabItem value="local" label="Local"> 38 + 39 + In this scenario you have Multi-Scrobbler running on the same machine as Discord. 40 + 41 + <details> 42 + 43 + <summary>Local (Non-Docker) Installation</summary> 44 + 45 + If MS was [installed locally](/installation#local-installation) (not Docker) then MS should automatically detect Discord. This will work for **any OS**. 46 + 47 + </details> 48 + 49 + 50 + <details> 51 + 52 + <summary>Docker Installation</summary> 53 + 54 + :::note 55 + 56 + This works for **Linux/MacOS only.** 57 + 58 + ::: 59 + 60 + If MS is in a docker container you need to find the location of **unix socket** for discord. This is normally named `discord-ipc-0`. 61 + 62 + With Discord running, run this command: 63 + 64 + ``` 65 + $ sudo netstat -apeen | grep discord 66 + 67 + unix 2 [ ACC ] STREAM LISTENING 11026149 1641787/Discord /home/foxx/.var/app/com.discordapp.Discord/cache/scoped_dirnnxNIn/SingletonSocket 68 + unix 2 [ ACC ] STREAM LISTENING 11036791 1642030/exe /run/user/1000/discord-ipc-0 69 + ``` 70 + 71 + We see that the path we need is 72 + 73 + ``` 74 + /run/user/1000/discord-ipc-0 75 + ``` 76 + 77 + Pass this file into your docker container as a volume and add this path to `DISCORD_IPC_LOCATIONS` `environment:` 78 + 79 + ```yaml title="docker-compose.yaml" 80 + 81 + environment: 82 + # ... 83 + DISCORD_IPC_LOCATIONS=/run/user/1000/discord-ipc-0 84 + volumes: 85 + - /run/user/1000/discord-ipc-0:/run/user/1000/discord-ipc-0 86 + ``` 87 + 88 + </details> 89 + 90 + </TabItem> 91 + <TabItem value="remove" label="Remote"> 92 + 93 + :::note 94 + 95 + This works for **Linux/MacOS only.** 96 + 97 + ::: 98 + 99 + 100 + :::warning 101 + 102 + This method is **insecure.** You should not expose this TCP port over a network unless access to the port is restricted and the network is trusted. 103 + 104 + ::: 105 + 106 + On the machine Discord is running on you need to find the location of **unix socket** for discord. This is normally named `discord-ipc-0`. 107 + 108 + With Discord running, run this command: 109 + 110 + ``` 111 + $ sudo netstat -apeen | grep discord 112 + 113 + unix 2 [ ACC ] STREAM LISTENING 11026149 1641787/Discord /home/foxx/.var/app/com.discordapp.Discord/cache/scoped_dirnnxNIn/SingletonSocket 114 + unix 2 [ ACC ] STREAM LISTENING 11036791 1642030/exe /run/user/1000/discord-ipc-0 115 + ``` 116 + 117 + We see that the path we need is 118 + 119 + ``` 120 + /run/user/1000/discord-ipc-0 121 + ``` 122 + 123 + Use [`socat`](https://linux.die.net/man/1/socat) to bidirectionally relay communication from Discord's unix socket to a listening TCP port. Likely, you will want to setup a service to start this command on login. 124 + 125 + ```shell 126 + $ socat -v TCP-LISTEN:6655,reuseaddr,fork UNIX-CONNECT:/run/user/1000/discord-ipc-0 127 + ``` 128 + 129 + Finally, in your MS docker compose file add the `host:port` of the host running Discord to `DISCORD_IPC_LOCATIONS` `environment:` 130 + 131 + ```yaml title="docker-compose.yaml" 132 + 133 + environment: 134 + # ... 135 + DISCORD_IPC_LOCATIONS=192.168.DISCORD_HOST.IP:6655 136 + ``` 137 + 138 + </TabItem> 139 + </Tabs> 140 + 141 + ## Optional Setup 142 + 143 + :::note 144 + 145 + Due to the limitations of the Discord local API the activity detection and online status customizations found in [Headless](/configuration/discord/discord-headless#optional-setup) are not available for the Local integration. 146 + 147 + ::: 148 + 149 + ### Artwork 150 + 151 + Multi-scrobbler can display Album Art in your Discord status. The image that is used differs based on if the image URL is publically accessible. 152 + 153 + The order in which multi-scrobbler determines what image to use is the same as the order of the tabs shown below. 154 + 155 + #### Artwork URL Types 156 + 157 + <Tabs groupId="artworkUrl" queryString> 158 + <TabItem label="External URLs" value="external"> 159 + MS can use external URLs for artwork. 160 + 161 + <img src={require('/img/discord_presence_art.png').default} height="127"/> 162 + 163 + External URLs that are not from known music services are **disabled by default.** This is to preserve *your* privacy because Discord does not host images, it only links to them and the URL is visible to other users. 164 + 165 + <details> 166 + 167 + <summary>Why Should I Care?</summary> 168 + 169 + There are some scenarios where you may not want your artwork URLs to be visible, for example: 170 + 171 + * A Source may be accessible only on your LAN (Jellyfin at `http://192.168.0.101`) 172 + * A Source may be internet-facing but you do not want to expose this information to discord/other users 173 + * A Source may be internet-facing but requires authentication to view the image 174 + </details> 175 + 176 + MS will use your artwork URLs only if: 177 + 178 + * artwork url is from a known music service/[Cover Art Archive](./?artworkUrl=caa#artwork-url-types) or 179 + * artwork url starts with `https` and 180 + * `artwork` (`DISCORD_ARTWORK`) is 181 + * `true` => always uses the url 182 + * a list of custom domains keywords an external artwork url should contain EX 183 + * ENV => `DISCORD_ARTWORK=mycdn,jellyfin` 184 + * File Config => `"artwork": ["mycdn","jellyfin"]` 185 + 186 + </TabItem> 187 + <TabItem label="Musicbrainz/Cover Art Achive" value="caa"> 188 + MS can try to use [Cover Art Archive](http://coverartarchive.org/) to get album art. The URL it retrieves is public which means it acts like an External URL that is always available for you. 189 + 190 + <img src={require('/img/discord_presence_art.png').default} height="127"/> 191 + 192 + MS will try to use Cover Art Archive if: 193 + 194 + * The scrobble data does not contain an album art url (no art in dashboard) or the existing url has failed an [External URL](./?artworkUrl=external#artwork-url-types) condition 195 + * and **the scrobble data contains a [Musicbrainz](/configuration/transforms/musicbrainz) Release (Album) MBID** 196 + 197 + Basically, if MS would normally use the [MS Default](./?artworkUrl=ms-default#artwork-url-types) art, it will first try to use Cover Art Archive if the scrobble has a Release MBID. 198 + 199 + Some Source, like [Jellyfin](/configuration/sources/jellyfin) and [Plex](/configuration/sources/plex), can automatically provide this ID if the music is "matched" in your library. 200 + 201 + For all others, you can [**configure MS to use the Musicbrainz Stage**](/configuration/transforms/musicbrainz#use-metadata-for-discord-album-art) for Discord to try to get this MBID. 202 + </TabItem> 203 + <TabItem label="MS Default" value="ms-default"> 204 + When these conditions are true: 205 + 206 + * Cannot use an [External URL](./?artworkUrl=external#artwork-url-types) and 207 + * Cannot get album art from [Cover Art Archive](./?artworkUrl=caa#artwork-url-types) and 208 + * Play does not contain artwork information (no artwork shown in dashboard) 209 + 210 + The status artwork will use a default URL instead of your artwork URL. 211 + 212 + Without any other configuration, the default URL is an icon from the [multi-scrobbler repository](https://github.com/FoxxMD/multi-scrobbler/blob/master/assets/default-artwork.png): 213 + 214 + <img src={require('/img/discord_presence_ms_default.png').default} height="127"/> 215 + 216 + This default image can be customized: 217 + 218 + * ENV Config => `DISCORD_ARTWORK_DEFAULT_URL=https://cooldomain.com/art.png` 219 + * File Config => `"artworkDefaultUrl": "https://cooldomain.com/art.png"` 220 + 221 + Remember, this URL need to be accessible on the public internet. 222 + </TabItem> 223 + </Tabs> 224 + 225 + ## Configuration 226 + 227 + <Config config="DiscordClientConfig" fileContent={JsonConfig} client name="discord"> 228 + | Environmental Variable | Required? | Default | Description | 229 + | :---------------------------- | --------- | ------- | :--------------------------------------------------------------------------------------------------------------- | 230 + | `DISCORD_APPLICATION_ID` | Yes | | Application ID | 231 + | `DISCORD_IPC_LOCATIONS` | No | | A commera-separated list of `host:port` network locations or file paths to unix socket for Discord | 232 + | `DISCORD_ARTWORK` | No | | A boolean indicating if external artwork URLs should be used. Or a comma-separated list of allowed domains | 233 + | `DISCORD_ARTWORK_DEFAULT_URL` | No | | A URL of an image to use as a fallback if the album art URL cannot be used. Or `false` to use discord's default. | 234 + </Config>
+35
docsite/docs/configuration/clients/discord/discord.mdx
··· 1 + --- 2 + sidebar_position: 2 3 + title: Discord 4 + toc_max_heading_level: 4 5 + --- 6 + 7 + This scrobbler uses **[Now Playing](/configuration/clients#now-playing)** functionality to set your Discord [Rich Presence](https://docs.discord.com/developers/rich-presence/overview) to the music you are currently monitoring with multi-scrobbler. 8 + 9 + <img src={require('/img/discord_presence_art.png').default} height="127"/> 10 + 11 + There are two types of Discord integrations to choose from: 12 + 13 + ## Local 14 + 15 + The **Local** integration uses an **existing** Discord application on Linux/MacOS to set Rich Presence. 16 + 17 + * Uses "permitted" access to Discord, no TOS issues 18 + * More restricted customization compares to Headless 19 + * Only active when your existing Discord application is open 20 + * For instances where Multi-Scrobbler is not on the same machine as Discord 21 + * Requires an additional program to relay the Discord connection 22 + * This is potentially insecure, or less secure, then Headless 23 + 24 + [**Local Docs**](#/configuration/client/discord/discord-local) 25 + 26 + ## Headless 27 + 28 + The **Headless** integration uses Discord cloud Gateway/API, directly, to set Rich Presence. 29 + 30 + * Does not require an existing Discord application (uses Discord cloud API) 31 + * Using this **Violates Discord TOS** because it requires a user token 32 + * More customization than Local 33 + * Active if **any** Discord application is online 34 + 35 + [**Headless Docs**](#/configuration/client/discord/discord-headless)
+2 -1
src/backend/common/infrastructure/config/client/discord.ts
··· 8 8 statusOverrideAllow?: string | StatusType[] 9 9 activitiesOverrideAllow?: boolean | string | ActivityType[] 10 10 applicationsOverrideDisallow?: string | string[] 11 - ipcLocations?: (string | [number, string])[] 11 + ipcLocations?: string | (string | [number, string])[] 12 12 } 13 13 14 14 export interface DiscordClientData extends DiscordData, CommonClientData {} ··· 37 37 statusOverrideAllow?: StatusType[] 38 38 activitiesOverrideAllow?: ActivityType[] 39 39 applicationsOverrideDisallow?: string[] 40 + ipcLocations?: (string | [number, string])[] 40 41 } 41 42 42 43 export interface DiscordWSData extends DiscordStrongData {
+9 -1
src/backend/common/vendor/discord/DiscordWSClient.ts
··· 900 900 applicationId, 901 901 applicationsOverrideDisallow: parseArrayFromMaybeString(applicationsOverrideDisallow), 902 902 artworkDefaultUrl, 903 - ipcLocations 904 903 } 905 904 906 905 if (typeof artwork === 'boolean' || Array.isArray(artwork)) { ··· 923 922 strongConfig.activitiesOverrideAllow = aaRaw.map(activityStringToType); 924 923 } 925 924 925 + if(ipcLocations !== undefined) { 926 + if(typeof ipcLocations === 'string') { 927 + const ipcRaw = parseArrayFromMaybeString(ipcLocations); 928 + strongConfig.ipcLocations = ipcRaw; 929 + } else { 930 + strongConfig.ipcLocations = ipcLocations; 931 + } 932 + } 933 + 926 934 return strongConfig; 927 935 } 928 936
+1
src/backend/scrobblers/ScrobbleClients.ts
··· 302 302 token: process.env.DISCORD_TOKEN, 303 303 artwork: process.env.DISCORD_ARTWORK, 304 304 applicationId: process.env.DISCORD_APPLICATION_ID, 305 + ipcLocations: process.env.DISCORD_IPC_LOCATIONS, 305 306 artworkDefaultUrl: process.env.DISCORD_ARTWORK_DEFAULT_URL, 306 307 statusOverrideAllow: process.env.DISCORD_STATUS_OVERRIDE_ALLOW, 307 308 activitiesOverrideAllow: process.env.DISCORD_ACTIVITIES_OVERRIDE_ALLOW,