#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Zusammenfassung von m3u8_get.py und ts_download.py
#	Testscript lokal: m3u8_get3.py
#
#	OK:
#		https://mcdn.daserste.de/daserste/de/master_3744/00803/master_3744_00925.ts
#
# Schema master.m3u8:
#	#EXTM3U
#	#EXT-X-VERSION:3
#	#EXT-X-INDEPENDENT-SEGMENTS
#	#EXT-X-STREAM-INF:BANDWIDTH=4237552,AVERAGE-BANDWIDTH=4118400,CODECS="avc1.640020,mp4a.40.2",RESOLUTION=1280x720,FRAME-RATE=50.000
#	master_3744.m3u8
# Schema S-Liste:
#	#EXTM3U
#	#EXT-X-VERSION:3
#	#EXT-X-TARGETDURATION:4
#	#EXT-X-MEDIA-SEQUENCE:2011743
#	#EXT-X-DISCONTINUITY-SEQUENCE:33741
#	#EXT-X-PROGRAM-DATE-TIME:2020-07-16T12:02:28.120Z		<- Zeitmarke (akt.: plus 2 Std.), Abdeckung 2 Std. zurück
#	#EXTINF:4.00000,
#	master_3744/00804/master_3744_01743.ts
#	#EXTINF:4.00000,
#	master_3744/00804/master_3744_01744.ts
#
# Details zum Livestream DasErste https://mcdn.daserste.de/daserste/de/master.m3u8:
# zwischen den Marken X-PROGRAM-DATE-TIME (10 Min.=600 sec) liegen ca.150 ts-Einträge, Größe einz.
# 	ts-Dateien: ca. 1.8 MByte, Länge in VLC 3 sec statt real 4, Verkettung von 10 ts-Downloads
#	ergeben 35 sec Video in VLC
# Länge laut ffprobe 4 sec (ffprobe master_3744_01948.ts) 
# Zeitmarke:
#	Die ts-Dateien decken den Zeitraum bis 2 Std. zurück ab. Die Zeitmarken haben den übl. Versatz minus 2 Std.
#	Die letzte Zeitmarke liegt 10 Min. zurück - bis zum Ende liegen ebenfalls 150 ts-Einträge 
#	Berechnung: 600 sec / 150 ts-Einträge = 4 sec/pro ts-Eintrag (wie Ausgabe ffprobe)
#	siehe ts_liste1.txt und ts_liste2.txt (Abstand 8 Min)
# Berechnung entfällt bei Marke TARGETDURATION (Kopf ts-Liste)
#
#		curl-header:
#		< Content-Type: video/mp2t
#		< Content-Length: 1900492
#		< Connection: keep-alive
#		< Cache-Control: max-age=14400
#
# s. https://www.dacast.com/blog/hls-streaming-protocol/
#	In terms of technical functionality, HLS will play video encoded with the H.264 or HEVC/H.265 codecs. 
#	It then chops video into 10-second segments. Remember, latency for delivery tends to be in the 30-second range.
#
# Playlist-Varianten: 
#	https://github.com/globocom/m3u8/blob/7d9e2bde4ba1cb69f0f76d95e7e098f54fd58af8/tests/playlists.py
#
# Alternativen: 
#	Download via VLC (google: vlc console recording settings):
#	https://blog.sourcefabric.org/en/news/blog/2077/Schedule-stream-recordings-from-the-command-line-Part-2.htm
#	Download via ffmpeg - s. LiveRecord (Kodi-Addon-ARDundZDF)


from urllib import quote, unquote, quote_plus, unquote_plus, urlencode, urlretrieve 
from urllib2 import Request, urlopen, URLError 
from urlparse import urljoin, urlparse, urlunparse , urlsplit, parse_qs 
import os
import sys, ssl, re
import time, datetime

def get_url_list(m3u8_url, body):									# Url-Liste für Einzelauflösungen
	print "get_url_list:"
	
	urlgroup = urlparse(m3u8_url)
	host =  urlgroup.scheme + '://' + urlgroup.hostname
	
	lines = body.split('\n')
	ts_url_list = []
	for line in lines:
		if not line.startswith('#') and line != '':
			if line.startswith('http'):
				ts_url_list.append(line)
			else:
				if '/' in line:									 # normaler Pfad			
					ts_url_list.append('%s/%s' % (host, line))
				else:											# rel. Pfad: url anpassen
					url_m3u8_path = m3u8_url.split('/')[-1]		# Bsp.: ..de/master.m3u8 -> ..de/master_320.m3u8
					line = m3u8_url.replace(url_m3u8_path, line)
					ts_url_list.append(line)
	return ts_url_list

def get_m3u8_body(m3u8_url):											# Master m3u8
	print 'hole Inhalt m3u8-Datei:' + m3u8_url
	req = Request(m3u8_url, headers={'user-agent': 'Mozilla/5.0'})
	r = urlopen(req)	
	new_url = r.geturl()										# follow redirects
	print("new_url: " + new_url)									# -> msg s.u.
	page = r.read()
	return page

