posting_duo 3.111.003 → 3.111.005
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/lib/posting_duo.rb +292 -128
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7059a2e61381d05735169ead0ee105eab4e3b457414fca4afbea96c2d76f1f6
|
4
|
+
data.tar.gz: 2640dcaa0dd2336a40877213eb3f3dd6b3953dc6f32d7770ed36abf31caedb7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b41153ffd4e39f2d2f8d2f9928c8cf8eb0af6497c8b9186b4ceb36bc4a6fff3633521f1f131d9a072d29246e0be4a8ed4ceca90a5427089228d8046ed3751f81
|
7
|
+
data.tar.gz: c2c833d13f58e5379c33160e43c0064ea3b25c7d1d87538111ac823ad0455752544b80726474af6cb291d7f76dc39d53657f8aedcc6e11cf50b0cb8568516cb6
|
data/lib/posting_duo.rb
CHANGED
@@ -13087,82 +13087,109 @@ class Wordpress
|
|
13087
13087
|
|
13088
13088
|
|
13089
13089
|
|
13090
|
+
def crop_image_height_under_width(path, min_crop_ratio = 0.625)
|
13091
|
+
img = Magick::Image.read(path).first
|
13092
|
+
width = img.columns
|
13093
|
+
height = img.rows
|
13094
|
+
|
13095
|
+
|
13096
|
+
|
13097
|
+
if height > width
|
13098
|
+
min_height = (width * min_crop_ratio).to_i
|
13099
|
+
new_height = rand(min_height..width)
|
13100
|
+
crop_top = ((height - new_height) / 2.0).round
|
13101
|
+
|
13102
|
+
cropped = img.crop(0, crop_top, width, new_height, true)
|
13103
|
+
cropped.write(path)
|
13104
|
+
|
13105
|
+
|
13106
|
+
else
|
13107
|
+
|
13108
|
+
end
|
13109
|
+
end
|
13110
|
+
|
13090
13111
|
def auto_image(keyword = nil)
|
13091
|
-
|
13092
|
-
|
13093
|
-
|
13094
|
-
|
13095
|
-
|
13096
|
-
|
13097
|
-
|
13098
|
-
|
13099
|
-
|
13100
|
-
|
13101
|
-
|
13102
|
-
|
13112
|
+
# auto_image 내부에서만 crop 호출
|
13113
|
+
keyword ||= @keyword
|
13114
|
+
puts "키워드: #{keyword}"
|
13115
|
+
|
13116
|
+
client = HTTPClient.new
|
13117
|
+
client.default_header = {
|
13118
|
+
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '\
|
13119
|
+
'(KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',
|
13120
|
+
'Accept' => 'application/json, text/javascript, */*; q=0.01',
|
13121
|
+
'Accept-Language' => 'en-US,en;q=0.9',
|
13122
|
+
'Referer' => "https://unsplash.com/s/photos/#{URI.encode_www_form_component(keyword)}",
|
13123
|
+
'X-Requested-With' => 'XMLHttpRequest'
|
13124
|
+
}
|
13103
13125
|
|
13104
|
-
|
13105
|
-
|
13106
|
-
|
13126
|
+
retry_count = 0
|
13127
|
+
max_retries = 10
|
13128
|
+
results = []
|
13107
13129
|
|
13108
|
-
|
13109
|
-
|
13110
|
-
|
13111
|
-
|
13112
|
-
|
13113
|
-
|
13114
|
-
|
13115
|
-
|
13116
|
-
|
13117
|
-
|
13130
|
+
begin
|
13131
|
+
page = rand(1..15)
|
13132
|
+
url = "https://unsplash.com/napi/search/photos?query=#{URI.encode_www_form_component(keyword)}&page=#{page}&per_page=20"
|
13133
|
+
puts "Request URL: #{url}"
|
13134
|
+
res = client.get(url)
|
13135
|
+
|
13136
|
+
unless res.status == 200
|
13137
|
+
puts "HTTP Error: #{res.status}"
|
13138
|
+
raise "HTTP Error"
|
13139
|
+
end
|
13118
13140
|
|
13119
|
-
|
13120
|
-
|
13121
|
-
|
13141
|
+
json = JSON.parse(res.body)
|
13142
|
+
results = json['results']
|
13143
|
+
mm = []
|
13122
13144
|
|
13123
|
-
|
13124
|
-
|
13125
|
-
|
13145
|
+
results.each do |photo|
|
13146
|
+
full_url = photo.dig('urls', 'full').to_s
|
13147
|
+
regular_url = photo.dig('urls', 'regular').to_s
|
13126
13148
|
|
13127
|
-
|
13128
|
-
|
13129
|
-
|
13130
|
-
|
13131
|
-
|
13149
|
+
if full_url.start_with?("https://images.unsplash.com/photo-") &&
|
13150
|
+
regular_url.include?("1080")
|
13151
|
+
mm << full_url
|
13152
|
+
end
|
13153
|
+
end
|
13132
13154
|
|
13133
|
-
|
13134
|
-
|
13135
|
-
|
13155
|
+
if mm.empty?
|
13156
|
+
raise "No matching image"
|
13157
|
+
end
|
13136
13158
|
|
13137
|
-
|
13138
|
-
|
13139
|
-
|
13159
|
+
selected_url = mm.sample
|
13160
|
+
destination_path = "./image/memory.png"
|
13161
|
+
Down.download(selected_url, destination: destination_path)
|
13162
|
+
puts "이미지 다운로드 완료: #{selected_url}"
|
13140
13163
|
|
13141
|
-
|
13142
|
-
|
13143
|
-
|
13144
|
-
|
13145
|
-
|
13146
|
-
|
13147
|
-
|
13148
|
-
|
13149
|
-
|
13150
|
-
|
13151
|
-
|
13152
|
-
|
13153
|
-
|
13154
|
-
|
13155
|
-
|
13156
|
-
|
13157
|
-
|
13158
|
-
|
13159
|
-
|
13164
|
+
# 오직 auto_image에서만 자르기 호출
|
13165
|
+
crop_image_height_under_width(destination_path)
|
13166
|
+
|
13167
|
+
rescue => e
|
13168
|
+
retry_count += 1
|
13169
|
+
puts "auto_image 에러: #{e.message} (재시도 #{retry_count}/#{max_retries})"
|
13170
|
+
sleep(3)
|
13171
|
+
if retry_count < max_retries
|
13172
|
+
retry
|
13173
|
+
else
|
13174
|
+
puts "최대 재시도 초과. 조건 무시하고 랜덤 이미지 다운로드 시도..."
|
13175
|
+
|
13176
|
+
if results && !results.empty?
|
13177
|
+
random_photo = results.sample
|
13178
|
+
fallback_url = random_photo.dig('urls', 'full')
|
13179
|
+
if fallback_url
|
13180
|
+
Down.download(fallback_url, destination: "./image/memory.png")
|
13181
|
+
puts "랜덤 이미지 다운로드 완료: #{fallback_url}"
|
13182
|
+
crop_image_height_under_width("./image/memory.png")
|
13160
13183
|
else
|
13161
|
-
|
13162
|
-
|
13163
|
-
end
|
13184
|
+
puts "랜덤 이미지 URL을 찾을 수 없습니다. 단색 배경 이미지 생성합니다."
|
13185
|
+
color_image
|
13164
13186
|
end
|
13187
|
+
else
|
13188
|
+
puts "이미지 결과가 없어 다운로드할 수 없습니다. 단색 배경 이미지 생성합니다."
|
13189
|
+
color_image
|
13165
13190
|
end
|
13191
|
+
end
|
13192
|
+
end
|
13166
13193
|
end
|
13167
13194
|
|
13168
13195
|
|
@@ -13174,79 +13201,187 @@ class Wordpress
|
|
13174
13201
|
|
13175
13202
|
def save_image
|
13176
13203
|
if @data['이미지설정']['이미지'].length == 0
|
13177
|
-
|
13204
|
+
return
|
13205
|
+
end
|
13206
|
+
|
13207
|
+
if @data['이미지설정']['순서사용'].checked?
|
13208
|
+
image_path = @data['이미지설정']['이미지'][@image_counter][2]
|
13209
|
+
@image_counter += 1
|
13210
|
+
if @image_counter > @data['이미지설정']['이미지'].length - 1
|
13211
|
+
@image_counter = 0
|
13212
|
+
end
|
13178
13213
|
else
|
13179
|
-
|
13180
|
-
|
13181
|
-
|
13182
|
-
|
13183
|
-
@image_counter = 0
|
13184
|
-
end
|
13185
|
-
else
|
13186
|
-
image_path = @data['이미지설정']['이미지'].sample[2]
|
13214
|
+
# 초기화가 안됐거나 다 썼으면 새롭게 섞는다
|
13215
|
+
@shuffled_images ||= []
|
13216
|
+
if @shuffled_images.empty?
|
13217
|
+
@shuffled_images = @data['이미지설정']['이미지'].shuffle
|
13187
13218
|
end
|
13188
|
-
|
13189
|
-
|
13219
|
+
|
13220
|
+
image_path = @shuffled_images.shift[2]
|
13190
13221
|
end
|
13222
|
+
|
13223
|
+
img = Magick::Image.read(image_path).first
|
13224
|
+
img.write('./image/memory.png')
|
13191
13225
|
end
|
13192
13226
|
|
13193
13227
|
def change_image_size(w)
|
13194
13228
|
img = Magick::Image.read('./image/memory.png').first
|
13195
13229
|
width = img.columns
|
13196
13230
|
height = img.rows
|
13197
|
-
|
13198
|
-
|
13199
|
-
|
13200
|
-
|
13201
|
-
|
13231
|
+
|
13232
|
+
# '원본'이 선택된 경우, 리사이징을 하지 않고 원본 이미지를 그대로 반환
|
13233
|
+
if w == 'original'
|
13234
|
+
return img # 원본 이미지 그대로 반환
|
13235
|
+
else
|
13236
|
+
begin
|
13237
|
+
if @data['image_type'][0].checked? or @data['image_type'][2].checked?
|
13238
|
+
# 비율을 맞추어 리사이징
|
13239
|
+
img.resize!(w, w * (height.to_f / width.to_f))
|
13240
|
+
else
|
13241
|
+
# 정사각형으로 리사이징
|
13242
|
+
img.resize!(w, w)
|
13243
|
+
end
|
13244
|
+
rescue
|
13245
|
+
img.resize!(w, w) # 예외 처리 시에도 리사이징
|
13202
13246
|
end
|
13203
|
-
rescue
|
13204
|
-
img.resize!(w, w)
|
13205
13247
|
end
|
13248
|
+
|
13249
|
+
# 리사이징된 이미지 저장
|
13206
13250
|
img.write('./image/memory.png')
|
13207
13251
|
end
|
13208
13252
|
|
13209
|
-
def
|
13210
|
-
|
13211
|
-
|
13212
|
-
|
13213
|
-
|
13214
|
-
|
13215
|
-
|
13216
|
-
|
13217
|
-
|
13218
|
-
|
13219
|
-
|
13220
|
-
|
13221
|
-
|
13222
|
-
|
13223
|
-
|
13224
|
-
|
13225
|
-
|
13226
|
-
|
13227
|
-
text.fill = '#000000'
|
13228
|
-
text.font = font2
|
13253
|
+
def wrap_text_to_fit(draw, text, max_width, max_height, font_path, initial_size)
|
13254
|
+
size = initial_size
|
13255
|
+
draw.font = font_path
|
13256
|
+
|
13257
|
+
loop do
|
13258
|
+
draw.pointsize = size
|
13259
|
+
words = text.chars # 글자 단위로 자름 (한국어 기준)
|
13260
|
+
lines = []
|
13261
|
+
line = ""
|
13262
|
+
|
13263
|
+
words.each do |char|
|
13264
|
+
test_line = line + char
|
13265
|
+
metrics = draw.get_type_metrics(test_line)
|
13266
|
+
if metrics.width > max_width
|
13267
|
+
lines << line
|
13268
|
+
line = char
|
13269
|
+
else
|
13270
|
+
line = test_line
|
13229
13271
|
end
|
13230
13272
|
end
|
13231
|
-
|
13232
|
-
|
13233
|
-
|
13234
|
-
|
13235
|
-
|
13236
|
-
|
13237
|
-
|
13238
|
-
|
13239
|
-
|
13240
|
-
|
13273
|
+
lines << line unless line.empty?
|
13274
|
+
|
13275
|
+
line_height = draw.get_type_metrics("가").height
|
13276
|
+
total_height = line_height * lines.size
|
13277
|
+
|
13278
|
+
# 세로 초과 안 하면 성공
|
13279
|
+
if total_height <= max_height || size <= 10
|
13280
|
+
return [lines.join("\n"), size]
|
13281
|
+
else
|
13282
|
+
size -= 2
|
13241
13283
|
end
|
13284
|
+
end
|
13285
|
+
end
|
13286
|
+
|
13287
|
+
|
13288
|
+
def image_text(text1, text2)
|
13289
|
+
begin
|
13290
|
+
color = File.open('./color.ini', 'r', encoding: 'utf-8').read.split("\n").map(&:strip).reject(&:empty?)
|
13291
|
+
font_files = Dir.entries('./fonts').select { |f| f.downcase.end_with?('.ttf') }
|
13292
|
+
font2 = './fonts/' + font_files.sample
|
13242
13293
|
|
13243
|
-
|
13294
|
+
# 랜덤 글자색 선택
|
13295
|
+
color2 = color.sample
|
13296
|
+
|
13297
|
+
# 헬퍼 함수: 색상 문자열 '#RRGGBB' -> [R,G,B] 배열로 변환
|
13298
|
+
def hex_to_rgb(hex)
|
13299
|
+
hex = hex.delete('#')
|
13300
|
+
[hex[0..1], hex[2..3], hex[4..5]].map { |c| c.to_i(16) }
|
13301
|
+
end
|
13302
|
+
|
13303
|
+
# 헬퍼 함수: 두 RGB 색상의 차이 계산 (간단한 유클리드 거리)
|
13304
|
+
def color_distance(c1, c2)
|
13305
|
+
Math.sqrt(
|
13306
|
+
(c1[0] - c2[0])**2 +
|
13307
|
+
(c1[1] - c2[1])**2 +
|
13308
|
+
(c1[2] - c2[2])**2
|
13309
|
+
)
|
13310
|
+
end
|
13311
|
+
|
13312
|
+
# 대비가 충분히 되는 테두리 색상 선택
|
13313
|
+
max_attempts = 10
|
13314
|
+
stroke_color = nil
|
13315
|
+
base_rgb = hex_to_rgb(color2)
|
13316
|
+
|
13317
|
+
max_attempts.times do
|
13318
|
+
candidate = color.sample
|
13319
|
+
candidate_rgb = hex_to_rgb(candidate)
|
13320
|
+
dist = color_distance(base_rgb, candidate_rgb)
|
13321
|
+
|
13322
|
+
# 거리(차이) 임계값 100 (0~441 범위) — 필요시 조절 가능
|
13323
|
+
if dist > 100
|
13324
|
+
stroke_color = candidate
|
13325
|
+
break
|
13326
|
+
end
|
13327
|
+
end
|
13328
|
+
stroke_color ||= '#000000' # 만약 충분히 다른 색 없으면 검정색 기본값
|
13329
|
+
|
13330
|
+
img = Magick::Image.read('./image/memory.png').first
|
13331
|
+
draw = Magick::Draw.new
|
13332
|
+
|
13333
|
+
raw_message = "#{text1}\n#{text2}".strip
|
13334
|
+
max_width = img.columns * 0.85
|
13335
|
+
max_height = img.rows * 0.6
|
13336
|
+
|
13337
|
+
begin
|
13338
|
+
size = rand(@data['이미지설정']['이미지글자1크기1'].text.to_i..@data['이미지설정']['이미지글자1크기2'].text.to_i)
|
13244
13339
|
rescue
|
13245
|
-
|
13246
|
-
|
13247
|
-
|
13340
|
+
size = 30
|
13341
|
+
end
|
13342
|
+
|
13343
|
+
wrapped_message, adjusted_size = wrap_text_to_fit(draw, raw_message, max_width, max_height, font2, size)
|
13344
|
+
|
13345
|
+
if @data['이미지설정']['글자그림자'].checked?
|
13346
|
+
img.annotate(draw, 0, 0, 2, 2, wrapped_message) do
|
13347
|
+
draw.gravity = Magick::CenterGravity
|
13348
|
+
draw.pointsize = adjusted_size
|
13349
|
+
draw.fill = '#000000'
|
13350
|
+
draw.font = font2
|
13351
|
+
end
|
13248
13352
|
end
|
13353
|
+
|
13354
|
+
if @data['이미지설정']['글자테두리'].checked?
|
13355
|
+
draw_stroke = Magick::Draw.new
|
13356
|
+
img.annotate(draw_stroke, 0, 0, 0, 0, wrapped_message) do
|
13357
|
+
draw_stroke.gravity = Magick::CenterGravity
|
13358
|
+
draw_stroke.pointsize = adjusted_size
|
13359
|
+
draw_stroke.fill = 'none'
|
13360
|
+
draw_stroke.stroke = stroke_color
|
13361
|
+
draw_stroke.stroke_width = rand(5..10)
|
13362
|
+
draw_stroke.font = font2
|
13363
|
+
end
|
13364
|
+
end
|
13365
|
+
|
13366
|
+
draw2 = Magick::Draw.new
|
13367
|
+
img.annotate(draw2, 0, 0, 0, 0, wrapped_message) do
|
13368
|
+
draw2.gravity = Magick::CenterGravity
|
13369
|
+
draw2.pointsize = adjusted_size
|
13370
|
+
draw2.fill = color2
|
13371
|
+
draw2.stroke = 'none'
|
13372
|
+
draw2.font = font2
|
13373
|
+
end
|
13374
|
+
|
13375
|
+
img.write('./image/memory.png')
|
13376
|
+
|
13377
|
+
rescue => e
|
13378
|
+
puts "이미지 폰트 불러오기 오류 재시도... (#{e.message})"
|
13379
|
+
sleep(3)
|
13380
|
+
retry
|
13249
13381
|
end
|
13382
|
+
end
|
13383
|
+
|
13384
|
+
|
13250
13385
|
|
13251
13386
|
def border()
|
13252
13387
|
color = File.open('./color.ini', 'r',:encoding => 'utf-8').read().split("\n")
|
@@ -13273,40 +13408,69 @@ class Wordpress
|
|
13273
13408
|
else
|
13274
13409
|
auto_image()
|
13275
13410
|
end
|
13276
|
-
|
13277
|
-
|
13411
|
+
|
13412
|
+
# '원본'을 포함한 이미지 크기 옵션 추가
|
13413
|
+
image_size = [480, 740, 650, 550, 480, 'original']
|
13278
13414
|
size = 0
|
13279
|
-
|
13415
|
+
|
13416
|
+
for n in 0..5 # 0부터 5까지 반복, '원본' 옵션까지 포함
|
13280
13417
|
if @data['image_size'][n].checked?
|
13281
|
-
if n ==
|
13282
|
-
size =
|
13418
|
+
if n == 5 # '원본'이 선택되었을 경우
|
13419
|
+
size = 'original'
|
13420
|
+
elsif n == 0
|
13421
|
+
size = image_size.sample # 랜덤 선택
|
13283
13422
|
else
|
13284
13423
|
size = image_size[n]
|
13285
13424
|
end
|
13286
13425
|
end
|
13287
13426
|
end
|
13427
|
+
|
13428
|
+
# '원본'이 선택되지 않았다면 기본 값 설정
|
13288
13429
|
if size == 0
|
13289
13430
|
size = 480
|
13290
13431
|
end
|
13432
|
+
|
13433
|
+
change_image_size(size) # 크기 변경 함수 호출
|
13291
13434
|
|
13292
|
-
change_image_size(size)
|
13293
13435
|
|
13294
13436
|
if @data['이미지설정']['필터사용'].checked?
|
13295
13437
|
image_filter()
|
13296
13438
|
end
|
13297
13439
|
|
13298
|
-
insert_image_text1 = ''
|
13299
|
-
insert_image_text2 = ''
|
13300
13440
|
if @data['이미지설정']['글자삽입1'].checked?
|
13301
|
-
|
13441
|
+
if @data['이미지설정']['이미지글자1'].length == 0
|
13442
|
+
image_text_path1 = ''
|
13443
|
+
else
|
13444
|
+
if @data['이미지설정']['글자랜덤'].checked?
|
13445
|
+
image_text_path1 = @data['이미지설정']['이미지글자1'].sample
|
13446
|
+
else
|
13447
|
+
image_text_path1 = @data['이미지설정']['이미지글자1'][@image_text_soon1]
|
13448
|
+
@image_text_soon1 += 1
|
13449
|
+
if @image_text_soon1 > @data['이미지설정']['이미지글자1'].length - 1
|
13450
|
+
@image_text_soon1 = 0
|
13451
|
+
end
|
13452
|
+
end
|
13453
|
+
end
|
13302
13454
|
end
|
13303
13455
|
|
13304
13456
|
if @data['이미지설정']['글자삽입2'].checked?
|
13305
|
-
|
13457
|
+
if @data['이미지설정']['이미지글자2'].length == 0
|
13458
|
+
image_text_path2 = ''
|
13459
|
+
else
|
13460
|
+
if @data['이미지설정']['글자랜덤'].checked?
|
13461
|
+
image_text_path2 = @data['이미지설정']['이미지글자2'].sample
|
13462
|
+
else
|
13463
|
+
image_text_path2 = @data['이미지설정']['이미지글자2'][@image_text_soon2]
|
13464
|
+
@image_text_soon2 += 1
|
13465
|
+
if @image_text_soon2 > @data['이미지설정']['이미지글자2'].length - 1
|
13466
|
+
@image_text_soon2 = 0
|
13467
|
+
end
|
13468
|
+
end
|
13469
|
+
end
|
13306
13470
|
end
|
13307
|
-
|
13471
|
+
|
13308
13472
|
if @data['이미지설정']['글자삽입1'].checked? or @data['이미지설정']['글자삽입2'].checked?
|
13309
|
-
image_text(
|
13473
|
+
image_text(image_text_path1, image_text_path2)
|
13310
13474
|
end
|
13311
13475
|
|
13312
13476
|
if @data['이미지설정']['테두리사용'].checked?
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posting_duo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.111.
|
4
|
+
version: 3.111.005
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zon
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-06-
|
10
|
+
date: 2025-06-26 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: File to Clipboard gem
|
13
13
|
email: mymin26@naver.com
|