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 -*- # -*- coding: utf-8 -*-
import os import os
from pathlib import Path
from urllib.parse import quote_plus from urllib.parse import quote_plus
from plexapi import media, utils from plexapi import media, utils
@ -240,6 +241,12 @@ class Artist(
key = f'{self.key}?includeStations=1' key = f'{self.key}?includeStations=1'
return next(iter(self.fetchItems(key, cls=Playlist, rtag="Stations")), None) 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 @utils.registerPlexObject
class Album( class Album(
@ -359,6 +366,12 @@ class Album(
""" Returns str, default title for a new syncItem. """ """ Returns str, default title for a new syncItem. """
return f'{self.parentTitle} - {self.title}' 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 @utils.registerPlexObject
class Track( class Track(
@ -470,6 +483,12 @@ class Track(
""" Get the Plex Web URL with the correct parameters. """ """ Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey) 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 @utils.registerPlexObject
class TrackSession(PlexSession, Track): class TrackSession(PlexSession, Track):

View file

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from pathlib import Path
from urllib.parse import quote_plus from urllib.parse import quote_plus
from plexapi import media, utils from plexapi import media, utils
@ -560,3 +561,9 @@ class Collection(
raise Unsupported('Unsupported collection content') raise Unsupported('Unsupported collection content')
return myplex.sync(sync_item, client=client, clientId=clientId) 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 -*- # -*- coding: utf-8 -*-
import xml import xml
from pathlib import Path
from urllib.parse import quote_plus from urllib.parse import quote_plus
from plexapi import log, settings, utils from plexapi import log, settings, utils
@ -1024,6 +1024,20 @@ class BaseResource(PlexObject):
except xml.etree.ElementTree.ParseError: except xml.etree.ElementTree.ParseError:
pass 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): class Art(BaseResource):
""" Represents a single Art object. """ """ Represents a single Art object. """

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
from pathlib import Path
from urllib.parse import quote_plus from urllib.parse import quote_plus
from plexapi import media, utils, video from plexapi import media, utils, video
@ -139,6 +140,12 @@ class Photoalbum(
""" Get the Plex Web URL with the correct parameters. """ """ Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.key, legacy=1) 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 @utils.registerPlexObject
class Photo( class Photo(
@ -290,6 +297,12 @@ class Photo(
""" Get the Plex Web URL with the correct parameters. """ """ Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey, legacy=1) 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 @utils.registerPlexObject
class PhotoSession(PlexSession, Photo): class PhotoSession(PlexSession, Photo):

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re import re
from pathlib import Path
from urllib.parse import quote_plus, unquote from urllib.parse import quote_plus, unquote
from plexapi import media, utils from plexapi import media, utils
@ -496,3 +497,9 @@ class Playlist(
def _getWebURL(self, base=None): def _getWebURL(self, base=None):
""" Get the Plex Web URL with the correct parameters. """ """ Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='playlist', key=self.key) 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 collections import deque
from datetime import datetime, timedelta from datetime import datetime, timedelta
from getpass import getpass from getpass import getpass
from hashlib import sha1
from threading import Event, Thread from threading import Event, Thread
from urllib.parse import quote from urllib.parse import quote
@ -650,3 +651,8 @@ def openOrRead(file):
return file.read() return file.read()
with open(file, 'rb') as f: with open(file, 'rb') as f:
return f.read() 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 -*- # -*- coding: utf-8 -*-
import os import os
from pathlib import Path
from urllib.parse import quote_plus from urllib.parse import quote_plus
from plexapi import media, utils from plexapi import media, utils
@ -445,6 +446,12 @@ class Movie(
self._server.query(key, params=params, method=self._server._session.put) self._server.query(key, params=params, method=self._server._session.put)
return self 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 @utils.registerPlexObject
class Show( class Show(
@ -655,6 +662,12 @@ class Show(
filepaths += episode.download(_savepath, keep_original_name, **kwargs) filepaths += episode.download(_savepath, keep_original_name, **kwargs)
return filepaths 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 @utils.registerPlexObject
class Season( class Season(
@ -808,6 +821,12 @@ class Season(
""" Returns str, default title for a new syncItem. """ """ Returns str, default title for a new syncItem. """
return f'{self.parentTitle} - {self.title}' 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 @utils.registerPlexObject
class Episode( class Episode(
@ -1000,6 +1019,12 @@ class Episode(
self._server.query(key, params=params, method=self._server._session.put) self._server.query(key, params=params, method=self._server._session.put)
return self 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 @utils.registerPlexObject
class Clip( class Clip(
@ -1058,6 +1083,12 @@ class Clip(
""" Returns a filename for use in download. """ """ Returns a filename for use in download. """
return self.title 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): class Extra(Clip):
""" Represents a single Extra (trailer, behindTheScenes, etc). """ """ 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_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_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") 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): def pytest_addoption(parser):

View file

@ -1459,3 +1459,15 @@ def test_video_optimize(plex, movie, tvshows, show):
movie.optimize() movie.optimize()
with pytest.raises(BadRequest): with pytest.raises(BadRequest):
movie.optimize(target="mobile", locationID=-100) 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))