Compare commits

...

34 Commits

Author SHA1 Message Date
Martin Brodbeck 8182cf2e07
Merge pull request #16 from gjelsas/master
Trying to fix the .m3u decoding problem
2021-02-14 13:18:40 +01:00
georg aba237db2e Changed the sys.exit, if not tmpstr, the errorhandling, the creation of the Target Directory, and some PEP8 Styling things too.. 2020-12-25 18:27:36 +01:00
georg 5ba2de2f40 reverted temporary Settings... 2020-12-22 16:31:35 +01:00
georg 9793b69715 Added some checks for missing Targetfolder too many http requests and wrong urls.
Also changed the url of wdr3.
2020-12-22 16:28:18 +01:00
georg 3498a8d00b Settings revert to Arbeitsfläche 2020-12-21 23:05:50 +01:00
georg b4988e7299 No mor open with, plain fileobject 2020-12-21 22:34:20 +01:00
georg a8ad2c03e7 NDR in settings 2020-12-21 18:33:49 +01:00
georg ce9f83dc96 fixing m3u Problem 2020-11-06 20:23:08 +01:00
gjelsas a6b3de0c92
Merge pull request #1 from beedaddy/master
test
2020-11-06 20:18:30 +01:00
Martin Brodbeck b0f0ee3135
Merge pull request #14 from lastsamurai26/lastsamurai26-patch-1
Lastsamurai26 patch 1
2020-08-31 13:04:27 +02:00
Frank 4468ab74a3
update
update
2020-08-30 22:10:38 +02:00
Frank b723a4bc42
Update radiorec.py
changed __ to T 
Changed - to _
2020-08-29 23:17:01 +02:00
Frank e4a9406cc6
Update radiorec.py 2020-08-24 20:06:05 +02:00
Martin Brodbeck e3fa69e0dd
Merge pull request #12 from lastsamurai26/patch-2
Update radiorec.py
2019-11-07 12:26:47 +01:00
Frank c489d19299
Update radiorec.py
Add  content Type for aac
2019-11-01 23:00:28 +01:00
Martin Brodbeck 1453a7efbc
Merge pull request #11 from gjelsas/master
Adding Commandline option to set the location of the settings.ini file
2019-03-19 07:19:13 +01:00
gJsas 302f59e204 Merge branch 'master' of https://github.com/gjelsas/radiorec 2019-03-18 21:50:42 +01:00
gjelsas 8a70c601eb Add files via upload 2019-03-18 21:48:45 +01:00
gjelsas b9635e15eb
Add files via upload 2019-03-18 21:34:29 +01:00
Martin Brodbeck c65e34b283 Handle case when no argument has been specified. 2018-01-30 12:03:25 +01:00
Martin Brodbeck b960e5810b
Merge pull request #8 from lastsamurai26/patch-1
Updating Space issue on Mac
2018-01-30 09:37:41 +01:00
Frank 8eba40a041
Updating Space issue on Mac
Updating Space issue on Mac

