musical 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ module Musical
3
+ class DVD::Chapter
4
+ include Musical::Util
5
+
6
+ attr_accessor :vob_path, :name, :chapter_number
7
+
8
+ DEFAULT_CHAPTER_NUMBER = 1
9
+ DEFAULT_CHAPTER_NAME = 'default chapter name'
10
+ DEFAULT_TITLE_NUMBER = 1
11
+
12
+ def initialize(vob_path, options = {})
13
+ raise ArgumentError.new 'VOB path is not given' if vob_path.nil?
14
+
15
+ @vob_path = vob_path
16
+ @name = options[:name] || DEFAULT_CHAPTER_NAME
17
+ @chapter_number = options[:chapter_number] || DEFAULT_CHAPTER_NUMBER
18
+ @title_number = options[:title_number] || DEFAULT_TITLE_NUMBER
19
+ end
20
+
21
+ def to_wav(wav_path = "#{Musical.configuration.output}/chapter_#{@title_number}_#{@chapter_number}.wav")
22
+ return @wav if @wav
23
+
24
+ command = "ffmpeg -i #{@vob_path} #{wav_path}"
25
+ execute_command(command, true)
26
+ DVD::Wav.new(wav_path)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ # coding: utf-8
2
+ module Musical
3
+ class DVD::Wav < File
4
+ def expand_path
5
+ File.expand_path(self.path)
6
+ end
7
+
8
+ def delete!
9
+ FileUtils.rm_f(self.expand_path)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ module Musical
3
+ module Util
4
+ REQUIRED_APPS = %w(dvdbackup ffmpeg).freeze
5
+
6
+ def check_env
7
+ REQUIRED_APPS.each do |app|
8
+ unless installed?(app)
9
+ messages = []
10
+ messages << "'#{app}' is not installed."
11
+ messages << "Try this command to install '#{app}'."
12
+ messages << ""
13
+ messages << " brew install #{app}"
14
+ messages << ""
15
+ raise RuntimeError, messages.join("\n")
16
+ end
17
+ end
18
+ true
19
+ end
20
+
21
+ def installed?(app)
22
+ !execute_command("which #{app}").empty?
23
+ end
24
+
25
+ def execute_command(cmd, silent = false)
26
+ cmd << ' 2>/dev/null' if silent
27
+ execute_out, _ = *Open3.capture2(cmd)
28
+ execute_out
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ # coding: utf-8
2
+ module Musical
3
+ VERSION = "1.0.0"
4
+ end
@@ -1,73 +1,34 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'musical/version'
5
5
 
