diff --git a/radiorec.py b/radiorec.py index 9ec4129..73543cf 100755 --- a/radiorec.py +++ b/radiorec.py @@ -26,19 +26,24 @@ import os import stat import sys import threading -import urllib.request +import urllib3 +import logging +import time +logging.basicConfig(level=logging.DEBUG) +def print_time(): + return time.strftime("%Y-%m-%d %H:%M:%S") def check_duration(value): try: value = int(value) except ValueError: raise argparse.ArgumentTypeError( - 'Duration must be a positive integer.') + 'Duration in minutes must be a positive integer.') if value < 1: raise argparse.ArgumentTypeError( - 'Duration must be a positive integer.') + 'Duration in minutes must be a positive integer.') else: return value @@ -60,14 +65,21 @@ def read_settings(args): config.read_file(open(settings_base_dir + 'settings.ini')) except FileNotFoundError as err: print(str(err)) - print('Please copy/create the settings file to/in the appropriate ' - 'location.') + print('Please copy/create the settings file to/in the appropriate location.') sys.exit() return dict(config.items()) def record_worker(stoprec, streamurl, target_dir, args): - conn = urllib.request.urlopen(streamurl) + pool = urllib3.PoolManager() + conn = pool.request('GET',streamurl, preload_content=False) + conn.auto_close = False + if conn.status != 200: + conn.release_conn() + time.sleep(10) + verboseprint(print_time() + " ... Waited to return for retry bcof status " + str(conn.status)) + return + cur_dt_string = datetime.datetime.now().strftime('%Y-%m-%dT%H_%M_%S') filename = target_dir + os.sep + cur_dt_string + "_" + args.station if args.name: @@ -75,6 +87,8 @@ def record_worker(stoprec, streamurl, target_dir, args): content_type = conn.getheader('Content-Type') if(content_type == 'audio/mpeg'): filename += '.mp3' + elif(content_type == 'application/aacp' or content_type == 'audio/aacp'): + filename += '.aac' elif(content_type == 'application/ogg' or content_type == 'audio/ogg'): filename += '.ogg' elif(content_type == 'audio/x-mpegurl'): @@ -84,15 +98,16 @@ def record_worker(stoprec, streamurl, target_dir, args): print('Unknown content type "' + content_type + '". Assuming mp3.') filename += '.mp3' - with open(filename, "wb") as target: + verboseprint(print_time() + " ... Writing to: " + filename + ", Content-Type: " + conn.getheader('Content-Type')) + with open(filename, 'wb') as target: if args.public: verboseprint('Apply public write permissions (Linux only)') - os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | - stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH) - verboseprint('Recording ' + args.station + '...') + os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH) while(not stoprec.is_set() and not conn.closed): target.write(conn.read(1024)) + verboseprint(print_time() + " ... Connection closed = " + str(conn.closed)) + conn.release_conn() def record(args): settings = read_settings(args) @@ -107,24 +122,38 @@ def record(args): sys.exit() if streamurl.endswith('.m3u'): verboseprint('Seems to be an M3U playlist. Trying to parse...') - with urllib.request.urlopen(streamurl) as remotefile: + pool = urllib3.PoolManager() + with pool.request('GET',streamurl) as remotefile: for line in remotefile: if not line.decode('utf-8').startswith('#') and len(line) > 1: tmpstr = line.decode('utf-8') break streamurl = tmpstr - verboseprint('stream url: ' + streamurl) + + verboseprint(print_time() + " ... Stream URL: " + streamurl) target_dir = os.path.expandvars(settings['GLOBAL']['target_dir']) - stoprec = threading.Event() + started_at = time.time() + should_end_at = started_at + (args.duration * 60) + remaining = (args.duration * 60) - recthread = threading.Thread(target=record_worker, - args=(stoprec, streamurl, target_dir, args)) - recthread.setDaemon(True) - recthread.start() - recthread.join(args.duration * 60) + # as long as recording is supposed to run + while time.time() < should_end_at: + stoprec = threading.Event() + recthread = threading.Thread(target=record_worker, args=(stoprec, streamurl, target_dir, args)) + recthread.setDaemon(True) + recthread.start() + verboseprint(print_time() + " ... Started thread " + str(recthread) + " timeout: " + str(remaining / 60) + " min") + recthread.join(remaining) + verboseprint(print_time() + " ... Came out of rec thread again") - if(recthread.is_alive): - stoprec.set() + if(recthread.is_alive): + stoprec.set() + verboseprint(print_time() + " ... Called stoprec.set()") + else: + verboseprint(print_time() + " ... recthread.is_alive = False") + + remaining = should_end_at - time.time() + verboseprint(print_time() + " ... Remaining: " + str(remaining / 60) + ", Threads: " + str(threading.activeCount())) def list(args): @@ -134,10 +163,7 @@ def list(args): def main(): - parser = argparse.ArgumentParser(description='This program records ' - 'internet radio streams. It is free ' - 'software and comes with ABSOLUTELY NO ' - 'WARRANTY.') + parser = argparse.ArgumentParser(description='This program records internet radio streams. It is free software and comes with ABSOLUTELY NO WARRANTY.') subparsers = parser.add_subparsers(help='sub-command help') parser_record = subparsers.add_parser('record', help='Record a station') parser_record.add_argument('station', type=str, @@ -158,9 +184,7 @@ def main(): parser_record.set_defaults(func=record) parser_list = subparsers.add_parser('list', help='List all known stations') parser_list.set_defaults(func=list) - parser_list.add_argument( - '-s', '--settings', nargs='?', type=str, - help="specify alternative location for settings.ini") + parser_list.add_argument('-s', '--settings', nargs='?', type=str, help="specify alternative location for settings.ini") if not len(sys.argv) > 1: print('Error: No argument specified.\n')