1
- #!/usr/bin/env python
2
1
# -*- coding: utf-8 -*-
3
2
# http://www.radio-browser.info/webservice#Advanced_station_search
4
3
import json
5
4
import requests
6
- import pprint
7
5
import threading
8
6
import logging
9
- from .widechar import is_wide , cjklen , PY3
10
- from os import get_terminal_size
7
+ from .widechar import cjklen , PY3
8
+ # from os import get_terminal_size
11
9
12
10
logger = logging .getLogger (__name__ )
13
11
12
+
14
13
class PyRadioStationsBrowser (object ):
15
14
"""A base class to get results from online radio directory services.
16
15
@@ -20,7 +19,7 @@ class PyRadioStationsBrowser(object):
20
19
def __init__ (self , search = None ):
21
20
"""Initialize the station's browser.
22
21
23
- It should return a valid search result (for example,
22
+ It should return a valid search result (for example,
24
23
www.radio-browser.info implementation, returns 100 stations
25
24
sorted by number of votes).
26
25
@@ -31,7 +30,10 @@ def __init__(self, search=None):
31
30
"""
32
31
33
32
self ._raw_stations = []
33
+ self ._last_search = None
34
34
self ._have_to_retrieve_url = False
35
+ self ._url_timeout = 3
36
+ self ._search_timeout = 3
35
37
36
38
@property
37
39
def have_to_retrieve_url (self ):
@@ -41,7 +43,7 @@ def have_to_retrieve_url(self):
41
43
def have_to_retrieve_url (self , value ):
42
44
raise ValueError ('property is read only' )
43
45
44
- def stations (self , playlist_format = 1 ):
46
+ def stations (self , playlist_format = 1 ):
45
47
return []
46
48
47
49
def url (self , id_in_list ):
@@ -78,6 +80,19 @@ def real_url(self, stationid):
78
80
79
81
return ''
80
82
83
+ def set_played (self , id_in_list , played ):
84
+ """Note that a player has been played.
85
+
86
+ Parameters
87
+ ----------
88
+ id_in_list
89
+ id in list of stations (0..len-1)
90
+ played
91
+ True or False
92
+
93
+ """
94
+ pass
95
+
81
96
def search (self , data = None ):
82
97
return []
83
98
@@ -106,14 +121,17 @@ class PyRadioBrowserInfoBrowser(PyRadioStationsBrowser):
106
121
107
122
_have_to_retrieve_url = True
108
123
124
+ _url_timeout = 3
125
+ _search_timeout = 3
126
+
109
127
def __init__ (self , search = None ):
110
128
self ._raw_stations = []
111
129
if search :
112
130
self .search (search )
113
131
else :
114
132
self .search ({'order' : 'votes' , 'reverse' : 'true' })
115
133
116
- def stations (self , playlist_format = 1 ):
134
+ def stations (self , playlist_format = 1 ):
117
135
""" Return stations' list (in PyRadio playlist format)
118
136
119
137
Parameters
@@ -126,7 +144,7 @@ def stations(self, playlist_format = 1):
126
144
127
145
ret = []
128
146
for n in self ._raw_stations :
129
- if playlist_format == 0 :
147
+ if playlist_format == 0 :
130
148
ret .append ([n ['name' ], n ['url' ]])
131
149
elif playlist_format == 1 :
132
150
enc = '' if n ['encoding' ] == 'utf-8' else n ['encoding' ]
@@ -163,6 +181,7 @@ def url(self, id_in_list):
163
181
logger .debug ('URL retrieved: "{0}" <- "{1}' .format (url , self ._raw_stations [id_in_list ]['url' ]))
164
182
self ._raw_stations [id_in_list ]['url' ] = url
165
183
self ._raw_stations [id_in_list ]['real_url' ] = True
184
+ self ._raw_stations [id_in_list ]['played' ] = True
166
185
else :
167
186
if logger .isEnabledFor (logging .ERROR ):
168
187
logger .error ('Failed to retrieve URL for station "{0}", using "{1}"' .format (self ._raw_stations [id_in_list ]['name' ], self ._raw_stations [id_in_list ]['url' ]))
@@ -187,7 +206,7 @@ def real_url(self, stationid):
187
206
'http://www.radio-browser.info/webservice/v2/json/url/' + \
188
207
str (stationid )
189
208
try :
190
- r = requests .get (url = url , headers = self ._open_headers )
209
+ r = requests .get (url = url , headers = self ._open_headers , timeout = self . _url_timeout )
191
210
r .raise_for_status ()
192
211
rep = json .loads (r .text )
193
212
if rep ['ok' ] == 'true' :
@@ -224,6 +243,7 @@ def search(self, data):
224
243
votes : station votes
225
244
clickcount : station clicks
226
245
country : station country
246
+ language : station language
227
247
encoding : station encoding ('' means utf-8)
228
248
"""
229
249
@@ -249,9 +269,10 @@ def search(self, data):
249
269
post_data ['limit' ] = 100
250
270
if not 'hidebroken' not in post_data .keys ():
251
271
post_data ['hidebroken' ] = 'true'
272
+ self ._last_search = post_data
252
273
url = 'http://www.radio-browser.info/webservice/json/stations/search'
253
274
try :
254
- r = requests .get (url = url , headers = self ._open_headers , json = post_data )
275
+ r = requests .get (url = url , headers = self ._open_headers , json = post_data , timeout = self . _search_timeout )
255
276
r .raise_for_status ()
256
277
self ._raw_stations = self ._extract_data (json .loads (r .text ))
257
278
except requests .exceptions .RequestException as e :
@@ -289,67 +310,67 @@ def format_station_line(self, id_in_list, pad, width):
289
310
empty string
290
311
"""
291
312
292
- info = (' [Votes: {0}, Clicks: {1}, Bitrate: {2}kb, Country: {3}]' ,
293
- ' [Votes: {0}, Clicks: {1}, Bitrate: {2}kb]' ,
294
- ' [{0} v, {1} cl, {2}kb]' ,
295
- ' [Bitrate: {}kb]' )
296
- now_width = get_terminal_size ().columns - 2
313
+ info = ('' ,
314
+ ' {0} {1}kb' ,
315
+ ' {0}{1} v|{2} cl|{3}kb' ,
316
+ ' {0} {1}| {2}| {3}kb' ,
317
+ ' {0} {1}| {2}| {3}kb|{4}' ,
318
+ ' {0} {1}| {2}| {3}kb|{4}|{5}' ,
319
+ )
320
+ # now_width = get_terminal_size().columns - 2
297
321
now_width = width
298
- if now_width <= 45 :
299
- self ._output_format = 4
300
- elif now_width <= 60 :
301
- self ._output_format = 3
302
- elif now_width <= 78 :
303
- self ._output_format = 2
304
- elif now_width <= 105 :
322
+ if now_width <= 45 :
323
+ self ._output_format = 0
324
+ elif now_width <= 55 :
305
325
self ._output_format = 1
326
+ elif now_width <= 78 :
327
+ self ._output_format = 2
328
+ elif now_width <= 100 :
329
+ self ._output_format = 3
330
+ elif now_width <= 125 :
331
+ self ._output_format = 4
306
332
else :
307
- self ._output_format = 0
308
-
309
- #logger.error('DE width = {0}, now_width = {1}'.format(width, now_width))
310
- #if self._output_format == -1:
311
- # self._info_len = []
312
- # self._info_len.append(len(info[0].format(' ' * self._max_len[3],
313
- # ' ' * self._max_len[4],
314
- # ' ' * self._max_len[2],
315
- # ' ' * self._max_len[1])))
316
- # self._info_len.append(len(info[1].format(' ' * self._max_len[3],
317
- # ' ' * self._max_len[4],
318
- # ' ' * self._max_len[2],
319
- # ' ' * self._max_len[1])))
320
- # self._info_len.append(len(info[2].format(' ' * self._max_len[3],
321
- # ' ' * self._max_len[4],
322
- # ' ' * self._max_len[2])))
323
- # self._info_len.append(len(info[3].format(' ' * self._max_len[2])))
324
- # self._info_name_len = len(' ' * pad + '. ' + ' ' * self._max_len[0])
325
-
326
- # self._output_format = 4
327
- # if width > 50:
328
- # for i, n in enumerate(self._info_len):
329
- # if n + self._info_name_len < width:
330
- # self._output_format = i
331
- # break
332
- #logger.error('DE output format = {}'.format(self._output_format))
333
+ self ._output_format = 5
333
334
334
335
out = ['{0}. ' .format (str (id_in_list + 1 ).rjust (pad )), '' , '' ]
335
336
336
337
# format info field
337
- if self ._output_format == 0 :
338
+ pl = '-' if self ._raw_stations [id_in_list ]['played' ] else '|'
339
+ if self ._output_format == 5 :
340
+ # full with state
341
+ out [2 ] = ' ' + info [self ._output_format ].format (
342
+ pl ,
343
+ self ._raw_stations [id_in_list ]['votes' ].rjust (self ._max_len [0 ]),
344
+ self ._raw_stations [id_in_list ]['clickcount' ].rjust (self ._max_len [1 ]),
345
+ self ._raw_stations [id_in_list ]['bitrate' ].rjust (7 )[:7 ],
346
+ self ._raw_stations [id_in_list ]['country' ].ljust (14 )[:14 ],
347
+ self ._raw_stations [id_in_list ]['language' ].ljust (15 )[:15 ]
348
+ )
349
+ if self ._output_format == 4 :
338
350
# full or condensed info
339
- out [2 ] = ' ' + info [self ._output_format ].format (self ._raw_stations [id_in_list ]['votes' ].rjust (self ._max_len [3 ]),
340
- self ._raw_stations [id_in_list ]['clickcount' ].rjust (self ._max_len [4 ]),
341
- self ._raw_stations [id_in_list ]['bitrate' ].rjust (self ._max_len [2 ]),
342
- self ._raw_stations [id_in_list ]['country' ].ljust (self ._max_len [1 ])[:14 ])
343
- elif self ._output_format in (1 , 2 ):
344
- out [2 ] = ' ' + info [self ._output_format ].format (self ._raw_stations [id_in_list ]['votes' ].rjust (self ._max_len [3 ]),
345
- self ._raw_stations [id_in_list ]['clickcount' ].rjust (self ._max_len [4 ]),
346
- self ._raw_stations [id_in_list ]['bitrate' ].rjust (self ._max_len [2 ]))
347
- elif self ._output_format == 3 :
351
+ out [2 ] = ' ' + info [self ._output_format ].format (
352
+ pl ,
353
+ self ._raw_stations [id_in_list ]['votes' ].rjust (self ._max_len [0 ]),
354
+ self ._raw_stations [id_in_list ]['clickcount' ].rjust (self ._max_len [1 ]),
355
+ self ._raw_stations [id_in_list ]['bitrate' ].rjust (7 )[:7 ],
356
+ self ._raw_stations [id_in_list ]['country' ].ljust (14 )[:14 ],
357
+ )
358
+ elif self ._output_format in (2 , 3 ):
359
+ out [2 ] = ' ' + info [self ._output_format ].format (
360
+ pl ,
361
+ self ._raw_stations [id_in_list ]['votes' ].rjust (self ._max_len [0 ]),
362
+ self ._raw_stations [id_in_list ]['clickcount' ].rjust (self ._max_len [1 ]),
363
+ self ._raw_stations [id_in_list ]['bitrate' ].rjust (7 )[:7 ],
364
+ )
365
+ elif self ._output_format == 1 :
348
366
# Bitrate only
349
- out [2 ] = info [self ._output_format ].format (self ._raw_stations [id_in_list ]['bitrate' ].rjust (self ._max_len [2 ]))
367
+ out [2 ] = info [self ._output_format ].format (
368
+ pl ,
369
+ self ._raw_stations [id_in_list ]['bitrate' ].rjust (7 )[:7 ]
370
+ )
350
371
351
372
name_width = width - len (out [0 ])- len (out [2 ])
352
- out [1 ] = self ._fix_cjk_string_width (self ._raw_stations [id_in_list ]['name' ].ljust (name_width )[:name_width ], name_width )
373
+ out [1 ] = self ._fix_cjk_string_width (self ._raw_stations [id_in_list ]['name' ].ljust (name_width )[:name_width ], name_width )
353
374
if PY3 :
354
375
return '{0}{1}{2}' .format (* out )
355
376
else :
@@ -372,27 +393,26 @@ def _fix_cjk_string_width(self, a_string, width):
372
393
373
394
def _extract_data (self , a_search_result ):
374
395
ret = []
375
- self ._max_len = [0 , 0 , 0 , 0 , 0 ]
396
+ self ._max_len = [0 , 0 ]
376
397
if a_search_result :
377
398
for n in a_search_result :
378
399
ret .append ({'name' : n ['name' ].replace (',' , ' ' )})
379
400
ret [- 1 ]['bitrate' ] = n ['bitrate' ]
380
401
ret [- 1 ]['votes' ] = n ['votes' ]
381
402
ret [- 1 ]['url' ] = n ['url' ]
382
403
ret [- 1 ]['real_url' ] = False
404
+ ret [- 1 ]['played' ] = False
383
405
ret [- 1 ]['hls' ] = n ['hls' ]
384
406
ret [- 1 ]['id' ] = n ['id' ]
385
407
ret [- 1 ]['country' ] = n ['country' ]
408
+ ret [- 1 ]['language' ] = n ['language' ]
386
409
ret [- 1 ]['clickcount' ] = n ['clickcount' ]
387
410
ret [- 1 ]['encoding' ] = ''
388
- self ._get_max_len (string_data = (ret [- 1 ]['name' ],
389
- ret [- 1 ]['country' ]),
390
- numeric_data = (ret [- 1 ]['bitrate' ],
391
- ret [- 1 ]['votes' ],
392
- ret [- 1 ]['clickcount' ]))
411
+ self ._get_max_len (ret [- 1 ]['votes' ],
412
+ ret [- 1 ]['clickcount' ])
393
413
return ret
394
414
395
- def _get_max_len (self , string_data , numeric_data ):
415
+ def _get_max_len (self , votes , clicks ):
396
416
""" Calculate the maximum length of numeric_data / country
397
417
398
418
Parameters
@@ -414,16 +434,11 @@ def _get_max_len(self, string_data, numeric_data):
414
434
max clickcount length]
415
435
"""
416
436
417
- for i , n in enumerate (string_data ):
418
- l = cjklen (n )
419
- if l > self ._max_len [i ]:
420
- self ._max_len [i ] = l
421
- if self ._max_len [0 ] > 50 : self ._max_len [0 ] = 50
422
- if self ._max_len [0 ] > 14 : self ._max_len [1 ] = 14
423
- add = len (string_data )
437
+ numeric_data = (votes , clicks )
438
+ min_data = (6 , 7 )
424
439
for i , n in enumerate (numeric_data ):
425
- if len (n ) > self ._max_len [i + add ]:
426
- self ._max_len [i + add ] = len (n )
440
+ if len (n ) > self ._max_len [i ]:
441
+ self ._max_len [i ] = len (n ) if len ( n ) > min_data [ i ] else min_data [ i ]
427
442
428
443
429
444
class PyRadioBrowserInfoData (object ):
@@ -436,10 +451,11 @@ class PyRadioBrowserInfoData(object):
436
451
_connection_error = False
437
452
_lock = threading .Lock ()
438
453
_stop_thread = False
454
+ _timeout = 3
439
455
data_thread = None
440
456
441
- def __init__ (self ):
442
- pass
457
+ def __init__ (self , timeout = 3 ):
458
+ self . _timeout = timeout
443
459
444
460
def start (self , force_update = False ):
445
461
""" Start data acquisition thread """
@@ -583,7 +599,7 @@ def get_data_dict(data):
583
599
headers = {'user-agent' : 'PyRadio/dev' ,
584
600
'encoding' : 'application/json' }
585
601
try :
586
- r = requests .get (url , headers = headers , json = jdata )
602
+ r = requests .get (url , headers = headers , json = jdata , timeout = self . _timeout )
587
603
r .raise_for_status ()
588
604
return False , json .loads (r .text )
589
605
# if r.status_code == 200:
@@ -613,6 +629,7 @@ def get_data_dict(data):
613
629
callback (my_data , ret )
614
630
lock .release ()
615
631
632
+
616
633
def probeBrowsers (a_browser_url ):
617
634
base_url = a_browser_url .split ('/' )[2 ]
618
635
if not base_url :
0 commit comments