download_tv 2.4.7 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/Rakefile +2 -0
- data/bin/tv +13 -0
- data/download_tv.gemspec +4 -4
- data/lib/download_tv.rb +3 -1
- data/lib/download_tv/configuration.rb +42 -6
- data/lib/download_tv/downloader.rb +74 -36
- data/lib/download_tv/grabbers/addic7ed.rb +4 -1
- data/lib/download_tv/grabbers/eztv.rb +13 -4
- data/lib/download_tv/grabbers/kat.rb +22 -7
- data/lib/download_tv/grabbers/torrentapi.rb +22 -14
- data/lib/download_tv/grabbers/tpb.rb +5 -5
- data/lib/download_tv/linkgrabber.rb +9 -2
- data/lib/download_tv/myepisodes.rb +39 -21
- data/lib/download_tv/subtitles.rb +2 -0
- data/lib/download_tv/torrent.rb +6 -3
- data/lib/download_tv/version.rb +3 -1
- data/test/config_test.rb +4 -0
- data/test/downloader_test.rb +24 -12
- data/test/grabbers_test.rb +2 -0
- data/test/test_helper.rb +3 -1
- data/test/torrent_test.rb +4 -2
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2db543fb9eaf70753008d797005ac32249c67fde6e0b9b6a41e916c0eb66fcb
|
4
|
+
data.tar.gz: 41fe118c7d18adb566dec211873d64a3f6d2fdcfeea0f6e18c173545e6b8368c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e01b1c252163640d4d9be4cf47c3488019387762924de1d72cf58398be28e82ba5355abdf11fe7b31bfe38db5f13969e19081671f45eb0199cffcb776028d152
|
7
|
+
data.tar.gz: 57c2389c98774ba8f31e78d4e0adfd857c044862224d2b39611accfc2cd850403f89e2154f91ab6280fc7b6bab187bf524aec4ac950e5385a3a1271bc81c7b30
|
data/.gitignore
CHANGED
data/Rakefile
CHANGED
data/bin/tv
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'optparse'
|
4
5
|
require 'download_tv'
|
@@ -58,6 +59,14 @@ opt_parser = OptionParser.new do |opts|
|
|
58
59
|
exit
|
59
60
|
end
|
60
61
|
|
62
|
+
opts.on('-p', '--pending', 'Show list of pending downloads') do
|
63
|
+
options[:cmd] = 'showpending'
|
64
|
+
end
|
65
|
+
|
66
|
+
opts.on('--clear-pending', 'Clear list of pending downloads') do
|
67
|
+
options[:cmd] = 'clearpending'
|
68
|
+
end
|
69
|
+
|
61
70
|
opts.on('-v', '--version', 'Print version') do
|
62
71
|
puts DownloadTV::VERSION
|
63
72
|
exit
|
@@ -86,6 +95,10 @@ begin
|
|
86
95
|
DownloadTV::Configuration.new(config, true)
|
87
96
|
when 'showconfig'
|
88
97
|
DownloadTV::Configuration.new(config).print_config
|
98
|
+
when 'showpending'
|
99
|
+
DownloadTV::Configuration.new(config).print_attr(:pending)
|
100
|
+
when 'clearpending'
|
101
|
+
DownloadTV::Configuration.new(config).clear_pending
|
89
102
|
end
|
90
103
|
rescue Interrupt
|
91
104
|
puts 'Interrupt signal detected. Exiting...'
|
data/download_tv.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path('
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
require 'download_tv/version'
|
6
6
|
|
@@ -23,11 +23,11 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.executables = ['tv']
|
24
24
|
|
25
25
|
s.add_development_dependency 'bundler', '~> 1.15'
|
26
|
-
s.add_development_dependency 'rake', '~> 10.0'
|
27
26
|
s.add_development_dependency 'minitest', '~> 5.0'
|
27
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
28
28
|
|
29
29
|
s.add_dependency('json')
|
30
30
|
s.add_dependency('mechanize')
|
31
31
|
|
32
|
-
s.license
|
32
|
+
s.license = 'MIT'
|
33
33
|
end
|
data/lib/download_tv.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'mechanize'
|
3
5
|
require 'date'
|
@@ -13,7 +15,7 @@ require 'download_tv/linkgrabber'
|
|
13
15
|
require 'download_tv/subtitles'
|
14
16
|
|
15
17
|
module DownloadTV
|
16
|
-
USER_AGENT = "DownloadTV #{DownloadTV::VERSION}"
|
18
|
+
USER_AGENT = "DownloadTV #{DownloadTV::VERSION}"
|
17
19
|
|
18
20
|
class NoTorrentsError < StandardError; end
|
19
21
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# Class used for managing the configuration of the application
|
@@ -6,7 +8,7 @@ module DownloadTV
|
|
6
8
|
|
7
9
|
def initialize(content = {}, force_change = false)
|
8
10
|
FileUtils.mkdir_p(File.join(ENV['HOME'], '.config', 'download_tv'))
|
9
|
-
@config_path = content[:path] ||
|
11
|
+
@config_path = content[:path] || default_config_path
|
10
12
|
|
11
13
|
if File.exist? @config_path
|
12
14
|
load_config
|
@@ -20,34 +22,52 @@ module DownloadTV
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def change_configuration
|
25
|
+
prompt_for_myep_user
|
26
|
+
prompt_for_cookie
|
27
|
+
prompt_for_ignored
|
28
|
+
STDOUT.flush
|
29
|
+
|
30
|
+
set_default_values
|
31
|
+
serialize
|
32
|
+
end
|
33
|
+
|
34
|
+
def prompt_for_myep_user
|
23
35
|
if @content[:myepisodes_user]
|
24
36
|
print "Enter your MyEpisodes username (#{@content[:myepisodes_user]}) : "
|
25
37
|
else
|
26
38
|
print 'Enter your MyEpisodes username: '
|
27
39
|
end
|
28
40
|
@content[:myepisodes_user] = STDIN.gets.chomp
|
41
|
+
end
|
29
42
|
|
43
|
+
def prompt_for_cookie
|
30
44
|
print 'Save cookie? (y)/n: '
|
31
45
|
@content[:cookie] = !(STDIN.gets.chomp.casecmp? 'n')
|
46
|
+
end
|
32
47
|
|
48
|
+
def prompt_for_ignored
|
33
49
|
if @content[:ignored]
|
34
50
|
puts "Enter a comma-separated list of shows to ignore: (#{@content[:ignored]})"
|
35
51
|
else
|
36
52
|
puts 'Enter a comma-separated list of shows to ignore: '
|
37
53
|
end
|
38
54
|
|
39
|
-
@content[:ignored] = STDIN.gets
|
40
|
-
|
55
|
+
@content[:ignored] = STDIN.gets
|
56
|
+
.chomp
|
57
|
+
.split(',')
|
58
|
+
.map(&:strip)
|
59
|
+
.map(&:downcase)
|
60
|
+
end
|
41
61
|
|
62
|
+
def set_default_values
|
42
63
|
# When modifying existing config, keeps previous values
|
43
64
|
# When creating new one, sets defaults
|
44
65
|
@content[:auto] ||= true
|
45
66
|
@content[:subs] ||= true
|
46
67
|
@content[:grabber] ||= 'TorrentAPI'
|
47
68
|
@content[:date] ||= Date.today - 1
|
69
|
+
@content[:pending] = []
|
48
70
|
@content[:version] = DownloadTV::VERSION
|
49
|
-
|
50
|
-
serialize
|
51
71
|
end
|
52
72
|
|
53
73
|
def serialize
|
@@ -67,16 +87,32 @@ module DownloadTV
|
|
67
87
|
retry
|
68
88
|
end
|
69
89
|
|
90
|
+
def default_config_path
|
91
|
+
File.join(ENV['HOME'], '.config', 'download_tv', 'config')
|
92
|
+
end
|
93
|
+
|
70
94
|
##
|
71
95
|
# Returns true if a major or minor update has been detected
|
72
96
|
# Returns false if a patch has been detected
|
73
97
|
# Returns nil if it's the same version
|
74
98
|
def breaking_changes?(version)
|
75
|
-
DownloadTV::VERSION.split('.')
|
99
|
+
DownloadTV::VERSION.split('.')
|
100
|
+
.zip(version.split('.'))
|
101
|
+
.find_index { |x, y| y < x }
|
102
|
+
&.< 2
|
76
103
|
end
|
77
104
|
|
78
105
|
def print_config
|
79
106
|
@content.each { |k, v| puts "#{k}: #{v}" }
|
80
107
|
end
|
108
|
+
|
109
|
+
def print_attr(arg)
|
110
|
+
puts @content[arg]
|
111
|
+
end
|
112
|
+
|
113
|
+
def clear_pending
|
114
|
+
@content[:pending].clear
|
115
|
+
@content.serialize
|
116
|
+
end
|
81
117
|
end
|
82
118
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# Entry point of the application
|
@@ -20,16 +22,19 @@ module DownloadTV
|
|
20
22
|
|
21
23
|
def download_single_show(show)
|
22
24
|
t = Torrent.new(@config.content[:grabber])
|
25
|
+
show = fix_names([show]).first
|
23
26
|
download(get_link(t, show))
|
24
27
|
end
|
25
28
|
|
26
29
|
##
|
27
|
-
# Given a file containing a list of episodes (one per line)
|
30
|
+
# Given a file containing a list of episodes (one per line)
|
31
|
+
# it tries to find download links for each
|
28
32
|
def download_from_file(filename)
|
29
33
|
if File.exist? filename
|
30
34
|
filename = File.realpath(filename)
|
31
35
|
t = Torrent.new(@config.content[:grabber])
|
32
|
-
File.readlines(filename
|
36
|
+
to_download = File.readlines(filename, chomp:true)
|
37
|
+
fix_names(to_download).each { |show| download(get_link(t, show)) }
|
33
38
|
else
|
34
39
|
puts "Error: #{filename} not found"
|
35
40
|
exit 1
|
@@ -37,28 +42,34 @@ module DownloadTV
|
|
37
42
|
end
|
38
43
|
|
39
44
|
##
|
40
|
-
# Finds download links for all new episodes aired since
|
41
|
-
#
|
45
|
+
# Finds download links for all new episodes aired since
|
46
|
+
# the last run of the program
|
47
|
+
# It connects to MyEpisodes in order to find which shows
|
48
|
+
# to track and which new episodes aired.
|
42
49
|
def run(dont_update_last_run, offset = 0)
|
50
|
+
pending = @config.content[:pending]
|
51
|
+
@config.content[:pending].clear
|
52
|
+
pending ||= []
|
43
53
|
date = check_date(offset)
|
54
|
+
if pending.empty? and date.nil?
|
55
|
+
puts 'Everything up to date'
|
56
|
+
exit
|
57
|
+
end
|
44
58
|
|
45
|
-
|
46
|
-
|
47
|
-
myepisodes.load_cookie
|
48
|
-
shows = myepisodes.get_shows(date)
|
49
|
-
to_download = fix_names(shows)
|
59
|
+
to_download = shows_to_download(date)
|
60
|
+
to_download.concat pending
|
50
61
|
|
51
62
|
if to_download.empty?
|
52
63
|
puts 'Nothing to download'
|
53
64
|
|
54
65
|
else
|
55
|
-
t = Torrent.new(
|
66
|
+
t = Torrent.new()
|
56
67
|
|
57
68
|
queue = Queue.new
|
58
69
|
|
59
70
|
# Adds a link (or empty string to the queue)
|
60
71
|
link_t = Thread.new do
|
61
|
-
to_download.each { |show| queue << get_link(t, show) }
|
72
|
+
to_download.each { |show| queue << get_link(t, show, true) }
|
62
73
|
end
|
63
74
|
|
64
75
|
# Downloads the links as they are added
|
@@ -66,6 +77,7 @@ module DownloadTV
|
|
66
77
|
to_download.size.times do
|
67
78
|
magnet = queue.pop
|
68
79
|
next if magnet == '' # Doesn't download if no torrents are found
|
80
|
+
|
69
81
|
download(magnet)
|
70
82
|
end
|
71
83
|
end
|
@@ -88,45 +100,64 @@ module DownloadTV
|
|
88
100
|
warn 'Wrong username/password combination'
|
89
101
|
end
|
90
102
|
|
103
|
+
def shows_to_download(date)
|
104
|
+
myepisodes = MyEpisodes.new(@config.content[:myepisodes_user],
|
105
|
+
@config.content[:cookie])
|
106
|
+
# Log in using cookie by default
|
107
|
+
myepisodes.load_cookie
|
108
|
+
shows = myepisodes.get_shows(date)
|
109
|
+
shows = reject_ignored(shows)
|
110
|
+
fix_names(shows)
|
111
|
+
end
|
112
|
+
|
91
113
|
##
|
92
114
|
# Uses a Torrent object to obtain links to the given tv show
|
93
|
-
# When :auto is true it will try to find the best match
|
115
|
+
# When :auto is true it will try to find the best match
|
116
|
+
# based on a set of filters.
|
94
117
|
# When it's false it will prompt the user to select the preferred result
|
95
118
|
# Returns either a magnet link or an emptry string
|
96
|
-
def get_link(
|
97
|
-
links =
|
119
|
+
def get_link(torrent, show, save_pending=false)
|
120
|
+
links = torrent.get_links(show)
|
98
121
|
|
99
|
-
|
122
|
+
if links.empty?
|
123
|
+
@config.content[:pending] << show if save_pending
|
124
|
+
return ''
|
125
|
+
end
|
100
126
|
|
101
127
|
if @config.content[:auto]
|
102
128
|
links = filter_shows(links)
|
103
129
|
links.first[1]
|
104
130
|
else
|
105
|
-
|
106
|
-
links
|
131
|
+
prompt_links(links)
|
132
|
+
get_link_from_user(links)
|
133
|
+
end
|
134
|
+
end
|
107
135
|
|
108
|
-
|
109
|
-
|
136
|
+
def get_link_from_user(links)
|
137
|
+
i = $stdin.gets.chomp.to_i
|
110
138
|
|
139
|
+
until i.between?(-1, links.size - 1)
|
140
|
+
puts 'Index out of bounds. Try again [-1 to skip]: '
|
111
141
|
i = $stdin.gets.chomp.to_i
|
142
|
+
end
|
112
143
|
|
113
|
-
|
114
|
-
|
115
|
-
i = $stdin.gets.chomp.to_i
|
116
|
-
end
|
144
|
+
i == -1 ? '' : links[i][1]
|
145
|
+
end
|
117
146
|
|
118
|
-
|
119
|
-
|
120
|
-
|
147
|
+
def prompt_links(links)
|
148
|
+
links.each_with_index { |data, i| puts "#{i}\t\t#{data[0]}" }
|
149
|
+
|
150
|
+
puts
|
151
|
+
print 'Select the torrent you want to download [-1 to skip]: '
|
121
152
|
end
|
122
153
|
|
154
|
+
##
|
155
|
+
# Returns the date from which to check for shows
|
156
|
+
# Or nil if the date is today
|
123
157
|
def check_date(offset)
|
124
158
|
last = @config.content[:date]
|
125
159
|
if last - offset != Date.today
|
126
160
|
last - offset
|
127
|
-
else
|
128
|
-
puts 'Everything up to date'
|
129
|
-
exit
|
130
161
|
end
|
131
162
|
end
|
132
163
|
|
@@ -134,19 +165,26 @@ module DownloadTV
|
|
134
165
|
# Given a list of shows and episodes:
|
135
166
|
#
|
136
167
|
# * Removes ignored shows
|
137
|
-
|
138
|
-
|
139
|
-
# Ignored shows
|
140
|
-
s = shows.reject do |i|
|
168
|
+
def reject_ignored(shows)
|
169
|
+
shows.reject do |i|
|
141
170
|
# Remove season+episode
|
142
|
-
@config.content[:ignored]
|
171
|
+
@config.content[:ignored]
|
172
|
+
.include?(i.split(' ')[0..-2].join(' ').downcase)
|
143
173
|
end
|
174
|
+
|
175
|
+
end
|
144
176
|
|
145
|
-
|
177
|
+
##
|
178
|
+
# Given a list of shows and episodes:
|
179
|
+
#
|
180
|
+
# * Removes apostrophes, colons and parens
|
181
|
+
def fix_names(shows)
|
182
|
+
shows.map { |i| i.gsub(/ \(.+\)|[':]/, '') }
|
146
183
|
end
|
147
184
|
|
148
185
|
##
|
149
|
-
# Iteratively applies filters until they've all been applied
|
186
|
+
# Iteratively applies filters until they've all been applied
|
187
|
+
# or applying the next filter would result in no results
|
150
188
|
# These filters are defined at @filters
|
151
189
|
def filter_shows(links)
|
152
190
|
@filters.each do |f| # Apply each filter
|
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# Addic7ed prototype (WIP)
|
4
6
|
class Addic7ed < LinkGrabber
|
5
7
|
def initialize
|
6
|
-
super('http://www.addic7ed.com/search.php?search=%s
|
8
|
+
super('http://www.addic7ed.com/search.php?search=%s'\
|
9
|
+
'&Submit=Search')
|
7
10
|
end
|
8
11
|
|
9
12
|
def get_subs(show)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# EZTV.ag grabber
|
@@ -6,9 +8,9 @@ module DownloadTV
|
|
6
8
|
super('https://eztv.ag/search/%s')
|
7
9
|
end
|
8
10
|
|
9
|
-
def get_links(
|
11
|
+
def get_links(show)
|
10
12
|
# Format the url
|
11
|
-
search = format(@url,
|
13
|
+
search = format(@url, show)
|
12
14
|
|
13
15
|
data = @agent.get(search).search('a.magnet')
|
14
16
|
|
@@ -18,8 +20,15 @@ module DownloadTV
|
|
18
20
|
# EZTV shows 50 latest releases if it can't find the torrent
|
19
21
|
raise NoTorrentsError if data.size == 50
|
20
22
|
|
21
|
-
names = data.collect
|
22
|
-
|
23
|
+
names = data.collect do |i|
|
24
|
+
i.attribute('title')
|
25
|
+
.text
|
26
|
+
.chomp(' Magnet Link')
|
27
|
+
end
|
28
|
+
links = data.collect do |i|
|
29
|
+
i.attribute('href')
|
30
|
+
.text
|
31
|
+
end
|
23
32
|
|
24
33
|
names.zip(links)
|
25
34
|
end
|
@@ -1,32 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# KATcr.co grabber
|
4
6
|
class KAT < LinkGrabber
|
7
|
+
attr_reader :max_tries
|
8
|
+
|
5
9
|
def initialize
|
6
10
|
super('https://katcr.co/advanced-usearch/')
|
11
|
+
@max_tries = 5
|
7
12
|
end
|
8
13
|
|
9
|
-
def get_links(
|
14
|
+
def get_links(show)
|
10
15
|
tries = 0
|
11
|
-
max_tries = 5
|
12
16
|
|
13
17
|
params = {
|
14
18
|
'category': 'TV',
|
15
19
|
'orderby': 'seeds-desc',
|
16
|
-
'search':
|
20
|
+
'search': show
|
17
21
|
}
|
18
22
|
|
19
|
-
data = @agent.post(@url, params)
|
23
|
+
data = @agent.post(@url, params)
|
24
|
+
.search('tbody tr td[1]')
|
20
25
|
|
21
|
-
names = data.map
|
22
|
-
|
26
|
+
names = data.map do |i|
|
27
|
+
i.search('a.torrents_table__torrent_title b')
|
28
|
+
.text
|
29
|
+
end
|
30
|
+
|
31
|
+
links = data.map do |i|
|
32
|
+
i.search('div.torrents_table__actions a[3]')
|
33
|
+
.first
|
34
|
+
.attribute('href')
|
35
|
+
.text
|
36
|
+
end
|
23
37
|
|
24
38
|
raise NoTorrentsError if data.empty?
|
25
39
|
|
26
40
|
names.zip(links)
|
27
41
|
rescue Net::HTTP::Persistent::Error => e
|
28
42
|
raise unless e.message =~ /too many connection resets/
|
29
|
-
raise if tries >= max_tries
|
43
|
+
raise if tries >= @max_tries
|
44
|
+
|
30
45
|
tries += 1
|
31
46
|
retry
|
32
47
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# TorrentAPI.org grabber
|
@@ -7,7 +9,9 @@ module DownloadTV
|
|
7
9
|
attr_reader :wait
|
8
10
|
|
9
11
|
def initialize
|
10
|
-
super('https://torrentapi.org/pubapi_v2.php?
|
12
|
+
super('https://torrentapi.org/pubapi_v2.php?'\
|
13
|
+
'mode=search&search_string=%s&token=%s&'\
|
14
|
+
'app_id=DownloadTV&sort=seeders')
|
11
15
|
@wait = 0.1
|
12
16
|
end
|
13
17
|
|
@@ -26,37 +30,41 @@ module DownloadTV
|
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
33
|
+
##
|
34
|
+
# Makes a get request tp the given url.
|
35
|
+
# Returns the JSON response parsed into a hash
|
36
|
+
def request_and_parse(url)
|
37
|
+
page = @agent.get(url).content
|
38
|
+
JSON.parse(page)
|
39
|
+
end
|
40
|
+
|
29
41
|
##
|
30
42
|
# Connects to Torrentapi.org and requests a token, returning it
|
31
43
|
# Tokens automatically expire every 15 minutes
|
32
44
|
def renew_token
|
33
|
-
|
34
|
-
|
35
|
-
|
45
|
+
obj = request_and_parse('https://torrentapi.org/pubapi_v2'\
|
46
|
+
'.php?get_token=get_token&app_id='\
|
47
|
+
'DownloadTV')
|
36
48
|
|
37
49
|
@token = obj['token']
|
38
50
|
end
|
39
51
|
|
40
|
-
def get_links(
|
52
|
+
def get_links(show)
|
41
53
|
@token ||= renew_token
|
42
54
|
|
43
|
-
|
44
|
-
search = format(@url, s, @token)
|
55
|
+
search = format(@url, show, @token)
|
45
56
|
|
46
|
-
|
47
|
-
obj = JSON.parse(page)
|
57
|
+
obj = request_and_parse(search)
|
48
58
|
|
49
59
|
if obj['error_code'] == 4 # Token expired
|
50
60
|
renew_token
|
51
|
-
search = format(@url,
|
52
|
-
|
53
|
-
obj = JSON.parse(page)
|
61
|
+
search = format(@url, show, @token)
|
62
|
+
obj = request_and_parse(search)
|
54
63
|
end
|
55
64
|
|
56
65
|
while obj['error_code'] == 5 # Violate 1req/2s limit
|
57
66
|
sleep(@wait)
|
58
|
-
|
59
|
-
obj = JSON.parse(page)
|
67
|
+
obj = request_and_parse(search)
|
60
68
|
end
|
61
69
|
|
62
70
|
raise NoTorrentsError if obj['error']
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# ThePirateBay grabber
|
@@ -8,13 +10,11 @@ module DownloadTV
|
|
8
10
|
super("#{proxy}/search/%s/0/7/0")
|
9
11
|
end
|
10
12
|
|
11
|
-
def get_links(
|
12
|
-
|
13
|
-
search = format(@url, s)
|
13
|
+
def get_links(show)
|
14
|
+
search = format(@url, show)
|
14
15
|
|
15
|
-
data = @agent.get(search).search('#searchResult tr')
|
16
16
|
# Skip the header
|
17
|
-
data =
|
17
|
+
data = @agent.get(search).search('#searchResult tr').drop 1
|
18
18
|
|
19
19
|
raise NoTorrentsError if data.empty?
|
20
20
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# Interface for the grabbers
|
@@ -12,13 +14,18 @@ module DownloadTV
|
|
12
14
|
|
13
15
|
def online?
|
14
16
|
@agent.read_timeout = 2
|
15
|
-
@
|
17
|
+
url = if @url.include? '%s'
|
18
|
+
format(@url, 'test')
|
19
|
+
else
|
20
|
+
@url
|
21
|
+
end
|
22
|
+
@agent.head(url)
|
16
23
|
true
|
17
24
|
rescue Mechanize::ResponseCodeError, Net::HTTP::Persistent::Error
|
18
25
|
false
|
19
26
|
end
|
20
27
|
|
21
|
-
def get_links(
|
28
|
+
def get_links(_show)
|
22
29
|
raise NotImplementedError
|
23
30
|
end
|
24
31
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# API wrapper for MyEpisodes
|
@@ -11,15 +13,7 @@ module DownloadTV
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def login
|
14
|
-
|
15
|
-
print 'Enter your MyEpisodes username: '
|
16
|
-
@user = STDIN.gets.chomp
|
17
|
-
end
|
18
|
-
|
19
|
-
print 'Enter your MyEpisodes password: '
|
20
|
-
pass = STDIN.noecho(&:gets).chomp
|
21
|
-
puts
|
22
|
-
|
16
|
+
pass = prompt_user_data
|
23
17
|
page = @agent.get 'https://www.myepisodes.com/login.php'
|
24
18
|
|
25
19
|
login_form = page.forms[1]
|
@@ -35,19 +29,34 @@ module DownloadTV
|
|
35
29
|
@agent
|
36
30
|
end
|
37
31
|
|
32
|
+
def prompt_user_data
|
33
|
+
if !@user || @user == ''
|
34
|
+
print 'Enter your MyEpisodes username: '
|
35
|
+
@user = STDIN.gets.chomp
|
36
|
+
end
|
37
|
+
|
38
|
+
print 'Enter your MyEpisodes password: '
|
39
|
+
pass = STDIN.noecho(&:gets).chomp
|
40
|
+
puts
|
41
|
+
pass
|
42
|
+
end
|
43
|
+
|
38
44
|
def load_cookie
|
39
45
|
if File.exist? @cookie_path
|
40
46
|
@agent.cookie_jar.load @cookie_path
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
login
|
45
|
-
end
|
46
|
-
@agent
|
47
|
+
return @agent if logged_in?
|
48
|
+
|
49
|
+
puts 'The cookie is invalid/has expired.'
|
47
50
|
else
|
48
51
|
puts 'Cookie file not found'
|
49
|
-
login
|
50
52
|
end
|
53
|
+
|
54
|
+
login
|
55
|
+
end
|
56
|
+
|
57
|
+
def logged_in?
|
58
|
+
page = @agent.get 'https://www.myepisodes.com/login.php'
|
59
|
+
page.links[1].text != 'Register'
|
51
60
|
end
|
52
61
|
|
53
62
|
def save_cookie
|
@@ -56,22 +65,31 @@ module DownloadTV
|
|
56
65
|
end
|
57
66
|
|
58
67
|
def get_shows(last)
|
68
|
+
return [] if last.nil?
|
59
69
|
page = @agent.get 'https://www.myepisodes.com/ajax/service.php?mode=view_privatelist'
|
60
70
|
shows = page.parser.css('tr.past')
|
61
71
|
|
62
|
-
|
72
|
+
shows = filter_newer_shows(shows, last)
|
73
|
+
|
74
|
+
build_show_strings(shows)
|
75
|
+
end
|
76
|
+
|
77
|
+
def filter_newer_shows(shows, date)
|
78
|
+
shows.select do |i|
|
63
79
|
airdate = i.css('td.date')[0].text
|
64
|
-
Date.parse(airdate) >=
|
80
|
+
Date.parse(airdate) >= date
|
65
81
|
end
|
82
|
+
end
|
66
83
|
|
67
|
-
|
68
|
-
|
84
|
+
def build_show_strings(shows)
|
85
|
+
shows.map do |i|
|
86
|
+
sname = i.css('td.showname').text
|
69
87
|
ep = i.css('td.longnumber').text
|
70
88
|
|
71
89
|
ep.insert(0, 'S')
|
72
90
|
ep.sub!('x', 'E')
|
73
91
|
|
74
|
-
"#{
|
92
|
+
"#{sname} #{ep}"
|
75
93
|
end
|
76
94
|
end
|
77
95
|
end
|
data/lib/download_tv/torrent.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DownloadTV
|
2
4
|
##
|
3
5
|
# Class in charge of managing the link grabbers
|
@@ -5,7 +7,8 @@ module DownloadTV
|
|
5
7
|
attr_reader :g_instances, :tries
|
6
8
|
|
7
9
|
def grabbers
|
8
|
-
%w[TorrentAPI ThePirateBay Eztv KAT]
|
10
|
+
# %w[TorrentAPI ThePirateBay Eztv KAT]
|
11
|
+
%w[TorrentAPI ThePirateBay Eztv]
|
9
12
|
end
|
10
13
|
|
11
14
|
def initialize(default_grabber = nil)
|
@@ -27,6 +30,7 @@ module DownloadTV
|
|
27
30
|
exit 1
|
28
31
|
end
|
29
32
|
return if @g_instances.first.online?
|
33
|
+
|
30
34
|
# We won't be using this grabber
|
31
35
|
warn "Problem accessing #{@g_instances.first.class.name}"
|
32
36
|
@tries -= 1
|
@@ -48,7 +52,6 @@ module DownloadTV
|
|
48
52
|
|
49
53
|
links
|
50
54
|
rescue NoTorrentsError
|
51
|
-
puts "No torrents found for #{show} using #{@g_instances.first.class.name}"
|
52
55
|
|
53
56
|
# Use next grabber
|
54
57
|
if @tries.positive?
|
@@ -59,7 +62,7 @@ module DownloadTV
|
|
59
62
|
else
|
60
63
|
reset_grabbers_order
|
61
64
|
reset_tries
|
62
|
-
|
65
|
+
puts "No torrents found for #{show}"
|
63
66
|
return []
|
64
67
|
end
|
65
68
|
end
|
data/lib/download_tv/version.rb
CHANGED
data/test/config_test.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
describe DownloadTV::Configuration do
|
@@ -104,6 +106,7 @@ describe DownloadTV::Configuration do
|
|
104
106
|
c.content[:ignored].must_equal ['anything']
|
105
107
|
c.content[:auto].must_equal true
|
106
108
|
c.content[:subs].must_equal true
|
109
|
+
c.content[:pending].must_equal []
|
107
110
|
c.content[:grabber].must_equal 'TorrentAPI'
|
108
111
|
c.content[:date].must_equal(Date.today - 1)
|
109
112
|
c.content[:version].must_equal DownloadTV::VERSION
|
@@ -149,6 +152,7 @@ describe DownloadTV::Configuration do
|
|
149
152
|
content[:ignored].must_equal ['anything']
|
150
153
|
content[:auto].must_equal true
|
151
154
|
content[:subs].must_equal true
|
155
|
+
content[:pending].must_equal []
|
152
156
|
content[:grabber].must_equal 'TorrentAPI'
|
153
157
|
content[:date].must_equal Date.today - 1
|
154
158
|
content[:version].must_equal DownloadTV::VERSION
|
data/test/downloader_test.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
describe DownloadTV::Downloader do
|
@@ -22,27 +24,32 @@ describe DownloadTV::Downloader do
|
|
22
24
|
|
23
25
|
describe 'the fix_names method' do
|
24
26
|
it 'should remove apostrophes, colons and parens' do
|
25
|
-
shows = ['Mr. Foo S01E02', 'Bar (UK) S00E22', "Let's S05E03",
|
26
|
-
|
27
|
+
shows = ['Mr. Foo S01E02', 'Bar (UK) S00E22', "Let's S05E03",
|
28
|
+
'Baz: The Story S05E22']
|
29
|
+
result = ['Mr. Foo S01E02', 'Bar S00E22', 'Lets S05E03',
|
30
|
+
'Baz The Story S05E22']
|
27
31
|
|
28
32
|
dl = DownloadTV::Downloader.new(ignored: [], path: config_path)
|
29
33
|
dl.fix_names(shows).must_equal result
|
30
34
|
end
|
31
35
|
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'the reject_ignored method' do
|
32
39
|
it 'should remove ignored shows' do
|
33
|
-
shows = ['
|
34
|
-
result = ['
|
40
|
+
shows = ['Bar S00E22', 'Ignored S20E22']
|
41
|
+
result = ['Bar S00E22']
|
35
42
|
|
36
43
|
dl = DownloadTV::Downloader.new(ignored: ['ignored'], path: config_path)
|
37
|
-
dl.
|
44
|
+
dl.reject_ignored(shows).must_equal result
|
38
45
|
end
|
46
|
+
|
39
47
|
end
|
40
48
|
|
41
49
|
describe 'the check_date method' do
|
42
50
|
it 'exits the script when up to date' do
|
43
51
|
dl = DownloadTV::Downloader.new(date: Date.today, path: config_path)
|
44
|
-
|
45
|
-
to_run.must_raise SystemExit
|
52
|
+
dl.check_date(0).must_be_nil
|
46
53
|
end
|
47
54
|
|
48
55
|
it 'uses the offset to adjust the date' do
|
@@ -80,7 +87,8 @@ describe DownloadTV::Downloader do
|
|
80
87
|
|
81
88
|
it 'removes names without PROPER or REPACK in them' do
|
82
89
|
dl = DownloadTV::Downloader.new(path: config_path)
|
83
|
-
links = [['Link 1', ''], ['Link 2 2160p', ''], ['Link 3', ''],
|
90
|
+
links = [['Link 1', ''], ['Link 2 2160p', ''], ['Link 3', ''],
|
91
|
+
['Link 4 PROPER', ''], ['Link REPACK 5', '']]
|
84
92
|
res = [['Link 4 PROPER', ''], ['Link REPACK 5', '']]
|
85
93
|
dl.filter_shows(links).must_equal res
|
86
94
|
end
|
@@ -99,11 +107,15 @@ describe DownloadTV::Downloader do
|
|
99
107
|
show = 'Example Show S01E01'
|
100
108
|
|
101
109
|
t.expect(:get_links, [], [show])
|
102
|
-
dl = DownloadTV::Downloader.new(auto: true, path: config_path)
|
103
|
-
dl.get_link(t, show).must_equal ''
|
110
|
+
dl = DownloadTV::Downloader.new(auto: true, path: config_path, pending: ['show 11'])
|
111
|
+
dl.get_link(t, show, true).must_equal ''
|
112
|
+
dl.config.content[:pending].must_equal ['show 11', show]
|
113
|
+
|
104
114
|
t.expect(:get_links, [], [show])
|
105
|
-
dl = DownloadTV::Downloader.new(auto: false, path: config_path)
|
106
|
-
dl.get_link(t, show).must_equal ''
|
115
|
+
dl = DownloadTV::Downloader.new(auto: false, path: config_path, pending: [])
|
116
|
+
dl.get_link(t, show, true).must_equal ''
|
117
|
+
dl.config.content[:pending].must_include show
|
118
|
+
|
107
119
|
t.verify
|
108
120
|
end
|
109
121
|
|
data/test/grabbers_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
data/test/torrent_test.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
describe DownloadTV::Torrent do
|
@@ -25,8 +27,8 @@ describe DownloadTV::Torrent do
|
|
25
27
|
describe 'when giving it a default grabber' do
|
26
28
|
it 'has a default order' do
|
27
29
|
t = DownloadTV::Torrent.new(nil)
|
28
|
-
expected = grabber_names.map { |i| "DownloadTV::#{i}"}
|
29
|
-
t.g_instances.map { |i| i.class.name}.must_equal expected
|
30
|
+
expected = grabber_names.map { |i| "DownloadTV::#{i}" }
|
31
|
+
t.g_instances.map { |i| i.class.name }.must_equal expected
|
30
32
|
end
|
31
33
|
|
32
34
|
grabber_names.each do |g|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: download_tv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- guille
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -25,33 +25,33 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.15'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '5.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '5.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '10.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '10.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|