public
Last active

This is a script that helps you download any apple keynote presentation on http://www.apple.com/apple-events/. You can copy any of the keynote broadcast link, pick a resolution and feed to this script, this script will help you download the stream video.

  • Download Gist
down_video.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
#!/usr/bin/env ruby
#
# Name: Apple keynote stream video downloader
# Author: venj<i[AT]venj.me>
# Date: 2012-09-13
#
# Description:
# This script helps you download apple keynote stream video on
# http://www.apple.com/apple-events/. Video format is TS.
#
# This script uses many dirty code to make it possible to download
# all the videos that is currently available on that URL.
#
# This script tried its best to be future proof (and it proves!),
# but Apple changes the stream video file format frequently, so it
# may not be able to download future keynotes. But I will update
# this script if any change happens.
#
# How to use:
# ./down_video resolution broadcast_link
#
# Example:
# ./down_video 720p http://www.apple.com/apple-events/education-january-2012/
#
# Update Notes:
# 2012-09-13 Added support for download September 2012 apple event.
# Now show a nicer fragment count while downloading.
# 2012-03-08 Fixed a bug that will cause open-uri error under Ruby 1.8.7
# 2012-03-08 Update resolution fields, for 3 different size 720 sized video(no 1080).
# 2012-02-07 First script that can download all apple keynotes stream video finished.
#
 
require "open-uri"
 
resolution = ARGV[0]
weblink = ARGV[1]
 
size_arr = ["720p", "720", "720i", "540p", "540i", "360p", "360", "360i", "224p", "224i"]
size_hash = {"720p" => "6540", "720" => "4540", "720i" => "3340", "540p" => "2540", "540i" => "1840", "360p" => "1240", "360" => "0640", "360i" => "0440", "224p" => "0240", "224i" => "0150"}
 
if ["--help", "help", "-h"].include?(resolution) || ARGV.size < 2
puts "Usage: #{File.basename __FILE__} resolution broadcast_link"
puts "\nPossible resolution values: \n\t#{size_arr.join(', ')}"
exit 0
end
 
unless size_arr.include?(resolution)
puts "Usage: #{File.basename __FILE__} resolution broadcast_link"
puts "\nPossible resolution values: \n\t#{size_arr.join(', ')}"
exit 0
end
 
m3u_link = ""
 
puts "Parsing webpage..."
#puts open(weblink).read
#exit
begin
m3u_link = open(weblink).read.match(/http:\/\/.+?\.m3u8/)[0]
rescue NoMethodError => e
begin # Handles 1 possible javascript redirection.
redirect_link = open(weblink).read.match(/http:\/\/.+?\.html/)[0]
m3u_link = open(redirect_link).read.match(/http:\/\/.+?\.m3u8/)[0]
rescue NoMethodError => e
begin
urljs = open(weblink).read.match(/http:\/\/.+?\/url\.js/)[0] # For september-2012 event.
m3u_link = open(urljs).read.match(/http:\/\/.+?\.m3u8/)[0]
rescue Exception => e
puts "No video found."
exit 1
end
end
rescue Exception => e
puts "Unknown error."
exit 1
end
 
playlist_links = []
 
puts "Parsing outter m3u8 file..."
m3u = open(m3u_link).each_line do |line|
playlist_links << line if line =~ /^[^#]/ && line.strip != ""
end
 
available_resolutions = []
playlist_links.each do |l|
rate = ""
segments_count = l.split("/").size
case segments_count # A dirty way to identify different m3u8 link structure
when 6 # Modern format with full link
rate = File.basename(File.dirname(l))
when 5 # Previous format with full link
rate = l.split("/").last.split(".").first
if !size_hash.values.include?(rate)
rate = File.basename(l).split("_").first
end
when 1 # Previous format with only file name.
rate = l.split(/(\.|_)/).first
else
puts "Unknown outter m3u8 format. \n\nPlease file a bug with failed link."
exit 1
end
available_resolutions << (size_hash.invert)[rate]
end
 
unless available_resolutions.include?(resolution)
puts "Resolution #{resolution} is not available."
# Ignore the audio track in one particular case: apple-events/september-2010.
puts "Available resolutions: #{available_resolutions.compact.sort.join(', ')}"
exit
end
 
puts "Parsing inner m3u8 file..."
link_index = available_resolutions.index(resolution)
outfilename = "video_#{resolution}.ts"
File.unlink outfilename if File.exists? outfilename
 
video_url = playlist_links[link_index]
unless video_url =~ /^http/ # Handles inner m3u8 with only filename listed in outter m3u8
video_url = "#{File.dirname(m3u_link)}/#{video_url}"
end
outfile = open outfilename, 'w+'
 
ts_fragments_list = []
open(video_url).each_line do |line|
next if line =~ /^#/
ts_fragments_list << line
end
 
counter = 1;fragments_count = ts_fragments_list.size
ts_fragments_list.each do |line|
ts_fragment = ""
if line.include?("http://")
ts_fragment = line
else
ts_fragment = "#{File.dirname(video_url)}/#{line.strip}"
end
print "Download fragment (#{counter}/#{fragments_count})..."
infile = open ts_fragment
outfile.write infile.read
infile.close
counter += 1
puts "Done"
end
outfile.close

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.