Repository: Flexget/Flexget

Issue:
flexget crashes when reading a letterboxd list
### Expected behaviour:

The movie in the letterboxd list is added to a trakt list

### Actual behaviour:

Flexget generates a crash report

### Steps to reproduce:

run `flexget execute --tasks letterboxd-comedy`

#### Config:

full config: https://github.com/effemmeffe/flexget/blob/master/config.yml

```yaml
fmf@kodi:~$ flexget execute --tasks letterboxd-comedy --dump-config
2021-05-19 11:47:33 VERBOSE  task_queue                    There are 1 tasks to execute. Shutdown will commence when they have completed.
--- config from task: letterboxd-comedy
accept_all: true
disable: seen
free_space:
  allotment: -1
  path: /media/video-nas/
  port: 22
  space: 5000
letterboxd:
  list: netflix-stand-up-comedy
  sort_by: default
  username: redacted
list_add:
  - trakt_list:
      account: redacted
      list: netflix-stand-up-comedy
      strip_dates: false
      type: auto
      username: redacted
metainfo_movie: true
no_entries_ok: true
parsing:
  movie: guessit
pathscrub: windows
priority: 191
sort_by: added
template:
  - movies-metainfo
trakt_lookup:
  account: redacted
  username: redacted
```
---
  
#### Log:
<details>
 <summary>(click to expand)</summary>

```
2021-05-19 11:56:23 DEBUG    utils.requests letterboxd-comedy GETing URL http://letterboxd.com/film/daniel-sloss-live-shows/ with args () and kwargs {'allow_redirects': True, 'timeout': 30}
2021-05-19 11:56:24 CRITICAL task_queue                    BUG: Unhandled exception during task queue run loop.
2021-05-19 11:56:24 CRITICAL manager                       An unexpected crash has occurred. Writing crash report to /media/video-nas/flexget/crash_report.2021.05.19.115624902209.log. Please verify you are running the latest version of flexget by using "flexget -V" from CLI or by using version_checker plugin at http://flexget.com/wiki/Plugins/version_checker. You are currently using version 3.1.126
2021-05-19 11:56:24 DEBUG    manager                       Traceback:
Traceback (most recent call last):

  File "/usr/lib/python3.8/threading.py", line 890, in _bootstrap
    self._bootstrap_inner()
    |    -> <function Thread._bootstrap_inner at 0x7fa512a770d0>
    -> <Thread(task_queue, started daemon 140346823902976)>
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
    |    -> <function Thread.run at 0x7fa512a75dc0>
    -> <Thread(task_queue, started daemon 140346823902976)>
  File "/usr/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
    |    |        |    |        |    -> {}
    |    |        |    |        -> <Thread(task_queue, started daemon 140346823902976)>
    |    |        |    -> ()
    |    |        -> <Thread(task_queue, started daemon 140346823902976)>
    |    -> <bound method TaskQueue.run of <flexget.task_queue.TaskQueue object at 0x7fa50acc5700>>
    -> <Thread(task_queue, started daemon 140346823902976)>

> File "/usr/local/lib/python3.8/dist-packages/flexget/task_queue.py", line 46, in run
    self.current_task.execute()
    |    |            -> <function Task.execute at 0x7fa50fd9adc0>
    |    -> <flexget.task.Task object at 0x7fa50a95c070>
    -> <flexget.task_queue.TaskQueue object at 0x7fa50acc5700>

  File "/usr/local/lib/python3.8/dist-packages/flexget/task.py", line 87, in wrapper
    return func(self, *args, **kw)
           |    |      |       -> {}
           |    |      -> ()
           |    -> <flexget.task.Task object at 0x7fa50a95c070>
           -> <function Task.execute at 0x7fa50fd9ad30>

  File "/usr/local/lib/python3.8/dist-packages/flexget/task.py", line 722, in execute
    self._execute()
    |    -> <function Task._execute at 0x7fa50fd9aca0>
    -> <flexget.task.Task object at 0x7fa50a95c070>

  File "/usr/local/lib/python3.8/dist-packages/flexget/task.py", line 688, in _execute
    self.__run_task_phase(phase)
    |                     -> 'input'
    -> <flexget.task.Task object at 0x7fa50a95c070>

  File "/usr/local/lib/python3.8/dist-packages/flexget/task.py", line 517, in __run_task_phase
    for e in response:
        |    -> <flexget.utils.cached_input.IterableCache object at 0x7fa50993dee0>
        -> <Entry(title=Bert Kreischer: Secret Time (2018),state=undecided)>

  File "/usr/local/lib/python3.8/dist-packages/flexget/utils/cached_input.py", line 232, in __iter__
    for item in self.iterable:
        |       |    -> <generator object Letterboxd.on_task_input at 0x7fa50a98bba0>
        |       -> <flexget.utils.cached_input.IterableCache object at 0x7fa50993dee0>
        -> <Entry(title=Bert Kreischer: Secret Time (2018),state=undecided)>

  File "/usr/local/lib/python3.8/dist-packages/flexget/plugins/input/letterboxd.py", line 121, in on_task_input
    yield self.parse_film(film, config)
          |    |          |     -> {'username': 'effemmeffe', 'list': 'netflix-stand-up-comedy', 'sort_by': '', 'p_slug': '/effemmeffe/list/netflix-stand-up-com...
          |    |          -> <div class="poster film-poster really-lazy-load" data-film-id="560863" data-film-slug="/film/daniel-sloss-live-shows/" data-i...
          |    -> <function Letterboxd.parse_film at 0x7fa510c299d0>
          -> <flexget.plugins.input.letterboxd.Letterboxd object at 0x7fa50ae77340>

  File "/usr/local/lib/python3.8/dist-packages/flexget/plugins/input/letterboxd.py", line 84, in parse_film
    result = self.tmdb_lookup(soup.find(attrs={'data-tmdb-id': True}).get('data-tmdb-id'))
             |    |           |    -> <function Tag.find at 0x7fa50eb02e50>
             |    |           -> <!DOCTYPE html>
             |    |              <!--[if lt IE 7 ]> <html lang="en" class="ie6 lte9 lte8 lte7 lte6 no-js"> <![endif]--><!--[if IE 7 ]>    <htm...
             |    -> <function Letterboxd.tmdb_lookup at 0x7fa510c29940>
             -> <flexget.plugins.input.letterboxd.Letterboxd object at 0x7fa50ae77340>

  File "/usr/local/lib/python3.8/dist-packages/flexget/plugins/input/letterboxd.py", line 70, in tmdb_lookup
    tmdb = plugin.get('api_tmdb', self).lookup(tmdb_id=search)
           |      |               |                    -> ''
           |      |               -> <flexget.plugins.input.letterboxd.Letterboxd object at 0x7fa50ae77340>
           |      -> <function get at 0x7fa510380790>
           -> <module 'flexget.plugin' from '/usr/local/lib/python3.8/dist-packages/flexget/plugin.py'>

  File "/usr/local/lib/python3.8/dist-packages/flexget/utils/database.py", line 30, in wrapper
    return func(*args, **kwargs)
           |     |       -> {'tmdb_id': '', 'session': <sqlalchemy.orm.session.ContextSession object at 0x7fa50aafa1f0>}
           |     -> ()
           -> <function ApiTmdb.lookup at 0x7fa50b120820>

  File "/usr/local/lib/python3.8/dist-packages/flexget/components/tmdb/api_tmdb.py", line 322, in lookup
    raise LookupError('No criteria specified for TMDb lookup')

LookupError: No criteria specified for TMDb lookup
```
</details>

