popcap 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +23 -32
  4. data/README.md +27 -24
  5. data/lib/pop_cap/audio_file.rb +13 -11
  6. data/lib/pop_cap/class_support.rb +73 -0
  7. data/lib/pop_cap/{commander.rb → ffmpeg/commander.rb} +8 -2
  8. data/lib/pop_cap/ffmpeg/converter.rb +57 -0
  9. data/lib/pop_cap/ffmpeg/ffmpeg.rb +45 -0
  10. data/lib/pop_cap/ffmpeg/tag_reader.rb +45 -0
  11. data/lib/pop_cap/ffmpeg/tag_writer.rb +44 -0
  12. data/lib/pop_cap/formatter.rb +62 -0
  13. data/lib/pop_cap/formatters/bit_rate.rb +5 -7
  14. data/lib/pop_cap/formatters/date.rb +12 -10
  15. data/lib/pop_cap/formatters/duration.rb +10 -12
  16. data/lib/pop_cap/formatters/filesize.rb +6 -8
  17. data/lib/pop_cap/tag/formatted_tag.rb +31 -0
  18. data/lib/pop_cap/tag/tag_hash.rb +55 -0
  19. data/lib/pop_cap/{tag_struct.rb → tag/tag_struct.rb} +2 -0
  20. data/lib/pop_cap/taggable.rb +50 -30
  21. data/lib/pop_cap/version.rb +1 -1
  22. data/popcap.gemspec +1 -2
  23. data/spec/integration/convert_audio_file_spec.rb +2 -2
  24. data/spec/integration/read_metatags_spec.rb +1 -1
  25. data/spec/integration/update_metatags_spec.rb +1 -1
  26. data/spec/lib/pop_cap/audio_file_spec.rb +3 -9
  27. data/spec/lib/pop_cap/class_support_spec.rb +59 -0
  28. data/spec/lib/pop_cap/{commander_spec.rb → ffmpeg/commander_spec.rb} +1 -1
  29. data/spec/lib/pop_cap/ffmpeg/converter_spec.rb +92 -0
  30. data/spec/lib/pop_cap/ffmpeg/ffmpeg_spec.rb +37 -0
  31. data/spec/lib/pop_cap/ffmpeg/tag_reader_spec.rb +67 -0
  32. data/spec/lib/pop_cap/ffmpeg/tag_writer_spec.rb +60 -0
  33. data/spec/lib/pop_cap/fileable_spec.rb +5 -5
  34. data/spec/lib/pop_cap/formatter_spec.rb +71 -0
  35. data/spec/lib/pop_cap/formatters/bit_rate_spec.rb +8 -0
  36. data/spec/lib/pop_cap/formatters/date_spec.rb +8 -0
  37. data/spec/lib/pop_cap/formatters/duration_spec.rb +8 -0
  38. data/spec/lib/pop_cap/formatters/filesize_spec.rb +8 -0
  39. data/spec/lib/pop_cap/tag/formatted_tag_spec.rb +29 -0
  40. data/spec/lib/pop_cap/tag/tag_hash_spec.rb +35 -0
  41. data/spec/lib/pop_cap/{tag_struct_spec.rb → tag/tag_struct_spec.rb} +5 -1
  42. data/spec/lib/pop_cap/taggable_spec.rb +24 -10
  43. data/spec/spec_helper.rb +0 -3
  44. data/spec/support/popcap_spec_helper.rb +50 -33
  45. metadata +40 -56
  46. data/lib/pop_cap/converter.rb +0 -40
  47. data/lib/pop_cap/ffmpeg.rb +0 -130
  48. data/lib/pop_cap/formatters.rb +0 -33
  49. data/lib/pop_cap/helper.rb +0 -53
  50. data/lib/pop_cap/tag_key.rb +0 -32
  51. data/lib/pop_cap/tag_line.rb +0 -36
  52. data/spec/lib/pop_cap/converter_spec.rb +0 -67
  53. data/spec/lib/pop_cap/ffmpeg_spec.rb +0 -96
  54. data/spec/lib/pop_cap/formatters_spec.rb +0 -36
  55. data/spec/lib/pop_cap/helper_spec.rb +0 -42
  56. data/spec/lib/pop_cap/tag_key_spec.rb +0 -48
  57. data/spec/lib/pop_cap/tag_line_spec.rb +0 -26
  58. /data/spec/{support → fixtures}/sample.flac +0 -0
