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: save/load game intent #44

Merged
merged 5 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
3 changes: 3 additions & 0 deletions ocp_pipeline/locale/en-us/load_game.intent
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
load
load [the] game
load [the] (last|previous) [saved] game
3 changes: 2 additions & 1 deletion ocp_pipeline/locale/en-us/next.intent
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
(play|go to) next (music|song|track|video|media)
next
next (song|track|music|movie|video|tune)
play next
play next
(play|go to) [the] next (song|track|music|movie|video|tune)
2 changes: 1 addition & 1 deletion ocp_pipeline/locale/en-us/open.intent
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
open (OCP|O C P|common|ovos|open voice os) (player|media player)
open (OCP|O C P|ovos common play|common play|ovos media player|open voice os player) (home screen|home page|homescreen|homepage|menu)
open (OCP|O C P|ovos common play|open voice os common play|common play)
open (media|music|gui|video) (player|catalog|skills|menu|playback)
open [the] (media|music|gui|video) (player|catalog|skills|menu|playback)
2 changes: 1 addition & 1 deletion ocp_pipeline/locale/en-us/pause.intent
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pause
pause (music|song|track|video|media|playback)
pause [the] (music|song|track|video|media|playback|game)
3 changes: 2 additions & 1 deletion ocp_pipeline/locale/en-us/prev.intent
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
(play previous|previous) (music|song|track|video|media)
(play previous|previous|go back)
previous
previous (song|track|music|movie|video|tune)
previous (song|track|music|movie|video|tune)
(play|go to) [the] previous (song|track|music|movie|video|tune)
2 changes: 1 addition & 1 deletion ocp_pipeline/locale/en-us/read.intent
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
read (book|audiobook|audio book) {query}
read [the] (book|audiobook|audio book) {query}
read {query}
read {query} (book|audiobook|audio book)
2 changes: 1 addition & 1 deletion ocp_pipeline/locale/en-us/resume.intent
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
(unpause|resume)
(unpause|resume|continue|restart) (music|song|track|video|media|playback)
(unpause|resume|continue|restart) [the] (music|song|track|video|media|playback|game)
play
2 changes: 2 additions & 0 deletions ocp_pipeline/locale/en-us/save_game.intent
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
save
save [the] game
48 changes: 47 additions & 1 deletion ocp_pipeline/opm.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class OCPPlayerProxy:
player_state: PlayerState = PlayerState.STOPPED
media_state: MediaState = MediaState.UNKNOWN
media_type: MediaType = MediaType.GENERIC
skill_id: Optional[str] = None


# for easier typing
Expand All @@ -47,7 +48,7 @@ class OCPPlayerProxy:
class OCPPipelineMatcher(ConfidenceMatcherPipeline, OVOSAbstractApplication):
intents = ["play.intent", "open.intent", "media_stop.intent",
"next.intent", "prev.intent", "pause.intent", "play_favorites.intent",
"resume.intent", "like_song.intent"]
"resume.intent", "like_song.intent", "save_game.intent", "load_game.intent"]
intent_matchers = {}
intent_cache = f"{xdg_data_home()}/{get_xdg_base()}/intent_cache"

Expand Down Expand Up @@ -187,6 +188,8 @@ def register_ocp_intents(self):
self.add_event("ocp:media_stop", self.handle_stop_intent, is_intent=True)
self.add_event("ocp:search_error", self.handle_search_error_intent, is_intent=True)
self.add_event("ocp:like_song", self.handle_like_intent, is_intent=True)
self.add_event("ocp:save_game", self.handle_save_intent, is_intent=True)
self.add_event("ocp:load_game", self.handle_load_intent, is_intent=True)

def update_player_proxy(self, player: OCPPlayerProxy):
"""remember OCP session state"""
Expand Down Expand Up @@ -289,6 +292,7 @@ def handle_track_state_update(self, message: Message):
TrackState.PLAYING_MPRIS]:
player = self.get_player(message)
player.player_state = PlayerState.PLAYING
player = self._update_player_skill_id(player, message)
LOG.info(f"Session: {player.session_id} OCP PlayerState: PlayerState.PLAYING")
self.update_player_proxy(player)

