def start_playing(self, on_exit, args):
        """
        Runs the given args in subprocess.Popen, and then calls the function
        on_exit when the subprocess completes.
        on_exit is a callable object, and args is a lists/tuple of args
        that would give to subprocess.Popen.
        """
        # print(args.get('cache'))
        if "cache" in args.keys() and os.path.isfile(args["cache"]):
            thread = threading.Thread(
                target=self.run_mpg123, args=(on_exit, args["cache"])
            )
        else:
            thread = threading.Thread(
                target=self.run_mpg123,
                args=(on_exit, args["mp3_url"], args["expires"], args["get_time"]),
            )
            cache_thread = threading.Thread(
                target=self.download_song,
                args=(
                    args["song_id"],
                    args["song_name"],
                    args["artist"],
                    args["mp3_url"],
                ),
            )
            cache_thread.start()
        thread.start()
        self.MUSIC_THREADS.append(thread)
        self.MUSIC_THREADS = [i for i in self.MUSIC_THREADS if i.is_alive()]
        lyric_download_thread = threading.Thread(target=self.download_lyric)
        lyric_download_thread.start()
        tlyric_download_thread = threading.Thread(
            target=self.download_lyric, args=(True,)
        )
        tlyric_download_thread.start()
        # returns immediately after the thread starts
        return thread

        Runs the given args in subprocess.Popen, and then calls the function
        on_exit when the subprocess completes.
        on_exit is a callable object, and args is a lists/tuple of args
        that would give to subprocess.Popen.
        
NEMbox/player.py