Skip to content

Commit 948be81

Browse files
committed
use hardcover artwork
1 parent 1a30ee4 commit 948be81

6 files changed

Lines changed: 260 additions & 6 deletions

File tree

BookPlayer.xcodeproj/project.pbxproj

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,11 @@
650650
990442102F889201007E2A44 /* ExternalSyncInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9904420F2F8891F9007E2A44 /* ExternalSyncInfoView.swift */; };
651651
990442122F88B101007E2A44 /* SyncInvitationCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990442112F88B0FC007E2A44 /* SyncInvitationCard.swift */; };
652652
990442142F894DDB007E2A44 /* SubscriptionPillView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990442132F894DD5007E2A44 /* SubscriptionPillView.swift */; };
653+
990775772FF33BD700E35FAE /* Get in Frameworks */ = {isa = PBXBuildFile; productRef = 990775762FF33BD700E35FAE /* Get */; };
654+
990775792FF33BE100E35FAE /* Get in Frameworks */ = {isa = PBXBuildFile; productRef = 990775782FF33BE100E35FAE /* Get */; };
655+
9907757C2FF33C0C00E35FAE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 9907757B2FF33C0C00E35FAE /* Kingfisher */; };
656+
9907757E2FF33C1500E35FAE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 9907757D2FF33C1500E35FAE /* Kingfisher */; };
657+
990775802FF33D5300E35FAE /* Get in Frameworks */ = {isa = PBXBuildFile; productRef = 9907757F2FF33D5300E35FAE /* Get */; };
653658
990BF7102FF3006D007DDE38 /* MappingModel_v11_to_v12.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 990BF70F2FF3006D007DDE38 /* MappingModel_v11_to_v12.xcmappingmodel */; };
654659
990BF7112FF3006D007DDE38 /* MappingModel_v11_to_v12.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 990BF70F2FF3006D007DDE38 /* MappingModel_v11_to_v12.xcmappingmodel */; };
655660
990BF7122FF3006D007DDE38 /* MappingModel_v11_to_v12.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 990BF70F2FF3006D007DDE38 /* MappingModel_v11_to_v12.xcmappingmodel */; };
@@ -1918,6 +1923,8 @@
19181923
isa = PBXFrameworksBuildPhase;
19191924
buildActionMask = 2147483647;
19201925
files = (
1926+
9907757E2FF33C1500E35FAE /* Kingfisher in Frameworks */,
1927+
990775792FF33BE100E35FAE /* Get in Frameworks */,
19211928
9F00F6EA2807B5D300996BBB /* RevenueCat in Frameworks */,
19221929
99D9959A2F71B05700BAF216 /* JellyfinAPI in Frameworks */,
19231930
);
@@ -1946,6 +1953,7 @@
19461953
41F1A1D7254B07E80043FCF3 /* MarqueeLabel in Frameworks */,
19471954
8A495CC62CC85D27001B4244 /* JellyfinAPI in Frameworks */,
19481955
41F1A228254B0C6C0043FCF3 /* ZipArchive in Frameworks */,
1956+
990775802FF33D5300E35FAE /* Get in Frameworks */,
19491957
);
19501958
runOnlyForDeploymentPostprocessing = 0;
19511959
};
@@ -1960,6 +1968,8 @@
19601968
isa = PBXFrameworksBuildPhase;
19611969
buildActionMask = 2147483647;
19621970
files = (
1971+
9907757C2FF33C0C00E35FAE /* Kingfisher in Frameworks */,
1972+
990775772FF33BD700E35FAE /* Get in Frameworks */,
19631973
9F00F6E82807B5CB00996BBB /* RevenueCat in Frameworks */,
19641974
99D995982F71B02D00BAF216 /* JellyfinAPI in Frameworks */,
19651975
);
@@ -3952,6 +3962,8 @@
39523962
packageProductDependencies = (
39533963
9F00F6E92807B5D300996BBB /* RevenueCat */,
39543964
99D995992F71B05700BAF216 /* JellyfinAPI */,
3965+
990775782FF33BE100E35FAE /* Get */,
3966+
9907757D2FF33C1500E35FAE /* Kingfisher */,
39553967
);
39563968
productName = BookPlayerWatchKit;
39573969
productReference = 4140EA5E227289720009F794 /* BookPlayerWatchKit.framework */;
@@ -4006,6 +4018,7 @@
40064018
41F1A227254B0C6C0043FCF3 /* ZipArchive */,
40074019
8A495CC52CC85D27001B4244 /* JellyfinAPI */,
40084020
63AE44722E6161B60088668F /* DirectoryWatcher */,
4021+
9907757F2FF33D5300E35FAE /* Get */,
40094022
);
40104023
productName = "Audiobook Player";
40114024
productReference = 418B6CF81D2707F800F974FB /* BookPlayer.app */;
@@ -4046,6 +4059,8 @@
40464059
packageProductDependencies = (
40474060
9F00F6E72807B5CB00996BBB /* RevenueCat */,
40484061
99D995972F71B02D00BAF216 /* JellyfinAPI */,
4062+
990775762FF33BD700E35FAE /* Get */,
4063+
9907757B2FF33C0C00E35FAE /* Kingfisher */,
40494064
);
40504065
productName = BookPlayerKit;
40514066
productReference = 41A1B0D2226E9BC400EA0400 /* BookPlayerKit.framework */;
@@ -4195,6 +4210,8 @@
41954210
9FE8188D2805BCE40089A41E /* XCRemoteSwiftPackageReference "purchases-ios" */,
41964211
8A495CC42CC8589F001B4244 /* XCRemoteSwiftPackageReference "jellyfin-sdk-swift" */,
41974212
63AE44712E6161B60088668F /* XCRemoteSwiftPackageReference "DirectoryWatcher" */,
4213+
990775752FF33BD700E35FAE /* XCRemoteSwiftPackageReference "Get" */,
4214+
9907757A2FF33C0C00E35FAE /* XCRemoteSwiftPackageReference "Kingfisher" */,
41984215
);
41994216
productRefGroup = 418B6CF91D2707F800F974FB /* Products */;
42004217
projectDirPath = "";
@@ -6814,6 +6831,22 @@
68146831
minimumVersion = 0.4.0;
68156832
};
68166833
};
6834+
990775752FF33BD700E35FAE /* XCRemoteSwiftPackageReference "Get" */ = {
6835+
isa = XCRemoteSwiftPackageReference;
6836+
repositoryURL = "https://github.com/kean/Get.git";
6837+
requirement = {
6838+
kind = upToNextMajorVersion;
6839+
minimumVersion = 2.2.1;
6840+
};
6841+
};
6842+
9907757A2FF33C0C00E35FAE /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
6843+
isa = XCRemoteSwiftPackageReference;
6844+
repositoryURL = "https://github.com/onevcat/Kingfisher.git";
6845+
requirement = {
6846+
kind = upToNextMajorVersion;
6847+
minimumVersion = 8.10.0;
6848+
};
6849+
};
68176850
9FE8188D2805BCE40089A41E /* XCRemoteSwiftPackageReference "purchases-ios" */ = {
68186851
isa = XCRemoteSwiftPackageReference;
68196852
repositoryURL = "https://github.com/RevenueCat/purchases-ios.git";
@@ -6865,6 +6898,31 @@
68656898
package = 8A495CC42CC8589F001B4244 /* XCRemoteSwiftPackageReference "jellyfin-sdk-swift" */;
68666899
productName = JellyfinAPI;
68676900
};
6901+
990775762FF33BD700E35FAE /* Get */ = {
6902+
isa = XCSwiftPackageProductDependency;
6903+
package = 990775752FF33BD700E35FAE /* XCRemoteSwiftPackageReference "Get" */;
6904+
productName = Get;
6905+
};
6906+
990775782FF33BE100E35FAE /* Get */ = {
6907+
isa = XCSwiftPackageProductDependency;
6908+
package = 990775752FF33BD700E35FAE /* XCRemoteSwiftPackageReference "Get" */;
6909+
productName = Get;
6910+
};
6911+
9907757B2FF33C0C00E35FAE /* Kingfisher */ = {
6912+
isa = XCSwiftPackageProductDependency;
6913+
package = 9907757A2FF33C0C00E35FAE /* XCRemoteSwiftPackageReference "Kingfisher" */;
6914+
productName = Kingfisher;
6915+
};
6916+
9907757D2FF33C1500E35FAE /* Kingfisher */ = {
6917+
isa = XCSwiftPackageProductDependency;
6918+
package = 9907757A2FF33C0C00E35FAE /* XCRemoteSwiftPackageReference "Kingfisher" */;
6919+
productName = Kingfisher;
6920+
};
6921+
9907757F2FF33D5300E35FAE /* Get */ = {
6922+
isa = XCSwiftPackageProductDependency;
6923+
package = 990775752FF33BD700E35FAE /* XCRemoteSwiftPackageReference "Get" */;
6924+
productName = Get;
6925+
};
68686926
99D995972F71B02D00BAF216 /* JellyfinAPI */ = {
68696927
isa = XCSwiftPackageProductDependency;
68706928
package = 8A495CC42CC8589F001B4244 /* XCRemoteSwiftPackageReference "jellyfin-sdk-swift" */;

BookPlayer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

BookPlayer/AppDelegate.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, BPLogger {
6262
Constants.UserDefaults.systemThemeVariantEnabled: true
6363
])
6464

