-
Notifications
You must be signed in to change notification settings - Fork 0
/
player.py
243 lines (182 loc) · 5.96 KB
/
player.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ,--(saket)--(RiPlayServer)--(01/07/16 09:40)--(【ツ】)---
# `--(~/player.py$)-->
import json
import os
import subprocess
import threading
from random import shuffle
import api
import ws
from main import log
__author__ = 'saket'
__tag__ = 'main'
source = None
player = None
_player_lock = threading.Lock()
_player_thread = None
_stop_requested = False
_collection = None
_radio_out = False
_playlist = []
_volume = 5
_freq = 100.1
def _start_player():
"""Play all items found on Playlist sequentially on Radio.
Starts player and radio process. Waits for the processes to end.
Returns if _stop_requested or playlist is empty
"""
global source, player, _stop_requested
_player_lock.acquire()
while _playlist:
try:
log.info("Playing : %s" % _playlist[0])
# avconv -i "$File" -f s16le -ar 44100 -ac 2 -loglevel panic -
source = subprocess.Popen(
["avconv", "-i", "Music/%s" % _playlist[0], "-f", "s16le", "-ar", "44100", "-ac", "2", "-loglevel",
"panic", "-"],
stdout=subprocess.PIPE)
if _radio_out:
# ./pifm - "$2" 44100 stereo "$Volume"
player = subprocess.Popen(["./pifm", "-", "%.1f" % _freq, "44100", "stereo", str(_volume)],
stdin=source.stdout)
else:
# aplay -c 2 -f cd -q
player = subprocess.Popen(["aplay", "-c", "2", "-f", "cd", "-q"],
stdin=source.stdout)
source.stdout.close()
except Exception as error:
log.error("Error: %s" % error.message)
api.inform_subscribers()
ws.inform_subscribers()
if player:
log.debug("player.wait() :)")
player.wait()
log.debug("Player terminated :)")
if source:
log.debug("source.wait() :)")
source.wait()
log.debug("Source terminated :)")
if _stop_requested:
_stop_requested = False
break
_playlist.pop(0)
source = None
player = None
api.inform_subscribers()
ws.inform_subscribers()
_player_lock.release()
log.info("Thread terminated :)")
def _stop_player_process():
"""Stop both Radio and Player processes.
Does not remove current playing song from playlist. User assumes responsibility of managing playlist.
Acts like pressing STOP on any media player.
"""
if player and player.poll() is None:
player.terminate()
log.debug("Terminating player process :)")
if source and source.poll() is None:
source.terminate()
log.debug("Terminating source process :)")
def start_player(force=False):
"""Start playing music from playlist.
Creates a thread to act as Player thread to play from playlist.
Returns if playlist is empty or player is running and not forced.
:type force:bool Should forcefully stop player
"""
if not _playlist:
return
global _player_thread
if _player_thread and _player_thread.isAlive():
if force:
# Player needs to be restarted
stop_player()
else:
# Player is already running
return
_player_thread = threading.Thread(target=_start_player)
_player_thread.start()
def stop_player():
"""Stops player without removing currently playing song"""
global _stop_requested
_stop_requested = True
_stop_player_process()
def skip():
"""Skip currently playing music. Stops player which removes currently playing song"""
_stop_player_process()
def clear():
"""Clear current playlist"""
global _playlist
if _player_thread and _player_thread.isAlive():
del _playlist[1:]
else:
_playlist = []
def play(position):
"""Play item at position in collection as new playlist"""
global _playlist
collection = get_collection()
if position == "all":
_playlist = [collection[k] for k in collection]
elif position == "shuffle":
_playlist = [collection[k] for k in collection]
shuffle(_playlist)
elif position in collection:
_playlist = [collection[position]]
else:
log.error("Play requested for %r" % position)
start_player(True)
def queue(position):
"""Add item at position in collection to playlist"""
global _playlist
collection = get_collection()
_playlist.append(collection[position])
log.info("Adding : %s" % collection[position])
start_player()
def get_collection():
"""
Read files available and return list of multimedia files available
:return: dict of files
:rtype dict
"""
global _collection
if _collection:
return _collection
try:
_collection = {i + 1: x for i, x in enumerate(os.listdir("Music"))}
return _collection
except OSError as err:
log.error(err.message)
return {}
def get_collection_json():
"""Get complete Collection(dict) as JSON object"""
return json.dumps(get_collection())
def get_playlist():
"""Get current playlist as list"""
return _playlist
def get_playlist_json():
"""Get current playlist as JSON object"""
return json.dumps(get_playlist())
def get_status():
if source and source.poll() is None:
keys = _collection.keys()
return {'playing': True,
'name': _playlist[0],
'index': keys[_collection.values().index(_playlist[0])],
'queued': len(_playlist) - 1
}
return {'playing': False,
'queued': len(_playlist)
}
def get_status_json():
return json.dumps(get_status())
def get_freq():
"""Return current broadcast frequency"""
return _freq
def get_mode():
return _radio_out
def set_mode(radio_out):
global _radio_out
stop_player()
_radio_out = radio_out
start_player()