forked from trakt/script.trakt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tagging.py
1294 lines (1083 loc) · 41.2 KB
/
tagging.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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
import xbmc
import xbmcaddon
import xbmcgui
import utilities as utils
import copy
from time import time
from traktapi import traktAPI
__addon__ = xbmcaddon.Addon("script.trakt")
TAG_PREFIX = "trakt.tv - "
PRIVACY_LIST = ['public', 'friends', 'private']
def isTaggingEnabled():
return utils.getSettingAsBool('tagging_enable')
def isWatchlistsEnabled():
return utils.getSettingAsBool('tagging_watchlists')
def isRatingsEnabled():
return utils.getSettingAsBool('tagging_ratings')
def getMinRating():
return utils.getSettingAsInt('tagging_ratings_min')
def tagToList(tag):
return tag.replace(TAG_PREFIX, "", 1)
def listToTag(list):
return "%s%s" % (TAG_PREFIX, list)
def ratingToTag(rating):
return "%sRating: %s" % (TAG_PREFIX, rating)
def isTraktList(tag):
return True if tag.startswith(TAG_PREFIX) else False
def hasTraktWatchlistTag(tags):
watchlist_tag = False
for tag in tags:
if isTraktList(tag):
_tag = tagToList(tag)
if _tag.lower() == "watchlist":
watchlist_tag = True
break
return watchlist_tag
def getTraktRatingTag(tags):
for tag in tags:
if isTraktList(tag):
_tag = tagToList(tag)
if _tag.lower().startswith("rating:"):
return tag
return None
def hasTraktRatingTag(tags):
return not getTraktRatingTag(tags) is None
def isTraktRatingTag(tag):
if isTraktList(tag):
_tag = tagToList(tag)
return _tag.lower().startswith("rating:")
return False
def xbmcSetTags(id, type, title, tags):
if not (utils.isMovie(type) or utils.isShow(type)):
return
req = None
if utils.isMovie(type):
req = {"jsonrpc": "2.0", "id": 1, "method": "VideoLibrary.SetMovieDetails", "params": {"movieid" : id, "tag": tags}}
elif utils.isShow(type):
req = {"jsonrpc": "2.0", "id": 1, "method": "VideoLibrary.SetTVShowDetails", "params": {"tvshowid" : id, "tag": tags}}
if utils.getSettingAsBool('simulate_tagging'):
utils.Debug("[Tagger] %s" % str(req))
return True
else:
result = utils.xbmcJsonRequest(req)
if result == "OK":
utils.Debug("[Tagger] XBMC tags for '%s' were updated with '%s'." % (title, str(tags)))
return True
return False
class Tagger():
traktLists = None
traktListData = None
traktListsLast = 0
def __init__(self, api=None):
if api is None:
api = traktAPI(loadSettings=False)
self.traktapi = api
self.updateSettings()
def updateSettings(self):
self._enabled = utils.getSettingAsBool('tagging_enable')
self._watchlists = utils.getSettingAsBool('tagging_watchlists')
self._ratings = utils.getSettingAsBool('tagging_ratings')
self._ratingMin = utils.getSettingAsInt('tagging_ratings_min')
self.simulate = utils.getSettingAsBool('simulate_tagging')
if self.simulate:
utils.Debug("[Tagger] Tagging is configured to be simulated.")
def xbmcLoadData(self, tags=False):
data = {'movies': [], 'tvshows': []}
props = ['title', 'imdbnumber', 'year']
if tags:
props.append('tag')
m = {'method': 'VideoLibrary.GetMovies', 'props': props}
s = {'method': 'VideoLibrary.GetTVShows', 'props': props}
params = {'movies': m, 'tvshows': s}
for type in params:
utils.Debug("[Tagger] Getting '%s' from XBMC." % type)
xbmc_data = utils.xbmcJsonRequest({'jsonrpc': '2.0', 'id': 0, 'method': params[type]['method'], 'params': {'properties': params[type]['props']}})
if not xbmc_data:
utils.Debug("[Tagger] XBMC JSON request was empty.")
return False
if not type in xbmc_data:
utils.Debug("[Tagger] Key '%s' not found." % type)
return False
data[type] = xbmc_data[type]
utils.Debug("[Tagger] XBMC JSON Result: '%s'" % str(data[type]))
db_field = 'tmdb_id' if type == 'movies' else 'tvdb_id'
for item in data[type]:
item['type'] = 'movie' if type == 'movies' else 'show'
id = item['imdbnumber']
item['imdb_id'] = id if id.startswith("tt") else ""
item[db_field] = unicode(id) if id.isdigit() else ""
del(item['imdbnumber'])
del(item['label'])
data['shows'] = data.pop('tvshows')
self.xbmcData = data
return True
def xbmcBuildTagList(self):
data = {}
for type in ['movies', 'shows']:
for index in range(len(self.xbmcData[type])):
item = self.xbmcData[type][index]
for tag in item['tag']:
if isTraktList(tag):
listName = tagToList(tag)
if not listName in data:
data[listName] = {'movies': [], 'shows': []}
data[listName][type].append(index)
return data
def getTraktLists(self, force=False):
if force or self.traktListData is None or self.traktLists is None or (time() - self.traktListsLast) > (60 * 10):
utils.Debug("[Tagger] Getting lists from trakt.tv")
data = self.traktapi.getUserLists()
if not isinstance(data, list):
utils.Debug("[Tagger] Invalid trakt.tv lists, possible error getting data from trakt.")
return False
lists = {}
list_data = {}
hidden_lists = utils.getSettingAsList('tagging_hidden_lists')
for item in data:
lists[item['name']] = item['slug']
del(item['url'])
list_data[item['slug']] = copy.deepcopy(item)
list_data[item['slug']]['hide'] = item['slug'] in hidden_lists
self.traktLists = lists
self.traktListData = list_data
self.traktListsLast = time()
else:
utils.Debug("[Tagger] Using cached lists.")
return self.traktLists
def getTraktListData(self):
data = {}
utils.Debug("[Tagger] Getting list data from trakt.tv")
lists = self.getTraktLists(force=True)
if not lists:
utils.Debug("[Tagger] No lists at trakt.tv, nothing to retrieve.")
return {}
for listName in lists:
slug = lists[listName]
data[listName] = {'movies': [], 'shows': []}
utils.Debug("[Tagger] Getting list data for list slug '%s'." % slug)
listdata = self.traktapi.getUserList(slug)
if not isinstance(listdata, dict):
utils.Debug("[Tagger] Invalid trakt.tv list data, possible error getting data from trakt.")
return None
for item in listdata['items']:
type = 'movies' if item['type'] == 'movie' else 'shows'
f = utils.findMovie if type == 'movies' else utils.findShow
i = f(item[item['type']], self.xbmcData[type], returnIndex=True)
if not i is None:
data[listName][type].append(i)
return data
def getTraktWatchlistData(self):
data = {}
utils.Debug("[Tagger] Getting watchlist data from trakt.tv")
w = {}
w['movies'] = self.traktapi.getWatchlistMovies()
w['shows'] = self.traktapi.getWatchlistShows()
if isinstance(w['movies'], list) and isinstance(w['shows'], list):
data['Watchlist'] = {'movies': [], 'shows': []}
for type in w:
f = utils.findMovie if type == 'movies' else utils.findShow
for item in w[type]:
i = f(item, self.xbmcData[type], returnIndex=True)
if not i is None:
data['Watchlist'][type].append(i)
else:
utils.Debug("[Tagger] There was a problem getting your watchlists.")
return None
return data
def getTraktRatingData(self):
data = {}
utils.Debug("[Tagger] Getting rating data from trakt.tv")
r = {}
r['movies'] = self.traktapi.getRatedMovies()
r['shows'] = self.traktapi.getRatedShows()
if isinstance(r['movies'], list) and isinstance(r['shows'], list):
for i in range(self._ratingMin, 11):
listName = "Rating: %s" % i
data[listName] = {'movies': [], 'shows': []}
for type in r:
f = utils.findMovie if type == 'movies' else utils.findShow
for item in r[type]:
if item['rating_advanced'] >= self._ratingMin:
i = f(item, self.xbmcData[type], returnIndex=True)
if not i is None:
listName = "Rating: %s" % item['rating_advanced']
data[listName][type].append(i)
else:
utils.Debug("[Tagger] There was a problem getting your rated movies or shows.")
return None
return data
def sanitizeTraktParams(self, data):
newData = copy.deepcopy(data)
for item in newData:
if 'imdb_id' in item and not item['imdb_id']:
del(item['imdb_id'])
if 'tmdb_id' in item and not item['tmdb_id']:
del(item['tmdb_id'])
if 'tvdb_id' in item and not item['tvdb_id']:
del(item['tvdb_id'])
if 'tvshowid' in item:
del(item['tvshowid'])
if 'movieid' in item:
del(item['movieid'])
if 'tag' in item:
del(item['tag'])
return newData
def isListOnTrakt(self, list):
lists = self.getTraktLists()
return list in lists
def getSlug(self, list):
if self.isListOnTrakt(list):
return self.getTraktLists[list]
return None
def xbmcUpdateTags(self, data):
chunked = utils.chunks([{"jsonrpc": "2.0", "id": 1, "method": "VideoLibrary.SetMovieDetails", "params": {"movieid" : movie, "tag": data['movies'][movie]}} for movie in data['movies']], 50)
chunked.extend(utils.chunks([{"jsonrpc": "2.0", "id": 1, "method": "VideoLibrary.SetTVShowDetails", "params": {"tvshowid" : show, "tag": data['shows'][show]}} for show in data['shows']], 50))
for chunk in chunked:
if self.simulate:
utils.Debug("[Tagger] %s" % str(chunk))
else:
utils.xbmcJsonRequest(chunk)
def traktListAddItem(self, list, data):
if not list:
utils.Debug("[Tagger] No list provided.")
return
if not data:
utils.Debug("[Tagger] Nothing to add to trakt lists")
return
params = {}
params['items'] = self.sanitizeTraktParams(data)
if self.simulate:
utils.Debug("[Tagger] '%s' adding '%s'" % (list, str(params)))
else:
if self.isListOnTrakt(list):
slug = self.traktLists[list]
params['slug'] = slug
else:
list_privacy = utils.getSettingAsInt('tagging_list_privacy')
allow_shouts = utils.getSettingAsBool('tagging_list_allowshouts')
utils.Debug("[Tagger] Creating new list '%s'" % list)
result = self.traktapi.userListAdd(list, PRIVACY_LIST[list_privacy], allow_shouts=allow_shouts)
if result and 'status' in result and result['status'] == 'success':
slug = result['slug']
params['slug'] = slug
self.traktLists[list] = slug
else:
utils.Debug("[Tagger] There was a problem create the list '%s' on trakt.tv" % list)
return
utils.Debug("[Tagger] Adding to list '%s', items '%s'" % (list, str(params['items'])))
self.traktapi.userListItemAdd(params)
def traktListRemoveItem(self, list, data):
if not list:
utils.Debug("[Tagger] No list provided.")
return
if not data:
utils.Debug("[Tagger] Nothing to remove from trakt list.")
return
if not self.isListOnTrakt(list):
utils.Debug("[Tagger] Trying to remove items from non-existant list '%s'." % list)
slug = self.traktLists[list]
params = {'slug': slug}
params['items'] = self.sanitizeTraktParams(data)
if self.simulate:
utils.Debug("[Tagger] '%s' removing '%s'" % (list, str(params)))
else:
self.traktapi.userListItemDelete(params)
def updateWatchlist(self, data, remove=False):
movie_params = []
show_params = []
for item in data:
if utils.isMovie(item['type']):
movie = {}
movie['title'] = item['title']
if 'imdb_id' in item:
movie['imdb_id'] = item['imdb_id']
if 'tmdb_id' in data:
movie['tmdb_id'] = item['tmdb_id']
movie['year'] = item['year']
movie_params.append(movie)
elif utils.isShow(item['type']):
show = {}
show['title'] = item['title']
if 'imdb_id' in item:
show['imdb_id'] = item['imdb_id']
if 'tvdb_id' in item:
show['tvdb_id'] = item['tvdb_id']
show_params.append(show)
if movie_params:
params = {'movies': movie_params}
if self.simulate:
utils.Debug("[Tagger] Movie watchlist %s '%s'." % ("remove" if remove else "add", str(params)))
else:
if not remove:
self.traktapi.watchlistAddMovies(params)
else:
self.traktapi.watchlistRemoveMovies(params)
if show_params:
params = {'shows': show_params}
if self.simulate:
utils.Debug("[Tagger] Show watchlist %s '%s'." % ("remove" if remove else "add", str(params)))
else:
if not remove:
self.traktapi.watchlistAddShows(params)
else:
self.traktapi.watchlistRemoveShows(params)
def isAborted(self):
if xbmc.abortRequested:
utils.Debug("[Tagger] XBMC abort requested, stopping.")
return true
def updateTagsFromTrakt(self):
if not self._enabled:
utils.Debug("[Tagger] Tagging is not enabled, aborting.")
return
utils.Debug("[Tagger] Starting List/Tag synchronization.")
if utils.getSettingAsBool('tagging_notifications'):
utils.notification(utils.getString(1201), utils.getString(1658))
tStart = time()
if not self.xbmcLoadData(tags=True):
utils.Debug("[Tagger] Problem loading XBMC data, aborting.")
utils.notification(utils.getString(1201), utils.getString(1662))
return
tTaken = time() - tStart
utils.Debug("[Tagger] Time to load data from XBMC: %0.3f seconds." % tTaken)
tStart = time()
xbmc_lists = self.xbmcBuildTagList()
tTaken = time() - tStart
utils.Debug("[Tagger] Time to load build list from XBMC data: %0.3f seconds." % tTaken)
if self.isAborted():
return
trakt_lists = {}
tStart = time()
trakt_lists = self.getTraktListData()
if trakt_lists is None:
utils.Debug("[Tagger] Problem getting list data, aborting.")
utils.notification(utils.getString(1201), utils.getString(1663))
return
if self.isAborted():
return
if self._watchlists:
watchlist = self.getTraktWatchlistData()
if watchlist is None:
utils.Debug("[Tagger] Problem getting watchlist data, aborting.")
utils.notification(utils.getString(1201), utils.getString(1664))
return
trakt_lists['Watchlist'] = watchlist['Watchlist']
if self.isAborted():
return
if self._ratings:
ratings = self.getTraktRatingData()
if ratings is None:
utils.Debug("[Tagger] Can not continue with managing lists, aborting.")
utils.notification(utils.getString(1201), utils.getString(1665))
return
trakt_lists = dict(trakt_lists, **ratings)
tTaken = time() - tStart
utils.Debug("[Tagger] Time to load data from trakt.tv: %0.3f seconds." % tTaken)
if self.isAborted():
return
tStart = time()
c = 0
tags = {'movies': {}, 'shows': {}}
for listName in trakt_lists:
for type in ['movies', 'shows']:
for index in trakt_lists[listName][type]:
if not index in tags[type]:
tags[type][index] = []
c = c + 1
tags[type][index].append(listToTag(listName))
tTaken = time() - tStart
utils.Debug("[Tagger] Time to build xbmc tag list for (%d) items: %0.3f seconds." % (c, tTaken))
tStart = time()
xbmc_update = {'movies': [], 'shows': []}
for listName in trakt_lists:
if listName in xbmc_lists:
for type in ['movies', 'shows']:
s1 = set(trakt_lists[listName][type])
s2 = set(xbmc_lists[listName][type])
if not s1 == s2:
xbmc_update[type].extend(list(s1.difference(s2)))
xbmc_update[type].extend(list(s2.difference(s1)))
else:
xbmc_update['movies'].extend(trakt_lists[listName]['movies'])
xbmc_update['shows'].extend(trakt_lists[listName]['shows'])
for list_name in xbmc_lists:
if not list_name in trakt_lists:
xbmc_update['movies'].extend(xbmc_lists[listName]['movies'])
xbmc_update['shows'].extend(xbmc_lists[listName]['shows'])
tTaken = time() - tStart
utils.Debug("[Tagger] Time to compare data: %0.3f seconds." % tTaken)
sStart = time()
old_tags = {'movies': {}, 'shows': {}}
c = 0
for type in ['movies', 'shows']:
for item in self.xbmcData[type]:
t = []
for old_tag in item['tag']:
if not isTraktList(old_tag):
t.append(old_tag)
id_field = 'movieid' if type == 'movies' else 'tvshowid'
id = item[id_field]
if t:
old_tags[type][id] = t
tTaken = time() - tStart
utils.Debug("[Tagger] Time to build list of old tags for (%d) items: %0.3f seconds." % (c, tTaken))
sStart = time()
xbmcTags = {'movies': {}, 'shows': {}}
c = 0
for type in xbmcTags:
xbmc_update[type] = list(set(xbmc_update[type]))
for index in xbmc_update[type]:
t = []
if index in tags[type]:
t = tags[type][index]
if index in old_tags[type]:
t.extend(old_tags[type][index])
id_field = 'movieid' if type == 'movies' else 'tvshowid'
id = self.xbmcData[type][index][id_field]
xbmcTags[type][id] = t
c = c + 1
tTaken = time() - tStart
utils.Debug("[Tagger] Time to build list of changes for (%d) items: %0.3f seconds." % (c, tTaken))
# update xbmc tags from trakt lists
utils.Debug("[Tagger] Updating XBMC tags from trakt.tv list(s).")
tStart = time()
self.xbmcUpdateTags(xbmcTags)
tTaken = time() - tStart
utils.Debug("[Tagger] Time to update changed xbmc tags: %0.3f seconds." % tTaken)
if utils.getSettingAsBool('tagging_notifications'):
utils.notification(utils.getString(1201), utils.getString(1659))
utils.Debug("[Tagger] Tags have been updated.")
def manageLists(self):
utils.notification(utils.getString(1201), utils.getString(1661))
utils.Debug("[Tagger] Starting to manage lists.")
tStart = time()
if not self.xbmcLoadData():
utils.Debug("[Tagger] Problem loading XBMC data, aborting.")
utils.notification(utils.getString(1201), utils.getString(1662))
return
tTaken = time() - tStart
utils.Debug("[Tagger] Time to load data from XBMC: %0.3f seconds." % tTaken)
selected = {}
tStart = time()
selected = self.getTraktListData()
if selected is None:
utils.Debug("[Tagger] Problem getting list data, aborting.")
utils.notification(utils.getString(1201), utils.getString(1663))
return
if self._watchlists:
watchlist = self.getTraktWatchlistData()
if watchlist is None:
utils.Debug("[Tagger] Problem getting watchlist data, aborting.")
utils.notification(utils.getString(1201), utils.getString(1664))
return
selected['Watchlist'] = watchlist['Watchlist']
tTaken = time() - tStart
utils.Debug("[Tagger] Time to load data from trakt.tv: %0.3f seconds." % tTaken)
d = traktManageListsDialog(lists=self.traktListData, xbmc_data=self.xbmcData, selected=selected)
d.doModal()
_button = d.button
_dirty = d.dirty
_newSelected = d.selected
_listData = d.listData
del d
if _button == BUTTON_OK:
if _dirty:
newSelected = _newSelected
tags = {'movies': {}, 'shows': {}}
ratingTags = {'movies': {}, 'shows': {}}
tagUpdates = {'movies': [], 'shows': []}
traktUpdates = {}
# apply changes and create new lists first.
tStart = time()
_lists_changed = []
_lists_added = []
keys_ignore = ['hide', 'slug', 'url']
for slug in _listData:
if not slug in self.traktListData:
_lists_added.append(slug)
continue
for key in _listData[slug]:
if key in keys_ignore:
continue
if not _listData[slug][key] == self.traktListData[slug][key]:
_lists_changed.append(slug)
break
_old_hidden = [slug for slug in self.traktListData if self.traktListData[slug]['hide']]
_new_hidden = [slug for slug in _listData if _listData[slug]['hide']]
if not set(_new_hidden) == set(_old_hidden):
utils.Debug("[Tagger] Updating hidden lists to '%s'." % str(_new_hidden))
utils.setSettingFromList('tagging_hidden_lists', _new_hidden)
if _lists_changed:
for slug in _lists_changed:
params = {}
params['slug'] = slug
for key in _listData[slug]:
if key in keys_ignore:
continue
params[key] = _listData[slug][key]
if self.simulate:
utils.Debug("[Tagger] Update list '%s' with params: %s" % (slug, str(params)))
else:
result = self.traktapi.userListUpdate(params)
if result and 'status' in result and result['status'] == 'success':
new_slug = result['slug']
if not slug == new_slug:
new_list_name = _listData[slug]['name']
old_list_name = self.traktListData[slug]['name']
self.traktLists[new_list_name] = new_slug
del(self.traktLists[old_list_name])
selected[new_list_name] = selected.pop(old_list_name)
_listData[new_slug] = _listData.pop(slug)
_listData[new_slug]['slug'] = new_slug
if _lists_added:
for list_name in _lists_added:
list_data = _listData[list_name]
result = self.traktapi.userListAdd(list_name, list_data['privacy'], list_data['description'], list_data['allow_shouts'], list_data['show_numbers'])
if result and 'status' in result and result['status'] == 'success':
slug = result['slug']
self.traktLists[list_name] = slug
_listData[slug] = _listData.pop(list_name)
else:
utils.Debug("[Tagger] There was a problem create the list '%s' on trakt.tv" % list)
tTaken = time() - tStart
utils.Debug("[Tagger] Time to update trakt.tv list settings: %0.3f seconds." % tTaken)
# build all tags
tStart = time()
c = 0
for listName in newSelected:
for type in ['movies', 'shows']:
for index in newSelected[listName][type]:
if not index in tags[type]:
tags[type][index] = []
tags[type][index].append(listToTag(listName))
c = c + 1
tTaken = time() - tStart
utils.Debug("[Tagger] Time to build xbmc tag list for (%d) items: %0.3f seconds." % (c, tTaken))
# check if we rating tags are enabled
c = 0
if self._ratings:
tStart = time()
ratings = self.getTraktRatingData()
if ratings is None:
utils.Debug("[Tagger] Can not continue with managing lists, aborting.")
utils.notification(utils.getString(1201), utils.getString(1665))
return
c = 0
for listName in ratings:
for type in ['movies', 'shows']:
for index in ratings[listName][type]:
if not index in ratingTags[type]:
ratingTags[type][index] = []
ratingTags[type][index].append(listToTag(listName))
c = c + 1
tTaken = time() - tStart
utils.Debug("[Tagger] Time to get and build rating tag list for (%d) items: %0.3f seconds." % (c, tTaken))
# build lists of changes
tStart = time()
for listName in newSelected:
if listName in selected:
for type in ['movies', 'shows']:
s1 = set(newSelected[listName][type])
s2 = set(selected[listName][type])
if not s1 == s2:
toAdd = list(s1.difference(s2))
toRemove = list(s2.difference(s1))
tagUpdates[type].extend(toAdd)
tagUpdates[type].extend(toRemove)
if not listName in traktUpdates:
traktUpdates[listName] = {'movies': {'add': [], 'remove': []}, 'shows': {'add': [], 'remove': []}}
traktUpdates[listName][type] = {'add': toAdd, 'remove': toRemove}
else:
tagUpdates['movies'].extend(newSelected[listName]['movies'])
tagUpdates['shows'].extend(newSelected[listName]['shows'])
traktUpdates[listName] = {}
traktUpdates[listName]['movies'] = {'add': newSelected[listName]['movies'], 'remove': []}
traktUpdates[listName]['shows'] = {'add': newSelected[listName]['shows'], 'remove': []}
# build xmbc update list
xbmcTags = {'movies': {}, 'shows': {}}
c = 0
for type in xbmcTags:
tagUpdates[type] = list(set(tagUpdates[type]))
f = utils.getMovieDetailsFromXbmc if type == 'movies' else utils.getShowDetailsFromXBMC
for index in tagUpdates[type]:
t = []
if index in tags[type]:
t = tags[type][index]
if index in ratingTags[type]:
t.extend(ratingTags[type][index])
id_field = 'movieid' if type == 'movies' else 'tvshowid'
id = self.xbmcData[type][index][id_field]
result = f(id, ['tag'])
for old_tag in result['tag']:
if not isTraktList(old_tag):
t.append(old_tag)
xbmcTags[type][id] = t
c = c + 1
tTaken = time() - tStart
utils.Debug("[Tagger] Time to build list of changes for (%d) items: %0.3f seconds." % (c, tTaken))
# update tags in xbmc
tStart = time()
self.xbmcUpdateTags(xbmcTags)
tTaken = time() - tStart
utils.Debug("[Tagger] Time to update changed xbmc tags: %0.3f seconds." % tTaken)
# update trakt.tv
tStart = time()
for listName in traktUpdates:
data = {'add': [], 'remove': []}
for type in ['movies', 'shows']:
data['add'].extend([self.xbmcData[type][index] for index in traktUpdates[listName][type]['add']])
data['remove'].extend([self.xbmcData[type][index] for index in traktUpdates[listName][type]['remove']])
if data['add']:
if listName.lower() == 'watchlist':
self.updateWatchlist(data['add'])
else:
self.traktListAddItem(listName, data['add'])
if data['remove']:
if listName.lower() == 'watchlist':
self.updateWatchlist(data['remove'], remove=True)
else:
self.traktListRemoveItem(listName, data['remove'])
tTaken = time() - tStart
utils.Debug("[Tagger] Time to update trakt.tv with changes: %0.3f seconds." % tTaken)
utils.Debug("[Tagger] Finished managing lists.")
utils.notification(utils.getString(1201), utils.getString(1666))
self.traktLists = None
self.traktListData = None
def itemLists(self, data):
lists = self.getTraktLists()
if not isinstance(lists, dict):
utils.Debug("[Tagger] Error getting lists from trakt.tv.")
return
d = traktItemListsDialog(list_data=self.traktListData, data=data)
d.doModal()
if not d.selectedLists is None:
non_trakt_tags = [tag for tag in data['tag'] if not isTraktList(tag)]
old_trakt_tags = [tagToList(tag) for tag in data['tag'] if isTraktList(tag)]
new_trakt_tags = d.selectedLists
if set(old_trakt_tags) == set(new_trakt_tags):
utils.Debug("[Tagger] '%s' had no changes made to the lists it belongs to." % data['title'])
else:
s1 = set(old_trakt_tags)
s2 = set(new_trakt_tags)
_changes = {}
_changes['add'] = list(s2.difference(s1))
_changes['remove'] = list(s1.difference(s2))
for _op in _changes:
debug_str = "[Tagger] Adding '%s' to '%s'." if _op == 'add' else "[Tagger] Removing: '%s' from '%s'."
f = self.traktListAddItem if _op == 'add' else self.traktListRemoveItem
for _list in _changes[_op]:
if _list.lower() == "watchlist":
utils.Debug(debug_str % (data['title'], _list))
remove = _op == 'remove'
self.updateWatchlist([data], remove=remove)
elif _list.lower().startswith("rating:"):
pass
else:
utils.Debug(debug_str % (data['title'], _list))
f(_list, [data])
tags = non_trakt_tags
tags.extend([listToTag(l) for l in new_trakt_tags])
s = utils.getFormattedItemName(data['type'], data)
id_field = "movieid" if data['type'] == 'movie' else "tvshowid"
if xbmcSetTags(data[id_field], data['type'], s, tags):
utils.notification(utils.getString(1201), utils.getString(1657) % s)
else:
utils.Debug("[Tagger] Dialog was cancelled.")
del d
def manualAddToList(self, list, data):
if list.lower().startswith("rating:"):
utils.Debug("[Tagger] '%s' is a reserved list name." % list)
return
tag = listToTag(list)
if tag in data['tag']:
utils.Debug("[Tagger] '%s' is already in the list '%s'." % (data['title'], list))
return
if list.lower() == "watchlist":
utils.Debug("[Tagger] Adding '%s' to Watchlist." % data['title'])
self.updateWatchlist([data])
else:
utils.Debug("[Tagger] Adding '%s' to '%s'." % (data['title'], list))
self.traktListAddItem(list, [data])
data['tag'].append(tag)
s = utils.getFormattedItemName(data['type'], data)
id_field = "movieid" if data['type'] == 'movie' else "tvshowid"
if xbmcSetTags(data[id_field], data['type'], s, data['tag']):
utils.notification(utils.getString(1201), utils.getString(1657) % s)
def manualRemoveFromList(self, list, data):
if list.lower().startswith("rating:"):
utils.Debug("[Tagger] '%s' is a reserved list name." % list)
return
tag = listToTag(list)
if not tag in data['tag']:
utils.Debug("[Tagger] '%s' is not in the list '%s'." % (data['title'], list))
return
if list.lower() == "watchlist":
utils.Debug("[Tagger] Removing: '%s' from Watchlist." % data['title'])
self.updateWatchlist([data], remove=True)
else:
utils.Debug("[Tagger] Removing: '%s' from '%s'." % (data['title'], list))
self.traktListRemoveItem(list, [data])
data['tag'].remove(tag)
s = utils.getFormattedItemName(data['type'], data)
id_field = "movieid" if data['type'] == 'movie' else "tvshowid"
if xbmcSetTags(data[id_field], data['type'], s, data['tag']):
utils.notification(utils.getString(1201), utils.getString(1657) % s)
TRAKT_LISTS = 4
GROUP_LIST_SETTINGS = 100
LIST_PRIVACY_SETTING = 111
LIST_OTHER_SETTINGS = 141
BUTTON_EDIT_DESC = 113
BUTTON_RENAME = 114
BUTTON_ADD_LIST = 15
BUTTON_OK = 16
BUTTON_CANCEL = 17
LABEL = 25
ACTION_PREVIOUS_MENU2 = 92
ACTION_PARENT_DIR = 9
ACTION_PREVIOUS_MENU = 10
ACTION_SELECT_ITEM = 7
ACTION_MOUSE_LEFT_CLICK = 100
ACTION_CLOSE_LIST = [ACTION_PREVIOUS_MENU2, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU]
ACTION_ITEM_SELECT = [ACTION_SELECT_ITEM, ACTION_MOUSE_LEFT_CLICK]
class traktItemListsDialog(xbmcgui.WindowXMLDialog):
selectedLists = None
def __new__(cls, list_data, data):
return super(traktItemListsDialog, cls).__new__(cls, "traktListDialog.xml", __addon__.getAddonInfo('path'), list_data=list_data, data=data)
def __init__(self, *args, **kwargs):
data = kwargs['data']
list_data = kwargs['list_data']
self.data = data
self.hasRating = False
self.tags = {}
for tag in data['tag']:
if isTraktList(tag):
t = tagToList(tag)
if t.startswith("Rating:"):
self.hasRating = True
self.ratingTag = t
continue
self.tags[t] = True
utils.Debug(str(list_data))
for slug in list_data:
list_name = list_data[slug]['name']
hidden = list_data[slug]['hide']
if not hidden and not list_name in self.tags:
self.tags[list_name] = False
if (not 'Watchlist' in self.tags) and utils.getSettingAsBool('tagging_watchlists'):
self.tags['Watchlist'] = False
super(traktItemListsDialog, self).__init__()
def onInit(self):
grp = self.getControl(GROUP_LIST_SETTINGS)
grp.setEnabled(False)
grp.setVisible(False)
self.setInfoLabel(utils.getFormattedItemName(self.data['type'], self.data))
self.list = self.getControl(TRAKT_LISTS)
self.populateList()
self.setFocus(self.list)
def onAction(self, action):
if not action.getId() in ACTION_ITEM_SELECT:
if action in ACTION_CLOSE_LIST:
self.close()
if action in ACTION_ITEM_SELECT:
cID = self.getFocusId()
if cID == TRAKT_LISTS:
item = self.list.getSelectedItem()
selected = not item.isSelected()
item.select(selected)
self.tags[item.getLabel()] = selected
def onClick(self, control):
if control == BUTTON_ADD_LIST:
keyboard = xbmc.Keyboard("", utils.getString(1654))
keyboard.doModal()
if keyboard.isConfirmed() and keyboard.getText():
new_list = keyboard.getText().strip()
if new_list:
if new_list.lower() == "watchlist" or new_list.lower().startswith("rating:"):
utils.Debug("[Tagger] Dialog: Tried to add a reserved list name '%s'." % new_list)
utils.notification(utils.getString(1650), utils.getString(1655) % new_list)
return
if new_list not in self.tags:
utils.Debug("[Tagger] Dialog: Adding list '%s', and selecting it." % new_list)
else:
utils.Debug("[Tagger] Dialog: '%s' already in list, selecting it." % new_list)
utils.notification(utils.getString(1650), utils.getString(1656) % new_list)
self.tags[new_list] = True
self.populateList()
elif control == BUTTON_OK:
data = []
for i in range(0, self.list.size()):
item = self.list.getListItem(i)
if item.isSelected():
data.append(item.getLabel())
if self.hasRating:
data.append(self.ratingTag)
self.selectedLists = data
self.close()
elif control == BUTTON_CANCEL:
self.close()
def setInfoLabel(self, text):
pl = self.getControl(LABEL)
pl.setLabel(text)
def populateList(self):
self.list.reset()
if 'Watchlist' in self.tags:
item = xbmcgui.ListItem('Watchlist')
item.select(self.tags['Watchlist'])
self.list.addItem(item)
for tag in sorted(self.tags.iterkeys()):
if tag.lower() == "watchlist":
continue
item = xbmcgui.ListItem(tag)