overdrive 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +57 -0
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/bin/overdrive +34 -0
- data/lib/dsl.rb +87 -0
- data/lib/feed.rb +75 -0
- data/lib/lexicon.rb +111 -0
- data/lib/options.rb +71 -0
- data/lib/torrent.rb +16 -0
- data/test/fixtures/test.rss +200 -0
- data/test/recipe.rb +2 -0
- data/test/test_helper.rb +28 -0
- data/test/unit/dsl.rb +105 -0
- data/test/unit/feed.rb +63 -0
- data/test/unit/lexicon.rb +112 -0
- data/test/unit/torrent.rb +22 -0
- metadata +103 -0
data/README.markdown
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
Overdrive
|
2
|
+
=========
|
3
|
+
|
4
|
+
Overdrive is a RSS frontend for the transmission bit torrent client. It has hooks to make downloading TV series
|
5
|
+
much easier, and allows recipes with callbacks to make filtering show much easier.
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
Overdrive is a ruby gem that uses gemcutter. To install, simply do the following:
|
11
|
+
|
12
|
+
sudo gem install gemcutter
|
13
|
+
sudo gem tumble
|
14
|
+
|
15
|
+
then
|
16
|
+
|
17
|
+
sudo gem install overdrive
|
18
|
+
|
19
|
+
This should install the overdrive binary in your path.
|
20
|
+
|
21
|
+
Usage
|
22
|
+
-----
|
23
|
+
|
24
|
+
To do anything useful, you need to setup a recipe. You can store the recipe (in Linux) at
|
25
|
+
|
26
|
+
/etc/overdrive.rb
|
27
|
+
/var/transmission/config/overdrive.rb
|
28
|
+
$HOME/.overdrive/overdrive.rb
|
29
|
+
|
30
|
+
and you can add paths via the -c argument:
|
31
|
+
|
32
|
+
overdrive -c /path/to/recipe.rb
|
33
|
+
|
34
|
+
A sample recipe might look like this:
|
35
|
+
|
36
|
+
add_feed "http://www.mytorrents.com/torrents.rss"
|
37
|
+
add_feed "http://www.myothertorrents.com/torrents.rss"
|
38
|
+
|
39
|
+
add_title "24"
|
40
|
+
add_title "30 Rock"
|
41
|
+
add_title "Alias"
|
42
|
+
|
43
|
+
filter do |item|
|
44
|
+
parsed = parse_metadata(item)
|
45
|
+
if parsed[:title] && parsed[:series] && parsed[:episode]
|
46
|
+
title = parsed[:title].split(' ').map { |t| t[0..0].upcase + t[1..-1] }.join(' ')
|
47
|
+
|
48
|
+
if titles.include?(title)
|
49
|
+
target = "/videos/#{title}/Season #{parsed[:series].to_i.to_s}"
|
50
|
+
download(item.url, :download_dir => target)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
You can get a full list of command line arguments by running
|
56
|
+
|
57
|
+
overdrive -h
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |s|
|
4
|
+
s.name = "overdrive"
|
5
|
+
s.executables = "overdrive"
|
6
|
+
s.summary = "An RSS frontend for transmission"
|
7
|
+
s.email = "myles@madpilot.com.au"
|
8
|
+
s.homepage = "http://github.com/madpilot/overdrive"
|
9
|
+
s.description = "An RSS frontend for transmissions. It defines a basic DSL that allows you decide what you do once the files have been downloaded"
|
10
|
+
s.authors = ["Myles Eftos"]
|
11
|
+
s.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
|
12
|
+
|
13
|
+
s.add_dependency 'daemons'
|
14
|
+
s.add_dependency 'transmission-client'
|
15
|
+
s.add_dependency 'SyslogLogger'
|
16
|
+
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
21
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/overdrive
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.join(File.dirname(File.expand_path(__FILE__)), '..', 'lib')
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'syslog_logger'
|
6
|
+
require 'daemons'
|
7
|
+
require 'options'
|
8
|
+
require 'feed'
|
9
|
+
require 'torrent'
|
10
|
+
require 'lexicon'
|
11
|
+
require 'dsl'
|
12
|
+
require 'transmission-client'
|
13
|
+
|
14
|
+
options = Options.parse
|
15
|
+
|
16
|
+
if options[:ontop]
|
17
|
+
options[:logger] = Logger.new(STDOUT)
|
18
|
+
else
|
19
|
+
options[:logger] = SyslogLogger.new('overdrive')
|
20
|
+
end
|
21
|
+
|
22
|
+
Daemons.call(options) do
|
23
|
+
@last_run = nil
|
24
|
+
@feed = Feed.new(options)
|
25
|
+
|
26
|
+
options[:logger].info("Running every #{options[:interval].to_s} minute#{options[:interval] == 1 ? "" : "s"}")
|
27
|
+
loop {
|
28
|
+
if @last_run == nil || (@last_run.to_i + options[:interval] * 60) <= Time.now.to_i
|
29
|
+
@feed.check
|
30
|
+
@last_run = Time.now
|
31
|
+
end
|
32
|
+
sleep(1)
|
33
|
+
}
|
34
|
+
end
|
data/lib/dsl.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'transmission-client'
|
2
|
+
|
3
|
+
class DSL
|
4
|
+
attr_accessor :titles
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@options = options
|
8
|
+
@items, @torrents, @feeds, @titles = [], [], [], []
|
9
|
+
|
10
|
+
contents = ''
|
11
|
+
options[:filter_paths].each do |path|
|
12
|
+
begin
|
13
|
+
File.open(path, 'r') do |fh|
|
14
|
+
@options[:logger].info "Adding #{path} to recipe list"
|
15
|
+
contents += fh.read
|
16
|
+
end
|
17
|
+
rescue => e
|
18
|
+
@options[:logger].info "Couldn't add #{path} to recipe list: #{e}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
eval <<-END
|
23
|
+
def run_dsl(obj = nil, type = nil)
|
24
|
+
@items = []
|
25
|
+
@torrents = []
|
26
|
+
|
27
|
+
case(type)
|
28
|
+
when :filter
|
29
|
+
@items = obj
|
30
|
+
when :torrent
|
31
|
+
@torrents = obj
|
32
|
+
end
|
33
|
+
|
34
|
+
#{contents}
|
35
|
+
end
|
36
|
+
END
|
37
|
+
|
38
|
+
# Prime the DSL
|
39
|
+
@primed = false
|
40
|
+
run_dsl
|
41
|
+
@primed = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def perform_filter(items)
|
45
|
+
run_dsl(items, :filter)
|
46
|
+
end
|
47
|
+
|
48
|
+
def download_complete(torrents)
|
49
|
+
run_dsl(torrents, :torrent)
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_feed(feed)
|
53
|
+
@feeds << feed unless @primed
|
54
|
+
end
|
55
|
+
|
56
|
+
def feeds
|
57
|
+
@feeds
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_title(title)
|
61
|
+
@titles << title unless @primed
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_metadata(item)
|
65
|
+
Lexicon.parse(item)
|
66
|
+
end
|
67
|
+
|
68
|
+
def log
|
69
|
+
options[:logger]
|
70
|
+
end
|
71
|
+
|
72
|
+
def options
|
73
|
+
@options
|
74
|
+
end
|
75
|
+
|
76
|
+
def download(link, options = {})
|
77
|
+
Torrent.download(link, options, self.options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def filter &block
|
81
|
+
@items.each { |item| yield(item) }
|
82
|
+
end
|
83
|
+
|
84
|
+
def after &block
|
85
|
+
@torrents.each { |torrent| yield(torrent) }
|
86
|
+
end
|
87
|
+
end
|
data/lib/feed.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'feedzirra'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
class Feed
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
@dsl = DSL.new(@options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def check
|
11
|
+
@results ? update_feeds : check_feeds
|
12
|
+
check_downloads
|
13
|
+
end
|
14
|
+
|
15
|
+
def check_feeds
|
16
|
+
number_processed = 0
|
17
|
+
feeds = @dsl.feeds
|
18
|
+
feed_size = feeds.size
|
19
|
+
|
20
|
+
begin
|
21
|
+
@options[:logger].info "Fetching #{feed_size} feed#{feed_size == 1 ? "" : "s"}" if @options[:verbose]
|
22
|
+
|
23
|
+
@results = Feedzirra::Feed.fetch_and_parse(feeds)
|
24
|
+
|
25
|
+
@options[:logger].info "Processing #{feed_size} feed#{feed_size == 1 ? "" : "s"}" if @options[:verbose]
|
26
|
+
|
27
|
+
@results.each do |url, rss|
|
28
|
+
if rss.is_a?(Fixnum)
|
29
|
+
@options[:logger].error "Couldn't download #{url}"
|
30
|
+
else
|
31
|
+
@dsl.perform_filter(rss.entries)
|
32
|
+
number_processed += 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rescue => e
|
36
|
+
@options[:logger].error "#{e}"
|
37
|
+
end
|
38
|
+
|
39
|
+
@options[:logger].info "Processed #{number_processed} out of #{feed_size} feed#{feed_size == 1 ? "" : "s"}" if @options[:verbose]
|
40
|
+
return number_processed
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_feeds
|
44
|
+
number_processed = 0
|
45
|
+
number_skipped = 0
|
46
|
+
feeds = @results
|
47
|
+
feed_size = feeds.size
|
48
|
+
|
49
|
+
begin
|
50
|
+
@options[:logger].info "Fetching #{feed_size} feed#{feed_size ? "" : "s"}" if @options[:verbose]
|
51
|
+
|
52
|
+
results = Feedzirra::Feed.update(feeds.values)
|
53
|
+
@options[:logger].info "Processing #{feed_size} feed#{feed_size ? "" : "s"}" if @options[:verbose]
|
54
|
+
|
55
|
+
if results.updated?
|
56
|
+
@dsl.perform_filter(results.new_entries)
|
57
|
+
else
|
58
|
+
number_skipped += 1
|
59
|
+
end
|
60
|
+
number_processed += 1
|
61
|
+
rescue => e
|
62
|
+
@options[:logger].error "#{e}"
|
63
|
+
end
|
64
|
+
|
65
|
+
@options[:logger].info "Processed #{number_processed} out of #{feed_size} feed#{feed_size ? "" : "s"} (#{number_skipped} skipped)" if @options[:verbose]
|
66
|
+
return number_processed
|
67
|
+
end
|
68
|
+
|
69
|
+
def check_downloads
|
70
|
+
return if @options[:dry_run]
|
71
|
+
@options[:logger].info "Checking Download Status" if @options[:verbose]
|
72
|
+
t = Transmission::Client.new(@options[:transmission_server], @options[:transmission_port])
|
73
|
+
@dsl.download_complete(t.torrents)
|
74
|
+
end
|
75
|
+
end
|
data/lib/lexicon.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
class Lexicon
|
2
|
+
def self.parse(item)
|
3
|
+
if item.is_a?(String)
|
4
|
+
title = item
|
5
|
+
else
|
6
|
+
title = item.title
|
7
|
+
end
|
8
|
+
|
9
|
+
parsed = {
|
10
|
+
:title => nil,
|
11
|
+
:series => nil,
|
12
|
+
:episode => nil,
|
13
|
+
:size => nil,
|
14
|
+
:publish_date => nil,
|
15
|
+
:high_def => false
|
16
|
+
}
|
17
|
+
|
18
|
+
tokens = title.split(/\.| /)
|
19
|
+
titles = []
|
20
|
+
state = :title
|
21
|
+
|
22
|
+
while tokens.length > 0
|
23
|
+
token = tokens.shift
|
24
|
+
|
25
|
+
case(state)
|
26
|
+
when :title
|
27
|
+
if token =~ /s(\d+)e(\d+)/i
|
28
|
+
parsed[:series] = Regexp.last_match[1].to_i
|
29
|
+
parsed[:episode] = Regexp.last_match[2].to_i
|
30
|
+
state = :done
|
31
|
+
elsif token =~ /^(\d)x?(\d\d)$/i
|
32
|
+
parsed[:series] = Regexp.last_match[1].to_i
|
33
|
+
parsed[:episode] = Regexp.last_match[2].to_i
|
34
|
+
state = :done
|
35
|
+
elsif token =~ /^s(\d+)$/i
|
36
|
+
parsed[:series] = Regexp.last_match[1].to_i
|
37
|
+
state = :series
|
38
|
+
else
|
39
|
+
titles << token
|
40
|
+
end
|
41
|
+
when :series
|
42
|
+
if token =~ /^e(\d+)$/i
|
43
|
+
parsed[:episode] = Regexp.last_match[1].to_i
|
44
|
+
state = :done
|
45
|
+
end
|
46
|
+
when :done
|
47
|
+
if token =~ /HDTV|720p|1080|mkv/i
|
48
|
+
parsed[:high_def] = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Reparse to look for some hd/sd info
|
54
|
+
if state != :done
|
55
|
+
tokens = title.split(/\.| /)
|
56
|
+
titles = [] if state == :title
|
57
|
+
|
58
|
+
while tokens.length > 0
|
59
|
+
token = tokens.shift
|
60
|
+
case(state)
|
61
|
+
when :title
|
62
|
+
if token =~ /HDTV|720p|1080|mkv/i
|
63
|
+
parsed[:high_def] = true
|
64
|
+
state = :done
|
65
|
+
else
|
66
|
+
titles << token
|
67
|
+
end
|
68
|
+
when :series
|
69
|
+
if token =~ /HDTV|720p|1080|mkv/i
|
70
|
+
parsed[:high_def] = true
|
71
|
+
state = :done
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Try to split on some common lines
|
78
|
+
if state != :done
|
79
|
+
tokens = title.split(/\.| /)
|
80
|
+
titles = [] if state == :title
|
81
|
+
|
82
|
+
while tokens.length > 0
|
83
|
+
token = tokens.shift
|
84
|
+
case(state)
|
85
|
+
when :title
|
86
|
+
if token =~ /vid|DVDRip|xvid|divx|repack|internal/i
|
87
|
+
state = :done
|
88
|
+
elsif token =~ /avi|ts|mkv|mpg/i
|
89
|
+
state = :done
|
90
|
+
else
|
91
|
+
titles << token
|
92
|
+
end
|
93
|
+
when :series
|
94
|
+
if token =~ /vid|DVDRip|xvid|divx|repack|internal/i
|
95
|
+
state = :done
|
96
|
+
elsif token =~ /avi|ts|mkv|mpg/i
|
97
|
+
state = :done
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
if state == :title
|
104
|
+
titles = title.split(/\.| /)
|
105
|
+
end
|
106
|
+
|
107
|
+
parsed[:title] = titles.join(' ')
|
108
|
+
parsed[:publish_date] = item.published if item.published
|
109
|
+
return parsed
|
110
|
+
end
|
111
|
+
end
|
data/lib/options.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class Options
|
4
|
+
def self.parse
|
5
|
+
options = {
|
6
|
+
:multiple => false
|
7
|
+
}
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: #{opts.program_name} [options]"
|
11
|
+
|
12
|
+
options[:verbose] = false
|
13
|
+
opts.on('-V', '--verbose', 'Be more verbose') do
|
14
|
+
options[:verbose] = true
|
15
|
+
end
|
16
|
+
|
17
|
+
options[:dry_run] = false
|
18
|
+
opts.on('-d', '--dry-run', "Don't actually queue torrents up") do |dry_run|
|
19
|
+
options[:dry_run] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
options[:interval] = 15
|
23
|
+
opts.on('-i [n]', '--interval [n]', /\d+/, 'Number of minutes between checks (Default: 15)') do |interval|
|
24
|
+
options[:interval] = interval.to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-h', '--help', "You're looking at it") do
|
28
|
+
puts opts
|
29
|
+
exit(-1)
|
30
|
+
end
|
31
|
+
|
32
|
+
options[:ontop] = false
|
33
|
+
opts.on('-f', '--foreground', "Run in the foreground") do
|
34
|
+
options[:ontop] = true
|
35
|
+
end
|
36
|
+
|
37
|
+
options[:filter_paths] = [ File.join('', File.dirname(__FILE__), '..', 'recipe.rb'), File.join('', 'etc', 'overdrive.rb'), File.join('', 'var' , 'transmission' , 'config' , 'overdrive.rb') ]
|
38
|
+
options[:filter_paths] << File.join(ENV['HOME'], '.overdrive', 'overdrive.rb') if ENV['HOME']
|
39
|
+
options[:filter_paths] += ENV['OVERDRIVE_RECIPES'].split(':') if ENV['OVERDRIVE_RECIPES']
|
40
|
+
|
41
|
+
opts.on('-r [recipe]', '--recipes [recipe]', /.+/, 'Reads in additional recipes') do |recipes|
|
42
|
+
options[:filter_paths] << recipes
|
43
|
+
end
|
44
|
+
|
45
|
+
options[:transmission_server] = 'localhost'
|
46
|
+
opts.on('-s [server]', '--transmission_server [server]', /.+/, 'Address of the transmission server (Default: localhost)') do |server|
|
47
|
+
options[:transmission_server] = server
|
48
|
+
end
|
49
|
+
|
50
|
+
options[:transmission_port] = 9091
|
51
|
+
opts.on('-p [port]', '--transmission_port', /\d+/, 'Port of the transmission server (Default: 9091)') do |port|
|
52
|
+
options[:transmission_port] = port.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-v', '--version') do
|
56
|
+
File.open(File.join(File.dirname(__FILE__), '..', 'VERSION')) do |fh|
|
57
|
+
puts fh.read
|
58
|
+
end
|
59
|
+
exit(0)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
optparse.parse!
|
65
|
+
options
|
66
|
+
rescue OptionParser::InvalidOption => e
|
67
|
+
puts optparse
|
68
|
+
exit(-1)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/torrent.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
class Torrent
|
5
|
+
include OpenURI::OpenRead
|
6
|
+
|
7
|
+
def self.download(link, http_options = {}, options = {})
|
8
|
+
return if options[:dry_run]
|
9
|
+
|
10
|
+
uri = URI.parse(link)
|
11
|
+
download_dir = http_options.delete(:download_dir)
|
12
|
+
torrent = uri.read(http_options)
|
13
|
+
t = Transmission::Client.new(options[:transmission_server], options[:transmission_port])
|
14
|
+
t.add_torrent('metainfo' => Base64.encode64(torrent), 'download-dir' => download_dir)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
2
|
+
<rss version="2.0">
|
3
|
+
<channel>
|
4
|
+
<ttl>10</ttl>
|
5
|
+
<title>My Torrent Site</title>
|
6
|
+
<link>http://www.mytorrentsite.com</link>
|
7
|
+
<description>This is a dummy torrent site</description>
|
8
|
+
<language>en-usde</language>
|
9
|
+
<item><title>dollhouse.s02e08.internal.hdtv.xvid-2hd.avi</title>
|
10
|
+
<link>http://www.mytorrentsite.com/download/dollhouse.s02e08.internal.hdtv.xvid-2hd.avi.torrent</link>
|
11
|
+
|
12
|
+
<description>
|
13
|
+
Category: Dollhouse
|
14
|
+
Size: 349.30 MB
|
15
|
+
</description>
|
16
|
+
</item>
|
17
|
+
<item><title>Ghost.Adventures.S03E07.HDTV.XviD-CRiMSON</title>
|
18
|
+
<link>http://www.mytorrentsite.com/download/ghost.adventures.s03e07.hdtv.xvid-crimson.avi.torrent</link>
|
19
|
+
<description>
|
20
|
+
Category: (Reality TV - Un-scripted)
|
21
|
+
Size: 350.15 MB
|
22
|
+
</description>
|
23
|
+
</item>
|
24
|
+
<item><title>Dollhouse.S02E08.720p.HDTV.x264-IMMERSE[NUKED]</title>
|
25
|
+
|
26
|
+
<link>http://www.mytorrentsite.com/download/dollhouse.s02e08.720p.hdtv.x264-immerse.mkv.torrent</link>
|
27
|
+
<description>
|
28
|
+
Category: Dollhouse
|
29
|
+
Size: 1.09 GB
|
30
|
+
</description>
|
31
|
+
</item>
|
32
|
+
<item><title>Sanctuary.US.S02E09.720p.HDTV.x264-ORENJi</title>
|
33
|
+
<link>http://www.mytorrentsite.com/download/sanctuary.us.s02e09.720p.hdtv.x264-orenji.mkv.torrent</link>
|
34
|
+
<description>
|
35
|
+
Category: Sanctuary
|
36
|
+
Size: 1.09 GB
|
37
|
+
</description>
|
38
|
+
|
39
|
+
</item>
|
40
|
+
<item><title>National.Geographic.Samurai.Sword.720p.HDTV.x264-DiCH</title>
|
41
|
+
<link>http://www.mytorrentsite.com/download/dich-national.geographic.ss.720p.hdtv.x264.mkv.torrent</link>
|
42
|
+
<description>
|
43
|
+
Category: (National Geographic)
|
44
|
+
Size: 1.09 GB
|
45
|
+
</description>
|
46
|
+
</item>
|
47
|
+
<item><title>Conan.O'Brien.12.11.2009.Zach.Braff.1080i.AC3.MPEG2-TSR [TS][Transport Stream]</title>
|
48
|
+
<link>http://www.mytorrentsite.com/download/conan.o%27brien.12.11.2009.zach.braff.1080i.ac3.mpeg2.tsr.ts.torrent</link>
|
49
|
+
|
50
|
+
<description>
|
51
|
+
Category: Tonight Show
|
52
|
+
Size: 4.18 GB
|
53
|
+
</description>
|
54
|
+
</item>
|
55
|
+
<item><title>Dollhouse S02E07 [reencode] [P0W4] [iPod].mp4</title>
|
56
|
+
<link>http://www.mytorrentsite.com/download/Dollhouse%20S02E07%20%5Breencode%5D%20%5BP0W4%5D%20%5BiPod%5D.mp4.torrent</link>
|
57
|
+
<description>
|
58
|
+
Category: (Portable TV Episodes)
|
59
|
+
Size: 387.05 MB
|
60
|
+
</description>
|
61
|
+
</item>
|
62
|
+
<item><title>Law.and.Order.S20E11.REPACK.720p.HDTV.x264-IMMERSE</title>
|
63
|
+
<link>http://www.mytorrentsite.com/download/law.and.order.s20e11.repack.720p.hdtv.x264-immerse.mkv.torrent</link>
|
64
|
+
|
65
|
+
<description>
|
66
|
+
Category: Law and Order
|
67
|
+
Size: 1.09 GB
|
68
|
+
</description>
|
69
|
+
</item>
|
70
|
+
<item><title>Hardcore TV (1994, HBO series)</title>
|
71
|
+
<link>http://www.mytorrentsite.com/download/Hardcore%20TV%20%281994%20HBO%20Complete%20Series%29.torrent</link>
|
72
|
+
<description>
|
73
|
+
Category: (US Comedy)
|
74
|
+
Size: 1.88 GB
|
75
|
+
</description>
|
76
|
+
</item>
|
77
|
+
<item><title>yes.virginia.hdtv.xvid-notv.avi</title>
|
78
|
+
|
79
|
+
<link>http://www.mytorrentsite.com/download/yes.virginia.hdtv.xvid-notv.avi.torrent</link>
|
80
|
+
<description>
|
81
|
+
Category: (Cartoons)
|
82
|
+
Size: 175.16 MB
|
83
|
+
</description>
|
84
|
+
</item>
|
85
|
+
<item><title>Crash.S02E12.HDTV.XviD-RED</title>
|
86
|
+
<link>http://www.mytorrentsite.com/download/Crash.S02E12.HDTV.XviD-RED.avi.torrent</link>
|
87
|
+
<description>
|
88
|
+
Category: (US Drama)
|
89
|
+
Size: 550.88 MB
|
90
|
+
</description>
|
91
|
+
|
92
|
+
</item>
|
93
|
+
<item><title>Being.Erica.S02.INTERNAL.HDTV.XviD-BitMeTV</title>
|
94
|
+
<link>http://www.mytorrentsite.com/download/Being.Erica.S02.INTERNAL.HDTV.XviD-BitMeTV.torrent</link>
|
95
|
+
<description>
|
96
|
+
Category: (Canadian TV)
|
97
|
+
Size: 4.10 GB
|
98
|
+
</description>
|
99
|
+
</item>
|
100
|
+
<item><title>Friday.Night.Lights.S04E06.HDTV.XviD-RED</title>
|
101
|
+
<link>http://www.mytorrentsite.com/download/Friday.Night.Lights.S04E06.HDTV.XviD-RED.avi.torrent</link>
|
102
|
+
|
103
|
+
<description>
|
104
|
+
Category: Friday Night Lights
|
105
|
+
Size: 350.65 MB
|
106
|
+
</description>
|
107
|
+
</item>
|
108
|
+
<item><title>National.Geographic.Inside.The.Pentagon.Xvid.DVDRip.avi</title>
|
109
|
+
<link>http://www.mytorrentsite.com/download/National.Geographic.Inside.The.Pentagon.Xvid.DVDRip.avi.torrent</link>
|
110
|
+
<description>
|
111
|
+
Category: (National Geographic)
|
112
|
+
Size: 700.17 MB
|
113
|
+
</description>
|
114
|
+
</item>
|
115
|
+
<item><title>Sanctuary.US.S02E09.Penance.HDTV.XviD-FQM</title>
|
116
|
+
<link>http://www.mytorrentsite.com/download/sanctuary.us.s02e09.hdtv.xvid-fqm.avi.torrent</link>
|
117
|
+
|
118
|
+
<description>
|
119
|
+
Category: Sanctuary
|
120
|
+
Size: 350.02 MB
|
121
|
+
</description>
|
122
|
+
</item>
|
123
|
+
<item><title>Million.Dollar.Listing.S03E03.Dolphins.Rats.and.Next.Door.Neighbors.HDTV.XviD-SAiNTS.avi</title>
|
124
|
+
<link>http://www.mytorrentsite.com/download/Million.Dollar.Listing.S03E03.Dolphins.Rats.and.Next.Door.Neighbors.HDTV.XviD-SAiNTS.avi.torrent</link>
|
125
|
+
<description>
|
126
|
+
Category: (Reality TV - Un-scripted)
|
127
|
+
Size: 349.30 MB
|
128
|
+
</description>
|
129
|
+
</item>
|
130
|
+
<item><title>Dollhouse.S02E07.Meet.Jane.Doe.720p.AC3.MPEG2-TSR [TS][Transport Stream]</title>
|
131
|
+
|
132
|
+
<link>http://www.mytorrentsite.com/download/dollhouse.s02e07.meet.jane.doe.720p.ac3.mpeg2.tsr.ts.torrent</link>
|
133
|
+
<description>
|
134
|
+
Category: Dollhouse
|
135
|
+
Size: 4.49 GB
|
136
|
+
</description>
|
137
|
+
</item>
|
138
|
+
<item><title>Dollhouse.S02E07.REPACK.720p.HDTV.x264-IMMERSE</title>
|
139
|
+
<link>http://www.mytorrentsite.com/download/dollhouse.s02e07.repack.720p.hdtv.x264-immerse.mkv.torrent</link>
|
140
|
+
<description>
|
141
|
+
Category: Dollhouse
|
142
|
+
Size: 1.09 GB
|
143
|
+
</description>
|
144
|
+
|
145
|
+
</item>
|
146
|
+
<item><title>The.Real.World.Road.Rules.Challenge.S18E10.DSR.XviD-CRiMSON</title>
|
147
|
+
<link>http://www.mytorrentsite.com/download/the.real.world.road.rules.challenge.s18e10.dsr.xvid-crimson.avi.torrent</link>
|
148
|
+
<description>
|
149
|
+
Category: (Reality TV - Competitive)
|
150
|
+
Size: 348.92 MB
|
151
|
+
</description>
|
152
|
+
</item>
|
153
|
+
<item><title>The.Real.World.Road.Rules.Challenge.S18E09.DSR.XviD-CRiMSON</title>
|
154
|
+
<link>http://www.mytorrentsite.com/download/The.Real.World.Road.Rules.Challenge.S18E09.DSR.XviD-CRiMSON.avi.torrent</link>
|
155
|
+
|
156
|
+
<description>
|
157
|
+
Category: (Reality TV - Competitive)
|
158
|
+
Size: 348.60 MB
|
159
|
+
</description>
|
160
|
+
</item>
|
161
|
+
<item><title>Ugly.Betty.S04E09.720p.HDTV.x264-CTU</title>
|
162
|
+
<link>http://www.mytorrentsite.com/download/ugly.betty.s04e09.720p.hdtv.x264-ctu.mkv.torrent</link>
|
163
|
+
<description>
|
164
|
+
Category: Ugly Betty
|
165
|
+
Size: 1.09 GB
|
166
|
+
</description>
|
167
|
+
</item>
|
168
|
+
<item><title>Ugly.Betty.S04E09.Be-Shure.HDTV.XviD-FQM</title>
|
169
|
+
<link>http://www.mytorrentsite.com/download/ugly.betty.s04e09.hdtv.xvid-fqm.avi.torrent</link>
|
170
|
+
|
171
|
+
<description>
|
172
|
+
Category: Ugly Betty
|
173
|
+
Size: 350.03 MB
|
174
|
+
</description>
|
175
|
+
</item>
|
176
|
+
<item><title>Dollhouse S02E04 [reencode] [2hd] [iPod].mp4</title>
|
177
|
+
<link>http://www.mytorrentsite.com/download/Dollhouse%20S02E04%20%5Breencode%5D%20%5B2hd%5D%20%5BiPod%5D.mp4.torrent</link>
|
178
|
+
<description>
|
179
|
+
Category: (Portable TV Episodes)
|
180
|
+
Size: 383.00 MB
|
181
|
+
</description>
|
182
|
+
</item>
|
183
|
+
<item><title>Dollhouse S02E03 [reencode] [xii] [iPod].mp4</title>
|
184
|
+
|
185
|
+
<link>http://www.mytorrentsite.com/download/Dollhouse%20S02E03%20%5Breencode%5D%20%5Bxii%5D%20%5BiPod%5D.mp4.torrent</link>
|
186
|
+
<description>
|
187
|
+
Category: (Portable TV Episodes)
|
188
|
+
Size: 388.40 MB
|
189
|
+
</description>
|
190
|
+
</item>
|
191
|
+
<item><title>Maury.2009.12.11.DSR.XviD-sHoTV</title>
|
192
|
+
<link>http://www.mytorrentsite.com/download/maury.2009.12.11.dsr.xvid-shotv.avi.torrent</link>
|
193
|
+
<description>
|
194
|
+
Category: Maury
|
195
|
+
Size: 349.14 MB
|
196
|
+
</description>
|
197
|
+
|
198
|
+
</item>
|
199
|
+
</channel>
|
200
|
+
</rss>
|
data/test/recipe.rb
ADDED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(File.expand_path(__FILE__)), '..', 'lib')
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'daemons'
|
5
|
+
require 'options'
|
6
|
+
require 'feed'
|
7
|
+
require 'torrent'
|
8
|
+
require 'lexicon'
|
9
|
+
require 'dsl'
|
10
|
+
|
11
|
+
require 'redgreen'
|
12
|
+
require 'test/unit'
|
13
|
+
require 'mocha'
|
14
|
+
require 'shoulda'
|
15
|
+
require 'fakefs/safe'
|
16
|
+
require 'fakeweb'
|
17
|
+
|
18
|
+
module TestHelper
|
19
|
+
def bitme_rss
|
20
|
+
File.open(File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'test.rss')) do |rss|
|
21
|
+
rss.read
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def filter_paths
|
26
|
+
[ File.join(File.dirname(File.expand_path(__FILE__)), '..', 'receipe.rb') ]
|
27
|
+
end
|
28
|
+
end
|
data/test/unit/dsl.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DslTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
5
|
+
|
6
|
+
context '' do
|
7
|
+
setup do
|
8
|
+
@logger = Logger.new(File.open('/dev/null', 'w'))
|
9
|
+
|
10
|
+
@options = {
|
11
|
+
:logger => @logger,
|
12
|
+
:filter_paths => filter_paths
|
13
|
+
}
|
14
|
+
|
15
|
+
@feed = Feed.new(@options)
|
16
|
+
@dsl = DSL.new(@options)
|
17
|
+
|
18
|
+
FakeWeb.register_uri(:get, "http://www.mytorrentsite.com/feed.rss", :body => bitme_rss)
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'setup' do
|
22
|
+
should 'prime the DSL on initialize' do
|
23
|
+
assert @dsl.instance_variable_get(:@primed)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'loader' do
|
28
|
+
should 'define run_dsl dynamically' do
|
29
|
+
assert @dsl.respond_to?(:run_dsl)
|
30
|
+
end
|
31
|
+
|
32
|
+
should 'map perform_filter to run_dsl' do
|
33
|
+
@items = []
|
34
|
+
@dsl.expects(:run_dsl).with(@items, :filter)
|
35
|
+
@dsl.perform_filter(@items)
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'map download_complete to run_dsl' do
|
39
|
+
@items = []
|
40
|
+
@dsl.expects(:run_dsl).with(@items, :torrent)
|
41
|
+
@dsl.download_complete(@items)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'add_title' do
|
46
|
+
should 'register a title if @primed is false' do
|
47
|
+
@dsl.instance_variable_set(:@primed, false)
|
48
|
+
assert 2, @dsl.instance_variable_get(:@titles).size
|
49
|
+
@dsl.add_title('Test Title 3')
|
50
|
+
assert 3, @dsl.instance_variable_get(:@titles).size
|
51
|
+
end
|
52
|
+
|
53
|
+
should 'not register a title if @primed is true' do
|
54
|
+
@dsl.instance_variable_set(:@primed, true)
|
55
|
+
assert 2, @dsl.instance_variable_get(:@titles).size
|
56
|
+
@dsl.add_title('Test Title 3')
|
57
|
+
assert 2, @dsl.instance_variable_get(:@titles).size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'add_feed' do
|
62
|
+
should 'register a feed if @primed is false' do
|
63
|
+
@dsl.instance_variable_set(:@primed, false)
|
64
|
+
assert 2, @dsl.instance_variable_get(:@feeds).size
|
65
|
+
@dsl.add_title('http://www.feedsfeedsfeeds.com/feed.rss')
|
66
|
+
assert 3, @dsl.instance_variable_get(:@feeds).size
|
67
|
+
end
|
68
|
+
|
69
|
+
should 'not register a title if @primed is true' do
|
70
|
+
@dsl.instance_variable_set(:@primed, true)
|
71
|
+
assert 2, @dsl.instance_variable_get(:@feeds).size
|
72
|
+
@dsl.add_title('http://www.feedsfeedsfeeds.com/feed.rss')
|
73
|
+
assert 2, @dsl.instance_variable_get(:@feeds).size
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'parse_metadata' do
|
78
|
+
should 'call Lexicon::parse' do
|
79
|
+
item = mock
|
80
|
+
ret = mock
|
81
|
+
Lexicon.expects(:parse).with(item).returns(ret)
|
82
|
+
assert_equal ret, @dsl.parse_metadata(item)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'download' do
|
87
|
+
should 'call Torrent::download' do
|
88
|
+
link = mock
|
89
|
+
options = mock
|
90
|
+
@dsl.stubs(:options).returns({})
|
91
|
+
Torrent.expects(:download).with(link, options, {})
|
92
|
+
@dsl.download(link, options)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'log' do
|
97
|
+
should 'logs' do
|
98
|
+
logger = mock
|
99
|
+
options = {:logger => logger}
|
100
|
+
@dsl.stubs(:options).returns(options)
|
101
|
+
assert_equal logger, @dsl.log
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/test/unit/feed.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
class FeedTest < Test::Unit::TestCase
|
5
|
+
include TestHelper
|
6
|
+
|
7
|
+
context '' do
|
8
|
+
setup do
|
9
|
+
@feed_urls = [ 'http://www.mytorrentsite.com/feed.rss' ]
|
10
|
+
DSL.any_instance.stubs(:feeds).returns(@feed_urls)
|
11
|
+
|
12
|
+
@logger = Logger.new(File.open('/dev/null', 'w'))
|
13
|
+
@feed = Feed.new(
|
14
|
+
{
|
15
|
+
:on_top => true,
|
16
|
+
:filter_paths => filter_paths,
|
17
|
+
:logger => @logger
|
18
|
+
})
|
19
|
+
|
20
|
+
feed = Feedzirra::Feed.parse(bitme_rss)
|
21
|
+
feed_ret = {}
|
22
|
+
@feed_urls.each { |f| feed_ret[f] = feed }
|
23
|
+
Feedzirra::Feed.stubs(:fetch_and_parse).returns(feed_ret)
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'check' do
|
27
|
+
setup do
|
28
|
+
@transmission_client = mock
|
29
|
+
Transmission::Client.stubs(:new).returns(@transmission_client)
|
30
|
+
@transmission_client.stubs(:torrents).returns([])
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'call check_feed' do
|
34
|
+
DSL.any_instance.expects(:download_complete).with([])
|
35
|
+
@feed.expects(:check_feeds)
|
36
|
+
@feed.check
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'check_feed' do
|
41
|
+
should 'download each RSS feed' do
|
42
|
+
DSL.any_instance.expects(:perform_filter).times(@feed_urls.size)
|
43
|
+
@logger.expects(:error).never
|
44
|
+
assert_equal @feed_urls.size, @feed.check_feeds
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'update_feed' do
|
49
|
+
should 'do nothing if there is no updated entries' do
|
50
|
+
feed = Feedzirra::Feed.parse(bitme_rss)
|
51
|
+
feed.entries.stubs(:values).returns([])
|
52
|
+
@feed.instance_variable_set(:@results, feed.entries)
|
53
|
+
Feedzirra::Feed.stubs(:update).returns(feed)
|
54
|
+
Feedzirra::Feed.stubs(:updated?).returns(false)
|
55
|
+
Feedzirra::Feed.stubs(:new_entries).returns([])
|
56
|
+
|
57
|
+
DSL.any_instance.expects(:perform_filter).never
|
58
|
+
@logger.expects(:error).never
|
59
|
+
assert_equal 1, @feed.update_feeds
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class LexiconTest < Test::Unit::TestCase
|
4
|
+
include TestHelper
|
5
|
+
|
6
|
+
context '' do
|
7
|
+
setup do
|
8
|
+
@item = mock
|
9
|
+
@item.stubs(:published).returns(DateTime.now)
|
10
|
+
end
|
11
|
+
|
12
|
+
should 'parse dollhouse.s02e08.internal.hdtv.xvid-2hd.avi' do
|
13
|
+
@item.stubs(:title).returns('dollhouse.s02e08.internal.hdtv.xvid-2hd.avi')
|
14
|
+
|
15
|
+
parsed = Lexicon::parse(@item)
|
16
|
+
assert_equal 'dollhouse', parsed[:title]
|
17
|
+
assert_equal 2, parsed[:series]
|
18
|
+
assert_equal 8, parsed[:episode]
|
19
|
+
assert parsed[:high_def]
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'parse Ghost.Adventures.S03E07.HDTV.XviD-CRiMSON' do
|
23
|
+
@item.stubs(:title).returns('Ghost.Adventures.S03E07.HDTV.XviD-CRiMSON')
|
24
|
+
|
25
|
+
parsed = Lexicon::parse(@item)
|
26
|
+
assert_equal 'Ghost Adventures', parsed[:title]
|
27
|
+
assert_equal 3, parsed[:series]
|
28
|
+
assert_equal 7, parsed[:episode]
|
29
|
+
assert parsed[:high_def]
|
30
|
+
end
|
31
|
+
|
32
|
+
should 'parse National.Geographic.Samurai.Sword.720p.HDTV.x264-DiCH' do
|
33
|
+
@item.stubs(:title).returns('National.Geographic.Samurai.Sword.720p.HDTV.x264-DiCH')
|
34
|
+
|
35
|
+
parsed = Lexicon::parse(@item)
|
36
|
+
assert_equal 'National Geographic Samurai Sword', parsed[:title]
|
37
|
+
assert_equal nil, parsed[:series]
|
38
|
+
assert_equal nil, parsed[:episode]
|
39
|
+
assert parsed[:high_def]
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'parse Hardcore TV (1994, HBO series)' do
|
43
|
+
@item.stubs(:title).returns('Hardcore TV (1994, HBO series)')
|
44
|
+
|
45
|
+
parsed = Lexicon::parse(@item)
|
46
|
+
assert_equal 'Hardcore TV (1994, HBO series)', parsed[:title]
|
47
|
+
assert_equal nil, parsed[:series]
|
48
|
+
assert_equal nil, parsed[:episode]
|
49
|
+
assert !parsed[:high_def]
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'parse Being.Erica.S02.INTERNAL.HDTV.XviD-BitMeTV' do
|
53
|
+
@item.stubs(:title).returns('Being.Erica.S02.INTERNAL.HDTV.XviD-BitMeTV')
|
54
|
+
|
55
|
+
parsed = Lexicon::parse(@item)
|
56
|
+
assert_equal 'Being Erica', parsed[:title]
|
57
|
+
assert_equal 2, parsed[:series]
|
58
|
+
assert_equal nil, parsed[:episode]
|
59
|
+
assert parsed[:high_def]
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'parse National.Geographic.Inside.The.Pentagon.Xvid.DVDRip.avi' do
|
63
|
+
@item.stubs(:title).returns('National.Geographic.Inside.The.Pentagon.Xvid.DVDRip.avi')
|
64
|
+
|
65
|
+
parsed = Lexicon::parse(@item)
|
66
|
+
assert_equal 'National Geographic Inside The Pentagon', parsed[:title]
|
67
|
+
assert_equal nil, parsed[:series]
|
68
|
+
assert_equal nil, parsed[:episode]
|
69
|
+
assert !parsed[:high_def]
|
70
|
+
end
|
71
|
+
|
72
|
+
should 'parse i.want.to.work.for.diddy.206.hdtv.xvid-sys.avi' do
|
73
|
+
@item.stubs(:title).returns('i.want.to.work.for.diddy.206.hdtv.xvid-sys.avi')
|
74
|
+
|
75
|
+
parsed = Lexicon::parse(@item)
|
76
|
+
assert_equal 'i want to work for diddy', parsed[:title]
|
77
|
+
assert_equal 2, parsed[:series]
|
78
|
+
assert_equal 6, parsed[:episode]
|
79
|
+
assert parsed[:high_def]
|
80
|
+
end
|
81
|
+
|
82
|
+
should 'parse Dollhouse S02E08 [reencode] [internal] [2hd] [iPod].mp4' do
|
83
|
+
@item.stubs(:title).returns('Dollhouse S02E08 [reencode] [internal] [2hd] [iPod].mp4')
|
84
|
+
|
85
|
+
parsed = Lexicon::parse(@item)
|
86
|
+
assert_equal 'Dollhouse', parsed[:title]
|
87
|
+
assert_equal 2, parsed[:series]
|
88
|
+
assert_equal 8, parsed[:episode]
|
89
|
+
assert !parsed[:high_def]
|
90
|
+
end
|
91
|
+
|
92
|
+
should 'parse The.Jeff.Dunham.Show.S01.E06.HDTV.XviD-RNR.avi' do
|
93
|
+
@item.stubs(:title).returns('The.Jeff.Dunham.Show.S01.E06.HDTV.XviD-RNR.avi')
|
94
|
+
|
95
|
+
parsed = Lexicon::parse(@item)
|
96
|
+
assert_equal 'The Jeff Dunham Show', parsed[:title]
|
97
|
+
assert_equal 1, parsed[:series]
|
98
|
+
assert_equal 6, parsed[:episode]
|
99
|
+
assert parsed[:high_def]
|
100
|
+
end
|
101
|
+
|
102
|
+
should 'parse Mouth To Mouth.1x04.Devine.REPACK.WS PDTV XviD-FoV' do
|
103
|
+
@item.stubs(:title).returns('Mouth To Mouth.1x04.Devine.REPACK.WS PDTV XviD-FoV')
|
104
|
+
|
105
|
+
parsed = Lexicon::parse(@item)
|
106
|
+
assert_equal 'Mouth To Mouth', parsed[:title]
|
107
|
+
assert_equal 1, parsed[:series]
|
108
|
+
assert_equal 4, parsed[:episode]
|
109
|
+
assert !parsed[:high_def]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
class DslTest < Test::Unit::TestCase
|
5
|
+
include TestHelper
|
6
|
+
|
7
|
+
context '' do
|
8
|
+
setup do
|
9
|
+
@fake_torrent_file = "This really isn't a torrent file"
|
10
|
+
@transmission_client = mock
|
11
|
+
Transmission::Client.stubs(:new).returns(@transmission_client)
|
12
|
+
FakeWeb.register_uri(:get, "http://www.mytorrentsite.com/tv_show.torrent", :body => @fake_torrent_file)
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'download' do
|
16
|
+
should 'pass a base64 encoded version of the supplied data and the download directory' do
|
17
|
+
@transmission_client.stubs(:add_torrent).with('metainfo' => Base64.encode64(@fake_torrent_file), 'download-dir' => '/Downloads')
|
18
|
+
Torrent.download('http://www.mytorrentsite.com/tv_show.torrent', :download_dir => '/Downloads')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: overdrive
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Myles Eftos
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-20 00:00:00 +08:00
|
13
|
+
default_executable: overdrive
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: daemons
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: transmission-client
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: SyslogLogger
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description: An RSS frontend for transmissions. It defines a basic DSL that allows you decide what you do once the files have been downloaded
|
46
|
+
email: myles@madpilot.com.au
|
47
|
+
executables:
|
48
|
+
- overdrive
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README.markdown
|
53
|
+
files:
|
54
|
+
- README.markdown
|
55
|
+
- Rakefile
|
56
|
+
- VERSION
|
57
|
+
- bin/overdrive
|
58
|
+
- lib/dsl.rb
|
59
|
+
- lib/feed.rb
|
60
|
+
- lib/lexicon.rb
|
61
|
+
- lib/options.rb
|
62
|
+
- lib/torrent.rb
|
63
|
+
- test/fixtures/test.rss
|
64
|
+
- test/recipe.rb
|
65
|
+
- test/test_helper.rb
|
66
|
+
- test/unit/dsl.rb
|
67
|
+
- test/unit/feed.rb
|
68
|
+
- test/unit/lexicon.rb
|
69
|
+
- test/unit/torrent.rb
|
70
|
+
has_rdoc: true
|
71
|
+
homepage: http://github.com/madpilot/overdrive
|
72
|
+
licenses: []
|
73
|
+
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options:
|
76
|
+
- --charset=UTF-8
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
version:
|
91
|
+
requirements: []
|
92
|
+
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.3.5
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: An RSS frontend for transmission
|
98
|
+
test_files:
|
99
|
+
- test/test_helper.rb
|
100
|
+
- test/unit/feed.rb
|
101
|
+
- test/unit/lexicon.rb
|
102
|
+
- test/unit/dsl.rb
|
103
|
+
- test/unit/torrent.rb
|