@@ -1,13 +1,11 @@
1
+ require 'pop_cap/formatter'
2
+
1
3
  module PopCap
2
4
  module Formatters
3
5
  # Public: This will format a duration tag as strftime.
4
6
  #
5
7
  # time - Provide a string, float, or integer.
6
- class Duration
7
- def initialize(time)
8
- @time = time
9
- end
10
-
8
+ class Duration < Formatter
11
9
  # Public: This will format a duration tag as strftime.
12
10
  # It will raise a warning if the time is greater than 24 hours.
13
11
  # Leading zeroes & colons are removed.
@@ -18,22 +16,22 @@ module PopCap
18
16
  # # => '7:00'
19
17
  #
20
18
  def format
21
- return unless @time.to_i > 0
19
+ return unless value.to_i > 0
22
20
  return warning_message if over_twenty_four_hours?
23
- remove_leading_zeroes(to_strftime)
21
+ human_readable_time
24
22
  end
25
23
 
26
24
  private
27
- def to_strftime
28
- @strftime = Time.at(@time.to_f).gmtime.strftime('%H:%M:%S')
25
+ def human_readable_time
26
+ to_strftime.sub(/^(0+|:)+/,'')
29
27
  end
30
28
 
31
- def remove_leading_zeroes(strftime)
32
- @strftime.sub(/^(0+|:)+/,'')
29
+ def to_strftime
30
+ Time.at(value.to_f).gmtime.strftime('%H:%M:%S')
33
31
  end
34
32
 
35
33
  def over_twenty_four_hours?
36
- @time.to_i > 86399
34
+ value.to_i > 86399
37
35
  end
38
36
 
39
37
  def warning_message
@@ -1,3 +1,5 @@
1
+ require 'pop_cap/formatter'
2
+
1
3
  module PopCap
2
4
  module Formatters
3
5
  # Public: This class formats a filesize as human readable,
@@ -5,14 +7,10 @@ module PopCap
5
7
  #
6
8
  # filesize - Provide a filesize as string, integer, or float.
7
9
  #
8
- class Filesize
10
+ class Filesize < Formatter
9
11
  ::BASE = 1024
10
12
  ::UNITS = %W{B K M G T}
11
13
 
12
- def initialize(filesize)
13
- @filesize = filesize
14
- end
15
-
16
14
  # Public: This method will format the filesize.
17
15
  # It raises a warning message if size is greater than 999 terabytes.
18
16
  #
@@ -22,7 +20,7 @@ module PopCap
22
20
  # # => '11.8M'
23
21
  #
24
22
  def format
25
- return if @filesize.to_i == 0
23
+ return if value.to_i == 0
26
24
  return warning_message if too_large?
27
25
  converted_filesize.to_s + measurement_character
28
26
  end
@@ -42,7 +40,7 @@ module PopCap
42
40
  end
43
41
 
44
42
  def float
45
- Float(@filesize)
43
+ Float(value)
46
44
  end
47
45
 
48
46
  def is_zero_decimal?
@@ -58,7 +56,7 @@ module PopCap
58
56
  end
59
57
 
60
58
  def too_large?
61
- @filesize.to_i > 1099400000000000
59
+ value.to_i > 1099400000000000
62
60
  end
63
61
 
64
62
  def warning_message
