seamus 0.2.4 → 0.3.0

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/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