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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 378b27ab3089eed1a4a51d4639c2ff78dc119643
4
+ data.tar.gz: ce72f42da6904205207bfb5a91665c0e4f34c92f
5
+ SHA512:
6
+ metadata.gz: 83a09271c9eb2c78cf16399d2b6dce680fe0c970fa140ce202448c32b8bcc2cfecf3fc5f4317a4289bbe74e9ef071173ab316e25426229c1a659dea6c8609cf6
7
+ data.tar.gz: e996832bcccd44afb3ef5286890c563b64b7510d953b3b4d558904f1518944bf06d75f30f7861a943b66e8f1a5aab669f73fdd5148c83871dbedbbfb7f616749
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -0,0 +1,23 @@
1
+ *.DS_Store
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ ripped
7
+ coverage
8
+ InstalledFiles
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
16
+
17
+ # YARD artifacts
18
+ .yardoc
19
+ _yardoc
20
+ doc/
21
+ .bundle
22
+ Gemfile.lock
23
+ pkg/*
@@ -0,0 +1 @@
1
+ 2.0.0-p247
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
data/Gemfile CHANGED
@@ -1,14 +1,4 @@
1
- source "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- gem "trollop", ">=1.16.2"
4
- gem "progressbar", ">=0.9.1"
5
- gem "rb-appscript", ">=0.6.1"
6
-
7
- # Add dependencies to develop your gem here.
8
- # Include everything needed to run rake, tests, features, etc.
9
- group :development do
10
- gem "rspec", "~> 2.3.0"
11
- gem "bundler", "~> 1.0.0"
12
- gem "jeweler", "~> 1.6.4"
13
- gem "rcov", ">= 0"
14
- end
3
+ # Specify your gem's dependencies in musical.gemspec
4
+ gemspec
@@ -0,0 +1,24 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+
17
+ # Capybara features specs
18
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
19
+
20
+ # Turnip features and steps
21
+ watch(%r{^spec/acceptance/(.+)\.feature$})
22
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23
+ end
24
+
@@ -0,0 +1,59 @@
1
+ # musical [![Build Status](https://travis-ci.org/katsuma/musical.png?branch=master)](https://travis-ci.org/katsuma/musical) [![Coverage Status](https://coveralls.io/repos/katsuma/musical/badge.png)](https://coveralls.io/r/katsuma/musical)
2
+
3
+ `musical` is a simple tool for your favorite music DVD.
4
+
5
+ You can rip vob files by each DVD chapter, convert them to wav file and add them to your iTunes library.
6
+
7
+
8
+ ## Install
9
+
10
+ `musical` depends on `dvdbackup` and `ffmpeg`.
11
+ To install them try this for example.
12
+
13
+ ```sh
14
+ brew install dvdbackup
15
+ brew install ffmpeg
16
+ ```
17
+
18
+ And install gem.
19
+
20
+ ```sh
21
+ gem install musical
22
+ ```
23
+
24
+
25
+ ## Usage
26
+ Set your DVD and type
27
+
28
+ ```sh
29
+ musical <options>
30
+ ```
31
+
32
+ Options:
33
+ ```sh
34
+ --info, -i: Show your DVD data
35
+ --ignore-convert-sound, -g: Rip data only, NOT convert them to wav file
36
+ --ignore-use-itunes, -n: NOT add ripped files to iTunes and encode them
37
+ --dev, -d <s>: Set location of DVD device
38
+ --title, -t <s>: Set DVD title (default: LIVE)
39
+ --artist, -a <s>: Set DVD artist (default: Artist)
40
+ --output, -o <s>: Set location of ripped data (default: ripped)
41
+ --version, -v: Print version and exit
42
+ --help, -h: Show this message
43
+ ```
44
+
45
+ When you use iTunes, you should use `--title` and `--artist` options.
46
+
47
+ They will help you to manage your music library easily.
48
+
49
+
50
+ ## Supported OS
51
+ - OSX 10.8 (Mountain Lion)
52
+
53
+ ## Supported Ruby
54
+ - 2.0.0
55
+ - 1.9.3
56
+
57
+
58
+ ## License
59
+ `musical` is released under the MIT License.
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
- # encoding: utf-8
2
-
3
1
  require 'rubygems'
2
+ require "bundler/gem_tasks"
4
3
  require 'bundler'
4
+
5
5
  begin
6
6
  Bundler.setup(:default, :development)
7
7
  rescue Bundler::BundlerError => e
@@ -9,41 +9,11 @@ rescue Bundler::BundlerError => e
9
9
  $stderr.puts "Run `bundle install` to install missing gems"
10
10
  exit e.status_code
11
11
  end
12
- require 'rake'
13
-
14
- require 'jeweler'
15
- Jeweler::Tasks.new do |gem|
16
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
- gem.name = "musical"
18
- gem.homepage = "http://github.com/katsuma/musical"
19
- gem.license = "MIT"
20
- gem.summary = %Q{A simple rip, encode and iTunes library tool for your favorite music DVD}
21
- gem.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.}
22
- gem.email = "katsuma@gmail.com"
23
- gem.authors = ["ryo katsuma"]
24
- # dependencies defined in Gemfile
25
- end
26
- Jeweler::RubygemsDotOrgTasks.new
27
12
 
28
13
  require 'rspec/core'
29
14
  require 'rspec/core/rake_task'
30
- RSpec::Core::RakeTask.new(:spec) do |spec|
31
- spec.pattern = FileList['spec/**/*_spec.rb']
32
- end
33
15
 