65+
// Hardcover settings live in the shared (app group) suite. Register their defaults
66+
// there so the service (`bool/object(forKey:)`) and the settings UI (`@AppStorage`)
67+
// resolve to the same value before the user explicitly saves anything.
68+
UserDefaults.sharedDefaults.register(defaults: [
69+
Constants.UserDefaults.hardcoverAutoAddWantToRead: true,
70+
Constants.UserDefaults.hardcoverReadingThreshold: 1.0,
71+
])
72+
6573
NotificationCenter.default.addObserver(
6674
self,
6775
selector: #selector(self.messageReceived),

BookPlayer/Hardcover/ItemDetails Section/ItemDetailsHardcoverSectionView.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,7 @@ struct ItemDetailsHardcoverSectionView: View {
3131
HardcoverBookPickerView(viewModel: viewModel.pickerViewModel)
3232
},
3333
label: {
34-
if let row = viewModel.pickerViewModel.selected {
35-
HardcoverBookRow(viewModel: row)
36-
} else {
37-
Text("select_title".localized)
38-
}
34+
HardcoverSelectionLabel(pickerViewModel: viewModel.pickerViewModel)
3935
}
4036
)
4137
.accessibilityHint("voiceover_hardcover_navigation_hint".localized)
@@ -44,6 +40,20 @@ struct ItemDetailsHardcoverSectionView: View {
4440
}
4541
}
4642

