download_tv 2.6.6 → 2.6.7
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/.github/workflows/ruby.yml +1 -1
- data/.rspec +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +2 -0
- data/README.md +1 -0
- data/bin/tv +8 -3
- data/lib/download_tv/configuration.rb +107 -81
- data/lib/download_tv/downloader.rb +14 -15
- data/lib/download_tv/filterer.rb +17 -17
- data/lib/download_tv/grabbers/eztv.rb +1 -1
- data/lib/download_tv/grabbers/torrentapi.rb +1 -1
- data/lib/download_tv/grabbers/torrentz.rb +25 -0
- data/lib/download_tv/grabbers/tpb.rb +1 -1
- data/lib/download_tv/linkgrabber.rb +6 -3
- data/lib/download_tv/myepisodes.rb +43 -32
- data/lib/download_tv/torrent.rb +37 -28
- data/lib/download_tv/version.rb +1 -1
- data/spec/download_tv/configuration_spec.rb +199 -0
- data/spec/download_tv/filterer_spec.rb +107 -0
- data/spec/download_tv/linkgrabber_spec.rb +16 -0
- data/spec/download_tv/myepisodes_spec.rb +38 -0
- data/spec/download_tv/torrent_spec.rb +132 -0
- data/spec/spec_helper.rb +92 -0
- data/test/downloader_test.rb +5 -5
- metadata +10 -8
- data/lib/download_tv/grabbers/kat.rb +0 -49
- data/lib/download_tv/grabbers/torrentz2.rb +0 -27
- data/test/config_test.rb +0 -175
- data/test/torrent_test.rb +0 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2cd494e098abb85a02a4a7182e66aef862aa14a04e73bd0c38f2e3a8991c1954
|
|
4
|
+
data.tar.gz: 275eccd2336a88bda38cd0711d169b7e95b9fd492164ae033b199b8d08eb4f32
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 884a973095e183b1499839d3b68f01e0ba475ae6ea1220f7a5a98d9f3427892bb78c5404aedc2cbdab6f8f157b9a84729ebd75a1aaef37b5a389670f98e67c40
|
|
7
|
+
data.tar.gz: 2a1d58fb22524127f7fe42040a652473f0146aaa7ea6a7c45fc6af35800d4c09e45ed821ec6ebd8c343e76952af3ec4a4771dfb5986c9f64bbf764347c0bc267
|
data/.github/workflows/ruby.yml
CHANGED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# download_tv CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 2.6.7 (2022-10-22)
|
|
4
|
+
|
|
5
|
+
* Features
|
|
6
|
+
* Add `--healthcheck` to check the status of all the grabbers (online/offline).
|
|
7
|
+
|
|
8
|
+
* Fixes
|
|
9
|
+
* Better detection for offline grabbers without crashing the app
|
|
10
|
+
|
|
11
|
+
* Grabbers
|
|
12
|
+
* Torrentz: Fix parser
|
|
13
|
+
|
|
3
14
|
## 2.6.6 (2022-01-21)
|
|
4
15
|
|
|
5
16
|
* Improvements
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -29,6 +29,7 @@ Specific options:
|
|
|
29
29
|
-a, --[no-]auto Automatically find links
|
|
30
30
|
-g, --grabber GRABBER Use given grabber as first option
|
|
31
31
|
--show-grabbers List available grabbers
|
|
32
|
+
--healthcheck Check status of all the grabbers
|
|
32
33
|
-p, --pending Show list of pending downloads
|
|
33
34
|
--clear-pending Clear list of pending downloads
|
|
34
35
|
-q, --queue SHOW Add show episode to pending downloads list
|
data/bin/tv
CHANGED
|
@@ -60,7 +60,12 @@ opt_parser = OptionParser.new do |opts|
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
opts.on('--show-grabbers', 'List available grabbers') do
|
|
63
|
-
puts DownloadTV::Torrent.
|
|
63
|
+
puts DownloadTV::Torrent.grabbers
|
|
64
|
+
exit
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
opts.on('--healthcheck', 'Check status of all the grabbers') do
|
|
68
|
+
DownloadTV::Torrent.healthcheck
|
|
64
69
|
exit
|
|
65
70
|
end
|
|
66
71
|
|
|
@@ -117,9 +122,9 @@ begin
|
|
|
117
122
|
when 'config'
|
|
118
123
|
DownloadTV::Configuration.new(config).change_configuration
|
|
119
124
|
when 'showconfig'
|
|
120
|
-
DownloadTV::Configuration.new(config)
|
|
125
|
+
puts DownloadTV::Configuration.new(config)
|
|
121
126
|
when 'showpending'
|
|
122
|
-
DownloadTV::Configuration.new(config)
|
|
127
|
+
puts DownloadTV::Configuration.new(config)[:pending]
|
|
123
128
|
when 'clearpending'
|
|
124
129
|
DownloadTV::Configuration.new(config).clear_pending
|
|
125
130
|
when 'queue'
|
|
@@ -4,134 +4,160 @@ module DownloadTV
|
|
|
4
4
|
##
|
|
5
5
|
# Class used for managing the configuration of the application
|
|
6
6
|
class Configuration
|
|
7
|
-
|
|
7
|
+
def initialize(user_config = {})
|
|
8
|
+
load_config(user_config)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def [](key)
|
|
12
|
+
@content[key]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def []=(key, value)
|
|
16
|
+
@content[key] = value
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def change_configuration
|
|
20
|
+
prompt_for_new_values
|
|
21
|
+
set_default_values
|
|
22
|
+
serialize
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def serialize
|
|
26
|
+
self[:pending] = self[:pending].uniq
|
|
27
|
+
File.write(config_path, JSON.generate(@content))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_s
|
|
31
|
+
@content.reduce('') do |mem, item|
|
|
32
|
+
key, val = item
|
|
33
|
+
"#{mem}#{key}: #{val}\n"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def clear_pending
|
|
38
|
+
self[:pending].clear
|
|
39
|
+
serialize
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def queue_pending(show)
|
|
43
|
+
self[:pending] << show
|
|
44
|
+
serialize
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
8
48
|
|
|
9
|
-
def
|
|
10
|
-
@
|
|
11
|
-
|
|
49
|
+
def content
|
|
50
|
+
@content ||= {}
|
|
51
|
+
end
|
|
12
52
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@content[:ignored]&.map!(&:downcase)
|
|
53
|
+
def load_config(user_config)
|
|
54
|
+
if File.exist? config_path
|
|
55
|
+
parse_config
|
|
17
56
|
else
|
|
18
|
-
|
|
57
|
+
FileUtils.mkdir_p(File.expand_path('..', config_path))
|
|
19
58
|
change_configuration
|
|
20
59
|
end
|
|
60
|
+
content.merge!(user_config) unless user_config.empty?
|
|
61
|
+
self[:ignored]&.map!(&:downcase)
|
|
21
62
|
end
|
|
22
63
|
|
|
23
|
-
def
|
|
64
|
+
def parse_config
|
|
65
|
+
source = File.read(config_path)
|
|
66
|
+
@content = JSON.parse(source, symbolize_names: true)
|
|
67
|
+
|
|
68
|
+
self[:date] = Date.parse(self[:date]) if self[:date]
|
|
69
|
+
|
|
70
|
+
if !self[:version] || breaking_changes?(self[:version])
|
|
71
|
+
warn 'Change configuration required (version with breaking changes detected)'
|
|
72
|
+
change_configuration
|
|
73
|
+
end
|
|
74
|
+
rescue JSON::ParserError => e
|
|
75
|
+
warn "Error parsing config file at #{config_path} => #{e.message}"
|
|
76
|
+
change_configuration
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# Returns true if a major or minor update has been detected, or if the config version is newer
|
|
81
|
+
# than the installed version. Returns something falsy otherwise
|
|
82
|
+
def breaking_changes?(version)
|
|
83
|
+
paired = DownloadTV::VERSION.split('.')
|
|
84
|
+
.map(&:to_i)
|
|
85
|
+
.zip(version.split('.').map(&:to_i))
|
|
86
|
+
# The configuration belongs to a newer version than is installed
|
|
87
|
+
return true unless paired.find_index { |x, y| x < y }.nil?
|
|
88
|
+
|
|
89
|
+
paired.find_index { |x, y| y < x }&.< 2
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def prompt_for_new_values
|
|
24
93
|
prompt_for_myep_user
|
|
25
94
|
prompt_for_cookie
|
|
26
95
|
prompt_for_ignored
|
|
27
96
|
prompt_for_filters
|
|
28
97
|
$stdout.flush
|
|
29
|
-
|
|
30
|
-
set_default_values
|
|
31
|
-
serialize
|
|
32
98
|
end
|
|
33
99
|
|
|
34
100
|
def prompt_for_myep_user
|
|
35
|
-
existing = "(#{
|
|
101
|
+
existing = "(#{self[:myepisodes_user]}) " if self[:myepisodes_user]
|
|
36
102
|
print "Enter your MyEpisodes username #{existing}: "
|
|
37
103
|
input = $stdin.gets.chomp
|
|
38
|
-
|
|
104
|
+
self[:myepisodes_user] = input if input
|
|
39
105
|
end
|
|
40
106
|
|
|
41
107
|
def prompt_for_cookie
|
|
42
108
|
print 'Save cookie? (y)/n: '
|
|
43
|
-
|
|
109
|
+
self[:cookie] = !($stdin.gets.chomp.casecmp? 'n')
|
|
44
110
|
end
|
|
45
111
|
|
|
46
112
|
def prompt_for_ignored
|
|
47
|
-
existing = "(#{
|
|
113
|
+
existing = "(#{self[:ignored]})" if self[:ignored]
|
|
48
114
|
puts "Enter a comma-separated list of shows to ignore: #{existing}"
|
|
49
115
|
|
|
50
|
-
|
|
116
|
+
self[:ignored] = read_and_split_list :downcase
|
|
51
117
|
end
|
|
52
118
|
|
|
53
119
|
def prompt_for_filters
|
|
54
|
-
puts "Current filters: (#{
|
|
55
|
-
|
|
120
|
+
puts "Current filters: (#{self[:filters]})" if self[:filters]
|
|
121
|
+
self[:filters] = {}
|
|
56
122
|
|
|
57
123
|
puts 'Enter a comma-separated list of terms to include: '
|
|
58
|
-
|
|
124
|
+
self[:filters][:includes] = read_and_split_list :upcase
|
|
59
125
|
|
|
60
126
|
puts 'Enter a comma-separated list of terms to exclude: '
|
|
61
|
-
|
|
127
|
+
self[:filters][:excludes] = read_and_split_list :upcase
|
|
62
128
|
end
|
|
63
129
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
.map(&:strip)
|
|
67
|
-
.map(&case_method)
|
|
130
|
+
def config_path
|
|
131
|
+
(content[:path] || default_config_path)
|
|
68
132
|
end
|
|
69
133
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
'includes' => %w[PROPER REPACK],
|
|
73
|
-
'excludes' => %w[2160P 1080P 720P]
|
|
74
|
-
}
|
|
134
|
+
def default_config_path
|
|
135
|
+
File.join(ENV['HOME'], '.config', 'download_tv', 'config')
|
|
75
136
|
end
|
|
76
137
|
|
|
77
138
|
##
|
|
78
139
|
# Update the +content+ attribute with the defaults, if needed.
|
|
79
140
|
# Maintains the previous values, in case it's an update from an existing file.
|
|
80
141
|
def set_default_values
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def serialize
|
|
90
|
-
@content[:pending] = @content[:pending].uniq
|
|
91
|
-
File.write(@config_path, JSON.generate(@content))
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def load_config
|
|
95
|
-
source = File.read(@config_path)
|
|
96
|
-
@content = JSON.parse(source, symbolize_names: true)
|
|
97
|
-
|
|
98
|
-
@content[:date] = Date.parse(@content[:date]) if @content[:date]
|
|
99
|
-
|
|
100
|
-
change_configuration if !@content[:version] || breaking_changes?(@content[:version])
|
|
101
|
-
rescue JSON::ParserError
|
|
102
|
-
@content = {}
|
|
103
|
-
change_configuration
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def default_config_path
|
|
107
|
-
File.join(ENV['HOME'], '.config', 'download_tv', 'config')
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
##
|
|
111
|
-
# Returns true if a major or minor update has been detected, something falsy otherwise
|
|
112
|
-
def breaking_changes?(version)
|
|
113
|
-
DownloadTV::VERSION.split('.')
|
|
114
|
-
.zip(version.split('.'))
|
|
115
|
-
.find_index { |x, y| y < x }
|
|
116
|
-
&.< 2
|
|
142
|
+
self[:auto] ||= true
|
|
143
|
+
self[:grabber] ||= 'TorrentAPI'
|
|
144
|
+
self[:date] ||= Date.today - 1
|
|
145
|
+
self[:filters] ||= default_filters
|
|
146
|
+
self[:pending] ||= []
|
|
147
|
+
self[:version] = DownloadTV::VERSION
|
|
117
148
|
end
|
|
118
149
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
puts @content[arg]
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def clear_pending
|
|
128
|
-
@content[:pending].clear
|
|
129
|
-
serialize
|
|
150
|
+
def default_filters
|
|
151
|
+
{
|
|
152
|
+
'includes' => %w[PROPER REPACK],
|
|
153
|
+
'excludes' => %w[2160P 1080P 720P]
|
|
154
|
+
}
|
|
130
155
|
end
|
|
131
156
|
|
|
132
|
-
def
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
def read_and_split_list(case_method)
|
|
158
|
+
$stdin.gets.chomp.split(',')
|
|
159
|
+
.map(&:strip)
|
|
160
|
+
.map(&case_method)
|
|
135
161
|
end
|
|
136
162
|
end
|
|
137
163
|
end
|
|
@@ -16,7 +16,7 @@ module DownloadTV
|
|
|
16
16
|
# Tries to download episodes in order for a given season,
|
|
17
17
|
# until it can't find any
|
|
18
18
|
def download_entire_season(show, season)
|
|
19
|
-
t = Torrent.new(@config
|
|
19
|
+
t = Torrent.new(@config[:grabber])
|
|
20
20
|
season.insert(0, '0') if season.size == 1
|
|
21
21
|
episode = "#{show} s#{season}e01"
|
|
22
22
|
loop do
|
|
@@ -29,7 +29,7 @@ module DownloadTV
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def download_single_show(show, season = nil)
|
|
32
|
-
t = Torrent.new(@config
|
|
32
|
+
t = Torrent.new(@config[:grabber])
|
|
33
33
|
show = fix_names([show]).first
|
|
34
34
|
if season
|
|
35
35
|
download_entire_season(show, season)
|
|
@@ -44,7 +44,7 @@ module DownloadTV
|
|
|
44
44
|
def download_from_file(filename)
|
|
45
45
|
if File.exist? filename
|
|
46
46
|
filename = File.realpath(filename)
|
|
47
|
-
t = Torrent.new(@config
|
|
47
|
+
t = Torrent.new(@config[:grabber])
|
|
48
48
|
to_download = File.readlines(filename, chomp: true)
|
|
49
49
|
fix_names(to_download).each { |show| download(get_link(t, show)) }
|
|
50
50
|
else
|
|
@@ -56,7 +56,7 @@ module DownloadTV
|
|
|
56
56
|
##
|
|
57
57
|
# Returns the date from which to check shows
|
|
58
58
|
def date_to_check_from(offset)
|
|
59
|
-
return @config
|
|
59
|
+
return @config[:date] if offset.zero?
|
|
60
60
|
|
|
61
61
|
Date.today - offset
|
|
62
62
|
end
|
|
@@ -70,8 +70,8 @@ module DownloadTV
|
|
|
70
70
|
# The param +offset+ can be used to move the date back that many days in the check
|
|
71
71
|
# The param +include_tomorrow+ will add the current day to the list of dates to search
|
|
72
72
|
def run(dry_run = false, offset = 0, include_tomorrow: false)
|
|
73
|
-
pending = @config
|
|
74
|
-
@config
|
|
73
|
+
pending = @config[:pending].clone
|
|
74
|
+
@config[:pending].clear
|
|
75
75
|
pending ||= []
|
|
76
76
|
date = date_to_check_from(offset)
|
|
77
77
|
|
|
@@ -85,10 +85,10 @@ module DownloadTV
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
unless dry_run
|
|
88
|
-
@config
|
|
88
|
+
@config[:date] = if include_tomorrow
|
|
89
89
|
Date.today.next
|
|
90
90
|
else
|
|
91
|
-
[Date.today, @config
|
|
91
|
+
[Date.today, @config[:date]].max
|
|
92
92
|
end
|
|
93
93
|
@config.serialize
|
|
94
94
|
end
|
|
@@ -126,9 +126,8 @@ module DownloadTV
|
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def shows_to_download(date, include_tomorrow)
|
|
129
|
-
myepisodes = MyEpisodes.new(@config
|
|
130
|
-
@config
|
|
131
|
-
myepisodes.load_cookie
|
|
129
|
+
myepisodes = MyEpisodes.new(@config[:myepisodes_user],
|
|
130
|
+
@config[:cookie])
|
|
132
131
|
shows = myepisodes.get_shows_since(date, include_tomorrow: include_tomorrow)
|
|
133
132
|
shows = reject_ignored(shows)
|
|
134
133
|
fix_names(shows)
|
|
@@ -144,11 +143,11 @@ module DownloadTV
|
|
|
144
143
|
links = torrent.get_links(show)
|
|
145
144
|
|
|
146
145
|
if links.empty?
|
|
147
|
-
@config
|
|
146
|
+
@config[:pending] << show if save_pending
|
|
148
147
|
return
|
|
149
148
|
end
|
|
150
149
|
|
|
151
|
-
if @config
|
|
150
|
+
if @config[:auto]
|
|
152
151
|
filter_shows(links).first[1]
|
|
153
152
|
else
|
|
154
153
|
prompt_links(links)
|
|
@@ -181,7 +180,7 @@ module DownloadTV
|
|
|
181
180
|
def reject_ignored(shows)
|
|
182
181
|
shows.reject do |i|
|
|
183
182
|
# Remove season+episode
|
|
184
|
-
@config
|
|
183
|
+
@config[:ignored]
|
|
185
184
|
.include?(i.split(' ')[0..-2].join(' ').downcase)
|
|
186
185
|
end
|
|
187
186
|
end
|
|
@@ -199,7 +198,7 @@ module DownloadTV
|
|
|
199
198
|
# Runs until no filters are left to be applied or applying
|
|
200
199
|
# a filter would leave no results
|
|
201
200
|
def filter_shows(links)
|
|
202
|
-
@filterer ||= Filterer.new(@config
|
|
201
|
+
@filterer ||= Filterer.new(@config[:filters])
|
|
203
202
|
@filterer.filter(links)
|
|
204
203
|
end
|
|
205
204
|
|
data/lib/download_tv/filterer.rb
CHANGED
|
@@ -4,28 +4,11 @@ module DownloadTV
|
|
|
4
4
|
##
|
|
5
5
|
# Builds and applies filters to the results
|
|
6
6
|
class Filterer
|
|
7
|
-
attr_reader :filters
|
|
8
|
-
|
|
9
7
|
def initialize(filters_config)
|
|
10
8
|
@filters = []
|
|
11
9
|
build_filters(filters_config)
|
|
12
10
|
end
|
|
13
11
|
|
|
14
|
-
def build_include_filter(str)
|
|
15
|
-
@filters << ->(n) { !n.upcase.include?(str) }
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def build_exclude_filter(str)
|
|
19
|
-
@filters << ->(n) { n.upcase.include?(str) }
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def build_filters(filters_config)
|
|
23
|
-
return unless filters_config
|
|
24
|
-
|
|
25
|
-
filters_config[:includes].map { |i| build_include_filter(i) }
|
|
26
|
-
filters_config[:excludes].map { |i| build_exclude_filter(i) }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
12
|
##
|
|
30
13
|
# Iteratively applies filters until they've all been applied
|
|
31
14
|
# or applying the next filter would result in no results
|
|
@@ -41,5 +24,22 @@ module DownloadTV
|
|
|
41
24
|
|
|
42
25
|
shows
|
|
43
26
|
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def build_filters(filters_config)
|
|
31
|
+
return unless filters_config
|
|
32
|
+
|
|
33
|
+
filters_config[:includes].map { |i| build_include_filter(i) }
|
|
34
|
+
filters_config[:excludes].map { |i| build_exclude_filter(i) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def build_include_filter(str)
|
|
38
|
+
@filters << ->(n) { !n.upcase.include?(str) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def build_exclude_filter(str)
|
|
42
|
+
@filters << ->(n) { n.upcase.include?(str) }
|
|
43
|
+
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -9,7 +9,7 @@ module DownloadTV
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def get_links(show)
|
|
12
|
-
raw_data =
|
|
12
|
+
raw_data = agent.get(format(@url, show))
|
|
13
13
|
raw_seeders = raw_data.search('td.forum_thread_post_end').map { |e| e.children[0].text.to_i }
|
|
14
14
|
raw_links = raw_data.search('a.magnet').sort_by.with_index { |_, index| raw_seeders[index] }.reverse
|
|
15
15
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DownloadTV
|
|
4
|
+
##
|
|
5
|
+
# Torrentz2 grabber
|
|
6
|
+
class Torrentz < LinkGrabber
|
|
7
|
+
def initialize
|
|
8
|
+
super('https://torrentz2.nz/search?q=%s')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def get_links(show)
|
|
12
|
+
raw_data = agent.get(format(@url, show))
|
|
13
|
+
results = raw_data.search('dl')
|
|
14
|
+
|
|
15
|
+
raise NoTorrentsError if results.empty?
|
|
16
|
+
|
|
17
|
+
data = results.sort_by { |e| e.search('dd span')[3].text.to_i }.reverse
|
|
18
|
+
|
|
19
|
+
data.collect do |i|
|
|
20
|
+
[i.children[0].text.strip,
|
|
21
|
+
i.search('dd span a').first.attribute('href').text]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -8,7 +8,10 @@ module DownloadTV
|
|
|
8
8
|
|
|
9
9
|
def initialize(url)
|
|
10
10
|
@url = url
|
|
11
|
-
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def agent
|
|
14
|
+
@agent ||= Mechanize.new do |a|
|
|
12
15
|
a.user_agent = DownloadTV::USER_AGENT
|
|
13
16
|
a.read_timeout = 10
|
|
14
17
|
end
|
|
@@ -20,9 +23,9 @@ module DownloadTV
|
|
|
20
23
|
else
|
|
21
24
|
@url
|
|
22
25
|
end
|
|
23
|
-
|
|
26
|
+
agent.head(url)
|
|
24
27
|
true
|
|
25
|
-
rescue Mechanize::ResponseCodeError, Net::HTTP::Persistent::Error, Errno::ECONNRESET
|
|
28
|
+
rescue Mechanize::ResponseCodeError, Net::HTTP::Persistent::Error, Errno::ECONNRESET, Net::ReadTimeout, OpenSSL::SSL::SSLError
|
|
26
29
|
false
|
|
27
30
|
end
|
|
28
31
|
|
|
@@ -5,32 +5,65 @@ module DownloadTV
|
|
|
5
5
|
# API wrapper for MyEpisodes
|
|
6
6
|
class MyEpisodes
|
|
7
7
|
def initialize(user, save_cookie)
|
|
8
|
-
@agent = Mechanize.new
|
|
9
|
-
@agent.user_agent = DownloadTV::USER_AGENT
|
|
10
8
|
@user = user
|
|
11
9
|
@save_cookie = save_cookie
|
|
12
10
|
@cookie_path = File.join(ENV['HOME'], '.config', 'download_tv', 'cookie')
|
|
11
|
+
agent.user_agent = DownloadTV::USER_AGENT
|
|
12
|
+
login
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get_shows_since(last, include_tomorrow: false)
|
|
16
|
+
page = agent.get 'https://www.myepisodes.com/ajax/service.php?mode=view_privatelist'
|
|
17
|
+
shows = page.parser.css('tr.past')
|
|
18
|
+
shows = filter_newer_shows(shows, last)
|
|
19
|
+
shows.concat(page.parser.css('tr.today')) if include_tomorrow
|
|
20
|
+
build_show_strings(shows)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def agent
|
|
26
|
+
@agent ||= Mechanize.new
|
|
13
27
|
end
|
|
14
28
|
|
|
15
29
|
def login
|
|
30
|
+
logged_in_with_cookie = load_cookie
|
|
31
|
+
manual_login unless logged_in_with_cookie
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def manual_login
|
|
16
35
|
pass = prompt_user_data
|
|
17
|
-
page =
|
|
36
|
+
page = agent.get 'https://www.myepisodes.com/login.php'
|
|
18
37
|
|
|
19
38
|
login_form = page.forms[1]
|
|
20
39
|
login_form.username = @user
|
|
21
40
|
login_form.password = pass
|
|
22
41
|
|
|
23
|
-
page =
|
|
42
|
+
page = agent.submit(login_form, login_form.buttons.first)
|
|
24
43
|
|
|
25
44
|
raise InvalidLoginError if page.filename == 'login.php'
|
|
26
45
|
|
|
27
|
-
|
|
46
|
+
store_cookie if @save_cookie
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# If there is a cookie file, tries to log in using it
|
|
51
|
+
# returns the result of the operation (true/false)
|
|
52
|
+
def load_cookie
|
|
53
|
+
if File.exist? @cookie_path
|
|
54
|
+
agent.cookie_jar.load @cookie_path
|
|
55
|
+
return true if logged_in?
|
|
56
|
+
|
|
57
|
+
puts 'The cookie is invalid/has expired.'
|
|
58
|
+
else
|
|
59
|
+
puts 'Cookie file not found'
|
|
60
|
+
end
|
|
28
61
|
|
|
29
|
-
|
|
62
|
+
false
|
|
30
63
|
end
|
|
31
64
|
|
|
32
65
|
def prompt_user_data
|
|
33
|
-
if
|
|
66
|
+
if @user.nil? || @user.empty?
|
|
34
67
|
print 'Enter your MyEpisodes username: '
|
|
35
68
|
@user = $stdin.gets.chomp
|
|
36
69
|
end
|
|
@@ -41,35 +74,13 @@ module DownloadTV
|
|
|
41
74
|
pass
|
|
42
75
|
end
|
|
43
76
|
|
|
44
|
-
def load_cookie
|
|
45
|
-
if File.exist? @cookie_path
|
|
46
|
-
@agent.cookie_jar.load @cookie_path
|
|
47
|
-
return @agent if logged_in?
|
|
48
|
-
|
|
49
|
-
puts 'The cookie is invalid/has expired.'
|
|
50
|
-
else
|
|
51
|
-
puts 'Cookie file not found'
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
login
|
|
55
|
-
end
|
|
56
|
-
|
|
57
77
|
def logged_in?
|
|
58
|
-
page =
|
|
78
|
+
page = agent.get 'https://www.myepisodes.com/login.php'
|
|
59
79
|
page.links[1].text != 'Register'
|
|
60
80
|
end
|
|
61
81
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
@agent
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def get_shows_since(last, include_tomorrow: false)
|
|
68
|
-
page = @agent.get 'https://www.myepisodes.com/ajax/service.php?mode=view_privatelist'
|
|
69
|
-
shows = page.parser.css('tr.past')
|
|
70
|
-
shows = filter_newer_shows(shows, last)
|
|
71
|
-
shows.concat(page.parser.css('tr.today')) if include_tomorrow
|
|
72
|
-
build_show_strings(shows)
|
|
82
|
+
def store_cookie
|
|
83
|
+
agent.cookie_jar.save(@cookie_path, session: true)
|
|
73
84
|
end
|
|
74
85
|
|
|
75
86
|
# Only keep the shows that have aired since the given date
|