6
- Gem::Specification.new do |s|
7
- s.name = %q{musical}
8
- s.version = "0.0.4"
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "musical"
8
+ spec.version = Musical::VERSION
9
+ spec.authors = ["ryo katsuma"]
10
+ spec.email = ["katsuma@gmail.com"]
11
+ spec.description = %q{Musical is a simple tool for your favorite music DVD. You can rip vob file by DVD chapter, convert it to wav file and add it to your iTunes library.}
12
+ spec.summary = %q{A simple rip, encode and iTunes library tool for your favorite music DVD}
13
+ spec.homepage = "http://github.com/katsuma/musical"
14
+ spec.license = "MIT"
9
15
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["ryo katsuma"]
12
- s.date = %q{2012-06-10}
13
- s.default_executable = %q{musical}
14
- s.description = %q{Musical is a simple tool for your favorite music DVD. You can rip vob file by DVD chapter, convert it to wav file and add it to your iTunes library.}
15
- s.email = %q{katsuma@gmail.com}
16
- s.executables = ["musical"]
17
- s.extra_rdoc_files = [
18
- "LICENSE.txt",
19
- "README.rdoc"
20
- ]
21
- s.files = [
22
- ".document",
23
- ".rspec",
24
- "Gemfile",
25
- "LICENSE.txt",
26
- "README.rdoc",
27
- "Rakefile",
28
- "VERSION",
29
- "bin/musical",
30
- "lib/musical.rb",
31
- "lib/musical/dvd.rb",
32
- "lib/musical/itunes.rb",
33
- "musical.gemspec",
34
- "spec/musical_spec.rb",
35
- "spec/spec_helper.rb"
36
- ]
37
- s.homepage = %q{http://github.com/katsuma/musical}
38
- s.licenses = ["MIT"]
39
- s.require_paths = ["lib"]
40
- s.rubygems_version = %q{1.6.2}
41
- s.summary = %q{A simple rip, encode and iTunes library tool for your favorite music DVD}
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
42
20
 
43
- if s.respond_to? :specification_version then
44
- s.specification_version = 3
45
-
46
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
- s.add_runtime_dependency(%q<trollop>, [">= 1.16.2"])
48
- s.add_runtime_dependency(%q<progressbar>, [">= 0.9.1"])
49
- s.add_runtime_dependency(%q<rb-appscript>, [">= 0.6.1"])
50
- s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
51
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
52
- s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
53
- s.add_development_dependency(%q<rcov>, [">= 0"])
54
- else
55
- s.add_dependency(%q<trollop>, [">= 1.16.2"])
56
- s.add_dependency(%q<progressbar>, [">= 0.9.1"])
57
- s.add_dependency(%q<rb-appscript>, [">= 0.6.1"])
58
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
59
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
60
- s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
61
- s.add_dependency(%q<rcov>, [">= 0"])
62
- end
63
- else
64
- s.add_dependency(%q<trollop>, [">= 1.16.2"])
65
- s.add_dependency(%q<progressbar>, [">= 0.9.1"])
66
- s.add_dependency(%q<rb-appscript>, [">= 0.6.1"])
67
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
68
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
- s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
70
- s.add_dependency(%q<rcov>, [">= 0"])
71
- end
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake", "~> 10.0.3"
23
+ spec.add_development_dependency "rspec", "~> 2.14.0"
24
+ spec.add_development_dependency "rb-fsevent", "~> 0.3"
25
+ spec.add_development_dependency "guard", "~> 1.6.2"
26
+ spec.add_development_dependency "guard-rspec", "~> 2.4.1"
27
+ spec.add_development_dependency "growl", "~> 1.0.3"
28
+ spec.add_development_dependency "fakefs", "~> 0.4.2"
29
+ spec.add_development_dependency "simplecov", "~> 0.7.1"
30
+ spec.add_development_dependency "coveralls", "~> 0.6.6"
31
+ spec.add_runtime_dependency "ruby-progressbar", ">= 1.2.0"
32
+ spec.add_runtime_dependency "trollop", ">= 2.0"
33
+ spec.add_runtime_dependency "itunes-client", "~> 0.1.2"
72
34
  end
73
-
@@ -0,0 +1,17 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'musical'
4
+
5
+ describe Musical::Configuration do
6
+ describe '#build' do
7
+ subject { described_class.build(options) }
8
+ let(:options) { { title: 'foo', artist: 'bar' } }
9
+
10
+ it 'sets hash data to config class varible as subclass of OpenStruct' do
11
+ subject
12
+ expect(described_class.config).to be_an OpenStruct
13
+ expect(subject.title).to eq('foo')
14
+ expect(subject.artist).to eq('bar')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,47 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'musical'
4
+
5
+ describe Musical::DVD::Chapter do
6
+ describe '#initialize' do
7
+ subject { described_class.new(vob_path, options) }
8
+
9
+ context 'when vob path is not given' do
10
+ let(:vob_path) { nil }
11
+ let(:options) { {} }
12
+ it 'raises an ArgumentError' do
13
+ expect{ subject }.to raise_error(ArgumentError)
14
+ end
15
+ end
16
+
17
+ context 'when vob path is given' do
18
+ let(:vob_path) { '/path/to/foo.vob' }
19
+ let(:options) { { chapter_number: 3 } }
20
+ it 'returns an instance of Chapter' do
21
+ expect(subject).to be_a(described_class)
22
+ expect(subject.chapter_number).to eq(3)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '#to_wav' do
28
+ subject { chapter.to_wav(wav_path) }
29
+
30
+ let(:chapter) { described_class.new(vob_path, chapter_number: 10) }
31
+ let(:vob_path) { '/path/to/foo.vob' }
32
+ let(:wav_path) { '/tmp/foo.wav' }
33
+
34
+ before do
35
+ chapter.should_receive(:execute_command).
36
+ with("ffmpeg -i #{vob_path} #{wav_path}", true).
37
+ and_return(FileUtils.touch(wav_path))
38
+ end
39
+
40
+ after { subject.delete! }
41
+
42
+ it 'returns wav file which is converted', fakefs: true do
43
+ expect(subject).to be_a(Musical::DVD::Wav)
44
+ expect(subject.path).to eq(wav_path)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'musical'
4
+
5
+ describe Musical::DVD::Wav do
6
+ describe '#delete!' do
7
+ subject { wav.delete! }
8
+ let!(:wav) do
9
+ FileUtils.touch(wav_path)
10
+ described_class.new(wav_path)
11
+ end
12
+ let(:wav_path) { '/tmp/foo.wav' }
13
+
14
+ it 'deletes original file', faksefs: true do
15
+ expect { subject }.to change { File.exist?(wav_path) }.from(true).to(false)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,235 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'musical'
4
+
5
+ include Musical
6
+
7
+ describe DVD do
8
+ describe '.detect' do
9
+ subject(:detect) { DVD.detect }
10
+ let(:drutil) { 'drutil status' }
11
+
12
+ context 'when DVD drive is not found' do
13
+ before do
14
+ DVD.should_receive(:execute_command).with(drutil).and_return('')
15
+ end
16
+
17
+ it 'raises a RuntimeError' do
18
+ expect { detect }.to raise_error(RuntimeError)
19
+ end
20
+ end
21
+
22
+ context 'when DVD is not inserted' do
23
+ let(:drutil_out) do
24
+ <<"EOM"
25
+ Vendor Product Rev
26
+ MATSHITA DVD-R UJ-85J FM0S
27
+
28
+ Type: No Media Inserted
29
+ EOM
30
+ end
31
+ before { DVD.should_receive(:execute_command).with(drutil).and_return(drutil_out) }
32
+
33
+ it 'raises a RuntimeError' do
34
+ expect { detect }.to raise_error(RuntimeError)
35
+ end
36
+ end
37
+
38
+ context 'when DVD is inserted' do
39
+ let(:drutil_out) do
40
+ <<"EOM"
41
+ Vendor Product Rev
42
+ MATSHITA DVD-R UJ-85J FM0S
43
+
44
+ Type: DVD-ROM Name: /dev/disk3
45
+ Sessions: 1 Tracks: 1
46
+ Overwritable: 00:00:00 blocks: 0 / 0.00MB / 0.00MiB
47
+ Space Free: 00:00:00 blocks: 0 / 0.00MB / 0.00MiB
48
+ Space Used: 787:56:00 blocks: 3545700 / 7.26GB / 6.76GiB
49
+ Writability:
50
+ Book Type: DVD-ROM (v1)
51
+ EOM
52
+ end
53
+ before do
54
+ DVD.should_receive(:execute_command).with(drutil).and_return(drutil_out)
55
+ end
56
+
57
+ it { should == '/dev/disk3' }
58
+ end
59
+ end
60
+
61
+ describe '.path' do
62
+ subject { DVD.path }
63
+ context 'path class property is not set' do
64
+ it 'returns nil' do
65
+ expect(subject).to eq(nil)
66
+ end
67
+ end
68
+
69
+ context 'path class property is set' do
70
+ before{ DVD.path = '/dev/path' }
71
+ it 'returns property value' do
72
+ expect(subject).to eq('/dev/path')
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '.load' do
78
+ before { DVD.path = nil }
79
+ before { DVD.any_instance.should_receive(:info).and_return('info data') }
80
+
81
+ context 'when options are not given' do
82
+ subject { DVD.load }
83
+
84
+ context 'and if path is set' do
85
+ before { DVD.path = '/dev/some/path' }
86
+ it 'does not call DVD.detect' do
87
+ DVD.should_not_receive(:detect)
88
+ subject
89
+ end
90
+ end
91
+
92
+ context 'and if DVD path is not set' do
93
+ it 'sets path by DVD.detect' do
94
+ DVD.should_receive(:detect).and_return('/dev/some/path')
95
+ subject
96
+ expect(DVD.path).to eq('/dev/some/path')
97
+ end
98
+ end
99
+ end
100
+
101
+ context 'when options are given' do
102
+ subject { DVD.load(options) }
103
+ context 'and if option does not have `forcibly` key' do
104
+ let(:options) { { path: '/dev/path', title: 'some title' } }
105
+
106
+ it 'sets path by given option' do
107
+ subject
108
+ expect(DVD.path).to eq('/dev/path')
109
+ end
110
+
111
+ it 'sets title by given option' do
112
+ subject
113
+ expect(DVD.instance.title).to eq('some title')
114
+ end
115
+ end
116
+
117
+ context 'and if option has `forcibly` key' do
118
+ let(:options) { { forcibly: true } }
119
+ before { DVD.path = '/dev/some/path' }
120
+
121
+ it 'calls DVD.detect forcibly even if path is already set' do
122
+ DVD.should_receive(:detect).and_return('/dev/some/path')
123
+ subject
124
+ end
125
+ end
126
+ end
127
+
128
+ context 'when block is given' do
129
+ subject { DVD.load { |dvd| dvd.artist = 'some artist' } }
130
+ before { DVD.should_receive(:detect).and_return('/dev/path') }
131
+ it 'calls proc object' do
132
+ subject
133
+ expect(DVD.instance.artist).to eq('some artist')
134
+ end
135
+ end
136
+ end
137
+
138
+ describe '#info' do
139
+ subject { dvd.info }
140
+ let(:dvd) { DVD.instance }
141
+
142
+ context 'when DVD.path is not set' do
143
+ before { DVD.path = nil }
144
+ it 'raises an RuntimeError' do
145
+ expect { subject }.to raise_error(RuntimeError)
146
+ end
147
+ end
148
+
149
+ context 'when DVD.path is set' do
150
+ before { DVD.path = '/dev/path' }
151
+ let(:info_data) { 'dvd data' }
152
+ it 'returns DVD disk data' do
153
+ dvd.should_receive(:execute_command).with("dvdbackup --info --input='/dev/path'", true).and_return(info_data)
154
+ expect(subject).to eq(info_data)
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#title_sets' do
160
+ subject { dvd.title_sets }
161
+ let(:info) do
162
+ <<"EOM"
163
+ Title Sets:
164
+
165
+ Title set 1
166
+ The aspect ratio of title set 1 is 16:9
167
+ Title set 1 has 1 angle
168
+ Title set 1 has 1 audio track
169
+ Title set 1 has 0 subpicture channels
170
+
171
+ Title included in title set 1 is
172
+ Title 1:
173
+ Title 1 has 15 chapters
174
+ Title 1 has 2 audio channels
175
+ EOM
176
+ end
177
+ let(:dvd) { DVD.instance }
178
+
179
+ before { dvd.should_receive(:info).and_return(info) }
180
+
181
+ it 'returns each pair of title and chapter data' do
182
+ expect(subject).to be_an Array
183
+ expect(subject.size).to eq(1)
184
+ expect(subject.first[:title]).to eq(1)
185
+ expect(subject.first[:chapter]).to eq(15)
186
+ end
187
+ end
188
+
189
+ describe '#rip' do
190
+ subject { dvd.rip }
191
+ let(:dvd) { DVD.instance }
192
+
193
+ context 'when DVD.path if not set' do
194
+ before { DVD.path = nil }
195
+ it 'raises an RuntimeError' do
196
+ expect { subject }.to raise_error(RuntimeError)
197
+ end
198
+ end
199
+
200
+ context 'when DVD.path is set' do
201
+ before { DVD.path = '/dev/path' }
202
+
203
+ let(:configuration) { OpenStruct.new(output: '/tmp/out', working_dir: '/tmp/working') }
204
+ let(:title_sets) { [{ title: 1, chapter: 3 }, { title: 2, chapter: 4 }] }
205
+ let(:vob_path) { '/tmp/working/foo.vob' }
206
+
207
+ def stub_methods
208
+ Musical.should_receive(:configuration).at_least(1).times.and_return(configuration)
209
+ dvd.should_receive(:title_sets).at_least(1).and_return(title_sets)
210
+ dvd.should_receive(:execute_command).at_least(1).with(/dvdbackup (.)*/, true) { FileUtils.touch(vob_path) }
211
+ dvd.should_receive(:execute_command).at_least(1).with(/find (.)*/).and_return("#{vob_path}\n")
212
+
213
+ progress_bar = double
214
+ progress_bar.should_receive(:increment).at_least(1)
215
+ ProgressBar.should_receive(:create).and_return(progress_bar)
216
+ end
217
+
218
+ before do
219
+ FileUtils.mkdir_p(configuration.output)
220
+ FileUtils.mkdir_p(configuration.working_dir)
221
+ stub_methods
222
+ end
223
+
224
+ after do
225
+ FileUtils.rm_rf(configuration.working_dir)
226
+ FileUtils.rm_rf(configuration.output)
227
+ end
228
+
229
+ it 'returns an array of all chapters', fakefs: true do
230
+ expect(subject).to be_an Array
231
+ expect(subject.size).to eq(7)
232
+ end
233
+ end
234
+ end
235
+ end