Merge remote-tracking branch 'origin/main' into main
This commit is contained in:
commit
c00559a4a4
|
@ -0,0 +1 @@
|
||||||
|
* @acvigue
|
|
@ -18,6 +18,10 @@ jobs:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with:
|
||||||
|
xcode-version: latest
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
||||||
|
531ABF6C2671F5CC00C0FE20 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 628B95212670CABD0091AF3B /* WidgetKit.framework */; };
|
||||||
5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; };
|
5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; };
|
||||||
5321753E2671DE9C005491E6 /* Typings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535870AC2669D8DD00D05A09 /* Typings.swift */; };
|
5321753E2671DE9C005491E6 /* Typings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535870AC2669D8DD00D05A09 /* Typings.swift */; };
|
||||||
5321753F2671DEA6005491E6 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; };
|
5321753F2671DEA6005491E6 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; };
|
||||||
|
@ -73,9 +74,38 @@
|
||||||
621C638026672A30004216EA /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 621C637F26672A30004216EA /* NukeUI */; };
|
621C638026672A30004216EA /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 621C637F26672A30004216EA /* NukeUI */; };
|
||||||
621C638226676728004216EA /* NukeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621C638126676728004216EA /* NukeExtensions.swift */; };
|
621C638226676728004216EA /* NukeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621C638126676728004216EA /* NukeExtensions.swift */; };
|
||||||
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */; };
|
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */; };
|
||||||
|
6228B1C22670EB010067FD35 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBFD263B596B003A4E83 /* PersistenceController.swift */; };
|
||||||
|
6267B3D42671024A00A7371D /* APIExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5364F454266CA0DC0026ECBA /* APIExtensions.swift */; };
|
||||||
|
6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D526710B8900A7371D /* CollectionExtensions.swift */; };
|
||||||
|
6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D526710B8900A7371D /* CollectionExtensions.swift */; };
|
||||||
|
6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D526710B8900A7371D /* CollectionExtensions.swift */; };
|
||||||
|
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D92671138200A7371D /* ImageExtensions.swift */; };
|
||||||
|
6267B3DB2671139400A7371D /* ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D92671138200A7371D /* ImageExtensions.swift */; };
|
||||||
|
6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D92671138200A7371D /* ImageExtensions.swift */; };
|
||||||
|
628B95242670CABD0091AF3B /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 628B95232670CABD0091AF3B /* SwiftUI.framework */; };
|
||||||
|
628B95272670CABD0091AF3B /* NextUpWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 628B95262670CABD0091AF3B /* NextUpWidget.swift */; };
|
||||||
|
628B95292670CABE0091AF3B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 628B95282670CABE0091AF3B /* Assets.xcassets */; };
|
||||||
|
628B952D2670CABE0091AF3B /* WidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 628B95202670CABD0091AF3B /* WidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
628B95332670CAEA0091AF3B /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 628B95322670CAEA0091AF3B /* NukeUI */; };
|
||||||
|
628B95352670CAEA0091AF3B /* JellyfinAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 628B95342670CAEA0091AF3B /* JellyfinAPI */; };
|
||||||
|
628B95372670CB800091AF3B /* JellyfinWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 628B95362670CB800091AF3B /* JellyfinWidget.swift */; };
|
||||||
|
628B95382670CDAB0091AF3B /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBFF263B596B003A4E83 /* Model.xcdatamodeld */; };
|
||||||
|
628B953A2670CE250091AF3B /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 628B95392670CE250091AF3B /* KeychainSwift */; };
|
||||||
|
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; };
|
||||||
|
62FA8A522671DE3C004BA2AB /* WidgetEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FA8A512671DE3C004BA2AB /* WidgetEnvironment.swift */; };
|
||||||
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
628B952B2670CABE0091AF3B /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 5377CBE9263B596A003A4E83 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 628B951F2670CABD0091AF3B;
|
||||||
|
remoteInfo = WidgetExtensionExtension;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
5302F8322658B74800647A2E /* CopyFiles */ = {
|
5302F8322658B74800647A2E /* CopyFiles */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
@ -108,6 +138,17 @@
|
||||||
name = "Embed Frameworks";
|
name = "Embed Frameworks";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
628B95312670CABE0091AF3B /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
628B952D2670CABE0091AF3B /* WidgetExtension.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
@ -161,6 +202,17 @@
|
||||||
621338B22660A07800A81A2A /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
|
621338B22660A07800A81A2A /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
|
||||||
621C638126676728004216EA /* NukeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NukeExtensions.swift; sourceTree = "<group>"; };
|
621C638126676728004216EA /* NukeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NukeExtensions.swift; sourceTree = "<group>"; };
|
||||||
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallaxHeader.swift; sourceTree = "<group>"; };
|
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallaxHeader.swift; sourceTree = "<group>"; };
|
||||||
|
6267B3D526710B8900A7371D /* CollectionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionExtensions.swift; sourceTree = "<group>"; };
|
||||||
|
6267B3D92671138200A7371D /* ImageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageExtensions.swift; sourceTree = "<group>"; };
|
||||||
|
628B95202670CABD0091AF3B /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
628B95212670CABD0091AF3B /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
|
||||||
|
628B95232670CABD0091AF3B /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||||
|
628B95262670CABD0091AF3B /* NextUpWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextUpWidget.swift; sourceTree = "<group>"; };
|
||||||
|
628B95282670CABE0091AF3B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
628B952A2670CABE0091AF3B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
628B95362670CB800091AF3B /* JellyfinWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinWidget.swift; sourceTree = "<group>"; };
|
||||||
|
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||||
|
62FA8A512671DE3C004BA2AB /* WidgetEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetEnvironment.swift; sourceTree = "<group>"; };
|
||||||
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
|
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
@ -189,6 +241,18 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
628B951D2670CABD0091AF3B /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
628B95332670CAEA0091AF3B /* NukeUI in Frameworks */,
|
||||||
|
628B95242670CABD0091AF3B /* SwiftUI.framework in Frameworks */,
|
||||||
|
531ABF6C2671F5CC00C0FE20 /* WidgetKit.framework in Frameworks */,
|
||||||
|
628B953A2670CE250091AF3B /* KeychainSwift in Frameworks */,
|
||||||
|
628B95352670CAEA0091AF3B /* JellyfinAPI in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
@ -245,6 +309,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
53D5E3DA264B460200BADDC8 /* Cartfile */,
|
53D5E3DA264B460200BADDC8 /* Cartfile */,
|
||||||
|
628B95252670CABD0091AF3B /* WidgetExtension */,
|
||||||
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
|
53D5E3DB264B47EE00BADDC8 /* Frameworks */,
|
||||||
5377CBF3263B596A003A4E83 /* JellyfinPlayer */,
|
5377CBF3263B596A003A4E83 /* JellyfinPlayer */,
|
||||||
535870612669D21600D05A09 /* JellyfinPlayer tvOS */,
|
535870612669D21600D05A09 /* JellyfinPlayer tvOS */,
|
||||||
|
@ -258,6 +323,7 @@
|
||||||
children = (
|
children = (
|
||||||
5377CBF1263B596A003A4E83 /* JellyfinPlayer iOS.app */,
|
5377CBF1263B596A003A4E83 /* JellyfinPlayer iOS.app */,
|
||||||
535870602669D21600D05A09 /* JellyfinPlayer tvOS.app */,
|
535870602669D21600D05A09 /* JellyfinPlayer tvOS.app */,
|
||||||
|
628B95202670CABD0091AF3B /* WidgetExtension.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -309,6 +375,8 @@
|
||||||
children = (
|
children = (
|
||||||
5358709C2669D82900D05A09 /* TVVLCKit.framework */,
|
5358709C2669D82900D05A09 /* TVVLCKit.framework */,
|
||||||
53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */,
|
53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */,
|
||||||
|
628B95212670CABD0091AF3B /* WidgetKit.framework */,
|
||||||
|
628B95232670CABD0091AF3B /* SwiftUI.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -324,10 +392,25 @@
|
||||||
621C638126676728004216EA /* NukeExtensions.swift */,
|
621C638126676728004216EA /* NukeExtensions.swift */,
|
||||||
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */,
|
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */,
|
||||||
621338922660107500A81A2A /* StringExtensions.swift */,
|
621338922660107500A81A2A /* StringExtensions.swift */,
|
||||||
|
6267B3D526710B8900A7371D /* CollectionExtensions.swift */,
|
||||||
|
6267B3D92671138200A7371D /* ImageExtensions.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
628B95252670CABD0091AF3B /* WidgetExtension */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */,
|
||||||
|
628B95362670CB800091AF3B /* JellyfinWidget.swift */,
|
||||||
|
628B95262670CABD0091AF3B /* NextUpWidget.swift */,
|
||||||
|
628B95282670CABE0091AF3B /* Assets.xcassets */,
|
||||||
|
628B952A2670CABE0091AF3B /* Info.plist */,
|
||||||
|
62FA8A512671DE3C004BA2AB /* WidgetEnvironment.swift */,
|
||||||
|
);
|
||||||
|
path = WidgetExtension;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
AE8C3157265D6F5E008AA076 /* Resources */ = {
|
AE8C3157265D6F5E008AA076 /* Resources */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -373,10 +456,12 @@
|
||||||
5377CBEF263B596A003A4E83 /* Resources */,
|
5377CBEF263B596A003A4E83 /* Resources */,
|
||||||
53D5E3DF264B47EE00BADDC8 /* Embed Frameworks */,
|
53D5E3DF264B47EE00BADDC8 /* Embed Frameworks */,
|
||||||
5302F8322658B74800647A2E /* CopyFiles */,
|
5302F8322658B74800647A2E /* CopyFiles */,
|
||||||
|
628B95312670CABE0091AF3B /* Embed App Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
628B952C2670CABE0091AF3B /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = "JellyfinPlayer iOS";
|
name = "JellyfinPlayer iOS";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
|
@ -389,6 +474,28 @@
|
||||||
productReference = 5377CBF1263B596A003A4E83 /* JellyfinPlayer iOS.app */;
|
productReference = 5377CBF1263B596A003A4E83 /* JellyfinPlayer iOS.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
628B951F2670CABD0091AF3B /* WidgetExtension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 628B952E2670CABE0091AF3B /* Build configuration list for PBXNativeTarget "WidgetExtension" */;
|
||||||
|
buildPhases = (
|
||||||
|
628B951C2670CABD0091AF3B /* Sources */,
|
||||||
|
628B951D2670CABD0091AF3B /* Frameworks */,
|
||||||
|
628B951E2670CABD0091AF3B /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = WidgetExtension;
|
||||||
|
packageProductDependencies = (
|
||||||
|
628B95322670CAEA0091AF3B /* NukeUI */,
|
||||||
|
628B95342670CAEA0091AF3B /* JellyfinAPI */,
|
||||||
|
628B95392670CE250091AF3B /* KeychainSwift */,
|
||||||
|
);
|
||||||
|
productName = WidgetExtensionExtension;
|
||||||
|
productReference = 628B95202670CABD0091AF3B /* WidgetExtension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
|
@ -407,6 +514,9 @@
|
||||||
5377CBF0263B596A003A4E83 = {
|
5377CBF0263B596A003A4E83 = {
|
||||||
CreatedOnToolsVersion = 12.5;
|
CreatedOnToolsVersion = 12.5;
|
||||||
};
|
};
|
||||||
|
628B951F2670CABD0091AF3B = {
|
||||||
|
CreatedOnToolsVersion = 12.5;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 5377CBEC263B596A003A4E83 /* Build configuration list for PBXProject "JellyfinPlayer" */;
|
buildConfigurationList = 5377CBEC263B596A003A4E83 /* Build configuration list for PBXProject "JellyfinPlayer" */;
|
||||||
|
@ -430,6 +540,7 @@
|
||||||
targets = (
|
targets = (
|
||||||
5377CBF0263B596A003A4E83 /* JellyfinPlayer iOS */,
|
5377CBF0263B596A003A4E83 /* JellyfinPlayer iOS */,
|
||||||
5358705F2669D21600D05A09 /* JellyfinPlayer tvOS */,
|
5358705F2669D21600D05A09 /* JellyfinPlayer tvOS */,
|
||||||
|
628B951F2670CABD0091AF3B /* WidgetExtension */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
@ -456,6 +567,14 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
628B951E2670CABD0091AF3B /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
628B95292670CABE0091AF3B /* Assets.xcassets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
@ -463,7 +582,9 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */,
|
||||||
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
||||||
|
6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */,
|
||||||
535870A52669D8AE00D05A09 /* ParallaxHeader.swift in Sources */,
|
535870A52669D8AE00D05A09 /* ParallaxHeader.swift in Sources */,
|
||||||
535870A72669D8AE00D05A09 /* MultiSelectorView.swift in Sources */,
|
535870A72669D8AE00D05A09 /* MultiSelectorView.swift in Sources */,
|
||||||
535870A92669D8AE00D05A09 /* NukeExtensions.swift in Sources */,
|
535870A92669D8AE00D05A09 /* NukeExtensions.swift in Sources */,
|
||||||
|
@ -504,12 +625,14 @@
|
||||||
532175402671EE4F005491E6 /* LibraryFilterView.swift in Sources */,
|
532175402671EE4F005491E6 /* LibraryFilterView.swift in Sources */,
|
||||||
5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */,
|
5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */,
|
||||||
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
|
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
|
||||||
|
6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */,
|
||||||
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */,
|
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */,
|
||||||
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
||||||
53E4E649263F725B00F67C6B /* MultiSelectorView.swift in Sources */,
|
53E4E649263F725B00F67C6B /* MultiSelectorView.swift in Sources */,
|
||||||
621338B32660A07800A81A2A /* LazyView.swift in Sources */,
|
621338B32660A07800A81A2A /* LazyView.swift in Sources */,
|
||||||
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,
|
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,
|
||||||
53C4404E266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */,
|
53C4404E266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */,
|
||||||
|
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
|
||||||
5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */,
|
5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */,
|
||||||
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
|
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
|
||||||
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
|
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
|
||||||
|
@ -521,8 +644,32 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
628B951C2670CABD0091AF3B /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
6267B3D42671024A00A7371D /* APIExtensions.swift in Sources */,
|
||||||
|
6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */,
|
||||||
|
628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */,
|
||||||
|
6267B3DB2671139400A7371D /* ImageExtensions.swift in Sources */,
|
||||||
|
628B95372670CB800091AF3B /* JellyfinWidget.swift in Sources */,
|
||||||
|
6228B1C22670EB010067FD35 /* PersistenceController.swift in Sources */,
|
||||||
|
628B95272670CABD0091AF3B /* NextUpWidget.swift in Sources */,
|
||||||
|
62FA8A522671DE3C004BA2AB /* WidgetEnvironment.swift in Sources */,
|
||||||
|
628B95382670CDAB0091AF3B /* Model.xcdatamodeld in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
628B952C2670CABE0091AF3B /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 628B951F2670CABD0091AF3B /* WidgetExtension */;
|
||||||
|
targetProxy = 628B952B2670CABE0091AF3B /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
535870722669D21700D05A09 /* Debug */ = {
|
535870722669D21700D05A09 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
@ -704,6 +851,7 @@
|
||||||
5377CC1C263B596B003A4E83 /* Debug */ = {
|
5377CC1C263B596B003A4E83 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
||||||
|
@ -735,6 +883,7 @@
|
||||||
5377CC1D263B596B003A4E83 /* Release */ = {
|
5377CC1D263B596B003A4E83 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
||||||
|
@ -764,6 +913,56 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
628B952F2670CABE0091AF3B /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 36;
|
||||||
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin.widget;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
628B95302670CABE0091AF3B /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 36;
|
||||||
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin.widget;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
@ -794,6 +993,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
628B952E2670CABE0091AF3B /* Build configuration list for PBXNativeTarget "WidgetExtension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
628B952F2670CABE0091AF3B /* Debug */,
|
||||||
|
628B95302670CABE0091AF3B /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
@ -872,6 +1080,21 @@
|
||||||
package = 621C637E26672A30004216EA /* XCRemoteSwiftPackageReference "NukeUI" */;
|
package = 621C637E26672A30004216EA /* XCRemoteSwiftPackageReference "NukeUI" */;
|
||||||
productName = NukeUI;
|
productName = NukeUI;
|
||||||
};
|
};
|
||||||
|
628B95322670CAEA0091AF3B /* NukeUI */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 621C637E26672A30004216EA /* XCRemoteSwiftPackageReference "NukeUI" */;
|
||||||
|
productName = NukeUI;
|
||||||
|
};
|
||||||
|
628B95342670CAEA0091AF3B /* JellyfinAPI */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 53A431BB266B0FF20016769F /* XCRemoteSwiftPackageReference "jellyfin-sdk-swift" */;
|
||||||
|
productName = JellyfinAPI;
|
||||||
|
};
|
||||||
|
628B95392670CE250091AF3B /* KeychainSwift */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 5338F755263B7E2E0014BF09 /* XCRemoteSwiftPackageReference "keychain-swift" */;
|
||||||
|
productName = KeychainSwift;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
|
||||||
/* Begin XCVersionGroup section */
|
/* Begin XCVersionGroup section */
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1250"
|
||||||
|
wasCreatedForAppExtension = "YES"
|
||||||
|
version = "2.0">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "628B951F2670CABD0091AF3B"
|
||||||
|
BuildableName = "WidgetExtension.appex"
|
||||||
|
BlueprintName = "WidgetExtension"
|
||||||
|
ReferencedContainer = "container:JellyfinPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "5377CBF0263B596A003A4E83"
|
||||||
|
BuildableName = "JellyfinPlayer iOS.app"
|
||||||
|
BlueprintName = "JellyfinPlayer iOS"
|
||||||
|
ReferencedContainer = "container:JellyfinPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = ""
|
||||||
|
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||||
|
launchStyle = "0"
|
||||||
|
askForAppToLaunch = "Yes"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES"
|
||||||
|
launchAutomaticallySubstyle = "2">
|
||||||
|
<RemoteRunnable
|
||||||
|
runnableDebuggingMode = "2"
|
||||||
|
BundleIdentifier = "com.apple.springboard">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "628B951F2670CABD0091AF3B"
|
||||||
|
BuildableName = "WidgetExtension.appex"
|
||||||
|
BlueprintName = "WidgetExtension"
|
||||||
|
ReferencedContainer = "container:JellyfinPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</RemoteRunnable>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "5377CBF0263B596A003A4E83"
|
||||||
|
BuildableName = "JellyfinPlayer iOS.app"
|
||||||
|
BlueprintName = "JellyfinPlayer iOS"
|
||||||
|
ReferencedContainer = "container:JellyfinPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<EnvironmentVariables>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "_XCWidgetKind"
|
||||||
|
value = ""
|
||||||
|
isEnabled = "NO">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "_XCWidgetDefaultView"
|
||||||
|
value = "snapshot"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "_XCWidgetFamily"
|
||||||
|
value = "medium"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
</EnvironmentVariables>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
askForAppToLaunch = "Yes"
|
||||||
|
launchAutomaticallySubstyle = "2">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "5377CBF0263B596A003A4E83"
|
||||||
|
BuildableName = "JellyfinPlayer iOS.app"
|
||||||
|
BlueprintName = "JellyfinPlayer iOS"
|
||||||
|
ReferencedContainer = "container:JellyfinPlayer.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -11,6 +11,7 @@ import KeychainSwift
|
||||||
import Nuke
|
import Nuke
|
||||||
import Combine
|
import Combine
|
||||||
import JellyfinAPI
|
import JellyfinAPI
|
||||||
|
import WidgetKit
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@Environment(\.managedObjectContext) private var viewContext
|
@Environment(\.managedObjectContext) private var viewContext
|
||||||
|
@ -63,6 +64,7 @@ struct ContentView: View {
|
||||||
let savedUser = savedUsers[0]
|
let savedUser = savedUsers[0]
|
||||||
|
|
||||||
let keychain = KeychainSwift()
|
let keychain = KeychainSwift()
|
||||||
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
||||||
if keychain.get("AccessToken_\(savedUser.user_id ?? "")") != nil {
|
if keychain.get("AccessToken_\(savedUser.user_id ?? "")") != nil {
|
||||||
globalData.authToken = keychain.get("AccessToken_\(savedUser.user_id ?? "")") ?? ""
|
globalData.authToken = keychain.get("AccessToken_\(savedUser.user_id ?? "")") ?? ""
|
||||||
globalData.server = servers[0]
|
globalData.server = servers[0]
|
||||||
|
@ -126,6 +128,7 @@ struct ContentView: View {
|
||||||
defaults.setValue(40_000_000, forKey: "OutOfNetworkBandwidth")
|
defaults.setValue(40_000_000, forKey: "OutOfNetworkBandwidth")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
|
@ -6,7 +6,15 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.me.vigue.jellyfin.mobileclient</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)me.vigue.jellyfin.sharedKeychain</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -30,6 +30,9 @@ struct PersistenceController {
|
||||||
|
|
||||||
init(inMemory: Bool = false) {
|
init(inMemory: Bool = false) {
|
||||||
container = NSPersistentCloudKitContainer(name: "Model")
|
container = NSPersistentCloudKitContainer(name: "Model")
|
||||||
|
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: "group.me.vigue.jellyfin.mobileclient")!.appendingPathComponent("\(container.name).sqlite"))]
|
||||||
|
|
||||||
if inMemory {
|
if inMemory {
|
||||||
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
|
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
public extension Collection {
|
||||||
|
|
||||||
|
/// SwifterSwift: Safe protects the array from out of bounds by use of optional.
|
||||||
|
///
|
||||||
|
/// let arr = [1, 2, 3, 4, 5]
|
||||||
|
/// arr[safe: 1] -> 2
|
||||||
|
/// arr[safe: 10] -> nil
|
||||||
|
///
|
||||||
|
/// - Parameter index: index of element to access element.
|
||||||
|
subscript(safe index: Index) -> Element? {
|
||||||
|
return indices.contains(index) ? self[index] : nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension Image {
|
||||||
|
func centerCropped() -> some View {
|
||||||
|
GeometryReader { geo in
|
||||||
|
self
|
||||||
|
.resizable()
|
||||||
|
.scaledToFill()
|
||||||
|
.frame(width: geo.size.width, height: geo.size.height)
|
||||||
|
.clipped()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.me.vigue.jellyfin.mobileclient</string>
|
||||||
|
</array>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)me.vigue.jellyfin.sharedKeychain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.765",
|
||||||
|
"green" : "0.361",
|
||||||
|
"red" : "0.667"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "iphone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.765",
|
||||||
|
"green" : "0.361",
|
||||||
|
"red" : "0.667"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "tv"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 255 KiB |
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "1024.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.widgetkit-extension</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,18 @@
|
||||||
|
/* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import WidgetKit
|
||||||
|
import KeychainSwift
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct JellyfinWidgetBundle: WidgetBundle {
|
||||||
|
@WidgetBundleBuilder
|
||||||
|
var body: some Widget {
|
||||||
|
NextUpWidget()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,484 @@
|
||||||
|
/* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import JellyfinAPI
|
||||||
|
import Nuke
|
||||||
|
import SwiftUI
|
||||||
|
import WidgetKit
|
||||||
|
|
||||||
|
enum WidgetError: String, Error {
|
||||||
|
case unknown
|
||||||
|
case emptyServer
|
||||||
|
case emptyUser
|
||||||
|
case emptyHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NextUpWidgetProvider: TimelineProvider {
|
||||||
|
func placeholder(in context: Context) -> NextUpEntry {
|
||||||
|
NextUpEntry(date: Date(), items: [], error: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSnapshot(in context: Context, completion: @escaping (NextUpEntry) -> Void) {
|
||||||
|
let currentDate = Date()
|
||||||
|
WidgetEnvironment.shared.update()
|
||||||
|
guard let server = WidgetEnvironment.shared.server else { return
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion(NextUpEntry(date: currentDate, items: [], error: WidgetError.emptyServer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard let savedUser = WidgetEnvironment.shared.user else { return
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion(NextUpEntry(date: currentDate, items: [], error: WidgetError.emptyUser))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard let header = WidgetEnvironment.shared.header else { return
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion(NextUpEntry(date: currentDate, items: [], error: WidgetError.emptyHeader))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tempCancellables = Set<AnyCancellable>()
|
||||||
|
JellyfinAPI.basePath = server.baseURI ?? ""
|
||||||
|
JellyfinAPI.customHeaders = ["X-Emby-Authorization": header]
|
||||||
|
TvShowsAPI.getNextUp(userId: savedUser.user_id, limit: 3,
|
||||||
|
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
||||||
|
imageTypeLimit: 1, enableImageTypes: [.primary, .backdrop, .thumb])
|
||||||
|
.subscribe(on: DispatchQueue.global(qos: .background))
|
||||||
|
.sink(receiveCompletion: { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
break
|
||||||
|
case let .failure(error):
|
||||||
|
completion(NextUpEntry(date: currentDate, items: [], error: error))
|
||||||
|
}
|
||||||
|
}, receiveValue: { response in
|
||||||
|
let dispatchGroup = DispatchGroup()
|
||||||
|
let items = response.items ?? []
|
||||||
|
var downloadedItems = [(BaseItemDto, UIImage?)]()
|
||||||
|
items.enumerated().forEach { _, item in
|
||||||
|
dispatchGroup.enter()
|
||||||
|
ImagePipeline.shared.loadImage(with: item.getBackdropImage(baseURL: server.baseURI ?? "", maxWidth: 320)) { result in
|
||||||
|
guard case let .success(image) = result else {
|
||||||
|
dispatchGroup.leave()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
downloadedItems.append((item, image.image))
|
||||||
|
dispatchGroup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchGroup.notify(queue: .main) {
|
||||||
|
completion(NextUpEntry(date: currentDate, items: downloadedItems, error: nil))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.store(in: &tempCancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
|
||||||
|
let currentDate = Date()
|
||||||
|
let entryDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!
|
||||||
|
WidgetEnvironment.shared.update()
|
||||||
|
guard let server = WidgetEnvironment.shared.server else { return
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion(Timeline(entries: [NextUpEntry(date: currentDate, items: [], error: WidgetError.emptyServer)],
|
||||||
|
policy: .after(entryDate)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard let savedUser = WidgetEnvironment.shared.user else { return
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion(Timeline(entries: [NextUpEntry(date: currentDate, items: [], error: WidgetError.emptyUser)],
|
||||||
|
policy: .after(entryDate)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard let header = WidgetEnvironment.shared.header else { return
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion(Timeline(entries: [NextUpEntry(date: currentDate, items: [], error: WidgetError.emptyHeader)],
|
||||||
|
policy: .after(entryDate)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tempCancellables = Set<AnyCancellable>()
|
||||||
|
JellyfinAPI.basePath = server.baseURI ?? ""
|
||||||
|
JellyfinAPI.customHeaders = ["X-Emby-Authorization": header]
|
||||||
|
TvShowsAPI.getNextUp(userId: savedUser.user_id, limit: 3,
|
||||||
|
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
||||||
|
imageTypeLimit: 1, enableImageTypes: [.primary, .backdrop, .thumb])
|
||||||
|
.subscribe(on: DispatchQueue.global(qos: .background))
|
||||||
|
.sink(receiveCompletion: { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
break
|
||||||
|
case let .failure(error):
|
||||||
|
completion(Timeline(entries: [NextUpEntry(date: currentDate, items: [], error: error)], policy: .after(entryDate)))
|
||||||
|
}
|
||||||
|
}, receiveValue: { response in
|
||||||
|
let dispatchGroup = DispatchGroup()
|
||||||
|
let items = response.items ?? []
|
||||||
|
var downloadedItems = [(BaseItemDto, UIImage?)]()
|
||||||
|
items.enumerated().forEach { _, item in
|
||||||
|
dispatchGroup.enter()
|
||||||
|
ImagePipeline.shared.loadImage(with: item.getBackdropImage(baseURL: server.baseURI ?? "", maxWidth: 320)) { result in
|
||||||
|
guard case let .success(image) = result else {
|
||||||
|
dispatchGroup.leave()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
downloadedItems.append((item, image.image))
|
||||||
|
dispatchGroup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchGroup.notify(queue: .main) {
|
||||||
|
completion(Timeline(entries: [NextUpEntry(date: currentDate, items: downloadedItems, error: nil)],
|
||||||
|
policy: .after(entryDate)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.store(in: &tempCancellables)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NextUpEntry: TimelineEntry {
|
||||||
|
let date: Date
|
||||||
|
let items: [(BaseItemDto, UIImage?)]
|
||||||
|
let error: Error?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NextUpEntryView: View {
|
||||||
|
var entry: NextUpWidgetProvider.Entry
|
||||||
|
|
||||||
|
@Environment(\.widgetFamily)
|
||||||
|
var family
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if let error = entry.error {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "exclamationmark.octagon")
|
||||||
|
Text((error as? WidgetError)?.rawValue ?? "")
|
||||||
|
}
|
||||||
|
.background(Color.blue)
|
||||||
|
} else if entry.items.isEmpty {
|
||||||
|
Text("Empty Next Up")
|
||||||
|
.font(.body)
|
||||||
|
.bold()
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
|
||||||
|
} else {
|
||||||
|
switch family {
|
||||||
|
case .systemSmall:
|
||||||
|
small(item: entry.items.first)
|
||||||
|
case .systemMedium:
|
||||||
|
medium(items: entry.items)
|
||||||
|
case .systemLarge:
|
||||||
|
large(items: entry.items)
|
||||||
|
@unknown default:
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(Color(.secondarySystemBackground))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NextUpEntryView {
|
||||||
|
var smallVideoPlaceholderView: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Color(.systemGray)
|
||||||
|
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
|
||||||
|
.cornerRadius(8)
|
||||||
|
.shadow(radius: 8)
|
||||||
|
Color(.systemGray2)
|
||||||
|
.frame(width: 100, height: 10)
|
||||||
|
Color(.systemGray3)
|
||||||
|
.frame(width: 80, height: 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var largeVideoPlaceholderView: some View {
|
||||||
|
HStack(spacing: 20) {
|
||||||
|
Color(.systemGray)
|
||||||
|
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
|
||||||
|
.cornerRadius(8)
|
||||||
|
.shadow(radius: 8)
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Color(.systemGray2)
|
||||||
|
.frame(width: 100, height: 10)
|
||||||
|
Color(.systemGray3)
|
||||||
|
.frame(width: 80, height: 10)
|
||||||
|
}
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NextUpEntryView {
|
||||||
|
var headerSymbol: some View {
|
||||||
|
Image("WidgetHeaderSymbol")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 12, height: 12)
|
||||||
|
.cornerRadius(4)
|
||||||
|
.shadow(radius: 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func smallVideoView(item: (BaseItemDto, UIImage?)) -> some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
if let image = item.1 {
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
|
||||||
|
.clipped()
|
||||||
|
.cornerRadius(8)
|
||||||
|
.shadow(radius: 8)
|
||||||
|
}
|
||||||
|
Text(item.0.seriesName ?? "")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.lineLimit(1)
|
||||||
|
Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func largeVideoView(item: (BaseItemDto, UIImage?)) -> some View {
|
||||||
|
HStack(spacing: 20) {
|
||||||
|
if let image = item.1 {
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
|
||||||
|
.clipped()
|
||||||
|
.cornerRadius(8)
|
||||||
|
.shadow(radius: 8)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(item.0.seriesName ?? "")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
|
Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NextUpEntryView {
|
||||||
|
func small(item: (BaseItemDto, UIImage?)?) -> some View {
|
||||||
|
VStack(alignment: .trailing) {
|
||||||
|
headerSymbol
|
||||||
|
if let item = item {
|
||||||
|
smallVideoView(item: item)
|
||||||
|
} else {
|
||||||
|
smallVideoPlaceholderView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(12)
|
||||||
|
}
|
||||||
|
|
||||||
|
func medium(items: [(BaseItemDto, UIImage?)]) -> some View {
|
||||||
|
VStack(alignment: .trailing) {
|
||||||
|
headerSymbol
|
||||||
|
HStack(spacing: 16) {
|
||||||
|
if let firstItem = items[safe: 0] {
|
||||||
|
smallVideoView(item: firstItem)
|
||||||
|
} else {
|
||||||
|
smallVideoPlaceholderView
|
||||||
|
}
|
||||||
|
if let secondItem = items[safe: 1] {
|
||||||
|
smallVideoView(item: secondItem)
|
||||||
|
} else {
|
||||||
|
smallVideoPlaceholderView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(12)
|
||||||
|
}
|
||||||
|
|
||||||
|
func large(items: [(BaseItemDto, UIImage?)]) -> some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
if let firstItem = items[safe: 0] {
|
||||||
|
ZStack(alignment: .topTrailing) {
|
||||||
|
ZStack(alignment: .bottomLeading) {
|
||||||
|
if let image = firstItem.1 {
|
||||||
|
Image(uiImage: image)
|
||||||
|
.centerCropped()
|
||||||
|
.innerShadow(color: Color.black.opacity(0.5), radius: 0.5)
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(firstItem.0.seriesName ?? "")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
|
Text("\(firstItem.0.name ?? "") · S\(firstItem.0.parentIndexNumber ?? 0):E\(firstItem.0.indexNumber ?? 0)")
|
||||||
|
.font(.caption)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
.shadow(radius: 8)
|
||||||
|
.padding(12)
|
||||||
|
}
|
||||||
|
headerSymbol
|
||||||
|
.padding(12)
|
||||||
|
}
|
||||||
|
.clipped()
|
||||||
|
.shadow(radius: 8)
|
||||||
|
}
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
if let secondItem = items[safe: 1] {
|
||||||
|
largeVideoView(item: secondItem)
|
||||||
|
} else {
|
||||||
|
largeVideoPlaceholderView
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
if let thirdItem = items[safe: 2] {
|
||||||
|
largeVideoView(item: thirdItem)
|
||||||
|
} else {
|
||||||
|
largeVideoPlaceholderView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(12)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NextUpWidget: Widget {
|
||||||
|
let kind: String = "NextUpWidget"
|
||||||
|
|
||||||
|
var body: some WidgetConfiguration {
|
||||||
|
StaticConfiguration(kind: kind,
|
||||||
|
provider: NextUpWidgetProvider()) { entry in
|
||||||
|
NextUpEntryView(entry: entry)
|
||||||
|
}
|
||||||
|
.configurationDisplayName("Next Up")
|
||||||
|
.description("Keep watching where you left off or see what's up next.")
|
||||||
|
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NextUpWidget_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
Group {
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol"))],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [
|
||||||
|
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemMedium))
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [
|
||||||
|
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemLarge))
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol"))],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [
|
||||||
|
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemMedium))
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [
|
||||||
|
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemLarge))
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [
|
||||||
|
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemMedium))
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
NextUpEntryView(entry: .init(date: Date(),
|
||||||
|
items: [
|
||||||
|
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
|
||||||
|
UIImage(named: "WidgetHeaderSymbol")),
|
||||||
|
],
|
||||||
|
error: nil))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemLarge))
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
private extension View {
|
||||||
|
func innerShadow(color: Color, radius: CGFloat = 0.1) -> some View {
|
||||||
|
modifier(InnerShadow(color: color, radius: min(max(0, radius), 1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct InnerShadow: ViewModifier {
|
||||||
|
var color: Color = .gray
|
||||||
|
var radius: CGFloat = 0.1
|
||||||
|
|
||||||
|
private var colors: [Color] {
|
||||||
|
[color.opacity(0.75), color.opacity(0.0), .clear]
|
||||||
|
}
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
GeometryReader { geo in
|
||||||
|
content
|
||||||
|
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .top, endPoint: .bottom)
|
||||||
|
.frame(height: self.radius * self.minSide(geo)),
|
||||||
|
alignment: .top)
|
||||||
|
.overlay(LinearGradient(gradient: Gradient(colors: self.colors), startPoint: .bottom, endPoint: .top)
|
||||||
|
.frame(height: self.radius * self.minSide(geo)),
|
||||||
|
alignment: .bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func minSide(_ geo: GeometryProxy) -> CGFloat {
|
||||||
|
CGFloat(3) * min(geo.size.width, geo.size.height) / 2
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* SwiftFin is subject to the terms of the Mozilla Public
|
||||||
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
import KeychainSwift
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
final class WidgetEnvironment {
|
||||||
|
static let shared = WidgetEnvironment()
|
||||||
|
|
||||||
|
var server: Server!
|
||||||
|
var user: SignedInUser!
|
||||||
|
var header: String?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
func update() {
|
||||||
|
let serverRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Server")
|
||||||
|
let servers = try? PersistenceController.shared.container.viewContext.fetch(serverRequest) as? [Server]
|
||||||
|
let savedUserRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "SignedInUser")
|
||||||
|
let savedUsers = try? PersistenceController.shared.container.viewContext.fetch(savedUserRequest) as? [SignedInUser]
|
||||||
|
|
||||||
|
server = servers?.first
|
||||||
|
user = savedUsers?.first
|
||||||
|
|
||||||
|
let keychain = KeychainSwift()
|
||||||
|
// need prefix
|
||||||
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
||||||
|
guard let authToken = keychain.get("AccessToken_\(user?.user_id ?? "")") else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||||
|
var deviceName = UIDevice.current.name
|
||||||
|
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
|
||||||
|
deviceName = deviceName.removeRegexMatches(pattern: "[^\\w\\s]")
|
||||||
|
|
||||||
|
var header = "MediaBrowser "
|
||||||
|
header.append("Client=\"SwiftFin\", ")
|
||||||
|
header.append("Device=\"\(deviceName)\", ")
|
||||||
|
header.append("DeviceId=\"\(user?.device_uuid ?? "")\", ")
|
||||||
|
header.append("Version=\"\(appVersion ?? "0.0.1")\", ")
|
||||||
|
header.append("Token=\"\(authToken)\"")
|
||||||
|
|
||||||
|
self.header = header
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.me.vigue.jellyfin.mobileclient</string>
|
||||||
|
</array>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)me.vigue.jellyfin.sharedKeychain</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
Loading…
Reference in New Issue