meme_captain 0.1.0 → 0.1.1
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/ChangeLog +62 -2
- data/Gemfile +3 -0
- data/README.md +6 -1
- data/config.ru +4 -0
- data/doc/lightweight_front_end.md +64 -0
- data/doc/setup.md +13 -0
- data/lib/meme_captain/image_list/fetch.rb +2 -0
- data/lib/meme_captain/meme.rb +16 -4
- data/lib/meme_captain/memebg.rb +62 -0
- data/lib/meme_captain/norm_params.rb +92 -0
- data/lib/meme_captain/pretty_format.rb +12 -0
- data/lib/meme_captain/server.rb +134 -74
- data/lib/meme_captain/upload.rb +30 -0
- data/lib/meme_captain/version.rb +1 -1
- data/lib/meme_captain.rb +4 -1
- data/meme_captain.gemspec +2 -0
- data/public/css/screen.css +14 -0
- data/public/favicon.ico +0 -0
- data/public/js/fabric.min.js +3 -3
- data/public/js/jquery-1.7.2.min.js +4 -0
- data/public/js/meme_captain.js +403 -0
- data/public/source_images.json +65 -1
- data/public/thumbs.jpg +0 -0
- data/public/thumbs_1330486916.jpg +0 -0
- data/public/thumbs_1333591668.jpg +0 -0
- data/public/thumbs_1334189407.jpg +0 -0
- data/public/thumbs_1334973608.jpg +0 -0
- data/script/thumb_sprites.rb +57 -14
- data/spec/caption_choice_spec.rb +53 -0
- data/spec/caption_spec.rb +45 -0
- data/spec/image_list/fetch_spec.rb +33 -0
- data/spec/meme_captain_spec.rb +8 -2
- data/spec/memebg_spec.rb +14 -0
- data/spec/norm_params_spec.rb +223 -0
- data/spec/pretty_format_spec.rb +9 -0
- data/spec/text_pos_spec.rb +29 -0
- data/views/index.erb +14 -335
- metadata +150 -161
- data/lib/meme_captain/file_body.rb +0 -15
data/ChangeLog
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
|
+
0.1.1 2012-04-26
|
|
2
|
+
|
|
3
|
+
Update jQuery and fabric.js to latest versions.
|
|
4
|
+
|
|
5
|
+
Disable border on image links for IE.
|
|
6
|
+
|
|
7
|
+
Disable text positioning for browsers that don't support canvas.
|
|
8
|
+
|
|
9
|
+
Add new source images to the site: annoying Facebook girl and you're
|
|
10
|
+
gonna have a bad time.
|
|
11
|
+
|
|
12
|
+
Add new source images to the site: bad luck Brian, laundry room viking,
|
|
13
|
+
paranoid parrot, conspiracy Keanu, socially awesome awkward penguin
|
|
14
|
+
and you should feel bad.
|
|
15
|
+
|
|
16
|
+
Increase default stroke width to 8 pixels.
|
|
17
|
+
|
|
18
|
+
Add new source images to the site: condescending Wonka and first world
|
|
19
|
+
problems.
|
|
20
|
+
|
|
21
|
+
Add unsharp mask step to make text sharper.
|
|
22
|
+
|
|
23
|
+
Scale stroke width by supersample.
|
|
24
|
+
|
|
25
|
+
Make search result thumbnail size relative to image size.
|
|
26
|
+
|
|
27
|
+
Add share on Reddit link.
|
|
28
|
+
|
|
29
|
+
Add templateUrl to JSON response and template link to page.
|
|
30
|
+
|
|
31
|
+
Add favicon.ico to site.
|
|
32
|
+
|
|
33
|
+
Fix 404 error on empty u parameter.
|
|
34
|
+
|
|
35
|
+
Add new source images to the site: Ron Swanson and the more you know.
|
|
36
|
+
|
|
37
|
+
Add setup document.
|
|
38
|
+
|
|
39
|
+
Follow redirects when fetching source images.
|
|
40
|
+
|
|
41
|
+
Highlight form fields when they are populated or preloaded.
|
|
42
|
+
|
|
43
|
+
Add source image upload support to site.
|
|
44
|
+
|
|
45
|
+
Generalize and document thumb sprites script.
|
|
46
|
+
|
|
47
|
+
Add docs for setting up lightweight third-party sites.
|
|
48
|
+
|
|
49
|
+
Add JSONP support for lightweight third-party sites to use.
|
|
50
|
+
|
|
51
|
+
Use Magick::Image.destroy! to reduce memory leaking.
|
|
52
|
+
|
|
53
|
+
Handle odd image formats like BMP.
|
|
54
|
+
|
|
55
|
+
Add debug logging.
|
|
56
|
+
|
|
57
|
+
Write more rspecs.
|
|
58
|
+
|
|
59
|
+
Add new source images to the site: bad joke eel, Neil DeGrasse Tyson,
|
|
60
|
+
Obama not bad and impaired duck.
|
|
61
|
+
|
|
1
62
|
0.1.0 2012-02-07
|
|
2
63
|
|
|
3
64
|
Redesign web interface.
|
|
@@ -45,8 +106,7 @@
|
|
|
45
106
|
Add new source images to the site: all the things 2, cool story bro,
|
|
46
107
|
aw yeah, Boromir, Ned Stark.
|
|
47
108
|
|
|
48
|
-
0.0.8
|
|
49
|
-
2011-11-23
|
|
109
|
+
0.0.8 2011-11-23
|
|
50
110
|
|
|
51
111
|
Start change log.
|
|
52
112
|
|
data/Gemfile
ADDED
data/README.md
CHANGED
|
@@ -76,9 +76,14 @@ http://memecaptain.com/g?u=http%3A%2F%2Fmemecaptain.com%2Fyao_ming.jpg&t1=sure+i
|
|
|
76
76
|
|
|
77
77
|
```json
|
|
78
78
|
{
|
|
79
|
-
imageUrl: "http://memecaptain.com/c7757f.jpg"
|
|
79
|
+
imageUrl: "http://memecaptain.com/c7757f.jpg",
|
|
80
|
+
templateUrl: "http://memecaptain.com/?u=c7757f.jpg"
|
|
80
81
|
}
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
Optional parameters t1x, t1y, t1w, t1h, t2x, t2y, t2w, t2h can be added to
|
|
84
85
|
position and size text (see example above).
|
|
86
|
+
|
|
87
|
+
If you want to host a customized version of the memecaptain.com web interface
|
|
88
|
+
on your own web server but use the memecaptain.com backend see
|
|
89
|
+
[lightweight front end](https://github.com/mmb/meme_captain/blob/master/doc/lightweight_front_end.md).
|
data/config.ru
CHANGED
|
@@ -3,12 +3,14 @@ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
|
|
3
3
|
require 'mongo'
|
|
4
4
|
require 'mongo_mapper'
|
|
5
5
|
require 'rack'
|
|
6
|
+
require 'rack/contrib/jsonp'
|
|
6
7
|
require 'rack/rewrite'
|
|
7
8
|
|
|
8
9
|
require 'meme_captain'
|
|
9
10
|
|
|
10
11
|
use Rack::ConditionalGet
|
|
11
12
|
use Rack::Sendfile
|
|
13
|
+
use Rack::JSONP
|
|
12
14
|
|
|
13
15
|
use Rack::Static, :urls => %w{/tmp}, :root => 'public'
|
|
14
16
|
|
|
@@ -22,6 +24,8 @@ MemeCaptain::MemeData.ensure_index [
|
|
|
22
24
|
[:texts, 1],
|
|
23
25
|
]
|
|
24
26
|
|
|
27
|
+
MemeCaptain::Upload.ensure_index :upload_id
|
|
28
|
+
|
|
25
29
|
MemeCaptain::SourceFetchFail.ensure_index :url
|
|
26
30
|
|
|
27
31
|
use Rack::Rewrite do
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
If you want to host your own custom Meme Captain site but do not want to set
|
|
2
|
+
up the ruby backend you can use the memecaptain.com backend. In this scenario,
|
|
3
|
+
the HTML, Javscript and CSS are hosted on your web server and the meme images
|
|
4
|
+
are created on and hosted on memecaptain.com.
|
|
5
|
+
|
|
6
|
+
This is an option if you are on shared hosting and do not have access to
|
|
7
|
+
ruby or MongoDB or do not want to deal with setting them up.
|
|
8
|
+
|
|
9
|
+
Download the files at these locations and save them in your document root:
|
|
10
|
+
|
|
11
|
+
* http://memecaptain.com/
|
|
12
|
+
* http://memecaptain.com/css/screen.css
|
|
13
|
+
* http://memecaptain.com/js/fabric.min.js
|
|
14
|
+
* http://memecaptain.com/js/meme_captain.js
|
|
15
|
+
* http://memecaptain.com/source_images.json
|
|
16
|
+
|
|
17
|
+
The css in js should go in css and js directories in your document root so
|
|
18
|
+
that they match the url paths.
|
|
19
|
+
|
|
20
|
+
Find this line in js/meme_captain.js
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
genUrl = '/g',
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
and change it to
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
genUrl = 'http://memecaptain.com/g',
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
# Changing the Default Source Images
|
|
33
|
+
|
|
34
|
+
The source image thumbnails that show up on the page are driven by a JSON
|
|
35
|
+
description and a single image that contain CSS sprites. The JSON and image
|
|
36
|
+
must be regenerated to change them.
|
|
37
|
+
|
|
38
|
+
The rmagick ruby gem must be installed:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
gem install rmagick
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Get and run the script:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
wget https://raw.github.com/mmb/meme_captain/master/script/thumb_sprites.rb
|
|
48
|
+
RUBYOPT='r rubygems'
|
|
49
|
+
ruby thumb_sprites.rb -u URL_ROOT -s SOURCE_URL_PREFIX SOURCE_IMAGE_PATH/*
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
SOURCE_IMAGE_PATH is the directory where the source images are stored.
|
|
53
|
+
URL_ROOT is the root url of the Meme Captain installation. SOURCE_URL_PREFIX
|
|
54
|
+
is the part between the root url and the image filenames in the image urls.
|
|
55
|
+
|
|
56
|
+
For example if the images are stored in /var/www/meme.com/source and the
|
|
57
|
+
url root is http://meme.com/, use:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
ruby thumb_sprites.rb -u http://meme.com/ -s source/ /var/www/meme.com/source/*
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Copy the generated source_images.json and thumbs_xxx.jpg files to the Meme
|
|
64
|
+
Captain document root.
|
data/doc/setup.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Requirements
|
|
2
|
+
|
|
3
|
+
* a Ruby/Rack web application environment (such as
|
|
4
|
+
[Passenger](http://www.modrails.com/))
|
|
5
|
+
|
|
6
|
+
* Ruby gem dependencies (see the gemspec)
|
|
7
|
+
|
|
8
|
+
* [MongoDB](http://www.mongodb.org/)
|
|
9
|
+
|
|
10
|
+
You will most likely need a dedicated or virtual private server to run
|
|
11
|
+
MongoDB as most shared hosting does not support it. See the lightweight front
|
|
12
|
+
end document for how host the static HTML/CSS/Javascript only and use
|
|
13
|
+
memecaptain.com for the backend.
|
|
@@ -11,6 +11,8 @@ module MemeCaptain
|
|
|
11
11
|
def fetch!(url)
|
|
12
12
|
curl = Curl::Easy.perform(url) do |c|
|
|
13
13
|
c.useragent = 'Meme Captain http://memecaptain.com/'
|
|
14
|
+
c.follow_location = true
|
|
15
|
+
c.max_redirects = 3
|
|
14
16
|
end
|
|
15
17
|
unless curl.response_code == 200
|
|
16
18
|
raise FetchError.new(curl.response_code)
|
data/lib/meme_captain/meme.rb
CHANGED
|
@@ -24,7 +24,6 @@ module MemeCaptain
|
|
|
24
24
|
text_layer = Magick::Image.new(
|
|
25
25
|
img.page.width * super_sample, img.page.height * super_sample) {
|
|
26
26
|
self.background_color = 'none'
|
|
27
|
-
self.density = 72.0 * super_sample
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
text_poss.each do |text_pos|
|
|
@@ -50,7 +49,15 @@ module MemeCaptain
|
|
|
50
49
|
min_pointsize = text_pos.min_pointsize * super_sample
|
|
51
50
|
|
|
52
51
|
draw = Magick::Draw.new {
|
|
53
|
-
text_pos.draw_options.each
|
|
52
|
+
text_pos.draw_options.each do |k,v|
|
|
53
|
+
# options that need to be scaled by super sample
|
|
54
|
+
if [
|
|
55
|
+
:stroke_width
|
|
56
|
+
].include?(k)
|
|
57
|
+
v *= super_sample
|
|
58
|
+
end
|
|
59
|
+
self.send("#{k}=", v)
|
|
60
|
+
end
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
draw.extend Draw
|
|
@@ -77,15 +84,20 @@ module MemeCaptain
|
|
|
77
84
|
end
|
|
78
85
|
end
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
if super_sample != 1
|
|
88
|
+
text_layer.resize!(1.0 / super_sample)
|
|
89
|
+
text_layer = text_layer.unsharp_mask
|
|
90
|
+
end
|
|
81
91
|
|
|
82
92
|
img.each do |frame|
|
|
83
93
|
frame.composite!(text_layer, -frame.page.x, -frame.page.y,
|
|
84
94
|
Magick::OverCompositeOp)
|
|
85
95
|
frame.strip!
|
|
86
96
|
end
|
|
87
|
-
img
|
|
88
97
|
|
|
98
|
+
text_layer.destroy!
|
|
99
|
+
|
|
100
|
+
img
|
|
89
101
|
end
|
|
90
102
|
|
|
91
103
|
# Shortcut to generate a typical meme with text at the top and bottom.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'RMagick'
|
|
2
|
+
|
|
3
|
+
module MemeCaptain
|
|
4
|
+
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Public: Generate a pie slice meme background.
|
|
8
|
+
#
|
|
9
|
+
# size - The side length in pixels of the generated image.
|
|
10
|
+
# colors - An Array of color strings (any values that RMagick accepts).
|
|
11
|
+
# num_rays - The Fixnum of rays to create.
|
|
12
|
+
# block - An optiona block passed to Draw.new for specifying additional
|
|
13
|
+
# draw options
|
|
14
|
+
#
|
|
15
|
+
# Examples
|
|
16
|
+
#
|
|
17
|
+
# memebg(400, %w{red orange yellow green blue indigo violet}, 20) {
|
|
18
|
+
# # draw options
|
|
19
|
+
# # self.stroke = 'white'
|
|
20
|
+
# }.display
|
|
21
|
+
#
|
|
22
|
+
# Returns a Magick::Image of the meme background.
|
|
23
|
+
def memebg(size, colors, num_rays, &block)
|
|
24
|
+
# make circle 5% too big to avoid empty space at corners from rounding
|
|
25
|
+
# errors
|
|
26
|
+
circle_radius = Math.sqrt(2 * ((size / 2.0) ** 2)) * 1.05
|
|
27
|
+
|
|
28
|
+
side_len = 2 * circle_radius
|
|
29
|
+
|
|
30
|
+
start_x = side_len
|
|
31
|
+
start_y = circle_radius
|
|
32
|
+
|
|
33
|
+
center = "#{circle_radius},#{circle_radius}"
|
|
34
|
+
|
|
35
|
+
img = Magick::Image.new(side_len, side_len)
|
|
36
|
+
|
|
37
|
+
color_cycle = colors.cycle
|
|
38
|
+
|
|
39
|
+
(1..num_rays).each do |ray_index|
|
|
40
|
+
ray_radius = 2 * Math::PI / num_rays * ray_index
|
|
41
|
+
|
|
42
|
+
end_x = circle_radius + (Math.cos(ray_radius) * circle_radius)
|
|
43
|
+
end_y = circle_radius - (Math.sin(ray_radius) * circle_radius)
|
|
44
|
+
|
|
45
|
+
svg = "M#{center} L#{start_x},#{start_y} A#{center} 0 0,0 #{end_x},#{end_y} z"
|
|
46
|
+
|
|
47
|
+
draw = Magick::Draw.new {
|
|
48
|
+
instance_eval(&block) if block_given?
|
|
49
|
+
self.fill = color_cycle.next
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
draw.path svg
|
|
53
|
+
draw.draw img
|
|
54
|
+
|
|
55
|
+
start_x = end_x
|
|
56
|
+
start_y = end_y
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
img.crop! Magick::CenterGravity, size, size
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module MemeCaptain
|
|
2
|
+
|
|
3
|
+
# Normalize query string parameters.
|
|
4
|
+
#
|
|
5
|
+
# Provide defaults, do some basic validation and convert some parameters
|
|
6
|
+
# to floats or integers.
|
|
7
|
+
class NormParams
|
|
8
|
+
|
|
9
|
+
def initialize(params={})
|
|
10
|
+
@u = ''
|
|
11
|
+
|
|
12
|
+
@t1 = ''
|
|
13
|
+
@t2 = ''
|
|
14
|
+
|
|
15
|
+
@t1x = 0.05
|
|
16
|
+
@t1y = 0
|
|
17
|
+
@t1w = 0.9
|
|
18
|
+
@t1h = 0.25
|
|
19
|
+
|
|
20
|
+
@t2x = 0.05
|
|
21
|
+
@t2y = 0.75
|
|
22
|
+
@t2w = 0.9
|
|
23
|
+
@t2h = 0.25
|
|
24
|
+
|
|
25
|
+
load params
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Load query string parameters.
|
|
29
|
+
#
|
|
30
|
+
# Do some basic validation and convert some parameters to floats or
|
|
31
|
+
# integers.
|
|
32
|
+
def load(params)
|
|
33
|
+
params.select { |k,v|
|
|
34
|
+
[
|
|
35
|
+
:u,
|
|
36
|
+
:t1,
|
|
37
|
+
:t2,
|
|
38
|
+
].include? k.to_sym }.each do |k,v|
|
|
39
|
+
send "#{k}=", v.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
params.select { |k,v|
|
|
43
|
+
[
|
|
44
|
+
:t1x,
|
|
45
|
+
:t1y,
|
|
46
|
+
:t1w,
|
|
47
|
+
:t1h,
|
|
48
|
+
|
|
49
|
+
:t2x,
|
|
50
|
+
:t2y,
|
|
51
|
+
:t2w,
|
|
52
|
+
:t2h,
|
|
53
|
+
].include?(k.to_sym) and !v.to_s.empty? }.each do |k,v|
|
|
54
|
+
send "#{k}=", convert_metric(v)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Return a sorted string representation of the fields.
|
|
59
|
+
def signature
|
|
60
|
+
instance_variables.sort.map { |v|
|
|
61
|
+
name = v[1..-1] # remove @
|
|
62
|
+
"#{name}#{instance_variable_get(v)}"
|
|
63
|
+
}.join
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
attr_accessor :u
|
|
67
|
+
|
|
68
|
+
attr_accessor :t1
|
|
69
|
+
attr_accessor :t2
|
|
70
|
+
|
|
71
|
+
attr_accessor :t1x
|
|
72
|
+
attr_accessor :t1y
|
|
73
|
+
attr_accessor :t1w
|
|
74
|
+
attr_accessor :t1h
|
|
75
|
+
|
|
76
|
+
attr_accessor :t2x
|
|
77
|
+
attr_accessor :t2y
|
|
78
|
+
attr_accessor :t2w
|
|
79
|
+
attr_accessor :t2h
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
# Convert a metric string to a float or integer.
|
|
84
|
+
#
|
|
85
|
+
# Expects a string.
|
|
86
|
+
def convert_metric(metric)
|
|
87
|
+
metric.index('.') ? metric.to_f : metric.to_i
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end
|