// returns false if we should retry
        private bool MoveFileIfRequired(bool deleteEmpty = true)
        {
            // TODO move A LOT of this into renamer helper methods. A renamer can do them optionally
            if (!ServerSettings.Instance.Import.MoveOnImport)
            {
                logger.Trace($"Skipping move of \"{this.FullServerPath}\" as move on import is disabled");
                return(true);
            }

            // TODO Make this take an argument to disable removing empty dirs. It's slow, and should only be done if needed
            try
            {
                logger.Trace($"Attempting to MOVE file: \"{FullServerPath ?? VideoLocal_Place_ID.ToString()}\"");

                if (FullServerPath == null)
                {
                    logger.Error($"Could not find or access the file to move: {VideoLocal_Place_ID}");
                    return(true);
                }

                // check if this file is in the drop folder
                // otherwise we don't need to move it
                if (ImportFolder.IsDropSource == 0)
                {
                    logger.Trace($"Not moving file as it is NOT in the drop folder: \"{FullServerPath}\"");
                    return(true);
                }

                if (!File.Exists(FullServerPath))
                {
                    logger.Error($"Could not find or access the file to move: \"{FullServerPath}\"");
                    // this can happen due to file locks, so retry
                    return(false);
                }
                var sourceFile = new FileInfo(FullServerPath);

                // find the default destination
                (var destImpl, string newFolderPath) = RenameFileHelper.GetDestination(this, null);

                if (!(destImpl is SVR_ImportFolder destFolder))
                {
                    // In this case, an error string was returned, but we'll suppress it and give an error elsewhere
                    if (newFolderPath != null)
                    {
                        return(true);
                    }
                    logger.Error($"Could not find a valid destination: \"{FullServerPath}\"");
                    return(true);
                }

                // keep the original drop folder for later (take a copy, not a reference)
                SVR_ImportFolder dropFolder = ImportFolder;

                if (string.IsNullOrEmpty(newFolderPath))
                {
                    return(true);
                }

                // We've already resolved FullServerPath, so it doesn't need to be checked
                string newFilePath       = Path.Combine(newFolderPath, Path.GetFileName(FullServerPath));
                string newFullServerPath = Path.Combine(destFolder.ImportFolderLocation, newFilePath);

                var destFullTree = Path.Combine(destFolder.ImportFolderLocation, newFolderPath);
                if (!Directory.Exists(destFullTree))
                {
                    try
                    {
                        Directory.CreateDirectory(destFullTree);
                    }
                    catch (Exception e)
                    {
                        logger.Error(e);
                        return(true);
                    }
                }

                // Last ditch effort to ensure we aren't moving a file unto itself
                if (newFullServerPath.Equals(FullServerPath, StringComparison.InvariantCultureIgnoreCase))
                {
                    logger.Error($"Resolved to move \"{newFullServerPath}\" unto itself. NOT MOVING");
                    return(true);
                }

                var originalFileName = FullServerPath;
                var textStreams      = SubtitleHelper.GetSubtitleStreams(this);

                if (File.Exists(newFullServerPath))
                {
                    // A file with the same name exists at the destination.
                    // Handle Duplicate Files, A duplicate file record won't exist yet,
                    // so we'll check the old fashioned way
                    logger.Trace("A file already exists at the new location, checking it for duplicate");
                    var destVideoLocalPlace = RepoFactory.VideoLocalPlace.GetByFilePathAndImportFolderID(newFilePath,
                                                                                                         destFolder.ImportFolderID);
                    var destVideoLocal = destVideoLocalPlace?.VideoLocal;
                    if (destVideoLocal == null)
                    {
                        logger.Error("The existing file at the new location does not have a VideoLocal. Not moving");
                        return(true);
                    }
                    if (destVideoLocal.Hash == VideoLocal.Hash)
                    {
                        logger.Info($"Not moving file as it already exists at the new location, deleting source file instead: \"{FullServerPath}\" --- \"{newFullServerPath}\"");

                        // if the file already exists, we can just delete the source file instead
                        // this is safer than deleting and moving
                        try
                        {
                            sourceFile.Delete();
                        }
                        catch (Exception e)
                        {
                            logger.Warn($"Unable to DELETE file: \"{FullServerPath}\" error {e}");
                            RemoveRecord(false);

                            // check for any empty folders in drop folder
                            // only for the drop folder
                            if (dropFolder.IsDropSource != 1)
                            {
                                return(true);
                            }
                            RecursiveDeleteEmptyDirectories(dropFolder?.ImportFolderLocation, true);
                            return(true);
                        }
                    }

                    // Not a dupe, don't delete it
                    logger.Trace("A file already exists at the new location, checking it for version and group");
                    var destinationExistingAniDBFile = destVideoLocal.GetAniDBFile();
                    if (destinationExistingAniDBFile == null)
                    {
                        logger.Error("The existing file at the new location does not have AniDB info. Not moving.");
                        return(true);
                    }

                    var aniDBFile = VideoLocal.GetAniDBFile();
                    if (aniDBFile == null)
                    {
                        logger.Error("The file does not have AniDB info. Not moving.");
                        return(true);
                    }

                    if (destinationExistingAniDBFile.Anime_GroupName == aniDBFile.Anime_GroupName &&
                        destinationExistingAniDBFile.FileVersion < aniDBFile.FileVersion)
                    {
                        // This is a V2 replacing a V1 with the same name.
                        // Normally we'd let the Multiple Files Utility handle it, but let's just delete the V1
                        logger.Info("The existing file is a V1 from the same group. Replacing it.");
                        // Delete the destination
                        (bool success, string _) = destVideoLocalPlace.RemoveAndDeleteFile();
                        if (!success)
                        {
                            return(false);
                        }

                        // Move
                        ShokoServer.PauseWatchingFiles();
                        logger.Info($"Moving file from \"{FullServerPath}\" to \"{newFullServerPath}\"");
                        try
                        {
                            sourceFile.MoveTo(newFullServerPath);
                        }
                        catch (Exception e)
                        {
                            logger.Error($"Unable to MOVE file: \"{FullServerPath}\" to \"{newFullServerPath}\" error {e}");
                            ShokoServer.UnpauseWatchingFiles();
                            return(false);
                        }

                        // Handle Duplicate Files
                        var dups = RepoFactory.DuplicateFile.GetByFilePathAndImportFolder(FilePath, ImportFolderID).ToList();

                        foreach (var dup in dups)
                        {
                            // Move source
                            if (dup.FilePathFile1.Equals(FilePath) && dup.ImportFolderIDFile1 == ImportFolderID)
                            {
                                dup.FilePathFile1       = newFilePath;
                                dup.ImportFolderIDFile1 = destFolder.ImportFolderID;
                            }
                            else if (dup.FilePathFile2.Equals(FilePath) && dup.ImportFolderIDFile2 == ImportFolderID)
                            {
                                dup.FilePathFile2       = newFilePath;
                                dup.ImportFolderIDFile2 = destFolder.ImportFolderID;
                            }
                            // validate the dup file
                            // There are cases where a dup file was not cleaned up before, so we'll do it here, too
                            if (!dup.GetFullServerPath1()
                                .Equals(dup.GetFullServerPath2(), StringComparison.InvariantCultureIgnoreCase))
                            {
                                RepoFactory.DuplicateFile.Save(dup);
                            }
                            else
                            {
                                RepoFactory.DuplicateFile.Delete(dup);
                            }
                        }

                        ImportFolderID = destFolder.ImportFolderID;
                        FilePath       = newFilePath;
                        RepoFactory.VideoLocalPlace.Save(this);

                        // check for any empty folders in drop folder
                        // only for the drop folder
                        if (dropFolder.IsDropSource == 1 && deleteEmpty)
                        {
                            RecursiveDeleteEmptyDirectories(dropFolder?.ImportFolderLocation, true);
                        }
                    }
                }
                else
                {
                    ShokoServer.PauseWatchingFiles();
                    logger.Info($"Moving file from \"{FullServerPath}\" to \"{newFullServerPath}\"");
                    try
                    {
                        sourceFile.MoveTo(newFullServerPath);
                    }
                    catch (Exception e)
                    {
                        logger.Error($"Unable to MOVE file: \"{FullServerPath}\" to \"{newFullServerPath}\" error {e}");
                        ShokoServer.UnpauseWatchingFiles();
                        return(false);
                    }

                    // Handle Duplicate Files
                    var dups = RepoFactory.DuplicateFile.GetByFilePathAndImportFolder(FilePath, ImportFolderID).ToList();

                    foreach (var dup in dups)
                    {
                        // Move source
                        if (dup.FilePathFile1.Equals(FilePath) && dup.ImportFolderIDFile1 == ImportFolderID)
                        {
                            dup.FilePathFile1       = newFilePath;
                            dup.ImportFolderIDFile1 = destFolder.ImportFolderID;
                        }
                        else if (dup.FilePathFile2.Equals(FilePath) && dup.ImportFolderIDFile2 == ImportFolderID)
                        {
                            dup.FilePathFile2       = newFilePath;
                            dup.ImportFolderIDFile2 = destFolder.ImportFolderID;
                        }
                        // validate the dup file
                        // There are cases where a dup file was not cleaned up before, so we'll do it here, too
                        if (!dup.GetFullServerPath1()
                            .Equals(dup.GetFullServerPath2(), StringComparison.InvariantCultureIgnoreCase))
                        {
                            RepoFactory.DuplicateFile.Save(dup);
                        }
                        else
                        {
                            RepoFactory.DuplicateFile.Delete(dup);
                        }
                    }

                    ImportFolderID = destFolder.ImportFolderID;
                    FilePath       = newFilePath;
                    RepoFactory.VideoLocalPlace.Save(this);

                    // check for any empty folders in drop folder
                    // only for the drop folder
                    if (dropFolder.IsDropSource == 1 && deleteEmpty)
                    {
                        RecursiveDeleteEmptyDirectories(dropFolder?.ImportFolderLocation, true);
                    }
                }

                try
                {
                    // move any subtitle files
                    foreach (TextStream subtitleFile in textStreams)
                    {
                        if (string.IsNullOrEmpty(subtitleFile.Filename))
                        {
                            continue;
                        }
                        var newParent = Path.GetDirectoryName(newFullServerPath);
                        var srcParent = Path.GetDirectoryName(originalFileName);
                        if (string.IsNullOrEmpty(newParent) || string.IsNullOrEmpty(srcParent))
                        {
                            continue;
                        }
                        var subPath = Path.Combine(srcParent, subtitleFile.Filename);
                        if (!File.Exists(subPath))
                        {
                            continue;
                        }
                        var    subFile    = new FileInfo(subPath);
                        string newSubPath = Path.Combine(newParent, subFile.Name);
                        if (File.Exists(newSubPath))
                        {
                            try
                            {
                                File.Delete(newSubPath);
                            }
                            catch (Exception e)
                            {
                                logger.Warn($"Unable to DELETE file: \"{subtitleFile}\" error {e}");
                            }
                        }

                        try
                        {
                            subFile.MoveTo(newSubPath);
                        }
                        catch (Exception e)
                        {
                            logger.Error($"Unable to MOVE file: \"{subtitleFile}\" to \"{newSubPath}\" error {e}");
                        }
                    }
                }
                catch (Exception ex)
                {
                    logger.Error(ex, ex.ToString());
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex, $"Could not MOVE file: \"{FullServerPath ?? VideoLocal_Place_ID.ToString()}\" -- {ex}");
            }
            ShokoServer.UnpauseWatchingFiles();
            return(true);
        }
