Skip to content

Commit

Permalink
Merge pull request #1619 from NYPL-Simplified/task/ios-420
Browse files Browse the repository at this point in the history
iOS-420 Implement last listen position sync
  • Loading branch information
Ernest Fan authored Dec 15, 2022
2 parents 9cf747c + 374ae86 commit 657857d
Show file tree
Hide file tree
Showing 14 changed files with 363 additions and 176 deletions.
2 changes: 1 addition & 1 deletion NYPLAEToolkit
36 changes: 22 additions & 14 deletions Simplified.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@
11F3773319E0876F00487769 /* NYPLCatalogFacet.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F3773219E0876F00487769 /* NYPLCatalogFacet.m */; };
145798F6215BE9E300F68AFD /* ProblemReportEmail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 145798F5215BE9E300F68AFD /* ProblemReportEmail.swift */; };
17071065242A923400E2648F /* NYPLSecrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17071060242A923400E2648F /* NYPLSecrets.swift */; };
17123D4327CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift */; };
17123D4427CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift */; };
17123D4527CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift */; };
17123D4327CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift */; };
17123D4427CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift */; };
17123D4527CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift */; };
171966A924170819007BB87E /* NYPLBookState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171966A824170819007BB87E /* NYPLBookState.swift */; };
1724CA6A26E00D030015A174 /* NYPLReaderSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1724CA6926E00D030015A174 /* NYPLReaderSettingsView.swift */; };
1724CA6B26E00D030015A174 /* NYPLReaderSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1724CA6926E00D030015A174 /* NYPLReaderSettingsView.swift */; };
Expand Down Expand Up @@ -141,6 +141,9 @@
17631AEE25E488CD006079C4 /* NYPLAgeCheckViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17631AEC25E488CD006079C4 /* NYPLAgeCheckViewController.swift */; };
17631AF025E488CD006079C4 /* NYPLAgeCheckViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17631AEC25E488CD006079C4 /* NYPLAgeCheckViewController.swift */; };
1763C0D624F460FE00A4D0E2 /* NYPLAnnouncementManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1763C0D524F460FE00A4D0E2 /* NYPLAnnouncementManagerTests.swift */; };
1765A586291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765A585291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift */; };
1765A587291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765A585291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift */; };
1765A588291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765A585291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift */; };
17681C5127E0314400DF1F8C /* NYPLCatalogUngroupedFeedWithZeroBooks.xml in Resources */ = {isa = PBXBuildFile; fileRef = 17681C5027E0314400DF1F8C /* NYPLCatalogUngroupedFeedWithZeroBooks.xml */; };
17681C5227E0314400DF1F8C /* NYPLCatalogUngroupedFeedWithZeroBooks.xml in Resources */ = {isa = PBXBuildFile; fileRef = 17681C5027E0314400DF1F8C /* NYPLCatalogUngroupedFeedWithZeroBooks.xml */; };
177C25FC28B704A600A786F1 /* NYPLUtilities in Frameworks */ = {isa = PBXBuildFile; productRef = 177C25FB28B704A600A786F1 /* NYPLUtilities */; };
Expand Down Expand Up @@ -1854,7 +1857,7 @@
11F54C2919423A040086FCAF /* NYPLOPDSLinkTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYPLOPDSLinkTests.m; sourceTree = "<group>"; };
145798F5215BE9E300F68AFD /* ProblemReportEmail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportEmail.swift; sourceTree = "<group>"; };
17071060242A923400E2648F /* NYPLSecrets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NYPLSecrets.swift; sourceTree = "<group>"; };
17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NYPLBookCellDelegate+AudiobookProgressSaving.swift"; sourceTree = "<group>"; };
17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NYPLBookCellDelegate+AudiobookLastListenPosition.swift"; sourceTree = "<group>"; };
171966A824170819007BB87E /* NYPLBookState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYPLBookState.swift; sourceTree = "<group>"; };
1724CA6926E00D030015A174 /* NYPLReaderSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYPLReaderSettingsView.swift; sourceTree = "<group>"; };
172F40F926F0A0170017476A /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
Expand All @@ -1868,6 +1871,7 @@
175E480724EF36520066A6CF /* NYPLAnnouncementBusinessLogic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYPLAnnouncementBusinessLogic.swift; sourceTree = "<group>"; };
17631AEC25E488CD006079C4 /* NYPLAgeCheckViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYPLAgeCheckViewController.swift; sourceTree = "<group>"; };
1763C0D524F460FE00A4D0E2 /* NYPLAnnouncementManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYPLAnnouncementManagerTests.swift; sourceTree = "<group>"; };
1765A585291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYPLLastListenPositionSynchronizer.swift; sourceTree = "<group>"; };
17681C5027E0314400DF1F8C /* NYPLCatalogUngroupedFeedWithZeroBooks.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = NYPLCatalogUngroupedFeedWithZeroBooks.xml; sourceTree = "<group>"; };
176F8A802519684D00CE5BFB /* AudioEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = AudioEngine.xcframework; path = NYPLAEToolkit/build/AudioEngine.xcframework; sourceTree = "<group>"; };
177E04FE28A72BD500DF7587 /* NYPLAudiobookBookmarksBusinessLogic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NYPLAudiobookBookmarksBusinessLogic.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2639,6 +2643,7 @@
2198F90F250A90EE000D9DAB /* AudioBookVendorsHelper.swift */,
1785228B27F52B58004445ED /* AudiobookManifestAdapter.swift */,
1785229327F7B955004445ED /* NYPLAudiobookDownloader.swift */,
1765A585291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift */,
);
path = Audiobooks;
sourceTree = "<group>";
Expand Down Expand Up @@ -3237,7 +3242,7 @@
085D31D51BE29E38007F7672 /* NYPLProblemReportViewController.h */,
085D31D61BE29E38007F7672 /* NYPLProblemReportViewController.m */,
085D31D81BE29ED4007F7672 /* NYPLProblemReportViewController.xib */,
17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift */,
17123D4227CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift */,
17D8633B29031A750096F11A /* NYPLBookCellDelegate+AudiobookBookmark.swift */,
);
path = UI;
Expand Down Expand Up @@ -4619,7 +4624,7 @@
739E60B5244A0D8600D00301 /* NYPLMyBooksViewController.m in Sources */,
73B550FA2511723000D05B86 /* NYPLCatalogs+SE.swift in Sources */,
21C7B87F25AE1DBA000E8BF3 /* LibraryServiceError.swift in Sources */,
17123D4427CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift in Sources */,
17123D4427CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift in Sources */,
739E60B6244A0D8600D00301 /* NYPLBookDetailTableView.swift in Sources */,
73A172F027ADA6FA005E7BCF /* NYPLFullAxisNowResource.swift in Sources */,
739E60B7244A0D8600D00301 /* Account.swift in Sources */,
Expand Down Expand Up @@ -4677,6 +4682,7 @@
739E60D9244A0D8600D00301 /* NYPLKeychain.m in Sources */,
7369A39D264AF9710029D8AB /* NYPLAdobeContentProtectionService.swift in Sources */,
739E60DC244A0D8600D00301 /* LibraryService.swift in Sources */,
1765A587291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift in Sources */,
73A172F327ADA6FA005E7BCF /* NYPLAxisXMLCreator.swift in Sources */,
739E60DD244A0D8600D00301 /* NYPLOPDSEntry.m in Sources */,
739E60E0244A0D8600D00301 /* NYPLRootTabBarController+R2.swift in Sources */,
Expand Down Expand Up @@ -4760,6 +4766,7 @@
73EB0A7A25821DF4006BC997 /* NYPLMyBooksNavigationController.m in Sources */,
17FFE882278BC65F0084E65D /* NYPLOPDSFeedFetcher.swift in Sources */,
73EB0A7B25821DF4006BC997 /* NYPLSignInBusinessLogicUIDelegate.swift in Sources */,
1765A588291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift in Sources */,
73EB0A7C25821DF4006BC997 /* NYPLBookCellCollectionViewController.m in Sources */,
73EB0A7D25821DF4006BC997 /* NYPLKeychainStoredVariable.swift in Sources */,
73EB0A7E25821DF4006BC997 /* NYPLXML.m in Sources */,
Expand Down Expand Up @@ -4915,7 +4922,7 @@
73EB0AF925821DF4006BC997 /* NYPLRootTabBarController.m in Sources */,
73EB0AFA25821DF4006BC997 /* ExtendedNavBarView.swift in Sources */,
73EB0AFB25821DF4006BC997 /* OPDS2Link.swift in Sources */,
17123D4527CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift in Sources */,
17123D4527CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift in Sources */,
73EB0AFC25821DF4006BC997 /* NYPLSignInBusinessLogic+BookmarkSyncing.swift in Sources */,
73EB0AFD25821DF4006BC997 /* NYPLBookAuthor.swift in Sources */,
2126FE3A25C0597E0095C45C /* LibraryServiceError.swift in Sources */,
Expand Down Expand Up @@ -5393,7 +5400,7 @@
116A5EB3194767DC00491A21 /* NYPLMyBooksViewController.m in Sources */,
21C7B87E25AE1DBA000E8BF3 /* LibraryServiceError.swift in Sources */,
E6B3269F1EE066DE00DB877A /* NYPLBookDetailTableView.swift in Sources */,
17123D4327CEFB5700088193 /* NYPLBookCellDelegate+AudiobookProgressSaving.swift in Sources */,
17123D4327CEFB5700088193 /* NYPLBookCellDelegate+AudiobookLastListenPosition.swift in Sources */,
734B78992565F7DE006FB8AD /* NYPLReauthenticator.swift in Sources */,
73A172DA27ADA6F9005E7BCF /* NYPLFullAxisNowResource.swift in Sources */,
175E480824EF36520066A6CF /* NYPLAnnouncementBusinessLogic.swift in Sources */,
Expand Down Expand Up @@ -5451,6 +5458,7 @@
113DB8A719C24E54004E1154 /* NYPLIndeterminateProgressView.m in Sources */,
119BEB89198C43A600121439 /* NSString+NYPLStringAdditions.m in Sources */,
E6B6E76F1F6859A4007EE361 /* NYPLKeychainManager.swift in Sources */,
1765A586291DE4470075A09E /* NYPLLastListenPositionSynchronizer.swift in Sources */,
73A172DD27ADA6F9005E7BCF /* NYPLAxisXMLCreator.swift in Sources */,
089E42C6249A823800310360 /* NYPLCookiesWebViewController.swift in Sources */,
0826CD2924AA21B2000F4030 /* NYPLSamlIDPCell.swift in Sources */,
Expand Down Expand Up @@ -5714,7 +5722,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR;
CODE_SIGN_ENTITLEMENTS = Simplified/SimplyE.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 7262U6ST2R;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
Expand Down Expand Up @@ -5765,7 +5773,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR;
CODE_SIGN_ENTITLEMENTS = Simplified/SimplyE.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 7262U6ST2R;
ENABLE_BITCODE = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
Expand Down Expand Up @@ -5815,7 +5823,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR;
CODE_SIGN_ENTITLEMENTS = Simplified/SimplyE.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 7262U6ST2R;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
Expand Down Expand Up @@ -5863,7 +5871,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR;
CODE_SIGN_ENTITLEMENTS = Simplified/SimplyE.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 7262U6ST2R;
ENABLE_BITCODE = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
Expand Down Expand Up @@ -6133,7 +6141,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR;
CODE_SIGN_ENTITLEMENTS = Simplified/SimplyE.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 7262U6ST2R;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
Expand Down Expand Up @@ -6184,7 +6192,7 @@
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR;
CODE_SIGN_ENTITLEMENTS = Simplified/SimplyE.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 7262U6ST2R;
ENABLE_BITCODE = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
Expand Down
125 changes: 125 additions & 0 deletions Simplified/Audiobooks/NYPLLastListenPositionSynchronizer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//
// NYPLLastListenPositionSynchronizer.swift
// Simplified
//
// Created by Ernest Fan on 2022-11-10.
// Copyright © 2022 NYPL. All rights reserved.
//
#if FEATURE_AUDIOBOOKS
import Foundation
import NYPLAudiobookToolkit

