From ac161fed9c2b230f6215e6a79b7b084692b86dc8 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:27:33 +0330 Subject: [PATCH 01/32] feat : NavaThread added --- nava/thread.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 nava/thread.py diff --git a/nava/thread.py b/nava/thread.py new file mode 100644 index 0000000..99b214f --- /dev/null +++ b/nava/thread.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +"""Nava thread.""" + +import sys +import threading + + +class NavaThread(threading.Thread): + def __init__(self, *args, **kwargs): + super(NavaThread, self).__init__(*args, **kwargs) + self.play_process = None + + def run(self): + if self._target is not None: + self.play_process = self._target(*self._args, **self._kwargs) + + def stop(self): + sys_platform = sys.platform + if sys_platform == "win32": + import winsound + winsound.PlaySound(None, winsound.SND_PURGE) + else: + if self.play_process is not None: + self.play_process.kill() + self.play_process.terminate() \ No newline at end of file From e10c3072f76d00bd61f01719c3ed1b6dab37e5c6 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:30:49 +0330 Subject: [PATCH 02/32] fix : linux and mac async functions removed --- nava/functions.py | 44 +++++++------------------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index cb2953a..7bd4175 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -75,7 +75,6 @@ def __play_win(sound_path, is_async=True): :return: None """ import winsound - # If is_async is ture, play async play_flags = winsound.SND_FILENAME | (is_async & winsound.SND_ASYNC) winsound.PlaySound(sound_path, play_flags) @@ -92,7 +91,7 @@ def __play_linux(sound_path, is_async=True): :return: None or sound thread (depending on async flag) """ if is_async: - sound_thread = threading.Thread(target=__play_async_linux, + sound_thread = threading.Thread(target=__play_sync_linux, args=(sound_path,), daemon=True) sound_thread.start() @@ -107,30 +106,16 @@ def __play_sync_linux(sound_path): :param sound_path: sound path to be played :type sound_path: str - :return: None + :return: process """ - _ = subprocess.check_call(["aplay", + proc = subprocess.check_call(["aplay", sound_path], shell=False, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - - -def __play_async_linux(sound_path): - """ - Play sound asynchronously in Linux. + return proc - :param sound_path: sound path to be played - :type sound_path: str - :return: None - """ - proc = subprocess.Popen(["aplay", - sound_path], - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - play_processes.append(proc) @quote @@ -145,7 +130,7 @@ def __play_mac(sound_path, is_async=True): :return: None or sound thread, depending on the flag """ if is_async: - sound_thread = threading.Thread(target=__play_async_mac, + sound_thread = threading.Thread(target=__play_sync_mac, args=(sound_path,), daemon=True) sound_thread.start() @@ -162,28 +147,13 @@ def __play_sync_mac(sound_path): :type sound_path: str :return: None """ - _ = subprocess.check_call(["afplay", + proc = subprocess.check_call(["afplay", sound_path], shell=False, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - - -def __play_async_mac(sound_path): - """ - Play sound asynchronously in macOS. - - :param sound_path: sound path to be played - :type sound_path: str - :return: None - """ - proc = subprocess.Popen(["afplay", - sound_path], - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - play_processes.append(proc) + return proc def path_check(func): From d8fe2a2fb0884a0bede3611afa73ead3b99e5c3d Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:37:32 +0330 Subject: [PATCH 03/32] fix : __play_win function updated --- nava/functions.py | 30 ++++++++++++++++++++++++------ nava/params.py | 3 +++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 7bd4175..2605b99 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -12,11 +12,8 @@ from .params import SOUND_FILE_PATH_TYPE_ERROR from .errors import NavaBaseError -""" -List of all aplay processes -""" -play_processes = [] - +def sound_id_gen(): + pass def nava_help(): """ @@ -76,7 +73,28 @@ def __play_win(sound_path, is_async=True): """ import winsound play_flags = winsound.SND_FILENAME | (is_async & winsound.SND_ASYNC) - winsound.PlaySound(sound_path, play_flags) + + if is_async: + sound_thread = threading.Thread(target=__play_win_by_flags, + args=(sound_path, play_flags), + daemon=True) + sound_thread.start() + else: + winsound.PlaySound(sound_path, play_flags) + + +def __play_win_by_flags(sound_path, flags): + """ + Play sound in Windows. + + :param sound_path: sound path + :type sound_path: str + :param flags: different mode flags + :type flags: winsound flags + :return: None + """ + import winsound + winsound.PlaySound(sound_path, flags) @quote diff --git a/nava/params.py b/nava/params.py index 2c1396c..c6c26a7 100644 --- a/nava/params.py +++ b/nava/params.py @@ -11,3 +11,6 @@ SOUND_FILE_PLAY_ERROR = "Sound can not play due to some issues." SOUND_FILE_EXIST_ERROR = "Given sound file doesn't exist." SOUND_FILE_PATH_TYPE_ERROR = "Sound file's path should be a string." + +_play_threads_map = dict() +_play_threads_counter = 0 \ No newline at end of file From 4d5e26434f579f3dd49abc1a1c1ef9bde2008e03 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:40:46 +0330 Subject: [PATCH 04/32] feat : sound_id_gen function added --- nava/functions.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 2605b99..496fef6 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -6,14 +6,16 @@ import shlex from functools import wraps import threading - from .params import OVERVIEW from .params import SOUND_FILE_PLAY_ERROR, SOUND_FILE_EXIST_ERROR from .params import SOUND_FILE_PATH_TYPE_ERROR from .errors import NavaBaseError +from . import params def sound_id_gen(): - pass + params._play_threads_counter +=1 + sound_id = params._play_threads_counter + 1000 + return sound_id def nava_help(): """ @@ -79,6 +81,9 @@ def __play_win(sound_path, is_async=True): args=(sound_path, play_flags), daemon=True) sound_thread.start() + sound_id = sound_id_gen() + params._play_threads_map[sound_id] = sound_thread + return sound_id else: winsound.PlaySound(sound_path, play_flags) @@ -113,7 +118,9 @@ def __play_linux(sound_path, is_async=True): args=(sound_path,), daemon=True) sound_thread.start() - return sound_thread + sound_id = sound_id_gen() + params._play_threads_map[sound_id] = sound_thread + return sound_id else: __play_sync_linux(sound_path) @@ -152,7 +159,9 @@ def __play_mac(sound_path, is_async=True): args=(sound_path,), daemon=True) sound_thread.start() - return sound_thread + sound_id = sound_id_gen() + params._play_threads_map[sound_id] = sound_thread + return sound_id else: __play_sync_mac(sound_path) From 25892c14fd23994e3e2b11b40f63296908979784 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:42:34 +0330 Subject: [PATCH 05/32] feat : stop and stop_all functions added --- nava/functions.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nava/functions.py b/nava/functions.py index 496fef6..d63b96e 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -12,6 +12,13 @@ from .errors import NavaBaseError from . import params +def stop(sound_id): + params._play_threads_map[sound_id].stop() + +def stop_all(): + for thread in params._play_threads_map.values(): + thread.stop() + def sound_id_gen(): params._play_threads_counter +=1 sound_id = params._play_threads_counter + 1000 From 2e83244be42019998665a56a4a9402ff1a378745 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:44:28 +0330 Subject: [PATCH 06/32] fix : use NavaThread instead of Thread --- nava/__init__.py | 7 +++---- nava/functions.py | 10 +++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/nava/__init__.py b/nava/__init__.py index 6ed050a..99732e6 100644 --- a/nava/__init__.py +++ b/nava/__init__.py @@ -2,10 +2,9 @@ """Nava modules.""" from .params import NAVA_VERSION from .errors import NavaBaseError -from .functions import play, cleanup_processes - +from .functions import play, stop, stop_all import atexit -# Async play processes clean up -atexit.register(cleanup_processes) + +atexit.register(stop_all) __version__ = NAVA_VERSION diff --git a/nava/functions.py b/nava/functions.py index d63b96e..38bca1e 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -5,7 +5,7 @@ import os import shlex from functools import wraps -import threading +from .thread import NavaThread from .params import OVERVIEW from .params import SOUND_FILE_PLAY_ERROR, SOUND_FILE_EXIST_ERROR from .params import SOUND_FILE_PATH_TYPE_ERROR @@ -18,7 +18,7 @@ def stop(sound_id): def stop_all(): for thread in params._play_threads_map.values(): thread.stop() - + def sound_id_gen(): params._play_threads_counter +=1 sound_id = params._play_threads_counter + 1000 @@ -84,7 +84,7 @@ def __play_win(sound_path, is_async=True): play_flags = winsound.SND_FILENAME | (is_async & winsound.SND_ASYNC) if is_async: - sound_thread = threading.Thread(target=__play_win_by_flags, + sound_thread = NavaThread(target=__play_win_by_flags, args=(sound_path, play_flags), daemon=True) sound_thread.start() @@ -121,7 +121,7 @@ def __play_linux(sound_path, is_async=True): :return: None or sound thread (depending on async flag) """ if is_async: - sound_thread = threading.Thread(target=__play_sync_linux, + sound_thread = NavaThread(target=__play_sync_linux, args=(sound_path,), daemon=True) sound_thread.start() @@ -162,7 +162,7 @@ def __play_mac(sound_path, is_async=True): :return: None or sound thread, depending on the flag """ if is_async: - sound_thread = threading.Thread(target=__play_sync_mac, + sound_thread = NavaThread(target=__play_sync_mac, args=(sound_path,), daemon=True) sound_thread.start() From bfc4d30d44cfcdad67015752e795f8a6a93f0872 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:49:22 +0330 Subject: [PATCH 07/32] fix : minor bug in play function windows section fixed --- nava/functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nava/functions.py b/nava/functions.py index 38bca1e..f2400c5 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -232,7 +232,7 @@ def play(sound_path, is_async=True): try: sys_platform = sys.platform if sys_platform == "win32": - __play_win(sound_path, is_async) + return __play_win(sound_path, is_async) elif sys_platform == "darwin": return __play_mac(sound_path, is_async) else: From 3954a8b7206e2d467a2393b26cd23d01d84ae7bb Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 09:50:42 +0330 Subject: [PATCH 08/32] fix : cleanup_processes function removed --- nava/functions.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index f2400c5..8f611bd 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -58,18 +58,6 @@ def quoter(sound_path, *args, **kwargs): return func(sound_path, *args, **kwargs) return quoter - -def cleanup_processes(): - """ - Cleanup undead play processes after module exit. - - :return: None - """ - for proc in play_processes: - proc.kill() - proc.terminate() - - def __play_win(sound_path, is_async=True): """ Play sound in Windows. From 7e1987f340ee2c8f61106b27ebcc64f5408b54d5 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 11:51:43 +0330 Subject: [PATCH 09/32] fix : minor bug in subprocess call fixed --- nava/functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 8f611bd..ecceb61 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -128,7 +128,7 @@ def __play_sync_linux(sound_path): :type sound_path: str :return: process """ - proc = subprocess.check_call(["aplay", + proc = subprocess.Popen(["aplay", sound_path], shell=False, stderr=subprocess.PIPE, @@ -169,7 +169,7 @@ def __play_sync_mac(sound_path): :type sound_path: str :return: None """ - proc = subprocess.check_call(["afplay", + proc = subprocess.Popen(["afplay", sound_path], shell=False, stderr=subprocess.PIPE, From be13c61893a525e700fd359963e733992bac567b Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 11:53:48 +0330 Subject: [PATCH 10/32] fix : minor edit in tests --- test/function_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/function_test.py b/test/function_test.py index ce58a04..992adae 100644 --- a/test/function_test.py +++ b/test/function_test.py @@ -2,7 +2,7 @@ """ >>> import os >>> from nava import play ->>> play(os.path.join("others", "test.wav")) +>>> sound_id = play(os.path.join("others", "test.wav")) >>> from nava.functions import nava_help >>> nava_help() From 106cd08623a25a8ca9d697e9a6b8405de9581592 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 12:50:34 +0330 Subject: [PATCH 11/32] fix : try to fix windows test bug --- .github/workflows/windows_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/windows_test.yml b/.github/workflows/windows_test.yml index c48ed48..f91d5b1 100644 --- a/.github/workflows/windows_test.yml +++ b/.github/workflows/windows_test.yml @@ -33,6 +33,8 @@ jobs: - name: Install Scream shell: powershell run: | + Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted + Install-Module -Name 7Zip4PowerShell -Force Start-Service audio* Invoke-WebRequest https://github.com/duncanthrax/scream/releases/download/3.6/Scream3.6.zip -OutFile C:\Scream3.6.zip Extract-7Zip -Path C:\Scream3.6.zip -DestinationPath C:\Scream From 6549a337b36c9cb33513d8a3492c646bf92d9c5b Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 13:06:30 +0330 Subject: [PATCH 12/32] fix : try to fix windows test bug --- .github/workflows/windows_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows_test.yml b/.github/workflows/windows_test.yml index f91d5b1..ed47f7f 100644 --- a/.github/workflows/windows_test.yml +++ b/.github/workflows/windows_test.yml @@ -37,7 +37,7 @@ jobs: Install-Module -Name 7Zip4PowerShell -Force Start-Service audio* Invoke-WebRequest https://github.com/duncanthrax/scream/releases/download/3.6/Scream3.6.zip -OutFile C:\Scream3.6.zip - Extract-7Zip -Path C:\Scream3.6.zip -DestinationPath C:\Scream + Expand-7Zip -ArchiveFileName C:\Scream3.6.zip -TargetPath C:\Scream $cert = (Get-AuthenticodeSignature C:\Scream\Install\driver\Scream.sys).SignerCertificate $store = [System.Security.Cryptography.X509Certificates.X509Store]::new("TrustedPublisher", "LocalMachine") $store.Open("ReadWrite") From 8c5fa3ac4e9e09a04f51b0944add436b536c353b Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 23:56:35 +0330 Subject: [PATCH 13/32] doc : NavaThread class docstring updated --- nava/thread.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/nava/thread.py b/nava/thread.py index 99b214f..310c1ea 100644 --- a/nava/thread.py +++ b/nava/thread.py @@ -6,15 +6,36 @@ class NavaThread(threading.Thread): + """ + Nava custom thread. + """ def __init__(self, *args, **kwargs): + """ + Init method. + + :param args: arguments + :type args: list + :param kwargs: keyword arguments + :type kwargs: dict + """ super(NavaThread, self).__init__(*args, **kwargs) self.play_process = None def run(self): + """ + Run target function. + + :return: None + """ if self._target is not None: self.play_process = self._target(*self._args, **self._kwargs) def stop(self): + """ + Stop sound. + + :return: None + """ sys_platform = sys.platform if sys_platform == "win32": import winsound From 5dd7a5988bd279df0f6046eb855c9190222635db Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Tue, 16 Jan 2024 23:58:00 +0330 Subject: [PATCH 14/32] doc : functions docstrings updated --- nava/functions.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/nava/functions.py b/nava/functions.py index ecceb61..73fa808 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -13,13 +13,30 @@ from . import params def stop(sound_id): + """ + Stop sound. + + :param sound_id: sound id + :type sound_id: int + :return: None + """ params._play_threads_map[sound_id].stop() def stop_all(): + """ + Stop all sounds. + + :return: None + """ for thread in params._play_threads_map.values(): thread.stop() def sound_id_gen(): + """ + Sound id generator. + + :return: sound id as int + """ params._play_threads_counter +=1 sound_id = params._play_threads_counter + 1000 return sound_id From ab1a6999295ef5b1cf4e9b516e9b2e4a1737578b Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:00:53 +0330 Subject: [PATCH 15/32] doc : functions docstrings updated --- nava/functions.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 73fa808..ef43209 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -83,15 +83,13 @@ def __play_win(sound_path, is_async=True): :type sound_path: str :param is_async: play async or not :type is_async: bool - :return: None + :return: None or sound id """ import winsound play_flags = winsound.SND_FILENAME | (is_async & winsound.SND_ASYNC) if is_async: - sound_thread = NavaThread(target=__play_win_by_flags, - args=(sound_path, play_flags), - daemon=True) + sound_thread = NavaThread(target=__play_win_by_flags, args=(sound_path, play_flags), daemon=True) sound_thread.start() sound_id = sound_id_gen() params._play_threads_map[sound_id] = sound_thread @@ -123,7 +121,7 @@ def __play_linux(sound_path, is_async=True): :type sound_path: str :param is_async: play async or not :type is_async: bool - :return: None or sound thread (depending on async flag) + :return: None or sound id """ if is_async: sound_thread = NavaThread(target=__play_sync_linux, @@ -164,7 +162,7 @@ def __play_mac(sound_path, is_async=True): :type sound_path: str :param is_async: play sound in async mode :type is_async: bool - :return: None or sound thread, depending on the flag + :return: None or sound id """ if is_async: sound_thread = NavaThread(target=__play_sync_mac, @@ -184,7 +182,7 @@ def __play_sync_mac(sound_path): :param sound_path: sound path to be played :type sound_path: str - :return: None + :return: process """ proc = subprocess.Popen(["afplay", sound_path], @@ -232,7 +230,7 @@ def play(sound_path, is_async=True): :type sound_path: str :param is_async: play synchronously or asynchronously (async by default) :type is_async: bool - :return: None or sound thread for futher handlings + :return: None or sound id """ try: sys_platform = sys.platform From c0b973e9c3e91f517a966b9e3a0a99fb33bf5dec Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:04:09 +0330 Subject: [PATCH 16/32] fix : __play_win_by_flags functions renamed to __play_win_flags --- nava/functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index ef43209..e5baac3 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -89,7 +89,7 @@ def __play_win(sound_path, is_async=True): play_flags = winsound.SND_FILENAME | (is_async & winsound.SND_ASYNC) if is_async: - sound_thread = NavaThread(target=__play_win_by_flags, args=(sound_path, play_flags), daemon=True) + sound_thread = NavaThread(target=__play_win_flags, args=(sound_path, play_flags), daemon=True) sound_thread.start() sound_id = sound_id_gen() params._play_threads_map[sound_id] = sound_thread @@ -98,9 +98,9 @@ def __play_win(sound_path, is_async=True): winsound.PlaySound(sound_path, play_flags) -def __play_win_by_flags(sound_path, flags): +def __play_win_flags(sound_path, flags): """ - Play sound in Windows. + Play sound in Windows using different flags. :param sound_path: sound path :type sound_path: str From 3ad0ff25bd7b52d7302e65bd5dfad419d7a2635b Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:05:31 +0330 Subject: [PATCH 17/32] fix : is_async renamed to async_mode --- nava/functions.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index e5baac3..12c6df9 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -75,20 +75,20 @@ def quoter(sound_path, *args, **kwargs): return func(sound_path, *args, **kwargs) return quoter -def __play_win(sound_path, is_async=True): +def __play_win(sound_path, async_mode=True): """ Play sound in Windows. :param sound_path: sound path :type sound_path: str - :param is_async: play async or not - :type is_async: bool + :param async_mode: play async or not + :type async_mode: bool :return: None or sound id """ import winsound - play_flags = winsound.SND_FILENAME | (is_async & winsound.SND_ASYNC) + play_flags = winsound.SND_FILENAME | (async_mode & winsound.SND_ASYNC) - if is_async: + if async_mode: sound_thread = NavaThread(target=__play_win_flags, args=(sound_path, play_flags), daemon=True) sound_thread.start() sound_id = sound_id_gen() @@ -113,17 +113,17 @@ def __play_win_flags(sound_path, flags): @quote -def __play_linux(sound_path, is_async=True): +def __play_linux(sound_path, async_mode=True): """ Play sound in Linux. :param sound_path: sound path to be played :type sound_path: str - :param is_async: play async or not - :type is_async: bool + :param async_mode: play async or not + :type async_mode: bool :return: None or sound id """ - if is_async: + if async_mode: sound_thread = NavaThread(target=__play_sync_linux, args=(sound_path,), daemon=True) @@ -154,17 +154,17 @@ def __play_sync_linux(sound_path): @quote -def __play_mac(sound_path, is_async=True): +def __play_mac(sound_path, async_mode=True): """ Play sound in macOS. :param sound_path: sound path :type sound_path: str - :param is_async: play sound in async mode - :type is_async: bool + :param async_mode: play sound in async mode + :type async_mode: bool :return: None or sound id """ - if is_async: + if async_mode: sound_thread = NavaThread(target=__play_sync_mac, args=(sound_path,), daemon=True) @@ -222,23 +222,23 @@ def path_checker(sound_path, *args, **kwargs): @path_check -def play(sound_path, is_async=True): +def play(sound_path, async_mode=True): """ Play sound. :param sound_path: sound path :type sound_path: str - :param is_async: play synchronously or asynchronously (async by default) - :type is_async: bool + :param async_mode: play synchronously or asynchronously (async by default) + :type async_mode: bool :return: None or sound id """ try: sys_platform = sys.platform if sys_platform == "win32": - return __play_win(sound_path, is_async) + return __play_win(sound_path, async_mode) elif sys_platform == "darwin": - return __play_mac(sound_path, is_async) + return __play_mac(sound_path, async_mode) else: - return __play_linux(sound_path, is_async) + return __play_linux(sound_path, async_mode) except Exception: # pragma: no cover raise NavaBaseError(SOUND_FILE_PLAY_ERROR) From 0955d078e1ed6c52006ee8eee7214447b60de715 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:07:09 +0330 Subject: [PATCH 18/32] doc : async_mode docstring updated --- nava/functions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 12c6df9..271328f 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -81,7 +81,7 @@ def __play_win(sound_path, async_mode=True): :param sound_path: sound path :type sound_path: str - :param async_mode: play async or not + :param async_mode: async mode flag :type async_mode: bool :return: None or sound id """ @@ -119,7 +119,7 @@ def __play_linux(sound_path, async_mode=True): :param sound_path: sound path to be played :type sound_path: str - :param async_mode: play async or not + :param async_mode: async mode flag :type async_mode: bool :return: None or sound id """ @@ -160,7 +160,7 @@ def __play_mac(sound_path, async_mode=True): :param sound_path: sound path :type sound_path: str - :param async_mode: play sound in async mode + :param async_mode: async mode flag :type async_mode: bool :return: None or sound id """ @@ -228,7 +228,7 @@ def play(sound_path, async_mode=True): :param sound_path: sound path :type sound_path: str - :param async_mode: play synchronously or asynchronously (async by default) + :param async_mode: async mode flag :type async_mode: bool :return: None or sound id """ From f42740ecaf7f268519c5dd52974f23357d806e53 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:08:37 +0330 Subject: [PATCH 19/32] fix : autopep8 scripts updated --- autopep8.bat | 6 +++--- autopep8.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/autopep8.bat b/autopep8.bat index 6c032ba..38c4052 100644 --- a/autopep8.bat +++ b/autopep8.bat @@ -1,3 +1,3 @@ -python -m autopep8 nava --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose -python -m autopep8 setup.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose -python -m autopep8 others --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose +python -m autopep8 nava --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose +python -m autopep8 setup.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose +python -m autopep8 others --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose diff --git a/autopep8.sh b/autopep8.sh index 5c6f113..ed96540 100755 --- a/autopep8.sh +++ b/autopep8.sh @@ -1,4 +1,4 @@ #!/bin/bash -python -m autopep8 nava --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose -python -m autopep8 setup.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose -python -m autopep8 others --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose +python -m autopep8 nava --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose +python -m autopep8 setup.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose +python -m autopep8 others --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --max-line-length 120 --verbose From eff7e52b72fc1c13912402a504c554498dc2b3f8 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:08:51 +0330 Subject: [PATCH 20/32] fix : autopep8 --- nava/functions.py | 30 +++++++++++++++++------------- nava/params.py | 2 +- nava/thread.py | 3 ++- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 271328f..ab98c52 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -12,6 +12,7 @@ from .errors import NavaBaseError from . import params + def stop(sound_id): """ Stop sound. @@ -22,6 +23,7 @@ def stop(sound_id): """ params._play_threads_map[sound_id].stop() + def stop_all(): """ Stop all sounds. @@ -31,16 +33,18 @@ def stop_all(): for thread in params._play_threads_map.values(): thread.stop() + def sound_id_gen(): """ Sound id generator. :return: sound id as int """ - params._play_threads_counter +=1 + params._play_threads_counter += 1 sound_id = params._play_threads_counter + 1000 return sound_id + def nava_help(): """ Print nava details. @@ -75,6 +79,7 @@ def quoter(sound_path, *args, **kwargs): return func(sound_path, *args, **kwargs) return quoter + def __play_win(sound_path, async_mode=True): """ Play sound in Windows. @@ -125,8 +130,8 @@ def __play_linux(sound_path, async_mode=True): """ if async_mode: sound_thread = NavaThread(target=__play_sync_linux, - args=(sound_path,), - daemon=True) + args=(sound_path,), + daemon=True) sound_thread.start() sound_id = sound_id_gen() params._play_threads_map[sound_id] = sound_thread @@ -144,7 +149,7 @@ def __play_sync_linux(sound_path): :return: process """ proc = subprocess.Popen(["aplay", - sound_path], + sound_path], shell=False, stderr=subprocess.PIPE, stdin=subprocess.PIPE, @@ -152,7 +157,6 @@ def __play_sync_linux(sound_path): return proc - @quote def __play_mac(sound_path, async_mode=True): """ @@ -166,8 +170,8 @@ def __play_mac(sound_path, async_mode=True): """ if async_mode: sound_thread = NavaThread(target=__play_sync_mac, - args=(sound_path,), - daemon=True) + args=(sound_path,), + daemon=True) sound_thread.start() sound_id = sound_id_gen() params._play_threads_map[sound_id] = sound_thread @@ -185,11 +189,11 @@ def __play_sync_mac(sound_path): :return: process """ proc = subprocess.Popen(["afplay", - sound_path], - shell=False, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + sound_path], + shell=False, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) return proc @@ -240,5 +244,5 @@ def play(sound_path, async_mode=True): return __play_mac(sound_path, async_mode) else: return __play_linux(sound_path, async_mode) - except Exception: # pragma: no cover + except Exception: # pragma: no cover raise NavaBaseError(SOUND_FILE_PLAY_ERROR) diff --git a/nava/params.py b/nava/params.py index c6c26a7..2ca956f 100644 --- a/nava/params.py +++ b/nava/params.py @@ -13,4 +13,4 @@ SOUND_FILE_PATH_TYPE_ERROR = "Sound file's path should be a string." _play_threads_map = dict() -_play_threads_counter = 0 \ No newline at end of file +_play_threads_counter = 0 diff --git a/nava/thread.py b/nava/thread.py index 310c1ea..3395bbd 100644 --- a/nava/thread.py +++ b/nava/thread.py @@ -9,6 +9,7 @@ class NavaThread(threading.Thread): """ Nava custom thread. """ + def __init__(self, *args, **kwargs): """ Init method. @@ -43,4 +44,4 @@ def stop(self): else: if self.play_process is not None: self.play_process.kill() - self.play_process.terminate() \ No newline at end of file + self.play_process.terminate() From b9f52df8a9e0b7616d0d9ea2297d6343facf5e59 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:16:10 +0330 Subject: [PATCH 21/32] doc : NavaThread docstring bug fixed --- nava/thread.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nava/thread.py b/nava/thread.py index 3395bbd..2ebc988 100644 --- a/nava/thread.py +++ b/nava/thread.py @@ -6,9 +6,7 @@ class NavaThread(threading.Thread): - """ - Nava custom thread. - """ + """Nava custom thread.""" def __init__(self, *args, **kwargs): """ From 024e7c53146730ba26940bfb1ae5b07b5c878eca Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:21:34 +0330 Subject: [PATCH 22/32] fix : stop function error added --- nava/functions.py | 4 +++- nava/params.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/nava/functions.py b/nava/functions.py index ab98c52..0dcd3c3 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -8,7 +8,7 @@ from .thread import NavaThread from .params import OVERVIEW from .params import SOUND_FILE_PLAY_ERROR, SOUND_FILE_EXIST_ERROR -from .params import SOUND_FILE_PATH_TYPE_ERROR +from .params import SOUND_FILE_PATH_TYPE_ERROR, SOUND_ID_EXIST_ERROR from .errors import NavaBaseError from . import params @@ -21,6 +21,8 @@ def stop(sound_id): :type sound_id: int :return: None """ + if sound_id not in params._play_threads_map: + raise NavaBaseError(SOUND_ID_EXIST_ERROR) params._play_threads_map[sound_id].stop() diff --git a/nava/params.py b/nava/params.py index 2ca956f..aa18bef 100644 --- a/nava/params.py +++ b/nava/params.py @@ -11,6 +11,7 @@ SOUND_FILE_PLAY_ERROR = "Sound can not play due to some issues." SOUND_FILE_EXIST_ERROR = "Given sound file doesn't exist." SOUND_FILE_PATH_TYPE_ERROR = "Sound file's path should be a string." +SOUND_ID_EXIST_ERROR = "Given sound id doesn't exist." _play_threads_map = dict() _play_threads_counter = 0 From d63fa9a720179ab67975e15cb20273865afe8e69 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:23:27 +0330 Subject: [PATCH 23/32] fix : error_test updated --- test/error_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/error_test.py b/test/error_test.py index b265c71..578c0d5 100644 --- a/test/error_test.py +++ b/test/error_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ ->>> from nava import play +>>> from nava import play, stop >>> play("test.wav") Traceback (most recent call last): ... @@ -9,4 +9,8 @@ Traceback (most recent call last): ... nava.errors.NavaBaseError: Sound file's path should be a string. +>>> stop(222222) +Traceback (most recent call last): + ... +nava.errors.NavaBaseError: Given sound id doesn't exist. """ From 8175182a408b492d5c5f73af62abfe399a26eb85 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:25:50 +0330 Subject: [PATCH 24/32] fix : function_test updated --- test/function_test.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/function_test.py b/test/function_test.py index 992adae..10d182e 100644 --- a/test/function_test.py +++ b/test/function_test.py @@ -1,8 +1,19 @@ # -*- coding: utf-8 -*- """ >>> import os ->>> from nava import play ->>> sound_id = play(os.path.join("others", "test.wav")) +>>> from nava import play, stop, stop_all +>>> play(os.path.join("others", "test.wav"), async_mode=False) +>>> sound_id_1 = play(os.path.join("others", "test.wav"), async_mode=True) +>>> sound_id_1 == 1001 +True +>>> sound_id_2 = play(os.path.join("others", "test.wav"), async_mode=True) +>>> sound_id_2 == 1002 +True +>>> sound_id_3 = play(os.path.join("others", "test.wav"), async_mode=True) +>>> sound_id_3 == 1003 +True +>>> stop(1001) +>>> stop_all() >>> from nava.functions import nava_help >>> nava_help() From 9face2dc74afe859c92c9e73b3bb3f1c8975f34c Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 00:29:17 +0330 Subject: [PATCH 25/32] doc : CHANGELOG.md updated --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5cc63a..cd62fb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Async mode +- `NavaThread` class +- `stop` function +- `stop_all` function ## [0.2] - 2023-07-10 ### Added - Logo From 297b9f11669a6a2a9daa1f90b368077cb2b7e281 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 01:08:04 +0330 Subject: [PATCH 26/32] doc : README.md updated --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index e381355..0049fa4 100644 --- a/README.md +++ b/README.md @@ -98,11 +98,22 @@ Nava is a Python library that allows users to play sound in Python without any d ## Usage ### Basic + ```python from nava import play play("alarm.wav") ``` +### Async mode + +```python +import time +from nava import play, stop +sound_id = play("alarm.wav", async_mode=True) +time.sleep(4) +stop(sound_id) +``` + ### Error ```python From b73352a3a1e76096f320a92e3f1f597220067250 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 01:29:18 +0330 Subject: [PATCH 27/32] fix : async_mode default value set to False --- nava/functions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 0dcd3c3..f88e5ff 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -82,7 +82,7 @@ def quoter(sound_path, *args, **kwargs): return quoter -def __play_win(sound_path, async_mode=True): +def __play_win(sound_path, async_mode=False): """ Play sound in Windows. @@ -120,7 +120,7 @@ def __play_win_flags(sound_path, flags): @quote -def __play_linux(sound_path, async_mode=True): +def __play_linux(sound_path, async_mode=False): """ Play sound in Linux. @@ -160,7 +160,7 @@ def __play_sync_linux(sound_path): @quote -def __play_mac(sound_path, async_mode=True): +def __play_mac(sound_path, async_mode=False): """ Play sound in macOS. @@ -228,7 +228,7 @@ def path_checker(sound_path, *args, **kwargs): @path_check -def play(sound_path, async_mode=True): +def play(sound_path, async_mode=False): """ Play sound. From 782fadd8df2792d6bc6c0272a94ac2f56ee2569e Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 01:29:56 +0330 Subject: [PATCH 28/32] fix : minor edit in async_mode test --- test/function_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/function_test.py b/test/function_test.py index 10d182e..aab6559 100644 --- a/test/function_test.py +++ b/test/function_test.py @@ -2,7 +2,7 @@ """ >>> import os >>> from nava import play, stop, stop_all ->>> play(os.path.join("others", "test.wav"), async_mode=False) +>>> play(os.path.join("others", "test.wav")) >>> sound_id_1 = play(os.path.join("others", "test.wav"), async_mode=True) >>> sound_id_1 == 1001 True From 9355cdddcf588ec7c6c66ba0322ae56be7c100b4 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 01:31:57 +0330 Subject: [PATCH 29/32] doc : README.md async section updated --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0049fa4..75d0beb 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ play("alarm.wav") ### Async mode +⚠️ The `async_mode` parameter has a default value of `False` + ```python import time from nava import play, stop From b9abdd78357567cd5c2bc01091ad7425718cff65 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Wed, 17 Jan 2024 08:31:54 +0330 Subject: [PATCH 30/32] fix : minor bug in linux/mac sync mode fixed --- nava/functions.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index f88e5ff..9a1d57d 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -131,7 +131,7 @@ def __play_linux(sound_path, async_mode=False): :return: None or sound id """ if async_mode: - sound_thread = NavaThread(target=__play_sync_linux, + sound_thread = NavaThread(target=__play_proc_linux, args=(sound_path,), daemon=True) sound_thread.start() @@ -139,12 +139,13 @@ def __play_linux(sound_path, async_mode=False): params._play_threads_map[sound_id] = sound_thread return sound_id else: - __play_sync_linux(sound_path) + proc = __play_proc_linux(sound_path) + proc.wait() -def __play_sync_linux(sound_path): +def __play_proc_linux(sound_path): """ - Play sound synchronously in Linux. + Create sound playing process in Linux. :param sound_path: sound path to be played :type sound_path: str @@ -171,7 +172,7 @@ def __play_mac(sound_path, async_mode=False): :return: None or sound id """ if async_mode: - sound_thread = NavaThread(target=__play_sync_mac, + sound_thread = NavaThread(target=__play_proc_mac, args=(sound_path,), daemon=True) sound_thread.start() @@ -179,12 +180,13 @@ def __play_mac(sound_path, async_mode=False): params._play_threads_map[sound_id] = sound_thread return sound_id else: - __play_sync_mac(sound_path) + proc = __play_proc_mac(sound_path) + proc.wait() -def __play_sync_mac(sound_path): +def __play_proc_mac(sound_path): """ - Play sound synchronously in macOS. + Create sound playing process in macOS. :param sound_path: sound path to be played :type sound_path: str From b0f52fcbda301d406a0ba456c779ac3575459f07 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Thu, 18 Jan 2024 09:33:00 +0330 Subject: [PATCH 31/32] fix : minor edit in __paly_win function --- nava/functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nava/functions.py b/nava/functions.py index 9a1d57d..568cd43 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -96,7 +96,8 @@ def __play_win(sound_path, async_mode=False): play_flags = winsound.SND_FILENAME | (async_mode & winsound.SND_ASYNC) if async_mode: - sound_thread = NavaThread(target=__play_win_flags, args=(sound_path, play_flags), daemon=True) + sound_thread = NavaThread(target=__play_win_flags, + args=(sound_path, play_flags), daemon=True) sound_thread.start() sound_id = sound_id_gen() params._play_threads_map[sound_id] = sound_thread From 435e701f1db083299d7dfe1164b4c00920b00047 Mon Sep 17 00:00:00 2001 From: sepandhaghighi Date: Thu, 18 Jan 2024 09:34:17 +0330 Subject: [PATCH 32/32] doc : CHANGELOG updated --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd62fb5..140b47a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added -- Async mode - `NavaThread` class - `stop` function - `stop_all` function +### Changed +- `async_mode` parameter added to `play` function ## [0.2] - 2023-07-10 ### Added - Logo