43+
/// Observes the picker view model directly so the row reflects `selected` as soon as it's
44+
/// set — the section's own `@ObservedObject` won't fire for changes on the nested model.
45+
private struct HardcoverSelectionLabel: View {
46+
@ObservedObject var pickerViewModel: HardcoverBookPickerView.Model
47+
48+
var body: some View {
49+
if let row = pickerViewModel.selected {
50+
HardcoverBookRow(viewModel: row)
51+
} else {
52+
Text("select_title".localized)
53+
}
54+
}
55+
}
56+
4757
extension ItemDetailsHardcoverSectionView {
4858
class Model: ObservableObject {
4959
@Published var pickerViewModel: HardcoverBookPickerView.Model

BookPlayer/Hardcover/Network/HardcoverService.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ extension HardcoverService {
386386

387387
await libraryService.setHardcoverBook(book, for: match.item.relativePath)
388388
await uploadExternalResource(for: book, to: match.item)
389+
await downloadArtworkIfNeeded(for: book, to: match.item)
389390
Self.logger.info("Auto-matched '\(match.item.title)' to Hardcover ID \(bookID)")
390391
processedCount += 1
391392
} catch {
@@ -436,6 +437,7 @@ extension HardcoverService {
436437
}
437438

438439
await uploadExternalResource(for: book, to: item)
440+
await downloadArtworkIfNeeded(for: book, to: item)
439441
}
440442

441443
/// Create the hardcover external resource for the item and sync-upload it if a sync service is available.
@@ -477,6 +479,29 @@ extension HardcoverService {
477479
)
478480
}
479481

482+
/// If the Hardcover book has artwork and the item has none, download it, set it as the
483+
/// item's artwork, and run the artwork-upload process (which itself no-ops unless cloud
484+
/// sync is active, i.e. the user is Pro/Lite).
485+
private func downloadArtworkIfNeeded(
486+
for book: SimpleHardcoverBook,
487+
to item: SimpleLibraryItem
488+
) async {
489+
guard
490+
let artworkURL = book.artworkURL,
491+
item.artworkURL == nil,
492+
!ArtworkService.isCached(relativePath: item.relativePath)
493+
else { return }
494+
495+
do {
496+
let (data, _) = try await URLSession.shared.data(from: artworkURL)
497+
await ArtworkService.storeInCache(data, for: item.relativePath)
498+
syncService?.scheduleUploadArtwork(relativePath: item.relativePath, uuid: item.uuid)
499+
Self.logger.info("Set Hardcover artwork for '\(item.title)'")
500+
} catch {
501+
Self.logger.error("Failed to download Hardcover artwork for '\(item.title)': \(error)")
502+
}
503+
}
504+
480505
func removeFromLibrary(_ book: SimpleHardcoverBook) async throws {
481506
guard let userBookID = book.userBookID else {
482507
Self.logger.error("Cannot remove book \(book.id) from Hardcover library: no userBookID")

0 commit comments

Comments
 (0)