class NYPLLastListenPositionSynchronizer: NYPLLastListenPositionSynchronizing {
private let book: NYPLBook
private let bookRegistryProvider: NYPLBookRegistryProvider
private let annotationsSynchronizer: NYPLLastReadPositionSupportAPI
private let deviceID: String?

private let renderer: String = "NYPLAudiobookToolkit"

init(book: NYPLBook,
bookRegistryProvider: NYPLBookRegistryProvider,
annotationsSynchronizer: NYPLLastReadPositionSupportAPI,
deviceID: String?) {
self.book = book
self.bookRegistryProvider = bookRegistryProvider
self.annotationsSynchronizer = annotationsSynchronizer
self.deviceID = deviceID
}

func getLastListenPosition(completion: @escaping (_ localPosition: NYPLAudiobookBookmark?, _ serverPosition: NYPLAudiobookBookmark?) -> ()) {

// Retrieve local last-listened position
let localPosition = self.getLocalLastListenPosition(for: book.identifier)

// Retrieve last-listened position from server, return both local and server positions
self.annotationsSynchronizer.syncReadingPosition(of: NYPLAudiobookBookmark.self,
forBook: book.identifier,
publication: nil,
toURL: book.annotationsURL) { serverPosition in
guard let serverPosition = serverPosition else {
Log.info(#function, "No reading position annotation exists on the server for \(self.book.loggableShortString()).")
completion(localPosition, nil)
return
}

guard let localPosition = localPosition else {
completion(nil, serverPosition)
return
}

// Pass through without server position (meaning the server doesn't have a
// last listen position worth restoring) if:
// 1 - The most recent position on the server comes from the same device, or
// 2 - The local position is further in the audiobook than the server position
if serverPosition.device == self.deviceID ||
localPosition >= serverPosition {
completion(localPosition, nil)
return
}

completion(localPosition, serverPosition)
}
}

func updateLastListenPositionInMemory(_ location: ChapterLocation) {
let selectorValue = NYPLAudiobookBookmarkFactory.makeLocatorString(title: location.title ?? "",
part: location.part,
chapter: location.number,
audiobookId: location.audiobookID,
duration: location.duration,
time: location.playheadOffset)

let bookLocation = NYPLBookLocation.init(locationString: selectorValue, renderer: self.renderer)

bookRegistryProvider.setLocation(bookLocation, forIdentifier: self.book.identifier)
}


func syncLastListenPositionToServer() {
let bookID = self.book.identifier

guard let localPosition = getLocalLastListenPosition(for: bookID) else {
return
}

let selectorValue = NYPLAudiobookBookmarkFactory.makeLocatorString(title: localPosition.title ?? "",
part: localPosition.part,
chapter: localPosition.chapter,
audiobookId: localPosition.audiobookId,
duration: localPosition.duration,
time: localPosition.time)

annotationsSynchronizer.postReadingPosition(forBook: bookID,
selectorValue: selectorValue)
}

// MARK: - Helper

private func getLocalLastListenPosition(for bookID: String) -> NYPLAudiobookBookmark? {
guard let bookLocation = self.bookRegistryProvider.location(forIdentifier: bookID),
bookLocation.renderer == self.renderer else {
return nil
}

if let bookmark = NYPLAudiobookBookmark(selectorString: bookLocation.locationString, creationTime: Date()) {
return bookmark
}

if let chapterLocation = self.chapterLocation(from: bookLocation.locationString) {
// If the retrieved location is a ChapterLocation object,
// we should create a bookmark object from it.
return NYPLAudiobookBookmark(chapterLocation: chapterLocation, creationTime: Date())
}
return nil
}

private func chapterLocation(from locationString: String) -> ChapterLocation? {
guard let data = locationString.data(using: .utf8),
let chapterLocation = ChapterLocation.fromData(data) else {
return nil
}
return chapterLocation
}
}
#endif
Loading

0 comments on commit 657857d

Please sign in to comment.