34
- RSpec::Core::RakeTask.new(:rcov) do |spec|
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
37
- end
16
+ RSpec::Core::RakeTask.new(:spec)
38
17
 
39
18
  task :default => :spec
40
-
41
- require 'rake/rdoctask'
42
- Rake::RDocTask.new do |rdoc|
43
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
-
45
- rdoc.rdoc_dir = 'rdoc'
46
- rdoc.title = "musical #{version}"
47
- rdoc.rdoc_files.include('README*')
48
- rdoc.rdoc_files.include('lib/**/*.rb')
49
- end
19
+ require "bundler/gem_tasks"
@@ -3,5 +3,42 @@
3
3
  require 'musical'
4
4
  include Musical
5
5
 
6
- opts = Musical.setup
7
- DVD.new opts
6
+ Musical.setup
7
+
8
+ dvd_options = {
9
+ title: Musical.configuration.title,
10
+ artist: Musical.configuration.artist,
11
+ path: Musical.configuration.path
12
+ }
13
+
14
+ DVD.load(dvd_options) do |dvd|
15
+ next puts dvd.info if Musical.configuration.info
16
+
17
+ chapters = dvd.rip
18
+
19
+ chapters.each do |chapter|
20
+ next if Musical.configuration.ignore_convert_sound
21
+
22
+ wav = chapter.to_wav
23
+ next if Musical.configuration.ignore_use_itunes
24
+
25
+ track = Itunes::Player.add(wav.expand_path)
26
+
27
+ converted_track = track.convert
28
+ converted_track.update_attributes(
29
+ name: chapter.name,
30
+ album: dvd.title,
31
+ artist: dvd.artist,
32
+ track_count: chapters.size,
33
+ track_number: chapter.chapter_number,
34
+ )
35
+
36
+ wav.delete!
37
+ track.delete!
38
+ end
39
+ end
40
+
41
+ if !Musical.configuration.ignore_convert_sound &&
42
+ !Musical.configuration.ignore_use_itunes
43
+ FileUtils.rm_f(Musical.configuration.output)
44
+ end
@@ -1,40 +1,48 @@
1
- require 'rubygems'
2
1
  require 'trollop'
3
- require 'progressbar'
2
+ require 'ruby-progressbar'
4
3
  require 'fileutils'
5
- require 'appscript'
4
+ require 'open3'
5
+ require 'ostruct'
6
+ require 'itunes-client'
6
7
 
8
+ require 'musical/configuration'
9
+ require 'musical/util'
10
+ require 'musical/version'
7
11
  require 'musical/dvd'
8
- require 'musical/itunes'
12
+ require 'musical/dvd/chapter'
13
+ require 'musical/dvd/wav'
9
14
 
10
15
  module Musical
16
+ extend Musical::Util
17
+
18
+ def configuration
19
+ Configuration.config || Musical.setup
20
+ end
21
+ module_function :configuration
22
+
11
23
  def setup
12
- # check env
13
- ['dvdbackup', 'ffmpeg'].each do |app|
14
- if `which #{app}`.empty?
15
- raise RuntimeError, "\n\n'#{app}' is not installed.\n\ntry:\n brew install #{app}\n\n"
16
- end
17
- end
24
+ return unless check_env
18
25
 
19
- version = open(File.join(File.dirname(__FILE__), "..", "VERSION")){ |f| f.gets }
26
+ # init working directory
27
+ working_dir = File.join(File.expand_path('~'), '.musical')
28
+ FileUtils.mkdir_p(working_dir) unless File.exist?(working_dir)
20
29
 
21
30
  # parse options
22
- opts = Trollop::options do
23
- version "Musical #{version}"
24
- opt :info, "Show your DVD data", :type => :boolean
25
- opt :ignore_convert_sound, "Rip data only, NOT convert them to wav file", :type => :boolean
26
- opt :ignore_use_itunes, "NOT add ripped files to iTunes and encode them", :type => :boolean
27
- opt :dev, "Set location of DVD device"
28
- opt :title, "Set DVD title", :default => 'LIVE'
29
- opt :artist, "Set DVD artist", :default => 'Artist'
30
- opt :output, "Set location of ripped data", :default => 'ripped'
31
+ options = Trollop::options do
32
+ version "Musical #{Musical::VERSION}"
33
+ opt :info, "Show your DVD data", type: :boolean
34
+ opt :ignore_convert_sound, "Rip data only, NOT convert them to wav file", type: :boolean
35
+ opt :ignore_use_itunes, "NOT add ripped files to iTunes and encode them", type: :boolean
36
+ opt :path, "Set device path of DVD", type: :string
37
+ opt :title, "Set DVD title", type: :string, default: 'LIVE'
38
+ opt :artist, "Set DVD artist", type: :string, default: 'Artist'
39
+ opt :output, "Set location of ripped data", type: :string, default: 'ripped'
31
40
  end
32
41
 
33
- # fix fox dvdbackup
34
- opts[:trim_title] = opts[:title].gsub(" ", "_")
35
- opts[:trim_artist] = opts[:artist].gsub(" ", "_")
42
+ configuration = Configuration.build(options.merge(working_dir: working_dir))
43
+ yield(configuration) if block_given?
36
44
 
37
- opts
45
+ configuration
38
46
  end
39
-
47
+ module_function :setup
40
48
  end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ require 'singleton'
3
+
4
+ module Musical
5
+ class Configuration < OpenStruct
6
+ include Singleton
7
+
8
+ @@config = nil
9
+
10
+ def self.build(options)
11
+ @@config = OpenStruct.new(options)
12
+ end
13
+
14
+ def self.config
15
+ @@config
16
+ end
17
+ end
18
+ end
@@ -1,132 +1,112 @@
1
+ # coding: utf-8
2
+ require 'singleton'
3
+
1
4
  module Musical
2
5
  class DVD
3
- attr_accessor :opts
6
+ include Singleton
7
+ include Musical::Util
8
+ extend Musical::Util
4
9
 
5
- def self.detect
6
- dev_infos = `df -h | awk {'print $1,$5,$6'}`.split("\n")
7
- candidates = []
8
- dev_infos.each do |dev_info|
9
- info = dev_info.split(" ")
10
- file_system = info[0]
11
- capacity = info[1]
12
- mounted = info[2]
13
- if capacity == "100%" && mounted.include?("/Volumes") && !(mounted.include?("MobileBackups"))
14
- candidates << mounted
15
- end
16
- end
10
+ attr_accessor :title, :artist
17
11
 
18
- raise "Not detect DVD device" if candidates.empty?
19
- candidates
20
- end
12
+ @@path = nil
13
+
14
+ DETECT_ERROR_MESSAGE = 'Not detect DVD, Try `DVD.load` and check your drive device path.'
15
+ DRIVE_NOT_FOUND_MESSAGE = 'DVD drive is not found.'
16
+ DVD_NOT_INSERTED_MESSAGE = 'DVD is not inserted.'
21
17
 
