transmission-rss 0.1.26 → 0.2.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/README.md +20 -1
- data/bin/transmission-add-file +2 -15
- data/bin/transmission-rss +23 -24
- data/lib/transmission-rss.rb +2 -0
- data/lib/transmission-rss/aggregator.rb +45 -63
- data/lib/transmission-rss/callback.rb +1 -1
- data/lib/transmission-rss/client.rb +19 -11
- data/lib/transmission-rss/config.rb +69 -5
- data/lib/transmission-rss/core_ext/object.rb +5 -0
- data/lib/transmission-rss/feed.rb +44 -2
- data/lib/transmission-rss/seen_file.rb +81 -0
- data/lib/transmission-rss/version.rb +1 -1
- data/transmission-rss.conf.example +11 -0
- metadata +25 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d767ee42ccfae63d8fe0dcbd4646c2b76e92afd
|
4
|
+
data.tar.gz: aad93524c03fc281512d4279e5cce49877f6e41e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3d62fe42f81e4c3fe76254411e7cba5ed5ffa305f06116326449348b26ff8857f645ade5479aacd90908995438829ccb5ca0cae8b4f5cb429ebff4eb7f6c51d
|
7
|
+
data.tar.gz: a2326919928da2f78a315f0bc022f32236fe2c44420b28f1e7c6333e8cfb55488537efcc1994ade7ae852b3392bec7a088bc53bba92663418b4595f717719549
|
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
transmission-rss
|
2
2
|
================
|
3
3
|
|
4
|
+
[](http://badge.fury.io/rb/transmission-rss)
|
5
|
+
[](https://travis-ci.org/nning/transmission-rss)
|
6
|
+
[](https://coveralls.io/r/nning/transmission-rss)
|
7
|
+
[](https://codeclimate.com/github/nning/transmission-rss)
|
8
|
+
|
4
9
|
transmission-rss is basically a workaround for transmission's lack of the
|
5
10
|
ability to monitor RSS feeds and automatically add enclosed torrent links.
|
6
11
|
|
@@ -27,6 +32,7 @@ Installation
|
|
27
32
|
|
28
33
|
git clone https://github.com/nning/transmission-rss
|
29
34
|
cd transmission-rss
|
35
|
+
bundle
|
30
36
|
gem build transmission-rss.gemspec
|
31
37
|
gem install transmission-rss-*.gem
|
32
38
|
|
@@ -57,7 +63,7 @@ Feed item titles can be filtered by a regular expression:
|
|
57
63
|
Feeds can also be configured to download files to specific directory:
|
58
64
|
|
59
65
|
feeds:
|
60
|
-
- url: http://example.com/feed1
|
66
|
+
- url: http://example.com/feed1
|
61
67
|
download_path: /home/user/Downloads
|
62
68
|
|
63
69
|
### All available options
|
@@ -78,6 +84,16 @@ transmission is configured for HTTP basic authentication.
|
|
78
84
|
regexp: (match1|match2)
|
79
85
|
- url: http://example.com/feed4
|
80
86
|
download_path: /home/user/Downloads
|
87
|
+
- url: http://example.com/feed4
|
88
|
+
regexp:
|
89
|
+
- match1
|
90
|
+
- match2
|
91
|
+
- url: http://example.com/feed5
|
92
|
+
regexp:
|
93
|
+
- matcher: match1
|
94
|
+
download_path: /home/user/match1
|
95
|
+
- matcher: match2
|
96
|
+
download_path: /home/user/match2
|
81
97
|
|
82
98
|
update_interval: 600
|
83
99
|
|
@@ -86,6 +102,7 @@ transmission is configured for HTTP basic authentication.
|
|
86
102
|
server:
|
87
103
|
host: localhost
|
88
104
|
port: 9091
|
105
|
+
rpc_path: /transmission/rpc
|
89
106
|
|
90
107
|
login:
|
91
108
|
username: transmission
|
@@ -101,6 +118,8 @@ transmission is configured for HTTP basic authentication.
|
|
101
118
|
|
102
119
|
pid_file: false
|
103
120
|
|
121
|
+
seen_file: ~/.config/transmission/seen
|
122
|
+
|
104
123
|
Daemonized Startup
|
105
124
|
------------------
|
106
125
|
|
data/bin/transmission-add-file
CHANGED
@@ -44,24 +44,11 @@ usage_message(config_file) if ARGV.empty?
|
|
44
44
|
# Otherwise Config is somehow mixed up with RbConfig.
|
45
45
|
config = TransmissionRSS::Config.instance
|
46
46
|
|
47
|
-
# Default configuration.
|
48
|
-
config.load({
|
49
|
-
'feeds' => [],
|
50
|
-
'update_interval' => 600,
|
51
|
-
'add_paused' => false,
|
52
|
-
'server' => {
|
53
|
-
'host' => 'localhost',
|
54
|
-
'port' => 9091
|
55
|
-
},
|
56
|
-
'log_target' => $stderr,
|
57
|
-
'privileges' => {}
|
58
|
-
})
|
59
|
-
|
60
47
|
# Initialize a log instance and configure it.
|
61
48
|
log = Log.instance
|
62
49
|
log.target = config.log_target
|
63
50
|
log.level = Logger::DEBUG
|
64
|
-
log.formatter = proc do |sev, time,
|
51
|
+
log.formatter = proc do |sev, time, _, msg|
|
65
52
|
"#{time.to_i}(#{sev.downcase}) #{msg}\n"
|
66
53
|
end
|
67
54
|
|
@@ -75,7 +62,7 @@ end
|
|
75
62
|
log.debug(config)
|
76
63
|
|
77
64
|
# Initialize communication to transmission.
|
78
|
-
client = Client.new(config.server
|
65
|
+
client = Client.new(config.server, config.login)
|
79
66
|
|
80
67
|
ARGV.each do |torrent_file|
|
81
68
|
client.add_torrent(torrent_file, :file, paused: config.add_paused)
|
data/bin/transmission-rss
CHANGED
@@ -18,6 +18,9 @@ dofork = false
|
|
18
18
|
# No PID file by default.
|
19
19
|
pid_file = false
|
20
20
|
|
21
|
+
# Seen file not reset by default.
|
22
|
+
reset_seen_file = false
|
23
|
+
|
21
24
|
# Shows a summary of the command line options.
|
22
25
|
def usage_message(config_file)
|
23
26
|
$stderr << "#{File.basename $0} [options]
|
@@ -27,6 +30,7 @@ Adds torrents from rss feeds to transmission web frontend.
|
|
27
30
|
-f Fork into background after startup.
|
28
31
|
-h This help.
|
29
32
|
-p <file> Write PID to file.
|
33
|
+
-r Reset seenfile on startup.
|
30
34
|
-v Show program version and exit.
|
31
35
|
|
32
36
|
"
|
@@ -39,6 +43,7 @@ options = GetoptLong.new \
|
|
39
43
|
['-f', GetoptLong::NO_ARGUMENT],
|
40
44
|
['-h', GetoptLong::NO_ARGUMENT],
|
41
45
|
['-p', GetoptLong::REQUIRED_ARGUMENT],
|
46
|
+
['-r', GetoptLong::NO_ARGUMENT],
|
42
47
|
['-v', GetoptLong::NO_ARGUMENT]
|
43
48
|
|
44
49
|
# Parse given options.
|
@@ -53,39 +58,23 @@ options.each do |option, argument|
|
|
53
58
|
usage_message(config_file)
|
54
59
|
when '-p'
|
55
60
|
pid_file = argument
|
61
|
+
when '-r'
|
62
|
+
reset_seen_file = true
|
56
63
|
when '-v'
|
57
64
|
puts TransmissionRSS::VERSION
|
58
65
|
exit
|
59
66
|
end
|
60
67
|
end
|
61
68
|
|
62
|
-
#
|
69
|
+
# Module prefix seems to be necessary when called from gem installation.
|
63
70
|
# Otherwise Config is somehow mixed up with RbConfig.
|
64
71
|
config = TransmissionRSS::Config.instance
|
65
72
|
|
66
|
-
# Default configuration.
|
67
|
-
defaults = {
|
68
|
-
'feeds' => [],
|
69
|
-
'update_interval' => 600,
|
70
|
-
'add_paused' => false,
|
71
|
-
'server' => {
|
72
|
-
'host' => 'localhost',
|
73
|
-
'port' => 9091
|
74
|
-
},
|
75
|
-
'login' => nil,
|
76
|
-
'log_target' => $stderr,
|
77
|
-
'fork' => false,
|
78
|
-
'pid_file' => false,
|
79
|
-
'privileges' => {},
|
80
|
-
'seen_file' => nil
|
81
|
-
}
|
82
|
-
config.load(defaults)
|
83
|
-
|
84
73
|
# Initialize a log instance and configure it.
|
85
74
|
log = Log.instance
|
86
75
|
log.target = config.log_target
|
87
76
|
log.level = Logger::DEBUG
|
88
|
-
log.formatter = proc do |sev, time,
|
77
|
+
log.formatter = proc do |sev, time, _, msg|
|
89
78
|
"#{time.to_i}(#{sev.downcase}) #{msg}\n"
|
90
79
|
end
|
91
80
|
|
@@ -103,7 +92,7 @@ if !(custom_config || ENV['HOME'].nil?)
|
|
103
92
|
prefix = ENV['XDG_CONFIG_HOME'] || File.expand_path('~/.config')
|
104
93
|
path = File.join(prefix, 'transmission-rss', 'config.yml')
|
105
94
|
|
106
|
-
if File.
|
95
|
+
if File.exist?(path)
|
107
96
|
log.debug('loading user config ' + path)
|
108
97
|
config.load(path)
|
109
98
|
end
|
@@ -146,12 +135,22 @@ end
|
|
146
135
|
# Initialize feed aggregator.
|
147
136
|
aggregator = Aggregator.new(config.feeds, seen_file: config.seen_file)
|
148
137
|
|
138
|
+
if reset_seen_file
|
139
|
+
aggregator.seen.clear!
|
140
|
+
log.debug('seenfile reset')
|
141
|
+
end
|
142
|
+
|
149
143
|
# Initialize communication to transmission.
|
150
|
-
client = Client.new(config.server
|
144
|
+
client = Client.new(config.server, config.login)
|
151
145
|
|
152
146
|
# Callback for a new item on one of the feeds.
|
153
|
-
aggregator.on_new_item do |torrent_file, feed|
|
154
|
-
client.add_torrent(torrent_file, :url, paused: config.add_paused, download_path:
|
147
|
+
aggregator.on_new_item do |torrent_file, feed, download_path|
|
148
|
+
client.add_torrent(torrent_file, :url, paused: config.add_paused, download_path: download_path)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Callback for changes to the config.
|
152
|
+
config.on_change do
|
153
|
+
aggregator.reinitialize!(config.feeds, seen_file: config.seen_file)
|
155
154
|
end
|
156
155
|
|
157
156
|
# Start the aggregation process.
|
data/lib/transmission-rss.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'etc'
|
2
|
-
require 'fileutils'
|
3
1
|
require 'open-uri'
|
4
2
|
require 'open_uri_redirections'
|
5
3
|
require 'rss'
|
@@ -14,35 +12,24 @@ module TransmissionRSS
|
|
14
12
|
extend Callback
|
15
13
|
callback(:on_new_item) # Declare callback for new items.
|
16
14
|
|
15
|
+
attr_reader :seen
|
16
|
+
|
17
17
|
def initialize(feeds = [], options = {})
|
18
|
+
reinitialize!(feeds, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def reinitialize!(feeds = [], options = {})
|
18
22
|
seen_file = options[:seen_file]
|
19
23
|
|
20
24
|
# Prepare Array of feeds URLs.
|
21
25
|
@feeds = feeds.map { |config| TransmissionRSS::Feed.new(config) }
|
22
26
|
|
23
27
|
# Nothing seen, yet.
|
24
|
-
@seen =
|
28
|
+
@seen = SeenFile.new(nil, seen_file)
|
25
29
|
|
26
30
|
# Initialize log instance.
|
27
31
|
@log = Log.instance
|
28
32
|
|
29
|
-
# Generate path for seen torrents store file.
|
30
|
-
@seenfile = seen_file || File.join(Etc.getpwuid.dir,
|
31
|
-
'/.config/transmission/seen-torrents.conf')
|
32
|
-
|
33
|
-
# Make directories in path if they are not existing.
|
34
|
-
FileUtils.mkdir_p(File.dirname(@seenfile))
|
35
|
-
|
36
|
-
# Touch seen torrents store file.
|
37
|
-
unless File.exists?(@seenfile)
|
38
|
-
FileUtils.touch(@seenfile)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Open file, read torrent URLs and add to +@seen+.
|
42
|
-
open(@seenfile).readlines.each do |line|
|
43
|
-
@seen.push(line.chomp)
|
44
|
-
end
|
45
|
-
|
46
33
|
# Log number of +@seen+ URIs.
|
47
34
|
@log.debug(@seen.size.to_s + ' uris from seenfile')
|
48
35
|
end
|
@@ -52,13 +39,13 @@ module TransmissionRSS
|
|
52
39
|
def run(interval = 600)
|
53
40
|
@log.debug('aggregator start')
|
54
41
|
|
55
|
-
|
42
|
+
loop do
|
56
43
|
@feeds.each do |feed|
|
57
44
|
@log.debug('aggregate ' + feed.url)
|
58
45
|
|
59
46
|
begin
|
60
47
|
content = open(feed.url, allow_redirections: :safe).read
|
61
|
-
rescue
|
48
|
+
rescue StandardError => e
|
62
49
|
@log.debug("retrieval error (#{e.class}: #{e.message})")
|
63
50
|
next
|
64
51
|
end
|
@@ -68,38 +55,14 @@ module TransmissionRSS
|
|
68
55
|
content = decompress(content) if RUBY_VERSION == '1.9.3'
|
69
56
|
begin
|
70
57
|
items = RSS::Parser.parse(content, false).items
|
71
|
-
rescue
|
58
|
+
rescue StandardError => e
|
72
59
|
@log.debug("parse error (#{e.class}: #{e.message})")
|
73
60
|
next
|
74
61
|
end
|
75
62
|
|
76
63
|
items.each do |item|
|
77
|
-
|
78
|
-
|
79
|
-
# Item contains no link.
|
80
|
-
next if link.nil?
|
81
|
-
|
82
|
-
# Link is not a String directly.
|
83
|
-
link = link.href if link.class != String
|
84
|
-
|
85
|
-
# The link is not in +@seen+ Array.
|
86
|
-
unless seen?(link)
|
87
|
-
# Skip if filter defined and not matching.
|
88
|
-
unless feed.matches_regexp?(item.title)
|
89
|
-
add_seen(link)
|
90
|
-
next
|
91
|
-
end
|
92
|
-
|
93
|
-
@log.debug('on_new_item event ' + link)
|
94
|
-
|
95
|
-
begin
|
96
|
-
on_new_item(link, feed)
|
97
|
-
rescue Client::Unauthorized, Errno::ECONNREFUSED, Timeout::Error
|
98
|
-
# Do not add to seen file.
|
99
|
-
else
|
100
|
-
add_seen(link)
|
101
|
-
end
|
102
|
-
end
|
64
|
+
result = process_link(feed, item)
|
65
|
+
next if result.nil?
|
103
66
|
end
|
104
67
|
end
|
105
68
|
|
@@ -107,20 +70,6 @@ module TransmissionRSS
|
|
107
70
|
end
|
108
71
|
end
|
109
72
|
|
110
|
-
# To add a link into the list of seen links.
|
111
|
-
def add_seen(link)
|
112
|
-
@seen.push(link)
|
113
|
-
|
114
|
-
File.open(@seenfile, 'w') do |file|
|
115
|
-
file.write(@seen.join("\n"))
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# To test if a link is in the list of seen links.
|
120
|
-
def seen?(link)
|
121
|
-
@seen.include?(link)
|
122
|
-
end
|
123
|
-
|
124
73
|
private
|
125
74
|
|
126
75
|
def decompress(string)
|
@@ -128,5 +77,38 @@ module TransmissionRSS
|
|
128
77
|
rescue Zlib::GzipFile::Error, Zlib::Error
|
129
78
|
string
|
130
79
|
end
|
80
|
+
|
81
|
+
def process_link(feed, item)
|
82
|
+
link = item.enclosure.url rescue item.link
|
83
|
+
|
84
|
+
# Item contains no link.
|
85
|
+
return if link.nil?
|
86
|
+
|
87
|
+
# Link is not a String directly.
|
88
|
+
link = link.href if link.class != String
|
89
|
+
|
90
|
+
# The link is not in +@seen+ Array.
|
91
|
+
unless @seen.include?(link)
|
92
|
+
# Skip if filter defined and not matching.
|
93
|
+
unless feed.matches_regexp?(item.title)
|
94
|
+
@seen.add(link)
|
95
|
+
return
|
96
|
+
end
|
97
|
+
|
98
|
+
@log.debug('on_new_item event ' + link)
|
99
|
+
|
100
|
+
download_path = feed.download_path(item.title)
|
101
|
+
|
102
|
+
begin
|
103
|
+
on_new_item(link, feed, download_path)
|
104
|
+
rescue Client::Unauthorized, Errno::ECONNREFUSED, Timeout::Error
|
105
|
+
@log.debug('not added to seen file ' + link)
|
106
|
+
else
|
107
|
+
@seen.add(link)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
return link
|
112
|
+
end
|
131
113
|
end
|
132
114
|
end
|
@@ -10,10 +10,14 @@ module TransmissionRSS
|
|
10
10
|
class Unauthorized < StandardError
|
11
11
|
end
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@host
|
15
|
-
@
|
16
|
-
@
|
13
|
+
def initialize(server = {}, login = nil, options = {})
|
14
|
+
@host = server[:host] || 'localhost'
|
15
|
+
@port = server[:port] || 9091
|
16
|
+
@rpc_path = server[:rpc_path] || '/transmission/rpc'
|
17
|
+
@login = login
|
18
|
+
|
19
|
+
@timeout = options[:timeout] || 5
|
20
|
+
@log = TransmissionRSS::Log.instance
|
17
21
|
end
|
18
22
|
|
19
23
|
# POST json packed torrent add command.
|
@@ -39,8 +43,8 @@ module TransmissionRSS
|
|
39
43
|
raise Unauthorized unless sid
|
40
44
|
|
41
45
|
post = Net::HTTP::Post.new \
|
42
|
-
|
43
|
-
|
46
|
+
@rpc_path,
|
47
|
+
{
|
44
48
|
'Content-Type' => 'application/json',
|
45
49
|
'X-Transmission-Session-Id' => sid
|
46
50
|
}
|
@@ -58,7 +62,7 @@ module TransmissionRSS
|
|
58
62
|
|
59
63
|
# Get transmission session id.
|
60
64
|
def get_session_id
|
61
|
-
get = Net::HTTP::Get.new(
|
65
|
+
get = Net::HTTP::Get.new(@rpc_path)
|
62
66
|
|
63
67
|
auth(get)
|
64
68
|
|
@@ -89,16 +93,14 @@ module TransmissionRSS
|
|
89
93
|
|
90
94
|
Timeout.timeout(@timeout) do
|
91
95
|
@log.debug("request #@host:#@port")
|
92
|
-
|
93
|
-
http.request(data)
|
94
|
-
end
|
96
|
+
http_get(data)
|
95
97
|
end
|
96
98
|
rescue Errno::ECONNREFUSED
|
97
99
|
@log.debug('connection refused')
|
98
100
|
raise
|
99
101
|
rescue Timeout::Error
|
100
102
|
s = 'connection timeout'
|
101
|
-
s
|
103
|
+
s << " (retry #{c})" if c > 0
|
102
104
|
@log.debug(s)
|
103
105
|
|
104
106
|
c += 1
|
@@ -106,5 +108,11 @@ module TransmissionRSS
|
|
106
108
|
|
107
109
|
raise
|
108
110
|
end
|
111
|
+
|
112
|
+
def http_get(data)
|
113
|
+
Net::HTTP.new(@host, @port).start do |http|
|
114
|
+
http.request(data)
|
115
|
+
end
|
116
|
+
end
|
109
117
|
end
|
110
118
|
end
|
@@ -1,27 +1,91 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'rb-inotify' if linux?
|
1
3
|
require 'singleton'
|
2
4
|
require 'yaml'
|
3
5
|
|
6
|
+
libdir = File.dirname(__FILE__)
|
7
|
+
require File.join(libdir, 'log')
|
8
|
+
require File.join(libdir, 'callback')
|
9
|
+
|
4
10
|
module TransmissionRSS
|
5
11
|
# Class handles configuration parameters.
|
6
12
|
class Config < Hash
|
7
13
|
# This is a singleton class.
|
8
14
|
include Singleton
|
9
15
|
|
16
|
+
extend Callback
|
17
|
+
callback(:on_change) # Declare callback for changed config.
|
18
|
+
|
19
|
+
def initialize(file = nil)
|
20
|
+
self.merge_defaults!
|
21
|
+
self.load(file) unless file.nil?
|
22
|
+
|
23
|
+
@log = Log.instance
|
24
|
+
end
|
25
|
+
|
10
26
|
# Merges a Hash or YAML file (containing a Hash) with itself.
|
11
27
|
def load(config)
|
12
28
|
case config.class.to_s
|
13
29
|
when 'Hash'
|
14
|
-
self.merge!
|
30
|
+
self.merge!(config)
|
15
31
|
when 'String'
|
16
|
-
self.merge_yaml!
|
32
|
+
self.merge_yaml!(config)
|
17
33
|
else
|
18
|
-
raise ArgumentError.new
|
34
|
+
raise ArgumentError.new('Could not load config.')
|
19
35
|
end
|
20
36
|
end
|
21
37
|
|
38
|
+
def merge_defaults!
|
39
|
+
self.merge!({
|
40
|
+
'feeds' => [],
|
41
|
+
'update_interval' => 600,
|
42
|
+
'add_paused' => false,
|
43
|
+
'server' => {
|
44
|
+
'host' => 'localhost',
|
45
|
+
'port' => 9091,
|
46
|
+
'rpc_path' => '/transmission/rpc'
|
47
|
+
},
|
48
|
+
'login' => nil,
|
49
|
+
'log_target' => $stderr,
|
50
|
+
'fork' => false,
|
51
|
+
'pid_file' => false,
|
52
|
+
'privileges' => {},
|
53
|
+
'seen_file' => nil
|
54
|
+
})
|
55
|
+
end
|
56
|
+
|
22
57
|
# Merge Config Hash with Hash from YAML file.
|
23
|
-
def merge_yaml!(path)
|
24
|
-
self.merge!
|
58
|
+
def merge_yaml!(path, watch = true)
|
59
|
+
self.merge!(YAML.load_file(path))
|
60
|
+
rescue TypeError
|
61
|
+
# If YAML loading fails, .load_file returns `false`.
|
62
|
+
else
|
63
|
+
watch_file(path) if watch && linux?
|
64
|
+
end
|
65
|
+
|
66
|
+
def reset!
|
67
|
+
self.clear
|
68
|
+
self.merge_defaults!
|
69
|
+
end
|
70
|
+
|
71
|
+
def watch_file(path)
|
72
|
+
path = Pathname.new(path).realpath.to_s
|
73
|
+
@log.debug('watch_file ' + path)
|
74
|
+
|
75
|
+
@notifier ||= INotify::Notifier.new
|
76
|
+
@notifier.watch(path, :close_write) do |e|
|
77
|
+
self.reset!
|
78
|
+
self.merge_yaml!(path, false)
|
79
|
+
|
80
|
+
@log.debug('reloaded config file ' + path)
|
81
|
+
@log.debug(self)
|
82
|
+
|
83
|
+
on_change
|
84
|
+
end
|
85
|
+
|
86
|
+
@notifier_thread ||= Thread.start do
|
87
|
+
@notifier.run
|
88
|
+
end
|
25
89
|
end
|
26
90
|
end
|
27
91
|
end
|
@@ -1,20 +1,62 @@
|
|
1
1
|
module TransmissionRSS
|
2
2
|
class Feed
|
3
|
-
attr_reader :url, :regexp
|
3
|
+
attr_reader :url, :regexp
|
4
4
|
|
5
5
|
def initialize(config = {})
|
6
|
+
@download_paths = {}
|
7
|
+
|
6
8
|
case config
|
7
9
|
when Hash
|
8
10
|
@url = URI.encode(config['url'] || config.keys.first)
|
9
|
-
|
11
|
+
|
10
12
|
@download_path = config['download_path']
|
13
|
+
|
14
|
+
matchers = Array(config['regexp']).map do |e|
|
15
|
+
e.is_a?(String) ? e : e['matcher']
|
16
|
+
end
|
17
|
+
|
18
|
+
@regexp = build_regexp(matchers)
|
19
|
+
|
20
|
+
initialize_download_paths(config['regexp'])
|
11
21
|
else
|
12
22
|
@url = config.to_s
|
13
23
|
end
|
14
24
|
end
|
15
25
|
|
26
|
+
def download_path(title = nil)
|
27
|
+
return @download_path if title.nil?
|
28
|
+
|
29
|
+
@download_paths.each do |regexp, path|
|
30
|
+
return path if title =~ to_regexp(regexp)
|
31
|
+
end
|
32
|
+
|
33
|
+
return @download_path
|
34
|
+
end
|
35
|
+
|
16
36
|
def matches_regexp?(title)
|
17
37
|
@regexp.nil? || !(title =~ @regexp).nil?
|
18
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def build_regexp(matchers)
|
43
|
+
matchers = Array(matchers).map { |m| to_regexp(m) }
|
44
|
+
matchers.empty? ? nil : Regexp.union(matchers)
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize_download_paths(regexps)
|
48
|
+
return unless regexps.is_a?(Array)
|
49
|
+
|
50
|
+
regexps.each do |regexp|
|
51
|
+
matcher = regexp['matcher']
|
52
|
+
path = regexp['download_path']
|
53
|
+
|
54
|
+
@download_paths[matcher] = path if matcher && path
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_regexp(s)
|
59
|
+
Regexp.new(s, Regexp::IGNORECASE)
|
60
|
+
end
|
19
61
|
end
|
20
62
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'etc'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
module TransmissionRSS
|
7
|
+
# Persist seen torrent URLs
|
8
|
+
class SeenFile
|
9
|
+
extend ::Forwardable
|
10
|
+
|
11
|
+
DEFAULT_LEGACY_PATH =
|
12
|
+
File.join(Etc.getpwuid.dir, '.config/transmission/seen-torrents.conf')
|
13
|
+
|
14
|
+
DEFAULT_PATH =
|
15
|
+
File.join(Etc.getpwuid.dir, '.config/transmission/seen')
|
16
|
+
|
17
|
+
def_delegators :@seen, :size, :to_a
|
18
|
+
|
19
|
+
def initialize(path = nil, legacy_path = nil)
|
20
|
+
@legacy_path = legacy_path || DEFAULT_LEGACY_PATH
|
21
|
+
@path = path || DEFAULT_PATH
|
22
|
+
|
23
|
+
initialize_path!
|
24
|
+
migrate!
|
25
|
+
|
26
|
+
@seen = Set.new(file_to_array(@path))
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(url)
|
30
|
+
hash = digest(url)
|
31
|
+
|
32
|
+
return if @seen.include?(hash)
|
33
|
+
|
34
|
+
@seen << hash
|
35
|
+
|
36
|
+
open(@path, 'a') do |f|
|
37
|
+
f.write(hash + "\n")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear!
|
42
|
+
@seen.clear
|
43
|
+
open(@path, 'w') {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def include?(url)
|
47
|
+
@seen.include?(digest(url))
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def digest(s)
|
53
|
+
Digest::SHA256.hexdigest(s)
|
54
|
+
end
|
55
|
+
|
56
|
+
def file_to_array(path)
|
57
|
+
open(path, 'r').readlines.map(&:chomp)
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize_path!
|
61
|
+
return if File.exist?(@path)
|
62
|
+
|
63
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
64
|
+
FileUtils.touch(@path)
|
65
|
+
end
|
66
|
+
|
67
|
+
def migrate!
|
68
|
+
return unless File.exist?(@legacy_path)
|
69
|
+
|
70
|
+
legacy_seen = file_to_array(@legacy_path)
|
71
|
+
hashes = legacy_seen.map { |url| digest(url) }
|
72
|
+
|
73
|
+
open(@path, 'w') do |f|
|
74
|
+
f.write(hashes.join("\n"))
|
75
|
+
f.write("\n")
|
76
|
+
end
|
77
|
+
|
78
|
+
FileUtils.rm_f(@legacy_path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -14,6 +14,16 @@ feeds:
|
|
14
14
|
regexp: (match1|match2)
|
15
15
|
- url: http://example.com/feed4
|
16
16
|
download_path: /home/user/Downloads
|
17
|
+
- url: http://example.com/feed4
|
18
|
+
regexp:
|
19
|
+
- match1
|
20
|
+
- match2
|
21
|
+
- url: http://example.com/feed5
|
22
|
+
regexp:
|
23
|
+
- matcher: match1
|
24
|
+
download_path: /home/user/match1
|
25
|
+
- matcher: match2
|
26
|
+
download_path: /home/user/match2
|
17
27
|
|
18
28
|
# Feed probing interval in seconds. Default is 600.
|
19
29
|
|
@@ -28,6 +38,7 @@ feeds:
|
|
28
38
|
#server:
|
29
39
|
# host: localhost
|
30
40
|
# port: 9091
|
41
|
+
# rpc_path: /transmission/rpc
|
31
42
|
|
32
43
|
# Uncomment if transmission server requires login.
|
33
44
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: transmission-rss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- henning mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: open_uri_redirections
|
@@ -30,6 +30,26 @@ dependencies:
|
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 0.1.4
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rb-inotify
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.9'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.9.7
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.9'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.9.7
|
33
53
|
description: |-
|
34
54
|
transmission-rss is basically a workaround for
|
35
55
|
transmission's lack of the ability to monitor RSS feeds and
|
@@ -49,9 +69,11 @@ files:
|
|
49
69
|
- lib/transmission-rss/callback.rb
|
50
70
|
- lib/transmission-rss/client.rb
|
51
71
|
- lib/transmission-rss/config.rb
|
72
|
+
- lib/transmission-rss/core_ext/object.rb
|
52
73
|
- lib/transmission-rss/feed.rb
|
53
74
|
- lib/transmission-rss/hash.rb
|
54
75
|
- lib/transmission-rss/log.rb
|
76
|
+
- lib/transmission-rss/seen_file.rb
|
55
77
|
- lib/transmission-rss/version.rb
|
56
78
|
- transmission-rss.conf.example
|
57
79
|
homepage: https://rubygems.org/gems/transmission-rss
|
@@ -74,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
96
|
version: '0'
|
75
97
|
requirements: []
|
76
98
|
rubyforge_project:
|
77
|
-
rubygems_version: 2.
|
99
|
+
rubygems_version: 2.6.3
|
78
100
|
signing_key:
|
79
101
|
specification_version: 4
|
80
102
|
summary: Adds torrents from rss feeds to transmission web frontend.
|