Changed line 54
2018-01-29 21:13:38 +01:00
Martin Brodbeck d8113b9cc9 Merge pull request #5 from SeBaBu/add-empty-line-check-m3u
Added length check and new station
2017-03-20 12:06:21 +01:00
Sebastian abb8c47dfc Added length check and new station
Added length check to support streams with empty lines (e.g.
http://www.radioeins.de/live.m3u)
Added RadioEins to stations.
2017-03-19 19:28:31 +01:00
Martin Brodbeck aad7f5637c Support for OS X 2016-10-05 12:21:16 +02:00
Martin Brodbeck 2622b083db pep8 2016-03-10 15:10:28 +01:00
Martin Brodbeck 3980c09bf9 ... 2015-02-18 14:20:25 +01:00
Martin Brodbeck 0e643c1f5d Fix: typo in filename suffix 2015-01-22 09:07:55 +01:00
Martin Brodbeck 52d689a330 Streaming URL for DKultur and DLF changed. 2014-05-19 14:15:28 +02:00
Martin Brodbeck 8cfac4abce Added WDR 3 to station list (high quality stream) 2014-05-15 16:24:03 +02:00
Martin Brodbeck 865cbd0f84 Merge pull request #2 from thoka/master
workarounds for python 3.2.3
2014-01-06 11:27:41 -08:00
Thomas Kalka 2f1215239d workarounds for python 3.2.3 2014-01-04 15:01:14 +01:00
Martin Brodbeck 6ddf870d18 Added another hint for adding radio stations 2013-11-05 08:36:25 +01:00
Martin Brodbeck e57732c1a5 typo 2013-11-04 14:00:55 +01:00
3 changed files with 138 additions and 43 deletions

10
README
View File

@ -8,6 +8,8 @@ Installation
depending on which platform you are using this program, e.g.:
* Linux: $HOME/.config/radiorec/settings.ini or
* Windows: %LOCALAPPDATA%/radiorec/settings.ini
or use the commandline option '-s' to specify the location of the
settings.ini file.
- Adjust the settings to your needs. You can happily add more radio stations
to the STATIONS section.
!!! Check at least the the target_dir !!!
@ -17,7 +19,7 @@ Usage
=====
Open a shell and go to the directory where radiorec.py is located.
General usage:
* Windows: py radirec.py […]
* Windows: py radiorec.py […]
* Linux: python3 radiorec.py […] OR JUST ./radiorec.py […]
What you want to do first is getting some help about how to use the scipt:
@ -37,6 +39,8 @@ Thus the command line is:
You can get a list of all known radio stations with:
./radiorec.py list
You can edit/add the radio stations in the STATIONS section of the settings
file.
Scheduling the recording task
=============================
@ -52,8 +56,8 @@ how to schedule your recording tasks.
Known problems
==============
The Windows command line (cmd and powershell) still has problems with UTF-8.
Using the --verbose option might cause the script to crash with an
UnicodeEncodeError. If you want to avoid the crash, you have to do both,
Using the --verbose option might cause the script to crash with an
UnicodeEncodeError. If you want to avoid the crash, you have to do both,
change the command line codepage and the font. For example, doing a
"chcp 65001" and changing the font to "Lucidia Console" should help.

View File

@ -26,25 +26,43 @@ import os
import stat
import sys
import threading
import urllib.request
import urllib3
import logging
import time
from urllib3.exceptions import MaxRetryError
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.')
raise argparse.ArgumentTypeError(
'Duration in minutes must be a positive integer.')
if value < 1:
raise argparse.ArgumentTypeError('Duration must be a positive integer.')
raise argparse.ArgumentTypeError(
'Duration in minutes must be a positive integer.')
else:
return value
def read_settings():
def read_settings(args):
settings_base_dir = ''
if sys.platform == 'linux':
settings_base_dir = os.getenv('HOME') + os.sep + '.config' + os.sep + 'radiorec'
if args.settings:
settings_base_dir = args.settings
elif sys.platform.startswith('linux'):
settings_base_dir = os.getenv(
'HOME') + os.sep + '.config' + os.sep + 'radiorec'
elif sys.platform == 'win32':
settings_base_dir = os.getenv('LOCALAPPDATA') + os.sep + 'radiorec'
elif sys.platform == 'darwin':
settings_base_dir = os.getenv('HOME') + os.sep + 'Library' + os.sep + \
'Application Support' + os.sep + 'radiorec'
settings_base_dir += os.sep
config = configparser.ConfigParser()
try:
@ -55,15 +73,26 @@ def read_settings():
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:
filename += '_' + args.name
content_type = conn.getheader('Content-Type')
if(content_type == 'audio/mpeg'):
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'):
@ -71,20 +100,24 @@ def record_worker(stoprec, streamurl, target_dir, args):
sys.exit()
else:
print('Unknown content type "' + content_type + '". Assuming mp3.')
filename += '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()
settings = read_settings(args)
streamurl = ''
tmpstr = ''
global verboseprint
verboseprint = print if args.verbose else lambda *a, **k: None
@ -95,48 +128,102 @@ 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:
for line in remotefile:
if not line.decode('utf-8').startswith('#'):
tmpstr = line.decode('utf-8')
pool = urllib3.PoolManager()
try:
remotefile = pool.request('GET', streamurl)
except MaxRetryError:
logging.getLogger(__name__).error('The URL of the station is somehow faulty! Check' +
args.station + ' in the Settings!')
sys.exit()
if remotefile.status != 200:
logging.getLogger(__name__).error(
'The URL of the station is somehow faulty! Check' + args.station + ' in the Settings!')
sys.exit(1)
else:
for line in remotefile.data.decode().split():
if not line.startswith('#') and len(line) > 1 and line.endswith('mp3'):
tmpstr = line
break
streamurl = tmpstr
verboseprint('stream url: ' + streamurl)
if not tmpstr:
logging.getLogger(__name__).error('Could not find a mp3 stream')
sys.exit(1)
else:
streamurl = tmpstr
verboseprint(print_time() + " ... Stream URL: " + streamurl)
target_dir = os.path.expandvars(settings['GLOBAL']['target_dir'])
stoprec = threading.Event()
if not os.path.isdir(target_dir):
try:
os.mkdir(target_dir)
except FileNotFoundError:
logging.getLogger(__name__).error('Target directory not found! Check that ' +
target_dir + ' is a valid folder!')
sys.exit(1)
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), daemon = 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()
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()))
if(recthread.is_alive):
stoprec.set()
def list(args):
settings = read_settings()
settings = read_settings(args)
for key in sorted(settings['STATIONS']):
print(key)
def main():
parser = argparse.ArgumentParser(description='This program records internet radio streams. '
'It is free software and comes with ABSOLUTELY NO WARRANTY.')
'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, help='Name of the radio station '
'(see `radiorec.py list`)')
parser_record.add_argument('duration', type=check_duration,
help='Recording time in minutes')
parser_record.add_argument('name', nargs='?', type=str,
help='A name for the recording')
parser_record.add_argument('-p', '--public', action='store_true', help="Public write permissions (Linux only)")
parser_record.add_argument('-v', '--verbose', action='store_true', help="Verbose output")
parser_record.add_argument('station', type=str,
help='Name of the radio station '
'(see `radiorec.py list`)')
parser_record.add_argument('duration', type=check_duration,
help='Recording time in minutes')
parser_record.add_argument('name', nargs='?', type=str,
help='A name for the recording')
parser_record.add_argument(
'-p', '--public', action='store_true',
help="Public write permissions (Linux only)")
parser_record.add_argument(
'-v', '--verbose', action='store_true', help="Verbose output")
parser_record.add_argument(
'-s', '--settings', nargs='?', type=str,
help="specify alternative location for settings.ini")
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")
if not len(sys.argv) > 1:
print('Error: No argument specified.\n')
parser.print_help()
sys.exit(1)
args = parser.parse_args()
args.func(args)
if __name__ == '__main__':
main()

