-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathLiveStreamEvent.swift
374 lines (340 loc) · 13.5 KB
/
LiveStreamEvent.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
import Argo
import Curry
import Prelude
import Runes
public struct LiveStreamEvent: Equatable {
public fileprivate(set) var backgroundImage: BackgroundImage
public fileprivate(set) var creator: Creator
public fileprivate(set) var description: String
public fileprivate(set) var firebase: Firebase?
public fileprivate(set) var hasReplay: Bool
public fileprivate(set) var hlsUrl: String?
public fileprivate(set) var id: Int
public fileprivate(set) var isRtmp: Bool?
public fileprivate(set) var isScale: Bool?
public fileprivate(set) var liveNow: Bool
public fileprivate(set) var maxOpenTokViewers: Int?
public fileprivate(set) var name: String
public fileprivate(set) var openTok: OpenTok?
public fileprivate(set) var project: Project
public fileprivate(set) var replayUrl: String?
public fileprivate(set) var startDate: Date
public fileprivate(set) var user: User?
public fileprivate(set) var webUrl: String
public fileprivate(set) var numberPeopleWatching: Int?
public struct BackgroundImage {
public fileprivate(set) var medium: String
public fileprivate(set) var smallCropped: String
}
public struct Creator {
public fileprivate(set) var avatar: String
public fileprivate(set) var name: String
}
public struct Firebase {
public fileprivate(set) var apiKey: String
public fileprivate(set) var chatAvatarUrl: String?
public fileprivate(set) var chatPath: String
public fileprivate(set) var chatPostPath: String?
public fileprivate(set) var chatUserId: String?
public fileprivate(set) var chatUserName: String?
public fileprivate(set) var greenRoomPath: String
public fileprivate(set) var hlsUrlPath: String
public fileprivate(set) var numberPeopleWatchingPath: String
public fileprivate(set) var project: String
public fileprivate(set) var scaleNumberPeopleWatchingPath: String
public fileprivate(set) var token: String?
}
public struct OpenTok {
public fileprivate(set) var appId: String
public fileprivate(set) var sessionId: String
public fileprivate(set) var token: String
}
public struct Project {
public fileprivate(set) var id: Int?
public fileprivate(set) var name: String
public fileprivate(set) var webUrl: String
}
public struct User {
public fileprivate(set) var isSubscribed: Bool
}
// Useful for safeguarding against getting a `hasReplay == true` yet the `replayUrl` is `nil`.
public var definitelyHasReplay: Bool {
return self.hasReplay && self.replayUrl != nil
}
public static func canonicalLiveStreamEventComparator(now: Date) -> Prelude.Comparator<LiveStreamEvent> {
// Compares two live streams, putting live ones first.
let currentlyLiveStreamsFirstComparator = Prelude.Comparator<LiveStreamEvent> { lhs, rhs in
switch (lhs.liveNow, rhs.liveNow) {
case (true, false): return .lt
case (false, true): return .gt
case (true, true), (false, false): return .eq
}
}
// Compares two live streams, putting the future ones first.
let futureLiveStreamsFirstComparator = Prelude.Comparator<LiveStreamEvent> { lhs, rhs in
lhs.startDate > now && rhs.startDate > now || lhs.startDate < now && rhs.startDate < now
? .eq : lhs.startDate < rhs.startDate ? .gt
: .lt
}
// Compares two live streams, putting soon-to-be-live first and way-back past last.
let startDateComparator = Prelude.Comparator<LiveStreamEvent> { lhs, rhs in
lhs.startDate > now
? (lhs.startDate == rhs.startDate ? .eq : lhs.startDate < rhs.startDate ? .lt: .gt)
: (lhs.startDate == rhs.startDate ? .eq : lhs.startDate < rhs.startDate ? .gt: .lt)
}
// Sort by:
// * live streams first
// * then future streams first and past streams last
// * future streams sorted by start date asc, past streams sorted by start date desc
return currentlyLiveStreamsFirstComparator
<> futureLiveStreamsFirstComparator
<> startDateComparator
}
}
public func == (lhs: LiveStreamEvent, rhs: LiveStreamEvent) -> Bool {
return lhs.id == rhs.id
}
extension LiveStreamEvent: Argo.Decodable {
static public func decode(_ json: JSON) -> Decoded<LiveStreamEvent> {
let create = curry(LiveStreamEvent.init)
let hlsUrl: Decoded<String?> = (json <| ["stream", "hls_url"] <|> json <| "hls_url")
.map(Optional.some)
<|> .success(nil)
let tmp1 = create
<^> (json <| ["stream", "background_image"] <|> json <| "background_image")
<*> json <| "creator"
<*> (json <| ["stream", "description"] <|> json <| "description")
<*> json <|? "firebase"
let tmp2 = tmp1
<*> (json <| ["stream", "has_replay"] <|> json <| "has_replay")
<*> hlsUrl
<*> json <| "id"
<*> json <|? ["stream", "is_rtmp"]
let tmp3 = tmp2
<*> json <|? ["stream", "is_scale"]
<*> (json <| ["stream", "live_now"] <|> json <| "live_now")
<*> json <|? ["stream", "max_opentok_viewers"]
<*> (json <| ["stream", "name"] <|> json <| "name")
let tmp4 = tmp3
<*> json <|? "opentok"
// Sometimes the project data is included in a `stream` sub-key, and sometimes it's in a `project`.
<*> (json <| "stream" <|> json <| "project")
<*> json <|? ["stream", "replay_url"]
<*> ((json <| "start_date" <|> json <| ["stream", "start_date"]) >>- toDate)
return tmp4
<*> json <|? "user"
<*> (json <| ["stream", "web_url"] <|> json <| "web_url")
<*> json <|? "number_people_watching"
}
}
extension LiveStreamEvent.BackgroundImage: Argo.Decodable {
public static func decode(_ json: JSON) -> Decoded<LiveStreamEvent.BackgroundImage> {
return curry(LiveStreamEvent.BackgroundImage.init)
<^> json <| "medium"
<*> json <| "small_cropped"
}
}
extension LiveStreamEvent.Creator: Argo.Decodable {
static public func decode(_ json: JSON) -> Decoded<LiveStreamEvent.Creator> {
return curry(LiveStreamEvent.Creator.init)
<^> json <| "creator_avatar"
<*> json <| "creator_name"
}
}
extension LiveStreamEvent.Firebase: Argo.Decodable {
static public func decode(_ json: JSON) -> Decoded<LiveStreamEvent.Firebase> {
let create = curry(LiveStreamEvent.Firebase.init)
let tmp = create
<^> json <| "firebase_api_key"
<*> json <|? "avatar"
<*> json <| "chat_path"
<*> json <|? "chat_post_path"
<*> json <|? "user_id"
<*> json <|? "user_name"
return tmp
<*> json <| "green_room_path"
<*> json <| "hls_url_path"
<*> json <| "number_people_watching_path"
<*> json <| "firebase_project"
<*> json <| "scale_number_people_watching_path"
<*> json <|? "token"
}
}
extension LiveStreamEvent.OpenTok: Argo.Decodable {
static public func decode(_ json: JSON) -> Decoded<LiveStreamEvent.OpenTok> {
return curry(LiveStreamEvent.OpenTok.init)
<^> json <| "app"
<*> json <| "session"
<*> json <| "token"
}
}
extension LiveStreamEvent.Project: Argo.Decodable {
static public func decode(_ json: JSON) -> Decoded<LiveStreamEvent.Project> {
// Sometimes the project id doesn't come back, and sometimes it comes back as `uid` even though it should
// probably just be `id`, so want to protect against that.
let id: Decoded<Int?> = (json <| "uid").map(Optional.some)
<|> (json <| "id").map(Optional.some)
<|> .success(nil)
return curry(LiveStreamEvent.Project.init)
<^> id
<*> (json <| "project_name" <|> json <| "name")
<*> (json <| "project_web_url" <|> json <| "web_url")
}
}
extension LiveStreamEvent.User: Argo.Decodable {
static public func decode(_ json: JSON) -> Decoded<LiveStreamEvent.User> {
return curry(LiveStreamEvent.User.init)
<^> json <| "is_subscribed"
}
}
private let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
return dateFormatter
}()
private func toDate(dateString: String) -> Decoded<Date> {
guard let date = dateFormatter.date(from: dateString) else {
return .failure(DecodeError.custom("Unable to parse date format"))
}
return .success(date)
}
extension LiveStreamEvent {
public enum lens {
public static let backgroundImage = Lens<LiveStreamEvent, LiveStreamEvent.BackgroundImage>(
view: { $0.backgroundImage },
set: { var new = $1; new.backgroundImage = $0; return new }
)
public static let creator = Lens<LiveStreamEvent, LiveStreamEvent.Creator>(
view: { $0.creator },
set: { var new = $1; new.creator = $0; return new }
)
public static let description = Lens<LiveStreamEvent, String>(
view: { $0.description },
set: { var new = $1; new.description = $0; return new }
)
public static let firebase = Lens<LiveStreamEvent, LiveStreamEvent.Firebase?>(
view: { $0.firebase },
set: { var new = $1; new.firebase = $0; return new }
)
public static let hasReplay = Lens<LiveStreamEvent, Bool>(
view: { $0.hasReplay },
set: { var new = $1; new.hasReplay = $0; return new }
)
public static let hlsUrl = Lens<LiveStreamEvent, String?>(
view: { $0.hlsUrl },
set: { var new = $1; new.hlsUrl = $0; return new }
)
public static let id = Lens<LiveStreamEvent, Int>(
view: { $0.id },
set: { var new = $1; new.id = $0; return new }
)
public static let isRtmp = Lens<LiveStreamEvent, Bool?>(
view: { $0.isRtmp },
set: { var new = $1; new.isRtmp = $0; return new }
)
public static let isScale = Lens<LiveStreamEvent, Bool?>(
view: { $0.isScale },
set: { var new = $1; new.isScale = $0; return new }
)
public static let liveNow = Lens<LiveStreamEvent, Bool>(
view: { $0.liveNow },
set: { var new = $1; new.liveNow = $0; return new }
)
public static let maxOpenTokViewers = Lens<LiveStreamEvent, Int?>(
view: { $0.maxOpenTokViewers },
set: { var new = $1; new.maxOpenTokViewers = $0; return new }
)
public static let name = Lens<LiveStreamEvent, String>(
view: { $0.name },
set: { var new = $1; new.name = $0; return new }
)
public static let numberPeopleWatching = Lens<LiveStreamEvent, Int?>(
view: { $0.numberPeopleWatching },
set: { var new = $1; new.numberPeopleWatching = $0; return new }
)
public static let replayUrl = Lens<LiveStreamEvent, String?>(
view: { $0.replayUrl },
set: { var new = $1; new.replayUrl = $0; return new }
)
public static let startDate = Lens<LiveStreamEvent, Date>(
view: { $0.startDate },
set: { var new = $1; new.startDate = $0; return new }
)
public static let user = Lens<LiveStreamEvent, LiveStreamEvent.User?>(
view: { $0.user },
set: { var new = $1; new.user = $0; return new }
)
public static let webUrl = Lens<LiveStreamEvent, String>(
view: { $0.webUrl },
set: { var new = $1; new.webUrl = $0; return new }
)
}
}
extension LiveStreamEvent.Firebase {
public enum lens {
public static let apiKey = Lens<LiveStreamEvent.Firebase, String>(
view: { $0.apiKey },
set: { var new = $1; new.apiKey = $0; return new }
)
public static let chatAvatarUrl = Lens<LiveStreamEvent.Firebase, String?>(
view: { $0.chatAvatarUrl },
set: { var new = $1; new.chatAvatarUrl = $0; return new }
)
public static let chatPath = Lens<LiveStreamEvent.Firebase, String>(
view: { $0.chatPath },
set: { var new = $1; new.chatPath = $0; return new }
)
public static let chatPostPath = Lens<LiveStreamEvent.Firebase, String?>(
view: { $0.chatPostPath },
set: { var new = $1; new.chatPostPath = $0; return new }
)
public static let chatUserId = Lens<LiveStreamEvent.Firebase, String?>(
view: { $0.chatUserId },
set: { var new = $1; new.chatUserId = $0; return new }
)
public static let chatUserName = Lens<LiveStreamEvent.Firebase, String?>(
view: { $0.chatUserName },
set: { var new = $1; new.chatUserName = $0; return new }
)
public static let greenRoomPath = Lens<LiveStreamEvent.Firebase, String>(
view: { $0.greenRoomPath },
set: { var new = $1; new.greenRoomPath = $0; return new }
)
public static let hlsUrlPath = Lens<LiveStreamEvent.Firebase, String>(
view: { $0.hlsUrlPath },
set: { var new = $1; new.hlsUrlPath = $0; return new }
)
public static let numberPeopleWatchingPath = Lens<LiveStreamEvent.Firebase, String>(
view: { $0.numberPeopleWatchingPath },
set: { var new = $1; new.numberPeopleWatchingPath = $0; return new }
)
public static let project = Lens<LiveStreamEvent.Firebase, String>(
view: { $0.project },
set: { var new = $1; new.project = $0; return new }
)
public static let scaleNumberPeopleWatchingPath = Lens<LiveStreamEvent.Firebase, String>(
view: { $0.scaleNumberPeopleWatchingPath },
set: { var new = $1; new.scaleNumberPeopleWatchingPath = $0; return new }
)
public static let token = Lens<LiveStreamEvent.Firebase, String?>(
view: { $0.token },
set: { var new = $1; new.token = $0; return new }
)
}
}
extension LiveStreamEvent.Project {
public enum lens {
public static let id = Lens<LiveStreamEvent.Project, Int?>(
view: { $0.id },
set: { var new = $1; new.id = $0; return new }
)
public static let name = Lens<LiveStreamEvent.Project, String>(
view: { $0.name },
set: { var new = $1; new.name = $0; return new }
)
public static let webUrl = Lens<LiveStreamEvent.Project, String>(
view: { $0.webUrl },
set: { var new = $1; new.webUrl = $0; return new }
)
}
}