@@ -0,0 +1,31 @@
1
+ module PopCap
2
+ # Public: This class will apply a provided Formatter to
3
+ # the supplied value.
4
+ #
5
+ class FormattedTag
6
+ attr_reader :formatter, :value
7
+
8
+ # Public: To construct an instance, provide a Formatter
9
+ # and the value to format.
10
+ #
11
+ # formatter - A class of type Formatters::Formatter.
12
+ # value - The value to format.
13
+ #
14
+ def initialize(formatter, value)
15
+ @formatter, @value = formatter, value
16
+ end
17
+
18
+ # Public: This returns the formatted value.
19
+ #
20
+ def format
21
+ formatter.format(value)
22
+ end
23
+
24
+ # Public: A class level method that initializes the class
25
+ # and formats the value.
26
+ #
27
+ def self.format(formatter, value)
28
+ new(formatter, value).format
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ require 'json'
2
+
3
+ module PopCap
4
+ # Public: This class formats the JSON output of TagReader.
5
+ #
6
+ class TagHash
7
+ attr_reader :json
8
+
9
+ def initialize(json)
10
+ @json = json
11
+ end
12
+
13
+ # Public: This method returns a Ruby hash.
14
+ # The keys have been converted to symbols.
15
+ #
16
+ def hash
17
+ renamed_hash.reject { |key,_| key == :size }
18
+ end
19
+
20
+ # Public: This method wraps #new & #hash.
21
+ #
22
+ def self.hash(json)
23
+ new(json).hash
24
+ end
25
+
26
+ private
27
+ def renamed_hash
28
+ symbolized_hash.merge({filesize: size_element})
29
+ end
30
+
31
+ def symbolized_hash
32
+ Hash[merged_hashes.map { |key,val| [key.downcase.to_sym, val] }]
33
+ end
34
+
35
+ def merged_hashes
36
+ format_hash.merge(tags_hash)
37
+ end
38
+
39
+ def size_element
40
+ format_hash['size']
41
+ end
42
+
43
+ def format_hash
44
+ @format ||= parsed_json.reject { |key,_| key == 'tags' }
45
+ end
46
+
47
+ def tags_hash
48
+ parsed_json['tags']
49
+ end
50
+
51
+ def parsed_json
52
+ @parsed ||= JSON.parse(json)['format']
53
+ end
54
+ end
55
+ end
@@ -11,6 +11,8 @@ module PopCap
11
11
  # ts.date => 1984
12
12
  #
13
13
  class TagStruct
14
+ include Enumerable
15
+
14
16
  def initialize(hash)
15
17
  raise(ArgumentError, argument_error_message) unless hash.kind_of?(Hash)
16
18
  @hash = hash
@@ -1,21 +1,48 @@
1
- require 'pop_cap/helper'
2
- require 'pop_cap/formatters'
3
- require 'pop_cap/tag_line'
4
- require 'pop_cap/tag_struct'
1
+ require 'pop_cap/class_support'
2
+ require 'pop_cap/formatter'
3
+ require 'pop_cap/tag/formatted_tag'
4
+ require 'pop_cap/tag/tag_hash'
5
+ require 'pop_cap/tag/tag_struct'
5
6
 
6
7
  module PopCap
7
- # Internal: This module is included in anything with a #raw_tags
8
+ # Public: This module is included in anything with a #raw_tags
8
9
  # attribute. It is used to parse and build tags from FFmpeg raw output.
9
10
  #
10
11
  module Taggable
11
- include Formatters
12
-
13
- # Internal: This method reloads memoized tags.
12
+ # Public: This method reloads memoized tags.
13
+ #
14
14
  def reload!
15
- @lined, @tags, @hash = nil, nil, nil
15
+ @unformatted_hash, @formatted_hash, @tag_struct = nil, nil, nil
16
+ end
17
+
18
+ # Public: This method builds an unformatted hash of tags.
19
+ #
20
+ # Examples
21
+ # class SomeClass
22
+ # def raw_tags
23
+ # end
24
+ # end
25
+ #
26
+ # klass = SomeClass.new
27
+ # klass.unformatted
28
+ # # =>
29
+ # { filename: 'spec/fixtures/sample.flac',
30
+ # format_name: 'flac',
31
+ # duration: '1.000000',
32
+ # filesize: '18291',
33
+ # bit_rate: '146328',
34
+ # genre: 'Sample Genre',
35
+ # track: '01',
36
+ # album: 'Sample Album',
37
+ # date: '2012',
38
+ # title: 'Sample Title',
39
+ # artist: 'Sample Artist' }
40
+ #
41
+ def unformatted(tag_hash: TagHash)
42
+ @unformatted_hash ||= tag_hash.hash(self.raw_tags)
16
43
  end
17
44
 
18
- # Internal: This method builds a sanitized hash from #raw_tags.
45
+ # Public: This method builds an unformatted hash of tags.
19
46
  #
20
47
  # Examples
21
48
  # class SomeClass
@@ -24,9 +51,9 @@ module PopCap
24
51
  # end
25
52
  #
26
53
  # klass = SomeClass.new
27
- # klass.to_hash
54
+ # klass.unformatted
28
55
  # # =>
29
- # { filename: 'spec/support/sample.flac',
56
+ # { filename: 'spec/fixtures/sample.flac',
30
57
  # format_name: 'flac',
31
58
  # duration: '1.000000',
32
59
  # filesize: '18291',
@@ -38,8 +65,8 @@ module PopCap
38
65
  # title: 'Sample Title',
39
66
  # artist: 'Sample Artist' }
40
67
  #
41
- def to_hash
42
- @hash ||= lined_hash.merge(formatted_hash)
68
+ def formatted
69
+ @formatted_hash ||= unformatted.merge(formatted_tags)
43
70
  end
44
71
 
45
72
  # Public: This method builds an tag structure from #to_hash. Also,
@@ -60,7 +87,7 @@ module PopCap
60
87
  # .bit_rate => '146 kb/s'
61
88
  # .date => 2012
62
89
  # .duration => '1'
63
- # .filename => 'spec/support/sample.flac'
90
+ # .filename => 'spec/fixtures/sample.flac'
64
91
  # .filesize => '17.9K'
65
92
  # .format_long_name => 'raw FLAC'
66
93
  # .format_name => 'flac'
@@ -71,30 +98,23 @@ module PopCap
71
98
  # .track => '01'
72
99
  #
73
100
  def tags
74
- @tags ||= build_tag_struct(to_hash)
101
+ @tag_struct ||= build_tag_struct(formatted)
75
102
  end
76
103
 
77
104
  private
78
- def lines
79
- self.raw_tags.split("\n")
80
- end
81
-
82
105
  def build_tag_struct(hash)
83
106
  TagStruct.new(hash)
84
107
  end
85
108
 
86
- def lined_hash
87
- @lined ||=
88
- lines.inject({}) { |hsh,line| hsh.merge(TagLine.new(line)).to_hash }
109
+ def formatted_tags(support: ClassSupport)
110
+ Formatters::Formatter.subclasses.inject({}) do |formatted, formatter|
111
+ attribute = support.new(formatter).symbolize
112
+ formatted.merge({attribute => format(formatter, attribute)})
113
+ end
89
114
  end
90
115
 
91
- def formatted_hash
92
- ::INCLUDED_FORMATTERS.inject({}) do |formatted, formatter|
93
- key, value = formatter
94
- helper = Helper.new(value)
95
- klass = helper.constantize
96
- formatted.merge({key => klass.new(lined_hash[key]).format})
97
- end
116
+ def format(formatter, attribute, tag: FormattedTag)
117
+ tag.format(formatter, unformatted[attribute])
98
118
  end
99
119
  end
100
120
  end
@@ -1,3 +1,3 @@
1
1
  module PopCap
2
- VERSION = '0.7.2'
2
+ VERSION = '0.8.0'
3
3
  end
data/popcap.gemspec CHANGED
@@ -13,12 +13,11 @@ Gem::Specification.new do |s|
13
13
  s.summary = %q{A library work with audio files on the filesystem .}
14
14
  s.description = %q{Read & write metadata tags, convert audio files to alternate formats, manage files on the filesystem.}
15
15
 
16
- s.required_ruby_version = '>= 1.9.3'
16
+ s.required_ruby_version = '>= 2.0.0'
17
17
  s.required_rubygems_version = '>= 1.3.6'
18
18
 
19
19
  s.add_development_dependency 'reek', '~> 1.2'
20
20
  s.add_development_dependency 'rspec', '~> 2.11'
21
- s.add_development_dependency 'simplecov', '~> 0.7'
22
21
 
23
22
  git_files = `git ls-files -z`.split("\0") rescue ''
24
23
  s.files = git_files
@@ -7,9 +7,9 @@ module PopCap
7
7
  after { PopCapSpecHelper.remove_converted }
8
8
 
9
9
  it 'creates a new audio file with the specified format & bitrate' do
