Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions plexapi/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,12 @@ def _loadData(self, data):
self.tagValue = utils.cast(int, data.attrib.get('tagValue'))
self.thumb = data.attrib.get('thumb')

def items(self, *args, **kwargs):
""" Return the list of items within this tag. """
if not self.key:
raise BadRequest('Key is not defined for this tag: %s' % self.tag)
return self.fetchItems(self.key)


@utils.registerPlexObject
class Tag(HubMediaTag):
Expand Down
232 changes: 114 additions & 118 deletions plexapi/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,57 +641,41 @@ class MediaTag(PlexObject):
the construct used for things such as Country, Director, Genre, etc.

Attributes:
server (:class:`~plexapi.server.PlexServer`): Server this client is connected to.
filter (str): The library filter for the tag.
id (id): Tag ID (This seems meaningless except to use it as a unique id).
role (str): Unknown
key (str): API URL (/library/section/<librarySectionID>/all?<filter>).
role (str): The name of the character role for :class:`~plexapi.media.Role` only.
tag (str): Name of the tag. This will be Animation, SciFi etc for Genres. The name of
person for Directors and Roles (ex: Animation, Stephen Graham, etc).
<Hub_Search_Attributes>: Attributes only applicable in search results from
PlexServer :func:`~plexapi.server.PlexServer.search`. They provide details of which
library section the tag was found as well as the url to dig deeper into the results.

* key (str): API URL to dig deeper into this tag (ex: /library/sections/1/all?actor=9081).
* librarySectionID (int): Section ID this tag was generated from.
* librarySectionTitle (str): Library section title this tag was found.
* librarySectionType (str): Media type of the library section this tag was found.
* tagType (int): Tag type ID.
* thumb (str): URL to thumbnail image.
thumb (str): URL to thumbnail image for :class:`~plexapi.media.Role` only.
"""

def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.filter = data.attrib.get('filter')
self.id = cast(int, data.attrib.get('id'))
self.key = data.attrib.get('key')
self.role = data.attrib.get('role')
self.tag = data.attrib.get('tag')
# additional attributes only from hub search
self.key = data.attrib.get('key')
self.librarySectionID = cast(int, data.attrib.get('librarySectionID'))
self.librarySectionTitle = data.attrib.get('librarySectionTitle')
self.librarySectionType = data.attrib.get('librarySectionType')
self.tagType = cast(int, data.attrib.get('tagType'))
self.thumb = data.attrib.get('thumb')

def items(self, *args, **kwargs):
""" Return the list of items within this tag. This function is only applicable
in search results from PlexServer :func:`~plexapi.server.PlexServer.search`.
"""
if not self.key:
raise BadRequest('Key is not defined for this tag: %s' % self.tag)
return self.fetchItems(self.key)


class GuidTag(PlexObject):
""" Base class for guid tags used only for Guids, as they contain only a string identifier
parent = self._parent()
self._librarySectionID = utils.cast(int, parent._data.attrib.get('librarySectionID'))
self._librarySectionKey = parent._data.attrib.get('librarySectionKey')
self._librarySectionTitle = parent._data.attrib.get('librarySectionTitle')
self._parentType = parent.TYPE

Attributes:
id (id): The guid for external metadata sources (e.g. IMDB, TMDB, TVDB).
"""
if self._librarySectionKey and self.filter:
self.key = '%s/all?%s&type=%s' % (
self._librarySectionKey, self.filter, utils.searchType(self._parentType))

def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.id = data.attrib.get('id')
def items(self):
""" Return the list of items within this tag. """
if not self.key:
raise BadRequest('Key is not defined for this tag: %s. '
'Reload the parent object.' % self.tag)
return self.fetchItems(self.key)


@utils.registerPlexObject
Expand All @@ -705,36 +689,11 @@ class Collection(MediaTag):
TAG = 'Collection'
FILTER = 'collection'


@utils.registerPlexObject
class Label(MediaTag):
""" Represents a single Label media tag.

Attributes:
TAG (str): 'Label'
FILTER (str): 'label'
"""
TAG = 'Label'
FILTER = 'label'


@utils.registerPlexObject
class Tag(MediaTag):
""" Represents a single Tag media tag.

Attributes:
TAG (str): 'Tag'
FILTER (str): 'tag'
"""
TAG = 'Tag'
FILTER = 'tag'

def _loadData(self, data):
self._data = data
self.id = cast(int, data.attrib.get('id', 0))
self.filter = data.attrib.get('filter')
self.tag = data.attrib.get('tag')
self.title = self.tag
def collection(self):
""" Return the :class:`~plexapi.collection.Collection` object for this collection tag.
"""
key = '%s/collections' % self._librarySectionKey
return self.fetchItem(key, etag='Directory', index=self.id)


@utils.registerPlexObject
Expand Down Expand Up @@ -774,13 +733,15 @@ class Genre(MediaTag):


@utils.registerPlexObject
class Guid(GuidTag):
""" Represents a single Guid media tag.
class Label(MediaTag):
""" Represents a single Label media tag.

Attributes:
TAG (str): 'Guid'
TAG (str): 'Label'
FILTER (str): 'label'
"""
TAG = "Guid"
TAG = 'Label'
FILTER = 'label'


@utils.registerPlexObject
Expand All @@ -795,6 +756,42 @@ class Mood(MediaTag):
FILTER = 'mood'


