Taxel / PlexTraktSync

A python script that syncs the movies, shows and ratings between trakt and Plex (without needing a PlexPass or Trakt VIP subscription)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Specific library throwing an error on sync: ParseError: not well-formed (invalid token)

BackedUpBooty opened this issue · comments

Confirmation

  • I have read the README.md on the project homepage
  • I have checked if identical issue already exists
  • I have tried downgrading to find version that can be used as a workaround

The problem

I have 3 plex libraries connected to plextraktsync as shown below when running the plextraktsync info command:

Plex Server version: 1.40.0.7775-456fbaf97, updated at: 2024-02-03 11:23:10
Enabled 3 libraries in Plex Server:
 - 1: Movies
 - 2: TV Shows
 - 8: Music

When running the sync command, Movies and TV Shows are processed, but it then throws an error:

Processing Movies 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 311/311  [ 0:00:05 < 0:00:00 , 53 it/s ]
INFO     Movies processed in 8.1 seconds
INFO     Preload shows data
Processing TV Shows 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 247/247  [ 0:00:01 < 0:00:00 , 233 it/s ]
INFO     TV Shows processed in 1.8 seconds
INFO     Preloaded shows data (247 shows)

ERROR    not well-formed (invalid token): line 152, column 1262

which then shows the following traceback (link) seemingly having an issue with the TV Show library/episodes etc. https://pastebin.com/9cyjWZZQ

If I add TV Shows to excluded-libraries: in the config then the sync works as expected with just Movies.

Using the inspect command on a specific TV show returns a valid and expected response.

Steps to reproduce the behavior

  1. Have PlexTraktSync working normally for a couple of months with TV Shows and Movies libraries being sync'd
  2. Update containers for both plextraktsync and plex as updates are released/confirmed
  3. Bug happens....

Error trace / logs