10
- audio_file = AudioFile.new('spec/support/sample.flac')
10
+ audio_file = AudioFile.new(PopCapSpecHelper::SAMPLE_FILE)
11
11
  audio_file.convert(:mp3, 128)
12
- expect(File.exists?('spec/support/sample.mp3')).to be_true
12
+ expect(File.exists?('spec/fixtures/sample.mp3')).to be_true
13
13
  end
14
14
  end
15
15
  end
@@ -4,7 +4,7 @@ require 'support/popcap_spec_helper'
4
4
 
5
5
  describe PopCap do
6
6
  let(:audio_file) { PopCap::AudioFile.new(filepath) }
7
- let(:filepath) { 'spec/support/sample.flac' }
7
+ let(:filepath) { PopCapSpecHelper::SAMPLE_FILE }
8
8
 
9
9
  it '#raw_tags' do
10
10
  expect(audio_file.raw_tags).to eq PopCapSpecHelper.raw_tags
@@ -4,7 +4,7 @@ require 'support/popcap_spec_helper'
4
4
 
5
5
  describe PopCap do
6
6
  let(:audio_file) { PopCap::AudioFile.new(filepath) }
7
- let(:filepath) { 'spec/support/sample.flac' }
7
+ let(:filepath) { PopCapSpecHelper::SAMPLE_FILE }
8
8
 
9
9
  context '#update_tags' do
10
10
  before { PopCapSpecHelper.setup }
@@ -5,7 +5,7 @@ require 'support/popcap_spec_helper'
5
5
  module PopCap
6
6
  describe AudioFile do
7
7
  let(:audio_file) { AudioFile.new(filepath) }
8
- let(:filepath) { 'spec/support/sample.flac' }
8
+ let(:filepath) { PopCapSpecHelper::SAMPLE_FILE }
9
9
  let(:included_modules) { AudioFile.included_modules }
10
10
 
11
11
  before { PopCapSpecHelper.setup }
@@ -50,22 +50,16 @@ module PopCap
50
50
  context '#reload!' do
51
51
  it 'reloads raw tags' do
52
52
  audio_file.raw_tags
53
+ expect(audio_file.instance_variable_get('@raw')).not_to be_nil
53
54
  audio_file.reload!
54
55
  expect(audio_file.instance_variable_get('@raw')).to be_nil
55
56
  end
56
-
57
- it 'calls up to Taggable#reload!' do
58
- audio_file.raw_tags
59
- audio_file.reload!
60
- expect(audio_file.instance_variable_get('@tags')).to be_nil
61
- expect(audio_file.instance_variable_get('@to_hash')).to be_nil
62
- end
63
57
  end
64
58
 
65
59
  context '#update_tags' do
66
60
  it 'reloads after tags updated' do
67
61
  audio_file.should_receive(:reload!)
68
- expect(audio_file.update_tags({foo: 'foo'}))
62
+ audio_file.update_tags({foo: 'foo'})
69
63
  end
70
64
  end
71
65
  end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+ require 'pop_cap/class_support'
3
+
4
+ module PopCap
5
+ describe ClassSupport do
6
+ context '#camelize' do
7
+ it 'converts to CamelCase' do
8
+ maker = ClassSupport.new('one_two')
9
+ expect(maker.camelize).to eq('OneTwo')
10
+ end
11
+
12
+ it 'handles symbols' do
13
+ maker = ClassSupport.new(:one_two_three)
14
+ expect(maker.camelize).to eq('OneTwoThree')
15
+ end
16
+ end
17
+
18
+ context '#symbolize' do
19
+ it 'returns a symbol' do
20
+ maker = ClassSupport.new('One')
21
+ expect(maker.symbolize).to eq(:one)
22
+ end
23
+
24
+ it 'handles CamelCase' do
25
+ maker = ClassSupport.new('OneTwo')
26
+ expect(maker.symbolize).to eq(:one_two)
27
+ end
28
+
29
+ it 'demodulizes the symbol' do
30
+ maker = ClassSupport.new('One::Two::ThreeFour')
31
+ expect(maker.symbolize).to eq(:three_four)
32
+ end
33
+ end
34
+
35
+ context '#namespace' do
36
+ it 'accounts for namespacing with a forward slash' do
37
+ maker = ClassSupport.new('one/two/three_four')
38
+ expect(maker.namespace).to eq('One::Two::ThreeFour')
39
+ end
40
+
41
+ it 'accounts for mixed case in namespacing' do
42
+ maker = ClassSupport.new('One/Two/Three_Four')
43
+ expect(maker.namespace).to eq('One::Two::ThreeFour')
44
+ end
45
+ end
46
+
47
+ context '#constantize' do
48
+ it 'converts to a constant' do
49
+ maker = ClassSupport.new('array')
50
+ expect(maker.constantize).to eq(Array)
51
+ end
52
+
53
+ it 'handles module namespacing' do
54
+ maker = ClassSupport.new('file/stat')
55
+ expect(maker.constantize).to eq(File::Stat)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'pop_cap/commander'
2
+ require 'pop_cap/ffmpeg/commander'
3
3
 
