cleanup
This commit is contained in:
parent
41669a949d
commit
fdea0d4573
|
@ -470,7 +470,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
CURRENT_PROJECT_VERSION = 10;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
@ -499,7 +499,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
CURRENT_PROJECT_VERSION = 10;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||
|
|
|
@ -16,6 +16,7 @@ import Sentry
|
|||
|
||||
struct ConnectToServerView: View {
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
@EnvironmentObject var globalData: GlobalData
|
||||
@EnvironmentObject var jsi: justSignedIn
|
||||
@State private var uri = "";
|
||||
@State private var isWorking = false;
|
||||
|
@ -80,12 +81,9 @@ struct ConnectToServerView: View {
|
|||
switch result {
|
||||
case .success(let response):
|
||||
let server = response.body
|
||||
print("Found server: " + server.ServerName)
|
||||
_serverName.wrappedValue = server.ServerName
|
||||
_server_id.wrappedValue = server.Id
|
||||
if(!server.StartupWizardCompleted) {
|
||||
print("Server needs configured")
|
||||
} else {
|
||||
if(server.StartupWizardCompleted) {
|
||||
_isConnected.wrappedValue = true;
|
||||
}
|
||||
case .failure(_):
|
||||
|
@ -108,14 +106,13 @@ struct ConnectToServerView: View {
|
|||
TextField("Username", text: $username)
|
||||
.disableAutocorrection(true)
|
||||
.autocapitalization(.none)
|
||||
SecureField("Password", text: $password)
|
||||
SecureField("Password (optional)", text: $password)
|
||||
.disableAutocorrection(true)
|
||||
.autocapitalization(.none)
|
||||
Button {
|
||||
_isWorking.wrappedValue = true
|
||||
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String;
|
||||
let authHeader = "MediaBrowser Client=\"SwiftFin\", Device=\"\(UIDevice.current.name)\", DeviceId=\"\(serverSkipped ? reauthDeviceID : userUUID.uuidString)\", Version=\"\(appVersion ?? "0.0.1")\"";
|
||||
print(authHeader)
|
||||
let authJson: [String: Any] = ["Username": _username.wrappedValue, "Pw": _password.wrappedValue]
|
||||
let request = RestRequest(method: .post, url: uri + "/Users/authenticatebyname")
|
||||
request.headerParameters["X-Emby-Authorization"] = authHeader
|
||||
|
@ -128,8 +125,6 @@ struct ConnectToServerView: View {
|
|||
case .success(let response):
|
||||
do {
|
||||
let json = try JSON(data: response.body)
|
||||
dump(json)
|
||||
|
||||
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
|
||||
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
|
||||
|
||||
|
@ -158,21 +153,18 @@ struct ConnectToServerView: View {
|
|||
newUser.username = _username.wrappedValue
|
||||
newUser.user_id = json["User"]["Id"].string ?? ""
|
||||
|
||||
globalData.authHeader = authHeader
|
||||
|
||||
let keychain = KeychainSwift()
|
||||
keychain.set(json["AccessToken"].string ?? "", forKey: "AccessToken_\(json["User"]["Id"].string ?? "")")
|
||||
|
||||
do {
|
||||
try viewContext.save()
|
||||
print("Saved to Core Data Store")
|
||||
_rootIsActive.wrappedValue = false
|
||||
DispatchQueue.main.async { [self] in
|
||||
jsi.did = true
|
||||
}
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
SentrySDK.capture(error: error)
|
||||
}
|
||||
} catch {
|
||||
|
||||
|
@ -196,14 +188,9 @@ struct ConnectToServerView: View {
|
|||
}
|
||||
}
|
||||
}.navigationTitle("Connect to Server")
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.navigationBarBackButtonHidden(true)
|
||||
.alert(isPresented: $serverSkippedAlert) {
|
||||
Alert(title: Text("Error"), message: Text("Credentials have expired"), dismissButton: .default(Text("Sign in again")))
|
||||
}
|
||||
.onAppear(perform: start)
|
||||
.introspectTabBarController { (UITabBarController) in
|
||||
UITabBarController.tabBar.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -345,12 +345,6 @@ struct ContentView: View {
|
|||
TabView(selection: $tabSelection) {
|
||||
NavigationView() {
|
||||
VStack {
|
||||
NavigationLink(destination: ConnectToServerView(isActive: $needsToSelectServer), isActive: $needsToSelectServer) {
|
||||
EmptyView()
|
||||
}.isDetailLink(false)
|
||||
NavigationLink(destination: ConnectToServerView(skip_server: true, skip_server_prefill: globalData.server, reauth_deviceId: globalData.user?.device_uuid ?? "", isActive: $isSignInErrored), isActive: $isSignInErrored) {
|
||||
EmptyView()
|
||||
}.isDetailLink(false)
|
||||
if(!needsToSelectServer && !isSignInErrored) {
|
||||
VStack(alignment: .leading) {
|
||||
ScrollView() {
|
||||
|
@ -362,7 +356,7 @@ struct ContentView: View {
|
|||
HStack() {
|
||||
Text("Latest \(library_names[library_id] ?? "")").font(.title2).fontWeight(.bold).padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16))
|
||||
Spacer()
|
||||
NavigationLink(destination: LibraryView(prefill: library_id, names: library_names, libraries: libraries, filter: "&SortBy=DateCreated&SortOrder=Descending")) {
|
||||
NavigationLink(destination: LibraryView(prefill: library_id, names: [library_id: library_names[library_id] ?? ""], libraries: [library_id], filter: "&SortBy=DateCreated&SortOrder=Descending")) {
|
||||
Text("See All").font(.subheadline).fontWeight(.bold)
|
||||
}
|
||||
}.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
||||
|
@ -400,9 +394,8 @@ struct ContentView: View {
|
|||
})
|
||||
.tag("All Media")
|
||||
|
||||
}.edgesIgnoringSafeArea(isPortrait ? [] : [.leading,.trailing])
|
||||
}
|
||||
}.environmentObject(globalData)
|
||||
.edgesIgnoringSafeArea(isPortrait ? [] : [.leading,.trailing])
|
||||
.onAppear(perform: startup)
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.alert(isPresented: $isNetworkErrored) {
|
||||
|
|
|
@ -21,6 +21,8 @@ struct EpisodeItemView: View {
|
|||
@State private var playing: Bool = false;
|
||||
@State private var vc: PreferenceUIHostingController? = nil;
|
||||
@State private var progressString: String = "";
|
||||
@State private var viewDidLoad: Bool = false;
|
||||
|
||||
@State private var watched: Bool = false {
|
||||
didSet {
|
||||
if(watched == true) {
|
||||
|
@ -83,17 +85,20 @@ struct EpisodeItemView: View {
|
|||
}
|
||||
}
|
||||
|
||||
func loadData() {
|
||||
if(UIDevice.current.orientation.isLandscape) {
|
||||
orientationInfo.orientation = .landscape;
|
||||
} else {
|
||||
orientationInfo.orientation = .portrait;
|
||||
}
|
||||
func unlockOrientations() {
|
||||
if(_vc.wrappedValue != nil) {
|
||||
_vc.wrappedValue?._prefersHomeIndicatorAutoHidden = false;
|
||||
_vc.wrappedValue?._orientations = .allButUpsideDown;
|
||||
_vc.wrappedValue?._viewPreference = .unspecified;
|
||||
}
|
||||
}
|
||||
|
||||
func loadData() {
|
||||
unlockOrientations();
|
||||
if(_viewDidLoad.wrappedValue == true) {
|
||||
return
|
||||
}
|
||||
_viewDidLoad.wrappedValue = true;
|
||||
let url = "/Users/\(globalData.user?.user_id ?? "")/Items/\(item.Id)"
|
||||
|
||||
let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + url)
|
||||
|
@ -202,14 +207,6 @@ struct EpisodeItemView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
|
||||
@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
|
||||
|
||||
var isPortrait: Bool {
|
||||
let result = verticalSizeClass == .regular && horizontalSizeClass == .compact
|
||||
return result
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if(playing) {
|
||||
PlayerDemo(item: fullItem, playing: $playing).onAppear(perform: lockOrientations)
|
||||
|
@ -586,16 +583,11 @@ struct EpisodeItemView: View {
|
|||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle("\(fullItem.Name) - S\(String(fullItem.ParentIndexNumber ?? 0)):E\(String(fullItem.IndexNumber ?? 0)) - \(fullItem.SeriesName ?? "")")
|
||||
.supportedOrientations(.allButUpsideDown)
|
||||
.prefersHomeIndicatorAutoHidden(false)
|
||||
.withHostingWindow() { window in
|
||||
let rootVC = window?.rootViewController;
|
||||
let UIHostingcontroller: PreferenceUIHostingController = rootVC as! PreferenceUIHostingController;
|
||||
vc = UIHostingcontroller;
|
||||
}
|
||||
.introspectTabBarController { (UITabBarController) in
|
||||
UITabBarController.tabBar.isHidden = false
|
||||
}
|
||||
}.onAppear(perform: loadData)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>9</string>
|
||||
<string>10</string>
|
||||
<key>DTXApplicationID</key>
|
||||
<string>8c1f6941-ec78-480c-b589-b41aca29a52e</string>
|
||||
<key>DTXBeaconURL</key>
|
||||
|
|
|
@ -24,29 +24,20 @@ class OrientationInfo: ObservableObject {
|
|||
case landscape
|
||||
}
|
||||
|
||||
@Published var orientation: Orientation
|
||||
@Published var orientation: Orientation = .portrait;
|
||||
|
||||
private var _observer: NSObjectProtocol?
|
||||
|
||||
init() {
|
||||
// fairly arbitrary starting value for 'flat' orientations
|
||||
if UIDevice.current.orientation.isLandscape {
|
||||
self.orientation = .landscape
|
||||
}
|
||||
else {
|
||||
self.orientation = .portrait
|
||||
}
|
||||
|
||||
// unowned self because we unregister before self becomes invalid
|
||||
_observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil) { [unowned self] note in
|
||||
_observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil) { [weak self] note in
|
||||
guard let device = note.object as? UIDevice else {
|
||||
return
|
||||
}
|
||||
if device.orientation.isPortrait {
|
||||
self.orientation = .portrait
|
||||
self?.orientation = .portrait
|
||||
}
|
||||
else if device.orientation.isLandscape {
|
||||
self.orientation = .landscape
|
||||
self?.orientation = .landscape
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@ struct MovieItemView: View {
|
|||
@State private var playing: Bool = false;
|
||||
@State private var vc: PreferenceUIHostingController? = nil;
|
||||
@State private var progressString: String = "";
|
||||
@State private var viewDidLoad: Bool = false;
|
||||
|
||||
@State private var watched: Bool = false {
|
||||
didSet {
|
||||
if(watched == true) {
|
||||
|
@ -130,12 +132,20 @@ struct MovieItemView: View {
|
|||
}
|
||||
}
|
||||
|
||||
func loadData() {
|
||||
func unlockOrientations() {
|
||||
if(_vc.wrappedValue != nil) {
|
||||
_vc.wrappedValue?._prefersHomeIndicatorAutoHidden = false;
|
||||
_vc.wrappedValue?._orientations = .allButUpsideDown;
|
||||
_vc.wrappedValue?._viewPreference = .unspecified;
|
||||
}
|
||||
}
|
||||
|
||||
func loadData() {
|
||||
unlockOrientations()
|
||||
if(_viewDidLoad.wrappedValue == true) {
|
||||
return;
|
||||
}
|
||||
_viewDidLoad.wrappedValue = true;
|
||||
let url = "/Users/\(globalData.user?.user_id ?? "")/Items/\(item.Id)"
|
||||
|
||||
let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + url)
|
||||
|
@ -243,14 +253,6 @@ struct MovieItemView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
|
||||
@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
|
||||
|
||||
var isPortrait: Bool {
|
||||
let result = verticalSizeClass == .regular && horizontalSizeClass == .compact
|
||||
return result
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if(playing) {
|
||||
PlayerDemo(item: fullItem, playing: $playing).onAppear(perform: lockOrientations)
|
||||
|
@ -626,16 +628,11 @@ struct MovieItemView: View {
|
|||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(fullItem.Name)
|
||||
.supportedOrientations(.allButUpsideDown)
|
||||
.prefersHomeIndicatorAutoHidden(false)
|
||||
.withHostingWindow() { window in
|
||||
let rootVC = window?.rootViewController;
|
||||
let UIHostingcontroller: PreferenceUIHostingController = rootVC as! PreferenceUIHostingController;
|
||||
vc = UIHostingcontroller;
|
||||
}
|
||||
.introspectTabBarController { (UITabBarController) in
|
||||
UITabBarController.tabBar.isHidden = false
|
||||
}
|
||||
}.onAppear(perform: loadData)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,11 +38,10 @@ extension String {
|
|||
}
|
||||
struct PlayerDemo: View {
|
||||
@EnvironmentObject var globalData: GlobalData
|
||||
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
|
||||
var item: DetailItem;
|
||||
@State private var pbitem: PlaybackItem = PlaybackItem(videoType: VideoType.direct, videoUrl: URL(string: "https://example.com")!, subtitles: []);
|
||||
@State private var streamLoading = false;
|
||||
@State private var vlcplayer: VLCMediaPlayer = VLCMediaPlayer(options: ["-vv", "--sub-margin=-50"]);
|
||||
@State private var vlcplayer: VLCMediaPlayer = VLCMediaPlayer(options: ["--sub-margin=-50"]);
|
||||
@State private var isPlaying = false;
|
||||
@State private var subtitles: [Subtitle] = [];
|
||||
@State private var inactivity: Bool = true;
|
||||
|
@ -206,7 +205,7 @@ struct PlayerDemo: View {
|
|||
|
||||
func startStream() {
|
||||
_streamLoading.wrappedValue = true;
|
||||
let request = RestRequest(method: .post, url: (globalData.server?.baseURI ?? "") + "/Items/\(item.Id)/PlaybackInfo?UserId=\(globalData.user?.user_id ?? "")&StartTimeTicks=0&IsPlayback=true&AutoOpenLiveStream=true&MaxStreamingBitrate=70000000")
|
||||
let request = RestRequest(method: .post, url: (globalData.server?.baseURI ?? "") + "/Items/\(item.Id)/PlaybackInfo?UserId=\(globalData.user?.user_id ?? "")&StartTimeTicks=\(item.Progress)&IsPlayback=true&AutoOpenLiveStream=true&MaxStreamingBitrate=70000000")
|
||||
request.headerParameters["X-Emby-Authorization"] = globalData.authHeader
|
||||
request.contentType = "application/json"
|
||||
request.acceptType = "application/json"
|
||||
|
@ -220,14 +219,7 @@ struct PlayerDemo: View {
|
|||
let json = try JSON(data: body)
|
||||
_playSessionId.wrappedValue = json["PlaySessionId"].string ?? "";
|
||||
if(json["MediaSources"][0]["TranscodingUrl"].string != nil) {
|
||||
//Video is transcoded due to TranscodingReason - also may just be remuxed
|
||||
for (_,stream):(String, JSON) in json["MediaSources"][0]["MediaStreams"] {
|
||||
if(stream["Type"].string == "Subtitle") {
|
||||
print("Found subtitle track: \(stream["DeliveryUrl"].string ?? "")")
|
||||
}
|
||||
}
|
||||
let streamURL: URL = URL(string: "\(globalData.server?.baseURI ?? "")\((json["MediaSources"][0]["TranscodingUrl"].string ?? "").replacingOccurrences(of: "master.m3u8", with: "main.m3u8"))")!
|
||||
print(streamURL);
|
||||
let item = PlaybackItem(videoType: VideoType.hls, videoUrl: streamURL, subtitles: [])
|
||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed")
|
||||
_subtitles.wrappedValue.append(disableSubtitleTrack);
|
||||
|
@ -243,14 +235,12 @@ struct PlayerDemo: View {
|
|||
pbitem.subtitles = subtitles;
|
||||
_isPlaying.wrappedValue = true;
|
||||
} else {
|
||||
print("Direct play of item \(item.Name)")
|
||||
let streamURL: URL = URL(string: "\(globalData.server?.baseURI ?? "")/Videos/\(item.Id)/stream?Static=true&mediaSourceId=\(item.Id)&deviceId=\(globalData.user?.device_uuid ?? "")&api_key=\(globalData.authToken)&Tag=\(json["MediaSources"][0]["ETag"])")!;
|
||||
let item = PlaybackItem(videoType: VideoType.direct, videoUrl: streamURL, subtitles: [])
|
||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed")
|
||||
_subtitles.wrappedValue.append(disableSubtitleTrack);
|
||||
for (_,stream):(String, JSON) in json["MediaSources"][0]["MediaStreams"] {
|
||||
if(stream["Type"].string == "Subtitle") {
|
||||
print("Found subtitle track with title \(stream["DisplayTitle"].string ?? "") Delivery method: \(stream["DeliveryMethod"].string ?? "")")
|
||||
let deliveryUrl = URL(string: "\(globalData.server?.baseURI ?? "")\(stream["DeliveryUrl"].string ?? "")")!
|
||||
let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["DeliveryMethod"].string ?? "")
|
||||
_subtitles.wrappedValue.append(subtitle);
|
||||
|
@ -260,7 +250,8 @@ struct PlayerDemo: View {
|
|||
pbitem.subtitles = subtitles;
|
||||
_isPlaying.wrappedValue = true;
|
||||
}
|
||||
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
||||
|
||||
DispatchQueue.global(qos: .userInteractive).async { [self] in
|
||||
self.keepUpWithPlayerState()
|
||||
}
|
||||
} catch {
|
||||
|
@ -280,8 +271,7 @@ struct PlayerDemo: View {
|
|||
while(vlcplayer.state == VLCMediaPlayerState.paused) {
|
||||
let secondsScrubbedTo = round(_scrub.wrappedValue * videoDuration);
|
||||
let scrubRemaining = videoDuration - secondsScrubbedTo;
|
||||
usleep(10000)
|
||||
|
||||
usleep(50000)
|
||||
let remainingTime = scrubRemaining;
|
||||
let hours = floor(remainingTime / 3600);
|
||||
let minutes = (remainingTime.truncatingRemainder(dividingBy: 3600)) / 60;
|
||||
|
@ -401,14 +391,8 @@ struct PlayerDemo: View {
|
|||
.navigationBarHidden(true)
|
||||
.navigationBarBackButtonHidden(true)
|
||||
.statusBar(hidden: true)
|
||||
.introspectTabBarController { (UITabBarController) in
|
||||
UITabBarController.tabBar.isHidden = true
|
||||
}
|
||||
.prefersHomeIndicatorAutoHidden(true)
|
||||
.supportedOrientations(.landscapeRight)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.onTapGesture(perform: resetTimer)
|
||||
.overrideViewPreference(.dark)
|
||||
.fullScreenCover(isPresented: self.$captionConfiguration) {
|
||||
NavigationView() {
|
||||
VStack() {
|
||||
|
|
|
@ -22,15 +22,6 @@ platform :ios do
|
|||
gym(output_name: "JellyfinPlayer.ipa")
|
||||
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"
|
||||
)
|
||||
upload_symbols_to_sentry(
|
||||
org_slug: 'aidenvigue',
|
||||
project_slug: 'jellyfin-swift-ios',
|
||||
|
@ -45,5 +36,14 @@ platform :ios do
|
|||
upload_assets: ["JellyfinPlayer.ipa"]
|
||||
)
|
||||
upload_to_testflight
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue