From 61c1476823707d04c47595814ca4db221774f0b3 Mon Sep 17 00:00:00 2001 From: lyswhut Date: Mon, 21 Feb 2022 16:05:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=BB=E5=8A=A1=E6=A0=8F?= =?UTF-8?q?=E7=BC=A9=E7=95=A5=E5=9B=BE=E5=B7=A5=E5=85=B7=E6=A0=8F=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- publish/changeLog.md | 1 + src/common/ipcNames.js | 3 + src/main/events/index.js | 2 + src/main/modules/index.js | 1 - src/main/modules/taskbar/event/event.js | 11 ++ src/main/modules/taskbar/event/name.js | 3 + src/main/modules/taskbar/index.js | 92 +++++++++++++ src/main/rendererEvents/index.js | 4 +- src/main/rendererEvents/taskbar.js | 18 +++ src/renderer/core/useApp/index.js | 3 +- src/renderer/core/useApp/usePlayer/index.js | 6 + .../core/useApp/usePlayer/usePlayer.js | 3 +- .../core/useApp/usePlayer/useTaskbar.js | 125 ++++++++++++++++++ src/renderer/event/names.js | 10 +- src/renderer/utils/tools.js | 13 ++ src/static/images/taskbar/collect.png | Bin 0 -> 1032 bytes src/static/images/taskbar/collected.png | Bin 0 -> 781 bytes src/static/images/taskbar/lrc-off.png | Bin 0 -> 943 bytes src/static/images/taskbar/lrc.png | Bin 0 -> 731 bytes src/static/images/taskbar/next.png | Bin 0 -> 729 bytes src/static/images/taskbar/pause.png | Bin 0 -> 491 bytes src/static/images/taskbar/play.png | Bin 0 -> 675 bytes src/static/images/taskbar/prev.png | Bin 0 -> 762 bytes 23 files changed, 287 insertions(+), 8 deletions(-) create mode 100644 src/main/modules/taskbar/event/event.js create mode 100644 src/main/modules/taskbar/event/name.js create mode 100644 src/main/modules/taskbar/index.js create mode 100644 src/main/rendererEvents/taskbar.js create mode 100644 src/renderer/core/useApp/usePlayer/useTaskbar.js create mode 100644 src/static/images/taskbar/collect.png create mode 100644 src/static/images/taskbar/collected.png create mode 100644 src/static/images/taskbar/lrc-off.png create mode 100644 src/static/images/taskbar/lrc.png create mode 100644 src/static/images/taskbar/next.png create mode 100644 src/static/images/taskbar/pause.png create mode 100644 src/static/images/taskbar/play.png create mode 100644 src/static/images/taskbar/prev.png diff --git a/publish/changeLog.md b/publish/changeLog.md index b9f0a1d9..b1bd03c6 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -3,6 +3,7 @@ - 新增“双击列表里的歌曲时自动切换到当前列表播放”设置,此功能仅对歌单、排行榜有效,默认关闭 - 新增打开收藏的在线列表的对应平台详情页功能,可以在我的列表-列表右键菜单中使用 - 新增定时暂停播放功能,由于此功能大多数人可能不常用,所以将其放在设置-基本设置中 +- 新增任务栏缩略图工具栏控制按钮(此功能仅在Windows平台可用),按钮分别为收藏/取消收藏(将歌曲添加到“我的收藏”列表)、上一曲、播放/暂停、下一曲 - 新增设置-基本设置-软件字体设置,此设置可用于设置主界面的字体(已知的问题:Windows 7 下可能会出现字体列表为空的情况,这是当前系统的 Powershell 版本小于5.1导致的,请自行尝试看常见解决) - 新增Scheme URL对音乐搜索的调用支持,详情看常见问题-Scheme URL支持 - 新增Scheme URL以url传参的方式调用,详情看常见问题-Scheme URL支持 diff --git a/src/common/ipcNames.js b/src/common/ipcNames.js index afd62c7a..e5827046 100644 --- a/src/common/ipcNames.js +++ b/src/common/ipcNames.js @@ -86,6 +86,9 @@ const names = { sync_action_list: 'sync_action_list', sync_list: 'sync_list', + taskbar_set_thumbar_buttons: 'taskbar_set_thumbar_buttons', + taskbar_set_thumbnail_clip: 'taskbar_set_thumbnail_clip', + taskbar_on_thumbar_button_click: 'taskbar_on_thumbar_button_click', }, winLyric: { close: 'close', diff --git a/src/main/events/index.js b/src/main/events/index.js index 9b543697..96352005 100644 --- a/src/main/events/index.js +++ b/src/main/events/index.js @@ -6,6 +6,7 @@ const Tray = require('./Tray') const WinLyric = require('./WinLyric') const HotKey = require('./HotKey') +const { Event: TaskBar } = require('../modules/taskbar') const { Event: UserApi } = require('../modules/userApi') const { Event: Sync } = require('../modules/sync') @@ -15,5 +16,6 @@ if (!global.lx_event.tray) global.lx_event.tray = new Tray() if (!global.lx_event.winLyric) global.lx_event.winLyric = new WinLyric() if (!global.lx_event.hotKey) global.lx_event.hotKey = new HotKey() +if (!global.lx_event.taskbar) global.lx_event.taskbar = new TaskBar() if (!global.lx_event.userApi) global.lx_event.userApi = new UserApi() if (!global.lx_event.sync) global.lx_event.sync = new Sync() diff --git a/src/main/modules/index.js b/src/main/modules/index.js index b73a9c7e..362fe83d 100644 --- a/src/main/modules/index.js +++ b/src/main/modules/index.js @@ -2,4 +2,3 @@ require('./appMenu') require('./winLyric') require('./tray') require('./hotKey') -require('./userApi') diff --git a/src/main/modules/taskbar/event/event.js b/src/main/modules/taskbar/event/event.js new file mode 100644 index 00000000..fbf26aa8 --- /dev/null +++ b/src/main/modules/taskbar/event/event.js @@ -0,0 +1,11 @@ +const { EventEmitter } = require('events') +const TASKBAR_EVENT_NAME = require('./name') + +class TaskBar extends EventEmitter { + thumbarButtonClick(type) { + this.emit(TASKBAR_EVENT_NAME.thumbarButtonClick, type) + } +} + +module.exports = TaskBar + diff --git a/src/main/modules/taskbar/event/name.js b/src/main/modules/taskbar/event/name.js new file mode 100644 index 00000000..822f5456 --- /dev/null +++ b/src/main/modules/taskbar/event/name.js @@ -0,0 +1,3 @@ +module.exports = { + thumbarButtonClick: 'thumbarButtonClick', +} diff --git a/src/main/modules/taskbar/index.js b/src/main/modules/taskbar/index.js new file mode 100644 index 00000000..9c8bcf65 --- /dev/null +++ b/src/main/modules/taskbar/index.js @@ -0,0 +1,92 @@ +const path = require('path') +const Event = require('./event/event') +const eventNames = require('./event/name') + +exports.Event = Event +exports.eventNames = eventNames + +exports.setThumbnailClip = (region = { x: 0, y: 0, width: 0, height: 0 }) => { + if (!global.modules.mainWindow) return + return global.modules.mainWindow.setThumbnailClip(region) +} + +const getIconPath = name => { + return path.join(global.__static, 'images/taskbar', name + '.png') +} + +const buttonsFlags = { + empty: false, + collect: false, + play: false, + next: true, + prev: true, +} +const createButtons = ({ empty = false, collect = false, play = false, next = true, prev = true }) => { + const buttons = [ + collect + ? { + icon: getIconPath('collected'), + click() { + global.lx_event.taskbar.thumbarButtonClick('unCollect') + }, + tooltip: '取消收藏', + flags: ['nobackground'], + } + : { + icon: getIconPath('collect'), + click() { + global.lx_event.taskbar.thumbarButtonClick('collect') + }, + tooltip: '收藏', + flags: ['nobackground'], + }, + { + icon: getIconPath('prev'), + click() { + global.lx_event.taskbar.thumbarButtonClick('prev') + }, + tooltip: '上一曲', + flags: prev ? ['nobackground'] : ['nobackground', 'disabled'], + }, + play + ? { + icon: getIconPath('pause'), + click() { + global.lx_event.taskbar.thumbarButtonClick('pause') + }, + tooltip: '暂停', + flags: ['nobackground'], + } + : { + icon: getIconPath('play'), + click() { + global.lx_event.taskbar.thumbarButtonClick('play') + }, + tooltip: '播放', + flags: ['nobackground'], + }, + { + icon: getIconPath('next'), + click() { + global.lx_event.taskbar.thumbarButtonClick('next') + }, + tooltip: '下一曲', + flags: next ? ['nobackground'] : ['nobackground', 'disabled'], + }, + ] + if (empty) { + for (const button of buttons) { + button.flags = ['nobackground', 'disabled'] + } + } + return buttons +} +exports.setThumbarButtons = ({ empty, collect, play, next, prev } = buttonsFlags) => { + if (!global.modules.mainWindow) return + buttonsFlags.empty = empty + buttonsFlags.collect = collect + buttonsFlags.play = play + buttonsFlags.next = next + buttonsFlags.prev = prev + global.modules.mainWindow.setThumbarButtons(createButtons(buttonsFlags)) +} diff --git a/src/main/rendererEvents/index.js b/src/main/rendererEvents/index.js index ef0960f1..c5f870e4 100644 --- a/src/main/rendererEvents/index.js +++ b/src/main/rendererEvents/index.js @@ -1,4 +1,4 @@ - +const { isWin } = require('@common/utils') // require('./request') // require('./appName') require('./progressBar') @@ -23,6 +23,8 @@ require('./systemFonts') require('./wait') require('./openDevtools') +if (isWin) require('./taskbar') + // require('./kw_decodeLyric') require('./userApi') diff --git a/src/main/rendererEvents/taskbar.js b/src/main/rendererEvents/taskbar.js new file mode 100644 index 00000000..c128a3ec --- /dev/null +++ b/src/main/rendererEvents/taskbar.js @@ -0,0 +1,18 @@ +const { mainSend, mainOn, NAMES: { mainWindow: ipcMainWindowNames }, mainHandle } = require('@common/ipc') +const { setThumbnailClip, setThumbarButtons, eventNames } = require('../modules/taskbar') +const { mainWindow: MAIN_WINDOW_EVENT_NAME } = require('@main/events/_name') + +const handleThumbarButtonClick = action => { + mainSend(global.modules.mainWindow, ipcMainWindowNames.taskbar_on_thumbar_button_click, action) +} + +global.lx_event.taskbar.on(eventNames.thumbarButtonClick, handleThumbarButtonClick) +global.lx_event.mainWindow.on(MAIN_WINDOW_EVENT_NAME.show, setThumbarButtons) + +mainHandle(ipcMainWindowNames.taskbar_set_thumbnail_clip, (event, clip) => { + return setThumbnailClip(clip) +}) + +mainOn(ipcMainWindowNames.taskbar_set_thumbar_buttons, (event, buttons) => { + setThumbarButtons(buttons) +}) diff --git a/src/renderer/core/useApp/index.js b/src/renderer/core/useApp/index.js index 59f2f431..89eb5f69 100644 --- a/src/renderer/core/useApp/index.js +++ b/src/renderer/core/useApp/index.js @@ -40,7 +40,7 @@ export default () => { isProd, isLinux, }) - usePlayer({ setting }) + const initPlayer = usePlayer({ setting }) const handleEnvParams = useHandleEnvParams() const initData = useDataInit({ setting, @@ -60,6 +60,7 @@ export default () => { // 初始化我的列表、下载列表等数据 initData().then(() => { + initPlayer() handleEnvParams(envParams) // 处理传入的启动参数 initDeepLink(envParams) }) diff --git a/src/renderer/core/useApp/usePlayer/index.js b/src/renderer/core/useApp/usePlayer/index.js index 479df21c..a8a2b9bd 100644 --- a/src/renderer/core/useApp/usePlayer/index.js +++ b/src/renderer/core/useApp/usePlayer/index.js @@ -4,6 +4,7 @@ import { import useMediaDevice from './useMediaDevice' import usePlayerEvent from './usePlayerEvent' import usePlayer from './usePlayer' +import useTaskbar from './useTaskbar' import { init as initPlayTimeoutStop } from '@renderer/utils/timeoutStop' export default ({ setting }) => { @@ -12,7 +13,12 @@ export default ({ setting }) => { usePlayerEvent() useMediaDevice({ setting }) // 初始化音频驱动输出设置 usePlayer({ setting }) + const initTaskbar = useTaskbar() initPlayTimeoutStop() + + return () => { + initTaskbar() + } } diff --git a/src/renderer/core/useApp/usePlayer/usePlayer.js b/src/renderer/core/useApp/usePlayer/usePlayer.js index 8c436c2c..3a9d7520 100644 --- a/src/renderer/core/useApp/usePlayer/usePlayer.js +++ b/src/renderer/core/useApp/usePlayer/usePlayer.js @@ -195,11 +195,9 @@ export default ({ setting }) => { const setPlayStatus = () => { setPlay(true) - setTitle(`${musicInfo.name} - ${musicInfo.singer}`) } const setPauseStatus = () => { setPlay(false) - setTitle() if (global.isPlayedStop) handlePause() } const setStopStatus = () => { @@ -282,6 +280,7 @@ export default ({ setting }) => { name: musicInfo.name, album: musicInfo.albumName, }) + setTitle(`${musicInfo.name} - ${musicInfo.singer}`) } const handelStop = () => { diff --git a/src/renderer/core/useApp/usePlayer/useTaskbar.js b/src/renderer/core/useApp/usePlayer/useTaskbar.js new file mode 100644 index 00000000..6df4e1bf --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/useTaskbar.js @@ -0,0 +1,125 @@ +import { onBeforeUnmount, useCommit } from '@renderer/utils/vueTools' +import { player as eventPlayerNames, taskbar as eventTaskbarNames } from '@renderer/event/names' +import { onTaskbarThumbarClick, setTaskbarThumbnailClip, setTaskbarThumbarButtons } from '@renderer/utils/tools' +// import store from '@renderer/store' + +import { loveList, getList } from '@renderer/core/share/list' +import { playMusicInfo } from '@renderer/core/share/player' + +export default () => { + const listAdd = useCommit('list', 'listAdd') + const listRemove = useCommit('list', 'listRemove') + // const setVisibleDesktopLyric = useCommit('setVisibleDesktopLyric') + // const setLockDesktopLyric = useCommit('setLockDesktopLyric') + + const buttons = { + empty: false, + collect: false, + play: false, + prev: true, + next: true, + lrc: false, + lockLrc: false, + } + const setButtons = () => { + setTaskbarThumbarButtons(buttons) + } + const updateCollectStatus = () => { + let status = getList(loveList.id).some(musicInfo => playMusicInfo.musicInfo.songmid == musicInfo.songmid) + if (buttons.collect == status) return false + buttons.collect = status + return true + } + + const handlePlay = () => { + if (buttons.empty) buttons.empty = false + buttons.play = true + setButtons() + } + const handlePause = () => { + if (buttons.empty) buttons.empty = false + buttons.play = false + setButtons() + } + const handleStop = () => { + if (playMusicInfo.musicInfo != null) return + buttons.empty = true + setButtons() + } + const handleSetPlayInfo = () => { + if (!updateCollectStatus()) return + setButtons() + } + const handleSetTaskbarThumbnailClip = (clip) => { + setTaskbarThumbnailClip(clip) + } + // const updateSetting = () => { + // const setting = store.getters.setting + // buttons.lrc = setting.desktopLyric.enable + // buttons.lockLrc = setting.desktopLyric.isLock + // setButtons() + // } + const rTaskbarThumbarClick = onTaskbarThumbarClick((event, action) => { + switch (action) { + case 'play': + window.eventHub.emit(eventPlayerNames.setPlay) + break + case 'pause': + window.eventHub.emit(eventPlayerNames.setPause) + break + case 'prev': + window.eventHub.emit(eventPlayerNames.setPlayPrev) + break + case 'next': + window.eventHub.emit(eventPlayerNames.setPlayNext) + break + case 'collect': + if (!playMusicInfo.musicInfo) return + listAdd({ id: loveList.id, musicInfo: playMusicInfo.musicInfo }) + if (updateCollectStatus()) setButtons() + break + case 'unCollect': + if (!playMusicInfo.musicInfo) return + listRemove({ listId: loveList.id, id: playMusicInfo.musicInfo.songmid }) + if (updateCollectStatus()) setButtons() + break + // case 'lrc': + // setVisibleDesktopLyric(true) + // updateSetting() + // break + // case 'unLrc': + // setVisibleDesktopLyric(false) + // updateSetting() + // break + // case 'lockLrc': + // setLockDesktopLyric(true) + // updateSetting() + // break + // case 'unlockLrc': + // setLockDesktopLyric(false) + // updateSetting() + // break + } + }) + window.eventHub.on(eventPlayerNames.play, handlePlay) + window.eventHub.on(eventPlayerNames.pause, handlePause) + window.eventHub.on(eventPlayerNames.stop, handleStop) + window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + window.eventHub.on(eventTaskbarNames.setTaskbarThumbnailClip, handleSetTaskbarThumbnailClip) + + onBeforeUnmount(() => { + rTaskbarThumbarClick() + window.eventHub.off(eventPlayerNames.play, handlePlay) + window.eventHub.off(eventPlayerNames.pause, handlePause) + window.eventHub.off(eventPlayerNames.stop, handleStop) + window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + window.eventHub.off(eventTaskbarNames.setTaskbarThumbnailClip, handleSetTaskbarThumbnailClip) + }) + + return () => { + // const setting = store.getters.setting + // buttons.lrc = setting.desktopLyric.enable + // buttons.lockLrc = setting.desktopLyric.isLock + setButtons() + } +} diff --git a/src/renderer/event/names.js b/src/renderer/event/names.js index 651f4d47..36f701fb 100644 --- a/src/renderer/event/names.js +++ b/src/renderer/event/names.js @@ -27,9 +27,9 @@ const names = { playMusic: 'playMusic', - setPlayInfo: 'setPlayInfo', - updatePic: 'updatePic', - updateLyric: 'updateLyric', + setPlayInfo: 'setPlayInfo', // 设置播放信息 + updatePic: 'updatePic', // 更新图片事件 + updateLyric: 'updateLyric', // 更新歌词事件 activeTransition: 'activeTransition', // 激活进度条动画事件 playedStop: 'playedStop', // 定时停止事件 @@ -66,6 +66,9 @@ const names = { send_sync_list: 'send_sync_list', handle_sync_list: 'handle_sync_list', }, + taskbar: { + setTaskbarThumbnailClip: 'setTaskbarThumbnailClip', + }, } for (const item of Object.keys(names)) { @@ -80,3 +83,4 @@ export const player = names.player export const list = names.list export const download = names.download export const sync = names.sync +export const taskbar = names.taskbar diff --git a/src/renderer/utils/tools.js b/src/renderer/utils/tools.js index 218e6e2d..f7c58782 100644 --- a/src/renderer/utils/tools.js +++ b/src/renderer/utils/tools.js @@ -310,3 +310,16 @@ export const hotKeySetConfig = (config) => { export const hotKeyGetStatus = () => { return rendererInvoke(NAMES.hotKey.status) } + +export const onTaskbarThumbarClick = callback => { + rendererOn(NAMES.mainWindow.taskbar_on_thumbar_button_click, callback) + return () => { + rendererOff(callback) + } +} +export const setTaskbarThumbnailClip = (clip) => { + return rendererInvoke(NAMES.mainWindow.taskbar_set_thumbnail_clip, clip) +} +export const setTaskbarThumbarButtons = (buttons) => { + rendererSend(NAMES.mainWindow.taskbar_set_thumbar_buttons, buttons) +} diff --git a/src/static/images/taskbar/collect.png b/src/static/images/taskbar/collect.png new file mode 100644 index 0000000000000000000000000000000000000000..389b46832fa62c1859f0bdfb64e777a02f8ae28b GIT binary patch literal 1032 zcmV+j1o!)iP)CsBjl%l+Wn1M&lqP*+YgGI-n2XODKXQ1fdc^f=LfSAqD0`FV>R} z5{ZW4TV^7ncnNQ0YV_doo*wa1c=VScbHQdZ=h|zpHP3NF=ZEpI=N|JP|G)O!Yt1

;!%U zZsszXfk+IH^r@sfDuymeALj67u|m>61v&3ZS}*B|7+xFcTOM$^Qyi8Tt<_s^fe_WVt;pX+vb*T^AvICuwwqbBLsMIdhl$OXx{S z7XpcYNE%h(k7@Co%ddxXk|q}9946^NfbVvuD@$A#NIE8IV8KFJSpM%h22M*F-XN#N zo&eXz%z*t|mjelHB??&#mUJ?}_ot+xB{^-W-Sqfud5yu{o_u^!vQQRwmH$d`i%o$q##bz6G1A??Z&bMQ{zd8XbZ%<_+PvG;Uf^ubZLcV<051ct0XGuxT?2mg z#>Sh0XT8Cb35sQ~ebUi{d?&omBh0{HZ!pEx>M8izTFpSWHyC>YHUoTZ-DcprH#oY9 z4fxt_nSnFjpzUv3dQns(c*V=@Y;hZ@T8wpvjb=kT@S>;bP>VZB`B>eA211&4WDkkm zfrr`B1P`B7(Gk#hOf{Ri`GEw(>?RODEpn`VPu_`aXT-!?k5K#!N%sSq_GLM>$zvs5 zkC<5a2*khX_EJ8~(A1Cti}r{C!?Io!zc5F~oZ8&bBybmNTFSkLcdiI|5JqXuEz<25;u zKPJ*ZGkjfEM->AWA4ddP*KB4@UR%yS>&q${sv4lqqkoEP3x;--hLu(8r!ZjgZbaZU z_r@j!d(~9Ac9EBSMKKH5RWP>$*p{MbN&~nzE7tydEGK9F!?SE6q&8rf02~B{dgJ$8 zXW?MVVm;CSISov5-41(~{I6SDlTwTCLm&S403HC8xfR)Ti!(C-0000WdP)+)@?REbAT>zV~o4fFsO z0&{_Jz+f%=3bX(ZflI&@;HROQ8eq1zI{_F9^a8#Et-w>@DsTb#T(qGg2D$;8fek7+ ziZ);?a5&<=RlqKwpFx|Qz!7b?J>Lzf0u|>0pMY(K;)TAzA>acrJ;$zJ)r@32Am{k2=xYzmLXU=PcOURpw3)>46N7b(HG?X+!Pq- zqf?u2)C0rBfGh+9Jv1OgvrY`uCP0iWwPIkbpW3qw7Xw2QAjFmdsTeD666l`N4U?~Q zih++F8)z2;FB2fdmKHH^*H7(P9;Ean)-1#Vo`Xg=gUf5(;aGC za&%`OsH9~Y!xswWh2+pYNwW%-<_j=cN5@|jG9Ww$M(A&)cn6G>6}M3QVg}G*Bt7xC z0tQ|I6M;9yZi+I1nivmUcc(3qN5Cje9v8*LK)?)L@XF0iU<&ZVBu*&?&;RP^8vmNp+`I`&3c3?V@}WW$hQ2{M=|;)A9LR+^Z9@{0}Y;Bt4)y?2gx#(V92usLh5z0ThI+k5SG z);<&{P@q78|20Z!?lRpLCGr7t1PXM=^ z?hbGgxKRD_@jUQ4FwWk;2pk5^4JHa*wUzElS&8r5PJCjzW=X#*8>b{Ks<6T{B^@i% z$KI-!paOwVzI7dcWV#Dhzb)yyq#Kg%Mw9qBqrbMu?xvAGE9s8u{>Ve1v&@Peb^~sc zG+^>NYky4A1WC&!U30dxqJCyalkbo;&b3dHv|7@US`&y#lw0Kmu6<3^cGR`KT4t%P>HLG;A+5xMCZ-6Ui^R%;l6L`tw4=hMz;Z~adK?wxR1rE9T3n?}sn%p+|AE#ez z&l|v(c?Pcqfq=JwzfE2UY>nDq1SXk$(&=Bb=Rx37o`Gvc;2Ln<)!zZOnB3$D{RQkY zc?R%|$pd)@9+~&W2&I&d;=90eR;HP*-L3RQXL~X4z_lWfg*QweTx11s+S!eE^3A*h z)XIfCLi({5_`&H0qRC?iJ3(rQtOi3t-~iAGyaIe}x|P7T$S$0R1QW@Lp&;N9;(V{w z+anU;EF_WZ*?J`q@S&3fi5(_i29BHDn%9kngFqk9@9J;6o#?UWRA5P-&MOfZ#5Jfa zzK9ybPILj?K$2x{G9p<>y$n<$FxH+eQ3+qw$v|_J1a<$f+p)2uIX2!D^e3^GQ^+~+6Lhir15-J^fbc;^@3 zW%Kj%z>nr0ArTC&)B#KZW?4cw+WI|VlaF068^ip!VYhRg&4c|bk&1`#%Ve)3v8xdY zgtEtx_^J^J#3Xtojc7Ci*y<;kUx~m_-v{zwDKNpJ)c!i0Ux5Mz3Oq6Z{sp}MlV;VM REf4?z002ovPDHLkV1lg6yYc`4 literal 0 HcmV?d00001 diff --git a/src/static/images/taskbar/lrc.png b/src/static/images/taskbar/lrc.png new file mode 100644 index 0000000000000000000000000000000000000000..4bcedb5999c18c18776527273ddbcba7846bb279 GIT binary patch literal 731 zcmV<10wn#3P)sP2d+5SX$E!xi-D=Y58xH>5I7YC!F{KJ5|M%o zaZp5Xcyb~#DCK!9A`NA1^(55)Wb12Hk}pmk50xvbegIqp>VO=u2ABmb0Q!K1mijk< z6{B2>iCR6z+wyBk8~Z@LqM!NN)qisos;ua8?e~>4UEEi1qq@ z0`#XboRtGxfEv}e^gRh!qWX4P!%0yn4bc**2gYk$-=}F;HA10hMif~FMw0^_#>O-W zL+EDX+YF0e(l(aW650!_(`XVg#?aP30vCqEfuq`?MH(7Ib^S6e70FKAXnbD{*ZDYf|guVfXRELLDi;?#lc&oa}QrC$r_P`n7tLk;0W+i^&4O^upj8MnC%5N=;obmEURU}O{3g%;5_g`FG_QP@K3Zp2!a}m zUAqGbmy3wi^6!Pl_B$Outwz1hSl%iT`I*pG-qzPrCvl&KzCwxoDxIMZ`u$65P)vlQ1#N%tj9Nt%?@FKL;L+Z0Kil7>~!XGv=l6yNQNu4UZB@(A>01Xr1n)Eik~ zpuza}@(6rrkf6%xhysnq|5#h^{0xWA=8eWRpg+g=d|3c2ZV5jwrH)9s}!rZ(Ow$C2$qk5BzF1Q4uY40=N?@SxSLv*YB@F zCQT(^TaP<|$Y%mLe}+ZdyG5(r!X4#gZGW(yqv9!1PwA%?&Num^Y-A$J|21zrQY z-HMZhkaNJ^nQcv3JOKsH0LKF5@*+eF^|+0yPFb3=bMgkU($6L}0SuOM< zN1XlQYa$eH`$AIAIbbkTfRguA~)p$8CV$TzgePauaW+00000 LNkvXXu0mjf+KoTs literal 0 HcmV?d00001 diff --git a/src/static/images/taskbar/pause.png b/src/static/images/taskbar/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..e8b8c988682676e078baa990467f8eff510c8fc6 GIT binary patch literal 491 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FmCd6aSX|5d^^kDo7qvoJ^r6Z zz(()XL}iUdQ|BKJ+7KYJCFsa0e?_yTRK+F*v6U*l3zoWW*>FXvwWF2e+M*;0%Nvs) z=be0eS4=ojK4;?Iv-#gzy-kA^TRT+D#TsH0q&m+1oAFmFfOTF2^8=f?k533~VD@oa z+8C!~zv?>cx5jRZbwMX%EVUD^E!}ZhaN@%w<)6b!zHM{(H?<=q*gh}5v8?gKjM+>~ zbA^89hBqD+dT1EUH~s#X6Y-w+w`UbblvdZSU1L2b^4p1c@BAAq&uqBPFrBqK@7^HU z|7YcwHs1vg0~0i!|28dNrZY2VcEe%&)PfD1f3%w#%p4tND=hF8xS+<7AaL$=`x^NB|M+b4eQuXsH#&2HVray1YH9i+?-uosn;f%?V_(tUiAKo82St$KL Z{MYmUJGW0fvLur literal 0 HcmV?d00001 diff --git a/src/static/images/taskbar/play.png b/src/static/images/taskbar/play.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2ec48c86c7615d883f83a55fb9f17d5385a108 GIT binary patch literal 675 zcmV;U0$lxxP)sM=m0=ASe0poE4F&F$&x*D4={1kxj7)jFk|u+!K*S zp#&y5=e-rW=my{oaI!+(+PDJ0YXlqwUIVK`3e-hEzmI`Cz|0f{EL(t4V7I6EfAJ-t zOam?gBfv^u5Ai8rSq3}>&I41L4&H`C^LX-!0B!mQxLEm2G+ZUDG4aY+=A2u=DG!`2~3z2 zN=@LCTacQ-kXw+Fzy;uzTbP2tH}exT(CuRE0y>nK2VBp(p{Lz^3ZsL$J;00qRfw$x zegFr7rA3k_vKG)Enj^qinMyGTd;sM9D zHXOSvu0WlMR$b)l2ZkHYF&DQF{HhS+K5*R3Hn)!30@}TI`^iV(hFNtC%0Js}fIl88X>yQ^OAY`4002ov JPDHLkV1nL{BX0lz literal 0 HcmV?d00001 diff --git a/src/static/images/taskbar/prev.png b/src/static/images/taskbar/prev.png new file mode 100644 index 0000000000000000000000000000000000000000..6739fdb8728288e72605111b5afc1b506298e283 GIT binary patch literal 762 zcmVJTm-NcC zVM&Y4cOlv$6LVbBn4|$ol_+i|BrTNmR!K}s>JA`&gQO2B@n1{o$YWqA<>fl=dKTX| zP3#C4^B9=OAZhm`{$xhn2%qA5XJ-$p1S-JC0`E0Kq~Qv%El1zq@ds%JerAyTYD(@@ zECX%;JvsX)V$XCa#ul4_Cl=yUQI>%NzzERREKwN-&H(3JkrkQ5MKKS!3T$#o8p^;5 z;3m-RnmEJ|+HNMRbMXW)a2U7_%=Hz(=MWkI_Iu>7!H0o1vpMy8;7-7`ftA25V2NYi zhHyCtcA3caf%>mpG5O58vcyx+z!=a4JO(;F zXtILsn5_b9fk&+%o&pBGm<_7SlOAi>4xv>opm_?)Jdrtpt$}A|vuYXG8ki641t$Ji z151I2W+i&(Ns|?d%z>yB^4?Sv9+arPh9U;)#ieLTT@MU7)?@`2t56?sG@#}waJ7t{ z0(LsC+U0`FeIP2CtOmwC>P^7)M)usC%I-PlZ3rJb@mF9YaMlBN0zOlTgTNt={5AN_ zh5CW5z&Bq3LL_rhm0}I>!UuV%Z1kl`#YbEdhfJq`09%05E=kKc4;%sZxgsm;Kr?9m zC-~efQJE*Q+h%f)HbatTU{1z6F6*gPW8zxQ{)yOW2I7^sI3}%SPLDsp4&X$NzO&*F z(hN+u)ZKUdC-=WN4(!S3b0?FECIA2c07*qoM6N<$g7!sF@Bjb+ literal 0 HcmV?d00001