mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 18:41:47 +00:00
fix: callback for time-changed event (#2577)
Co-authored-by: Derek Alsop <15299183+Azorant@users.noreply.github.com>
This commit is contained in:
@ -23,6 +23,8 @@ export const backend = createBackend<BackendType, APIServerConfig>({
|
||||
this.songInfo = songInfo;
|
||||
});
|
||||
|
||||
ctx.ipc.on('ytmd:player-api-loaded', () => ctx.ipc.send('ytmd:setup-time-changed-listener'));
|
||||
|
||||
this.run(config.hostname, config.port);
|
||||
},
|
||||
stop() {
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { app, dialog, ipcMain } from 'electron';
|
||||
import { app, dialog } from 'electron';
|
||||
import { Client as DiscordClient } from '@xhayper/discord-rpc';
|
||||
import { dev } from 'electron-is';
|
||||
|
||||
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 { t } from '@/i18n';
|
||||
|
||||
@ -243,25 +246,28 @@ export const backend = createBackend<
|
||||
|
||||
// If the page is ready, register the callback
|
||||
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();
|
||||
ipcMain.on('ytmd:time-changed', (_, t: number) => {
|
||||
const currentTime = Date.now();
|
||||
// if lastSent is more than 5 seconds ago, send the new time
|
||||
if (currentTime - lastSent > 5000) {
|
||||
lastSent = currentTime;
|
||||
if (lastSongInfo) {
|
||||
lastSongInfo.elapsedSeconds = t;
|
||||
if (this.config) this.updateActivity(lastSongInfo, this.config);
|
||||
registerCallback((songInfo, event) => {
|
||||
if (event !== SongInfoEvent.TimeChanged) {
|
||||
info.lastSongInfo = songInfo;
|
||||
if (this.config) this.updateActivity(songInfo, this.config);
|
||||
} else {
|
||||
const currentTime = Date.now();
|
||||
// if lastSent is more than 5 seconds ago, send the new time
|
||||
if (currentTime - lastSent > 5000) {
|
||||
lastSent = currentTime;
|
||||
if (songInfo) {
|
||||
info.lastSongInfo = songInfo;
|
||||
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);
|
||||
},
|
||||
stop() {
|
||||
|
||||
@ -30,12 +30,13 @@ import registerCallback, {
|
||||
getImage,
|
||||
MediaType,
|
||||
type SongInfo,
|
||||
SongInfoEvent,
|
||||
} from '@/providers/song-info';
|
||||
import { getNetFetchAsFetch } from '@/plugins/utils/main';
|
||||
|
||||
import { t } from '@/i18n';
|
||||
|
||||
import { YoutubeFormatList, type Preset, DefaultPresetList } from '../types';
|
||||
import { DefaultPresetList, type Preset, YoutubeFormatList } from '../types';
|
||||
|
||||
import type { DownloaderPluginConfig } from '../index';
|
||||
|
||||
@ -68,7 +69,12 @@ const sendError = (error: Error, source?: string) => {
|
||||
sendFeedback_(win); // Reset feedback
|
||||
|
||||
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}`;
|
||||
|
||||
console.error(message);
|
||||
@ -174,7 +180,12 @@ function downloadSongOnFinishSetup({
|
||||
|
||||
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 (
|
||||
!songInfo.isPaused &&
|
||||
songInfo.url !== currentUrl &&
|
||||
@ -213,10 +224,6 @@ function downloadSongOnFinishSetup({
|
||||
ipcMain.on('ytmd:player-api-loaded', () => {
|
||||
ipc.send('ytmd:setup-time-changed-listener');
|
||||
});
|
||||
|
||||
ipcMain.on('ytmd:time-changed', (_, t: number) => {
|
||||
if (t > time) time = t;
|
||||
});
|
||||
}
|
||||
|
||||
async function downloadSongUnsafe(
|
||||
|
||||
@ -30,7 +30,7 @@ export default createPlugin({
|
||||
config: {
|
||||
enabled: false,
|
||||
},
|
||||
backend() {
|
||||
backend({ ipc }) {
|
||||
const secToMilisec = (t?: number) =>
|
||||
t ? Math.round(Number(t) * 1e3) : undefined;
|
||||
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) => {
|
||||
if (!songInfo.title && !songInfo.artist) {
|
||||
return;
|
||||
|
||||
@ -8,7 +8,10 @@ import previousIcon from '@assets/media-icons-black/previous.png?asset&asarUnpac
|
||||
import { notificationImage, secondsToMinutes, ToastStyles } from './utils';
|
||||
|
||||
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 { setTrayOnClick, setTrayOnDoubleClick } from '@/tray';
|
||||
import { mediaIcons } from '@/types/media-icons';
|
||||
@ -258,15 +261,14 @@ export default (
|
||||
let currentSeconds = 0;
|
||||
on('ytmd:player-api-loaded', () => send('ytmd:setup-time-changed-listener'));
|
||||
|
||||
on('ytmd:time-changed', (t: number) => {
|
||||
currentSeconds = t;
|
||||
});
|
||||
|
||||
let savedSongInfo: SongInfo;
|
||||
let lastUrl: string | undefined;
|
||||
|
||||
// Register songInfoCallback
|
||||
registerCallback((songInfo) => {
|
||||
registerCallback((songInfo, event) => {
|
||||
if (event === SongInfoEvent.TimeChanged) {
|
||||
currentSeconds = songInfo.elapsedSeconds ?? 0;
|
||||
}
|
||||
if (!songInfo.artist && !songInfo.title) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5,7 +5,10 @@ import is from 'electron-is';
|
||||
import { notificationImage } from './utils';
|
||||
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 { BackendContext } from '@/types/contexts';
|
||||
@ -30,8 +33,9 @@ const setup = () => {
|
||||
let oldNotification: Notification;
|
||||
let currentUrl: string | undefined;
|
||||
|
||||
registerCallback((songInfo: SongInfo) => {
|
||||
registerCallback((songInfo: SongInfo, event) => {
|
||||
if (
|
||||
event !== SongInfoEvent.TimeChanged &&
|
||||
!songInfo.isPaused &&
|
||||
(songInfo.url !== currentUrl || config.unpauseNotification)
|
||||
) {
|
||||
|
||||
@ -3,6 +3,7 @@ import { BrowserWindow } from 'electron';
|
||||
import registerCallback, {
|
||||
MediaType,
|
||||
type SongInfo,
|
||||
SongInfoEvent,
|
||||
} from '@/providers/song-info';
|
||||
import { createBackend } from '@/utils';
|
||||
|
||||
@ -70,7 +71,8 @@ export const backend = createBackend<
|
||||
await this.createSessions(config, setConfig);
|
||||
this.setConfig = setConfig;
|
||||
|
||||
registerCallback((songInfo: SongInfo) => {
|
||||
registerCallback((songInfo: SongInfo, event) => {
|
||||
if (event === SongInfoEvent.TimeChanged) return;
|
||||
// Set remove the old scrobble timer
|
||||
clearTimeout(scrobbleTimer);
|
||||
if (!songInfo.isPaused) {
|
||||
|
||||
@ -1,20 +1,23 @@
|
||||
import { BrowserWindow, ipcMain } from 'electron';
|
||||
|
||||
import MprisPlayer, {
|
||||
Track,
|
||||
LoopStatus,
|
||||
type PlayBackStatus,
|
||||
type PlayerOptions,
|
||||
PLAYBACK_STATUS_STOPPED,
|
||||
PLAYBACK_STATUS_PAUSED,
|
||||
PLAYBACK_STATUS_PLAYING,
|
||||
LOOP_STATUS_NONE,
|
||||
LOOP_STATUS_PLAYLIST,
|
||||
LOOP_STATUS_TRACK,
|
||||
LoopStatus,
|
||||
PLAYBACK_STATUS_PAUSED,
|
||||
PLAYBACK_STATUS_PLAYING,
|
||||
PLAYBACK_STATUS_STOPPED,
|
||||
type PlayBackStatus,
|
||||
type PlayerOptions,
|
||||
type Position,
|
||||
Track,
|
||||
} 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 config from '@/config';
|
||||
import { LoggerPrefix } from '@/utils';
|
||||
@ -134,10 +137,6 @@ function registerMPRIS(win: BrowserWindow) {
|
||||
player.seeked(secToMicro(t));
|
||||
});
|
||||
|
||||
ipcMain.on('ytmd:time-changed', (_, t: number) => {
|
||||
player.setPosition(secToMicro(t));
|
||||
});
|
||||
|
||||
ipcMain.on('ytmd:repeat-changed', (_, mode: RepeatMode) => {
|
||||
switch (mode) {
|
||||
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) {
|
||||
const data: Track = {
|
||||
'mpris:length': secToMicro(songInfo.songDuration),
|
||||
|
||||
@ -8,7 +8,10 @@ import previousIcon from '@assets/media-icons-black/previous.png?asset&asarUnpac
|
||||
|
||||
import { createPlugin } from '@/utils';
|
||||
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 { t } from '@/i18n';
|
||||
|
||||
@ -102,11 +105,13 @@ export default createPlugin({
|
||||
]);
|
||||
};
|
||||
|
||||
registerCallback((songInfo) => {
|
||||
// Update currentsonginfo for win.on('show')
|
||||
currentSongInfo = songInfo;
|
||||
// Update thumbar
|
||||
setThumbar(songInfo);
|
||||
registerCallback((songInfo, event) => {
|
||||
if (event !== SongInfoEvent.TimeChanged) {
|
||||
// Update currentsonginfo for win.on('show')
|
||||
currentSongInfo = songInfo;
|
||||
// Update thumbar
|
||||
setThumbar(songInfo);
|
||||
}
|
||||
});
|
||||
|
||||
// Need to set thumbar again after win.show
|
||||
|
||||
@ -2,7 +2,7 @@ import { nativeImage, type NativeImage, TouchBar } from 'electron';
|
||||
|
||||
import { createPlugin } from '@/utils';
|
||||
import getSongControls from '@/providers/song-controls';
|
||||
import registerCallback from '@/providers/song-info';
|
||||
import registerCallback, { SongInfoEvent } from '@/providers/song-info';
|
||||
import { t } from '@/i18n';
|
||||
|
||||
import youtubeMusicIcon from '@assets/youtube-music.png?asset&asarUnpack';
|
||||
@ -81,7 +81,8 @@ export default createPlugin({
|
||||
controls = [previous, playPause, next, dislike, like];
|
||||
|
||||
// Register the callback
|
||||
registerCallback((songInfo) => {
|
||||
registerCallback((songInfo, event) => {
|
||||
if (event === SongInfoEvent.TimeChanged) return;
|
||||
// Song information changed, so lets update the touchBar
|
||||
|
||||
// Set the song title
|
||||
|
||||
@ -28,18 +28,6 @@ export default createPlugin({
|
||||
},
|
||||
backend: {
|
||||
liteMode: false,
|
||||
data: {
|
||||
cover: '',
|
||||
cover_url: '',
|
||||
title: '',
|
||||
artists: [] as string[],
|
||||
status: '',
|
||||
progress: 0,
|
||||
duration: 0,
|
||||
album_url: '',
|
||||
album: undefined,
|
||||
url: '',
|
||||
} as Data,
|
||||
start({ ipc }) {
|
||||
const secToMilisec = (t: number) => Math.round(Number(t) * 1e3);
|
||||
|
||||
@ -85,31 +73,24 @@ export default createPlugin({
|
||||
ipc.on('ytmd:player-api-loaded', () =>
|
||||
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) => {
|
||||
if (!songInfo.title && !songInfo.artist) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.duration = secToMilisec(songInfo.songDuration);
|
||||
this.data.progress = secToMilisec(songInfo.elapsedSeconds ?? 0);
|
||||
this.data.cover = songInfo.imageSrc ?? '';
|
||||
this.data.cover_url = songInfo.imageSrc ?? '';
|
||||
this.data.album_url = songInfo.imageSrc ?? '';
|
||||
this.data.title = songInfo.title;
|
||||
this.data.artists = [songInfo.artist];
|
||||
this.data.status = songInfo.isPaused ? 'stopped' : 'playing';
|
||||
this.data.album = songInfo.album;
|
||||
this.data.url = songInfo.url ?? '';
|
||||
post(this.data);
|
||||
post({
|
||||
duration: secToMilisec(songInfo.songDuration),
|
||||
progress: secToMilisec(songInfo.elapsedSeconds ?? 0),
|
||||
cover: songInfo.imageSrc ?? '',
|
||||
cover_url: songInfo.imageSrc ?? '',
|
||||
album_url: songInfo.imageSrc ?? '',
|
||||
title: songInfo.title,
|
||||
artists: [songInfo.artist],
|
||||
status: songInfo.isPaused ? 'stopped' : 'playing',
|
||||
album: songInfo.album,
|
||||
url: songInfo.url ?? '',
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@ -149,8 +149,17 @@ const handleData = async (
|
||||
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
|
||||
export type SongInfoCallback = (songInfo: SongInfo, event?: string) => void;
|
||||
export type SongInfoCallback = (
|
||||
songInfo: SongInfo,
|
||||
event: SongInfoEvent,
|
||||
) => void;
|
||||
const callbacks: Set<SongInfoCallback> = new Set();
|
||||
|
||||
// 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) {
|
||||
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) {
|
||||
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 = [
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Menu, screen, nativeImage, Tray } from 'electron';
|
||||
import { Menu, nativeImage, screen, Tray } from 'electron';
|
||||
import is from 'electron-is';
|
||||
|
||||
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 { 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 { t } from '@/i18n';
|
||||
@ -125,7 +125,9 @@ export const setUpTray = (app: Electron.App, win: Electron.BrowserWindow) => {
|
||||
const trayMenu = Menu.buildFromTemplate(template);
|
||||
tray.setContextMenu(trayMenu);
|
||||
|
||||
registerCallback((songInfo) => {
|
||||
registerCallback((songInfo, event) => {
|
||||
if (event === SongInfoEvent.TimeChanged) return;
|
||||
|
||||
if (tray) {
|
||||
if (typeof songInfo.isPaused === 'undefined') {
|
||||
tray.setImage(defaultTrayIcon);
|
||||
|
||||
Reference in New Issue
Block a user