seamus 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Scott Burton
1
+ Copyright (c) 2010 Scott Burton
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -4,37 +4,53 @@ Seamus is not an Irish monk. Instead, it inspects a file and returns whatever me
4
4
 
5
5
  == Usage
6
6
 
7
- Mix Seamus in to your class and get a hash of #file_attributes appropriate to the file type.
8
- Provide an instance of File, or any class that responds to #path, and include Seamus:
7
+ Seamus inspects your file and discovers useful attributes. It is at its best with rich
8
+ digital media - video, audio and image files - but is suitable for use with any file type.
9
+ Just provide a path to Seamus::Builder.new for an enhanced File object that knows about your
10
+ file.
9
11
 
10
- class MyClass
12
+ movie = Seamus::Builder.new("/path/to/my/movie.mov")
13
+
14
+ movie.width
15
+ # => 720
16
+
17
+ movie.video_codec
18
+ # => 'h264'
19
+
20
+ Or if you prefer, include Seamus in a class and use the has_file :file_attribute class
21
+ method for attachments.
22
+
23
+ class Upload
11
24
  include Seamus
12
- attr_accessor :file
13
-
14
- def initialize(path)
15
- @path = File.open(path)
16
- end
25
+ has_file :file
17
26
  end
18
27
 
19
- my_file = MyClass.new("path/to/file.mov")
20
- my_file.file_attributes
28
+ u = Upload.new
29
+ u.file = "/path/to/image.jpg"
30
+ u.file.width
31
+ # => 3284
21
32
 
22
- # => {"video_codec"=>"h264", "bitrate_units"=>"kb/s", "container"=>"mov,mp4,m4a,3gp,3g2,mj2", "audio_channels_string"=>"stereo", "audio?"=>true, "audio_channels"=>2, "video?"=>true, "audio_sample_rate"=>44100, "bitrate"=>346, "audio_codec"=>"mpeg4aac", "video_colorspace"=>"yuv420p", "height"=>576, "audio_sample_units"=>"Hz", "fps"=>"7.00", "duration"=>84000, "width"=>1024}
23
-
24
-
25
- Generate thumbnails automatically:
33
+ Seamus supports thumbnail creation for visual media types. The #thumbnail method returns
34
+ an IO instance.
26
35
 
27
- my_file.thumbnail {|thumb| my_open_file_instance.write thumb.read }
36
+ u.file.thumbnail
37
+ # => #<IO:0x357898>
38
+
39
+ == Extending Seamus
28
40
 
29
- Files that probably can't generate thumbnails (audio, PDF, etc.) will raise ThumbnailError.
41
+ Seamus dynamically generates classes for file types as it encounters them. Seamus::Builder
42
+ will, for example, generate a Seamus::File::Mp4 class and instance when given a path to
43
+ an MPEG-4 file with .mp4 extension, which subclasses Seamus::File::Video.
30
44
 
31
- Video and audio file attributes are inspected using the RVideo gem.
45
+ You can build custom behavior around a file type by defining a class Seamus::File::<extname>
46
+ that either subclasses File, subclasses one of Seamus::File's template classes, or implements
47
+ a path handler of some kind.
32
48
 
33
49
  == Note on Patches/Pull Requests
34
50
 
35
- * I'd be very surprised if anyone wanted to do such a thing.
51
+ * I'd be very surprised if anyone wanted to do such a thing. But if you really do, contact me and we'll work something out.
36
52
 
37
53
 
38
54
  == Copyright
39
55
 
40
- Copyright (c) 2009 Scott Burton. See LICENSE for details.
56
+ Copyright (c) 2010 Scott Burton. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
@@ -85,6 +85,7 @@ MimeTable.register "text/UTF-8", :txt
85
85
  MimeTable.register "text/x-java-source", :jav
86
86
  MimeTable.register "text/x-script.python", :py
87
87
  MimeTable.register "text/x-script.sh", :sh
88
+ MimeTable.register "text/x-ruby", :rb
88
89
  MimeTable.register "video/mp4", :f4v
89
90
  MimeTable.register "video/mp4", :mp4
90
91
  MimeTable.register "video/quicktime", :mov
@@ -1,11 +1,11 @@
1
- require 'rvideo'
2
1
  require 'digest/md5'
3
-
2
+ require 'open3'
4
3
  require 'tempfile'
5
4
 
6
5
  $LOAD_PATH << './lib'
7
6
 
8
7
  require 'core/numeric'
8
+ require 'seamus/standard_additions'
9
9
  require 'seamus/inspector'
10
10
  require 'seamus/inspector/video_inspector'
11
11
  require 'seamus/inspector/audio_inspector'
@@ -18,138 +18,67 @@ require 'seamus/processor/audio_processor'
18
18
  require 'seamus/processor/image_processor'
19
19
  require 'seamus/processor/application_processor'
20
20
  require 'seamus/processor/text_processor'
21
+ require 'seamus/file/video'
22
+ require 'seamus/file/audio'
23
+ require 'seamus/file/text'
24
+ require 'seamus/file/image'
25
+ require 'seamus/file/application'
26
+ require 'seamus/builder'
21
27
  require 'mime_table'
22
28
 
23
29
 
24
30
  ## Seamus - The File Inspector
25
- # Mix Seamus in to your class and get a hash of #file_attributes appropriate to the file type.
26
- # Provide an instance of File, or any class that responds to #path, and include Seamus:
27
- #
28
- # class MyClass
31
+ #
32
+ # Seamus inspects your file and discovers useful attributes. It is at its best with rich
33
+ # digital media - video, audio and image files - but is suitable for use with any file type.
34
+ # Just provide a path to Seamus::Builder.new for an enhanced File object that knows about your
35
+ # file.
36
+ #
37
+ # movie = Seamus::Builder.new("/path/to/my/movie.mov")
38
+ #
39
+ # movie.width
40
+ # # => 720
41
+ #
42
+ # movie.video_codec
43
+ # # => 'h264'
44
+ #
45
+ # Or if you prefer, include Seamus in a class and use the has_file :file_attribute class
46
+ # method for attachments.
47
+ #
48
+ # class Upload
29
49
  # include Seamus
30
- # attr_accessor :file
31
- #
32
- # def initialize(path)
33
- # @path = File.open(path)
34
- # end
50
+ # has_file :file
35
51
  # end
36
52
  #
37
- # my_file = MyClass.new("path/to/file.mov")
38
- # my_file.file_attributes
39
- #
40
- # # => {"video_codec"=>"h264", "bitrate_units"=>"kb/s", "container"=>"mov,mp4,m4a,3gp,3g2,mj2", "audio_channels_string"=>"stereo", "audio?"=>true, "audio_channels"=>2, "video?"=>true, "audio_sample_rate"=>44100, "bitrate"=>346, "audio_codec"=>"mpeg4aac", "video_colorspace"=>"yuv420p", "height"=>576, "audio_sample_units"=>"Hz", "fps"=>"7.00", "duration"=>84000, "width"=>1024}
53
+ # u = Upload.new
54
+ # u.file = "/path/to/image.jpg"
55
+ # u.file.width
56
+ # # => 3284
41
57
  #
42
- #
43
- # Generate thumbnails automatically:
58
+ # Seamus supports thumbnail creation for visual media types. The #thumbnail method returns
59
+ # an IO instance.
44
60
  #
45
- # my_file.thumbnail {|thumb| my_open_file_instance.write thumb.read }
61
+ # u.file.thumbnail
62
+ # # => #<IO:0x357898>
46
63
  #
47
-
48
64
 
49
65
  module Seamus
50
66
 
51
67
  module ClassMethods
52
-
53
- end
54
-
55
- module InstanceMethods
56
-
57
- def file_path
58
- file.path
59
- end
60
-
61
- def file_name
62
- File.basename(file_path)
63
- end
64
-
65
- def file_attributes
66
- @file_attributes ||= inspect_file
67
- end
68
-
69
- def md5
70
- @md5 ||= Digest::MD5.file(file_path)
71
- end
72
-
73
- def md5_hex_string
74
- md5.to_s
75
- end
76
-
77
- def md5_base64_encoded
78
- Base64.encode64(md5.digest).strip
79
- end
80
-
81
- def content_type
82
- MimeTable.lookup_by_extension(extension).to_s
83
- end
84
-
85
- def thumbnail(&block)
86
- @thumbnail ||= generate_thumbnail
87
- if block_given?
88
- begin
89
- @thumbnail.open
90
- yield @thumbnail
91
- ensure
92
- @thumbnail.close
68
+ def has_file(attribute)
69
+ self.class_eval(%Q(
70
+ def #{attribute.to_s}=(file)
71
+ @#{attribute.to_s} = Seamus::Builder.new(file)
93
72
  end
94
- end
95
- return @thumbnail
96
- end
97
-
98
- protected
99
-
100
- def inspect_file
101
- inspector = Seamus.const_get("#{file_type.classify}Inspector").new(file_path)
102
- return inspector.stats
103
- end
104
-
105
- def generate_thumbnail
106
- processor = Seamus.const_get("#{file_type.classify}Processor").new(file_path, file_attributes)
107
- begin
108
- processor.thumbnail
109
- rescue ThumbnailError => e
110
- return nil
111
- rescue NameError
112
- return nil
113
- end
114
- end
115
-
116
- private
117
-
118
- def file_type
119
- # begin
120
- # @file_attributes["content-type"].split("/").first
121
- # rescue
122
- determine_type_from_extension
123
- # end
124
- end
125
-
126
- def determine_type_from_extension
127
- begin
128
- Mime::Type.lookup_by_extension(extension).to_s.split("/").first
129
- rescue NoMethodError
130
- "application"
131
- rescue NameError
132
- if MimeTable
133
- MimeTable.lookup_by_extension(extension).to_s.split("/").first
134
- else
135
- raise LoadError, "Mime module not loaded"
73
+
74
+ def #{attribute.to_s}
75
+ @#{attribute.to_s}
136
76
  end
137
- end
138
- end
139
-
140
- # returns the last segment of the filename, eg "foo.mov".extension => ".mov"
141
- def extension
142
- file_name.split('.').last.downcase
77
+ ))
143
78
  end
