diff --git a/.DS_Store b/.DS_Store index f66bac1b..e483c735 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 421ae0bb..9e506f0c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,11 @@ build/ DerivedData/ +dynatraceSymbols.zip +Cartfile.resolved +Gemfile.lock +dynatrace/ + ## Various settings *.pbxuser !default.pbxuser diff --git a/Cartfile b/Cartfile index 7c69ffb1..b3261ac7 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1,2 @@ binary "https://code.videolan.org/videolan/VLCKit/raw/master/Packaging/MobileVLCKit.json" ~> 3.3.0 +binary "https://files.dynatrace.com/mobileagent/carthage/dynatrace.json" ~> 8.197 \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved index 23fc6ef2..fc277c52 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1,2 @@ binary "https://code.videolan.org/videolan/VLCKit/raw/master/Packaging/MobileVLCKit.json" "3.3.16" +binary "https://files.dynatrace.com/mobileagent/carthage/dynatrace.json" "8.217.1" diff --git a/Carthage/.DS_Store b/Carthage/.DS_Store index 2574ebf1..d721178e 100644 Binary files a/Carthage/.DS_Store and b/Carthage/.DS_Store differ diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..295faaa5 --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +source "https://rubygems.org" + +gem "fastlane" +gem "rest-client" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..dff0ea71 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,216 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.3) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + artifactory (3.0.15) + atomos (0.1.3) + aws-eventstream (1.1.1) + aws-partitions (1.461.0) + aws-sdk-core (3.114.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.239.0) + aws-sigv4 (~> 1.1) + jmespath (~> 1.0) + aws-sdk-kms (1.43.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.95.0) + aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.2.3) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.0.3) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.3) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.6) + emoji_regex (3.2.2) + excon (0.81.0) + faraday (1.4.1) + faraday-excon (~> 1.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) + multipart-post (>= 1.2, < 3) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-excon (1.1.0) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.1.0) + faraday_middleware (1.0.0) + faraday (~> 1.0) + fastimage (2.2.3) + fastlane (2.184.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.1) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (~> 2.0.0) + naturally (~> 2.2) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-dynatrace (1.0.3) + fastlane-plugin-sentry (1.8.1) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.3.0) + google-apis-core (~> 0.1) + google-apis-core (0.3.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (~> 0.14) + httpclient (>= 2.8.1, < 3.0) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + rexml + signet (~> 0.14) + webrick + google-apis-iamcredentials_v1 (0.3.0) + google-apis-core (~> 0.1) + google-apis-playcustomapp_v1 (0.2.0) + google-apis-core (~> 0.1) + google-apis-storage_v1 (0.3.0) + google-apis-core (~> 0.1) + google-cloud-core (1.6.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.5.0) + faraday (>= 0.17.3, < 2.0) + google-cloud-errors (1.1.0) + google-cloud-storage (1.31.1) + addressable (~> 2.5) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.1) + google-cloud-core (~> 1.2) + googleauth (~> 0.9) + mini_mime (~> 1.0) + googleauth (0.16.2) + faraday (>= 0.17.3, < 2.0) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.14) + highline (2.0.3) + http-accept (1.7.0) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.4.0) + json (2.5.1) + jwt (2.2.3) + memoist (0.16.2) + mime-types (3.3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2021.0225) + mini_magick (4.11.0) + mini_mime (1.1.0) + multi_json (1.15.0) + multipart-post (2.0.0) + nanaimo (0.3.0) + naturally (2.2.1) + netrc (0.11.0) + os (1.1.1) + plist (3.6.0) + public_suffix (4.0.6) + rake (13.0.3) + representable (3.1.1) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) + retriable (3.1.2) + rexml (3.2.5) + rouge (2.0.7) + ruby2_keywords (0.0.4) + rubyzip (2.3.0) + security (0.1.3) + signet (0.15.0) + addressable (~> 2.3) + faraday (>= 0.17.3, < 2.0) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.8) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + trailblazer-option (0.1.1) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.7) + unicode-display_width (1.7.0) + webrick (1.7.0) + word_wrap (1.0.0) + xcodeproj (1.19.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + universal-darwin-20 + +DEPENDENCIES + fastlane + fastlane-plugin-dynatrace + fastlane-plugin-sentry + rest-client + +BUNDLED WITH + 2.2.17 diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index 372e6599..a7571608 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 5302F82A2658791C00647A2E /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 5302F8292658791C00647A2E /* Sentry */; }; + 5302F82C2658B5FE00647A2E /* Dynatrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5302F82B2658B5FE00647A2E /* Dynatrace.framework */; }; + 5302F82F2658B60900647A2E /* DynatraceSessionReplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5302F82E2658B60900647A2E /* DynatraceSessionReplay.framework */; }; 5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5338F74D263B61370014BF09 /* ConnectToServerView.swift */; }; 5338F751263B62E80014BF09 /* HidingViews in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F750263B62E80014BF09 /* HidingViews */; }; 5338F754263B65E10014BF09 /* SwiftyRequest in Frameworks */ = {isa = PBXBuildFile; productRef = 5338F753263B65E10014BF09 /* SwiftyRequest */; }; @@ -47,10 +49,19 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 53D5E3DF264B47EE00BADDC8 /* Embed Frameworks */ = { + 5302F8322658B74800647A2E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; + dstSubfolderSpec = 7; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53D5E3DF264B47EE00BADDC8 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 12; + dstPath = ""; dstSubfolderSpec = 10; files = ( 53D5E3DE264B47EE00BADDC8 /* MobileVLCKit.xcframework in Embed Frameworks */, @@ -61,6 +72,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 5302F82B2658B5FE00647A2E /* Dynatrace.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Dynatrace.framework; path = Carthage/Build/iOS/Dynatrace.framework; sourceTree = ""; }; + 5302F82E2658B60900647A2E /* DynatraceSessionReplay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DynatraceSessionReplay.framework; path = Carthage/Build/iOS/DynatraceSessionReplay.framework; sourceTree = ""; }; 5338F74D263B61370014BF09 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = ""; }; 535BAE9E2649E569005FA86D /* ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemView.swift; sourceTree = ""; }; 535BAEA4264A151C005FA86D /* VLCPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VLCPlayer.swift; sourceTree = ""; }; @@ -99,10 +112,12 @@ files = ( 538CD954263E3DC100BB5AF0 /* SDWebImageSwiftUI in Frameworks */, 53E4E645263F6BC000F67C6B /* PartialSheet in Frameworks */, + 5302F82C2658B5FE00647A2E /* Dynatrace.framework in Frameworks */, 5338F757263B7E2E0014BF09 /* KeychainSwift in Frameworks */, 53D5E3DD264B47EE00BADDC8 /* MobileVLCKit.xcframework in Frameworks */, 5338F754263B65E10014BF09 /* SwiftyRequest in Frameworks */, 5302F82A2658791C00647A2E /* Sentry in Frameworks */, + 5302F82F2658B60900647A2E /* DynatraceSessionReplay.framework in Frameworks */, 53892782263CC8770035E14B /* URLImage in Frameworks */, 53D2F74A264C69F6005792BB /* Introspect in Frameworks */, 5389277A263CBFE70035E14B /* SwiftyJSON in Frameworks */, @@ -176,6 +191,8 @@ 53D5E3DB264B47EE00BADDC8 /* Frameworks */ = { isa = PBXGroup; children = ( + 5302F82E2658B60900647A2E /* DynatraceSessionReplay.framework */, + 5302F82B2658B5FE00647A2E /* Dynatrace.framework */, 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */, ); name = Frameworks; @@ -192,6 +209,8 @@ 5377CBEE263B596A003A4E83 /* Frameworks */, 5377CBEF263B596A003A4E83 /* Resources */, 53D5E3DF264B47EE00BADDC8 /* Embed Frameworks */, + 5302F8322658B74800647A2E /* CopyFiles */, + 5302F8332658B74B00647A2E /* ShellScript */, ); buildRules = ( ); @@ -220,6 +239,9 @@ 5377CBE9263B596A003A4E83 /* Project object */ = { isa = PBXProject; attributes = { + KnownAssetTags = ( + New, + ); LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1250; TargetAttributes = { @@ -270,6 +292,28 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 5302F8332658B74B00647A2E /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "input-ios.xcfilelist", + ); + inputPaths = ( + ); + outputFileListPaths = ( + "output-ios.xcfilelist", + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n/usr/local/bin/carthage copy-frameworks\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 5377CBED263B596A003A4E83 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -426,11 +470,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer/Preview Content\""; DEVELOPMENT_TEAM = 9R8RREG67J; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = JellyfinPlayer/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.1; LD_RUNPATH_SEARCH_PATHS = ( @@ -451,11 +499,16 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 7; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer/Preview Content\""; DEVELOPMENT_TEAM = 9R8RREG67J; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = JellyfinPlayer/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.1; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/JellyfinPlayer/ContentView.swift b/JellyfinPlayer/ContentView.swift index 1e9335bd..c793ab18 100644 --- a/JellyfinPlayer/ContentView.swift +++ b/JellyfinPlayer/ContentView.swift @@ -11,6 +11,7 @@ import SwiftyRequest import SwiftyJSON import Introspect import Sentry +import Dynatrace import SDWebImageSwiftUI class GlobalData: ObservableObject { @@ -198,13 +199,22 @@ struct ContentView: View { func startup() { SentrySDK.start { options in - options.dsn = "https://7ef695d745e942f8a52d69317c5ae241@o704459.ingest.sentry.io/5778161" + options.dsn = "https://75ac77d6af4d406eb989f3d8ef0f119f@o513670.ingest.sentry.io/5778242" options.debug = false // Enabled debug when first installing is always helpful options.releaseName = "ios-" + (Bundle.main.infoDictionary?["CFBundleVersion"] as! String); + options.enableOutOfMemoryTracking = false + } + + let privacyConfig = Dynatrace.userPrivacyOptions() + privacyConfig.dataCollectionLevel = .userBehavior + privacyConfig.crashReportingOptedIn = true + privacyConfig.crashReplayOptedIn = true + Dynatrace.applyUserPrivacyOptions(privacyConfig) { (Bool) in + print("Dynatrace privacy changed!") } let cache = SDImageCache(namespace: "tiny") - cache.config.maxMemoryCost = 100 * 1024 * 1024 // 100MB memory + cache.config.maxMemoryCost = 50 * 1024 * 1024 // 100MB memory cache.config.maxDiskSize = 1000 * 1024 * 1024 // 1000MB disk SDImageCachesManager.shared.addCache(cache) SDWebImageManager.defaultImageCache = SDImageCachesManager.shared @@ -238,11 +248,7 @@ struct ContentView: View { case .success( let resp): do { let json = try JSON(data: resp.body) - _libraries.wrappedValue = json["Configuration"]["OrderedViews"].arrayObject as? [String] ?? []; let array2 = json["Configuration"]["LatestItemsExcludes"].arrayObject as? [String] ?? [] - _librariesShowRecentlyAdded.wrappedValue = _libraries.wrappedValue.filter { element in - return !array2.contains(element) - } let request2 = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + "/Users/\(globalData.user?.user_id ?? "")/Views") request2.headerParameters["X-Emby-Authorization"] = globalData.authHeader @@ -258,12 +264,16 @@ struct ContentView: View { _library_names.wrappedValue[item2["Id"].string ?? ""] = item2["Name"].string ?? "" } - if(_libraries.wrappedValue.count == 0 && _librariesShowRecentlyAdded.wrappedValue.count == 0) { - for (_,item2):(String, JSON) in json2["Items"] { + for (_,item2):(String, JSON) in json2["Items"] { + if(item2["CollectionType"].string == "tvshows" || item2["CollectionType"].string == "movies") { _libraries.wrappedValue.append(item2["Id"].string ?? "") _librariesShowRecentlyAdded.wrappedValue.append(item2["Id"].string ?? "") } } + + _librariesShowRecentlyAdded.wrappedValue = _libraries.wrappedValue.filter { element in + return !array2.contains(element) + } } catch { } diff --git a/JellyfinPlayer/ContinueWatchingView.swift b/JellyfinPlayer/ContinueWatchingView.swift index 524c2969..cdecc48b 100644 --- a/JellyfinPlayer/ContinueWatchingView.swift +++ b/JellyfinPlayer/ContinueWatchingView.swift @@ -86,7 +86,7 @@ struct ContinueWatchingView: View { VStack(alignment: .leading) { Spacer().frame(height: 10) if(item.Type == "Episode") { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=360&quality=90&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=500&quality=96&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) @@ -115,7 +115,7 @@ struct ContinueWatchingView: View { ) .shadow(radius: 5) } else { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=360&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=500&quality=96&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) diff --git a/JellyfinPlayer/EpisodeItemView.swift b/JellyfinPlayer/EpisodeItemView.swift index 2182bb9c..9edcd7ac 100644 --- a/JellyfinPlayer/EpisodeItemView.swift +++ b/JellyfinPlayer/EpisodeItemView.swift @@ -142,7 +142,7 @@ struct EpisodeItemView: View { let imageTag = person["PrimaryImageTag"].string ?? ""; cast.ImageBlurHash = person["ImageBlurHashes"]["Primary"][imageTag].string ?? ""; cast.Role = person["Role"].string ?? ""; - cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxHeight=120&quality=90&tag=\(imageTag)")! + cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxHeight=150&quality=90&tag=\(imageTag)")! fullItem.Cast.append(cast); } } @@ -214,7 +214,7 @@ struct EpisodeItemView: View { if(isPortrait) { GeometryReader { geometry in VStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.ParentBackdropItemId)/Images/Backdrop?maxWidth=400&quality=90&tag=\(fullItem.Backdrop)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.ParentBackdropItemId)/Images/Backdrop?maxWidth=450&quality=90&tag=\(fullItem.Backdrop)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!) @@ -228,7 +228,7 @@ struct EpisodeItemView: View { .shadow(radius: 5) .overlay( HStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=150&quality=90&tag=\(fullItem.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) @@ -410,7 +410,7 @@ struct EpisodeItemView: View { .edgesIgnoringSafeArea(.all) HStack() { VStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=150&quality=90&tag=\(fullItem.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.SeriesId ?? "")/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) diff --git a/JellyfinPlayer/Info.plist b/JellyfinPlayer/Info.plist index c140b301..b84f7c33 100644 --- a/JellyfinPlayer/Info.plist +++ b/JellyfinPlayer/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 7 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -57,5 +57,13 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + DTXApplicationID + 8c1f6941-ec78-480c-b589-b41aca29a52e + DTXBeaconURL + https://bf64941kgh.bf.dynatrace.com/mbeacon + DTXUserOptIn + + DTXStartupLoadBalancing + diff --git a/JellyfinPlayer/LatestMediaView.swift b/JellyfinPlayer/LatestMediaView.swift index 3f7f01a2..394aabe5 100644 --- a/JellyfinPlayer/LatestMediaView.swift +++ b/JellyfinPlayer/LatestMediaView.swift @@ -91,7 +91,7 @@ struct LatestMediaView: View { VStack(alignment: .leading) { if(item.Type == "Series") { Spacer().frame(height:10) - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=90&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=250&quality=90&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) @@ -114,7 +114,7 @@ struct LatestMediaView: View { ).shadow(radius: 6) } else { Spacer().frame(height:10) - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=90&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=250&quality=90&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) diff --git a/JellyfinPlayer/MovieItemView.swift b/JellyfinPlayer/MovieItemView.swift index be1d1de1..dc607020 100644 --- a/JellyfinPlayer/MovieItemView.swift +++ b/JellyfinPlayer/MovieItemView.swift @@ -261,7 +261,7 @@ struct MovieItemView: View { if(isPortrait) { GeometryReader { geometry in VStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Backdrop?maxWidth=400&quality=90&tag=\(fullItem.Backdrop)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Backdrop?maxWidth=450&quality=90&tag=\(fullItem.Backdrop)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!) @@ -275,7 +275,7 @@ struct MovieItemView: View { .shadow(radius: 5) .overlay( HStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=150&quality=90&tag=\(fullItem.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) @@ -457,7 +457,7 @@ struct MovieItemView: View { .edgesIgnoringSafeArea(.all) HStack() { VStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=150&quality=90&tag=\(fullItem.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) diff --git a/JellyfinPlayer/NextUpView.swift b/JellyfinPlayer/NextUpView.swift index 166f97ae..8bf61665 100644 --- a/JellyfinPlayer/NextUpView.swift +++ b/JellyfinPlayer/NextUpView.swift @@ -79,7 +79,7 @@ struct NextUpView: View { NavigationLink(destination: ItemView(item: item)) { VStack(alignment: .leading) { Spacer().frame(height:10) - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.SeriesId ?? "")/Images/\(item.ImageType)?maxWidth=150&quality=90&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.SeriesId ?? "")/Images/\(item.ImageType)?maxWidth=250&quality=90&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) diff --git a/JellyfinPlayer/SeasonItemView.swift b/JellyfinPlayer/SeasonItemView.swift index 66017f21..8daa4c03 100644 --- a/JellyfinPlayer/SeasonItemView.swift +++ b/JellyfinPlayer/SeasonItemView.swift @@ -74,7 +74,7 @@ struct SeasonItemView: View { let imageTag = person["PrimaryImageTag"].string ?? ""; cast.ImageBlurHash = person["ImageBlurHashes"]["Primary"][imageTag].string ?? ""; cast.Role = person["Role"].string ?? ""; - cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxWidth=150&quality=96&tag=\(imageTag)")! + cast.Image = URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(cast.Id)/Images/Primary?maxWidth=2000&quality=90&tag=\(imageTag)")! fullItem.Cast.append(cast); } } @@ -202,7 +202,7 @@ struct SeasonItemView: View { .shadow(radius: 5) .overlay( HStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=150&quality=80&tag=\(fullItem.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) @@ -235,7 +235,7 @@ struct SeasonItemView: View { ForEach(episodes, id: \.Id) { episode in NavigationLink(destination: ItemView(item: episode.ResumeItem ?? ResumeItem())) { HStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(episode.Id)/Images/Primary?maxWidth=250&quality=80&tag=\(episode.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(episode.Id)/Images/Primary?maxWidth=300&quality=90&tag=\(episode.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (episode.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) @@ -313,7 +313,7 @@ struct SeasonItemView: View { .edgesIgnoringSafeArea(.all) HStack() { VStack(alignment: .leading) { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=150&quality=80&tag=\(fullItem.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) @@ -338,7 +338,7 @@ struct SeasonItemView: View { ForEach(episodes, id: \.Id) { episode in NavigationLink(destination: ItemView(item: episode.ResumeItem ?? ResumeItem())) { HStack() { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(episode.Id)/Images/Primary?maxWidth=200&quality=80&tag=\(episode.Poster)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(episode.Id)/Images/Primary?maxWidth=300&quality=90&tag=\(episode.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (episode.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) diff --git a/JellyfinPlayer/SeriesItemView.swift b/JellyfinPlayer/SeriesItemView.swift index ae63f7e0..ee19b341 100644 --- a/JellyfinPlayer/SeriesItemView.swift +++ b/JellyfinPlayer/SeriesItemView.swift @@ -84,7 +84,7 @@ struct SeriesItemView: View { ForEach(items, id: \.Id) { item in NavigationLink(destination: ItemView(item: item )) { VStack(alignment: .leading) { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=90&tag=\(item.Image)")) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=250&quality=90&tag=\(item.Image)")) .resizable() .placeholder { Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) diff --git a/dynatrace/DTXDssClient b/dynatrace/DTXDssClient new file mode 100755 index 00000000..f350c8b5 Binary files /dev/null and b/dynatrace/DTXDssClient differ diff --git a/dynatrace/version b/dynatrace/version new file mode 100644 index 00000000..0980f2f6 --- /dev/null +++ b/dynatrace/version @@ -0,0 +1 @@ +https://api.mobileagent.downloads.dynatrace.com/sprint-latest-dss-client/217 \ No newline at end of file diff --git a/dynatraceSymbols.zip b/dynatraceSymbols.zip new file mode 100644 index 00000000..e94840af Binary files /dev/null and b/dynatraceSymbols.zip differ diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 00000000..baeb4db7 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,8 @@ +app_identifier("me.vigue.jellyfin") # The bundle identifier of your app +apple_id("acvigue@me.com") # Your Apple email address + +itc_team_id("103277821") # App Store Connect Team ID +team_id("9R8RREG67J") # Developer Portal Team ID + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 00000000..680a6950 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,41 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:ios) + +platform :ios do + desc "Push a new beta build to TestFlight" + lane :beta do + increment_build_number(xcodeproj: "JellyfinPlayer.xcodeproj") + gym + upload_to_testflight + upload_symbols_to_sentry( + org_slug: 'aidenvigue', + project_slug: 'jellyfin-swift-ios', + dsym_path: "JellyfinPlayer.app.dSYM.zip" + ) + identifier_v = get_version_number(xcodeproj: "JellyfinPlayer.xcodeproj") + identifier_s = get_info_plist_value(path: "JellyfinPlayer/Info.plist", key: "CFBundleVersion") + dynatrace_process_symbols( + appId: "8c1f6941-ec78-480c-b589-b41aca29a52e", + os: "ios", + bundleId: "me.vigue.jellyfin", + versionStr: identifier_v, + version: identifier_s, + server: "https://ofa89490.live.dynatrace.com", + symbolsfile: "JellyfinPlayer.app.dSYM.zip" + ) + end +end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 00000000..43e539cd --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,6 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-sentry' +gem 'fastlane-plugin-dynatrace' diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 00000000..f213a16c --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,29 @@ +fastlane documentation +================ +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +``` +xcode-select --install +``` + +Install _fastlane_ using +``` +[sudo] gem install fastlane -NV +``` +or alternatively using `brew install fastlane` + +# Available Actions +## iOS +### ios beta +``` +fastlane ios beta +``` +Push a new beta build to TestFlight + +---- + +This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). +The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/input-ios.xcfilelist b/input-ios.xcfilelist new file mode 100644 index 00000000..06717521 --- /dev/null +++ b/input-ios.xcfilelist @@ -0,0 +1,2 @@ +$(SRCROOT)/Carthage/Build/iOS/Dynatrace.framework +$(SRCROOT)/Carthage/Build/iOS/DynatraceSessionReplay.framework \ No newline at end of file diff --git a/output-ios.xcfilelist b/output-ios.xcfilelist new file mode 100644 index 00000000..0bf749f2 --- /dev/null +++ b/output-ios.xcfilelist @@ -0,0 +1,2 @@ +$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Dynatrace.framework +$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/DynatraceSessionReplay.framework \ No newline at end of file