ERROR    not well-formed (invalid token): line 152, column 1262
         ╭─────────────────────────────────────────────────────── Traceback (most recent call last) ────────────────────────────────────────────────────────╮
         │ /app/plextraktsync/cli.py:26 in wrap                                                                                                             │
         │                                                                                                                                                  │
         │    23 │   │   │   cmd = getattr(module, name)                                                                                                    │
         │    24 │   │   │                                                                                                                                  │
         │    25 │   │   │   try:                                                                                                                           │
         │ ❱  26 │   │   │   │   cmd(*args, **kwargs)                                                                                                       │
         │    27 │   │   │   except EOFError as e:                                                                                                          │
         │    28 │   │   │   │   raise ClickException(f"Program requested terminal, No terminal is                                                          │
         │       connected: {e}")                                                                                                                           │
         │    29 │   │   │   except ClickException as e:                                                                                                    │
         │                                                                                                                                                  │
         │ /app/plextraktsync/commands/sync.py:68 in sync                                                                                                   │
         │                                                                                                                                                  │
         │   65 │   │   │   w.print_plan(print=logger.info)                                                                                                 │
         │   66 │   │   if dry_run:                                                                                                                         │
         │   67 │   │   │   logger.info("Enabled dry-run mode: not making actual changes")                                                                  │
         │ ❱ 68 │   │   runner.sync(walker=w, dry_run=config.dry_run)                                                                                       │
         │   69                                                                                                                                             │
         │                                                                                                                                                  │
         │ /app/plextraktsync/sync.py:78 in sync                                                                                                            │
         │                                                                                                                                                  │
         │    75 │   │   │                                                                                                                                  │
         │    76 │   │   │   shows = set()                                                                                                                  │
         │    77 │   │   │   episode_trakt_ids = set()                                                                                                      │
         │ ❱  78 │   │   │   for episode in walker.find_episodes():                                                                                         │
         │    79 │   │   │   │   self.sync_collection(episode, dry_run=dry_run)                                                                             │
         │    80 │   │   │   │   self.sync_ratings(episode, dry_run=dry_run)                                                                                │
         │    81 │   │   │   │   self.sync_watched(episode, dry_run=dry_run)                                                                                │
         │                                                                                                                                                  │
         │ /app/plextraktsync/plan/Walker.py:115 in find_episodes                                                                                           │
         │                                                                                                                                                  │
         │   112 │   │   self.logger.info(f"Preloaded shows data ({len(plex_shows)} shows)")                                                                │
         │   113 │   │                                                                                                                                      │
         │   114 │   │   show_cache = {}                                                                                                                    │
         │ ❱ 115 │   │   for ep in self.episodes_from_sections(self.plan.show_sections):                                                                    │
         │   116 │   │   │   show_id = ep.show_id                                                                                                           │
         │   117 │   │   │   ep.show = plex_shows[show_id]                                                                                                  │
         │   118 │   │   │   show = show_cache[show_id] if show_id in show_cache else None                                                                  │
         │                                                                                                                                                  │
         │ /app/plextraktsync/plan/Walker.py:164 in episodes_from_sections                                                                                  │
         │                                                                                                                                                  │
         │   161 │   │   │   │   │   section.pager("episode"),                                                                                              │
         │   162 │   │   │   │   │   desc=f"Processing {section.title_link}",                                                                               │
         │   163 │   │   │   │   )                                                                                                                          │
         │ ❱ 164 │   │   │   │   yield from it                                                                                                              │
         │   165 │                                                                                                                                          │
         │   166 │   def media_from_items(self, libtype: str, items: list) -> Generator[PlexLibraryItem,                                                    │
         │       Any, None]:                                                                                                                                │
         │   167 │   │   it = self.progressbar(items, desc=f"Processing {libtype}s")                                                                        │
         │                                                                                                                                                  │
         │ /app/plextraktsync/plan/Walker.py:184 in progressbar                                                                                             │
         │                                                                                                                                                  │
         │   181 │   │   if self._progressbar:                                                                                                              │
         │   182 │   │   │   pb = self._progressbar(iterable, **kwargs)                                                                                     │
         │   183 │   │   │   with pb as it:                                                                                                                 │
         │ ❱ 184 │   │   │   │   yield from it                                                                                                              │
         │   185 │   │   else:                                                                                                                              │
         │   186 │   │   │   yield from iterable                                                                                                            │
         │   187                                                                                                                                            │
         │                                                                                                                                                  │
         │ /app/plextraktsync/rich/RichProgressBar.py:22 in __next__                                                                                        │
         │                                                                                                                                                  │
         │   19 │   │   return self                                                                                                                         │
         │   20 │                                                                                                                                           │
         │   21 │   def __next__(self):                                                                                                                     │
         │ ❱ 22 │   │   res = self.iterable_next()                                                                                                          │
         │   23 │   │   self.update()                                                                                                                       │
         │   24 │   │   return res                                                                                                                          │
         │   25                                                                                                                                             │
         │                                                                                                                                                  │
         │ /app/plextraktsync/plex/PlexSectionPager.py:40 in __iter__                                                                                       │
         │                                                                                                                                                  │
         │   37 │   │   size = X_PLEX_CONTAINER_SIZE                                                                                                        │
         │   38 │   │                                                                                                                                       │
         │   39 │   │   while True:                                                                                                                         │
         │ ❱ 40 │   │   │   items = self.fetch_items(start=start, size=size)                                                                                │
         │   41 │   │   │                                                                                                                                   │
         │   42 │   │   │   if not len(items):                                                                                                              │
         │   43 │   │   │   │   break                                                                                                                       │
         │                                                                                                                                                  │
         │ /app/plextraktsync/decorators/retry.py:27 in wrapper                                                                                             │
         │                                                                                                                                                  │
         │   24 │   │   │   count = 0                                                                                                                       │
         │   25 │   │   │   while True:                                                                                                                     │
         │   26 │   │   │   │   try:                                                                                                                        │
         │ ❱ 27 │   │   │   │   │   return fn(*args, **kwargs)                                                                                              │
         │   28 │   │   │   │   except (                                                                                                                    │
         │   29 │   │   │   │   │   │   BadRequest,                                                                                                         │
         │   30 │   │   │   │   │   │   BadResponseException,                                                                                               │
         │                                                                                                                                                  │
         │ /app/plextraktsync/plex/PlexSectionPager.py:30 in fetch_items                                                                                    │
         │                                                                                                                                                  │
         │   27 │                                                                                                                                           │
         │   28 │   @retry()                                                                                                                                │
         │   29 │   def fetch_items(self, start: int, size: int):                                                                                           │
         │ ❱ 30 │   │   return self.section.search(libtype=self.libtype, container_start=start,                                                             │
         │      container_size=size, maxresults=size)                                                                                                       │
         │   31 │                                                                                                                                           │
         │   32 │   def __iter__(self):                                                                                                                     │
         │   33 │   │   from plexapi import X_PLEX_CONTAINER_SIZE                                                                                           │
         │                                                                                                                                                  │
         │ /usr/local/lib/python3.12/site-packages/plexapi/library.py:1517 in search                                                                        │
         │                                                                                                                                                  │
         │   1514 │   │   """                                                                                                                               │
         │   1515 │   │   key, kwargs = self._buildSearchKey(                                                                                               │
         │   1516 │   │   │   title=title, sort=sort, libtype=libtype, limit=limit, filters=filters,                                                        │
         │        returnKwargs=True, **kwargs)                                                                                                              │
         │ ❱ 1517 │   │   return self.fetchItems(                                                                                                           │
         │   1518 │   │   │   key, container_start=container_start, container_size=container_size,                                                          │
         │        maxresults=maxresults, **kwargs)                                                                                                          │
         │   1519 │                                                                                                                                         │
         │   1520 │   def _locations(self):                                                                                                                 │
         │                                                                                                                                                  │
         │ /usr/local/lib/python3.12/site-packages/plexapi/base.py:255 in fetchItems                                                                        │
         │                                                                                                                                                  │
         │    252 │   │   │   headers['X-Plex-Container-Start'] = str(container_start)                                                                      │
         │    253 │   │   │   headers['X-Plex-Container-Size'] = str(container_size)                                                                        │
         │    254 │   │   │                                                                                                                                 │
         │ ❱  255 │   │   │   data = self._server.query(ekey, headers=headers)                                                                              │
         │    256 │   │   │   subresults = self.findItems(data, cls, ekey, **kwargs)                                                                        │
         │    257 │   │   │   total_size = utils.cast(int, data.attrib.get('totalSize') or                                                                  │
         │        data.attrib.get('size')) or len(subresults)                                                                                               │
         │    258                                                                                                                                           │
         │                                                                                                                                                  │
         │ /usr/local/lib/python3.12/site-packages/plexapi/server.py:771 in query                                                                           │
         │                                                                                                                                                  │
         │    768 │   │   │   else:                                                                                                                         │
         │    769 │   │   │   │   raise BadRequest(message)                                                                                                 │
         │    770 │   │   data = response.text.encode('utf8')                                                                                               │
         │ ❱  771 │   │   return ElementTree.fromstring(data) if data.strip() else None                                                                     │
         │    772 │                                                                                                                                         │
         │    773 │   def search(self, query, mediatype=None, limit=None, sectionId=None):                                                                  │
         │    774 │   │   """ Returns a list of media items or filter categories from the resulting                                                         │
         │                                                                                                                                                  │
         │ /usr/local/lib/python3.12/xml/etree/ElementTree.py:1323 in XML                                                                                   │
         │                                                                                                                                                  │
         │   1320 │   """                                                                                                                                   │
         │   1321 │   if not parser:                                                                                                                        │
         │   1322 │   │   parser = XMLParser(target=TreeBuilder())                                                                                          │
         │ ❱ 1323 │   parser.feed(text)                                                                                                                     │
         │   1324 │   return parser.close()                                                                                                                 │
         │   1325                                                                                                                                           │
         │   1326                                                                                                                                           │
         ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
         ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
         │                                                                                                                                                  │
         │ ▲                                                                                                                                                │
         ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
         ParseError: not well-formed (invalid token): line 152, column 1262
Error: Error running sync command: not well-formed (invalid token): line 152, column 1262

Expected behavior

Movies and TV shows are sync'd which then moves on to watchlist etc. and the command is completed without error

Inspect of problematic items

inspect commands on TV shows return results (the following is one of the latest shows to be watched by multiple users) :

$ docker-compose run --rm plextraktsync inspect 38110
PlexTraktSync [0.28.15]
INFO     Connecting with url: https://[redacted], timeout 30 seconds

Inspecting <PlexId:38110>: <tmdb:60622:Show:38110:Fargo>
--- Plex
Plex Web URL: https://app.plex.tv/desktop/#!/server/a1100675d843bd0ab696c3451acf8232d0d9a46f/details?key=/library/metadata/38110
Discover URL: https://app.plex.tv/desktop/#!/provider/tv.plex.provider.discover/details?key=/library/metadata/5d9c0838ffd9ef001e98fd77
Title: Fargo
Media.Type: 'show'
Media.Guid: 'plex://show/5d9c0838ffd9ef001e98fd77'
Media.Guids: [<Guid:imdb://tt2802850>, <Guid:tmdb://60622>, <Guid:tvdb://269613>]
Guids:
  Guid: <PlexGuid:tmdb://60622>, Id: 60622, Provider: 'tmdb'
  Guid: <PlexGuid:tvdb://269613>, Id: 269613, Provider: 'tvdb'
  Guid: <PlexGuid:imdb://tt2802850>, Id: tt2802850, Provider: 'imdb'
Metadata: {'collected_at': '2023-12-25:T06:04:39.000Z', 'media_type': 'digital'}
Played on Plex: False
Plex play history:
- 2024-02-01 23:30:14 <EpisodeHistory:38112:Fargo-s05e01>: by [redacted] on iPad with iOS
- 2024-01-31 23:08:58 <EpisodeHistory:38130:Fargo-s04e11>: by [redacted] on iPad with iOS
- 2024-01-30 23:20:16 <EpisodeHistory:38129:Fargo-s04e10>: by [redacted] on iPad with iOS
- 2024-01-28 22:57:46 <EpisodeHistory:38128:Fargo-s04e09>: by [redacted] on iPad with iOS
- 2024-01-28 09:14:44 <EpisodeHistory:38117:Fargo-s04e01>: by [redacted] on AFTSSS with Android
- 2024-01-27 19:48:36 <EpisodeHistory:38127:Fargo-s04e08>: by [redacted] on iPad with iOS
- 2024-01-26 22:52:27 <EpisodeHistory:38125:Fargo-s04e06>: by [redacted] on iPhone with iOS
- 2024-01-26 19:58:35 <EpisodeHistory:38124:Fargo-s04e05>: by [redacted] on iPhone with iOS
- 2024-01-24 22:34:03 <EpisodeHistory:38123:Fargo-s04e04>: by [redacted] on iPad with iOS
- 2024-01-22 23:18:22 <EpisodeHistory:38121:Fargo-s04e03>: by [redacted] on iPad with iOS
- 2024-01-21 19:17:02 <EpisodeHistory:38119:Fargo-s04e02>: by [redacted] on iPad with iOS
- 2024-01-20 00:27:21 <EpisodeHistory:38117:Fargo-s04e01>: by [redacted] on iPad with iOS
- 2023-12-31 17:34:56 <EpisodeHistory:38166:Fargo-s05e07>: by [redacted] on LG OLED55C1PJB with webOS
- 2023-12-31 16:46:49 <EpisodeHistory:38122:Fargo-s05e06>: by [redacted] on LG OLED55C1PJB with webOS
- 2023-12-29 01:17:35 <EpisodeHistory:38120:Fargo-s05e05>: by [redacted] on LG OLED55C1PJB with webOS
- 2023-12-29 00:36:14 <EpisodeHistory:38118:Fargo-s05e04>: by [redacted] on LG OLED55C1PJB with webOS
- 2023-12-28 23:51:45 <EpisodeHistory:38115:Fargo-s05e03>: by [redacted] on LG OLED55C1PJB with webOS
- 2023-12-28 23:11:02 <EpisodeHistory:38114:Fargo-s05e02>: by [redacted] on LG OLED55C1PJB with webOS
- 2023-12-28 22:25:21 <EpisodeHistory:38112:Fargo-s05e01>: by [redacted] on LG OLED55C1PJB with webOS
--- Trakt
Trakt: https://trakt.tv/shows/fargo
Plex Rating: None
Trakt Rating: None

Workarounds

I'm not about any workarounds which may exist here. I have a task which runs the sync command once a night, the first one happened January 30th, v 28.12, I rolled back to 28.11 and 28.10, no change. Today I updated to latest (28.15) and the issue persists.

Install method

docker-compose

Config file contents

cache:
  path: $[redacted]_CACHE_DIR/trakt_cache

excluded-libraries:
  - Private
  - Family Holidays
#  - TV Shows

config:
  dotenv_override: true

plex:
  timeout: 30

logging:
  append: true
  # Whether to show timestamps in console messages
  console_time: false
  debug: false
  filename: plextraktsync.log
  filter:
#    # Filter out all messages with level WARNING
#    - level: WARNING
#    # Filter out message with level WARNING and containing a text
#    - level: WARNING
#      message: "not found on Trakt"
#    - message: "because provider local has no external Id"
#    - message: "because provider none has no external Id"
#    - message: "Retry using search for specific Plex Episode"

# settings for 'sync' command (default)
sync:
  plex_to_trakt:
    collection: true
    ratings: true
    watched_status: true
    # If plex_to_trakt watchlist=false and trakt_to_plex watchlist=true
    # the Plex watchlist will be overwritten by Trakt watchlist
    watchlist: true
  trakt_to_plex:
    liked_lists: true
    ratings: true
    watched_status: true
    # If trakt_to_plex watchlist=false and plex_to_trakt watchlist=true
    # the Trakt watchlist will be overwritten by Plex watchlist
    watchlist: true
    # If you prefer to fetch trakt watchlist as a playlist instead of
    # plex watchlist, toggle this to true (is read only if watchlist=true)
    watchlist_as_playlist: false

# settings for 'watch' command
watch:
  add_collection: false
  remove_collection: false
  # what video watched percentage (0 to 100) triggers the watched status
  scrobble_threshold: 80
  # true to scrobble only what's watched by you, false for all your PMS users
  username_filter: true

xbmc-providers:
  movies: imdb
  shows: tmdb

# vim:ts=2:sw=2:et

Version

PlexTraktSync 0.28.15

Python Version

Python Version: 3.12.1 (main, Jan 27 2024, 06:36:14) [GCC 13.2.1 20231014]

Plex Server Version

Plex Server version: 1.40.0.7775-456fbaf97, updated at: 2024-02-03 11:23:10

Operating System and Version

Synology DSM

The issue seems to come very low level, from XML that Plex server returns. Unfortunately not much that can be done here. Did you check plex server logs? and plex forums? is plex server latest? was it updated recently?

how do you think item id=38110 is related to the problem?

enable debug log, and look what is the last plex URL requested, then user plex-api.sh script (from this repository) to download the XML. is that XML still not valid?

Thanks for the quick replies. I figured out it was a plex issue, not a plextraktsync issue. Some users hadn't told me, but it seems a few of them weren't seeing TV Shows in Home or 'Recommended' anymore, but because they could still browse through the 'Library' tab they'd not bothered to report it.

Something (not sure what) happened to the library settings for some users, I just recreated the library from the same media folder and now the sync is working.

Sorry for wasting your time, closing the issue.