You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
4.5 KiB

  1. # demotape.py checks regulary the webstreams of all district parlaments
  2. # in Vienna. If a webstream is online, it gets recorded into seperate
  3. # directories per district.
  4. import os
  5. import sys
  6. import time
  7. from datetime import datetime
  8. import random
  9. import m3u8
  10. import youtube_dl
  11. import asyncio
  12. import concurrent.futures
  13. try:
  14. if sys.argv[1] and os.path.exists(sys.argv[1]):
  15. ROOT_PATH = sys.argv[1]
  16. print('Root path for downloaded streams: ' + ROOT_PATH)
  17. sys.stdout.flush()
  18. else:
  19. print('destination path does not exist')
  20. sys.stdout.flush()
  21. sys.exit()
  22. except IndexError:
  23. print('Script needs a valid destination path for recorded videos as argument')
  24. print('For example: \ndemotape.py /path/to/videos')
  25. sys.stdout.flush()
  26. sys.exit()
  27. def generate_channellist():
  28. channels = []
  29. districts = range(1, 23 + 1) # districts of vienna
  30. for district_num in districts:
  31. district_str = str(district_num)
  32. district_str_lz = str(district_num).zfill(2) # leading zero
  33. channel = {
  34. 'name': district_str+'. Bezirk',
  35. 'url': 'https://stream.wien.gv.at/live/ngrp:bv' + district_str_lz + '.stream_all/playlist.m3u8'
  36. }
  37. channels.append(channel)
  38. return channels
  39. def check_stream(url):
  40. playlist = m3u8.load(url)
  41. try:
  42. if playlist.data['playlists']:
  43. # has active live stream
  44. return True
  45. else:
  46. # no livestream
  47. return False
  48. except (ValueError, KeyError):
  49. print('some connection error or so')
  50. sys.stdout.flush()
  51. class MyLogger(object):
  52. def debug(self, msg):
  53. #pass
  54. print(msg)
  55. sys.stdout.flush()
  56. def warning(self, msg):
  57. #pass
  58. print(msg)
  59. sys.stdout.flush()
  60. def error(self, msg):
  61. print(msg)
  62. sys.stdout.flush()
  63. def my_ytdl_hook(d):
  64. if d['status'] == 'finished':
  65. print('Done downloading!')
  66. print(d)
  67. sys.stdout.flush()
  68. def download_stream(channel):
  69. now = datetime.now() # current date and time
  70. dest_dir = DISTRICT_PATH + '/' + channel['name'] +'/'
  71. dest_filename = now.strftime("%Y-%m-%d--%H.%M.%S")
  72. # create directory if it doesn't exist
  73. if not os.path.exists(dest_dir):
  74. print('creating directory ' + dest_dir)
  75. os.makedirs(dest_dir)
  76. dest = dest_dir + dest_filename
  77. ytdl_opts = {
  78. 'logger': MyLogger(),
  79. 'outtmpl': dest,
  80. 'format': 'bestaudio/best',
  81. 'progress_hooks': [my_ytdl_hook],
  82. }
  83. ytdl = youtube_dl.YoutubeDL(ytdl_opts)
  84. try:
  85. ytdl.download([channel['url']])
  86. except (youtube_dl.utils.DownloadError) as e:
  87. print("Download error: " + str(e))
  88. except (youtube_dl.utils.SameFileError) as e:
  89. print("Download error: " + str(e))
  90. except (UnicodeDecodeError) as e:
  91. print("UnicodeDecodeError: " + str(e))
  92. def process_channel(channel):
  93. #print('entered function process_channel with ' + channel['name'])
  94. while True:
  95. #print('checking ' + channel['name'])
  96. if check_stream(channel['url']):
  97. print(channel['name'] + ': found stream! Downloading ...')
  98. sys.stdout.flush()
  99. download_stream(channel)
  100. else:
  101. print(channel['name'] + ': no stream')
  102. sys.stdout.flush()
  103. # wait between checks
  104. waitingtime = random.randint(20,30)
  105. time.sleep(waitingtime)
  106. print('end processing ' + channel['name'] + ' ... (shouldn\'t happen!)')
  107. sys.stdout.flush()
  108. def main():
  109. channels = generate_channellist()
  110. with concurrent.futures.ThreadPoolExecutor(max_workers=23) as executor:
  111. future_to_channel = {executor.submit(process_channel, channel): channel for channel in channels}
  112. for future in concurrent.futures.as_completed(future_to_channel):
  113. channel = future_to_channel[future]
  114. try:
  115. data = future.result()
  116. except Exception as exc:
  117. print('%r generated an exception: %s' % (channel, exc))
  118. else:
  119. print('%r page is %d bytes' % (channel, len(data)))
  120. print('end main (this shouldn\'t happen!)')
  121. sys.stdout.flush()
  122. main()
  123. #test_channel = {
  124. # 'name': 'Test Channel',
  125. # 'url': 'https://1000338copo-app2749759488.r53.cdn.tv1.eu/1000518lf/1000338copo/live/app2749759488/w2928771075/live247.smil/playlist.m3u8'
  126. # }
  127. #download_stream(test_channel)