fix: callback for time-changed event (#2577)

Co-authored-by: Derek Alsop <15299183+Azorant@users.noreply.github.com>
This commit is contained in:
JellyBrick
2024-11-03 19:18:06 +09:00
committed by GitHub
parent 516fbff3d7
commit 1e4cd699db
13 changed files with 137 additions and 91 deletions

View File

@ -23,6 +23,8 @@ export const backend = createBackend<BackendType, APIServerConfig>({
this.songInfo = songInfo; this.songInfo = songInfo;
}); });
ctx.ipc.on('ytmd:player-api-loaded', () => ctx.ipc.send('ytmd:setup-time-changed-listener'));
this.run(config.hostname, config.port); this.run(config.hostname, config.port);
}, },
stop() { stop() {

View File

@ -1,10 +1,13 @@
import { app, dialog, ipcMain } from 'electron'; import { app, dialog } from 'electron';
import { Client as DiscordClient } from '@xhayper/discord-rpc'; import { Client as DiscordClient } from '@xhayper/discord-rpc';
import { dev } from 'electron-is'; import { dev } from 'electron-is';
import { ActivityType, GatewayActivityButton } from 'discord-api-types/v10'; import { ActivityType, GatewayActivityButton } from 'discord-api-types/v10';
import registerCallback, { type SongInfo } from '@/providers/song-info'; import registerCallback, {
type SongInfo,
SongInfoEvent,
} from '@/providers/song-info';
import { createBackend, LoggerPrefix } from '@/utils'; import { createBackend, LoggerPrefix } from '@/utils';
import { t } from '@/i18n'; import { t } from '@/i18n';
@ -243,25 +246,28 @@ export const backend = createBackend<
// If the page is ready, register the callback // If the page is ready, register the callback
ctx.window.once('ready-to-show', () => { ctx.window.once('ready-to-show', () => {
let lastSongInfo: SongInfo;
registerCallback((songInfo) => {
lastSongInfo = songInfo;
if (this.config) this.updateActivity(songInfo, this.config);
});
connect();
let lastSent = Date.now(); let lastSent = Date.now();
ipcMain.on('ytmd:time-changed', (_, t: number) => { registerCallback((songInfo, event) => {
if (event !== SongInfoEvent.TimeChanged) {
info.lastSongInfo = songInfo;
if (this.config) this.updateActivity(songInfo, this.config);
} else {
const currentTime = Date.now(); const currentTime = Date.now();
// if lastSent is more than 5 seconds ago, send the new time // if lastSent is more than 5 seconds ago, send the new time
if (currentTime - lastSent > 5000) { if (currentTime - lastSent > 5000) {
lastSent = currentTime; lastSent = currentTime;
if (lastSongInfo) { if (songInfo) {
lastSongInfo.elapsedSeconds = t; info.lastSongInfo = songInfo;
if (this.config) this.updateActivity(lastSongInfo, this.config); if (this.config) this.updateActivity(songInfo, this.config);
}
} }
} }
}); });
connect();
}); });
ctx.ipc.on('ytmd:player-api-loaded', () =>
ctx.ipc.send('ytmd:setup-time-changed-listener'),
);
app.on('window-all-closed', clear); app.on('window-all-closed', clear);
}, },
stop() { stop() {

View File

@ -30,12 +30,13 @@ import registerCallback, {
getImage, getImage,
MediaType, MediaType,
type SongInfo, type SongInfo,
SongInfoEvent,
} from '@/providers/song-info'; } from '@/providers/song-info';
import { getNetFetchAsFetch } from '@/plugins/utils/main'; import { getNetFetchAsFetch } from '@/plugins/utils/main';
import { t } from '@/i18n'; import { t } from '@/i18n';
import { YoutubeFormatList, type Preset, DefaultPresetList } from '../types'; import { DefaultPresetList, type Preset, YoutubeFormatList } from '../types';
import type { DownloaderPluginConfig } from '../index'; import type { DownloaderPluginConfig } from '../index';
@ -68,7 +69,12 @@ const sendError = (error: Error, source?: string) => {
sendFeedback_(win); // Reset feedback sendFeedback_(win); // Reset feedback
const songNameMessage = source ? `\nin ${source}` : ''; const songNameMessage = source ? `\nin ${source}` : '';
const cause = error.cause ? `\n\n${String(error.cause)}` : ''; const cause = error.cause
? `\n\n${
// eslint-disable-next-line @typescript-eslint/no-base-to-string,@typescript-eslint/restrict-template-expressions
error.cause instanceof Error ? error.cause.toString() : error.cause
}`
: '';
const message = `${error.toString()}${songNameMessage}${cause}`; const message = `${error.toString()}${songNameMessage}${cause}`;
console.error(message); console.error(message);
@ -174,7 +180,12 @@ function downloadSongOnFinishSetup({
const defaultDownloadFolder = app.getPath('downloads'); const defaultDownloadFolder = app.getPath('downloads');
registerCallback((songInfo: SongInfo) => { registerCallback((songInfo: SongInfo, event) => {
if (event === SongInfoEvent.TimeChanged) {
const elapsedSeconds = songInfo.elapsedSeconds ?? 0;
if (elapsedSeconds > time) time = elapsedSeconds;
return;
}
if ( if (
!songInfo.isPaused && !songInfo.isPaused &&
songInfo.url !== currentUrl && songInfo.url !== currentUrl &&
@ -213,10 +224,6 @@ function downloadSongOnFinishSetup({
ipcMain.on('ytmd:player-api-loaded', () => { ipcMain.on('ytmd:player-api-loaded', () => {
ipc.send('ytmd:setup-time-changed-listener'); ipc.send('ytmd:setup-time-changed-listener');
}); });
ipcMain.on('ytmd:time-changed', (_, t: number) => {
if (t > time) time = t;
});
} }
async function downloadSongUnsafe( async function downloadSongUnsafe(

View File

@ -30,7 +30,7 @@ export default createPlugin({
config: { config: {
enabled: false, enabled: false,
}, },
backend() { backend({ ipc }) {
const secToMilisec = (t?: number) => const secToMilisec = (t?: number) =>
t ? Math.round(Number(t) * 1e3) : undefined; t ? Math.round(Number(t) * 1e3) : undefined;
const previousStatePaused = null; const previousStatePaused = null;
@ -65,6 +65,10 @@ export default createPlugin({
}); });
}; };
ipc.on('ytmd:player-api-loaded', () =>
ipc.send('ytmd:setup-time-changed-listener'),
);
registerCallback((songInfo) => { registerCallback((songInfo) => {
if (!songInfo.title && !songInfo.artist) { if (!songInfo.title && !songInfo.artist) {
return; return;

View File

@ -8,7 +8,10 @@ import previousIcon from '@assets/media-icons-black/previous.png?asset&asarUnpac
import { notificationImage, secondsToMinutes, ToastStyles } from './utils'; import { notificationImage, secondsToMinutes, ToastStyles } from './utils';
import getSongControls from '@/providers/song-controls'; import getSongControls from '@/providers/song-controls';
import registerCallback, { SongInfo } from '@/providers/song-info'; import registerCallback, {
type SongInfo,
SongInfoEvent,
} from '@/providers/song-info';
import { changeProtocolHandler } from '@/providers/protocol-handler'; import { changeProtocolHandler } from '@/providers/protocol-handler';
import { setTrayOnClick, setTrayOnDoubleClick } from '@/tray'; import { setTrayOnClick, setTrayOnDoubleClick } from '@/tray';
import { mediaIcons } from '@/types/media-icons'; import { mediaIcons } from '@/types/media-icons';
@ -258,15 +261,14 @@ export default (
let currentSeconds = 0; let currentSeconds = 0;
on('ytmd:player-api-loaded', () => send('ytmd:setup-time-changed-listener')); on('ytmd:player-api-loaded', () => send('ytmd:setup-time-changed-listener'));
on('ytmd:time-changed', (t: number) => {
currentSeconds = t;
});
let savedSongInfo: SongInfo; let savedSongInfo: SongInfo;
let lastUrl: string | undefined; let lastUrl: string | undefined;
// Register songInfoCallback // Register songInfoCallback
registerCallback((songInfo) => { registerCallback((songInfo, event) => {
if (event === SongInfoEvent.TimeChanged) {
currentSeconds = songInfo.elapsedSeconds ?? 0;
}
if (!songInfo.artist && !songInfo.title) { if (!songInfo.artist && !songInfo.title) {
return; return;
} }

View File

@ -5,7 +5,10 @@ import is from 'electron-is';
import { notificationImage } from './utils'; import { notificationImage } from './utils';
import interactive from './interactive'; import interactive from './interactive';
import registerCallback, { type SongInfo } from '@/providers/song-info'; import registerCallback, {
type SongInfo,
SongInfoEvent,
} from '@/providers/song-info';
import type { NotificationsPluginConfig } from './index'; import type { NotificationsPluginConfig } from './index';
import type { BackendContext } from '@/types/contexts'; import type { BackendContext } from '@/types/contexts';
@ -30,8 +33,9 @@ const setup = () => {
let oldNotification: Notification; let oldNotification: Notification;
let currentUrl: string | undefined; let currentUrl: string | undefined;
registerCallback((songInfo: SongInfo) => { registerCallback((songInfo: SongInfo, event) => {
if ( if (
event !== SongInfoEvent.TimeChanged &&
!songInfo.isPaused && !songInfo.isPaused &&
(songInfo.url !== currentUrl || config.unpauseNotification) (songInfo.url !== currentUrl || config.unpauseNotification)
) { ) {

View File

@ -3,6 +3,7 @@ import { BrowserWindow } from 'electron';
import registerCallback, { import registerCallback, {
MediaType, MediaType,
type SongInfo, type SongInfo,
SongInfoEvent,
} from '@/providers/song-info'; } from '@/providers/song-info';
import { createBackend } from '@/utils'; import { createBackend } from '@/utils';
@ -70,7 +71,8 @@ export const backend = createBackend<
await this.createSessions(config, setConfig); await this.createSessions(config, setConfig);
this.setConfig = setConfig; this.setConfig = setConfig;
registerCallback((songInfo: SongInfo) => { registerCallback((songInfo: SongInfo, event) => {
if (event === SongInfoEvent.TimeChanged) return;
// Set remove the old scrobble timer // Set remove the old scrobble timer
clearTimeout(scrobbleTimer); clearTimeout(scrobbleTimer);
if (!songInfo.isPaused) { if (!songInfo.isPaused) {

View File

@ -1,20 +1,23 @@
import { BrowserWindow, ipcMain } from 'electron'; import { BrowserWindow, ipcMain } from 'electron';
import MprisPlayer, { import MprisPlayer, {
Track,
LoopStatus,
type PlayBackStatus,
type PlayerOptions,
PLAYBACK_STATUS_STOPPED,
PLAYBACK_STATUS_PAUSED,
PLAYBACK_STATUS_PLAYING,
LOOP_STATUS_NONE, LOOP_STATUS_NONE,
LOOP_STATUS_PLAYLIST, LOOP_STATUS_PLAYLIST,
LOOP_STATUS_TRACK, LOOP_STATUS_TRACK,
LoopStatus,
PLAYBACK_STATUS_PAUSED,
PLAYBACK_STATUS_PLAYING,
PLAYBACK_STATUS_STOPPED,
type PlayBackStatus,
type PlayerOptions,
type Position, type Position,
Track,
} from '@jellybrick/mpris-service'; } from '@jellybrick/mpris-service';
import registerCallback, { type SongInfo } from '@/providers/song-info'; import registerCallback, {
type SongInfo,
SongInfoEvent,
} from '@/providers/song-info';
import getSongControls from '@/providers/song-controls'; import getSongControls from '@/providers/song-controls';
import config from '@/config'; import config from '@/config';
import { LoggerPrefix } from '@/utils'; import { LoggerPrefix } from '@/utils';
@ -134,10 +137,6 @@ function registerMPRIS(win: BrowserWindow) {
player.seeked(secToMicro(t)); player.seeked(secToMicro(t));
}); });
ipcMain.on('ytmd:time-changed', (_, t: number) => {
player.setPosition(secToMicro(t));
});
ipcMain.on('ytmd:repeat-changed', (_, mode: RepeatMode) => { ipcMain.on('ytmd:repeat-changed', (_, mode: RepeatMode) => {
switch (mode) { switch (mode) {
case 'NONE': { case 'NONE': {
@ -319,7 +318,11 @@ function registerMPRIS(win: BrowserWindow) {
} }
}); });
registerCallback((songInfo: SongInfo) => { registerCallback((songInfo: SongInfo, event) => {
if (event === SongInfoEvent.TimeChanged) {
player.setPosition(secToMicro(songInfo.elapsedSeconds ?? 0));
return;
}
if (player) { if (player) {
const data: Track = { const data: Track = {
'mpris:length': secToMicro(songInfo.songDuration), 'mpris:length': secToMicro(songInfo.songDuration),

View File

@ -8,7 +8,10 @@ import previousIcon from '@assets/media-icons-black/previous.png?asset&asarUnpac
import { createPlugin } from '@/utils'; import { createPlugin } from '@/utils';
import getSongControls from '@/providers/song-controls'; import getSongControls from '@/providers/song-controls';
import registerCallback, { type SongInfo } from '@/providers/song-info'; import registerCallback, {
type SongInfo,
SongInfoEvent,
} from '@/providers/song-info';
import { mediaIcons } from '@/types/media-icons'; import { mediaIcons } from '@/types/media-icons';
import { t } from '@/i18n'; import { t } from '@/i18n';
@ -102,11 +105,13 @@ export default createPlugin({
]); ]);
}; };
registerCallback((songInfo) => { registerCallback((songInfo, event) => {
if (event !== SongInfoEvent.TimeChanged) {
// Update currentsonginfo for win.on('show') // Update currentsonginfo for win.on('show')
currentSongInfo = songInfo; currentSongInfo = songInfo;
// Update thumbar // Update thumbar
setThumbar(songInfo); setThumbar(songInfo);
}
}); });
// Need to set thumbar again after win.show // Need to set thumbar again after win.show

View File

@ -2,7 +2,7 @@ import { nativeImage, type NativeImage, TouchBar } from 'electron';
import { createPlugin } from '@/utils'; import { createPlugin } from '@/utils';
import getSongControls from '@/providers/song-controls'; import getSongControls from '@/providers/song-controls';
import registerCallback from '@/providers/song-info'; import registerCallback, { SongInfoEvent } from '@/providers/song-info';
import { t } from '@/i18n'; import { t } from '@/i18n';
import youtubeMusicIcon from '@assets/youtube-music.png?asset&asarUnpack'; import youtubeMusicIcon from '@assets/youtube-music.png?asset&asarUnpack';
@ -81,7 +81,8 @@ export default createPlugin({
controls = [previous, playPause, next, dislike, like]; controls = [previous, playPause, next, dislike, like];
// Register the callback // Register the callback
registerCallback((songInfo) => { registerCallback((songInfo, event) => {
if (event === SongInfoEvent.TimeChanged) return;
// Song information changed, so lets update the touchBar // Song information changed, so lets update the touchBar
// Set the song title // Set the song title

View File

@ -28,18 +28,6 @@ export default createPlugin({
}, },
backend: { backend: {
liteMode: false, liteMode: false,
data: {
cover: '',
cover_url: '',
title: '',
artists: [] as string[],
status: '',
progress: 0,
duration: 0,
album_url: '',
album: undefined,
url: '',
} as Data,
start({ ipc }) { start({ ipc }) {
const secToMilisec = (t: number) => Math.round(Number(t) * 1e3); const secToMilisec = (t: number) => Math.round(Number(t) * 1e3);
@ -85,31 +73,24 @@ export default createPlugin({
ipc.on('ytmd:player-api-loaded', () => ipc.on('ytmd:player-api-loaded', () =>
ipc.send('ytmd:setup-time-changed-listener'), ipc.send('ytmd:setup-time-changed-listener'),
); );
ipc.on('ytmd:time-changed', (t: number) => {
if (!this.data.title) {
return;
}
this.data.progress = secToMilisec(t);
post(this.data);
});
registerCallback((songInfo) => { registerCallback((songInfo) => {
if (!songInfo.title && !songInfo.artist) { if (!songInfo.title && !songInfo.artist) {
return; return;
} }
this.data.duration = secToMilisec(songInfo.songDuration); post({
this.data.progress = secToMilisec(songInfo.elapsedSeconds ?? 0); duration: secToMilisec(songInfo.songDuration),
this.data.cover = songInfo.imageSrc ?? ''; progress: secToMilisec(songInfo.elapsedSeconds ?? 0),
this.data.cover_url = songInfo.imageSrc ?? ''; cover: songInfo.imageSrc ?? '',
this.data.album_url = songInfo.imageSrc ?? ''; cover_url: songInfo.imageSrc ?? '',
this.data.title = songInfo.title; album_url: songInfo.imageSrc ?? '',
this.data.artists = [songInfo.artist]; title: songInfo.title,
this.data.status = songInfo.isPaused ? 'stopped' : 'playing'; artists: [songInfo.artist],
this.data.album = songInfo.album; status: songInfo.isPaused ? 'stopped' : 'playing',
this.data.url = songInfo.url ?? ''; album: songInfo.album,
post(this.data); url: songInfo.url ?? '',
});
}); });
}, },
}, },

View File

@ -149,8 +149,17 @@ const handleData = async (
return songInfo; return songInfo;
}; };
export enum SongInfoEvent {
VideoSrcChanged = 'ytmd:video-src-changed',
PlayOrPaused = 'ytmd:play-or-paused',
TimeChanged = 'ytmd:time-changed',
}
// This variable will be filled with the callbacks once they register // This variable will be filled with the callbacks once they register
export type SongInfoCallback = (songInfo: SongInfo, event?: string) => void; export type SongInfoCallback = (
songInfo: SongInfo,
event: SongInfoEvent,
) => void;
const callbacks: Set<SongInfoCallback> = new Set(); const callbacks: Set<SongInfoCallback> = new Set();
// This function will allow plugins to register callback that will be triggered when data changes // This function will allow plugins to register callback that will be triggered when data changes
@ -173,7 +182,7 @@ const registerProvider = (win: BrowserWindow) => {
if (tempSongInfo) { if (tempSongInfo) {
for (const c of callbacks) { for (const c of callbacks) {
c(tempSongInfo, 'ytmd:video-src-changed'); c(tempSongInfo, SongInfoEvent.VideoSrcChanged);
} }
} }
}); });
@ -199,11 +208,29 @@ const registerProvider = (win: BrowserWindow) => {
if (tempSongInfo) { if (tempSongInfo) {
for (const c of callbacks) { for (const c of callbacks) {
c(tempSongInfo, 'ytmd:play-or-paused'); c(tempSongInfo, SongInfoEvent.PlayOrPaused);
} }
} }
}, },
); );
ipcMain.on('ytmd:time-changed', async (_, seconds: number) => {
const tempSongInfo = await dataMutex.runExclusive<SongInfo | null>(() => {
if (!songInfo) {
return null;
}
songInfo.elapsedSeconds = seconds;
return songInfo;
});
if (tempSongInfo) {
for (const c of callbacks) {
c(tempSongInfo, SongInfoEvent.TimeChanged);
}
}
});
}; };
const suffixesToRemove = [ const suffixesToRemove = [

View File

@ -1,4 +1,4 @@
import { Menu, screen, nativeImage, Tray } from 'electron'; import { Menu, nativeImage, screen, Tray } from 'electron';
import is from 'electron-is'; import is from 'electron-is';
import defaultTrayIconAsset from '@assets/youtube-music-tray.png?asset&asarUnpack'; import defaultTrayIconAsset from '@assets/youtube-music-tray.png?asset&asarUnpack';
@ -7,7 +7,7 @@ import pausedTrayIconAsset from '@assets/youtube-music-tray-paused.png?asset&asa
import config from './config'; import config from './config';
import { restart } from './providers/app-controls'; import { restart } from './providers/app-controls';
import registerCallback from './providers/song-info'; import registerCallback, { SongInfoEvent } from './providers/song-info';
import getSongControls from './providers/song-controls'; import getSongControls from './providers/song-controls';
import { t } from '@/i18n'; import { t } from '@/i18n';
@ -125,7 +125,9 @@ export const setUpTray = (app: Electron.App, win: Electron.BrowserWindow) => {
const trayMenu = Menu.buildFromTemplate(template); const trayMenu = Menu.buildFromTemplate(template);
tray.setContextMenu(trayMenu); tray.setContextMenu(trayMenu);
registerCallback((songInfo) => { registerCallback((songInfo, event) => {
if (event === SongInfoEvent.TimeChanged) return;
if (tray) { if (tray) {
if (typeof songInfo.isPaused === 'undefined') { if (typeof songInfo.isPaused === 'undefined') {
tray.setImage(defaultTrayIcon); tray.setImage(defaultTrayIcon);