or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-control.mdindex.mdlibrary-management.mdmedia-objects.mdmedia-streaming.mdplaylists.mdserver-connection.mdsettings-utilities.md

media-streaming.mddocs/

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

```