# Buggy code
def process(self):
    """
    Post-process a given file.

    :return: True on success, False on failure
    """
    self.log("Processing {0}".format(self.file_path))

    if os.path.isdir(self.file_path):
        self.log("File {0} seems to be a directory".format(self.file_path))
        return False

    if not os.path.exists(self.file_path):
        raise EpisodePostProcessingFailedException(
            "File {0} doesn't exist, did unrar fail?".format(self.file_path)
        )

    for ignore_file in self.IGNORED_FILESTRINGS:
        if ignore_file in self.file_path:
            self.log("File {0} is ignored type, skipping".format(self.file_path))
            return False

    # reset in_history
    self.in_history = False

    # reset the anidb episode object
    self.anidbEpisode = None

    # try to find the file info
    (show, season, episodes, quality, version) = self._find_info()
    if not show:
        raise EpisodePostProcessingFailedException(
            "This show isn't in your list, you need to add it "
            "before post-processing an episode"
        )
    elif season is None or not episodes:
        raise EpisodePostProcessingFailedException(
            "Not enough information to determine what episode this is"
        )

    # retrieve/create the corresponding Episode objects
    ep_obj = self._get_ep_obj(show, season, episodes)
    _, old_ep_quality = common.Quality.split_composite_status(ep_obj.status)

    # get the quality of the episode we're processing
    if quality and common.Quality.qualityStrings[quality] != "Unknown":
        self.log(
            "The episode file has a quality in it, using that: {0}".format(
                common.Quality.qualityStrings[quality]
            ),
            logger.DEBUG,
        )
        new_ep_quality = quality
    else:
        new_ep_quality = self._quality_from_status(ep_obj.status)

    # check snatched history to see if we should set the download as priority
    self._priority_from_history(show.indexerid, season, episodes, new_ep_quality)
    if self.in_history:
        self.log("This episode was found in history as SNATCHED.", logger.DEBUG)

    if new_ep_quality == common.Quality.UNKNOWN:
        new_ep_quality = self._get_quality(ep_obj)

    logger.log(
        "Quality of the episode we are processing: {0}".format(
            common.Quality.qualityStrings[new_ep_quality]
        ),
        logger.DEBUG,
    )

    # see if this is a priority download (is it snatched, in history, PROPER, or BEST)
    priority_download = self._is_priority(old_ep_quality, new_ep_quality)
    self.log(
        "This episode is a priority download: {0}".format(priority_download),
        logger.DEBUG,
    )

    # get the version of the episode we're processing (default is -1)
    if version != -1:
        self.log(
            "Episode has a version in it, using that: v{0}".format(version),
            logger.DEBUG,
        )
    new_ep_version = version

    # check for an existing file
    existing_file_status = self._compare_file_size(ep_obj.location)

    if not priority_download:
        if existing_file_status == PostProcessor.EXISTS_SAME:
            self.log(
                "File exists and the new file has the same size, aborting post-processing"
            )
            return True

        if existing_file_status != PostProcessor.DOESNT_EXIST:
            if self.is_proper and new_ep_quality == old_ep_quality:
                self.log("New file is a PROPER, marking it safe to replace")
                self.flag_kodi_clean_library()
            else:
                allowed_qualities, preferred_qualities = show.current_qualities
                self.log(
                    "Checking if new quality {0} should replace current quality: {1}".format(
                        common.Quality.qualityStrings[new_ep_quality],
                        common.Quality.qualityStrings[old_ep_quality],
                    )
                )
                should_process, should_process_reason = self._should_process(
                    old_ep_quality,
                    new_ep_quality,
                    allowed_qualities,
                    preferred_qualities,
                )
                if not should_process:
                    raise EpisodePostProcessingFailedException(
                        "File exists. Marking it unsafe to replace. Reason: {0}".format(
                            should_process_reason
                        )
                    )
                else:
                    self.log(
                        "File exists. Marking it safe to replace. Reason: {0}".format(
                            should_process_reason
                        )
                    )
                    self.flag_kodi_clean_library()

        # Check if the processed file season is already in our indexer. If not,
        # the file is most probably mislabled/fake and will be skipped.
        # Only proceed if the file season is > 0
        if int(ep_obj.season) > 0:
            main_db_con = db.DBConnection()
            max_season = main_db_con.select(
                "SELECT MAX(season) FROM tv_episodes WHERE showid = ? and indexer = ?",
                [show.indexerid, show.indexer],
            )

            # If the file season (ep_obj.season) is bigger than
            # the indexer season (max_season[0][0]), skip the file
            if int(ep_obj.season) > int(max_season[0][0]):
                self.log(
                    "File has season {0}, while the indexer is on season {1}. "
                    "The file may be incorrectly labeled or fake, aborting.".format(
                        ep_obj.season, max_season[0][0]
                    )
                )
                return False

    # if the file is priority then we're going to replace it even if it exists
    else:
        # Set to clean Kodi if file exists and it is priority_download
        if existing_file_status != PostProcessor.DOESNT_EXIST:
            self.flag_kodi_clean_library()
        self.log(
            "This download is marked a priority download so I'm going to replace "
            "an existing file if I find one"
        )

    # try to find out if we have enough space to perform the copy or move action.
    if not helpers.is_file_locked(self.file_path, False):
        if not verify_freespace(
            self.file_path, ep_obj.series._location, [ep_obj] + ep_obj.related_episodes
        ):
            self.log(
                "Not enough space to continue post-processing, exiting", logger.WARNING
            )
            return False
    else:
        self.log(
            "Unable to determine needed filespace as the source file is locked for access"
        )

    # delete the existing file (and company)
    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        try:
            self._delete(cur_ep.location, associated_files=True)
            # clean up any left over folders
            if cur_ep.location:
                helpers.delete_empty_folders(
                    os.path.dirname(cur_ep.location), keep_dir=ep_obj.series._location
                )
        except (OSError, IOError):
            raise EpisodePostProcessingFailedException(
                "Unable to delete the existing files"
            )

        # set the status of the episodes
        # for cur_ep in [ep_obj] + ep_obj.related_episodes:
        #    cur_ep.status = common.Quality.composite_status(common.SNATCHED, new_ep_quality)

    # if the show directory doesn't exist then make it if desired
    if not os.path.isdir(ep_obj.series._location) and app.CREATE_MISSING_SHOW_DIRS:
        self.log("Show directory doesn't exist, creating it", logger.DEBUG)
        try:
            os.mkdir(ep_obj.series._location)
            helpers.chmod_as_parent(ep_obj.series._location)

            # do the library update for synoindex
            notifiers.synoindex_notifier.addFolder(ep_obj.series._location)
        except (OSError, IOError):
            raise EpisodePostProcessingFailedException(
                "Unable to create the show directory: {0}".format(
                    ep_obj.series._location
                )
            )

        # get metadata for the show (but not episode because it hasn't been fully processed)
        ep_obj.series.write_metadata(True)

    # update the ep info before we rename so the quality & release name go into the name properly
    sql_l = []

    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        with cur_ep.lock:
            if self.release_name:
                self.log(
                    "Found release name {0}".format(self.release_name), logger.DEBUG
                )
                cur_ep.release_name = self.release_name
            elif self.file_name:
                # If we can't get the release name we expect, save the original release name instead
                self.log(
                    "Using original release name {0}".format(self.file_name),
                    logger.DEBUG,
                )
                cur_ep.release_name = self.file_name
            else:
                cur_ep.release_name = ""

            cur_ep.status = common.Quality.composite_status(
                common.DOWNLOADED, new_ep_quality
            )

            cur_ep.subtitles = ""

            cur_ep.subtitles_searchcount = 0

            cur_ep.subtitles_lastsearch = "0001-01-01 00:00:00"

            cur_ep.is_proper = self.is_proper

            cur_ep.version = new_ep_version

            if self.release_group:
                cur_ep.release_group = self.release_group
            else:
                cur_ep.release_group = ""

            sql_l.append(cur_ep.get_sql())

    # Just want to keep this consistent for failed handling right now
    nzb_release_name = naming.determine_release_name(self.folder_path, self.nzb_name)
    if nzb_release_name is not None:
        failed_history.log_success(nzb_release_name)
    else:
        self.log("Couldn't determine NZB release name, aborting", logger.WARNING)

    # find the destination folder
    try:
        proper_path = ep_obj.proper_path()
        proper_absolute_path = os.path.join(ep_obj.series.location, proper_path)
        dest_path = os.path.dirname(proper_absolute_path)
    except ShowDirectoryNotFoundException:
        raise EpisodePostProcessingFailedException(
            "Unable to post-process an episode if the show dir '{0}' "
            "doesn't exist, quitting".format(ep_obj.series.raw_location)
        )

    self.log("Destination folder for this episode: {0}".format(dest_path), logger.DEBUG)

    # create any folders we need
    if not helpers.make_dirs(dest_path):
        raise EpisodePostProcessingFailedException(
            "Unable to create destination folder to the files"
        )

    # figure out the base name of the resulting episode file
    if app.RENAME_EPISODES:
        orig_extension = self.file_name.rpartition(".")[-1]
        new_base_name = os.path.basename(proper_path)
        new_file_name = new_base_name + "." + orig_extension

    else:
        # if we're not renaming then there's no new base name, we'll just use the existing name
        new_base_name = None
        new_file_name = self.file_name

    # add to anidb
    if ep_obj.series.is_anime and app.ANIDB_USE_MYLIST:
        self._add_to_anidb_mylist(self.file_path)

    try:
        # do the action to the episode and associated files to the show dir
        if self.process_method in ["copy", "hardlink", "move", "symlink"]:
            if not self.process_method == "hardlink":
                if helpers.is_file_locked(self.file_path, False):
                    raise EpisodePostProcessingFailedException(
                        "File is locked for reading"
                    )

            self.post_process_action(
                self.file_path,
                dest_path,
                new_base_name,
                app.MOVE_ASSOCIATED_FILES,
                app.USE_SUBTITLES and ep_obj.series.subtitles,
            )
        else:
            logger.log(
                "'{0}' is an unknown file processing method. "
                "Please correct your app's usage of the API.".format(
                    self.process_method
                ),
                logger.WARNING,
            )
            raise EpisodePostProcessingFailedException(
                "Unable to move the files to their new home"
            )
    except (OSError, IOError):
        raise EpisodePostProcessingFailedException(
            "Unable to move the files to their new home"
        )

    # download subtitles
    if app.USE_SUBTITLES and ep_obj.series.subtitles:
        for cur_ep in [ep_obj] + ep_obj.related_episodes:
            with cur_ep.lock:
                cur_ep.location = os.path.join(dest_path, new_file_name)
                cur_ep.refresh_subtitles()
                cur_ep.download_subtitles()

    # now that processing has finished, we can put the info in the DB.
    # If we do it earlier, then when processing fails, it won't try again.
    if sql_l:
        main_db_con = db.DBConnection()
        main_db_con.mass_action(sql_l)

    # put the new location in the database
    sql_l = []
    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        with cur_ep.lock:
            cur_ep.location = os.path.join(dest_path, new_file_name)
            sql_l.append(cur_ep.get_sql())

    if sql_l:
        main_db_con = db.DBConnection()
        main_db_con.mass_action(sql_l)

    cur_ep.airdate_modify_stamp()

    # generate nfo/tbn
    try:
        ep_obj.create_meta_files()
    except Exception:
        logger.log(
            "Could not create/update meta files. Continuing with post-processing..."
        )

    # log it to history episode and related episodes (multi-episode for example)
    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        history.log_download(
            cur_ep, self.file_path, new_ep_quality, self.release_group, new_ep_version
        )

    # send notifications
    notifiers.notify_download(ep_obj._format_pattern("%SN - %Sx%0E - %EN - %QN"))
    # do the library update for KODI
    notifiers.kodi_notifier.update_library(ep_obj.series.name)
    # do the library update for Plex
    notifiers.plex_notifier.update_library(ep_obj)
    # do the library update for EMBY
    notifiers.emby_notifier.update_library(ep_obj.series)
    # do the library update for NMJ
    # nmj_notifier kicks off its library update when the notify_download is issued (inside notifiers)
    # do the library update for Synology Indexer
    notifiers.synoindex_notifier.addFile(ep_obj.location)
    # do the library update for pyTivo
    notifiers.pytivo_notifier.update_library(ep_obj)
    # do the library update for Trakt
    notifiers.trakt_notifier.update_library(ep_obj)

    self._run_extra_scripts(ep_obj)

    # Store self.info_hash and self.release_name so later we can remove from client if setting is enabled
    if self.info_hash:
        existing_release_names = app.RECENTLY_POSTPROCESSED.get(self.info_hash, [])
        existing_release_names.append(self.release_name)
        app.RECENTLY_POSTPROCESSED[self.info_hash] = existing_release_names

    return True


