audio_book_creator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +8 -0
  6. data/README.md +60 -0
  7. data/Rakefile +8 -0
  8. data/audio_book_creator.gemspec +31 -0
  9. data/bin/audio_book_creator +6 -0
  10. data/lib/audio_book_creator.rb +59 -0
  11. data/lib/audio_book_creator/binder.rb +61 -0
  12. data/lib/audio_book_creator/book_creator.rb +31 -0
  13. data/lib/audio_book_creator/book_def.rb +36 -0
  14. data/lib/audio_book_creator/cached_hash.rb +20 -0
  15. data/lib/audio_book_creator/cascading_array.rb +57 -0
  16. data/lib/audio_book_creator/chapter.rb +33 -0
  17. data/lib/audio_book_creator/cli.rb +119 -0
  18. data/lib/audio_book_creator/conductor.rb +67 -0
  19. data/lib/audio_book_creator/editor.rb +20 -0
  20. data/lib/audio_book_creator/logging.rb +7 -0
  21. data/lib/audio_book_creator/page_db.rb +42 -0
  22. data/lib/audio_book_creator/page_def.rb +31 -0
  23. data/lib/audio_book_creator/runner.rb +22 -0
  24. data/lib/audio_book_creator/speaker.rb +54 -0
  25. data/lib/audio_book_creator/speaker_def.rb +39 -0
  26. data/lib/audio_book_creator/spider.rb +60 -0
  27. data/lib/audio_book_creator/spoken_chapter.rb +16 -0
  28. data/lib/audio_book_creator/surfer_def.rb +15 -0
  29. data/lib/audio_book_creator/url_filter.rb +33 -0
  30. data/lib/audio_book_creator/version.rb +3 -0
  31. data/lib/audio_book_creator/web.rb +44 -0
  32. data/spec/audio_book_creator/binder_spec.rb +103 -0
  33. data/spec/audio_book_creator/book_creator_spec.rb +63 -0
  34. data/spec/audio_book_creator/book_def_spec.rb +61 -0
  35. data/spec/audio_book_creator/cached_hash_spec.rb +19 -0
  36. data/spec/audio_book_creator/cascading_array_spec.rb +64 -0
  37. data/spec/audio_book_creator/chapter_spec.rb +80 -0
  38. data/spec/audio_book_creator/cli_spec.rb +274 -0
  39. data/spec/audio_book_creator/conductor_spec.rb +102 -0
  40. data/spec/audio_book_creator/editor_spec.rb +39 -0
  41. data/spec/audio_book_creator/logging_spec.rb +21 -0
  42. data/spec/audio_book_creator/page_db_spec.rb +74 -0
  43. data/spec/audio_book_creator/page_def_spec.rb +79 -0
  44. data/spec/audio_book_creator/runner_spec.rb +65 -0
  45. data/spec/audio_book_creator/speaker_def_spec.rb +39 -0
  46. data/spec/audio_book_creator/speaker_spec.rb +105 -0
  47. data/spec/audio_book_creator/spider_spec.rb +172 -0
  48. data/spec/audio_book_creator/spoken_chapter_spec.rb +30 -0
  49. data/spec/audio_book_creator/surfer_def_spec.rb +17 -0
  50. data/spec/audio_book_creator/url_filter_spec.rb +52 -0
  51. data/spec/audio_book_creator/version_spec.rb +5 -0
  52. data/spec/audio_book_creator/web_spec.rb +66 -0
  53. data/spec/audio_book_creator_spec.rb +25 -0
  54. data/spec/spec_helper.rb +106 -0
  55. data/spec/support/test_logger.rb +21 -0
  56. metadata +238 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9f56e4fa63b655b47b0d52b825263b0d755d6ca
4
+ data.tar.gz: 501684df9d61ef2fc39573f8e445718c3c61d42b
5
+ SHA512:
6
+ metadata.gz: c78b612fe641e112006a348b2e9e196579c8edf6788dbf7d4b421c420285b1bf7b866b869ed8051b41a34684552e99f9eb9352f42b6045155c709a51952341ed
7
+ data.tar.gz: ccfddad3fe91416a3f463163d441570e27ab3b4b95926f7e3cbd187f637e2f3e732c25c497de12469578610276ae6302bd8ddebca69027db3f5f2beaa5a839ad
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ # audio files
20
+ *.db
21
+ *.txt
22
+ *.m4a
23
+ *.m4b
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format progress
2
+ #--format documentation
3
+ --color
4
+ #--profile
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - "ruby-head"
5
+ addons:
6
+ code_climate:
7
+ repo_token:
8
+ secure: pXarV/tOUyHVKJS+OmnpJuZ6zi8JVFACz4LSBg7s09Ek7Rhn0/6+VBCQomlewOg6Ev03huae44FdbLk6pPg0OrEbkp6ZxWqWnFVP43ibOzzdeiF46omOX9UmBcgmat9S9BX6DI28+Q5ww+UiOGc+uY1QcG9/6EQzsfEkaAcgrF8=
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in audio_book_creator.gemspec
4
+ gemspec
5
+
6
+ # gem "pry"
7
+ # gem "mutant"
8
+ # gem "mutant-rspec"
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # AudioBookCreator
2
+
3
+ [![Gem Version][GV img]][Gem Version]
4
+ [![Build Status][BS img]][Build Status]
5
+ [![Dependency Status][DS img]][Dependency Status]
6
+ [![Code Climate][CC img]][Code Climate]
7
+ [![Coverage Status][CS img]][Coverage Status]
8
+
9
+ [Gem Version]: https://rubygems.org/gems/audio_book_creator
10
+ [Build Status]: https://travis-ci.org/kbrock/audio_book_creator
11
+ [travis pull requests]: https://travis-ci.org/kbrock/audio_book_creator/pull_requests
12
+ [Dependency Status]: https://gemnasium.com/kbrock/audio_book_creator
13
+ [Code Climate]: https://codeclimate.com/github/kbrock/audio_book_creator
14
+ [Coverage Status]: https://coveralls.io/r/kbrock/audio_book_creator
15
+
16
+ [GV img]: https://badge.fury.io/rb/audio_book_creator.png
17
+ [BS img]: https://travis-ci.org/kbrock/audio_book_creator.png
18
+ [DS img]: https://gemnasium.com/kbrock/audio_book_creator.png
19
+ [CC img]: https://codeclimate.com/github/kbrock/audio_book_creator.png
20
+ [CS img]: https://coveralls.io/repos/kbrock/audio_book_creator/badge.png?branch=master
21
+
22
+
23
+ ## Description
24
+
25
+ This takes html files and creates a chapterized audiobook.
26
+ It leverages Apple's speak command and audio book binder
27
+
28
+ ## Installation
29
+
30
+ Download [audio book binder][], link the executable into the path, and install gem. The current app store version of the application does not have the cli available.
31
+
32
+ $ ln -s /Applications/AudioBookBinder.app/Contents/MacOS/abbinder /usr/local/bin/
33
+ $ gem install audio_book_creator
34
+
35
+ [audio book binder]: http://bluezbox.com/audiobookbinder.html
36
+
37
+ ## Usage
38
+
39
+ audio_book_creator the_great_book http://bookurl.com/greak_book/
40
+
41
+ # **TODO:** provide the command line flags
42
+
43
+ it will spider the files referenced by the url and create the following:
44
+
45
+ the_great_book.mpa
46
+ the_great_book/pages.db
47
+ the_great_book/chapter1.txt
48
+ the_great_book/chapter1.mpg
49
+ the_great_book/chapter2.txt
50
+ the_great_book/chapter2.mpg
51
+ the_great_book/chapter3.txt
52
+ the_great_book/chapter3.mpg
53
+
54
+ ## Contributing
55
+
56
+ 1. Fork it
57
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
58
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
59
+ 4. Push to the branch (`git push origin my-new-feature`)
60
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |opt|
5
+ (opt.ruby_opts ||= []) << "-Ispec"
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'audio_book_creator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "audio_book_creator"
8
+ spec.version = AudioBookCreator::VERSION
9
+ spec.authors = ["Keenan Brock"]
10
+ spec.email = ["keenan@thebrocks.net"]
11
+ spec.summary = %q{Create an audiobook from a url}
12
+ spec.description = %q{This takes html files and creates a chapterized audiobook.
13
+ It leverages Apple's speak command and audio book binder}
14
+ spec.homepage = "http://github.com/kbrock/audio_book_creator"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_runtime_dependency "sqlite3-ruby"
23
+ spec.add_runtime_dependency "nokogiri"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "simplecov"
29
+ spec.add_development_dependency "coveralls"
30
+ spec.add_development_dependency "codeclimate-test-reporter"
31
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'audio_book_creator'
4
+ require 'audio_book_creator/cli'
5
+
6
+ AudioBookCreator::Cli.new.parse(ARGV).run
@@ -0,0 +1,59 @@
1
+ require "audio_book_creator/version"
2
+ require "logger"
3
+
4
+ module AudioBookCreator
5
+ def self.should_write?(filename, force)
6
+ force || !File.exist?(filename)
7
+ end
8
+
9
+ def self.optionally_write(filename, force)
10
+ if should_write?(filename, force)
11
+ File.write(filename, yield)
12
+ end
13
+ end
14
+
15
+ def self.optionally_run(filename, force)
16
+ if should_write?(filename, force)
17
+ Runner.new.run!(*yield)
18
+ end
19
+ end
20
+
21
+ def self.logger=(val)
22
+ @log = val
23
+ end
24
+
25
+ def self.logger
26
+ @log ||= Logger.new(STDOUT).tap { |log| log.level = Logger::WARN }
27
+ end
28
+ end
29
+
30
+ # general classes
31
+ require "audio_book_creator/cached_hash"
32
+ require "audio_book_creator/cascading_array"
33
+ require "audio_book_creator/logging"
34
+ require "audio_book_creator/runner"
35
+
36
+ # data config objects
37
+ require "audio_book_creator/book_def"
38
+ require "audio_book_creator/page_def"
39
+ require "audio_book_creator/speaker_def"
40
+ require "audio_book_creator/surfer_def"
41
+
42
+ # data models
43
+ require "audio_book_creator/chapter"
44
+ require "audio_book_creator/spoken_chapter"
45
+
46
+ # web surfing objects
47
+ require "audio_book_creator/web"
48
+ require "audio_book_creator/url_filter"
49
+ require "audio_book_creator/page_db"
50
+ require "audio_book_creator/spider"
51
+
52
+ # business logic
53
+ # reformat
54
+ require "audio_book_creator/editor"
55
+ # flow
56
+ require "audio_book_creator/speaker"
57
+ require "audio_book_creator/binder"
58
+ require "audio_book_creator/book_creator" # full workflow
59
+ require "audio_book_creator/conductor" # creates components of flow
@@ -0,0 +1,61 @@
1
+ module AudioBookCreator
2
+ class Binder
3
+ attr_accessor :book_def
4
+ attr_accessor :speaker_def
5
+
6
+ # these are more for documentation than actual variables
7
+
8
+ def initialize(book_def, speaker_def)
9
+ @book_def = book_def
10
+ @speaker_def = speaker_def
11
+ end
12
+
13
+ def create(chapters)
14
+ raise "No Chapters" if chapters.nil? || chapters.empty?
15
+
16
+ AudioBookCreator.optionally_run(book_def.filename, force) do
17
+ ["abbinder", params: params(chapters)]
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def force
24
+ speaker_def.regen_audio
25
+ end
26
+
27
+ def itunes
28
+ book_def.itunes
29
+ end
30
+
31
+ def params(chapters)
32
+ ret = {
33
+ "-a" => book_def.author,
34
+ "-t" => book_def.title,
35
+ "-b" => speaker_def.bit_rate,
36
+ "-c" => speaker_def.channels,
37
+ "-r" => speaker_def.sample_rate,
38
+ "-g" => "Audiobook",
39
+ "-l" => speaker_def.max_hours,
40
+ "-o" => book_def.filename,
41
+ # "-v" => verbose,
42
+ # "-C" => "file.png" cover image
43
+ nil => chapter_params(chapters),
44
+ }
45
+ ret["-A"] = nil if itunes # add audiobook to iTunes
46
+ ret
47
+ end
48
+
49
+ def chapter_params(chapters)
50
+ chapters.map { |ch| [ctitle(ch), cfilename(ch)] }
51
+ end
52
+
53
+ def ctitle(chapter)
54
+ "@#{chapter.title}@"
55
+ end
56
+
57
+ def cfilename(chapter)
58
+ chapter.filename
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,31 @@
1
+ module AudioBookCreator
2
+ class BookCreator
3
+ # config:
4
+ # web (factory), page_def.link_path || outstanding[] -> pages[]
5
+ # decision (!invalid, !outstanding, !visited)
6
+ attr_accessor :spider
7
+ # page_def || pages -> chapters
8
+ attr_accessor :editor
9
+ # spoken_def, base_dir || chapter[] -> spoken_chapters[]
10
+ attr_accessor :speaker
11
+ # spoken_chapter[], book_def -> book
12
+ # force, channels, max_hours, bit_rate, sample_rate
13
+ attr_accessor :binder
14
+
15
+ def initialize(spider, editor, speaker, binder)
16
+ @spider = spider
17
+ @editor = editor
18
+ @speaker = speaker
19
+ @binder = binder
20
+ end
21
+
22
+ def create(outstanding)
23
+ speaker.make_directory_structure
24
+ binder.create(
25
+ editor.parse(
26
+ spider.run(outstanding)
27
+ ).map { |chapter| speaker.say(chapter) }
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ module AudioBookCreator
2
+ class BookDef
3
+ attr_accessor :base_dir
4
+ attr_accessor :title
5
+ attr_accessor :author
6
+
7
+ attr_accessor :urls
8
+ attr_accessor :itunes
9
+
10
+ def initialize(title = nil, author = nil, base_dir = nil, urls = nil, itunes = nil)
11
+ @title = title
12
+ @base_dir = base_dir
13
+ @author = author || "Vicki"
14
+ @urls = urls
15
+ @itunes = itunes
16
+ end
17
+
18
+ def filename
19
+ BookDef.sanitize_filename(title, "m4b")
20
+ end
21
+
22
+ def base_dir
23
+ @base_dir || BookDef.sanitize_filename(title)
24
+ end
25
+
26
+ def unique_urls
27
+ urls.uniq
28
+ end
29
+
30
+ private
31
+
32
+ def self.sanitize_filename(*filenames)
33
+ filenames.compact.join(".").gsub(/[^-._a-z0-9A-Z]/, "-").gsub(/--*/, "-").sub(/-$/, "")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ module AudioBookCreator
2
+ class CachedHash
3
+ # @!attribute caching layer
4
+ # @return Hash<String, String> cache
5
+ attr_accessor :cache
6
+
7
+ # @!attribute main hash
8
+ # @return Hash<String, String> hash for main content
9
+ attr_accessor :main
10
+
11
+ def initialize(cache, main)
12
+ @cache = cache
13
+ @main = main
14
+ end
15
+
16
+ def [](name)
17
+ @cache[name] ||= @main[name]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,57 @@
1
+ require 'set'
2
+
3
+ module AudioBookCreator
4
+ # this fetching from the first array, fetching from the second if nothing is available
5
+ class CascadingArray
6
+ include Enumerable
7
+
8
+ # @!attribute main
9
+ # @return Array the alternative array if this one is empty
10
+ attr_accessor :main
11
+
12
+ # @!attribute alt
13
+ # @return Array the alternative array if this one is empty
14
+ attr_accessor :alt
15
+
16
+ attr_accessor :all
17
+
18
+ def initialize(main, alt)
19
+ @all = Set.new.merge(main).merge(alt)
20
+ @main = main
21
+ @alt = alt
22
+ end
23
+
24
+ def add_chapter(value)
25
+ @all << value
26
+ @alt << value
27
+ end
28
+
29
+ def add_unique_chapter(value)
30
+ add_chapter(value) unless include?(value)
31
+ end
32
+
33
+ def add_page(value)
34
+ @all << value
35
+ @main << value
36
+ end
37
+
38
+ def add_unique_page(value)
39
+ add_page(value) unless include?(value)
40
+ end
41
+
42
+ def each(&block)
43
+ return enum_for unless block_given?
44
+ @main.each(&block)
45
+ @alt.each(&block)
46
+ end
47
+
48
+ # note: ever included
49
+ def include?(value)
50
+ @all.include?(value)
51
+ end
52
+
53
+ def shift
54
+ @main.shift || @alt.shift
55
+ end
56
+ end
57
+ end