audio_book_creator 0.0.1

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.
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