79
+ end
144
80
 
145
- # Add file_attributes as getter methods
146
- def method_missing(method, *args, &block)
147
- if file_attributes.include?(method.to_s)
148
- file_attributes[method.to_s]
149
- else
150
- super(method, *args, &block)
151
- end
152
- end
81
+ module InstanceMethods
153
82
 
154
83
  end
155
84
 
@@ -0,0 +1,45 @@
1
+ module Seamus
2
+
3
+ module Builder
4
+ extend self
5
+ def new(file)
6
+ path = file.is_a?(File) ? Pathname.new(file.path) : Pathname.new(file)
7
+ type = file_type(path.extname)
8
+ extension = path.extname.gsub('.', '')
9
+ build_class(type, extension).new(path.to_s)
10
+ end
11
+
12
+ def file_type(extension)
13
+ determine_type_from_extension(extension)
14
+ end
15
+
16
+ private
17
+
18
+ def build_class(type, extension)
19
+ Seamus::File.module_eval %Q{
20
+ class #{extension.capitalize} < #{type}; end
21
+ } unless Seamus::File.const_defined?("#{extension.capitalize}")
22
+ Seamus::File.const_get("#{extension.capitalize}")
23
+ end
24
+
25
+ def determine_type_from_extension(extension)
26
+ begin
27
+ Mime::Type.lookup_by_extension(extension).to_s.split("/").first.capitalize
28
+ rescue NoMethodError
29
+ "Application"
30
+ rescue NameError
31
+ if MimeTable
32
+ MimeTable.lookup_by_extension(extension).to_s.split("/").first.capitalize
33
+ else
34
+ raise LoadError, "Mime module not loaded"
35
+ end
36
+ end
37
+ end
38
+ #
39
+ # # returns the last segment of the filename, eg "foo.mov".extension => ".mov"
40
+ # def extension
41
+ # file_name.split('.').last.downcase
42
+ # end
43
+
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ module Seamus
2
+ module File
3
+ class Application < ::File
4
+ include Seamus::ApplicationInspector
5
+ include Seamus::ApplicationProcessor
6
+ include Seamus::StandardAdditions
7
+ def initialize(*args, &block)
8
+ super(*args, &block)
9
+ # create_methods_for inspector
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Seamus
2
+ module File
3
+ class Audio < ::File
4
+ include Seamus::AudioInspector
5
+ include Seamus::AudioProcessor
6
+ include Seamus::StandardAdditions
7
+ def initialize(*args, &block)
8
+ super(*args, &block)
9
+ create_methods_for inspector, :except => "path"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Seamus
2
+ module File
3
+ class Image < ::File
4
+ include Seamus::ImageInspector
5
+ include Seamus::ImageProcessor
6
+ include Seamus::StandardAdditions
7
+ def initialize(*args, &block)
8
+ super(*args, &block)
9
+ create_methods_for inspector, :except => ["thumbnail", "thumbnail2"]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Seamus
2
+ module File
3
+ class Text < ::File
4
+ include Seamus::TextInspector
5
+ include Seamus::TextProcessor
6
+ include Seamus::StandardAdditions
7
+ def initialize(*args, &block)
8
+ super(*args, &block)
9
+ # create_methods_for inspector
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Seamus
2
+ module File
3
+ class Video < ::File
4
+ include Seamus::VideoInspector
5
+ include Seamus::VideoProcessor
6
+ include Seamus::StandardAdditions
7
+ def initialize(*args, &block)
8
+ super(*args, &block)
9
+ create_methods_for inspector, :except => "path"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,16 +1,5 @@
1
1
  module Seamus
2
- class ApplicationInspector < Inspector
2
+ module ApplicationInspector
3
3
 
4
- def stats
5
- inspection_attributes
6
- end
7
-
8
- private
9
-
10
- def inspection_attributes
11
- attr_hash = {"size" => file_stat.size}
12
- return attr_hash
13
- end
14
-
15
4
  end
16
5
  end
@@ -1,32 +1,9 @@
1
1
  module Seamus
2
- class AudioInspector < Inspector
2
+ module AudioInspector
3
3
 
