forked from pbrink231/plex_top_playlists
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplex_playlist_update.py
executable file
·378 lines (313 loc) · 13.9 KB
/
plex_playlist_update.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
#!/usr/bin/python
# a python test script
# -*- coding: utf-8 -*-
#------------------------------------------------------------------------------
#
# Automated Ranked Playlists - RugbyHunter231
#
# *** Use at your own risk! ***
# *** I am not responsible for damages to your Plex server or libraries. ***
#
#------------------------------------------------------------------------------
import json
import os
import requests
import subprocess
import time
import xmltodict
import ConfigParser
from lxml.html import parse
from plexapi.server import PlexServer
from plexapi.utils import NA
from urllib2 import Request, urlopen
config_file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'settings.ini')
config = ConfigParser.SafeConfigParser()
config.read(config_file_path)
PLEX_URL = config.get('Plex', 'plex-host')
PLEX_TOKEN = config.get('Plex', 'plex-token')
MOVIE_LIBRARY_NAME = config.get('Plex', 'movie-library')
SHOW_LIBRARY_NAME = config.get('Plex', 'tv-library')
REMOVE_ONLY = config.getboolean('Plex', 'remove')
SYNC_WITH_SHARED_USERS = config.getboolean('Plex', 'shared')
ALLOW_SYNCED_USERS = config.get('Plex', 'users')
TRAKT_API_KEY = config.get('Trakt', 'api-key')
TRAKT_NUM_MOVIES = config.get('Trakt', 'movie-total')
TRAKT_WEEKLY_PLAYLIST_NAME = config.get('Trakt', 'weekly-movie-name')
TRAKT_POPULAR_PLAYLIST_NAME = config.get('Trakt', 'popular-movie-name')
TRAKT_NUM_SHOWS = config.get('Trakt', 'tv-total')
TRAKT_WEEKLY_SHOW_PLAYLIST_NAME = config.get('Trakt', 'weekly-tv-name')
TRAKT_POPULAR_SHOW_PLAYLIST_NAME = config.get('Trakt', 'popular-tv-name')
IMDB_CHART_URL = config.get('IMDb', 'chart-url')
IMDB_SEARCH_URL = ''.join([config.get('IMDb', 'search-url'),'&count=',config.get('IMDb', 'search-total')])
IMDB_PLAYLIST_NAME = config.get('IMDb', 'playlist-name')
IMDB_SEARCH_NAME = config.get('IMDb', 'search-list-name')
IMDB_CUSTOM_URL = config.get('IMDb', 'list-url')
IMDB_CUSTOM_LIST = config.get('IMDb', 'list-name')
####### CODE HERE (Nothing to change) ############
def get_user_tokens(server_id):
headers = {'Accept': 'application/json', 'X-Plex-Token': PLEX_TOKEN}
result = requests.get('https://plex.tv/api/servers/{server_id}/shared_servers?X-Plex-Token={token}'.format(server_id=server_id, token=PLEX_TOKEN), headers=headers)
xmlData = xmltodict.parse(result.content)
users = {user['@username']: user['@accessToken'] for user in xmlData['MediaContainer']['SharedServer']}
return users
def remove_playlist(plex, playlist_name):
for playlist in plex.playlists():
if playlist.title == playlist_name:
try:
playlist.delete()
#print("{}: Playlist deleted".format(playlist_name))
except:
print("ERROR - cannot delete playlist: {}".format(playlist_name))
return None
def create_playlists(plex, list, playlist_name):
# Remove old playlists
#print('{}: Checking if playlist exist to delete if needed'.format(playlist_name))
remove_playlist(plex, playlist_name)
plex.createPlaylist(playlist_name, list)
#print("{}: playlist created".format(playlist_name))
def loop_plex_users(plex, list, playlist_name):
#update my list
create_playlists(plex, list, playlist_name)
#update list for shared users
if SYNC_WITH_SHARED_USERS:
plex_users = get_user_tokens(plex.machineIdentifier)
for user in plex_users:
if not ALLOW_SYNCED_USERS or user in ALLOW_SYNCED_USERS:
print("{}: updating playlist for user {}".format(playlist_name, user))
user_token = plex_users[user]
user_plex = PlexServer(PLEX_URL, user_token)
create_playlists(user_plex, list, playlist_name)
else:
print("Skipping adding to shared users")
def setup_show_playlist(plex, tvdb_ids, plex_shows, playlist_name):
if tvdb_ids:
# Create a list of matching shows using last episode
print("{}: finding matching movies for playlist with count {}".format(playlist_name, len(tvdb_ids)))
matching_episodes = []
sorted_shows = []
for show in plex_shows:
last_episode = show.episodes()[-1]
if last_episode.guid != NA and 'thetvdb://' in last_episode.guid:
tvdb_id = last_episode.guid.split('thetvdb://')[1].split('?')[0].split('/')[0]
else:
tvdb_id = None
if tvdb_id and tvdb_id in tvdb_ids:
matching_episodes.append(last_episode)
print("{}: Sorting list in correct order".format(playlist_name))
for tvdb_id in tvdb_ids:
for episode in matching_episodes:
show_tvdb_id = episode.guid.split('thetvdb://')[1].split('?')[0].split('/')[0]
if show_tvdb_id == tvdb_id:
sorted_shows.append(episode)
break;
print("{}: Created shows list".format(playlist_name))
loop_plex_users(plex, sorted_shows, playlist_name)
else:
print('{}: WARNING - Playlist is empty'.format(playlist_name))
def setup_movie_playlist(plex, imdb_ids, plex_movies, playlist_name):
# check that the list is not empty
if imdb_ids:
# Create a list of matching movies
print("{}: finding matching movies for playlist with count {}".format(playlist_name, len(imdb_ids)))
matching_movies = []
sorted_movies = []
for movie in plex_movies:
if movie.guid != NA and 'imdb://' in movie.guid:
imdb_id = movie.guid.split('imdb://')[1].split('?')[0]
else:
imdb_id = None
if imdb_id and imdb_id in imdb_ids:
matching_movies.append(movie)
print("{}: Sorting list in correct order".format(playlist_name))
for imdb_id in imdb_ids:
for movie in matching_movies:
movie_imdb_id = movie.guid.split('imdb://')[1].split('?')[0]
if movie_imdb_id == imdb_id:
sorted_movies.append(movie)
break;
print("{}: Created movie list".format(playlist_name))
loop_plex_users(plex, sorted_movies, playlist_name)
else:
print('{}: WARNING - Playlist is empty'.format(playlist_name))
def trakt_watched_imdb_id_list():
# Get the weekly watched list
print("Retrieving the Trakt weekly list...")
imdb_ids = []
headers = {
'Content-Type': 'application/json',
'trakt-api-version': '2',
'trakt-api-key': TRAKT_API_KEY
}
request = Request('https://api.trakt.tv/movies/watched/weekly?page=1&limit={}'.format(TRAKT_NUM_MOVIES), headers=headers)
try:
response = urlopen(request)
trakt_movies = json.load(response)
# loop through movies and add movies to list if match
for movie in trakt_movies:
imdb_ids.append(movie['movie']['ids']['imdb'])
except:
print "Bad Trakt Code"
return []
return imdb_ids
def trakt_popular_imdb_id_list():
# Get the weekly watched list
print("Retrieving the Trakt popular list...")
imdb_ids = []
headers = {
'Content-Type': 'application/json',
'trakt-api-version': '2',
'trakt-api-key': TRAKT_API_KEY
}
try:
request = Request('https://api.trakt.tv/movies/popular?page=1&limit={}'.format(TRAKT_NUM_MOVIES), headers=headers)
response = urlopen(request)
trakt_movies = json.load(response)
# loop through movies and add movies to list if match
for movie in trakt_movies:
imdb_ids.append(movie['ids']['imdb'])
except:
print "Bad Trakt Code"
return []
return imdb_ids
def trakt_watched_show_imdb_id_list():
# Get the weekly watched list
print("Retrieving the Trakt weekly list...")
tvdb_ids = []
headers = {
'Content-Type': 'application/json',
'trakt-api-version': '2',
'trakt-api-key': TRAKT_API_KEY
}
try:
request = Request('https://api.trakt.tv/shows/watched/weekly?page=1&limit={}'.format(TRAKT_NUM_SHOWS), headers=headers)
response = urlopen(request)
trakt_show = json.load(response)
# loop through movies and add movies to list if match
for show in trakt_show:
tvdb_ids.append(str(show['show']['ids']['tvdb']))
except:
print "Bad Trakt Code"
return []
return tvdb_ids
def trakt_popular_show_imdb_id_list():
# Get the weekly watched list
print("Retrieving the Trakt popular list...")
tvdb_ids = []
headers = {
'Content-Type': 'application/json',
'trakt-api-version': '2',
'trakt-api-key': TRAKT_API_KEY
}
try:
request = Request('https://api.trakt.tv/shows/popular?page=1&limit={}'.format(TRAKT_NUM_SHOWS), headers=headers)
response = urlopen(request)
trakt_show = json.load(response)
# loop through movies and add movies to list if match
for show in trakt_show:
tvdb_ids.append(str(show['ids']['tvdb']))
except:
print "Bad Trakt Code"
return []
return tvdb_ids
def imdb_top_imdb_id_list(list_url):
# Get the IMDB Top 250 list
print("Retrieving the IMDB Top 250 list...")
tree = parse(list_url)
top_250_ids = tree.xpath("//table[contains(@class, 'chart')]//td[@class='ratingColumn']/div//@data-titleid")
return top_250_ids
def imdb_search_list(search_url):
# Get the IMDB Search list
print("Retrieving the IMDB Search list...")
tree = parse(search_url)
search_ids = tree.xpath("//img[@class='loadlate']/@data-tconst")
return search_ids
def imdb_custom_list(custom_url):
# Get the IMDB Custom list
print("Retrieving the IMDB Custom list...")
tree = parse(custom_url)
custom_ids = tree.xpath("//div[contains(@class, 'hover-over-image')]/@data-const")
return custom_ids
def run_movies_lists(plex):
# Get list of movies from the Plex server
print("Retrieving a list of movies from the '{library}' library in Plex...".format(library=MOVIE_LIBRARY_NAME))
try:
movie_library = plex.library.section(MOVIE_LIBRARY_NAME)
all_movies = movie_library.all()
except:
print("The '{library}' library does not exist in Plex.".format(library=MOVIE_LIBRARY_NAME))
print("Exiting script.")
return [], 0
print("Retrieving new lists")
if TRAKT_API_KEY:
trakt_weekly_imdb_ids = trakt_watched_imdb_id_list()
trakt_popular_imdb_ids = trakt_popular_imdb_id_list()
setup_movie_playlist(plex, trakt_weekly_imdb_ids, all_movies, TRAKT_WEEKLY_PLAYLIST_NAME)
setup_movie_playlist(plex, trakt_popular_imdb_ids, all_movies, TRAKT_POPULAR_PLAYLIST_NAME)
else:
print("No Trakt API key, skipping lists")
imdb_top_movies_ids = imdb_top_imdb_id_list(IMDB_CHART_URL)
imdb_search_movies_ids = imdb_search_list(IMDB_SEARCH_URL)
imdb_custom_movies_ids = imdb_custom_list(IMDB_CUSTOM_URL)
setup_movie_playlist(plex, imdb_top_movies_ids, all_movies, IMDB_PLAYLIST_NAME)
setup_movie_playlist(plex, imdb_search_movies_ids, all_movies, IMDB_SEARCH_NAME)
setup_movie_playlist(plex, imdb_custom_movies_ids, all_movies, IMDB_CUSTOM_LIST)
def run_show_lists(plex):
# Get list of shows from the Plex server
print("Retrieving a list of movies from the '{library}' library in Plex...".format(library=SHOW_LIBRARY_NAME))
try:
show_library = plex.library.section(SHOW_LIBRARY_NAME)
all_shows = show_library.all()
except:
print("The '{library}' library does not exist in Plex.".format(library=SHOW_LIBRARY_NAME))
print("Exiting script.")
return [], 0
print("Retrieving new lists")
if TRAKT_API_KEY:
trakt_weekly_show_imdb_ids = trakt_watched_show_imdb_id_list()
trakt_popular_show_imdb_ids = trakt_popular_show_imdb_id_list()
setup_show_playlist(plex, trakt_weekly_show_imdb_ids, all_shows, TRAKT_WEEKLY_SHOW_PLAYLIST_NAME)
setup_show_playlist(plex, trakt_popular_show_imdb_ids, all_shows, TRAKT_POPULAR_SHOW_PLAYLIST_NAME)
else:
print("No Trakt API key, skipping lists")
def list_remover(plex, playlist_name):
#update my list
print("{}: removing playlist for script user".format(playlist_name))
remove_playlist(plex, playlist_name)
#update list for shared users
if SYNC_WITH_SHARED_USERS:
plex_users = get_user_tokens(plex.machineIdentifier)
for user in plex_users:
if not ALLOW_SYNCED_USERS or user in ALLOW_SYNCED_USERS:
print("{}: removing playlist for user {}".format(playlist_name, user))
user_token = plex_users[user]
user_plex = PlexServer(PLEX_URL, user_token)
remove_playlist(user_plex, playlist_name)
else:
print("Skipping removal from shared users")
def list_updater():
try:
plex = PlexServer(PLEX_URL, PLEX_TOKEN)
except:
print("No Plex server found at: {base_url} or bad plex token code".format(base_url=PLEX_URL))
print("Exiting script.")
raw_input("press enter to exit")
return [], 0
if REMOVE_ONLY:
list_remover(plex, TRAKT_WEEKLY_PLAYLIST_NAME)
list_remover(plex, TRAKT_POPULAR_PLAYLIST_NAME)
list_remover(plex, TRAKT_WEEKLY_SHOW_PLAYLIST_NAME)
list_remover(plex, TRAKT_POPULAR_SHOW_PLAYLIST_NAME)
list_remover(plex, IMDB_PLAYLIST_NAME)
list_remover(plex, IMDB_SEARCH_NAME)
list_remover(plex, IMDB_CUSTOM_LIST)
else:
run_movies_lists(plex)
run_show_lists(plex)
if __name__ == "__main__":
print("===================================================================")
print(" Automated Playlist to Plex script ")
print("===================================================================\n")
list_updater()
print("\n===================================================================")
print(" Done! ")
print("===================================================================\n")