Files
simvision/KSPlayer-main/Sources/KSPlayer/Video/KSMenu.swift
Michael Simard 872354b834 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>
2026-01-21 22:12:08 -06:00

100 lines
2.6 KiB
Swift

//
// 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