download_tv 2.6.6 → 2.6.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.rspec +1 -0
- data/CHANGELOG.md +17 -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 +2 -2
- 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 +11 -9
- 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: 8220befd4c805752e34c0d67d992fb6a2ba16c901131304c0c630a1c02908340
|
4
|
+
data.tar.gz: 99f28d45aecd2738f941e49befc895b3e4228e9b5403f53886811d617abf2e85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70e69b388a88a2764d129fafaab67a55b0ac222c6c03b64562ed84cd5b3b3a278dd29876161ee2a7f7949185fbf8ec0ae2f47fc04a11ce375f2c656764b31a1e
|
7
|
+
data.tar.gz: cb9897a14a4dc1a3341a94751525240b93d6cfbbeef8d14231fe3d7f5688e15f2942091d90d6fd105e9eea0b18c18f36f8ad3491b4642e3f7eefaa7039e2c00e
|
data/.github/workflows/ruby.yml
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# download_tv CHANGELOG
|
2
2
|
|
3
|
+
## 2.6.8 (2022-12-02)
|
4
|
+
|
5
|
+
* Improvements
|
6
|
+
* Improvements on the TorrentAPI grabber.
|
7
|
+
* Better detection of grabbers being offline.
|
8
|
+
|
9
|
+
## 2.6.7 (2022-10-22)
|
10
|
+
|
11
|
+
* Features
|
12
|
+
* Add `--healthcheck` to check the status of all the grabbers (online/offline).
|
13
|
+
|
14
|
+
* Fixes
|
15
|
+
* Better detection for offline grabbers without crashing the app
|
16
|
+
|
17
|
+
* Grabbers
|
18
|
+
* Torrentz: Fix parser
|
19
|
+
|
3
20
|
## 2.6.6 (2022-01-21)
|
4
21
|
|
5
22
|
* 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
|
|
@@ -35,7 +35,7 @@ module DownloadTV
|
|
35
35
|
# Makes a get request tp the given url.
|
36
36
|
# Returns the JSON response parsed into a hash
|
37
37
|
def request_and_parse(url)
|
38
|
-
page =
|
38
|
+
page = agent.get(url).content
|
39
39
|
JSON.parse(page)
|
40
40
|
end
|
41
41
|
|
@@ -63,7 +63,7 @@ module DownloadTV
|
|
63
63
|
obj = request_and_parse(search)
|
64
64
|
end
|
65
65
|
|
66
|
-
while obj['error_code'] == TOO_MANY_REQUESTS_ERROR
|
66
|
+
while obj['error_code'] == TOO_MANY_REQUESTS_ERROR || obj.has_key?('rate_limit')
|
67
67
|
sleep(@wait)
|
68
68
|
obj = request_and_parse(search)
|
69
69
|
end
|
@@ -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
|
28
|
+
rescue StandardError
|
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
|