Repository: Flexget/Flexget

Issue:
Request - Add support to output path to subtitle_list plugin
### Expected behaviour:

In the former subtitle_queue plugin, we could add the subtitles to the queue with the destination path, instead of the current one, and with this, we could move the files and the add the subtitles to the queue in the same task, and the subtitles would be downloaded to the final path.
### Actual behaviour:

It doesnt support the path option anymore, so its going to emit paths that do not exist anymore, because the file was previouslly moved.
### Steps to reproduce:
- Step 1: Move a file and add it to the subtitle list in the same task
- Step 2: Emit the subtitle list
#### Config:

move task:

```
  move-movies:
    template:
      - disable-seen-retry
      - movies-metainfo
    filesystem:
      path: '/{{secrets.folder.root}}{{secrets.folder.downmovies}}'
      recursive: yes
      retrieve: files
      regexp: '.*\.(avi|mkv|mp4)$'
    accept_all: yes
    move:
      to: '/{{secrets.folder.root}}{{secrets.folder.movies}}'
      filename: "{{tmdb_name|pathscrub}} ({{tmdb_year}}){% if quality|default(False) %}: - [{{quality}}]{% endif %}"
      clean_source: 100
    list_add:
      - subtitle_list:
          list: subtitles-list
```

subtitle download task:

```
  download-subtitles:
    template:
      - disable-seen-retry
    subtitle_list:
      list: subtitles-list
    list_queue:
      - subtitle_list:
          list: subtitles-list
    subliminal:
      exact_match: yes
      languages:
        - eng
```
#### Log:

move task:

```
2016-06-05 19:34 VERBOSE  task          move-movies     ACCEPTED: `Beginners (2010) - [720p bluray h264]` by accept_all plugin
2016-06-05 19:34 VERBOSE  details       move-movies     Summary - Accepted: 1 (Rejected: 0 Undecided: 0 Failed: 0)
2016-06-05 19:34 VERBOSE  move          move-movies     Adding extension `.mp4` to dst `/mnt/data/Movies/Beginners (2010) - [720p bluray h264]`
2016-06-05 19:34 INFO     move          move-movies     `/mnt/data/Downloads/Movies/Beginners/Beginners (2010) - [720p bluray h264].mp4` has been moved to `/mnt/data/Movies/Beginners (2010) - [720p bluray h264].mp4`
2016-06-05 19:34 INFO     move          move-movies     Path `/mnt/data/Downloads/Movies/Beginners` has been deleted because was less than clean_source safe value.
2016-06-05 19:34 VERBOSE  list_add      move-movies     adding accepted entries into subtitle_list - {u'list': u'subtitles-list', u'check_subtitles': True}
```

subtitle download task:

```
2016-06-05 19:39 ERROR    subtitle_list download-subtitles File /mnt/data/Downloads/Movies/Beginners/Beginners (2010) - [720p bluray h264].mp4 does not exist. Removing from list.
2016-06-05 19:39 VERBOSE  details       download-subtitles Task didn't produce any entries.
2016-06-05 19:39 VERBOSE  details       download-subtitles Summary - Accepted: 0 (Rejected: 0 Undecided: 0 Failed: 0)
```
### Additional information:
- Flexget Version: 2.0.40
- Python Version: 2.7.11
- Installation method: pip
- OS and version: Fedora 23

Request - Add support to output path to subtitle_list plugin
### Expected behaviour:

In the former subtitle_queue plugin, we could add the subtitles to the queue with the destination path, instead of the current one, and with this, we could move the files and the add the subtitles to the queue in the same task, and the subtitles would be downloaded to the final path.
### Actual behaviour:

It doesnt support the path option anymore, so its going to emit paths that do not exist anymore, because the file was previouslly moved.
### Steps to reproduce:
- Step 1: Move a file and add it to the subtitle list in the same task
- Step 2: Emit the subtitle list
#### Config:

move task:

