mirror of
https://github.com/pkkid/python-plexapi
synced 2025-02-16 21:08:27 +00:00
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:
parent
0679816201
commit
b98b6050fe
9 changed files with 112 additions and 1 deletions
|
@ -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):
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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. """
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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). """
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Add table
Reference in a new issue