This commit is contained in:
Aiden Vigue 2021-05-22 18:34:58 -04:00
parent 41669a949d
commit fdea0d4573
9 changed files with 52 additions and 108 deletions

View File

@ -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;

View File

@ -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(_):
@ -97,7 +95,7 @@ struct ConnectToServerView: View {
HStack {
Text("Connect")
Spacer()
ProgressView().isHidden(!isWorking)
ProgressView().isHidden(!isWorking)
}
}.disabled(isWorking || uri.isEmpty)
}.alert(isPresented: $isErrored) {
@ -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
}
}
}

View File

@ -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) {

View File

@ -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)
}
}

View File

@ -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>

View File

@ -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
}
}
}

View File

@ -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)
}
}

View File

@ -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() {

View File

@ -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