Skip to content

Commit d982077

Browse files
committed
Add additional histroy methods
1 parent 481e55b commit d982077

File tree

4 files changed

+167
-1
lines changed

4 files changed

+167
-1
lines changed

plexapi/library.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ def add(self, name='', type='', agent='', scanner='', location='', language='en'
294294
part += urlencode(kwargs)
295295
return self._server.query(part, method=self._server._session.post)
296296

297+
def history(self):
298+
""" Get Play History for all library Sections for the owner. """
299+
hist = []
300+
for section in self.sections():
301+
hist.extend(section.history())
302+
return hist
303+
297304

298305
class LibrarySection(PlexObject):
299306
""" Base class for a single library section.
@@ -633,6 +640,10 @@ def sync(self, policy, mediaSettings, client=None, clientId=None, title=None, so
633640

634641
return myplex.sync(client=client, clientId=clientId, sync_item=sync_item)
635642

643+
def history(self):
644+
""" Get Play History for this library Section for the owner. """
645+
return self._server.history(librarySectionID=self.key, accountID=1)
646+
636647

637648
class MovieSection(LibrarySection):
638649
""" Represents a :class:`~plexapi.library.LibrarySection` section containing movies.

plexapi/myplex.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,15 @@ def claimToken(self):
600600
raise BadRequest('(%s) %s %s; %s' % (response.status_code, codename, response.url, errtext))
601601
return response.json()['token']
602602

603+
def history(self):
604+
""" Get Play History for all library sections on all servers for the owner. """
605+
servers = [x for x in self.resources() if x.provides == 'server' and x.owned]
606+
hist = []
607+
for server in servers:
608+
conn = server.connect()
609+
hist.extend(conn.history(accountID=1))
610+
return hist
611+
603612

604613
class MyPlexUser(PlexObject):
605614
""" This object represents non-signed in users such as friends and linked
@@ -654,6 +663,8 @@ def _loadData(self, data):
654663
self.title = data.attrib.get('title', '')
655664
self.username = data.attrib.get('username', '')
656665
self.servers = self.findItems(data, MyPlexServerShare)
666+
for server in self.servers:
667+
server.accountID = self.id
657668

658669
def get_token(self, machineIdentifier):
659670
try:
@@ -663,6 +674,25 @@ def get_token(self, machineIdentifier):
663674
except Exception:
664675
log.exception('Failed to get access token for %s' % self.title)
665676

677+
def server(self, name):
678+
""" Returns the :class:`~plexapi.myplex.MyPlexServerShare` that matches the name specified.
679+
680+
Parameters:
681+
name (str): Name of the server to return.
682+
"""
683+
for server in self.servers:
684+
if name.lower() == server.name.lower():
685+
return server
686+
687+
raise NotFound('Unable to find server %s' % name)
688+
689+
def history(self):
690+
""" Get all Play History for a user in all shared servers. """
691+
hist = []
692+
for server in self.servers:
693+
hist.extend(server.history())
694+
return hist
695+
666696

667697
class Section(PlexObject):
668698
""" This refers to a shared section. The raw xml for the data presented here
@@ -689,6 +719,11 @@ def _loadData(self, data):
689719
self.type = data.attrib.get('type')
690720
self.shared = utils.cast(bool, data.attrib.get('shared'))
691721

722+
def history(self):
723+
""" Get all Play History for a user for this section in this shared server. """
724+
server = self._server._server.resource(self._server.name).connect()
725+
return server.history(accountID=self._server.accountID, librarySectionID=self.sectionKey)
726+
692727

693728
class MyPlexServerShare(PlexObject):
694729
""" Represents a single user's server reference. Used for library sharing.
@@ -711,6 +746,7 @@ def _loadData(self, data):
711746
""" Load attribute values from Plex XML response. """
712747
self._data = data
713748
self.id = utils.cast(int, data.attrib.get('id'))
749+
self.accountID = utils.cast(int, data.attrib.get('accountID'))
714750
self.serverId = utils.cast(int, data.attrib.get('serverId'))
715751
self.machineIdentifier = data.attrib.get('machineIdentifier')
716752
self.name = data.attrib.get('name')
@@ -720,7 +756,21 @@ def _loadData(self, data):
720756
self.owned = utils.cast(bool, data.attrib.get('owned'))
721757
self.pending = utils.cast(bool, data.attrib.get('pending'))
722758

759+
def section(self, name):
760+
""" Returns the :class:`~plexapi.myplex.Section` that matches the name specified.
761+
762+
Parameters:
763+
name (str): Name of the section to return.
764+
"""
765+
for section in self.sections():
766+
if name.lower() == section.title.lower():
767+
return section
768+
769+
raise NotFound('Unable to find section %s' % name)
770+
723771
def sections(self):
772+
""" Returns a list of all :class:`~plexapi.myplex.Section` objects shared with this user.
773+
"""
724774
url = MyPlexAccount.FRIENDSERVERS.format(machineId=self.machineIdentifier, serverId=self.id)
725775
data = self._server.query(url)
726776
sections = []
@@ -731,6 +781,11 @@ def sections(self):
731781

732782
return sections
733783

784+
def history(self):
785+
""" Get all Play History for a user in this shared server. """
786+
server = self._server.resource(self.name).connect()
787+
return server.history(accountID=self.accountID)
788+
734789

735790
class MyPlexResource(PlexObject):
736791
""" This object represents resources connected to your Plex server that can provide

plexapi/server.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def installUpdate(self):
322322
# figure out what method this is..
323323
return self.query(part, method=self._session.put)
324324

325-
def history(self, maxresults=9999999, mindate=None, ratingKey=None):
325+
def history(self, maxresults=9999999, mindate=None, ratingKey=None, accountID=None, librarySectionID=None):
326326
""" Returns a list of media items from watched history. If there are many results, they will
327327
be fetched from the server in batches of X_PLEX_CONTAINER_SIZE amounts. If you're only
328328
looking for the first <num> results, it would be wise to set the maxresults option to that
@@ -333,11 +333,17 @@ def history(self, maxresults=9999999, mindate=None, ratingKey=None):
333333
mindate (datetime): Min datetime to return results from. This really helps speed
334334
up the result listing. For example: datetime.now() - timedelta(days=7)
335335
ratingKey (int/str) Request history for a specific ratingKey item.
336+
accountID (int/str) Request history for a specific account ID.
337+
librarySectionID (int/str) Request history for a specific library section ID.
336338
"""
337339
results, subresults = [], '_init'
338340
args = {'sort': 'viewedAt:desc'}
339341
if ratingKey:
340342
args['metadataItemID'] = ratingKey
343+
if accountID:
344+
args['accountID'] = accountID
345+
if librarySectionID:
346+
args['librarySectionID'] = librarySectionID
341347
if mindate:
342348
args['viewedAt>'] = int(mindate.timestamp())
343349
args['X-Plex-Container-Start'] = 0

tests/test_history.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
from datetime import datetime
4+
from plexapi.exceptions import BadRequest, NotFound
5+
from . import conftest as utils
6+
7+
8+
def test_history_Movie(movie):
9+
movie.markWatched()
10+
history = movie.history()
11+
assert len(history)
12+
movie.markUnwatched()
13+
14+
15+
def test_history_Show(show):
16+
show.markWatched()
17+
history = show.history()
18+
assert len(history)
19+
show.markUnwatched()
20+
21+
22+
def test_history_Season(show):
23+
season = show.season('Season 1')
24+
season.markWatched()
25+
history = season.history()
26+
assert len(history)
27+
season.markUnwatched()
28+
29+
30+
def test_history_Episode(episode):
31+
episode.markWatched()
32+
history = episode.history()
33+
assert len(history)
34+
episode.markUnwatched()
35+
36+
37+
def test_history_Artist(artist):
38+
history = artist.history()
39+
40+
41+
def test_history_Album(album):
42+
history = album.history()
43+
44+
45+
def test_history_Track(track):
46+
history = track.history()
47+
48+
49+
def test_history_MyAccount(account, movie, show):
50+
movie.markWatched()
51+
show.markWatched()
52+
history = account.history()
53+
assert len(history)
54+
movie.markUnwatched()
55+
show.markUnwatched()
56+
57+
58+
def test_history_MyLibrary(plex, movie, show):
59+
movie.markWatched()
60+
show.markWatched()
61+
history = plex.library.history()
62+
assert len(history)
63+
movie.markUnwatched()
64+
show.markUnwatched()
65+
66+
67+
def test_history_MySection(plex, movie):
68+
movie.markWatched()
69+
history = plex.library.section('Movies').history()
70+
assert len(history)
71+
movie.markUnwatched()
72+
73+
74+
def test_history_MyServer(plex, movie):
75+
movie.markWatched()
76+
history = plex.history()
77+
assert len(history)
78+
movie.markUnwatched()
79+
80+
81+
def test_history_User(account, shared_username):
82+
user = account.user(shared_username)
83+
history = user.history()
84+
85+
86+
def test_history_UserServer(account, shared_username, plex):
87+
userSharedServer = account.user(shared_username).server(plex.friendlyName)
88+
history = userSharedServer.history()
89+
90+
91+
def test_history_UserSection(account, shared_username, plex):
92+
userSharedServerSection = account.user(shared_username).server(plex.friendlyName).section('Movies')
93+
history = userSharedServerSection.history()
94+

0 commit comments

Comments
 (0)