]> gitweb.michael.orlitzky.com - dead/whatever-dl.git/blob - bin/whatever-dl
Allow the downloaders to take advantage of the websites' headers.
[dead/whatever-dl.git] / bin / whatever-dl
1 #!/usr/bin/ruby -wKU
2 #
3 # whatever-dl, a script to download online (web-based) videos.
4 #
5 # Copyright Michael Orlitzky
6 #
7 # http://michael.orlitzky.com/
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # http://www.fsf.org/licensing/licenses/gpl.html
20 #
21
22 # We need Pathname to get the real filesystem path
23 # of this script (and not, for example, the path of
24 # a symlink which points to it.
25 require 'pathname'
26
27 # And getoptlong to check for our one option, --continue.
28 require 'getoptlong'
29
30 # This bit of magic adds the parent directory (the
31 # project root) to the list of ruby load paths.
32 # Thus, our require statements will work regardless of
33 # how or from where the script was run.
34 executable = Pathname.new(__FILE__).realpath.to_s
35 $: << File.dirname(executable) + '/../'
36
37 # Load our config file.
38 require 'bin/configuration'
39
40 # And the downloaders...
41 require 'src/downloader'
42
43 # The Dir.glob that's coming up doesn't use the
44 # Ruby library path so we need to tell it where to
45 # look explicitly.
46 websites_pattern = File.dirname(executable) + '/../src/websites/*.rb'
47
48 # All of the website classes are located in one
49 # directory, so we can 'require' them automatically.
50 Dir.glob(websites_pattern).each do |r|
51 require r
52 end
53
54
55 EXIT_SUCCESS = 0
56 EXIT_NO_URL = 1
57 EXIT_INVALID_URL = 2
58 EXIT_COULDNT_GET_VIDEO_URL = 3
59 EXIT_IO_ERROR = 4
60 EXIT_ERROR_READING_FROM_VIDEO_URL = 5
61 EXIT_CONNECTION_REFUSED = 6
62 EXIT_HTTP_ERROR = 7
63 EXIT_ACCESS_DENIED = 8
64
65 def usage()
66 puts <<EOF
67
68 Usage: whatever-dl [options] <url>
69
70 Options:
71 -c, --continue Continue downloading a previously-attempted file.
72
73 EOF
74
75 end
76
77 # Only actually do something if this script was called
78 # directly (i.e. not from the tests).
79 if (__FILE__ == $0) then
80 # Default options.
81 options = { :continue => false }
82
83 # Parse the command-line options into the options hash.
84 opts = GetoptLong.new(["--continue", "-c", GetoptLong::NO_ARGUMENT],
85 ["--help", "-h", GetoptLong::NO_ARGUMENT])
86
87 opts.each do |opt, arg|
88 case opt
89 when '--help'
90 usage()
91 Kernel.exit(EXIT_SUCCESS)
92 when '--continue'
93 options[:continue] = true
94 end
95 end
96
97 # Warn about nonsensical options.
98 if options[:continue] and not (Configuration::DOWNLOAD_METHOD == :wget)
99 puts 'WARNING: The --continue flag does nothing unless DOWNLOAD_METHOD is :wget.'
100 end
101
102 # Note that GetoptLong steals its arguments from ARGV, so we don't need
103 # to take optional arguments into account when figuring out whether or not
104 # we were passed a URL.
105 if (ARGV.length < 1) then
106 # If the user didn't give us a URL, yell
107 # at him or her.
108 usage()
109 Kernel.exit(EXIT_NO_URL)
110 end
111
112 # Factory method.
113 site = Website.create(ARGV[0])
114
115 if site.nil?
116 puts 'Invalid URL.'
117 exit(EXIT_INVALID_URL)
118 end
119
120 video_url = site.get_video_url()
121
122 if video_url.nil?
123 puts 'Error retrieving video URL.'
124 exit(EXIT_COULDNT_GET_VIDEO_URL)
125 end
126
127 # The Downloader class is a factory; it should decide
128 # which subclass we get.
129 downloader = Downloader.create(Configuration::DOWNLOAD_METHOD)
130
131 # Attempt to download the file, and rescue and report
132 # any (predictable) exceptions. The wget downloader will
133 # naturally not report any of these, since it will die in
134 # its own process.
135 begin
136 downloader.download(video_url,
137 site.get_video_filename(),
138 site.headers(),
139 continue=options[:continue])
140 rescue Errno::ECONNREFUSED => e
141 puts 'The connection to the server (to download the video file) was refused. Check your connection, and try again later.'
142 Kernel.exit(EXIT_CONNECTION_REFUSED)
143 rescue Errno::EACCES => e
144 puts "Access denied. Check that you have write permission to the output file/directory. Details: #{e.message}."
145 rescue OpenURI::HTTPError => e
146 puts "An HTTP error occurred while downloading the video file: #{e.message}."
147 Kernel.exit(EXIT_HTTP_ERROR)
148 rescue IOError => e
149 puts "Input/Output Error: #{e.message}"
150 Kernel.exit(EXIT_IO_ERROR)
151 end
152
153 # Write an empty line at the end for aesthetic reasons.
154 puts ''
155
156 Kernel.exit(EXIT_SUCCESS)
157 end