#!/usr/bin/python3

# coding: utf-8

# For HTTP-requests
import requests

# Various modules
import json
import sys

fps = 25            # Frames per second of video files
iwidth = 400;		# Input video width
iheight = 300;		# Input video height
islidewidth = 640;		# Input slide width
islideheight = 512;		# Input slide height
oheight = 300;		# Output video height
crf = 20;			# H264 Video quality (lower is better, 23 is default)

ffmpeg = 'ffmpeg '
ffmpeg_options_mp4 = ' -c:v libx264 -preset slow -crf ' + str(crf) + ' -r ' + str(fps)
ffmpeg_options_jpeg = ' -v 4 -loop_input -shortest -f image2 -r 0.04 '
ffmpeg_options_ts = ' -vbsf h264_mp4toannexb'

# Optional: command to process audio, set to None or empty string if no processing is needed 
process_audio = 'sox --norm videos/audio_orig.wav videos/audio.wav compand 0.3,1 6:-80,-60,-20 -8 -90 0.2'

wget = 'wget --load-cookies=cookies.txt '

cmdfile_download = 'get_videos.sh'
cmdfile_convert = 'convert_videos.sh'

json_file = sys.argv[1]


def get_json(url):
    '''
    Do a HTTP GET request on "url", and parse the JSON data 
    returned by the request into a Python data structure.
    Returns None in case of an error.
    '''
    response = requests.get(url)
    if (response.status_code == 200):
        json_response = response.text
        return json.loads(json_response)
    else:
        print("GET", url, "returned", response.status_code)
        return None


if json_file.startswith("http"):
    get_json(json_file)
else:
    with open(json_file) as json_data:
        d = json.load(json_data)


title = d['d']['Presentation']['Title']
duration = d['d']['Presentation']['Duration']
date = d['d']['Presentation']['AirDate']

print('Presentation:', title, date)

for stream in d['d']['Presentation']['Streams']:
    slides = len(stream['Slides'])
    if slides:
        slidearray = stream['Slides']
        sbu = stream['SlideBaseUrl']
        sft = stream['SlideImageFileNameTemplate']
        slide_aspect = stream['AspectRatio']
        print(slides, 'slides at', sbu)
        
    if 'VideoUrls' in stream:

        video_aspect = stream['AspectRatio']
        for vid in stream['VideoUrls']:
            if vid['MediaType'] == 'MP4':
                vid_url = vid['Location']
                print('Video URL:', vid_url)

slide_offset = []
slide_url = []
slide_file = []

# TODO: convert filename template in sft to Python template (e.g. 'slide_{0:D4}.jpg' to 'slide_{:04d}.jpg') with a regexp

for sd in slidearray:
    nr = sd['Number']
    time = sd['Time']
    sf = 'slide_{:04d}.jpg'.format(nr)
    url = sbu + sf
    slide_offset.append(time)
    slide_url.append(url)
    slide_file.append(sf)


slide_duration = []
idx = 0

for so in slide_offset:
    try:
         sd = slide_offset[idx + 1] - so
    except:
        sd = duration - so
    slide_duration.append(sd)
    idx += 1


with open('urls.txt', 'w') as f:
    for url in slide_url:
        f.write(url + '\n')
    f.write(vid_url + '\n')
    
cmds = open(cmdfile_download, 'w')
cmds.write('#!/bin/sh\n')
cmds.write('mkdir videos\n')
cmds.write('mkdir slides\n')
cmds.write('\n# Download slides and presentation video\n')

slide_jpg = []
idx = 0

for url in slide_url:
    jpg = 'slides/' + slide_file[idx] 
    cmds.write(wget + '-O ' + jpg + ' ' + url + '\n')
    slide_jpg.append(jpg)
    idx += 1

cmds.write(wget + '-O videos/_original.mp4 ' + vid_url + '\n')

cmds.close()

cmds = open(cmdfile_convert, 'w')
cmds.write('#!/bin/sh\n')
cmds.write('\n# Convert slide JPG-files to MPEG transport stream video files\n')

slide_video = []
idx = 0

for jpg in slide_jpg:
    sd = slide_duration[idx]
    frames = round(fps * sd / 1000)
    print('Slide', idx, jpg, sd, 'ms', frames, 'frames')
    out = 'videos/slide_{:04d}.ts'.format(idx)
    slide_video.append(out)
    cmd = ffmpeg + ' ' + ffmpeg_options_jpeg + ' -i ' + jpg + ffmpeg_options_mp4 + ' -vframes ' + str(frames) + ' -an -y ' + ffmpeg_options_ts + ' ' + out
    cmds.write(cmd + '\n')
    idx += 1

# Concatenate slide videos

cmds.write('\n# Concatenate slide videos\n')
cmds.write('cat videos/*.ts >videos/slides.ts\n')
cmds.write(ffmpeg + '-i videos/slides.ts -isync -acodec copy -vcodec copy -absf aac_adtstoasc videos/slides.mp4\n')

# Extract audio and video

cmds.write('\n# Extract video\n')
cmds.write(ffmpeg + '-i videos/_original.mp4 -an -vcodec copy videos/presentation.m4v\n')

cmds.write('\n# Extract audio\n')
cmds.write(ffmpeg + '-i videos/_original.mp4 -vn -acodec pcm_s16le videos/audio.wav\n')

if process_audio:
    cmds.write('\n# Process audio\n')
    cmds.write('mv videos/audio.wav videos/audio_orig.wav\n')
    cmds.write(process_audio + '\n')

cmds.write('\n# Encode audio\n')
cmds.write(ffmpeg + '-i videos/audio.wav -vn -c aac -q:a 1 videos/audio.m4a\n')

oslidescale = oheight/islideheight; 	# Slide scaling factor
oslidewidth = islidewidth * oslidescale;	# Output slide width
owidth = oslidewidth + iwidth;		# Composite output video width

ovidwidth_large = iwidth * (islideheight/oheight);	# Output video width for large version of composite video
owidth_large = ovidwidth_large + islidewidth;		# Large composite output video width

owidth_large += 4 - (owidth_large % 4);			# Round output width up to nearest multiple of 4

cmds.write('\n# Combine videos\n')
cmds.write(ffmpeg + '-i videos/ -i $audio_stream -filter:v "[in]pad={}:{}:{}:0,[left]overlay=0:0[out];movie=videos/slides.mp4,scale=-1:{}[left]" -c:v libx264 -preset slow -crf {} -c:a copy -y videos/combined.mp4\n'.format(owidth, oheight, oslidewidth, oheight, crf))
cmds.write(ffmpeg + '-i videos/ -i $audio_stream -filter:v "[in]scale=-1:{},pad={}:{}:{}:0,[left]overlay=0:0[out];movie=videos/slides.mp4,scale=-1:{}[left]" -c:v libx264 -preset slow -crf {} -c:a copy -y videos/combined_large.mp4\n'.format(islideheight, owidth_large, islideheight, islidewidth, islideheight, crf))

destdir = date + ' ' + title

cmds.write('\n# Rename output directory\n')
cmds.write('mv slides videos\n')
cmds.write('mv videos "' + destdir + '"\n')

cmds.close()