def download_ts_file(ts_url):									# TS-Listen für Einzelauflösungen
	print 'hole Inhalt ts-Datei: ' + ts_url
	req = Request(ts_url, headers={'user-agent': 'Mozilla/5.0'})
	r = urlopen(req)	
	new_url = r.geturl()										# follow redirects
	print("new_url: " + new_url)									# -> msg s.u.
	page = r.read()
	print "len(page): " + str(len(page))
	return page	
	
def download_ts(host, ts_page, dest_video, duration):	
	print 'hole ts-Dateien:' + dest_video
	lines = ts_page.splitlines()
	
	if 'PROGRAM-DATE-TIME:' in ts_page:							# kann fehlen
		dateLines=[]
		for line in lines:											# Abstand zw. 2 Zeitmarken ermitteln
			if 'PROGRAM-DATE-TIME:' in line:						#	kann ev. entfallen
				d = line.split('DATE-TIME:')[1]
				dateLines.append(d)
		print "dateLines: " + str(dateLines)
		
		if 	len(dateLines) > 0:	
			date_format = "%Y-%m-%dT%H:%M:%S"	
			unix_time1 = dateLines[0].split('.')[0]
			unix_time1 = time.mktime(time.strptime(unix_time1, date_format))
			unix_time2 = dateLines[1].split('.')[0]
			unix_time2 = time.mktime(time.strptime(unix_time2, date_format))
			diff = unix_time2 - unix_time1
			print "diff time2-time1: " + str(diff) 					# Differenz in Sek. 
			
	try:
		ts_dur = re.search('TARGETDURATION:(\d+)', ts_page).group(1)
	except Exception as exception:	
		print (str(exception))
		ts_dur=4													# Default ARD & Co
	print "ts_dur: " + ts_dur

	#return	#Debug
	f = open(dest_video, 'wb')

	dt = datetime.datetime.now()						# s. EPG get_unixtime 
	now = time.mktime(dt.timetuple())							
	start = str(now).split('.')[0]								
	lines = ts_page.splitlines()
	cnt=1;
	for line in lines:
		#print line
		if 'PROGRAM-DATE-TIME:' in line or line.startswith('#') == False:
			if 'PROGRAM-DATE-TIME:' in line:
				# print line
				pass
			else:
				# hier Check ob der Download in Realtime erfolgt. Falls schneller, dann
				#	Verzicht auf now, stattdessen einfach 10 sec addieren
				dt = datetime.datetime.now()						# s. EPG get_unixtime 
				now = time.mktime(dt.timetuple())							
				now = str(now).split('.')[0]								
				diff = int(now) - int(start)
				print "diff: " + str(diff) 
				if diff > duration:					# Dauer erreicht
					break
				if line.startswith('http'):
					ts_url = line
				else:
					ts_url = "%s/%s" % (host, line)
				print "ts_url: " + ts_url
				
				req = Request(ts_url, headers={'user-agent': 'Mozilla/5.0'})
				gcontext = ssl.create_default_context()
				gcontext.check_hostname = False
				r = urlopen(req, context=gcontext, timeout=3)
				new_url = r.geturl()				# follow redirects
				meta = r.info()
				print "%d. Download %s, ts: %s" % (cnt, dest_video, ts_url)
				# os.system('cls')				# clear Shell
				file_size_dl = 0
				block_sz = 8192
				cnt=cnt+1
				while True:
					buffer = r.read(block_sz)
					if not buffer:
						break
					file_size_dl += len(buffer)
					f.write(buffer)
					
				time.sleep(int(ts_dur))									# DasErste je ts 4 sec laut ffprobe
					
	f.close()
					
			

def main(m3u8_url, dest_video, duration):
	body = get_m3u8_body(m3u8_url)								# gesamte m3u8-Seite 
	print (body)
	ts_url_list = get_url_list(m3u8_url, body)					# alle TS-Listen
	ts_url = ts_url_list[0]										# 1. Qual.
	#ts_url = ts_url_list[-1]									# Debug  letzte Qual.	
	print 'Anzahl ts-Quellen:', len(ts_url_list)
	print "ts_url_list: " + ts_url
	ts_page = download_ts_file(ts_url)							# nur 1. Liste (höchste Qual.)
	with open("./ts_liste.txt",'w') as f:						# Debug
		f.write(ts_page)
	
	pos=m3u8_url.rfind('/')
	host=m3u8_url[:pos]
	print "host: " + host							
	
	download_ts(host, ts_page, dest_video, duration)			# ts-Dateien laden + verketten

#-----------------------------------------------------------------------
duration = 60			# Dauer in sec. 
#m3u8_url = "https://mcdn.daserste.de/daserste/de/master.m3u8"							# DasErste
m3u8_url = "https://ndrfs-lh.akamaihd.net/i/ndrfs_mv@430232/master.m3u8"				# NDR
#m3u8_url = "https://rbmn-live.akamaized.net/hls/live/2002830/geoSTVDEweb/master.m3u8"	# ServusTV
dest_video = "./m3u8_Test/Video.mp4"
main(m3u8_url, dest_video, duration)
