Version 1.1 App Store

This commit is contained in:
Scarriffle
2026-05-25 10:21:11 +02:00
parent 7ca511d37f
commit 15d8e71d09
17 changed files with 478 additions and 71 deletions

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>

View File

@@ -254,22 +254,23 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
"CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "ABS Client-macOS.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = PP34X97WS3; DEVELOPMENT_TEAM = PP34X97WS3;
ENABLE_APP_SANDBOX = NO; "ENABLE_APP_SANDBOX[sdk=macosx*]" = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "ABS Client";
INFOPLIST_KEY_CFBundleName = "ABS Client";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
"GENERATE_INFOPLIST_FILE[sdk=iphoneos*]" = NO; "GENERATE_INFOPLIST_FILE[sdk=iphoneos*]" = NO;
"GENERATE_INFOPLIST_FILE[sdk=iphonesimulator*]" = NO; "GENERATE_INFOPLIST_FILE[sdk=iphonesimulator*]" = NO;
"INFOPLIST_FILE[sdk=iphoneos*]" = "Info-iOS.plist"; "INFOPLIST_FILE[sdk=iphoneos*]" = "Info-iOS.plist";
"INFOPLIST_FILE[sdk=iphonesimulator*]" = "Info-iOS.plist"; "INFOPLIST_FILE[sdk=iphonesimulator*]" = "Info-iOS.plist";
INFOPLIST_KEY_CFBundleDisplayName = "ABS Client";
INFOPLIST_KEY_CFBundleName = "ABS Client";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.0; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -283,6 +284,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.local.ABS-Client"; PRODUCT_BUNDLE_IDENTIFIER = "com.local.ABS-Client";
PRODUCT_NAME = "ABS Client"; PRODUCT_NAME = "ABS Client";
@@ -296,7 +298,7 @@
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = 1;
}; };
name = Debug; name = Debug;
}; };
@@ -305,22 +307,23 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
"CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "ABS Client-macOS.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = PP34X97WS3; DEVELOPMENT_TEAM = PP34X97WS3;
ENABLE_APP_SANDBOX = NO; "ENABLE_APP_SANDBOX[sdk=macosx*]" = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "ABS Client";
INFOPLIST_KEY_CFBundleName = "ABS Client";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
"GENERATE_INFOPLIST_FILE[sdk=iphoneos*]" = NO; "GENERATE_INFOPLIST_FILE[sdk=iphoneos*]" = NO;
"GENERATE_INFOPLIST_FILE[sdk=iphonesimulator*]" = NO; "GENERATE_INFOPLIST_FILE[sdk=iphonesimulator*]" = NO;
"INFOPLIST_FILE[sdk=iphoneos*]" = "Info-iOS.plist"; "INFOPLIST_FILE[sdk=iphoneos*]" = "Info-iOS.plist";
"INFOPLIST_FILE[sdk=iphonesimulator*]" = "Info-iOS.plist"; "INFOPLIST_FILE[sdk=iphonesimulator*]" = "Info-iOS.plist";
INFOPLIST_KEY_CFBundleDisplayName = "ABS Client";
INFOPLIST_KEY_CFBundleName = "ABS Client";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.books";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.0; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -334,6 +337,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.local.ABS-Client"; PRODUCT_BUNDLE_IDENTIFIER = "com.local.ABS-Client";
PRODUCT_NAME = "ABS Client"; PRODUCT_NAME = "ABS Client";
@@ -347,7 +351,7 @@
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = 1;
}; };
name = Release; name = Release;
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 895 KiB

After

Width:  |  Height:  |  Size: 632 KiB

View File

@@ -35,13 +35,9 @@ struct Audiobookshelf_swiftApp: App {
#if os(iOS) #if os(iOS)
private func configureAudioSession() { private func configureAudioSession() {
do { // Nur die Kategorie registrieren setActive(true) passiert erst in play(),
let session = AVAudioSession.sharedInstance() // damit beim App-Start keine laufende Fremd-Wiedergabe unterbrochen wird.
try session.setCategory(.playback, mode: .default, options: []) try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
try session.setActive(true)
} catch {
// non-fatal
}
} }
#endif #endif
} }