@utils.registerPlexObject
class Producer(MediaTag):
""" Represents a single Producer media tag.

Attributes:
TAG (str): 'Producer'
FILTER (str): 'producer'
"""
TAG = 'Producer'
FILTER = 'producer'


@utils.registerPlexObject
class Role(MediaTag):
""" Represents a single Role (actor/actress) media tag.

Attributes:
TAG (str): 'Role'
FILTER (str): 'role'
"""
TAG = 'Role'
FILTER = 'role'


@utils.registerPlexObject
class Similar(MediaTag):
""" Represents a single Similar media tag.

Attributes:
TAG (str): 'Similar'
FILTER (str): 'similar'
"""
TAG = 'Similar'
FILTER = 'similar'


@utils.registerPlexObject
class Style(MediaTag):
""" Represents a single Style media tag.
Expand All @@ -807,6 +804,53 @@ class Style(MediaTag):
FILTER = 'style'


@utils.registerPlexObject
class Tag(MediaTag):
""" Represents a single Tag media tag.

Attributes:
TAG (str): 'Tag'
FILTER (str): 'tag'
"""
TAG = 'Tag'
FILTER = 'tag'


@utils.registerPlexObject
class Writer(MediaTag):
""" Represents a single Writer media tag.

Attributes:
TAG (str): 'Writer'
FILTER (str): 'writer'
"""
TAG = 'Writer'
FILTER = 'writer'


class GuidTag(PlexObject):
""" Base class for guid tags used only for Guids, as they contain only a string identifier

Attributes:
id (id): The guid for external metadata sources (e.g. IMDB, TMDB, TVDB).
"""

def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data
self.id = data.attrib.get('id')


@utils.registerPlexObject
class Guid(GuidTag):
""" Represents a single Guid media tag.

Attributes:
TAG (str): 'Guid'
"""
TAG = 'Guid'


class BaseImage(PlexObject):
""" Base class for all Art, Banner, and Poster objects.

Expand Down Expand Up @@ -849,54 +893,6 @@ class Poster(BaseImage):
""" Represents a single Poster object. """


@utils.registerPlexObject
class Producer(MediaTag):
""" Represents a single Producer media tag.

Attributes:
TAG (str): 'Producer'
FILTER (str): 'producer'
"""
TAG = 'Producer'
FILTER = 'producer'


@utils.registerPlexObject
class Role(MediaTag):
""" Represents a single Role (actor/actress) media tag.

Attributes:
TAG (str): 'Role'
FILTER (str): 'role'
"""
TAG = 'Role'
FILTER = 'role'


@utils.registerPlexObject
class Similar(MediaTag):
""" Represents a single Similar media tag.

Attributes:
TAG (str): 'Similar'
FILTER (str): 'similar'
"""
TAG = 'Similar'
FILTER = 'similar'


@utils.registerPlexObject
class Writer(MediaTag):
""" Represents a single Writer media tag.

Attributes:
TAG (str): 'Writer'
FILTER (str): 'writer'
"""
TAG = 'Writer'
FILTER = 'writer'


@utils.registerPlexObject
class Chapter(PlexObject):
""" Represents a single Writer media tag.
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,11 @@ def movie(movies):
@pytest.fixture()
def collection(movies):
try:
return movies.collections(title="marvel")[0]
return movies.collections(title="Marvel")[0]
except IndexError:
movie = movies.get("Elephants Dream")
movie.addCollection("marvel")
return movies.collections(title="marvel")[0]
movie.addCollection("Marvel")
return movies.collections(title="Marvel")[0]


@pytest.fixture()
Expand Down
27 changes: 26 additions & 1 deletion tests/test_audio.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from . import conftest as utils
from . import test_mixins
from . import test_media, test_mixins


def test_audio_Artist_attr(artist):
Expand Down Expand Up @@ -87,6 +87,16 @@ def test_audio_Artist_mixins_tags(artist):
test_mixins.edit_style(artist)


def test_audio_Artist_media_tags(artist):
artist.reload()
test_media.tag_collection(artist)
test_media.tag_country(artist)
test_media.tag_genre(artist)
test_media.tag_mood(artist)
test_media.tag_similar(artist)
test_media.tag_style(artist)


def test_audio_Album_attrs(album):
assert utils.is_datetime(album.addedAt)
if album.art:
Expand Down Expand Up @@ -165,6 +175,15 @@ def test_audio_Album_mixins_tags(album):
test_mixins.edit_style(album)


def test_audio_Album_media_tags(album):
album.reload()
test_media.tag_collection(album)
test_media.tag_genre(album)
test_media.tag_label(album)
test_media.tag_mood(album)
test_media.tag_style(album)


def test_audio_Track_attrs(album):
track = album.get("As Colourful As Ever").reload()
assert utils.is_datetime(track.addedAt)
Expand Down Expand Up @@ -294,6 +313,12 @@ def test_audio_Track_mixins_tags(track):
test_mixins.edit_mood(track)


def test_audio_Track_media_tags(track):
track.reload()
test_media.tag_collection(track)
test_media.tag_mood(track)


def test_audio_Audio_section(artist, album, track):
assert artist.section()
assert album.section()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_Collection_attrs(collection):
assert collection.summary == ""
assert collection.thumb.startswith("/library/collections/%s/composite" % collection.ratingKey)
assert collection.thumbBlurHash is None
assert collection.title == "marvel"
assert collection.title == "Marvel"
assert collection.titleSort == collection.title
assert collection.type == "collection"
assert utils.is_datetime(collection.updatedAt)
Expand Down
Loading