piwave 2.1.3__tar.gz → 2.1.4__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.
- {piwave-2.1.3 → piwave-2.1.4}/PKG-INFO +10 -2
- {piwave-2.1.3 → piwave-2.1.4}/README.md +8 -1
- {piwave-2.1.3 → piwave-2.1.4}/piwave/__main__.py +9 -0
- {piwave-2.1.3 → piwave-2.1.4}/piwave/backends/base.py +8 -3
- {piwave-2.1.3 → piwave-2.1.4}/piwave/backends/fm_transmitter.py +5 -1
- {piwave-2.1.3 → piwave-2.1.4}/piwave/backends/pi_fm_rds.py +6 -2
- piwave-2.1.4/piwave/logger.py +46 -0
- {piwave-2.1.3 → piwave-2.1.4}/piwave/piwave.py +22 -24
- {piwave-2.1.3 → piwave-2.1.4}/piwave.egg-info/PKG-INFO +10 -2
- {piwave-2.1.3 → piwave-2.1.4}/piwave.egg-info/requires.txt +1 -0
- {piwave-2.1.3 → piwave-2.1.4}/setup.cfg +2 -1
- piwave-2.1.3/piwave/logger.py +0 -99
- {piwave-2.1.3 → piwave-2.1.4}/LICENSE +0 -0
- {piwave-2.1.3 → piwave-2.1.4}/piwave/__init__.py +0 -0
- {piwave-2.1.3 → piwave-2.1.4}/piwave/backends/__init__.py +0 -0
- {piwave-2.1.3 → piwave-2.1.4}/piwave.egg-info/SOURCES.txt +0 -0
- {piwave-2.1.3 → piwave-2.1.4}/piwave.egg-info/dependency_links.txt +0 -0
- {piwave-2.1.3 → piwave-2.1.4}/piwave.egg-info/top_level.txt +0 -0
- {piwave-2.1.3 → piwave-2.1.4}/pyproject.toml +0 -0
- {piwave-2.1.3 → piwave-2.1.4}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: piwave
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
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
|
|
@@ -18,6 +18,7 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
18
18
|
Requires-Python: >=3.7
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
+
Requires-Dist: dlogger==1.0.1
|
|
21
22
|
Provides-Extra: dev
|
|
22
23
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
23
24
|
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
@@ -614,6 +615,10 @@ class CustomBackend(Backend):
|
|
|
614
615
|
@property
|
|
615
616
|
def supports_rds(self):
|
|
616
617
|
return True # RDS capability
|
|
618
|
+
|
|
619
|
+
@property
|
|
620
|
+
def supports_loop(self):
|
|
621
|
+
return True # loop support capability
|
|
617
622
|
|
|
618
623
|
def _get_executable_name(self):
|
|
619
624
|
return "my_transmitter"
|
|
@@ -621,10 +626,13 @@ class CustomBackend(Backend):
|
|
|
621
626
|
def _get_search_paths(self):
|
|
622
627
|
return ["/opt", "/usr/local/bin", "/usr/bin"]
|
|
623
628
|
|
|
624
|
-
def build_command(self, wav_file: str):
|
|
629
|
+
def build_command(self, wav_file: str, loop: bool):
|
|
625
630
|
cmd = ['sudo', self.required_executable, '-f', str(self.frequency)]
|
|
626
631
|
if self.supports_rds and self.ps:
|
|
627
632
|
cmd.extend(['-ps', self.ps])
|
|
633
|
+
|
|
634
|
+
if self.supports_rds and loop:
|
|
635
|
+
cmd.extend(['-loop'])
|
|
628
636
|
cmd.append(wav_file)
|
|
629
637
|
return cmd
|
|
630
638
|
```
|
|
@@ -587,6 +587,10 @@ class CustomBackend(Backend):
|
|
|
587
587
|
@property
|
|
588
588
|
def supports_rds(self):
|
|
589
589
|
return True # RDS capability
|
|
590
|
+
|
|
591
|
+
@property
|
|
592
|
+
def supports_loop(self):
|
|
593
|
+
return True # loop support capability
|
|
590
594
|
|
|
591
595
|
def _get_executable_name(self):
|
|
592
596
|
return "my_transmitter"
|
|
@@ -594,10 +598,13 @@ class CustomBackend(Backend):
|
|
|
594
598
|
def _get_search_paths(self):
|
|
595
599
|
return ["/opt", "/usr/local/bin", "/usr/bin"]
|
|
596
600
|
|
|
597
|
-
def build_command(self, wav_file: str):
|
|
601
|
+
def build_command(self, wav_file: str, loop: bool):
|
|
598
602
|
cmd = ['sudo', self.required_executable, '-f', str(self.frequency)]
|
|
599
603
|
if self.supports_rds and self.ps:
|
|
600
604
|
cmd.extend(['-ps', self.ps])
|
|
605
|
+
|
|
606
|
+
if self.supports_rds and loop:
|
|
607
|
+
cmd.extend(['-loop'])
|
|
601
608
|
cmd.append(wav_file)
|
|
602
609
|
return cmd
|
|
603
610
|
```
|
|
@@ -72,6 +72,7 @@ def main():
|
|
|
72
72
|
|
|
73
73
|
args = parser.parse_args(sys.argv[2:])
|
|
74
74
|
|
|
75
|
+
pw = None
|
|
75
76
|
try:
|
|
76
77
|
pw = PiWave(
|
|
77
78
|
frequency=args.frequency,
|
|
@@ -92,9 +93,17 @@ def main():
|
|
|
92
93
|
except KeyboardInterrupt:
|
|
93
94
|
pw.stop()
|
|
94
95
|
Log.info("Broadcast stopped")
|
|
96
|
+
raise
|
|
97
|
+
|
|
95
98
|
except PiWaveError as e:
|
|
96
99
|
Log.error(f"PiWaveError: {e}")
|
|
97
100
|
sys.exit(1)
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
finally:
|
|
104
|
+
if pw is not None:
|
|
105
|
+
pw.cleanup()
|
|
106
|
+
|
|
98
107
|
|
|
99
108
|
else:
|
|
100
109
|
Log.error(f"Unknown command: {cmd}")
|
|
@@ -43,6 +43,11 @@ class Backend(ABC):
|
|
|
43
43
|
@abstractmethod
|
|
44
44
|
def supports_live_streaming(self):
|
|
45
45
|
pass
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def supports_loop(self):
|
|
50
|
+
pass
|
|
46
51
|
|
|
47
52
|
@property
|
|
48
53
|
def cache_file(self):
|
|
@@ -208,7 +213,7 @@ class Backend(ABC):
|
|
|
208
213
|
return False
|
|
209
214
|
|
|
210
215
|
@abstractmethod
|
|
211
|
-
def build_command(self, wav_file: str):
|
|
216
|
+
def build_command(self, wav_file: str, loop: bool):
|
|
212
217
|
pass
|
|
213
218
|
|
|
214
219
|
@abstractmethod
|
|
@@ -219,12 +224,12 @@ class Backend(ABC):
|
|
|
219
224
|
min_freq, max_freq = self.frequency_range
|
|
220
225
|
return min_freq <= self.frequency <= max_freq
|
|
221
226
|
|
|
222
|
-
def play_file(self, wav_file: str) -> subprocess.Popen:
|
|
227
|
+
def play_file(self, wav_file: str, loop: bool) -> subprocess.Popen:
|
|
223
228
|
if not self.validate_settings():
|
|
224
229
|
min_freq, max_freq = self.frequency_range
|
|
225
230
|
raise BackendError(f"{self.name} supports {min_freq}-{max_freq}MHz, got {self.frequency}MHz")
|
|
226
231
|
|
|
227
|
-
cmd = self.build_command(wav_file)
|
|
232
|
+
cmd = self.build_command(wav_file, loop)
|
|
228
233
|
self.current_process = subprocess.Popen(
|
|
229
234
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
230
235
|
preexec_fn=os.setsid
|
|
@@ -23,13 +23,17 @@ class FmTransmitterBackend(Backend):
|
|
|
23
23
|
def supports_live_streaming(self):
|
|
24
24
|
return True
|
|
25
25
|
|
|
26
|
+
@property
|
|
27
|
+
def supports_loop(self):
|
|
28
|
+
return False
|
|
29
|
+
|
|
26
30
|
def _get_executable_name(self):
|
|
27
31
|
return "fm_transmitter"
|
|
28
32
|
|
|
29
33
|
def _get_search_paths(self):
|
|
30
34
|
return ["/opt/PiWave/fm_transmitter", "/opt", "/usr/local/bin", "/usr/bin", "/bin", "/home"]
|
|
31
35
|
|
|
32
|
-
def build_command(self, wav_file: str):
|
|
36
|
+
def build_command(self, wav_file: str, loop: bool):
|
|
33
37
|
return [
|
|
34
38
|
'sudo', self.required_executable,
|
|
35
39
|
'-f', str(self.frequency),
|
|
@@ -13,7 +13,7 @@ class PiFmRdsBackend(Backend):
|
|
|
13
13
|
|
|
14
14
|
@property
|
|
15
15
|
def frequency_range(self):
|
|
16
|
-
return (
|
|
16
|
+
return (76.0, 108.0) # taken from pi_fm_rds.c
|
|
17
17
|
|
|
18
18
|
@property
|
|
19
19
|
def supports_rds(self):
|
|
@@ -23,13 +23,17 @@ class PiFmRdsBackend(Backend):
|
|
|
23
23
|
def supports_live_streaming(self):
|
|
24
24
|
return False
|
|
25
25
|
|
|
26
|
+
@property
|
|
27
|
+
def supports_loop(self):
|
|
28
|
+
return False
|
|
29
|
+
|
|
26
30
|
def _get_executable_name(self):
|
|
27
31
|
return "pi_fm_rds"
|
|
28
32
|
|
|
29
33
|
def _get_search_paths(self):
|
|
30
34
|
return ["/opt/PiWave/PiFmRds", "/opt", "/usr/local/bin", "/usr/bin", "/bin", "/home"]
|
|
31
35
|
|
|
32
|
-
def build_command(self, wav_file: str) -> list:
|
|
36
|
+
def build_command(self, wav_file: str, loop: bool) -> list:
|
|
33
37
|
cmd = [
|
|
34
38
|
'sudo', self.required_executable,
|
|
35
39
|
'-freq', str(self.frequency),
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# PiWave is available at https://piwave.xyz
|
|
2
|
+
# Licensed under GPLv3.0, main GitHub repository at https://github.com/douxxtech/piwave/
|
|
3
|
+
# piwave/Logger.py : Main logging manage
|
|
4
|
+
|
|
5
|
+
from dlogger import DLogger
|
|
6
|
+
|
|
7
|
+
class Logger(DLogger):
|
|
8
|
+
|
|
9
|
+
ICONS = {
|
|
10
|
+
'success': 'OK',
|
|
11
|
+
'error': 'ERR',
|
|
12
|
+
'warning': 'WARN',
|
|
13
|
+
'info': 'INFO',
|
|
14
|
+
'file': 'FILE',
|
|
15
|
+
'broadcast': 'BCAST'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
STYLES = {
|
|
19
|
+
'success': 'bright_green',
|
|
20
|
+
'error': 'bright_red',
|
|
21
|
+
'warning': 'bright_yellow',
|
|
22
|
+
'info': 'bright_cyan',
|
|
23
|
+
'file': 'yellow',
|
|
24
|
+
'broadcast': 'bright_magenta'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
SILENT = False
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
# Initialize with prebuilt icons & styles and silent support.
|
|
31
|
+
|
|
32
|
+
super().__init__(
|
|
33
|
+
icons=self.ICONS,
|
|
34
|
+
styles=self.STYLES
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def config(self, silent: bool = False):
|
|
39
|
+
self.SILENT = silent
|
|
40
|
+
|
|
41
|
+
def print(self, message: str, style: str = '', icon: str = '', end: str = '\n'):
|
|
42
|
+
if self.SILENT:
|
|
43
|
+
return
|
|
44
|
+
super().print(message, style, icon, end)
|
|
45
|
+
|
|
46
|
+
Log = Logger()
|
|
@@ -13,10 +13,8 @@ import shutil
|
|
|
13
13
|
import queue
|
|
14
14
|
from typing import Optional, Callable
|
|
15
15
|
from pathlib import Path
|
|
16
|
-
from urllib.parse import urlparse
|
|
17
|
-
import atexit
|
|
18
16
|
|
|
19
|
-
from .backends import discover_backends, backends, get_best_backend
|
|
17
|
+
from .backends import discover_backends, backends, get_best_backend
|
|
20
18
|
from .logger import Log
|
|
21
19
|
|
|
22
20
|
class PiWaveError(Exception):
|
|
@@ -214,7 +212,7 @@ class PiWave:
|
|
|
214
212
|
if self._is_wav_file(filepath):
|
|
215
213
|
return filepath
|
|
216
214
|
|
|
217
|
-
Log.
|
|
215
|
+
Log.file(f"Converting {filepath} to WAV")
|
|
218
216
|
|
|
219
217
|
output_file = f"{os.path.splitext(filepath)[0]}_converted.wav"
|
|
220
218
|
|
|
@@ -267,35 +265,37 @@ class PiWave:
|
|
|
267
265
|
def _play_file(self, wav_file: str) -> bool:
|
|
268
266
|
if self.stop_event.is_set():
|
|
269
267
|
return False
|
|
270
|
-
|
|
268
|
+
|
|
271
269
|
duration = self._get_file_duration(wav_file)
|
|
270
|
+
|
|
272
271
|
if duration <= 0:
|
|
273
272
|
Log.error(f"Could not determine duration for {wav_file}")
|
|
274
273
|
return False
|
|
275
|
-
|
|
274
|
+
|
|
276
275
|
try:
|
|
277
276
|
# update settings
|
|
278
277
|
self.backend.frequency = self.frequency
|
|
279
278
|
self.backend.ps = self.ps
|
|
280
279
|
self.backend.rt = self.rt
|
|
281
280
|
self.backend.pi = self.pi
|
|
282
|
-
|
|
281
|
+
|
|
283
282
|
# validate frequency
|
|
284
283
|
min_freq, max_freq = self.backend.frequency_range
|
|
285
284
|
if not (min_freq <= self.frequency <= max_freq):
|
|
286
285
|
raise PiWaveError(f"Current backend '{self.backend.name}' doesn't support {self.frequency}MHz (supports {min_freq}-{max_freq}MHz). Use update() to change backend or frequency.")
|
|
287
|
-
|
|
288
|
-
|
|
286
|
+
|
|
289
287
|
loop_status = "looping" if self.loop else f"Duration: {duration:.1f}s"
|
|
290
288
|
rds_info = f" (PS: {self.ps})" if self.backend.supports_rds and self.ps else ""
|
|
291
|
-
Log.
|
|
292
|
-
|
|
293
|
-
self.current_process = self.backend.play_file(wav_file)
|
|
289
|
+
Log.broadcast(f"Playing {wav_file} ({loop_status}) at {self.frequency}MHz{rds_info}")
|
|
290
|
+
|
|
291
|
+
self.current_process = self.backend.play_file(wav_file, self.loop)
|
|
294
292
|
|
|
295
293
|
if self.on_track_change:
|
|
296
294
|
self.on_track_change(wav_file)
|
|
297
295
|
|
|
298
|
-
if self.loop:
|
|
296
|
+
if self.loop and not self.backend.supports_loop:
|
|
297
|
+
|
|
298
|
+
# Only manually loop if the backend does not support it
|
|
299
299
|
while not self.stop_event.is_set():
|
|
300
300
|
if self.stop_event.wait(timeout=0.1):
|
|
301
301
|
self._stop_current_process()
|
|
@@ -304,26 +304,24 @@ class PiWave:
|
|
|
304
304
|
if self.current_process.poll() is not None:
|
|
305
305
|
Log.error("Process ended unexpectedly while looping")
|
|
306
306
|
return False
|
|
307
|
+
|
|
307
308
|
else:
|
|
308
|
-
|
|
309
|
-
while
|
|
309
|
+
# fi backend supports looping or we are not looping, just wait for the process to finish
|
|
310
|
+
while not self.stop_event.is_set():
|
|
310
311
|
if self.stop_event.wait(timeout=0.1):
|
|
311
312
|
self._stop_current_process()
|
|
312
313
|
return False
|
|
313
|
-
|
|
314
|
-
elapsed = time.time() - start_time
|
|
315
|
-
if elapsed >= duration:
|
|
316
|
-
self._stop_current_process()
|
|
314
|
+
if not self.loop and self.current_process.poll() is not None:
|
|
317
315
|
break
|
|
318
|
-
|
|
319
316
|
return True
|
|
320
|
-
|
|
317
|
+
|
|
321
318
|
except Exception as e:
|
|
322
319
|
Log.error(f"Error playing {wav_file}: {e}")
|
|
323
320
|
if self.on_error:
|
|
324
321
|
self.on_error(e)
|
|
325
322
|
self._stop_current_process()
|
|
326
323
|
return False
|
|
324
|
+
|
|
327
325
|
|
|
328
326
|
def _playback_worker_wrapper(self):
|
|
329
327
|
# wrapper for non-blocking playback
|
|
@@ -397,7 +395,7 @@ class PiWave:
|
|
|
397
395
|
consumer_thread.daemon = True
|
|
398
396
|
consumer_thread.start()
|
|
399
397
|
|
|
400
|
-
Log.
|
|
398
|
+
Log.broadcast(f"Live streaming at {self.frequency}MHz ({sample_rate}Hz, {channels}ch)")
|
|
401
399
|
return True
|
|
402
400
|
|
|
403
401
|
def _live_producer_worker(self, audio_source, chunk_size: int):
|
|
@@ -741,7 +739,7 @@ class PiWave:
|
|
|
741
739
|
>>> pw.set_frequency(101.5)
|
|
742
740
|
"""
|
|
743
741
|
self.frequency = frequency
|
|
744
|
-
Log.
|
|
742
|
+
Log.broadcast(f"Frequency changed to {frequency}MHz. Will update on next file's broadcast.")
|
|
745
743
|
|
|
746
744
|
def set_loop(self, loop: bool):
|
|
747
745
|
"""Enable or disable looping for the current track.
|
|
@@ -758,7 +756,7 @@ class PiWave:
|
|
|
758
756
|
"""
|
|
759
757
|
self.loop = loop
|
|
760
758
|
loop_status = "enabled" if loop else "disabled"
|
|
761
|
-
Log.
|
|
759
|
+
Log.broadcast(f"Looping {loop_status}. Will update on next file's broadcast.")
|
|
762
760
|
|
|
763
761
|
def get_status(self) -> dict:
|
|
764
762
|
"""Get current status information.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: piwave
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
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
|
|
@@ -18,6 +18,7 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
18
18
|
Requires-Python: >=3.7
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
|
+
Requires-Dist: dlogger==1.0.1
|
|
21
22
|
Provides-Extra: dev
|
|
22
23
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
23
24
|
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
@@ -614,6 +615,10 @@ class CustomBackend(Backend):
|
|
|
614
615
|
@property
|
|
615
616
|
def supports_rds(self):
|
|
616
617
|
return True # RDS capability
|
|
618
|
+
|
|
619
|
+
@property
|
|
620
|
+
def supports_loop(self):
|
|
621
|
+
return True # loop support capability
|
|
617
622
|
|
|
618
623
|
def _get_executable_name(self):
|
|
619
624
|
return "my_transmitter"
|
|
@@ -621,10 +626,13 @@ class CustomBackend(Backend):
|
|
|
621
626
|
def _get_search_paths(self):
|
|
622
627
|
return ["/opt", "/usr/local/bin", "/usr/bin"]
|
|
623
628
|
|
|
624
|
-
def build_command(self, wav_file: str):
|
|
629
|
+
def build_command(self, wav_file: str, loop: bool):
|
|
625
630
|
cmd = ['sudo', self.required_executable, '-f', str(self.frequency)]
|
|
626
631
|
if self.supports_rds and self.ps:
|
|
627
632
|
cmd.extend(['-ps', self.ps])
|
|
633
|
+
|
|
634
|
+
if self.supports_rds and loop:
|
|
635
|
+
cmd.extend(['-loop'])
|
|
628
636
|
cmd.append(wav_file)
|
|
629
637
|
return cmd
|
|
630
638
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = piwave
|
|
3
|
-
version = 2.1.
|
|
3
|
+
version = 2.1.4
|
|
4
4
|
description = A python module to broadcast radio waves with your Raspberry Pi.
|
|
5
5
|
long_description = file: README.md
|
|
6
6
|
long_description_content_type = text/markdown
|
|
@@ -24,6 +24,7 @@ project_urls =
|
|
|
24
24
|
packages = find:
|
|
25
25
|
include_package_data = True
|
|
26
26
|
install_requires =
|
|
27
|
+
dlogger==1.0.1
|
|
27
28
|
python_requires = >=3.7
|
|
28
29
|
|
|
29
30
|
[options.extras_require]
|
piwave-2.1.3/piwave/logger.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# PiWave is available at https://piwave.xyz
|
|
2
|
-
# Licensed under GPLv3.0, main GitHub repository at https://github.com/douxxtech/piwave/
|
|
3
|
-
# piwave/Logger.py : Main logging manager
|
|
4
|
-
|
|
5
|
-
import sys
|
|
6
|
-
|
|
7
|
-
class Log:
|
|
8
|
-
COLORS = { # absolutely not taken from stackoverflow trust
|
|
9
|
-
'reset': '\033[0m',
|
|
10
|
-
'bold': '\033[1m',
|
|
11
|
-
'underline': '\033[4m',
|
|
12
|
-
'red': '\033[31m',
|
|
13
|
-
'green': '\033[32m',
|
|
14
|
-
'yellow': '\033[33m',
|
|
15
|
-
'blue': '\033[34m',
|
|
16
|
-
'magenta': '\033[35m',
|
|
17
|
-
'cyan': '\033[36m',
|
|
18
|
-
'white': '\033[37m',
|
|
19
|
-
'bright_red': '\033[91m',
|
|
20
|
-
'bright_green': '\033[92m',
|
|
21
|
-
'bright_yellow': '\033[93m',
|
|
22
|
-
'bright_blue': '\033[94m',
|
|
23
|
-
'bright_magenta': '\033[95m',
|
|
24
|
-
'bright_cyan': '\033[96m',
|
|
25
|
-
'bright_white': '\033[97m',
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
ICONS = {
|
|
29
|
-
'success': 'OK',
|
|
30
|
-
'error': 'ERR',
|
|
31
|
-
'warning': 'WARN',
|
|
32
|
-
'info': 'INFO',
|
|
33
|
-
'client': 'CLIENT',
|
|
34
|
-
'server': 'SERVER',
|
|
35
|
-
'file': 'FILE',
|
|
36
|
-
'broadcast': 'BCAST',
|
|
37
|
-
'version': 'VER',
|
|
38
|
-
'update': 'UPD',
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
SILENT = False
|
|
42
|
-
|
|
43
|
-
@classmethod
|
|
44
|
-
def config(cls, silent: bool = False):
|
|
45
|
-
cls.SILENT = silent
|
|
46
|
-
|
|
47
|
-
@classmethod
|
|
48
|
-
def print(cls, message: str, style: str = '', icon: str = '', end: str = '\n'):
|
|
49
|
-
|
|
50
|
-
if cls.SILENT: return
|
|
51
|
-
|
|
52
|
-
color = cls.COLORS.get(style, '')
|
|
53
|
-
icon_char = cls.ICONS.get(icon, '')
|
|
54
|
-
if icon_char:
|
|
55
|
-
if color:
|
|
56
|
-
print(f"{color}[{icon_char}]\033[0m {message}", end=end)
|
|
57
|
-
else:
|
|
58
|
-
print(f"[{icon_char}] {message}", end=end)
|
|
59
|
-
else:
|
|
60
|
-
if color:
|
|
61
|
-
print(f"{color}{message}\033[0m", end=end)
|
|
62
|
-
else:
|
|
63
|
-
print(f"{message}", end=end)
|
|
64
|
-
sys.stdout.flush()
|
|
65
|
-
|
|
66
|
-
@classmethod
|
|
67
|
-
def header(cls, text: str):
|
|
68
|
-
cls.print(text, 'bright_blue', end='\n\n')
|
|
69
|
-
sys.stdout.flush()
|
|
70
|
-
|
|
71
|
-
@classmethod
|
|
72
|
-
def section(cls, text: str):
|
|
73
|
-
cls.print(f" {text} ", 'bright_blue', end='')
|
|
74
|
-
cls.print("─" * (len(text) + 2), 'blue', end='\n\n')
|
|
75
|
-
sys.stdout.flush()
|
|
76
|
-
|
|
77
|
-
@classmethod
|
|
78
|
-
def success(cls, message: str):
|
|
79
|
-
cls.print(message, 'bright_green', 'success')
|
|
80
|
-
|
|
81
|
-
@classmethod
|
|
82
|
-
def error(cls, message: str):
|
|
83
|
-
cls.print(message, 'bright_red', 'error')
|
|
84
|
-
|
|
85
|
-
@classmethod
|
|
86
|
-
def warning(cls, message: str):
|
|
87
|
-
cls.print(message, 'bright_yellow', 'warning')
|
|
88
|
-
|
|
89
|
-
@classmethod
|
|
90
|
-
def info(cls, message: str):
|
|
91
|
-
cls.print(message, 'bright_cyan', 'info')
|
|
92
|
-
|
|
93
|
-
@classmethod
|
|
94
|
-
def file_message(cls, message: str):
|
|
95
|
-
cls.print(message, 'yellow', 'file')
|
|
96
|
-
|
|
97
|
-
@classmethod
|
|
98
|
-
def broadcast_message(cls, message: str):
|
|
99
|
-
cls.print(message, 'bright_magenta', 'broadcast')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|