Merge pull request #601 from JonnyWong16/server_browse

Add ability to browse and walk the Plex server system file directories
This commit is contained in:
Steffen Fredriksen 2020-11-23 07:48:53 +01:00 committed by GitHub
commit ae59620e9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 125 additions and 1 deletions

4
.gitignore vendored
View file

@ -26,3 +26,7 @@ lib/
pip-selfcheck.json
pyvenv.cfg
MANIFEST
# path for the test lib.
tools/plex

View file

@ -1570,3 +1570,54 @@ class Collections(PlexPartialObject):
# def edit(self, **kwargs):
# TODO
@utils.registerPlexObject
class Path(PlexObject):
""" Represents a single directory Path.
Attributes:
TAG (str): 'Path'
home (bool): True if the path is the home directory
key (str): API URL (/services/browse/<base64path>)
network (bool): True if path is a network location
path (str): Full path to folder
title (str): Folder name
"""
TAG = 'Path'
def _loadData(self, data):
self.home = utils.cast(bool, data.attrib.get('home'))
self.key = data.attrib.get('key')
self.network = utils.cast(bool, data.attrib.get('network'))
self.path = data.attrib.get('path')
self.title = data.attrib.get('title')
def browse(self, includeFiles=True):
""" Alias for :func:`~plexapi.server.PlexServer.browse`. """
return self._server.browse(self, includeFiles)
def walk(self):
""" Alias for :func:`~plexapi.server.PlexServer.walk`. """
for path, paths, files in self._server.walk(self):
yield path, paths, files
@utils.registerPlexObject
class File(PlexObject):
""" Represents a single File.
Attributes:
TAG (str): 'File'
key (str): API URL (/services/browse/<base64path>)
path (str): Full path to file
title (str): File name
"""
TAG = 'File'
def _loadData(self, data):
self.key = data.attrib.get('key')
self.path = data.attrib.get('path')
self.title = data.attrib.get('title')

View file

@ -15,7 +15,7 @@ from plexapi.alert import AlertListener
from plexapi.base import PlexObject
from plexapi.client import PlexClient
from plexapi.exceptions import BadRequest, NotFound, Unauthorized
from plexapi.library import Hub, Library
from plexapi.library import Hub, Library, Path, File
from plexapi.media import Conversion, Optimized
from plexapi.playlist import Playlist
from plexapi.playqueue import PlayQueue
@ -247,6 +247,53 @@ class PlexServer(PlexObject):
log.warning('Unable to fetch client ports from myPlex: %s', err)
return ports
def browse(self, path=None, includeFiles=True):
""" Browse the system file path using the Plex API.
Returns list of :class:`~plexapi.library.Path` and :class:`~plexapi.library.File` objects.
Parameters:
path (:class:`~plexapi.library.Path` or str, optional): Full path to browse.
includeFiles (bool): True to include files when browsing (Default).
False to only return folders.
"""
if isinstance(path, Path):
key = path.key
elif path is not None:
base64path = utils.base64str(path)
key = '/services/browse/%s' % base64path
else:
key = '/services/browse'
if includeFiles:
key += '?includeFiles=1'
return self.fetchItems(key)
def walk(self, path=None):
""" Walk the system file tree using the Plex API similar to `os.walk`.
Yields a 3-tuple `(path, paths, files)` where
`path` is a string of the directory path,
`paths` is a list of :class:`~plexapi.library.Path` objects, and
`files` is a list of :class:`~plexapi.library.File` objects.
Parameters:
path (:class:`~plexapi.library.Path` or str, optional): Full path to walk.
"""
paths = []
files = []
for item in self.browse(path):
if isinstance(item, Path):
paths.append(item)
elif isinstance(item, File):
files.append(item)
if isinstance(path, Path):
path = path.path
yield path or '', paths, files
for _path in paths:
for path, paths, files in self.walk(_path):
yield path, paths, files
def clients(self):
""" Returns list of all :class:`~plexapi.client.PlexClient` objects connected to server. """
items = []

View file

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import base64
import logging
import os
import re
@ -411,3 +412,7 @@ def getAgentIdentifier(section, agent):
agents += identifiers
raise NotFound('Couldnt find "%s" in agents list (%s)' %
(agent, ', '.join(agents)))
def base64str(text):
return base64.b64encode(text.encode('utf-8')).decode('utf-8')

View file

@ -287,6 +287,23 @@ def test_server_downloadDatabases(tmpdir, plex):
assert len(tmpdir.listdir()) > 1
def test_server_browse(plex, movies):
movies_path = movies.locations[0]
# browse root
paths = plex.browse()
assert len(paths)
# browse the path of the movie library
paths = plex.browse(movies_path)
assert len(paths)
# browse the path of the movie library without files
paths = plex.browse(movies_path, includeFiles=False)
assert not len([f for f in paths if f.TAG == 'File'])
# walk the path of the movie library
for path, paths, files in plex.walk(movies_path):
assert path.startswith(movies_path)
assert len(paths) or len(files)
def test_server_allowMediaDeletion(account):
plex = PlexServer(utils.SERVER_BASEURL, account.authenticationToken)
# Check server current allowMediaDeletion setting