4
4
  module PopCap
5
5
  describe Commander do
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+ require 'pop_cap/ffmpeg/converter'
3
+
4
+ module PopCap
5
+ describe Converter do
6
+ let(:bitrate) { %W{-ab 192k}}
7
+ let(:command) { input + bitrate + output_path }
8
+ let(:commander) { double('commander') }
9
+ let(:filepath) { 'path/to/file.flac' }
10
+ let(:input) { %W{ffmpeg -i #{filepath}} }
11
+ let(:instance) { double('converter_instance') }
12
+
13
+ def shared_expectation(command=command)
14
+ commander.should_receive(:new).with(*command) { instance }
15
+ instance.stub_chain(:execute, :success?) { true }
16
+ end
17
+
18
+ describe '.convert' do
19
+ let(:output_path) { %W{path/to/file.ogg} }
20
+
21
+ it 'has a class method for #convert' do
22
+ shared_expectation
23
+ Converter.convert(filepath, {format: 'ogg', commander: commander})
24
+ end
25
+ end
26
+
27
+ describe '#convert' do
28
+ let(:output_path) { %W{path/to/file.ogg} }
29
+
30
+ context 'format' do
31
+ it 'handles a string' do
32
+ shared_expectation
33
+ Converter.new(filepath, {format: 'ogg', commander: commander}).convert
34
+ end
35
+
36
+ it 'handles a symbol' do
37
+ shared_expectation
38
+ Converter.new(filepath, {format: :ogg, commander: commander}).convert
39
+ end
40
+
41
+ it 'is case insenstive' do
42
+ shared_expectation
43
+ Converter.new(filepath, {format: 'OGG', commander: commander}).convert
44
+ end
45
+ end
46
+
47
+ context 'bitrate' do
48
+ let(:output_path) { %W{path/to/file.mp3} }
49
+
50
+ it 'defaults to 192 kb/s' do
51
+ shared_expectation
52
+ Converter.new(filepath, {format: :mp3, commander: commander}).convert
53
+ end
54
+
55
+ it 'takes an optional bitrate' do
56
+ options = {format: :mp3, bitrate: 64, commander: commander}
57
+ bitrate_command = input + %W{-ab 64k} + output_path
58
+ shared_expectation(bitrate_command)
59
+ Converter.new(filepath, options).convert
60
+ end
61
+
62
+ it 'handles bitrate as string' do
63
+ options = {format: :mp3, bitrate: '128', commander: commander}
64
+ bitrate_command = input + %W{-ab 128k} + output_path
65
+ shared_expectation(bitrate_command)
66
+ Converter.new(filepath, options).convert
67
+ end
68
+ end
69
+
70
+ context 'm4a' do
71
+ let(:output_path) { %W{path/to/file.m4a} }
72
+
73
+ it 'uses strict mode' do
74
+ strict = input + %W{-strict -2} + bitrate + output_path
75
+ shared_expectation(strict)
76
+ Converter.new(filepath, {format: :m4a, commander: commander}).convert
77
+ end
78
+ end
79
+
80
+ context 'errors' do
81
+ it 'raises error if could not convert file' do
82
+ expect do
83
+ commander.should_receive(:new).with(*command) { instance }
84
+ instance.stub_chain(:execute, :success?) { false }
85
+ Converter.new(filepath, {format: :ogg,
86
+ commander: commander}).convert
87
+ end.to raise_error(FFmpegError, "Error converting #{filepath}.")
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'support/popcap_spec_helper'
3
+ require 'pop_cap/ffmpeg/ffmpeg'
4
+
5
+ module PopCap
6
+ describe FFmpeg do
7
+
8
+ let(:filepath) { File.realpath(PopCapSpecHelper::SAMPLE_FILE) }
9
+ let(:ffmpeg) { FFmpeg.new(filepath) }
10
+
11
+ it 'returns its filepath' do
12
+ expect(ffmpeg.filepath).to eq filepath
13
+ end
14
+
15
+ it 'sets default commander to Commander' do
16
+ expect(ffmpeg.commander).to eq Commander
17
+ end
18
+
19
+ it '#new takes an options hash' do
20
+ opts = {foo: 'foo'}
21
+ ffmpeg = FFmpeg.new(filepath, opts)
22
+ expect(ffmpeg.options).to eq({foo: 'foo'})
23
+ end
24
+
25
+ it 'has a custom error message' do
26
+ expect(ffmpeg.error_message('loving')).to eq "Error loving #{filepath}."
27
+ end
28
+
29
+ it 'raises error if FFmpeg not installed' do
30
+ error_message = 'No such file or directory - FFmpeg is not installed.'
31
+ expect do
32
+ Open3.stub(:capture3).with('ffmpeg').and_raise(Errno::ENOENT)
33
+ FFmpeg.new('filepath')
34
+ end.to raise_error(MissingDependency, error_message)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'pop_cap/ffmpeg/tag_reader'
3
+
4
+ module PopCap
5
+ describe TagReader do
6
+ let(:commander) { double('commander') }
7
+ let(:instance) { double('commander_instance') }
8
+ let(:file) { 'path/to/file.flac' }
9
+ let(:reader) { TagReader.new(file, {commander: commander}) }
10
+ let(:command) { %W{ffprobe -show_format -print_format json #{file}} }
11
+
12
+ let(:shared_example) do
13
+ commander.should_receive(:new).with(*command) { instance }
14
+ instance.stub_chain(:execute, :success?) { true }
15
+ instance.stub_chain(:execute, :stdout) { instance }
16
+ end
17
+
18
+ it 'test' do
19
+ puts JSON.parse(TagReader.read('spec/fixtures/sample.flac'))
20
+ end
21
+
22
+ describe '.read' do
23
+ before { shared_example }
24
+
25
+ it 'has a class method for #read' do
26
+ instance.stub(:valid_encoding?) { true }
27
+ JSON.stub(:load)
28
+ TagReader.read(file, {commander: commander})
29
+ end
30
+ end
31
+
32
+ describe '#read' do
33
+ before { shared_example }
34
+ after { reader.read }
35
+
36
+ it 'returns the output as JSON' do
37
+ instance.stub(:valid_encoding?) { true }
38
+ JSON.should_receive(:load).with(instance) { instance }
39
+ instance.should_receive(:to_json)
40
+ end
41
+
42
+ it 'does not encode valid byte strings' do
43
+ instance.stub(:valid_encoding?) { true }
44
+ JSON.stub(:load)
45
+ instance.should_not_receive(:encode!)
46
+ end
47
+
48
+ it 'encodes invalid byte strings as UTF-8' do
49
+ instance.stub(:valid_encoding?) { false }
50
+ instance.stub_chain(:encoding, :name) { 'UTF-8' }
51
+ instance.should_receive(:encode!).
52
+ with('UTF-16', 'UTF-8', undef: :replace, invalid: :replace)
53
+ instance.should_receive(:encode!).with('UTF-8')
54
+ end
55
+ end
56
+
57
+ context 'errors' do
58
+ it 'raises error if could not read tags' do
59
+ expect do
60
+ commander.should_receive(:new).with(*command) { instance }
61
+ instance.stub_chain(:execute, :success?) { false }
62
+ reader.read
63
+ end.to raise_error(FFmpegError, "Error reading #{file}.")
64
+ end
65
+ end
66
+ end
67
+ end