View File

@ -1,11 +1,15 @@
[GLOBAL]
target_dir = $HOME/Arbeitsfläche
[STATIONS]
brklassik = http://streams.br-online.de/br-klassik_2.m3u
dkultur = http://dradio_mp3_dkultur_m.akacast.akamaistream.net/7/530/142684/v1/gnl.akacast.akamaistream.net/dradio_mp3_dkultur_m
dlf = http://dradio_mp3_dlf_m.akacast.akamaistream.net/7/249/142684/v1/gnl.akacast.akamaistream.net/dradio_mp3_dlf_m
dkultur = http://www.deutschlandradio.de/streaming/dkultur.m3u
dlf = http://www.deutschlandradio.de/streaming/dlf.m3u
dwissen = http://dradio_mp3_dwissen_m.akacast.akamaistream.net/7/728/142684/v1/gnl.akacast.akamaistream.net/dradio_mp3_dwissen_m
erfplus = http://c14000-l.i.core.cdn.streamfarm.net/14000cina/live/3212erf_96/live_de_96.mp3
mdrklassik = http://avw.mdr.de/livestreams/mdr_klassik_live_128.m3u
mdrklassik = http://avw.mdr.de/streams/284350-0_mp3_high.m3u
radioeins = http://www.radioeins.de/live.m3u
swr2 = http://mp3-live.swr.de/swr2_m.m3u
wdr3 = http://wdr-wdr3-live.icecast.wdr.de/wdr/wdr3/live/mp3/256/stream.mp3
ndrkultur = http://www.ndr.de/resources/metadaten/audio/m3u/ndrkultur.m3u