4
- def stats
5
- inspection_attributes
4
+ def inspector
5
+ @inspector ||= RVideo::Inspector.new(:file => self.path)
6
6
  end
7
-
8
- private
9
-
10
- def inspection_attributes
11
- attr_hash = {}
12
- ["audio?", "audio_bitrate", "audio_channels", "audio_channels_string", "audio_codec", "audio_sample_rate", "audio_sample_units", "bitrate", "bitrate_units", "container", "duration"].each do |attribute|
13
- attr_hash[attribute.to_s] = media_stat.send(attribute) if media_stat.respond_to?(attribute)
14
- end
15
- attr_hash.merge!("size" => file_stat.size)
16
- return attr_hash
17
- end
18
-
19
- def media_stat
20
- RVideo::Inspector.new(:raw_response => raw_response)
21
- end
22
-
23
- def raw_response
24
- process = IO.popen("ffmpeg -i '#{file.path}' 2>&1", "r")
25
- response = process.read
26
- process.close
27
- return response
28
- end
29
-
30
7
 
31
8
  end
32
9
  end
@@ -1,24 +1,10 @@
1
1
  require 'devil'
2
2
 
3
3
  module Seamus
4
- class ImageInspector < Inspector
5
- def stats
6
- inspection_attributes
7
- end
8
-
9
- private
10
-
11
- def inspection_attributes
12
- attr_hash = {}
13
- ["width", "height"].each do |attribute|
14
- attr_hash[attribute] ||= image.send(attribute.to_sym) if image.respond_to?(attribute)
15
- end
16
- attr_hash.merge!("size" => file_stat.size)
17
- return attr_hash
18
- end
4
+ module ImageInspector
19
5
 
20
- def image
21
- @image ||= Devil.load_image(file.path)
6
+ def inspector
7
+ @inspector ||= Devil.load_image(self.path)
22
8
  end
23
9
 
24
10
  end
@@ -1,16 +1,26 @@
1
1
  module Seamus
2
- class TextInspector < Inspector
2
+ module TextInspector
3
3
 
4
- def stats
5
- inspection_attributes
4
+ def words
5
+ characters.scan(/\S+/)
6
6
  end
7
-
7
+
8
+ def word_count
9
+ words.size
10
+ end
11
+
12
+ def lines
13
+ characters.scan(/.*\n/)
14
+ end
15
+
16
+ def lines_count
17
+ lines.size
18
+ end
19
+
8
20
  private
9
-
10
- def inspection_attributes
11
- attr_hash = {"size" => file_stat.size}
12
- return attr_hash
21
+
22
+ def characters
23
+ @characters ||= ::File.read(self.path)
13
24
  end
14
-
15
25
  end
16
26
  end
@@ -1,33 +1,11 @@
1
+ require 'rvideo'
2
+ require 'forwardable'
3
+
1
4
  module Seamus
2
- class VideoInspector < Inspector
5
+ module VideoInspector
3
6
 
4
- def stats
5
- inspection_attributes
6
- end
7
-
8
- private
9
-
10
- def inspection_attributes
11
- attr_hash = {}
12
- ["audio?", "audio_bitrate", "audio_channels", "audio_channels_string", "audio_codec", "audio_sample_rate", "audio_sample_units", "bitrate", "bitrate_units", "container", "duration", "fps", "height", "video?", "video_codec", "video_colorspace", "width"].each do |attribute|
13
- attr_hash[attribute.to_s] = media_stat.send(attribute) if media_stat.respond_to?(attribute)
14
- end
15
- attr_hash.merge!("size" => file_stat.size)
16
- return attr_hash
17
- end
18
-
19
- def media_stat
20
- RVideo::Inspector.new(:raw_response => raw_response)
21
- end
22
-
23
- def raw_response
24
- response = ffmpeg_response_process.read
25
- ffmpeg_response_process.close
26
- return response
27
- end
28
-
29
- def ffmpeg_response_process
30
- IO.popen("ffmpeg -i '#{@file.path}' 2>&1", "r")
7
+ def inspector
8
+ @inspector ||= RVideo::Inspector.new(:file => self.path)
31
9
  end
32
10
 
33
11
  end
@@ -1,5 +1,5 @@
1
1
  module Seamus
2
- class ApplicationProcessor < Processor
2
+ module ApplicationProcessor
3
3
 
4
4
  def thumbnail
5
5
  raise ThumbnailError, "invalid type for thumbnail"
@@ -1,5 +1,5 @@
1
1
  module Seamus
2
- class AudioProcessor < Processor
2
+ module AudioProcessor
3
3
 
4
4
  def thumbnail
5
5
  raise ThumbnailError, "invalid type for thumbnail"
@@ -1,21 +1,25 @@
1
1
  module Seamus
2
- class ImageProcessor < Processor
2
+ module ImageProcessor
3
3
  require 'devil'
4
4
 