Expand All @@ -310,6 +314,7 @@ def handle_player_state_update(self, message: Message):
if mtype is not None:
player.media_type = MediaType(pstate)
LOG.debug(f"Session: {player.session_id} MediaType: {player.media_type}")
player = self._update_player_skill_id(player, message)
self.update_player_proxy(player)

# pipeline
Expand Down Expand Up @@ -347,6 +352,21 @@ def match_high(self, utterances: List[str], lang: str, message: Message = None)

player = self.get_player(message)

if player.media_type == MediaType.GAME:
# if the user is currently playing a game
# disable: next/prev/shuffle/... intents
# enable: load/save intents
game_blacklist = ["next", "prev", "open", "like_song", "play_favorites"]
if match["name"] in game_blacklist:
LOG.info(f'Ignoring OCP intent match {match["name"]}, playing MediaType.GAME')
return None
else:
# if no game is being played, disable game specific intents
game_only = ["save_game"]
if match["name"] in game_only:
LOG.info(f'Ignoring OCP intent match {match["name"]}, not playing MediaType.GAME')
return None

if match["name"] == "play":
utterance = match["entities"].pop("query")
return self._process_play_query(utterance, lang, match)
Expand Down Expand Up @@ -535,6 +555,14 @@ def _normalize_media_enum(m: Union[int, MediaType]):
return e
raise ValueError(f"{m} is not a valid media type")

def handle_save_intent(self, message: Message):
skill_id = self.get_player(message).skill_id
self.bus.emit(message.forward(f"ovos.common_play.{skill_id}.save"))

def handle_load_intent(self, message: Message):
skill_id = self.get_player(message).skill_id
self.bus.emit(message.forward(f"ovos.common_play.{skill_id}.load"))

def handle_play_intent(self, message: Message):

if not len(self.skill_aliases): # skill_id registered when skills load
Expand Down Expand Up @@ -574,6 +602,8 @@ def handle_play_intent(self, message: Message):

# ovos-PHAL-plugin-mk1 will display music icon in response to play message
player = self.get_player(message)
player.skill_id = best.skill_id
self.update_player_proxy(player)
if not player.ocp_available:
self.legacy_play(results, query, message=message)
else:
Expand Down Expand Up @@ -603,6 +633,7 @@ def handle_stop_intent(self, message: Message):
self.ocp_api.stop(source_message=message)
player = self.get_player(message)
player.player_state = PlayerState.STOPPED
player.skill_id = None
self.update_player_proxy(player)

def handle_next_intent(self, message: Message):
Expand Down Expand Up @@ -633,6 +664,7 @@ def handle_pause_intent(self, message: Message):
self.ocp_api.pause(source_message=message)
player = self.get_player(message)
player.player_state = PlayerState.PAUSED
player = self._update_player_skill_id(player, message)
self.update_player_proxy(player)

def handle_resume_intent(self, message: Message):
Expand All @@ -645,6 +677,7 @@ def handle_resume_intent(self, message: Message):
self.ocp_api.resume(source_message=message)
player = self.get_player(message)
player.player_state = PlayerState.PLAYING
player = self._update_player_skill_id(player, message)
self.update_player_proxy(player)

def handle_search_error_intent(self, message: Message):
Expand Down Expand Up @@ -833,6 +866,13 @@ def get_player(self, message: Optional[Message] = None, timeout=1) -> OCPPlayerP
player = self._player_sync(player, message, timeout)
return player

@staticmethod
def _update_player_skill_id(player, message):
skill_id = message.data.get("skill_id") or message.context.get("skill_id")
if skill_id and skill_id != OCP_ID:
player.skill_id = skill_id
return player

@staticmethod
def normalize_results(results: RawResultsList) -> NormalizedResultsList:
# support Playlist and MediaEntry objects in tracks
Expand Down Expand Up @@ -1055,6 +1095,7 @@ def legacy_play(self, results: NormalizedResultsList, phrase="",
playing = True
self.legacy_api.play(real_uri, utterance=phrase, source_message=message)
player.player_state = PlayerState.PLAYING
player.skill_id = r.skill_id
self.update_player_proxy(player)
else:
self.legacy_api.queue(real_uri, source_message=message)
Expand All @@ -1064,34 +1105,39 @@ def _handle_legacy_audio_stop(self, message: Message):
if not player.ocp_available:
player.player_state = PlayerState.STOPPED
player.media_state = MediaState.NO_MEDIA
player.skill_id = None
self.update_player_proxy(player)

def _handle_legacy_audio_pause(self, message: Message):
player = self.get_player(message)
if not player.ocp_available and player.player_state == PlayerState.PLAYING:
player.player_state = PlayerState.PAUSED
player.media_state = MediaState.LOADED_MEDIA
player = self._update_player_skill_id(player, message)
self.update_player_proxy(player)

def _handle_legacy_audio_resume(self, message: Message):
player = self.get_player(message)
if not player.ocp_available and player.player_state == PlayerState.PAUSED:
player.player_state = PlayerState.PLAYING
player.media_state = MediaState.LOADED_MEDIA
player = self._update_player_skill_id(player, message)
self.update_player_proxy(player)

def _handle_legacy_audio_start(self, message: Message):
player = self.get_player(message)
if not player.ocp_available:
player.player_state = PlayerState.PLAYING
player.media_state = MediaState.LOADED_MEDIA
player = self._update_player_skill_id(player, message)
self.update_player_proxy(player)

def _handle_legacy_audio_end(self, message: Message):
player = self.get_player(message)
if not player.ocp_available:
player.player_state = PlayerState.STOPPED
player.media_state = MediaState.END_OF_MEDIA
player.skill_id = None
self.update_player_proxy(player)

@classmethod
Expand Down
5 changes: 2 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
ovos-workshop>=0.1.7,<4.0.0
ovos-classifiers
ovos-utils>=0.3.5,<1.0.0
ovos-plugin-manager>=0.5.0,<1.0.0
langcodes
ovos-utils[extras]>=0.3.5,<1.0.0
ovos-plugin-manager>=0.5.0,<1.0.0
19 changes: 15 additions & 4 deletions translations/en-us/intents.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,30 @@
"(play|go to) next (music|song|track|video|media)",
"play next",
"next",
"next (song|track|music|movie|video|tune)"
"next (song|track|music|movie|video|tune)",
"(play|go to) [the] next (song|track|music|movie|video|tune)"
],
"resume.intent": [
"(unpause|resume)",
"(unpause|resume|continue|restart) (music|song|track|video|media|playback)",
"(unpause|resume|continue|restart) (music|song|track|video|media|playback|game)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Align game-related phrases across intents

The resume and pause intents handle games differently. Consider using consistent phrasing for game-related commands.

"(unpause|resume|continue|restart) (music|song|track|video|media|playback|game)"
-"pause [the] (music|song|track|video|media|playback|game)"
+"pause [the] (music|song|track|video|media|playback|game|gameplay)"

Also applies to: 44-44

"play"
],
"read.intent": [
"read (book|audiobook|audio book) {query}",
"read [the] (book|audiobook|audio book) {query}",
"read {query}",
"read {query} (book|audiobook|audio book)"
],
"prev.intent": [
"(play previous|go back one) (music|song|track|video|media)",
"(play previous|previous) (music|song|track|video|media)",
"(play previous|previous|go back)",
"(play|go to) [the] previous (song|track|music|movie|video|tune)",
"previous",
"previous (song|track|music|movie|video|tune)"
],
"pause.intent": [
"pause",
"pause (music|song|track|video|media|playback)"
"pause [the] (music|song|track|video|media|playback|game)"
],
"open.intent": [
"open (OCP|O C P|common|ovos|open voice os) (player|media player)",
Expand All @@ -59,5 +61,14 @@
"stop",
"stop (playback|media|media playback|music|movie|movies|noise)",
"stop everything"
],
"save_game.intent": [
"save",
"save [the] game"
],
"load_game.intent": [
"load",
"load [the] game",
"load [the] (last|previous) [saved] game"
]
}
Loading