Skip to content

Commit 9f53fbe

Browse files
fix(discord): replace dead client ID, make it configurable (#88)
The bundled Rich Presence application ID was rejected by Discord (404 / InvalidID 4000), so RPC never connected. Swap in a freshly registered app ID and expose it as [discord] client_id so users can point at their own application — and so a future ID rotation is a config change, not a code release. The bundled ID lives in one place (DEFAULT_DISCORD_CLIENT_ID); an empty config value falls back to it. Verified live: connects to a running client and publishes the activity.
1 parent eb2de8e commit 9f53fbe

6 files changed

Lines changed: 32 additions & 6 deletions

File tree

docs/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ enabled = true
107107
```toml
108108
[discord]
109109
enabled = false # requires `pip install ytm-player[discord]`
110+
client_id = "" # blank uses the bundled app; set your own
111+
# Discord application ID to publish under it
110112
```
111113

112114
### `[lastfm]`

src/ytm_player/app/_app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ async def on_mount(self) -> None:
608608

609609
# Start Discord Rich Presence if enabled.
610610
if self.settings.discord.enabled:
611-
self.discord = DiscordRPC()
611+
self.discord = DiscordRPC(client_id=self.settings.discord.client_id)
612612
await self.discord.connect()
613613

614614
# Start Last.fm scrobbling if enabled.

src/ytm_player/config/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class MPRISSettings:
102102
@dataclass
103103
class DiscordSettings:
104104
enabled: bool = False
105+
# Empty = use the bundled "YouTube Music" app; set to your own Discord
106+
# application ID to publish Rich Presence under your own app.
107+
client_id: str = ""
105108

106109

107110
@dataclass

src/ytm_player/services/discord_rpc.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111

1212
logger = logging.getLogger(__name__)
1313

14-
# Discord application ID for ytm-player (YouTube Music category).
15-
_CLIENT_ID = "1338519089781362698"
14+
# Default Discord application ID — the bundled "YouTube Music" Rich Presence
15+
# app. Users can point at their own app via [discord] client_id in config.
16+
DEFAULT_DISCORD_CLIENT_ID = "1517429270115258490"
1617

1718

1819
class DiscordRPC:
@@ -22,7 +23,10 @@ class DiscordRPC:
2223
Reconnects automatically if the connection drops.
2324
"""
2425

25-
def __init__(self) -> None:
26+
def __init__(self, client_id: str = "") -> None:
27+
# Blank/whitespace falls back to the bundled app, so an empty config
28+
# value doesn't break RPC; `enabled = false` is the real off switch.
29+
self._client_id = client_id.strip() or DEFAULT_DISCORD_CLIENT_ID
2630
self._rpc: object | None = None
2731
self._connected = False
2832
self._start_time: float = 0
@@ -38,7 +42,7 @@ async def connect(self) -> bool:
3842
return False
3943

4044
try:
41-
self._rpc = AioPresence(_CLIENT_ID)
45+
self._rpc = AioPresence(self._client_id)
4246
await self._rpc.connect() # type: ignore[union-attr]
4347
self._connected = True
4448
logger.info("Connected to Discord Rich Presence")

tests/test_config/test_settings_extended.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class TestPhase2Fields:
3636
def test_discord_defaults(self):
3737
d = DiscordSettings()
3838
assert d.enabled is False
39+
assert d.client_id == ""
3940

4041
def test_lastfm_defaults(self):
4142
lf = LastFMSettings()
@@ -65,10 +66,12 @@ def test_discord_round_trip(self, tmp_config_dir):
6566
path = tmp_config_dir / "config.toml"
6667
s = Settings()
6768
s.discord.enabled = True
69+
s.discord.client_id = "1234567890"
6870
s.save(path)
6971

7072
loaded = Settings.load(path)
7173
assert loaded.discord.enabled is True
74+
assert loaded.discord.client_id == "1234567890"
7275

7376

7477
class TestLyricsSettings:
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Tests for DiscordRPC (no external dependencies needed)."""
22

3-
from ytm_player.services.discord_rpc import DiscordRPC
3+
from ytm_player.services.discord_rpc import DEFAULT_DISCORD_CLIENT_ID, DiscordRPC
44

55

66
class TestDiscordRPCInit:
@@ -9,3 +9,17 @@ def test_initial_state(self):
99
assert rpc.is_connected is False
1010
assert rpc._rpc is None
1111
assert rpc._start_time == 0
12+
13+
def test_default_client_id(self):
14+
"""No client_id given → uses the bundled default."""
15+
assert DiscordRPC()._client_id == DEFAULT_DISCORD_CLIENT_ID
16+
17+
def test_custom_client_id(self):
18+
"""A user-supplied client_id is honoured (and trimmed)."""
19+
assert DiscordRPC(client_id="998877665544332211")._client_id == "998877665544332211"
20+
assert DiscordRPC(client_id=" 998877665544332211 ")._client_id == "998877665544332211"
21+
22+
def test_empty_client_id_falls_back(self):
23+
"""Empty or whitespace-only client_id falls back to the default (#88)."""
24+
assert DiscordRPC(client_id="")._client_id == DEFAULT_DISCORD_CLIENT_ID
25+
assert DiscordRPC(client_id=" ")._client_id == DEFAULT_DISCORD_CLIENT_ID

0 commit comments

Comments
 (0)