readability_importer 0.1.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.
- data/README.md +38 -0
- data/bin/readability_importer +6 -0
- data/lib/readability_importer/command.rb +65 -0
- data/lib/readability_importer/event_machine_patch.rb +16 -0
- data/lib/readability_importer/importer.rb +67 -0
- data/lib/readability_importer/loader.rb +69 -0
- data/lib/readability_importer/version.rb +3 -0
- data/lib/readability_importer.rb +8 -0
- metadata +99 -0
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Readability Importer
|
2
|
+
====================
|
3
|
+
|
4
|
+
A simple command to import many URLs to [Readability](http://www.readability.com/).
|
5
|
+
|
6
|
+
Install
|
7
|
+
-------
|
8
|
+
|
9
|
+
This command is provided in RubyGems format.
|
10
|
+
Run next command to insall all dependencies and command.
|
11
|
+
|
12
|
+
gem install redability_importer
|
13
|
+
|
14
|
+
Usage
|
15
|
+
-----
|
16
|
+
|
17
|
+
To import [Instapaper](http://www.instapaper.com/) URL(s),
|
18
|
+
export Instapaper CSV, then run next command.
|
19
|
+
|
20
|
+
redability_importer import -e <Your Readability Email Address> --instapaper-csv <Path to CSV>
|
21
|
+
|
22
|
+
You can grab your Readability email address in [My Account](https://www.readability.com/account/email) page.
|
23
|
+
|
24
|
+
If you have a file includes a URL per line,
|
25
|
+
also you can use ``--urls`` option.
|
26
|
+
|
27
|
+
cat path_to_file | readability_importer import -e <Your Readability Email Address> --urls -
|
28
|
+
|
29
|
+
Or, just simply assign URLs.
|
30
|
+
|
31
|
+
readability_importer import -e <Your Readability Email Address> --urls http://a.com http://b.com
|
32
|
+
|
33
|
+
Note
|
34
|
+
----
|
35
|
+
|
36
|
+
This command is, of course, unofficial tool.
|
37
|
+
Please do *NOT* contact with Readability about this tool.
|
38
|
+
Also, use at your own risk.
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
module ReadabilityImporter
|
4
|
+
class Command < Thor
|
5
|
+
method_option(:email_address, {
|
6
|
+
:type => :string,
|
7
|
+
:aliases => "-e",
|
8
|
+
:required => true,
|
9
|
+
:desc => "Readability email address, like 'name-abc@inbox.readability.com'."
|
10
|
+
})
|
11
|
+
method_option(:from, {
|
12
|
+
:type => :string,
|
13
|
+
:aliases => "-f",
|
14
|
+
:desc => "Your email address."
|
15
|
+
})
|
16
|
+
method_option(:jobs, {
|
17
|
+
:type => :numeric,
|
18
|
+
:aliases => "-j",
|
19
|
+
:desc => "Number of jobs in parallel."
|
20
|
+
})
|
21
|
+
method_option(:verbose, {
|
22
|
+
:type => :boolean,
|
23
|
+
:aliases => "-v",
|
24
|
+
:desc => "Verbose mode"
|
25
|
+
})
|
26
|
+
Loader.loaders.each do |(name, klass)|
|
27
|
+
method_option(name, {
|
28
|
+
:type => klass.type,
|
29
|
+
:desc => klass.desc
|
30
|
+
})
|
31
|
+
end
|
32
|
+
desc "import", "Import URL to Redability."
|
33
|
+
def import
|
34
|
+
p options[:jobs]
|
35
|
+
importer = Importer.new(options[:email_address], {
|
36
|
+
:verbose => options[:verbose],
|
37
|
+
:from => options[:from],
|
38
|
+
:max_concurrency => options[:job],
|
39
|
+
|
40
|
+
:on_importing => proc do |urls|
|
41
|
+
puts "Importing #{urls.size} url(s)..."
|
42
|
+
end,
|
43
|
+
:on_imported => proc do |urls|
|
44
|
+
puts urls.map{|u| "Imported #{u}"}
|
45
|
+
end,
|
46
|
+
:on_failed => proc do |urls|
|
47
|
+
puts urls.map{|u| "Failed #{u}"}
|
48
|
+
end
|
49
|
+
})
|
50
|
+
Loader.loaders.each do |(name, klass)|
|
51
|
+
if options[name]
|
52
|
+
loader = klass.new(options[name])
|
53
|
+
urls = loader.load
|
54
|
+
puts "Start importing #{urls.size} url(s)."
|
55
|
+
importer.import(loader.load)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "version", "Print the version"
|
61
|
+
def version
|
62
|
+
puts VERSION
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
# Patch EventMachine::Protocol::SmtpClient to use HELO instead.
|
4
|
+
# Readability SMTP server doesn't understand EHLO.
|
5
|
+
# See lib/em/protocols/smtpclient.rb
|
6
|
+
module EventMachine
|
7
|
+
module Protocols
|
8
|
+
class SmtpClient
|
9
|
+
def receive_signon
|
10
|
+
return invoke_error unless @range == 2
|
11
|
+
send_data "HELO #{@args[:domain]}\r\n"
|
12
|
+
@responder = :receive_ehlo_response
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'resolv'
|
3
|
+
|
4
|
+
module ReadabilityImporter
|
5
|
+
class Importer
|
6
|
+
MAX_URLS_IN_MAIL = 20.freeze
|
7
|
+
|
8
|
+
def initialize(email_address, options = {})
|
9
|
+
@email_address = email_address
|
10
|
+
|
11
|
+
@from = options[:from] || "foo@bar"
|
12
|
+
@max_concurrency = options[:max_concurrency] || 4
|
13
|
+
@verbose = !!options[:verbose]
|
14
|
+
|
15
|
+
@on_importing = options[:on_importing]
|
16
|
+
@on_imported = options[:on_imported]
|
17
|
+
@on_failed = options[:on_failed]
|
18
|
+
end
|
19
|
+
|
20
|
+
def import(urls)
|
21
|
+
urls_set = []
|
22
|
+
urls.each_slice(MAX_URLS_IN_MAIL){|urls| urls_set << urls}
|
23
|
+
|
24
|
+
EventMachine.run do
|
25
|
+
iterator = EventMachine::Iterator.new(urls_set, @max_concurrency)
|
26
|
+
iterator.each(proc do |urls, iterator|
|
27
|
+
@on_importing.call(urls) if @on_importing
|
28
|
+
EventMachine::Protocols::SmtpClient.send({
|
29
|
+
:verbose => @verbose,
|
30
|
+
:domain => domain,
|
31
|
+
:host => host,
|
32
|
+
:from => @from,
|
33
|
+
:to => @email_address,
|
34
|
+
:header => {"From" => @from, "To" => @email_address},
|
35
|
+
:body => urls.join("\r\n")
|
36
|
+
}).tap do |job|
|
37
|
+
job.callback do
|
38
|
+
@on_imported.call(urls) if @on_imported
|
39
|
+
iterator.next
|
40
|
+
end
|
41
|
+
job.errback do
|
42
|
+
@on_failed.call(urls) if @on_failed
|
43
|
+
iterator.next
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end, proc do
|
47
|
+
EventMachine.stop
|
48
|
+
end)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def domain
|
55
|
+
@domain ||= $1 if /@([^@]+)$/ === @email_address
|
56
|
+
end
|
57
|
+
|
58
|
+
def host
|
59
|
+
@host ||= begin
|
60
|
+
resource = Resolv::DNS.new.getresource(domain, Resolv::DNS::Resource::IN::MX)
|
61
|
+
if Resolv::DNS::Resource::IN::MX === resource
|
62
|
+
resource.exchange.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module ReadabilityImporter
|
4
|
+
module Loader
|
5
|
+
def self.loaders
|
6
|
+
@loaders ||= constants.inject({}) do |loaders, klass_name|
|
7
|
+
if /Loader$/ === klass_name
|
8
|
+
klass = const_get(klass_name)
|
9
|
+
name = klass_name.to_s.tap do |s|
|
10
|
+
s.gsub!(/Loader$/, '')
|
11
|
+
s.gsub!(/([A-Z+])([A-Z][a-z])/, '\1_\2')
|
12
|
+
s.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
13
|
+
s.tr!("-", "_")
|
14
|
+
s.downcase!
|
15
|
+
end.to_sym
|
16
|
+
loaders[name] = klass
|
17
|
+
end
|
18
|
+
loaders
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Base
|
23
|
+
def self.desc
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.type
|
27
|
+
:string
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class UrlLoader < Base
|
32
|
+
def self.desc
|
33
|
+
"List of URLs."
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.type
|
37
|
+
:array
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(urls)
|
41
|
+
@urls = urls
|
42
|
+
end
|
43
|
+
|
44
|
+
def load
|
45
|
+
if @urls.empty?
|
46
|
+
STDIN.read.split(/\s+/)
|
47
|
+
else
|
48
|
+
@urls
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class InstapaperCsvLoader < Base
|
54
|
+
def self.desc
|
55
|
+
"Path to CSV file"
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize(path)
|
59
|
+
@path = path
|
60
|
+
end
|
61
|
+
|
62
|
+
def load
|
63
|
+
CSV.read(@path).map do |line|
|
64
|
+
line[0]
|
65
|
+
end.reverse
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'readability_importer/version'
|
2
|
+
require 'readability_importer/event_machine_patch'
|
3
|
+
|
4
|
+
module ReadabilityImporter
|
5
|
+
autoload :Importer, "readability_importer/importer"
|
6
|
+
autoload :Loader, "readability_importer/loader"
|
7
|
+
autoload :Command, "readability_importer/command"
|
8
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: readability_importer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Yoshimasa Niwa
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: eventmachine
|
16
|
+
requirement: &70180650792080 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0.beta.4
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70180650792080
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: thor
|
27
|
+
requirement: &70180650791560 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70180650791560
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: bundler
|
38
|
+
requirement: &70180650790860 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70180650790860
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &70180650790120 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70180650790120
|
58
|
+
description: Import many URLs to Readability.
|
59
|
+
email:
|
60
|
+
- niw@niw.at
|
61
|
+
executables:
|
62
|
+
- readability_importer
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files:
|
65
|
+
- README.md
|
66
|
+
files:
|
67
|
+
- bin/readability_importer
|
68
|
+
- lib/readability_importer.rb
|
69
|
+
- lib/readability_importer/command.rb
|
70
|
+
- lib/readability_importer/event_machine_patch.rb
|
71
|
+
- lib/readability_importer/importer.rb
|
72
|
+
- lib/readability_importer/loader.rb
|
73
|
+
- lib/readability_importer/version.rb
|
74
|
+
- README.md
|
75
|
+
homepage: https://github.com/niw/readability_importer
|
76
|
+
licenses: []
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.8.11
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: Import many URLs to Readability.
|
99
|
+
test_files: []
|