Skip to content
This repository was archived by the owner on Nov 26, 2020. It is now read-only.

ePub 3.0 media overlays can be played and example updated to demonstrate #23

Merged
merged 33 commits into from
Jan 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0127909
ePup parser supports media:duration and media-overlay
kjantzer Dec 30, 2015
7ed410e
Media Overlay .smil files are parsed and saved to FRBook model
kjantzer Dec 31, 2015
696db91
Adding play button images
kjantzer Jan 4, 2016
525f1ec
Basic audio syncing works but still needs work
kjantzer Jan 4, 2016
dbaa669
Removing dev print
kjantzer Jan 4, 2016
2db3c61
Removing test code
kjantzer Jan 5, 2016
44ea7aa
Changing text menu items back to text #11
kjantzer Jan 5, 2016
79e6f05
Improving SMIL parsing to work with nested elements #11
kjantzer Jan 5, 2016
619a320
Fixing epub parser when testing for smil media type
kjantzer Jan 5, 2016
d4ecbbe
Extended String with `clockTimeToSeconds` method
kjantzer Jan 6, 2016
994c452
Added `title()` method to FRBook model
kjantzer Jan 6, 2016
39a30e7
Audio player improvements
kjantzer Jan 6, 2016
d117a65
Audio syncing will not autoscroll the page if the user already is
kjantzer Jan 6, 2016
84c8365
Improved the display of audio "duration" in TOC
kjantzer Jan 6, 2016
95dc749
If navbar does not hide on tap, it should not hide when font menu is …
kjantzer Jan 6, 2016
11c5247
Media Overlay audio controls and `allowSharing` config option
kjantzer Jan 6, 2016
14ac40c
Changing varible name from fontName to playbackRate
kjantzer Jan 6, 2016
84ac42e
Moving sample ebook to directory and adding another ebook
kjantzer Jan 8, 2016
c4d1dd6
Adding two more sample audio ebooks
kjantzer Jan 8, 2016
abeecf2
Fixing `findFirstResource` when mediaType nil
kjantzer Jan 8, 2016
cbe8a95
Changed play/pause from segment view to a button
kjantzer Jan 8, 2016
3b66a0f
FRResource has `basePath` method
kjantzer Jan 8, 2016
f594fdb
Functional methods added to Smil classes
kjantzer Jan 8, 2016
18cdbb8
MediaOverlay color matches overal theme
kjantzer Jan 8, 2016
e70a41c
Fixing `clockTimeToSeconds` when no leading zero
kjantzer Jan 8, 2016
fdeb3e7
Tab adjustments
kjantzer Jan 8, 2016
cd81f36
Config settings for media overlay color
kjantzer Jan 8, 2016
c5f608e
Changing default play/pause wording
kjantzer Jan 8, 2016
eebbd28
Audio player improvements and fixes
kjantzer Jan 8, 2016
c6cd84b
Demo app has multiple ebooks for testing
kjantzer Jan 8, 2016
47aa7a9
Removing two of the sample epubs as they are too big for GitHub
kjantzer Jan 8, 2016
46d5199
Improving the parser to avoid crash on nil values
hebertialmeida Jan 5, 2016
7f0ac9e
Removing internal test epub references
kjantzer Jan 11, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
1A42C28F1C0E3882000F2137 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A42C28E1C0E3882000F2137 /* ViewController.swift */; };
1A42C2921C0E3883000F2137 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A42C2901C0E3883000F2137 /* Main.storyboard */; };
1A42C2941C0E3883000F2137 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1A42C2931C0E3883000F2137 /* Assets.xcassets */; };
1A42C29F1C0E39B3000F2137 /* book.epub in Resources */ = {isa = PBXBuildFile; fileRef = 1A42C29E1C0E39B3000F2137 /* book.epub */; };
1A42C2A21C0E3A8D000F2137 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A42C2A01C0E3A8D000F2137 /* LaunchScreen.storyboard */; };
6E3C4A3F1C3C6708009CBC8C /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E3C4A3E1C3C6708009CBC8C /* MediaPlayer.framework */; };
6E5C9B051C4037B7008F6FD9 /* The Silver Chair.epub in Resources */ = {isa = PBXBuildFile; fileRef = 6E5C9B031C4037B7008F6FD9 /* The Silver Chair.epub */; };
6E5C9B091C403968008F6FD9 /* The Adventures Of Sherlock Holmes - Adventure I.epub in Resources */ = {isa = PBXBuildFile; fileRef = 6E5C9B081C403942008F6FD9 /* The Adventures Of Sherlock Holmes - Adventure I.epub */; };
F2DF7E310FE62B1F477F22A1 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37A980705BFFE71CCA6F9C31 /* Pods.framework */; };
/* End PBXBuildFile section */

