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