mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-10 06:04:15 +00:00
Add plex-download.py tool; Added new utility to request user/pass from user, config, or env for use when creating cmd line tools
This commit is contained in:
parent
fe68c8f590
commit
63dc1507d2
4 changed files with 155 additions and 50 deletions
|
@ -92,7 +92,7 @@ class PlexObject(object):
|
||||||
the first item in the result set is returned.
|
the first item in the result set is returned.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
key (str or int): Path in Plex to fetch items from. If an int is passed
|
ekey (str or int): Path in Plex to fetch items from. If an int is passed
|
||||||
in, the key will be translated to /library/metadata/<key>. This allows
|
in, the key will be translated to /library/metadata/<key>. This allows
|
||||||
fetching an item only knowing its key-id.
|
fetching an item only knowing its key-id.
|
||||||
cls (:class:`~plexapi.base.PlexObject`): If you know the class of the
|
cls (:class:`~plexapi.base.PlexObject`): If you know the class of the
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import logging
|
import logging, os, re, requests, time, zipfile
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import requests
|
|
||||||
import time
|
|
||||||
import zipfile
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from getpass import getpass
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from plexapi import compat
|
from plexapi import compat
|
||||||
from plexapi.exceptions import NotFound
|
from plexapi.exceptions import NotFound
|
||||||
|
@ -281,20 +277,43 @@ def download(url, filename=None, savepath=None, session=None, chunksize=4024, un
|
||||||
|
|
||||||
|
|
||||||
def tag_helper(tag, items, locked=True, remove=False):
|
def tag_helper(tag, items, locked=True, remove=False):
|
||||||
"""Simple tag helper for editing a object."""
|
""" Simple tag helper for editing a object. """
|
||||||
if not isinstance(items, list):
|
if not isinstance(items, list):
|
||||||
items = [items]
|
items = [items]
|
||||||
|
data = {}
|
||||||
d = {}
|
|
||||||
if not remove:
|
if not remove:
|
||||||
for i, item in enumerate(items):
|
for i, item in enumerate(items):
|
||||||
tag_name = '%s[%s].tag.tag' % (tag, i)
|
tagname = '%s[%s].tag.tag' % (tag, i)
|
||||||
d[tag_name] = item
|
data[tagname] = item
|
||||||
|
|
||||||
if remove:
|
if remove:
|
||||||
tag_name = '%s[].tag.tag-' % tag
|
tagname = '%s[].tag.tag-' % tag
|
||||||
d[tag_name] = ','.join(items)
|
data[tagname] = ','.join(items)
|
||||||
|
data['%s.locked' % tag] = 1 if locked else 0
|
||||||
|
return data
|
||||||
|
|
||||||
d['%s.locked' % tag] = 1 if locked else 0
|
|
||||||
|
|
||||||
return d
|
def getMyPlexAccount(opts=None):
|
||||||
|
""" Helper function tries to get a MyPlex Account instance by checking
|
||||||
|
the the following locations for a username and password. This is
|
||||||
|
useful to create user-friendly command line tools.
|
||||||
|
1. command-line options (opts).
|
||||||
|
2. environment variables and config.ini
|
||||||
|
3. Prompt on the command line.
|
||||||
|
"""
|
||||||
|
from plexapi import CONFIG
|
||||||
|
from plexapi.myplex import MyPlexAccount
|
||||||
|
# 1. Check command-line options
|
||||||
|
if opts and opts.username and opts.password:
|
||||||
|
print('Authenticating with Plex.tv as %s..' % opts.username)
|
||||||
|
return MyPlexAccount(opts.username, opts.password)
|
||||||
|
# 2. Check Plexconfig (environment variables and config.ini)
|
||||||
|
config_username = CONFIG.get('auth.myplex_username')
|
||||||
|
config_password = CONFIG.get('auth.myplex_password')
|
||||||
|
if config_username and config_password:
|
||||||
|
print('Authenticating with Plex.tv as %s..' % config_username)
|
||||||
|
return MyPlexAccount(config_username, config_password)
|
||||||
|
# 3. Prompt for username and password on the command line
|
||||||
|
username = input('What is your plex.tv username: ')
|
||||||
|
password = getpass('What is your plex.tv password: ')
|
||||||
|
print('Authenticating with Plex.tv as %s..' % username)
|
||||||
|
return MyPlexAccount(username, password)
|
||||||
|
|
67
tools/plex-download.py
Executable file
67
tools/plex-download.py
Executable file
|
@ -0,0 +1,67 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Allows downloading a Plex media item from a local or shared library. You
|
||||||
|
may specify the item by the PlexWeb url (everything after !) or by
|
||||||
|
manually searching the items from the command line wizard.
|
||||||
|
|
||||||
|
Original contribution by lad1337.
|
||||||
|
"""
|
||||||
|
import argparse, re
|
||||||
|
from plexapi import utils
|
||||||
|
from plexapi.compat import unquote
|
||||||
|
from plexapi.video import Episode, Movie, Show
|
||||||
|
|
||||||
|
|
||||||
|
def choose(msg, items, attr):
|
||||||
|
print()
|
||||||
|
for index, i in enumerate(items):
|
||||||
|
name = attr(i) if callable(attr) else getattr(i, attr)
|
||||||
|
print(' %s: %s' % (index, name))
|
||||||
|
number = int(input('\n%s: ' % msg))
|
||||||
|
return items[number]
|
||||||
|
|
||||||
|
|
||||||
|
def search_for_item(url=None):
|
||||||
|
if url: return get_item_from_url(opts.url)
|
||||||
|
server = choose('Choose a Server', account.resources(), 'name').connect()
|
||||||
|
query = input('What are you looking for?: ')
|
||||||
|
item = choose('Choose result', server.search(query), lambda x: repr(x))
|
||||||
|
if isinstance(item, Show):
|
||||||
|
item = choose('Choose episode', item.episodes(), lambda x: x._prettyfilename())
|
||||||
|
if not isinstance(item, (Movie, Episode)):
|
||||||
|
raise SystemExit('Unable to download %s' % item.__class__.__name__)
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
def get_item_from_url(url):
|
||||||
|
# Parse the ClientID and Key from the URL
|
||||||
|
clientid = re.findall('[a-f0-9]{40}', url)
|
||||||
|
key = re.findall('key=(.*?)(&.*)?$', url)
|
||||||
|
if not clientid or not key:
|
||||||
|
raise SystemExit('Cannot parse URL: %s' % url)
|
||||||
|
clientid = clientid[0]
|
||||||
|
key = unquote(key[0][0])
|
||||||
|
# Connect to the server and fetch the item
|
||||||
|
servers = [r for r in account.resources() if r.clientIdentifier == clientid]
|
||||||
|
if len(servers) != 1:
|
||||||
|
raise SystemExit('Unknown or ambiguous client id: %s' % clientid)
|
||||||
|
server = servers[0].connect()
|
||||||
|
return server.fetchItem(key)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Command line parser
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('--username', help='Your Plex username')
|
||||||
|
parser.add_argument('--password', help='Your Plex password')
|
||||||
|
parser.add_argument('--url', default=None, help='Download from URL (only paste after !)')
|
||||||
|
opts = parser.parse_args()
|
||||||
|
# Search item to download
|
||||||
|
account = utils.getMyPlexAccount(opts)
|
||||||
|
item = search_for_item(opts.url)
|
||||||
|
# Download the item
|
||||||
|
print("Downloading '%s' from %s.." % (item._prettyfilename(), item._server.friendlyName))
|
||||||
|
filepaths = item.download('./')
|
||||||
|
for filepath in filepaths:
|
||||||
|
print(' %s' % filepath)
|
|
@ -8,45 +8,45 @@ and password. Alternatively, if you do not wish to enter your login
|
||||||
information below, you can retrieve the same information from plex.tv
|
information below, you can retrieve the same information from plex.tv
|
||||||
at the URL: https://plex.tv/api/resources?includeHttps=1
|
at the URL: https://plex.tv/api/resources?includeHttps=1
|
||||||
"""
|
"""
|
||||||
from getpass import getpass
|
import argparse
|
||||||
from plexapi import utils
|
from plexapi import utils
|
||||||
from plexapi.exceptions import BadRequest
|
from plexapi.exceptions import BadRequest
|
||||||
from plexapi.myplex import MyPlexAccount, _connect
|
from plexapi.myplex import _connect
|
||||||
from plexapi.server import PlexServer
|
from plexapi.server import PlexServer
|
||||||
|
|
||||||
FORMAT = ' %-17s %-25s %-20s %s'
|
|
||||||
FORMAT2 = ' %-17s %-25s %-20s %-30s (%s)'
|
|
||||||
SERVER = 'Plex Media Server'
|
SERVER = 'Plex Media Server'
|
||||||
|
FORMAT = '%-8s %-6s %-17s %-25s %-20s %s (%s)'
|
||||||
|
|
||||||
|
|
||||||
def _list_resources(account, servers):
|
def _list_resources(account, servers):
|
||||||
print('\nHTTPS Resources:')
|
items = []
|
||||||
resources = MyPlexAccount(username, password).resources()
|
print('Finding Plex resources..')
|
||||||
for r in resources:
|
resources = account.resources()
|
||||||
if r.accessToken:
|
for r in [r for r in resources if r.accessToken]:
|
||||||
for connection in r.connections:
|
for connection in r.connections:
|
||||||
print(FORMAT % (r.product, r.name, r.accessToken, connection.uri))
|
local = 'Local' if connection.local else 'Remote'
|
||||||
servers[connection.uri] = r.accessToken
|
extras = [r.provides]
|
||||||
print('\nDirect Resources:')
|
items.append(FORMAT % ('Resource', local, r.product, r.name, r.accessToken, connection.uri, ','.join(extras)))
|
||||||
for r in resources:
|
items.append(FORMAT % ('Resource', local, r.product, r.name, r.accessToken, connection.httpuri, ','.join(extras)))
|
||||||
if r.accessToken:
|
servers[connection.httpuri] = r.accessToken
|
||||||
for connection in r.connections:
|
servers[connection.uri] = r.accessToken
|
||||||
print(FORMAT % (r.product, r.name, r.accessToken, connection.httpuri))
|
return items
|
||||||
servers[connection.httpuri] = r.accessToken
|
|
||||||
|
|
||||||
|
|
||||||
def _list_devices(account, servers):
|
def _list_devices(account, servers):
|
||||||
print('\nDevices:')
|
items = []
|
||||||
for d in MyPlexAccount(username, password).devices():
|
print('Finding Plex devices..')
|
||||||
if d.token:
|
for d in [d for d in account.devices() if d.token]:
|
||||||
for conn in d.connections:
|
for connection in d.connections:
|
||||||
print(FORMAT % (d.product, d.name, d.token, conn))
|
extras = [d.provides]
|
||||||
servers[conn] = d.token
|
items.append(FORMAT % ('Device', '--', d.product, d.name, d.token, connection, ','.join(extras)))
|
||||||
|
servers[connection] = d.token
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
def _test_servers(servers):
|
def _test_servers(servers):
|
||||||
seen = set()
|
items, seen = [], set()
|
||||||
print('\nServer Clients:')
|
print('Finding Plex clients..')
|
||||||
listargs = [[PlexServer, s, t, 5] for s,t in servers.items()]
|
listargs = [[PlexServer, s, t, 5] for s,t in servers.items()]
|
||||||
results = utils.threaded(_connect, listargs)
|
results = utils.threaded(_connect, listargs)
|
||||||
for url, token, plex, runtime in results:
|
for url, token, plex, runtime in results:
|
||||||
|
@ -54,19 +54,38 @@ def _test_servers(servers):
|
||||||
if plex and clients:
|
if plex and clients:
|
||||||
for c in plex.clients():
|
for c in plex.clients():
|
||||||
if c._baseurl not in seen:
|
if c._baseurl not in seen:
|
||||||
print(FORMAT2 % (c.product, c.title, token, c._baseurl, plex.friendlyName))
|
extras = [plex.friendlyName] + c.protocolCapabilities
|
||||||
|
items.append(FORMAT % ('Client', '--', c.product, c.title, token, c._baseurl, ','.join(extras)))
|
||||||
seen.add(c._baseurl)
|
seen.add(c._baseurl)
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
def _print_items(items, _filter=None):
|
||||||
|
if _filter:
|
||||||
|
print('Displaying items matching filter: %s' % _filter)
|
||||||
|
print()
|
||||||
|
for item in items:
|
||||||
|
filtered_out = False
|
||||||
|
for f in _filter.split():
|
||||||
|
if f.lower() not in item.lower():
|
||||||
|
filtered_out = True
|
||||||
|
if not filtered_out:
|
||||||
|
print(item)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print(__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
username = input('What is your plex.tv username: ')
|
parser.add_argument('--username', help='Your Plex username')
|
||||||
password = getpass('What is your plex.tv password: ')
|
parser.add_argument('--password', help='Your Plex password')
|
||||||
|
parser.add_argument('--filter', default='', help='Only display items containing specified filter')
|
||||||
|
opts = parser.parse_args()
|
||||||
try:
|
try:
|
||||||
servers = {}
|
servers = {}
|
||||||
account = MyPlexAccount(username, password)
|
account = utils.getMyPlexAccount(opts)
|
||||||
_list_resources(account, servers)
|
items = _list_resources(account, servers)
|
||||||
_list_devices(account, servers)
|
items += _list_devices(account, servers)
|
||||||
_test_servers(servers)
|
items += _test_servers(servers)
|
||||||
|
_print_items(items, opts.filter)
|
||||||
except BadRequest as err:
|
except BadRequest as err:
|
||||||
print('Unable to login to plex.tv: %s' % err)
|
print('Unable to login to plex.tv: %s' % err)
|
||||||
|
|
Loading…
Reference in a new issue