musical 0.0.4 → 1.0.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.
@@ -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