multistockphoto 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -0
- data/Manifest.txt +1 -0
- data/bin/multistockphoto +162 -75
- data/bin/multistockphoto-gui +36 -1
- data/config.yaml +7 -0
- data/lib/multistockphoto/generic_site.rb +13 -1
- data/lib/multistockphoto/photo.rb +64 -33
- data/lib/multistockphoto/sender.rb +4 -0
- data/lib/multistockphoto/site_fotolia.rb +22 -1
- data/lib/multistockphoto/site_panthermedia.rb +132 -0
- data/lib/multistockphoto/site_photocase.rb +72 -59
- data/lib/multistockphoto/site_zoonar.rb +3 -2
- data/lib/multistockphoto/version.rb +1 -1
- data/lib/multistockphoto.rb +1 -0
- data/test/test_multistockphoto.rb +153 -49
- data/website/index.html +650 -900
- data/website/index.txt +236 -22
- metadata +15 -5
@@ -5,17 +5,19 @@ include Magick
|
|
5
5
|
|
6
6
|
class Photo
|
7
7
|
attr_reader :filename, :rotated_filename
|
8
|
-
attr_accessor :tags
|
8
|
+
attr_accessor :tags, :title, :description
|
9
9
|
|
10
10
|
def initialize(filename)
|
11
11
|
@filename = filename
|
12
12
|
@tags = nil
|
13
|
+
@title = nil
|
14
|
+
@description = nil
|
13
15
|
end
|
14
16
|
|
15
17
|
# ist Bild im Hochformat
|
16
18
|
# TODO: paßt dieser Test für alle gedrehten Bilder?
|
17
19
|
def portrait?
|
18
|
-
if false
|
20
|
+
if false # vorher
|
19
21
|
pic = ImageList.new(@filename)
|
20
22
|
if pic.orientation == LeftBottomOrientation
|
21
23
|
return true
|
@@ -77,13 +79,15 @@ class Photo
|
|
77
79
|
self.tags = tags
|
78
80
|
#print "writing tags to file ... "
|
79
81
|
#$stdout.flush
|
80
|
-
|
82
|
+
write_attributes
|
81
83
|
#puts "done"
|
82
84
|
rescue Errno::ENOENT
|
83
85
|
#warn "WARNING: no tags file #{fn}"
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
89
|
+
# setzt Keywords in Photo-Objekt, schreibt aber nicht in Datei.
|
90
|
+
# Aus Performanzgründen (z.B. bei --plist)
|
87
91
|
def set_keywords_without_write(from_lang=nil,to_lang=nil)
|
88
92
|
ext = File.extname(@filename)
|
89
93
|
fn = @filename.sub(ext,".tags")
|
@@ -94,6 +98,10 @@ class Photo
|
|
94
98
|
}
|
95
99
|
#puts "tags file #{fn} found"
|
96
100
|
tags = Photo.to_tags(lines)
|
101
|
+
# tags.each_with_index {|tag,i|
|
102
|
+
# p tag
|
103
|
+
# p i
|
104
|
+
# }
|
97
105
|
if from_lang and to_lang
|
98
106
|
tags = translate_tags(tags,from_lang,to_lang)
|
99
107
|
end
|
@@ -112,40 +120,46 @@ class Photo
|
|
112
120
|
def file_keywords
|
113
121
|
photo = MiniExiftool.new @filename
|
114
122
|
self.tags = photo.tags
|
123
|
+
self.title = photo.title
|
115
124
|
photo.keywords
|
116
125
|
end
|
117
126
|
|
118
|
-
# schreibt Keywords in Datei-Header
|
119
|
-
def
|
127
|
+
# schreibt Keywords und Titel in Datei-Header
|
128
|
+
def write_attributes
|
120
129
|
photo = MiniExiftool.new @filename
|
121
|
-
#s = ''
|
122
|
-
#tags.each {|tag|
|
123
|
-
#if s == ''
|
124
|
-
#s = s + tag
|
125
|
-
#else
|
126
|
-
## s = s + ' ' + tag
|
127
|
-
## lieber durch Komma trennen, da sonst das Formular z.B. bei fotolia nicht
|
128
|
-
## richtig gefüllt wird
|
129
|
-
#s = s + ',' + tag
|
130
|
-
#end
|
131
|
-
#}
|
132
130
|
# lieber durch Komma trennen, da sonst das Formular z.B. bei fotolia nicht
|
133
131
|
# richtig gefüllt wird
|
132
|
+
# Die jetzige Lösung funktioniert bei: Fotolia, Zoonar (die restliche konnte
|
133
|
+
# ich noch nicht überprüfen, da ich dort noch kein einziges Photo durchbekommen
|
134
|
+
# habe :-(
|
134
135
|
photo['keywords'] = tags.join(',')
|
135
|
-
|
136
136
|
#TODO:
|
137
|
-
photo['title'] =
|
138
|
-
|
137
|
+
photo['title'] = self.title
|
139
138
|
photo.save
|
140
139
|
end
|
141
140
|
|
141
|
+
def width
|
142
|
+
photo = MiniExiftool.new @filename
|
143
|
+
|
144
|
+
42
|
145
|
+
end
|
146
|
+
|
147
|
+
def height
|
148
|
+
42
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def file_title
|
153
|
+
@title
|
154
|
+
end
|
155
|
+
|
142
156
|
private
|
143
157
|
|
144
158
|
# erstellt Key-Array aus String (getrennt durch newline,',' oder ' ')
|
145
159
|
def self.to_tags(s)
|
146
160
|
keys = []
|
147
161
|
s.split(/[, |\t\n]+/).each {|w|
|
148
|
-
keys << w
|
162
|
+
keys << w if w[0,2].downcase != 't:'
|
149
163
|
}
|
150
164
|
keys
|
151
165
|
end
|
@@ -167,19 +181,36 @@ class Photo
|
|
167
181
|
# Achtung: bisher nur experimentell. Nicht verwenden!
|
168
182
|
def translate_tag(tag,from_lang,to_lang)
|
169
183
|
if from_lang == :de and to_lang == :en
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
'
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
184
|
+
if File.exist?('translation_de_en.dat') # uebersetzungsdatei_existiert
|
185
|
+
# puts "translation file :de :en found"
|
186
|
+
found = false
|
187
|
+
result = nil
|
188
|
+
File.open('translation_de_en.dat') { |file|
|
189
|
+
file.each_line() { |line|
|
190
|
+
first,second = line.split(':')
|
191
|
+
if first == tag
|
192
|
+
result = second
|
193
|
+
found = true
|
194
|
+
end
|
195
|
+
}
|
196
|
+
}
|
197
|
+
raise "unknown translation for '#{tag}' (#{from_lang} #{to_lang})" if not found
|
198
|
+
return result
|
199
|
+
# else
|
200
|
+
# case tag
|
201
|
+
# when 'Baum'
|
202
|
+
# 'tree'
|
203
|
+
# when 'Berlin'
|
204
|
+
# 'Berlin'
|
205
|
+
# when 'gwb'
|
206
|
+
# 'gwb'
|
207
|
+
# when 'Blume'
|
208
|
+
# 'flower'
|
209
|
+
# when 'Biene'
|
210
|
+
# 'bee'
|
211
|
+
# else
|
212
|
+
# raise "unknown translation for '#{tag}' (#{from_lang} #{to_lang})"
|
213
|
+
# end
|
183
214
|
end
|
184
215
|
else
|
185
216
|
raise "unknown translation codes: #{from_lang} #{to_lang}"
|
@@ -12,7 +12,11 @@ class Sender
|
|
12
12
|
# gibt ein Array in Form [:fotolia,:zoonar,:photocase] zurueck
|
13
13
|
def active_sites
|
14
14
|
if ENV['MSP_CONFIG']
|
15
|
+
if File.exist? ENV['MSP_CONFIG']
|
15
16
|
@config = YAML.load_file(ENV['MSP_CONFIG'])
|
17
|
+
else
|
18
|
+
@config = YAML.load_file("config.yaml")
|
19
|
+
end
|
16
20
|
else
|
17
21
|
@config = YAML.load_file("config.yaml")
|
18
22
|
end
|
@@ -101,7 +101,7 @@ class Fotolia < GenericSite
|
|
101
101
|
ext = ext[1..-1]
|
102
102
|
end
|
103
103
|
case ext
|
104
|
-
when 'jpg','jpeg' #TODO: pruefen
|
104
|
+
when 'jpg','jpeg','svg' #TODO: pruefen
|
105
105
|
return true
|
106
106
|
else
|
107
107
|
return false
|
@@ -112,6 +112,27 @@ class Fotolia < GenericSite
|
|
112
112
|
Fotolia.accept?(ext)
|
113
113
|
end
|
114
114
|
|
115
|
+
def photosize_valid?(photo)
|
116
|
+
return true # TODO: provisorisch
|
117
|
+
if File.extname(photo.filename).downcase == '.jpeg' or
|
118
|
+
File.extname(photo.filename).downcase == '.jpg'
|
119
|
+
# JPEG-Datei mit mind. 4 MP (Megapixel).
|
120
|
+
if photo.width*photo.height > 3 * 3_000_000
|
121
|
+
return true
|
122
|
+
else
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
elsif File.extname(photo.filename).downcase == '.svg'
|
126
|
+
# SVG-Datei mit maximal 1 MB (Megabyte).
|
127
|
+
if photo.size <= 1024*1024
|
128
|
+
return true
|
129
|
+
else
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
115
136
|
private
|
116
137
|
|
117
138
|
def site_can_handle_keywords?
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Das hier schreibt Panthermedia:
|
5
|
+
Wichtig:
|
6
|
+
- Umlaute und Sonderzeichen im Dateinamen vermeiden
|
7
|
+
- Bitte nur JPG-Dateien (max. Größe: 12 MB) hochladen
|
8
|
+
- Bilder im FTP-Ordner nach spätestens 7 Tagen bearbeiten/entfernen
|
9
|
+
- Ihr verfügbarer FTP-Speicher: 150 MB (bitte nicht überschreiten)
|
10
|
+
- Bitte keine Verzeichnisse im FTP-Ordner anlegen
|
11
|
+
|
12
|
+
TODO: Groesse der Datei testen - ist aber momentan kein kritischer Wert fuer mich
|
13
|
+
=end
|
14
|
+
class Panthermedia < GenericSite
|
15
|
+
|
16
|
+
@@errors = 0
|
17
|
+
|
18
|
+
attr_reader :total_per_day, :user, :password # user/password wird aber eigentlich nicht benoetigt
|
19
|
+
attr_accessor :ftp_user, :ftp_password, :max_errors
|
20
|
+
|
21
|
+
FTP_HOST = 'panthermedia.net'
|
22
|
+
SITENAME = 'panthermedia'
|
23
|
+
|
24
|
+
def initialize(name)
|
25
|
+
super
|
26
|
+
@config = load_config
|
27
|
+
begin
|
28
|
+
@user = @config[:panthermedia][:user]
|
29
|
+
@password = @config[:panthermedia][:password]
|
30
|
+
@ftp_user = @config[:panthermedia][:ftp_user]
|
31
|
+
@ftp_password = @config[:panthermedia][:ftp_password]
|
32
|
+
@total_per_day = @config[:panthermedia][:total_per_day]
|
33
|
+
rescue
|
34
|
+
puts 'cannot read configuration entries of '+SITENAME
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def transfer(photo, dont_send=nil, dont_log=nil)
|
39
|
+
# falls nicht in config Datei eingetragen dann nicht senden
|
40
|
+
if @user == nil or @password == nil
|
41
|
+
return
|
42
|
+
end
|
43
|
+
unless photo.class == Photo
|
44
|
+
raise 'not a Photo object'
|
45
|
+
end
|
46
|
+
unless File.exist?(photo.filename)
|
47
|
+
raise "file #{photo.filename} does not exist"
|
48
|
+
end
|
49
|
+
if @ftp_user == nil or @ftp_password == nil
|
50
|
+
raise "ftp_user/ftp_password not set"
|
51
|
+
end
|
52
|
+
print "#{SITENAME}:#{photo.filename} ... "
|
53
|
+
$stdout.flush
|
54
|
+
if already_sent_site?(photo,SITENAME)
|
55
|
+
puts "already sent"
|
56
|
+
return :duplicate
|
57
|
+
end
|
58
|
+
if photo.portrait? and ! Panthermedia.can_handle_orientation?
|
59
|
+
photo.drehen
|
60
|
+
end
|
61
|
+
if site_can_handle_keywords?
|
62
|
+
photo.set_keywords
|
63
|
+
end
|
64
|
+
if ! dont_send
|
65
|
+
begin
|
66
|
+
ftp = Net::FTP.new(FTP_HOST)
|
67
|
+
ftp.login(@ftp_user,@ftp_password)
|
68
|
+
files=ftp.list('*')
|
69
|
+
if photo.portrait?
|
70
|
+
#p photo.rotated_filename
|
71
|
+
res = ftp.putbinaryfile(photo.rotated_filename)
|
72
|
+
else
|
73
|
+
res = ftp.putbinaryfile(photo.filename)
|
74
|
+
end
|
75
|
+
files =ftp.list('*')
|
76
|
+
#p files
|
77
|
+
ftp.close
|
78
|
+
rescue Errno::EPIPE,Net::FTPTempError
|
79
|
+
raise UploadException
|
80
|
+
end
|
81
|
+
end
|
82
|
+
@@errors = 0
|
83
|
+
puts 'OK'
|
84
|
+
if ! dont_log
|
85
|
+
File.open(SENDLIST,'a') {|f|
|
86
|
+
f.puts "#{SITENAME}\t#{photo.filename}\t#{Time.now}"
|
87
|
+
}
|
88
|
+
end
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
# Anzahl heute schon gesendeter Photos
|
93
|
+
def sent_today
|
94
|
+
site = SITENAME
|
95
|
+
return sent_today_site(site)
|
96
|
+
end
|
97
|
+
|
98
|
+
# wurde dieses Photo schon gesendet?
|
99
|
+
def already_sent?(photo)
|
100
|
+
already_sent_site?(photo,SITENAME)
|
101
|
+
end
|
102
|
+
|
103
|
+
# can site handle orientation?
|
104
|
+
def self.can_handle_orientation?
|
105
|
+
false
|
106
|
+
end
|
107
|
+
|
108
|
+
# Site akzeptiert diese Extension
|
109
|
+
def self.accept?(ext)
|
110
|
+
ext = ext.downcase
|
111
|
+
if ext[0,1] == '.'
|
112
|
+
ext = ext[1..-1]
|
113
|
+
end
|
114
|
+
case ext
|
115
|
+
when 'jpg','jpeg'
|
116
|
+
return true
|
117
|
+
else
|
118
|
+
return false
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def accept?(ext)
|
123
|
+
Panthermedia.accept?(ext)
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def site_can_handle_keywords?
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
end # class
|
@@ -26,6 +26,7 @@ class Photocase < GenericSite
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def transfer(photo,dont_send=false,dont_log=false)
|
29
|
+
$log.debug 'Photocase#transfer BEGIN'
|
29
30
|
sav_photo_filename = photo.filename.dup
|
30
31
|
# falls nicht in config Datei eingetragen dann nicht senden
|
31
32
|
if @user == nil or @password == nil
|
@@ -38,7 +39,7 @@ class Photocase < GenericSite
|
|
38
39
|
unless File.exist?(photo.filename)
|
39
40
|
raise "file #{photo.filename} does not exist"
|
40
41
|
end
|
41
|
-
print "#{SITENAME}:#{photo.filename} ... "
|
42
|
+
print "checking #{SITENAME}:#{photo.filename} ... "
|
42
43
|
$stdout.flush
|
43
44
|
if already_sent_site?(photo,SITENAME)
|
44
45
|
puts 'already sent'
|
@@ -53,58 +54,78 @@ class Photocase < GenericSite
|
|
53
54
|
|
54
55
|
if ! dont_send
|
55
56
|
begin
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
|
57
|
+
agent = WWW::Mechanize.new
|
58
|
+
agent.user_agent_alias = 'Linux Mozilla'
|
59
|
+
page = agent.get 'http://www.photocase.de/en/login.asp'
|
60
|
+
if !page.body.include?('Log in')
|
61
|
+
raise UploadException, 'not at the login page'
|
62
|
+
end
|
63
|
+
form = page.forms.first #TODO: besser nach Name
|
64
|
+
# form['loginForm[Username]'] = @config[:zoonar][:user]
|
65
|
+
# form['loginForm[Password]'] = @config[:zoonar][:password]
|
66
|
+
form['UserName'] = @config[:photocase][:user]
|
67
|
+
form['UserPassword'] = @config[:photocase][:password]
|
68
|
+
#UserName
|
69
|
+
#UserPassword
|
70
|
+
page = agent.submit form
|
71
|
+
# raise "login error" if ! page.body.include?("Login successful")
|
72
|
+
# if ! page.body.include?("Login successful")
|
73
|
+
# obiges kommt anscheinend seit 1.8.2008 nicht mehr auf der Seite.
|
74
|
+
# mit folgendem ersetzt
|
75
|
+
if ! page.body.include?('<div id="loggedIn">')
|
76
|
+
$log.debug page.body
|
77
|
+
raise UploadException, "login error"
|
78
|
+
end
|
79
|
+
# old mechanize version: page = agent.click page.links.text('Upload')
|
80
|
+
page = agent.click page.link_with(:text => 'Upload')
|
81
|
+
if page.body.include?('Upload a photo') or
|
82
|
+
page.body.include?('Foto hochladen')
|
83
|
+
true # alles OK
|
84
|
+
else
|
85
|
+
raise UploadException, "no 'Upload a photo' on page"
|
86
|
+
end
|
87
|
+
$log.debug 'bin auf der Upload-Seite'
|
88
|
+
form = page.form('Form')
|
89
|
+
if photo.portrait?
|
90
|
+
form.file_uploads.first.file_name = photo.rotated_filename
|
91
|
+
# b = File.basename(photo.filename)
|
92
|
+
# newfilename = photo.filename.sub(b,'')+'rot_'+b
|
93
|
+
# # form.file_uploads.first.file_name = 'r' + photo.filename
|
94
|
+
# form.file_uploads.first.file_name = newfilename
|
95
|
+
else
|
96
|
+
form.file_uploads.first.file_name = photo.filename
|
97
|
+
end
|
98
|
+
#TODO:
|
99
|
+
# 2 Checkboxen
|
100
|
+
#UploadDisclaimerAccepted
|
101
|
+
#HasCopyright
|
102
|
+
# old syntax of mechanize gem: form.checkboxes.name('UploadDisclaimerAccepted').check
|
103
|
+
form.checkbox_with(:name => 'UploadDisclaimerAccepted').check
|
104
|
+
# old syntax of mechanize gem: form.checkboxes.name('HasCopyright').check
|
105
|
+
form.checkbox_with(:name => 'HasCopyright').check
|
106
|
+
page = agent.submit form
|
107
|
+
$log.debug 'nach Uploadformular'
|
108
|
+
$log.debug page.body
|
109
|
+
# if !page.body.include?('Your photo was uploaded successfully.') and
|
110
|
+
# !page.body.include?('Dein Foto wurde erfolgreich hochgeladen')
|
111
|
+
# raise "not successfully uploaded"
|
112
|
+
# end
|
113
|
+
if page.body.include?('Your photo was uploaded successfully.') or
|
114
|
+
page.body.include?('Dein Foto wurde erfolgreich hochgeladen')
|
115
|
+
@@errors = 0
|
116
|
+
puts "OK"
|
117
|
+
else
|
118
|
+
#Fehler aufgetreten
|
119
|
+
@@errors += 1
|
120
|
+
if @@errors > 3
|
121
|
+
raise "too many errors"
|
122
|
+
end
|
101
123
|
end
|
102
|
-
end
|
103
124
|
rescue Net::HTTPInternalServerError, WWW::Mechanize::ResponseCodeError
|
104
|
-
# Net::HTTPInternalServerError (WWW::Mechanize::ResponseCodeError)
|
105
|
-
raise UploadException
|
125
|
+
# Net::HTTPInternalServerError (WWW::Mechanize::ResponseCodeError)
|
126
|
+
raise UploadException
|
106
127
|
end # rescue
|
107
|
-
|
128
|
+
else
|
108
129
|
puts "OK"
|
109
130
|
end
|
110
131
|
#'Your photo was uploaded successfully.'
|
@@ -117,6 +138,7 @@ raise UploadException
|
|
117
138
|
f.puts "#{SITENAME}\t#{photo.filename}\t#{Time.now}"
|
118
139
|
}
|
119
140
|
end
|
141
|
+
$log.debug 'Photocase#transfer END'
|
120
142
|
true
|
121
143
|
end
|
122
144
|
|
@@ -129,15 +151,6 @@ raise UploadException
|
|
129
151
|
# wurde dieses Photo schon gesendet?
|
130
152
|
def already_sent?(photo)
|
131
153
|
already_sent_site?(photo,SITENAME)
|
132
|
-
#
|
133
|
-
# unless photo.class == Photo
|
134
|
-
# raise 'not a Photo object'
|
135
|
-
# end
|
136
|
-
# unless File.exist?(photo.filename)
|
137
|
-
# raise "file #{photo.filename} does not exist"
|
138
|
-
# end
|
139
|
-
# g = grep(SENDLIST, /photocase\t#{photo.filename}/)
|
140
|
-
# return g.size > 0
|
141
154
|
end
|
142
155
|
|
143
156
|
# can site handle orientation?
|
@@ -67,7 +67,8 @@ class Zoonar < GenericSite
|
|
67
67
|
|
68
68
|
page = agent.submit form
|
69
69
|
raise "login error" if ! page.body.include?("Hallo, #{@config[:zoonar][:user]}!")
|
70
|
-
page = agent.click page.links.text('Bilder hochladen')
|
70
|
+
# old syntax of mechanize: page = agent.click page.links.text('Bilder hochladen')
|
71
|
+
page = agent.click page.link_with(:text => 'Bilder hochladen')
|
71
72
|
raise "nicht auf Upload-Seite" if ! page.body.include?('bertragen Sie Ihre Bilder einzeln mit Hilfe Ihres Internet-Browsers,')
|
72
73
|
form = page.form('fUploadResource')
|
73
74
|
if photo.portrait?
|
@@ -89,7 +90,7 @@ class Zoonar < GenericSite
|
|
89
90
|
end
|
90
91
|
end
|
91
92
|
@@errors = 0
|
92
|
-
"Es ist ein Fehler aufgetreten!
|
93
|
+
fehlermeldung_falls_bild_zu_klein = "Es ist ein Fehler aufgetreten!
|
93
94
|
|
94
95
|
Bei der Übertragung des Bildes ist ein Fehler aufgetreten.
|
95
96
|
|
data/lib/multistockphoto.rb
CHANGED
@@ -17,6 +17,7 @@ require 'multistockphoto/site_zoonar'
|
|
17
17
|
require 'multistockphoto/site_photocase'
|
18
18
|
require 'multistockphoto/site_dreamstime'
|
19
19
|
require 'multistockphoto/site_bigstockphoto'
|
20
|
+
require 'multistockphoto/site_panthermedia'
|
20
21
|
|
21
22
|
|
22
23
|
require 'multistockphoto/upload_exception'
|