Clean up media module logic and code. (#380)
* Organized media menu code * More consolidation
This commit is contained in:
@@ -55,7 +55,9 @@ export const module = ({
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip_text: tooltipText,
|
tooltip_text: tooltipText,
|
||||||
children: Utils.merge([showLabelBinding, useTextIcon], (showLabel, forceTextIcon): Gtk.Widget[] => {
|
children: Utils.merge(
|
||||||
|
[showLabelBinding, useTextIcon],
|
||||||
|
(showLabel: boolean, forceTextIcon: boolean): Gtk.Widget[] => {
|
||||||
const childrenArray: Gtk.Widget[] = [];
|
const childrenArray: Gtk.Widget[] = [];
|
||||||
const iconWidget = getIconWidget(forceTextIcon);
|
const iconWidget = getIconWidget(forceTextIcon);
|
||||||
|
|
||||||
@@ -73,7 +75,8 @@ export const module = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return childrenArray;
|
return childrenArray;
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
setup: hook,
|
setup: hook,
|
||||||
}),
|
}),
|
||||||
tooltip_text: tooltipText,
|
tooltip_text: tooltipText,
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ let weatherIntervalInstance: null | number = null;
|
|||||||
|
|
||||||
key.connect('changed', () => {
|
key.connect('changed', () => {
|
||||||
const fetchedKey = getWeatherKey(key.value);
|
const fetchedKey = getWeatherKey(key.value);
|
||||||
console.log('fetchedKey');
|
|
||||||
console.log(fetchedKey);
|
|
||||||
|
|
||||||
weatherApiKey.value = fetchedKey;
|
weatherApiKey.value = fetchedKey;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
import { BoxWidget } from 'lib/types/widget';
|
|
||||||
import { Mpris, MprisPlayer } from 'types/service/mpris';
|
|
||||||
|
|
||||||
const media = await Service.import('mpris');
|
|
||||||
|
|
||||||
const Bar = (getPlayerInfo: (media: Mpris) => MprisPlayer): BoxWidget => {
|
|
||||||
return Widget.Box({
|
|
||||||
class_name: 'media-indicator-current-progress-bar',
|
|
||||||
hexpand: true,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
hexpand: true,
|
|
||||||
child: Widget.Slider({
|
|
||||||
hexpand: true,
|
|
||||||
tooltip_text: '--',
|
|
||||||
class_name: 'menu-slider media progress',
|
|
||||||
draw_value: false,
|
|
||||||
on_change: ({ value }) => {
|
|
||||||
const foundPlayer = getPlayerInfo(media);
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return (foundPlayer.position = value * foundPlayer.length);
|
|
||||||
},
|
|
||||||
setup: (self) => {
|
|
||||||
const update = (): void => {
|
|
||||||
const foundPlayer = getPlayerInfo(media);
|
|
||||||
if (foundPlayer !== undefined) {
|
|
||||||
const value = foundPlayer.length ? foundPlayer.position / foundPlayer.length : 0;
|
|
||||||
self.value = value > 0 ? value : 0;
|
|
||||||
} else {
|
|
||||||
self.value = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.hook(media, update);
|
|
||||||
self.poll(1000, update);
|
|
||||||
|
|
||||||
const updateTooltip = (): void => {
|
|
||||||
const foundPlayer = getPlayerInfo(media);
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
self.tooltip_text = '00:00';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const curHour = Math.floor(foundPlayer.position / 3600);
|
|
||||||
const curMin = Math.floor((foundPlayer.position % 3600) / 60);
|
|
||||||
const curSec = Math.floor(foundPlayer.position % 60);
|
|
||||||
|
|
||||||
if (typeof foundPlayer.position === 'number' && foundPlayer.position >= 0) {
|
|
||||||
const formatTime = (time: number): string => {
|
|
||||||
return time.toString().padStart(2, '0');
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatHour = (hour: number): string => {
|
|
||||||
return hour > 0 ? formatTime(hour) + ':' : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tooltip_text = `${formatHour(curHour)}${formatTime(curMin)}:${formatTime(curSec)}`;
|
|
||||||
} else {
|
|
||||||
self.tooltip_text = `00:00`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.poll(1000, updateTooltip);
|
|
||||||
self.hook(media, updateTooltip);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Bar };
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
import { MprisPlayer } from 'types/service/mpris.js';
|
|
||||||
import icons from '../../../icons/index.js';
|
|
||||||
import { LoopStatus, PlaybackStatus } from 'lib/types/mpris.js';
|
|
||||||
import { BoxWidget } from 'lib/types/widget.js';
|
|
||||||
const media = await Service.import('mpris');
|
|
||||||
|
|
||||||
const Controls = (getPlayerInfo: () => MprisPlayer): BoxWidget => {
|
|
||||||
const isValidLoopStatus = (status: string): status is LoopStatus => ['none', 'track', 'playlist'].includes(status);
|
|
||||||
|
|
||||||
const isValidPlaybackStatus = (status: string): status is PlaybackStatus =>
|
|
||||||
['playing', 'paused', 'stopped'].includes(status);
|
|
||||||
|
|
||||||
const isLoopActive = (player: MprisPlayer): string => {
|
|
||||||
return player['loop_status'] !== null && ['track', 'playlist'].includes(player['loop_status'].toLowerCase())
|
|
||||||
? 'active'
|
|
||||||
: '';
|
|
||||||
};
|
|
||||||
|
|
||||||
const isShuffleActive = (player: MprisPlayer): string => {
|
|
||||||
return player['shuffle_status'] !== null && player['shuffle_status'] ? 'active' : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
return Widget.Box({
|
|
||||||
class_name: 'media-indicator-current-player-controls',
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
class_name: 'media-indicator-current-controls',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
class_name: 'media-indicator-control shuffle',
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
hpack: 'center',
|
|
||||||
hasTooltip: true,
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const foundPlayer = getPlayerInfo();
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
self.tooltip_text = 'Unavailable';
|
|
||||||
self.class_name = 'media-indicator-control-button shuffle disabled';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tooltip_text =
|
|
||||||
foundPlayer.shuffle_status !== null
|
|
||||||
? foundPlayer.shuffle_status
|
|
||||||
? 'Shuffling'
|
|
||||||
: 'Not Shuffling'
|
|
||||||
: null;
|
|
||||||
self.on_primary_click = (): void => {
|
|
||||||
foundPlayer.shuffle();
|
|
||||||
};
|
|
||||||
self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? 'enabled' : 'disabled'}`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Widget.Icon(icons.mpris.shuffle['enabled']),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
hpack: 'center',
|
|
||||||
child: Widget.Icon(icons.mpris.prev),
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const foundPlayer = getPlayerInfo();
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
self.class_name = 'media-indicator-control-button prev disabled';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.on_primary_click = (): void => {
|
|
||||||
foundPlayer.previous();
|
|
||||||
};
|
|
||||||
self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? 'enabled' : 'disabled'}`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
hpack: 'center',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const foundPlayer = getPlayerInfo();
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
self.class_name = 'media-indicator-control-button play disabled';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.on_primary_click = (): void => {
|
|
||||||
foundPlayer.playPause();
|
|
||||||
};
|
|
||||||
self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? 'enabled' : 'disabled'}`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Widget.Icon({
|
|
||||||
icon: Utils.watch(icons.mpris.paused, media, 'changed', () => {
|
|
||||||
const foundPlayer: MprisPlayer = getPlayerInfo();
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
return icons.mpris['paused'];
|
|
||||||
}
|
|
||||||
const playbackStatus = foundPlayer.play_back_status?.toLowerCase();
|
|
||||||
|
|
||||||
if (playbackStatus && isValidPlaybackStatus(playbackStatus)) {
|
|
||||||
return icons.mpris[playbackStatus];
|
|
||||||
} else {
|
|
||||||
return icons.mpris['paused'];
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
class_name: `media-indicator-control next`,
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
hpack: 'center',
|
|
||||||
child: Widget.Icon(icons.mpris.next),
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const foundPlayer = getPlayerInfo();
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
self.class_name = 'media-indicator-control-button next disabled';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.on_primary_click = (): void => {
|
|
||||||
foundPlayer.next();
|
|
||||||
};
|
|
||||||
self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? 'enabled' : 'disabled'}`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
class_name: 'media-indicator-control loop',
|
|
||||||
children: [
|
|
||||||
Widget.Button({
|
|
||||||
hpack: 'center',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const foundPlayer = getPlayerInfo();
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
self.tooltip_text = 'Unavailable';
|
|
||||||
self.class_name = 'media-indicator-control-button shuffle disabled';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tooltip_text =
|
|
||||||
foundPlayer.loop_status !== null
|
|
||||||
? foundPlayer.loop_status
|
|
||||||
? 'Shuffling'
|
|
||||||
: 'Not Shuffling'
|
|
||||||
: null;
|
|
||||||
self.on_primary_click = (): void => {
|
|
||||||
foundPlayer.loop();
|
|
||||||
};
|
|
||||||
self.class_name = `media-indicator-control-button loop ${isLoopActive(foundPlayer)} ${foundPlayer.loop_status !== null ? 'enabled' : 'disabled'}`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Widget.Icon({
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const foundPlayer: MprisPlayer = getPlayerInfo();
|
|
||||||
|
|
||||||
if (foundPlayer === undefined) {
|
|
||||||
self.icon = icons.mpris.loop['none'];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loopStatus = foundPlayer.loop_status?.toLowerCase();
|
|
||||||
|
|
||||||
if (loopStatus && isValidLoopStatus(loopStatus)) {
|
|
||||||
self.icon = icons.mpris.loop[loopStatus];
|
|
||||||
} else {
|
|
||||||
self.icon = icons.mpris.loop['none'];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Controls };
|
|
||||||
22
modules/menus/media/components/controls/index.ts
Normal file
22
modules/menus/media/components/controls/index.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { BoxWidget } from 'lib/types/widget.js';
|
||||||
|
import { shuffleControl } from './shuffle/index.js';
|
||||||
|
import { previousTrack } from './previous/index.js';
|
||||||
|
import { playPause } from './playpause/index.js';
|
||||||
|
import { nextTrack } from './next/index.js';
|
||||||
|
import { loopControl } from './loop/index.js';
|
||||||
|
|
||||||
|
const Controls = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-current-player-controls',
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Widget.Box({
|
||||||
|
class_name: 'media-indicator-current-controls',
|
||||||
|
hpack: 'center',
|
||||||
|
children: [shuffleControl(), previousTrack(), playPause(), nextTrack(), loopControl()],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Controls };
|
||||||
11
modules/menus/media/components/controls/loop/helpers.ts
Normal file
11
modules/menus/media/components/controls/loop/helpers.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { LoopStatus } from 'lib/types/mpris';
|
||||||
|
import { MprisPlayer } from 'types/service/mpris';
|
||||||
|
|
||||||
|
export const isValidLoopStatus = (status: string): status is LoopStatus =>
|
||||||
|
['none', 'track', 'playlist'].includes(status);
|
||||||
|
|
||||||
|
export const isLoopActive = (player: MprisPlayer): string => {
|
||||||
|
return player['loop_status'] !== null && ['track', 'playlist'].includes(player['loop_status'].toLowerCase())
|
||||||
|
? 'active'
|
||||||
|
: '';
|
||||||
|
};
|
||||||
59
modules/menus/media/components/controls/loop/index.ts
Normal file
59
modules/menus/media/components/controls/loop/index.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import icons from 'lib/icons';
|
||||||
|
import { BoxWidget } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
import { MprisPlayer } from 'types/service/mpris';
|
||||||
|
import { isLoopActive, isValidLoopStatus } from './helpers';
|
||||||
|
|
||||||
|
const media = await Service.import('mpris');
|
||||||
|
|
||||||
|
export const loopControl = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-control loop',
|
||||||
|
children: [
|
||||||
|
Widget.Button({
|
||||||
|
hpack: 'center',
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
self.tooltip_text = 'Unavailable';
|
||||||
|
self.class_name = 'media-indicator-control-button shuffle disabled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tooltip_text = foundPlayer.loop_status !== null ? foundPlayer.loop_status : 'None';
|
||||||
|
|
||||||
|
self.on_primary_click = (): void => {
|
||||||
|
foundPlayer.loop();
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusTag = foundPlayer.loop_status !== null ? 'enabled' : 'disabled';
|
||||||
|
const isActiveTag = isLoopActive(foundPlayer);
|
||||||
|
|
||||||
|
self.class_name = `media-indicator-control-button loop ${isActiveTag} ${statusTag}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Widget.Icon({
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const foundPlayer: MprisPlayer = getPlayerInfo();
|
||||||
|
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
self.icon = icons.mpris.loop['none'];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loopStatus = foundPlayer.loop_status?.toLowerCase();
|
||||||
|
|
||||||
|
if (loopStatus && isValidLoopStatus(loopStatus)) {
|
||||||
|
self.icon = icons.mpris.loop[loopStatus];
|
||||||
|
} else {
|
||||||
|
self.icon = icons.mpris.loop['none'];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
30
modules/menus/media/components/controls/next/index.ts
Normal file
30
modules/menus/media/components/controls/next/index.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import icons from 'lib/icons';
|
||||||
|
import { BoxWidget } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
|
||||||
|
export const nextTrack = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: `media-indicator-control next`,
|
||||||
|
children: [
|
||||||
|
Widget.Button({
|
||||||
|
hpack: 'center',
|
||||||
|
child: Widget.Icon(icons.mpris.next),
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
self.class_name = 'media-indicator-control-button next disabled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.on_primary_click = (): void => {
|
||||||
|
foundPlayer.next();
|
||||||
|
};
|
||||||
|
self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? 'enabled' : 'disabled'}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PlaybackStatus } from 'lib/types/mpris';
|
||||||
|
|
||||||
|
export const isValidPlaybackStatus = (status: string): status is PlaybackStatus =>
|
||||||
|
['playing', 'paused', 'stopped'].includes(status);
|
||||||
43
modules/menus/media/components/controls/playpause/index.ts
Normal file
43
modules/menus/media/components/controls/playpause/index.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
import { MprisPlayer } from 'types/service/mpris';
|
||||||
|
import { isValidPlaybackStatus } from './helpers';
|
||||||
|
import Button from 'types/widgets/button';
|
||||||
|
import Icon from 'types/widgets/icon';
|
||||||
|
import { Attribute } from 'lib/types/widget';
|
||||||
|
import icons from 'modules/icons/index';
|
||||||
|
|
||||||
|
export const playPause = (): Button<Icon<Attribute>, Attribute> => {
|
||||||
|
return Widget.Button({
|
||||||
|
hpack: 'center',
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
self.class_name = 'media-indicator-control-button play disabled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.on_primary_click = (): void => {
|
||||||
|
foundPlayer.playPause();
|
||||||
|
};
|
||||||
|
self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? 'enabled' : 'disabled'}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Widget.Icon({
|
||||||
|
icon: Utils.watch(icons.mpris.paused, media, 'changed', () => {
|
||||||
|
const foundPlayer: MprisPlayer = getPlayerInfo();
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
return icons.mpris['paused'];
|
||||||
|
}
|
||||||
|
const playbackStatus = foundPlayer.play_back_status?.toLowerCase();
|
||||||
|
|
||||||
|
if (playbackStatus && isValidPlaybackStatus(playbackStatus)) {
|
||||||
|
return icons.mpris[playbackStatus];
|
||||||
|
} else {
|
||||||
|
return icons.mpris['paused'];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
27
modules/menus/media/components/controls/previous/index.ts
Normal file
27
modules/menus/media/components/controls/previous/index.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import icons from 'lib/icons';
|
||||||
|
import { Attribute } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
import Button from 'types/widgets/button';
|
||||||
|
import Icon from 'types/widgets/icon';
|
||||||
|
|
||||||
|
export const previousTrack = (): Button<Icon<Attribute>, Attribute> => {
|
||||||
|
return Widget.Button({
|
||||||
|
hpack: 'center',
|
||||||
|
child: Widget.Icon(icons.mpris.prev),
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
self.class_name = 'media-indicator-control-button prev disabled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.on_primary_click = (): void => {
|
||||||
|
foundPlayer.previous();
|
||||||
|
};
|
||||||
|
self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? 'enabled' : 'disabled'}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { MprisPlayer } from 'types/service/mpris';
|
||||||
|
|
||||||
|
export const isShuffleActive = (player: MprisPlayer): string => {
|
||||||
|
return player['shuffle_status'] !== null && player['shuffle_status'] ? 'active' : '';
|
||||||
|
};
|
||||||
40
modules/menus/media/components/controls/shuffle/index.ts
Normal file
40
modules/menus/media/components/controls/shuffle/index.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import { Attribute, Child } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
import Box from 'types/widgets/box';
|
||||||
|
import { isShuffleActive } from './helpers';
|
||||||
|
import icons from 'lib/icons';
|
||||||
|
|
||||||
|
export const shuffleControl = (): Box<Child, Attribute> => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-control shuffle',
|
||||||
|
children: [
|
||||||
|
Widget.Button({
|
||||||
|
hpack: 'center',
|
||||||
|
hasTooltip: true,
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
self.tooltip_text = 'Unavailable';
|
||||||
|
self.class_name = 'media-indicator-control-button shuffle disabled';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tooltip_text =
|
||||||
|
foundPlayer.shuffle_status !== null
|
||||||
|
? foundPlayer.shuffle_status
|
||||||
|
? 'Shuffling'
|
||||||
|
: 'Not Shuffling'
|
||||||
|
: null;
|
||||||
|
self.on_primary_click = (): void => {
|
||||||
|
foundPlayer.shuffle();
|
||||||
|
};
|
||||||
|
self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? 'enabled' : 'disabled'}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Widget.Icon(icons.mpris.shuffle['enabled']),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
51
modules/menus/media/components/helpers.ts
Normal file
51
modules/menus/media/components/helpers.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import options from 'options.js';
|
||||||
|
import { MprisPlayer } from 'types/service/mpris';
|
||||||
|
const { tint, color } = options.theme.bar.menus.menu.media.card;
|
||||||
|
|
||||||
|
const curPlayer = Variable('');
|
||||||
|
|
||||||
|
export const generateAlbumArt = (imageUrl: string): string => {
|
||||||
|
const userTint = tint.value;
|
||||||
|
const userHexColor = color.value;
|
||||||
|
|
||||||
|
const r = parseInt(userHexColor.slice(1, 3), 16);
|
||||||
|
const g = parseInt(userHexColor.slice(3, 5), 16);
|
||||||
|
const b = parseInt(userHexColor.slice(5, 7), 16);
|
||||||
|
|
||||||
|
const alpha = userTint / 100;
|
||||||
|
|
||||||
|
const css = `background-image: linear-gradient(
|
||||||
|
rgba(${r}, ${g}, ${b}, ${alpha}),
|
||||||
|
rgba(${r}, ${g}, ${b}, ${alpha}),
|
||||||
|
${userHexColor} 65em
|
||||||
|
), url("${imageUrl}");`;
|
||||||
|
|
||||||
|
return css;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initializeActivePlayerHook = (): void => {
|
||||||
|
media.connect('changed', () => {
|
||||||
|
const statusOrder = {
|
||||||
|
Playing: 1,
|
||||||
|
Paused: 2,
|
||||||
|
Stopped: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPlaying = media.players.find((p) => p['play_back_status'] === 'Playing');
|
||||||
|
|
||||||
|
const playerStillExists = media.players.some((p) => curPlayer.value === p['bus_name']);
|
||||||
|
|
||||||
|
const nextPlayerUp = media.players.sort(
|
||||||
|
(a, b) => statusOrder[a['play_back_status']] - statusOrder[b['play_back_status']],
|
||||||
|
)[0].bus_name;
|
||||||
|
|
||||||
|
if (isPlaying || !playerStillExists) {
|
||||||
|
curPlayer.value = nextPlayerUp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPlayerInfo = (): MprisPlayer => {
|
||||||
|
return media.players.find((p) => p.bus_name === curPlayer.value) || media.players[0];
|
||||||
|
};
|
||||||
@@ -1,58 +1,16 @@
|
|||||||
const media = await Service.import('mpris');
|
const media = await Service.import('mpris');
|
||||||
import { MediaInfo } from './components/mediainfo.js';
|
import { MediaInfo } from './title/index.js';
|
||||||
import { Controls } from './components/controls.js';
|
import { Controls } from './controls/index.js';
|
||||||
import { Bar } from './components/bar.js';
|
import { Bar } from './timebar/index.js';
|
||||||
import { MprisPlayer } from 'types/service/mpris.js';
|
|
||||||
import options from 'options.js';
|
import options from 'options.js';
|
||||||
import { BoxWidget } from 'lib/types/widget.js';
|
import { BoxWidget } from 'lib/types/widget.js';
|
||||||
|
import { generateAlbumArt, getPlayerInfo, initializeActivePlayerHook } from './helpers.js';
|
||||||
|
|
||||||
const { tint, color } = options.theme.bar.menus.menu.media.card;
|
const { tint, color } = options.theme.bar.menus.menu.media.card;
|
||||||
|
|
||||||
const generateAlbumArt = (imageUrl: string): string => {
|
initializeActivePlayerHook();
|
||||||
const userTint = tint.value;
|
|
||||||
const userHexColor = color.value;
|
|
||||||
|
|
||||||
const r = parseInt(userHexColor.slice(1, 3), 16);
|
|
||||||
const g = parseInt(userHexColor.slice(3, 5), 16);
|
|
||||||
const b = parseInt(userHexColor.slice(5, 7), 16);
|
|
||||||
|
|
||||||
const alpha = userTint / 100;
|
|
||||||
|
|
||||||
const css = `background-image: linear-gradient(
|
|
||||||
rgba(${r}, ${g}, ${b}, ${alpha}),
|
|
||||||
rgba(${r}, ${g}, ${b}, ${alpha}),
|
|
||||||
${userHexColor} 65em
|
|
||||||
), url("${imageUrl}");`;
|
|
||||||
|
|
||||||
return css;
|
|
||||||
};
|
|
||||||
const Media = (): BoxWidget => {
|
const Media = (): BoxWidget => {
|
||||||
const curPlayer = Variable('');
|
|
||||||
|
|
||||||
media.connect('changed', () => {
|
|
||||||
const statusOrder = {
|
|
||||||
Playing: 1,
|
|
||||||
Paused: 2,
|
|
||||||
Stopped: 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
const isPlaying = media.players.find((p) => p['play_back_status'] === 'Playing');
|
|
||||||
|
|
||||||
const playerStillExists = media.players.some((p) => curPlayer.value === p['bus_name']);
|
|
||||||
|
|
||||||
const nextPlayerUp = media.players.sort(
|
|
||||||
(a, b) => statusOrder[a['play_back_status']] - statusOrder[b['play_back_status']],
|
|
||||||
)[0].bus_name;
|
|
||||||
|
|
||||||
if (isPlaying || !playerStillExists) {
|
|
||||||
curPlayer.value = nextPlayerUp;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const getPlayerInfo = (): MprisPlayer => {
|
|
||||||
return media.players.find((p) => p.bus_name === curPlayer.value) || media.players[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
return Widget.Box({
|
return Widget.Box({
|
||||||
class_name: 'menu-section-container',
|
class_name: 'menu-section-container',
|
||||||
children: [
|
children: [
|
||||||
@@ -69,13 +27,14 @@ const Media = (): BoxWidget => {
|
|||||||
hpack: 'fill',
|
hpack: 'fill',
|
||||||
hexpand: true,
|
hexpand: true,
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: [MediaInfo(getPlayerInfo), Controls(getPlayerInfo), Bar(getPlayerInfo)],
|
children: [MediaInfo(), Controls(), Bar()],
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
self.hook(media, () => {
|
self.hook(media, () => {
|
||||||
const curPlayer = getPlayerInfo();
|
const curPlayer = getPlayerInfo();
|
||||||
|
|
||||||
if (curPlayer !== undefined) {
|
if (curPlayer !== undefined) {
|
||||||
self.css = generateAlbumArt(curPlayer.track_cover_url);
|
self.css = generateAlbumArt(curPlayer.track_cover_url);
|
||||||
}
|
}
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import { BoxWidget } from 'lib/types/widget';
|
|
||||||
import { MprisPlayer } from 'types/service/mpris';
|
|
||||||
|
|
||||||
const media = await Service.import('mpris');
|
|
||||||
|
|
||||||
const MediaInfo = (getPlayerInfo: () => MprisPlayer): BoxWidget => {
|
|
||||||
return Widget.Box({
|
|
||||||
class_name: 'media-indicator-current-media-info',
|
|
||||||
hpack: 'center',
|
|
||||||
hexpand: true,
|
|
||||||
vertical: true,
|
|
||||||
children: [
|
|
||||||
Widget.Box({
|
|
||||||
class_name: 'media-indicator-current-song-name',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
truncate: 'end',
|
|
||||||
max_width_chars: 31,
|
|
||||||
wrap: true,
|
|
||||||
class_name: 'media-indicator-current-song-name-label',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const curPlayer = getPlayerInfo();
|
|
||||||
return (self.label =
|
|
||||||
curPlayer !== undefined && curPlayer['track_title'].length
|
|
||||||
? curPlayer['track_title']
|
|
||||||
: 'No Media Currently Playing');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
class_name: 'media-indicator-current-song-author',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
truncate: 'end',
|
|
||||||
wrap: true,
|
|
||||||
max_width_chars: 35,
|
|
||||||
class_name: 'media-indicator-current-song-author-label',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const curPlayer = getPlayerInfo();
|
|
||||||
|
|
||||||
const makeArtistList = (trackArtists: string[]): string => {
|
|
||||||
if (trackArtists.length === 1 && !trackArtists[0].length) {
|
|
||||||
return '-----';
|
|
||||||
}
|
|
||||||
|
|
||||||
return trackArtists.join(', ');
|
|
||||||
};
|
|
||||||
|
|
||||||
return (self.label =
|
|
||||||
curPlayer !== undefined && curPlayer['track_artists'].length
|
|
||||||
? makeArtistList(curPlayer['track_artists'])
|
|
||||||
: '-----');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
Widget.Box({
|
|
||||||
class_name: 'media-indicator-current-song-album',
|
|
||||||
hpack: 'center',
|
|
||||||
children: [
|
|
||||||
Widget.Label({
|
|
||||||
truncate: 'end',
|
|
||||||
wrap: true,
|
|
||||||
max_width_chars: 40,
|
|
||||||
class_name: 'media-indicator-current-song-album-label',
|
|
||||||
setup: (self) => {
|
|
||||||
self.hook(media, () => {
|
|
||||||
const curPlayer = getPlayerInfo();
|
|
||||||
return (self.label =
|
|
||||||
curPlayer !== undefined && curPlayer['track_album'].length
|
|
||||||
? curPlayer['track_album']
|
|
||||||
: '---');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export { MediaInfo };
|
|
||||||
39
modules/menus/media/components/timebar/helpers.ts
Normal file
39
modules/menus/media/components/timebar/helpers.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Attribute } from 'lib/types/widget';
|
||||||
|
import { MprisPlayer } from 'types/service/mpris';
|
||||||
|
import Slider from 'types/widgets/slider';
|
||||||
|
|
||||||
|
export const updateTooltip = (self: Slider<Attribute>, foundPlayer: MprisPlayer): void => {
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
self.tooltip_text = '00:00';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const playerPosition = foundPlayer.position;
|
||||||
|
|
||||||
|
const curHour = Math.floor(playerPosition / 3600);
|
||||||
|
const curMin = Math.floor((playerPosition % 3600) / 60);
|
||||||
|
const curSec = Math.floor(playerPosition % 60);
|
||||||
|
|
||||||
|
if (typeof foundPlayer.position === 'number' && foundPlayer.position >= 0) {
|
||||||
|
const formatTime = (time: number): string => {
|
||||||
|
return time.toString().padStart(2, '0');
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatHour = (hour: number): string => {
|
||||||
|
return hour > 0 ? formatTime(hour) + ':' : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
self.tooltip_text = `${formatHour(curHour)}${formatTime(curMin)}:${formatTime(curSec)}`;
|
||||||
|
} else {
|
||||||
|
self.tooltip_text = `00:00`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = (self: Slider<Attribute>, foundPlayer: MprisPlayer): void => {
|
||||||
|
if (foundPlayer !== undefined) {
|
||||||
|
const value = foundPlayer.length ? foundPlayer.position / foundPlayer.length : 0;
|
||||||
|
self.value = value > 0 ? value : 0;
|
||||||
|
} else {
|
||||||
|
self.value = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
47
modules/menus/media/components/timebar/index.ts
Normal file
47
modules/menus/media/components/timebar/index.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import { BoxWidget } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../helpers';
|
||||||
|
import { update, updateTooltip } from './helpers';
|
||||||
|
|
||||||
|
const Bar = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-current-progress-bar',
|
||||||
|
hexpand: true,
|
||||||
|
children: [
|
||||||
|
Widget.Box({
|
||||||
|
hexpand: true,
|
||||||
|
child: Widget.Slider({
|
||||||
|
hexpand: true,
|
||||||
|
tooltip_text: '--',
|
||||||
|
class_name: 'menu-slider media progress',
|
||||||
|
draw_value: false,
|
||||||
|
on_change: ({ value }) => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
if (foundPlayer === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return (foundPlayer.position = value * foundPlayer.length);
|
||||||
|
},
|
||||||
|
setup: (self) => {
|
||||||
|
self.poll(1000, () => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
|
||||||
|
if (foundPlayer.play_back_status === 'Playing') {
|
||||||
|
update(self, foundPlayer);
|
||||||
|
updateTooltip(self, foundPlayer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.hook(media, () => {
|
||||||
|
const foundPlayer = getPlayerInfo();
|
||||||
|
update(self, foundPlayer);
|
||||||
|
updateTooltip(self, foundPlayer);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Bar };
|
||||||
27
modules/menus/media/components/title/album/index.ts
Normal file
27
modules/menus/media/components/title/album/index.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import { BoxWidget } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
|
||||||
|
export const songAlbum = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-current-song-album',
|
||||||
|
hpack: 'center',
|
||||||
|
children: [
|
||||||
|
Widget.Label({
|
||||||
|
truncate: 'end',
|
||||||
|
wrap: true,
|
||||||
|
max_width_chars: 40,
|
||||||
|
class_name: 'media-indicator-current-song-album-label',
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const curPlayer = getPlayerInfo();
|
||||||
|
return (self.label =
|
||||||
|
curPlayer !== undefined && curPlayer['track_album'].length
|
||||||
|
? curPlayer['track_album']
|
||||||
|
: '---');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
36
modules/menus/media/components/title/author/index.ts
Normal file
36
modules/menus/media/components/title/author/index.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const media = await Service.import('mpris');
|
||||||
|
import { BoxWidget } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
|
||||||
|
export const songAuthor = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-current-song-author',
|
||||||
|
hpack: 'center',
|
||||||
|
children: [
|
||||||
|
Widget.Label({
|
||||||
|
truncate: 'end',
|
||||||
|
wrap: true,
|
||||||
|
max_width_chars: 35,
|
||||||
|
class_name: 'media-indicator-current-song-author-label',
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const curPlayer = getPlayerInfo();
|
||||||
|
|
||||||
|
const makeArtistList = (trackArtists: string[]): string => {
|
||||||
|
if (trackArtists.length === 1 && !trackArtists[0].length) {
|
||||||
|
return '-----';
|
||||||
|
}
|
||||||
|
|
||||||
|
return trackArtists.join(', ');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (self.label =
|
||||||
|
curPlayer !== undefined && curPlayer['track_artists'].length
|
||||||
|
? makeArtistList(curPlayer['track_artists'])
|
||||||
|
: '-----');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
14
modules/menus/media/components/title/index.ts
Normal file
14
modules/menus/media/components/title/index.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { BoxWidget } from 'lib/types/widget';
|
||||||
|
import { songName } from './name/index';
|
||||||
|
import { songAuthor } from './author/index';
|
||||||
|
import { songAlbum } from './album/index';
|
||||||
|
|
||||||
|
export const MediaInfo = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-current-media-info',
|
||||||
|
hpack: 'center',
|
||||||
|
hexpand: true,
|
||||||
|
vertical: true,
|
||||||
|
children: [songName(), songAuthor(), songAlbum()],
|
||||||
|
});
|
||||||
|
};
|
||||||
28
modules/menus/media/components/title/name/index.ts
Normal file
28
modules/menus/media/components/title/name/index.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { BoxWidget } from 'lib/types/widget';
|
||||||
|
import { getPlayerInfo } from '../../helpers';
|
||||||
|
|
||||||
|
const media = await Service.import('mpris');
|
||||||
|
|
||||||
|
export const songName = (): BoxWidget => {
|
||||||
|
return Widget.Box({
|
||||||
|
class_name: 'media-indicator-current-song-name',
|
||||||
|
hpack: 'center',
|
||||||
|
children: [
|
||||||
|
Widget.Label({
|
||||||
|
truncate: 'end',
|
||||||
|
max_width_chars: 31,
|
||||||
|
wrap: true,
|
||||||
|
class_name: 'media-indicator-current-song-name-label',
|
||||||
|
setup: (self) => {
|
||||||
|
self.hook(media, () => {
|
||||||
|
const curPlayer = getPlayerInfo();
|
||||||
|
return (self.label =
|
||||||
|
curPlayer !== undefined && curPlayer['track_title'].length
|
||||||
|
? curPlayer['track_title']
|
||||||
|
: 'No Media Currently Playing');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Window from 'types/widgets/window.js';
|
import Window from 'types/widgets/window.js';
|
||||||
import DropdownMenu from '../shared/dropdown/index.js';
|
import DropdownMenu from '../shared/dropdown/index.js';
|
||||||
import { Media } from './media.js';
|
import { Media } from './components/index.js';
|
||||||
import { Attribute, Child } from 'lib/types/widget.js';
|
import { Attribute, Child } from 'lib/types/widget.js';
|
||||||
import options from 'options.js';
|
import options from 'options.js';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user