View File

@@ -11,6 +11,12 @@ import AppKit
private typealias PlayerArtworkImage = NSImage private typealias PlayerArtworkImage = NSImage
#endif #endif
enum SleepTimerMode: Equatable, Hashable {
case off
case minutes(Int)
case endOfBook
}
@Observable @Observable
@MainActor @MainActor
final class PlayerEngine { final class PlayerEngine {
@@ -21,6 +27,11 @@ final class PlayerEngine {
var isReady: Bool = false var isReady: Bool = false
var errorMessage: String? var errorMessage: String?
var sleepTimer: SleepTimerMode = .off
/// Verbleibende Wallclock-Sekunden bis der Sleep-Timer auslöst (0 wenn off).
/// Pausiert mit der Wiedergabe; bei `.endOfBook` rate-skaliert aus der Restspielzeit.
var sleepRemainingSeconds: Double = 0
private var player: AVQueuePlayer? private var player: AVQueuePlayer?
private var trackDurations: [Double] = [] private var trackDurations: [Double] = []
private var trackPlayerItems: [AVPlayerItem] = [] private var trackPlayerItems: [AVPlayerItem] = []
@@ -28,6 +39,7 @@ final class PlayerEngine {
private var timeObserver: Any? private var timeObserver: Any?
private var endObservers: [NSObjectProtocol] = [] private var endObservers: [NSObjectProtocol] = []
private var isSeeking: Bool = false private var isSeeking: Bool = false
private var sleepTickTask: Task<Void, Never>?
var itemId: String? var itemId: String?
@@ -127,12 +139,16 @@ final class PlayerEngine {
player?.play() player?.play()
player?.rate = rate player?.rate = rate
isPlaying = true isPlaying = true
if case .minutes = sleepTimer, sleepRemainingSeconds > 0, sleepTickTask == nil {
startSleepTickTask()
}
updateNowPlayingInfo() updateNowPlayingInfo()
} }
func pause() { func pause() {
player?.pause() player?.pause()
isPlaying = false isPlaying = false
cancelSleepTickTask()
updateNowPlayingInfo() updateNowPlayingInfo()
} }
@@ -143,6 +159,9 @@ final class PlayerEngine {
func setRate(_ newRate: Float) { func setRate(_ newRate: Float) {
rate = newRate rate = newRate
if isPlaying { player?.rate = newRate } if isPlaying { player?.rate = newRate }
if case .endOfBook = sleepTimer {
sleepRemainingSeconds = wallclockRemainingUntilEndOfBook()
}
updateNowPlayingInfo() updateNowPlayingInfo()
} }
@@ -221,9 +240,13 @@ final class PlayerEngine {
let wasPlaying = isPlaying let wasPlaying = isPlaying
isPlaying = player.timeControlStatus == .playing isPlaying = player.timeControlStatus == .playing
if wasPlaying != isPlaying { updateNowPlayingInfo() } if wasPlaying != isPlaying { updateNowPlayingInfo() }
updateEndOfBookSleep()
} }
func teardown() { func teardown() {
cancelSleepTickTask()
sleepTimer = .off
sleepRemainingSeconds = 0
if let token = timeObserver { player?.removeTimeObserver(token) } if let token = timeObserver { player?.removeTimeObserver(token) }
timeObserver = nil timeObserver = nil
for obs in endObservers { NotificationCenter.default.removeObserver(obs) } for obs in endObservers { NotificationCenter.default.removeObserver(obs) }
@@ -248,6 +271,57 @@ final class PlayerEngine {
clearNowPlayingInfo() clearNowPlayingInfo()
} }
// MARK: - Sleep timer
func setSleepTimer(_ mode: SleepTimerMode) {
cancelSleepTickTask()
sleepTimer = mode
switch mode {
case .off:
sleepRemainingSeconds = 0
case .minutes(let m):
sleepRemainingSeconds = Double(m * 60)
if isPlaying { startSleepTickTask() }
case .endOfBook:
sleepRemainingSeconds = wallclockRemainingUntilEndOfBook()
}
}
private func startSleepTickTask() {
cancelSleepTickTask()
sleepTickTask = Task { @MainActor [weak self] in
while !Task.isCancelled {
try? await Task.sleep(nanoseconds: 500_000_000)
guard !Task.isCancelled, let self else { return }
self.sleepRemainingSeconds = max(0, self.sleepRemainingSeconds - 0.5)
if self.sleepRemainingSeconds <= 0 {
self.sleepTimer = .off
self.pause()
return
}
}
}
}
private func cancelSleepTickTask() {
sleepTickTask?.cancel()
sleepTickTask = nil
}
private func updateEndOfBookSleep() {
guard case .endOfBook = sleepTimer else { return }
sleepRemainingSeconds = wallclockRemainingUntilEndOfBook()
if sleepRemainingSeconds <= 0 {
sleepTimer = .off
}
}
private func wallclockRemainingUntilEndOfBook() -> Double {
let playback = max(0, totalDuration - absoluteCurrentTime)
let r = max(0.1, Double(rate))
return playback / r
}
// MARK: - Now-playing / remote commands // MARK: - Now-playing / remote commands
private func configureRemoteCommandsIfNeeded() { private func configureRemoteCommandsIfNeeded() {

View File

@@ -5,9 +5,22 @@ struct LibraryGridView: View {
var onRefresh: (() async -> Void)? = nil var onRefresh: (() async -> Void)? = nil
var onSelect: (LibraryItem) -> Void var onSelect: (LibraryItem) -> Void
@AppStorage("libraryCoverSize") private var coverSize: Double = Self.defaultCoverSize
var body: some View { var body: some View {
#if os(macOS)
VStack(spacing: 0) {
zoomBar
gridContent
}
#else
gridContent
#endif
}
private var gridContent: some View {
ScrollView { ScrollView {
LazyVGrid(columns: gridColumns, spacing: 8) { LazyVGrid(columns: gridColumns, spacing: gridSpacing) {
ForEach(items) { item in ForEach(items) { item in
LibraryItemCell(item: item) LibraryItemCell(item: item)
.onTapGesture { onSelect(item) } .onTapGesture { onSelect(item) }
@@ -25,14 +38,59 @@ struct LibraryGridView: View {
#endif #endif
} }
#if os(macOS)
private var zoomBar: some View {
HStack(spacing: 8) {
Spacer()
Image(systemName: "rectangle.grid.3x2")
.font(.caption2)
.foregroundStyle(.secondary)
Slider(value: $coverSize, in: Self.minCoverSize...Self.maxCoverSize)
.frame(maxWidth: 220)
Image(systemName: "square")
.font(.caption2)
.foregroundStyle(.secondary)
}
.padding(.horizontal, 12)
.padding(.vertical, 4)
}
#endif
private var gridColumns: [GridItem] { private var gridColumns: [GridItem] {
[GridItem(.adaptive(minimum: CGFloat(coverSize)), spacing: gridSpacing)]
}
private var gridSpacing: CGFloat {
#if os(iOS) #if os(iOS)
// 3 equal columns compact spacing for full height utilization return 8
[GridItem(.flexible(), spacing: 8),
GridItem(.flexible(), spacing: 8),
GridItem(.flexible())]
#else #else
[GridItem(.adaptive(minimum: 180), spacing: 20)] return 20
#endif
}
// MARK: - Cover-Größen-Defaults (plattformabhängig)
private static var defaultCoverSize: Double {
#if os(iOS)
return 110
#else
return 180
#endif
}
private static var minCoverSize: Double {
#if os(iOS)
return 80
#else
return 120
#endif
}
private static var maxCoverSize: Double {
#if os(iOS)
return 200
#else
return 320
#endif #endif
} }
} }

View File

@@ -45,17 +45,44 @@ struct LibraryItemCell: View {
#if os(iOS) #if os(iOS)
private var iOSCover: some View { private var iOSCover: some View {
Color(.systemGray6) // neutral bg for PNG transparent areas coverContainer(background: AnyShapeStyle(Color(.systemGray6)))
.frame(maxWidth: .infinity) // explicitly fill the column width }
#endif
#if os(macOS)
private var macosCover: some View {
coverContainer(background: AnyShapeStyle(.quaternary))
}
#endif
/// Quadratischer Cover-Container: füllt die Spaltenbreite, behält 1:1-Aspektverhältnis
/// und zeigt das Bild **ohne Verzerrung** (`.scaledToFit`). Nicht-quadratische Cover
/// bekommen Letterbox-/Pillarbox-Ränder im neutralen Hintergrund.
private func coverContainer(background: AnyShapeStyle) -> some View {
Rectangle()
.fill(background)
.frame(maxWidth: .infinity)
.aspectRatio(1, contentMode: .fit) .aspectRatio(1, contentMode: .fit)
.overlay { .overlay {
if let url = app.client.coverURL(itemId: item.id) { if let url = app.client.coverURL(itemId: item.id) {
AsyncImage(url: url) { image in AsyncImage(url: url) { phase in
image.resizable().scaledToFill() switch phase {
} placeholder: { case .success(let img):
ProgressView().tint(.accentColor) img.resizable().scaledToFit()
case .empty:
ProgressView()
#if os(macOS)
.controlSize(.small)
#else
.tint(.accentColor)
#endif
case .failure:
Image(systemName: "book.closed")
.foregroundStyle(.secondary)
@unknown default:
EmptyView()
}
} }
.clipped() // clip image overflow before rounding
} else { } else {
Image(systemName: "book.closed") Image(systemName: "book.closed")
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
@@ -63,34 +90,6 @@ struct LibraryItemCell: View {
} }
.clipShape(RoundedRectangle(cornerRadius: 8)) .clipShape(RoundedRectangle(cornerRadius: 8))
} }
#endif
#if os(macOS)
private var macosCover: some View {
Group {
if let url = app.client.coverURL(itemId: item.id) {
AsyncImage(url: url) { phase in
switch phase {
case .empty:
Rectangle().fill(.quaternary)
.overlay(ProgressView().controlSize(.small))
case .success(let img):
img.resizable().aspectRatio(contentMode: .fill)
case .failure:
Rectangle().fill(.quaternary)
.overlay(Image(systemName: "book.closed").foregroundStyle(.secondary))
@unknown default:
Rectangle().fill(.quaternary)
}
}
} else {
Rectangle().fill(.quaternary)
}
}
.frame(width: 180, height: 180)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
#endif
// MARK: - Download badge // MARK: - Download badge

View File

@@ -62,7 +62,7 @@ struct PlayerBar: View {
} }
scrubber scrubber
HStack(spacing: 24) { HStack(spacing: 20) {
Button { app.skip(by: -Double(skipSeconds)) } label: { Button { app.skip(by: -Double(skipSeconds)) } label: {
Image(systemName: skipBackImage).font(.system(size: 22)) Image(systemName: skipBackImage).font(.system(size: 22))
} }
@@ -77,6 +77,8 @@ struct PlayerBar: View {
Spacer() Spacer()
sleepMenu
rateMenu rateMenu
Button { Button {
@@ -113,6 +115,8 @@ struct PlayerBar: View {
rateMenu rateMenu
sleepMenu
Spacer(minLength: 0) Spacer(minLength: 0)
statusIndicator statusIndicator
@@ -218,9 +222,73 @@ struct PlayerBar: View {
.font(.caption2.monospacedDigit()) .font(.caption2.monospacedDigit())
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
if app.player.sleepTimer != .off {
HStack(spacing: 4) {
Image(systemName: "moon.zzz.fill")
.font(.caption2)
Text("\(formatTime(app.player.sleepRemainingSeconds)) · endet \(formatWallTime(sleepEndsAt))")
.font(.caption2.monospacedDigit())
}
.foregroundStyle(.tint)
.frame(maxWidth: .infinity, alignment: .center)
}
} }
} }
private var sleepMenu: some View {
Menu {
sleepOption(title: "Aus", mode: .off)
Divider()
sleepOption(title: "10 Minuten", mode: .minutes(10))
sleepOption(title: "20 Minuten", mode: .minutes(20))
sleepOption(title: "30 Minuten", mode: .minutes(30))
sleepOption(title: "1 Stunde", mode: .minutes(60))
sleepOption(title: endOfPlaybackLabel, mode: .endOfBook)
} label: {
Image(systemName: app.player.sleepTimer == .off ? "moon.zzz" : "moon.zzz.fill")
#if os(iOS)
.font(.system(size: 22))
#else
.font(.system(size: 16))
#endif
.foregroundStyle(app.player.sleepTimer == .off ? Color.secondary : Color.accentColor)
}
#if os(macOS)
.menuStyle(.borderlessButton)
.fixedSize()
#endif
.menuIndicator(.hidden)
.help("Sleep-Timer")
.disabled(!app.player.isReady)
}
@ViewBuilder
private func sleepOption(title: String, mode: SleepTimerMode) -> some View {
Button {
app.player.setSleepTimer(mode)
} label: {
if app.player.sleepTimer == mode {
Label(title, systemImage: "checkmark")
} else {
Text(title)
}
}
}
private var sleepEndsAt: Date {
Date().addingTimeInterval(app.player.sleepRemainingSeconds)
}
private var endOfPlaybackLabel: String {
app.currentItem?.isPodcast == true
? "Bis Ende der Folge"
: "Bis Ende des Hörbuchs"
}
private func formatWallTime(_ date: Date) -> String {
date.formatted(.dateTime.hour().minute())
}
private var rateMenu: some View { private var rateMenu: some View {
Menu { Menu {
ForEach([0.75, 1.0, 1.25, 1.5, 1.75, 2.0], id: \.self) { r in ForEach([0.75, 1.0, 1.25, 1.5, 1.75, 2.0], id: \.self) { r in

View File

@@ -40,11 +40,18 @@
<key>UIColorName</key> <key>UIColorName</key>
<string>LaunchBackground</string> <string>LaunchBackground</string>
</dict> </dict>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations~iphone</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>25F71</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>ABS Client</string>
<key>CFBundleExecutable</key>
<string>ABS Client</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>com.local.ABS-Client</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>ABS Client</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>25F70</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>26.5</string>
<key>DTSDKBuild</key>
<string>25F70</string>
<key>DTSDKName</key>
<string>macosx26.5</string>
<key>DTXcode</key>
<string>2650</string>
<key>DTXcodeBuild</key>
<string>17F42</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.books</string>
<key>LSMinimumSystemVersion</key>
<string>26.0</string>
<key>NSAccentColorName</key>
<string>AccentColor</string>
</dict>
</plist>

Binary file not shown.

View File

@@ -0,0 +1 @@
APPL????

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/AppIcon.icns</key>
<data>
PIslMkIS+dR91jAoGiXhG5ZQxNw=
</data>
<key>Resources/Assets.car</key>
<data>
q6mmhkyHvvRXVcw9rIIm/MbqTqI=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/AppIcon.icns</key>
<dict>
<key>hash2</key>
<data>
UmgGZpb6aN5Xnz7XswrsNpMxnVcQINgSUpIhHWS1GBM=
</data>
</dict>
<key>Resources/Assets.car</key>
<dict>
<key>hash2</key>
<data>
lfMPkDEQ0MA2q3+uXr1255juM39IkgqKOiK8Kede+X0=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

Binary file not shown.

View File

@@ -1,9 +1,2 @@
create-dmg \ create-dmg --volname "ABS Client" --window-size 600 400 --icon-size 100 --icon "ABS Client.app" 150 200 --app-drop-link 450 200 ~/ABS-Client/Exports/Mac/ABS-Client.dmg 'ABS Client.app'
--volname "ABS-Client" \
--window-size 600 400 \
--icon-size 100 \
--icon "ABS-Client.app" 150 200 \
--app-drop-link 450 200 \
~/ABS-Client/Exports/Mac/ABS-Client.dmg \
ABS Client.app