Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: better snip + loop controls #201

Merged
merged 43 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a867a76
feat: add inputs to snip + set slider step to .01
cdrani Dec 13, 2023
6e99143
refactor: time util functions + new formatters
cdrani Dec 13, 2023
8688f71
fix: Snip#share url generation
cdrani Dec 13, 2023
d7acda5
feat: add controls + event handlers for snip inputs
cdrani Dec 13, 2023
958191e
styles: update slider styles + misc. ui fixes
cdrani Dec 14, 2023
bd1c852
fix: action buttons inner text alignment
cdrani Dec 14, 2023
8b34954
refactor: add startTime & endTime attributes to video
cdrani Dec 15, 2023
c402348
refactor: auto-loop snip while in edit mode
cdrani Dec 15, 2023
337ec20
chore: remove extra whitespaces in snip models
cdrani Dec 15, 2023
471db65
chore: remove update of currentTime on Snip#save
cdrani Dec 15, 2023
342ac08
fix: Snip#tempShareTimes values retrieval
cdrani Dec 15, 2023
0246d6a
fix: ensure track remove from cache on storage remove
cdrani Dec 16, 2023
03ff130
refactor: add autoLoop key-value to track defaults
cdrani Dec 16, 2023
fd613da
feat: add new autoLoop toggle ui to snip controls
cdrani Dec 16, 2023
7f16739
feat: add handlers for snip autoLoop toggle
cdrani Dec 16, 2023
ad45f51
feat: set/update autoLoop key on Snip#save
cdrani Dec 16, 2023
cb99b5a
refactor: use autoLoop value to set snip looping
cdrani Dec 16, 2023
aa7dd15
chore: prettify SeekIcon
cdrani Dec 17, 2023
23af14f
style: set button cursor to pointer
cdrani Dec 17, 2023
d0f5e5c
feat: add LoopIcon to manage loop icon + autoLoop
cdrani Dec 17, 2023
6dd9b91
feat: add util function to highlight loop icon
cdrani Dec 17, 2023
7556cc7
refactor: highlight loop icon on Snip#save
cdrani Dec 17, 2023
910447b
refactor: set, highlight, & remove loop icon on song changes
cdrani Dec 17, 2023
432b888
refactor: use mouseup event to set speed and position
cdrani Dec 18, 2023
234fffb
refactor: update to controls styles + layout
cdrani Dec 18, 2023
1fb98d9
styles: add hover outline to header buttons
cdrani Dec 18, 2023
a283858
fix: snip controls inputs not reset on view enter
cdrani Dec 18, 2023
bcd6e84
styles: update styles + cleanup of snip components
cdrani Dec 18, 2023
1985ab7
styles: display loop role aria-label as tooltip
cdrani Dec 18, 2023
962d567
styles: update position of artist-disco icon
cdrani Dec 18, 2023
7dc76cb
styles: update width + alignment of speed inputs
cdrani Dec 18, 2023
a1618cf
refactor: set track cover link to hi-res source
cdrani Dec 19, 2023
c282eab
refactor: make looping available for non-snip tracks
cdrani Dec 19, 2023
2f3114e
refactor: cache popup controls state for fast load
cdrani Dec 19, 2023
e92ca5b
refactor: optimize image-colours util results
cdrani Dec 19, 2023
0eaba07
feat: add loop button to popup ui
cdrani Dec 20, 2023
3f2bb31
fix: set default auotLoop for now-playing object
cdrani Dec 21, 2023
adbe9c9
styles: update position & height of media controls
cdrani Dec 21, 2023
5217119
fix: loop icon in app not sync with popup
cdrani Dec 21, 2023
42c5783
fix: clear video temp times on non-snip non-loops
cdrani Dec 21, 2023
e1780f4
refactor: set active tab default to active window
cdrani Dec 21, 2023
4cff59e
feat: add loop to shortcut keys commands
cdrani Dec 21, 2023
fd988fa
docs: bump version to v1.21.0
cdrani Dec 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "chorus",
"private": true,
"type": "module",
"version": "1.20.0",
"version": "1.21.0",
"scripts": {
"build": "rollup -c",
"watch": "rollup -c -w"
Expand Down
8 changes: 3 additions & 5 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ async function getUIState({ selector, tabId }) {
}

async function getMediaControlsState(tabId) {
const requiredKeys = ['repeat', 'shuffle', 'play/pause', 'save/unsave', 'seek-rewind', 'seek-fastforward']
const requiredKeys = ['repeat', 'shuffle', 'play/pause', 'seek-rewind', 'seek-fastforward', 'loop', 'save/unsave']
const selectorKeys = { ...mediaKeys, ...chorusKeys }
const selectors = Object.keys(selectorKeys).map(key => (
requiredKeys.includes(key) ? selectorKeys[key] : undefined
)).filter(Boolean)
const selectors = requiredKeys.map(key => selectorKeys[key] ?? undefined).filter(Boolean)

const promises = selectors.map(selector => (
new Promise(resolve => {
if (!selector.includes('add-button')) return resolve(getUIState({ selector, tabId }))
if (!selector.includes('loop')) return resolve(getUIState({ selector, tabId }))
return setTimeout(() => resolve(getUIState({ selector, tabId })), 500)
})
))
Expand Down
2 changes: 1 addition & 1 deletion src/components/artist-disco.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const dispatcher = new Dispatcher()
const createArtistDiscoUI = () => `
<div
id="artist-disco"
style="display:flex;width:3.5rem;height:3.5rem;align-items:center;justify-content:center"
style="display:flex;align-items:center;"
>
<button
role="artist-disco"
Expand Down
2 changes: 1 addition & 1 deletion src/components/effects/effects-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { convolverPresets, drinkPresets } from '../../lib/reverb/presets.js'

export const createEffectsControls = () => `
<div id="chorus-fx-controls" style="display: none">
<div style="display:flex;flex-direction:column;justify-content:space-evenly;height:6rem;">
<div style="display:flex;flex-direction:column;justify-content:space-between;height:5.5rem;">
${createEffectsSelector({ name: 'drink-effect', labelName: 'cup-sized reverb', optionNames: drinkPresets })}
${createEffectsSelector({ name: 'convolver-effect', labelName: 'impulse reverb', optionNames: convolverPresets })}

Expand Down
2 changes: 1 addition & 1 deletion src/components/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const createHeader = () => `
<div class="chorus-common">
<span class="chorus-header">chorus</span>
<div style="display:flex;justify-content:space-between;align-items:center;">
<div style="display:flex;height:25px;align-items:center;justify-content:space-around;">
<div id="header-buttons" style="display:flex;height:25px;align-items:center;justify-content:space-around;">
${createHeaderButton({ role: 'snip', ariaLabel: 'Snip Controls', additionalStyles: 'background-color:green;' })}
${createHeaderButton({ role: 'speed', ariaLabel: 'Speed Controls', additionalStyles: 'margin-left:.5rem;' })}
${createHeaderButton({ role: 'fx', ariaLabel: 'FX Controls', additionalStyles: 'margin-left:.5rem;' })}
Expand Down
22 changes: 12 additions & 10 deletions src/components/snip/snip-controls.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { createSlider } from './snip-slider.js'
import { createRangeLabels } from './snip-labels.js'
import { createSnipToggler } from './snip-toggle.js'
import { createSnipButtons } from './snip-buttons.js'

import { playback } from '../../utils/playback.js'

export const createSnipControls = () => {
const current = playback.current()
const duration = playback.duration()

return `
<div id="chorus-snip-controls" style="display: block">
${createSlider({ current, duration })}
export const createSnipControls = () => `
<div id="chorus-snip-controls" style="display: block">
${createSlider({ current: playback.current(), duration: playback.duration(), style: "margin:6px 0" })}
<p style="font-size:.75rem;color:#fff;padding:0;margin-top:-8px;padding-bottom:.5rem">
* while editing 'end', track plays 3 secs past set 'end'
</p>
<div style="display:flex;justify-content:space-between;height:3rem;">
${createRangeLabels()}
${createSnipButtons()}
${createSnipToggler()}
</div>
`
}
${createSnipButtons()}
</div>
`
13 changes: 6 additions & 7 deletions src/components/snip/snip-labels.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
const style = "min-width:24px;font-size:0.85rem;text-align:end;margin-left:4px"

const createLabel = text => `
<div>
<label class="chorus-common" style="width:100%;align-items: end">
${text}: <span style="${style}" id="chorus-${text}"></span>
</label>
<div class="chorus-common" style="display:flex;align-items:center;height:22px;">
<span class="chorus-text chorus-pill" style="font-size:1rem;line-height:22px;height:100%;text-align:center;min-width:43px;width:56px;padding:0 4px">
${text}
</span>
<input id="chorus-${text}" class="chorus-text" style="letter-spacing:0.5px;padding:0 4px;width:100%;text-align:end;background:green;border:none;color:#fff;height:100%;">
</div>
`

export const createRangeLabels = () => `
<div style="display:flex;justify-content:space-between;height:3rem;align-items:center;">
<div style="display:flex;flex-direction:column;justify-content:space-between;width:100%">
${createLabel('start')}
${createLabel('end')}
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/components/snip/snip-slider.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { secondsToTime } from '../../utils/time.js'

const createInput = ({ type, max, value }) => `
<input min="0" max="${max}" type="range" class="input" value="${value}" id="input-${type}">
<input min="0" max="${max}" step="0.01" type="range" class="input" value="${value}" id="input-${type}">
`

const createTime = ({ id, time }) => `
<p class="time${id == 'end' ? ' end' : ''}"><span id="${id}">${time}</span></p>
`

export const createSlider = ({ current, duration }) => `
<div id="snippy" class="snippy">
export const createSlider = ({ current, duration, style = '' }) => `
<div id="snippy" class="snippy" style="${style}">
${createTime({ id: 'start', time: '0:00' })}
<div class="slider-container">
${createInput({ type: 'start', max: duration, value: current })}
Expand Down
16 changes: 16 additions & 0 deletions src/components/snip/snip-toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createToggleButton } from '../toggle-button.js'