Example #2
0
        // returns false if we should retry
        private bool MoveFileIfRequired()
        {
            try
            {
                logger.Trace("Attempting to MOVE file: {0}", FullServerPath ?? VideoLocal_Place_ID.ToString());

                if (FullServerPath == null)
                {
                    logger.Error("Could not find or access the file to move: {0}",
                                 VideoLocal_Place_ID);
                    return(true);
                }

                // check if this file is in the drop folder
                // otherwise we don't need to move it
                if (ImportFolder.IsDropSource == 0)
                {
                    logger.Trace("Not moving file as it is NOT in the drop folder: {0}", FullServerPath);
                    return(true);
                }
                IFileSystem f = ImportFolder.FileSystem;
                if (f == null)
                {
                    logger.Trace("Unable to MOVE, filesystem not working: {0}", FullServerPath);
                    return(true);
                }

                FileSystemResult <IObject> fsrresult = f.Resolve(FullServerPath);
                if (fsrresult == null || !fsrresult.IsOk)
                {
                    logger.Error("Could not find or access the file to move: {0}", FullServerPath);
                    // this can happen due to file locks, so retry
                    return(false);
                }
                IFile source_file = fsrresult.Result as IFile;
                if (source_file == null)
                {
                    logger.Error("Could not move the file (it isn't a file): {0}", FullServerPath);
                    // this means it isn't a file, but something else, so don't retry
                    return(true);
                }

                // find the default destination
                (var destImpl, string newFullPath) = RenameFileHelper.GetRenamerWithFallback()?.GetDestinationFolder(this) ?? (null, null);

                if (!(destImpl is SVR_ImportFolder destFolder))
                {
                    // In this case, an error string was returned, but we'll suppress it and give an error elsewhere
                    if (newFullPath != null)
                    {
                        return(true);
                    }
                    logger.Error("Could not find a valid destination: {0}", FullServerPath);
                    return(true);
                }

                // keep the original drop folder for later (take a copy, not a reference)
                SVR_ImportFolder dropFolder = ImportFolder;

                if (string.IsNullOrEmpty(newFullPath))
                {
                    return(true);
                }

                // We've already resolved FullServerPath, so it doesn't need to be checked
                string relativeFilePath  = Path.Combine(newFullPath, Path.GetFileName(FullServerPath));
                string newFullServerPath = Path.Combine(destFolder.ImportFolderLocation, relativeFilePath);

                IDirectory destination;

                fsrresult = destFolder.FileSystem.Resolve(Path.Combine(destFolder.ImportFolderLocation, newFullPath));
                if (fsrresult != null && fsrresult.IsOk)
                {
                    destination = fsrresult.Result as IDirectory;
                }
                else
                {
                    //validate the directory tree.
                    destination = destFolder.BaseDirectory;
                    {
                        var dir = Path.GetDirectoryName(relativeFilePath);

                        foreach (var part in dir.Split(Path.DirectorySeparatorChar))
                        {
                            var wD = destination.Directories.FirstOrDefault(d => d.Name == part);
                            if (wD == null)
                            {
                                var result = destination.CreateDirectory(part, null);
                                if (!result.IsOk)
                                {
                                    logger.Error(
                                        $"Unable to create directory {part} in {destination.FullName}: {result.Error}");
                                    return(true);
                                }
                                destination = result.Result;
                                continue;
                            }

                            destination = wD;
                        }
                    }
                }


                // Last ditch effort to ensure we aren't moving a file unto itself
                if (newFullServerPath.Equals(FullServerPath, StringComparison.InvariantCultureIgnoreCase))
                {
                    logger.Error($"Resolved to move {newFullServerPath} unto itself. NOT MOVING");
                    return(true);
                }

                FileSystemResult <IObject> dst = f.Resolve(newFullServerPath);
                if (dst != null && dst.IsOk)
                {
                    logger.Info(
                        "Not moving file as it already exists at the new location, deleting source file instead: {0} --- {1}",
                        FullServerPath, newFullServerPath);

                    // if the file already exists, we can just delete the source file instead
                    // this is safer than deleting and moving
                    FileSystemResult fr = new FileSystemResult();
                    try
                    {
                        fr = source_file.Delete(false);
                        if (fr == null || !fr.IsOk)
                        {
                            logger.Warn("Unable to DELETE file: {0} error {1}", FullServerPath,
                                        fr?.Error ?? string.Empty);
                            return(false);
                        }
                        RemoveRecord();

                        // check for any empty folders in drop folder
                        // only for the drop folder
                        if (dropFolder.IsDropSource != 1)
                        {
                            return(true);
                        }
                        FileSystemResult <IObject> dd = f.Resolve(dropFolder.ImportFolderLocation);
                        if (dd != null && dd.IsOk && dd.Result is IDirectory)
                        {
                            RecursiveDeleteEmptyDirectories((IDirectory)dd.Result, true);
                        }
                        return(true);
                    }
                    catch
                    {
                        logger.Error("Unable to DELETE file: {0} error {1}", FullServerPath,
                                     fr?.Error ?? string.Empty);
                    }
                }
                else
                {
                    logger.Info("Moving file from {0} to {1}", FullServerPath, newFullServerPath);
                    FileSystemResult fr = source_file.Move(destination);
                    if (fr == null || !fr.IsOk)
                    {
                        logger.Error("Unable to MOVE file: {0} to {1} error {2}", FullServerPath,
                                     newFullServerPath, fr?.Error ?? "No Error String");
                        return(false);
                    }

/*
 *                  // Pause FileWatchDog
 *                  ShokoServer._pauseFileWatchDog.Reset();
 *                  foreach (System.IO.FileSystemEventArgs evt in ShokoServer.queueFileEvents)
 *                  {
 *                      try
 *                      {
 *                          // this shouldn't happend but w/e
 *                          if (evt?.ChangeType == System.IO.WatcherChangeTypes.Created)
 *                          {
 *                              // check if we know the fine by exact name
 *                              if (RepoFactory.VideoLocal.GetByName(Path.GetFileName(evt.Name)) != null)
 *                              {
 *                                  logger.Info("This file is known: {0}", evt.Name);
 *                                  // delete it from queue for hashing
 *                                  ShokoServer.queueFileEvents.Remove(evt);
 *                                  break;
 *                              }
 *                          }
 *                      }
 *                      catch { }
 *                  }
 *                  // Resume FileWatchDog
 *                  ShokoServer._pauseFileWatchDog.Set();
 */
                    string originalFileName = FullServerPath;

                    ImportFolderID = destFolder.ImportFolderID;
                    FilePath       = relativeFilePath;
                    RepoFactory.VideoLocalPlace.Save(this);

                    try
                    {
                        // move any subtitle files
                        foreach (string subtitleFile in Utils.GetPossibleSubtitleFiles(originalFileName))
                        {
                            FileSystemResult <IObject> src = f.Resolve(subtitleFile);
                            if (src == null || !src.IsOk || !(src.Result is IFile))
                            {
                                continue;
                            }
                            string newSubPath = Path.Combine(Path.GetDirectoryName(newFullServerPath),
                                                             ((IFile)src.Result).Name);
                            dst = f.Resolve(newSubPath);
                            if (dst != null && dst.IsOk && dst.Result is IFile)
                            {
                                FileSystemResult fr2 = src.Result.Delete(false);
                                if (fr2 == null || !fr2.IsOk)
                                {
                                    logger.Warn("Unable to DELETE file: {0} error {1}", subtitleFile,
                                                fr2?.Error ?? string.Empty);
                                }
                            }
                            else
                            {
                                FileSystemResult fr2 = ((IFile)src.Result).Move(destination);
                                if (fr2 == null || !fr2.IsOk)
                                {
                                    logger.Error("Unable to MOVE file: {0} to {1} error {2)", subtitleFile,
                                                 newSubPath, fr2?.Error ?? string.Empty);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.Error(ex, ex.ToString());
                    }

                    // check for any empty folders in drop folder
                    // only for the drop folder
                    if (dropFolder.IsDropSource == 1)
                    {
                        FileSystemResult <IObject> dd = f.Resolve(dropFolder.ImportFolderLocation);
                        if (dd != null && dd.IsOk && dd.Result is IDirectory)
                        {
                            RecursiveDeleteEmptyDirectories((IDirectory)dd.Result, true);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = $"Could not MOVE file: {FullServerPath ?? VideoLocal_Place_ID.ToString()} -- {ex}";
                logger.Error(ex, msg);
            }
            return(true);
        }
        // TODO Merge these, with proper logic depending on the scenario (import, force, etc)
        public (string, string) MoveWithResultString(string scriptName, bool force = false)
        {
            // TODO Make this take an argument to disable removing empty dirs. It's slow, and should only be done if needed
            if (FullServerPath == null)
            {
                logger.Error($"Could not find or access the file to move: {VideoLocal_Place_ID}");
                return(string.Empty, "ERROR: Unable to access file");
            }

            if (!File.Exists(FullServerPath))
            {
                logger.Error($"Could not find or access the file to move: \"{FullServerPath}\"");
                // this can happen due to file locks, so retry
                return(string.Empty, "ERROR: Could not access the file");
            }

            FileInfo sourceFile = new FileInfo(FullServerPath);

            // There is a possibility of weird logic based on source of the file. Some handling should be made for it....later
            (var destImpl, string newFolderPath) = RenameFileHelper.GetDestination(this, scriptName);

            if (!(destImpl is SVR_ImportFolder destFolder))
            {
                // In this case, an error string was returned, but we'll suppress it and give an error elsewhere
                if (newFolderPath != null)
                {
                    logger.Error($"Unable to find destination for: \"{FullServerPath}\"");
                    logger.Error($"The error message was: {newFolderPath}");
                    return(string.Empty, "ERROR: " + newFolderPath);
                }
                logger.Error($"Unable to find destination for: \"{FullServerPath}\"");
                return(string.Empty, "ERROR: There was an error but no error code returned...");
            }

            // keep the original drop folder for later (take a copy, not a reference)
            SVR_ImportFolder dropFolder = ImportFolder;

            if (string.IsNullOrEmpty(newFolderPath))
            {
                logger.Error($"Unable to find destination for: \"{FullServerPath}\"");
                return(string.Empty, "ERROR: The returned path was null or empty");
            }

            // We've already resolved FullServerPath, so it doesn't need to be checked
            string newFilePath       = Path.Combine(newFolderPath, Path.GetFileName(FullServerPath));
            string newFullServerPath = Path.Combine(destFolder.ImportFolderLocation, newFilePath);

            var destFullTree = Path.Combine(destFolder.ImportFolderLocation, newFolderPath);

            if (!Directory.Exists(destFullTree))
            {
                try
                {
                    Directory.CreateDirectory(destFullTree);
                }
                catch (Exception e)
                {
                    logger.Error(e);
                    return(string.Empty, $"ERROR: Unable to create directory tree: \"{destFullTree}\"");
                }
            }

            // Last ditch effort to ensure we aren't moving a file unto itself
            if (newFullServerPath.Equals(FullServerPath, StringComparison.InvariantCultureIgnoreCase))
            {
                logger.Info($"Moving file SKIPPED! The file is already at its desired location: \"{FullServerPath}\"");
                return(newFolderPath, string.Empty);
            }

            if (File.Exists(newFullServerPath))
            {
                logger.Error($"A file already exists at the desired location: \"{FullServerPath}\"");
                return(string.Empty, "ERROR: A file already exists at the destination");
            }

            ShokoServer.PauseWatchingFiles();

            logger.Info($"Moving file from \"{FullServerPath}\" to \"{newFullServerPath}\"");
            try
            {
                sourceFile.MoveTo(newFullServerPath);
            }
            catch (Exception e)
            {
                logger.Error($"Unable to MOVE file: \"{FullServerPath}\" to \"{newFullServerPath}\" error {e}");
                ShokoServer.UnpauseWatchingFiles();
                return(newFullServerPath, "ERROR: " + e);
            }

            // Save for later. Scan for subtitles while the vlplace is still set for the source location
            string originalFileName = FullServerPath;
            var    textStreams      = SubtitleHelper.GetSubtitleStreams(this);

            // Handle Duplicate Files
            var dups = RepoFactory.DuplicateFile.GetByFilePathAndImportFolder(FilePath, ImportFolderID).ToList();

            foreach (var dup in dups)
            {
                // Move source
                if (dup.FilePathFile1.Equals(FilePath) && dup.ImportFolderIDFile1 == ImportFolderID)
                {
                    dup.FilePathFile1       = newFilePath;
                    dup.ImportFolderIDFile1 = destFolder.ImportFolderID;
                }
                else if (dup.FilePathFile2.Equals(FilePath) && dup.ImportFolderIDFile2 == ImportFolderID)
                {
                    dup.FilePathFile2       = newFilePath;
                    dup.ImportFolderIDFile2 = destFolder.ImportFolderID;
                }
                // validate the dup file
                // There are cases where a dup file was not cleaned up before, so we'll do it here, too
                if (!dup.GetFullServerPath1()
                    .Equals(dup.GetFullServerPath2(), StringComparison.InvariantCultureIgnoreCase))
                {
                    RepoFactory.DuplicateFile.Save(dup);
                }
                else
                {
                    RepoFactory.DuplicateFile.Delete(dup);
                }
            }

            ImportFolderID = destFolder.ImportFolderID;
            FilePath       = newFilePath;
            RepoFactory.VideoLocalPlace.Save(this);

            try
            {
                // move any subtitle files
                foreach (TextStream subtitleFile in textStreams)
                {
                    if (string.IsNullOrEmpty(subtitleFile.Filename))
                    {
                        continue;
                    }
                    var newParent = Path.GetDirectoryName(newFullServerPath);
                    var srcParent = Path.GetDirectoryName(originalFileName);
                    if (string.IsNullOrEmpty(newParent) || string.IsNullOrEmpty(srcParent))
                    {
                        continue;
                    }
                    var subPath = Path.Combine(srcParent, subtitleFile.Filename);
                    if (!File.Exists(subPath))
                    {
                        continue;
                    }
                    var    subFile    = new FileInfo(subPath);
                    string newSubPath = Path.Combine(newParent, subFile.Name);
                    if (File.Exists(newSubPath))
                    {
                        try
                        {
                            File.Delete(newSubPath);
                        }
                        catch (Exception e)
                        {
                            logger.Warn($"Unable to DELETE file: \"{subtitleFile}\" error {e}");
                        }
                    }

                    try
                    {
                        subFile.MoveTo(newSubPath);
                    }
                    catch (Exception e)
                    {
                        logger.Error($"Unable to MOVE file: \"{subtitleFile}\" to \"{newSubPath}\" error {e}");
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex, ex.ToString());
            }

            // check for any empty folders in drop folder
            // only for the drop folder
            if (dropFolder.IsDropSource == 1)
            {
                RecursiveDeleteEmptyDirectories(dropFolder?.ImportFolderLocation, true);
            }
            ShokoServer.UnpauseWatchingFiles();
            return(newFolderPath, string.Empty);
        }
Example #4
0
        // returns false if we should retry
        private bool MoveFileIfRequired()
        {
            try
            {
                logger.Trace("Attempting to MOVE file: {0}", this.FullServerPath);

                // check if this file is in the drop folder
                // otherwise we don't need to move it
                if (ImportFolder.IsDropSource == 0)
                {
                    logger.Trace("Not moving file as it is NOT in the drop folder: {0}", this.FullServerPath);
                    return(true);
                }
                IFileSystem f = this.ImportFolder.FileSystem;
                if (f == null)
                {
                    logger.Trace("Unable to MOVE, filesystem not working: {0}", this.FullServerPath);
                    return(true);
                }

                FileSystemResult <IObject> fsrresult = f.Resolve(FullServerPath);
                if (!fsrresult.IsOk)
                {
                    logger.Error("Could not find or access the file to move: {0}", this.FullServerPath);
                    // this can happen due to file locks, so retry
                    return(false);
                }
                IFile source_file = fsrresult.Result as IFile;
                if (source_file == null)
                {
                    logger.Error("Could not move the file (it isn't a file): {0}", this.FullServerPath);
                    // this means it isn't a file, but something else, so don't retry
                    return(true);
                }
                // find the default destination
                SVR_ImportFolder destFolder = null;
                foreach (SVR_ImportFolder fldr in RepoFactory.ImportFolder.GetAll()
                         .Where(a => a != null && a.CloudID == ImportFolder.CloudID).ToList())
                {
                    if (!fldr.FolderIsDropDestination)
                    {
                        continue;
                    }
                    if (fldr.FolderIsDropSource)
                    {
                        continue;
                    }
                    IFileSystem fs = fldr.FileSystem;
                    FileSystemResult <IObject> fsresult = fs?.Resolve(fldr.ImportFolderLocation);
                    if (fsresult == null || !fsresult.IsOk)
                    {
                        continue;
                    }

                    string tempNewPath = Path.Combine(fldr.ImportFolderLocation, FilePath);
                    fsresult = fs.Resolve(tempNewPath);
                    if (fsresult.IsOk)
                    {
                        continue;
                    }

                    destFolder = fldr;
                    break;
                }

                if (destFolder == null)
                {
                    logger.Error("Could not find a valid destination: {0}", this.FullServerPath);
                    return(true);
                }

                // keep the original drop folder for later (take a copy, not a reference)
                SVR_ImportFolder dropFolder = this.ImportFolder;

                // we can only move the file if it has an anime associated with it
                List <CrossRef_File_Episode> xrefs = this.VideoLocal.EpisodeCrossRefs;
                if (xrefs.Count == 0)
                {
                    return(true);
                }
                CrossRef_File_Episode xref = xrefs[0];

                // find the series associated with this episode
                SVR_AnimeSeries series = RepoFactory.AnimeSeries.GetByAnimeID(xref.AnimeID);
                if (series == null)
                {
                    return(true);
                }

                // find where the other files are stored for this series
                // if there are no other files except for this one, it means we need to create a new location
                bool   foundLocation = false;
                string newFullPath   = "";

                // sort the episodes by air date, so that we will move the file to the location of the latest episode
                List <SVR_AnimeEpisode> allEps = series.GetAnimeEpisodes()
                                                 .OrderByDescending(a => a.AniDB_Episode.AirDate)
                                                 .ToList();

                IDirectory destination = null;

                foreach (SVR_AnimeEpisode ep in allEps)
                {
                    // check if this episode belongs to more than one anime
                    // if it does we will ignore it
                    List <CrossRef_File_Episode> fileEpXrefs =
                        RepoFactory.CrossRef_File_Episode.GetByEpisodeID(ep.AniDB_EpisodeID);
                    int? animeID   = null;
                    bool crossOver = false;
                    foreach (CrossRef_File_Episode fileEpXref in fileEpXrefs)
                    {
                        if (!animeID.HasValue)
                        {
                            animeID = fileEpXref.AnimeID;
                        }
                        else
                        {
                            if (animeID.Value != fileEpXref.AnimeID)
                            {
                                crossOver = true;
                            }
                        }
                    }
                    if (crossOver)
                    {
                        continue;
                    }

                    foreach (SVR_VideoLocal vid in ep.GetVideoLocals()
                             .Where(a => a.Places.Any(b => b.ImportFolder.CloudID == destFolder.CloudID &&
                                                      b.ImportFolder.IsDropSource == 0)).ToList())
                    {
                        if (vid.VideoLocalID == this.VideoLocalID)
                        {
                            continue;
                        }

                        SVR_VideoLocal_Place place =
                            vid.Places.FirstOrDefault(a => a.ImportFolder.CloudID == destFolder.CloudID);
                        string thisFileName = place?.FullServerPath;
                        if (thisFileName == null)
                        {
                            continue;
                        }
                        string folderName = Path.GetDirectoryName(thisFileName);

                        FileSystemResult <IObject> dir = f.Resolve(folderName);
                        if (!dir.IsOk)
                        {
                            continue;
                        }
                        // ensure we aren't moving to the current directory
                        if (folderName.Equals(Path.GetDirectoryName(FullServerPath),
                                              StringComparison.InvariantCultureIgnoreCase))
                        {
                            continue;
                        }
                        destination = dir.Result as IDirectory;
                        // Not a directory
                        if (destination == null)
                        {
                            continue;
                        }
                        newFullPath   = folderName;
                        foundLocation = true;
                        break;
                    }
                    if (foundLocation)
                    {
                        break;
                    }
                }

                if (!foundLocation)
                {
                    // we need to create a new folder
                    string newFolderName = Utils.RemoveInvalidFolderNameCharacters(series.GetSeriesName());

                    newFullPath = Path.Combine(destFolder.ImportFolderLocation, newFolderName);
                    FileSystemResult <IObject> dirn = f.Resolve(newFullPath);
                    if (!dirn.IsOk)
                    {
                        dirn = f.Resolve(destFolder.ImportFolderLocation);
                        if (dirn.IsOk)
                        {
                            IDirectory d = (IDirectory)dirn.Result;
                            FileSystemResult <IDirectory> d2 = Task
                                                               .Run(async() => await d.CreateDirectoryAsync(newFolderName, null))
                                                               .Result;
                            destination = d2.Result;
                        }
                        else
                        {
                            logger.Error("Import folder couldn't be resolved: {0}", destFolder.ImportFolderLocation);
                            newFullPath = null;
                        }
                    }
                    else if (dirn.Result is IFile)
                    {
                        logger.Error("Destination folder is a file: {0}", newFolderName);
                        newFullPath = null;
                    }
                    else
                    {
                        destination = (IDirectory)dirn.Result;
                    }
                }

                if (string.IsNullOrEmpty(newFullPath))
                {
                    return(true);
                }

                // We've already resolved FullServerPath, so it doesn't need to be checked
                string newFullServerPath             = Path.Combine(newFullPath, Path.GetFileName(this.FullServerPath));
                Tuple <SVR_ImportFolder, string> tup = VideoLocal_PlaceRepository.GetFromFullPath(newFullServerPath);
                if (tup == null)
                {
                    logger.Error($"Unable to LOCATE file {newFullServerPath} inside the import folders");
                    return(true);
                }

                // Last ditch effort to ensure we aren't moving a file unto itself
                if (newFullServerPath.Equals(FullServerPath, StringComparison.InvariantCultureIgnoreCase))
                {
                    logger.Error($"Resolved to move {newFullServerPath} unto itself. NOT MOVING");
                    return(true);
                }

                logger.Info("Moving file from {0} to {1}", this.FullServerPath, newFullServerPath);

                FileSystemResult <IObject> dst = f.Resolve(newFullServerPath);
                if (dst.IsOk)
                {
                    logger.Trace(
                        "Not moving file as it already exists at the new location, deleting source file instead: {0} --- {1}",
                        this.FullServerPath, newFullServerPath);

                    // if the file already exists, we can just delete the source file instead
                    // this is safer than deleting and moving
                    FileSystemResult fr = new FileSystemResult();
                    try
                    {
                        fr = source_file.Delete(false);
                        if (!fr.IsOk)
                        {
                            logger.Warn("Unable to DELETE file: {0} error {1}", this.FullServerPath,
                                        fr?.Error ?? String.Empty);
                        }
                        this.ImportFolderID = tup.Item1.ImportFolderID;
                        this.FilePath       = tup.Item2;
                        RepoFactory.VideoLocalPlace.Save(this);

                        // check for any empty folders in drop folder
                        // only for the drop folder
                        if (dropFolder.IsDropSource == 1)
                        {
                            FileSystemResult <IObject> dd = f.Resolve(dropFolder.ImportFolderLocation);
                            if (dd != null && dd.IsOk && dd.Result is IDirectory)
                            {
                                RecursiveDeleteEmptyDirectories((IDirectory)dd.Result, true);
                            }
                        }
                    }
                    catch
                    {
                        logger.Error("Unable to DELETE file: {0} error {1}", this.FullServerPath,
                                     fr?.Error ?? String.Empty);
                    }
                }
                else
                {
                    FileSystemResult fr = source_file.Move(destination);
                    if (!fr.IsOk)
                    {
                        logger.Error("Unable to MOVE file: {0} to {1} error {2}", this.FullServerPath,
                                     newFullServerPath, fr?.Error ?? "No Error String");
                        return(false);
                    }
                    string originalFileName = this.FullServerPath;


                    this.ImportFolderID = tup.Item1.ImportFolderID;
                    this.FilePath       = tup.Item2;
                    RepoFactory.VideoLocalPlace.Save(this);

                    try
                    {
                        // move any subtitle files
                        foreach (string subtitleFile in Utils.GetPossibleSubtitleFiles(originalFileName))
                        {
                            FileSystemResult <IObject> src = f.Resolve(subtitleFile);
                            if (src.IsOk && src.Result is IFile)
                            {
                                string newSubPath = Path.Combine(Path.GetDirectoryName(newFullServerPath),
                                                                 ((IFile)src.Result).Name);
                                dst = f.Resolve(newSubPath);
                                if (dst.IsOk && dst.Result is IFile)
                                {
                                    FileSystemResult fr2 = src.Result.Delete(false);
                                    if (!fr2.IsOk)
                                    {
                                        logger.Warn("Unable to DELETE file: {0} error {1}", subtitleFile,
                                                    fr2?.Error ?? String.Empty);
                                    }
                                }
                                else
                                {
                                    FileSystemResult fr2 = ((IFile)src.Result).Move(destination);
                                    if (!fr2.IsOk)
                                    {
                                        logger.Error("Unable to MOVE file: {0} to {1} error {2)", subtitleFile,
                                                     newSubPath, fr2?.Error ?? String.Empty);
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.Error(ex, ex.ToString());
                    }

                    // check for any empty folders in drop folder
                    // only for the drop folder
                    if (dropFolder.IsDropSource == 1)
                    {
                        FileSystemResult <IObject> dd = f.Resolve(dropFolder.ImportFolderLocation);
                        if (dd != null && dd.IsOk && dd.Result is IDirectory)
                        {
                            RecursiveDeleteEmptyDirectories((IDirectory)dd.Result, true);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = $"Could not MOVE file: {this.FullServerPath} -- {ex.ToString()}";
                logger.Error(ex, msg);
            }
            return(true);
        }