22
- def initialize(options={})
23
- @opts = options
18
+ def self.detect
19
+ drutil_out = execute_command('drutil status')
24
20
 
25
- return puts info if @opts[:info]
21
+ raise RuntimeError.new DRIVE_NOT_FOUND_MESSAGE unless drutil_out
22
+ raise RuntimeError.new DVD_NOT_INSERTED_MESSAGE unless drutil_out.include?('Name:')
26
23
 
27
- rip_by_chapter
28
- convert_sound unless @opts[:ignore_convert_sound]
29
- to_itunes unless @opts[:ignore_use_itunes]
24
+ file_system = drutil_out.split("\n").select do |line|
25
+ line.include?('Name:')
26
+ end.first.match(/Name: (.+)/)[1]
30
27
  end
31
28
 
32
- def dev
33
- @_dev ||= DVD.detect.first
29
+ def self.path=(path)
30
+ @@path = path
34
31
  end
35
32
 
36
- def info
37
- raise "Not detect DVD device" if dev.empty?
38
- @_info ||= `dvdbackup --input=#{dev} --info 2>/dev/null`
33
+ def self.path
34
+ @@path
39
35
  end
40
36
 
41
- def title_with_chapters
42
- return @_title_chapters unless @_title_chapters.nil?
43
-
44
- @_title_chapters = []
45
- info_str = info.split("\n")
46
- info_str.each_with_index do |line, index|
47
- if line =~ /\s*Title (\d):$/
48
- @_title_chapters << { :title => $1.to_i, :line => index }
49
- end
37
+ def self.load(options = {})
38
+ if @@path.nil? || options[:forcibly]
39
+ @@path = options[:path] || self.detect
50
40
  end
51
41
 
52
- @_title_chapters.each do |title_chapter|
53
- line = title_chapter[:line]
54
- if info_str[line + 1] =~ /\s*Title (\d) has (\d*) chapter/
55
- title_chapter[:chapter] = $2.to_i
56
- title_chapter.delete(:line)
57
- end
58
- end
59
- @_title_chapters
60
- end
42
+ dvd = DVD.instance
43
+ dvd.title = options[:title] || Musical.configuration.title
44
+ dvd.artist = options[:artist] || Musical.configuration.artist
61
45
 
62
- def rip_by_chapter
63
- task_message "Ripping"
64
-
65
- pbar = ProgressBar.new "Ripping", chapter_size
66
- title_with_chapters.each_with_index do |title_chapter, title_index|
67
- ripped_dir_base = "#{@opts[:output]}"
68
- saved_dir = "#{ripped_dir_base}/#{@opts[:trim_title]}/title_#{title_index+1}"
69
- FileUtils.mkdir_p "#{saved_dir}"
70
-
71
- 1.upto title_chapter[:chapter] do |chapter|
72
- `dvdbackup --name=#{@opts[:trim_title]} --input=#{dev} --title=#{title_index+1} --start=#{chapter} --end=#{chapter} --output=#{ripped_dir_base}_#{title_index}_#{chapter} 2>/dev/null`
73
- pbar.inc
74
- # moved file
75
- vob_path = `find #{ripped_dir_base}_#{title_index}_#{chapter} -name "*.VOB"`.chomp
76
- FileUtils.mv vob_path, "#{saved_dir}/chapter_#{chapter}.VOB"
77
- FileUtils.rm_rf "#{ripped_dir_base}_#{title_index}_#{chapter}"
78
- end
46
+ if block_given?
47
+ yield(dvd)
79
48
  end
80
- pbar.finish
49
+
50
+ dvd.info
81
51
  end
82
52
 
83
- def convert_sound
84
- task_message "Converting"
53
+ def info
54
+ raise RuntimeError.new DETECT_ERROR_MESSAGE unless @@path
85
55
 
86
- pbar = ProgressBar.new "Converting", chapter_size
87
- title_with_chapters.each_with_index do |title_chapter, title_index|
88
- ripped_dir_base = "#{@opts[:output]}"
89
- saved_dir = "#{ripped_dir_base}/#{@opts[:trim_title]}/title_#{title_index+1}"
56
+ return @info if @info
90
57
 
