mirror of
https://github.com/cranci1/Sora.git
synced 2026-01-11 20:10:24 +00:00
Merge branch 'dev'
This commit is contained in:
commit
8c09010a9a
22 changed files with 813 additions and 266 deletions
435
Sora/Localizable.xcstrings
Normal file
435
Sora/Localizable.xcstrings
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"" : {
|
||||
|
||||
},
|
||||
"%lld" : {
|
||||
|
||||
},
|
||||
"%lld Episodes" : {
|
||||
|
||||
},
|
||||
"%lld of %lld" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "%1$lld of %2$lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld-%lld" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "%1$lld-%2$lld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld%%" : {
|
||||
|
||||
},
|
||||
"%lld%% seen" : {
|
||||
|
||||
},
|
||||
"•" : {
|
||||
|
||||
},
|
||||
"About" : {
|
||||
|
||||
},
|
||||
"Actively downloading media can be tracked from here." : {
|
||||
|
||||
},
|
||||
"Add Module" : {
|
||||
|
||||
},
|
||||
"AKA Sulfur" : {
|
||||
|
||||
},
|
||||
"All Bookmarks" : {
|
||||
|
||||
},
|
||||
"All Prev" : {
|
||||
|
||||
},
|
||||
"All Watching" : {
|
||||
|
||||
},
|
||||
"AniList ID" : {
|
||||
|
||||
},
|
||||
"AniList Match" : {
|
||||
|
||||
},
|
||||
"AniList.co" : {
|
||||
|
||||
},
|
||||
"App Data" : {
|
||||
|
||||
},
|
||||
"Are you sure you want to delete '%@'?" : {
|
||||
|
||||
},
|
||||
"Are you sure you want to delete all %lld episodes in '%@'?" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Are you sure you want to delete all %1$lld episodes in '%2$@'?"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Are you sure you want to delete all downloaded assets? You can choose to clear only the library while preserving the downloaded files for future use." : {
|
||||
|
||||
},
|
||||
"Are you sure you want to erase all app data? This action cannot be undone." : {
|
||||
|
||||
},
|
||||
"Are you sure you want to remove all files in the Documents folder? This will remove all modules." : {
|
||||
|
||||
},
|
||||
"Author" : {
|
||||
|
||||
},
|
||||
"Bookmark items for an easier access later." : {
|
||||
|
||||
},
|
||||
"Bookmarks" : {
|
||||
|
||||
},
|
||||
"Cancel" : {
|
||||
|
||||
},
|
||||
"Check out some community modules here!" : {
|
||||
|
||||
},
|
||||
"Clear" : {
|
||||
|
||||
},
|
||||
"Clear All Caches" : {
|
||||
|
||||
},
|
||||
"Clear All Downloads" : {
|
||||
|
||||
},
|
||||
"Clear Library Only" : {
|
||||
|
||||
},
|
||||
"Clear Logs" : {
|
||||
|
||||
},
|
||||
"Click the plus button to add a module!" : {
|
||||
|
||||
},
|
||||
"Continue Watching" : {
|
||||
|
||||
},
|
||||
"Copy to Clipboard" : {
|
||||
|
||||
},
|
||||
"Copy URL" : {
|
||||
|
||||
},
|
||||
"cranci1" : {
|
||||
|
||||
},
|
||||
"Current Cache Size" : {
|
||||
|
||||
},
|
||||
"DATA/LOGS" : {
|
||||
|
||||
},
|
||||
"Delete" : {
|
||||
|
||||
},
|
||||
"Delete All" : {
|
||||
|
||||
},
|
||||
"Delete All Downloads" : {
|
||||
|
||||
},
|
||||
"Delete All Episodes" : {
|
||||
|
||||
},
|
||||
"Delete Download" : {
|
||||
|
||||
},
|
||||
"Delete Episode" : {
|
||||
|
||||
},
|
||||
"Download" : {
|
||||
|
||||
},
|
||||
"Download Episode" : {
|
||||
|
||||
},
|
||||
"Downloads" : {
|
||||
|
||||
},
|
||||
"Enter the AniList ID for this media" : {
|
||||
|
||||
},
|
||||
"Episode %lld" : {
|
||||
|
||||
},
|
||||
"Episodes" : {
|
||||
|
||||
},
|
||||
"Episodes might not be available yet or there could be an issue with the source." : {
|
||||
|
||||
},
|
||||
"Erase" : {
|
||||
|
||||
},
|
||||
"Erase App Data" : {
|
||||
|
||||
},
|
||||
"Error" : {
|
||||
|
||||
},
|
||||
"Failed to load contributors" : {
|
||||
|
||||
},
|
||||
"Files Downloaded" : {
|
||||
|
||||
},
|
||||
"General" : {
|
||||
|
||||
},
|
||||
"INFOS" : {
|
||||
|
||||
},
|
||||
"LESS" : {
|
||||
|
||||
},
|
||||
"Library" : {
|
||||
|
||||
},
|
||||
"Loading Episode %lld..." : {
|
||||
|
||||
},
|
||||
"Loading logs..." : {
|
||||
|
||||
},
|
||||
"Loading module information..." : {
|
||||
|
||||
},
|
||||
"Loading Stream" : {
|
||||
|
||||
},
|
||||
"Log Debug Info" : {
|
||||
|
||||
},
|
||||
"Log Filters" : {
|
||||
|
||||
},
|
||||
"Log In with AniList" : {
|
||||
|
||||
},
|
||||
"Log In with Trakt" : {
|
||||
|
||||
},
|
||||
"Log Out from AniList" : {
|
||||
|
||||
},
|
||||
"Log Out from Trakt" : {
|
||||
|
||||
},
|
||||
"Logged in as " : {
|
||||
|
||||
},
|
||||
"Logs" : {
|
||||
|
||||
},
|
||||
"MAIN" : {
|
||||
|
||||
},
|
||||
"Mark All Previous Watched" : {
|
||||
|
||||
},
|
||||
"Mark as Watched" : {
|
||||
|
||||
},
|
||||
"Match with AniList" : {
|
||||
|
||||
},
|
||||
"Matched with: %@" : {
|
||||
|
||||
},
|
||||
"Max Concurrent Downloads" : {
|
||||
|
||||
},
|
||||
"me frfr" : {
|
||||
|
||||
},
|
||||
"Modules" : {
|
||||
|
||||
},
|
||||
"MORE" : {
|
||||
|
||||
},
|
||||
"No Active Downloads" : {
|
||||
|
||||
},
|
||||
"No Data Available" : {
|
||||
|
||||
},
|
||||
"No Downloads" : {
|
||||
|
||||
},
|
||||
"No episodes available" : {
|
||||
|
||||
},
|
||||
"No Episodes Available" : {
|
||||
|
||||
},
|
||||
"No items to continue watching." : {
|
||||
|
||||
},
|
||||
"No matches found" : {
|
||||
|
||||
},
|
||||
"No Module Selected" : {
|
||||
|
||||
},
|
||||
"No Modules" : {
|
||||
|
||||
},
|
||||
"No Results Found" : {
|
||||
|
||||
},
|
||||
"OK" : {
|
||||
|
||||
},
|
||||
"Open Community Library" : {
|
||||
|
||||
},
|
||||
"Open in AniList" : {
|
||||
|
||||
},
|
||||
"Play" : {
|
||||
|
||||
},
|
||||
"Player" : {
|
||||
|
||||
},
|
||||
"Please select a module from settings" : {
|
||||
|
||||
},
|
||||
"Queued" : {
|
||||
|
||||
},
|
||||
"Recently watched content will appear here." : {
|
||||
|
||||
},
|
||||
"Refresh Storage Info" : {
|
||||
|
||||
},
|
||||
"Remove" : {
|
||||
|
||||
},
|
||||
"Remove Documents" : {
|
||||
|
||||
},
|
||||
"Remove from Bookmarks" : {
|
||||
|
||||
},
|
||||
"Remove Item" : {
|
||||
|
||||
},
|
||||
"Reset" : {
|
||||
|
||||
},
|
||||
"Reset AniList ID" : {
|
||||
|
||||
},
|
||||
"Reset Progress" : {
|
||||
|
||||
},
|
||||
"Running Sora %@ - cranci1" : {
|
||||
|
||||
},
|
||||
"Save" : {
|
||||
|
||||
},
|
||||
"Search" : {
|
||||
|
||||
},
|
||||
"Search downloads" : {
|
||||
|
||||
},
|
||||
"Search for something..." : {
|
||||
|
||||
},
|
||||
"Search..." : {
|
||||
|
||||
},
|
||||
"Season %lld" : {
|
||||
|
||||
},
|
||||
"Select Module" : {
|
||||
|
||||
},
|
||||
"Set Custom AniList ID" : {
|
||||
|
||||
},
|
||||
"Settings" : {
|
||||
|
||||
},
|
||||
"Show More (%lld more characters)" : {
|
||||
|
||||
},
|
||||
"Sora" : {
|
||||
|
||||
},
|
||||
"Sort" : {
|
||||
|
||||
},
|
||||
"Storage Used" : {
|
||||
|
||||
},
|
||||
"Tap a title to override the current match." : {
|
||||
|
||||
},
|
||||
"The module provided only a single episode, this is most likely a movie, so we decided to make separate screens for these cases." : {
|
||||
|
||||
},
|
||||
"Trackers" : {
|
||||
|
||||
},
|
||||
"Trakt.tv" : {
|
||||
|
||||
},
|
||||
"Try different keywords" : {
|
||||
|
||||
},
|
||||
"Use TMDB Poster Image" : {
|
||||
|
||||
},
|
||||
"v%@" : {
|
||||
|
||||
},
|
||||
"View All" : {
|
||||
|
||||
},
|
||||
"Watched" : {
|
||||
|
||||
},
|
||||
"Why am I not seeing any episodes?" : {
|
||||
|
||||
},
|
||||
"You have no items saved." : {
|
||||
|
||||
},
|
||||
"Your downloaded episodes will appear here" : {
|
||||
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ extension JSContext {
|
|||
}
|
||||
|
||||
func setupFetchV2() {
|
||||
let fetchV2NativeFunction: @convention(block) (String, [String: String]?, String?, String?, ObjCBool, JSValue, JSValue) -> Void = { urlString, headers, method, body, redirect, resolve, reject in
|
||||
let fetchV2NativeFunction: @convention(block) (String, [String: String]?, String?, String?, ObjCBool, String?, JSValue, JSValue) -> Void = { urlString, headers, method, body, redirect, encoding, resolve, reject in
|
||||
guard let url = URL(string: urlString) else {
|
||||
Logger.shared.log("Invalid URL", type: "Error")
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -91,7 +91,33 @@ extension JSContext {
|
|||
var request = URLRequest(url: url)
|
||||
request.httpMethod = httpMethod
|
||||
|
||||
Logger.shared.log("FetchV2 Request: URL=\(url), Method=\(httpMethod), Body=\(body ?? "nil")", type: "Debug")
|
||||
Logger.shared.log("FetchV2 Request: URL=\(url), Method=\(httpMethod), Body=\(body ?? "nil"), Encoding=\(encoding ?? "utf-8")", type: "Debug")
|
||||
|
||||
func getEncoding(from encodingString: String?) -> String.Encoding {
|
||||
guard let encodingString = encodingString?.lowercased() else {
|
||||
return .utf8
|
||||
}
|
||||
|
||||
switch encodingString {
|
||||
case "utf-8", "utf8":
|
||||
return .utf8
|
||||
case "windows-1251", "cp1251":
|
||||
return .windowsCP1251
|
||||
case "windows-1252", "cp1252":
|
||||
return .windowsCP1252
|
||||
case "iso-8859-1", "latin1":
|
||||
return .isoLatin1
|
||||
case "ascii":
|
||||
return .ascii
|
||||
case "utf-16", "utf16":
|
||||
return .utf16
|
||||
default:
|
||||
Logger.shared.log("Unknown encoding '\(encodingString)', defaulting to UTF-8", type: "Warning")
|
||||
return .utf8
|
||||
}
|
||||
}
|
||||
|
||||
let textEncoding = getEncoding(from: encoding)
|
||||
|
||||
if httpMethod == "GET", let body = body, !body.isEmpty, body != "null", body != "undefined" {
|
||||
Logger.shared.log("GET request must not have a body", type: "Error")
|
||||
|
|
@ -164,12 +190,18 @@ extension JSContext {
|
|||
return
|
||||
}
|
||||
|
||||
if let text = String(data: data, encoding: .utf8) {
|
||||
if let text = String(data: data, encoding: textEncoding) {
|
||||
responseDict["body"] = text
|
||||
callResolve(responseDict)
|
||||
} else {
|
||||
Logger.shared.log("Unable to decode data to text", type: "Error")
|
||||
callResolve(responseDict)
|
||||
Logger.shared.log("Unable to decode data with encoding \(encoding ?? "utf-8"), trying UTF-8 fallback", type: "Warning")
|
||||
if let fallbackText = String(data: data, encoding: .utf8) {
|
||||
responseDict["body"] = fallbackText
|
||||
callResolve(responseDict)
|
||||
} else {
|
||||
Logger.shared.log("Unable to decode data to text with any encoding", type: "Error")
|
||||
callResolve(responseDict)
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
|
@ -184,18 +216,19 @@ extension JSContext {
|
|||
self.setObject(fetchV2NativeFunction, forKeyedSubscript: "fetchV2Native" as NSString)
|
||||
|
||||
let fetchv2Definition = """
|
||||
function fetchv2(url, headers = {}, method = "GET", body = null, redirect = true ) {
|
||||
function fetchv2(url, headers = {}, method = "GET", body = null, redirect = true, encoding ) {
|
||||
|
||||
|
||||
var processedBody = null;
|
||||
if(method != "GET")
|
||||
{
|
||||
// Ensure body is properly serialized
|
||||
processedBody = (body && (typeof body === 'object')) ? JSON.stringify(body) : (body || null)
|
||||
}
|
||||
|
||||
var finalEncoding = encoding || "utf-8";
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
fetchV2Native(url, headers, method, processedBody, redirect, function(rawText) {
|
||||
fetchV2Native(url, headers, method, processedBody, redirect, finalEncoding, function(rawText) {
|
||||
const responseObj = {
|
||||
headers: rawText.headers,
|
||||
status: rawText.status,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ struct ModuleAdditionSettingsView: View {
|
|||
VStack(spacing: 24) {
|
||||
if let metadata = moduleMetadata {
|
||||
VStack(spacing: 0) {
|
||||
LazyImage(source: URL(string: metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -72,7 +72,7 @@ struct ModuleAdditionSettingsView: View {
|
|||
.padding(.top, 6)
|
||||
|
||||
HStack(spacing: 10) {
|
||||
LazyImage(source: URL(string: metadata.author.icon)) { state in
|
||||
LazyImage(url: URL(string: metadata.author.icon)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
/*
|
||||
|
||||
struct DeviceScaleModifier: ViewModifier {
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
|
||||
|
|
@ -28,13 +28,14 @@ struct DeviceScaleModifier: ViewModifier {
|
|||
.position(x: geo.size.width / 2, y: geo.size.height / 2)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/*
|
||||
struct DeviceScaleModifier: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content // does nothing for now
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
extension View {
|
||||
|
|
|
|||
|
|
@ -741,7 +741,7 @@ struct EnhancedActiveDownloadCard: View {
|
|||
HStack(spacing: 16) {
|
||||
Group {
|
||||
if let imageURL = download.imageURL {
|
||||
LazyImage(source: imageURL) { state in
|
||||
LazyImage(url: imageURL) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -904,7 +904,7 @@ struct EnhancedDownloadGroupCard: View {
|
|||
HStack(spacing: 16) {
|
||||
Group {
|
||||
if let posterURL = group.posterURL {
|
||||
LazyImage(source: posterURL) { state in
|
||||
LazyImage(url: posterURL) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -1008,7 +1008,7 @@ struct EnhancedShowEpisodesView: View {
|
|||
HStack(alignment: .top, spacing: 20) {
|
||||
Group {
|
||||
if let posterURL = group.posterURL {
|
||||
LazyImage(source: posterURL) { state in
|
||||
LazyImage(url: posterURL) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -1200,7 +1200,7 @@ struct EnhancedEpisodeRow: View {
|
|||
HStack(spacing: 16) {
|
||||
Group {
|
||||
if let backdropURL = asset.metadata?.backdropURL ?? asset.metadata?.posterURL {
|
||||
LazyImage(source: backdropURL) { state in
|
||||
LazyImage(url: backdropURL) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ struct BookmarkCell: View {
|
|||
var body: some View {
|
||||
if let module = moduleManager.modules.first(where: { $0.id.uuidString == bookmark.moduleId }) {
|
||||
ZStack {
|
||||
LazyImage(source: URL(string: bookmark.imageUrl)) { state in
|
||||
LazyImage(url: URL(string: bookmark.imageUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -79,7 +79,7 @@ struct BookmarkCell: View {
|
|||
.fill(Color.black.opacity(0.5))
|
||||
.frame(width: 28, height: 28)
|
||||
.overlay(
|
||||
LazyImage(source: URL(string: module.metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: module.metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ struct FullWidthContinueWatchingCell: View {
|
|||
}) {
|
||||
GeometryReader { geometry in
|
||||
ZStack(alignment: .bottomLeading) {
|
||||
LazyImage(source: URL(string: item.imageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : item.imageUrl)) { state in
|
||||
LazyImage(url: URL(string: item.imageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : item.imageUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -268,7 +268,7 @@ struct FullWidthContinueWatchingCell: View {
|
|||
.fill(Color.black.opacity(0.5))
|
||||
.frame(width: 28, height: 28)
|
||||
.overlay(
|
||||
LazyImage(source: URL(string: item.module.metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: item.module.metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ struct ContinueWatchingCell: View {
|
|||
}
|
||||
}) {
|
||||
ZStack(alignment: .bottomLeading) {
|
||||
LazyImage(source: URL(string: item.imageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : item.imageUrl)) { state in
|
||||
LazyImage(url: URL(string: item.imageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : item.imageUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -357,7 +357,7 @@ struct ContinueWatchingCell: View {
|
|||
.fill(Color.black.opacity(0.5))
|
||||
.frame(width: 28, height: 28)
|
||||
.overlay(
|
||||
LazyImage(source: URL(string: item.module.metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: item.module.metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -542,7 +542,7 @@ struct BookmarkItemView: View {
|
|||
isDetailActive = true
|
||||
}) {
|
||||
ZStack {
|
||||
LazyImage(source: URL(string: item.imageUrl)) { state in
|
||||
LazyImage(url: URL(string: item.imageUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -563,7 +563,7 @@ struct BookmarkItemView: View {
|
|||
.fill(Color.black.opacity(0.5))
|
||||
.frame(width: 28, height: 28)
|
||||
.overlay(
|
||||
LazyImage(source: URL(string: module.metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: module.metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ struct AnilistMatchPopupView: View {
|
|||
HStack(spacing: 12) {
|
||||
if let cover = result["cover"] as? String,
|
||||
let url = URL(string: cover) {
|
||||
LazyImage(source: url) { state in
|
||||
LazyImage(url: url) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ struct EpisodeCell: View {
|
|||
@State private var swipeOffset: CGFloat = 0
|
||||
@State private var isShowingActions: Bool = false
|
||||
@State private var actionButtonWidth: CGFloat = 60
|
||||
@State private var dragState: DragState = .inactive
|
||||
|
||||
@State private var retryAttempts: Int = 0
|
||||
private let maxRetryAttempts: Int = 3
|
||||
|
|
@ -58,6 +59,39 @@ struct EpisodeCell: View {
|
|||
@Environment(\.colorScheme) private var colorScheme
|
||||
@AppStorage("selectedAppearance") private var selectedAppearance: Appearance = .system
|
||||
|
||||
enum DragState {
|
||||
case inactive
|
||||
case pressing
|
||||
case dragging(translation: CGSize)
|
||||
|
||||
var translation: CGSize {
|
||||
switch self {
|
||||
case .inactive, .pressing:
|
||||
return .zero
|
||||
case .dragging(let translation):
|
||||
return translation
|
||||
}
|
||||
}
|
||||
|
||||
var isActive: Bool {
|
||||
switch self {
|
||||
case .inactive:
|
||||
return false
|
||||
case .pressing, .dragging:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
var isDragging: Bool {
|
||||
switch self {
|
||||
case .dragging:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var downloadStatusString: String {
|
||||
switch downloadStatus {
|
||||
case .notDownloaded:
|
||||
|
|
@ -152,67 +186,26 @@ struct EpisodeCell: View {
|
|||
)
|
||||
)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 15))
|
||||
.offset(x: swipeOffset)
|
||||
.offset(x: swipeOffset + dragState.translation.width)
|
||||
.zIndex(1)
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: swipeOffset)
|
||||
.scaleEffect(dragState.isActive ? 0.98 : 1.0)
|
||||
.animation(.spring(response: 0.4, dampingFraction: 0.8), value: swipeOffset)
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.6), value: dragState.isActive)
|
||||
.contextMenu {
|
||||
contextMenuContent
|
||||
}
|
||||
.simultaneousGesture(
|
||||
DragGesture()
|
||||
DragGesture(coordinateSpace: .local)
|
||||
.onChanged { value in
|
||||
let horizontalTranslation = value.translation.width
|
||||
let verticalTranslation = value.translation.height
|
||||
|
||||
let isDefinitelyHorizontalSwipe = abs(horizontalTranslation) > 10 && abs(horizontalTranslation) > abs(verticalTranslation) * 1.5
|
||||
|
||||
if isShowingActions || isDefinitelyHorizontalSwipe {
|
||||
if horizontalTranslation < 0 {
|
||||
let maxSwipe = calculateMaxSwipeDistance()
|
||||
swipeOffset = max(horizontalTranslation, -maxSwipe)
|
||||
} else if isShowingActions {
|
||||
let maxSwipe = calculateMaxSwipeDistance()
|
||||
swipeOffset = max(horizontalTranslation - maxSwipe, -maxSwipe)
|
||||
}
|
||||
}
|
||||
handleDragChanged(value)
|
||||
}
|
||||
.onEnded { value in
|
||||
let horizontalTranslation = value.translation.width
|
||||
let verticalTranslation = value.translation.height
|
||||
|
||||
let wasHandlingGesture = abs(horizontalTranslation) > 10 && abs(horizontalTranslation) > abs(verticalTranslation) * 1.5
|
||||
|
||||
if isShowingActions || wasHandlingGesture {
|
||||
let maxSwipe = calculateMaxSwipeDistance()
|
||||
let threshold = maxSwipe * 0.2
|
||||
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
|
||||
if horizontalTranslation < -threshold && !isShowingActions {
|
||||
swipeOffset = -maxSwipe
|
||||
isShowingActions = true
|
||||
} else if horizontalTranslation > threshold && isShowingActions {
|
||||
swipeOffset = 0
|
||||
isShowingActions = false
|
||||
} else {
|
||||
swipeOffset = isShowingActions ? -maxSwipe : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
handleDragEnded(value)
|
||||
}
|
||||
)
|
||||
}
|
||||
.onTapGesture {
|
||||
if isShowingActions {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
|
||||
swipeOffset = 0
|
||||
isShowingActions = false
|
||||
}
|
||||
} else if isMultiSelectMode {
|
||||
onSelectionChanged?(!isSelected)
|
||||
} else {
|
||||
let imageUrl = episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl
|
||||
onTap(imageUrl)
|
||||
}
|
||||
handleTap()
|
||||
}
|
||||
.onAppear {
|
||||
updateProgress()
|
||||
|
|
@ -264,7 +257,7 @@ struct EpisodeCell: View {
|
|||
private var episodeThumbnail: some View {
|
||||
ZStack {
|
||||
if let url = URL(string: episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl) {
|
||||
LazyImage(source: url) { state in
|
||||
LazyImage(url: url) { state in
|
||||
if let image = state.imageContainer?.image {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
|
|
@ -975,13 +968,97 @@ struct EpisodeCell: View {
|
|||
.padding(.horizontal, 8)
|
||||
}
|
||||
|
||||
private func handleDragChanged(_ value: DragGesture.Value) {
|
||||
let translation = value.translation
|
||||
let velocity = value.velocity
|
||||
|
||||
let isHorizontalGesture = abs(translation.width) > abs(translation.height)
|
||||
let hasSignificantHorizontalMovement = abs(translation.width) > 10
|
||||
|
||||
if isHorizontalGesture && hasSignificantHorizontalMovement {
|
||||
dragState = .dragging(translation: .zero)
|
||||
|
||||
let proposedOffset = swipeOffset + translation.width
|
||||
let maxSwipe = calculateMaxSwipeDistance()
|
||||
|
||||
if translation.width < 0 {
|
||||
let newOffset = max(proposedOffset, -maxSwipe)
|
||||
if proposedOffset < -maxSwipe {
|
||||
let resistance = abs(proposedOffset + maxSwipe) * 0.15
|
||||
swipeOffset = -maxSwipe - resistance
|
||||
} else {
|
||||
swipeOffset = newOffset
|
||||
}
|
||||
} else if isShowingActions {
|
||||
swipeOffset = max(proposedOffset, -maxSwipe)
|
||||
}
|
||||
} else if !hasSignificantHorizontalMovement {
|
||||
dragState = .inactive
|
||||
}
|
||||
}
|
||||
|
||||
private func handleDragEnded(_ value: DragGesture.Value) {
|
||||
let translation = value.translation
|
||||
let velocity = value.velocity
|
||||
|
||||
dragState = .inactive
|
||||
|
||||
let isHorizontalGesture = abs(translation.width) > abs(translation.height)
|
||||
let hasSignificantHorizontalMovement = abs(translation.width) > 10
|
||||
|
||||
if isHorizontalGesture && hasSignificantHorizontalMovement {
|
||||
let maxSwipe = calculateMaxSwipeDistance()
|
||||
let threshold = maxSwipe * 0.3
|
||||
let velocityThreshold: CGFloat = 500
|
||||
|
||||
withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) {
|
||||
if translation.width < -threshold || velocity.width < -velocityThreshold {
|
||||
swipeOffset = -maxSwipe
|
||||
isShowingActions = true
|
||||
} else if translation.width > threshold || velocity.width > velocityThreshold {
|
||||
swipeOffset = 0
|
||||
isShowingActions = false
|
||||
} else {
|
||||
swipeOffset = isShowingActions ? -maxSwipe : 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) {
|
||||
swipeOffset = isShowingActions ? -calculateMaxSwipeDistance() : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleTap() {
|
||||
if isShowingActions {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
|
||||
swipeOffset = 0
|
||||
isShowingActions = false
|
||||
}
|
||||
} else if isMultiSelectMode {
|
||||
onSelectionChanged?(!isSelected)
|
||||
} else {
|
||||
let imageUrl = episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl
|
||||
onTap(imageUrl)
|
||||
}
|
||||
}
|
||||
|
||||
private func closeActionsIfNeeded() {
|
||||
if isShowingActions {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
|
||||
swipeOffset = 0
|
||||
isShowingActions = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func closeActionsAndPerform(action: @escaping () -> Void) {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
|
||||
withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) {
|
||||
swipeOffset = 0
|
||||
isShowingActions = false
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,38 +123,6 @@ struct MediaInfoView: View {
|
|||
.ignoresSafeArea(.container, edges: .top)
|
||||
.onAppear {
|
||||
buttonRefreshTrigger.toggle()
|
||||
|
||||
let savedID = UserDefaults.standard.integer(forKey: "custom_anilist_id_\(href)")
|
||||
if savedID != 0 { customAniListID = savedID }
|
||||
|
||||
if let savedPoster = UserDefaults.standard.string(forKey: "tmdbPosterURL_\(href)") {
|
||||
self.imageUrl = savedPoster
|
||||
}
|
||||
|
||||
if !hasFetched {
|
||||
DropManager.shared.showDrop(
|
||||
title: "Fetching Data",
|
||||
subtitle: "Please wait while fetching.",
|
||||
duration: 0.5,
|
||||
icon: UIImage(systemName: "arrow.triangle.2.circlepath")
|
||||
)
|
||||
fetchDetails()
|
||||
|
||||
if let savedID = UserDefaults.standard.object(forKey: "custom_anilist_id_\(href)") as? Int {
|
||||
customAniListID = savedID
|
||||
itemID = savedID
|
||||
Logger.shared.log("Using custom AniList ID: \(savedID)", type: "Debug")
|
||||
} else {
|
||||
fetchMetadataIDIfNeeded()
|
||||
}
|
||||
|
||||
hasFetched = true
|
||||
AnalyticsManager.shared.sendEvent(
|
||||
event: "MediaInfoView",
|
||||
additionalData: ["title": title]
|
||||
)
|
||||
}
|
||||
|
||||
tabBarController.hideTabBar()
|
||||
}
|
||||
.onChange(of: selectedRange) { newValue in
|
||||
|
|
@ -174,12 +142,8 @@ struct MediaInfoView: View {
|
|||
if let savedPoster = UserDefaults.standard.string(forKey: "tmdbPosterURL_\(href)") {
|
||||
imageUrl = savedPoster
|
||||
}
|
||||
DropManager.shared.showDrop(
|
||||
title: "Fetching Data",
|
||||
subtitle: "Please wait while fetching.",
|
||||
duration: 0.5,
|
||||
icon: UIImage(systemName: "arrow.triangle.2.circlepath")
|
||||
)
|
||||
|
||||
DropManager.shared.showDrop(title: "Fetching Data", subtitle: "Please wait while fetching.", duration: 0.5, icon: UIImage(systemName: "arrow.triangle.2.circlepath"))
|
||||
fetchDetails()
|
||||
|
||||
if savedCustomID != 0 {
|
||||
|
|
@ -240,7 +204,7 @@ struct MediaInfoView: View {
|
|||
private var mainScrollView: some View {
|
||||
ScrollView {
|
||||
ZStack(alignment: .top) {
|
||||
LazyImage(source: URL(string: imageUrl)) { state in
|
||||
LazyImage(url: URL(string: imageUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -255,6 +219,7 @@ struct MediaInfoView: View {
|
|||
.clipped()
|
||||
}
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Rectangle()
|
||||
.fill(Color.clear)
|
||||
|
|
@ -283,13 +248,11 @@ struct MediaInfoView: View {
|
|||
.shadow(color: (colorScheme == .dark ? Color.black : Color.white).opacity(1), radius: 10, x: 0, y: 10)
|
||||
)
|
||||
}
|
||||
.deviceScaled()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
UIScrollView.appearance().bounces = false
|
||||
}
|
||||
.ignoresSafeArea(.container, edges: .top)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
|
@ -704,7 +667,7 @@ struct MediaInfoView: View {
|
|||
|
||||
@ViewBuilder
|
||||
private var flatEpisodeList: some View {
|
||||
LazyVStack(spacing: 15) {
|
||||
VStack(spacing: 15) {
|
||||
ForEach(episodeLinks.indices.filter { selectedRange.contains($0) }, id: \.self) { i in
|
||||
let ep = episodeLinks[i]
|
||||
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(ep.href)")
|
||||
|
|
@ -751,7 +714,7 @@ struct MediaInfoView: View {
|
|||
private var seasonsEpisodeList: some View {
|
||||
let seasons = groupedEpisodes()
|
||||
if !seasons.isEmpty, selectedSeason < seasons.count {
|
||||
LazyVStack(spacing: 15) {
|
||||
VStack(spacing: 15) {
|
||||
ForEach(seasons[selectedSeason]) { ep in
|
||||
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(ep.href)")
|
||||
let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(ep.href)")
|
||||
|
|
@ -1328,12 +1291,16 @@ struct MediaInfoView: View {
|
|||
videoPlayerViewController.mediaTitle = title
|
||||
videoPlayerViewController.subtitles = subtitles ?? ""
|
||||
videoPlayerViewController.aniListID = itemID ?? 0
|
||||
videoPlayerViewController.modalPresentationStyle = .overFullScreen
|
||||
videoPlayerViewController.modalPresentationStyle = .fullScreen
|
||||
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
let rootVC = windowScene.windows.first?.rootViewController {
|
||||
findTopViewController.findViewController(rootVC).present(videoPlayerViewController, animated: true, completion: nil)
|
||||
} else {
|
||||
Logger.shared.log("Failed to find root view controller", type: "Error")
|
||||
DropManager.shared.showDrop(title: "Error", subtitle: "Failed to present player", duration: 2.0, icon: UIImage(systemName: "xmark.circle"))
|
||||
}
|
||||
|
||||
return
|
||||
default:
|
||||
break
|
||||
|
|
@ -1368,7 +1335,7 @@ struct MediaInfoView: View {
|
|||
episodeImageUrl: selectedEpisodeImage,
|
||||
headers: headers ?? nil
|
||||
)
|
||||
customMediaPlayer.modalPresentationStyle = .overFullScreen
|
||||
customMediaPlayer.modalPresentationStyle = .fullScreen
|
||||
Logger.shared.log("Opening custom media player with url: \(url)")
|
||||
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ struct SearchResultsGrid: View {
|
|||
ForEach(items) { item in
|
||||
NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: selectedModule)) {
|
||||
ZStack {
|
||||
LazyImage(source: URL(string: item.imageUrl)) { state in
|
||||
LazyImage(url: URL(string: item.imageUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -71,59 +71,71 @@ struct SearchView: View {
|
|||
return availableWidth / CGFloat(columnsCount)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("Search")
|
||||
.font(.largeTitle)
|
||||
.fontWeight(.bold)
|
||||
|
||||
Spacer()
|
||||
|
||||
ModuleSelectorMenu(
|
||||
selectedModule: selectedModule,
|
||||
moduleGroups: getModuleLanguageGroups(),
|
||||
modulesByLanguage: getModulesByLanguage(),
|
||||
selectedModuleId: selectedModuleId,
|
||||
onModuleSelected: { moduleId in
|
||||
selectedModuleId = moduleId
|
||||
}
|
||||
)
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.top, 20)
|
||||
private var mainContent: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("Search")
|
||||
.font(.largeTitle)
|
||||
.fontWeight(.bold)
|
||||
|
||||
ScrollView {
|
||||
SearchContent(
|
||||
selectedModule: selectedModule,
|
||||
searchQuery: searchQuery,
|
||||
searchHistory: searchHistory,
|
||||
searchItems: searchItems,
|
||||
isSearching: isSearching,
|
||||
hasNoResults: hasNoResults,
|
||||
columns: columns,
|
||||
columnsCount: columnsCount,
|
||||
cellWidth: cellWidth,
|
||||
onHistoryItemSelected: { query in
|
||||
searchQuery = query
|
||||
},
|
||||
onHistoryItemDeleted: { index in
|
||||
removeFromHistory(at: index)
|
||||
},
|
||||
onClearHistory: clearSearchHistory
|
||||
)
|
||||
}
|
||||
.scrollViewBottomPadding()
|
||||
.simultaneousGesture(
|
||||
DragGesture().onChanged { _ in
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
Spacer()
|
||||
|
||||
ModuleSelectorMenu(
|
||||
selectedModule: selectedModule,
|
||||
moduleGroups: getModuleLanguageGroups(),
|
||||
modulesByLanguage: getModulesByLanguage(),
|
||||
selectedModuleId: selectedModuleId,
|
||||
onModuleSelected: { moduleId in
|
||||
selectedModuleId = moduleId
|
||||
}
|
||||
)
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.top, 20)
|
||||
|
||||
ScrollView {
|
||||
SearchContent(
|
||||
selectedModule: selectedModule,
|
||||
searchQuery: searchQuery,
|
||||
searchHistory: searchHistory,
|
||||
searchItems: searchItems,
|
||||
isSearching: isSearching,
|
||||
hasNoResults: hasNoResults,
|
||||
columns: columns,
|
||||
columnsCount: columnsCount,
|
||||
cellWidth: cellWidth,
|
||||
onHistoryItemSelected: { query in
|
||||
searchQuery = query
|
||||
},
|
||||
onHistoryItemDeleted: { index in
|
||||
removeFromHistory(at: index)
|
||||
},
|
||||
onClearHistory: clearSearchHistory
|
||||
)
|
||||
}
|
||||
.scrollViewBottomPadding()
|
||||
.simultaneousGesture(
|
||||
DragGesture().onChanged { _ in
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if #available(iOS 16.0, *) {
|
||||
NavigationStack {
|
||||
mainContent
|
||||
}
|
||||
} else {
|
||||
NavigationView {
|
||||
mainContent
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
.onAppear {
|
||||
loadSearchHistory()
|
||||
if !searchQuery.isEmpty {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ struct ModuleSelectorMenu: View {
|
|||
onModuleSelected(module.id.uuidString)
|
||||
} label: {
|
||||
HStack {
|
||||
LazyImage(source: URL(string: module.metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: module.metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -56,7 +56,7 @@ struct ModuleSelectorMenu: View {
|
|||
Text(selectedModule.metadata.sourceName)
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
LazyImage(source: URL(string: selectedModule.metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: selectedModule.metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ struct SettingsViewAbout: View {
|
|||
VStack(spacing: 24) {
|
||||
SettingsSection(title: "App Info", footer: "Sora/Sulfur will always remain free with no ADs!") {
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
LazyImage(source: URL(string: "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/dev/Sora/Assets.xcassets/AppIcons/AppIcon_Default.appiconset/darkmode.png")) { state in
|
||||
LazyImage(url: URL(string: "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/dev/Sora/Assets.xcassets/AppIcons/AppIcon_Default.appiconset/darkmode.png")) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -100,7 +100,7 @@ struct SettingsViewAbout: View {
|
|||
}
|
||||
}) {
|
||||
HStack {
|
||||
LazyImage(source: URL(string: "https://avatars.githubusercontent.com/u/100066266?v=4")) { state in
|
||||
LazyImage(url: URL(string: "https://avatars.githubusercontent.com/u/100066266?v=4")) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -213,7 +213,7 @@ struct ContributorView: View {
|
|||
}
|
||||
}) {
|
||||
HStack {
|
||||
LazyImage(source: URL(string: contributor.avatarUrl)) { state in
|
||||
LazyImage(url: URL(string: contributor.avatarUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ fileprivate struct ModuleListItemView: View {
|
|||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
LazyImage(source: URL(string: module.metadata.iconUrl)) { state in
|
||||
LazyImage(url: URL(string: module.metadata.iconUrl)) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ struct SettingsViewTrackers: View {
|
|||
SettingsSection(title: "AniList") {
|
||||
VStack(spacing: 0) {
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
LazyImage(source: URL(string: "https://raw.githubusercontent.com/cranci1/Ryu/2f10226aa087154974a70c1ec78aa83a47daced9/Ryu/Assets.xcassets/Listing/Anilist.imageset/anilist.png")) { state in
|
||||
LazyImage(url: URL(string: "https://raw.githubusercontent.com/cranci1/Ryu/2f10226aa087154974a70c1ec78aa83a47daced9/Ryu/Assets.xcassets/Listing/Anilist.imageset/anilist.png")) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
@ -215,7 +215,7 @@ struct SettingsViewTrackers: View {
|
|||
SettingsSection(title: "Trakt") {
|
||||
VStack(spacing: 0) {
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
LazyImage(source: URL(string: "https://static-00.iconduck.com/assets.00/trakt-icon-2048x2048-2633ksxg.png")) { state in
|
||||
LazyImage(url: URL(string: "https://static-00.iconduck.com/assets.00/trakt-icon-2048x2048-2633ksxg.png")) { state in
|
||||
if let uiImage = state.imageContainer?.image {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@
|
|||
132AF1212D99951700A0140B /* JSController-Streams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1202D99951700A0140B /* JSController-Streams.swift */; };
|
||||
132AF1232D9995C300A0140B /* JSController-Details.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1222D9995C300A0140B /* JSController-Details.swift */; };
|
||||
132AF1252D9995F900A0140B /* JSController-Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1242D9995F900A0140B /* JSController-Search.swift */; };
|
||||
13367ECC2DF70698009CB33F /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 13367ECB2DF70698009CB33F /* Nuke */; };
|
||||
13367ECE2DF70698009CB33F /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 13367ECD2DF70698009CB33F /* NukeUI */; };
|
||||
13367ED02DF70819009CB33F /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 13367ECF2DF70819009CB33F /* Localizable.xcstrings */; };
|
||||
133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6D2D2BE2500075467E /* SoraApp.swift */; };
|
||||
133D7C702D2BE2500075467E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6F2D2BE2500075467E /* ContentView.swift */; };
|
||||
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 133D7C712D2BE2520075467E /* Assets.xcassets */; };
|
||||
|
|
@ -60,7 +63,6 @@
|
|||
1399FAD62D3AB3DB00E97C31 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1399FAD52D3AB3DB00E97C31 /* Logger.swift */; };
|
||||
13B77E202DA457AA00126FDF /* AniListPushUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13B77E1F2DA457AA00126FDF /* AniListPushUpdates.swift */; };
|
||||
13B7F4C12D58FFDD0045714A /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13B7F4C02D58FFDD0045714A /* Shimmer.swift */; };
|
||||
13BC689F2DF61327009A0651 /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 13BC689E2DF61327009A0651 /* NukeUI */; };
|
||||
13C0E5EA2D5F85EA00E7F619 /* ContinueWatchingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C0E5E92D5F85EA00E7F619 /* ContinueWatchingManager.swift */; };
|
||||
13C0E5EC2D5F85F800E7F619 /* ContinueWatchingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13C0E5EB2D5F85F800E7F619 /* ContinueWatchingItem.swift */; };
|
||||
13CBA0882D60F19C00EFE70A /* VTTSubtitlesLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CBA0872D60F19C00EFE70A /* VTTSubtitlesLoader.swift */; };
|
||||
|
|
@ -123,6 +125,7 @@
|
|||
132AF1202D99951700A0140B /* JSController-Streams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Streams.swift"; sourceTree = "<group>"; };
|
||||
132AF1222D9995C300A0140B /* JSController-Details.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Details.swift"; sourceTree = "<group>"; };
|
||||
132AF1242D9995F900A0140B /* JSController-Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Search.swift"; sourceTree = "<group>"; };
|
||||
13367ECF2DF70819009CB33F /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
133D7C6A2D2BE2500075467E /* Sulfur.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sulfur.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
133D7C6D2D2BE2500075467E /* SoraApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoraApp.swift; sourceTree = "<group>"; };
|
||||
133D7C6F2D2BE2500075467E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -191,9 +194,10 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13367ECC2DF70698009CB33F /* Nuke in Frameworks */,
|
||||
13637B902DE0ECD200BDA2FC /* Drops in Frameworks */,
|
||||
13637B932DE0ECDB00BDA2FC /* MarqueeLabel in Frameworks */,
|
||||
13BC689F2DF61327009A0651 /* NukeUI in Frameworks */,
|
||||
13367ECE2DF70698009CB33F /* NukeUI in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -320,6 +324,7 @@
|
|||
133D7C6F2D2BE2500075467E /* ContentView.swift */,
|
||||
133D7C712D2BE2520075467E /* Assets.xcassets */,
|
||||
133D7C732D2BE2520075467E /* Preview Content */,
|
||||
13367ECF2DF70819009CB33F /* Localizable.xcstrings */,
|
||||
);
|
||||
path = Sora;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -616,7 +621,8 @@
|
|||
packageProductDependencies = (
|
||||
13637B8F2DE0ECD200BDA2FC /* Drops */,
|
||||
13637B922DE0ECDB00BDA2FC /* MarqueeLabel */,
|
||||
13BC689E2DF61327009A0651 /* NukeUI */,
|
||||
13367ECB2DF70698009CB33F /* Nuke */,
|
||||
13367ECD2DF70698009CB33F /* NukeUI */,
|
||||
);
|
||||
productName = Sora;
|
||||
productReference = 133D7C6A2D2BE2500075467E /* Sulfur.app */;
|
||||
|
|
@ -648,7 +654,7 @@
|
|||
packageReferences = (
|
||||
13637B8E2DE0ECD200BDA2FC /* XCRemoteSwiftPackageReference "Drops" */,
|
||||
13637B912DE0ECDB00BDA2FC /* XCRemoteSwiftPackageReference "MarqueeLabel" */,
|
||||
13BC689D2DF61327009A0651 /* XCRemoteSwiftPackageReference "NukeUI" */,
|
||||
13367ECA2DF70698009CB33F /* XCRemoteSwiftPackageReference "Nuke" */,
|
||||
);
|
||||
productRefGroup = 133D7C6B2D2BE2500075467E /* Products */;
|
||||
projectDirPath = "";
|
||||
|
|
@ -666,6 +672,7 @@
|
|||
files = (
|
||||
133D7C752D2BE2520075467E /* Preview Assets.xcassets in Resources */,
|
||||
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */,
|
||||
13367ED02DF70819009CB33F /* Localizable.xcstrings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -820,11 +827,18 @@
|
|||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
VALID_ARCHS = "arm64 x86_64";
|
||||
"VALID_ARCHS[sdk=iphoneos*]" = "arm64 arm64e";
|
||||
"VALID_ARCHS[sdk=iphonesimulator*]" = "arm64 x86_64";
|
||||
"VALID_ARCHS[sdk=macosx*]" = "arm64 x86_64";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -877,12 +891,19 @@
|
|||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
|
||||
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VALID_ARCHS = "arm64 x86_64";
|
||||
"VALID_ARCHS[sdk=iphoneos*]" = "arm64 arm64e";
|
||||
"VALID_ARCHS[sdk=iphonesimulator*]" = "arm64 x86_64";
|
||||
"VALID_ARCHS[sdk=macosx*]" = "arm64 x86_64";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
@ -907,24 +928,24 @@
|
|||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.cranci.sulfur;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -949,24 +970,24 @@
|
|||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.cranci.sulfur;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2,6";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
@ -994,6 +1015,14 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
13367ECA2DF70698009CB33F /* XCRemoteSwiftPackageReference "Nuke" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/kean/Nuke.git";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
13637B8E2DE0ECD200BDA2FC /* XCRemoteSwiftPackageReference "Drops" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/omaralbeik/Drops.git";
|
||||
|
|
@ -1006,21 +1035,23 @@
|
|||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/cbpowell/MarqueeLabel";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 4.2.1;
|
||||
};
|
||||
};
|
||||
13BC689D2DF61327009A0651 /* XCRemoteSwiftPackageReference "NukeUI" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/kean/NukeUI";
|
||||
requirement = {
|
||||
branch = main;
|
||||
branch = master;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
13367ECB2DF70698009CB33F /* Nuke */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 13367ECA2DF70698009CB33F /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = Nuke;
|
||||
};
|
||||
13367ECD2DF70698009CB33F /* NukeUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 13367ECA2DF70698009CB33F /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = NukeUI;
|
||||
};
|
||||
13637B8F2DE0ECD200BDA2FC /* Drops */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 13637B8E2DE0ECD200BDA2FC /* XCRemoteSwiftPackageReference "Drops" */;
|
||||
|
|
@ -1031,11 +1062,6 @@
|
|||
package = 13637B912DE0ECDB00BDA2FC /* XCRemoteSwiftPackageReference "MarqueeLabel" */;
|
||||
productName = MarqueeLabel;
|
||||
};
|
||||
13BC689E2DF61327009A0651 /* NukeUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 13BC689D2DF61327009A0651 /* XCRemoteSwiftPackageReference "NukeUI" */;
|
||||
productName = NukeUI;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 133D7C622D2BE2500075467E /* Project object */;
|
||||
|
|
|
|||
|
|
@ -1,52 +1,32 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Drops",
|
||||
"repositoryURL": "https://github.com/omaralbeik/Drops.git",
|
||||
"state": {
|
||||
"branch": "main",
|
||||
"revision": "5824681795286c36bdc4a493081a63e64e2a064e",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Gifu",
|
||||
"repositoryURL": "https://github.com/kaishin/Gifu",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "82da0086dea14ca9afc9801234ad8dc4cd9e2738",
|
||||
"version": "3.4.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "MarqueeLabel",
|
||||
"repositoryURL": "https://github.com/cbpowell/MarqueeLabel",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "cffb6938940d3242882e6a2f9170b7890a4729ea",
|
||||
"version": "4.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Nuke",
|
||||
"repositoryURL": "https://github.com/kean/Nuke.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
|
||||
"version": "10.11.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "NukeUI",
|
||||
"repositoryURL": "https://github.com/kean/NukeUI",
|
||||
"state": {
|
||||
"branch": "main",
|
||||
"revision": "7338ed8ea76de18598bfafbca0cbdc74300a6b10",
|
||||
"version": null
|
||||
}
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "drops",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/omaralbeik/Drops.git",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "5824681795286c36bdc4a493081a63e64e2a064e"
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"identity" : "marqueelabel",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/cbpowell/MarqueeLabel",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "18e4787f4dc1c26d2d581c4bc9aeae34686eeeae"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nuke",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/Nuke.git",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "c7ba4833b1b38f09e9708858aeaf91babc69f65c"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 65 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 73 KiB |
26
macbuild.sh
26
macbuild.sh
|
|
@ -16,19 +16,35 @@ cd build
|
|||
xcodebuild -project "$WORKING_LOCATION/$APPLICATION_NAME.xcodeproj" \
|
||||
-scheme "$APPLICATION_NAME" \
|
||||
-configuration Release \
|
||||
-derivedDataPath "$WORKING_LOCATION/build/DerivedDataApp" \
|
||||
-destination 'platform=macOS,variant=Mac Catalyst' \
|
||||
-derivedDataPath "$WORKING_LOCATION/build/DerivedDataApp-x86_64" \
|
||||
-destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst' \
|
||||
clean build \
|
||||
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
|
||||
|
||||
DD_APP_PATH="$WORKING_LOCATION/build/DerivedDataApp/Build/Products/Release-maccatalyst/$APPLICATION_NAME.app"
|
||||
xcodebuild -project "$WORKING_LOCATION/$APPLICATION_NAME.xcodeproj" \
|
||||
-scheme "$APPLICATION_NAME" \
|
||||
-configuration Release \
|
||||
-derivedDataPath "$WORKING_LOCATION/build/DerivedDataApp-arm64" \
|
||||
-destination 'platform=macOS,arch=arm64,variant=Mac Catalyst' \
|
||||
clean build \
|
||||
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
|
||||
|
||||
DD_APP_PATH_X86_64="$WORKING_LOCATION/build/DerivedDataApp-x86_64/Build/Products/Release-maccatalyst/$APPLICATION_NAME.app"
|
||||
DD_APP_PATH_ARM64="$WORKING_LOCATION/build/DerivedDataApp-arm64/Build/Products/Release-maccatalyst/$APPLICATION_NAME.app"
|
||||
TARGET_APP="$WORKING_LOCATION/build/$APPLICATION_NAME.app"
|
||||
|
||||
cp -r "$DD_APP_PATH" "$TARGET_APP"
|
||||
rm -rf "$TARGET_APP"
|
||||
cp -r "$DD_APP_PATH_ARM64" "$TARGET_APP"
|
||||
|
||||
lipo -create \
|
||||
"$DD_APP_PATH_X86_64/Contents/MacOS/$APPLICATION_NAME" \
|
||||
"$DD_APP_PATH_ARM64/Contents/MacOS/$APPLICATION_NAME" \
|
||||
-output "$TARGET_APP/Contents/MacOS/$APPLICATION_NAME"
|
||||
|
||||
codesign --remove "$TARGET_APP"
|
||||
if [ -e "$TARGET_APP/_CodeSignature" ]; then
|
||||
rm -rf "$TARGET_APP/_CodeSignature"
|
||||
fi
|
||||
|
||||
echo "Mac Catalyst build completed: $TARGET_APP"
|
||||
echo "Universal Mac Catalyst build completed: $TARGET_APP"
|
||||
lipo -archs "$TARGET_APP/Contents/MacOS/$APPLICATION_NAME"
|
||||
|
|
|
|||
Loading…
Reference in a new issue