0
# Media Streaming and Transcoding
1
2
Media file information, streaming details, and transcoding management. Provides access to media parts, streams, quality settings, and active transcoding sessions with comprehensive stream analysis and optimization control for efficient media delivery.
3
4
## Capabilities
5
6
### Media - Media File Container
7
8
Represents media file information and container-level properties for video, audio, and photo content.
9
10
```python { .api }
11
class Media:
12
@property
13
def id(self):
14
"""Media ID."""
15
16
@property
17
def duration(self):
18
"""Media duration in milliseconds."""
19
20
@property
21
def bitrate(self):
22
"""Media bitrate in kbps."""
23
24
@property
25
def width(self):
26
"""Video width in pixels."""
27
28
@property
29
def height(self):
30
"""Video height in pixels."""
31
32
@property
33
def aspectRatio(self):
34
"""Video aspect ratio."""
35
36
@property
37
def audioChannels(self):
38
"""Number of audio channels."""
39
40
@property
41
def audioCodec(self):
42
"""Audio codec name."""
43
44
@property
45
def videoCodec(self):
46
"""Video codec name."""
47
48
@property
49
def container(self):
50
"""Media container format."""
51
52
@property
53
def videoFrameRate(self):
54
"""Video frame rate."""
55
56
@property
57
def videoResolution(self):
58
"""Video resolution string (e.g., '1080p')."""
59
60
def parts(self):
61
"""
62
Get media parts (files).
63
64
Returns:
65
List[MediaPart]: Media file parts
66
"""
67
```
68
69
### MediaPart - Individual Media Files
70
71
Represents individual media files with file-level information and stream details.
72
73
```python { .api }
74
class MediaPart:
75
@property
76
def id(self):
77
"""Part ID."""
78
79
@property
80
def key(self):
81
"""Part key/path."""
82
83
@property
84
def file(self):
85
"""File path on server."""
86
87
@property
88
def size(self):
89
"""File size in bytes."""
90
91
@property
92
def duration(self):
93
"""Part duration in milliseconds."""
94
95
@property
96
def container(self):
97
"""File container format."""
98
99
@property
100
def indexes(self):
101
"""Available index types (e.g., 'sd' for subtitles)."""
102
103
def streams(self):
104
"""
105
Get all streams in this part.
106
107
Returns:
108
List[MediaPartStream]: All streams (video, audio, subtitle)
109
"""
110
111
def videoStreams(self):
112
"""
113
Get video streams.
114
115
Returns:
116
List[VideoStream]: Video streams
117
"""
118
119
def audioStreams(self):
120
"""
121
Get audio streams.
122
123
Returns:
124
List[AudioStream]: Audio streams
125
"""
126
127
def subtitleStreams(self):
128
"""
129
Get subtitle streams.
130
131
Returns:
132
List[SubtitleStream]: Subtitle streams
133
"""
134
```
135
136
### Stream Classes - Detailed Stream Information
137
138
Individual stream information for video, audio, and subtitle tracks within media files.
139
140
```python { .api }
141
class MediaPartStream:
142
"""Base class for media streams."""
143
144
@property
145
def id(self):
146
"""Stream ID."""
147
148
@property
149
def index(self):
150
"""Stream index within file."""
151
152
@property
153
def codec(self):
154
"""Stream codec."""
155
156
@property
157
def language(self):
158
"""Stream language code."""
159
160
@property
161
def languageCode(self):
162
"""ISO language code."""
163
164
@property
165
def selected(self):
166
"""Whether stream is selected for playback."""
167
168
class VideoStream(MediaPartStream):
169
"""Video stream information."""
170
171
@property
172
def bitDepth(self):
173
"""Video bit depth."""
174
175
@property
176
def bitrate(self):
177
"""Video bitrate in kbps."""
178
179
@property
180
def cabac(self):
181
"""CABAC encoding information."""
182
183
@property
184
def chromaSubsampling(self):
185
"""Chroma subsampling format."""
186
187
@property
188
def colorRange(self):
189
"""Color range (limited/full)."""
190
191
@property
192
def colorSpace(self):
193
"""Color space information."""
194
195
@property
196
def frameRate(self):
197
"""Video frame rate."""
198
199
@property
200
def hasScalingMatrix(self):
201
"""Whether stream has scaling matrix."""
202
203
@property
204
def height(self):
205
"""Video height in pixels."""
206
207
@property
208
def level(self):
209
"""Codec level."""
210
211
@property
212
def profile(self):
213
"""Codec profile."""
214
215
@property
216
def refFrames(self):
217
"""Reference frame count."""
218
219
@property
220
def scanType(self):
221
"""Scan type (progressive/interlaced)."""
222
223
@property
224
def width(self):
225
"""Video width in pixels."""
226
227
class AudioStream(MediaPartStream):
228
"""Audio stream information."""
229
230
@property
231
def audioChannelLayout(self):
232
"""Audio channel layout (e.g., '5.1')."""
233
234
@property
235
def bitDepth(self):
236
"""Audio bit depth."""
237
238
@property
239
def bitrate(self):
240
"""Audio bitrate in kbps."""
241
242
@property
243
def bitrateMode(self):
244
"""Bitrate mode (constant/variable)."""
245
246
@property
247
def channels(self):
248
"""Number of audio channels."""
249
250
@property
251
def profile(self):
252
"""Audio codec profile."""
253
254
@property
255
def samplingRate(self):
256
"""Audio sampling rate in Hz."""
257
258
@property
259
def title(self):
260
"""Stream title/description."""
261
262
class SubtitleStream(MediaPartStream):
263
"""Subtitle stream information."""
264
265
@property
266
def format(self):
267
"""Subtitle format."""
268
269
@property
270
def title(self):
271
"""Subtitle title/description."""
272
273
@property
274
def forced(self):
275
"""Whether subtitles are forced."""
276
277
@property
278
def hearingImpaired(self):
279
"""Whether subtitles are for hearing impaired."""
280
```
281
282
### Transcoding Classes - Active Transcoding Management
283
284
Management and monitoring of active transcoding sessions and jobs.
285
286
```python { .api }
287
class Session:
288
"""Active playback session information."""
289
290
@property
291
def id(self):
292
"""Session ID."""
293
294
@property
295
def bandwidth(self):
296
"""Session bandwidth in kbps."""
297
298
@property
299
def location(self):
300
"""Playback location (lan/wan)."""
301
302
@property
303
def player(self):
304
"""Player information."""
305
306
@property
307
def state(self):
308
"""Playback state."""
309
310
@property
311
def user(self):
312
"""User information."""
313
314
@property
315
def transcodeSession(self):
316
"""Associated transcode session if transcoding."""
317
318
class TranscodeSession:
319
"""Active transcoding session."""
320
321
@property
322
def key(self):
323
"""Transcode session key."""
324
325
@property
326
def throttled(self):
327
"""Whether transcoding is throttled."""
328
329
@property
330
def complete(self):
331
"""Whether transcoding is complete."""
332
333
@property
334
def progress(self):
335
"""Transcoding progress (0-100)."""
336
337
@property
338
def size(self):
339
"""Current transcoded size in bytes."""
340
341
@property
342
def speed(self):
343
"""Transcoding speed multiplier."""
344
345
@property
346
def error(self):
347
"""Transcoding error information."""
348
349
@property
350
def duration(self):
351
"""Total duration being transcoded."""
352
353
@property
354
def remaining(self):
355
"""Estimated time remaining."""
356
357
@property
358
def context(self):
359
"""Transcoding context (streaming/sync)."""
360
361
@property
362
def sourceVideoCodec(self):
363
"""Source video codec."""
364
365
@property
366
def sourceAudioCodec(self):
367
"""Source audio codec."""
368
369
@property
370
def videoDecision(self):
371
"""Video transcoding decision (transcode/copy/directplay)."""
372
373
@property
374
def audioDecision(self):
375
"""Audio transcoding decision (transcode/copy/directplay)."""
376
377
class TranscodeJob:
378
"""Transcoding job information."""
379
380
@property
381
def key(self):
382
"""Job key."""
383
384
@property
385
def type(self):
386
"""Job type."""
387
388
@property
389
def uuid(self):
390
"""Job UUID."""
391
392
@property
393
def progress(self):
394
"""Job progress percentage."""
395
396
@property
397
def targetTagID(self):
398
"""Target optimization tag ID."""
399
400
class Optimized:
401
"""Optimized media version."""
402
403
@property
404
def id(self):
405
"""Optimization ID."""
406
407
@property
408
def version(self):
409
"""Optimization version."""
410
411
@property
412
def target(self):
413
"""Optimization target."""
414
415
@property
416
def targetTagID(self):
417
"""Target tag ID."""
418
419
@property
420
def title(self):
421
"""Optimization title."""
422
423
@property
424
def type(self):
425
"""Optimization type."""
426
427
class Conversion:
428
"""Media conversion job."""
429
430
@property
431
def key(self):
432
"""Conversion key."""
433
434
@property
435
def status(self):
436
"""Conversion status."""
437
438
@property
439
def progress(self):
440
"""Conversion progress."""
441
442
@property
443
def step(self):
444
"""Current conversion step."""
445
```
446
447
## Stream Analysis Examples
448
449
### Analyzing Media Files
450
451
```python
452
# Get a movie and analyze its media information
453
movie = plex.library.section('Movies').get('Inception')
454
455
# Access media information
456
for media in movie.media:
457
print(f"Media ID: {media.id}")
458
print(f"Duration: {media.duration // 1000} seconds")
459
print(f"Resolution: {media.width}x{media.height}")
460
print(f"Bitrate: {media.bitrate} kbps")
461
print(f"Container: {media.container}")
462
print(f"Video Codec: {media.videoCodec}")
463
print(f"Audio Codec: {media.audioCodec}")
464
print(f"Audio Channels: {media.audioChannels}")
465
466
# Analyze parts (files)
467
for part in media.parts():
468
print(f"\nFile: {part.file}")
469
print(f"Size: {part.size // (1024*1024)} MB")
470
print(f"Container: {part.container}")
471
472
# Analyze streams
473
video_streams = part.videoStreams()
474
audio_streams = part.audioStreams()
475
subtitle_streams = part.subtitleStreams()
476
477
print(f"Video streams: {len(video_streams)}")
478
print(f"Audio streams: {len(audio_streams)}")
479
print(f"Subtitle streams: {len(subtitle_streams)}")
480
```
481
482
### Detailed Stream Information
483
484
```python
485
def analyze_streams(media_item):
486
"""Detailed stream analysis for any media item."""
487
488
for media in media_item.media:
489
for part in media.parts():
490
print(f"File: {part.file}")
491
492
# Video stream details
493
for video in part.videoStreams():
494
print(f"Video Stream {video.index}:")
495
print(f" Codec: {video.codec}")
496
print(f" Resolution: {video.width}x{video.height}")
497
print(f" Frame Rate: {video.frameRate}")
498
print(f" Bitrate: {video.bitrate} kbps")
499
print(f" Profile: {video.profile}")
500
print(f" Level: {video.level}")
501
print(f" Bit Depth: {video.bitDepth}")
502
print(f" Color Space: {video.colorSpace}")
503
504
# Audio stream details
505
for audio in part.audioStreams():
506
print(f"Audio Stream {audio.index}:")
507
print(f" Codec: {audio.codec}")
508
print(f" Language: {audio.language}")
509
print(f" Channels: {audio.channels}")
510
print(f" Channel Layout: {audio.audioChannelLayout}")
511
print(f" Bitrate: {audio.bitrate} kbps")
512
print(f" Sampling Rate: {audio.samplingRate} Hz")
513
print(f" Bit Depth: {audio.bitDepth}")
514
print(f" Title: {audio.title}")
515
print(f" Selected: {audio.selected}")
516
517
# Subtitle stream details
518
for subtitle in part.subtitleStreams():
519
print(f"Subtitle Stream {subtitle.index}:")
520
print(f" Codec: {subtitle.codec}")
521
print(f" Language: {subtitle.language}")
522
print(f" Format: {subtitle.format}")
523
print(f" Title: {subtitle.title}")
524
print(f" Forced: {subtitle.forced}")
525
print(f" Hearing Impaired: {subtitle.hearingImpaired}")
526
print(f" Selected: {subtitle.selected}")
527
528
# Analyze specific movie
529
movie = plex.library.section('Movies').get('Test Movie')
530
analyze_streams(movie)
531
```
532
533
## Transcoding Monitoring
534
535
### Active Session Monitoring
536
537
```python
538
# Monitor active playback sessions
539
sessions = plex.sessions()
540
541
for session in sessions:
542
print(f"Session: {session.user.title}")
543
print(f"Playing: {session.player.title}")
544
print(f"State: {session.state}")
545
print(f"Location: {session.location}")
546
print(f"Bandwidth: {session.bandwidth} kbps")
547
548
# Check if transcoding
549
if hasattr(session, 'transcodeSession') and session.transcodeSession:
550
transcode = session.transcodeSession
551
print(f"Transcoding: Yes")
552
print(f" Progress: {transcode.progress}%")
553
print(f" Speed: {transcode.speed}x")
554
print(f" Video Decision: {transcode.videoDecision}")
555
print(f" Audio Decision: {transcode.audioDecision}")
556
print(f" Source Video: {transcode.sourceVideoCodec}")
557
print(f" Source Audio: {transcode.sourceAudioCodec}")
558
559
if transcode.throttled:
560
print(" Status: Throttled")
561
if transcode.error:
562
print(f" Error: {transcode.error}")
563
else:
564
print("Transcoding: No (Direct Play/Stream)")
565
566
print("-" * 40)
567
```
568
569
### Transcoding Job Management
570
571
```python
572
# Monitor transcoding jobs
573
def monitor_transcoding():
574
"""Monitor all transcoding activities."""
575
576
# Active sessions with transcoding
577
active_transcodes = []
578
for session in plex.sessions():
579
if hasattr(session, 'transcodeSession') and session.transcodeSession:
580
active_transcodes.append(session.transcodeSession)
581
582
print(f"Active transcoding sessions: {len(active_transcodes)}")
583
584
for transcode in active_transcodes:
585
print(f"Session: {transcode.key}")
586
print(f"Progress: {transcode.progress}%")
587
print(f"Speed: {transcode.speed}x")
588
589
if transcode.remaining:
590
print(f"Time remaining: {transcode.remaining} seconds")
591
592
if transcode.throttled:
593
print("Status: Throttled (CPU limited)")
594
595
# Check optimization jobs
596
# Note: This would require server-level access to optimization status
597
print("\nOptimization jobs would be checked here")
598
599
# Run monitoring
600
monitor_transcoding()
601
```
602
603
### Stream Selection and Optimization
604
605
```python
606
def find_optimal_streams(media_item, target_resolution='1080p', preferred_language='en'):
607
"""Find optimal streams for playback."""
608
609
for media in media_item.media:
610
# Check if media meets resolution requirements
611
if media.videoResolution == target_resolution:
612
for part in media.parts():
613
# Find best video stream
614
video_streams = part.videoStreams()
615
best_video = None
616
best_bitrate = 0
617
618
for video in video_streams:
619
if video.bitrate and video.bitrate > best_bitrate:
620
best_video = video
621
best_bitrate = video.bitrate
622
623
# Find preferred audio stream
624
audio_streams = part.audioStreams()
625
preferred_audio = None
626
fallback_audio = None
627
628
for audio in audio_streams:
629
if audio.language == preferred_language:
630
if audio.channels >= 6: # Prefer surround sound
631
preferred_audio = audio
632
break
633
elif not preferred_audio:
634
preferred_audio = audio
635
elif not fallback_audio:
636
fallback_audio = audio
637
638
# Find subtitle streams
639
subtitle_streams = part.subtitleStreams()
640
preferred_subtitles = None
641
642
for subtitle in subtitle_streams:
643
if subtitle.language == preferred_language:
644
preferred_subtitles = subtitle
645
break
646
647
return {
648
'video': best_video,
649
'audio': preferred_audio or fallback_audio,
650
'subtitles': preferred_subtitles,
651
'part': part
652
}
653
654
return None
655
656
# Find optimal streams for a movie
657
movie = plex.library.section('Movies').get('Sample Movie')
658
optimal = find_optimal_streams(movie, target_resolution='1080p', preferred_language='en')
659
660
if optimal:
661
print("Optimal stream configuration:")
662
print(f"Video: {optimal['video'].codec} {optimal['video'].width}x{optimal['video'].height}")
663
print(f"Audio: {optimal['audio'].codec} {optimal['audio'].channels}ch {optimal['audio'].language}")
664
if optimal['subtitles']:
665
print(f"Subtitles: {optimal['subtitles'].language} ({optimal['subtitles'].format})")
666
```
667
668
### Quality Analysis
669
670
```python
671
def analyze_library_quality(section_name):
672
"""Analyze quality distribution across a library section."""
673
674
section = plex.library.section(section_name)
675
quality_stats = {
676
'resolutions': {},
677
'codecs': {},
678
'containers': {},
679
'total_size': 0,
680
'total_duration': 0
681
}
682
683
items = section.all()
684
685
for item in items:
686
for media in item.media:
687
# Resolution stats
688
resolution = f"{media.width}x{media.height}" if media.width and media.height else "Unknown"
689
quality_stats['resolutions'][resolution] = quality_stats['resolutions'].get(resolution, 0) + 1
690
691
# Codec stats
692
if media.videoCodec:
693
quality_stats['codecs'][media.videoCodec] = quality_stats['codecs'].get(media.videoCodec, 0) + 1
694
695
# Container stats
696
if media.container:
697
quality_stats['containers'][media.container] = quality_stats['containers'].get(media.container, 0) + 1
698
699
# Size and duration
700
for part in media.parts():
701
if part.size:
702
quality_stats['total_size'] += part.size
703
if part.duration:
704
quality_stats['total_duration'] += part.duration
705
706
# Report results
707
print(f"Quality analysis for {section_name}:")
708
print(f"Total items: {len(items)}")
709
print(f"Total size: {quality_stats['total_size'] // (1024**3)} GB")
710
print(f"Total duration: {quality_stats['total_duration'] // (1000*3600)} hours")
711
712
print("\nResolution distribution:")
713
for resolution, count in sorted(quality_stats['resolutions'].items(), key=lambda x: x[1], reverse=True):
714
percentage = (count / len(items)) * 100
715
print(f" {resolution}: {count} ({percentage:.1f}%)")
716
717
print("\nCodec distribution:")
718
for codec, count in sorted(quality_stats['codecs'].items(), key=lambda x: x[1], reverse=True):
719
percentage = (count / len(items)) * 100
720
print(f" {codec}: {count} ({percentage:.1f}%)")
721
722
print("\nContainer distribution:")
723
for container, count in sorted(quality_stats['containers'].items(), key=lambda x: x[1], reverse=True):
724
percentage = (count / len(items)) * 100
725
print(f" {container}: {count} ({percentage:.1f}%)")
726
727
# Analyze movie library quality
728
analyze_library_quality('Movies')
729
```