91
- 1.upto title_chapter[:chapter] do |chapter|
92
- `ffmpeg -i #{saved_dir}/chapter_#{chapter}.VOB #{saved_dir}/chapter_#{chapter}.wav 2>/dev/null`
93
- FileUtils.rm_f "#{saved_dir}/chapter_#{chapter}.VOB"
94
- pbar.inc
95
- end
96
- end
97
- pbar.finish
58
+ @info = execute_command("dvdbackup --info --input='#{@@path}'", true)
59
+ raise RuntimeError.new DETECT_ERROR_MESSAGE if @info.empty?
60
+ @info
98
61
  end
99
62
 
100
- def to_itunes
101
- task_message "To iTunes"
63
+ def title_sets
64
+ return @title_sets if @title_sets
102
65
 
103
- pbar = ProgressBar.new "To iTunes", chapter_size
104
- its = ITunes.new
105
-
106
- title_with_chapters.each_with_index do |title_chapter, title_index|
107
- ripped_dir_base = "#{@opts[:output]}"
108
- saved_dir = "#{ripped_dir_base}/#{@opts[:trim_title]}/title_#{title_index+1}"
109
-
110
- options = { :album => @opts[:title], :artist => @opts[:artist], :track_count => title_chapter[:chapter]}
111
- 1.upto title_chapter[:chapter] do |chapter|
112
- its.add("#{saved_dir}/chapter_#{chapter}.wav", options.merge(:track_number => chapter))
113
- pbar.inc
66
+ @title_sets = [].tap do |sets|
67
+ sets_regexp = /\s*Title (\d) has (\d*) chapter/
68
+ info.split("\n").each do |line|
69
+ if line =~ sets_regexp
70
+ sets << { title: $1.to_i, chapter: $2.to_i }
71
+ end
114
72
  end
115
73
  end
116
- FileUtils.rm_rf @opts[:output]
117
- pbar.finish
118
74
  end
119
75
 
120
- def chapter_size
121
- @_chapter_size ||= title_with_chapters.inject(0){ |count, t| count + t[:chapter]}
76
+ def vob_path
77
+ find_command = "find '#{Musical.configuration.working_dir}' -name '*.VOB'"
78
+ execute_command(find_command).split("\n").first
122
79
  end
123
- private :chapter_size
124
-
125
- def task_message(task)
126
- raise "Not found any titles and chapters" if title_with_chapters.size == 0 && chapter_size == 0
127
- puts "#{task} #{title_with_chapters.size} titles, #{chapter_size} chapters"
80
+ private :vob_path
81
+
82
+ def rip
83
+ raise RuntimeError.new DETECT_ERROR_MESSAGE unless @@path
84
+ save_dir = Musical.configuration.output
85
+ FileUtils.mkdir_p save_dir
86
+
87
+ chapter_size = title_sets.inject(0){ |size, set| size + set[:chapter] }
88
+ progress_bar = ProgressBar.create(title: 'Ripping', total: chapter_size, format: '%a %B %p%% %t')
89
+ chapters = []
90
+
91
+ title_sets.each do |title_set|
92
+ chapters << (1..title_set[:chapter]).map do |chapter_index|
93
+ commands = []
94
+ commands << 'dvdbackup'
95
+ commands << "--input='#{@@path}'"
96
+ commands << "--title='#{title_set[:title]}'"
97
+ commands << "--start=#{chapter_index}"
98
+ commands << "--end=#{chapter_index}"
99
+ commands << "--output='#{Musical.configuration.working_dir}'"
100
+ execute_command(commands.join(' '), true)
101
+
102
+ progress_bar.increment
103
+
104
+ vob_save_path = "#{save_dir}/TITLE_#{title_set[:title]}_#{chapter_index}.VOB"
105
+ FileUtils.mv(vob_path, vob_save_path)
106
+ Chapter.new(vob_save_path, title_number: title_set[:title], chapter_number: chapter_index)
107
+ end
108
+ end
109
+ chapters.flatten
128
110
  end
129
- private :task_message
130
-
131
111
  end
132
112
  end