Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/i18n/resources/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@
"disconnected": "Disconnected",
"hide-duration-left": "Hide duration left",
"hide-github-button": "Hide GitHub link Button",
"show-youtube-user": "Show YouTube Music user info",
"play-on-pear-desktop": "Play on Pear Desktop",
"set-inactivity-timeout": "Set inactivity timeout",
"set-status-display-type": {
Expand Down
1 change: 1 addition & 0 deletions src/i18n/resources/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@
"disconnected": "Отключено",
"hide-duration-left": "Скрыть сколько осталось времени",
"hide-github-button": "Скрыть ссылку на GitHub",
"show-youtube-user": "Показать информацию пользователя YouTube Music",
"play-on-pear-desktop": "Воспроизвести на Pear Desktop",
"set-inactivity-timeout": "Поставить таймер неактивности",
"set-status-display-type": {
Expand Down
1 change: 1 addition & 0 deletions src/i18n/resources/uk.json
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@
"disconnected": "Від'єднано",
"hide-duration-left": "Приховати тривалість, яка залишилася",
"hide-github-button": "Приховати посилання на GitHub",
"show-youtube-user": "Показати інформацію користувача YouTube Music",
"play-on-pear-desktop": "Слухати на Pear Desktop",
"set-inactivity-timeout": "Встановити тайм-аут бездіяльності",
"set-status-display-type": {
Expand Down
113 changes: 112 additions & 1 deletion src/plugins/discord/discord-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export class DiscordService {

mainWindow: Electron.BrowserWindow;

/**
* Stores the logged-in YouTube Music user information.
*/
private youtubeUser: { name: string; avatar: string } | null = null;

/**
* Initializes the Discord service with configuration and main window reference.
* Sets up RPC event listeners.
Expand All @@ -71,6 +76,10 @@ export class DiscordService {

this.rpc.on('ready', () => {
this.ready = true;
// Fetch YouTube Music user info after connecting if enabled
if (this.config?.showYouTubeUser) {
this.fetchYouTubeUserInfo();
}
if (this.lastSongInfo && this.config) {
this.updateActivity(this.lastSongInfo);
}
Expand Down Expand Up @@ -107,6 +116,8 @@ export class DiscordService {
largeImageText: songInfo.album
? truncateString(songInfo.album, 128)
: undefined,
smallImageKey: config.showYouTubeUser ? this.getYouTubeUserAvatar() : undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Replace ·?·this.getYouTubeUserAvatar() with ⏎········?·this.getYouTubeUserAvatar()⏎·······

Suggested change
smallImageKey: config.showYouTubeUser ? this.getYouTubeUserAvatar() : undefined,
smallImageKey: config.showYouTubeUser
? this.getYouTubeUserAvatar()
: undefined,

smallImageText: config.showYouTubeUser ? this.getYouTubeUserName() : undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Replace ·?·this.getYouTubeUserName() with ⏎········?·this.getYouTubeUserName()⏎·······

Suggested change
smallImageText: config.showYouTubeUser ? this.getYouTubeUserName() : undefined,
smallImageText: config.showYouTubeUser
? this.getYouTubeUserName()
: undefined,

buttons: buildDiscordButtons(config, songInfo),
};

Expand Down Expand Up @@ -280,6 +291,11 @@ export class DiscordService {
// Cache the latest song info
this.timerManager.clear(TimerKey.ClearActivity);

// Fetch YouTube user info if not already available and feature is enabled
if (!this.youtubeUser && this.config?.showYouTubeUser) {
this.fetchYouTubeUserInfo();
}

if (!this.rpc || !this.ready) {
// skip update if not ready
return;
Expand Down Expand Up @@ -400,6 +416,101 @@ export class DiscordService {
return this.rpc.isConnected && this.ready;
}


/**
Comment on lines +419 to +420
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Delete

Suggested change
/**
/**

* Fetches the YouTube Music user avatar and name from the page.
* This method opens the settings menu to access the username.
*/
private async fetchYouTubeUserInfo(): Promise<void> {
try {
const result = await this.mainWindow.webContents.executeJavaScript(`
(async function() {
try {
// Find avatar first - this is always visible
const accountButton = document.querySelector('ytmusic-settings-button img#img')
|| document.querySelector('ytmusic-settings-button yt-img-shadow img')
|| document.querySelector('ytmusic-settings-button img');

let avatar = null;
if (accountButton) {
avatar = accountButton.src || accountButton.getAttribute('src');
}

// Now get the username by clicking the settings button
const settingsButton = document.querySelector('ytmusic-settings-button button')
|| document.querySelector('ytmusic-settings-button tp-yt-paper-icon-button');

let name = 'Pear Desktop User';

if (settingsButton) {
// Click to open the menu
settingsButton.click();

// Wait for the menu to appear (check multiple times)
for (let i = 0; i < 20; i++) {
await new Promise(resolve => setTimeout(resolve, 50));

const accountNameElement = document.querySelector('ytd-active-account-header-renderer #account-name')
|| document.querySelector('yt-formatted-string#account-name');

if (accountNameElement) {
name = accountNameElement.textContent?.trim()
|| accountNameElement.getAttribute('title')
|| name;
break;
}
}

// Close the menu by pressing Escape
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27 }));
}

if (avatar) {
return { avatar, name };
}

return null;
} catch (e) {
console.error('Failed to fetch YouTube user info:', e);
return null;
}
})();
`);
Comment on lines +426 to +478
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.


if (result && result.avatar) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .avatar on an any value.

this.youtubeUser = {
name: result.name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .name on an any value.

avatar: result.avatar,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-assignment> reported by reviewdog 🐶
Unsafe assignment of an any value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .avatar on an any value.

};
console.log(LoggerPrefix, `Fetched YouTube user: ${result.name}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .name on an any value.

console.log(LoggerPrefix, `Fetched Avatar URL: ${result.avatar}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <@typescript-eslint/no-unsafe-member-access> reported by reviewdog 🐶
Unsafe member access .avatar on an any value.

} else {
console.log(LoggerPrefix, 'Could not fetch YouTube user info - retrying in 5 seconds');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Replace LoggerPrefix,·'Could·not·fetch·YouTube·user·info·-·retrying·in·5·seconds' with ⏎··········LoggerPrefix,⏎··········'Could·not·fetch·YouTube·user·info·-·retrying·in·5·seconds',⏎········

Suggested change
console.log(LoggerPrefix, 'Could not fetch YouTube user info - retrying in 5 seconds');
console.log(
LoggerPrefix,
'Could not fetch YouTube user info - retrying in 5 seconds',
);

// Retry after a delay if enabled
if (this.config?.showYouTubeUser) {
setTimeout(() => this.fetchYouTubeUserInfo(), 5000);
}
}
} catch (err) {
console.error(LoggerPrefix, 'Failed to fetch YouTube user info:', err);
}
}

/**
* Get the YouTube user's avatar URL for use in rich presence.
* @returns The avatar URL or undefined if not available
*/
private getYouTubeUserAvatar(): string | undefined {
return this.youtubeUser?.avatar ?? undefined;
}

/**
* Get the YouTube user's name for use in rich presence.
* @returns The username or undefined if not available
*/
private getYouTubeUserName(): string | undefined {
return this.youtubeUser?.name ?? undefined;
}
/**
* Cleans up resources: disconnects RPC, clears all timers, and clears callbacks.
* Should be called when the plugin stops or the application quits.
Expand All @@ -408,4 +519,4 @@ export class DiscordService {
this.disconnect();
this.refreshCallbacks = [];
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Insert

Suggested change
}
}

7 changes: 7 additions & 0 deletions src/plugins/discord/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export type DiscordPluginConfig = {
* Controls which field is displayed in the Discord status text
*/
statusDisplayType: (typeof StatusDisplayType)[keyof typeof StatusDisplayType];
/**
* Show YouTube Music user avatar and username in Discord Rich Presence
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prettier/prettier> reported by reviewdog 🐶
Delete ·

Suggested change
*
*

* @default true
*/
showYouTubeUser: boolean;
};

export default createPlugin({
Expand All @@ -54,6 +60,7 @@ export default createPlugin({
hideGitHubButton: false,
hideDurationLeft: false,
statusDisplayType: StatusDisplayType.Details,
showYouTubeUser: true,
} as DiscordPluginConfig,
menu: onMenu,
backend,
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/discord/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ export const onMenu = async ({
});
},
},
{
label: t('plugins.discord.menu.show-youtube-user'),
type: 'checkbox',
checked: config.showYouTubeUser,
click(item: Electron.MenuItem) {
setConfig({
showYouTubeUser: item.checked,
});
},
},
{
label: t('plugins.discord.menu.hide-github-button'),
type: 'checkbox',
Expand Down
Loading