5
5
  def thumbnail
6
- tmp = thumb_tempfile
7
- Devil.load_image(file.path) do |img|
6
+ tmp = Tempfile.new(::File.basename(path))
7
+ # io = IO.new
8
+ Devil.load_image(path) do |img|
8
9
  img.thumbnail(320)
9
- img.save("#{file.path}_#{File.basename(file.path)}_thumb.jpg")
10
+ img.save("#{path}_#{::File.basename(path)}_thumb.jpg")
10
11
  end
11
- File.open("#{file.path}_#{File.basename(file.path)}_thumb.jpg", "r") do |thumb|
12
+ ::File.open("#{path}_#{::File.basename(path)}_thumb.jpg", "r") do |thumb|
12
13
  tmp.write thumb.read
13
14
  end
14
- tmp.close
15
- File.unlink("#{file.path}_#{File.basename(file.path)}_thumb.jpg")
15
+ # tmp.close
16
+ tmp.rewind
17
+ # io.close
18
+ ::File.unlink("#{path}_#{::File.basename(path)}_thumb.jpg")
19
+ # return io
16
20
  return tmp
17
21
  end
18
-
22
+
19
23
 
20
24
  end
21
25
  end
@@ -1,5 +1,5 @@
1
1
  module Seamus
2
- class TextProcessor < Processor
2
+ module TextProcessor
3
3
 
4
4
  def thumbnail
5
5
  raise ThumbnailError, "invalid type for thumbnail"
@@ -1,30 +1,18 @@
1
1
  module Seamus
2
- class VideoProcessor < Processor
2
+ module VideoProcessor
3
3
 
4
4
  def thumbnail
5
- thumb = thumb_tempfile
6
- IO.popen("ffmpeg -y -ss #{start_time} -i '#{@file.path}' -t 00:00:01 -vcodec mjpeg -vframes 1 -an -f rawvideo -s #{dimensions_string} - 2> /dev/null") do |io|
7
- thumb.write io.read
8
- end
9
- thumb.close
10
- return thumb
5
+ Open3.popen3("ffmpeg -y -ss #{start_time} -i '#{self.path}' -t 00:00:01 -vcodec mjpeg -vframes 1 -an -f rawvideo -s #{dimensions_string} - ")[1]
11
6
  end
12
7
 
13
8
  private
14
9
 
15
10
  def start_time
16
- (file_attributes["duration"] || 10000)/3333
17
- end
18
-
19
- # nodoc
20
- def thumb_tempfile
21
- Tempfile.new("#{File.basename(@file.path, File.extname(@file.path))}_thumb.jpg")
11
+ (duration || 10000)/3333
22
12
  end
23
13
 
24
14
  def aspect_ratio
25
- width = (file_attributes["width"]).to_f
26
- height = (file_attributes["height"]).to_f
27
- return (width/height)
15
+ width.to_r/height.to_r
28
16
  end
29
17
 
30
18
  # For ffmpeg dimensions to work, the dimension components have to be even numbers,
@@ -0,0 +1,44 @@
1
+ module Seamus
2
+ module StandardAdditions
3
+ def md5
4
+ @md5 ||= Digest::MD5.file(self.path)
5
+ end
6
+
7
+ def md5_digest
8
+ md5.digest
9
+ end
10
+
11
+ def md5_base64_encoded
12
+ Base64.encode64(md5_digest).strip
13
+ end
14
+
15
+ def size
16
+ stat.size
17
+ end
18
+
19
+ def content_type
20
+ MimeTable.lookup_by_extension(extension).to_s
21
+ end
22
+
23
+ private
24
+
25
+ def create_method(method, method_binding)
26
+ self.class.send(:define_method, method.to_sym, method_binding)
27
+ end
28
+
29
+ def create_methods_for(object, options={})
30
+ options[:except] = options[:except].to_a unless options[:except].is_a?(Array)
31
+ object.public_methods(false).each do |method|
32
+ create_method(method, lambda {object.method(method.to_s).call}) unless options[:except].include?(method)
33
+ end
34
+ end
35
+
36
+ # def method_missing(method, *args, &block)
37
+ # if file_attributes.has_key?(method.to_s)
38
+ # file_attributes[method.to_s]
39
+ # else
40
+ # super(method, *args, &block)
41
+ # end
42
+ # end
43
+ end
44
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{seamus}
8
- s.version = "0.2.4"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Scott Burton"]
12
- s.date = %q{2010-01-26}
12
+ s.date = %q{2010-02-10}
13
13
  s.description = %q{Seamus is not an Irish monk. Instead, it inspects a file and returns whatever metadata it can determine.}
14
14
  s.email = %q{scottburton11@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -29,22 +29,33 @@ Gem::Specification.new do |s|
29
29
  "lib/core/numeric.rb",
30
30
  "lib/mime_table.rb",
31
31
  "lib/seamus.rb",
32
- "lib/seamus/inspector.rb",
32
+ "lib/seamus/builder.rb",
33
+ "lib/seamus/file/application.rb",
34
+ "lib/seamus/file/audio.rb",
35
+ "lib/seamus/file/image.rb",
36
+ "lib/seamus/file/text.rb",
37
+ "lib/seamus/file/video.rb",
33
38
  "lib/seamus/inspector/application_inspector.rb",
34
39
  "lib/seamus/inspector/audio_inspector.rb",
35
40
  "lib/seamus/inspector/image_inspector.rb",
36
41
  "lib/seamus/inspector/text_inspector.rb",
37
42
  "lib/seamus/inspector/video_inspector.rb",
38
- "lib/seamus/processor.rb",
39
43
  "lib/seamus/processor/application_processor.rb",
40
44
  "lib/seamus/processor/audio_processor.rb",
41
45
  "lib/seamus/processor/image_processor.rb",
42
46
  "lib/seamus/processor/text_processor.rb",
43
47
  "lib/seamus/processor/video_processor.rb",
48
+ "lib/seamus/standard_additions.rb",
44
49
  "seamus.gemspec",
50
+ "spec/factories.rb",
51
+ "spec/fixtures/test.jpg",
52
+ "spec/fixtures/test.mov",
53
+ "spec/fixtures/test.mp4",
45
54
  "spec/lib/core/numeric_spec.rb",
46
55
  "spec/lib/seamus/inspector/image_inspector_spec.rb",
56
+ "spec/lib/seamus/inspector/video_inspector_spec.rb",
47
57
  "spec/lib/seamus/processor/image_processor_spec.rb",
58
+ "spec/lib/seamus/processor/video_processor_spec.rb",
48
59
  "spec/seamus_spec.rb",
49
60
  "spec/spec_helper.rb"
50
61
  ]
@@ -54,9 +65,12 @@ Gem::Specification.new do |s|
54
65
  s.rubygems_version = %q{1.3.5}
55
66
  s.summary = %q{Seamus is not an Irish monk}
56
67
  s.test_files = [
57
- "spec/lib/core/numeric_spec.rb",
68
+ "spec/factories.rb",
69
+ "spec/lib/core/numeric_spec.rb",
58
70
  "spec/lib/seamus/inspector/image_inspector_spec.rb",
71
+ "spec/lib/seamus/inspector/video_inspector_spec.rb",
59
72
  "spec/lib/seamus/processor/image_processor_spec.rb",
73
+ "spec/lib/seamus/processor/video_processor_spec.rb",
60
74
  "spec/seamus_spec.rb",
61
75
  "spec/spec_helper.rb"
62
76
  ]
@@ -0,0 +1,13 @@
1
+ module Factories
2
+ def mov
3
+ File.open(File.expand_path(File.dirname(__FILE__) + "/fixtures/test.mov"))
4
+ end
5
+
6
+ def mp4
7
+ File.open(File.expand_path(File.dirname(__FILE__) + "/fixtures/test.mp4"))
8
+ end
9
+
10
+ def jpg
11
+ File.open(File.expand_path(File.dirname(__FILE__) + "/fixtures/test.jpg"))
12
+ end
13
+ end
Binary file
Binary file
Binary file
@@ -1,43 +1,13 @@
1
1
  require File.expand_path(File.dirname(__FILE__)) + "/../../../spec_helper.rb"
2
2
 
3
3
  describe Seamus::ImageInspector do
4
-
5
- include FakeFS::SpecHelpers
6
-
7
4
  before(:each) do
8
- FileUtils.touch("./image.jpg")
9
- @inspector = Seamus::ImageInspector.new("./image.jpg")
5
+ @image = mock(Seamus::File::Image, :path => "/path/to/image.jpg")
6
+ @image.extend Seamus::ImageInspector
10
7
  end
11
8
 
12
- it "responds to #stats" do
13
- @inspector.should respond_to(:stats)
9
+ it "loads an image inspector using Devil.load_image" do
10
+ Devil.should_receive(:load_image).with("/path/to/image.jpg")
11
+ @image.inspector
14
12
  end
