Created
February 6, 2012 16:43
-
-
Save venj/1753206 to your computer and use it in GitHub Desktop.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment