mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-22 03:33:08 +00:00
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:
commit
ae59620e9a
5 changed files with 125 additions and 1 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -26,3 +26,7 @@ lib/
|
||||||
pip-selfcheck.json
|
pip-selfcheck.json
|
||||||
pyvenv.cfg
|
pyvenv.cfg
|
||||||
MANIFEST
|
MANIFEST
|
||||||
|
|
||||||
|
|
||||||
|
# path for the test lib.
|
||||||
|
tools/plex
|
|
@ -1570,3 +1570,54 @@ class Collections(PlexPartialObject):
|
||||||
|
|
||||||
# def edit(self, **kwargs):
|
# def edit(self, **kwargs):
|
||||||
# TODO
|
# 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')
|
||||||
|
|
|
@ -15,7 +15,7 @@ from plexapi.alert import AlertListener
|
||||||
from plexapi.base import PlexObject
|
from plexapi.base import PlexObject
|
||||||
from plexapi.client import PlexClient
|
from plexapi.client import PlexClient
|
||||||
from plexapi.exceptions import BadRequest, NotFound, Unauthorized
|
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.media import Conversion, Optimized
|
||||||
from plexapi.playlist import Playlist
|
from plexapi.playlist import Playlist
|
||||||
from plexapi.playqueue import PlayQueue
|
from plexapi.playqueue import PlayQueue
|
||||||
|
@ -247,6 +247,53 @@ class PlexServer(PlexObject):
|
||||||
log.warning('Unable to fetch client ports from myPlex: %s', err)
|
log.warning('Unable to fetch client ports from myPlex: %s', err)
|
||||||
return ports
|
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):
|
def clients(self):
|
||||||
""" Returns list of all :class:`~plexapi.client.PlexClient` objects connected to server. """
|
""" Returns list of all :class:`~plexapi.client.PlexClient` objects connected to server. """
|
||||||
items = []
|
items = []
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import base64
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
@ -411,3 +412,7 @@ def getAgentIdentifier(section, agent):
|
||||||
agents += identifiers
|
agents += identifiers
|
||||||
raise NotFound('Couldnt find "%s" in agents list (%s)' %
|
raise NotFound('Couldnt find "%s" in agents list (%s)' %
|
||||||
(agent, ', '.join(agents)))
|
(agent, ', '.join(agents)))
|
||||||
|
|
||||||
|
|
||||||
|
def base64str(text):
|
||||||
|
return base64.b64encode(text.encode('utf-8')).decode('utf-8')
|
||||||
|
|
|
@ -287,6 +287,23 @@ def test_server_downloadDatabases(tmpdir, plex):
|
||||||
assert len(tmpdir.listdir()) > 1
|
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):
|
def test_server_allowMediaDeletion(account):
|
||||||
plex = PlexServer(utils.SERVER_BASEURL, account.authenticationToken)
|
plex = PlexServer(utils.SERVER_BASEURL, account.authenticationToken)
|
||||||
# Check server current allowMediaDeletion setting
|
# Check server current allowMediaDeletion setting
|
||||||
|
|
Loading…
Reference in a new issue