A Java library to interact with Plex Media Server.
The library can query the server, edit and delete the items on the server (Playlists, Collections, Artists, Album, Track, ...). The library does not upload or delete the items of the server, although it has the capability to delete playlists and collections.
It works both with remotely-available (public) servers and local servers
You can use jitpack.io. Click on the following badge for instructions for several building tools.
HttpClient
, Apache HttpClientBuilder
, PlexApi.Builder
and PlexApi
The entry-point for the library is the class PlexApi
, which can be built using the Builder PlexApi.Builder
. The PlexApi
class makes requests to the remote services using an Apache HttpClient
. If you do not have particular needs, i.e. you just need to query local servers, you can build the PlexApi
object as simply as this:
PlexApi.Builder apiBuilder = PlexApi.Builder.withDefaultHttpClient();
PlexApi api = apiBuilder.build();
In this case the PlexApi
will use the default Apache HttpClient. In case you need to customise the backing HttpClient
, for example to tunnel the requests through a proxy, you can create the necessary HttpClientBuilder
and then pass it to the PlexApi.Builder
:
HttpClientBuilder clientBuilder;
/* Customise the HttpClientBuilder */
PlexApi.Builder apiBuilder = PlexApi.Builder.withCustomHttpClient(clientBuilder);
PlexApi api = apiBuilder.build();
So far we have explored the HttpClient
customisation; the PlexApi
can be customised as well by adding Plex-related headers to the requests. These headers are needed if you have to interact with remotely-available (i.e. non-local) servers or query the public Plex services.
As far as I am concerned, in case you need to access the public Plex apis, the following properties must be defined:
PlexApi.Builder apiBuilder;
apiBuilder.withPlexProduct("myPlexProduct")
.withPlexProductVersion("myPlexProductVersion")
.withPlexClientIdentifier("myPlexClientIdentifier");;
PlexApi api = apiBuilder.build();
Using a client built with a Plex product, a Plex Product Version and a Plex Client Identifier, an authorization step is required to access a public server. The only supported authentication scheme is the PIN-based authentication scheme. Hereby the steps to authenticate an app:
PlexAuthorizer authorizer = new api.getAuthorizer();
PlexPin pin = authorizer.requestAuthenticationPin();
String code = pin.getCode();
The user must then input the code
on https://www.plex.tv/link in order to authorize the app
After the user has correctly authorized the app, the authorizer must be used to verify the pin:
PlexPin verifiedPin = authorizer.verify(pin);
If the user has authorized the app, the verifiedPin
will contain the authorization token
:
String token = verifiedPin.getAuthToken();
With this token you can access the list of all the publicly-available resources
(among which the servers) of the user. You can feed this code to the PlexApi
through the class PlexApi.Builder
PlexApi api;
api.withToken(token);
If you already know the parameters to access the server, the PlexApi
class makes several ways to access a server by overloading the method PlexApi.getMediaServer
, e.g.:
PlexMediaServer mediaServer = api.getMediaServer("serverHostname", serverPort);
or
PlexMediaServer mediaServer = api.getMediaServer(serverURI);
In case you need to discover the servers for an account, the PlexApi.getResources
method can be used:
PlexApi api;
List<PlexDevice> devices = api.getResources().list();
List<PlexDevice> serverDevices = devices.stream().filter(PlexDevice::isServer).toList();
for (PlexDevice serverDevice : serverDevices) {
for (PlexConnection connection : serverDevice.getConnections())
if (connection.getLocal() == 0)
try {
PlexMediaServer server = api.getMediaServer(connection);
break;
} catch (PlexException e) {
e.printStackTrace();
}
}
In the example above the first publicly-available server is identified.
A Plex Media Server has a hierarchical organisation of the content. Starting from the Plex Media Server:
The recently-added content, can contain any of the media defined above: Artist, Album, Track, TV Show, Season, Episode, Movie, PhotoAlbum, Photo, Clip.
The On-Deck Content, instead, contains only videos, i.e. Episodes or Movies.
You can navigate through the server using the methods provided. For example:
PlexMediaServer server;
PlexMusicSection aMusicSection = server
.library() // The PlexLibrary
.musicSections() // All the music sections
.get(0);
List<PlexArtist> artists = aMusicSection.all().execute(); // Let's get all the artists
PlexMediaServer server;
PlexSeason season = server.library()
.showSections()
.get(0)
.all().execute() // Get all the shows
.get(0) // Get the first show
.children() // Get all the seasons
.get(0); // Get the first
List<PlexEpisode> episodes = season.children();
Editing some of the properties of an item is supported. To edit the property of a media (any of PlexArtist
, PlexAlbum
, PlexTrack
, PlexMovie
, ...), user the methods starting with edit and not the setters (whose use should be reserver to Jackson for serialization and deserialization).
PlexArtist artist; = ... // Retrieve the artist in some way
artist.editTitle("newArtistName", Optional.empty());
You can access information on ongoing streaming sessions:
PlexMediaServer server;
List<PlexMediatag<?>> mediatags = server
.status()
.sessions(); // A list of all the items being streamed
// The following lines need to have a session ongoing
PlexMediatag<?> mediatag = mediatags.get(0);
PlexSession session = mediatag.getSession(); // session will have info on the session
mediatag.getSessionKey();
mediatag.getPlayer();
// transcodeSession will have info on the transcoding session
PlexTranscodeSession transcodeSession = mediatag.getTranscodeSession();
transcodeSession.getKey();
All and only the transcoding sessions can also be listed in a different way
PlexMediaServer server;
List<PlexTranscodeSession> transcodeSessions = server
.transcode()
.sessions(); // All the transcode sessions
PlexTranscodeSession transcodeSession = transcodeSessions.get(0); // The first transcode session
transcodeSession.getKey(); // Accessing a generic property of a transcoding session
To kill a transcoding session:
PlexMediaServer server;
PlexTranscode transcode = server.transcode();
PlexTranscodeSession transcodeSession = transcode
.sessions()
.get(0);
transcode.killSession(transcodeSession);
The library supports listing the active shares and creating new ones. If you want to share all the server sections with the user whose email is dummy@example.com
and with default settings:
PlexServer server;
PlexServerShares serverShares = server.serverShares();
List<PlexServer.Section> sectionsToShare = server.getSections();
PlexServerShare share = serverShares.inviteFriend("dummy@example.com", sectionsToShare);
while if you want to customise the sharing settings:
PlexServer server;
PlexServerShares serverShares = server.serverShares();
List<PlexServer.Section> sectionsToShare = server.getSections();
PlexSharingSettings sharingSettings = new PlexSharingSettings();
sharingSettings.setAllowCameraUpload(true);
sharingSettings.setAllowChannels(false);
sharingSettings.setAllowSync(true);
sharingSettings.setFilterMovies(new HashMap<String, List<String>>());
sharingSettings.setFilterMusic(new HashMap<String, List<String>>());
sharingSettings.setFilterTelevision(new HashMap<String, List<String>>());
PlexServerShare share = serverShares.inviteFriend("dummy@example.com", sectionsToShare, sharingSettings);
The library also supports:
PlexLibrary.search
In the test classes you can find more examples... Navigate them all... I do not guarantee that the test-class names are meaningful. A test on playlist might as well be in PlexCollectionsTest.java
.
Some thigs which could be integrated, but I do not know when I will have the time to do:
Feel free to create a discussion, send an issue or contact me in any way you can think of to suggest, emend, correct add stuff.
In case you need to run the tests, these relies on the presence of a file named src/test/resources/testVariables.properties
, which is not synchronised with the github repo (it might contain personal data, such as proxy credentials), defining the following properties:
plexProduct={PLEX_PRODUCT}
plexProductVersion={PLEX_PRODUCT_VERSION}
plexClientIdentifier={PLEX_CLIENT_IDENTIFIER}
proxyHost={PROXY_PORT}
proxyUser={PROXY_USERNAME}
proxyPass={PROXY_PASSWORD}
This is a very personal project. It is so personal that the tests, for example, are specific to my plex and rely on my artists, movies and playlists. I should rewrite them, but hey... if you have the time to fix the tests maybe you'd better add a functionality, no?
Some weeks ago I discovered it was being used actively by somebody it honoured me, on the one side, and astonished me, on the other.
Anyway, I carry it on mainly at night and when I have some spare time from the (paying 😅) job.
Being such type of a project, I tend to use it more to improve my developing skills, study new language features, clean coding style, ..., than to mantain a long-term stable library. This means that I feel quite free to rearchtiecture, change class names, break compatibility from version to version. Still, I tend to respect the paradigm of Semantic Versioning, so... take a close look to first-digit changes 😜.
This project has no connection or affiliation with Plex. All trademarks belong to the respective owners.
Thanks go to these wonderful people (emoji key):
JZomDev 💻 |