Skip to content

Commit

Permalink
Merge pull request #810 from JonnyWong16/feature/web_url
Browse files Browse the repository at this point in the history
Add methods to retrieve the Plex Web URL
  • Loading branch information
JonnyWong16 authored Oct 4, 2021
2 parents 26648f6 + 516c8ed commit 3fce245
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 0 deletions.
4 changes: 4 additions & 0 deletions plexapi/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,7 @@ def trackNumber(self):
def _defaultSyncTitle(self):
""" Returns str, default title for a new syncItem. """
return '%s - %s - %s' % (self.grandparentTitle, self.parentTitle, self.title)

def _getWebURL(self, base=None):
""" Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey)
16 changes: 16 additions & 0 deletions plexapi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,12 +579,28 @@ def delete(self):

def history(self, maxresults=9999999, mindate=None):
""" Get Play History for a media item.
Parameters:
maxresults (int): Only return the specified number of results (optional).
mindate (datetime): Min datetime to return results from.
"""
return self._server.history(maxresults=maxresults, mindate=mindate, ratingKey=self.ratingKey)

def _getWebURL(self, base=None):
""" Get the Plex Web URL with the correct parameters.
Private method to allow overriding parameters from subclasses.
"""
return self._server._buildWebURL(base=base, endpoint='details', key=self.key)

def getWebURL(self, base=None):
""" Returns the Plex Web URL for a media item.
Parameters:
base (str): The base URL before the fragment (``#!``).
Default is https://app.plex.tv/desktop.
"""
return self._getWebURL(base=base)


class Playable(object):
""" This is a general place to store functions specific to media that is Playable.
Expand Down
25 changes: 25 additions & 0 deletions plexapi/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -1456,6 +1456,23 @@ def filterFields(self, mediaType=None):
def listChoices(self, category, libtype=None, **kwargs):
return self.listFilterChoices(field=category, libtype=libtype)

def getWebURL(self, base=None, tab=None, key=None):
""" Returns the Plex Web URL for the library.
Parameters:
base (str): The base URL before the fragment (``#!``).
Default is https://app.plex.tv/desktop.
tab (str): The library tab (recommended, library, collections, playlists, timeline).
key (str): A hub key.
"""
params = {'source': self.key}
if tab is not None:
params['pivot'] = tab
if key is not None:
params['key'] = key
params['pageType'] = 'list'
return self._server._buildWebURL(base=base, **params)


class MovieSection(LibrarySection):
""" Represents a :class:`~plexapi.library.LibrarySection` section containing movies.
Expand Down Expand Up @@ -1857,6 +1874,7 @@ def _loadData(self, data):
self.style = data.attrib.get('style')
self.title = data.attrib.get('title')
self.type = data.attrib.get('type')
self._section = None # cache for self.section

def __len__(self):
return self.size
Expand All @@ -1868,6 +1886,13 @@ def reload(self):
self.more = False
self.size = len(self.items)

def section(self):
""" Returns the :class:`~plexapi.library.LibrarySection` this hub belongs to.
"""
if self._section is None:
self._section = self._server.library.sectionByID(self.librarySectionID)
return self._section


class HubMediaTag(PlexObject):
""" Base class of hub media tag search results.
Expand Down
8 changes: 8 additions & 0 deletions plexapi/photo.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ def download(self, savepath=None, keep_original_name=False, showstatus=False):
filepaths.append(filepath)
return filepaths

def _getWebURL(self, base=None):
""" Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.key, legacy=1)


@utils.registerPlexObject
class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin, TagMixin):
Expand Down Expand Up @@ -301,3 +305,7 @@ def download(self, savepath=None, keep_original_name=False, showstatus=False):
if filepath:
filepaths.append(filepath)
return filepaths

def _getWebURL(self, base=None):
""" Get the Plex Web URL with the correct parameters. """
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey, legacy=1)
4 changes: 4 additions & 0 deletions plexapi/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,7 @@ def sync(self, videoQuality=None, photoResolution=None, audioBitrate=None, clien
raise Unsupported('Unsupported playlist content')

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

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)
36 changes: 36 additions & 0 deletions plexapi/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,42 @@ def resources(self):
key = '/statistics/resources?timespan=6'
return self.fetchItems(key, StatisticsResources)

def _buildWebURL(self, base=None, endpoint=None, **kwargs):
""" Build the Plex Web URL for the object.
Parameters:
base (str): The base URL before the fragment (``#!``).
Default is https://app.plex.tv/desktop.
endpoint (str): The Plex Web URL endpoint.
None for server, 'playlist' for playlists, 'details' for all other media types.
**kwargs (dict): Dictionary of URL parameters.
"""
if base is None:
base = 'https://app.plex.tv/desktop/'

if endpoint:
return '%s#!/server/%s/%s%s' % (
base, self.machineIdentifier, endpoint, utils.joinArgs(kwargs)
)
else:
return '%s#!/media/%s/com.plexapp.plugins.library%s' % (
base, self.machineIdentifier, utils.joinArgs(kwargs)
)

def getWebURL(self, base=None, playlistTab=None):
""" Returns the Plex Web URL for the server.
Parameters:
base (str): The base URL before the fragment (``#!``).
Default is https://app.plex.tv/desktop.
playlistTab (str): The playlist tab (audio, video, photo). Only used for the playlist URL.
"""
if playlistTab is not None:
params = {'source': 'playlists', 'pivot': 'playlists.%s' % playlistTab}
else:
params = {'key': '/hubs', 'pageType': 'hub'}
return self._buildWebURL(base=base, **params)


class Account(PlexObject):
""" Contains the locally cached MyPlex account information. The properties provided don't
Expand Down
26 changes: 26 additions & 0 deletions tests/test_audio.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from urllib.parse import quote_plus

from . import conftest as utils
from . import test_media, test_mixins

Expand Down Expand Up @@ -105,6 +107,14 @@ def test_audio_Artist_media_tags(artist):
test_media.tag_style(artist)


def test_video_Artist_PlexWebURL(plex, artist):
url = artist.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'details' in url
assert quote_plus(artist.key) in url


def test_audio_Album_attrs(album):
assert utils.is_datetime(album.addedAt)
if album.art:
Expand Down Expand Up @@ -200,6 +210,14 @@ def test_audio_Album_media_tags(album):
test_media.tag_style(album)


def test_video_Album_PlexWebURL(plex, album):
url = album.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'details' in url
assert quote_plus(album.key) in url


def test_audio_Track_attrs(album):
track = album.get("As Colourful As Ever").reload()
assert utils.is_datetime(track.addedAt)
Expand Down Expand Up @@ -341,6 +359,14 @@ def test_audio_Track_media_tags(track):
test_media.tag_mood(track)


def test_video_Track_PlexWebURL(plex, track):
url = track.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'details' in url
assert quote_plus(track.parentKey) in url


def test_audio_Audio_section(artist, album, track):
assert artist.section()
assert album.section()
Expand Down
10 changes: 10 additions & 0 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from urllib.parse import quote_plus

import pytest
from plexapi.exceptions import BadRequest, NotFound

Expand Down Expand Up @@ -285,3 +287,11 @@ def test_Collection_mixins_rating(collection):

def test_Collection_mixins_tags(collection):
test_mixins.edit_label(collection)


def test_Collection_PlexWebURL(plex, collection):
url = collection.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'details' in url
assert quote_plus(collection.key) in url
26 changes: 26 additions & 0 deletions tests/test_library.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from collections import namedtuple
from datetime import datetime, timedelta
from urllib.parse import quote_plus

import pytest
from plexapi.exceptions import BadRequest, NotFound

Expand Down Expand Up @@ -212,6 +214,30 @@ def test_library_MovieSection_collection_exception(movies):
movies.collection("Does Not Exists")


def test_library_MovieSection_PlexWebURL(plex, movies):
tab = 'library'
url = movies.getWebURL(tab=tab)
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'source=%s' % movies.key in url
assert 'pivot=%s' % tab in url
# Test a different base
base = 'https://doesnotexist.com/plex'
url = movies.getWebURL(base=base)
assert url.startswith(base)


def test_library_MovieSection_PlexWebURL_hub(plex, movies):
hubs = movies.hubs()
hub = next(iter(hubs), None)
assert hub is not None
url = hub.section().getWebURL(key=hub.key)
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'source=%s' % movies.key in url
assert quote_plus(hub.key) in url


def test_library_ShowSection_all(tvshows):
assert len(tvshows.all(title__iexact="The 100"))

Expand Down
20 changes: 20 additions & 0 deletions tests/test_photo.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
from urllib.parse import quote_plus

from . import test_media, test_mixins


Expand All @@ -24,6 +26,15 @@ def test_photo_Photoalbum_mixins_rating(photoalbum):
test_mixins.edit_rating(photoalbum)


def test_video_Photoalbum_PlexWebURL(plex, photoalbum):
url = photoalbum.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'details' in url
assert quote_plus(photoalbum.key) in url
assert 'legacy=1' in url


def test_photo_Photo_mixins_rating(photo):
test_mixins.edit_rating(photo)

Expand All @@ -35,3 +46,12 @@ def test_photo_Photo_mixins_tags(photo):
def test_photo_Photo_media_tags(photo):
photo.reload()
test_media.tag_tag(photo)


def test_video_Photo_PlexWebURL(plex, photo):
url = photo.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'details' in url
assert quote_plus(photo.parentKey) in url
assert 'legacy=1' in url
15 changes: 15 additions & 0 deletions tests/test_playlist.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import time
from urllib.parse import quote_plus

import pytest
from plexapi.exceptions import BadRequest, NotFound, Unsupported
Expand Down Expand Up @@ -258,6 +259,20 @@ def test_Playlist_exceptions(plex, movies, movie, artist):
playlist.delete()


def test_Playlist_PlexWebURL(plex, show):
title = 'test_playlist_plexweburl'
episodes = show.episodes()
playlist = plex.createPlaylist(title, items=episodes[:3])
try:
url = playlist.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'playlist' in url
assert quote_plus(playlist.key) in url
finally:
playlist.delete()


def test_Playlist_mixins_images(playlist):
# test_mixins.lock_art(playlist)
test_mixins.lock_poster(playlist)
Expand Down
22 changes: 22 additions & 0 deletions tests/test_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import re
import time
from urllib.parse import quote_plus

import pytest
from datetime import datetime
Expand Down Expand Up @@ -512,3 +513,24 @@ def test_server_transcode_sessions(plex, requests_mock):
assert session.videoCodec in utils.CODECS
assert session.videoDecision == "transcode"
assert utils.is_int(session.width, gte=852)


def test_server_PlexWebURL(plex):
url = plex.getWebURL()
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert quote_plus('/hubs') in url
assert 'pageType=hub' in url
# Test a different base
base = 'https://doesnotexist.com/plex'
url = plex.getWebURL(base=base)
assert url.startswith(base)


def test_server_PlexWebURL_playlists(plex):
tab = 'audio'
url = plex.getWebURL(playlistTab=tab)
assert url.startswith('https://app.plex.tv/desktop')
assert plex.machineIdentifier in url
assert 'source=playlists' in url
assert 'pivot=playlists.%s' % tab in url
Loading

0 comments on commit 3fce245

Please sign in to comment.