### Additional information:

- FlexGet version: 3.1.126
- Python version: Python 3.8.5
- Installation method: pip
- Using daemon (yes/no): yes
- OS and version: Ubuntu 20.04.2 LTS
- Link to debug level log: https://paste.ubuntu.com/p/PMBGdkrqNg/
- Link to crash log: https://paste.ubuntu.com/p/63KR4BNd6g/




Discussion:

Resolving patch:
diff --git a/flexget/plugins/input/letterboxd.py b/flexget/plugins/input/letterboxd.py
--- a/flexget/plugins/input/letterboxd.py
+++ b/flexget/plugins/input/letterboxd.py
@@ -67,7 +67,16 @@ def build_config(self, config):
         return config
 
     def tmdb_lookup(self, search):
-        tmdb = plugin.get('api_tmdb', self).lookup(tmdb_id=search)
+        if not search:
+            logger.warning('Can\'t search tmdb, no tmdb_id')
+            return
+
+        try:
+            tmdb = plugin.get('api_tmdb', self).lookup(tmdb_id=search)
+        except LookupError as e:
+            logger.warning('Error searching tmdb: {}', e)
+            return
+
         result = {
             'title': '%s (%s)' % (tmdb.name, tmdb.year),
             'imdb_id': tmdb.imdb_id,
@@ -82,6 +91,8 @@ def parse_film(self, film, config):
         url = base_url + film.get(config['f_slug'])
         soup = get_soup(requests.get(url).content)
         result = self.tmdb_lookup(soup.find(attrs={'data-tmdb-id': True}).get('data-tmdb-id'))
+        if not result:
+            return
 
         entry = Entry(result)
         entry['url'] = url
@@ -118,7 +129,10 @@ def on_task_input(self, task, config=None):
 
             for film in soup.find_all(attrs={config['f_slug']: True}):
                 if rcount < max_results:
-                    yield self.parse_film(film, config)
+                    result = self.parse_film(film, config)
+                    if not result:
+                        continue
+                    yield result
                     if 'max_results' in config:
                         rcount += 1
 


Test patch: