meme_captain 0.0.8 → 0.0.9
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/.gitignore +2 -1
- data/ChangeLog +33 -0
- data/README.md +4 -0
- data/bin/memecaptain +68 -0
- data/config.ru +13 -0
- data/lib/meme_captain/caption_choice.rb +12 -5
- data/lib/meme_captain/draw.rb +7 -5
- data/lib/meme_captain/image_list/cache.rb +49 -0
- data/lib/meme_captain/image_list/fetch.rb +26 -0
- data/lib/meme_captain/image_list/source_image.rb +28 -0
- data/lib/meme_captain/image_list/watermark.rb +24 -0
- data/lib/meme_captain/image_list.rb +5 -0
- data/lib/meme_captain/meme.rb +3 -2
- data/lib/meme_captain/meme_data.rb +34 -0
- data/lib/meme_captain/server.rb +124 -41
- data/lib/meme_captain/version.rb +1 -1
- data/lib/meme_captain.rb +2 -2
- data/meme_captain.gemspec +4 -0
- data/public/source_images.json +142 -0
- data/public/thumbs.jpg +0 -0
- data/script/thumb_sprites.rb +17 -4
- data/views/404.erb +17 -0
- data/views/index.erb +100 -83
- data/watermark.png +0 -0
- metadata +64 -15
- data/img_cache/source/.gitignore +0 -0
- data/lib/meme_captain/filesystem_cache.rb +0 -70
- data/lib/meme_captain/mime_type.rb +0 -20
data/.gitignore
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
img_cache/*
|
|
1
|
+
img_cache/*
|
|
2
|
+
*~
|
data/ChangeLog
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
0.0.9 2012-01-19
|
|
2
|
+
|
|
3
|
+
Design and markup improvements on the site.
|
|
4
|
+
|
|
5
|
+
Allow use of all type metrics when comparing caption choices.
|
|
6
|
+
|
|
7
|
+
Add ability to use the ids in the URLs of generated images to make
|
|
8
|
+
new images from the same source by using the id as the source image
|
|
9
|
+
URL (an example id is abcdef.jpg).
|
|
10
|
+
|
|
11
|
+
Add a 404 page to the site.
|
|
12
|
+
|
|
13
|
+
Deprecate concept of temporary URLs and permanent URLs for images
|
|
14
|
+
generated from the site.
|
|
15
|
+
|
|
16
|
+
Use MongoDB for the site datastore.
|
|
17
|
+
|
|
18
|
+
Add "memecaptain" executable for creating memes using the gem from the
|
|
19
|
+
command line.
|
|
20
|
+
|
|
21
|
+
Add a watermark to images created on the site.
|
|
22
|
+
|
|
23
|
+
Shrink large source images to a maximum of 800 pixels per side for
|
|
24
|
+
images created on the site.
|
|
25
|
+
|
|
26
|
+
Define local source images in JSON instead of in the template and load
|
|
27
|
+
them with Ajax.
|
|
28
|
+
|
|
29
|
+
Add Google Image Search to the site.
|
|
30
|
+
|
|
31
|
+
Add new source images to the site: all the things 2, cool story bro,
|
|
32
|
+
aw yeah, Boromir, Ned Stark.
|
|
33
|
+
|
|
1
34
|
0.0.8
|
|
2
35
|
2011-11-23
|
|
3
36
|
|
data/README.md
CHANGED
|
@@ -47,6 +47,10 @@ Example:
|
|
|
47
47
|
http://memecaptain.com/g?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
Note: tempUrl is deprecated and will now always be the same as permUrl. It is
|
|
51
|
+
left for compability with older clients.
|
|
52
|
+
|
|
53
|
+
|
|
50
54
|
```json
|
|
51
55
|
{
|
|
52
56
|
permUrl: "http://memecaptain.com/i?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&tt=sure+i%27ll+test&tb=the+api"
|
data/bin/memecaptain
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'mime/types'
|
|
4
|
+
require 'open-uri'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
|
|
7
|
+
require 'meme_captain'
|
|
8
|
+
|
|
9
|
+
options = {}
|
|
10
|
+
|
|
11
|
+
option_parser = OptionParser.new do |opts|
|
|
12
|
+
opts.banner = <<-eos
|
|
13
|
+
Usage: memecaptain INPUT_IMAGE [OPTIONS]
|
|
14
|
+
|
|
15
|
+
INPUT_IMAGE can be a file path, URL or - for stdin.
|
|
16
|
+
|
|
17
|
+
eos
|
|
18
|
+
|
|
19
|
+
opts.on('-t', '--top-text TEXT', 'top text') do |text|
|
|
20
|
+
options[:top_text] = text
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
opts.on('-b', '--bottom-text TEXT', 'bottom text') do |text|
|
|
24
|
+
options[:bottom_text] = text
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
opts.on('-o', '--output PATH', 'output path (- for stdout)') do |path|
|
|
28
|
+
options[:output] = path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
opts.on('-f', '--font FONT', 'font') do |font|
|
|
32
|
+
options[:font] = font
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
option_parser.parse!
|
|
37
|
+
|
|
38
|
+
unless ARGV.empty?
|
|
39
|
+
input = ARGV[0]
|
|
40
|
+
|
|
41
|
+
input_io = if input == '-'
|
|
42
|
+
$stdin
|
|
43
|
+
else
|
|
44
|
+
open input, 'rb'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
meme_options = options.select do |k,v|
|
|
48
|
+
[
|
|
49
|
+
:font,
|
|
50
|
+
].include? k
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
output_image = MemeCaptain.meme(
|
|
54
|
+
input_io, options[:top_text], options[:bottom_text], meme_options)
|
|
55
|
+
|
|
56
|
+
input_io.close
|
|
57
|
+
|
|
58
|
+
output = if options[:output]
|
|
59
|
+
options[:output] == '-' ? $stdout : options[:output]
|
|
60
|
+
else
|
|
61
|
+
"meme.#{MIME::Types[output_image.mime_type][0].extensions[0]}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
output_image.write(output) { self.quality = 100 }
|
|
65
|
+
else
|
|
66
|
+
$stderr.puts option_parser
|
|
67
|
+
exit 1
|
|
68
|
+
end
|
data/config.ru
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
|
2
2
|
|
|
3
|
+
require 'mongo'
|
|
4
|
+
require 'mongo_mapper'
|
|
3
5
|
require 'rack'
|
|
4
6
|
|
|
5
7
|
require 'meme_captain'
|
|
@@ -9,4 +11,15 @@ use Rack::Sendfile
|
|
|
9
11
|
|
|
10
12
|
use Rack::Static, :urls => %w{/tmp}, :root => 'public'
|
|
11
13
|
|
|
14
|
+
MongoMapper.connection = Mongo::Connection.new
|
|
15
|
+
MongoMapper.database = 'memecaptain'
|
|
16
|
+
|
|
17
|
+
MemeCaptain::MemeData.ensure_index :meme_id
|
|
18
|
+
|
|
19
|
+
MemeCaptain::MemeData.ensure_index [
|
|
20
|
+
[:source_url, 1],
|
|
21
|
+
[:top_text, 1],
|
|
22
|
+
[:bottom_text, 1],
|
|
23
|
+
]
|
|
24
|
+
|
|
12
25
|
run MemeCaptain::Server
|
|
@@ -4,18 +4,24 @@ module MemeCaptain
|
|
|
4
4
|
class CaptionChoice
|
|
5
5
|
include Comparable
|
|
6
6
|
|
|
7
|
-
def initialize(
|
|
8
|
-
@fits = fits
|
|
7
|
+
def initialize(pointsize, metrics, text, bound_width, bound_height)
|
|
9
8
|
@pointsize = pointsize
|
|
9
|
+
@metrics = metrics
|
|
10
10
|
@text = text
|
|
11
|
+
@bound_width = bound_width
|
|
12
|
+
@bound_height = bound_height
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def num_lines
|
|
14
16
|
text.count("\n") + 1
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
def fits
|
|
20
|
+
metrics.width <= bound_width and metrics.height <= bound_height
|
|
21
|
+
end
|
|
22
|
+
|
|
17
23
|
def fits_i
|
|
18
|
-
fits ? 1: 0
|
|
24
|
+
fits ? 1 : 0
|
|
19
25
|
end
|
|
20
26
|
|
|
21
27
|
def <=>(other)
|
|
@@ -24,10 +30,11 @@ module MemeCaptain
|
|
|
24
30
|
other.fits ? -other.num_lines : other.num_lines]
|
|
25
31
|
end
|
|
26
32
|
|
|
27
|
-
attr_accessor :fits
|
|
28
33
|
attr_accessor :pointsize
|
|
34
|
+
attr_accessor :metrics
|
|
29
35
|
attr_accessor :text
|
|
30
|
-
|
|
36
|
+
attr_accessor :bound_width
|
|
37
|
+
attr_accessor :bound_height
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
end
|
data/lib/meme_captain/draw.rb
CHANGED
|
@@ -6,20 +6,22 @@ module MemeCaptain
|
|
|
6
6
|
# Calculate the largest pointsize for text that will be in a width x
|
|
7
7
|
# height box.
|
|
8
8
|
#
|
|
9
|
-
# Return [pointsize,
|
|
10
|
-
#
|
|
9
|
+
# Return [pointsize, metrics] where pointsize is the largest pointsize and
|
|
10
|
+
# metrics is the RMagick multiline type metrics of the best fit.
|
|
11
11
|
def calc_pointsize(width, height, text, min_pointsize)
|
|
12
12
|
current_pointsize = min_pointsize
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
metrics = nil
|
|
15
15
|
|
|
16
16
|
loop {
|
|
17
17
|
self.pointsize = current_pointsize
|
|
18
|
+
last_metrics = metrics
|
|
18
19
|
metrics = get_multiline_type_metrics(text)
|
|
20
|
+
|
|
19
21
|
if metrics.width > width or metrics.height > height
|
|
20
22
|
if current_pointsize > min_pointsize
|
|
21
23
|
current_pointsize -= 1
|
|
22
|
-
|
|
24
|
+
metrics = last_metrics
|
|
23
25
|
end
|
|
24
26
|
break
|
|
25
27
|
else
|
|
@@ -27,7 +29,7 @@ module MemeCaptain
|
|
|
27
29
|
end
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
[current_pointsize,
|
|
32
|
+
[current_pointsize, metrics]
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'digest/sha1'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'mime/types'
|
|
4
|
+
|
|
5
|
+
module MemeCaptain
|
|
6
|
+
|
|
7
|
+
module ImageList
|
|
8
|
+
|
|
9
|
+
# Mix-in for Magick::ImageList to add saving to the filesystem based on a
|
|
10
|
+
# hash.
|
|
11
|
+
module Cache
|
|
12
|
+
|
|
13
|
+
# Get the extension for this image.
|
|
14
|
+
def extension
|
|
15
|
+
{
|
|
16
|
+
'image/jpeg' => 'jpg',
|
|
17
|
+
}[mime_type] || MIME::Types[mime_type][0].extensions[0]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Store this image in the filesystem and return its path.
|
|
21
|
+
def cache(hash_base, dir)
|
|
22
|
+
hashe = Digest::SHA1.hexdigest(hash_base)
|
|
23
|
+
|
|
24
|
+
cache_dir = File.join(dir, hashe[0,3])
|
|
25
|
+
FileUtils.mkdir_p cache_dir
|
|
26
|
+
|
|
27
|
+
file_part = hashe[3..-1]
|
|
28
|
+
fs_path = File.join(cache_dir, "#{file_part}.#{extension}")
|
|
29
|
+
|
|
30
|
+
# If there is a collision add 0's until the filename is unique.
|
|
31
|
+
zeroes = 0
|
|
32
|
+
while File.exist? fs_path
|
|
33
|
+
zeroes += 1
|
|
34
|
+
fs_path = File.join(cache_dir,
|
|
35
|
+
"#{file_part}#{'0' * zeroes}.#{extension}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
write(fs_path) {
|
|
39
|
+
self.quality = 100
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fs_path
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'curb'
|
|
2
|
+
|
|
3
|
+
module MemeCaptain
|
|
4
|
+
|
|
5
|
+
module ImageList
|
|
6
|
+
|
|
7
|
+
# Mix-in for Magick::ImageList to add loading from a URL.
|
|
8
|
+
module Fetch
|
|
9
|
+
|
|
10
|
+
# Load this image from a URL.
|
|
11
|
+
def fetch!(url)
|
|
12
|
+
curl = Curl::Easy.perform(url) do |c|
|
|
13
|
+
c.useragent = 'Meme Captain http://memecaptain.com/'
|
|
14
|
+
end
|
|
15
|
+
unless curl.response_code == 200
|
|
16
|
+
raise "Error loading source image url #{url}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
from_blob curl.body_str
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'RMagick'
|
|
2
|
+
|
|
3
|
+
module MemeCaptain
|
|
4
|
+
|
|
5
|
+
module ImageList
|
|
6
|
+
|
|
7
|
+
# Source image for meme generation.
|
|
8
|
+
class SourceImage < Magick::ImageList
|
|
9
|
+
include Cache
|
|
10
|
+
include Fetch
|
|
11
|
+
include Watermark
|
|
12
|
+
|
|
13
|
+
# Shrink image if necessary and add watermark.
|
|
14
|
+
def prepare!(max_side, watermark_img)
|
|
15
|
+
if size == 1 and (columns > max_side or rows > max_side)
|
|
16
|
+
resize_to_fit! max_side
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
watermark_mc watermark_img
|
|
20
|
+
|
|
21
|
+
strip!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'RMagick'
|
|
2
|
+
|
|
3
|
+
module MemeCaptain
|
|
4
|
+
|
|
5
|
+
module ImageList
|
|
6
|
+
|
|
7
|
+
# Mix-in for Magick::ImageList to add watermark.
|
|
8
|
+
module Watermark
|
|
9
|
+
|
|
10
|
+
# Watermark this image using another image.
|
|
11
|
+
def watermark_mc(watermark_img)
|
|
12
|
+
self.each do |frame|
|
|
13
|
+
frame.composite!(watermark_img, Magick::SouthEastGravity,
|
|
14
|
+
-frame.page.width + frame.columns + frame.page.x,
|
|
15
|
+
-frame.page.height + frame.rows + frame.page.y,
|
|
16
|
+
Magick::OverCompositeOp)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
data/lib/meme_captain/meme.rb
CHANGED
|
@@ -69,10 +69,11 @@ module MemeCaptain
|
|
|
69
69
|
}.uniq
|
|
70
70
|
|
|
71
71
|
choices = wrap_tries.map do |wrap_try|
|
|
72
|
-
pointsize,
|
|
72
|
+
pointsize, metrics = draw.calc_pointsize(
|
|
73
73
|
text_width, text_height, wrap_try, min_pointsize)
|
|
74
74
|
|
|
75
|
-
CaptionChoice.new(
|
|
75
|
+
CaptionChoice.new(pointsize, metrics, wrap_try, text_width,
|
|
76
|
+
text_height)
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
choice = choices.max
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'mongo_mapper'
|
|
2
|
+
|
|
3
|
+
module MemeCaptain
|
|
4
|
+
|
|
5
|
+
class MemeData
|
|
6
|
+
include MongoMapper::Document
|
|
7
|
+
|
|
8
|
+
set_collection_name 'meme'
|
|
9
|
+
|
|
10
|
+
key :meme_id, String
|
|
11
|
+
key :fs_path, String
|
|
12
|
+
key :mime_type, String
|
|
13
|
+
key :size, Integer
|
|
14
|
+
|
|
15
|
+
key :source_url, String
|
|
16
|
+
key :source_fs_path, String
|
|
17
|
+
key :top_text, String
|
|
18
|
+
key :bottom_text, String
|
|
19
|
+
|
|
20
|
+
key :request_count, Integer
|
|
21
|
+
key :last_request, Time
|
|
22
|
+
|
|
23
|
+
key :creator_ip, String
|
|
24
|
+
|
|
25
|
+
timestamps!
|
|
26
|
+
|
|
27
|
+
def requested!
|
|
28
|
+
increment :request_count => 1
|
|
29
|
+
set :last_request => Time.now
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
data/lib/meme_captain/server.rb
CHANGED
|
@@ -1,84 +1,167 @@
|
|
|
1
1
|
require 'digest/sha1'
|
|
2
|
-
require 'uri'
|
|
3
2
|
|
|
4
|
-
require 'curb'
|
|
5
3
|
require 'json'
|
|
4
|
+
require 'rack'
|
|
6
5
|
require 'sinatra/base'
|
|
7
6
|
|
|
8
|
-
require 'meme_captain'
|
|
9
|
-
|
|
10
7
|
module MemeCaptain
|
|
11
8
|
|
|
12
9
|
class Server < Sinatra::Base
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
set :
|
|
11
|
+
set :root, File.expand_path(File.join('..', '..'), File.dirname(__FILE__))
|
|
12
|
+
set :source_img_max_side, 800
|
|
13
|
+
set :watermark, Magick::ImageList.new(File.expand_path(
|
|
14
|
+
File.join('..', '..', 'watermark.png'), File.dirname(__FILE__)))
|
|
17
15
|
|
|
18
16
|
get '/' do
|
|
19
17
|
@u = params[:u]
|
|
20
18
|
@tt= params[:tt]
|
|
21
19
|
@tb = params[:tb]
|
|
22
20
|
|
|
21
|
+
@root_url = url('/')
|
|
22
|
+
|
|
23
23
|
erb :index
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
def normalize_params(p)
|
|
27
|
+
result = {
|
|
28
|
+
'u' => p[:u],
|
|
29
|
+
# convert to empty string if null
|
|
30
|
+
'tt' => p[:tt].to_s,
|
|
31
|
+
'tb' => p[:tb].to_s,
|
|
32
|
+
}
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
34
|
+
# if the id of an existing meme is passed in as the source url, use the
|
|
35
|
+
# source image of that meme for the source image
|
|
36
|
+
if result['u'][%r{^[a-f0-9]+\.(?:gif|jpg|png)$}]
|
|
37
|
+
if existing_as_source = MemeData.find_by_meme_id(result['u'])
|
|
38
|
+
result['u'] = existing_as_source.source_url
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# hash with string keys that can be accessed by symbol
|
|
43
|
+
Hash.new { |hash,key| hash[key.to_s] if Symbol === key }.merge(result)
|
|
44
|
+
end
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
def gen(p)
|
|
47
|
+
norm_params = normalize_params(p)
|
|
48
|
+
|
|
49
|
+
if existing = MemeData.first(
|
|
50
|
+
:source_url => norm_params[:u],
|
|
51
|
+
:top_text => norm_params[:tt],
|
|
52
|
+
:bottom_text => norm_params[:tb]
|
|
53
|
+
)
|
|
54
|
+
existing
|
|
55
|
+
else
|
|
56
|
+
if same_source = MemeData.find_by_source_url(norm_params[:u])
|
|
57
|
+
source_fs_path = same_source.source_fs_path
|
|
58
|
+
else
|
|
59
|
+
source_img = ImageList::SourceImage.new
|
|
60
|
+
source_img.fetch! norm_params[:u]
|
|
61
|
+
source_img.prepare! settings.source_img_max_side, settings.watermark
|
|
62
|
+
source_fs_path = source_img.cache(norm_params[:u], 'source_cache')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
open(source_fs_path, 'rb') do |source_io|
|
|
66
|
+
meme_img = MemeCaptain.meme(source_io, norm_params[:tt],
|
|
67
|
+
norm_params[:tb])
|
|
68
|
+
meme_img.extend ImageList::Cache
|
|
45
69
|
|
|
46
|
-
meme_img.to_blob {
|
|
47
|
-
self.quality = 100
|
|
48
70
|
# convert non-animated gifs to png
|
|
49
|
-
if
|
|
50
|
-
|
|
71
|
+
if meme_img.format == 'GIF' and meme_img.size == 1
|
|
72
|
+
meme_img.format = 'PNG'
|
|
51
73
|
end
|
|
52
|
-
|
|
53
|
-
|
|
74
|
+
|
|
75
|
+
params_s = norm_params.sort.map(&:join).join
|
|
76
|
+
meme_hash = Digest::SHA1.hexdigest(params_s)
|
|
77
|
+
|
|
78
|
+
meme_id = nil
|
|
79
|
+
(6..meme_hash.size).each do |len|
|
|
80
|
+
meme_id = "#{meme_hash[0,len]}.#{meme_img.extension}"
|
|
81
|
+
break unless MemeData.where(:meme_id => meme_id).count > 0
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
meme_fs_path = meme_img.cache(params_s, File.join('public', 'meme'))
|
|
85
|
+
|
|
86
|
+
meme_img.write(meme_fs_path) {
|
|
87
|
+
self.quality = 100
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
meme_data = MemeData.new(
|
|
91
|
+
:meme_id => meme_id,
|
|
92
|
+
:fs_path => meme_fs_path,
|
|
93
|
+
:mime_type => meme_img.mime_type,
|
|
94
|
+
:size => File.size(meme_fs_path),
|
|
95
|
+
|
|
96
|
+
:source_url => norm_params[:u],
|
|
97
|
+
:source_fs_path => source_fs_path,
|
|
98
|
+
:top_text => norm_params[:tt],
|
|
99
|
+
:bottom_text => norm_params[:tb],
|
|
100
|
+
|
|
101
|
+
:request_count => 0,
|
|
102
|
+
|
|
103
|
+
:creator_ip => request.ip
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
meme_data.save! :safe => true
|
|
107
|
+
|
|
108
|
+
meme_data
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
54
112
|
end
|
|
55
113
|
|
|
56
114
|
get '/g' do
|
|
115
|
+
raise Sinatra::NotFound if params[:u].to_s.empty?
|
|
116
|
+
|
|
57
117
|
begin
|
|
58
|
-
|
|
118
|
+
meme_data = gen(params)
|
|
59
119
|
|
|
60
|
-
|
|
61
|
-
temp_url.path = processed_cache_path.sub('public', '')
|
|
62
|
-
temp_url.query = nil
|
|
120
|
+
meme_url = url("/#{meme_data.meme_id}")
|
|
63
121
|
|
|
64
|
-
|
|
65
|
-
|
|
122
|
+
template_query = [
|
|
123
|
+
[:u, meme_data.meme_id],
|
|
124
|
+
[:tt, meme_data.top_text],
|
|
125
|
+
[:tb, meme_data.bottom_text],
|
|
126
|
+
].map { |k,v|
|
|
127
|
+
"#{Rack::Utils.escape(k)}=#{Rack::Utils.escape(v)}" }.join('&')
|
|
66
128
|
|
|
67
129
|
[200, { 'Content-Type' => 'application/json' }, {
|
|
68
|
-
'tempUrl' =>
|
|
69
|
-
'permUrl' =>
|
|
130
|
+
'tempUrl' => meme_url,
|
|
131
|
+
'permUrl' => meme_url,
|
|
132
|
+
'templateUrl' => url("/?#{template_query}"),
|
|
70
133
|
}.to_json]
|
|
71
134
|
rescue => error
|
|
72
135
|
[500, { 'Content-Type' => 'text/plain' }, error.to_s]
|
|
73
136
|
end
|
|
74
137
|
end
|
|
75
138
|
|
|
139
|
+
def serve_img(meme_data)
|
|
140
|
+
meme_data.requested!
|
|
141
|
+
|
|
142
|
+
content_type meme_data.mime_type
|
|
143
|
+
|
|
144
|
+
FileBody.new meme_data.fs_path
|
|
145
|
+
end
|
|
146
|
+
|
|
76
147
|
get '/i' do
|
|
77
|
-
|
|
148
|
+
raise Sinatra::NotFound if params[:u].to_s.empty?
|
|
149
|
+
|
|
150
|
+
serve_img(gen(params))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
get %r{^/([a-f0-9]+\.(?:gif|jpg|png))$} do
|
|
154
|
+
if meme_data = MemeData.find_by_meme_id(params[:captures][0])
|
|
155
|
+
serve_img meme_data
|
|
156
|
+
else
|
|
157
|
+
raise Sinatra::NotFound
|
|
158
|
+
end
|
|
159
|
+
end
|
|
78
160
|
|
|
79
|
-
|
|
161
|
+
not_found do
|
|
162
|
+
@root_url = url('/')
|
|
80
163
|
|
|
81
|
-
|
|
164
|
+
erb :'404'
|
|
82
165
|
end
|
|
83
166
|
|
|
84
167
|
helpers do
|
data/lib/meme_captain/version.rb
CHANGED
data/lib/meme_captain.rb
CHANGED
|
@@ -2,8 +2,8 @@ require 'meme_captain/caption'
|
|
|
2
2
|
require 'meme_captain/caption_choice'
|
|
3
3
|
require 'meme_captain/draw'
|
|
4
4
|
require 'meme_captain/file_body'
|
|
5
|
-
require 'meme_captain/
|
|
5
|
+
require 'meme_captain/image_list'
|
|
6
6
|
require 'meme_captain/meme'
|
|
7
|
-
require 'meme_captain/
|
|
7
|
+
require 'meme_captain/meme_data'
|
|
8
8
|
require 'meme_captain/server'
|
|
9
9
|
require 'meme_captain/version'
|
data/meme_captain.gemspec
CHANGED
|
@@ -15,13 +15,17 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
s.email = %w{matthewm@boedicker.org}
|
|
16
16
|
|
|
17
17
|
%w{
|
|
18
|
+
bson_ext
|
|
18
19
|
curb
|
|
19
20
|
json
|
|
20
21
|
mime-types
|
|
22
|
+
mongo
|
|
23
|
+
mongo_mapper
|
|
21
24
|
rack
|
|
22
25
|
rmagick
|
|
23
26
|
sinatra
|
|
24
27
|
}.each { |g| s.add_dependency g }
|
|
25
28
|
|
|
26
29
|
s.files = `git ls-files`.split("\n")
|
|
30
|
+
s.executables = %w{memecaptain}
|
|
27
31
|
end
|