Add properties to return the Plex Media Server data metadata paths (#1207)

* Add property to return the Plex Media Server data metadata path

* Add property to return the filepath for a resource

* Add tests for metadataDirectory and resourceFilepath
This commit is contained in:
JonnyWong16 2023-08-27 12:59:53 -07:00 committed by GitHub
parent 0679816201
commit b98b6050fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 112 additions and 1 deletions

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import os
from pathlib import Path
from urllib.parse import quote_plus
from plexapi import media, utils
@ -240,6 +241,12 @@ class Artist(
key = f'{self.key}?includeStations=1'
return next(iter(self.fetchItems(key, cls=Playlist, rtag="Stations")), None)
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'Artists' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class Album(
@ -359,6 +366,12 @@ class Album(
""" Returns str, default title for a new syncItem. """
return f'{self.parentTitle} - {self.title}'
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'Albums' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class Track(
@ -470,6 +483,12 @@ class Track(
""" Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey)
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.parentGuid)
return str(Path('Metadata') / 'Albums' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class TrackSession(PlexSession, Track):

View file

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from pathlib import Path
from urllib.parse import quote_plus
from plexapi import media, utils
@ -560,3 +561,9 @@ class Collection(
raise Unsupported('Unsupported collection content')
return myplex.sync(sync_item, client=client, clientId=clientId)
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'Collections' / guid_hash[0] / f'{guid_hash[1:]}.bundle')

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import xml
from pathlib import Path
from urllib.parse import quote_plus
from plexapi import log, settings, utils
@ -1024,6 +1024,20 @@ class BaseResource(PlexObject):
except xml.etree.ElementTree.ParseError:
pass
@property
def resourceFilepath(self):
""" Returns the file path to the resource in the Plex Media Server data directory.
Note: Returns the URL if the resource is not stored locally.
"""
if self.ratingKey.startswith('media://'):
return str(Path('Media') / 'localhost' / self.ratingKey.split('://')[-1])
elif self.ratingKey.startswith('metadata://'):
return str(Path(self._parent().metadataDirectory) / 'Contents' / '_combined' / self.ratingKey.split('://')[-1])
elif self.ratingKey.startswith('upload://'):
return str(Path(self._parent().metadataDirectory) / 'Uploads' / self.ratingKey.split('://')[-1])
else:
return self.ratingKey
class Art(BaseResource):
""" Represents a single Art object. """

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import os
from pathlib import Path
from urllib.parse import quote_plus
from plexapi import media, utils, video
@ -139,6 +140,12 @@ class Photoalbum(
""" Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.key, legacy=1)
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'Photos' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class Photo(
@ -290,6 +297,12 @@ class Photo(
""" Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey, legacy=1)
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.parentGuid)
return str(Path('Metadata') / 'Photos' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class PhotoSession(PlexSession, Photo):

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import re
from pathlib import Path
from urllib.parse import quote_plus, unquote
from plexapi import media, utils
@ -496,3 +497,9 @@ class Playlist(
def _getWebURL(self, base=None):
""" Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='playlist', key=self.key)
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'Playlists' / guid_hash[0] / f'{guid_hash[1:]}.bundle')

View file

@ -13,6 +13,7 @@ import zipfile
from collections import deque
from datetime import datetime, timedelta
from getpass import getpass
from hashlib import sha1
from threading import Event, Thread
from urllib.parse import quote
@ -650,3 +651,8 @@ def openOrRead(file):
return file.read()
with open(file, 'rb') as f:
return f.read()
def sha1hash(guid):
""" Return the SHA1 hash of a guid. """
return sha1(guid.encode('utf-8')).hexdigest()

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import os
from pathlib import Path
from urllib.parse import quote_plus
from plexapi import media, utils
@ -445,6 +446,12 @@ class Movie(
self._server.query(key, params=params, method=self._server._session.put)
return self
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'Movies' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class Show(
@ -655,6 +662,12 @@ class Show(
filepaths += episode.download(_savepath, keep_original_name, **kwargs)
return filepaths
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'TV Shows' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class Season(
@ -808,6 +821,12 @@ class Season(
""" Returns str, default title for a new syncItem. """
return f'{self.parentTitle} - {self.title}'
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.parentGuid)
return str(Path('Metadata') / 'TV Shows' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class Episode(
@ -1000,6 +1019,12 @@ class Episode(
self._server.query(key, params=params, method=self._server._session.put)
return self
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.grandparentGuid)
return str(Path('Metadata') / 'TV Shows' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
@utils.registerPlexObject
class Clip(
@ -1058,6 +1083,12 @@ class Clip(
""" Returns a filename for use in download. """
return self.title
@property
def metadataDirectory(self):
""" Returns the Plex Media Server data directory where the metadata is stored. """
guid_hash = utils.sha1hash(self.guid)
return str(Path('Metadata') / 'Movies' / guid_hash[0] / f'{guid_hash[1:]}.bundle')
class Extra(Clip):
""" Represents a single Extra (trailer, behindTheScenes, etc). """

View file

@ -72,6 +72,8 @@ BASE_DIR_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STUB_MOVIE_PATH = os.path.join(BASE_DIR_PATH, "tests", "data", "video_stub.mp4")
STUB_MP3_PATH = os.path.join(BASE_DIR_PATH, "tests", "data", "audio_stub.mp3")
STUB_IMAGE_PATH = os.path.join(BASE_DIR_PATH, "tests", "data", "cute_cat.jpg")
# For the default Docker bootstrap test Plex Media Server data directory
BOOTSTRAP_DATA_PATH = os.path.join(BASE_DIR_PATH, "plex", "db", "Library", "Application Support", "Plex Media Server")
def pytest_addoption(parser):

View file

@ -1459,3 +1459,15 @@ def test_video_optimize(plex, movie, tvshows, show):
movie.optimize()
with pytest.raises(BadRequest):
movie.optimize(target="mobile", locationID=-100)
def test_video_Movie_matadataDirectory(movie):
assert os.path.exists(os.path.join(utils.BOOTSTRAP_DATA_PATH, movie.metadataDirectory))
for poster in movie.posters():
if not poster.ratingKey.startswith('http'):
assert os.path.exists(os.path.join(utils.BOOTSTRAP_DATA_PATH, poster.resourceFilepath))
for art in movie.arts():
if not art.ratingKey.startswith('http'):
assert os.path.exists(os.path.join(utils.BOOTSTRAP_DATA_PATH, art.resourceFilepath))