Features: - VOD library with movie grouping and version detection - TV show library with season/episode organization - TMDB integration for trending shows and recently aired episodes - Recent releases section with TMDB release date sorting - Watch history tracking with continue watching - Playlist caching (12-hour TTL) for offline support - M3U playlist parsing with XStream API support - Authentication with credential storage Technical: - SwiftUI for tvOS - Actor-based services for thread safety - Persistent caching for playlists, TMDB data, and watch history - KSPlayer integration for video playback Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
90 lines
2.7 KiB
Swift
90 lines
2.7 KiB
Swift
//
|
|
// MotionSensor.swift
|
|
// KSPlayer-iOS
|
|
//
|
|
// Created by kintan on 2020/1/13.
|
|
//
|
|
|
|
#if canImport(UIKit) && canImport(CoreMotion)
|
|
import CoreMotion
|
|
import Foundation
|
|
import simd
|
|
import UIKit
|
|
|
|
@MainActor
|
|
final class MotionSensor {
|
|
static let shared = MotionSensor()
|
|
private let manager = CMMotionManager()
|
|
private let worldToInertialReferenceFrame = simd_float4x4(euler: -90, y: 0, z: 90)
|
|
private var deviceToDisplay = simd_float4x4.identity
|
|
private let defaultRadiansY: Float
|
|
private var orientation = UIInterfaceOrientation.unknown {
|
|
didSet {
|
|
if oldValue != orientation {
|
|
switch orientation {
|
|
case .portraitUpsideDown:
|
|
deviceToDisplay = simd_float4x4(euler: 0, y: 0, z: 180)
|
|
case .landscapeRight:
|
|
deviceToDisplay = simd_float4x4(euler: 0, y: 0, z: -90)
|
|
case .landscapeLeft:
|
|
deviceToDisplay = simd_float4x4(euler: 0, y: 0, z: 90)
|
|
default:
|
|
deviceToDisplay = simd_float4x4.identity
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private init() {
|
|
switch KSOptions.windowScene?.interfaceOrientation {
|
|
case .landscapeRight:
|
|
defaultRadiansY = -.pi / 2
|
|
case .landscapeLeft:
|
|
defaultRadiansY = .pi / 2
|
|
default:
|
|
defaultRadiansY = 0
|
|
}
|
|
}
|
|
|
|
func ready() -> Bool {
|
|
manager.isDeviceMotionAvailable ? manager.isDeviceMotionActive : false
|
|
}
|
|
|
|
func start() {
|
|
if manager.isDeviceMotionAvailable, !manager.isDeviceMotionActive {
|
|
manager.deviceMotionUpdateInterval = 1 / 60
|
|
manager.startDeviceMotionUpdates()
|
|
}
|
|
}
|
|
|
|
func stop() {
|
|
manager.stopDeviceMotionUpdates()
|
|
}
|
|
|
|
func matrix() -> simd_float4x4? {
|
|
if var matrix = manager.deviceMotion.flatMap(simd_float4x4.init(motion:)) {
|
|
matrix = matrix.transpose
|
|
matrix *= worldToInertialReferenceFrame
|
|
orientation = KSOptions.windowScene?.interfaceOrientation ?? .portrait
|
|
matrix = deviceToDisplay * matrix
|
|
matrix = matrix.rotateY(radians: defaultRadiansY)
|
|
return matrix
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public extension simd_float4x4 {
|
|
init(motion: CMDeviceMotion) {
|
|
self.init(rotation: motion.attitude.rotationMatrix)
|
|
}
|
|
|
|
init(rotation: CMRotationMatrix) {
|
|
self.init(SIMD4<Float>(Float(rotation.m11), Float(rotation.m12), Float(rotation.m13), 0.0),
|
|
SIMD4<Float>(Float(rotation.m21), Float(rotation.m22), Float(rotation.m23), 0.0),
|
|
SIMD4<Float>(Float(rotation.m31), Float(rotation.m32), Float(rotation.m33), -1),
|
|
SIMD4<Float>(0, 0, 0, 1))
|
|
}
|
|
}
|
|
#endif
|