mad-play 0.1.0__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.
mad_play-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: mad-play
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Play music in the terminal like a divine madman!
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# Mad Play
|
|
9
|
+
|
|
10
|
+
**Play your music from the terminal like a Divine Madman!**
|
|
11
|
+
|
|
12
|
+
[](https://pypi.org/project/mad-play)
|
|
13
|
+
[](https://pypi.org/project/mad-play)
|
|
14
|
+
|
|
15
|
+
-----
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```console
|
|
20
|
+
pip install mad_play
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
For ease of use you could add an alias to your `.bashrc`, for example:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
alias = mp="mad-play"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Remember to close and re-open your terminal or run:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
source ~/.bashrc
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then open your terminal where the music is. To play a file:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
mp funkytown.mp3
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If you have a list of filenames in a text file, one filename per line and you have given it the .playlist extension, you can start playing it by:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
mp bebop.playlist
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or just play every audio file in a folder like so:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
mp mongolian/
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
press `space` to pause and resume, `n` for next tune, `b` to go back and `q` to quit.
|
|
56
|
+
|
|
57
|
+
## Project scope
|
|
58
|
+
|
|
59
|
+
Dead simple terminal audio player that plays audio files from anywhere on your computer, with playlists edited as plain text files and the ability to play everything in a folder. The player has intuitive controls and does not require learning exotic key-bindings.
|
|
60
|
+
|
|
61
|
+
## Contribute
|
|
62
|
+
|
|
63
|
+
This is a fun, simple project, but very useful all the same. It's a single python file with code that is fairly easy to catch. Mind you, it's early days and everything very raw. The Divine Madman invites you all to tinker, suggest improvements within the scope of the project and contribute improvements if doing so tickles your fancy!
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
`-` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
mad_play-0.1.0/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Mad Play
|
|
2
|
+
|
|
3
|
+
**Play your music from the terminal like a Divine Madman!**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/mad-play)
|
|
6
|
+
[](https://pypi.org/project/mad-play)
|
|
7
|
+
|
|
8
|
+
-----
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```console
|
|
13
|
+
pip install mad_play
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
For ease of use you could add an alias to your `.bashrc`, for example:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
alias = mp="mad-play"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Remember to close and re-open your terminal or run:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
source ~/.bashrc
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then open your terminal where the music is. To play a file:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
mp funkytown.mp3
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
If you have a list of filenames in a text file, one filename per line and you have given it the .playlist extension, you can start playing it by:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
mp bebop.playlist
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or just play every audio file in a folder like so:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
mp mongolian/
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
press `space` to pause and resume, `n` for next tune, `b` to go back and `q` to quit.
|
|
49
|
+
|
|
50
|
+
## Project scope
|
|
51
|
+
|
|
52
|
+
Dead simple terminal audio player that plays audio files from anywhere on your computer, with playlists edited as plain text files and the ability to play everything in a folder. The player has intuitive controls and does not require learning exotic key-bindings.
|
|
53
|
+
|
|
54
|
+
## Contribute
|
|
55
|
+
|
|
56
|
+
This is a fun, simple project, but very useful all the same. It's a single python file with code that is fairly easy to catch. Mind you, it's early days and everything very raw. The Divine Madman invites you all to tinker, suggest improvements within the scope of the project and contribute improvements if doing so tickles your fancy!
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
`-` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mad-play"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Play music in the terminal like a divine madman!"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[project.scripts]
|
|
10
|
+
mad-play = "mad_play.player:main"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
14
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mad-play"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Play music in the terminal like a divine madman!"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[project.scripts]
|
|
10
|
+
mad-play = "mad_play.player:main"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
14
|
+
build-backend = "uv_build"
|
|
File without changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import select
|
|
3
|
+
import signal
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import termios
|
|
7
|
+
import tty
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
AUDIO_EXTENSIONS = {
|
|
11
|
+
".mp3",
|
|
12
|
+
".wav",
|
|
13
|
+
".ogg",
|
|
14
|
+
".flac",
|
|
15
|
+
".m4a",
|
|
16
|
+
".aac",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_files(input_path):
|
|
21
|
+
path = Path(input_path).resolve()
|
|
22
|
+
|
|
23
|
+
if path.is_dir():
|
|
24
|
+
return sorted(
|
|
25
|
+
file_path
|
|
26
|
+
for file_path in path.iterdir()
|
|
27
|
+
if file_path.is_file()
|
|
28
|
+
and file_path.suffix.lower() in AUDIO_EXTENSIONS
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if path.suffix.lower() == ".playlist":
|
|
32
|
+
return load_playlist(path)
|
|
33
|
+
|
|
34
|
+
return [path]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_playlist(path):
|
|
39
|
+
playlist_path = Path(path).resolve()
|
|
40
|
+
base_dir = playlist_path.parent
|
|
41
|
+
|
|
42
|
+
files = []
|
|
43
|
+
|
|
44
|
+
for line in playlist_path.read_text().splitlines():
|
|
45
|
+
line = line.strip()
|
|
46
|
+
|
|
47
|
+
# Ignore blank lines and comments
|
|
48
|
+
if not line or line.startswith("#"):
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
file_path = Path(line)
|
|
52
|
+
|
|
53
|
+
if not file_path.is_absolute():
|
|
54
|
+
file_path = base_dir / file_path
|
|
55
|
+
|
|
56
|
+
files.append(file_path)
|
|
57
|
+
|
|
58
|
+
return files
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def play_file(file_path, input_stream):
|
|
62
|
+
print(f"\nPlaying: {file_path}")
|
|
63
|
+
|
|
64
|
+
player = subprocess.Popen([
|
|
65
|
+
"ffplay",
|
|
66
|
+
"-nodisp",
|
|
67
|
+
"-autoexit",
|
|
68
|
+
"-loglevel", "quiet",
|
|
69
|
+
str(file_path),
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
paused = False
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
while player.poll() is None:
|
|
76
|
+
ready, _, _ = select.select([input_stream], [], [], 0.1)
|
|
77
|
+
|
|
78
|
+
if not ready:
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
key = input_stream.read(1).lower()
|
|
82
|
+
|
|
83
|
+
if key in (" ", "space"):
|
|
84
|
+
if paused:
|
|
85
|
+
os.kill(player.pid, signal.SIGCONT)
|
|
86
|
+
paused = False
|
|
87
|
+
print("\rResumed ")
|
|
88
|
+
else:
|
|
89
|
+
os.kill(player.pid, signal.SIGSTOP)
|
|
90
|
+
paused = True
|
|
91
|
+
print("\rPaused ")
|
|
92
|
+
|
|
93
|
+
elif key == "n":
|
|
94
|
+
# Next song
|
|
95
|
+
if paused:
|
|
96
|
+
os.kill(player.pid, signal.SIGCONT)
|
|
97
|
+
|
|
98
|
+
player.terminate()
|
|
99
|
+
player.wait()
|
|
100
|
+
return "next"
|
|
101
|
+
|
|
102
|
+
elif key == "b":
|
|
103
|
+
# Previous song
|
|
104
|
+
if paused:
|
|
105
|
+
os.kill(player.pid, signal.SIGCONT)
|
|
106
|
+
|
|
107
|
+
player.terminate()
|
|
108
|
+
player.wait()
|
|
109
|
+
return "previous"
|
|
110
|
+
|
|
111
|
+
elif key == "q":
|
|
112
|
+
# Stop the entire playlist
|
|
113
|
+
if paused:
|
|
114
|
+
os.kill(player.pid, signal.SIGCONT)
|
|
115
|
+
|
|
116
|
+
player.terminate()
|
|
117
|
+
player.wait()
|
|
118
|
+
return "stop"
|
|
119
|
+
|
|
120
|
+
finally:
|
|
121
|
+
if player.poll() is None:
|
|
122
|
+
if paused:
|
|
123
|
+
os.kill(player.pid, signal.SIGCONT)
|
|
124
|
+
|
|
125
|
+
player.terminate()
|
|
126
|
+
player.wait()
|
|
127
|
+
|
|
128
|
+
# The song finished naturally
|
|
129
|
+
return "finished"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def main():
|
|
133
|
+
if len(sys.argv) != 2:
|
|
134
|
+
print(f"Usage: python3 {sys.argv[0]} <file, folder, or playlist>")
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
|
|
137
|
+
files = get_files(sys.argv[1])
|
|
138
|
+
|
|
139
|
+
if not files:
|
|
140
|
+
print("No audio files found.")
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
fd = sys.stdin.fileno()
|
|
144
|
+
old_terminal_settings = termios.tcgetattr(fd)
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
tty.setcbreak(fd)
|
|
148
|
+
|
|
149
|
+
terminal_settings = termios.tcgetattr(fd)
|
|
150
|
+
terminal_settings[3] &= ~termios.ECHO
|
|
151
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, terminal_settings)
|
|
152
|
+
|
|
153
|
+
print("Commands: p = pause/resume, n = next, b = previous, s = stop")
|
|
154
|
+
|
|
155
|
+
index = 0
|
|
156
|
+
|
|
157
|
+
while 0 <= index < len(files):
|
|
158
|
+
file_path = files[index]
|
|
159
|
+
|
|
160
|
+
if not file_path.is_file():
|
|
161
|
+
print(f"\nSkipping missing file: {file_path}")
|
|
162
|
+
index += 1
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
action = play_file(file_path, sys.stdin)
|
|
166
|
+
|
|
167
|
+
if action == "stop":
|
|
168
|
+
break
|
|
169
|
+
|
|
170
|
+
elif action == "previous":
|
|
171
|
+
if index > 0:
|
|
172
|
+
index -= 1
|
|
173
|
+
# At the first song, restart it
|
|
174
|
+
continue
|
|
175
|
+
|
|
176
|
+
elif action in ("next", "finished"):
|
|
177
|
+
index += 1
|
|
178
|
+
|
|
179
|
+
finally:
|
|
180
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old_terminal_settings)
|
|
181
|
+
print("\nDone.")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
main()
|
|
186
|
+
|