Merge branch 'main' into main
This commit is contained in:
commit
5f6be60f42
|
@ -40,6 +40,7 @@
|
||||||
53E4E647263F6CF100F67C6B /* LibraryFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */; };
|
53E4E647263F6CF100F67C6B /* LibraryFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */; };
|
||||||
53E4E649263F725B00F67C6B /* MultiSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E648263F725B00F67C6B /* MultiSelector.swift */; };
|
53E4E649263F725B00F67C6B /* MultiSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E648263F725B00F67C6B /* MultiSelector.swift */; };
|
||||||
53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EE24E5265060780068F029 /* LibrarySearchView.swift */; };
|
53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EE24E5265060780068F029 /* LibrarySearchView.swift */; };
|
||||||
|
53F8377D265FF67C00F456B3 /* VideoPlayerSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */; };
|
||||||
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */; };
|
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */; };
|
||||||
6213388E265F777C00A81A2A /* LibraryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388D265F777C00A81A2A /* LibraryViewModel.swift */; };
|
6213388E265F777C00A81A2A /* LibraryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388D265F777C00A81A2A /* LibraryViewModel.swift */; };
|
||||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* LibraryListView.swift */; };
|
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* LibraryListView.swift */; };
|
||||||
|
@ -106,6 +107,7 @@
|
||||||
53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryFilterView.swift; sourceTree = "<group>"; };
|
53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryFilterView.swift; sourceTree = "<group>"; };
|
||||||
53E4E648263F725B00F67C6B /* MultiSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSelector.swift; sourceTree = "<group>"; };
|
53E4E648263F725B00F67C6B /* MultiSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSelector.swift; sourceTree = "<group>"; };
|
||||||
53EE24E5265060780068F029 /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; };
|
53EE24E5265060780068F029 /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = "<group>"; };
|
||||||
|
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerSettingsView.swift; sourceTree = "<group>"; };
|
||||||
53FF7F29263CF3F500585C35 /* LatestMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaView.swift; sourceTree = "<group>"; };
|
53FF7F29263CF3F500585C35 /* LatestMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaView.swift; sourceTree = "<group>"; };
|
||||||
6213388D265F777C00A81A2A /* LibraryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryViewModel.swift; sourceTree = "<group>"; };
|
6213388D265F777C00A81A2A /* LibraryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryViewModel.swift; sourceTree = "<group>"; };
|
||||||
6213388F265F83A900A81A2A /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
|
6213388F265F83A900A81A2A /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -273,6 +275,7 @@
|
||||||
535BAEA4264A151C005FA86D /* VideoPlayer.swift */,
|
535BAEA4264A151C005FA86D /* VideoPlayer.swift */,
|
||||||
539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */,
|
539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */,
|
||||||
53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */,
|
53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */,
|
||||||
|
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -401,6 +404,7 @@
|
||||||
5377CBFE263B596B003A4E83 /* PersistenceController.swift in Sources */,
|
5377CBFE263B596B003A4E83 /* PersistenceController.swift in Sources */,
|
||||||
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
||||||
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */,
|
||||||
|
53F8377D265FF67C00F456B3 /* VideoPlayerSettingsView.swift in Sources */,
|
||||||
53987CA426572C1300E7EA70 /* SeasonItemView.swift in Sources */,
|
53987CA426572C1300E7EA70 /* SeasonItemView.swift in Sources */,
|
||||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */,
|
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */,
|
||||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
||||||
|
@ -557,7 +561,7 @@
|
||||||
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_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 22;
|
CURRENT_PROJECT_VERSION = 23;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
@ -583,7 +587,7 @@
|
||||||
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_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 22;
|
CURRENT_PROJECT_VERSION = 23;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>22</string>
|
<string>23</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="ipad11_0rounded" orientation="portrait" layout="fullscreen" appearance="light"/>
|
<device id="retina6_5" orientation="landscape" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
|
||||||
<capability name="Image references" minToolsVersion="12.0"/>
|
<capability name="Image references" minToolsVersion="12.0"/>
|
||||||
|
@ -14,10 +14,10 @@
|
||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="VideoPlayer" id="Y6W-OH-hqX" customClass="PlayerViewController" customModule="JellyfinPlayer" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="VideoPlayer" id="Y6W-OH-hqX" customClass="PlayerViewController" customModule="JellyfinPlayer" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" autoresizesSubviews="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IQg-r0-AeH">
|
<view key="view" autoresizesSubviews="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IQg-r0-AeH">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="834" height="1194"/>
|
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view autoresizesSubviews="NO" tag="1" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tsh-rC-BwO" userLabel="VideoContentView">
|
<view autoresizesSubviews="NO" tag="1" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tsh-rC-BwO" userLabel="VideoContentView">
|
||||||
<rect key="frame" x="-13" y="0.0" width="860" height="1194"/>
|
<rect key="frame" x="31" y="0.0" width="834" height="414"/>
|
||||||
<viewLayoutGuide key="safeArea" id="aVY-BC-PZU"/>
|
<viewLayoutGuide key="safeArea" id="aVY-BC-PZU"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<gestureRecognizers/>
|
<gestureRecognizers/>
|
||||||
|
@ -26,10 +26,10 @@
|
||||||
</connections>
|
</connections>
|
||||||
</view>
|
</view>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Qcb-Fb-qZl" userLabel="VideoControlsView">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Qcb-Fb-qZl" userLabel="VideoControlsView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="834" height="1194"/>
|
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="e9f-8l-RdN" userLabel="SeekSlider">
|
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="e9f-8l-RdN" userLabel="SeekSlider">
|
||||||
<rect key="frame" x="50" y="1135" width="627" height="31"/>
|
<rect key="frame" x="50" y="355" width="689" height="31"/>
|
||||||
<color key="tintColor" red="0.66666666666666663" green="0.36078431372549019" blue="0.76470588235294112" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="tintColor" red="0.66666666666666663" green="0.36078431372549019" blue="0.76470588235294112" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<color key="thumbTintColor" red="0.66666666666666663" green="0.36078431372549019" blue="0.76470588235294112" alpha="1" colorSpace="calibratedRGB"/>
|
<color key="thumbTintColor" red="0.66666666666666663" green="0.36078431372549019" blue="0.76470588235294112" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
</connections>
|
</connections>
|
||||||
</slider>
|
</slider>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="-:--:--" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qft-iu-f1z">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="-:--:--" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qft-iu-f1z">
|
||||||
<rect key="frame" x="704" y="1133" width="91" height="34"/>
|
<rect key="frame" x="766" y="353" width="91" height="34"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="91" id="LbL-h0-EYA"/>
|
<constraint firstAttribute="width" constant="91" id="LbL-h0-EYA"/>
|
||||||
<constraint firstAttribute="height" constant="34" id="OkD-Dr-Ina"/>
|
<constraint firstAttribute="height" constant="34" id="OkD-Dr-Ina"/>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="t2L-Oz-fe9" userLabel="MainActionButton">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="t2L-Oz-fe9" userLabel="MainActionButton">
|
||||||
<rect key="frame" x="375.5" y="555.5" width="83" height="83"/>
|
<rect key="frame" x="406.66666666666669" y="165.66666666666666" width="83" height="83"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="83" id="PdD-nW-y9r"/>
|
<constraint firstAttribute="width" constant="83" id="PdD-nW-y9r"/>
|
||||||
<constraint firstAttribute="height" constant="83" id="e9j-PI-Ic4"/>
|
<constraint firstAttribute="height" constant="83" id="e9j-PI-Ic4"/>
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bYM-Xp-bZO">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bYM-Xp-bZO">
|
||||||
<rect key="frame" x="182" y="559" width="75" height="76"/>
|
<rect key="frame" x="213" y="169" width="75" height="76"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="76" id="5lC-V1-lHH"/>
|
<constraint firstAttribute="height" constant="76" id="5lC-V1-lHH"/>
|
||||||
<constraint firstAttribute="width" constant="75" id="IPn-pO-Rxo"/>
|
<constraint firstAttribute="width" constant="75" id="IPn-pO-Rxo"/>
|
||||||
|
@ -95,7 +95,7 @@
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="An8-jF-FAY">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="An8-jF-FAY">
|
||||||
<rect key="frame" x="577" y="559" width="75" height="76"/>
|
<rect key="frame" x="608" y="169" width="75" height="76"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="76" id="huv-QZ-HSc"/>
|
<constraint firstAttribute="height" constant="76" id="huv-QZ-HSc"/>
|
||||||
<constraint firstAttribute="width" constant="75" id="uPN-A8-EV1"/>
|
<constraint firstAttribute="width" constant="75" id="uPN-A8-EV1"/>
|
||||||
|
@ -109,16 +109,19 @@
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="riN-y1-ABZ">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="riN-y1-ABZ">
|
||||||
<rect key="frame" x="739" y="24" width="56" height="56"/>
|
<rect key="frame" x="801" y="24" width="56" height="56"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||||
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<state key="normal" image="gear" catalog="system">
|
<state key="normal" image="gear" catalog="system">
|
||||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="23"/>
|
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="23"/>
|
||||||
</state>
|
</state>
|
||||||
|
<connections>
|
||||||
|
<action selector="settingsButtonTapped:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="NeC-px-2TY"/>
|
||||||
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Loading" textAlignment="center" lineBreakMode="tailTruncation" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="o8N-R1-DhT">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Loading" textAlignment="center" lineBreakMode="tailTruncation" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="o8N-R1-DhT">
|
||||||
<rect key="frame" x="106" y="-367" width="622" height="838"/>
|
<rect key="frame" x="106" y="23.333333333333332" width="684" height="57.666666666666671"/>
|
||||||
<accessibility key="accessibilityConfiguration">
|
<accessibility key="accessibilityConfiguration">
|
||||||
<accessibilityTraits key="traits" staticText="YES" header="YES"/>
|
<accessibilityTraits key="traits" staticText="YES" header="YES"/>
|
||||||
</accessibility>
|
</accessibility>
|
||||||
|
|
|
@ -39,7 +39,7 @@ protocol PlayerViewControllerDelegate: AnyObject {
|
||||||
func exitPlayer(_ viewController: PlayerViewController)
|
func exitPlayer(_ viewController: PlayerViewController)
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDelegate {
|
class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDelegate, VideoPlayerSettingsDelegate {
|
||||||
|
|
||||||
weak var delegate: PlayerViewControllerDelegate?
|
weak var delegate: PlayerViewControllerDelegate?
|
||||||
|
|
||||||
|
@ -153,17 +153,54 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func settingsButtonTapped(_ sender: UIButton) {
|
||||||
|
let optionsVC = VideoPlayerSettingsView()
|
||||||
|
print(self.selectedAudioTrack)
|
||||||
|
print(self.selectedCaptionTrack)
|
||||||
|
optionsVC.currentSubtitleTrack = self.selectedCaptionTrack
|
||||||
|
optionsVC.currentAudioTrack = self.selectedAudioTrack
|
||||||
|
optionsVC.delegate = self;
|
||||||
|
optionsVC.subtitles = subtitleTrackArray
|
||||||
|
optionsVC.audioTracks = audioTrackArray
|
||||||
|
// Use the popover presentation style for your view controller.
|
||||||
|
optionsVC.modalPresentationStyle = .popover
|
||||||
|
|
||||||
|
// Specify the anchor point for the popover.
|
||||||
|
optionsVC.popoverPresentationController?.sourceView = playerSettingsButton
|
||||||
|
|
||||||
|
// Present the view controller (in a popover).
|
||||||
|
self.present(optionsVC, animated: true) {
|
||||||
|
print("popover visible, pause playback")
|
||||||
|
self.mediaPlayer.pause()
|
||||||
|
self.mainActionButton.setImage(UIImage(systemName: "play"), for: .normal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func settingsPopoverDismissed() {
|
||||||
|
self.mediaPlayer.play()
|
||||||
|
self.mainActionButton.setImage(UIImage(systemName: "pause"), for: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
//View has loaded.
|
//View has loaded.
|
||||||
//Show loading screen
|
//Show loading screen
|
||||||
usleep(10000);
|
|
||||||
delegate?.showLoadingView(self)
|
delegate?.showLoadingView(self)
|
||||||
|
|
||||||
mediaPlayer.perform(Selector(("setTextRendererFontSize:")), with: 14)
|
mediaPlayer.perform(Selector(("setTextRendererFontSize:")), with: 14)
|
||||||
//mediaPlayer.wrappedValue.perform(Selector(("setTextRendererFont:")), with: "Copperplate")
|
//mediaPlayer.wrappedValue.perform(Selector(("setTextRendererFont:")), with: "Copperplate")
|
||||||
|
|
||||||
|
|
||||||
|
mediaPlayer.delegate = self
|
||||||
|
mediaPlayer.drawable = videoContentView
|
||||||
|
|
||||||
|
if(manifest.Type == "Episode") {
|
||||||
|
titleLabel.text = "\(manifest.Name) - S\(String(manifest.ParentIndexNumber ?? 0)):E\(String(manifest.IndexNumber ?? 0)) - \(manifest.SeriesName ?? "")"
|
||||||
|
} else {
|
||||||
|
titleLabel.text = manifest.Name
|
||||||
|
}
|
||||||
|
|
||||||
//Fetch max bitrate from UserDefaults depending on current connection mode
|
//Fetch max bitrate from UserDefaults depending on current connection mode
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
let maxBitrate = globalData.isInNetwork ? defaults.integer(forKey: "InNetworkBandwidth") : defaults.integer(forKey: "OutOfNetworkBandwidth")
|
let maxBitrate = globalData.isInNetwork ? defaults.integer(forKey: "InNetworkBandwidth") : defaults.integer(forKey: "OutOfNetworkBandwidth")
|
||||||
|
@ -258,24 +295,23 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
sendPlayReport()
|
sendPlayReport()
|
||||||
playbackItem = item;
|
playbackItem = item;
|
||||||
}
|
}
|
||||||
mediaPlayer.stop()
|
|
||||||
DispatchQueue.global(qos: .background).async {
|
DispatchQueue.global(qos: .background).async {
|
||||||
|
mediaPlayer.media = VLCMedia(url: playbackItem.videoUrl)
|
||||||
mediaPlayer.play()
|
mediaPlayer.play()
|
||||||
|
mediaPlayer.jumpForward(Int32(manifest.Progress/10000000))
|
||||||
subtitleTrackArray.forEach() { sub in
|
subtitleTrackArray.forEach() { sub in
|
||||||
if(sub.id != -1 && sub.delivery == "External" && sub.codec != "subrip") {
|
if(sub.id != -1 && sub.delivery == "External" && sub.codec != "subrip") {
|
||||||
print("adding subs for id: \(sub.id) w/ url: \(sub.url)")
|
print("adding subs for id: \(sub.id) w/ url: \(sub.url)")
|
||||||
mediaPlayer.addPlaybackSlave(sub.url, type: .subtitle, enforce: false)
|
mediaPlayer.addPlaybackSlave(sub.url, type: .subtitle, enforce: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sleep(3)
|
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
usleep(10000);
|
delegate?.showLoadingView(self)
|
||||||
mediaPlayer.play()
|
sleep(3)
|
||||||
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack;
|
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack;
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
usleep(10000);
|
|
||||||
mediaPlayer.play()
|
mediaPlayer.play()
|
||||||
mediaPlayer.jumpForward(Int32(manifest.Progress/10000000))
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
||||||
|
@ -286,15 +322,6 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaPlayer.delegate = self
|
|
||||||
mediaPlayer.drawable = videoContentView
|
|
||||||
|
|
||||||
if(manifest.Type == "Episode") {
|
|
||||||
titleLabel.text = "\(manifest.Name) - S\(String(manifest.ParentIndexNumber ?? 0)):E\(String(manifest.IndexNumber ?? 0)) - \(manifest.SeriesName ?? "")"
|
|
||||||
} else {
|
|
||||||
titleLabel.text = manifest.Name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -302,17 +329,24 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
self.tabBarController?.tabBar.isHidden = true;
|
self.tabBarController?.tabBar.isHidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: VideoPlayerSettings Delegate
|
||||||
|
func subtitleTrackChanged(newTrackID: Int32) {
|
||||||
|
mediaPlayer.currentVideoSubTitleIndex = newTrackID
|
||||||
|
}
|
||||||
|
|
||||||
|
func audioTrackChanged(newTrackID: Int32) {
|
||||||
|
mediaPlayer.currentAudioTrackIndex = newTrackID
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//MARK: VLCMediaPlayer Delegates
|
//MARK: VLCMediaPlayer Delegates
|
||||||
func mediaPlayerStateChanged(_ aNotification: Notification!) {
|
func mediaPlayerStateChanged(_ aNotification: Notification!) {
|
||||||
let currentState: VLCMediaPlayerState = mediaPlayer.state
|
let currentState: VLCMediaPlayerState = mediaPlayer.state
|
||||||
switch currentState {
|
switch currentState {
|
||||||
case .stopped :
|
case .stopped :
|
||||||
print("Video is done playing)")
|
break;
|
||||||
sendStopReport()
|
|
||||||
case .ended :
|
case .ended :
|
||||||
print("Video is done playing)")
|
break;
|
||||||
sendStopReport()
|
|
||||||
case .playing :
|
case .playing :
|
||||||
print("Video is playing")
|
print("Video is playing")
|
||||||
sendProgressReport(eventName: "unpause")
|
sendProgressReport(eventName: "unpause")
|
||||||
|
@ -328,7 +362,6 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
|
|
||||||
case .buffering :
|
case .buffering :
|
||||||
print("Video is buffering)")
|
print("Video is buffering)")
|
||||||
sendProgressReport(eventName: "pause")
|
|
||||||
delegate?.showLoadingView(self)
|
delegate?.showLoadingView(self)
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
usleep(10000)
|
usleep(10000)
|
||||||
|
@ -348,6 +381,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
let time = mediaPlayer.position;
|
let time = mediaPlayer.position;
|
||||||
if(time != lastTime) {
|
if(time != lastTime) {
|
||||||
paused = false;
|
paused = false;
|
||||||
|
mainActionButton.setImage(UIImage(systemName: "pause"), for: .normal)
|
||||||
seekSlider.setValue(mediaPlayer.position, animated: true)
|
seekSlider.setValue(mediaPlayer.position, animated: true)
|
||||||
delegate?.hideLoadingView(self)
|
delegate?.hideLoadingView(self)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
//
|
||||||
|
// VideoPlayerSettingsView.swift
|
||||||
|
// JellyfinPlayer
|
||||||
|
//
|
||||||
|
// Created by Aiden Vigue on 5/27/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import SwiftUI
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
enum SettingsChangedEventTypes {
|
||||||
|
case subTrackChanged
|
||||||
|
case audioTrackChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
struct settingsChangedEvent {
|
||||||
|
let eventType: SettingsChangedEventTypes
|
||||||
|
let payload: AnyObject
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol VideoPlayerSettingsDelegate: AnyObject {
|
||||||
|
func subtitleTrackChanged(newTrackID: Int32)
|
||||||
|
func audioTrackChanged(newTrackID: Int32)
|
||||||
|
func settingsPopoverDismissed()
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsViewDelegate: ObservableObject {
|
||||||
|
|
||||||
|
var subtitlesDidChange = PassthroughSubject<SettingsViewDelegate, Never>()
|
||||||
|
|
||||||
|
var subtitleTrackID: Int32 = 0 {
|
||||||
|
didSet {
|
||||||
|
self.subtitlesDidChange.send(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var audioTrackDidChange = PassthroughSubject<SettingsViewDelegate, Never>()
|
||||||
|
|
||||||
|
var audioTrackID: Int32 = 0 {
|
||||||
|
didSet {
|
||||||
|
self.audioTrackDidChange.send(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var shouldClose = PassthroughSubject<SettingsViewDelegate, Never>()
|
||||||
|
|
||||||
|
var close: Bool = false {
|
||||||
|
didSet {
|
||||||
|
self.shouldClose.send(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoPlayerSettingsView: UIViewController {
|
||||||
|
private var ctntView: VideoPlayerSettings!
|
||||||
|
private var contentViewDelegate: SettingsViewDelegate = SettingsViewDelegate()
|
||||||
|
weak var delegate: VideoPlayerSettingsDelegate?
|
||||||
|
private var subChangePublisher: AnyCancellable?
|
||||||
|
private var audioChangePublisher: AnyCancellable?
|
||||||
|
private var shouldClosePublisher: AnyCancellable?
|
||||||
|
var subtitles: [Subtitle] = []
|
||||||
|
var audioTracks: [AudioTrack] = []
|
||||||
|
var currentSubtitleTrack: Int32!
|
||||||
|
var currentAudioTrack: Int32!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
ctntView = VideoPlayerSettings(delegate: self.contentViewDelegate, subtitles: self.subtitles, audioTracks: self.audioTracks, initSub: currentSubtitleTrack, initAudio: currentAudioTrack)
|
||||||
|
let contentView = UIHostingController(rootView: ctntView)
|
||||||
|
self.view.addSubview(contentView.view)
|
||||||
|
contentView.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
contentView.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
|
||||||
|
contentView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||||
|
contentView.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
|
||||||
|
contentView.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
|
||||||
|
|
||||||
|
self.subChangePublisher = self.contentViewDelegate.subtitlesDidChange.sink { suiDelegate in
|
||||||
|
self.delegate?.subtitleTrackChanged(newTrackID: suiDelegate.subtitleTrackID)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.audioChangePublisher = self.contentViewDelegate.audioTrackDidChange.sink { suiDelegate in
|
||||||
|
self.delegate?.audioTrackChanged(newTrackID: suiDelegate.audioTrackID)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.shouldClosePublisher = self.contentViewDelegate.shouldClose.sink { suiDelegate in
|
||||||
|
if(suiDelegate.close == true) {
|
||||||
|
self.delegate?.settingsPopoverDismissed()
|
||||||
|
self.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
delegate?.settingsPopoverDismissed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VideoPlayerSettings: View {
|
||||||
|
@ObservedObject var delegate: SettingsViewDelegate
|
||||||
|
@State private var subtitles: [Subtitle]
|
||||||
|
@State private var audioTracks: [AudioTrack]
|
||||||
|
@State private var subtitleSelection: Int32
|
||||||
|
@State private var audioTrackSelection: Int32
|
||||||
|
|
||||||
|
init(delegate: SettingsViewDelegate, subtitles: [Subtitle], audioTracks: [AudioTrack], initSub: Int32, initAudio: Int32) {
|
||||||
|
self.delegate = delegate
|
||||||
|
self.subtitles = subtitles
|
||||||
|
self.audioTracks = audioTracks
|
||||||
|
|
||||||
|
self.subtitleSelection = initSub
|
||||||
|
self.audioTrackSelection = initAudio
|
||||||
|
print(initSub)
|
||||||
|
print(initAudio)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { proxy in
|
||||||
|
NavigationView() {
|
||||||
|
Form() {
|
||||||
|
Picker("Closed Captions", selection: $subtitleSelection) {
|
||||||
|
ForEach(subtitles, id: \.id) { caption in
|
||||||
|
Text(caption.name).tag(caption.id)
|
||||||
|
}
|
||||||
|
}.onChange(of: subtitleSelection) { id in
|
||||||
|
self.delegate.subtitleTrackID = id
|
||||||
|
}
|
||||||
|
Picker("Audio Track", selection: $audioTrackSelection) {
|
||||||
|
ForEach(audioTracks, id: \.id) { caption in
|
||||||
|
Text(caption.name).tag(caption.id).lineLimit(1)
|
||||||
|
}
|
||||||
|
}.onChange(of: audioTrackSelection) { id in
|
||||||
|
self.delegate.audioTrackID = id
|
||||||
|
}
|
||||||
|
}.navigationTitle("Audio & Captions")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .navigationBarLeading) {
|
||||||
|
Button {
|
||||||
|
delegate.close = true;
|
||||||
|
} label: {
|
||||||
|
HStack() {
|
||||||
|
Text("Back").font(.callout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.navigationViewStyle(StackNavigationViewStyle())
|
||||||
|
.padding(6)
|
||||||
|
.frame(width: proxy.size.width, height: proxy.size.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
README.md
22
README.md
|
@ -1,4 +1,24 @@
|
||||||
Native Jellyfin Player
|
<h1 align="center">Jellyfin Native for iOS</h1>
|
||||||
|
<h3 align="center">Part of the <a href="https://jellyfin.org">Jellyfin Project</a></h3>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Logo Banner" src="https://raw.githubusercontent.com/jellyfin/jellyfin-ux/master/branding/SVG/banner-logo-solid.svg?sanitize=true"/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<a href="https://github.com/jellyfin/JellyfinPlayer">
|
||||||
|
<img src="https://img.shields.io/github/license/jellyfin/JellyfinPlayer" alt="GPL 3.0 License" />
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/jellyfin/JellyfinPlayer/releases">
|
||||||
|
<img src="https://img.shields.io/github/v/release/jellyfin/JellyfinPlayer" alt="GitHub release (latest SemVer)" />
|
||||||
|
</a>
|
||||||
|
<a href="https://matrix.to/#/+jellyfin:matrix.org">
|
||||||
|
<img alt="Chat on Matrix" src="https://img.shields.io/matrix/jellyfin:matrix.org.svg?logo=matrix"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
[Join the Discord!](https://discord.gg/WghTptH2)
|
[Join the Discord!](https://discord.gg/WghTptH2)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue