From 6432d8953424a00096364d4c1ecf06f93204efdd Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sat, 1 Nov 2025 17:24:39 +0300 Subject: [PATCH 1/8] New plugin: Taskbar Progress --- src/i18n/resources/en.json | 4 ++ src/i18n/resources/ru.json | 4 ++ src/plugins/taskbar-progress/index.ts | 67 +++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/plugins/taskbar-progress/index.ts diff --git a/src/i18n/resources/en.json b/src/i18n/resources/en.json index 4cf482aa3b..69b334f835 100644 --- a/src/i18n/resources/en.json +++ b/src/i18n/resources/en.json @@ -879,6 +879,10 @@ "description": "Control playback from your Windows taskbar", "name": "Taskbar Media Control" }, + "taskbar-progress": { + "name": "Taskbar Progress", + "description": "Shows the current track playback progress on the taskbar" + }, "touchbar": { "description": "Adds a TouchBar widget for macOS users", "name": "TouchBar" diff --git a/src/i18n/resources/ru.json b/src/i18n/resources/ru.json index a443dda1ce..6cdd90fe58 100644 --- a/src/i18n/resources/ru.json +++ b/src/i18n/resources/ru.json @@ -879,6 +879,10 @@ "description": "Управляйте воспроизведением с панели задач Windows", "name": "Управление мультимедиа на панели задач" }, + "taskbar-progress": { + "name": "Прогресс на панели задач", + "description": "Отображает прогресс воспроизведения текущего трека на панели задач" + }, "touchbar": { "description": "Добавляет виджет тачбара для пользователей macOS", "name": "Тачбар" diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts new file mode 100644 index 0000000000..86c0d14c31 --- /dev/null +++ b/src/plugins/taskbar-progress/index.ts @@ -0,0 +1,67 @@ +import { createPlugin } from '@/utils'; +import { registerCallback, type SongInfo } from '@/providers/song-info'; +import { t } from '@/i18n'; + +export default createPlugin({ + name: () => t('plugins.taskbar-progress.name'), + description: () => t('plugins.taskbar-progress.description'), + restartNeeded: true, + config: { enabled: false }, + + async backend({ window }) { + let lastSongInfo: SongInfo | null = null; + let progressInterval: ReturnType | null = null; + + const updateProgressBar = (songInfo: SongInfo) => { + if (!songInfo?.title || typeof songInfo.elapsedSeconds !== 'number' || !songInfo.songDuration) { + return; + } + + if (!lastSongInfo || + songInfo.title !== lastSongInfo.title || + songInfo.elapsedSeconds !== lastSongInfo.elapsedSeconds || + songInfo.isPaused !== lastSongInfo.isPaused) { + lastSongInfo = songInfo; + } + + const progress = songInfo.elapsedSeconds / songInfo.songDuration; + window.setProgressBar(progress, { mode: songInfo.isPaused ? 'paused' : 'normal' }); + }; + + const startProgressInterval = (songInfo: SongInfo) => { + stopProgressInterval(); + if (!songInfo.isPaused) { + progressInterval = setInterval(() => { + if (lastSongInfo && !lastSongInfo.isPaused && typeof lastSongInfo.elapsedSeconds === 'number') { + updateProgressBar({ + ...lastSongInfo, + elapsedSeconds: lastSongInfo.elapsedSeconds + 1 + }); + } + }, 1000); + } + }; + + const stopProgressInterval = () => { + if (progressInterval) { + clearInterval(progressInterval); + progressInterval = null; + } + }; + + registerCallback((songInfo) => { + if (!songInfo?.title) return; + updateProgressBar(songInfo); + if (songInfo.isPaused) { + stopProgressInterval(); + } else { + startProgressInterval(songInfo); + } + }); + + return () => { + stopProgressInterval(); + window.setProgressBar(-1); + }; + } +}); \ No newline at end of file From 9d82fb94d79d913c03278392bd437a7a6c9e2b33 Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sat, 1 Nov 2025 17:43:30 +0300 Subject: [PATCH 2/8] Taskbar Progress: review fixes --- src/plugins/taskbar-progress/index.ts | 34 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts index 86c0d14c31..fc6b255485 100644 --- a/src/plugins/taskbar-progress/index.ts +++ b/src/plugins/taskbar-progress/index.ts @@ -8,34 +8,46 @@ export default createPlugin({ restartNeeded: true, config: { enabled: false }, - async backend({ window }) { + backend({ window }) { let lastSongInfo: SongInfo | null = null; let progressInterval: ReturnType | null = null; const updateProgressBar = (songInfo: SongInfo) => { - if (!songInfo?.title || typeof songInfo.elapsedSeconds !== 'number' || !songInfo.songDuration) { + if ( + !songInfo?.title || + typeof songInfo.elapsedSeconds !== 'number' || + !songInfo.songDuration + ) { return; } - if (!lastSongInfo || - songInfo.title !== lastSongInfo.title || - songInfo.elapsedSeconds !== lastSongInfo.elapsedSeconds || - songInfo.isPaused !== lastSongInfo.isPaused) { + if ( + !lastSongInfo || + songInfo.title !== lastSongInfo.title || + songInfo.elapsedSeconds !== lastSongInfo.elapsedSeconds || + songInfo.isPaused !== lastSongInfo.isPaused + ) { lastSongInfo = songInfo; } const progress = songInfo.elapsedSeconds / songInfo.songDuration; - window.setProgressBar(progress, { mode: songInfo.isPaused ? 'paused' : 'normal' }); + window.setProgressBar(progress, { + mode: songInfo.isPaused ? 'paused' : 'normal', + }); }; const startProgressInterval = (songInfo: SongInfo) => { stopProgressInterval(); if (!songInfo.isPaused) { progressInterval = setInterval(() => { - if (lastSongInfo && !lastSongInfo.isPaused && typeof lastSongInfo.elapsedSeconds === 'number') { + if ( + lastSongInfo && + !lastSongInfo.isPaused && + typeof lastSongInfo.elapsedSeconds === 'number' + ) { updateProgressBar({ ...lastSongInfo, - elapsedSeconds: lastSongInfo.elapsedSeconds + 1 + elapsedSeconds: lastSongInfo.elapsedSeconds + 1, }); } }, 1000); @@ -63,5 +75,5 @@ export default createPlugin({ stopProgressInterval(); window.setProgressBar(-1); }; - } -}); \ No newline at end of file + }, +}); From 4b6c7f603bb688952ed344bd9cfe0e730d33630e Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sun, 21 Dec 2025 03:54:00 +0300 Subject: [PATCH 3/8] Updated taskbar-progress plugin --- src/plugins/taskbar-progress/index.ts | 152 ++++++++++++++++---------- src/providers/song-info.ts | 10 ++ 2 files changed, 102 insertions(+), 60 deletions(-) diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts index fc6b255485..b4191d6a03 100644 --- a/src/plugins/taskbar-progress/index.ts +++ b/src/plugins/taskbar-progress/index.ts @@ -1,79 +1,111 @@ import { createPlugin } from '@/utils'; -import { registerCallback, type SongInfo } from '@/providers/song-info'; +import { registerCallback, getCurrentSongInfo, type SongInfo } from '@/providers/song-info'; import { t } from '@/i18n'; +import { z } from 'zod'; -export default createPlugin({ - name: () => t('plugins.taskbar-progress.name'), - description: () => t('plugins.taskbar-progress.description'), - restartNeeded: true, - config: { enabled: false }, +const requiredSongInfoSchema = z.object({ + title: z.string().min(1), + elapsedSeconds: z.number().optional(), + songDuration: z.number(), + isPaused: z.boolean().optional(), +}); - backend({ window }) { - let lastSongInfo: SongInfo | null = null; - let progressInterval: ReturnType | null = null; +let lastSongInfo: SongInfo | null = null; +let progressInterval: ReturnType | null = null; +let isEnabled = false; +let intervalStart: number | null = null; - const updateProgressBar = (songInfo: SongInfo) => { - if ( - !songInfo?.title || - typeof songInfo.elapsedSeconds !== 'number' || - !songInfo.songDuration - ) { - return; - } +const stopProgressInterval = () => { + if (progressInterval) { + clearInterval(progressInterval); + progressInterval = null; + intervalStart = null; + } +}; + +const updateProgressBar = (songInfo: SongInfo, window: any) => { + const validated = requiredSongInfoSchema.safeParse(songInfo); + + if (!validated.success) { + return; + } + + const { title, elapsedSeconds, songDuration, isPaused } = validated.data; + if ( + !lastSongInfo || + title !== lastSongInfo.title || + elapsedSeconds !== lastSongInfo.elapsedSeconds || + isPaused !== lastSongInfo.isPaused + ) { + lastSongInfo = songInfo; + } + + const progress = (elapsedSeconds ?? 0) / songDuration; + window.setProgressBar(progress, { + mode: isPaused ? 'paused' : 'normal', + }); +}; + +const startProgressInterval = (songInfo: SongInfo, window: any) => { + stopProgressInterval(); + if (!songInfo.isPaused) { + intervalStart = performance.now(); + progressInterval = setInterval(() => { if ( - !lastSongInfo || - songInfo.title !== lastSongInfo.title || - songInfo.elapsedSeconds !== lastSongInfo.elapsedSeconds || - songInfo.isPaused !== lastSongInfo.isPaused + lastSongInfo && + !lastSongInfo.isPaused && + typeof lastSongInfo.elapsedSeconds === 'number' && + intervalStart !== null ) { - lastSongInfo = songInfo; + const elapsedSeconds = Math.floor( + lastSongInfo.elapsedSeconds + (performance.now() - intervalStart) / 1000, + ); + updateProgressBar( + { + ...lastSongInfo, + elapsedSeconds, + }, + window, + ); } + }, 1000); + } +}; - const progress = songInfo.elapsedSeconds / songInfo.songDuration; - window.setProgressBar(progress, { - mode: songInfo.isPaused ? 'paused' : 'normal', - }); - }; +export default createPlugin({ + name: () => t('plugins.taskbar-progress.name'), + description: () => t('plugins.taskbar-progress.description'), + restartNeeded: false, + config: { enabled: false }, - const startProgressInterval = (songInfo: SongInfo) => { - stopProgressInterval(); - if (!songInfo.isPaused) { - progressInterval = setInterval(() => { - if ( - lastSongInfo && - !lastSongInfo.isPaused && - typeof lastSongInfo.elapsedSeconds === 'number' - ) { - updateProgressBar({ - ...lastSongInfo, - elapsedSeconds: lastSongInfo.elapsedSeconds + 1, - }); - } - }, 1000); - } - }; + backend: { + start({ window }) { + isEnabled = true; - const stopProgressInterval = () => { - if (progressInterval) { - clearInterval(progressInterval); - progressInterval = null; + const currentSongInfo = getCurrentSongInfo(); + if (currentSongInfo?.title) { + updateProgressBar(currentSongInfo, window); + if (!currentSongInfo.isPaused) { + startProgressInterval(currentSongInfo, window); + } } - }; - registerCallback((songInfo) => { - if (!songInfo?.title) return; - updateProgressBar(songInfo); - if (songInfo.isPaused) { - stopProgressInterval(); - } else { - startProgressInterval(songInfo); - } - }); + registerCallback((songInfo) => { + if (!isEnabled || !songInfo?.title) return; + updateProgressBar(songInfo, window); + if (songInfo.isPaused) { + stopProgressInterval(); + } else { + startProgressInterval(songInfo, window); + } + }); + }, - return () => { + stop({ window }) { + isEnabled = false; stopProgressInterval(); window.setProgressBar(-1); - }; + }, }, }); diff --git a/src/providers/song-info.ts b/src/providers/song-info.ts index 7957287473..f5865398e9 100644 --- a/src/providers/song-info.ts +++ b/src/providers/song-info.ts @@ -183,11 +183,18 @@ export type SongInfoCallback = ( ) => void; const callbacks: Set = new Set(); +let currentSongInfo: SongInfo | null = null; + // This function will allow plugins to register callback that will be triggered when data changes export const registerCallback = (callback: SongInfoCallback) => { callbacks.add(callback); }; +// This function allows plugins to get the current song info at any time +export const getCurrentSongInfo = (): SongInfo | null => { + return currentSongInfo; +}; + const registerProvider = (win: BrowserWindow) => { const dataMutex = new Mutex(); let songInfo: SongInfo | null = null; @@ -197,6 +204,7 @@ const registerProvider = (win: BrowserWindow) => { const tempSongInfo = await dataMutex.runExclusive( async () => { songInfo = await handleData(data, win); + currentSongInfo = songInfo; return songInfo; }, ); @@ -223,6 +231,7 @@ const registerProvider = (win: BrowserWindow) => { songInfo.isPaused = isPaused; songInfo.elapsedSeconds = elapsedSeconds; + currentSongInfo = songInfo; return songInfo; }); @@ -242,6 +251,7 @@ const registerProvider = (win: BrowserWindow) => { } songInfo.elapsedSeconds = seconds; + currentSongInfo = songInfo; return songInfo; }); From c889c214bd9eb2b0f0f287be1a7b3916e937e80d Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sun, 21 Dec 2025 04:02:00 +0300 Subject: [PATCH 4/8] Lint fix --- src/plugins/taskbar-progress/index.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts index b4191d6a03..e2b53034d6 100644 --- a/src/plugins/taskbar-progress/index.ts +++ b/src/plugins/taskbar-progress/index.ts @@ -1,7 +1,13 @@ +import { z } from 'zod'; +import type { BrowserWindow } from 'electron'; import { createPlugin } from '@/utils'; -import { registerCallback, getCurrentSongInfo, type SongInfo } from '@/providers/song-info'; +import { + registerCallback, + getCurrentSongInfo, + type SongInfo, +} from '@/providers/song-info'; import { t } from '@/i18n'; -import { z } from 'zod'; + const requiredSongInfoSchema = z.object({ title: z.string().min(1), @@ -23,7 +29,7 @@ const stopProgressInterval = () => { } }; -const updateProgressBar = (songInfo: SongInfo, window: any) => { +const updateProgressBar = (songInfo: SongInfo, window: BrowserWindow) => { const validated = requiredSongInfoSchema.safeParse(songInfo); if (!validated.success) { @@ -42,12 +48,13 @@ const updateProgressBar = (songInfo: SongInfo, window: any) => { } const progress = (elapsedSeconds ?? 0) / songDuration; - window.setProgressBar(progress, { + const options: { mode: 'normal' | 'paused' } = { mode: isPaused ? 'paused' : 'normal', - }); + }; + window.setProgressBar(progress, options); }; -const startProgressInterval = (songInfo: SongInfo, window: any) => { +const startProgressInterval = (songInfo: SongInfo, window: BrowserWindow) => { stopProgressInterval(); if (!songInfo.isPaused) { intervalStart = performance.now(); @@ -59,7 +66,8 @@ const startProgressInterval = (songInfo: SongInfo, window: any) => { intervalStart !== null ) { const elapsedSeconds = Math.floor( - lastSongInfo.elapsedSeconds + (performance.now() - intervalStart) / 1000, + lastSongInfo.elapsedSeconds + + ((performance.now() - intervalStart) / 1000), ); updateProgressBar( { From 0a97693fc1675191f8539c82ecf519ed15d6a424 Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sun, 21 Dec 2025 04:07:56 +0300 Subject: [PATCH 5/8] Lint fix 2 --- src/plugins/taskbar-progress/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts index e2b53034d6..bd267440d1 100644 --- a/src/plugins/taskbar-progress/index.ts +++ b/src/plugins/taskbar-progress/index.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import type { BrowserWindow } from 'electron'; + import { createPlugin } from '@/utils'; import { registerCallback, @@ -8,6 +8,7 @@ import { } from '@/providers/song-info'; import { t } from '@/i18n'; +import type { BrowserWindow } from 'electron'; const requiredSongInfoSchema = z.object({ title: z.string().min(1), @@ -66,8 +67,8 @@ const startProgressInterval = (songInfo: SongInfo, window: BrowserWindow) => { intervalStart !== null ) { const elapsedSeconds = Math.floor( - lastSongInfo.elapsedSeconds + - ((performance.now() - intervalStart) / 1000), + lastSongInfo.elapsedSeconds + + (performance.now() - intervalStart) / 1000, ); updateProgressBar( { From da67a8856b7ee23f0b27744b142091d7b82e2b87 Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sun, 21 Dec 2025 04:11:12 +0300 Subject: [PATCH 6/8] Lint fix 3 --- src/plugins/taskbar-progress/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts index bd267440d1..5787843924 100644 --- a/src/plugins/taskbar-progress/index.ts +++ b/src/plugins/taskbar-progress/index.ts @@ -67,8 +67,8 @@ const startProgressInterval = (songInfo: SongInfo, window: BrowserWindow) => { intervalStart !== null ) { const elapsedSeconds = Math.floor( - lastSongInfo.elapsedSeconds + - (performance.now() - intervalStart) / 1000, + (lastSongInfo.elapsedSeconds + + (performance.now() - intervalStart) / 1000), ); updateProgressBar( { From c81ae427ef57749ca2a3c51eb28d212efb1670c4 Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sun, 21 Dec 2025 04:13:51 +0300 Subject: [PATCH 7/8] Lint fix 4 --- src/plugins/taskbar-progress/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts index 5787843924..bd267440d1 100644 --- a/src/plugins/taskbar-progress/index.ts +++ b/src/plugins/taskbar-progress/index.ts @@ -67,8 +67,8 @@ const startProgressInterval = (songInfo: SongInfo, window: BrowserWindow) => { intervalStart !== null ) { const elapsedSeconds = Math.floor( - (lastSongInfo.elapsedSeconds + - (performance.now() - intervalStart) / 1000), + lastSongInfo.elapsedSeconds + + (performance.now() - intervalStart) / 1000, ); updateProgressBar( { From 5d9a784b6f24e10bfe9c9ea510ffc86d1062eb06 Mon Sep 17 00:00:00 2001 From: ImMALWARE Date: Sun, 21 Dec 2025 04:17:16 +0300 Subject: [PATCH 8/8] Lint fix 5 --- src/plugins/taskbar-progress/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/taskbar-progress/index.ts b/src/plugins/taskbar-progress/index.ts index bd267440d1..3ecdff4a6b 100644 --- a/src/plugins/taskbar-progress/index.ts +++ b/src/plugins/taskbar-progress/index.ts @@ -66,9 +66,9 @@ const startProgressInterval = (songInfo: SongInfo, window: BrowserWindow) => { typeof lastSongInfo.elapsedSeconds === 'number' && intervalStart !== null ) { + const timeDelta = (performance.now() - intervalStart) / 1000; const elapsedSeconds = Math.floor( - lastSongInfo.elapsedSeconds + - (performance.now() - intervalStart) / 1000, + lastSongInfo.elapsedSeconds + timeDelta, ); updateProgressBar( {