@shortkitsdk/react-native 0.2.29 → 0.2.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/libs/shortkit-release.aar +0 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +8 -1
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +6 -0
- package/ios/ShortKitBridge.swift +5 -1
- package/ios/ShortKitFeedView.swift +9 -13
- package/ios/ShortKitFeedViewManager.mm +1 -0
- package/ios/ShortKitModule.mm +5 -1
- package/ios/ShortKitSDK.xcframework/Info.plist +5 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +4732 -872
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +77 -8
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +77 -8
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +9 -9
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +4732 -872
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +77 -8
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +77 -8
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +4732 -872
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +77 -8
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +77 -8
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +17 -17
- package/ios/ShortKitWidgetNativeView.swift +23 -0
- package/ios/ShortKitWidgetNativeViewManager.mm +1 -0
- package/package.json +1 -1
- package/src/ShortKitFeed.tsx +2 -0
- package/src/ShortKitProvider.tsx +4 -0
- package/src/ShortKitWidget.tsx +47 -2
- package/src/specs/NativeShortKitModule.ts +9 -0
- package/src/specs/ShortKitFeedViewNativeComponent.ts +5 -0
- package/src/specs/ShortKitWidgetViewNativeComponent.ts +6 -0
- package/src/types.ts +21 -0
- package/ios/DebugPanelView.swift +0 -302
package/src/types.ts
CHANGED
|
@@ -293,6 +293,10 @@ export interface ShortKitProviderProps {
|
|
|
293
293
|
loadingViewComponent?: React.ComponentType<{}>;
|
|
294
294
|
/** Enable the debug panel overlay (shake device to toggle). Defaults to false. */
|
|
295
295
|
debugPanel?: boolean;
|
|
296
|
+
/** Enable production trace logging to the ShortKit backend. Defaults to false. */
|
|
297
|
+
serverTracingEnabled?: boolean;
|
|
298
|
+
/** Enable [SKTrace] console output. For development only — defaults to false. */
|
|
299
|
+
consoleTracingEnabled?: boolean;
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
// --- Feed Component Props ---
|
|
@@ -342,6 +346,12 @@ export interface ShortKitFeedProps {
|
|
|
342
346
|
* @default true
|
|
343
347
|
*/
|
|
344
348
|
active?: boolean;
|
|
349
|
+
/**
|
|
350
|
+
* Enable the debug panel overlay on this specific feed surface (shake
|
|
351
|
+
* device to toggle). When omitted, falls back to the provider-level
|
|
352
|
+
* `debugPanel` flag on `<ShortKitProvider>`.
|
|
353
|
+
*/
|
|
354
|
+
debugPanel?: boolean;
|
|
345
355
|
onLoop?: (event: LoopEvent) => void;
|
|
346
356
|
onFeedTransition?: (event: FeedTransitionEvent) => void;
|
|
347
357
|
onFormatChange?: (event: FormatChangeEvent) => void;
|
|
@@ -474,6 +484,17 @@ export interface ShortKitWidgetProps {
|
|
|
474
484
|
* how `<ShortKitFeed feedItems={...} />` works.
|
|
475
485
|
*/
|
|
476
486
|
items?: WidgetInput[];
|
|
487
|
+
/**
|
|
488
|
+
* Per-card tap callback. When set, the SDK skips the built-in
|
|
489
|
+
* `clickAction: 'feed'` modal-presentation path and hands the tap to the
|
|
490
|
+
* host — typically used to push a feed screen via the host's navigator
|
|
491
|
+
* instead of letting the SDK present a UIKit modal that escapes the host's
|
|
492
|
+
* navigation hierarchy.
|
|
493
|
+
*
|
|
494
|
+
* Mirrors `ShortKitPlayer.onTap`. `clickAction: 'mute'` continues to work
|
|
495
|
+
* regardless (per-cell mute toggle runs alongside `onCardTap`).
|
|
496
|
+
*/
|
|
497
|
+
onCardTap?: (playbackId: string, index: number) => void;
|
|
477
498
|
style?: ViewStyle;
|
|
478
499
|
}
|
|
479
500
|
|
package/ios/DebugPanelView.swift
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
import UIKit
|
|
2
|
-
import Combine
|
|
3
|
-
import ShortKitSDK
|
|
4
|
-
|
|
5
|
-
private enum AdjacentSlot {
|
|
6
|
-
case previous, next
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
final class DebugPanelView: UIView {
|
|
10
|
-
static let panelWidth: CGFloat = 310
|
|
11
|
-
static let panelHeight: CGFloat = 296 // +20 for collapsed header row
|
|
12
|
-
static let expandedExtraHeight: CGFloat = 80 // 4 labels × ~20pt
|
|
13
|
-
|
|
14
|
-
/// Currently selected swipe curve — FeedViewController reads SwipeCurve.selected.
|
|
15
|
-
static var selectedSwipeCurve: SwipeCurve {
|
|
16
|
-
get { SwipeCurve.selected }
|
|
17
|
-
set { SwipeCurve.selected = newValue }
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
private let stackView = UIStackView()
|
|
21
|
-
private let headerLabel = UILabel()
|
|
22
|
-
private let curveSegment = UISegmentedControl()
|
|
23
|
-
private let positionLabel = UILabel()
|
|
24
|
-
private let intentTtffLabel = UILabel()
|
|
25
|
-
private let transitionTtffLabel = UILabel()
|
|
26
|
-
private let prerollBufferLabel = UILabel()
|
|
27
|
-
private let videoQualityLabel = UILabel()
|
|
28
|
-
private let thumbnailLabel = UILabel()
|
|
29
|
-
private let prefetchLabel = UILabel()
|
|
30
|
-
private let bitrateLabel = UILabel()
|
|
31
|
-
private let rebufferLabel = UILabel()
|
|
32
|
-
private let loopWatchLabel = UILabel()
|
|
33
|
-
|
|
34
|
-
// Adjacent players section
|
|
35
|
-
private let adjacentHeader = UILabel()
|
|
36
|
-
private let prevHeaderLabel = UILabel()
|
|
37
|
-
private let prevDetailLabel = UILabel()
|
|
38
|
-
private let nextHeaderLabel = UILabel()
|
|
39
|
-
private let nextDetailLabel = UILabel()
|
|
40
|
-
private let separatorView = UIView()
|
|
41
|
-
private var adjacentLabels: [UIView] = []
|
|
42
|
-
private var isAdjacentExpanded = false
|
|
43
|
-
private var prevCancellable: AnyCancellable?
|
|
44
|
-
private var nextCancellable: AnyCancellable?
|
|
45
|
-
|
|
46
|
-
private var cancellable: AnyCancellable?
|
|
47
|
-
private var dragOffset: CGPoint = .zero
|
|
48
|
-
|
|
49
|
-
override init(frame: CGRect) {
|
|
50
|
-
super.init(frame: frame)
|
|
51
|
-
setupViews()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
required init?(coder: NSCoder) {
|
|
55
|
-
fatalError("init(coder:) has not been implemented")
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private func setupViews() {
|
|
59
|
-
backgroundColor = UIColor.black.withAlphaComponent(0.75)
|
|
60
|
-
layer.cornerRadius = 10
|
|
61
|
-
clipsToBounds = true
|
|
62
|
-
|
|
63
|
-
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
|
64
|
-
addGestureRecognizer(pan)
|
|
65
|
-
|
|
66
|
-
let labels = [headerLabel, positionLabel, intentTtffLabel, transitionTtffLabel, prerollBufferLabel, videoQualityLabel, thumbnailLabel, prefetchLabel, bitrateLabel, rebufferLabel, loopWatchLabel]
|
|
67
|
-
for label in labels {
|
|
68
|
-
label.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .regular)
|
|
69
|
-
label.textColor = .white
|
|
70
|
-
label.numberOfLines = 1
|
|
71
|
-
}
|
|
72
|
-
headerLabel.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .bold)
|
|
73
|
-
|
|
74
|
-
stackView.axis = .vertical
|
|
75
|
-
stackView.spacing = 4
|
|
76
|
-
stackView.alignment = .leading
|
|
77
|
-
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
78
|
-
addSubview(stackView)
|
|
79
|
-
|
|
80
|
-
for label in labels {
|
|
81
|
-
stackView.addArrangedSubview(label)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Swipe curve picker
|
|
85
|
-
for (i, curve) in SwipeCurve.allCases.enumerated() {
|
|
86
|
-
curveSegment.insertSegment(withTitle: curve.label, at: i, animated: false)
|
|
87
|
-
}
|
|
88
|
-
curveSegment.selectedSegmentIndex = SwipeCurve.quarticOut.rawValue
|
|
89
|
-
curveSegment.setTitleTextAttributes([.foregroundColor: UIColor.white, .font: UIFont.monospacedSystemFont(ofSize: 10, weight: .medium)], for: .normal)
|
|
90
|
-
curveSegment.setTitleTextAttributes([.foregroundColor: UIColor.black], for: .selected)
|
|
91
|
-
curveSegment.backgroundColor = UIColor.white.withAlphaComponent(0.15)
|
|
92
|
-
curveSegment.selectedSegmentTintColor = UIColor.white.withAlphaComponent(0.9)
|
|
93
|
-
curveSegment.addTarget(self, action: #selector(curveChanged), for: .valueChanged)
|
|
94
|
-
stackView.addArrangedSubview(curveSegment)
|
|
95
|
-
|
|
96
|
-
// Separator
|
|
97
|
-
separatorView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
|
|
98
|
-
separatorView.translatesAutoresizingMaskIntoConstraints = false
|
|
99
|
-
separatorView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
|
|
100
|
-
stackView.addArrangedSubview(separatorView)
|
|
101
|
-
|
|
102
|
-
// Adjacent header (tap target)
|
|
103
|
-
adjacentHeader.font = UIFont.monospacedSystemFont(ofSize: 12, weight: .bold)
|
|
104
|
-
adjacentHeader.textColor = .white
|
|
105
|
-
adjacentHeader.text = "▶ Adjacent Players"
|
|
106
|
-
adjacentHeader.isUserInteractionEnabled = true
|
|
107
|
-
adjacentHeader.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(toggleAdjacent)))
|
|
108
|
-
stackView.addArrangedSubview(adjacentHeader)
|
|
109
|
-
|
|
110
|
-
// Adjacent player labels (initially hidden)
|
|
111
|
-
let adjLabels = [prevHeaderLabel, prevDetailLabel, nextHeaderLabel, nextDetailLabel]
|
|
112
|
-
for label in adjLabels {
|
|
113
|
-
label.font = UIFont.monospacedSystemFont(ofSize: 11, weight: .regular)
|
|
114
|
-
label.textColor = UIColor.white.withAlphaComponent(0.85)
|
|
115
|
-
label.numberOfLines = 1
|
|
116
|
-
label.isHidden = true
|
|
117
|
-
stackView.addArrangedSubview(label)
|
|
118
|
-
}
|
|
119
|
-
adjacentLabels = adjLabels
|
|
120
|
-
|
|
121
|
-
prevHeaderLabel.text = "▲ Prev — not assigned"
|
|
122
|
-
prevDetailLabel.text = ""
|
|
123
|
-
nextHeaderLabel.text = "▼ Next — not assigned"
|
|
124
|
-
nextDetailLabel.text = ""
|
|
125
|
-
|
|
126
|
-
NSLayoutConstraint.activate([
|
|
127
|
-
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 10),
|
|
128
|
-
stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
|
|
129
|
-
stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
|
|
130
|
-
])
|
|
131
|
-
|
|
132
|
-
// Initial state
|
|
133
|
-
update(DebugMetrics())
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
|
|
137
|
-
guard let parent = superview else { return }
|
|
138
|
-
switch gesture.state {
|
|
139
|
-
case .began:
|
|
140
|
-
dragOffset = gesture.location(in: self)
|
|
141
|
-
case .changed:
|
|
142
|
-
let location = gesture.location(in: parent)
|
|
143
|
-
var newCenter = CGPoint(
|
|
144
|
-
x: location.x - dragOffset.x + bounds.width / 2,
|
|
145
|
-
y: location.y - dragOffset.y + bounds.height / 2
|
|
146
|
-
)
|
|
147
|
-
// Keep panel within parent bounds
|
|
148
|
-
let hw = bounds.width / 2, hh = bounds.height / 2
|
|
149
|
-
newCenter.x = max(hw, min(parent.bounds.width - hw, newCenter.x))
|
|
150
|
-
newCenter.y = max(hh, min(parent.bounds.height - hh, newCenter.y))
|
|
151
|
-
center = newCenter
|
|
152
|
-
default:
|
|
153
|
-
break
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@objc private func curveChanged(_ sender: UISegmentedControl) {
|
|
158
|
-
guard let curve = SwipeCurve(rawValue: sender.selectedSegmentIndex) else { return }
|
|
159
|
-
Self.selectedSwipeCurve = curve
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@objc private func toggleAdjacent() {
|
|
163
|
-
isAdjacentExpanded.toggle()
|
|
164
|
-
adjacentHeader.text = isAdjacentExpanded ? "▼ Adjacent Players" : "▶ Adjacent Players"
|
|
165
|
-
|
|
166
|
-
UIView.animate(withDuration: 0.25) {
|
|
167
|
-
for label in self.adjacentLabels {
|
|
168
|
-
label.isHidden = !self.isAdjacentExpanded
|
|
169
|
-
label.alpha = self.isAdjacentExpanded ? 1 : 0
|
|
170
|
-
}
|
|
171
|
-
// Resize panel
|
|
172
|
-
var newFrame = self.frame
|
|
173
|
-
let baseHeight = Self.panelHeight
|
|
174
|
-
newFrame.size.height = self.isAdjacentExpanded
|
|
175
|
-
? baseHeight + Self.expandedExtraHeight
|
|
176
|
-
: baseHeight
|
|
177
|
-
self.frame = newFrame
|
|
178
|
-
self.layoutIfNeeded()
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
func subscribe(to subject: CurrentValueSubject<DebugMetrics, Never>) {
|
|
183
|
-
cancellable = subject
|
|
184
|
-
.receive(on: DispatchQueue.main)
|
|
185
|
-
.sink { [weak self] metrics in
|
|
186
|
-
self?.update(metrics)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
func subscribeAdjacent(
|
|
191
|
-
prev: CurrentValueSubject<DebugMetrics, Never>,
|
|
192
|
-
next: CurrentValueSubject<DebugMetrics, Never>
|
|
193
|
-
) {
|
|
194
|
-
prevCancellable = prev
|
|
195
|
-
.receive(on: DispatchQueue.main)
|
|
196
|
-
.sink { [weak self] metrics in
|
|
197
|
-
self?.updateAdjacent(metrics, slot: .previous)
|
|
198
|
-
}
|
|
199
|
-
nextCancellable = next
|
|
200
|
-
.receive(on: DispatchQueue.main)
|
|
201
|
-
.sink { [weak self] metrics in
|
|
202
|
-
self?.updateAdjacent(metrics, slot: .next)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private func updateAdjacent(_ m: DebugMetrics, slot: AdjacentSlot) {
|
|
207
|
-
let headerLabel = slot == .previous ? prevHeaderLabel : nextHeaderLabel
|
|
208
|
-
let detailLabel = slot == .previous ? prevDetailLabel : nextDetailLabel
|
|
209
|
-
let arrow = slot == .previous ? "▲ Prev" : "▼ Next"
|
|
210
|
-
|
|
211
|
-
guard m.contentId != nil else {
|
|
212
|
-
headerLabel.text = "\(arrow) — not assigned"
|
|
213
|
-
detailLabel.text = ""
|
|
214
|
-
return
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
let id = m.contentId.map { String($0.prefix(8)) } ?? "—"
|
|
218
|
-
let stateIcon: String
|
|
219
|
-
switch m.playerState {
|
|
220
|
-
case .playing: stateIcon = "▶"
|
|
221
|
-
case .paused: stateIcon = "⏸"
|
|
222
|
-
case .buffering: stateIcon = "⏳"
|
|
223
|
-
default: stateIcon = "○"
|
|
224
|
-
}
|
|
225
|
-
headerLabel.text = "\(arrow) #\(m.feedIndex) \(id) \(stateIcon) \(m.playerState)"
|
|
226
|
-
|
|
227
|
-
let buf = String(format: "%.1f", m.bufferAhead)
|
|
228
|
-
let preroll = m.prerollCompleted ? "✓" : "✗"
|
|
229
|
-
let mbps = m.observedBitrateBps / 1_000_000
|
|
230
|
-
var detail = " buf: \(buf)s preroll: \(preroll)"
|
|
231
|
-
if mbps > 0 {
|
|
232
|
-
detail += String(format: " %.1f Mbps", mbps)
|
|
233
|
-
}
|
|
234
|
-
if m.videoWidth > 0 {
|
|
235
|
-
let codec = m.codec ?? ""
|
|
236
|
-
detail += " \(m.videoWidth)×\(m.videoHeight) \(codec)"
|
|
237
|
-
}
|
|
238
|
-
detailLabel.text = detail
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
private func update(_ m: DebugMetrics) {
|
|
242
|
-
let id = m.contentId.map { String($0.prefix(8)) } ?? "—"
|
|
243
|
-
let stateIcon: String
|
|
244
|
-
switch m.playerState {
|
|
245
|
-
case .playing: stateIcon = "▶"
|
|
246
|
-
case .paused: stateIcon = "⏸"
|
|
247
|
-
case .buffering: stateIcon = "⏳"
|
|
248
|
-
case .seeking: stateIcon = "⏩"
|
|
249
|
-
case .error: stateIcon = "⚠️"
|
|
250
|
-
default: stateIcon = "○"
|
|
251
|
-
}
|
|
252
|
-
headerLabel.text = "#\(m.feedIndex) \(id) \(stateIcon) \(m.playerState)"
|
|
253
|
-
|
|
254
|
-
let cur = String(format: "%.1f", m.currentTime)
|
|
255
|
-
let dur = String(format: "%.1f", m.duration)
|
|
256
|
-
positionLabel.text = "Position: \(cur)s / \(dur)s"
|
|
257
|
-
|
|
258
|
-
if let intent = m.intentToFrameMs {
|
|
259
|
-
intentTtffLabel.text = "Intent: \(intent)ms"
|
|
260
|
-
} else {
|
|
261
|
-
intentTtffLabel.text = "Intent: N/A"
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if let transition = m.transitionToFrameMs {
|
|
265
|
-
let net = m.networkMs.map { "\($0)" } ?? "?"
|
|
266
|
-
let dec = m.decodeMs.map { "\($0)" } ?? "?"
|
|
267
|
-
transitionTtffLabel.text = "Transition: \(transition)ms (net: \(net) + dec: \(dec))"
|
|
268
|
-
} else {
|
|
269
|
-
transitionTtffLabel.text = "Transition: measuring..."
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
let preroll = m.prerollCompleted ? "✓" : "✗"
|
|
273
|
-
let buffer = String(format: "%.1f", m.bufferAhead)
|
|
274
|
-
prerollBufferLabel.text = "Preroll: \(preroll) Buffer: \(buffer)s"
|
|
275
|
-
|
|
276
|
-
let codec = m.codec ?? "—"
|
|
277
|
-
if m.videoWidth > 0 {
|
|
278
|
-
videoQualityLabel.text = "Video: \(m.videoWidth)×\(m.videoHeight) \(codec)"
|
|
279
|
-
} else {
|
|
280
|
-
videoQualityLabel.text = "Video: —"
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
thumbnailLabel.text = "Thumbnail: \(m.thumbnailVisible ? "visible" : "removed")"
|
|
284
|
-
prefetchLabel.text = "Prefetch: \(m.thumbsCachedAhead)/\(m.metadataAhead) thumbs \(m.metadataAhead) meta"
|
|
285
|
-
|
|
286
|
-
let obsMbps = m.observedBitrateBps / 1_000_000
|
|
287
|
-
let indMbps = m.indicatedBitrateBps / 1_000_000
|
|
288
|
-
if obsMbps > 0 {
|
|
289
|
-
bitrateLabel.text = String(format: "Bitrate: %.1f Mbps (ind: %.1f)", obsMbps, indMbps)
|
|
290
|
-
} else {
|
|
291
|
-
bitrateLabel.text = "Bitrate: —"
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if let lastMs = m.lastRebufferDurationMs {
|
|
295
|
-
rebufferLabel.text = "Rebuffers: \(m.rebufferCount) (last: \(lastMs)ms)"
|
|
296
|
-
} else {
|
|
297
|
-
rebufferLabel.text = "Rebuffers: \(m.rebufferCount)"
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
loopWatchLabel.text = "Loops: \(m.loopCount)"
|
|
301
|
-
}
|
|
302
|
-
}
|