autonzb 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ autonzb
23
+ pkg
24
+ manifest
25
+ tasks
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Pirate
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,44 @@
1
+ # AutoNZB
2
+
3
+ Ruby tool to automatically download x264 HD nzb movies files from newzleech.com
4
+ Currently only english and french subtitles are supported, but it's super easy to add your own language.
5
+
6
+ ## Install
7
+
8
+ sudo gem install pirate-autonzb --source http://gems.github.com
9
+
10
+ ## Usage
11
+
12
+ In your terminal:
13
+
14
+ autonzb -d /path/of/download/nzb/directory
15
+
16
+ Will download new x264 HD movies nzb from newzleech.com, with imdb score >= 7.0, year >= 1950 and nzb age <= 160 days
17
+
18
+ autonzb -d /path/of/download/nzb/directory -backup /path/of/backuped/already/downloaded/nzb
19
+
20
+ -backup option it's very useful to prevent useless re-download of nzb already downloaded
21
+
22
+ autonzb -d /path/of/download/nzb/directory -movies /path/with/already/downloaded/movies -age 1 -imdb 7.5 -year 1980 -srt fr,en
23
+
24
+ Will download only new nzb of the day with imdb score >= 7.5, year >= 1980 and subtitles french or english (and unknown srt).
25
+ The -movies setting prevents already owned movies to be re-downloaded (only if the owned movie is 'better' than the new release),
26
+ movies folder need to follow the name convention (especially {imdb_id})
27
+
28
+ more details with:
29
+
30
+ autonzb -h
31
+
32
+ ## Folder Name Convention
33
+
34
+ AutoNZB use (and needs if you want to use -movies options) specific folders name for your movies:
35
+
36
+ name of the movie (year) tag(s) format source sound encoding lang {imdb_id} [srt(s)]
37
+
38
+ Burn After Reading (2008) PROPER 1080p BluRay DTS x264 {tt0887883} [fr,en]
39
+ Le Fabuleux Destin d'Amelie Poulain (2001) 720p BluRay x264 FRENCH {tt0211915} [en]
40
+ ...
41
+
42
+ ## Copyright
43
+
44
+ Copyright (c) 2009 Pirate. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "autonzb"
8
+ gem.summary = "Automatically download x264 HD nzb movies files."
9
+ gem.description = "Ruby tool to automatically download x264 HD nzb movies files from newzleech.com & nzbs.org"
10
+ gem.email = "pirate.2061@gmail.com"
11
+ gem.homepage = "http://github.com/pirate/autonzb"
12
+ gem.authors = ["Pirate"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ # gem.ignore_pattern = ["tmp/*", "script/*"]
16
+ gem.add_dependency "hpricot"
17
+ gem.add_dependency "optiflag"
18
+ gem.add_dependency "rubyzip"
19
+ gem.add_dependency "htmlentities"
20
+ gem.default_executable = "bin/autonzb"
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :spec => :check_dependencies
40
+
41
+ task :default => :spec
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "test-gem #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.4
data/asset/failure.png ADDED
Binary file
data/autonzb.gemspec ADDED
@@ -0,0 +1,73 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{autonzb}
8
+ s.version = "0.5.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Pirate"]
12
+ s.date = %q{2009-11-01}
13
+ s.default_executable = %q{bin/autonzb}
14
+ s.description = %q{Ruby tool to automatically download x264 HD nzb movies files from newzleech.com & nzbs.org}
15
+ s.email = %q{pirate.2061@gmail.com}
16
+ s.executables = ["autonzb"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.markdown"
20
+ ]
21
+ s.files = [
22
+ ".gitignore",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "asset/failure.png",
27
+ "autonzb.gemspec",
28
+ "bin/autonzb",
29
+ "lib/imdb.rb",
30
+ "lib/inspector.rb",
31
+ "lib/movie.rb",
32
+ "lib/sites/newzleech/nfo.rb",
33
+ "lib/sites/newzleech/nzb.rb",
34
+ "lib/sites/nzbs/mechanize_hack.rb",
35
+ "lib/sites/nzbs/nfo.rb",
36
+ "lib/sites/nzbs/nzb.rb"
37
+ ]
38
+ s.homepage = %q{http://github.com/pirate/autonzb}
39
+ s.rdoc_options = ["--charset=UTF-8"]
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.3.5}
42
+ s.summary = %q{Automatically download x264 HD nzb movies files.}
43
+ s.test_files = [
44
+ "spec/autonzb_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
47
+
48
+ if s.respond_to? :specification_version then
49
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
50
+ s.specification_version = 3
51
+
52
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
53
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
54
+ s.add_runtime_dependency(%q<hpricot>, [">= 0"])
55
+ s.add_runtime_dependency(%q<optiflag>, [">= 0"])
56
+ s.add_runtime_dependency(%q<rubyzip>, [">= 0"])
57
+ s.add_runtime_dependency(%q<htmlentities>, [">= 0"])
58
+ else
59
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
60
+ s.add_dependency(%q<hpricot>, [">= 0"])
61
+ s.add_dependency(%q<optiflag>, [">= 0"])
62
+ s.add_dependency(%q<rubyzip>, [">= 0"])
63
+ s.add_dependency(%q<htmlentities>, [">= 0"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
67
+ s.add_dependency(%q<hpricot>, [">= 0"])
68
+ s.add_dependency(%q<optiflag>, [">= 0"])
69
+ s.add_dependency(%q<rubyzip>, [">= 0"])
70
+ s.add_dependency(%q<htmlentities>, [">= 0"])
71
+ end
72
+ end
73
+
data/lib/imdb.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'cgi'
2
+ require 'hpricot'
3
+ require 'open-uri'
4
+ require 'htmlentities'
5
+
6
+ class IMDB
7
+
8
+ attr_accessor :link, :doc, :id
9
+
10
+ def initialize(name, year = nil, link = nil)
11
+ # $stdout.print 'i'
12
+ @try = 3
13
+ @name, @year, @link = name, year, link
14
+ @coder = HTMLEntities.new
15
+ set_doc
16
+ set_id
17
+ end
18
+
19
+ def score
20
+ if @doc && score_text = @doc.search("div.meta b").first
21
+ score_text.inner_html.match(/(.*)\/10/)[1].to_f
22
+ else
23
+ 0
24
+ end
25
+ end
26
+
27
+ def year
28
+ @doc ? @doc.search("title").inner_html.match(/\s\(([0-9]{4})/)[1].to_i : @year.to_i
29
+ end
30
+
31
+ def name
32
+ $KCODE = 'utf-8'
33
+ @doc ? @coder.decode(@doc.search("title").inner_html.match(/(.*)\s\(/u)[1]) : @name
34
+ end
35
+
36
+ private
37
+
38
+ def set_doc
39
+ if @link
40
+ @doc = Hpricot(open(@link.gsub(/\/\s*$/,'')))
41
+ @id = @link.match(/tt[0-9]+/)[0]
42
+ else
43
+ query = "#{@name} (#{@year})"
44
+ search_url = "http://www.imdb.com/find?q=#{CGI::escape(query)}"
45
+ doc = Hpricot(open(search_url))
46
+ case doc.search("title").inner_html
47
+ when "IMDb Title Search", "IMDb Search" # search result page
48
+ if !doc.search("b[text()*='Media from'] a").empty?
49
+ imdb_id = doc.search("b[text()*='Media from'] a").first[:href]
50
+ movie_url = "http://www.imdb.com#{imdb_id}"
51
+ elsif !doc.search("td[@valign='top'] a[@href^='/title/tt']").empty?
52
+ imdb_id = doc.search("td[@valign='top'] a[@href^='/title/tt']").first[:href]
53
+ movie_url = "http://www.imdb.com#{imdb_id}"
54
+ else
55
+ movie_url = nil
56
+ end
57
+ if movie_url
58
+ @doc = Hpricot(open(movie_url))
59
+ @id = movie_url.match(/tt[0-9]+/)[0]
60
+ end
61
+ else # direct in movie page
62
+ @doc = doc
63
+ end
64
+ end
65
+ rescue
66
+ if @try > 0
67
+ @try -= 1
68
+ $stdout.print '*'
69
+ sleep 2
70
+ set_doc #retry
71
+ else
72
+ @doc = nil
73
+ end
74
+ end
75
+
76
+ def set_id
77
+ @id ||= doc.search("a[@href*='/title/tt']").first[:href].match(/tt[0-9]+/)[0] if doc
78
+ end
79
+
80
+ end
data/lib/inspector.rb ADDED
@@ -0,0 +1,215 @@
1
+ require File.join(File.dirname(__FILE__), 'movie')
2
+ require File.join(File.dirname(__FILE__), 'sites', 'newzleech', 'nzb')
3
+ require File.join(File.dirname(__FILE__), 'sites', 'nzbs', 'nzb')
4
+
5
+ class Inspector
6
+
7
+ attr_accessor :download_path, :backup_path, :movies, :nzbs
8
+
9
+ def initialize(download_path, options = {})
10
+ @download_path = download_path.gsub(/\/$/,'')
11
+ @paths = options[:movie_paths] ? options[:movie_paths].split(',').map { |p| p.gsub(/\/$/,'') } : []
12
+ @options = options
13
+ @options[:srt] = @options[:srt] ? (@options[:srt].split(',') - ["unknown"] + ['unknown']).uniq : nil
14
+ @options[:imdb_score] = @options[:imdb_score] ? @options[:imdb_score].to_f : 7.0
15
+ @options[:year] = @options[:year] ? @options[:year].to_i : 1950
16
+
17
+ @movies = []
18
+ initialize_movies
19
+
20
+ if @options[:backup_path]
21
+ @backup_path = @options[:backup_path].gsub(/\/$/,'')
22
+ @nzbs = []
23
+ initialize_nzbs
24
+ @movies = @nzbs + @movies
25
+ load_age_from_yaml
26
+ end
27
+
28
+ $stdout.print "Movie criteria: imdb score >= #{@options[:imdb_score]}, year >= #{@options[:year]}#{" and srt [#{@options[:srt].join(',')}]" if @options[:srt]}\n"
29
+ end
30
+
31
+ def search_and_download
32
+ if @options[:login] && @options[:pass]
33
+ site = Nzbs::NZB.new(@options)
34
+ parse_nzbs(site)
35
+ end
36
+ site = Newzleech::NZB.new(@options)
37
+ parse_nzbs(site)
38
+
39
+ if backup_path
40
+ keep_only_best_nzbs
41
+ set_age_on_yaml
42
+ end
43
+ end
44
+
45
+ def parse_nzbs(site)
46
+ site.nzbs.each do |movie|
47
+ $stdout.print "#{movie.dirname}, imdb score: #{movie.score}, age: #{movie.age} day(s)\n"
48
+ if need?(movie)
49
+ $stdout.print " => DOWNLOAD: #{movie.name} (#{movie.year})\n"
50
+ download(site, movie)
51
+ end
52
+ end
53
+ $stdout.print "No nzb found, maybe change -age or -page setting\n" if site.nzbs.empty?
54
+ end
55
+
56
+ def download(site, movie)
57
+ Tempfile.open("movie.nzb") do |tempfile|
58
+ tempfile.write(site.download(movie)) # download nzb
59
+ tempfile.close
60
+ File.copy(tempfile.path, "#{backup_path}/#{movie.dirname}.nzb") if backup_path
61
+ File.move(tempfile.path, "#{download_path}/#{movie.dirname}.nzb")
62
+ end
63
+ @movies << movie
64
+ end
65
+
66
+ def need?(movie, movies = @movies, not_validate = false, log = true)
67
+ if not_validate || valid?(movie)
68
+ $stdout.print " => movie has required criteria " if log
69
+ if m = movies.detect { |m| m == movie }
70
+ $stdout.print "but is already owned " if log
71
+ if srt_score(movie) > srt_score(m)
72
+ $stdout.print "but new movie has better subtitle: [#{movie.srt.join(',')}]\n" if log
73
+ true
74
+ elsif srt_score(movie) == srt_score(m)
75
+ if format_score(movie) > format_score(m)
76
+ $stdout.print "but new movie has better format: #{movie.format}\n" if log
77
+ true
78
+ elsif format_score(movie) == format_score(m)
79
+ if source_score(movie) > source_score(m)
80
+ $stdout.print "but new movie has better source: #{movie.source}\n" if log
81
+ true
82
+ elsif source_score(movie) == source_score(m)
83
+ if sound_score(movie) > sound_score(m)
84
+ $stdout.print "but new movie has better sound: #{movie.sound}\n" if log
85
+ true
86
+ else
87
+ $stdout.print "with same srt, format, source and sound\n" if log
88
+ false
89
+ end
90
+ else
91
+ $stdout.print "with same srt, format and better source: #{m.source}\n" if log
92
+ false
93
+ end
94
+ else
95
+ $stdout.print "with same srt and better format: #{m.format}\n" if log
96
+ false
97
+ end
98
+ else
99
+ $stdout.print "with better subtitle: [#{m.srt.join(',')}]\n" if log
100
+ false
101
+ end
102
+ else
103
+ $stdout.print "and is not already owned\n" if log
104
+ true
105
+ end
106
+ else
107
+ $stdout.print " => movie doesn't have required criteria\n" if log
108
+ false
109
+ end
110
+ end
111
+
112
+ def self.growl(title, msg, pri = 0)
113
+ system("/usr/local/bin/growlnotify -w -n autonzb --image #{File.dirname(__FILE__) + "/../asset/failure.png"} -p #{pri} -m #{msg.inspect} #{title} &")
114
+ end
115
+
116
+ private
117
+
118
+ def initialize_movies
119
+ @paths.each do |path|
120
+ old_movies_size = @movies.size
121
+ base_dir = clean_dir(Dir.new(path))
122
+ base_dir.each do |movie|
123
+ movie_path = "#{path}/#{movie}"
124
+ @movies << Movie.new(movie, :path => movie_path) if File.directory?(movie_path)
125
+ end
126
+ $stdout.print "Found #{@movies.size - old_movies_size} movie(s) in #{path}\n"
127
+ end
128
+ end
129
+
130
+ def initialize_nzbs
131
+ base_dir = clean_dir(Dir.new(@backup_path))
132
+ base_dir.each do |nzb|
133
+ nzb_path = "#{@backup_path}/#{nzb}"
134
+ @nzbs << Movie.new(nzb, :path => nzb_path) if File.extname(nzb_path) == '.nzb'
135
+ end
136
+ $stdout.print "Found #{@nzbs.size} backuped nzb(s) in #{@backup_path}\n"
137
+ rescue => e
138
+ $stdout.print "Problem with backup nzb: #{e}\n"
139
+ end
140
+
141
+ def clean_dir(dir)
142
+ dir.select { |e| !["..", ".", ".DS_Store", ".com.apple.timemachine.supported", "Icon\r"].include?(e) }
143
+ end
144
+
145
+ def valid?(movie)
146
+ (@options[:srt].nil? || (((@options[:srt] - movie.srt).size < @options[:srt].size)) || movie.lang == 'FRENCH') &&
147
+ movie.year != nil && movie.year >= @options[:year] &&
148
+ movie.score != nil && movie.score >= @options[:imdb_score]
149
+ end
150
+
151
+ def srt_score(movie)
152
+ if @options[:srt]
153
+ srts = @options[:srt].reverse
154
+ movie.srt.inject(-1) do |score, srt|
155
+ s = (i = srts.index(srt)) ? i : -1
156
+ score = s if s > score
157
+ score
158
+ end
159
+ else
160
+ 0
161
+ end
162
+ end
163
+
164
+ def format_score(movie)
165
+ case movie.format
166
+ when '1080p'; 2
167
+ when '720p'; 1
168
+ else; 0
169
+ end
170
+ end
171
+
172
+ def source_score(movie)
173
+ case movie.format
174
+ when 'BluRay'; 4
175
+ when 'HDDVD'; 3
176
+ when 'HDTV'; 2
177
+ when 'DVD'; 1
178
+ else; 0
179
+ end
180
+ end
181
+
182
+ def sound_score(movie)
183
+ movie.format == 'DTS' ? 1 : 0
184
+ end
185
+
186
+ def keep_only_best_nzbs
187
+ size = 0
188
+ @nzbs.each do |nzb|
189
+ nzbs = @nzbs.select { |item| item.path != nzb.path }
190
+ unless need?(nzb, nzbs, true, false)
191
+ File.delete(nzb.path)
192
+ size += 1
193
+ end
194
+ end
195
+ if size > 0
196
+ $stdout.print "#########################################################################\n"
197
+ $stdout.print "Deleted #{size} useless backuped nzb(s) (keep only the best nzb by movie)\n"
198
+ end
199
+ end
200
+
201
+ def load_age_from_yaml
202
+ yml_file = "#{backup_path}/options.yml"
203
+ yml_options = File.exist?(yml_file) ? YAML.load_file(yml_file) : {}
204
+ if yml_age = yml_options['age']
205
+ age = sprintf("%.1f", ((Time.now.tv_sec - yml_age.to_i) / 60.0 / 60.0 / 24.0)).to_f
206
+ @options[:age] = age if age > 0
207
+ end
208
+ end
209
+
210
+ def set_age_on_yaml
211
+ yml_file = "#{backup_path}/options.yml"
212
+ File.open(yml_file, "w") { |f| f.write(({ 'age' => Time.now.tv_sec }).to_yaml) }
213
+ end
214
+
215
+ end
data/lib/movie.rb ADDED
@@ -0,0 +1,209 @@
1
+ require File.join(File.dirname(__FILE__), 'imdb')
2
+
3
+ class Movie
4
+ include Comparable
5
+
6
+ attr_accessor :path, :name, :format, :source, :sound, :encoding, :year, :srt, :lang, :score, :tags, :age,
7
+ :imdb_link, :imdb_id, :nzb_link, :nfo
8
+
9
+ def initialize(raw_name, attributes = {})
10
+ @raw_name = raw_name.gsub(/\.|\_/,' ')
11
+ attributes.each { |k,v| self.send("#{k}=", v) }
12
+ @srt, @tags = [], []
13
+
14
+ # for info set with nzbs title
15
+ if name && year && score
16
+ @info_get_from_title = true
17
+ end
18
+
19
+ set_name
20
+ set_year
21
+ set_format
22
+ set_source
23
+ set_sound
24
+ set_srt
25
+ set_lang
26
+ set_encoding
27
+ set_tags
28
+
29
+ set_imdb_id
30
+ set_data_from_imdb unless path || @info_get_from_title
31
+ end
32
+
33
+ def score
34
+ @score ||= imdb.score
35
+ end
36
+
37
+ def dirname
38
+ "#{name} (#{year}) #{tags.join(' ')} #{format} #{source} #{sound} #{encoding} #{lang} {#{imdb_id}} [#{srt.join(',')}]".gsub(/\s+|\//,' ')
39
+ end
40
+
41
+ def <=>(other_movie)
42
+ if imdb_id && other_movie.imdb_id
43
+ imdb_id <=> other_movie.imdb_id
44
+ else
45
+ "#{name} #{year}".downcase <=> "#{other_movie.name} #{other_movie.year}".downcase
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def imdb
52
+ @imdb ||= IMDB.new(name, year, imdb_link)
53
+ end
54
+
55
+ def imdb_link
56
+ @imdb_link || (imdb_id && "http://imdb.com/title/#{imdb_id}")
57
+ end
58
+
59
+ def set_imdb_id
60
+ if imdb_link
61
+ @imdb_id = imdb_link.match(/tt[0-9]+/)[0]
62
+ elsif matched = @raw_name.match(/\{(tt[0-9]+)\}/)
63
+ @imdb_id = matched[1]
64
+ elsif imdb
65
+ @imdb_id = imdb.id
66
+ add_imdb_id_to_file if path
67
+ else
68
+ nil
69
+ end
70
+ end
71
+
72
+ def set_name
73
+ if name.nil?
74
+ raw_name = @raw_name.gsub(/\(|\)|\[|\]|\{|\}|\//, ' ')
75
+ if matched = raw_name.match(/(.*)(19[0-9]{2}|20[0-9]{2})[^p]/)
76
+ @name = matched[1]
77
+ elsif matched = raw_name.match(/(.*)1080p/i)
78
+ @name = matched[1]
79
+ elsif matched = raw_name.match(/(.*)720p/i)
80
+ @name = matched[1]
81
+ else
82
+ @name = ''
83
+ end
84
+ @name.gsub!(/REPACK|LIMITED|UNRATED|PROPER|REPOST|Directors\sCut/iu,'')
85
+ @name.gsub!(/^\s+|\s+$/u,'')
86
+ end
87
+ end
88
+
89
+ def set_year
90
+ if year.nil?
91
+ if matched = @raw_name.match(/19[0-9]{2}|20[0-9]{2}/)
92
+ @year = matched[0].to_i
93
+ end
94
+ end
95
+ end
96
+
97
+ def set_format
98
+ @format = case @raw_name
99
+ when /1080p/i
100
+ '1080p'
101
+ when /720p/i
102
+ '720p'
103
+ end
104
+ end
105
+
106
+ def set_encoding
107
+ @encoding = case @raw_name
108
+ when /x264/i
109
+ 'x264'
110
+ when /VC1/i
111
+ 'VC1'
112
+ when /PS3/i
113
+ 'PS3'
114
+ when /divx/i
115
+ 'DIVX'
116
+ when /xvid/i
117
+ 'XVID'
118
+ end
119
+ end
120
+
121
+ def set_source
122
+ @source = case @raw_name
123
+ when /Blu\s?Ray|Blu-Ray|BDRip/i
124
+ 'BluRay'
125
+ when /HDDVD/i
126
+ 'HDDVD'
127
+ when /HDTV/i
128
+ 'HDTV'
129
+ when /DVD/i
130
+ 'DVD'
131
+ end
132
+ end
133
+
134
+ def set_sound
135
+ @sound = case @raw_name
136
+ when /DTS/i
137
+ 'DTS'
138
+ when /AC3/i
139
+ 'AC3'
140
+ end
141
+ end
142
+
143
+ def set_srt
144
+ if nfo
145
+ @srt = nfo.srt
146
+ elsif matched = @raw_name.match(/\[(.*)\]/)
147
+ srts = matched[1].split(',')
148
+ if srts.empty?
149
+ @srt << 'none'
150
+ else
151
+ srts.each { |srt| @srt << srt }
152
+ end
153
+ else
154
+ @srt << 'unknown'
155
+ end
156
+ end
157
+
158
+ def set_lang
159
+ @lang = case @raw_name
160
+ when /FRENCH/i
161
+ 'FRENCH'
162
+ when /GERMAN/i
163
+ 'GERMAN'
164
+ when /DANISH/i
165
+ 'DANISH'
166
+ when /NORDIC/i
167
+ 'NORDIC'
168
+ end
169
+ end
170
+
171
+ def set_tags
172
+ @tags << 'REPACK' if @raw_name =~ /REPACK/i
173
+ @tags << 'LIMITED' if @raw_name =~ /LIMITED/i
174
+ @tags << 'UNRATED' if @raw_name =~ /UNRATED/i
175
+ @tags << 'PROPER' if @raw_name =~ /PROPER/i
176
+ @tags << 'REPOST' if @raw_name =~ /REPOST/i
177
+ @tags << 'OUTDATED' if @raw_name =~ /OUTDATED/i
178
+ @tags << 'Directors Cut' if @raw_name =~ /Directors\sCut|DirCut/i
179
+ end
180
+
181
+ def set_data_from_imdb
182
+ @name = imdb.name
183
+ imdb_year = imdb.year
184
+ if !imdb_year.nil? && imdb_year != 0
185
+ @year = imdb_year
186
+ end
187
+ end
188
+
189
+ def add_imdb_id_to_file
190
+ dir_name = File.dirname(path)
191
+ if File.directory?(path)
192
+ base_name = File.basename(path)
193
+ ext_name = ''
194
+ else
195
+ ext_name = path.include?('.nzb') ? '.nzb' : File.extname(path)
196
+ base_name = File.basename(path, ext_name)
197
+ end
198
+ if matched = base_name.match(/^(.*)\s(\[{1}.*\]{1})$/)
199
+ base_name_without_srts = matched[1]
200
+ srts = matched[2]
201
+ new_base_name = "#{base_name_without_srts} {#{imdb_id}} #{srts}#{ext_name}"
202
+ else
203
+ new_base_name = "#{base_name} {#{imdb_id}}#{ext_name}"
204
+ end
205
+ File.rename(path, "#{dir_name}/#{new_base_name}")
206
+ $stdout.print "Added {#{imdb_id}} (imdb id) => #{dir_name}/#{new_base_name}\n"
207
+ end
208
+
209
+ end
@@ -0,0 +1,35 @@
1
+ require 'open-uri'
2
+
3
+ module Newzleech
4
+ class Nfo
5
+
6
+ attr_accessor :srt, :imdb_link
7
+
8
+ def initialize(url)
9
+ @nfo = open(url.gsub(/\/$/,'')).read
10
+ @srt = []
11
+
12
+ parse_nfo
13
+ @srt.uniq!
14
+ end
15
+
16
+ private
17
+
18
+ # searching for subtitles languages info & imdb link
19
+ def parse_nfo
20
+ @nfo.split(/\n/).each do |line|
21
+ case line
22
+ when /subtitle|sub/i
23
+ @srt << 'fr' if line =~ /fr|fre|french/i
24
+ @srt << 'en' if line =~ /en|eng|english/i
25
+ # TODO add more language parsing here
26
+ @srt << 'none' if line =~ /none/i
27
+ when /imdb\.com\/title\//
28
+ @imdb_link = (matched = line.match(/imdb.com\/title\/(tt[0-9]+)/)) && "http://imdb.com/title/#{matched[1]}"
29
+ end
30
+ end
31
+ @srt << 'unknown' if @srt.empty?
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,100 @@
1
+ require 'open-uri'
2
+ require 'hpricot'
3
+ require 'zip/zip'
4
+
5
+ require File.join(File.dirname(__FILE__), 'nfo')
6
+
7
+ module Newzleech
8
+ class NZB
9
+
10
+ URL = 'http://www.newzleech.com'
11
+ DAYS_BY_PAGE = 1.7
12
+
13
+ attr_accessor :nzbs
14
+
15
+ def initialize(options = {})
16
+ @options = options
17
+ @options[:age] ||= 160
18
+ @options[:pages] ||= ((@options[:age].to_f + DAYS_BY_PAGE) / DAYS_BY_PAGE).ceil
19
+
20
+ @nzb_urls = []
21
+ @nzbs = []
22
+
23
+ (1..(@options[:pages].to_i)).each do |page|
24
+ @nzb_urls << "#{URL}?group=143&minage=&age=160&min=4000&max=max&q=&m=search&adv=1&offset=#{(page.to_i - 1) * 60}"
25
+ end
26
+
27
+ parse_newzleech
28
+ end
29
+
30
+ def download(movie)
31
+ open(movie.nzb_link).read
32
+ end
33
+
34
+ private
35
+
36
+ def parse_newzleech
37
+ $stdout.print "Parsing #{URL} for new x264 HD nzb movies, with age <= #{@options[:age]}, in last #{@options[:pages]} page(s)\n"
38
+ @nzb_urls.each do |url|
39
+ doc = Hpricot(open(url))
40
+ doc.search("table.contentt").each do |table|
41
+ raw_name = raw_name(table)
42
+ if nfo_link = nfo_link(table)
43
+ nfo = Nfo.new(nfo_link)
44
+ imdb_link = nfo.imdb_link
45
+ else
46
+ imdb_link = imdb_link(table)
47
+ end
48
+ if raw_name && (imdb_link || (raw_name = find_clean_nzb_name(raw_name))) && raw_name.include?('x264')
49
+ age = parse_age(table.search("td.age").first.inner_html) # get age of the nzb
50
+ if age <= @options[:age].to_f
51
+ raw_name = clean_raw_name(raw_name)
52
+ movie = Movie.new(raw_name, :nfo => nfo, :imdb_link => imdb_link, :nzb_link => nzb_link(table), :age => age)
53
+ @nzbs << movie
54
+
55
+ $stdout.print '.'
56
+ $stdout.flush
57
+ end
58
+ end
59
+ end
60
+ end
61
+ $stdout.print "\n"
62
+ end
63
+
64
+ def clean_raw_name(raw_name)
65
+ (find_clean_nzb_name(raw_name) || raw_name).strip
66
+ end
67
+
68
+ def find_clean_nzb_name(raw_name)
69
+ if matched = raw_name.match(/^\[[0-9]*\]-\[.*\]-\[(.*)\]-/)
70
+ matched[1]
71
+ end
72
+ end
73
+
74
+ def raw_name(table)
75
+ (a = table.search("td.subject a[@href^='?p=']").first) && a.inner_html
76
+ end
77
+
78
+ def nfo_link(table)
79
+ (nfo = table.search("td.subject a[@href^='nfo.php?id=']").first) && "#{URL}/#{nfo[:href]}"
80
+ end
81
+
82
+ def imdb_link(table)
83
+ (imdb = table.search("td.subject a[@href^='http://anonym.to/?http://www.imdb.com']").first) && imdb[:href].match(/\?(.*)/)[1]
84
+ end
85
+
86
+ def nzb_link(table)
87
+ (nzb = table.search("td.get a[@href^='?m=gen&dl=1']").first) && "#{URL}/#{nzb[:href]}"
88
+ end
89
+
90
+ def parse_age(string)
91
+ case string
92
+ when /h/i
93
+ ((string.to_f / 24) * 10).to_i / 10.0
94
+ when /d/i
95
+ string.to_f
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,41 @@
1
+ module WWW
2
+ class Mechanize
3
+ class Chain
4
+ class ResponseReader
5
+ include WWW::Handler
6
+
7
+ def initialize(response)
8
+ @response = response
9
+ end
10
+
11
+ def handle(ctx, params)
12
+ params[:response] = @response
13
+ body = StringIO.new
14
+ total = 0
15
+ @response.read_body { |part|
16
+ total += part.length
17
+ body.write(part)
18
+ Mechanize.log.debug("Read #{total} bytes") if Mechanize.log
19
+ }
20
+ body.rewind
21
+
22
+ res_klass = Net::HTTPResponse::CODE_TO_OBJ[@response.code.to_s]
23
+ raise ResponseCodeError.new(@response) unless res_klass
24
+
25
+ # Net::HTTP ignores EOFError if Content-length is given, so we emulate it here.
26
+ # unless res_klass <= Net::HTTPRedirection
27
+ # raise EOFError if (!params[:request].is_a?(Net::HTTP::Head)) && @response.content_length() && @response.content_length() != total
28
+ # end
29
+
30
+ @response.each_header { |k,v|
31
+ Mechanize.log.debug("response-header: #{ k } => #{ v }")
32
+ } if Mechanize.log
33
+
34
+ params[:response_body] = body
35
+ params[:res_klass] = res_klass
36
+ super
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,36 @@
1
+ require 'open-uri'
2
+
3
+ module Nzbs
4
+ class Nfo
5
+
6
+ attr_accessor :srt, :imdb_link
7
+
8
+ def initialize(agent, url)
9
+ @page = agent.get(url.gsub(/\/$/,''))
10
+ @srt = []
11
+
12
+ parse_nfo
13
+ @srt.uniq!
14
+ end
15
+
16
+ private
17
+
18
+ # searching for subtitles languages info & imdb link
19
+ def parse_nfo
20
+ pre = @page.search('pre.nfo')
21
+ pre.to_s.split(/\n/).each do |line|
22
+ case line
23
+ when /subtitle|sub/i
24
+ @srt << 'fr' if line =~ /fr|fre|french/i
25
+ @srt << 'en' if line =~ /en|eng|english/i
26
+ # TODO add more language parsing here
27
+ @srt << 'none' if line =~ /none/i
28
+ when /imdb\.com\/title\//
29
+ @imdb_link = (matched = line.match(/imdb.com\/title\/(tt[0-9]+)/)) && "http://imdb.com/title/#{matched[1]}"
30
+ end
31
+ end
32
+ @srt << 'unknown' if @srt.empty?
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,120 @@
1
+ require 'open-uri'
2
+ require 'hpricot'
3
+ require 'zip/zip'
4
+ require 'mechanize'
5
+
6
+ require File.join(File.dirname(__FILE__), 'nfo')
7
+ require File.join(File.dirname(__FILE__), 'mechanize_hack')
8
+
9
+
10
+ module Nzbs
11
+ class NZB
12
+
13
+ URL = 'http://www.nzbs.org'
14
+ DAYS_BY_PAGE = 5
15
+
16
+ attr_accessor :nzbs, :agent
17
+
18
+ def initialize(options = {})
19
+ @options = options
20
+ @options[:age] ||= DAYS_BY_PAGE
21
+ @options[:pages] ||= ((@options[:age].to_f + DAYS_BY_PAGE) / DAYS_BY_PAGE).ceil
22
+
23
+ @nzb_urls = []
24
+ @nzbs = []
25
+
26
+ (1..(@options[:pages].to_i)).each do |page|
27
+ @nzb_urls << "#{URL}/index.php?action=browse&catid=4&page=#{(page)}"
28
+ end
29
+
30
+ login
31
+ parse_nzbs
32
+ end
33
+
34
+ def download(movie)
35
+ @agent.get_file(movie.nzb_link)
36
+ end
37
+
38
+ private
39
+
40
+ def login
41
+ @agent = WWW::Mechanize.new
42
+ page = @agent.get "#{URL}/index.php?action=browse&catid=4"
43
+
44
+ form = page.forms[1]
45
+ form.username = @options[:login]
46
+ form.password = @options[:pass]
47
+
48
+ @agent.submit form
49
+ end
50
+
51
+ def parse_nzbs
52
+ $stdout.print "Parsing #{URL} for new x264 HD nzb movies, with age <= #{@options[:age]}, in last #{@options[:pages]} page(s)\n"
53
+ @nzb_urls.each do |url|
54
+ page = @agent.get url
55
+ page.search("#nzbtable tr").each do |tr|
56
+ if raw_name = raw_name(tr)
57
+ if (age = age(tr)) <= @options[:age].to_f
58
+ if nfo_link = nfo_link(tr)
59
+ @nfo = Nfo.new(@agent, nfo_link(tr))
60
+ end
61
+
62
+ name, year, score = get_info_from_imdb_title(tr)
63
+ movie = Movie.new(raw_name, :name => name,
64
+ :year => year,
65
+ :score => score,
66
+ :imdb_link => imdb_link(tr),
67
+ :nzb_link => nzb_link(tr, page),
68
+ :age => age,
69
+ :nfo => @nfo)
70
+ @nzbs << movie
71
+
72
+ $stdout.print '.'
73
+ $stdout.flush
74
+ end
75
+ end
76
+ end
77
+ end
78
+ $stdout.print "\n"
79
+ end
80
+
81
+ def raw_name(tr)
82
+ (a = tr.search("a.nzb").first) && a.inner_html
83
+ end
84
+
85
+ def age(tr)
86
+ age_text = (td = tr.search("td[@align='right']").first) && td.inner_html
87
+ if age_text && age_text.include?("days ago")
88
+ @last_age = age_text.to_f
89
+ else
90
+ $stdout.print "AGE NOT FOUND"
91
+ end
92
+ @last_age
93
+ end
94
+
95
+ def nfo_link(tr)
96
+ (a = tr.search("small a[@href$='#nfo']").first) && a[:href]
97
+ end
98
+
99
+ def get_info_from_imdb_title(tr)
100
+ title = (a = tr.search("small a.viewimdb").first) && a[:title]
101
+ if title
102
+ if matched = title.match(/^(.*)\s\(([0-9]{4}).*\s\((.*)\/10\)/)
103
+ name = matched[1]
104
+ year = matched[2]
105
+ score = matched[3]
106
+ return name, year.to_i, score.to_f
107
+ end
108
+ end
109
+ end
110
+
111
+ def imdb_link(tr)
112
+ (a = tr.search("small a.viewimdb").first) && a[:href].match(/^http:\/\/anonym.to\/\?(.*)/)[1]
113
+ end
114
+
115
+ def nzb_link(tr, page)
116
+ (a = tr.search("a.dlnzb").first) && "#{URL}/#{a[:href]}"
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "AutoNZB" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'test-gem'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: autonzb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.4
5
+ platform: ruby
6
+ authors:
7
+ - Pirate
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-01 00:00:00 +01:00
13
+ default_executable: bin/autonzb
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hpricot
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: optiflag
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
+ - !ruby/object:Gem::Dependency
46
+ name: rubyzip
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: htmlentities
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ description: Ruby tool to automatically download x264 HD nzb movies files from newzleech.com & nzbs.org
66
+ email: pirate.2061@gmail.com
67
+ executables:
68
+ - autonzb
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - LICENSE
73
+ - README.markdown
74
+ files:
75
+ - .gitignore
76
+ - README.markdown
77
+ - Rakefile
78
+ - VERSION
79
+ - asset/failure.png
80
+ - autonzb.gemspec
81
+ - bin/autonzb
82
+ - lib/imdb.rb
83
+ - lib/inspector.rb
84
+ - lib/movie.rb
85
+ - lib/sites/newzleech/nfo.rb
86
+ - lib/sites/newzleech/nzb.rb
87
+ - lib/sites/nzbs/mechanize_hack.rb
88
+ - lib/sites/nzbs/nfo.rb
89
+ - lib/sites/nzbs/nzb.rb
90
+ - LICENSE
91
+ has_rdoc: true
92
+ homepage: http://github.com/pirate/autonzb
93
+ licenses: []
94
+
95
+ post_install_message:
96
+ rdoc_options:
97
+ - --charset=UTF-8
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: "0"
105
+ version:
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: "0"
111
+ version:
112
+ requirements: []
113
+
114
+ rubyforge_project:
115
+ rubygems_version: 1.3.5
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: Automatically download x264 HD nzb movies files.
119
+ test_files:
120
+ - spec/autonzb_spec.rb
121
+ - spec/spec_helper.rb