bookbinder 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bookbinder.rb +6 -5
- data/lib/bookbinder/file.rb +1 -2
- data/lib/bookbinder/file_system/zip_file.rb +4 -4
- data/lib/bookbinder/operations.rb +7 -1
- data/lib/bookbinder/package.rb +25 -27
- data/lib/bookbinder/package/openbook.rb +2 -3
- data/lib/bookbinder/scratch.rb +36 -0
- data/lib/bookbinder/transform/{openbook/json.rb → book_json.rb} +1 -1
- data/lib/bookbinder/version.rb +1 -1
- metadata +12 -63
- data/lib/bookbinder/package/epub.rb +0 -64
- data/lib/bookbinder/package/media_ripper.rb +0 -47
- data/lib/bookbinder/package/mp3_audiobook.rb +0 -43
- data/lib/bookbinder/transform/epub/audio_overlay.rb +0 -227
- data/lib/bookbinder/transform/epub/audio_soundtrack.rb +0 -73
- data/lib/bookbinder/transform/epub/contributor.rb +0 -11
- data/lib/bookbinder/transform/epub/cover_image.rb +0 -80
- data/lib/bookbinder/transform/epub/cover_page.rb +0 -148
- data/lib/bookbinder/transform/epub/creator.rb +0 -67
- data/lib/bookbinder/transform/epub/description.rb +0 -43
- data/lib/bookbinder/transform/epub/language.rb +0 -29
- data/lib/bookbinder/transform/epub/metadata.rb +0 -140
- data/lib/bookbinder/transform/epub/nav.rb +0 -60
- data/lib/bookbinder/transform/epub/nav_toc.rb +0 -177
- data/lib/bookbinder/transform/epub/ncx.rb +0 -63
- data/lib/bookbinder/transform/epub/ocf.rb +0 -33
- data/lib/bookbinder/transform/epub/opf.rb +0 -22
- data/lib/bookbinder/transform/epub/package_identifier.rb +0 -87
- data/lib/bookbinder/transform/epub/rendition.rb +0 -273
- data/lib/bookbinder/transform/epub/resources.rb +0 -38
- data/lib/bookbinder/transform/epub/spine.rb +0 -79
- data/lib/bookbinder/transform/epub/title.rb +0 -92
- data/lib/bookbinder/transform/epub/version.rb +0 -39
- data/lib/bookbinder/transform/media_ripper/cover_image.rb +0 -12
- data/lib/bookbinder/transform/media_ripper/eisbn.rb +0 -15
- data/lib/bookbinder/transform/media_ripper/metadata.rb +0 -27
- data/lib/bookbinder/transform/media_ripper/nav_toc.rb +0 -57
- data/lib/bookbinder/transform/media_ripper/publisher.rb +0 -15
- data/lib/bookbinder/transform/media_ripper/rendition.rb +0 -7
- data/lib/bookbinder/transform/media_ripper/spine.rb +0 -34
- data/lib/bookbinder/transform/media_ripper/title.rb +0 -16
- data/lib/bookbinder/transform/mp3_audiobook/cover_image.rb +0 -25
- data/lib/bookbinder/transform/mp3_audiobook/creator.rb +0 -27
- data/lib/bookbinder/transform/mp3_audiobook/description.rb +0 -18
- data/lib/bookbinder/transform/mp3_audiobook/metadata.rb +0 -20
- data/lib/bookbinder/transform/mp3_audiobook/nav_toc.rb +0 -71
- data/lib/bookbinder/transform/mp3_audiobook/publisher.rb +0 -18
- data/lib/bookbinder/transform/mp3_audiobook/rendition.rb +0 -7
- data/lib/bookbinder/transform/mp3_audiobook/spine.rb +0 -33
- data/lib/bookbinder/transform/mp3_audiobook/subject.rb +0 -18
- data/lib/bookbinder/transform/mp3_audiobook/title.rb +0 -18
@@ -1,25 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_CoverImage < Bookbinder::Transform
|
2
|
-
|
3
|
-
TAG = 'APIC'
|
4
|
-
PATH = 'cover.jpg'
|
5
|
-
|
6
|
-
def to_map(package)
|
7
|
-
return unless path = package.audio_paths.first
|
8
|
-
package.file(path).get_file { |mp3|
|
9
|
-
Mp3Info.open(mp3) { |info|
|
10
|
-
info.tag2.pictures.each { |desc, data|
|
11
|
-
parts = [File.basename(desc), File.extname(desc)]
|
12
|
-
path = Dir::Tmpname.create(parts) {}
|
13
|
-
File.binwrite(path, data)
|
14
|
-
package.file_system.set_file(desc, File.new(path))
|
15
|
-
package.map['cover'] = { 'front' => {
|
16
|
-
'path' => desc,
|
17
|
-
'media-type' => Bookbinder::MediaType.of(desc)
|
18
|
-
} }
|
19
|
-
return
|
20
|
-
}
|
21
|
-
}
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_Creator < Bookbinder::Transform
|
2
|
-
|
3
|
-
TAG = 'TPE1'
|
4
|
-
|
5
|
-
|
6
|
-
def dependencies
|
7
|
-
[Bookbinder::Transform::MP3Audiobook_Metadata]
|
8
|
-
end
|
9
|
-
|
10
|
-
|
11
|
-
def to_map(package)
|
12
|
-
return unless md = package.map['metadata']
|
13
|
-
return unless md[TAG] && md[TAG].first
|
14
|
-
creator = md[TAG].first['@']
|
15
|
-
md.delete(TAG)
|
16
|
-
creators = creator.split('/')
|
17
|
-
package.map['creator'] = [{
|
18
|
-
'name' => creators.shift,
|
19
|
-
'role' => 'aut'
|
20
|
-
}]
|
21
|
-
package.map['creator'].push({
|
22
|
-
'name' => creators.shift,
|
23
|
-
'role' => 'nrt'
|
24
|
-
}) if creators.any?
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_Description < Bookbinder::Transform
|
2
|
-
|
3
|
-
TAG = 'COMM'
|
4
|
-
|
5
|
-
|
6
|
-
def dependencies
|
7
|
-
[Bookbinder::Transform::MP3Audiobook_Metadata]
|
8
|
-
end
|
9
|
-
|
10
|
-
|
11
|
-
def to_map(package)
|
12
|
-
return unless md = package.map['metadata']
|
13
|
-
return unless md[TAG] && md[TAG].first
|
14
|
-
package.map['description'] = { 'full' => md[TAG].first['@'] }
|
15
|
-
md.delete(TAG)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_Metadata < Bookbinder::Transform
|
2
|
-
|
3
|
-
SKIP_FIELDS = ['TRCK', 'TENC', 'APIC']
|
4
|
-
|
5
|
-
|
6
|
-
def to_map(package)
|
7
|
-
return unless path = package.audio_paths.first
|
8
|
-
md = {}
|
9
|
-
package.file(path).get_file { |mp3|
|
10
|
-
Mp3Info.open(mp3) { |info|
|
11
|
-
info.tag2.each_pair { |name, value|
|
12
|
-
next if value == 'null' || SKIP_FIELDS.include?(name)
|
13
|
-
md[name] = [md[name], { '@' => value }].flatten.compact
|
14
|
-
}
|
15
|
-
}
|
16
|
-
}
|
17
|
-
package.map['metadata'] = md
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# TXXX MediaMarker XML format is like this:
|
2
|
-
#
|
3
|
-
# OverDrive MediaMarkers\\x00<Markers><Marker><Name>Part I - Chapter 1</Name><Time>0:00.000</Time></Marker>... etc ...</Markers>
|
4
|
-
#
|
5
|
-
class Bookbinder::Transform::MP3Audiobook_NavToc < Bookbinder::Transform
|
6
|
-
|
7
|
-
TAG = 'TXXX'
|
8
|
-
MEDIAMARKER_PREFIX_RE = /^.*MediaMarkers[^<]*/
|
9
|
-
|
10
|
-
|
11
|
-
def dependencies
|
12
|
-
[Bookbinder::Transform::MP3Audiobook_Metadata]
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
def to_map(package)
|
17
|
-
toc = []
|
18
|
-
package.audio_paths.each { |aud_path|
|
19
|
-
xml = nil
|
20
|
-
package.file(aud_path).get_file { |mp3|
|
21
|
-
Mp3Info.open(mp3) { |info| xml = info.tag2[TAG] }
|
22
|
-
}
|
23
|
-
next unless xml && xml.slice!(MEDIAMARKER_PREFIX_RE)
|
24
|
-
doc = Nokogiri::XML(xml)
|
25
|
-
doc.root.css('Marker').each { |marker_tag|
|
26
|
-
arr = toc
|
27
|
-
title = marker_tag.at_css('Name').content
|
28
|
-
if title.match(/^[[:space:]]+/)
|
29
|
-
arr = (toc.last['contents'] ||= [])
|
30
|
-
title.gsub!(/^[[:space:]]+|[[:space:]]+$/, '')
|
31
|
-
end
|
32
|
-
seconds = translate_time(marker_tag.at_css('Time').content)
|
33
|
-
arr << {
|
34
|
-
'title' => title,
|
35
|
-
'path' => "#{aud_path}#{seconds ? "##{seconds}" : ''}"
|
36
|
-
}
|
37
|
-
}
|
38
|
-
}
|
39
|
-
package.map['nav'] = { 'toc' => toc }
|
40
|
-
if txxx = package.map['metadata'][TAG]
|
41
|
-
txxx.delete_if { |val| val['@'].match(MEDIAMARKER_PREFIX_RE) }
|
42
|
-
end
|
43
|
-
package.map['metadata'].delete(TAG) if txxx.empty?
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
protected
|
48
|
-
|
49
|
-
# MediaMarker time string comes in like:
|
50
|
-
#
|
51
|
-
# 74:48.000
|
52
|
-
#
|
53
|
-
# We should translate to 74*60+48.0 ==> 4488
|
54
|
-
#
|
55
|
-
def translate_time(time_str)
|
56
|
-
if match = time_str.match(/^(\d+):(\d+):([\d\.]+)$/)
|
57
|
-
minutes = match[1].to_i*60 + match[2].to_i
|
58
|
-
seconds = match[3].to_f
|
59
|
-
elsif match = time_str.match(/^(\d+):([\d\.]+)$/)
|
60
|
-
minutes = match[1].to_i
|
61
|
-
seconds = match[2].to_f
|
62
|
-
else
|
63
|
-
puts("Unknown time format: #{time_str}")
|
64
|
-
return nil
|
65
|
-
end
|
66
|
-
seconds = seconds.round if seconds % 1 == 0
|
67
|
-
out = minutes * 60 + seconds
|
68
|
-
out > 0 ? out : nil
|
69
|
-
end
|
70
|
-
|
71
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_Publisher < Bookbinder::Transform
|
2
|
-
|
3
|
-
TAG = 'TPUB'
|
4
|
-
|
5
|
-
|
6
|
-
def dependencies
|
7
|
-
[Bookbinder::Transform::MP3Audiobook_Metadata]
|
8
|
-
end
|
9
|
-
|
10
|
-
|
11
|
-
def to_map(package)
|
12
|
-
return unless md = package.map['metadata']
|
13
|
-
return unless md[TAG] && md[TAG].first
|
14
|
-
package.map['publisher'] = md[TAG].first['@']
|
15
|
-
md.delete(TAG)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_Spine < Bookbinder::Transform
|
2
|
-
|
3
|
-
def to_map(package)
|
4
|
-
package.map['resources'] = []
|
5
|
-
package.map['spine'] = package.audio_paths.collect { |path|
|
6
|
-
{
|
7
|
-
'path' => path,
|
8
|
-
'media-type' => 'audio/mpeg'
|
9
|
-
}.tap { |cmpt|
|
10
|
-
break unless cmpt_info = audio_info(package, path)
|
11
|
-
cmpt.update('audio-duration' => cmpt_info.length)
|
12
|
-
cmpt.update('audio-bitrate' => cmpt_info.bitrate) unless cmpt_info.vbr
|
13
|
-
}
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
protected
|
19
|
-
|
20
|
-
def audio_info(package, path)
|
21
|
-
info = nil
|
22
|
-
begin
|
23
|
-
package.file(path).get_file { |mp3|
|
24
|
-
Mp3Info.open(mp3) { |inf| info = inf }
|
25
|
-
}
|
26
|
-
rescue => e
|
27
|
-
# TODO - is there error handling to do here?
|
28
|
-
raise e
|
29
|
-
end
|
30
|
-
info
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_Subject < Bookbinder::Transform
|
2
|
-
|
3
|
-
TAG = 'TCON'
|
4
|
-
|
5
|
-
|
6
|
-
def dependencies
|
7
|
-
[Bookbinder::Transform::MP3Audiobook_Metadata]
|
8
|
-
end
|
9
|
-
|
10
|
-
|
11
|
-
def to_map(package)
|
12
|
-
return unless md = package.map['metadata']
|
13
|
-
return unless md[TAG] && md[TAG].first
|
14
|
-
package.map['subject'] = md[TAG].collect { |h| h['@'] }
|
15
|
-
md.delete(TAG)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
class Bookbinder::Transform::MP3Audiobook_Title < Bookbinder::Transform
|
2
|
-
|
3
|
-
TAG = 'TALB'
|
4
|
-
|
5
|
-
def dependencies
|
6
|
-
[Bookbinder::Transform::MP3Audiobook_Metadata]
|
7
|
-
end
|
8
|
-
|
9
|
-
|
10
|
-
def to_map(package)
|
11
|
-
package.map['title'] = titles = {}
|
12
|
-
return unless md = package.map['metadata']
|
13
|
-
return unless md[TAG] && md[TAG].first
|
14
|
-
titles['main'] = md[TAG].first['@']
|
15
|
-
md.delete(TAG)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|