playsound3-2.5.2/.github/workflows/check-code-quality.yaml0000644000000000000000000000143613615410400020501 0ustar00name: Check code quality with Python 3.13 on: push: branches: ["main"] pull_request: branches: ["main"] permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.13 uses: actions/setup-python@v5 with: python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip pip install Flake8-pyproject pytest mypy black isort - name: Lint with flake8 run: | flake8 . - name: Lint with mypy run: | mypy . - name: Check style with isort run: | isort --check . - name: Check style with black run: | black --check . playsound3-2.5.2/.github/workflows/check-with-pytest-linux.yaml0000644000000000000000000000431313615410400021534 0ustar00name: Install locally and run pytest on Linux on: push: branches: ["main"] pull_request: branches: ["main"] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: # - name: Setup GStreamer # id: setup_gstreamer # uses: blinemedical/setup-gstreamer@v1.4.0 # TODO(Martin): Migrate to pipewire-pulse - name: Start PulseAudio server - TODO migrate to pipewire-pulse! run: | sudo apt-get update sudo apt-get -y install pulseaudio pulseaudio-utils pulseaudio --start pactl load-module module-null-sink sink_name=virtual-sink sink_properties=device.description="Virtual_Sink" pactl set-default-sink virtual-sink - name: Check PulseAudio status run: pulseaudio --check - name: List PulseAudio sinks run: pactl list short sinks - name: Setup GStreamer run: | sudo apt-get update sudo apt-get -y install \ libunwind-dev \ libgirepository1.0-dev \ gstreamer1.0 \ gstreamer1.0-pulseaudio \ gstreamer1.0-alsa \ libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev \ libgstreamer-plugins-bad1.0-dev \ gstreamer1.0-plugins-base \ gstreamer1.0-plugins-good \ gstreamer1.0-plugins-bad \ gstreamer1.0-plugins-ugly \ gstreamer1.0-libav \ gstreamer1.0-tools \ gstreamer1.0-x \ gstreamer1.0-alsa \ gstreamer1.0-gl \ gstreamer1.0-gtk3 \ gstreamer1.0-qt5 \ gstreamer1.0-pulseaudio gst-inspect-1.0 - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | pip install . pip install pytest - name: Test with pytest run: | timeout 60 pytest tests --log-cli-level=WARNING -vv playsound3-2.5.2/.github/workflows/check-with-pytest-macos.yaml0000644000000000000000000000122713615410400021500 0ustar00name: Install locally and run pytest on macOS on: push: branches: ["main"] pull_request: branches: ["main"] jobs: build: runs-on: macos-latest strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | pip install . pip install pytest - name: Test with pytest run: | pytest tests --log-cli-level=WARNING playsound3-2.5.2/.github/workflows/check-with-pytest-windows.yaml0000644000000000000000000000142613615410400022071 0ustar00name: Install locally and run pytest on Windows # This test fails on windows server without audio devices # So we don't run it automatically on: workflow_dispatch: #on: # push: # branches: ["main"] # pull_request: # branches: ["main"] jobs: build: runs-on: windows-2019 strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | pip install . pip install pytest - name: Test with pytest run: | pytest tests --log-cli-level=WARNING playsound3-2.5.2/.idea/.gitignore0000755000000000000000000000026013615410400013512 0ustar00# Default ignored files /shelf/ /workspace.xml # Editor-based HTTP Client requests /httpRequests/ # Datasource local storage ignored files /dataSources/ /dataSources.local.xml playsound3-2.5.2/.idea/misc.xml0000755000000000000000000000041013615410400013174 0ustar00 playsound3-2.5.2/.idea/modules.xml0000755000000000000000000000042013615410400013712 0ustar00 playsound3-2.5.2/.idea/playsound3.iml0000755000000000000000000000072613615410400014335 0ustar00 playsound3-2.5.2/.idea/vcs.xml0000755000000000000000000000024713615410400013044 0ustar00 playsound3-2.5.2/.idea/workspace.xml0000755000000000000000000001161113615410400014244 0ustar00 { "customColor": "", "associatedIndex": 3 } { "keyToString": { "RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.git.unshallow": "true", "git-widget-placeholder": "main", "node.js.detected.package.eslint": "true", "node.js.detected.package.tslint": "true", "node.js.selected.package.eslint": "(autodetect)", "node.js.selected.package.tslint": "(autodetect)", "nodejs_package_manager_path": "npm", "settings.editor.selected.configurable": "editor.preferences.fonts.default", "vue.rearranger.settings.migration": "true" } } 1715170337178 playsound3-2.5.2/.idea/inspectionProfiles/Project_Default.xml0000755000000000000000000002236213615410400021204 0ustar00 playsound3-2.5.2/.idea/inspectionProfiles/profiles_settings.xml0000755000000000000000000000025613615410400021673 0ustar00 playsound3-2.5.2/playsound3/__init__.py0000644000000000000000000000015713615410400014756 0ustar00from playsound3.playsound3 import AVAILABLE_BACKENDS, playsound __all__ = ["playsound", "AVAILABLE_BACKENDS"] playsound3-2.5.2/playsound3/playsound3.py0000644000000000000000000002332113615410400015316 0ustar00from __future__ import annotations import atexit import logging import ssl import subprocess import sys import tempfile import urllib.error import urllib.request from pathlib import Path from threading import Thread from typing import TYPE_CHECKING, Any, Callable # Satisfy mypy if TYPE_CHECKING or sys.platform == "win32": import ctypes import uuid import certifi logger = logging.getLogger(__name__) _DOWNLOAD_CACHE = {} class PlaysoundException(Exception): pass def playsound( sound: str | Path, block: bool = True, backend: str | None = None, daemon: bool = True, ) -> Thread | None: """Play a sound file using an audio backend availabile in your system. Args: sound: Path or URL to the sound file. Can be a string or pathlib.Path. block: If True, the function will block execution until the sound finishes playing. If False, sound will play in a background thread. backend: Name of the audio backend to use. Use None for automatic selection. daemon: If True, and `block` is True, the background thread will be a daemon thread. This means that the thread will stay alive even after the main program exits. Returns: If `block` is True, the function will return None after the sound finishes playing. If `block` is False, the function will return the background thread object. """ if backend is None: _play = _PLAYSOUND_DEFAULT_BACKEND elif backend in _BACKEND_MAPPING: _play = _BACKEND_MAPPING[backend] else: raise PlaysoundException(f"Unknown backend: {backend}. Available backends: {', '.join(AVAILABLE_BACKENDS)}") path = _prepare_path(sound) if block: _play(path) else: thread = Thread(target=_play, args=(path,), daemon=daemon) thread.start() return thread return None def _download_sound_from_web(link: str, destination: Path) -> None: # Identifies itself as a browser to avoid HTTP 403 errors headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64)"} request = urllib.request.Request(link, headers=headers) context = ssl.create_default_context(cafile=certifi.where()) with urllib.request.urlopen(request, context=context) as response, destination.open("wb") as out_file: out_file.write(response.read()) def _prepare_path(sound: str | Path) -> str: if isinstance(sound, str) and sound.startswith(("http://", "https://")): # To play file from URL, we download the file first to a temporary location and cache it if sound not in _DOWNLOAD_CACHE: sound_suffix = Path(sound).suffix with tempfile.NamedTemporaryFile(delete=False, prefix="playsound3-", suffix=sound_suffix) as f: _download_sound_from_web(sound, Path(f.name)) _DOWNLOAD_CACHE[sound] = f.name sound = _DOWNLOAD_CACHE[sound] path = Path(sound) if not path.exists(): raise PlaysoundException(f"File not found: {sound}") return path.absolute().as_posix() def _select_linux_backend() -> Callable[[str], None]: """Select the best available audio backend for Linux systems.""" logger.info("Selecting the best available audio backend for Linux systems.") try: subprocess.run(["gst-play-1.0", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) logger.info("Using gst-play-1.0 as the audio backend.") return _playsound_gst_play except FileNotFoundError: pass try: subprocess.run(["ffplay", "-version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) logger.info("Using ffplay as the audio backend.") return _playsound_ffplay except FileNotFoundError: pass try: subprocess.run(["aplay", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) subprocess.run(["mpg123", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) logger.info("Using aplay and mpg123 as the audio backend.") return _playsound_alsa except FileNotFoundError: pass logger.info("No suitable audio backend found.") raise PlaysoundException("No suitable audio backend found. Install gstreamer or ffmpeg!") def _playsound_gst_play(sound: str) -> None: """Uses gst-play-1.0 utility (built-in Linux).""" logger.debug("gst-play-1.0: starting playing %s", sound) try: subprocess.run(["gst-play-1.0", "--no-interactive", "--quiet", sound], check=True) except subprocess.CalledProcessError as e: raise PlaysoundException(f"gst-play-1.0 failed to play sound: {e}") logger.debug("gst-play-1.0: finishing play %s", sound) def _playsound_ffplay(sound: str) -> None: """Uses ffplay utility (built-in Linux).""" logger.debug("ffplay: starting playing %s", sound) try: subprocess.run( ["ffplay", "-nodisp", "-autoexit", "-loglevel", "quiet", sound], check=True, stdout=subprocess.DEVNULL, # suppress output as ffplay prints an unwanted newline ) except subprocess.CalledProcessError as e: raise PlaysoundException(f"ffplay failed to play sound: {e}") logger.debug("ffplay: finishing play %s", sound) def _playsound_alsa(sound: str) -> None: """Play a sound using alsa and mpg123 (built-in Linux).""" suffix = Path(sound).suffix if suffix == ".wav": logger.debug("alsa: starting playing %s", sound) try: subprocess.run(["aplay", "--quiet", sound], check=True) except subprocess.CalledProcessError as e: raise PlaysoundException(f"aplay failed to play sound: {e}") logger.debug("alsa: finishing play %s", sound) elif suffix == ".mp3": logger.debug("mpg123: starting playing %s", sound) try: subprocess.run(["mpg123", "-q", sound], check=True) except subprocess.CalledProcessError as e: raise PlaysoundException(f"mpg123 failed to play sound: {e}") logger.debug("mpg123: finishing play %s", sound) else: raise PlaysoundException(f"Backend not supported for {suffix} files") def _playsound_gst_legacy(sound: str) -> None: """Play a sound using gstreamer (built-in Linux).""" if not sound.startswith("file://"): sound = "file://" + urllib.request.pathname2url(sound) try: import gi except ImportError: raise PlaysoundException("PyGObject not found. Run 'pip install pygobject'") # Silences gi warning gi.require_version("Gst", "1.0") try: # Gst will be available only if GStreamer is installed from gi.repository import Gst except ImportError: raise PlaysoundException("GStreamer not found. Install GStreamer on your system") Gst.init(None) playbin = Gst.ElementFactory.make("playbin", "playbin") playbin.props.uri = sound logger.debug("gstreamer: starting playing %s", sound) set_result = playbin.set_state(Gst.State.PLAYING) if set_result != Gst.StateChangeReturn.ASYNC: raise PlaysoundException("playbin.set_state returned " + repr(set_result)) bus = playbin.get_bus() try: bus.poll(Gst.MessageType.EOS, Gst.CLOCK_TIME_NONE) finally: playbin.set_state(Gst.State.NULL) logger.debug("gstreamer: finishing play %s", sound) def _send_winmm_mci_command(command: str) -> Any: if sys.platform != "win32": raise RuntimeError("WinMM is only available on Windows systems.") winmm = ctypes.WinDLL("winmm.dll") buffer = ctypes.create_unicode_buffer(255) # Unicode buffer for wide characters error_code = winmm.mciSendStringW(ctypes.c_wchar_p(command), buffer, 254, 0) # Use mciSendStringW if error_code: logger.error("MCI error code: %s", error_code) return buffer.value def _playsound_mci_winmm(sound: str) -> None: """Play a sound utilizing windll.winmm.""" if sys.platform != "win32": raise RuntimeError("WinMM is only available on Windows systems.") # Select a unique alias for the sound alias = str(uuid.uuid4()) logger.debug("winmm: starting playing %s", sound) _send_winmm_mci_command(f'open "{sound}" type mpegvideo alias {alias}') _send_winmm_mci_command(f"play {alias} wait") _send_winmm_mci_command(f"close {alias}") logger.debug("winmm: finishing play %s", sound) def _playsound_afplay(sound: str) -> None: """Uses afplay utility (built-in macOS).""" logger.debug("afplay: starting playing %s", sound) try: subprocess.run(["afplay", sound], check=True) except subprocess.CalledProcessError as e: raise PlaysoundException(f"afplay failed to play sound: {e}") logger.debug("afplay: finishing play %s", sound) def _initialize_default_backend() -> Callable[[str], None]: if sys.platform == "win32": return _playsound_mci_winmm if sys.platform == "darwin": return _playsound_afplay # Linux version serves as the fallback # because tools like gstreamer and ffmpeg could be installed on unrecognized systems return _select_linux_backend() def _remove_cached_downloads(cache: dict[str, str]) -> None: """Remove all files saved in the cache when the program ends.""" for path in cache.values(): Path(path).unlink() # ######################## # # PLAYSOUND INITIALIZATION # # ######################## # _PLAYSOUND_DEFAULT_BACKEND = _initialize_default_backend() atexit.register(_remove_cached_downloads, _DOWNLOAD_CACHE) _BACKEND_MAPPING = { "afplay": _playsound_afplay, "alsa_mpg123": _playsound_alsa, "ffplay": _playsound_ffplay, "gst_play": _playsound_gst_play, "gst_legacy": _playsound_gst_legacy, "mci_winmm": _playsound_mci_winmm, } AVAILABLE_BACKENDS = list(_BACKEND_MAPPING.keys()) playsound3-2.5.2/playsound3/py.typed0000644000000000000000000000000013615410400014327 0ustar00playsound3-2.5.2/tests/test_playsound.py0000644000000000000000000000143713615410400015337 0ustar00import time from playsound3 import playsound MP3_3s = "https://samplelib.com/lib/preview/mp3/sample-3s.mp3" MP3_6s = "https://samplelib.com/lib/preview/mp3/sample-6s.mp3" WAV_3s = "https://samplelib.com/lib/preview/wav/sample-3s.wav" WAV_6s = "https://samplelib.com/lib/preview/wav/sample-6s.wav" def test_dummy(): pass def test_with_blocking(): for sound in [MP3_3s, WAV_3s]: t0 = time.time() playsound(sound, block=True) assert time.time() - t0 >= 3.0 for sound in [MP3_6s, WAV_6s]: t0 = time.time() playsound(sound, block=True) assert time.time() - t0 >= 6.0 def test_non_blocking(): t0 = time.time() for sound in [MP3_3s, WAV_3s, MP3_6s, WAV_6s]: playsound(sound, block=False) assert time.time() - t0 < 1.0 playsound3-2.5.2/.gitignore0000644000000000000000000000136213615410400012533 0ustar00# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ # Custom devel/ **/script* playsound3-2.5.2/LICENSE0000644000000000000000000000220313615410400011543 0ustar00The MIT License (MIT) Copyright (c) 2024 Szymon Mikler Copyright (c) 2021 Taylor Marks Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. playsound3-2.5.2/README.md0000644000000000000000000000620513615410400012023 0ustar00# playsound3 [![PyPi version](https://img.shields.io/badge/dynamic/json?label=latest&query=info.version&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fplaysound3%2Fjson)](https://pypi.org/project/playsound3) [![PyPI license](https://img.shields.io/badge/dynamic/json?label=license&query=info.license&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fplaysound3%2Fjson)](https://pypi.org/project/playsound3) Cross platform library to play sound files in Python. ## Installation Install via pip: ``` pip install playsound3 ``` ## Quick Start Once installed, you can use the playsound function to play sound files: ```python from playsound3 import playsound playsound("/path/to/sound/file.mp3") # or use directly on URLs playsound("http://url/to/sound/file.mp3") ``` ## Documentation The `playsound3` module contains a single function named `playsound`: ```python def playsound( sound: str | Path, block: bool = True, backend: str | None = None, daemon: bool = True, ) -> Thread | None: """Play a sound file using an audio backend availabile in your system. Args: sound: Path or URL to the sound file. Can be a string or pathlib.Path. block: If True, the function will block execution until the sound finishes playing. If False, sound will play in a background thread. backend: Name of the audio backend to use. Use None for automatic selection. daemon: If True, and `block` is True, the background thread will be a daemon thread. This means that the thread will stay alive even after the main program exits. Returns: If `block` is True, the function will return None after the sound finishes playing. If `block` is False, the function will return the background thread object. """ ... ``` It requires one argument: `sound` - the path to the file with the sound you'd like to play. This should be a local file or a URL. There's an optional second argument: `block` which is set to `True` by default. Setting it to `False` makes the function run asynchronously. You can manually specify a backend by passing its name as the third argument. You can browse available backends by using `playsound3.AVAILABLE_BACKENDS`. It is recommended to use the default value of `None` to let the library choose the best backend available. ## Supported systems * **Linux** using one of the below backends, whichever is available: * GStreamer * FFmpeg * aplay for .wav and mpg123 .mp3 * **Windows** using winmm.dll (built-in on Windows) * **macOS** using afplay utility (built-in on macOS) ## Fork information This repository was originally forked from [playsound](https://github.com/TaylorSMarks/playsound) library created by Taylor Marks. The original library is unfortunately not maintained anymore and doesn't accept pull requests. This library: `playsound3` is a major rewrite of the original, including its own set of tests hosted using GitHub Actions. Compared to the original, `playsound3`: * Drops support for Python 2 * Adheres to the latest PEP standards * Offers multiple backends with a fallback mechanism if the default backend is not available * Accepts contributions playsound3-2.5.2/pyproject.toml0000644000000000000000000000425613615410400013464 0ustar00[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "playsound3" version = "2.5.2" requires-python = ">=3.9" authors = [ { name = "Szymon Mikler", email = "sjmikler@gmail.com" }, { name = "Taylor Marks", email = "taylor@marksfam.com" }, ] maintainers = [ { name = "Szymon Mikler", email = "sjmikler@gmail.com" }, ] description = "Cross-platform library to play audio files" readme = { file = "README.md", content-type = "text/markdown" } license = { text = "MIT License" } keywords = ["audio", "sound", "song", "play", "media", "playsound", "music", "wave", "wav", "mp3"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Multimedia :: Sound/Audio :: MIDI", "Topic :: Multimedia :: Sound/Audio :: Players", "Topic :: Multimedia :: Sound/Audio :: Players :: MP3", "Typing :: Typed", "Operating System :: OS Independent", ] dependencies = ["certifi"] [project.urls] Repository = "https://github.com/sjmikler/playsound3" Issues = "https://github.com/sjmikler/playsound3/issues" Documentation = "https://github.com/sjmikler/playsound3?tab=readme-ov-file#documentation" ################################## ## Formatting and testing tools ## ################################## [tool.black] line_length = 120 [tool.flake8] max-line-length = 120 [tool.isort] profile = "black" line_length = 120 [tool.mypy] ignore_missing_imports = true [tool.pyright] pythonVersion = "3.9" [tool.ruff] line-length = 120 [tool.ruff.format] quote-style = "double" indent-style = "space" [tool.ruff.lint] select = ['ALL'] ignore = [ 'FBT001', # boolean-type-hint-positional-argument - Allow positional booleans in functions, it's not really that much of an issue 'FBT002', # boolean-default-value-positional-argument - ^ 'FBT003', # boolean-positional-value-in-call - ^ ] playsound3-2.5.2/PKG-INFO0000644000000000000000000001073213615410400011641 0ustar00Metadata-Version: 2.3 Name: playsound3 Version: 2.5.2 Summary: Cross-platform library to play audio files Project-URL: Repository, https://github.com/sjmikler/playsound3 Project-URL: Issues, https://github.com/sjmikler/playsound3/issues Project-URL: Documentation, https://github.com/sjmikler/playsound3?tab=readme-ov-file#documentation Author-email: Szymon Mikler , Taylor Marks Maintainer-email: Szymon Mikler License: MIT License License-File: LICENSE Keywords: audio,media,mp3,music,play,playsound,song,sound,wav,wave Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Topic :: Multimedia :: Sound/Audio :: MIDI Classifier: Topic :: Multimedia :: Sound/Audio :: Players Classifier: Topic :: Multimedia :: Sound/Audio :: Players :: MP3 Classifier: Typing :: Typed Requires-Python: >=3.9 Requires-Dist: certifi Description-Content-Type: text/markdown # playsound3 [![PyPi version](https://img.shields.io/badge/dynamic/json?label=latest&query=info.version&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fplaysound3%2Fjson)](https://pypi.org/project/playsound3) [![PyPI license](https://img.shields.io/badge/dynamic/json?label=license&query=info.license&url=https%3A%2F%2Fpypi.org%2Fpypi%2Fplaysound3%2Fjson)](https://pypi.org/project/playsound3) Cross platform library to play sound files in Python. ## Installation Install via pip: ``` pip install playsound3 ``` ## Quick Start Once installed, you can use the playsound function to play sound files: ```python from playsound3 import playsound playsound("/path/to/sound/file.mp3") # or use directly on URLs playsound("http://url/to/sound/file.mp3") ``` ## Documentation The `playsound3` module contains a single function named `playsound`: ```python def playsound( sound: str | Path, block: bool = True, backend: str | None = None, daemon: bool = True, ) -> Thread | None: """Play a sound file using an audio backend availabile in your system. Args: sound: Path or URL to the sound file. Can be a string or pathlib.Path. block: If True, the function will block execution until the sound finishes playing. If False, sound will play in a background thread. backend: Name of the audio backend to use. Use None for automatic selection. daemon: If True, and `block` is True, the background thread will be a daemon thread. This means that the thread will stay alive even after the main program exits. Returns: If `block` is True, the function will return None after the sound finishes playing. If `block` is False, the function will return the background thread object. """ ... ``` It requires one argument: `sound` - the path to the file with the sound you'd like to play. This should be a local file or a URL. There's an optional second argument: `block` which is set to `True` by default. Setting it to `False` makes the function run asynchronously. You can manually specify a backend by passing its name as the third argument. You can browse available backends by using `playsound3.AVAILABLE_BACKENDS`. It is recommended to use the default value of `None` to let the library choose the best backend available. ## Supported systems * **Linux** using one of the below backends, whichever is available: * GStreamer * FFmpeg * aplay for .wav and mpg123 .mp3 * **Windows** using winmm.dll (built-in on Windows) * **macOS** using afplay utility (built-in on macOS) ## Fork information This repository was originally forked from [playsound](https://github.com/TaylorSMarks/playsound) library created by Taylor Marks. The original library is unfortunately not maintained anymore and doesn't accept pull requests. This library: `playsound3` is a major rewrite of the original, including its own set of tests hosted using GitHub Actions. Compared to the original, `playsound3`: * Drops support for Python 2 * Adheres to the latest PEP standards * Offers multiple backends with a fallback mechanism if the default backend is not available * Accepts contributions