export const createSnipToggler = () => `
<div style="display:flex;justify-content:flex-end;align-items:center;width:100%;">
<div style="display:flex;justify-content:space-between;align-items:flex-end">
${createToggleButton({
labelId: 'loop-label',
labelText: 'Auto-Loop',
onPathId: 'loop-toggle-on',
offPathId: 'loop-toggle-off',
checkboxId: 'loop-checkbox',
buttonId: 'loop-toggle-button',
})}
</div>
</div>
`
10 changes: 5 additions & 5 deletions src/components/speed/speed-toggler.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { createToggleButton } from '../toggle-button.js'

const createTag = ({ tag, id, style = '' }) => `
<div style="display:flex;align-items:center;${style};">
<span class="chorus-text chorus-pill" style="text-align:left;width:46px;padding-left:4px">${tag}</span>
<input id="${id}" class="chorus-text" style="color:#fff;width:42px;height:100%;padding:0 4px">
<div style="display:flex;align-items:center;height:22px;${style};">
<span class="chorus-text chorus-pill" style="line-height:22px;height:100%;font-size:14px;text-align:center;width:56px;padding:0 4px">${tag}</span>
<input id="${id}" class="chorus-text" style="text-align:end;letter-spacing:0.5px;border:none;color:#fff;width:56px;height:100%;padding:0 4px">
</div>
`

export const createSpeedToggler = () => `
<div style="display:flex;justify-content:space-between;align-items:center;width:100%;">
<div style="display:flex;flex-direction:column;justify-content:space-between;">
<div style="display:flex;justify-content:space-between;width:100%;height:3.5rem;">
<div style="display:flex;flex-direction:column;justify-content:space-between;height:100%">
${createTag({ tag: 'Track', id: 'speed-track-value', style: 'margin-bottom:4px' })}
${createTag({ tag: 'Global', id: 'speed-global-value' })}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/text-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const createTextButton = ({ id, text, style }) => {
<button
id="chorus-${id}-button"
class="chorus-text-button ${btnTypeClass ?? ''}"
style="padding:0 10px;height:100%;font-size:1rem;${style || ''}"
style="display:flex;justify-content:center;align-items:center;padding:0 10px;height:100%;font-size:1rem;${style || ''}"
>
<span>${text}</span>
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/toggle-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const createToggleButton = ({
style="z-index:100;padding:0;background-color:transparent;display:flex;align-items:center;user-select:none;"
>
${labelId && labelText && `
<span id="${labelId}" style="font-size:1rem;color:#fff;margin-right:1rem">${labelText}</span>
<span id="${labelId}" style="font-weight:600;font-size:1rem;color:#fff;margin-right:1rem">${labelText}</span>
`}

<input
Expand Down
2 changes: 1 addition & 1 deletion src/components/track-info.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const createTrackInfo = () => `
<div style="display:flex;flex-direction:column;width:100%;justify-content:space-evenly;margin-top:.5rem">
<div style="display:flex;flex-direction:column;width:100%;justify-content:space-evenly;margin:.5rem 0">
<div class="container" style="width:282px"><div class="track-text" id="track-title"></div></div>
<div class="container" style="width:282px"><div class="track-text" id="track-artists"></div></div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/data/current.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class CurrentData {
...currentSongInfo(),
startTime: 0,
isSnip: false,
autoLoop: false,
isSkipped: false,
endTime: playback.duration()
}
Expand Down Expand Up @@ -67,7 +68,7 @@ class CurrentData {
}

async readTrack() {
return await this._store.getTrack({ id: currentSongInfo().id, value: this.#trackDefaults })
return await this._store.getTrack({ id: currentSongInfo().id, value: { ...this.#trackDefaults, ...currentSongInfo() }})
}

async readGlobals() {
Expand Down
19 changes: 13 additions & 6 deletions src/events/listeners/header-listeners.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import Listeners from './listeners.js'
import { spotifyVideo } from '../../actions/overload.js'

export default class HeaderListeners extends Listeners {
constructor(songTracker) {
super(songTracker)

this._setup = false
this._viewInFocus = null
this._video = spotifyVideo.element
this._VIEWS = ['snip', 'speed', 'fx', 'seek']
}

Expand All @@ -18,7 +20,7 @@ export default class HeaderListeners extends Listeners {
this.#effectsViewToggle()
this.#closeModalListener()

this._currentView = 'snip'
this.currentView = 'snip'
this._setup = true
}

Expand All @@ -30,13 +32,15 @@ export default class HeaderListeners extends Listeners {
#seekViewToggle() {
const seekButton = document.getElementById('chorus-seek-button')
seekButton?.addEventListener('click', async () => {
this._currentView = 'seek'
this.currentView = 'seek'
await this._seek.init()
})
}

set _currentView(selectedView) {
set currentView(selectedView = 'snip') {
this._viewInFocus = selectedView
if (selectedView != 'snip') this._video.resetTempTimes()

this._VIEWS.forEach(view => {
const viewButton = document.getElementById(`chorus-${view}-button`)
const viewInFocusContainer = document.getElementById(`chorus-${view}-controls`)
Expand All @@ -49,21 +53,24 @@ export default class HeaderListeners extends Listeners {

#snipViewToggle() {
const snipButton = document.getElementById('chorus-snip-button')
snipButton?.addEventListener('click', () => { this._currentView = 'snip' })
snipButton?.addEventListener('click', async () => {
this.currentView = 'snip'
await this._snip.init()
})
}

#speedViewToggle() {
const speedButton = document.getElementById('chorus-speed-button')
speedButton?.addEventListener('click', async () => {
this._currentView = 'speed'
this.currentView = 'speed'
await this._speed.init()
})
}

#effectsViewToggle() {
const effectsButton = document.getElementById('chorus-fx-button')
effectsButton?.addEventListener('click', () => {
this._currentView = 'fx'
this.currentView = 'fx'
this._reverb.init()
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/events/listeners/listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export default class Listeners {
_hide() {
const mainElement = document.getElementById('chorus-main')
mainElement.style.display = 'none'
this._video.isEditing = false
this._video.resetTempTimes()
}
}
5 changes: 4 additions & 1 deletion src/manifest.chrome.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"short_name": "Chorus",
"name": "Chorus - Spotify Enhancer",
"description": "Enhance Spotify with controls to save favourite snips, auto-skip tracks, and set global and custom speed. More to come!",
"version": "1.20.0",
"version": "1.21.0",
"manifest_version": 3,
"author": "cdrani",
"action": {
Expand Down Expand Up @@ -49,6 +49,9 @@
"on/off": {
"description": "Toggle Extension On/Off"
},
"loop": {
"description": "Loop/UnLoop Snip/Track"
},
"next": {
"description": "Next Track"
},
Expand Down
5 changes: 4 additions & 1 deletion src/manifest.firefox.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"short_name": "Chorus",
"name": "Chorus - Spotify Enhancer",
"description": "Enhance Spotify with controls to save favourite snips, auto-skip tracks, and set global and custom speed. More to come!",
"version": "1.20.0",
"version": "1.21.0",
"manifest_version": 3,
"author": "cdrani",
"action": {
Expand Down Expand Up @@ -49,6 +49,9 @@
"on/off": {
"description": "Toggle Extension On/Off"
},
"loop": {
"description": "Loop/UnLoop Snip/Track"
},
"next": {
"description": "Next Track"
},
Expand Down
19 changes: 5 additions & 14 deletions src/models/chorus.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,13 @@ export default class Chorus {
return this.mainElement.style.display == 'block'
}

get mainElement() {
return document.getElementById('chorus-main')
}
get mainElement() { return document.getElementById('chorus-main') }

get chorusControls() {
return document.getElementById('chorus-controls')
}
get chorusControls() { return document.getElementById('chorus-controls') }

toggle() {
this.isShowing ? this.hide() : this.show()
}
toggle() { this.isShowing ? this.hide() : this.show() }

get #hasSnipControls() {
return !!document.getElementById('chorus-snip-controls')
}
get #hasSnipControls() { return !!document.getElementById('chorus-snip-controls') }

#insertIntoDOM() {
if (this.#hasSnipControls) return
Expand All @@ -60,15 +52,14 @@ export default class Chorus {
if (!this.mainElement) return

await this.headerListeners.hide()
this._video.isEditing = false
this.mainElement.style.display = 'none'
this._video.resetTempTimes()
}

show() {
this.#insertIntoDOM()
this.mainElement.style.display = 'block'

this._video.isEditing = true
this.headerListeners.init()
this.actionListeners.init()
}
Expand Down
Loading