commit fd1dc6d83c6fe28b512d2cfcad16765a07374229 Author: Andreas Demmelbauer Date: Tue Dec 1 18:47:56 2020 +0100 init diff --git a/demotape.py b/demotape.py new file mode 100644 index 0000000..814d748 --- /dev/null +++ b/demotape.py @@ -0,0 +1,163 @@ +# demotape.py checks regulary the webstreams of all district parlaments +# in Vienna. If a webstream is online, it gets recorded into seperate +# directories per district. + +import os +import sys +import time +from datetime import datetime +import random +import m3u8 +import youtube_dl +import asyncio +import concurrent.futures + + + +try: + if sys.argv[1] and os.path.exists(sys.argv[1]): + ROOT_PATH = sys.argv[1] + else: + print('destination path does not exist') + sys.exit() +except IndexError: + print('Script needs a valid destination path for recorded videos as argument') + print('For example: \ndemotape.py /path/to/videos') + sys.exit() + + + + + + +def generate_channellist(): + channels = [] + districts = range(1, 23 + 1) # districts of vienna + + for district_num in districts: + district_str = str(district_num) + district_str_lz = str(district_num).zfill(2) # leading zero + channel = { + 'name': district_str+'. Bezirk', + 'url': 'https://stream.wien.gv.at/live/ngrp:bv' + district_str_lz + '.stream_all/playlist.m3u8' + } + channels.append(channel) + return channels + + +def check_stream(url): + playlist = m3u8.load(url) + try: + if playlist.data['playlists']: + # has active live stream + return True + else: + # no livestream + return False + except (ValueError, KeyError): + print('some connection error or so') + + + +class MyLogger(object): + def debug(self, msg): + #pass + print(msg) + + def warning(self, msg): + #pass + print(msg) + + def error(self, msg): + print(msg) + + +def my_ytdl_hook(d): + if d['status'] == 'finished': + print('Done downloading!') + print(d) + + +def download_stream(channel): + now = datetime.now() # current date and time + + dest_dir = DISTRICT_PATH + '/' + channel['name'] +'/' + dest_filename = now.strftime("%Y-%m-%d--%H.%M.%S") + + # create directory if it doesn't exist + if not os.path.exists(dest_dir): + print('creating directory ' + dest_dir) + os.makedirs(dest_dir) + + dest = dest_dir + dest_filename + + ytdl_opts = { + 'logger': MyLogger(), + 'outtmpl': dest, + 'format': 'bestaudio/best', + 'progress_hooks': [my_ytdl_hook], + } + + ytdl = youtube_dl.YoutubeDL(ytdl_opts) + + try: + ytdl.download([channel['url']]) + except (youtube_dl.utils.DownloadError) as e: + print("Download error: " + str(e)) + except (youtube_dl.utils.SameFileError) as e: + print("Download error: " + str(e)) + except (UnicodeDecodeError) as e: + print("UnicodeDecodeError: " + str(e)) + + + +def process_channel(channel): + #print('entered function process_channel with ' + channel['name']) + while True: + #print('checking ' + channel['name']) + if check_stream(channel['url']): + print(channel['name'] + ': found stream! Downloading ...') + download_stream(channel) + else: + print(channel['name'] + ': no stream') + # wait between checks + waitingtime = random.randint(20,30) + time.sleep(waitingtime) + + print('end processing ' + channel['name'] + ' ... (shouldn\'t happen!)') + + + + +def main(): + channels = generate_channellist() + + with concurrent.futures.ThreadPoolExecutor(max_workers=23) as executor: + future_to_channel = {executor.submit(process_channel, channel): channel for channel in channels} + + for future in concurrent.futures.as_completed(future_to_channel): + channel = future_to_channel[future] + try: + data = future.result() + except Exception as exc: + print('%r generated an exception: %s' % (channel, exc)) + else: + print('%r page is %d bytes' % (channel, len(data))) + + + print('end main (this shouldn\'t happen!)') + + +main() + + + + + + +#test_channel = { +# 'name': 'Test Channel', +# 'url': 'https://1000338copo-app2749759488.r53.cdn.tv1.eu/1000518lf/1000338copo/live/app2749759488/w2928771075/live247.smil/playlist.m3u8' +# } + +#download_stream(test_channel)