piwave 2.0.3__tar.gz → 2.0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,38 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piwave
3
- Version: 2.0.3
4
- Summary: A python module to broacast radio waves with your Raspberry Pi.
3
+ Version: 2.0.5
4
+ Summary: A python module to broadcast radio waves with your Raspberry Pi.
5
5
  Home-page: https://github.com/douxxtech/piwave
6
6
  Author: Douxx
7
7
  Author-email: douxx@douxx.tech
8
+ License: GPL-3.0-or-later
8
9
  Project-URL: Bug Reports, https://github.com/douxxtech/piwave/issues
9
10
  Project-URL: Source, https://github.com/douxxtech/piwave
10
11
  Keywords: raspberry pi,radio,fm,rds,streaming,audio,broadcast
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
15
  Classifier: Programming Language :: Python :: 3
12
16
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
13
17
  Classifier: Operating System :: POSIX :: Linux
14
18
  Requires-Python: >=3.7
15
19
  Description-Content-Type: text/markdown
16
20
  License-File: LICENSE
17
- Requires-Dist: pathlib
18
21
  Provides-Extra: dev
19
22
  Requires-Dist: pytest>=6.0; extra == "dev"
20
23
  Requires-Dist: pytest-cov>=2.0; extra == "dev"
21
24
  Requires-Dist: black>=22.0; extra == "dev"
22
25
  Requires-Dist: flake8>=4.0; extra == "dev"
23
- Dynamic: author
24
- Dynamic: author-email
25
- Dynamic: classifier
26
- Dynamic: description
27
- Dynamic: description-content-type
28
- Dynamic: home-page
29
- Dynamic: keywords
30
26
  Dynamic: license-file
31
- Dynamic: project-url
32
- Dynamic: provides-extra
33
- Dynamic: requires-dist
34
- Dynamic: requires-python
35
- Dynamic: summary
36
27
 
37
28
  <div align=center>
38
29
  <img alt="PiWave image" src="https://piwave.xyz/static/img/logo.png"/>
@@ -51,11 +42,6 @@ Dynamic: summary
51
42
  - Supports streaming from URLs.
52
43
  - Better error handling and event callbacks.
53
44
  - Non-blocking playback with threading.
54
- - Temporary file management for streamed content.
55
-
56
- > [!NOTE]
57
- > Are you looking for a minimal GUI for PiWave?
58
- > Take a look at the [PiWave WebGUI](https://github.com/douxxtech/piwave-webgui)
59
45
 
60
46
  ## Hardware Installation
61
47
 
@@ -15,11 +15,6 @@
15
15
  - Supports streaming from URLs.
16
16
  - Better error handling and event callbacks.
17
17
  - Non-blocking playback with threading.
18
- - Temporary file management for streamed content.
19
-
20
- > [!NOTE]
21
- > Are you looking for a minimal GUI for PiWave?
22
- > Take a look at the [PiWave WebGUI](https://github.com/douxxtech/piwave-webgui)
23
18
 
24
19
  ## Hardware Installation
25
20
 
@@ -0,0 +1,5 @@
1
+ # piwave/__init__.py
2
+
3
+ from .piwave import PiWave
4
+
5
+ __all__ = ["PiWave"]
@@ -1,4 +1,4 @@
1
- # piwave/piwave.py
1
+ # piwave/pw.py
2
2
  # pi_fm_rds is required !!! Check https://github.com/ChristopheJacquet/PiFmRds
3
3
 
4
4
  import os
@@ -112,6 +112,30 @@ class PiWave:
112
112
  debug: bool = False,
113
113
  on_track_change: Optional[Callable] = None,
114
114
  on_error: Optional[Callable] = None):
115
+ """Initialize PiWave FM transmitter.
116
+
117
+ :param frequency: FM frequency to broadcast on (80.0-108.0 MHz)
118
+ :type frequency: float
119
+ :param ps: Program Service name (max 8 characters)
120
+ :type ps: str
121
+ :param rt: Radio Text message (max 64 characters)
122
+ :type rt: str
123
+ :param pi: Program Identification code (4 hex digits)
124
+ :type pi: str
125
+ :param loop: Whether to loop the playlist when it ends
126
+ :type loop: bool
127
+ :param debug: Enable debug logging
128
+ :type debug: bool
129
+ :param on_track_change: Callback function called when track changes
130
+ :type on_track_change: Optional[Callable]
131
+ :param on_error: Callback function called when an error occurs
132
+ :type on_error: Optional[Callable]
133
+ :raises PiWaveError: If not running on Raspberry Pi or without root privileges
134
+
135
+ .. note::
136
+ This class requires pi_fm_rds to be installed and accessible.
137
+ Must be run on a Raspberry Pi with root privileges.
138
+ """
115
139
 
116
140
  self.debug = debug
117
141
  self.frequency = frequency
@@ -422,6 +446,21 @@ class PiWave:
422
446
  os._exit(0)
423
447
 
424
448
  def add_files(self, files: List[str]) -> bool:
449
+ """Add audio files to the playlist.
450
+
451
+ :param files: List of file paths or URLs to add to the playlist
452
+ :type files: List[str]
453
+ :return: True if at least one file was successfully added, False otherwise
454
+ :rtype: bool
455
+
456
+ .. note::
457
+ Files are automatically converted to WAV format if needed.
458
+ URLs are supported for streaming audio.
459
+
460
+ Example:
461
+ >>> pw.add_files(['song1.mp3', 'song2.wav', 'http://stream.url'])
462
+ """
463
+
425
464
  converted_files = []
426
465
 
427
466
  for file_path in files:
@@ -442,6 +481,21 @@ class PiWave:
442
481
  return False
443
482
 
444
483
  def play(self, files: Optional[List[str]] = None) -> bool:
484
+ """Start playing the playlist or specified files.
485
+
486
+ :param files: Optional list of files to play. If provided, replaces current playlist
487
+ :type files: Optional[List[str]]
488
+ :return: True if playback started successfully, False otherwise
489
+ :rtype: bool
490
+
491
+ .. note::
492
+ If no files are specified, plays the current playlist.
493
+ Automatically stops any current playback before starting.
494
+
495
+ Example:
496
+ >>> pw.play(['song1.mp3', 'song2.wav']) # Play specific files
497
+ >>> pw.play() # Play current playlist
498
+ """
445
499
  if files:
446
500
  self.playlist.clear()
447
501
  self.current_index = 0
@@ -467,45 +521,129 @@ class PiWave:
467
521
  return True
468
522
 
469
523
  def stop(self):
524
+ """Stop all playback and streaming.
525
+
526
+ Stops the current playback, kills all related processes, and resets the player state.
527
+ This method is safe to call multiple times.
528
+
529
+ Example:
530
+ >>> pw.stop()
531
+ """
470
532
  if not self.is_playing:
471
533
  return
472
534
 
473
- Log.warning("Stopping playback")
535
+ Log.warning("Stopping playback...")
536
+
474
537
  self.is_stopped = True
475
538
  self.stop_event.set()
476
-
477
- self._stop_current_process()
478
-
539
+
540
+ if self.current_process:
541
+ try:
542
+ os.killpg(os.getpgid(self.current_process.pid), signal.SIGTERM)
543
+ self.current_process.wait(timeout=5)
544
+ except Exception:
545
+ pass
546
+ finally:
547
+ self.current_process = None
548
+
549
+ if self.stream_process:
550
+ try:
551
+ os.killpg(os.getpgid(self.stream_process.pid), signal.SIGTERM)
552
+ self.stream_process.wait(timeout=5)
553
+ except Exception:
554
+ pass
555
+ finally:
556
+ self.stream_process = None
557
+
479
558
  if self.playback_thread and self.playback_thread.is_alive():
480
559
  self.playback_thread.join(timeout=5)
481
-
560
+
482
561
  self.is_playing = False
483
562
  Log.success("Playback stopped")
484
563
 
485
564
  def pause(self):
565
+ """Pause the current playback.
566
+
567
+ Stops the current track but maintains the playlist position.
568
+ Use :meth:`resume` to continue playback.
569
+
570
+ Example:
571
+ >>> pw.pause()
572
+ """
486
573
  if self.is_playing:
487
574
  self._stop_current_process()
488
575
  Log.info("Playback paused")
489
576
 
490
577
  def resume(self):
578
+ """Resume playback from the current position.
579
+
580
+ Continues playback from where it was paused or stopped.
581
+
582
+ Example:
583
+ >>> pw.resume()
584
+ """
491
585
  if not self.is_playing and self.playlist:
492
586
  self.play()
493
587
 
494
588
  def next_track(self):
589
+ """Skip to the next track in the playlist.
590
+
591
+ If currently playing, stops the current track and advances to the next one.
592
+
593
+ Example:
594
+ >>> pw.next_track()
595
+ """
495
596
  if self.is_playing:
496
597
  self._stop_current_process()
497
598
  self.current_index += 1
498
599
 
499
600
  def previous_track(self):
601
+ """Go back to the previous track in the playlist.
602
+
603
+ If currently playing, stops the current track and goes to the previous one.
604
+ Cannot go before the first track.
605
+
606
+ Example:
607
+ >>> pw.previous_track()
608
+ """
500
609
  if self.is_playing:
501
610
  self._stop_current_process()
502
611
  self.current_index = max(0, self.current_index - 1)
503
612
 
504
613
  def set_frequency(self, frequency: float):
614
+ """Change the FM broadcast frequency.
615
+
616
+ :param frequency: New frequency in MHz (typically 88.0-108.0)
617
+ :type frequency: float
618
+
619
+ .. note::
620
+ The frequency change will take effect on the next track or broadcast.
621
+
622
+ Example:
623
+ >>> pw.set_frequency(101.5)
624
+ """
505
625
  self.frequency = frequency
506
626
  Log.broadcast_message(f"Frequency changed to {frequency}MHz. Will update on next file's broadcast.")
507
627
 
508
628
  def get_status(self) -> dict:
629
+ """Get current status information.
630
+
631
+ :return: Dictionary containing current player status
632
+ :rtype: dict
633
+
634
+ The returned dictionary contains:
635
+
636
+ - **is_playing** (bool): Whether playback is active
637
+ - **current_index** (int): Current position in playlist
638
+ - **playlist_length** (int): Total number of items in playlist
639
+ - **frequency** (float): Current broadcast frequency
640
+ - **current_file** (str|None): Path of currently playing file
641
+
642
+ Example:
643
+ >>> status = pw.get_status()
644
+ >>> print(f"Playing: {status['is_playing']}")
645
+ >>> print(f"Track {status['current_index'] + 1} of {status['playlist_length']}")
646
+ """
509
647
  return {
510
648
  'is_playing': self.is_playing,
511
649
  'current_index': self.current_index,
@@ -515,6 +653,14 @@ class PiWave:
515
653
  }
516
654
 
517
655
  def cleanup(self):
656
+ """Clean up resources and temporary files.
657
+
658
+ Stops all playback, removes temporary files, and cleans up system resources.
659
+ This method is automatically called when the object is destroyed.
660
+
661
+ Example:
662
+ >>> pw.cleanup()
663
+ """
518
664
  self.stop()
519
665
 
520
666
  if os.path.exists(self.temp_dir):
@@ -526,9 +672,38 @@ class PiWave:
526
672
  self.cleanup()
527
673
 
528
674
  def send(self, files: List[str]):
675
+ """Alias for the play method.
676
+
677
+ :param files: List of file paths or URLs to play
678
+ :type files: List[str]
679
+ :return: True if playback started successfully, False otherwise
680
+ :rtype: bool
681
+
682
+ .. note::
683
+ This is an alias for :meth:`play` for backward compatibility.
684
+
685
+ Example:
686
+ >>> pw.send(['song1.mp3', 'song2.wav'])
687
+ """
529
688
  return self.play(files)
530
689
 
531
690
  def restart(self):
691
+ """Restart playback from the beginning of the playlist.
692
+
693
+ Resets the current position to the first track and starts playback.
694
+ Only works if there are files in the playlist.
695
+
696
+ Example:
697
+ >>> pw.restart()
698
+ """
532
699
  if self.playlist:
533
700
  self.current_index = 0
534
- self.play()
701
+ self.play()
702
+
703
+ if __name__ == "__main__":
704
+ Log.header("PiWave Radio Module")
705
+ Log.info("This module is designed to run on a Raspberry Pi with root privileges.")
706
+ Log.info("Please import this module in your main application to use its features.")
707
+ Log.info("Exiting PiWave module")
708
+
709
+ __all__ = ["PiWave"]
@@ -1,38 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piwave
3
- Version: 2.0.3
4
- Summary: A python module to broacast radio waves with your Raspberry Pi.
3
+ Version: 2.0.5
4
+ Summary: A python module to broadcast radio waves with your Raspberry Pi.
5
5
  Home-page: https://github.com/douxxtech/piwave
6
6
  Author: Douxx
7
7
  Author-email: douxx@douxx.tech
8
+ License: GPL-3.0-or-later
8
9
  Project-URL: Bug Reports, https://github.com/douxxtech/piwave/issues
9
10
  Project-URL: Source, https://github.com/douxxtech/piwave
10
11
  Keywords: raspberry pi,radio,fm,rds,streaming,audio,broadcast
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
15
  Classifier: Programming Language :: Python :: 3
12
16
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
13
17
  Classifier: Operating System :: POSIX :: Linux
14
18
  Requires-Python: >=3.7
15
19
  Description-Content-Type: text/markdown
16
20
  License-File: LICENSE
17
- Requires-Dist: pathlib
18
21
  Provides-Extra: dev
19
22
  Requires-Dist: pytest>=6.0; extra == "dev"
20
23
  Requires-Dist: pytest-cov>=2.0; extra == "dev"
21
24
  Requires-Dist: black>=22.0; extra == "dev"
22
25
  Requires-Dist: flake8>=4.0; extra == "dev"
23
- Dynamic: author
24
- Dynamic: author-email
25
- Dynamic: classifier
26
- Dynamic: description
27
- Dynamic: description-content-type
28
- Dynamic: home-page
29
- Dynamic: keywords
30
26
  Dynamic: license-file
31
- Dynamic: project-url
32
- Dynamic: provides-extra
33
- Dynamic: requires-dist
34
- Dynamic: requires-python
35
- Dynamic: summary
36
27
 
37
28
  <div align=center>
38
29
  <img alt="PiWave image" src="https://piwave.xyz/static/img/logo.png"/>
@@ -51,11 +42,6 @@ Dynamic: summary
51
42
  - Supports streaming from URLs.
52
43
  - Better error handling and event callbacks.
53
44
  - Non-blocking playback with threading.
54
- - Temporary file management for streamed content.
55
-
56
- > [!NOTE]
57
- > Are you looking for a minimal GUI for PiWave?
58
- > Take a look at the [PiWave WebGUI](https://github.com/douxxtech/piwave-webgui)
59
45
 
60
46
  ## Hardware Installation
61
47
 
@@ -1,5 +1,7 @@
1
1
  LICENSE
2
2
  README.md
3
+ pyproject.toml
4
+ setup.cfg
3
5
  setup.py
4
6
  piwave/__init__.py
5
7
  piwave/piwave.py
@@ -1,4 +1,3 @@
1
- pathlib
2
1
 
3
2
  [dev]
4
3
  pytest>=6.0
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
piwave-2.0.5/setup.cfg ADDED
@@ -0,0 +1,39 @@
1
+ [metadata]
2
+ name = piwave
3
+ version = 2.0.5
4
+ description = A python module to broadcast radio waves with your Raspberry Pi.
5
+ long_description = file: README.md
6
+ long_description_content_type = text/markdown
7
+ author = Douxx
8
+ author_email = douxx@douxx.tech
9
+ url = https://github.com/douxxtech/piwave
10
+ license = GPL-3.0-or-later
11
+ keywords = raspberry pi, radio, fm, rds, streaming, audio, broadcast
12
+ classifiers =
13
+ Development Status :: 5 - Production/Stable
14
+ Intended Audience :: Developers
15
+ Topic :: Software Development :: Libraries :: Python Modules
16
+ Programming Language :: Python :: 3
17
+ License :: OSI Approved :: GNU General Public License v3 (GPLv3)
18
+ Operating System :: POSIX :: Linux
19
+ project_urls =
20
+ Bug Reports = https://github.com/douxxtech/piwave/issues
21
+ Source = https://github.com/douxxtech/piwave
22
+
23
+ [options]
24
+ packages = find:
25
+ include_package_data = True
26
+ install_requires =
27
+ python_requires = >=3.7
28
+
29
+ [options.extras_require]
30
+ dev =
31
+ pytest>=6.0
32
+ pytest-cov>=2.0
33
+ black>=22.0
34
+ flake8>=4.0
35
+
36
+ [egg_info]
37
+ tag_build =
38
+ tag_date = 0
39
+
piwave-2.0.5/setup.py ADDED
@@ -0,0 +1,3 @@
1
+ from setuptools import setup
2
+
3
+ setup()
@@ -1,3 +0,0 @@
1
- # piwave/__init__.py
2
-
3
- from .piwave import PiWave
piwave-2.0.3/setup.cfg DELETED
@@ -1,4 +0,0 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
piwave-2.0.3/setup.py DELETED
@@ -1,38 +0,0 @@
1
- # setup.py
2
-
3
- from setuptools import setup, find_packages
4
-
5
- setup(
6
- name="piwave",
7
- version="2.0.3",
8
- description="A python module to broacast radio waves with your Raspberry Pi.",
9
- long_description=open('README.md').read(),
10
- long_description_content_type='text/markdown',
11
- author="Douxx",
12
- author_email="douxx@douxx.tech",
13
- url="https://github.com/douxxtech/piwave",
14
- packages=find_packages(),
15
- include_package_data=True,
16
- install_requires=[
17
- "pathlib",
18
- ],
19
- extras_require={
20
- "dev": [
21
- "pytest>=6.0",
22
- "pytest-cov>=2.0",
23
- "black>=22.0",
24
- "flake8>=4.0",
25
- ],
26
- },
27
- classifiers=[
28
- "Programming Language :: Python :: 3",
29
- "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
30
- "Operating System :: POSIX :: Linux",
31
- ],
32
- python_requires='>=3.7',
33
- keywords="raspberry pi, radio, fm, rds, streaming, audio, broadcast",
34
- project_urls={
35
- "Bug Reports": "https://github.com/douxxtech/piwave/issues",
36
- "Source": "https://github.com/douxxtech/piwave",
37
- }
38
- )
File without changes