-
-
Notifications
You must be signed in to change notification settings - Fork 119
/
matching.py
390 lines (380 loc) · 17.1 KB
/
matching.py
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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
'''
Match functions take a single Media object instance as its only argument and return
two boolean values. The first value is if the match was exact or "best fit", the
second argument is the ID of the format that was matched.
'''
from django.conf import settings
min_height = getattr(settings, 'VIDEO_HEIGHT_CUTOFF', 360)
fallback_hd_cutoff = getattr(settings, 'VIDEO_HEIGHT_IS_HD', 500)
def get_best_combined_format(media):
'''
Attempts to see if there is a single, combined audio and video format that
exactly matches the source requirements. This is used over separate audio
and video formats if possible. Combined formats are the easiest to check
for as they must exactly match the source profile be be valid.
'''
for fmt in media.iter_formats():
# Check height matches
if media.source.source_resolution.strip().upper() != fmt['format']:
continue
# Check the video codec matches
if media.source.source_vcodec != fmt['vcodec']:
continue
# Check the audio codec matches
if media.source.source_acodec != fmt['acodec']:
continue
# if the source prefers 60fps, check for it
if media.source.prefer_60fps:
if not fmt['is_60fps']:
continue
# If the source prefers HDR, check for it
if media.source.prefer_hdr:
if not fmt['is_hdr']:
continue
# If we reach here, we have a combined match!
return True, fmt['id']
return False, False
def get_best_audio_format(media):
'''
Finds the best match for the source required audio format. If the source
has a 'fallback' of fail this can return no match.
'''
# Order all audio-only formats by bitrate
audio_formats = []
for fmt in media.iter_formats():
# If the format has a video stream, skip it
if fmt['vcodec'] is not None:
continue
if not fmt['acodec']:
continue
audio_formats.append(fmt)
audio_formats = list(reversed(sorted(audio_formats, key=lambda k: k['abr'])))
if not audio_formats:
# Media has no audio formats at all
return False, False
# Find the highest bitrate audio format with a matching codec
for fmt in audio_formats:
if media.source.source_acodec == fmt['acodec']:
# Matched!
return True, fmt['id']
# No codecs matched
if media.source.can_fallback:
# Can fallback, find the next highest bitrate non-matching codec
return False, audio_formats[0]['id']
else:
# Can't fallback
return False, False
def get_best_video_format(media):
'''
Finds the best match for the source required video format. If the source
has a 'fallback' of fail this can return no match. Resolution is treated
as the most important factor to match. This is pretty verbose due to the
'soft' matching requirements for prefer_hdr and prefer_60fps.
'''
# Check if the source wants audio only, fast path to return
if media.source.is_audio:
return False, False
# Filter video-only formats by resolution that matches the source
video_formats = []
for fmt in media.iter_formats():
# If the format has an audio stream, skip it
if fmt['acodec'] is not None:
continue
if not fmt['vcodec']:
continue
if media.source.source_resolution.strip().upper() == fmt['format']:
video_formats.append(fmt)
# Check we matched some streams
if not video_formats:
# No streams match the requested resolution, see if we can fallback
if media.source.can_fallback:
# Find the next-best format matches by height
for fmt in media.iter_formats():
# If the format has an audio stream, skip it
if fmt['acodec'] is not None:
continue
if (fmt['height'] <= media.source.source_resolution_height and
fmt['height'] >= min_height):
video_formats.append(fmt)
else:
# Can't fallback
return False, False
video_formats = list(reversed(sorted(video_formats, key=lambda k: k['height'])))
source_resolution = media.source.source_resolution.strip().upper()
source_vcodec = media.source.source_vcodec
if not video_formats:
# Still no matches
return False, False
exact_match, best_match = None, None
# Of our filtered video formats, check for resolution + codec + hdr + fps match
if media.source.prefer_60fps and media.source.prefer_hdr:
for fmt in video_formats:
# Check for an exact match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
fmt['is_hdr'] and
fmt['is_60fps']):
# Exact match
exact_match, best_match = True, fmt
break
if media.source.can_fallback:
if not best_match:
for fmt in video_formats:
# Check for a resolution, hdr and fps match but drop the codec
if (source_resolution == fmt['format'] and
fmt['is_hdr'] and fmt['is_60fps']):
# Close match
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for a codec, hdr and fps match but drop the resolution
if (source_vcodec == fmt['vcodec'] and
fmt['is_hdr'] and fmt['is_60fps']):
# Close match
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution, codec and 60fps match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
fmt['is_60fps']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution and hdr match
if (source_resolution == fmt['format'] and
fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution and 60fps match
if (source_resolution == fmt['format'] and
fmt['is_60fps']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution, codec and hdr match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution and codec
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution
if source_resolution == fmt['format']:
exact_match, best_match = False, fmt
break
if not best_match:
# Match the highest resolution
exact_match, best_match = False, video_formats[0]
# Check for resolution + codec + fps match
if media.source.prefer_60fps and not media.source.prefer_hdr:
for fmt in video_formats:
# Check for an exact match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
fmt['is_60fps'] and
not fmt['is_hdr']):
# Exact match
exact_match, best_match = True, fmt
break
if media.source.can_fallback:
if not best_match:
for fmt in video_formats:
# Check for a resolution and fps match but drop the codec
if (source_resolution == fmt['format'] and
fmt['is_60fps'] and
not fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for a codec and fps match but drop the resolution
if (source_vcodec == fmt['vcodec'] and
fmt['is_60fps'] and
not fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for a codec and 60fps match
if (source_vcodec == fmt['vcodec'] and
fmt['is_60fps']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for codec and resolution match bot drop 60fps
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
not fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for codec and resolution match only
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution
if source_resolution == fmt['format']:
exact_match, best_match = False, fmt
break
if not best_match:
# Match the highest resolution
exact_match, best_match = False, video_formats[0]
# Check for resolution + codec + hdr
elif media.source.prefer_hdr and not media.source.prefer_60fps:
for fmt in video_formats:
# Check for an exact match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
fmt['is_hdr']):
# Exact match
exact_match, best_match = True, fmt
break
if media.source.can_fallback:
if not best_match:
for fmt in video_formats:
# Check for a resolution and fps match but drop the codec
if (source_resolution == fmt['format'] and
fmt['is_hdr'] and
not fmt['is_60fps']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for a codec and fps match but drop the resolution
if (source_vcodec == fmt['vcodec'] and
fmt['is_hdr'] and
not fmt['is_60fps']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for a codec and 60fps match
if (source_vcodec == fmt['vcodec'] and
fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for codec and resolution match bot drop hdr
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
not fmt['is_60fps']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for codec and resolution match only
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution
if source_resolution == fmt['format']:
exact_match, best_match = False, fmt
break
if not best_match:
# Match the highest resolution
exact_match, best_match = False, video_formats[0]
# check for resolution + codec
elif not media.source.prefer_hdr and not media.source.prefer_60fps:
for fmt in video_formats:
# Check for an exact match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
not fmt['is_60fps'] and
not fmt['is_hdr']):
# Exact match
exact_match, best_match = True, fmt
break
if media.source.can_fallback:
if not best_match:
for fmt in video_formats:
# Check for a resolution, hdr and fps match but drop the codec
if (source_resolution == fmt['format'] and
not fmt['is_hdr'] and not fmt['is_60fps']):
# Close match
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for a codec, hdr and fps match but drop the resolution
if (source_vcodec == fmt['vcodec'] and
not fmt['is_hdr'] and fmt['is_60fps']):
# Close match
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution, codec and hdr match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
not fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution, codec and 60fps match
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec'] and
not fmt['is_60fps']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution and codec
if (source_resolution == fmt['format'] and
source_vcodec == fmt['vcodec']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution and not hdr
if (source_resolution == fmt['format'] and
not fmt['is_hdr']):
exact_match, best_match = False, fmt
break
if not best_match:
for fmt in video_formats:
# Check for resolution
if source_resolution == fmt['format']:
exact_match, best_match = False, fmt
break
if not best_match:
# Match the highest resolution
exact_match, best_match = False, video_formats[0]
# See if we found a match
if best_match:
# Final check to see if the match we found was good enough
if exact_match:
return True, best_match['id']
elif media.source.can_fallback:
# Allow the fallback if it meets requirements
if (media.source.fallback == media.source.FALLBACK_NEXT_BEST_HD and
best_match['height'] >= fallback_hd_cutoff):
return False, best_match['id']
elif media.source.fallback == media.source.FALLBACK_NEXT_BEST:
return False, best_match['id']
# Nope, failed to find match
return False, False