```
  move-movies:
    template:
      - disable-seen-retry
      - movies-metainfo
    filesystem:
      path: '/{{secrets.folder.root}}{{secrets.folder.downmovies}}'
      recursive: yes
      retrieve: files
      regexp: '.*\.(avi|mkv|mp4)$'
    accept_all: yes
    move:
      to: '/{{secrets.folder.root}}{{secrets.folder.movies}}'
      filename: "{{tmdb_name|pathscrub}} ({{tmdb_year}}){% if quality|default(False) %}: - [{{quality}}]{% endif %}"
      clean_source: 100
    list_add:
      - subtitle_list:
          list: subtitles-list
```

subtitle download task:

```
  download-subtitles:
    template:
      - disable-seen-retry
    subtitle_list:
      list: subtitles-list
    list_queue:
      - subtitle_list:
          list: subtitles-list
    subliminal:
      exact_match: yes
      languages:
        - eng
```
#### Log:

move task:

```
2016-06-05 19:34 VERBOSE  task          move-movies     ACCEPTED: `Beginners (2010) - [720p bluray h264]` by accept_all plugin
2016-06-05 19:34 VERBOSE  details       move-movies     Summary - Accepted: 1 (Rejected: 0 Undecided: 0 Failed: 0)
2016-06-05 19:34 VERBOSE  move          move-movies     Adding extension `.mp4` to dst `/mnt/data/Movies/Beginners (2010) - [720p bluray h264]`
2016-06-05 19:34 INFO     move          move-movies     `/mnt/data/Downloads/Movies/Beginners/Beginners (2010) - [720p bluray h264].mp4` has been moved to `/mnt/data/Movies/Beginners (2010) - [720p bluray h264].mp4`
2016-06-05 19:34 INFO     move          move-movies     Path `/mnt/data/Downloads/Movies/Beginners` has been deleted because was less than clean_source safe value.
2016-06-05 19:34 VERBOSE  list_add      move-movies     adding accepted entries into subtitle_list - {u'list': u'subtitles-list', u'check_subtitles': True}
```

subtitle download task:

```
2016-06-05 19:39 ERROR    subtitle_list download-subtitles File /mnt/data/Downloads/Movies/Beginners/Beginners (2010) - [720p bluray h264].mp4 does not exist. Removing from list.
2016-06-05 19:39 VERBOSE  details       download-subtitles Task didn't produce any entries.
2016-06-05 19:39 VERBOSE  details       download-subtitles Summary - Accepted: 0 (Rejected: 0 Undecided: 0 Failed: 0)
```
### Additional information:
- Flexget Version: 2.0.40
- Python Version: 2.7.11
- Installation method: pip
- OS and version: Fedora 23



Discussion:
Hmm, this is actually something I've discussed with @gazpachoking. It is my belief that `move` and `download` (and other such plugins) should set the `location` field rather than the `output` field.

This should help you achieve what you want, right?

It does make sense, but its not something that is already available, right?

What about tasks that deal with torrents tho?
In my case I'm using rTorrent; the download plugin may download the torrent file somewhere, but the path where the actual files will be downloaded is a different matter altogether.

God dammit, I removed that feature because it was a mess :D 

`subtitle_queue` allowed you to queue the torrent contents, but there's no standardized way of doing this what with `move_done`, multifile torrents, singlefile torrents etc. So I removed that feature from `subtitle_list`.

I could introduce a `path` config option that supports jinja replacement. Does that cater to you both?

I never used it together with move_done, so i had no idea, but that does sound like a problem. 

If the path does the same thing that it used to do in subtitle_queue, then yes, that would be great. 

Thanks 

I agree, the `path` property would make it simple enough to do what I need.
Thanks for taking my comment into consideration :D

I don't think `path` will suffice for what you want. `path` in your case will be a directory and not a file.

Well yeah, that's what I asumed. Isn't that how the `subtitle_queue`'s `path` works?

`subtitle_list` operates solely on files atm

