download_tv 2.4.7 → 2.5.0
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.
- 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
|