Reducing the YouTube response time by 90%
In this blog post, we are going to cover how the audio from Youtube is being used in SUSI Smart Speaker and how we reduced the response time from ~40 seconds to ~4 seconds for an average music video length. First Approach Earlier, we were using MPV player’s inbuilt feature to fetch the YouTube music. However, MPV player was a bulky option and the music server had to be started every time before initiating a music video. video_process = subprocess.Popen(['mpv', '--no-video', 'https://www.youtube.com/watch?v=' + video_url[4:], '--really-quiet']) # nosec #pylint-disable type: ignore requests.get('http://localhost:7070/song/' + video_url) self.video_process = video_process stopAction.run() stopAction.detector.terminate() Making it Efficient To reduce the response time, we created a custom Music Server based on Flask,python-vlc and python-pafy which accepts requests from the main client and instructs the System to play the music with just 90% more efficiency. app = Flask(__name__) Instance = vlc.Instance('--no-video') player = Instance.media_player_new() url = '' @app.route('/song', methods=['GET']) def youtube(): vid = request.args.get('vid') url = 'https://www.youtube.com/watch?v=' + vid video = pafy.new(url) streams = video.audiostreams best = streams[3] playurl = best.url Media = Instance.media_new(playurl) Media.get_mrl() player.set_media(Media) player.play() display_message = {"song":"started"} resp = jsonify(display_message) resp.status_code = 200 return resp However, shifting to this Server removed the ability to process multiple queries and hence we were unable to pause/play/stop the music until it completed the time duration. We wanted to retain the ability to have ‘play/pause/stop’ actions without implementing multiprocessing or multithreading as it would’ve required extensive testing to successfully implement them without creating deadlocks and would’ve been overkill for a simple feature. Bringing Back the Lost Functionalities The first Step we took was to remove the vlc-python module and implement a way to obtain an URL that we use in another asynchronous music player. @app.route('/song', methods=['GET']) def youtube(): vid = request.args.get('vid') streams = video.audiostreams best = streams[3] playurl = best.url display_message = {"song": "started", "url": playurl} resp = jsonify(display_message) resp.status_code = 200 return resp The next issue was to actually find a way to run the Music Player asynchronously. We used the `subprocess. Popen` method and cvlc to play the songs asynchronously. try: x = requests.get('http://localhost:7070/song?vid=' + video_url[4:]) data = x.json() url = data['url'] video_process = subprocess.Popen(['cvlc', 'http' + url[5:], '--no-video']) self.video_process = video_process except Exception as e: logger.error(e); And this is how we were able to increase the efficiency of the music player while maintaining the functionalities. References https://helpmanual.io/help/cvlc/https://docs.python.org/3/library/subprocess.htmlhttps://pypi.org/project/pafy/