15
-
16
- describe "stats" do
17
-
18
- before(:each) do
19
- @image = mock(Devil::Image, :width => 100, :height => 100)
20
- @file_stat = mock(File::Stat, :size => 100)
21
- @inspector.should_receive(:image).any_number_of_times.and_return(@image)
22
- @inspector.should_receive(:file_stat).and_return(@file_stat)
23
- end
24
-
25
- it "returns a hash" do
26
- @inspector.stats.should be_an_instance_of(Hash)
27
- end
28
-
29
- it "assigns :width" do
30
- @inspector.stats["width"].should eql(100)
31
- end
32
-
33
- it "assigns :height" do
34
- @inspector.stats["height"].should eql(100)
35
- end
36
-
37
- it "assigns :size" do
38
- @inspector.stats["size"].should eql(100)
39
- end
40
-
41
- end
42
-
43
13
  end
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + "/../../../spec_helper.rb"
2
+
3
+ describe Seamus::VideoInspector do
4
+ before(:each) do
5
+ @video = mock(Seamus::File::Video, :path => "/path/to/video.jpg")
6
+ @video.extend Seamus::VideoInspector
7
+ end
8
+
9
+ it "loads an video inspector using RVideo::Inspector" do
10
+ RVideo::Inspector.should_receive(:new).with(:file => "/path/to/video.jpg")
11
+ @video.inspector
12
+ end
13
+ end
@@ -0,0 +1,66 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + "/../../../spec_helper.rb"
2
+
3
+ describe Seamus::VideoProcessor do
4
+ # include FakeFS::SpecHelpers
5
+
6
+ before(:each) do
7
+ # File.new("/path/to/movie.mov", "w+") {|file| file << "MovieData"}
8
+ # file = File.open("/path/to/movie.mov", "r")
9
+ # @video = file.dup.extend Seamus::VideoProcessor
10
+ @video = mock(Seamus::File::Video, :path => "/path/to/movie.mov")
11
+ @video.extend Seamus::VideoProcessor
12
+ end
13
+
14
+ describe "thumbnail creation" do
15
+ before(:each) do
16
+ @video.stub!(:start_time => 3333, :dimensions_string => "320x180")
17
+ end
18
+
19
+ it "uses Open3 to capture the stdout of ffmpeg in an IO instance" do
20
+ @stdin, @stdout, @stderr = mock(IO), mock(IO), mock(IO)
21
+ Open3.should_receive(:popen3).
22
+ with("ffmpeg -y -ss 3333 -i '/path/to/movie.mov' -t 00:00:01 -vcodec mjpeg -vframes 1 -an -f rawvideo -s 320x180 - ").
23
+ and_return([@stdin, @stdout, @stderr])
24
+ @video.thumbnail.should eql(@stdout)
25
+ end
26
+
27
+ end
28
+
29
+ describe "thumbnail capture start time" do
30
+ it "defaults to 10000/3333 - roughly 3 seconds in - when the #duration attribute is nil" do
31
+ @video.stub!(:duration => nil)
32
+ @video.send(:start_time).should eql(10000/3333)
33
+ end
34
+
35
+ it "equals one third of the duration (in milliseconds)" do
36
+ @video.stub!(:duration => 20000)
37
+ @video.send(:start_time).should eql(20000/3333)
38
+ end
39
+ end
40
+
41
+ describe "aspect ratio" do
42
+ before(:each) do
43
+ @video.stub!(:width => 640, :height => 480)
44
+ end
45
+
46
+ it "is a rational number calculated from #width divided by #height" do
47
+ @video.send(:aspect_ratio).should == Rational(4,3)
48
+ end
49
+ end
50
+
51
+ describe "dimensions string" do
52
+ before(:each) do
53
+ @video.stub!(:aspect_ratio => Rational(4,3))
54
+ end
55
+
56
+ it "is a formatted 'WxH' string, using the aspect ratio to calculate the height constant width" do
57
+ @video.send(:dimensions_string).should eql("320x240")
58
+ end
59
+
60
+ it "can be provided with an alternate width" do
61
+ @video.send(:dimensions_string, 240).should eql("240x180")
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -1,16 +1,53 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe Seamus do
3
+ describe Seamus::Builder do
4
4
 
5
- describe "thumbnail" do
6
-
7
- describe "failure" do
8
-
9
- class InspectableFile
10
- include Seamus
11
- end
5
+ describe "md5 hashcode calculation" do
6
+
7
+ before(:each) do
8
+ @movie = Seamus::Builder.new(Factories::mov.path)
9
+ end
10
+
11
+ it "creates a md5 instance" do
12
+ Digest::MD5.should_receive(:file).
13
+ with(@movie.path).
14
+ and_return("Digest_MD5_instance")
15
+ @movie.md5.should eql("Digest_MD5_instance")
16
+ end
12
17
 
18
+ it "Base64 encodes a md5 hex string" do
19
+ @movie.stub!(:md5_digest).
20
+ and_return("md5-hexdigest-string")
21
+
22
+ Base64.should_receive(:encode64).
23
+ with("md5-hexdigest-string").
24
+ and_return("base64-encoded")
25
+ @movie.md5_base64_encoded.should eql("base64-encoded")
26
+ end
27
+ end
28
+
29
+ describe "on-demand classes" do
30
+ it "creates a ::Mov class for .mov files" do
31
+ @movie = Seamus::Builder.new(Factories::mov.path)
32
+ @movie.should be_an_instance_of(Seamus::File::Mov)
13
33
  end
