publicscripts/getdistorted/getdistorted.rb

261 lines
6.4 KiB
Ruby
Executable file

#!/usr/bin/env ruby
# $Id$
# $URL$
#
# Copyright (c) 2006-2007 Ward Wouts <ward@wouts.nl>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
require 'net/http'
require 'uri'
require 'rexml/document'
require 'date'
require 'getoptlong'
@debug=false
def usage
puts <<EOT
Usage: #{$0.sub(/.*\//, "")} [options]
-c, --configfile <file> use configuration from <file> (default $HOME/.getdistortedrc)
-h, --help show this message
-l, --list list podcasts
-p, --podcast <podcast> only fetch <podcast>
-v, --verbose be more verbose
To use a proxy set the HTTP_PROXY environment variable, like such:
export HTTP_PROXY=http://<host>:<port>
EOT
exit
end
def cmdline
options = Hash.new
begin
opts = GetoptLong.new(
[ "-c", "--configfile", GetoptLong::REQUIRED_ARGUMENT ],
[ "-h", "--help", GetoptLong::NO_ARGUMENT ],
[ "-l", "--list", GetoptLong::NO_ARGUMENT ],
[ "-p", "--podcast", GetoptLong::REQUIRED_ARGUMENT ],
[ "-v", "--verbose", GetoptLong::NO_ARGUMENT ]
)
opts.quiet=true
opts.each do |opt, arg|
options[opt] = arg
end
rescue
print "#{$!}\n"
usage
end
if options["-h"]
usage
end
if options["-v"]
@verbose = true
end
return options
end
def verbose(msg)
if @verbose
puts msg
end
end
def readconfig(configfile=nil)
configfile = configfile.nil? ? "#{ENV['HOME']}/.getdistortedrc" : configfile
load configfile
end
def listpodcasts
for podcast in @podcasts.keys.sort
puts podcast
end
exit
end
def fetch(uri_str, limit = 10)
# You should choose better exception.
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
verbose("Fetching #{uri_str}\n\n")
host = URI.parse(uri_str).host
path = URI.parse(uri_str).path
query = URI.parse(uri_str).query
proxy_host = nil
proxy_port = nil
proxy_user = nil
proxy_pass = nil
if ENV['HTTP_PROXY']
begin
uri = URI.parse(ENV['HTTP_PROXY'])
proxy_host = uri.host
proxy_port = uri.port
proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
rescue
puts $!
end
end
if @debug
p "Host: #{host}"
p "Path: #{path}"
p "Query: #{query}"
p "proxy_host #{proxy_host}"
p "proxy_port #{proxy_port}"
p "proxy_user #{proxy_user}"
p "proxy_pass #{proxy_pass}"
end
Net::HTTP::Proxy(proxy_host, proxy_port,
proxy_user, proxy_pass).start(host) {|http|
if query
req = Net::HTTP::Get.new("#{path}?#{query}")
else
req = Net::HTTP::Get.new("#{path}")
end
if ! @user.nil? && ! @pass.nil?
req.basic_auth @user, @pass
end
response = http.request(req)
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then
verbose("Redirecting to #{response['location']}")
fetch(response['location'], limit - 1)
when Net::HTTPNotFound then puts "404 Not Found #{uri_str}"; response.error!
when Net::HTTPUnauthorized then puts "401 Authorization Required #{uri_str}"; response.error!
else
puts response.message
response.error!
end
}
end
def deleteold(podcast)
Dir.open(@podcasts[podcast]["savedir"]).each {|entry|
if File.file?("#{@podcasts[podcast]["savedir"]}/#{entry}") &&
! @filelist[entry]
puts " deleting #{entry}"
File.unlink("#{@podcasts[podcast]["savedir"]}/#{entry}")
end
}
end
def getenclosure(podcast, item)
enclosure = nil
item.each_element{|x|
if x.name == "enclosure"
enclosure = x
end
}
pubdate = item.get_elements("pubDate")[0]
date = nil
if ! pubdate.nil?
date = Date.parse(pubdate.text).strftime("%Y%m%d")
end
if ! enclosure.nil? && ! enclosure.attribute("url").nil?
cast = enclosure.attribute("url").value.to_s.dup
filename = cast.dup
filename.sub!(/^.*\//, "")
if @podcasts[podcast]["rename"]
replacement = @podcasts[podcast]["rename"][1].dup
if replacement.match(/%%DATE%%/)
replacement.gsub!(/%%DATE%%/, date)
end
filename.gsub!(@podcasts[podcast]["rename"][0], replacement)
end
@filelist[filename] = true
if ! File.exists?("#{@podcasts[podcast]["savedir"]}/#{filename}")
puts " getting #{@podcasts[podcast]["savedir"]}/#{filename}"
begin
response = fetch(cast)
File.open("#{@podcasts[podcast]["savedir"]}/#{filename}", "w"){|f|
f.print(response.body)
}
if @podcasts[podcast]["linkdir"]
File.symlink("#{@podcasts[podcast]["savedir"]}/#{filename}", "#{@podcasts[podcast]["linkdir"]}/#{filename}")
end
return "#{@podcasts[podcast]["savedir"]}/#{filename}"
rescue Timeout::Error, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Net::HTTPFatalError
puts " error #{$!} fetching, skipping"
rescue
puts "WTF"
end
else
return "#{@podcasts[podcast]["savedir"]}/#{filename}"
end
end
end
def getcasts(podcast=nil)
worklist = Array.new
if podcast.nil?
worklist = @podcasts.keys.sort
else
worklist.push podcast
end
for podcast in worklist
files = Array.new
puts podcast
begin
@user = @podcasts[podcast]["username"].nil? ? nil : @podcasts[podcast]["username"]
@pass = @podcasts[podcast]["password"].nil? ? nil : @podcasts[podcast]["password"]
res = fetch(@podcasts[podcast]["rss"])
@filelist = {}
# body.gsub!(/#{13.chr}#{10.chr}/, "#{10.chr}")
xmldoc = REXML::Document.new(res.body)
xmldoc.elements.each("rss/channel") {|item|
item.each_element{|x|
if x.name == "item"
verbose("Item found:\n#{x}\n\n")
files.push getenclosure(podcast, x)
end
}
}
if @podcasts[podcast]["delete"]
deleteold(podcast)
end
if @podcasts[podcast]["m3u"]
File.open(@podcasts[podcast]["m3u"], "w"){|f|
files.each{|file|
f.puts file
}
}
end
rescue
rescue Timeout::Error, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Net::HTTPFatalError
puts " error #{$!} fetching, skipping"
end
end
end
@verbose = false
options = cmdline
readconfig
options["-l"].nil? || listpodcasts
if options["-p"].nil?
getcasts
else
getcasts(options["-p"])
end