diff --git a/ocp_pipeline/locale/en-us/load_game.intent b/ocp_pipeline/locale/en-us/load_game.intent new file mode 100644 index 0000000..2100c87 --- /dev/null +++ b/ocp_pipeline/locale/en-us/load_game.intent @@ -0,0 +1,3 @@ +load +load [the] game +load [the] (last|previous) [saved] game \ No newline at end of file diff --git a/ocp_pipeline/locale/en-us/next.intent b/ocp_pipeline/locale/en-us/next.intent index 0269afe..6c4ed2b 100644 --- a/ocp_pipeline/locale/en-us/next.intent +++ b/ocp_pipeline/locale/en-us/next.intent @@ -3,4 +3,5 @@ (play|go to) next (music|song|track|video|media) next next (song|track|music|movie|video|tune) -play next \ No newline at end of file +play next +(play|go to) [the] next (song|track|music|movie|video|tune) \ No newline at end of file diff --git a/ocp_pipeline/locale/en-us/open.intent b/ocp_pipeline/locale/en-us/open.intent index f03a7ee..e9e7dc8 100644 --- a/ocp_pipeline/locale/en-us/open.intent +++ b/ocp_pipeline/locale/en-us/open.intent @@ -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) \ No newline at end of file +open [the] (media|music|gui|video) (player|catalog|skills|menu|playback) \ No newline at end of file diff --git a/ocp_pipeline/locale/en-us/pause.intent b/ocp_pipeline/locale/en-us/pause.intent index 4161dcf..42e8ca7 100644 --- a/ocp_pipeline/locale/en-us/pause.intent +++ b/ocp_pipeline/locale/en-us/pause.intent @@ -1,2 +1,2 @@ pause -pause (music|song|track|video|media|playback) \ No newline at end of file +pause [the] (music|song|track|video|media|playback|game) \ No newline at end of file diff --git a/ocp_pipeline/locale/en-us/prev.intent b/ocp_pipeline/locale/en-us/prev.intent index e7c2b28..ee9a12e 100644 --- a/ocp_pipeline/locale/en-us/prev.intent +++ b/ocp_pipeline/locale/en-us/prev.intent @@ -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) \ No newline at end of file +previous (song|track|music|movie|video|tune) +(play|go to) [the] previous (song|track|music|movie|video|tune) \ No newline at end of file diff --git a/ocp_pipeline/locale/en-us/read.intent b/ocp_pipeline/locale/en-us/read.intent index 91c5ae0..3a5fe0c 100644 --- a/ocp_pipeline/locale/en-us/read.intent +++ b/ocp_pipeline/locale/en-us/read.intent @@ -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) \ No newline at end of file diff --git a/ocp_pipeline/locale/en-us/resume.intent b/ocp_pipeline/locale/en-us/resume.intent index 40f5c97..4d46186 100644 --- a/ocp_pipeline/locale/en-us/resume.intent +++ b/ocp_pipeline/locale/en-us/resume.intent @@ -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 \ No newline at end of file diff --git a/ocp_pipeline/locale/en-us/save_game.intent b/ocp_pipeline/locale/en-us/save_game.intent new file mode 100644 index 0000000..c2a59a4 --- /dev/null +++ b/ocp_pipeline/locale/en-us/save_game.intent @@ -0,0 +1,2 @@ +save +save [the] game \ No newline at end of file diff --git a/ocp_pipeline/opm.py b/ocp_pipeline/opm.py index b723c6a..ebe89e0 100644 --- a/ocp_pipeline/opm.py +++ b/ocp_pipeline/opm.py @@ -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 @@ -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" @@ -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""" @@ -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) @@ -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 @@ -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) @@ -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 @@ -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: @@ -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): @@ -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): @@ -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): @@ -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 @@ -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) @@ -1064,6 +1105,7 @@ 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): @@ -1071,6 +1113,7 @@ def _handle_legacy_audio_pause(self, message: 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): @@ -1078,6 +1121,7 @@ def _handle_legacy_audio_resume(self, message: 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): @@ -1085,6 +1129,7 @@ def _handle_legacy_audio_start(self, message: 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): @@ -1092,6 +1137,7 @@ def _handle_legacy_audio_end(self, message: 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 diff --git a/requirements.txt b/requirements.txt index d656040..c324737 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 \ No newline at end of file +ovos-utils[extras]>=0.3.5,<1.0.0 +ovos-plugin-manager>=0.5.0,<1.0.0 \ No newline at end of file diff --git a/translations/en-us/intents.json b/translations/en-us/intents.json index 753db64..4d2b4c6 100644 --- a/translations/en-us/intents.json +++ b/translations/en-us/intents.json @@ -18,15 +18,16 @@ "(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) [the] (music|song|track|video|media|playback|game)", "play" ], "read.intent": [ - "read (book|audiobook|audio book) {query}", + "read [the] (book|audiobook|audio book) {query}", "read {query}", "read {query} (book|audiobook|audio book)" ], @@ -34,18 +35,19 @@ "(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)", "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)" ], "featured.intent": [ "(open|show|display) (featured|) {media} (catalog|collection|playlist)", @@ -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" ] } \ No newline at end of file