Initial commit: SimVision tvOS streaming app
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>
This commit is contained in:
99
KSPlayer-main/Sources/KSPlayer/Video/KSMenu.swift
Normal file
99
KSPlayer-main/Sources/KSPlayer/Video/KSMenu.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// KSMenu.swift
|
||||
// KSPlayer
|
||||
//
|
||||
// Created by Alanko5 on 15/12/2022.
|
||||
//
|
||||
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
#else
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
extension UIMenu {
|
||||
func updateActionState(actionTitle: String? = nil) -> UIMenu {
|
||||
for action in children {
|
||||
guard let action = action as? UIAction else {
|
||||
continue
|
||||
}
|
||||
action.state = action.title == actionTitle ? .on : .off
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
@available(tvOS 15.0, *)
|
||||
convenience init?<U>(title: String, current: U?, list: [U], addDisabled: Bool = false, titleFunc: (U) -> String, completition: @escaping (String, U?) -> Void) {
|
||||
if list.count < (addDisabled ? 1 : 2) {
|
||||
return nil
|
||||
}
|
||||
var actions = list.map { value in
|
||||
let item = UIAction(title: titleFunc(value)) { item in
|
||||
completition(item.title, value)
|
||||
}
|
||||
|
||||
if let current, titleFunc(value) == titleFunc(current) {
|
||||
item.state = .on
|
||||
}
|
||||
return item
|
||||
}
|
||||
if addDisabled {
|
||||
actions.insert(UIAction(title: "Disabled") { item in
|
||||
completition(item.title, nil)
|
||||
}, at: 0)
|
||||
}
|
||||
|
||||
self.init(title: title, children: actions)
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
extension UIButton {
|
||||
@available(iOS 14.0, *)
|
||||
func setMenu<U>(title: String, current: U?, list: [U], addDisabled: Bool = false, titleFunc: (U) -> String, completition handler: @escaping (U?) -> Void) {
|
||||
menu = UIMenu(title: title, current: current, list: list, addDisabled: addDisabled, titleFunc: titleFunc) { [weak self] title, value in
|
||||
guard let self else { return }
|
||||
handler(value)
|
||||
self.menu = self.menu?.updateActionState(actionTitle: title)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if canImport(UIKit)
|
||||
|
||||
#else
|
||||
public typealias UIMenu = NSMenu
|
||||
|
||||
public final class UIAction: NSMenuItem {
|
||||
private let handler: (UIAction) -> Void
|
||||
init(title: String, handler: @escaping (UIAction) -> Void) {
|
||||
self.handler = handler
|
||||
super.init(title: title, action: #selector(menuPressed), keyEquivalent: "")
|
||||
state = .off
|
||||
target = self
|
||||
}
|
||||
|
||||
@objc private func menuPressed() {
|
||||
handler(self)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
extension UIMenu {
|
||||
var children: [NSMenuItem] {
|
||||
items
|
||||
}
|
||||
|
||||
convenience init(title: String, children: [UIAction]) {
|
||||
self.init(title: title)
|
||||
for item in children {
|
||||
addItem(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user