# Traceback (TypeError)
2017-08-21 12:15:30 ERROR    Thread-16 :: [49e9fc3] Exception generated: sequence item 0: expected string or Unicode, NoneType found
Traceback (most recent call last):
File "/app/medusa/medusa/server/web/core/base.py", line 258, in async_call
result = function(**kwargs)
File "/app/medusa/medusa/server/web/home/post_process.py", line 50, in processEpisode
ignore_subs=argToBool(ignore_subs)
File "/app/medusa/medusa/process_tv.py", line 202, in process
if self.move_torrent(info_hash, release_names):
File "/app/medusa/medusa/process_tv.py", line 645, in move_torrent
release_names = ', '.join(release_names)
TypeError: sequence item 0: expected string or Unicode, NoneType found
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] Successfully processed.
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] No processable items found in folder: /mnt/user/Downloads/DONE/tvshows/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta/Screens
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] Processing succeeded for /mnt/user/Downloads/DONE/tvshows/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] File exists and the new file has the same size, aborting post-processing
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] New file is the same size.
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] New size: 517.11 MB
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] Old size: 517.11 MB
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] New file: /mnt/user/Downloads/DONE/tvshows/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] Old file: /mnt/user/TV Shows/Wayward Pines/Wayward Pines - S2E10 - Bedtime Story - 1080p HDTV.mkv
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] This episode is a priority download: False
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Proper: False
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] New quality: 1080p HDTV
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Current quality: 1080p HDTV
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Manually snatched: False
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Snatch in history: False
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Quality of the episode we are processing: 1080p HDTV
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Looks like 'Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv' has quality 1080p HDTV, using that
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Trying other methods to get quality for 'Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv', got 1080p HDTV
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Skipping rule language since property is already present
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Handling subtitle track
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Skipping rule language since property is already present
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Handling audio track
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Skipping rule language since property is already present
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Handling video track
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Handling general track
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Looking up quality for 'Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv', got Unknown
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Looking up quality for 'Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv', got Unknown
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Retrieving episode object for Wayward Pines S02E10
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Using cached parse result for Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Analyzing name: Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Using cached parse result for Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Analyzing name: Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv
2017-08-21 12:15:30 INFO     Thread-16 :: [49e9fc3] Processing /mnt/user/Downloads/DONE/tvshows/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Post-processing video files: [u'Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv']
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Post-processing files: [u'Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta.mkv']
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Processing folder: /mnt/user/Downloads/DONE/tvshows/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta
2017-08-21 12:15:30 DEBUG    Thread-16 :: [49e9fc3] Processing path: /mnt/user/Downloads/DONE/tvshows/Wayward.Pines.S02E10.1080p.HEVC.x265-MeGusta