Hmm, this is actually something I've discussed with @gazpachoking. It is my belief that `move` and `download` (and other such plugins) should set the `location` field rather than the `output` field.

This should help you achieve what you want, right?

It does make sense, but its not something that is already available, right?

What about tasks that deal with torrents tho?
In my case I'm using rTorrent; the download plugin may download the torrent file somewhere, but the path where the actual files will be downloaded is a different matter altogether.

God dammit, I removed that feature because it was a mess :D 

`subtitle_queue` allowed you to queue the torrent contents, but there's no standardized way of doing this what with `move_done`, multifile torrents, singlefile torrents etc. So I removed that feature from `subtitle_list`.

I could introduce a `path` config option that supports jinja replacement. Does that cater to you both?

I never used it together with move_done, so i had no idea, but that does sound like a problem. 

If the path does the same thing that it used to do in subtitle_queue, then yes, that would be great. 

Thanks 

I agree, the `path` property would make it simple enough to do what I need.
Thanks for taking my comment into consideration :D

I don't think `path` will suffice for what you want. `path` in your case will be a directory and not a file.

Well yeah, that's what I asumed. Isn't that how the `subtitle_queue`'s `path` works?

`subtitle_list` operates solely on files atm


Resolving patch:
diff --git a/flexget/plugins/list/subtitle_list.py b/flexget/plugins/list/subtitle_list.py
--- a/flexget/plugins/list/subtitle_list.py
+++ b/flexget/plugins/list/subtitle_list.py
@@ -1,5 +1,6 @@
 from __future__ import unicode_literals, division, absolute_import
 from builtins import *
+from past.builtins import basestring
 
 import logging
 import os
@@ -8,7 +9,6 @@
 
 from sqlalchemy import Column, Unicode, Integer, ForeignKey, func, DateTime, and_
 from sqlalchemy.orm import relationship
-from sqlalchemy.schema import Table
 from babelfish import Language
 
 from flexget import plugin
@@ -16,15 +16,30 @@
 from flexget.entry import Entry
 from flexget.event import event
 from flexget.utils.tools import parse_timedelta
+from flexget.utils.template import RenderError
 
 log = logging.getLogger('subtitle_list')
 Base = versioned_base('subtitle_list', 1)
 
 
+#: Video extensions stolen from https://github.com/Diaoul/subliminal/blob/master/subliminal/video.py
+VIDEO_EXTENSIONS = ('.3g2', '.3gp', '.3gp2', '.3gpp', '.60d', '.ajp', '.asf', '.asx', '.avchd', '.avi', '.bik',
+                    '.bix', '.box', '.cam', '.dat', '.divx', '.dmf', '.dv', '.dvr-ms', '.evo', '.flc', '.fli',
+                    '.flic', '.flv', '.flx', '.gvi', '.gvp', '.h264', '.m1v', '.m2p', '.m2ts', '.m2v', '.m4e',
+                    '.m4v', '.mjp', '.mjpeg', '.mjpg', '.mkv', '.moov', '.mov', '.movhd', '.movie', '.movx', '.mp4',
+                    '.mpe', '.mpeg', '.mpg', '.mpv', '.mpv2', '.mxf', '.nsv', '.nut', '.ogg', '.ogm', '.omf', '.ps',
+                    '.qt', '.ram', '.rm', '.rmvb', '.swf', '.ts', '.vfw', '.vid', '.video', '.viv', '.vivo', '.vob',
+                    '.vro', '.wm', '.wmv', '.wmx', '.wrap', '.wvx', '.wx', '.x264', '.xvid')
+
+
 def normalize_language(language):
     return str(Language.fromietf(language))
 
 
+def normalize_path(path):
+    return os.path.normpath(os.path.abspath(path)) if path else None
+
+
 class SubtitleListList(Base):
     __tablename__ = 'subtitle_list_lists'
     id = Column(Integer, primary_key=True)
@@ -94,7 +109,21 @@ class SubtitleList(MutableSet):
             'list': {'type': 'string'},
             'languages': {'type': 'array', 'items': {'type': 'string'}, 'minItems': 1},
             'check_subtitles': {'type': 'boolean', 'default': True},
-            'remove_after': {'type': 'string', 'format': 'interval'}
+            'remove_after': {'type': 'string', 'format': 'interval'},
+            'path': {
+                'oneOf': [
+                    {
+                        'type': 'object',
+                        'properties': {
+                            'from_field': {'type': 'string'},
+                        },
+                        'required': ['from_field']
+                    },
+                    {'type': 'string'}
+                ]
+            },
+            'allow_dir': {'type': 'boolean', 'default': False},
+            'force_file_existence': {'type': 'boolean', 'default': True}
         },
         'required': ['list'],
         'additionalProperties': False
@@ -122,8 +151,38 @@ def __iter__(self, session=None):
     def __len__(self, session=None):
         return self._db_list(session).files.count()
 
+    def _extract_path(self, entry):
+        path = ''
+        if isinstance(self.config.get('path'), dict):
+            try:
+                path = entry.render(entry.get(self.config['path']['from_field']))
+            except RenderError as e:
+                log.error(e)
+        elif isinstance(self.config.get('path'), basestring):
+            try:
+                path = entry.render(self.config['path'])
+            except RenderError as e:
+                log.error(e)
+        else:
+            path = entry.get('location')
+        return normalize_path(path)
+
     @with_session
     def add(self, entry, session=None):
+        path = self._extract_path(entry)
+
+        if not path:
+            log.error('Entry %s does not represent a local file/dir.')
+            return
+
+        path_exists = os.path.exists(path)
+        if self.config['force_file_existence'] and not path_exists:
+            log.error('Path %s does not exist. Not adding to list.', path)
+            return
+        elif path_exists and not self.config['allow_dir'] and os.path.isdir(path):
+            log.error('Path %s is a directory and "allow_dir"=%s.', path, self.config['allow_dir'])
+            return
+
         # Check if this is already in the list, refresh info if so
         db_list = self._db_list(session=session)
         db_file = self._find_entry(entry, session=session)
@@ -132,7 +191,7 @@ def add(self, entry, session=None):
             session.delete(db_file)
         db_file = SubtitleListFile()
         db_file.title = entry['title']
-        db_file.location = entry['location']
+        db_file.location = path
         db_file.languages = []
         db_file.remove_after = self.config.get('remove_after')
         db_file.languages = []
@@ -160,7 +219,11 @@ def __contains__(self, entry):
     @with_session
     def _find_entry(self, entry, session=None):
         """Finds `SubtitleListFile` corresponding to this entry, if it exists."""
-        res = self._db_list(session).files.filter(SubtitleListFile.location == entry.get('location', '')).first()
+        path = self._extract_path(entry)
+        res = self._db_list(session).files.filter(SubtitleListFile.location == path).first()
+        if not res:
+            path = os.path.dirname(path)
+            res = self._db_list(session).files.filter(SubtitleListFile.location == path).first()
         return res
 
     @with_session
@@ -196,28 +259,53 @@ def get_list(config):
 
     def on_task_input(self, task, config):
         subtitle_list = SubtitleList(config)
-        if config['check_subtitles']:
-            for file in subtitle_list:
-                if not os.path.isfile(file['location']):
-                    log.error('File %s does not exist. Removing from list.', file['location'])
-                    subtitle_list.discard(file)
-                elif self._expired(file, config):
-                    log.verbose('File %s has been in the list for %s. Removing from list.', file['location'],
-                                file['remove_after'] or config['remove_after'])
-                    subtitle_list.discard(file)
+        temp_discarded_items = set()
+        temp_items_from_dir = set()
+        for item in subtitle_list:
+            if not config['allow_dir'] and os.path.isdir(item['location']):
+                log.error('Path %s is a directory. If you wish to allow directories, change your config.',
+                          item['location'])
+                temp_discarded_items.add(item)
+            elif not config['force_file_existence'] and not os.path.exists(item['location']):
+                log.error('File %s does not exist. Skipping.', item['location'])
+                temp_discarded_items.add(item)
+            elif not os.path.exists(item['location']):
+                log.error('File %s does not exist. Removing from list.', item['location'])
+                subtitle_list.discard(item)
+            elif self._expired(item, config):
+                log.info('File %s has been in the list for %s. Removing from list.', item['location'],
+                         item['remove_after'] or config['remove_after'])
+                subtitle_list.discard(item)
+            else:
+                if os.path.isdir(item['location']):
+                    files = [os.path.join(item['location'], file) for file in os.listdir(item['location'])]
+                    temp_discarded_items.add(item)
                 else:
-                    try:
-                        import subliminal
-                        existing_subtitles = set(subliminal.core.search_external_subtitles(file['location']).values())
-                        wanted_languages = set(file['subtitle_languages']) or set(config.get('languages', []))
-                        if wanted_languages and len(wanted_languages - existing_subtitles) == 0:
-                            log.verbose('Local subtitle(s) already exists for %s. Removing from list.',
-                                        file['location'])
-                            subtitle_list.discard(file)
-                    except ImportError:
-                        log.warning('Subliminal not found. Unable to check for local subtitles.')
-
-        return list(subtitle_list)
+                    files = [item['location']]
+                number_of_files = len(files)
+                for file in files:
+                    if os.path.splitext(file)[1] not in VIDEO_EXTENSIONS:
+                        log.debug('File %s is not a video file. Skipping', file)
+                        number_of_files -= 1
+                        continue
+                    if config['check_subtitles']:
+                        try:
+                            import subliminal
+                            existing_subtitles = set(subliminal.core.search_external_subtitles(file).values())
+                            wanted_languages = set(item['subtitle_languages']) or set(config.get('languages', []))
+                            if wanted_languages and len(wanted_languages - existing_subtitles) == 0:
+                                log.info('Local subtitle(s) already exists for %s.', file)
+                                number_of_files -= 1
+                                continue
+                        except ImportError:
+                            log.warning('Subliminal not found. Unable to check for local subtitles.')
+                    if os.path.isdir(item['location']):
+                        temp_items_from_dir.add(Entry(title=os.path.splitext(os.path.basename(file))[0],
+                                                      url='file://' + file, location=file))
+                if number_of_files == 0:
+                    subtitle_list.discard(item)
+
+        return list((set(subtitle_list) | temp_items_from_dir) - temp_discarded_items)
 
     @classmethod
     def _expired(cls, file, config):
diff --git a/flexget/plugins/output/subtitles_subliminal.py b/flexget/plugins/output/subtitles_subliminal.py
--- a/flexget/plugins/output/subtitles_subliminal.py
+++ b/flexget/plugins/output/subtitles_subliminal.py
@@ -138,7 +138,7 @@ def on_task_output(self, task, config):
                         hash_scores = movie_scores['hash']
                     log.info('Name computed for %s was %s', entry['location'], title)
                     msc = hash_scores if config['exact_match'] else 0
-                    if entry_languages & existing_subtitles:
+                    if entry_languages == existing_subtitles:
                         log.debug('All preferred languages already exist for "%s"', entry['title'])
                         entry['subtitles_missing'] = set()
                         continue  # subs for preferred lang(s) already exists


Test patch:
diff --git a/tests/test_subtitle_list.py b/tests/test_subtitle_list.py
--- a/tests/test_subtitle_list.py
+++ b/tests/test_subtitle_list.py
@@ -57,7 +57,7 @@ class TestSubtitleList(object):
                - subtitle_list:
                    list: test
              rerun: 0
-           subtitle_success:
+           subtitle_simulate_success:
              template: no_global
              subtitle_list:
                list: test
@@ -120,6 +120,69 @@ class TestSubtitleList(object):
                - subtitle_list:
                    list: test
                    remove_after: 7 days
+           subtitle_add_local_dir:
+             disable: builtins
+             template: no_global
+             mock:
+               - {title: "My Videos", location: "subtitle_list_test_dir"}
+             list_add:
+               - subtitle_list:
+                   list: test
+                   allow_dir: yes
+                   languages: ['ja']
+             accept_all: yes
+           subtitle_emit_dir:
+             disable: builtins
+             template: no_global
+             subtitle_list:
+               list: test
+               allow_dir: yes
+               languages: [en]
+           subtitle_simulate_success_no_check:
+             template: no_global
+             subtitle_list:
+               list: test
+               allow_dir: yes
+               check_subtitles: no
+             subliminal:
+               languages: [ja]
+               exact_match: no
+               providers:
+                 - opensubtitles
+             list_queue:
+               - subtitle_list:
+                   list: test
+                   allow_dir: yes
+           subtitle_add_force_file:
+             disable: builtins
+             template: no_global
+             mock:
+               - {title: "The.Walking.Dead.S06E09-FlexGet",
+                  location: "subtitle_list_test_dir/The.Walking.Dead.S06E09-FlexGet.mp4"}
+             list_add:
+               - subtitle_list:
+                   list: test
+                   allow_dir: yes
+                   languages: ['ja']
+             accept_all: yes
+           subtitle_add_force_file_no:
+             disable: builtins
+             template: no_global
+             mock:
+               - {title: "The.Walking.Dead.S06E09-FlexGet",
+                  location: "subtitle_list_test_dir/The.Walking.Dead.S06E09-FlexGet.mp4"}
+             list_add:
+               - subtitle_list:
+                   list: test
+                   languages: ['ja']
+                   force_file_existence: no
+             accept_all: yes
+           subtitle_emit_force_no:
+             disable: builtins
+             template: no_global
+             subtitle_list:
+               list: test
+               force_file_existence: no
     """
 
     def test_subtitle_list_del(self, execute_task):
@@ -188,7 +251,16 @@ def test_subtitle_list_subliminal_success(self, execute_task):
         task = execute_task('subtitle_add_another_local_file')
         assert len(task.entries) == 1, 'Task should have accepted jessica jones file'
 
-        task = execute_task('subtitle_success')
+        with open('subtitle_list_test_dir/The.Walking.Dead.S06E08-FlexGet.en.srt', 'a'):
+            os.utime('subtitle_list_test_dir/The.Walking.Dead.S06E08-FlexGet.en.srt', None)
+
+        with open('subtitle_list_test_dir/The.Walking.Dead.S06E08-FlexGet.ja.srt', 'a'):
+            os.utime('subtitle_list_test_dir/The.Walking.Dead.S06E08-FlexGet.ja.srt', None)
+
+        with open('subtitle_list_test_dir/Marvels.Jessica.Jones.S01E02-FlexGet.en.srt', 'a'):
+            os.utime('subtitle_list_test_dir/Marvels.Jessica.Jones.S01E02-FlexGet.en.srt', None)
+
+        task = execute_task('subtitle_simulate_success')
         assert len(task.failed) == 1, 'Should have found both languages for walking dead but not for jessica jones'
 
         task = execute_task('subtitle_emit')
@@ -216,3 +288,103 @@ def test_subtitle_list_local_subtitles(self, execute_task):
 
         task = execute_task('subtitle_emit')
         assert len(task.entries) == 2, 'Big Bang Theory already has a local subtitle and should have been removed.'
+
+    def test_subtitle_list_local_dir(self, execute_task):
+        task = execute_task('subtitle_add_local_dir')
+
+        with Session() as session:
+            s = session.query(SubtitleListFile).first()
+            assert s.title == 'My Videos', 'Should have added the dir with title "My Videos" to the list'
+
+        task = execute_task('subtitle_emit_dir')
+
+        assert len(task.entries) == 3, 'Should have found 3 video files and the containing dir should not be included.'
+
+    def test_subtitle_list_local_disallow_dir(self, execute_task):
+        task = execute_task('subtitle_add_local_dir')
+
+        task = execute_task('subtitle_emit')
+
+        assert len(task.entries) == 0, 'Dirs are not allowed'
+
+    # Skip if subliminal is not installed or if python version <2.7
+    @pytest.mark.skipif(sys.version_info < (2, 7), reason='requires python2.7')
+    @pytest.mark.skipif(not subliminal, reason='requires subliminal')
+    def test_subtitle_list_subliminal_dir_success(self, execute_task):
+        task = execute_task('subtitle_add_local_dir')
+
+        with open('subtitle_list_test_dir/The.Walking.Dead.S06E08-FlexGet.ja.srt', 'a'):
+            os.utime('subtitle_list_test_dir/The.Walking.Dead.S06E08-FlexGet.ja.srt', None)
+
+        with open('subtitle_list_test_dir/Marvels.Jessica.Jones.S01E02-FlexGet.ja.srt', 'a'):
+            os.utime('subtitle_list_test_dir/Marvels.Jessica.Jones.S01E02-FlexGet.ja.srt', None)
+
+        with open('subtitle_list_test_dir/The.Big.Bang.Theory.S09E09-FlexGet.ja.srt', 'a'):
+            os.utime('subtitle_list_test_dir/The.Big.Bang.Theory.S09E09-FlexGet.ja.srt', None)
+
+        task = execute_task('subtitle_simulate_success_no_check')
+        assert len(task.accepted) == 1, 'All files have subtitles, but list_queue can only accept one'
+
+        with Session() as session:
+            s = session.query(SubtitleListFile).first()
+            assert s is None, '"My Videos" should have been deleted from the list'
+
+        try:
+            os.remove('subtitle_list_test_dir/The.Walking.Dead.S06E08-FlexGet.ja.srt')
+        except OSError:
+            pass
+        try:
+            os.remove('subtitle_list_test_dir/Marvels.Jessica.Jones.S01E02-FlexGet.ja.srt')
+        except OSError:
+            pass
+        try:
+            os.remove('subtitle_list_test_dir/The.Big.Bang.Theory.S09E09-FlexGet.ja.srt')
+        except OSError:
+            pass
+
+    def test_subtitle_list_force_file_existence_no(self, execute_task):
+        task = execute_task('subtitle_add_force_file_no')
+
+        assert not os.path.exists(task.entries[0]['location']), 'File should not exist.'
+
+        with Session() as session:
+            s = session.query(SubtitleListFile).first()
+            assert s, 'The file should have been added to the list even though it does not exist'
+
+        task = execute_task('subtitle_emit_force_no')
+
+        assert len(task.entries) == 0, 'List should not be empty, but since file does not exist it isn\' returned'
+
+        with Session() as session:
+            s = session.query(SubtitleListFile).first()
+            assert s, 'The file should still be in the list'
+
+    def test_subtitle_list_force_file_existence_yes(self, execute_task):
+        task = execute_task('subtitle_add_force_file')
+
+        assert not os.path.exists(task.entries[0]['location']), 'File should not exist.'
+
+        with Session() as session:
+            s = session.query(SubtitleListFile).first()
+            assert s is None, 'The file should not have been added to the list as it does not exist'
+
+        task = execute_task('subtitle_emit')
+
+        assert len(task.entries) == 0, 'List should be empty'
+
+    def test_subtitle_list_force_file_existence_yes_input(self, execute_task):
+        task = execute_task('subtitle_add_force_file_no')
+
+        assert not os.path.exists(task.entries[0]['location']), 'File should not exist.'
+
+        with Session() as session:
+            s = session.query(SubtitleListFile).first()
+            assert s, 'The file should have been added to the list even though it does not exist'
+
+        task = execute_task('subtitle_emit')
+
+        assert len(task.entries) == 0, 'No input should be returned as the file does not exist'
+
+        with Session() as session:
+            s = session.query(SubtitleListFile).first()
+            assert s is None, 'The file should have been removed from the list since it does not exist'