Update Nuke and implement BlurHashKit (#516)
This commit is contained in:
parent
e88dc5538b
commit
599cff4c95
|
@ -1,63 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct BlurHash {
|
||||
public let components: [[(Float, Float, Float)]]
|
||||
|
||||
public var numberOfHorizontalComponents: Int { components.first!.count }
|
||||
public var numberOfVerticalComponents: Int { components.count }
|
||||
|
||||
public init(components: [[(Float, Float, Float)]]) {
|
||||
self.components = components
|
||||
}
|
||||
|
||||
public func punch(_ factor: Float) -> BlurHash {
|
||||
BlurHash(components: components.enumerated().map { j, horizontalComponents -> [(Float, Float, Float)] in
|
||||
horizontalComponents.enumerated().map { i, component -> (Float, Float, Float) in
|
||||
if i == 0 && j == 0 {
|
||||
return component
|
||||
} else {
|
||||
return component * factor
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public func + (lhs: BlurHash, rhs: BlurHash) throws -> BlurHash {
|
||||
BlurHash(components: paddedZip(lhs.components, rhs.components, [], []).map {
|
||||
paddedZip($0.0, $0.1, (0, 0, 0) as (Float, Float, Float), (0, 0, 0) as (Float, Float, Float))
|
||||
.map { ($0.0.0 + $0.1.0, $0.0.1 + $0.1.1, $0.0.2 + $0.1.2) }
|
||||
})
|
||||
}
|
||||
|
||||
public func - (lhs: BlurHash, rhs: BlurHash) throws -> BlurHash {
|
||||
BlurHash(components: paddedZip(lhs.components, rhs.components, [], []).map {
|
||||
paddedZip($0.0, $0.1, (0, 0, 0) as (Float, Float, Float), (0, 0, 0) as (Float, Float, Float))
|
||||
.map { ($0.0.0 - $0.1.0, $0.0.1 - $0.1.1, $0.0.2 - $0.1.2) }
|
||||
})
|
||||
}
|
||||
|
||||
private func paddedZip<Collection1, Collection2>(
|
||||
_ collection1: Collection1,
|
||||
_ collection2: Collection2,
|
||||
_ padding1: Collection1.Element,
|
||||
_ padding2: Collection2.Element
|
||||
) -> Zip2Sequence<[Collection1.Element], [Collection2.Element]> where Collection1: Collection, Collection2: Collection {
|
||||
if collection1.count < collection2.count {
|
||||
let padded = collection1 + Array(repeating: padding1, count: collection2.count - collection1.count)
|
||||
return zip(padded, Array(collection2))
|
||||
} else if collection2.count < collection1.count {
|
||||
let padded = collection2 + Array(repeating: padding2, count: collection1.count - collection2.count)
|
||||
return zip(Array(collection1), padded)
|
||||
} else {
|
||||
return zip(Array(collection1), Array(collection2))
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension BlurHash {
|
||||
func linearRGB(atX x: Float) -> (Float, Float, Float) {
|
||||
return components[0].enumerated().reduce((0, 0, 0)) { sum, horizontalEnumerated -> (Float, Float, Float) in
|
||||
let (i, component) = horizontalEnumerated
|
||||
return sum + component * cos(Float.pi * Float(i) * x)
|
||||
}
|
||||
}
|
||||
|
||||
func linearRGB(atY y: Float) -> (Float, Float, Float) {
|
||||
return components.enumerated().reduce((0, 0, 0)) { sum, verticalEnumerated in
|
||||
let (j, horizontalComponents) = verticalEnumerated
|
||||
return sum + horizontalComponents[0] * cos(Float.pi * Float(j) * y)
|
||||
}
|
||||
}
|
||||
|
||||
func linearRGB(at position: (Float, Float)) -> (Float, Float, Float) {
|
||||
return components.enumerated().reduce((0, 0, 0)) { sum, verticalEnumerated in
|
||||
let (j, horizontalComponents) = verticalEnumerated
|
||||
return horizontalComponents.enumerated().reduce(sum) { sum, horizontalEnumerated in
|
||||
let (i, component) = horizontalEnumerated
|
||||
return sum + component * cos(Float.pi * Float(i) * position.0) * cos(Float.pi * Float(j) * position.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func linearRGB(from upperLeft: (Float, Float), to lowerRight: (Float, Float)) -> (Float, Float, Float) {
|
||||
return components.enumerated().reduce((0, 0, 0)) { sum, verticalEnumerated in
|
||||
let (j, horizontalComponents) = verticalEnumerated
|
||||
return horizontalComponents.enumerated().reduce(sum) { sum, horizontalEnumerated in
|
||||
let (i, component) = horizontalEnumerated
|
||||
let horizontalAverage: Float = i == 0 ? 1 :
|
||||
(sin(Float.pi * Float(i) * lowerRight.0) - sin(Float.pi * Float(i) * upperLeft.0)) /
|
||||
(Float(i) * Float.pi * (lowerRight.0 - upperLeft.0))
|
||||
let veritcalAverage: Float = j == 0 ? 1 :
|
||||
(sin(Float.pi * Float(j) * lowerRight.1) - sin(Float.pi * Float(j) * upperLeft.1)) /
|
||||
(Float(j) * Float.pi * (lowerRight.1 - upperLeft.1))
|
||||
return sum + component * horizontalAverage * veritcalAverage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func linearRGB(at upperLeft: (Float, Float), size: (Float, Float)) -> (Float, Float, Float) {
|
||||
return linearRGB(from: upperLeft, to: (upperLeft.0 + size.0, upperLeft.1 + size.1))
|
||||
}
|
||||
|
||||
var averageLinearRGB: (Float, Float, Float) {
|
||||
return components[0][0]
|
||||
}
|
||||
|
||||
var leftEdgeLinearRGB: (Float, Float, Float) { return linearRGB(atX: 0) }
|
||||
var rightEdgeLinearRGB: (Float, Float, Float) { return linearRGB(atX: 1) }
|
||||
var topEdgeLinearRGB: (Float, Float, Float) { return linearRGB(atY: 0) }
|
||||
var bottomEdgeLinearRGB: (Float, Float, Float) { return linearRGB(atY: 1) }
|
||||
var topLeftCornerLinearRGB: (Float, Float, Float) { return linearRGB(at: (0, 0)) }
|
||||
var topRightCornerLinearRGB: (Float, Float, Float) { return linearRGB(at: (1, 0)) }
|
||||
var bottomLeftCornerLinearRGB: (Float, Float, Float) { return linearRGB(at: (0, 1)) }
|
||||
var bottomRightCornerLinearRGB: (Float, Float, Float) { return linearRGB(at: (1, 1)) }
|
||||
}
|
||||
|
||||
public extension BlurHash {
|
||||
func isDark(linearRGB rgb: (Float, Float, Float), threshold: Float = 0.3) -> Bool {
|
||||
rgb.0 * 0.299 + rgb.1 * 0.587 + rgb.2 * 0.114 < threshold
|
||||
}
|
||||
|
||||
func isDark(threshold: Float = 0.3) -> Bool { isDark(linearRGB: averageLinearRGB, threshold: threshold) }
|
||||
|
||||
func isDark(atX x: Float, threshold: Float = 0.3) -> Bool { isDark(linearRGB: linearRGB(atX: x), threshold: threshold) }
|
||||
func isDark(atY y: Float, threshold: Float = 0.3) -> Bool { isDark(linearRGB: linearRGB(atY: y), threshold: threshold) }
|
||||
func isDark(
|
||||
at position: (Float, Float),
|
||||
threshold: Float = 0.3
|
||||
) -> Bool { isDark(linearRGB: linearRGB(at: position), threshold: threshold) }
|
||||
func isDark(
|
||||
from upperLeft: (Float, Float),
|
||||
to lowerRight: (Float, Float),
|
||||
threshold: Float = 0.3
|
||||
) -> Bool { isDark(linearRGB: linearRGB(from: upperLeft, to: lowerRight), threshold: threshold) }
|
||||
func isDark(
|
||||
at upperLeft: (Float, Float),
|
||||
size: (Float, Float),
|
||||
threshold: Float = 0.3
|
||||
) -> Bool { isDark(linearRGB: linearRGB(at: upperLeft, size: size), threshold: threshold) }
|
||||
|
||||
var isLeftEdgeDark: Bool { isDark(atX: 0) }
|
||||
var isRightEdgeDark: Bool { isDark(atX: 1) }
|
||||
var isTopEdgeDark: Bool { isDark(atY: 0) }
|
||||
var isBottomEdgeDark: Bool { isDark(atY: 1) }
|
||||
var isTopLeftCornerDark: Bool { isDark(at: (0, 0)) }
|
||||
var isTopRightCornerDark: Bool { isDark(at: (1, 0)) }
|
||||
var isBottomLeftCornerDark: Bool { isDark(at: (0, 1)) }
|
||||
var isBottomRightCornerDark: Bool { isDark(at: (1, 1)) }
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
func signPow(_ value: Float, _ exp: Float) -> Float {
|
||||
copysign(pow(abs(value), exp), value)
|
||||
}
|
||||
|
||||
func linearTosRGB(_ value: Float) -> Int {
|
||||
let v = max(0, min(1, value))
|
||||
if v <= 0.0031308 { return Int(v * 12.92 * 255 + 0.5) }
|
||||
else { return Int((1.055 * pow(v, 1 / 2.4) - 0.055) * 255 + 0.5) }
|
||||
}
|
||||
|
||||
func sRGBToLinear<Type: BinaryInteger>(_ value: Type) -> Float {
|
||||
let v = Float(Int64(value)) / 255
|
||||
if v <= 0.04045 { return v / 12.92 }
|
||||
else { return pow((v + 0.055) / 1.055, 2.4) }
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension BlurHash {
|
||||
var twoByThreeEscapeSequence: String {
|
||||
let areas: [(from: (Float, Float), to: (Float, Float))] = [
|
||||
(from: (0, 0), to: (0.333, 0.5)),
|
||||
(from: (0, 0.5), to: (0.333, 1.0)),
|
||||
(from: (0.333, 0), to: (0.666, 0.5)),
|
||||
(from: (0.333, 0.5), to: (0.666, 1.0)),
|
||||
(from: (0.666, 0), to: (1.0, 0.5)),
|
||||
(from: (0.666, 0.5), to: (1.0, 1.0)),
|
||||
]
|
||||
|
||||
let rgb: [(Float, Float, Float)] = areas.map { area in
|
||||
linearRGB(from: area.from, to: area.to)
|
||||
}
|
||||
|
||||
let maxRgb: (Float, Float, Float) = rgb.reduce((-Float.infinity, -Float.infinity, -Float.infinity), max)
|
||||
let minRgb: (Float, Float, Float) = rgb.reduce((Float.infinity, Float.infinity, Float.infinity), min)
|
||||
|
||||
let positiveScale: (Float, Float, Float) = ((1, 1, 1) - averageLinearRGB) / (maxRgb - averageLinearRGB)
|
||||
let negativeScale: (Float, Float, Float) = averageLinearRGB / (averageLinearRGB - minRgb)
|
||||
let scale: (Float, Float, Float) = min(positiveScale, negativeScale)
|
||||
|
||||
let scaledRgb: [(Float, Float, Float)] = rgb.map { rgb in
|
||||
(rgb - averageLinearRGB) * scale + averageLinearRGB
|
||||
}
|
||||
|
||||
let c = scaledRgb.map { rgb in
|
||||
(linearTosRGB(rgb.0) / 51) * 36 + (linearTosRGB(rgb.1) / 51) * 6 + (linearTosRGB(rgb.2) / 51) + 16
|
||||
}
|
||||
|
||||
return "\u{1b}[38;5;\(c[1]);48;5;\(c[0])m▄\u{1b}[38;5;\(c[3]);48;5;\(c[2])m▄\u{1b}[38;5;\(c[5]);48;5;\(c[4])m▄\u{1b}[m"
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension BlurHash {
|
||||
init?(string: String) {
|
||||
guard string.count >= 6 else { return nil }
|
||||
|
||||
let sizeFlag = String(string[0]).decode83()
|
||||
let numberOfHorizontalComponents = (sizeFlag % 9) + 1
|
||||
let numberOfVerticalComponents = (sizeFlag / 9) + 1
|
||||
|
||||
let quantisedMaximumValue = String(string[1]).decode83()
|
||||
let maximumValue = Float(quantisedMaximumValue + 1) / 166
|
||||
|
||||
guard string.count == 4 + 2 * numberOfHorizontalComponents * numberOfVerticalComponents else { return nil }
|
||||
|
||||
self.components = (0 ..< numberOfVerticalComponents).map { j in
|
||||
(0 ..< numberOfHorizontalComponents).map { i in
|
||||
if i == 0 && j == 0 {
|
||||
let value = String(string[2 ..< 6]).decode83()
|
||||
return BlurHash.decodeDC(value)
|
||||
} else {
|
||||
let index = i + j * numberOfHorizontalComponents
|
||||
let value = String(string[4 + index * 2 ..< 4 + index * 2 + 2]).decode83()
|
||||
return BlurHash.decodeAC(value, maximumValue: maximumValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func decodeDC(_ value: Int) -> (Float, Float, Float) {
|
||||
let intR = value >> 16
|
||||
let intG = (value >> 8) & 255
|
||||
let intB = value & 255
|
||||
return (sRGBToLinear(intR), sRGBToLinear(intG), sRGBToLinear(intB))
|
||||
}
|
||||
|
||||
private static func decodeAC(_ value: Int, maximumValue: Float) -> (Float, Float, Float) {
|
||||
let quantR = value / (19 * 19)
|
||||
let quantG = (value / 19) % 19
|
||||
let quantB = value % 19
|
||||
|
||||
let rgb = (
|
||||
signPow((Float(quantR) - 9) / 9, 2) * maximumValue,
|
||||
signPow((Float(quantG) - 9) / 9, 2) * maximumValue,
|
||||
signPow((Float(quantB) - 9) / 9, 2) * maximumValue
|
||||
)
|
||||
|
||||
return rgb
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
subscript(offset: Int) -> Character {
|
||||
self[index(startIndex, offsetBy: offset)]
|
||||
}
|
||||
|
||||
subscript(bounds: CountableClosedRange<Int>) -> Substring {
|
||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
||||
return self[start ... end]
|
||||
}
|
||||
|
||||
subscript(bounds: CountableRange<Int>) -> Substring {
|
||||
let start = index(startIndex, offsetBy: bounds.lowerBound)
|
||||
let end = index(startIndex, offsetBy: bounds.upperBound)
|
||||
return self[start ..< end]
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension BlurHash {
|
||||
init?(image: UIImage, numberOfComponents components: (Int, Int)) {
|
||||
guard components.0 >= 1, components.0 <= 9,
|
||||
components.1 >= 1, components.1 <= 9
|
||||
else {
|
||||
fatalError("Number of components bust be between 1 and 9 inclusive on each axis")
|
||||
}
|
||||
|
||||
let pixelWidth = Int(round(image.size.width * image.scale))
|
||||
let pixelHeight = Int(round(image.size.height * image.scale))
|
||||
|
||||
let context = CGContext(
|
||||
data: nil,
|
||||
width: pixelWidth,
|
||||
height: pixelHeight,
|
||||
bitsPerComponent: 8,
|
||||
bytesPerRow: pixelWidth * 4,
|
||||
space: CGColorSpace(name: CGColorSpace.sRGB)!,
|
||||
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
|
||||
)!
|
||||
context.scaleBy(x: image.scale, y: -image.scale)
|
||||
context.translateBy(x: 0, y: -image.size.height)
|
||||
|
||||
UIGraphicsPushContext(context)
|
||||
image.draw(at: .zero)
|
||||
UIGraphicsPopContext()
|
||||
|
||||
guard let cgImage = context.makeImage(),
|
||||
let dataProvider = cgImage.dataProvider,
|
||||
let data = dataProvider.data,
|
||||
let pixels = CFDataGetBytePtr(data)
|
||||
else {
|
||||
assertionFailure("Unexpected error!")
|
||||
return nil
|
||||
}
|
||||
|
||||
let width = cgImage.width
|
||||
let height = cgImage.height
|
||||
let bytesPerRow = cgImage.bytesPerRow
|
||||
|
||||
self.components = (0 ..< components.1).map { j -> [(Float, Float, Float)] in
|
||||
(0 ..< components.0).map { i -> (Float, Float, Float) in
|
||||
let normalisation: Float = (i == 0 && j == 0) ? 1 : 2
|
||||
return BlurHash.multiplyBasisFunction(
|
||||
pixels: pixels,
|
||||
width: width,
|
||||
height: height,
|
||||
bytesPerRow: bytesPerRow,
|
||||
bytesPerPixel: cgImage.bitsPerPixel / 8
|
||||
) { x, y in
|
||||
normalisation * cos(Float.pi * Float(i) * x / Float(width)) as Float *
|
||||
cos(Float.pi * Float(j) * y / Float(height)) as Float
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func multiplyBasisFunction(
|
||||
pixels: UnsafePointer<UInt8>,
|
||||
width: Int,
|
||||
height: Int,
|
||||
bytesPerRow: Int,
|
||||
bytesPerPixel: Int,
|
||||
basisFunction: (Float, Float) -> Float
|
||||
) -> (Float, Float, Float) {
|
||||
var c: (Float, Float, Float) = (0, 0, 0)
|
||||
|
||||
let buffer = UnsafeBufferPointer(start: pixels, count: height * bytesPerRow)
|
||||
|
||||
for x in 0 ..< width {
|
||||
for y in 0 ..< height {
|
||||
c += basisFunction(Float(x), Float(y)) * (
|
||||
sRGBToLinear(buffer[bytesPerPixel * x + 0 + y * bytesPerRow]),
|
||||
sRGBToLinear(buffer[bytesPerPixel * x + 1 + y * bytesPerRow]),
|
||||
sRGBToLinear(buffer[bytesPerPixel * x + 2 + y * bytesPerRow])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return c / Float(width * height)
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension BlurHash {
|
||||
init(blendingTop top: BlurHash, bottom: BlurHash) {
|
||||
guard top.components.count == 1, bottom.components.count == 1 else {
|
||||
fatalError("Blended BlurHashses must have only one vertical component")
|
||||
}
|
||||
|
||||
let average = zip(top.components[0], bottom.components[0]).map { ($0 + $1) / 2 }
|
||||
let difference = zip(top.components[0], bottom.components[0]).map { ($0 - $1) / 2 }
|
||||
self.init(components: [average, difference])
|
||||
}
|
||||
|
||||
init(blendingLeft left: BlurHash, right: BlurHash) {
|
||||
self = BlurHash(blendingTop: left.transposed, bottom: right.transposed).transposed
|
||||
}
|
||||
}
|
||||
|
||||
public extension BlurHash {
|
||||
init(colour: UIColor) {
|
||||
self.init(components: [[colour.linear]])
|
||||
}
|
||||
|
||||
init(blendingTop topColour: UIColor, bottom bottomColour: UIColor) {
|
||||
self = BlurHash(blendingTop: .init(colour: topColour), bottom: .init(colour: bottomColour))
|
||||
}
|
||||
|
||||
init(blendingLeft leftColour: UIColor, right rightColour: UIColor) {
|
||||
self = BlurHash(blendingLeft: .init(colour: leftColour), right: .init(colour: rightColour))
|
||||
}
|
||||
|
||||
init(
|
||||
blendingTopLeft topLeftColour: UIColor,
|
||||
topRight topRightColour: UIColor,
|
||||
bottomLeft bottomLeftColour: UIColor,
|
||||
bottomRight bottomRightColour: UIColor
|
||||
) {
|
||||
self = BlurHash(
|
||||
blendingTop: BlurHash(blendingTop: topLeftColour, bottom: topRightColour).transposed,
|
||||
bottom: BlurHash(blendingTop: bottomLeftColour, bottom: bottomRightColour).transposed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public extension BlurHash {
|
||||
init(horizontalColours colours: [(Float, Float, Float)], numberOfComponents: Int) {
|
||||
guard numberOfComponents >= 1, numberOfComponents <= 9 else {
|
||||
fatalError("Number of components bust be between 1 and 9 inclusive")
|
||||
}
|
||||
|
||||
self.init(components: [(0 ..< numberOfComponents).map { i in
|
||||
let normalisation: Float = i == 0 ? 1 : 2
|
||||
var sum: (Float, Float, Float) = (0, 0, 0)
|
||||
for x in 0 ..< colours.count {
|
||||
let basis = normalisation * cos(Float.pi * Float(i) * Float(x) / Float(colours.count - 1))
|
||||
sum += basis * colours[x]
|
||||
}
|
||||
|
||||
return sum / Float(colours.count)
|
||||
}])
|
||||
}
|
||||
}
|
||||
|
||||
public extension BlurHash {
|
||||
var mirroredHorizontally: BlurHash {
|
||||
.init(components: (0 ..< numberOfVerticalComponents).map { j -> [(Float, Float, Float)] in
|
||||
(0 ..< numberOfHorizontalComponents).map { i -> (Float, Float, Float) in
|
||||
components[j][i] * (i % 2 == 0 ? 1 : -1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var mirroredVertically: BlurHash {
|
||||
.init(components: (0 ..< numberOfVerticalComponents).map { j -> [(Float, Float, Float)] in
|
||||
(0 ..< numberOfHorizontalComponents).map { i -> (Float, Float, Float) in
|
||||
components[j][i] * (j % 2 == 0 ? 1 : -1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var transposed: BlurHash {
|
||||
.init(components: (0 ..< numberOfHorizontalComponents).map { i in
|
||||
(0 ..< numberOfVerticalComponents).map { j in
|
||||
components[j][i]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
var linear: (Float, Float, Float) {
|
||||
guard let c = cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil)?.components
|
||||
else { return (0, 0, 0) }
|
||||
|
||||
switch c.count {
|
||||
case 1, 2: return (sRGBToLinear(c[0]), sRGBToLinear(c[0]), sRGBToLinear(c[0]))
|
||||
case 3, 4: return (sRGBToLinear(c[0]), sRGBToLinear(c[1]), sRGBToLinear(c[2]))
|
||||
default: return (0, 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sRGBToLinear(_ value: CGFloat) -> Float {
|
||||
let v = Float(value)
|
||||
if v <= 0.04045 { return v / 12.92 }
|
||||
else { return pow((v + 0.055) / 1.055, 2.4) }
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private let encodeCharacters: [String] = {
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~".map { String($0) }
|
||||
}()
|
||||
|
||||
private let decodeCharacters: [String: Int] = {
|
||||
var dict: [String: Int] = [:]
|
||||
for (index, character) in encodeCharacters.enumerated() {
|
||||
dict[character] = index
|
||||
}
|
||||
return dict
|
||||
}()
|
||||
|
||||
extension BinaryInteger {
|
||||
func encode83(length: Int) -> String {
|
||||
var result = ""
|
||||
for i in 1 ... length {
|
||||
let digit = (Int(self) / pow(83, length - i)) % 83
|
||||
result += encodeCharacters[Int(digit)]
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func decode83() -> Int {
|
||||
var value: Int = 0
|
||||
for character in self {
|
||||
if let digit = decodeCharacters[String(character)] {
|
||||
value = value * 83 + digit
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
private func pow(_ base: Int, _ exponent: Int) -> Int {
|
||||
(0 ..< exponent).reduce(1) { value, _ in value * base }
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension BlurHash {
|
||||
var string: String {
|
||||
let flatComponents = components.reduce([]) { $0 + $1 }
|
||||
let dc = flatComponents.first!
|
||||
let ac = flatComponents.dropFirst()
|
||||
|
||||
var hash = ""
|
||||
|
||||
let sizeFlag = (numberOfHorizontalComponents - 1) + (numberOfVerticalComponents - 1) * 9
|
||||
hash += sizeFlag.encode83(length: 1)
|
||||
|
||||
let maximumValue: Float
|
||||
if !ac.isEmpty {
|
||||
let actualMaximumValue = ac.map { max(abs($0.0), abs($0.1), abs($0.2)) }.max()!
|
||||
let quantisedMaximumValue = Int(max(0, min(82, floor(actualMaximumValue * 166 - 0.5))))
|
||||
maximumValue = Float(quantisedMaximumValue + 1) / 166
|
||||
hash += quantisedMaximumValue.encode83(length: 1)
|
||||
} else {
|
||||
maximumValue = 1
|
||||
hash += 0.encode83(length: 1)
|
||||
}
|
||||
|
||||
hash += encodeDC(dc).encode83(length: 4)
|
||||
|
||||
for factor in ac {
|
||||
hash += encodeAC(factor, maximumValue: maximumValue).encode83(length: 2)
|
||||
}
|
||||
|
||||
return hash
|
||||
}
|
||||
|
||||
private func encodeDC(_ value: (Float, Float, Float)) -> Int {
|
||||
let roundedR = linearTosRGB(value.0)
|
||||
let roundedG = linearTosRGB(value.1)
|
||||
let roundedB = linearTosRGB(value.2)
|
||||
return (roundedR << 16) + (roundedG << 8) + roundedB
|
||||
}
|
||||
|
||||
private func encodeAC(_ value: (Float, Float, Float), maximumValue: Float) -> Int {
|
||||
let quantR = Int(max(0, min(18, floor(signPow(value.0 / maximumValue, 0.5) * 9 + 9.5))))
|
||||
let quantG = Int(max(0, min(18, floor(signPow(value.1 / maximumValue, 0.5) * 9 + 9.5))))
|
||||
let quantB = Int(max(0, min(18, floor(signPow(value.2 / maximumValue, 0.5) * 9 + 9.5))))
|
||||
|
||||
return quantR * 19 * 19 + quantG * 19 + quantB
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension BlurHash {
|
||||
func cgImage(size: CGSize) -> CGImage? {
|
||||
let width = Int(size.width)
|
||||
let height = Int(size.height)
|
||||
let bytesPerRow = width * 3
|
||||
|
||||
guard let data = CFDataCreateMutable(kCFAllocatorDefault, bytesPerRow * height) else { return nil }
|
||||
CFDataSetLength(data, bytesPerRow * height)
|
||||
|
||||
guard let pixels = CFDataGetMutableBytePtr(data) else { return nil }
|
||||
|
||||
for y in 0 ..< height {
|
||||
for x in 0 ..< width {
|
||||
var c: (Float, Float, Float) = (0, 0, 0)
|
||||
|
||||
for j in 0 ..< numberOfVerticalComponents {
|
||||
for i in 0 ..< numberOfHorizontalComponents {
|
||||
let basis = cos(Float.pi * Float(x) * Float(i) / Float(width)) * cos(Float.pi * Float(y) * Float(j) / Float(height))
|
||||
let component = components[j][i]
|
||||
c += component * basis
|
||||
}
|
||||
}
|
||||
|
||||
let intR = UInt8(linearTosRGB(c.0))
|
||||
let intG = UInt8(linearTosRGB(c.1))
|
||||
let intB = UInt8(linearTosRGB(c.2))
|
||||
|
||||
pixels[3 * x + 0 + y * bytesPerRow] = intR
|
||||
pixels[3 * x + 1 + y * bytesPerRow] = intG
|
||||
pixels[3 * x + 2 + y * bytesPerRow] = intB
|
||||
}
|
||||
}
|
||||
|
||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
|
||||
|
||||
guard let provider = CGDataProvider(data: data) else { return nil }
|
||||
guard let cgImage = CGImage(
|
||||
width: width,
|
||||
height: height,
|
||||
bitsPerComponent: 8,
|
||||
bitsPerPixel: 24,
|
||||
bytesPerRow: bytesPerRow,
|
||||
space: CGColorSpaceCreateDeviceRGB(),
|
||||
bitmapInfo: bitmapInfo,
|
||||
provider: provider,
|
||||
decode: nil,
|
||||
shouldInterpolate: true,
|
||||
intent: .defaultIntent
|
||||
) else { return nil }
|
||||
|
||||
return cgImage
|
||||
}
|
||||
|
||||
func cgImage(numberOfPixels: Int = 1024, originalSize size: CGSize) -> CGImage? {
|
||||
let width: CGFloat
|
||||
let height: CGFloat
|
||||
if size.width > size.height {
|
||||
width = floor(sqrt(CGFloat(numberOfPixels) * size.width / size.height) + 0.5)
|
||||
height = floor(CGFloat(numberOfPixels) / width + 0.5)
|
||||
} else {
|
||||
height = floor(sqrt(CGFloat(numberOfPixels) * size.height / size.width) + 0.5)
|
||||
width = floor(CGFloat(numberOfPixels) / height + 0.5)
|
||||
}
|
||||
return cgImage(size: CGSize(width: width, height: height))
|
||||
}
|
||||
|
||||
func image(size: CGSize) -> UIImage? {
|
||||
guard let cgImage = cgImage(size: size) else { return nil }
|
||||
return UIImage(cgImage: cgImage)
|
||||
}
|
||||
|
||||
func image(numberOfPixels: Int = 1024, originalSize size: CGSize) -> UIImage? {
|
||||
guard let cgImage = cgImage(numberOfPixels: numberOfPixels, originalSize: size) else { return nil }
|
||||
return UIImage(cgImage: cgImage)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public extension UIImage {
|
||||
convenience init?(blurHash string: String, size: CGSize, punch: Float = 1) {
|
||||
guard let blurHash = BlurHash(string: string),
|
||||
let cgImage = blurHash.punch(punch).cgImage(size: size) else { return nil }
|
||||
self.init(cgImage: cgImage)
|
||||
}
|
||||
|
||||
convenience init?(blurHash string: String, numberOfPixels: Int = 1024, originalSize size: CGSize, punch: Float = 1) {
|
||||
guard let blurHash = BlurHash(string: string),
|
||||
let cgImage = blurHash.punch(punch).cgImage(numberOfPixels: numberOfPixels, originalSize: size) else { return nil }
|
||||
self.init(cgImage: cgImage)
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
//
|
||||
// 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 (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
func + (lhs: (Float, Float, Float), rhs: (Float, Float, Float)) -> (Float, Float, Float) {
|
||||
return (lhs.0 + rhs.0, lhs.1 + rhs.1, lhs.2 + rhs.2)
|
||||
}
|
||||
|
||||
func - (lhs: (Float, Float, Float), rhs: (Float, Float, Float)) -> (Float, Float, Float) {
|
||||
return (lhs.0 - rhs.0, lhs.1 - rhs.1, lhs.2 - rhs.2)
|
||||
}
|
||||
|
||||
func * (lhs: (Float, Float, Float), rhs: (Float, Float, Float)) -> (Float, Float, Float) {
|
||||
return (lhs.0 * rhs.0, lhs.1 * rhs.1, lhs.2 * rhs.2)
|
||||
}
|
||||
|
||||
func * (lhs: (Float, Float, Float), rhs: Float) -> (Float, Float, Float) {
|
||||
return (lhs.0 * rhs, lhs.1 * rhs, lhs.2 * rhs)
|
||||
}
|
||||
|
||||
func * (lhs: Float, rhs: (Float, Float, Float)) -> (Float, Float, Float) {
|
||||
return (lhs * rhs.0, lhs * rhs.1, lhs * rhs.2)
|
||||
}
|
||||
|
||||
func / (lhs: (Float, Float, Float), rhs: (Float, Float, Float)) -> (Float, Float, Float) {
|
||||
return (lhs.0 / rhs.0, lhs.1 / rhs.1, lhs.2 / rhs.2)
|
||||
}
|
||||
|
||||
func / (lhs: (Float, Float, Float), rhs: Float) -> (Float, Float, Float) {
|
||||
return (lhs.0 / rhs, lhs.1 / rhs, lhs.2 / rhs)
|
||||
}
|
||||
|
||||
func += (lhs: inout (Float, Float, Float), rhs: (Float, Float, Float)) {
|
||||
lhs = lhs + rhs
|
||||
}
|
||||
|
||||
func -= (lhs: inout (Float, Float, Float), rhs: (Float, Float, Float)) {
|
||||
lhs = lhs - rhs
|
||||
}
|
||||
|
||||
func *= (lhs: inout (Float, Float, Float), rhs: Float) {
|
||||
lhs = lhs * rhs
|
||||
}
|
||||
|
||||
func /= (lhs: inout (Float, Float, Float), rhs: Float) {
|
||||
lhs = lhs / rhs
|
||||
}
|
||||
|
||||
func min(_ a: (Float, Float, Float), _ b: (Float, Float, Float)) -> (Float, Float, Float) {
|
||||
return (min(a.0, b.0), min(a.1, b.1), min(a.2, b.2))
|
||||
}
|
||||
|
||||
func max(_ a: (Float, Float, Float), _ b: (Float, Float, Float)) -> (Float, Float, Float) {
|
||||
return (max(a.0, b.0), max(a.1, b.1), max(a.2, b.2))
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import BlurHashKit
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import JellyfinAPI
|
||||
import Nuke
|
||||
import NukeExtensions
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
|
@ -18,6 +19,7 @@ class DynamicCinematicBackgroundViewModel: ObservableObject {
|
|||
@Published
|
||||
var currentImageView: UIImageView?
|
||||
|
||||
@MainActor
|
||||
func select(item: BaseItemDto) {
|
||||
|
||||
guard item.id != currentItem?.id else { return }
|
||||
|
@ -36,7 +38,7 @@ class DynamicCinematicBackgroundViewModel: ObservableObject {
|
|||
|
||||
let options = ImageLoadingOptions(transition: .fadeIn(duration: 0.2))
|
||||
|
||||
Nuke.loadImage(with: backdropImage, options: options, into: itemImageView, completion: { _ in })
|
||||
loadImage(with: backdropImage, options: options, into: itemImageView, completion: { _ in })
|
||||
|
||||
currentImageView = itemImageView
|
||||
}
|
||||
|
|
|
@ -272,7 +272,6 @@
|
|||
E11CEB9428999D9E003E74C7 /* EpisodeItemContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11CEB9328999D9E003E74C7 /* EpisodeItemContentView.swift */; };
|
||||
E11D224227378428003F9CB3 /* ServerDetailCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11D224127378428003F9CB3 /* ServerDetailCoordinator.swift */; };
|
||||
E11D224327378428003F9CB3 /* ServerDetailCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11D224127378428003F9CB3 /* ServerDetailCoordinator.swift */; };
|
||||
E11D83AF278FA998006E9776 /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = E11D83AE278FA998006E9776 /* NukeUI */; };
|
||||
E12186DE2718F1C50010884C /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = E12186DD2718F1C50010884C /* Defaults */; };
|
||||
E1218C9E271A2CD600EA0737 /* CombineExt in Frameworks */ = {isa = PBXBuildFile; productRef = E1218C9D271A2CD600EA0737 /* CombineExt */; };
|
||||
E122A9132788EAAD0060FA63 /* MediaStreamExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E122A9122788EAAD0060FA63 /* MediaStreamExtension.swift */; };
|
||||
|
@ -282,13 +281,16 @@
|
|||
E126F742278A656C00A522BF /* ServerStreamType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E126F740278A656C00A522BF /* ServerStreamType.swift */; };
|
||||
E1347DB2279E3C6200BC6161 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = E1347DB1279E3C6200BC6161 /* Puppy */; };
|
||||
E1347DB6279E3CA500BC6161 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = E1347DB5279E3CA500BC6161 /* Puppy */; };
|
||||
E1361DA7278FA7A300BEC523 /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = E1361DA6278FA7A300BEC523 /* NukeUI */; };
|
||||
E1384944278036C70024FB48 /* VLCPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1384943278036C70024FB48 /* VLCPlayerViewController.swift */; };
|
||||
E13849452780370B0024FB48 /* PlaybackSpeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C812B4277A8E5D00918266 /* PlaybackSpeed.swift */; };
|
||||
E1399474289B1EA900401ABC /* Defaults+Workaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1399473289B1EA900401ABC /* Defaults+Workaround.swift */; };
|
||||
E1399475289B1EA900401ABC /* Defaults+Workaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1399473289B1EA900401ABC /* Defaults+Workaround.swift */; };
|
||||
E13AD72E2798BC8D00FDCEE8 /* NativePlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13AD72D2798BC8D00FDCEE8 /* NativePlayerViewController.swift */; };
|
||||
E13AD7302798C60F00FDCEE8 /* NativePlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13AD72F2798C60F00FDCEE8 /* NativePlayerViewController.swift */; };
|
||||
E13AF3B628A0C598009093AB /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = E13AF3B528A0C598009093AB /* Nuke */; };
|
||||
E13AF3B828A0C598009093AB /* NukeExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = E13AF3B728A0C598009093AB /* NukeExtensions */; };
|
||||
E13AF3BA28A0C598009093AB /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = E13AF3B928A0C598009093AB /* NukeUI */; };
|
||||
E13AF3BC28A0C59E009093AB /* BlurHashKit in Frameworks */ = {isa = PBXBuildFile; productRef = E13AF3BB28A0C59E009093AB /* BlurHashKit */; };
|
||||
E13DD3BF27163DD7009D4DAF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3BE27163DD7009D4DAF /* AppDelegate.swift */; };
|
||||
E13DD3C227164941009D4DAF /* SwiftfinStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3C127164941009D4DAF /* SwiftfinStore.swift */; };
|
||||
E13DD3C327164941009D4DAF /* SwiftfinStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13DD3C127164941009D4DAF /* SwiftfinStore.swift */; };
|
||||
|
@ -411,29 +413,10 @@
|
|||
E193D5502719430400900D82 /* ServerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E193D54F2719430400900D82 /* ServerDetailView.swift */; };
|
||||
E193D5512719432400900D82 /* ServerDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA5326D050F500CC4EB7 /* ServerDetailViewModel.swift */; };
|
||||
E193D553271943D500900D82 /* tvOSMainTabCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E193D552271943D500900D82 /* tvOSMainTabCoordinator.swift */; };
|
||||
E19E550428972B97003CE330 /* ColourSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54F828972B97003CE330 /* ColourSpace.swift */; };
|
||||
E19E550528972B97003CE330 /* ColourSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54F828972B97003CE330 /* ColourSpace.swift */; };
|
||||
E19E550628972B97003CE330 /* BlurHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54F928972B97003CE330 /* BlurHash.swift */; };
|
||||
E19E550728972B97003CE330 /* BlurHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54F928972B97003CE330 /* BlurHash.swift */; };
|
||||
E19E550828972B97003CE330 /* StringCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FA28972B97003CE330 /* StringCoding.swift */; };
|
||||
E19E550928972B97003CE330 /* StringCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FA28972B97003CE330 /* StringCoding.swift */; };
|
||||
E19E550A28972B97003CE330 /* FromString.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FB28972B97003CE330 /* FromString.swift */; };
|
||||
E19E550B28972B97003CE330 /* FromString.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FB28972B97003CE330 /* FromString.swift */; };
|
||||
E19E550C28972B97003CE330 /* EscapeSequences.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FC28972B97003CE330 /* EscapeSequences.swift */; };
|
||||
E19E550D28972B97003CE330 /* EscapeSequences.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FC28972B97003CE330 /* EscapeSequences.swift */; };
|
||||
E19E550E28972B97003CE330 /* ColourProbes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FD28972B97003CE330 /* ColourProbes.swift */; };
|
||||
E19E550F28972B97003CE330 /* ColourProbes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FD28972B97003CE330 /* ColourProbes.swift */; };
|
||||
E19E551028972B97003CE330 /* ToString.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FE28972B97003CE330 /* ToString.swift */; };
|
||||
E19E551128972B97003CE330 /* ToString.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FE28972B97003CE330 /* ToString.swift */; };
|
||||
E19E551228972B97003CE330 /* ToUIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FF28972B97003CE330 /* ToUIImage.swift */; };
|
||||
E19E551328972B97003CE330 /* ToUIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E54FF28972B97003CE330 /* ToUIImage.swift */; };
|
||||
E19E551428972B97003CE330 /* FromUIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E550028972B97003CE330 /* FromUIImage.swift */; };
|
||||
E19E551528972B97003CE330 /* FromUIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E550028972B97003CE330 /* FromUIImage.swift */; };
|
||||
E19E551828972B97003CE330 /* Generation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E550228972B97003CE330 /* Generation.swift */; };
|
||||
E19E551928972B97003CE330 /* Generation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E550228972B97003CE330 /* Generation.swift */; };
|
||||
E19E551A28972B97003CE330 /* TupleMaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E550328972B97003CE330 /* TupleMaths.swift */; };
|
||||
E19E551B28972B97003CE330 /* TupleMaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E550328972B97003CE330 /* TupleMaths.swift */; };
|
||||
E19E551F2897326C003CE330 /* BottomEdgeGradientModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E551E2897326C003CE330 /* BottomEdgeGradientModifier.swift */; };
|
||||
E19E6E0528A0B958005C10C8 /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = E19E6E0428A0B958005C10C8 /* Nuke */; };
|
||||
E19E6E0728A0B958005C10C8 /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = E19E6E0628A0B958005C10C8 /* NukeUI */; };
|
||||
E19E6E0A28A0BEFF005C10C8 /* BlurHashKit in Frameworks */ = {isa = PBXBuildFile; productRef = E19E6E0928A0BEFF005C10C8 /* BlurHashKit */; };
|
||||
E1A16C9D2889AF1E00EA4679 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A16C9C2889AF1E00EA4679 /* AboutView.swift */; };
|
||||
E1A16CA1288A7CFD00EA4679 /* AboutViewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A16CA0288A7CFD00EA4679 /* AboutViewCard.swift */; };
|
||||
E1A2C154279A7D5A005EC829 /* UIApplicationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A2C153279A7D5A005EC829 /* UIApplicationExtensions.swift */; };
|
||||
|
@ -447,8 +430,6 @@
|
|||
E1AD104D26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AD104C26D96CE3003E4A08 /* BaseItemDtoExtensions.swift */; };
|
||||
E1AD104E26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AD104C26D96CE3003E4A08 /* BaseItemDtoExtensions.swift */; };
|
||||
E1AD105F26D9ADDD003E4A08 /* NameGUIDPairExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AD105E26D9ADDD003E4A08 /* NameGUIDPairExtensions.swift */; };
|
||||
E1AE8E7C2789135A00FBDDAA /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = E1AE8E7B2789135A00FBDDAA /* Nuke */; };
|
||||
E1AE8E7E2789136D00FBDDAA /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = E1AE8E7D2789136D00FBDDAA /* Nuke */; };
|
||||
E1B2AB9928808E150072B3B9 /* GoogleCast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1B2AB9628808CDF0072B3B9 /* GoogleCast.xcframework */; };
|
||||
E1B59FD52786ADE500A5287E /* ContinueWatchingCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B59FD42786ADE500A5287E /* ContinueWatchingCard.swift */; };
|
||||
E1B6DCE8271A23780015B715 /* CombineExt in Frameworks */ = {isa = PBXBuildFile; productRef = E1B6DCE7271A23780015B715 /* CombineExt */; };
|
||||
|
@ -864,17 +845,6 @@
|
|||
E193D54C2719426600900D82 /* LibraryFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryFilterView.swift; sourceTree = "<group>"; };
|
||||
E193D54F2719430400900D82 /* ServerDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetailView.swift; sourceTree = "<group>"; };
|
||||
E193D552271943D500900D82 /* tvOSMainTabCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = tvOSMainTabCoordinator.swift; sourceTree = "<group>"; };
|
||||
E19E54F828972B97003CE330 /* ColourSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColourSpace.swift; sourceTree = "<group>"; };
|
||||
E19E54F928972B97003CE330 /* BlurHash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHash.swift; sourceTree = "<group>"; };
|
||||
E19E54FA28972B97003CE330 /* StringCoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringCoding.swift; sourceTree = "<group>"; };
|
||||
E19E54FB28972B97003CE330 /* FromString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromString.swift; sourceTree = "<group>"; };
|
||||
E19E54FC28972B97003CE330 /* EscapeSequences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EscapeSequences.swift; sourceTree = "<group>"; };
|
||||
E19E54FD28972B97003CE330 /* ColourProbes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColourProbes.swift; sourceTree = "<group>"; };
|
||||
E19E54FE28972B97003CE330 /* ToString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToString.swift; sourceTree = "<group>"; };
|
||||
E19E54FF28972B97003CE330 /* ToUIImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToUIImage.swift; sourceTree = "<group>"; };
|
||||
E19E550028972B97003CE330 /* FromUIImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromUIImage.swift; sourceTree = "<group>"; };
|
||||
E19E550228972B97003CE330 /* Generation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generation.swift; sourceTree = "<group>"; };
|
||||
E19E550328972B97003CE330 /* TupleMaths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TupleMaths.swift; sourceTree = "<group>"; };
|
||||
E19E551E2897326C003CE330 /* BottomEdgeGradientModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomEdgeGradientModifier.swift; sourceTree = "<group>"; };
|
||||
E1A16C9C2889AF1E00EA4679 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
E1A16CA0288A7CFD00EA4679 /* AboutViewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewCard.swift; sourceTree = "<group>"; };
|
||||
|
@ -949,7 +919,6 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
62666E1727E501CC00EC0ECD /* CFNetwork.framework in Frameworks */,
|
||||
E11D83AF278FA998006E9776 /* NukeUI in Frameworks */,
|
||||
C409CE9C284EA6EA00CABC12 /* SwiftUICollection in Frameworks */,
|
||||
62666DFA27E5013700EC0ECD /* TVVLCKit.xcframework in Frameworks */,
|
||||
62666E3227E5021E00EC0ECD /* UIKit.framework in Frameworks */,
|
||||
|
@ -963,6 +932,8 @@
|
|||
6220D0C926D63F3700B8E046 /* Stinsen in Frameworks */,
|
||||
637FCAF5287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */,
|
||||
535870912669D7A800D05A09 /* Introspect in Frameworks */,
|
||||
E13AF3B828A0C598009093AB /* NukeExtensions in Frameworks */,
|
||||
E13AF3BA28A0C598009093AB /* NukeUI in Frameworks */,
|
||||
62666E1B27E501D400EC0ECD /* CoreGraphics.framework in Frameworks */,
|
||||
62666E2C27E5021000EC0ECD /* QuartzCore.framework in Frameworks */,
|
||||
62666E1927E501D000EC0ECD /* CoreFoundation.framework in Frameworks */,
|
||||
|
@ -971,8 +942,9 @@
|
|||
62666E1F27E501DF00EC0ECD /* CoreText.framework in Frameworks */,
|
||||
E13DD3CD27164CA7009D4DAF /* CoreStore in Frameworks */,
|
||||
62666E1527E501C800EC0ECD /* AVFoundation.framework in Frameworks */,
|
||||
E13AF3BC28A0C59E009093AB /* BlurHashKit in Frameworks */,
|
||||
62666E1327E501C300EC0ECD /* AudioToolbox.framework in Frameworks */,
|
||||
E1AE8E7E2789136D00FBDDAA /* Nuke in Frameworks */,
|
||||
E13AF3B628A0C598009093AB /* Nuke in Frameworks */,
|
||||
E178857D278037FD0094FBCF /* JellyfinAPI in Frameworks */,
|
||||
E12186DE2718F1C50010884C /* Defaults in Frameworks */,
|
||||
E1347DB6279E3CA500BC6161 /* Puppy in Frameworks */,
|
||||
|
@ -989,7 +961,6 @@
|
|||
62666DFF27E5016400EC0ECD /* CFNetwork.framework in Frameworks */,
|
||||
E13DD3D327168E65009D4DAF /* Defaults in Frameworks */,
|
||||
E1101177281B1E8A006A3584 /* Puppy in Frameworks */,
|
||||
E1361DA7278FA7A300BEC523 /* NukeUI in Frameworks */,
|
||||
E1002B682793CFBA00E47059 /* Algorithms in Frameworks */,
|
||||
62666E1127E501B900EC0ECD /* UIKit.framework in Frameworks */,
|
||||
62666DF727E5012C00EC0ECD /* MobileVLCKit.xcframework in Frameworks */,
|
||||
|
@ -1001,6 +972,7 @@
|
|||
62666E1027E501B400EC0ECD /* VideoToolbox.framework in Frameworks */,
|
||||
62666E0C27E501A500EC0ECD /* OpenGLES.framework in Frameworks */,
|
||||
C409CE9E285044C800CABC12 /* SwiftUICollection in Frameworks */,
|
||||
E19E6E0A28A0BEFF005C10C8 /* BlurHashKit in Frameworks */,
|
||||
62666E0127E5016900EC0ECD /* CoreFoundation.framework in Frameworks */,
|
||||
62400C4B287ED19600F6AD3D /* UDPBroadcast.xcframework in Frameworks */,
|
||||
E1B6DCEA271A23880015B715 /* SwiftyJSON in Frameworks */,
|
||||
|
@ -1012,8 +984,9 @@
|
|||
E1B2AB9928808E150072B3B9 /* GoogleCast.xcframework in Frameworks */,
|
||||
62666DFE27E5015700EC0ECD /* AVFoundation.framework in Frameworks */,
|
||||
62666DFD27E5014F00EC0ECD /* AudioToolbox.framework in Frameworks */,
|
||||
E19E6E0528A0B958005C10C8 /* Nuke in Frameworks */,
|
||||
62666E0D27E501AA00EC0ECD /* QuartzCore.framework in Frameworks */,
|
||||
E1AE8E7C2789135A00FBDDAA /* Nuke in Frameworks */,
|
||||
E19E6E0728A0B958005C10C8 /* NukeUI in Frameworks */,
|
||||
62666E3F27E5040300EC0ECD /* SystemConfiguration.framework in Frameworks */,
|
||||
625CB57A2678C4A400530A6E /* ActivityIndicator in Frameworks */,
|
||||
62666E3927E502CE00EC0ECD /* SwizzleSwift in Frameworks */,
|
||||
|
@ -1152,7 +1125,6 @@
|
|||
535870752669D60C00D05A09 /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E19E54F728972B97003CE330 /* BlurHashKit */,
|
||||
62C29E9D26D0FE5900C1D2E7 /* Coordinators */,
|
||||
E1FCD08E26C466F3007C8DCF /* Errors */,
|
||||
621338912660106C00A81A2A /* Extensions */,
|
||||
|
@ -1961,24 +1933,6 @@
|
|||
path = ItemView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E19E54F728972B97003CE330 /* BlurHashKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E19E54F828972B97003CE330 /* ColourSpace.swift */,
|
||||
E19E54F928972B97003CE330 /* BlurHash.swift */,
|
||||
E19E54FA28972B97003CE330 /* StringCoding.swift */,
|
||||
E19E54FB28972B97003CE330 /* FromString.swift */,
|
||||
E19E54FC28972B97003CE330 /* EscapeSequences.swift */,
|
||||
E19E54FD28972B97003CE330 /* ColourProbes.swift */,
|
||||
E19E54FE28972B97003CE330 /* ToString.swift */,
|
||||
E19E54FF28972B97003CE330 /* ToUIImage.swift */,
|
||||
E19E550028972B97003CE330 /* FromUIImage.swift */,
|
||||
E19E550228972B97003CE330 /* Generation.swift */,
|
||||
E19E550328972B97003CE330 /* TupleMaths.swift */,
|
||||
);
|
||||
path = BlurHashKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E1A16C9328875F2F00EA4679 /* Objects */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2179,11 +2133,13 @@
|
|||
E12186DD2718F1C50010884C /* Defaults */,
|
||||
E1218C9D271A2CD600EA0737 /* CombineExt */,
|
||||
E178857C278037FD0094FBCF /* JellyfinAPI */,
|
||||
E1AE8E7D2789136D00FBDDAA /* Nuke */,
|
||||
E11D83AE278FA998006E9776 /* NukeUI */,
|
||||
E1002B6A2793E36600E47059 /* Algorithms */,
|
||||
E1347DB5279E3CA500BC6161 /* Puppy */,
|
||||
C409CE9B284EA6EA00CABC12 /* SwiftUICollection */,
|
||||
E13AF3B528A0C598009093AB /* Nuke */,
|
||||
E13AF3B728A0C598009093AB /* NukeExtensions */,
|
||||
E13AF3B928A0C598009093AB /* NukeUI */,
|
||||
E13AF3BB28A0C59E009093AB /* BlurHashKit */,
|
||||
);
|
||||
productName = "JellyfinPlayer tvOS";
|
||||
productReference = 535870602669D21600D05A09 /* Swiftfin tvOS.app */;
|
||||
|
@ -2216,13 +2172,14 @@
|
|||
E1B6DCE9271A23880015B715 /* SwiftyJSON */,
|
||||
E10EAA44277BB646000269ED /* JellyfinAPI */,
|
||||
E10EAA4C277BB716000269ED /* Sliders */,
|
||||
E1AE8E7B2789135A00FBDDAA /* Nuke */,
|
||||
E1361DA6278FA7A300BEC523 /* NukeUI */,
|
||||
E1002B672793CFBA00E47059 /* Algorithms */,
|
||||
62666E3827E502CE00EC0ECD /* SwizzleSwift */,
|
||||
E1101176281B1E8A006A3584 /* Puppy */,
|
||||
C4D0CE4A2848570700345D11 /* ASCollectionView */,
|
||||
C409CE9D285044C800CABC12 /* SwiftUICollection */,
|
||||
E19E6E0428A0B958005C10C8 /* Nuke */,
|
||||
E19E6E0628A0B958005C10C8 /* NukeUI */,
|
||||
E19E6E0928A0BEFF005C10C8 /* BlurHashKit */,
|
||||
);
|
||||
productName = JellyfinPlayer;
|
||||
productReference = 5377CBF1263B596A003A4E83 /* Swiftfin iOS.app */;
|
||||
|
@ -2282,13 +2239,13 @@
|
|||
E1C16B89271A2180009A5D25 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
||||
E10EAA43277BB646000269ED /* XCRemoteSwiftPackageReference "jellyfin-sdk-swift" */,
|
||||
E10EAA4B277BB716000269ED /* XCRemoteSwiftPackageReference "swiftui-sliders" */,
|
||||
E1AE8E7A2789135A00FBDDAA /* XCRemoteSwiftPackageReference "Nuke" */,
|
||||
E1361DA5278FA7A300BEC523 /* XCRemoteSwiftPackageReference "NukeUI" */,
|
||||
E1002B662793CFBA00E47059 /* XCRemoteSwiftPackageReference "swift-algorithms" */,
|
||||
62666E3727E502CE00EC0ECD /* XCRemoteSwiftPackageReference "SwizzleSwift" */,
|
||||
E1101175281B1E8A006A3584 /* XCRemoteSwiftPackageReference "Puppy" */,
|
||||
C4D0CE492848570700345D11 /* XCRemoteSwiftPackageReference "ASCollectionView" */,
|
||||
C409CE9A284EA6EA00CABC12 /* XCRemoteSwiftPackageReference "SwiftUICollection" */,
|
||||
E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */,
|
||||
E19E6E0828A0BEFF005C10C8 /* XCRemoteSwiftPackageReference "BlurHashKit" */,
|
||||
);
|
||||
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -2413,7 +2370,6 @@
|
|||
E1A2C15A279A7D76005EC829 /* BundleExtensions.swift in Sources */,
|
||||
C40CD929271F8DAB000FB198 /* MovieLibrariesView.swift in Sources */,
|
||||
C4E5081D2703F8370045C9AB /* LibrarySearchView.swift in Sources */,
|
||||
E19E550728972B97003CE330 /* BlurHash.swift in Sources */,
|
||||
53ABFDE9267974EF00886593 /* HomeViewModel.swift in Sources */,
|
||||
E13DD3F027178F87009D4DAF /* SwiftfinNotificationCenter.swift in Sources */,
|
||||
E1A16CA1288A7CFD00EA4679 /* AboutViewCard.swift in Sources */,
|
||||
|
@ -2431,7 +2387,6 @@
|
|||
E193D53527193F8100900D82 /* ItemCoordinator.swift in Sources */,
|
||||
E1C926162887565C002A7A66 /* SeriesItemView.swift in Sources */,
|
||||
E1EBCB4A278BE443009FE6E9 /* ItemOverviewCoordinator.swift in Sources */,
|
||||
E19E550928972B97003CE330 /* StringCoding.swift in Sources */,
|
||||
C4BE07772725EBEA003F4AD1 /* LiveTVProgramsViewModel.swift in Sources */,
|
||||
E11CEB9128999D84003E74C7 /* EpisodeItemView.swift in Sources */,
|
||||
E1C9260C2887565C002A7A66 /* MovieItemContentView.swift in Sources */,
|
||||
|
@ -2469,16 +2424,13 @@
|
|||
E1C925F528875037002A7A66 /* ItemViewType.swift in Sources */,
|
||||
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
||||
53A83C33268A309300DF3D92 /* LibraryView.swift in Sources */,
|
||||
E19E550B28972B97003CE330 /* FromString.swift in Sources */,
|
||||
62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */,
|
||||
5398514526B64DA100101B49 /* SettingsView.swift in Sources */,
|
||||
62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
|
||||
E193D54B271941D300900D82 /* ServerListView.swift in Sources */,
|
||||
53ABFDE6267974EF00886593 /* SettingsViewModel.swift in Sources */,
|
||||
C400DB6B27FE8C97007B65FE /* LiveTVChannelItemElement.swift in Sources */,
|
||||
E19E551928972B97003CE330 /* Generation.swift in Sources */,
|
||||
62E632F4267D54030063E547 /* ItemViewModel.swift in Sources */,
|
||||
E19E550F28972B97003CE330 /* ColourProbes.swift in Sources */,
|
||||
6267B3D826710B9800A7371D /* CollectionExtensions.swift in Sources */,
|
||||
62E632E7267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
|
||||
E1C926122887565C002A7A66 /* SeriesItemContentView.swift in Sources */,
|
||||
|
@ -2490,12 +2442,10 @@
|
|||
E103A6A0278A7E4500820EC7 /* UICinematicBackgroundView.swift in Sources */,
|
||||
E11CEB9428999D9E003E74C7 /* EpisodeItemContentView.swift in Sources */,
|
||||
E17885A02780F55C0094FBCF /* tvOSVLCOverlay.swift in Sources */,
|
||||
E19E551328972B97003CE330 /* ToUIImage.swift in Sources */,
|
||||
E1BDE359278E9ED2004E4022 /* MissingItemsSettingsView.swift in Sources */,
|
||||
E193D54D2719426600900D82 /* LibraryFilterView.swift in Sources */,
|
||||
C4BE07892728448B003F4AD1 /* LiveTVChannelsCoordinator.swift in Sources */,
|
||||
E193D53927193F8E00900D82 /* SearchCoordinator.swift in Sources */,
|
||||
E19E551128972B97003CE330 /* ToString.swift in Sources */,
|
||||
C4BE078C272844AF003F4AD1 /* LiveTVChannelsView.swift in Sources */,
|
||||
E1D4BF852719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
||||
E1C926142887565C002A7A66 /* FocusGuide.swift in Sources */,
|
||||
|
@ -2555,7 +2505,6 @@
|
|||
E13DD3C927164B1E009D4DAF /* UIDeviceExtensions.swift in Sources */,
|
||||
E193D53A27193F9000900D82 /* ServerListCoordinator.swift in Sources */,
|
||||
6220D0AE26D5EABB00B8E046 /* ViewExtensions.swift in Sources */,
|
||||
E19E551528972B97003CE330 /* FromUIImage.swift in Sources */,
|
||||
E11895B42893844A0042947B /* BackgroundParallaxHeaderModifier.swift in Sources */,
|
||||
5321753E2671DE9C005491E6 /* Typings.swift in Sources */,
|
||||
E1C812CA277AE40900918266 /* PlayerOverlayDelegate.swift in Sources */,
|
||||
|
@ -2565,7 +2514,6 @@
|
|||
53ABFDEB2679753200886593 /* ConnectToServerView.swift in Sources */,
|
||||
E1C9260D2887565C002A7A66 /* CinematicScrollView.swift in Sources */,
|
||||
6264E88D273850380081A12A /* Strings.swift in Sources */,
|
||||
E19E550D28972B97003CE330 /* EscapeSequences.swift in Sources */,
|
||||
E1C926102887565C002A7A66 /* PlayButton.swift in Sources */,
|
||||
E193D5512719432400900D82 /* ServerDetailViewModel.swift in Sources */,
|
||||
C4E5081B2703F82A0045C9AB /* LibraryListView.swift in Sources */,
|
||||
|
@ -2575,7 +2523,6 @@
|
|||
E10D87E327852FD000BD264C /* EpisodesRowManager.swift in Sources */,
|
||||
535870632669D21600D05A09 /* JellyfinPlayer_tvOSApp.swift in Sources */,
|
||||
E1D4BF8F271A079A00A11E64 /* BasicAppSettingsView.swift in Sources */,
|
||||
E19E550528972B97003CE330 /* ColourSpace.swift in Sources */,
|
||||
E193D547271941C500900D82 /* UserListView.swift in Sources */,
|
||||
E1C926152887565C002A7A66 /* EpisodeCard.swift in Sources */,
|
||||
E1D4BF7F2719D1DD00A11E64 /* BasicAppSettingsViewModel.swift in Sources */,
|
||||
|
@ -2585,7 +2532,6 @@
|
|||
5364F456266CA0DC0026ECBA /* BaseItemPersonExtensions.swift in Sources */,
|
||||
E1937A62288F32DB00CB80AA /* Poster.swift in Sources */,
|
||||
C4BE0764271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */,
|
||||
E19E551B28972B97003CE330 /* TupleMaths.swift in Sources */,
|
||||
E1E5D5512783E67700692DFE /* ExperimentalSettingsView.swift in Sources */,
|
||||
E1A2C160279A7DCA005EC829 /* AboutAppView.swift in Sources */,
|
||||
C4BE076A271FC164003F4AD1 /* TVLibrariesView.swift in Sources */,
|
||||
|
@ -2628,14 +2574,11 @@
|
|||
E18E0208288749200022598C /* BlurView.swift in Sources */,
|
||||
E18E01E7288747230022598C /* CollectionItemContentView.swift in Sources */,
|
||||
53F866442687A45F00DCD1D7 /* PortraitPosterButton.swift in Sources */,
|
||||
E19E551A28972B97003CE330 /* TupleMaths.swift in Sources */,
|
||||
E18E01DF288747230022598C /* iPadOSMovieItemView.swift in Sources */,
|
||||
E168BD13289A4162001A6922 /* ContinueWatchingView.swift in Sources */,
|
||||
E19E550C28972B97003CE330 /* EscapeSequences.swift in Sources */,
|
||||
C45942CD27F6994A00C54FE7 /* LiveTVPlayerView.swift in Sources */,
|
||||
62C29EA126D102A500C1D2E7 /* iOSMainTabCoordinator.swift in Sources */,
|
||||
E18E01E8288747230022598C /* SeriesItemContentView.swift in Sources */,
|
||||
E19E550428972B97003CE330 /* ColourSpace.swift in Sources */,
|
||||
E18E01E5288747230022598C /* CinematicScrollView.swift in Sources */,
|
||||
E1C812C0277A8E5D00918266 /* VLCPlayerView.swift in Sources */,
|
||||
E176DE6D278E30D2001EFD8D /* EpisodeCard.swift in Sources */,
|
||||
|
@ -2668,7 +2611,6 @@
|
|||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
||||
62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */,
|
||||
E1047E2027E584AF00CB0D4A /* BlurHashView.swift in Sources */,
|
||||
E19E551028972B97003CE330 /* ToString.swift in Sources */,
|
||||
E18E01E1288747230022598C /* EpisodeItemContentView.swift in Sources */,
|
||||
62E632DA267D2BC40063E547 /* LatestMediaViewModel.swift in Sources */,
|
||||
C400DB6A27FE894F007B65FE /* LiveTVChannelsView.swift in Sources */,
|
||||
|
@ -2690,7 +2632,6 @@
|
|||
6264E88C273850380081A12A /* Strings.swift in Sources */,
|
||||
C4AE2C3227498D6A00AE13CF /* LiveTVProgramsView.swift in Sources */,
|
||||
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */,
|
||||
E19E550A28972B97003CE330 /* FromString.swift in Sources */,
|
||||
62E632E6267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
|
||||
5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */,
|
||||
E107BB9327880A8F00354E07 /* CollectionItemViewModel.swift in Sources */,
|
||||
|
@ -2701,7 +2642,6 @@
|
|||
E11895A9289383BC0042947B /* ScrollViewOffsetModifier.swift in Sources */,
|
||||
E1C812C3277A8E5D00918266 /* VLCPlayerOverlayView.swift in Sources */,
|
||||
E18E01F0288747230022598C /* AttributeHStack.swift in Sources */,
|
||||
E19E551228972B97003CE330 /* ToUIImage.swift in Sources */,
|
||||
6334175B287DDFB9000603CE /* QuickConnectSettingsView.swift in Sources */,
|
||||
E18E0205288749200022598C /* AppIcon.swift in Sources */,
|
||||
E168BD10289A4162001A6922 /* HomeView.swift in Sources */,
|
||||
|
@ -2727,10 +2667,7 @@
|
|||
E11895AC289383EE0042947B /* NavBarOffsetModifier.swift in Sources */,
|
||||
E13DD3FC2717EAE8009D4DAF /* UserListView.swift in Sources */,
|
||||
E18E01DE288747230022598C /* iPadOSSeriesItemView.swift in Sources */,
|
||||
E19E550828972B97003CE330 /* StringCoding.swift in Sources */,
|
||||
E19E551428972B97003CE330 /* FromUIImage.swift in Sources */,
|
||||
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */,
|
||||
E19E550628972B97003CE330 /* BlurHash.swift in Sources */,
|
||||
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
||||
E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */,
|
||||
631759CF2879DB6A00A621AD /* PublicUserSignInCellView.swift in Sources */,
|
||||
|
@ -2791,11 +2728,9 @@
|
|||
E13DD3D5271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */,
|
||||
62E1DCC3273CE19800C9AE76 /* URLExtensions.swift in Sources */,
|
||||
E1267D3E271A1F46003C492E /* PreferenceUIHostingController.swift in Sources */,
|
||||
E19E551828972B97003CE330 /* Generation.swift in Sources */,
|
||||
E18E01E6288747230022598C /* CollectionItemView.swift in Sources */,
|
||||
6220D0BA26D6092100B8E046 /* FilterCoordinator.swift in Sources */,
|
||||
E1E5D54C2783E27200692DFE /* ExperimentalSettingsView.swift in Sources */,
|
||||
E19E550E28972B97003CE330 /* ColourProbes.swift in Sources */,
|
||||
E1E00A35278628A40022235B /* DoubleExtensions.swift in Sources */,
|
||||
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
|
||||
E1D4BF8A2719D3D000A11E64 /* BasicAppSettingsCoordinator.swift in Sources */,
|
||||
|
@ -3328,14 +3263,6 @@
|
|||
kind = branch;
|
||||
};
|
||||
};
|
||||
E1361DA5278FA7A300BEC523 /* XCRemoteSwiftPackageReference "NukeUI" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/kean/NukeUI";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.7.0;
|
||||
};
|
||||
};
|
||||
E13DD3C42716499E009D4DAF /* XCRemoteSwiftPackageReference "CoreStore" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/JohnEstropia/CoreStore.git";
|
||||
|
@ -3352,12 +3279,20 @@
|
|||
minimumVersion = 6.0.0;
|
||||
};
|
||||
};
|
||||
E1AE8E7A2789135A00FBDDAA /* XCRemoteSwiftPackageReference "Nuke" */ = {
|
||||
E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/kean/Nuke";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 10.0.0;
|
||||
minimumVersion = 11.1.0;
|
||||
};
|
||||
};
|
||||
E19E6E0828A0BEFF005C10C8 /* XCRemoteSwiftPackageReference "BlurHashKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/LePips/BlurHashKit";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
};
|
||||
};
|
||||
E1C16B89271A2180009A5D25 /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
|
||||
|
@ -3446,11 +3381,6 @@
|
|||
package = E1101175281B1E8A006A3584 /* XCRemoteSwiftPackageReference "Puppy" */;
|
||||
productName = Puppy;
|
||||
};
|
||||
E11D83AE278FA998006E9776 /* NukeUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E1361DA5278FA7A300BEC523 /* XCRemoteSwiftPackageReference "NukeUI" */;
|
||||
productName = NukeUI;
|
||||
};
|
||||
E12186DD2718F1C50010884C /* Defaults */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E13DD3D127168E65009D4DAF /* XCRemoteSwiftPackageReference "Defaults" */;
|
||||
|
@ -3471,11 +3401,26 @@
|
|||
package = E1347DB0279E3C6200BC6161 /* XCRemoteSwiftPackageReference "Puppy" */;
|
||||
productName = Puppy;
|
||||
};
|
||||
E1361DA6278FA7A300BEC523 /* NukeUI */ = {
|
||||
E13AF3B528A0C598009093AB /* Nuke */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E1361DA5278FA7A300BEC523 /* XCRemoteSwiftPackageReference "NukeUI" */;
|
||||
package = E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = Nuke;
|
||||
};
|
||||
E13AF3B728A0C598009093AB /* NukeExtensions */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = NukeExtensions;
|
||||
};
|
||||
E13AF3B928A0C598009093AB /* NukeUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = NukeUI;
|
||||
};
|
||||
E13AF3BB28A0C59E009093AB /* BlurHashKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E19E6E0828A0BEFF005C10C8 /* XCRemoteSwiftPackageReference "BlurHashKit" */;
|
||||
productName = BlurHashKit;
|
||||
};
|
||||
E13DD3C52716499E009D4DAF /* CoreStore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E13DD3C42716499E009D4DAF /* XCRemoteSwiftPackageReference "CoreStore" */;
|
||||
|
@ -3496,15 +3441,20 @@
|
|||
package = E10EAA43277BB646000269ED /* XCRemoteSwiftPackageReference "jellyfin-sdk-swift" */;
|
||||
productName = JellyfinAPI;
|
||||
};
|
||||
E1AE8E7B2789135A00FBDDAA /* Nuke */ = {
|
||||
E19E6E0428A0B958005C10C8 /* Nuke */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E1AE8E7A2789135A00FBDDAA /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
package = E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = Nuke;
|
||||
};
|
||||
E1AE8E7D2789136D00FBDDAA /* Nuke */ = {
|
||||
E19E6E0628A0B958005C10C8 /* NukeUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E1AE8E7A2789135A00FBDDAA /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = Nuke;
|
||||
package = E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
productName = NukeUI;
|
||||
};
|
||||
E19E6E0928A0BEFF005C10C8 /* BlurHashKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E19E6E0828A0BEFF005C10C8 /* XCRemoteSwiftPackageReference "BlurHashKit" */;
|
||||
productName = BlurHashKit;
|
||||
};
|
||||
E1B6DCE7271A23780015B715 /* CombineExt */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
|
|
|
@ -27,6 +27,15 @@
|
|||
"version" : "2.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "blurhashkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/LePips/BlurHashKit",
|
||||
"state" : {
|
||||
"revision" : "ee9f34f4f8fc03f3d67622e2a6eeb65f5108f2a3",
|
||||
"version" : "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "combineext",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
@ -63,15 +72,6 @@
|
|||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gifu",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kaishin/Gifu",
|
||||
"state" : {
|
||||
"revision" : "51f2eab32903e336f590c013267cfa4d7f8b06c4",
|
||||
"version" : "3.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "jellyfin-sdk-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
@ -86,17 +86,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/Nuke",
|
||||
"state" : {
|
||||
"revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
|
||||
"version" : "10.11.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nukeui",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/NukeUI",
|
||||
"state" : {
|
||||
"revision" : "db55022a8e483774ebbf3568727071477546d9ea",
|
||||
"version" : "0.8.3"
|
||||
"revision" : "8ea514737c2011ff7d7544aa065ad44905bdae0b",
|
||||
"version" : "11.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
//
|
||||
|
||||
import JellyfinAPI
|
||||
import NukeUI
|
||||
import SwiftUI
|
||||
|
||||
struct ContinueWatchingView: View {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import BlurHashKit
|
||||
import SwiftUI
|
||||
|
||||
extension ItemView {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import BlurHashKit
|
||||
import SwiftUI
|
||||
|
||||
extension ItemView {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import BlurHashKit
|
||||
import SwiftUI
|
||||
|
||||
extension ItemView {
|
||||
|
|
Loading…
Reference in New Issue