Skip to content

Commit f57cbe8

Browse files
committed
- Added active and inactive borders in Play Context button.
- Added a fallback to the first non-speaker device if there are no active devices.
1 parent e397ff1 commit f57cbe8

6 files changed

Lines changed: 46 additions & 19 deletions

File tree

com.ntanis.essentials-for-spotify.sdPlugin/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"Name": "Essentials for Spotify",
3-
"Version": "1.1.0.1",
3+
"Version": "1.1.0.2",
44
"Author": "Ntanis",
55
"Actions": [
66
{

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "essentials-for-spotify",
3-
"version": "1.1.0.1",
3+
"version": "1.1.0.2",
44
"description": "Effortlessly control your Spotify through your Elgato Stream Deck.",
55
"author": "https://github.com/ntanis-dev",
66
"license": "ISC",

src/actions/action.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ export class Action extends SingletonAction {
100100
return lines.join('\n')
101101
}
102102

103-
processImage(iconDataUrl: string, heart: 'top-left' | 'center' | 'none', border: boolean = false): string {
104-
if ((heart === 'none' && (!border)))
103+
processImage(iconDataUrl: string, heart: 'top-left' | 'center' | 'none', borderColor: string | null = null): string {
104+
if ((heart === 'none' && (!borderColor)))
105105
return iconDataUrl
106106

107107
const iconSize = 120
@@ -113,12 +113,12 @@ export class Action extends SingletonAction {
113113
const availableWidth = iconSize - (heartPadding * 2)
114114
const availableHeight = iconSize - (heartPadding * 2)
115115

116-
const borderStrokeWidth = border ? 8 : 0
117-
const borderInset = border ? borderStrokeWidth / 2 : 0
118-
const glowRadius = border ? Math.max(4, Math.round(borderStrokeWidth * 1.25)) : 0
116+
const borderStrokeWidth = borderColor ? 12 : 0
117+
const borderInset = borderColor ? borderStrokeWidth / 2 : 0
118+
const glowRadius = borderColor ? Math.max(4, Math.round(borderStrokeWidth * 1.25)) : 0
119119

120-
const borderRect = border ? `
121-
<rect x="${borderInset}" y="${borderInset}" width="${iconSize - (borderInset * 2)}" height="${iconSize - (borderInset * 2)}" fill="none" stroke="#1db954" stroke-width="${borderStrokeWidth}" shape-rendering="geometricPrecision"/>
120+
const borderRect = borderColor ? `
121+
<rect x="${borderInset}" y="${borderInset}" width="${iconSize - (borderInset * 2)}" height="${iconSize - (borderInset * 2)}" fill="none" stroke="${borderColor}" stroke-width="${borderStrokeWidth}" shape-rendering="geometricPrecision"/>
122122
` : ''
123123

124124
let scaleFactor = Math.min(availableWidth / originalWidth, availableHeight / originalHeight)
@@ -147,7 +147,7 @@ export class Action extends SingletonAction {
147147
vector-effect="non-scaling-stroke"/>
148148
` : ''
149149

150-
const innerGlowRect = border? `
150+
const innerGlowRect = borderColor? `
151151
<rect x="${borderInset}" y="${borderInset}" width="${iconSize - (borderInset * 2)}" height="${iconSize - (borderInset * 2)}" fill="white" filter="url(#innerGlow)"/>
152152
` : ''
153153

@@ -162,7 +162,7 @@ export class Action extends SingletonAction {
162162
<filter id="innerGlow" x="-50%" y="-50%" width="200%" height="200%" color-interpolation-filters="sRGB">
163163
<feGaussianBlur in="SourceAlpha" stdDeviation="${glowRadius}" result="blur"/>
164164
<feComposite in="SourceAlpha" in2="blur" operator="out" result="innerEdge"/>
165-
<feFlood flood-color="#1db954" flood-opacity="1" result="glowColor"/>
165+
<feFlood flood-color="#${borderColor}" flood-opacity="1" result="glowColor"/>
166166
<feComposite in="glowColor" in2="innerEdge" operator="in" result="coloredGlow"/>
167167
<feComposite in="coloredGlow" in2="coloredGlow" operator="over"/>
168168
</filter>

src/actions/play-context-button.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,30 @@ export default class PlayContextButton extends Button {
1919
[context: string]: any
2020
} = {}
2121

22+
#forcedActiveContexts: {
23+
[context: string]: boolean
24+
} = {}
25+
2226
constructor() {
2327
super()
2428
this.setStatelessImage('images/states/play-context-unknown')
29+
30+
wrapper.on('playbackContextChanged', () => {
31+
for (const context of this.contexts)
32+
if (wrapper.playbackContext?.uri === this.#cachedPlayContexts[context]?.uri)
33+
delete this.#forcedActiveContexts[context]
34+
35+
for (const context of this.contexts)
36+
this.#updatePlayContext(context, this.settings[context])
37+
})
2538
}
2639

2740
async #updatePlayContext(context: string, oldSettings: any = undefined) {
2841
this.setUnpressable(context, true)
2942

3043
const badUrl = !/^https?:\/\/open\.spotify\.com\/(?:intl-[a-z]{2}\/)?(?:album|artist|playlist)\/[A-Za-z0-9]{22}(?:\/)?(?:\?.*)?$/.test(this.settings[context].spotify_url)
3144

32-
if ((!oldSettings) || badUrl || this.settings[context].spotify_url !== this.#cachedPlayContexts[context]?.url || (!(oldSettings.show || []).every((entry: string) => entry === 'border' || (this.settings[context].show || []).includes(entry))) || (!(this.settings[context].show || []).every((entry: string) => entry === 'border' || (oldSettings.show || []).includes(entry))))
45+
if ((!oldSettings) || badUrl || this.settings[context].spotify_url !== this.#cachedPlayContexts[context]?.url || (!(oldSettings.show || []).every((entry: string) => entry === 'active_border' || entry === 'inactive_border' || (this.settings[context].show || []).includes(entry))) || (!(this.settings[context].show || []).every((entry: string) => entry === 'active_border' || entry === 'inactive_border' || (oldSettings.show || []).includes(entry))))
3346
this.clearMarquee(context)
3447

3548
if (this.settings[context].spotify_url !== this.#cachedPlayContexts[context]?.url || badUrl) {
@@ -71,7 +84,7 @@ export default class PlayContextButton extends Button {
7184
this.resumeMarquee(context)
7285

7386
if (image)
74-
await this.setImage(context, this.processImage(`data:image/jpeg;base64,${image}`, 'none', this.settings[context].show.includes('border')))
87+
await this.setImage(context, this.processImage(`data:image/jpeg;base64,${image}`, 'none', (this.settings[context].show.includes('active_border') && (wrapper.playbackContext?.uri === this.#cachedPlayContexts[context].uri || this.#forcedActiveContexts[context] === this.#cachedPlayContexts[context].uri)) ? (wrapper.playbackContext?.uri === this.#cachedPlayContexts[context].uri ? '#1db954' : '#dab824') : (this.settings[context].show.includes('inactive_border') ? '#888888' : null)))
7588
else if (this.#cachedPlayContexts[context].type === 'local')
7689
await this.setImage(context, 'images/states/local')
7790
else
@@ -86,9 +99,15 @@ export default class PlayContextButton extends Button {
8699
if (this.#cachedPlayContexts[context]) {
87100
const response = await wrapper.playItem(this.#cachedPlayContexts[context])
88101

89-
if (response === constants.WRAPPER_RESPONSE_SUCCESS)
102+
if (response === constants.WRAPPER_RESPONSE_SUCCESS) {
103+
this.#forcedActiveContexts = {}
104+
this.#forcedActiveContexts[context] = this.#cachedPlayContexts[context].uri
105+
106+
for (const ctx of this.contexts)
107+
setImmediate(() => this.#updatePlayContext(ctx, this.settings[ctx]))
108+
90109
return constants.WRAPPER_RESPONSE_SUCCESS_INDICATIVE
91-
else
110+
} else
92111
return response
93112
} else
94113
return constants.WRAPPER_RESPONSE_NOT_AVAILABLE
@@ -99,7 +118,7 @@ export default class PlayContextButton extends Button {
99118

100119
if (!this.settings[context].show)
101120
await this.setSettings(context, {
102-
show: ['title', 'extra', 'subtitle', 'border']
121+
show: ['title', 'extra', 'subtitle', 'active_border', 'inactive_border']
103122
})
104123

105124
if (this.#cachedPlayContexts[context]?.url !== this.settings[context].spotify_url || oldSettings.show?.length !== this.settings[context].show?.length || (oldSettings.show && this.settings[context].show && (!oldSettings.show.every((value: any, index: number) => value === this.settings[context].show[index]))))

src/library/wrapper.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,15 @@ class Wrapper extends EventEmitter {
117117
}
118118

119119
async #deviceCall(path, options, deviceId) {
120-
if (!deviceId)
121-
throw new constants.NoDeviceError('No device specified.')
120+
if (!deviceId) {
121+
if (this.#lastDevice)
122+
throw new constants.NoDeviceError('No device specified.')
123+
124+
const activeDevices = this.#lastDevices || []
125+
126+
if (activeDevices.length > 0)
127+
this.#setDevices(activeDevices.filter(device => device.type !== 'Speaker')[0], activeDevices)
128+
}
122129

123130
path = `${path}${path.includes('?') ? '&' : '?'}`
124131

src/ui/pi/play-context-button.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
<option value="title">Name</option>
1919
<option value="subtitle">Artist(s) (Album / Track Context)</option>
2020
<option value="extra">Type</option>
21-
<option value="border">Border</option>
21+
<option value="active_border">Active Border</option>
22+
<option value="inactive_border">Inactive Border</option>
2223
</sdpi-checkbox-list>
2324
</sdpi-item>
2425
</body>

0 commit comments

Comments
 (0)