kemonomachi-wanko 0.4.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c386e5f07a045d367717650f2f6fe54e2223b080
4
+ data.tar.gz: 90358f22e960700a46024b874a129cee3f2bfe17
5
+ SHA512:
6
+ metadata.gz: 4f70b0fd6c5fff57da833b5599a756f1256e4f55a38bdb1b15637d203b541ec8f82a62df6379c8d73281de4260ade511f9aa29cfa6862d0830818a1748566029
7
+ data.tar.gz: 88b87e0b9b1f8f1b86d91cb78f879be33f52a56cdc62b7dbf34105c02f7dc3ad6bccc652270a7d528d0897dc79e2c71b50896e89385cdafb9586509e8489ae7e
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
14
+
data/README ADDED
@@ -0,0 +1,49 @@
1
+ Wanko - RSS Torrent Fetcher
2
+
3
+ Description
4
+ -----------
5
+
6
+ Fetches torrent file links from RSS feeds based on user-specified rules. The
7
+ links can be printed to stdout or sent to Transmission for download, or the
8
+ torrent files can be downloaded to a watchdir for processing by other torrent
9
+ clients.
10
+
11
+ Installation
12
+ ------------
13
+
14
+ wanko can be installed from RubyGems:
15
+
16
+ gem install kemonomachi+wanko
17
+
18
+ 'kemonomachi+' is used as a ghetto namespace, since RubyGems doesn't support
19
+ proper ones.
20
+
21
+ Usage
22
+ -----
23
+
24
+ wanko [options]
25
+
26
+ The optional -c switch specifies the configuration directory, which defaults to
27
+ $HOME/.wanko.
28
+
29
+ See 'wanko --help' for usage info.
30
+
31
+ Can be run manually, but creating a cron job to run it at regular intervals is
32
+ recommended. It is not a daemon.
33
+
34
+ Configuration
35
+ -------------
36
+
37
+ The config file is named 'config.yaml'. The example folder contains a full
38
+ example config. RSS feeds, default fetch directory, fetch method and rules can
39
+ be specified. Regexen are used to match torrents against rules.
40
+
41
+ The config directory also holds 'history.yaml', which contains already read RSS
42
+ items. This prevents fetching the same torrent multiple times.
43
+
44
+ About the Name
45
+ --------------
46
+
47
+ Wanko (わんこ) is Japanese for 'doggy'. Because it's small and it fetches
48
+ stuff for you.
49
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'wanko'
4
+
5
+ options = Wanko::parse_cli_switches ARGV
6
+
7
+ options[:command].call options
8
+
@@ -0,0 +1,7 @@
1
+ require 'wanko/data'
2
+ require 'wanko/exception'
3
+ require 'wanko/fetch'
4
+ require 'wanko/read'
5
+ require 'wanko/wanko'
6
+ require 'wanko/write'
7
+
@@ -0,0 +1,73 @@
1
+ require 'terminal-table'
2
+
3
+ require 'wanko/read'
4
+ require 'wanko/wanko'
5
+ require 'wanko/write'
6
+
7
+ module Wanko
8
+ module Command
9
+ def self.fetch(options)
10
+ config = begin
11
+ Wanko::Read.config options[:config_dir]
12
+ rescue Errno::ENOENT
13
+ abort 'Config file not found, aborting...'
14
+ end
15
+
16
+ [:feeds, :rules].each do |key|
17
+ warn 'WARN: No #{key} specified.' if config[key].empty?
18
+ end
19
+
20
+ history = Wanko::Read.history options[:config_dir]
21
+
22
+ torrents, new_history = Wanko::check_feeds config[:feeds], config[:rules], history
23
+
24
+ config[:fetcher].call torrents
25
+
26
+ Wanko::Write.history options[:config_dir], new_history
27
+ end
28
+
29
+ def self.add(options)
30
+ config = begin
31
+ Wanko::Read.raw_config options[:config_dir]
32
+ rescue Errno::ENOENT
33
+ abort 'Config file not found, aborting...'
34
+ end
35
+
36
+ id = if config[:rules].empty?
37
+ 0
38
+ else
39
+ config[:rules].map {|x| x[:id]}.max + 1
40
+ end
41
+
42
+ rule = [id: id, regex: options[:regex], dir: options[:dir] || config[:base_dir]]
43
+
44
+ new_config = config.merge rules: config[:rules] + rule
45
+
46
+ Wanko::Write.config options[:config_dir], Wanko::Utility.stringify_keys(new_config)
47
+ end
48
+
49
+ def self.list(options)
50
+ config = begin
51
+ Wanko::Read.raw_config options[:config_dir]
52
+ rescue Errno::ENOENT
53
+ abort 'Config file not found, aborting...'
54
+ end
55
+
56
+ puts Terminal::Table.new(rows: config[:rules].map {|r| [r[:id], r[:regex], r[:dir]]},
57
+ headings: ['ID', 'Regex', 'Directory']) {align_column 0, :right}
58
+ end
59
+
60
+ def self.remove(options)
61
+ config = begin
62
+ Wanko::Read.raw_config options[:config_dir]
63
+ rescue Errno::ENOENT
64
+ abort 'Config file not found, aborting...'
65
+ end
66
+
67
+ new_rules = config[:rules].reject {|rule| options[:ids].include? rule[:id]}
68
+
69
+ Wanko::Write.config options[:config_dir], Wanko::Utility.stringify_keys(config.merge rules: new_rules)
70
+ end
71
+ end
72
+ end
73
+
@@ -0,0 +1,70 @@
1
+ require 'json'
2
+
3
+ module Wanko
4
+
5
+ # Data representations.
6
+ module Data
7
+
8
+ # Class for torrent data. Contains the name of and the link to a torrent,
9
+ # as well as the directory the torrent will be downloaded to.
10
+ class Torrent
11
+ def initialize(name, link, dir)
12
+ @name = name
13
+ @link = link
14
+ @dir = dir
15
+ end
16
+
17
+ attr_reader :name, :link, :dir
18
+
19
+ # Returns a Hash representation of this Torrent
20
+ def to_h()
21
+ {
22
+ name: name,
23
+ link: link,
24
+ dir: dir
25
+ }
26
+ end
27
+
28
+ # Returns a JSON representation of this Torrent
29
+ def to_json(state = nil)
30
+ to_h.to_json state
31
+ end
32
+ end
33
+
34
+ # Rule for matching against RSS items. Contains a regex for matching and a
35
+ # directory to download matched torrents to.
36
+ class Rule
37
+
38
+ # Public: Initialize a Rule object.
39
+ #
40
+ #regex - String or Regexp for matching.
41
+ #dir - Directory to download matched torrents to.
42
+ def initialize(regex, dir)
43
+ @regex = Regexp.new regex, Regexp::IGNORECASE
44
+ @dir = dir
45
+ end
46
+
47
+ attr_reader :regex, :dir
48
+
49
+ # Public: Match the regex of this Rule against a String
50
+ #
51
+ # str - String to match.
52
+ #
53
+ # Returns true if str matches, false otherwise.
54
+ def =~(str)
55
+ regex =~ str
56
+ end
57
+
58
+ # Public: Compare this Rule to another object.
59
+ #
60
+ # other - Object to compare this Rule to.
61
+ #
62
+ # Returns true if the regex and dir fields of this Rule are equal to
63
+ # the corresponding fields in other, false otherwise.
64
+ def ==(other)
65
+ regex == other.regex && dir == other.dir
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,8 @@
1
+ module Wanko
2
+ class Error < StandardError
3
+ end
4
+
5
+ class ConfigError < Wanko::Error
6
+ end
7
+ end
8
+
@@ -0,0 +1,178 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'open-uri'
4
+ require 'yaml'
5
+
6
+ require 'wanko/exception'
7
+ require 'wanko/utility'
8
+
9
+ module Wanko
10
+
11
+ # Functions for fetching torrents in different ways. Several functions have
12
+ # side-effects, and ::fetcher_for returns Lambdas that have side-effects.
13
+ module Fetch
14
+
15
+ # Public: Create a lambda that fetches torrents when called.
16
+ #
17
+ # config - Hash with options for how to fetch torrents. :name is always
18
+ # required and should be one of 'stdout', 'watchdir' and
19
+ # 'transmission'. Other options vary depending on the value of
20
+ # :name, as follows:
21
+ #
22
+ # name: 'stdout'
23
+ # :format - Serialization format. See ::serialize for values.
24
+ # name: 'transmission'
25
+ # :host - The daemon's host
26
+ # :port - The daemon's port
27
+ # :path - The daemon's path.
28
+ # :user - Username for Basic auth (optional)
29
+ # :password - Password for Basic auth (optional)
30
+ #
31
+ # Returns a fetcher Lambda.
32
+ # Raises Wanko::ConfigError if :name option is not recognized.
33
+ def self.fetcher_for(config)
34
+ case config[:name]
35
+ when 'stdout'
36
+ ->(torrents) {$stdout.puts serialize(config[:format], torrents)}
37
+ when 'watchdir'
38
+ ->(torrents) {to_watchdir torrents}
39
+ when 'transmission'
40
+ url = make_transmission_url config[:host], config[:port], config[:path]
41
+
42
+ auth = if config[:user] || config[:password]
43
+ config.select {|k, _| [:user, :password].include? k}
44
+ end
45
+
46
+ ->(torrents) {to_transmission url, auth, torrents}
47
+ else
48
+ raise Wanko::ConfigError, "Invalid fetcher name '#{config[:name]}'"
49
+ end
50
+ end
51
+
52
+ # Internal: Serialize torrents for the stdout fetcher.
53
+ #
54
+ # format - Name of serialization method to use. Should be either 'json',
55
+ # 'simple' or 'yaml'.
56
+ # torrents - Torrents to serialize.
57
+ #
58
+ # Returns a String representation of torrents, in the given format.
59
+ # Raises Wanko::ConfigError if format is not recognized.
60
+ def self.serialize(format, torrents)
61
+ case format
62
+ when 'json'
63
+ torrents.to_json
64
+ when 'simple'
65
+ torrents.map(&:link).join "\n"
66
+ when 'yaml', nil
67
+ Utility.stringify_keys(torrents.map &:to_h).to_yaml
68
+ else
69
+ raise Wanko::ConfigError, "Invalid format '#{format}' for stdout fetcher."
70
+ end
71
+ end
72
+
73
+ # Internal: Download torrent files to watch directories. Creates any
74
+ # nonexisting directories.
75
+ #
76
+ # torrents - Torrents to download.
77
+ #
78
+ # Returns nothing.
79
+ def self.to_watchdir(torrents)
80
+ torrents.each do |tor|
81
+ FileUtils.mkdir_p tor.dir
82
+
83
+ open tor.link do |remote|
84
+ File.binwrite File.join(tor.dir, "#{tor.name}.torrent"), remote.read
85
+ end
86
+ end
87
+ end
88
+
89
+ # Internal: Send RPC commands to Transmission daemon for downloading
90
+ # torrents.
91
+ #
92
+ # url - URI object containing the URL of the daemon
93
+ # auth - Hash with :user and :password for Basic auth, or falsey value
94
+ # for no authorization.
95
+ # torrents - Array of torrents to download.
96
+ #
97
+ # Returns nothing.
98
+ def self.to_transmission(url, auth, torrents)
99
+ return if torrents.empty?
100
+
101
+ requests = torrents.map {|torrent| make_post_request url, make_transmission_rpc_command(torrent), auth}
102
+
103
+ Net::HTTP.start url.host, url.port do |transmission|
104
+ send_transmission_requests transmission, requests
105
+ end
106
+ end
107
+
108
+ # Internal: Send requests to a Transmission daemon. If a 409 Conflict
109
+ # response is recieved, update the 'X-transmission-Session-Id' header of
110
+ # the request and resend it.
111
+ #
112
+ # transmission - Net::HTTP connection to a Transmission daemon.
113
+ # reqs - Net::HTTP::Post requests to send to Transmission.
114
+ #
115
+ # Returns nothing.
116
+ def self.send_transmission_requests(transmission, reqs)
117
+ session_id = ''
118
+
119
+ reqs.each do |req|
120
+ req['X-Transmission-Session-Id'] = session_id
121
+
122
+ response = transmission.request req
123
+
124
+ case response
125
+ when Net::HTTPConflict
126
+ session_id = response['X-Transmission-Session-Id']
127
+ redo
128
+ end
129
+ end
130
+ end
131
+
132
+ # Internal: Create a URI object for a Transmission daemon URL.
133
+ #
134
+ # host - Host part of the URL.
135
+ # port - Port part of the URL.
136
+ # path - Path part of the URL.
137
+ #
138
+ # Returns a URI object.
139
+ def self.make_transmission_url(host, port, path)
140
+ URI::HTTP.build host: host || '127.0.0.1',
141
+ port: port || 9091,
142
+ path: "#{path || '/transmission/'}rpc"
143
+ end
144
+
145
+ # Internal: Generate a JSON-encoded 'torrent-add' RPC command that can be
146
+ # sent to a Transmission daemon.
147
+ #
148
+ # torrent - Torrent to generate command for.
149
+ #
150
+ # Returns a String RPC command.
151
+ def self.make_transmission_rpc_command(torrent)
152
+ JSON.generate(
153
+ {
154
+ "method" => "torrent-add",
155
+ "arguments" => {
156
+ "filename" => torrent.link,
157
+ "download-dir" => torrent.dir
158
+ }
159
+ }
160
+ )
161
+ end
162
+
163
+ # Internal: Create an HTTP POST request.
164
+ #
165
+ # url - URL this request will be sent to.
166
+ # body - String with body data.
167
+ # auth - Hash with Basic auth data, or falsey value for no authorization.
168
+ #
169
+ # Returns a Net::HTTP::Post request
170
+ def self.make_post_request(url, body, auth)
171
+ request = Net::HTTP::Post.new url
172
+ request.body = body
173
+ request.basic_auth auth[:user], auth[:password] if auth
174
+ request
175
+ end
176
+ end
177
+ end
178
+
@@ -0,0 +1,88 @@
1
+ require 'yaml'
2
+
3
+ require 'wanko/data'
4
+ require 'wanko/fetch'
5
+ require 'wanko/utility'
6
+
7
+ module Wanko
8
+
9
+ # Functions to read in data from files, urls and similar.
10
+ # All functions can be considered to rely on external state.
11
+ module Read
12
+
13
+ # Public: Read a config file, create a fetcher lambda and convert the
14
+ # rules.
15
+ #
16
+ # dir - Path to the directory containing the config file.
17
+ #
18
+ # Returns a Hash containing the configuration.
19
+ def self.config(dir)
20
+ config = raw_config dir
21
+
22
+ config.merge(
23
+ fetcher: Fetch.fetcher_for({name: 'stdout'}.merge Hash config[:fetcher]),
24
+ rules: Array(config[:rules]).map {|rule| convert_rule rule, config[:base_dir]}
25
+ )
26
+ end
27
+
28
+ # Public: Read a config file and supply some sensible defaults.
29
+ #
30
+ # dir - Path to the directory containing the config file.
31
+ #
32
+ # Returns a Hash containing the configuration.
33
+ def self.raw_config(dir)
34
+ config = Utility.symbolize_keys(YAML.load_file File.join(dir, 'config.yaml')) || {}
35
+
36
+ config.merge(
37
+ feeds: Array(config[:feeds]),
38
+ base_dir: config[:base_dir] || File.join(Dir.home, 'downloads'),
39
+ rules: Array(config[:rules])
40
+ )
41
+ end
42
+
43
+ # Internal: Convert a rule Hash into a Rule object, making the path
44
+ # absolute.
45
+ #
46
+ # rule - Rule to convert. Has a required :regex entry and an optional
47
+ # :dir entry.
48
+ # base_dir - Directory to use as base for relative paths, or as download
49
+ # dir if the rule has no :dir entry.
50
+ #
51
+ # Returns an Array of converted Rules.
52
+ def self.convert_rule(rule, base_dir)
53
+ Data::Rule.new rule[:regex], File.absolute_path(rule[:dir] || '', base_dir)
54
+ end
55
+
56
+ # Public: Read an RSS feed history file.
57
+ #
58
+ # dir - Path to the directory containing the history file.
59
+ #
60
+ # Returns a Hash containing the history, or an empty Hash if the file does
61
+ # not exist.
62
+ def self.history(dir)
63
+ history = begin
64
+ YAML.load_file File.join(dir, 'history.yaml')
65
+ rescue Errno::ENOENT
66
+ {}
67
+ end
68
+
69
+ # This is OK, since history is treated as read-only.
70
+ history.default = []
71
+
72
+ history
73
+ end
74
+
75
+ # Public: Read an RSS feed.
76
+ #
77
+ # url - Location of the feed. Must be openable by OpenURI.
78
+ #
79
+ # Returns an RSS::Rss object, or nil if the feed couldn't be read.
80
+ def self.feed(url)
81
+ begin
82
+ open(url, read_timeout: 10) {|rss| RSS::Parser.parse rss}
83
+ rescue OpenURI::HTTPError, Timeout::Error, Errno::ECONNREFUSED, SocketError => ex
84
+ warn "WARN: #{url} --> #{ex}"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,59 @@
1
+ module Wanko
2
+
3
+ # Various utility functions.
4
+ module Utility
5
+
6
+ # Public: Recursively convert all keys of a Hash using the supplied block.
7
+ # Array values are traversed and each entry is converted. If arg is any
8
+ # other kind of object it is left untouched. If the conversion results in
9
+ # duplicate keys, later keys will overwrite earlier ones.
10
+ #
11
+ # Convenience functions exist for some often used operations, see
12
+ # ::symbolize_keys and ::stringify_keys.
13
+ #
14
+ # arg - Object to convert.
15
+ # block - Block for converting the keys. Will be called with each key
16
+ # individually. Should return an object that can be used as a hash key.
17
+ #
18
+ # Examples
19
+ #
20
+ # Utility.convert_keys h, &:to_sym
21
+ #
22
+ # Utility.convert_keys(h) {|key| key.downcase.squeeze}
23
+ #
24
+ # Returns an object with converted hash keys.
25
+ def self.convert_keys(arg, &block)
26
+ case arg
27
+ when Hash
28
+ arg.each_with_object({}) { |(key, val), memo|
29
+ memo[yield(key)] = convert_keys(val, &block)
30
+ }
31
+ when Array
32
+ arg.map {|val| convert_keys val, &block}
33
+ else
34
+ arg
35
+ end
36
+ end
37
+
38
+ # Public: Recursively convert all keys in a Hash to Symbols. Does not check
39
+ # for duplicate keys. See ::convert_keys for more info.
40
+ #
41
+ # hash - Hash to convert. All keys must respond to #to_sym.
42
+ #
43
+ # Returns a structurally identical Hash with keys converted to Symbols.
44
+ def self.symbolize_keys(hash)
45
+ convert_keys hash, &:to_sym
46
+ end
47
+
48
+ # Public: Recursively convert all keys in a Hash to Strings. Does not check
49
+ # for duplicate keys. See ::convert_keys for more info.
50
+ #
51
+ # hash - Hash to convert. All keys must respond to #to_s.
52
+ #
53
+ # Returns a structurally identical Hash with keys converted to Strings.
54
+ def self.stringify_keys(hash)
55
+ convert_keys hash, &:to_s
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,111 @@
1
+ require 'optparse'
2
+ require 'rss'
3
+
4
+ require 'wanko/command'
5
+ require 'wanko/data'
6
+ require 'wanko/read'
7
+
8
+ module Wanko
9
+
10
+ # Public: Parse cli switches. Exits and prints a usage message when given
11
+ # -h, --help or an invalid switch.
12
+ #
13
+ # args - Array of switches to parse. Will not be altered.
14
+ #
15
+ # Returns a Hash containing the parsed options.
16
+ def self.parse_cli_switches(args)
17
+ options = {
18
+ command: Command.method(:fetch),
19
+ config_dir: File.join(Dir.home, ".wanko")
20
+ }
21
+
22
+ parser = OptionParser.new do |parser|
23
+ parser.banner = 'Usage: wanko [options]'
24
+
25
+ parser.separator ''
26
+ parser.separator 'Options:'
27
+
28
+ parser.on '-c DIR', '--config_dir', 'Use a different config directory' do |dir|
29
+ options[:config_dir] = File.absolute_path dir
30
+ end
31
+
32
+ parser.on '-a REGEX', '--add', 'Add a fetch rule' do |regex|
33
+ options[:command] = Command.method :add
34
+ options[:regex] = regex
35
+ end
36
+
37
+ parser.on '-d DIR', '--directory', 'Optional directory for fetch rules added with -a' do |dir|
38
+ options[:dir] = dir
39
+ end
40
+
41
+ parser.on '-l', '--list', 'List all rules' do
42
+ options[:command] = Command.method :list
43
+ end
44
+
45
+ parser.on '-r ID', '--remove', Integer, 'Remove a fetch rule' do |id|
46
+ options[:command] = Command.method :remove
47
+ options[:ids] = [id]
48
+ end
49
+
50
+ parser.separator ''
51
+ parser.separator 'Other:'
52
+
53
+ parser.on '-h', '--help',
54
+ 'Show this message' do
55
+ puts parser
56
+ exit
57
+ end
58
+ end
59
+
60
+ begin
61
+ parser.parse args
62
+ rescue OptionParser::InvalidOption
63
+ puts parser
64
+ exit
65
+ end
66
+
67
+ options
68
+ end
69
+
70
+ # Public: Check RSS feeds for new torrents matching a set of rules, excluding
71
+ # already read items.
72
+ #
73
+ # urls - Array of feed urls to check.
74
+ # rules - Array of Rules to match against.
75
+ # history - Hash with urls mapped to Arrays of already read items. Will be
76
+ # accessed using the urls in the urls parameter. If a url is
77
+ # missing, an empty Array will be substituted.
78
+ #
79
+ # Returns a pair [[Torrent], Hash] of matched items and an updated history
80
+ def self.check_feeds(urls, rules, history)
81
+ matches, new_history = urls.map { |url|
82
+ feed = Read.feed(url) or next [[], {url => history[url]}]
83
+
84
+ [
85
+ match(feed, rules, Array(history[url])),
86
+ {url => feed.items.map {|item| item.guid.content}}
87
+ ]
88
+ }.transpose
89
+
90
+ [matches.flatten, new_history.reduce(:merge)]
91
+ end
92
+
93
+ # Internal: Match a set of rules against the items of an RSS feed, excluding
94
+ # already read items.
95
+ #
96
+ # feed - RSS object which items to search through.
97
+ # rules - Array of Rules.
98
+ # history - Array of GUIDs. Items found in this array will be rejected.
99
+ #
100
+ # Returns an Array of Torrents representing the matched items.
101
+ def self.match(feed, rules, history)
102
+ feed.items
103
+ .reject {|item| history.include? item.guid.content}
104
+ .product(rules)
105
+ .select {|item, rule| rule =~ item.title}
106
+ .map { |item, rule|
107
+ Data::Torrent.new item.title, item.link, rule.dir
108
+ }
109
+ end
110
+ end
111
+
@@ -0,0 +1,27 @@
1
+ require 'yaml'
2
+
3
+ module Wanko
4
+
5
+ # Functions for writing data out to files or similar. All functions can be
6
+ # considered to have destructive side-effects.
7
+ module Write
8
+
9
+ # Public: Write a YAML representation of an object to a file named
10
+ # 'history.yaml'.
11
+ #
12
+ # This function _will_ clobber an existing file.
13
+ #
14
+ # dir - Path of the directory to write the file in.
15
+ # history - Object to write out. Responds to #to_yaml.
16
+ #
17
+ # Returns nothing
18
+ def self.history(dir, history)
19
+ File.write File.join(dir, 'history.yaml'), history.to_yaml
20
+ end
21
+
22
+ def self.config(dir, config)
23
+ File.write File.join(dir, 'config.yaml'), config.to_yaml
24
+ end
25
+ end
26
+ end
27
+
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kemonomachi-wanko
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.2
5
+ platform: ruby
6
+ authors:
7
+ - Ookami Kenrou
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: terminal-table
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ description: |2
28
+ Fetches torrent file links from RSS feeds based on user-specified rules. The
29
+ links can be printed to stdout or sent to Transmission for download, or the
30
+ torrent files can be downloaded to a watchdir for processing by other torrent
31
+ clients.
32
+ email: ookamikenrou@gmail.com
33
+ executables:
34
+ - wanko
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - LICENSE
39
+ - README
40
+ - bin/wanko
41
+ - lib/wanko.rb
42
+ - lib/wanko/command.rb
43
+ - lib/wanko/data.rb
44
+ - lib/wanko/exception.rb
45
+ - lib/wanko/fetch.rb
46
+ - lib/wanko/read.rb
47
+ - lib/wanko/utility.rb
48
+ - lib/wanko/wanko.rb
49
+ - lib/wanko/write.rb
50
+ homepage: https://github.com/kemonomachi/wanko
51
+ licenses:
52
+ - WTFPL
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.0.0
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements:
69
+ - Transmission bittorrent client (optional)
70
+ rubyforge_project:
71
+ rubygems_version: 2.5.1
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: RSS Torrent Fetcher
75
+ test_files: []