# 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)