Skip to content

Commit

Permalink
New card actions: play, pause, prev, next, toggle, repeat, shuffle (M…
Browse files Browse the repository at this point in the history
…iczFlor#2179)

* Adding additional player controls to assign to cards

* streamline some RPC command namings across jukebox and webapp

* Fix typos

* Fix Repeat toggles

* Fix toggle_shuffle

* Fix typo in command

* Fix another typo

* Fix wrong parameter for shuffle

* Update German translation for single-repeat

* Simplify Shuffle option

* Simplify Repeat option

* Streamline Player functions with new set_ methods in jukebox

* Abstract OptionsSelector

* Refactor some code

* Fix indentation

* Rename set_ methods

* Undo some doc changes
  • Loading branch information
pabera authored Jan 4, 2024
1 parent c4ee966 commit 03e64ff
Show file tree
Hide file tree
Showing 18 changed files with 297 additions and 131 deletions.
15 changes: 14 additions & 1 deletion documentation/developers/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,20 @@ They can be run individually or in combination. To do that, we use
### Mac

1. [Install Docker & Compose (Mac)](https://docs.docker.com/docker-for-mac/install/)
2. [Install pulseaudio](https://gist.github.com/seongyongkim/b7d630a03e74c7ab1c6b53473b592712) (Other references: [[1]](https://devops.datenkollektiv.de/running-a-docker-soundbox-on-mac.html), [[2]](https://stackoverflow.com/a/50939994/1062438))
2. Install pulseaudio
1. Use Homebrew to install
```
$ brew install pulseaudio
```
2. Enable pulseaudio network capabilities. In an editor, open `/opt/homebrew/Cellar/pulseaudio/16.1/etc/pulse/default.pa` (you might need to adapt this path to your own system settings). Uncomment the following line.
```
load-module module-native-protocol-tcp
```
3. Restart the pulseaudio service
```
$ brew services restart pulseaudio
```
4. If you have trouble with your audio, try these resources to troubleshoot: [[1]](https://gist.github.com/seongyongkim/b7d630a03e74c7ab1c6b53473b592712), [[2]](https://devops.datenkollektiv.de/running-a-docker-soundbox-on-mac.html), [[3]](https://stackoverflow.com/a/50939994/1062438)

> [!NOTE]
> In order for Pulseaudio to work properly with Docker on your Mac, you need to start Pulseaudio in a specific way. Otherwise MPD will throw an exception. See [Pulseaudio issues on Mac](#pulseaudio-issue-on-mac) for more info.
Expand Down
7 changes: 3 additions & 4 deletions documentation/developers/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,9 @@ Topics marked _in progress_ are already in the process of implementation by comm
- [ ] Folder configuration (_in progress_)
- [ ] [Reference](https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/MANUAL#manage-playout-behaviour)
- [ ] Resume: Save and restore position (how interact with shuffle?)
- [ ] Single: Enable mpc single
- [ ] Shuffle: Enable mpc random (not shuffle)
- Rename to random, as this is mpc random
- [ ] Loop: Loop playlist
- [ ] Repeat Playlist
- [ ] Repeat Song
- [ ] Shuffle

### MPD Player

Expand Down
55 changes: 48 additions & 7 deletions src/jukebox/components/playermpd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,6 @@ def seek(self, new_time):
with self.mpd_lock:
self.mpd_client.seekcur(new_time)

@plugs.tag
def shuffle(self, random):
# As long as we don't work with waiting lists (aka playlist), this implementation is ok!
self.mpd_retry_with_mutex(self.mpd_client.random, 1 if random else 0)

@plugs.tag
def rewind(self):
"""
Expand All @@ -363,7 +358,6 @@ def replay(self):
@plugs.tag
def toggle(self):
"""Toggle pause state, i.e. do a pause / resume depending on current state"""
logger.debug("Toggle")
with self.mpd_lock:
self.mpd_client.pause()

Expand All @@ -378,8 +372,27 @@ def replay_if_stopped(self):
if self.mpd_status['state'] == 'stop':
self.play_folder(self.music_player_status['player_status']['last_played_folder'])

# Shuffle
def _shuffle(self, random):
# As long as we don't work with waiting lists (aka playlist), this implementation is ok!
self.mpd_retry_with_mutex(self.mpd_client.random, 1 if random else 0)

@plugs.tag
def repeatmode(self, mode):
def shuffle(self, option='toggle'):
if option == 'toggle':
if self.mpd_status['random'] == '0':
self._shuffle(1)
else:
self._shuffle(0)
elif option == 'enable':
self._shuffle(1)
elif option == 'disable':
self._shuffle(0)
else:
logger.error(f"'{option}' does not exist for 'shuffle'")

# Repeat
def _repeatmode(self, mode):
if mode == 'repeat':
repeat = 1
single = 0
Expand All @@ -394,6 +407,34 @@ def repeatmode(self, mode):
self.mpd_client.repeat(repeat)
self.mpd_client.single(single)

@plugs.tag
def repeat(self, option='toggle'):
if option == 'toggle':
if self.mpd_status['repeat'] == '0':
self._repeatmode('repeat')
elif self.mpd_status['repeat'] == '1' and self.mpd_status['single'] == '0':
self._repeatmode('single')
else:
self._repeatmode(None)
elif option == 'toggle_repeat':
if self.mpd_status['repeat'] == '0':
self._repeatmode('repeat')
else:
self._repeatmode(None)
elif option == 'toggle_repeat_single':
if self.mpd_status['single'] == '0':
self._repeatmode('single')
else:
self._repeatmode(None)
elif option == 'enable_repeat':
self._repeatmode('repeat')
elif option == 'enable_repeat_single':
self._repeatmode('single')
elif option == 'disable':
self._repeatmode(None)
else:
logger.error(f"'{option}' does not exist for 'repeat'")

@plugs.tag
def get_current_song(self, param):
return self.mpd_status
Expand Down
18 changes: 18 additions & 0 deletions src/jukebox/components/rpc_command_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
'package': 'player',
'plugin': 'ctrl',
'method': 'play_folder'},
'play': {
'package': 'player',
'plugin': 'ctrl',
'method': 'play',
'note': 'Play the currently selected song',
'ignore_card_removal_action': True},
'pause': {
'package': 'player',
'plugin': 'ctrl',
Expand All @@ -57,6 +63,18 @@
'plugin': 'ctrl',
'method': 'toggle',
'ignore_card_removal_action': True},
'shuffle': {
'package': 'player',
'plugin': 'ctrl',
'method': 'shuffle',
'note': 'Shuffle',
'ignore_card_removal_action': True},
'repeat': {
'package': 'player',
'plugin': 'ctrl',
'method': 'repeat',
'note': 'Repeat',
'ignore_card_removal_action': True},

# VOLUME
'set_volume': {
Expand Down
44 changes: 34 additions & 10 deletions src/webapp/public/locales/de/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@
"timer_fade_volume": "Fade volume",
"toggle_output": "Audio-Ausgang umschalten",
"sync_rfidcards_all": "Alle Audiodateien und Karteneinträge synchronisieren",
"sync_rfidcards_change_on_rfid_scan": "Aktivierung ändern für 'on RFID scan' "
"sync_rfidcards_change_on_rfid_scan": "Aktivierung ändern für 'on RFID scan'",
"next_song": "Nächster Song",
"pause": "Pause",
"play": "Abspielen",
"prev_song": "Vorheriger Song",
"shuffle": "Zufallswiedergabe",
"repeat": "Wiedergabe wiederholen",
"toggle": "Abspielen/Pause umschalten"
}
},
"controls-selector": {
Expand Down Expand Up @@ -56,8 +63,25 @@
"no-music-selected": "Es ist keine Musik ausgewählt.",
"loading-song-error": "Während des Ladens des Songs ist ein Fehler aufgetreten."
},
"volume": {
"title": "Lautstärke Stufen"
"audio": {
"repeat": {
"description": "Wähle den zu setzenden Status.",
"label-toggle": "Umschalten - Schaltet durch 1) Wiedergabeliste Wiederholen, 2) Song Wiederholen, 3) Wiederholen Deaktiveren",
"label-toggle-repeat": "Wiedergabeliste Wiederholen umschalten",
"label-toggle-repeat-single": "Song Wiederholen umschalten",
"label-enable-repeat": "Wiedergabeliste Wiederholen aktivieren",
"label-enable-repeat-single": "Song Wiederholen aktivieren",
"label-disable": "Wiederholen deaktivieren"
},
"shuffle": {
"description": "Wähle den zu setzenden Status.",
"label-toggle": "Umschalten",
"label-enable": "Aktivieren",
"label-disable": "Deaktivieren"
},
"volume": {
"title": "Lautstärke Stufen"
}
},
"timers": {
"description": "Wähle die Anzahl der Minuten nachdem die Aktion ausgeführt werden soll."
Expand Down Expand Up @@ -138,17 +162,17 @@
"player": {
"controls": {
"shuffle": {
"activate": "Shuffle aktivieren",
"deactivate": "Shuffle deaktivieren"
"enable": "Zufallswiedergabe aktivieren",
"disable": "Zufallswiedergabe deaktivieren"
},
"skip": "Zurück",
"prev_song": "Zurück",
"play": "Abspielen",
"pause": "Pause",
"next": "Weiter",
"next_song": "Weiter",
"repeat": {
"activate": "Wiederholen aktivieren",
"activate-single": "1 Wiederholen aktivieren",
"deactivate": "Wiederholen deaktivieren"
"enable": "Wiedergabeliste Wiederholen aktivieren",
"enable-single": "Song Wiederholen aktivieren",
"disable": "Wiederholen deaktivieren"
}
},
"cover": {
Expand Down
45 changes: 35 additions & 10 deletions src/webapp/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@
"timer_fade_volume": "Fade volume",
"toggle_output": "Toggle audio output",
"sync_rfidcards_all": "Sync all audiofiles and card entries",
"sync_rfidcards_change_on_rfid_scan": "Change activation of 'on RFID scan'"
"sync_rfidcards_change_on_rfid_scan": "Change activation of 'on RFID scan'",
"next_song": "Next song",
"pause": "Pause",
"play": "Play",
"prev_song": "Previous song",
"shuffle": "Shuffle",
"repeat": "Repeat",
"toggle": "Toggle Play/Pause"
}
},
"controls-selector": {
Expand Down Expand Up @@ -56,8 +63,25 @@
"no-music-selected": "No music selected",
"loading-song-error": "An error occurred while loading song."
},
"volume": {
"title": "Volume steps"
"audio": {
"repeat": {
"description": "Choose the state to set.",
"label-toggle": "Toggle - Loops through 1) Repeat playlist, 2) Repeat song, 3) Disable repeat",
"label-toggle-repeat": "Toggle Repeat Playlist",
"label-toggle-repeat-single": "Toggle Repeat Song",
"label-enable-repeat": "Enable Repeat Playlist",
"label-enable-repeat-single": "Enable Repeat Song",
"label-disable": "Disable"
},
"shuffle": {
"description": "Choose the state to set.",
"label-toggle": "Toggle",
"label-enable": "Enable",
"label-disable": "Disable"
},
"volume": {
"title": "Volume steps"
}
},
"timers": {
"description": "Choose the amount of minutes you want the action to be performed."
Expand Down Expand Up @@ -138,17 +162,17 @@
"player": {
"controls": {
"shuffle": {
"activate": "Activate shuffle",
"deactivate": "Deactivate shuffle"
"enable": "Enable shuffle",
"disable": "Disable shuffle"
},
"skip": "Skip previous track",
"prev_song": "Skip to previous track",
"play": "Play",
"pause": "Pause",
"next": "Skip next track",
"next_song": "Skip to next track",
"repeat": {
"activate": "Activate repeat",
"activate-single": "Activate single repeat",
"deactivate": "Deactivate repeat"
"enable": "Enable Playlist repeat",
"enable-single": "Enable Song repeat",
"disable": "Disable repeat"
}
},
"cover": {
Expand Down Expand Up @@ -207,6 +231,7 @@
},
"secondswipe": {
"title": "Second Swipe",
"description": "Second action after the same card swiped again",
"restart": "Restart playlist",
"toggle": "Toggle pause / play",
"skip": "Skip to next track",
Expand Down
13 changes: 10 additions & 3 deletions src/webapp/src/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,32 @@ const commands = {
plugin: 'ctrl',
method: 'pause',
},
previous: {
prev_song: {
_package: 'player',
plugin: 'ctrl',
method: 'prev',
},
next: {
next_song: {
_package: 'player',
plugin: 'ctrl',
method: 'next',
},
toggle: {
_package: 'player',
plugin: 'ctrl',
method: 'toggle',
},
shuffle: {
_package: 'player',
plugin: 'ctrl',
method: 'shuffle',
argKeys: ['option'],
},
repeat: {
_package: 'player',
plugin: 'ctrl',
method: 'repeatmode',
method: 'repeat',
argKeys: ['option'],
},
seek: {
_package: 'player',
Expand Down
34 changes: 32 additions & 2 deletions src/webapp/src/components/Cards/controls/actions/audio/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import React from 'react';

import CommandSelector from '../../command-selector';
import SliderChangeVolume from './slider-change-volume';
import OptionsSelector from '../../options-selector';

import { getActionAndCommand } from '../../../utils';

const SelectVolume = ({
const SelectAudioVolume = ({
actionData,
handleActionDataChange,
}) => {
Expand All @@ -23,8 +24,37 @@ const SelectVolume = ({
handleActionDataChange={handleActionDataChange}
/>
}
{command === 'shuffle' &&
<OptionsSelector
actionType="audio_shuffle"
actionData={actionData}
handleActionDataChange={handleActionDataChange}
optionLabel="cards.controls.actions.audio.shuffle.description"
options={[
{ labelKey: 'cards.controls.actions.audio.shuffle.label-toggle', value: 'toggle' },
{ labelKey: 'cards.controls.actions.audio.shuffle.label-enable', value: 'enable' },
{ labelKey: 'cards.controls.actions.audio.shuffle.label-disable', value: 'disable' },
]}
/>
}
{command === 'repeat' &&
<OptionsSelector
actionType="audio_repeat"
actionData={actionData}
handleActionDataChange={handleActionDataChange}
optionLabel="cards.controls.actions.audio.repeat.description"
options={[
{ labelKey: 'cards.controls.actions.audio.repeat.label-toggle', value: 'toggle' },
{ labelKey: 'cards.controls.actions.audio.repeat.label-toggle-repeat', value: 'toggle_repeat' },
{ labelKey: 'cards.controls.actions.audio.repeat.label-toggle-repeat-single', value: 'toggle_repeat_single' },
{ labelKey: 'cards.controls.actions.audio.repeat.label-enable-repeat', value: 'enable_repeat' },
{ labelKey: 'cards.controls.actions.audio.repeat.label-enable-repeat-single', value: 'enable_repeat_single' },
{ labelKey: 'cards.controls.actions.audio.repeat.label-disable', value: 'disable' },
]}
/>
}
</>
);
};

export default SelectVolume;
export default SelectAudioVolume;
Loading

0 comments on commit 03e64ff

Please sign in to comment.