-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1120 from xsteadfastx/master
Added plexupdate plugin for refreshing plex music library after importing music
- Loading branch information
Showing
4 changed files
with
231 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
"""Updates an Plex library whenever the beets library is changed. | ||
Put something like the following in your config.yaml to configure: | ||
plex: | ||
host: localhost | ||
port: 32400 | ||
""" | ||
import requests | ||
from urlparse import urljoin | ||
import xml.etree.ElementTree as ET | ||
from beets import config | ||
from beets.plugins import BeetsPlugin | ||
|
||
|
||
# Global variable to detect if database is changed that the update | ||
# is only run once before beets exists. | ||
database_changed = False | ||
|
||
|
||
def get_music_section(host, port): | ||
"""Getting the section key for the music library in Plex. | ||
""" | ||
api_endpoint = 'library/sections' | ||
url = urljoin('http://{0}:{1}'.format(host, port), api_endpoint) | ||
|
||
# Sends request. | ||
r = requests.get(url) | ||
|
||
# Parse xml tree and extract music section key. | ||
tree = ET.fromstring(r.text) | ||
for child in tree.findall('Directory'): | ||
if child.get('title') == 'Music': | ||
return child.get('key') | ||
|
||
|
||
def update_plex(host, port): | ||
"""Sends request to the Plex api to start a library refresh. | ||
""" | ||
# Getting section key and build url. | ||
section_key = get_music_section(host, port) | ||
api_endpoint = 'library/sections/{0}/refresh'.format(section_key) | ||
url = urljoin('http://{0}:{1}'.format(host, port), api_endpoint) | ||
|
||
# Sends request and returns requests object. | ||
r = requests.get(url) | ||
return r | ||
|
||
|
||
class PlexUpdate(BeetsPlugin): | ||
def __init__(self): | ||
super(PlexUpdate, self).__init__() | ||
|
||
# Adding defaults. | ||
config['plex'].add({ | ||
u'host': u'localhost', | ||
u'port': 32400}) | ||
|
||
|
||
@PlexUpdate.listen('database_change') | ||
def listen_for_db_change(lib=None): | ||
"""Listens for beets db change and set global database_changed | ||
variable to True. | ||
""" | ||
global database_changed | ||
database_changed = True | ||
|
||
|
||
@PlexUpdate.listen('cli_exit') | ||
def update(lib=None): | ||
"""When the client exists and the database_changed variable is True | ||
trying to send refresh request to Plex server. | ||
""" | ||
if database_changed: | ||
print('Updating Plex library...') | ||
|
||
# Try to send update request. | ||
try: | ||
update_plex( | ||
config['plex']['host'].get(), | ||
config['plex']['port'].get()) | ||
print('... started.') | ||
|
||
except requests.exceptions.RequestException: | ||
print('Update failed.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
PlexUpdate Plugin | ||
================= | ||
|
||
``plexupdate`` is a very simple plugin for beets that lets you automatically | ||
update `Plex`_'s music library whenever you change your beets library. | ||
|
||
To use ``plexupdate`` plugin, enable it in your configuration | ||
(see :ref:`using-plugins`). | ||
Then, you'll probably want to configure the specifics of your Plex server. | ||
You can do that using an ``plex:`` section in your ``config.yaml``, | ||
which looks like this:: | ||
|
||
plex: | ||
host: localhost | ||
port: 32400 | ||
|
||
To use the ``plexupdate`` plugin you need to install the `requests`_ library with: | ||
|
||
pip install requests | ||
|
||
With that all in place, you'll see beets send the "update" command to your Plex | ||
server every time you change your beets library. | ||
|
||
.. _Plex: http://plex.tv/ | ||
.. _requests: http://docs.python-requests.org/en/latest/ | ||
|
||
Configuration | ||
------------- | ||
|
||
The available options under the ``plex:`` section are: | ||
|
||
- **host**: The Plex server name. | ||
Default: ``localhost``. | ||
- **port**: The Plex server port. | ||
Default: 32400. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from _common import unittest | ||
from helper import TestHelper | ||
from beetsplug.plexupdate import get_music_section, update_plex | ||
import responses | ||
|
||
|
||
class PlexUpdateTest(unittest.TestCase, TestHelper): | ||
def add_response_get_music_section(self): | ||
"""Create response for mocking the get_music_section function. | ||
""" | ||
body = ( | ||
'<?xml version="1.0" encoding="UTF-8"?>' | ||
'<MediaContainer size="3" allowSync="0" ' | ||
'identifier="com.plexapp.plugins.library" ' | ||
'mediaTagPrefix="/system/bundle/media/flags/" ' | ||
'mediaTagVersion="1413367228" title1="Plex Library">' | ||
'<Directory allowSync="0" art="/:/resources/movie-fanart.jpg" ' | ||
'filters="1" refreshing="0" thumb="/:/resources/movie.png" ' | ||
'key="3" type="movie" title="Movies" ' | ||
'composite="/library/sections/3/composite/1416232668" ' | ||
'agent="com.plexapp.agents.imdb" scanner="Plex Movie Scanner" ' | ||
'language="de" uuid="92f68526-21eb-4ee2-8e22-d36355a17f1f" ' | ||
'updatedAt="1416232668" createdAt="1415720680">' | ||
'<Location id="3" path="/home/marv/Media/Videos/Movies" />' | ||
'</Directory>' | ||
'<Directory allowSync="0" art="/:/resources/artist-fanart.jpg" ' | ||
'filters="1" refreshing="0" thumb="/:/resources/artist.png" ' | ||
'key="2" type="artist" title="Music" ' | ||
'composite="/library/sections/2/composite/1416929243" ' | ||
'agent="com.plexapp.agents.lastfm" scanner="Plex Music Scanner" ' | ||
'language="en" uuid="90897c95-b3bd-4778-a9c8-1f43cb78f047" ' | ||
'updatedAt="1416929243" createdAt="1415691331">' | ||
'<Location id="2" path="/home/marv/Media/Musik" />' | ||
'</Directory>' | ||
'<Directory allowSync="0" art="/:/resources/show-fanart.jpg" ' | ||
'filters="1" refreshing="0" thumb="/:/resources/show.png" ' | ||
'key="1" type="show" title="TV Shows" ' | ||
'composite="/library/sections/1/composite/1416320800" ' | ||
'agent="com.plexapp.agents.thetvdb" scanner="Plex Series Scanner" ' | ||
'language="de" uuid="04d2249b-160a-4ae9-8100-106f4ec1a218" ' | ||
'updatedAt="1416320800" createdAt="1415690983">' | ||
'<Location id="1" path="/home/marv/Media/Videos/Series" />' | ||
'</Directory>' | ||
'</MediaContainer>') | ||
status = 200 | ||
content_type = 'text/xml;charset=utf-8' | ||
|
||
responses.add(responses.GET, | ||
'http://localhost:32400/library/sections', | ||
body=body, | ||
status=status, | ||
content_type=content_type) | ||
|
||
def add_response_update_plex(self): | ||
"""Create response for mocking the update_plex function. | ||
""" | ||
body = '' | ||
status = 200 | ||
content_type = 'text/html' | ||
|
||
responses.add(responses.GET, | ||
'http://localhost:32400/library/sections/2/refresh', | ||
body=body, | ||
status=status, | ||
content_type=content_type) | ||
|
||
def setUp(self): | ||
self.setup_beets() | ||
self.load_plugins('plexupdate') | ||
|
||
self.config['plex'] = { | ||
u'host': u'localhost', | ||
u'port': 32400} | ||
|
||
def tearDown(self): | ||
self.teardown_beets() | ||
self.unload_plugins() | ||
|
||
@responses.activate | ||
def test_get_music_section(self): | ||
# Adding response. | ||
self.add_response_get_music_section() | ||
|
||
# Test if section key is "2" out of the mocking data. | ||
self.assertEqual(get_music_section( | ||
self.config['plex']['host'], | ||
self.config['plex']['port']), '2') | ||
|
||
@responses.activate | ||
def test_update_plex(self): | ||
# Adding responses. | ||
self.add_response_get_music_section() | ||
self.add_response_update_plex() | ||
|
||
# Testing status code of the mocking request. | ||
self.assertEqual(update_plex( | ||
self.config['plex']['host'], | ||
self.config['plex']['port']).status_code, 200) | ||
|
||
|
||
def suite(): | ||
return unittest.TestLoader().loadTestsFromName(__name__) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main(defaultTest='suite') |