mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 02:31:45 +00:00
fix(discord-rpc, scrobbler): Align artist and title with the last.fm's de facto standard
- Display only the main artist. - Display the title in its original language without romanization. - fix #3358 - fix #3641
This commit is contained in:
@ -758,6 +758,7 @@
|
|||||||
"token": "Enter ListenBrainz user token"
|
"token": "Enter ListenBrainz user token"
|
||||||
},
|
},
|
||||||
"scrobble-alternative-title": "Use alternative titles",
|
"scrobble-alternative-title": "Use alternative titles",
|
||||||
|
"scrobble-alternative-artist": "Use alternative artists",
|
||||||
"scrobble-other-media": "Scrobble other media"
|
"scrobble-other-media": "Scrobble other media"
|
||||||
},
|
},
|
||||||
"name": "Scrobbler",
|
"name": "Scrobbler",
|
||||||
|
|||||||
@ -23,13 +23,3 @@ export enum TimerKey {
|
|||||||
UpdateTimeout = 'updateTimeout', // Timer for throttled activity updates
|
UpdateTimeout = 'updateTimeout', // Timer for throttled activity updates
|
||||||
DiscordConnectRetry = 'discordConnectRetry', // Timer for Discord connection retries
|
DiscordConnectRetry = 'discordConnectRetry', // Timer for Discord connection retries
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* An enum for Discord's activity.status_display_type field, governing which field of the activity should be used after
|
|
||||||
* "Listening to..." in the user's Discord status.
|
|
||||||
*/
|
|
||||||
export const DiscordStatusDisplayType = {
|
|
||||||
YOUTUBE_MUSIC: 0,
|
|
||||||
ARTIST: 1,
|
|
||||||
TITLE: 2,
|
|
||||||
} as const;
|
|
||||||
|
|||||||
@ -99,9 +99,9 @@ export class DiscordService {
|
|||||||
const activityInfo: SetActivity = {
|
const activityInfo: SetActivity = {
|
||||||
type: ActivityType.Listening,
|
type: ActivityType.Listening,
|
||||||
statusDisplayType: config.statusDisplayType,
|
statusDisplayType: config.statusDisplayType,
|
||||||
details: truncateString(songInfo.title, 128), // Song title
|
details: truncateString(songInfo.alternativeTitle ?? songInfo.title, 128), // Song title
|
||||||
detailsUrl: songInfo.url ?? undefined,
|
detailsUrl: songInfo.url ?? undefined,
|
||||||
state: truncateString(songInfo.artist, 128), // Artist name
|
state: truncateString(songInfo.tags?.at(0) ?? songInfo.artist, 128), // Artist name
|
||||||
stateUrl: songInfo.artistUrl,
|
stateUrl: songInfo.artistUrl,
|
||||||
largeImageKey: songInfo.imageSrc ?? undefined,
|
largeImageKey: songInfo.imageSrc ?? undefined,
|
||||||
largeImageText: songInfo.album
|
largeImageText: songInfo.album
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
|
import { StatusDisplayType } from 'discord-api-types/v10';
|
||||||
|
|
||||||
import { createPlugin } from '@/utils';
|
import { createPlugin } from '@/utils';
|
||||||
import { backend } from './main';
|
import { backend } from './main';
|
||||||
import { onMenu } from './menu';
|
import { onMenu } from './menu';
|
||||||
import { t } from '@/i18n';
|
import { t } from '@/i18n';
|
||||||
import { DiscordStatusDisplayType } from './constants';
|
|
||||||
|
|
||||||
export type DiscordPluginConfig = {
|
export type DiscordPluginConfig = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@ -37,7 +38,7 @@ export type DiscordPluginConfig = {
|
|||||||
/**
|
/**
|
||||||
* Controls which field is displayed in the Discord status text
|
* Controls which field is displayed in the Discord status text
|
||||||
*/
|
*/
|
||||||
statusDisplayType: (typeof DiscordStatusDisplayType)[keyof typeof DiscordStatusDisplayType];
|
statusDisplayType: (typeof StatusDisplayType)[keyof typeof StatusDisplayType];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createPlugin({
|
export default createPlugin({
|
||||||
@ -52,7 +53,7 @@ export default createPlugin({
|
|||||||
playOnYouTubeMusic: true,
|
playOnYouTubeMusic: true,
|
||||||
hideGitHubButton: false,
|
hideGitHubButton: false,
|
||||||
hideDurationLeft: false,
|
hideDurationLeft: false,
|
||||||
statusDisplayType: DiscordStatusDisplayType.ARTIST,
|
statusDisplayType: StatusDisplayType.Details,
|
||||||
} as DiscordPluginConfig,
|
} as DiscordPluginConfig,
|
||||||
menu: onMenu,
|
menu: onMenu,
|
||||||
backend,
|
backend,
|
||||||
|
|||||||
@ -1,30 +1,27 @@
|
|||||||
import prompt from 'custom-electron-prompt';
|
import prompt from 'custom-electron-prompt';
|
||||||
|
|
||||||
import { discordService } from './main';
|
import { StatusDisplayType } from 'discord-api-types/v10';
|
||||||
|
|
||||||
|
import { discordService } from './main';
|
||||||
import { singleton } from '@/providers/decorators';
|
import { singleton } from '@/providers/decorators';
|
||||||
import promptOptions from '@/providers/prompt-options';
|
import promptOptions from '@/providers/prompt-options';
|
||||||
import { setMenuOptions } from '@/config/plugins';
|
import { setMenuOptions } from '@/config/plugins';
|
||||||
|
|
||||||
import { t } from '@/i18n';
|
import { t } from '@/i18n';
|
||||||
|
|
||||||
import { DiscordStatusDisplayType } from './constants';
|
|
||||||
|
|
||||||
import type { MenuContext } from '@/types/contexts';
|
import type { MenuContext } from '@/types/contexts';
|
||||||
import type { DiscordPluginConfig } from './index';
|
import type { DiscordPluginConfig } from './index';
|
||||||
|
|
||||||
import type { MenuTemplate } from '@/menu';
|
import type { MenuTemplate } from '@/menu';
|
||||||
|
|
||||||
const registerRefreshOnce = singleton((refreshMenu: () => void) => {
|
const registerRefreshOnce = singleton((refreshMenu: () => void) => {
|
||||||
discordService?.registerRefreshCallback(refreshMenu);
|
discordService?.registerRefreshCallback(refreshMenu);
|
||||||
});
|
});
|
||||||
|
|
||||||
const DiscordStatusDisplayTypeLabels = {
|
const DiscordStatusDisplayTypeLabels: Record<StatusDisplayType, string> = {
|
||||||
[DiscordStatusDisplayType.YOUTUBE_MUSIC]:
|
[StatusDisplayType.Name]:
|
||||||
'plugins.discord.menu.set-status-display-type.submenu.youtube-music',
|
'plugins.discord.menu.set-status-display-type.submenu.youtube-music',
|
||||||
[DiscordStatusDisplayType.ARTIST]:
|
[StatusDisplayType.State]:
|
||||||
'plugins.discord.menu.set-status-display-type.submenu.artist',
|
'plugins.discord.menu.set-status-display-type.submenu.artist',
|
||||||
[DiscordStatusDisplayType.TITLE]:
|
[StatusDisplayType.Details]:
|
||||||
'plugins.discord.menu.set-status-display-type.submenu.title',
|
'plugins.discord.menu.set-status-display-type.submenu.title',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -105,18 +102,24 @@ export const onMenu = async ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('plugins.discord.menu.set-status-display-type.label'),
|
label: t('plugins.discord.menu.set-status-display-type.label'),
|
||||||
submenu: Object.values(DiscordStatusDisplayType).map(
|
submenu: Object.values(StatusDisplayType)
|
||||||
(statusDisplayType) => ({
|
.filter(
|
||||||
label: t(DiscordStatusDisplayTypeLabels[statusDisplayType]),
|
(v) => typeof StatusDisplayType[v as StatusDisplayType] !== 'number',
|
||||||
|
)
|
||||||
|
.map((statusDisplayType) => ({
|
||||||
|
label: t(
|
||||||
|
DiscordStatusDisplayTypeLabels[
|
||||||
|
statusDisplayType as StatusDisplayType
|
||||||
|
],
|
||||||
|
),
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: config.statusDisplayType == statusDisplayType,
|
checked: config.statusDisplayType === statusDisplayType,
|
||||||
click() {
|
click() {
|
||||||
setConfig({
|
setConfig({
|
||||||
statusDisplayType,
|
statusDisplayType: statusDisplayType as StatusDisplayType,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
})),
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,11 +13,17 @@ export interface ScrobblerPluginConfig {
|
|||||||
*/
|
*/
|
||||||
scrobbleOtherMedia: boolean;
|
scrobbleOtherMedia: boolean;
|
||||||
/**
|
/**
|
||||||
* Use alternative titles for scrobbling (Useful for non-roman song titles)
|
* Use alternative titles for scrobbling (Useful for non-roman song titles, e.g. (Not) A Devil -> デビルじゃないもん)
|
||||||
*
|
*
|
||||||
* @default false
|
* @default true
|
||||||
*/
|
*/
|
||||||
alternativeTitles: boolean;
|
alternativeTitles: boolean;
|
||||||
|
/**
|
||||||
|
* Use alternative artist for scrobbling (e.g., DECO27 & (or) PinocchioP -> DECO27 / marasy -> まらしぃ)
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
alternativeArtist: boolean;
|
||||||
scrobblers: {
|
scrobblers: {
|
||||||
lastfm: {
|
lastfm: {
|
||||||
/**
|
/**
|
||||||
@ -77,7 +83,8 @@ export interface ScrobblerPluginConfig {
|
|||||||
export const defaultConfig: ScrobblerPluginConfig = {
|
export const defaultConfig: ScrobblerPluginConfig = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
scrobbleOtherMedia: true,
|
scrobbleOtherMedia: true,
|
||||||
alternativeTitles: false,
|
alternativeTitles: true,
|
||||||
|
alternativeArtist: true,
|
||||||
scrobblers: {
|
scrobblers: {
|
||||||
lastfm: {
|
lastfm: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|||||||
@ -105,6 +105,15 @@ export const onMenu = async ({
|
|||||||
setConfig(config);
|
setConfig(config);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('plugins.scrobbler.menu.scrobble-alternative-artist'),
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: Boolean(config.alternativeArtist),
|
||||||
|
click(item) {
|
||||||
|
config.alternativeArtist = item.checked;
|
||||||
|
setConfig(config);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Last.fm',
|
label: 'Last.fm',
|
||||||
submenu: [
|
submenu: [
|
||||||
|
|||||||
@ -132,10 +132,15 @@ export class LastFmScrobbler extends ScrobblerBase {
|
|||||||
? songInfo.alternativeTitle
|
? songInfo.alternativeTitle
|
||||||
: songInfo.title;
|
: songInfo.title;
|
||||||
|
|
||||||
|
const artist =
|
||||||
|
config.alternativeArtist && songInfo.tags?.at(0) !== undefined
|
||||||
|
? songInfo.tags?.at(0)
|
||||||
|
: songInfo.artist;
|
||||||
|
|
||||||
const postData: LastFmSongData = {
|
const postData: LastFmSongData = {
|
||||||
track: title,
|
track: title,
|
||||||
duration: songInfo.songDuration,
|
duration: songInfo.songDuration,
|
||||||
artist: songInfo.artist,
|
artist: artist,
|
||||||
...(songInfo.album ? { album: songInfo.album } : undefined), // Will be undefined if current song is a video
|
...(songInfo.album ? { album: songInfo.album } : undefined), // Will be undefined if current song is a video
|
||||||
api_key: config.scrobblers.lastfm.apiKey,
|
api_key: config.scrobblers.lastfm.apiKey,
|
||||||
sk: config.scrobblers.lastfm.sessionKey,
|
sk: config.scrobblers.lastfm.sessionKey,
|
||||||
|
|||||||
@ -81,8 +81,13 @@ function createRequestBody(
|
|||||||
? songInfo.alternativeTitle
|
? songInfo.alternativeTitle
|
||||||
: songInfo.title;
|
: songInfo.title;
|
||||||
|
|
||||||
|
const artist =
|
||||||
|
config.alternativeArtist && songInfo.tags?.at(0) !== undefined
|
||||||
|
? songInfo.tags?.at(0)
|
||||||
|
: songInfo.artist;
|
||||||
|
|
||||||
const trackMetadata = {
|
const trackMetadata = {
|
||||||
artist_name: songInfo.artist,
|
artist_name: artist,
|
||||||
track_name: title,
|
track_name: title,
|
||||||
release_name: songInfo.album ?? undefined,
|
release_name: songInfo.album ?? undefined,
|
||||||
additional_info: {
|
additional_info: {
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import { fileURLToPath } from 'node:url';
|
|||||||
|
|
||||||
import { globSync } from 'glob';
|
import { globSync } from 'glob';
|
||||||
import { Project } from 'ts-morph';
|
import { Project } from 'ts-morph';
|
||||||
import { Platform } from '../src/types/plugins'
|
|
||||||
|
import { Platform } from '../src/types/plugins';
|
||||||
|
|
||||||
const kebabToCamel = (text: string) =>
|
const kebabToCamel = (text: string) =>
|
||||||
text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase());
|
text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase());
|
||||||
@ -75,7 +76,7 @@ export const pluginVirtualModuleGenerator = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
writer.blankLine();
|
writer.blankLine();
|
||||||
if (mode === "main" || mode === "preload") {
|
if (mode === 'main' || mode === 'preload') {
|
||||||
writer.writeLine("import * as is from 'electron-is';");
|
writer.writeLine("import * as is from 'electron-is';");
|
||||||
writer.writeLine('globalThis.electronIs = is;');
|
writer.writeLine('globalThis.electronIs = is;');
|
||||||
}
|
}
|
||||||
@ -137,7 +138,7 @@ export const pluginVirtualModuleGenerator = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
function supportsPlatform({ platform }: { platform: string }) {
|
function supportsPlatform({ platform }: { platform: string }) {
|
||||||
if (typeof platform !== "number") return true;
|
if (typeof platform !== 'number') return true;
|
||||||
|
|
||||||
const is = globalThis.electronIs;
|
const is = globalThis.electronIs;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user