14
34
 
35
+ it "creates a ::Mp4 class for .mp4 files" do
36
+ @mpeg4 = Seamus::Builder.new(Factories::mp4.path)
37
+ @mpeg4.should be_an_instance_of(Seamus::File::Mp4)
38
+ end
39
+
40
+ it "creates a ::Jpg class for .jpg files" do
41
+ @image = Seamus::Builder.new(Factories::jpg.path)
42
+ @image.should be_an_instance_of(Seamus::File::Jpg)
43
+ end
44
+
45
+ it "includes Seamus::File::Video into Mov" do
46
+ Seamus::File::Mov.superclass.should be(Seamus::File::Video)
47
+ end
48
+
49
+ #etc
50
+
15
51
  end
16
- end
52
+
53
+ end
@@ -1,10 +1,12 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rubygems'
3
4
  require 'Seamus'
4
5
  require 'spec'
5
6
  require 'spec/autorun'
6
7
  require 'fakefs/spec_helpers'
8
+ require 'factories'
7
9
 
8
10
  Spec::Runner.configure do |config|
9
-
11
+ include Factories
10
12
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seamus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Burton
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-26 00:00:00 -08:00
12
+ date: 2010-02-10 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -35,22 +35,33 @@ files:
35
35
  - lib/core/numeric.rb
36
36
  - lib/mime_table.rb
37
37
  - lib/seamus.rb
38
- - lib/seamus/inspector.rb
38
+ - lib/seamus/builder.rb
39
+ - lib/seamus/file/application.rb
40
+ - lib/seamus/file/audio.rb
41
+ - lib/seamus/file/image.rb
42
+ - lib/seamus/file/text.rb
43
+ - lib/seamus/file/video.rb
39
44
  - lib/seamus/inspector/application_inspector.rb
40
45
  - lib/seamus/inspector/audio_inspector.rb
41
46
  - lib/seamus/inspector/image_inspector.rb
42
47
  - lib/seamus/inspector/text_inspector.rb
43
48
  - lib/seamus/inspector/video_inspector.rb
44
- - lib/seamus/processor.rb
45
49
  - lib/seamus/processor/application_processor.rb
46
50
  - lib/seamus/processor/audio_processor.rb
47
51
  - lib/seamus/processor/image_processor.rb
48
52
  - lib/seamus/processor/text_processor.rb
49
53
  - lib/seamus/processor/video_processor.rb
54
+ - lib/seamus/standard_additions.rb
50
55
  - seamus.gemspec
56
+ - spec/factories.rb
57
+ - spec/fixtures/test.jpg
58
+ - spec/fixtures/test.mov
59
+ - spec/fixtures/test.mp4
51
60
  - spec/lib/core/numeric_spec.rb
52
61
  - spec/lib/seamus/inspector/image_inspector_spec.rb
62
+ - spec/lib/seamus/inspector/video_inspector_spec.rb
53
63
  - spec/lib/seamus/processor/image_processor_spec.rb
64
+ - spec/lib/seamus/processor/video_processor_spec.rb
54
65
  - spec/seamus_spec.rb
55
66
  - spec/spec_helper.rb
56
67
  has_rdoc: true
@@ -82,8 +93,11 @@ signing_key:
82
93
  specification_version: 3
83
94
  summary: Seamus is not an Irish monk
84
95
  test_files:
96
+ - spec/factories.rb
85
97
  - spec/lib/core/numeric_spec.rb
86
98
  - spec/lib/seamus/inspector/image_inspector_spec.rb
99
+ - spec/lib/seamus/inspector/video_inspector_spec.rb
87
100
  - spec/lib/seamus/processor/image_processor_spec.rb
101
+ - spec/lib/seamus/processor/video_processor_spec.rb
88
102
  - spec/seamus_spec.rb
89
103
  - spec/spec_helper.rb
@@ -1,20 +0,0 @@
1
- module Seamus
2
- class Inspector
3
- attr_reader :file
4
-
5
- def initialize(file_path)
6
- @file = File.open(file_path, "r")
7
- end
8
-
9
- def stats
10
- raise RuntimeError, "method called on base class"
11
- end
12
-
13
- private
14
-
15
- def file_stat
16
- File::Stat.new(file.path)
17
- end
18
-
19
- end
20
- end
@@ -1,16 +0,0 @@
1
- module Seamus
2
- class Processor
3
- attr_reader :file, :file_attributes
4
-
5
- def initialize(file_path, file_attributes)
6
- @file = File.open(file_path, "r")
7
- @file_attributes = file_attributes
8
- end
9
-
10
- # nodoc
11
- def thumb_tempfile
12
- Tempfile.new("#{File.basename(@file.path, File.extname(@file.path))}_thumb.jpg")
13
- end
14
-
15
- end
16
- end