Expand All @@ -24,9 +26,17 @@
1A42C2911C0E3883000F2137 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
1A42C2931C0E3883000F2137 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
1A42C2981C0E3883000F2137 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
1A42C29E1C0E39B3000F2137 /* book.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = book.epub; sourceTree = "<group>"; };
1A42C2A11C0E3A8D000F2137 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
37A980705BFFE71CCA6F9C31 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6E3C4A341C3C5181009CBC8C /* 0005-v3.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "0005-v3.epub"; sourceTree = "<group>"; };
6E3C4A351C3C5181009CBC8C /* 0006-v3.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "0006-v3.epub"; sourceTree = "<group>"; };
6E3C4A361C3C5181009CBC8C /* 0007-v3.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "0007-v3.epub"; sourceTree = "<group>"; };
6E3C4A371C3C5181009CBC8C /* 0008-v3.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "0008-v3.epub"; sourceTree = "<group>"; };
6E3C4A381C3C5181009CBC8C /* 0024-v3.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "0024-v3.epub"; sourceTree = "<group>"; };
6E3C4A3E1C3C6708009CBC8C /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
6E5C9B031C4037B7008F6FD9 /* The Silver Chair.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "The Silver Chair.epub"; sourceTree = "<group>"; };
6E5C9B061C4037DA008F6FD9 /* Coraline.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = Coraline.epub; sourceTree = "<group>"; };
6E5C9B081C403942008F6FD9 /* The Adventures Of Sherlock Holmes - Adventure I.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "The Adventures Of Sherlock Holmes - Adventure I.epub"; sourceTree = "<group>"; };
8BCB174DF233A59B81E828D3 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand All @@ -35,6 +45,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
6E3C4A3F1C3C6708009CBC8C /* MediaPlayer.framework in Frameworks */,
F2DF7E310FE62B1F477F22A1 /* Pods.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -69,14 +80,38 @@
1A42C2931C0E3883000F2137 /* Assets.xcassets */,
1A42C2981C0E3883000F2137 /* Info.plist */,
1A42C2A01C0E3A8D000F2137 /* LaunchScreen.storyboard */,
1A42C29E1C0E39B3000F2137 /* book.epub */,
6E5C9B011C4037B7008F6FD9 /* Sample eBooks */,
6E3C4A331C3C5181009CBC8C /* bsa-epubs */,
);
path = Example;
sourceTree = "<group>";
};
6E3C4A331C3C5181009CBC8C /* bsa-epubs */ = {
isa = PBXGroup;
children = (
6E5C9B061C4037DA008F6FD9 /* Coraline.epub */,
6E3C4A341C3C5181009CBC8C /* 0005-v3.epub */,
6E3C4A351C3C5181009CBC8C /* 0006-v3.epub */,
6E3C4A361C3C5181009CBC8C /* 0007-v3.epub */,
6E3C4A371C3C5181009CBC8C /* 0008-v3.epub */,
6E3C4A381C3C5181009CBC8C /* 0024-v3.epub */,
);
path = "bsa-epubs";
sourceTree = "<group>";
};
6E5C9B011C4037B7008F6FD9 /* Sample eBooks */ = {
isa = PBXGroup;
children = (
6E5C9B081C403942008F6FD9 /* The Adventures Of Sherlock Holmes - Adventure I.epub */,
6E5C9B031C4037B7008F6FD9 /* The Silver Chair.epub */,
);
path = "Sample eBooks";
sourceTree = "<group>";
};
D394FAB8B11D7C692E7BE00D /* Frameworks */ = {
isa = PBXGroup;
children = (
6E3C4A3E1C3C6708009CBC8C /* MediaPlayer.framework */,
37A980705BFFE71CCA6F9C31 /* Pods.framework */,
);
name = Frameworks;
Expand Down Expand Up @@ -154,7 +189,8 @@
files = (
1A42C2941C0E3883000F2137 /* Assets.xcassets in Resources */,
1A42C2A21C0E3A8D000F2137 /* LaunchScreen.storyboard in Resources */,
1A42C29F1C0E39B3000F2137 /* book.epub in Resources */,
6E5C9B091C403968008F6FD9 /* The Adventures Of Sherlock Holmes - Adventure I.epub in Resources */,
6E5C9B051C4037B7008F6FD9 /* The Silver Chair.epub in Resources */,
1A42C2921C0E3883000F2137 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -319,6 +355,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
Expand Down
24 changes: 17 additions & 7 deletions Example/Example/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7702" systemVersion="14E11f" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="14F1509" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--View Controller-->
Expand All @@ -16,20 +17,29 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4VN-zC-gTV">
<rect key="frame" x="236" y="285" width="129" height="30"/>
<state key="normal" title="Open Folio Reader">
<button opaque="NO" tag="1" contentMode="scaleToFill" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4VN-zC-gTV">
<rect key="frame" x="247" y="78" width="108" height="30"/>
<state key="normal" title="The Silver Road">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="didOpen:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="Ibf-j6-YlH"/>
</connections>
</button>
<button opaque="NO" tag="2" contentMode="scaleToFill" ambiguous="YES" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vdn-HF-kCC">
<rect key="frame" x="150" y="143" width="302" height="30"/>
<state key="normal" title="The Adventures Of Sherlock Holmes (Audio)">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="didOpen:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="M8S-eM-283"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerY" secondItem="4VN-zC-gTV" secondAttribute="centerY" id="Dvz-rB-mvx"/>
<constraint firstAttribute="centerX" secondItem="4VN-zC-gTV" secondAttribute="centerX" constant="-0.5" id="omE-LJ-w7Q"/>
<constraint firstItem="4VN-zC-gTV" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="8gi-wn-vte"/>
<constraint firstItem="vdn-HF-kCC" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="XaE-aP-bux"/>
</constraints>
</view>
</viewController>
Expand Down
Binary file not shown.
18 changes: 16 additions & 2 deletions Example/Example/ViewController.swift
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,28 @@ class ViewController: UIViewController {
}

@IBAction func didOpen(sender: AnyObject) {
openEpub(sender.tag);
}

func openEpub(sampleNum:Int) {
let config = FolioReaderConfig()
// config.shouldHideNavigationOnTap = false
config.shouldHideNavigationOnTap = false
// config.allowSharing = false
// config.toolBarTintColor = UIColor.redColor()
// config.toolBarBackgroundColor = UIColor.purpleColor()
// config.menuTextColor = UIColor.brownColor()
// config.menuBackgroundColor = UIColor.lightGrayColor()

let bookPath = NSBundle.mainBundle().pathForResource("book", ofType: "epub")
// http://www.readbeyond.it/ebooks.html
let epubSampleFiles = [
"The Silver Chair", // standard eBook
"The Adventures Of Sherlock Holmes - Adventure I", // audio-eBook
]

let epubName = epubSampleFiles[sampleNum-1];
let bookPath = NSBundle.mainBundle().pathForResource(epubName, ofType: "epub")

FolioReader.presentReader(parentViewController: self, withEpubPath: bookPath!, andConfig: config)
}

}
60 changes: 60 additions & 0 deletions Source/EPUBCore/FRBook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// FolioReaderKit
//
// Created by Heberti Almeida on 09/04/15.
// Extended by Kevin Jantzer on 12/30/15
// Copyright (c) 2015 Folio Reader. All rights reserved.
//

Expand All @@ -12,8 +13,67 @@ class FRBook: NSObject {
var resources = FRResources()
var metadata = FRMetadata()
var spine = FRSpine()
var smils = FRSmils()
var tableOfContents: [FRTocReference]!
var opfResource: FRResource!
var ncxResource: FRResource!
var coverImage: FRResource!

func hasAudio() -> Bool {
return smils.smils.count > 0 ? true : false;
}

func title() -> String! {
return metadata.titles[0]
}

// MARK: - Media Overlay Metadata
// http://www.idpf.org/epub/301/spec/epub-mediaoverlays.html#sec-package-metadata

func duration() -> String? {
return metadata.findMetaByProperty("media:duration");
}

// @NOTE: should "#" be automatically prefixed with the ID?
func durationFor(ID: String) -> String? {
return metadata.findMetaByProperty("media:duration", refinedBy: ID)
}


func activeClass() -> String! {
let className = metadata.findMetaByProperty("media:active-class");
return className != nil ? className : "epub-media-overlay-active";
}

func playbackActiveClass() -> String! {
let className = metadata.findMetaByProperty("media:playback-active-class");
return className != nil ? className : "epub-media-overlay-playing";
}


// MARK: - Media Overlay (SMIL) retrieval

/**
Get Smil File from a resource (if it has a media-overlay)
*/
func smilFileForResource(resource: FRResource!) -> FRSmilFile! {
if( resource == nil || resource.mediaOverlay == nil ){
return nil
}

// lookup the smile resource to get info about the file
let smilResource = resources.getById(resource.mediaOverlay)

// use the resource to get the file
return smils.getByHref( smilResource!.href )
}

func smilFileForHref(href: String) -> FRSmilFile! {
return smilFileForResource(resources.getByHref(href))
}

func smilFileForId(ID: String) -> FRSmilFile! {
return smilFileForResource(resources.getById(ID))
}

}
61 changes: 61 additions & 0 deletions Source/EPUBCore/FREpubParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,26 @@ class FREpubParser: NSObject, SSZipArchiveDelegate {

do {
let xmlDoc = try AEXMLDocument(xmlData: opfData!)

// parse and save each "manifest item"
for item in xmlDoc.root["manifest"]["item"].all! {
let resource = FRResource()
resource.id = item.attributes["id"]
resource.href = item.attributes["href"]
resource.fullHref = (resourcesBasePath as NSString).stringByAppendingPathComponent(item.attributes["href"]!).stringByRemovingPercentEncoding
resource.mediaType = FRMediaType.mediaTypesByName[item.attributes["media-type"]!]
resource.mediaOverlay = item.attributes["media-overlay"]

// if a .smil file is listed in resources, go parse that file now and save it on book model
if( resource.mediaType != nil && resource.mediaType == FRMediaType.SMIL ){
readSmilFile(resource);
}

book.resources.add(resource)
}

book.smils.basePath = resourcesBasePath

// Get the first resource with the NCX mediatype
book.ncxResource = book.resources.findFirstResource(byMediaType: FRMediaType.NCX)

Expand All @@ -112,6 +123,50 @@ class FREpubParser: NSObject, SSZipArchiveDelegate {
}
}

/**
Reads and parses a .smil file
*/
private func readSmilFile(resource: FRResource){
let smilData = try? NSData(contentsOfFile: resource.fullHref, options: .DataReadingMappedAlways)

var smilFile = FRSmilFile(resource: resource);

do {
let xmlDoc = try AEXMLDocument(xmlData: smilData!)

let children = xmlDoc.root["body"].children

if( children.count > 0 ){
smilFile.data.appendContentsOf( readSmilFileElements(children) )
}

} catch {
print("Cannot read .smil file: "+resource.href)
}

book.smils.add(smilFile);
}

private func readSmilFileElements(children:[AEXMLElement]) -> [FRSmilElement] {

var data = [FRSmilElement]()

// convert each smil element to a FRSmil object
for item in children {

let smil = FRSmilElement(name: item.name, attributes: item.attributes)

// if this element has children, convert them to objects too
if( item.children.count > 0 ){
smil.children.appendContentsOf( readSmilFileElements(item.children) )
}

data.append(smil)
}

return data
}

/**
Read and parse the Table of Contents.
*/
Expand Down Expand Up @@ -209,8 +264,14 @@ class FREpubParser: NSObject, SSZipArchiveDelegate {
if tag.attributes["property"] != nil && tag.attributes["id"] != nil {
metadata.metaAttributes.append(Meta(id: tag.attributes["id"]!, property: tag.attributes["property"]!, value: tag.value ?? ""))
}

if tag.attributes["property"] != nil {
metadata.metaAttributes.append(Meta(property: tag.attributes["property"]!, value: tag.value != nil ? tag.value! : "", refines: tag.attributes["refines"] != nil ? tag.attributes["refines"] : nil))
}

}
}

return metadata
}

Expand Down
30 changes: 30 additions & 0 deletions Source/EPUBCore/FRMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct Meta {
var id: String?
var property: String?
var value: String?
var refines: String?

init(name: String, content: String) {
self.name = name
Expand All @@ -69,6 +70,12 @@ struct Meta {
self.property = property
self.value = value
}

init(property: String, value: String, refines: String!) {
self.property = property
self.value = value
self.refines = refines
}
}

/**
Expand Down Expand Up @@ -102,4 +109,27 @@ class FRMetadata: NSObject {
}
return nil
}

func findMetaByProperty(property: String, refinedBy: String?) -> String? {
if property.isEmpty {
return nil
}

for meta in metaAttributes {
if meta.property != nil {
if( meta.property == property && refinedBy == nil && meta.refines == nil){
return meta.value
}
if( meta.property == property && meta.refines == refinedBy){
return meta.value
}
}
}
return nil
}

func findMetaByProperty(property: String) -> String? {
return findMetaByProperty(property, refinedBy: nil);
}

}
Loading