diff --git a/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/Contents.json b/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/Contents.json
index 069cdf4d..90b58dbe 100644
--- a/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/Contents.json
+++ b/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "audiobookshelf-icon.png",
+ "filename" : "abs.svg",
"idiom" : "universal"
}
],
diff --git a/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/abs.svg b/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/abs.svg
new file mode 100644
index 00000000..54cb1849
--- /dev/null
+++ b/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/abs.svg
@@ -0,0 +1,31 @@
+
\ No newline at end of file
diff --git a/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/audiobookshelf-icon.png b/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/audiobookshelf-icon.png
deleted file mode 100644
index 87fcee04..00000000
Binary files a/BookPlayer/Assets.xcassets/audiobookshelf-icon.imageset/audiobookshelf-icon.png and /dev/null differ
diff --git a/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/Contents.json b/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/Contents.json
index da19810b..7385abcb 100644
--- a/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/Contents.json
+++ b/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "icon-transparent.png",
+ "filename" : "jellyfin.svg",
"idiom" : "universal"
}
],
diff --git a/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/icon-transparent.png b/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/icon-transparent.png
deleted file mode 100644
index 50c881b3..00000000
Binary files a/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/icon-transparent.png and /dev/null differ
diff --git a/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/jellyfin.svg b/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/jellyfin.svg
new file mode 100644
index 00000000..5c75f1c0
--- /dev/null
+++ b/BookPlayer/Assets.xcassets/jellyfin-icon.imageset/jellyfin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/BookPlayer/Library/ItemList/ItemListViewModel.swift b/BookPlayer/Library/ItemList/ItemListViewModel.swift
index bfe5316f..9e5bfe50 100644
--- a/BookPlayer/Library/ItemList/ItemListViewModel.swift
+++ b/BookPlayer/Library/ItemList/ItemListViewModel.swift
@@ -375,8 +375,14 @@ extension ItemListViewModel {
func createFolder(with title: String, items: [String]? = nil, type: SimpleItemType) {
Task { @MainActor in
do {
+ let trimmedTitle = title.trimmingCharacters(in: .whitespacesAndNewlines)
+
+ guard !trimmedTitle.isEmpty else {
+ return
+ }
+
let folder = try libraryService.createFolder(
- with: title,
+ with: trimmedTitle,
inside: libraryNode.folderRelativePath
)
await syncService.scheduleUpload(items: [folder])
diff --git a/BookPlayer/Player/PlayerManager.swift b/BookPlayer/Player/PlayerManager.swift
index 0fb6e32b..207d4724 100755
--- a/BookPlayer/Player/PlayerManager.swift
+++ b/BookPlayer/Player/PlayerManager.swift
@@ -590,7 +590,7 @@ final class PlayerManager: NSObject, PlayerManagerProtocol, ObservableObject {
self.nowPlayingInfo[MPMediaItemPropertyTitle] = chapter.title
/// If the chapter title is the same as the current item, show the author instead
- if chapter.title == currentItem.title {
+ if currentItem.isBoundBook || chapter.title == currentItem.title {
self.nowPlayingInfo[MPMediaItemPropertyArtist] = currentItem.author
} else {
self.nowPlayingInfo[MPMediaItemPropertyArtist] = currentItem.title
diff --git a/BookPlayer/Settings/Sections/SettingsTipView.swift b/BookPlayer/Settings/Sections/SettingsTipView.swift
index 48ee17d1..61d7c4df 100644
--- a/BookPlayer/Settings/Sections/SettingsTipView.swift
+++ b/BookPlayer/Settings/Sections/SettingsTipView.swift
@@ -19,9 +19,26 @@ struct SettingsTipView: View {
@State private var loadProductTask: Task<(), Error>?
@EnvironmentObject private var theme: ThemeViewModel
@Environment(\.loadingState) var loadingState
+ @Environment(\.accountService) var accountService
let purchaseCompleted: () -> Void
+ /// Whether this is the user's first donation (uses non-consumable) or a repeat donation (uses consumable)
+ private var isFirstDonation: Bool {
+ return accountService.getAccount()?.donationMade != true
+ }
+
+ /// Returns the appropriate product ID based on donation history
+ private var productId: String {
+ if isFirstDonation {
+ // First time donation uses non-consumable
+ return tipOption.rawValue
+ } else {
+ // Subsequent donations use consumable
+ return tipOption.rawValue + ".consumable"
+ }
+ }
+
var buttonBackgroundColor: Color {
switch tipOption {
case .kind:
@@ -93,7 +110,8 @@ struct SettingsTipView: View {
func loadProduct() {
loadProductTask = Task {
self.isLoading = true
- let products = await Purchases.shared.products([tipOption.rawValue])
+ // Use non-consumable for first donation, consumable for repeat donations
+ let products = await Purchases.shared.products([productId])
self.product = products.first
self.buttonTitle = self.product?.localizedPriceString ?? ""
self.isLoading = false
@@ -104,7 +122,7 @@ struct SettingsTipView: View {
guard AppEnvironment.isPurchaseEnabled else {
throw PurchaseError.testFlightPurchasesDisabled
}
-
+
_ = await loadProductTask?.result
guard let product else {
diff --git a/Shared/Services/PlaybackService.swift b/Shared/Services/PlaybackService.swift
index 17d91f36..e368b9c0 100644
--- a/Shared/Services/PlaybackService.swift
+++ b/Shared/Services/PlaybackService.swift
@@ -260,7 +260,7 @@ public final class PlaybackService: PlaybackServiceProtocol {
return PlayableItem(
title: folder.title,
- author: chapters.first?.author ?? "voiceover_unknown_author".localized,
+ author: chapters.first?.author ?? folder.details,
chapters: chapters,
currentTime: folder.currentTime,
duration: duration ?? folder.duration,