Skip to content

Commit eae4f52

Browse files
committed
Start work on plex-backupwatched
1 parent 9d4966d commit eae4f52

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

tools/plex-backupwatched.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Backup and restore the watched status of Plex libraries to a json file.
5+
"""
6+
import argparse, json
7+
from collections import defaultdict
8+
from plexapi import utils
9+
10+
SECTIONS = ('movie', 'show')
11+
12+
13+
def _find_server(account, servername=None):
14+
""" Find and return a PlexServer object. """
15+
servers = servers = [s for s in account.resources() if 'server' in s.provides]
16+
# If servername specified find and return it
17+
if servername is not None:
18+
for server in servers:
19+
if server.name == servername:
20+
return server.connect()
21+
raise SystemExit('Unknown server name: %s' % servername)
22+
# If servername not specified; allow user to choose
23+
return utils.choose('Choose a Server', servers, 'name').connect()
24+
25+
26+
def _item_key(item):
27+
if item.type == 'movie':
28+
return '%s.%s' % (item._prettyfilename().lower(), item.year)
29+
elif item.type == 'episode':
30+
return '%s.%s' % (item._prettyfilename().lower(), item.year)
31+
32+
33+
def _iter_sections(plex, opts):
34+
libraries = opts.libraries.split(',') if opts.libraries else []
35+
libraries = [l.strip().lower() for l in libraries]
36+
for section in plex.library.sections():
37+
title = section.title.lower()
38+
if section.type in SECTIONS and (not libraries or title in libraries):
39+
yield section
40+
41+
42+
def _iter_items(section):
43+
if section.type == 'movie':
44+
for movie in section.all():
45+
yield movie
46+
elif section.type == 'show':
47+
for show in section.all():
48+
for episode in show.episodes():
49+
yield episode
50+
51+
52+
def backup_watched(plex, opts):
53+
""" Backup watched status to the specified filepath. """
54+
data = defaultdict(lambda: dict())
55+
for section in _iter_sections(plex, opts):
56+
print('Fetching watched status for %s..' % section.title)
57+
skey = section.title.lower()
58+
for item in _iter_items(section):
59+
if not opts.watchedonly or item.isWatched:
60+
ikey = _item_key(item)
61+
data[skey][ikey] = item.isWatched
62+
import pprint; pprint.pprint(item.__dict__); break
63+
print('Writing backup file to %s' % opts.filepath)
64+
with open(opts.filepath, 'w') as handle:
65+
json.dump(dict(data), handle, sort_keys=True, indent=2)
66+
67+
68+
def restore_watched(plex, opts):
69+
""" Restore watched status from the specified filepath. """
70+
with open(opts.filepath, 'r') as handle:
71+
source = json.load(handle)
72+
# Find the differences
73+
differences = defaultdict(lambda: dict())
74+
for section in _iter_sections(plex, opts):
75+
print('Finding differences in %s..' % section.title)
76+
skey = section.title.lower()
77+
for item in _iter_items(section):
78+
ikey = _item_key(item)
79+
sval = source.get(skey,{}).get(ikey)
80+
if sval is None:
81+
raise SystemExit('%s not found' % ikey)
82+
if (sval is not None and item.isWatched != sval) and (not opts.watchedonly or sval):
83+
differences[skey][ikey] = {'isWatched':sval, 'item':item}
84+
print('Applying %s differences to destination' % len(differences))
85+
import pprint; pprint.pprint(differences)
86+
87+
88+
if __name__ == '__main__':
89+
from plexapi import CONFIG
90+
parser = argparse.ArgumentParser(description=__doc__)
91+
parser.add_argument('action', help='Action to perform: backup or restore', choices=('backup', 'restore'))
92+
parser.add_argument('filepath', help='File path to backup to or restore from')
93+
parser.add_argument('-u', '--username', default=CONFIG.get('auth.myplex_username'), help='Plex username')
94+
parser.add_argument('-p', '--password', default=CONFIG.get('auth.myplex_password'), help='Plex password')
95+
parser.add_argument('-s', '--servername', help='Plex server name')
96+
parser.add_argument('-w', '--watchedonly', default=False, action='store_true', help='Only backup or restore watched items.')
97+
parser.add_argument('-l', '--libraries', help='Only backup or restore the specified libraries (comma seperated).')
98+
opts = parser.parse_args()
99+
account = utils.getMyPlexAccount(opts)
100+
plex = _find_server(account, opts.servername)
101+
action = backup_watched if opts.action == 'backup' else restore_watched
102+
action(plex, opts)

0 commit comments

Comments
 (0)