# Fixed code
def process(self):
    """
    Post-process a given file.

    :return: True on success, False on failure
    """
    self.log("Processing {0}".format(self.file_path))

    if os.path.isdir(self.file_path):
        self.log("File {0} seems to be a directory".format(self.file_path))
        return False

    if not os.path.exists(self.file_path):
        raise EpisodePostProcessingFailedException(
            "File {0} doesn't exist, did unrar fail?".format(self.file_path)
        )

    for ignore_file in self.IGNORED_FILESTRINGS:
        if ignore_file in self.file_path:
            self.log("File {0} is ignored type, skipping".format(self.file_path))
            return False

    # reset in_history
    self.in_history = False

    # reset the anidb episode object
    self.anidbEpisode = None

    # try to find the file info
    (show, season, episodes, quality, version) = self._find_info()
    if not show:
        raise EpisodePostProcessingFailedException(
            "This show isn't in your list, you need to add it "
            "before post-processing an episode"
        )
    elif season is None or not episodes:
        raise EpisodePostProcessingFailedException(
            "Not enough information to determine what episode this is"
        )

    # retrieve/create the corresponding Episode objects
    ep_obj = self._get_ep_obj(show, season, episodes)
    _, old_ep_quality = common.Quality.split_composite_status(ep_obj.status)

    # get the quality of the episode we're processing
    if quality and common.Quality.qualityStrings[quality] != "Unknown":
        self.log(
            "The episode file has a quality in it, using that: {0}".format(
                common.Quality.qualityStrings[quality]
            ),
            logger.DEBUG,
        )
        new_ep_quality = quality
    else:
        new_ep_quality = self._quality_from_status(ep_obj.status)

    # check snatched history to see if we should set the download as priority
    self._priority_from_history(show.indexerid, season, episodes, new_ep_quality)
    if self.in_history:
        self.log("This episode was found in history as SNATCHED.", logger.DEBUG)

    if new_ep_quality == common.Quality.UNKNOWN:
        new_ep_quality = self._get_quality(ep_obj)

    logger.log(
        "Quality of the episode we are processing: {0}".format(
            common.Quality.qualityStrings[new_ep_quality]
        ),
        logger.DEBUG,
    )

    # see if this is a priority download (is it snatched, in history, PROPER, or BEST)
    priority_download = self._is_priority(old_ep_quality, new_ep_quality)
    self.log(
        "This episode is a priority download: {0}".format(priority_download),
        logger.DEBUG,
    )

    # get the version of the episode we're processing (default is -1)
    if version != -1:
        self.log(
            "Episode has a version in it, using that: v{0}".format(version),
            logger.DEBUG,
        )
    new_ep_version = version

    # check for an existing file
    existing_file_status = self._compare_file_size(ep_obj.location)

    if not priority_download:
        if existing_file_status == PostProcessor.EXISTS_SAME:
            self.log(
                "File exists and the new file has the same size, aborting post-processing"
            )
            return True

        if existing_file_status != PostProcessor.DOESNT_EXIST:
            if self.is_proper and new_ep_quality == old_ep_quality:
                self.log("New file is a PROPER, marking it safe to replace")
                self.flag_kodi_clean_library()
            else:
                allowed_qualities, preferred_qualities = show.current_qualities
                self.log(
                    "Checking if new quality {0} should replace current quality: {1}".format(
                        common.Quality.qualityStrings[new_ep_quality],
                        common.Quality.qualityStrings[old_ep_quality],
                    )
                )
                should_process, should_process_reason = self._should_process(
                    old_ep_quality,
                    new_ep_quality,
                    allowed_qualities,
                    preferred_qualities,
                )
                if not should_process:
                    raise EpisodePostProcessingFailedException(
                        "File exists. Marking it unsafe to replace. Reason: {0}".format(
                            should_process_reason
                        )
                    )
                else:
                    self.log(
                        "File exists. Marking it safe to replace. Reason: {0}".format(
                            should_process_reason
                        )
                    )
                    self.flag_kodi_clean_library()

        # Check if the processed file season is already in our indexer. If not,
        # the file is most probably mislabled/fake and will be skipped.
        # Only proceed if the file season is > 0
        if int(ep_obj.season) > 0:
            main_db_con = db.DBConnection()
            max_season = main_db_con.select(
                "SELECT MAX(season) FROM tv_episodes WHERE showid = ? and indexer = ?",
                [show.indexerid, show.indexer],
            )

            # If the file season (ep_obj.season) is bigger than
            # the indexer season (max_season[0][0]), skip the file
            if int(ep_obj.season) > int(max_season[0][0]):
                self.log(
                    "File has season {0}, while the indexer is on season {1}. "
                    "The file may be incorrectly labeled or fake, aborting.".format(
                        ep_obj.season, max_season[0][0]
                    )
                )
                return False

    # if the file is priority then we're going to replace it even if it exists
    else:
        # Set to clean Kodi if file exists and it is priority_download
        if existing_file_status != PostProcessor.DOESNT_EXIST:
            self.flag_kodi_clean_library()
        self.log(
            "This download is marked a priority download so I'm going to replace "
            "an existing file if I find one"
        )

    # try to find out if we have enough space to perform the copy or move action.
    if not helpers.is_file_locked(self.file_path, False):
        if not verify_freespace(
            self.file_path, ep_obj.series._location, [ep_obj] + ep_obj.related_episodes
        ):
            self.log(
                "Not enough space to continue post-processing, exiting", logger.WARNING
            )
            return False
    else:
        self.log(
            "Unable to determine needed filespace as the source file is locked for access"
        )

    # delete the existing file (and company)
    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        try:
            self._delete(cur_ep.location, associated_files=True)
            # clean up any left over folders
            if cur_ep.location:
                helpers.delete_empty_folders(
                    os.path.dirname(cur_ep.location), keep_dir=ep_obj.series._location
                )
        except (OSError, IOError):
            raise EpisodePostProcessingFailedException(
                "Unable to delete the existing files"
            )

        # set the status of the episodes
        # for cur_ep in [ep_obj] + ep_obj.related_episodes:
        #    cur_ep.status = common.Quality.composite_status(common.SNATCHED, new_ep_quality)

    # if the show directory doesn't exist then make it if desired
    if not os.path.isdir(ep_obj.series._location) and app.CREATE_MISSING_SHOW_DIRS:
        self.log("Show directory doesn't exist, creating it", logger.DEBUG)
        try:
            os.mkdir(ep_obj.series._location)
            helpers.chmod_as_parent(ep_obj.series._location)

            # do the library update for synoindex
            notifiers.synoindex_notifier.addFolder(ep_obj.series._location)
        except (OSError, IOError):
            raise EpisodePostProcessingFailedException(
                "Unable to create the show directory: {0}".format(
                    ep_obj.series._location
                )
            )

        # get metadata for the show (but not episode because it hasn't been fully processed)
        ep_obj.series.write_metadata(True)

    # update the ep info before we rename so the quality & release name go into the name properly
    sql_l = []

    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        with cur_ep.lock:
            if self.release_name:
                self.log(
                    "Found release name {0}".format(self.release_name), logger.DEBUG
                )
                cur_ep.release_name = self.release_name
            elif self.file_name:
                # If we can't get the release name we expect, save the original release name instead
                self.log(
                    "Using original release name {0}".format(self.file_name),
                    logger.DEBUG,
                )
                cur_ep.release_name = self.file_name
            else:
                cur_ep.release_name = ""

            cur_ep.status = common.Quality.composite_status(
                common.DOWNLOADED, new_ep_quality
            )

            cur_ep.subtitles = ""

            cur_ep.subtitles_searchcount = 0

            cur_ep.subtitles_lastsearch = "0001-01-01 00:00:00"

            cur_ep.is_proper = self.is_proper

            cur_ep.version = new_ep_version

            if self.release_group:
                cur_ep.release_group = self.release_group
            else:
                cur_ep.release_group = ""

            sql_l.append(cur_ep.get_sql())

    # Just want to keep this consistent for failed handling right now
    nzb_release_name = naming.determine_release_name(self.folder_path, self.nzb_name)
    if nzb_release_name is not None:
        failed_history.log_success(nzb_release_name)
    else:
        self.log("Couldn't determine NZB release name, aborting", logger.WARNING)

    # find the destination folder
    try:
        proper_path = ep_obj.proper_path()
        proper_absolute_path = os.path.join(ep_obj.series.location, proper_path)
        dest_path = os.path.dirname(proper_absolute_path)
    except ShowDirectoryNotFoundException:
        raise EpisodePostProcessingFailedException(
            "Unable to post-process an episode if the show dir '{0}' "
            "doesn't exist, quitting".format(ep_obj.series.raw_location)
        )

    self.log("Destination folder for this episode: {0}".format(dest_path), logger.DEBUG)

    # create any folders we need
    if not helpers.make_dirs(dest_path):
        raise EpisodePostProcessingFailedException(
            "Unable to create destination folder to the files"
        )

    # figure out the base name of the resulting episode file
    if app.RENAME_EPISODES:
        orig_extension = self.file_name.rpartition(".")[-1]
        new_base_name = os.path.basename(proper_path)
        new_file_name = new_base_name + "." + orig_extension

    else:
        # if we're not renaming then there's no new base name, we'll just use the existing name
        new_base_name = None
        new_file_name = self.file_name

    # add to anidb
    if ep_obj.series.is_anime and app.ANIDB_USE_MYLIST:
        self._add_to_anidb_mylist(self.file_path)

    try:
        # do the action to the episode and associated files to the show dir
        if self.process_method in ["copy", "hardlink", "move", "symlink"]:
            if not self.process_method == "hardlink":
                if helpers.is_file_locked(self.file_path, False):
                    raise EpisodePostProcessingFailedException(
                        "File is locked for reading"
                    )

            self.post_process_action(
                self.file_path,
                dest_path,
                new_base_name,
                app.MOVE_ASSOCIATED_FILES,
                app.USE_SUBTITLES and ep_obj.series.subtitles,
            )
        else:
            logger.log(
                "'{0}' is an unknown file processing method. "
                "Please correct your app's usage of the API.".format(
                    self.process_method
                ),
                logger.WARNING,
            )
            raise EpisodePostProcessingFailedException(
                "Unable to move the files to their new home"
            )
    except (OSError, IOError):
        raise EpisodePostProcessingFailedException(
            "Unable to move the files to their new home"
        )

    # download subtitles
    if app.USE_SUBTITLES and ep_obj.series.subtitles:
        for cur_ep in [ep_obj] + ep_obj.related_episodes:
            with cur_ep.lock:
                cur_ep.location = os.path.join(dest_path, new_file_name)
                cur_ep.refresh_subtitles()
                cur_ep.download_subtitles()

    # now that processing has finished, we can put the info in the DB.
    # If we do it earlier, then when processing fails, it won't try again.
    if sql_l:
        main_db_con = db.DBConnection()
        main_db_con.mass_action(sql_l)

    # put the new location in the database
    sql_l = []
    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        with cur_ep.lock:
            cur_ep.location = os.path.join(dest_path, new_file_name)
            sql_l.append(cur_ep.get_sql())

    if sql_l:
        main_db_con = db.DBConnection()
        main_db_con.mass_action(sql_l)

    cur_ep.airdate_modify_stamp()

    # generate nfo/tbn
    try:
        ep_obj.create_meta_files()
    except Exception:
        logger.log(
            "Could not create/update meta files. Continuing with post-processing..."
        )

    # log it to history episode and related episodes (multi-episode for example)
    for cur_ep in [ep_obj] + ep_obj.related_episodes:
        history.log_download(
            cur_ep, self.file_path, new_ep_quality, self.release_group, new_ep_version
        )

    # send notifications
    notifiers.notify_download(ep_obj._format_pattern("%SN - %Sx%0E - %EN - %QN"))
    # do the library update for KODI
    notifiers.kodi_notifier.update_library(ep_obj.series.name)
    # do the library update for Plex
    notifiers.plex_notifier.update_library(ep_obj)
    # do the library update for EMBY
    notifiers.emby_notifier.update_library(ep_obj.series)
    # do the library update for NMJ
    # nmj_notifier kicks off its library update when the notify_download is issued (inside notifiers)
    # do the library update for Synology Indexer
    notifiers.synoindex_notifier.addFile(ep_obj.location)
    # do the library update for pyTivo
    notifiers.pytivo_notifier.update_library(ep_obj)
    # do the library update for Trakt
    notifiers.trakt_notifier.update_library(ep_obj)

    self._run_extra_scripts(ep_obj)

    # Store self.info_hash and self.release_name so later we can remove from client if setting is enabled
    if self.info_hash:
        existing_release_names = app.RECENTLY_POSTPROCESSED.get(self.info_hash, [])
        existing_release_names.append(self.release_name or "N/A")
        app.RECENTLY_POSTPROCESSED[self.info_hash] = existing_release_names
    else:
        logger.log(
            "Unable to get info to move torrent later as no info hash available for: {0}".format(
                self.file_path
            ),
            logger.WARNING,
        )

    return True