rubyhexagon 0.0.6 → 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/bin/e621 +23 -0
- data/lib/rubyhexagon.rb +1 -1
- metadata +3 -9
- data/bin/e621_search +0 -116
- data/bin/e621_server +0 -185
- data/bin/fav-sync +0 -82
- data/bin/set-sync +0 -261
data/bin/e621
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
=begin
|
3
|
+
Copyright 2014 Maxine Red <maxine_red1@yahoo.com>
|
4
|
+
|
5
|
+
This file is part of rubyhexahon.
|
6
|
+
|
7
|
+
rubyhexahon is free software: you can redistribute it and/or modify
|
8
|
+
it under the terms of the GNU General Public License as published by
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
10
|
+
(at your option) any later version.
|
11
|
+
|
12
|
+
rubyhexahon is distributed in the hope that it will be useful,
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
GNU General Public License for more details.
|
16
|
+
|
17
|
+
You should have received a copy of the GNU General Public License
|
18
|
+
along with rubyhexahon. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
=end
|
20
|
+
|
21
|
+
require "rubyhexahon"
|
22
|
+
|
23
|
+
require "examples/#{ARGV.shift}"
|
data/lib/rubyhexagon.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyhexagon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,10 +14,7 @@ dependencies: []
|
|
14
14
|
description: Rubyhexagon provides Ruby bindings for the e621 [dot] net API.
|
15
15
|
email: maxine_red1@yahoo.com
|
16
16
|
executables:
|
17
|
-
-
|
18
|
-
- set-sync
|
19
|
-
- e621_search
|
20
|
-
- e621_server
|
17
|
+
- e621
|
21
18
|
extensions: []
|
22
19
|
extra_rdoc_files: []
|
23
20
|
files:
|
@@ -36,10 +33,7 @@ files:
|
|
36
33
|
- lib/config.rb
|
37
34
|
- lib/api.rb
|
38
35
|
- lib/set.rb
|
39
|
-
- bin/
|
40
|
-
- bin/set-sync
|
41
|
-
- bin/e621_search
|
42
|
-
- bin/e621_server
|
36
|
+
- bin/e621
|
43
37
|
homepage: https://github.com/maxine-red/rubyhexagon
|
44
38
|
licenses:
|
45
39
|
- GPL-3.0
|
data/bin/e621_search
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
=begin
|
3
|
-
Copyright 2014 Maxine Red <maxine_red1@yahoo.com>
|
4
|
-
|
5
|
-
This file is part of rubyhexagon.
|
6
|
-
|
7
|
-
rubyhexagon is free software: you can redistribute it and/or modify
|
8
|
-
it under the terms of the GNU General Public License as published by
|
9
|
-
the Free Software Foundation, either version 3 of the License, or
|
10
|
-
(at your option) any later version.
|
11
|
-
|
12
|
-
rubyhexagon is distributed in the hope that it will be useful,
|
13
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
-
GNU General Public License for more details.
|
16
|
-
|
17
|
-
You should have received a copy of the GNU General Public License
|
18
|
-
along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
|
19
|
-
=end
|
20
|
-
|
21
|
-
file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
22
|
-
$:.unshift(File.expand_path(File.dirname(file)+"/../lib"))
|
23
|
-
|
24
|
-
require "rubyhexagon"
|
25
|
-
require "readline"
|
26
|
-
require "socket"
|
27
|
-
require "base64"
|
28
|
-
|
29
|
-
include E621
|
30
|
-
|
31
|
-
E621::Config.config = File.expand_path("~/.hexagon/conf.json")
|
32
|
-
tags = Array.new
|
33
|
-
File.open(File.expand_path(E621::Config.paths["tags"])) do |f|
|
34
|
-
tags = f.read.parse
|
35
|
-
end
|
36
|
-
Readline.completion_proc = proc do |s|
|
37
|
-
add = s[0] if s.match(/^\-|\~/)
|
38
|
-
add = s[0,2] if s.match(/^\*_/)
|
39
|
-
s.sub!(/^\-|\~|(\*_)/,"")
|
40
|
-
s = Regexp.escape(s)
|
41
|
-
words = Array.new
|
42
|
-
words += Dir["*"].reject{|d|!File.directory?(d)}.map{|d|"name:#{File.basename(d)}"}
|
43
|
-
words += ["rating:s","rating:q","rating:e"]
|
44
|
-
words += ["id:","md5:","description:","desc:","pool:","set:","width:",\
|
45
|
-
"height:","score:","favcount:","views:","ratio:","parent:"]
|
46
|
-
words += ["type:jpg","type:png","type:gif","type:swf"]
|
47
|
-
bools = ["hassource","hasdescription","ischild","idparent","inpool"]
|
48
|
-
words += bools.map{|b|["#{b}:true","#{b}:false"]}.flatten
|
49
|
-
# Make this a one dimensional array again!
|
50
|
-
words += ["order:random"]
|
51
|
-
orders = ["id","score","views","set","favcount","tagcount","comments",\
|
52
|
-
"mpixels","filesize","ratio","desclength"]
|
53
|
-
words += orders.map{|b|["#{b}_asc","#{b}_desc"]}.flatten.map{|b|"order:#{b}"}
|
54
|
-
# Here we make all orders in both directions and keep it as just an one
|
55
|
-
# dimensional array.
|
56
|
-
words += tags.map{|t|t["name"]}
|
57
|
-
words = words.grep(/^#{s}/)
|
58
|
-
words.map!{|w|"#{add}#{w}"}
|
59
|
-
s = "#{add}#{s}"
|
60
|
-
words
|
61
|
-
end
|
62
|
-
Readline.completer_word_break_characters = " "
|
63
|
-
Readline.completion_append_character = " "
|
64
|
-
history = File.open(File.expand_path(E621::Config.paths["history"]),"a+")
|
65
|
-
history.read.split($/).each do |l|
|
66
|
-
Readline::HISTORY << l
|
67
|
-
end
|
68
|
-
|
69
|
-
def net_send(query)
|
70
|
-
socket = TCPSocket.new(E621::Config.config["server"],5621)
|
71
|
-
socket.puts query.to_json
|
72
|
-
res = socket.read.parse
|
73
|
-
if res["error"] then
|
74
|
-
$stderr.puts "Server error: #{res["message"]}"
|
75
|
-
abort
|
76
|
-
end
|
77
|
-
socket.close
|
78
|
-
return res
|
79
|
-
end
|
80
|
-
|
81
|
-
api = API.new("user")
|
82
|
-
prompt = "#{api.user}@e621.net/posts> "
|
83
|
-
Dir.chdir(File.expand_path(E621::Config.paths["posts"])) do
|
84
|
-
while buff = Readline.readline(prompt.bold("yellow"), false) do
|
85
|
-
next if buff == String.new
|
86
|
-
if !(buff == String.new || buff == Readline::HISTORY.to_a.last) then
|
87
|
-
Readline::HISTORY << buff
|
88
|
-
history.puts buff
|
89
|
-
end
|
90
|
-
buff = buff.split(/\s+/)
|
91
|
-
name = buff.shift.sub("name:","")
|
92
|
-
query = {"action"=>"search","name"=>name,"query"=>buff}
|
93
|
-
rid = net_send(query)["rid"]
|
94
|
-
posts,page,num = Array.new,{"posts"=>[2]},1
|
95
|
-
while page["posts"] != Array.new do
|
96
|
-
print "Fetching page #{num.pad(3," ")} (#{posts.length.pad(6," ")} posts fetched).\r"
|
97
|
-
page = net_send({"action"=>"page","page"=>num,"rid"=>rid})
|
98
|
-
posts += page["posts"] if page["posts"]
|
99
|
-
num += 1
|
100
|
-
end
|
101
|
-
puts
|
102
|
-
Dir.mkdir(name) unless File.exist?(name)
|
103
|
-
Dir.chdir(name) do
|
104
|
-
count = 1
|
105
|
-
posts.reverse.each do |post|
|
106
|
-
print "Downloading post #{count.pad(6," ")} of #{posts.length.pad(6, " ")}.\r"
|
107
|
-
query = {"action"=>"show", "post"=>post}
|
108
|
-
res = net_send(query)
|
109
|
-
File.open(res["name"],"w"){|f|f.print Base64.decode64(res["data"])}
|
110
|
-
count += 1
|
111
|
-
end
|
112
|
-
end
|
113
|
-
puts
|
114
|
-
end
|
115
|
-
end
|
116
|
-
puts
|
data/bin/e621_server
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
=begin
|
3
|
-
Copyright 2014 Maxine Red <maxine_red1@yahoo.com>
|
4
|
-
|
5
|
-
This file is part of rubyhexagon.
|
6
|
-
|
7
|
-
rubyhexagon is free software: you can redistribute it and/or modify
|
8
|
-
it under the terms of the GNU General Public License as published by
|
9
|
-
the Free Software Foundation, either version 3 of the License, or
|
10
|
-
(at your option) any later version.
|
11
|
-
|
12
|
-
rubyhexagon is distributed in the hope that it will be useful,
|
13
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
-
GNU General Public License for more details.
|
16
|
-
|
17
|
-
You should have received a copy of the GNU General Public License
|
18
|
-
along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
|
19
|
-
=end
|
20
|
-
|
21
|
-
$:.unshift(File.expand_path(File.dirname(__FILE__)+"/../lib"))
|
22
|
-
|
23
|
-
require "rubyhexagon"
|
24
|
-
require "openssl"
|
25
|
-
require "thread"
|
26
|
-
require "logger"
|
27
|
-
require "socket"
|
28
|
-
require "digest"
|
29
|
-
require "base64"
|
30
|
-
|
31
|
-
include E621
|
32
|
-
|
33
|
-
Thread.abort_on_exception = true
|
34
|
-
|
35
|
-
E621::Config.config = File.expand_path("~/.hexagon/conf.json")
|
36
|
-
api = API.new
|
37
|
-
tasks = Hash.new
|
38
|
-
mt = Mutex.new
|
39
|
-
Process.daemon
|
40
|
-
$0 = "e621 Proxy Server"
|
41
|
-
Dir.chdir(File.expand_path("~/.hexagon")) do
|
42
|
-
@log = Logger.new(File.expand_path(E621::Config.paths["logging"]))
|
43
|
-
@log.formatter = proc do |sev,dat,prog,msg|
|
44
|
-
"#{Time.now.strftime("%b %e, %Y %I:%M:%S %p")} [#{sev.pad(5)}] #{msg}#$/"
|
45
|
-
end
|
46
|
-
begin
|
47
|
-
begin
|
48
|
-
File.open("run"){|f|Process.kill("TERM",f.read.to_i)}
|
49
|
-
rescue
|
50
|
-
end
|
51
|
-
sleep 2
|
52
|
-
File.open("run","w"){|f|f.puts $$}
|
53
|
-
server = TCPServer.new(5621)
|
54
|
-
#@log = Logger.new(STDOUT)
|
55
|
-
#@log.level = Logger::DEBUG
|
56
|
-
queue = Queue.new
|
57
|
-
10.times do
|
58
|
-
Thread.new do
|
59
|
-
loop do
|
60
|
-
post = queue.pop
|
61
|
-
next if File.exist?("cache/#{post.md5[0,2]}/#{post.md5}")
|
62
|
-
mt.synchronize do
|
63
|
-
Dir.mkdir("cache") unless File.exist?("cache")
|
64
|
-
Dir.mkdir("cache/#{post.md5[0,2]}") unless File.exist?("cache/#{post.md5[0,2]}")
|
65
|
-
end
|
66
|
-
body = post.download_data
|
67
|
-
mt.synchronize do
|
68
|
-
File.open("cache/#{post.md5[0,2]}/#{post.md5}","w") do |f|
|
69
|
-
f.print body
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
@log.level = Logger::INFO
|
77
|
-
@log.info("New server instance started.")
|
78
|
-
Thread.new do
|
79
|
-
loop do
|
80
|
-
files,size,tm = Dir["cache/**/*"].reject{|x|!File.file?(x)},0,Array.new
|
81
|
-
files.each do |f|
|
82
|
-
size += File.size(f)
|
83
|
-
end
|
84
|
-
if size > 30*2**30 then
|
85
|
-
files.each do |f|
|
86
|
-
tm << {"file"=>f,"time"=>File.atime(f),"size"=>File.size(f)}
|
87
|
-
end
|
88
|
-
tm = tm.sort{|f1,f2|f1["time"]<=>f2["time"]}
|
89
|
-
while size > 30*2**30 do
|
90
|
-
unlink = tm.shift
|
91
|
-
size -= unlink["size"]
|
92
|
-
File.unlink(unlink["file"])
|
93
|
-
end
|
94
|
-
end
|
95
|
-
sleep 3600
|
96
|
-
end
|
97
|
-
end
|
98
|
-
Dir["searches/*"].each{|f|File.unlink(f)}
|
99
|
-
loop do
|
100
|
-
client = server.accept
|
101
|
-
Thread.new(client) do |c|
|
102
|
-
begin
|
103
|
-
begin
|
104
|
-
query = c.gets.parse
|
105
|
-
rescue
|
106
|
-
@log.error("[#{c.remote_address.ip_address}]: Error #$!.")
|
107
|
-
next
|
108
|
-
end
|
109
|
-
case query["action"]
|
110
|
-
when "search" then
|
111
|
-
name,query = query["name"],query["query"]
|
112
|
-
@log.info("[#{c.remote_address.ip_address}]: Search init #{name}/#{query.join("+")}.")
|
113
|
-
Dir.mkdir("searches") unless File.exist?("searches")
|
114
|
-
rid = Digest::SHA2.hexdigest(name+Time.now.strftime("%s"))
|
115
|
-
Thread.new do
|
116
|
-
s = Search.new(query)
|
117
|
-
s.each_post do |post|
|
118
|
-
queue << post
|
119
|
-
end
|
120
|
-
end
|
121
|
-
File.open("searches/#{rid}.json","w") do |f|
|
122
|
-
search = {"name"=>name,"query"=>query}
|
123
|
-
f.print search.to_json
|
124
|
-
end
|
125
|
-
rid = {"rid"=>rid}.to_json
|
126
|
-
c.print rid
|
127
|
-
when "page" then
|
128
|
-
page, rid = query["page"],query["rid"]
|
129
|
-
File.open("searches/#{rid}.json"){|f|query=f.read.parse}
|
130
|
-
@log.info("[#{c.remote_address.ip_address}]: Search page #{query["name"]}/#{page}.")
|
131
|
-
search = Search.new(query["query"])
|
132
|
-
search.page = page
|
133
|
-
posts = {"posts"=>search.posts}.to_json
|
134
|
-
c.print posts
|
135
|
-
when "show" then
|
136
|
-
post = Post.new(query["post"])
|
137
|
-
body = String.new
|
138
|
-
mt.synchronize do
|
139
|
-
Dir.mkdir("cache") unless File.exist?("cache")
|
140
|
-
Dir.mkdir("cache/#{post.md5[0,2]}") unless File.exist?("cache/#{post.md5[0,2]}")
|
141
|
-
end
|
142
|
-
if File.exist?("cache/#{post.md5[0,2]}/#{post.md5}") then
|
143
|
-
cached = " (cached)"
|
144
|
-
mt.synchronize do
|
145
|
-
File.open("cache/#{post.md5[0,2]}/#{post.md5}") do |f|
|
146
|
-
body = f.read
|
147
|
-
end
|
148
|
-
end
|
149
|
-
else
|
150
|
-
cached = ""
|
151
|
-
body = post.download_data
|
152
|
-
mt.synchronize do
|
153
|
-
File.open("cache/#{post.md5[0,2]}/#{post.md5}","w") do |f|
|
154
|
-
f.print body
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
@log.info("[#{c.remote_address.ip_address}]: Show post #{query["post"]["id"].pad(6," ")}#{cached}.")
|
159
|
-
file = {"name"=>"#{post.id.pad(8)}.#{post.md5}.#{post.file_ext}","data"=>Base64.encode64(body)}
|
160
|
-
c.print file.to_json
|
161
|
-
end
|
162
|
-
rescue
|
163
|
-
@log.error("Error: #$!!")
|
164
|
-
raise
|
165
|
-
if c && !c.closed? then
|
166
|
-
err = {"error"=>true,"message"=> $!.to_s}
|
167
|
-
c.puts err.to_json
|
168
|
-
end
|
169
|
-
ensure
|
170
|
-
if c && !c.closed? then
|
171
|
-
c.close
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
rescue Errno
|
177
|
-
@log.error("Error: #$!!")
|
178
|
-
rescue => e
|
179
|
-
p e.class
|
180
|
-
@log.fatal("Fatal Error: #$!!")
|
181
|
-
raise
|
182
|
-
ensure
|
183
|
-
@log.info("Server instance terminated.")
|
184
|
-
end
|
185
|
-
end
|
data/bin/fav-sync
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
=begin
|
3
|
-
Copyright 2014 Maxine Red <maxine_red1@yahoo.com>
|
4
|
-
|
5
|
-
This file is part of rubyhexagon.
|
6
|
-
|
7
|
-
rubyhexagon is free software: you can redistribute it and/or modify
|
8
|
-
it under the terms of the GNU General Public License as published by
|
9
|
-
the Free Software Foundation, either version 3 of the License, or
|
10
|
-
(at your option) any later version.
|
11
|
-
|
12
|
-
rubyhexagon is distributed in the hope that it will be useful,
|
13
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
-
GNU General Public License for more details.
|
16
|
-
|
17
|
-
You should have received a copy of the GNU General Public License
|
18
|
-
along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
|
19
|
-
=end
|
20
|
-
|
21
|
-
loader = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
22
|
-
$:.unshift(File.expand_path(File.dirname(loader))+"/../lib")
|
23
|
-
|
24
|
-
require "rubyhexagon"
|
25
|
-
|
26
|
-
include E621
|
27
|
-
|
28
|
-
max_wait = 5
|
29
|
-
E621::Config.config = File.expand_path("~/.hexagon/conf.json")
|
30
|
-
api = API.new("user")
|
31
|
-
uid = api.get("index",{"name"=>api.user}).first["id"]
|
32
|
-
Dir.chdir(File.expand_path("~/pictures/e621/favorites"))
|
33
|
-
remote = Array.new
|
34
|
-
Search.new("fav:maxine_red order:id".split(" ")).each_post do |post|
|
35
|
-
string = ["0"*(7-post.id.to_s.length)+post.id.to_s,post.md5,post.file_ext].join(".")
|
36
|
-
remote << Array.new
|
37
|
-
if !File.exist?(string) then
|
38
|
-
post.download(string)
|
39
|
-
else
|
40
|
-
md5 = String.new
|
41
|
-
File.open(string){|f|md5 = Digest::MD5.hexdigest(f.read)}
|
42
|
-
next if md5 == post.md5
|
43
|
-
post.download(string)
|
44
|
-
end
|
45
|
-
puts "Downloaded post #{" "*(7-post.id.to_s.length)}#{post.id}."
|
46
|
-
sleep(rand*max_wait)
|
47
|
-
end
|
48
|
-
local = Dir["*"].reject{|x|x.match(/db$/)}.map{|x|x.sub(/\..+/,"").to_i}.sort.uniq
|
49
|
-
(remote-local).each do |id|
|
50
|
-
post = Post.new({"id"=>id})
|
51
|
-
f = post.unfavorite
|
52
|
-
if f["success"] then
|
53
|
-
print "\e[1;33mRemoved favorite on post #{" "*(7-post.id.to_s.length)}#{post.id}.\e[0m "
|
54
|
-
else
|
55
|
-
print "\e[1;31m#{f["reason"].to_s.gsub(/<.+?>/,"")}.\e[0m "
|
56
|
-
end
|
57
|
-
f = post.vote(1)
|
58
|
-
if f["success"] then
|
59
|
-
puts "\e[1;33mScored post #{" "*(7-post.id.to_s.length)}#{post.id} down.\e[0m"
|
60
|
-
else
|
61
|
-
puts "\e[1;31m#{f["reason"].to_s.gsub(/<.+?>/,"")}.\e[0m"
|
62
|
-
end
|
63
|
-
sleep(rand*max_wait)
|
64
|
-
end
|
65
|
-
(local-remote).each do |id|
|
66
|
-
next if id == 0
|
67
|
-
post = Post.new({"id"=>id})
|
68
|
-
f = post.favorite
|
69
|
-
if f["success"] then
|
70
|
-
print "\e[1;32mFavorited #{" "*(7-post.id.to_s.length)}#{post.id}.\e[0m "
|
71
|
-
else
|
72
|
-
print "\e[1;31m#{f["reason"].gsub(/<.+?>/,"")}.\e[0m "
|
73
|
-
#File.unlink(Dir["#{set["shortname"]}/*.#{"0"*(8-post.id.to_s.length)}#{post.id}.*"].first)
|
74
|
-
end
|
75
|
-
f = post.vote(1)
|
76
|
-
if f["success"] then
|
77
|
-
puts "\e[1;32mScored post #{" "*(7-post.id.to_s.length)}#{post.id} up.\e[0m"
|
78
|
-
else
|
79
|
-
puts "\e[1;31m#{f["reason"].to_s.gsub(/<.+?>/,"")}.\e[0m"
|
80
|
-
end
|
81
|
-
sleep(rand*max_wait)
|
82
|
-
end
|
data/bin/set-sync
DELETED
@@ -1,261 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
=begin
|
3
|
-
Copyright 2014 Maxine Red <maxine_red1@yahoo.com>
|
4
|
-
|
5
|
-
This file is part of rubyhexagon.
|
6
|
-
|
7
|
-
rubyhexagon is free software: you can redistribute it and/or modify
|
8
|
-
it under the terms of the GNU General Public License as published by
|
9
|
-
the Free Software Foundation, either version 3 of the License, or
|
10
|
-
(at your option) any later version.
|
11
|
-
|
12
|
-
rubyhexagon is distributed in the hope that it will be useful,
|
13
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
-
GNU General Public License for more details.
|
16
|
-
|
17
|
-
You should have received a copy of the GNU General Public License
|
18
|
-
along with rubyhexagon. If not, see <http://www.gnu.org/licenses/>.
|
19
|
-
=end
|
20
|
-
|
21
|
-
loader = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
22
|
-
$:.unshift(File.expand_path(File.dirname(loader))+"/../lib")
|
23
|
-
|
24
|
-
require "rubyhexagon"
|
25
|
-
require "logger"
|
26
|
-
|
27
|
-
include E621
|
28
|
-
|
29
|
-
$0 = "E621 Set-sync"
|
30
|
-
$form = "%b %e, %Y %I:%M:%S %p"
|
31
|
-
|
32
|
-
def download(post,name,set)
|
33
|
-
artist = post.tags.split(/\s+/).map do |t|
|
34
|
-
@tags[@tags.index(t)] if @tags.index(t)
|
35
|
-
end
|
36
|
-
begin
|
37
|
-
artist = artist.compact.first.sub("_(artist)","")
|
38
|
-
rescue
|
39
|
-
artist = "unknown"
|
40
|
-
end
|
41
|
-
string = [post.created_at.to_i,post.id.pad(8),artist+"_"+name,post.file_ext]
|
42
|
-
string = string.join(".")
|
43
|
-
tries = 0
|
44
|
-
begin
|
45
|
-
api = API.new("post")
|
46
|
-
post = Post.new(api.post("show",{"id"=>post.id}))
|
47
|
-
post.download("#{name}/#{string}")
|
48
|
-
rescue
|
49
|
-
raise if tries > 4
|
50
|
-
tries += 1
|
51
|
-
puts "#{Time.now.strftime($form)}: #$!"
|
52
|
-
@log.info("#$!")
|
53
|
-
sleep 1
|
54
|
-
retry
|
55
|
-
end
|
56
|
-
File.utime(post.created_at,post.created_at,"#{name}/#{string}")
|
57
|
-
@posts["downloads"] << post.id
|
58
|
-
jposts = @posts.to_json
|
59
|
-
set_files = File.expand_path("~/.hexagon/sets")
|
60
|
-
File.open(set_files+"/#{name}.json","w"){|f|f.print jposts}
|
61
|
-
s = "Downloaded post #{" "*(6-post.id.to_s.length)}#{post.id} from \"#{set["name"]}\"."
|
62
|
-
puts "#{Time.now.strftime($form)}: #{s}"
|
63
|
-
@log.info(s)
|
64
|
-
end
|
65
|
-
begin
|
66
|
-
run,running = (File.expand_path("~/.hexagon/sets.run")),false
|
67
|
-
if File.exist?(run) then
|
68
|
-
running = true
|
69
|
-
else
|
70
|
-
File.open(run,"w"){|f|f.print $$}
|
71
|
-
end
|
72
|
-
max_wait = 2.5
|
73
|
-
E621::Config.config = File.expand_path("~/.hexagon/conf.json")
|
74
|
-
api = API.new("user")
|
75
|
-
if $stdout.tty? then
|
76
|
-
@log = Logger.new($stdout)
|
77
|
-
@log.level = Logger::DEBUG
|
78
|
-
else
|
79
|
-
@log = Logger.new(File.expand_path(E621::Config.paths["logging"]))
|
80
|
-
@log.level = Logger::INFO
|
81
|
-
end
|
82
|
-
@log.formatter = proc do |sev,dat,prog,msg|
|
83
|
-
"#{Time.now.strftime("%b %e, %Y %I:%M:%S %p")} [#{sev.pad(5)}] #{msg}#$/"
|
84
|
-
end
|
85
|
-
@log.info("Program #$0 started.")
|
86
|
-
uid = api.get("index",{"name"=>api.user}).first["id"]
|
87
|
-
tags = File.expand_path("~/.hexagon/tags.json")
|
88
|
-
if !File.exist?(tags) then
|
89
|
-
@tags = Array.new
|
90
|
-
else
|
91
|
-
File.open(tags) do |f|
|
92
|
-
@tags = f.read.parse
|
93
|
-
end
|
94
|
-
end
|
95
|
-
@tags = @tags.sort{|k1,k2|k1["id"]<=>k2["id"]}
|
96
|
-
net = Net::HTTP.new("e621.net",443)
|
97
|
-
net.use_ssl = true
|
98
|
-
body,page = [2],1
|
99
|
-
until body == Array.new do
|
100
|
-
body = net.get("/tag/index.json?limit=0&order=count&after_id=#{@tags.last["id"]}&page=#{page}").body.parse
|
101
|
-
body = body.map{|x|x["name"] = x["name"].encode("us-ascii", :invalid => :replace, :undef => :replace, :replace => "");x}
|
102
|
-
@tags += body
|
103
|
-
page += 1
|
104
|
-
end
|
105
|
-
jtags = @tags.to_json
|
106
|
-
File.open(tags,"w"){|f|f.print jtags}
|
107
|
-
@tags = @tags.reject{|x|x["type"]!=1}.map{|x|x["name"]}
|
108
|
-
Dir.chdir(File.expand_path("~/Dropbox/Furry/e621/sets")) do
|
109
|
-
sets = Array.new
|
110
|
-
File.open(File.expand_path("~/.hexagon/sets.json")) do |f|
|
111
|
-
sets = f.read.parse
|
112
|
-
end
|
113
|
-
sets = sets.sort{|s1,s2|s1["id"]<=>s2["id"]}
|
114
|
-
set_files = File.expand_path("~/.hexagon/sets")
|
115
|
-
api = API.new("set")
|
116
|
-
sets.each do |set|
|
117
|
-
sid, owner, query = set["id"], set["owner"], set["search"]
|
118
|
-
set = api.get("show", {"id"=>sid})
|
119
|
-
@log.info("Fetching set #{set["name"]}")
|
120
|
-
posts = set["posts"].map{|post|Post.new(post)}
|
121
|
-
name = set["shortname"]
|
122
|
-
if !File.exists?(set_files+"/#{name}.json") then
|
123
|
-
File.open(set_files+"/#{name}.json","w"){|f|f.print "{\"downloads\":[]}"}
|
124
|
-
end
|
125
|
-
File.open(set_files+"/#{name}.json"){|f|@posts = f.read.parse}
|
126
|
-
Dir.mkdir(name) unless File.exist?(name)
|
127
|
-
posts.each do |post|
|
128
|
-
next if @posts["downloads"].include?(post.id)
|
129
|
-
download(post,name,set)
|
130
|
-
end
|
131
|
-
next if owner != uid
|
132
|
-
Search.new("fav:maxine_red #{query} order:id".split(" ")).each_post do |post|
|
133
|
-
next if @posts["downloads"].include?(post.id)
|
134
|
-
download(post,name,set)
|
135
|
-
sleep(rand*max_wait)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
sets.each do |set|
|
139
|
-
sid, owner, query = set["id"], set["owner"], set["search"]
|
140
|
-
next if owner != uid
|
141
|
-
set = api.get("show", {"id"=>sid})
|
142
|
-
posts = set["posts"].map{|post|Post.new(post).id}
|
143
|
-
name = set["shortname"]
|
144
|
-
local = Dir["#{name}/*"].reject{|x|x.match(/db$/)}.map{|x|x.split(".")[1].to_i}.sort.uniq
|
145
|
-
(posts-local).each do |id|
|
146
|
-
post = Post.new({"id"=>id})
|
147
|
-
f = post.remove_from_set(sid)
|
148
|
-
if f["success"] then
|
149
|
-
s = "Removed Post #{" "*(6-post.id.to_s.length)}#{post.id} from \"#{set["name"]}\""
|
150
|
-
puts "#{Time.now.strftime($form)}: \e[1;33m#{s}\e[0m"
|
151
|
-
@log.info(s)
|
152
|
-
else
|
153
|
-
s = "#{f["reason"].to_s.gsub(/<.+?>/,"")}."
|
154
|
-
puts "#{Time.now.strftime($form)}: \e[1;31m#{s}\e[0m"
|
155
|
-
@log.info(s)
|
156
|
-
end
|
157
|
-
sleep(rand*max_wait)
|
158
|
-
end
|
159
|
-
(local-posts).each do |id|
|
160
|
-
next if id == 0
|
161
|
-
post = Post.new({"id"=>id})
|
162
|
-
f = post.add_to_set(sid)
|
163
|
-
if f["success"] then
|
164
|
-
s = "Added Post #{" "*(6-post.id.to_s.length)}#{post.id} to \"#{set["name"]}\"."
|
165
|
-
puts "#{Time.now.strftime($form)}: #{s}"
|
166
|
-
@log.info(s)
|
167
|
-
else
|
168
|
-
s = "#{f["reason"].gsub(/<.+?>/,"")}. Removing local file."
|
169
|
-
puts "#{Time.now.strftime($form)}: #{s}"
|
170
|
-
@log.info(s)
|
171
|
-
File.unlink(Dir["#{set["shortname"]}/*.#{"0"*(8-post.id.to_s.length)}#{post.id}.*"].first)
|
172
|
-
end
|
173
|
-
sleep(rand*max_wait)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
Dir.chdir(File.expand_path("~/Dropbox/Furry/e621/pools")) do
|
178
|
-
pools = Array.new
|
179
|
-
File.open(File.expand_path("~/.hexagon/pools.json")) do |f|
|
180
|
-
pools = f.read.parse.sort.uniq
|
181
|
-
end
|
182
|
-
pool_files = File.expand_path("~/.hexagon/pools")
|
183
|
-
api = API.new("pool")
|
184
|
-
pools = pools-(1..600).to_a
|
185
|
-
pools.each do |id|
|
186
|
-
spool = {"updated_at"=>0, "posts"=>[]}
|
187
|
-
npool = api.get("show",{"id"=>id})
|
188
|
-
begin
|
189
|
-
pool = Pool.new(npool)
|
190
|
-
@log.debug("Fetched pool \"#{pool.name.gsub("_"," ")}\"")
|
191
|
-
rescue
|
192
|
-
#$stderr.puts "#{pool.inspect}"
|
193
|
-
puts "#{Time.now.strftime($form)}: #{id} raised an error!"
|
194
|
-
@log.debug("Pool object: #{pool.inspect}")
|
195
|
-
next
|
196
|
-
end
|
197
|
-
begin
|
198
|
-
pfile = pool_files+"/#{pool.id.pad(5)}.json"
|
199
|
-
rescue
|
200
|
-
@log.error("Error occured: #{npool.inspect}")
|
201
|
-
raise
|
202
|
-
end
|
203
|
-
if !File.exists?(pfile) then
|
204
|
-
File.open(pfile,"w"){|f|f.print spool.to_json}
|
205
|
-
spool = Pool.new(spool)
|
206
|
-
else
|
207
|
-
File.open(pfile){|f|spool = Pool.new(f.read.parse)}
|
208
|
-
end
|
209
|
-
posts = Array.new
|
210
|
-
@log.debug("Skipping pool \"#{pool.name.gsub("_"," ")}\" (#{pool.id.pad(4)})")
|
211
|
-
next if pool.updated_at.to_i <= spool.updated_at.to_i
|
212
|
-
(pool.post_count/24.0).ceil.times do |page|
|
213
|
-
posts += pool.posts.map{|post|Post.new(post)}
|
214
|
-
pool = Pool.new(api.get("show",{"id"=>pool.id,"page"=>page+2}))
|
215
|
-
end
|
216
|
-
pool.name = "Love_Can_Be_Different" if pool.name == "Love_can_be_different"
|
217
|
-
pool.name = pool.name.gsub("_"," ").encode("us-ascii", :invalid => :replace, :undef => :replace, :replace => "")
|
218
|
-
name = pool.name.gsub(/[^0-9, ,_,a-z,\-]/i,"").sub(/\s+$/,"")
|
219
|
-
Dir.mkdir(name) unless File.exist?(name)
|
220
|
-
@log.info("Fetching pool #{pool.name}")
|
221
|
-
posts.each_with_index do |post,id|
|
222
|
-
id = id.succ
|
223
|
-
next if spool.posts.include?(post.id)
|
224
|
-
artist = post.tags.split(/\s+/).map do |t|
|
225
|
-
@tags[@tags.index(t)] if @tags.index(t)
|
226
|
-
end
|
227
|
-
begin
|
228
|
-
artist = artist.compact.first.sub("_(artist)","")
|
229
|
-
rescue
|
230
|
-
artist = "unknown"
|
231
|
-
end
|
232
|
-
string = [pool.id.pad(5),id.pad(4),post.id.pad(8),artist,name.downcase.gsub(/\s/,"_"),post.file_ext]
|
233
|
-
string = string.join(".")
|
234
|
-
tries = 0
|
235
|
-
begin
|
236
|
-
post.download("#{name}/#{string}")
|
237
|
-
rescue
|
238
|
-
raise if tries > 4
|
239
|
-
tries += 1
|
240
|
-
puts "#{Time.now.strftime($form)}: #$!"
|
241
|
-
@log.info("#$!")
|
242
|
-
sleep 1
|
243
|
-
retry
|
244
|
-
end
|
245
|
-
File.utime(post.created_at,post.created_at,"#{name}/#{string}")
|
246
|
-
spool.posts << post.id
|
247
|
-
jposts = spool.to_json
|
248
|
-
File.open(pfile,"w"){|f|f.print jposts}
|
249
|
-
s = "Downloaded post #{post.id.pad(6," ")} (#{id.pad(posts.length.to_s.length," ")}/#{posts.length}) from \"#{pool.name}\"."
|
250
|
-
puts "#{Time.now.strftime($form)}: #{s}"
|
251
|
-
@log.info(s)
|
252
|
-
sleep(rand*max_wait)
|
253
|
-
end
|
254
|
-
spool.updated_at = pool.updated_at
|
255
|
-
jposts = spool.to_json
|
256
|
-
File.open(pfile,"w"){|f|f.print jposts}
|
257
|
-
end
|
258
|
-
end
|
259
|
-
ensure
|
260
|
-
File.unlink(run) unless running
|
261
|
-
end
|