gmusicaiy icon indicating copy to clipboard operation
gmusicaiy copied to clipboard

AIY project update no action.py

Open joshfokis opened this issue 7 years ago • 16 comments

Creating the issue since the AIY project has been updated and removed the action.py and main.py. Now the example code in the readme is out dated, I have attempted without testing to create a small example based on your code example and the assistant_library_with_local_commands_demo.py

from playscroll import Player

if text == 'play (your playlist)':
    assistant.stop_conversation()
    playlist = text.replace("play","").strip()
    if Player.load_playlist(playlist) is not None:
        aiy.audio.say("Playing " + playlist)
        Player.start_playlist()

        RPi.GPIO.setmode(RPi.GPIO.BCM)
        RPi.GPIO.setup(23, RPi.GPIO.IN)

        # Wait for button press to stop
        while Player.playing and RPi.GPIO.input(23):
            time.sleep(1)

        Player.stop()`

There might be a better way to do it but as a quick example that is what I have come up with. Again I have not tested this but I don't see why it wouldn't work.

joshfokis avatar Nov 04 '17 07:11 joshfokis

Thanks for raising this issue, hadn't realised the API had changed so radically. Hopefully I'll be able to find the time to grab the latest AIY image and check out the latest docs.

Tom-Archer avatar Nov 04 '17 22:11 Tom-Archer

You probably need to remove any references to "scrollphat" if you don't have it connected to display the currently playing track.

Hope that helps.

On 5 November 2017 at 19:34, dougtran [email protected] wrote:

After enabling I2C, I now have the following error:

Traceback (most recent call last): File "src/aiy_music.py", line 34, in from playscroll import Player File "/home/pi/AIY-voice-kit-python/src/playscroll.py", line 10, in import scrollphat File "/usr/lib/python3/dist-packages/scrollphat/init.py", line 20, in controller = IS31FL3730(smbus, font) File "/usr/lib/python3/dist-packages/scrollphat/IS31FL3730.py", line 21, in init self.set_mode(self.i2cConstants.MODE_5X11)

File "/usr/lib/python3/dist-packages/scrollphat/IS31FL3730.py", line 62, in set_mode self.bus.write_i2c_block_data(self.i2cConstants.I2C_ADDR, self.i2cConstants.CMD_SET_MODE, [self.i2cConstants.MODE_5X11]) OSError: [Errno 121] Remote I/O error

— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/Tom-Archer/gmusicaiy/issues/4#issuecomment-341998689, or mute the thread https://github.com/notifications/unsubscribe-auth/ARfAYzV-2wYQtrSUA3eW7AMpCFWp5t2tks5szg3DgaJpZM4QR653 .

Tom-Archer avatar Nov 05 '17 20:11 Tom-Archer

Thank you, I realized that after I re-read the project README. I set the enable_display = False and that fixed that error. However, AIY still doesn't know what to do when I say "play {nameofsound}". Where should the code from joshfokis be inserted in the assistant_library_with_local_commands_demo.py file? Right now, I have the code as follows:

elif event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED and event.args:
    print('You said:', event.args['text'])
    text = event.args['text'].lower()
    if text == 'power off':
        assistant.stop_conversation()
        power_off_pi()
    elif text == 'reboot':
        assistant.stop_conversation()
        reboot_pi()
    elif text == 'ip address':
        assistant.stop_conversation()
        say_ip()

    elif text == 'play (your playlist)':
        assistant.stop_conversation()
        playlist = text.replace("play","").strip()
        if Player.load_playlist(playlist) is not None:
            aiy.audio.say("Playing " + playlist)
            Player.start_playlist()

            RPi.GPIO.setmode(RPi.GPIO.BCM)
            RPi.GPIO.setup(23, RPi.GPIO.IN)

            # Wait for button press to stop
            while Player.playing and RPi.GPIO.input(23):
                time.sleep(1)
            Player.stop()

dougtran avatar Nov 05 '17 20:11 dougtran

That code should be an elif statement after elif text == 'ip address':

joshfokis avatar Nov 05 '17 20:11 joshfokis

That's what I did as shown in my above post.

dougtran avatar Nov 05 '17 20:11 dougtran

Sorry I am replying on my phone but your problem is you will need to remove the (your playlist) from the equals statement and set a static playlist or use that to match the playlists in a list.

joshfokis avatar Nov 05 '17 20:11 joshfokis

Aah, that make sense, I was wondering how the code work using the (your playlist) as an argument.

dougtran avatar Nov 05 '17 20:11 dougtran

Ok, still not working. This is what I tried. in mpsyt, did a search and saved the playlist as music. Replaced (your playlist" with music, entire line reads.

elif text == 'play music':

When I say "play music", scripts crash and receive the following: TypeError: load_playlist() missing 1 required positional argument: 'name'

I assume I'm not creating the playlist correctly? This is also the first time I'm using mpsyt.

dougtran avatar Nov 06 '17 00:11 dougtran

You need to pass a playlist and also Player expects your login info. I am testing this currently to get it to work also. I will update with once I get it going.

joshfokis avatar Nov 06 '17 06:11 joshfokis

Ok, I'm getting two threads mixed up, one from "Play music from Youtube" by mikerr and this one for gmusicaiy. So the playlist, is the Google Music's playlist and need to edit playscroll.py with an actual Google account credentials?

dougtran avatar Nov 06 '17 23:11 dougtran

So I got this working and this is what my local commands python file looks like

import logging
import subprocess
import sys

import aiy.assistant.auth_helpers
import aiy.audio
import aiy.voicehat
from google.assistant.library import Assistant
from google.assistant.library.event import EventType
import RPi.GPIO
import time

import playscroll

logging.basicConfig(
    level=logging.INFO,
    format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
)

def music(playlist):
    player = playscroll.Player('email address', 'password', 'device id')
    if player.load_playlist(playlist) is not None:
        aiy.audio.say("Playing " + playlist)
        player.start_playlist()

        RPi.GPIO.setmode(RPi.GPIO.BCM)
        RPi.GPIO.setup(23, RPi.GPIO.IN)

        # Wait for button press to stop
        while player.playing and RPi.GPIO.input(23):
            time.sleep(1)
        player.stop()

def power_off_pi():
    ...

def reboot_pi():
    ...

def say_ip():
    ...

def process_event(assistant, event):
    status_ui = aiy.voicehat.get_status_ui()
    if event.type == EventType.ON_START_FINISHED:
        status_ui.status('ready')
        if sys.stdout.isatty():
            print('Say "OK, Google" then speak, or press Ctrl+C to quit...')

    elif event.type == EventType.ON_CONVERSATION_TURN_STARTED:
        status_ui.status('listening')

    elif event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED and event.args:
        print('You said:', event.args['text'])
        text = event.args['text'].lower()
        if text == 'power off':
            assistant.stop_conversation()
            power_off_pi()
        elif text == 'reboot':
            assistant.stop_conversation()
            reboot_pi()
        elif text == 'ip address':
            assistant.stop_conversation()
            say_ip()
        elif text.split()[0] == 'play':
            assistant.stop_conversation()
            playlist = text.replace('play','').strip()
            music(playlist)

    elif event.type == EventType.ON_END_OF_UTTERANCE:
        status_ui.status('thinking')

    elif event.type == EventType.ON_CONVERSATION_TURN_FINISHED:
        status_ui.status('ready')

    elif event.type == EventType.ON_ASSISTANT_ERROR and event.args and event.args['is_fatal']:
        sys.exit(1)


def main():
    credentials = aiy.assistant.auth_helpers.get_assistant_credentials()
    with Assistant(credentials) as assistant:
        for event in assistant.start():
            process_event(assistant, event)


if __name__ == '__main__':
    main()

I ran a python shell to get the device ids that are in play music. Once I did that it worked without any issues. Hope this helps.

joshfokis avatar Nov 07 '17 04:11 joshfokis

Thanks, this works! Any chance this can be adapted to play music that are not on the playlist like what mikerr did to play youtube?

dougtran avatar Nov 08 '17 00:11 dougtran

@joshfokis You might not need to create an instance of the Player class every time the command is triggered. But otherwise, nice work!

@dougtran It certainly could be adapted, but it's unlikely to be something I have the time (or inclination) to do. Sorry!

Tom-Archer avatar Nov 08 '17 08:11 Tom-Archer

Is there going to be an official update to the repo, or will we just use josh's code manually?

theg00s3 avatar Apr 18 '18 02:04 theg00s3

I noticed one flaw in this setup and have a quick dirty workaround. If you launch "okay google" but don't give it a response it will hit an error on the line: elif text.split()[0] == 'play': I added a new if to the top to catch a blank answer

if text == '': #Nothing was said but the player will want to still parse and fail aiy.audio.say('I did not hear you. can you repeat that') pass

etruj avatar Aug 23 '18 21:08 etruj

i had a lot of errors thrown up trying these bits of code, editted together my own 99% working script, but it still crashes shortly after finding the playlist with a segmentation fault. any advice would be great.

import sys
sys.path.append('/home/pi/AIY-projects-python/src/examples/voice')
import logging
import platform
import subprocess
import sys
import playscroll
import time
import RPi.GPIO

from google.assistant.library.event import EventType

from aiy.assistant import auth_helpers
from aiy.assistant.library import Assistant
from aiy.board import Board, Led
from aiy.voice import tts
import aiy.voice.audio

logging.basicConfig(
    level=logging.INFO,
    format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
)

def music(playlist):
    player = playscroll.Player('myemail', 'mypassword', 'adeviceid')
    if player.load_playlist(playlist) is not None:
        tts.say("Playing " + playlist)
        player.start_playlist()

        RPi.GPIO.setmode(RPi.GPIO.BCM)
        RPi.GPIO.setup(23, RPi.GPIO.IN)

        # Wait for button press to stop
        while player.playing and RPi.GPIO.input(23):
            time.sleep(1)
        player.stop()

def power_off_pi():
    ...

def reboot_pi():
    ...

def say_ip():
    ...

def process_event(assistant, led, event):
    logging.info(event)

    if event.type == EventType.ON_START_FINISHED:
        led.state = Led.BEACON_DARK  # Ready.
        print('Say "OK, Google" then speak, or press Ctrl+C to quit...')

    elif event.type == EventType.ON_CONVERSATION_TURN_STARTED:
        led.state = Led.ON  # Listening.

    elif event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED and event.args:
        print('You said:', event.args['text'])
        text = event.args['text'].lower()
        if text == '': #Nothing was said but the player will want to still parse and fail aiy.audio.say('I did not hear you. can you repeat that')
            pass
        if text == 'power off':
            assistant.stop_conversation()
            power_off_pi()
        elif text == 'reboot':
            assistant.stop_conversation()
            reboot_pi()
        elif text == 'ip address':
            assistant.stop_conversation()
            say_ip()
        elif text.split()[0] == 'play':
            assistant.stop_conversation()
            playlist = text.replace('play','').strip()
            music(playlist)

    elif event.type == EventType.ON_END_OF_UTTERANCE:
        led.state = Led.PULSE_QUICK  # Thinking.
    elif (event.type == EventType.ON_CONVERSATION_TURN_FINISHED
          or event.type == EventType.ON_CONVERSATION_TURN_TIMEOUT
          or event.type == EventType.ON_NO_RESPONSE):
        led.state = Led.BEACON_DARK  # Ready.
    elif event.type == EventType.ON_ASSISTANT_ERROR and event.args and event.args['is_fatal']:
        sys.exit(1)

def main():
    logging.basicConfig(level=logging.INFO)

    credentials = auth_helpers.get_assistant_credentials()
    with Board() as board, Assistant(credentials) as assistant:
        for event in assistant.start():
            process_event(assistant, board.led, event)


if __name__ == '__main__':
    main()

im using the lastest aiy voicekit image

here's my slightly altered playscroll.py

import gmusicapi
#from gmusicapi import Mobileclient
import vlc
#from vlc import vlc.EventType, Instance
from threading import Thread
import json
import os.path
import time

#enable_display = False
#if enable_display:
#    import scrollphat
#    scrollphat.set_brightness(5)

class Player(object):

    def __init__(self, email, password, device_id):
        self.api = gmusicapi.Mobileclient()
        self.vlc = vlc.Instance()
        self.loaded_tracks = []
        self.playing = False
        self.thread_running = False
        
        self.api.login(email, password, device_id)
        if os.path.isfile("songs.json"):
            # Load from file
            print("Found songs data.")
            with open('songs.json') as input_file:
                self.song_library = json.load(input_file)
        else:
            self.song_library = self.api.get_all_songs()
            # Save to file
            with open('songs.json', 'w') as output_file:
                json.dump(self.song_library, output_file)    
        
    def load_playlist(self, name):
        name = name.strip().lower()
        print("Looking for...", name)
        if os.path.isfile("playlists.json"):
            # Load from file
            print("Found playlist data.")
            with open('playlists.json') as input_file:
                self.playlists = json.load(input_file)
        else:
            self.playlists = self.api.get_all_user_playlist_contents()
            # Save to file
            with open('playlists.json', 'w') as output_file:
                json.dump(self.playlists, output_file)
            
        self.loaded_tracks = []
        for playlist_dict in self.playlists:
            playlist_name = playlist_dict['name'].strip().lower()
            if (playlist_name == name) or (name in playlist_name):
                print("Found match...", playlist_dict['name'])
                for track_dict in playlist_dict['tracks']:
                    self.loaded_tracks.append(track_dict)
                return playlist_dict['name']
            else:
                print("Found...", playlist_dict['name'])
        return None
 
    def end_callback(self, event, track_index):
        if track_index < len(self.loaded_tracks):
            self.play_song(self.loaded_tracks[track_index])
            event_manager = self.player.event_manager()
            event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback, track_index + 1)
            self.playing = True
        else:
            self.playing = False

    def start_playlist(self):
        if len(self.loaded_tracks) > 0:
            self.play_song(self.loaded_tracks[0])
        
            if len(self.loaded_tracks) > 1:
                event_manager = self.player.event_manager()
                event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback, 1)
  
    def play_song(self, song_dict):
        stream_url = self.api.get_stream_url(song_dict['trackId'])
        self.player = self.vlc.media_player_new()
        media = self.vlc.media_new(stream_url)
        self.player.set_media(media)
        self.player.play()

        song_string = ""
        if (song_dict['source'] == '2'):
            song_string = self.get_song_details(song_dict)
        else:
            song_string = self.get_local_song_details(song_dict['trackId'])

        print("Playing...",song_string)
        
       # if enable_display:
        #    scrollphat.clear()
        #    scrollphat.write_string(" "*5+song_string)

         #   if not self.thread_running:
          #      thread = Thread(target=self.scroll_string)
          #      thread.start()

        self.playing = True

    def scroll_string(self):
        self.thread_running = True
        while self.thread_running:
            scrollphat.scroll()
            time.sleep(0.1)

    def stop(self):
        if self.player != None:
            self.player.stop()
        #if enable_display:
        #    scrollphat.clear()
        self.thread_running = False
        self.playing = False

    def get_local_song_details(self, track_id):
        for song_dict in self.song_library:
            if track_id == song_dict['id']:
                return song_dict['albumArtist']+" - "+song_dict['title']

    def get_song_details(self, song_dict):
        return song_dict['track']['albumArtist']+" - "+song_dict['track']['title']


rawesomeawesome avatar Jan 27 '19 16:01 rawesomeawesome