Skip to content

Commit c332feb

Browse files
committed
feat: add complete audiobook support
feat: add complete audiobook support Adds comprehensive audiobook functionality to the Plex client including: - Audiobook library browsing and detail screens - Full-featured audiobook player with chapter navigation - Author browsing and filtering - Audiobook-specific UI widgets and controls - Playback state management and seeking - Integration with existing library navigation This feature provides feature parity with video/music playback for audiobook content types in Plex libraries.
1 parent 3e068b2 commit c332feb

14 files changed

+2688
-56
lines changed

android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,4 @@
66
android:drawable="@drawable/ic_launcher_foreground"
77
android:inset="16%" />
88
</foreground>
9-
<monochrome>
10-
<inset
11-
android:drawable="@drawable/ic_launcher_monochrome"
12-
android:inset="16%" />
13-
</monochrome>
149
</adaptive-icon>

lib/constants/plex_constants.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// Constants for Plex API and player configuration
2+
class PlexConstants {
3+
// Plex API Type Parameters
4+
/// Type parameter for albums (used for audiobooks - shows books instead of artists)
5+
static const int plexTypeAlbum = 9;
6+
7+
/// Type parameter for artists
8+
static const int plexTypeArtist = 8;
9+
10+
/// Type parameter for tracks (chapters)
11+
static const int plexTypeTrack = 10;
12+
13+
// Player Configuration
14+
/// Maximum attempts to wait for player initialization
15+
static const int maxPlayerInitAttempts = 100;
16+
17+
/// Interval between player initialization checks
18+
static const Duration playerInitCheckInterval = Duration(milliseconds: 100);
19+
20+
/// Delay to ensure player is fully ready before seeking
21+
static const Duration playerReadyDelay = Duration(milliseconds: 500);
22+
23+
/// Delay to verify seek operation completed
24+
static const Duration seekVerificationDelay = Duration(milliseconds: 200);
25+
26+
// Progress Tracking
27+
/// Interval for sending progress updates to Plex server
28+
static const Duration progressUpdateInterval = Duration(seconds: 10);
29+
30+
// UI Behavior
31+
/// Duration before hiding player controls automatically
32+
static const Duration controlsHideDelay = Duration(seconds: 5);
33+
34+
PlexConstants._(); // Private constructor to prevent instantiation
35+
}

lib/models/plex_library.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flutter/material.dart';
12
import 'package:json_annotation/json_annotation.dart';
23

34
part 'plex_library.g.dart';
@@ -30,4 +31,49 @@ class PlexLibrary {
3031
_$PlexLibraryFromJson(json);
3132

3233
Map<String, dynamic> toJson() => _$PlexLibraryToJson(this);
34+
35+
/// Determines if this library is an audiobook library.
36+
///
37+
/// Audiobooks in Plex are stored as music libraries (type: "artist") but use
38+
/// specific metadata agents like audnexus, audiobooks, or audiobookshelf.
39+
///
40+
/// Returns true if:
41+
/// - The library type is "artist" AND
42+
/// - The agent contains "audnexus", "audiobook", or "audiobookshelf" (case-insensitive)
43+
///
44+
/// Returns false if:
45+
/// - The library type is not "artist"
46+
/// - The agent is null
47+
/// - The agent doesn't contain any audiobook-specific patterns
48+
bool get isAudiobookLibrary {
49+
if (type.toLowerCase() != 'artist') return false;
50+
51+
final agentLower = agent?.toLowerCase() ?? '';
52+
return agentLower.contains('audnexus') ||
53+
agentLower.contains('audiobook') ||
54+
agentLower.contains('audiobookshelf');
55+
}
56+
57+
/// Returns the appropriate icon for this library type.
58+
///
59+
/// Uses Icons.headphones for audiobook libraries, and type-specific icons
60+
/// for other library types.
61+
IconData get libraryIcon {
62+
if (isAudiobookLibrary) {
63+
return Icons.headphones;
64+
}
65+
66+
switch (type.toLowerCase()) {
67+
case 'movie':
68+
return Icons.movie;
69+
case 'show':
70+
return Icons.tv;
71+
case 'artist':
72+
return Icons.music_note;
73+
case 'photo':
74+
return Icons.photo;
75+
default:
76+
return Icons.folder;
77+
}
78+
}
3379
}

0 commit comments

Comments
 (0)