private bool IsEpisodeFileMissing(Series series, EpisodeFile episodeFile) { var fullPath = Path.Combine(series.Path, episodeFile.RelativePath); return(!_diskProvider.FileExists(fullPath)); }
private void MigrateAppDataFolder() { try { var oldDbFile = Path.Combine(_appFolderInfo.AppDataFolder, "nzbdrone.db"); if (_startupContext.Args.ContainsKey(StartupContext.APPDATA)) { if (_diskProvider.FileExists(_appFolderInfo.GetDatabase())) { return; } if (!_diskProvider.FileExists(oldDbFile)) { return; } MoveSqliteDatabase(oldDbFile, _appFolderInfo.GetDatabase()); RemovePidFile(); } if (_appFolderInfo.LegacyAppDataFolder.IsNullOrWhiteSpace()) { return; } if (_diskProvider.FileExists(_appFolderInfo.GetDatabase()) || _diskProvider.FileExists(_appFolderInfo.GetConfigPath())) { return; } if (!_diskProvider.FolderExists(_appFolderInfo.LegacyAppDataFolder)) { return; } // Delete the bin folder on Windows var binFolder = Path.Combine(_appFolderInfo.LegacyAppDataFolder, "bin"); if (OsInfo.IsWindows && _diskProvider.FolderExists(binFolder)) { _diskProvider.DeleteFolder(binFolder, true); } // Transfer other files and folders (with copy so a backup is maintained) _diskTransferService.TransferFolder(_appFolderInfo.LegacyAppDataFolder, _appFolderInfo.AppDataFolder, TransferMode.Copy); // Rename the DB file if (_diskProvider.FileExists(oldDbFile)) { MoveSqliteDatabase(oldDbFile, _appFolderInfo.GetDatabase()); } // Remove Old PID file RemovePidFile(); // Delete the old files after everything has been copied _diskProvider.DeleteFolder(_appFolderInfo.LegacyAppDataFolder, true); } catch (Exception ex) { _logger.Debug(ex, ex.Message); throw new SonarrStartupException("Unable to migrate AppData folder from {0} to {1}. Migrate manually", _appFolderInfo.LegacyAppDataFolder, _appFolderInfo.AppDataFolder); } }
private bool CompareFiles(string sourceFile, string targetFile) { if (!_diskProvider.FileExists(sourceFile) || !_diskProvider.FileExists(targetFile)) { return(false); } if (_diskProvider.GetFileSize(sourceFile) != _diskProvider.GetFileSize(targetFile)) { return(false); } var sourceBuffer = new byte[64 * 1024]; var targetBuffer = new byte[64 * 1024]; using (var sourceStream = _diskProvider.OpenReadStream(sourceFile)) using (var targetStream = _diskProvider.OpenReadStream(targetFile)) { while (true) { var sourceLength = sourceStream.Read(sourceBuffer, 0, sourceBuffer.Length); var targetLength = targetStream.Read(targetBuffer, 0, targetBuffer.Length); if (sourceLength != targetLength) { return(false); } if (sourceLength == 0) { return(true); } for (var i = 0; i < sourceLength; i++) { if (sourceBuffer[i] != targetBuffer[i]) { return(false); } } } } }
public HealthCheck Check(IEvent message) { if (typeof(TrackImportFailedEvent).IsAssignableFrom(message.GetType())) { var failureMessage = (TrackImportFailedEvent)message; // if we can see the file exists but the import failed then likely a permissions issue if (failureMessage.TrackInfo != null) { var trackPath = failureMessage.TrackInfo.Path; if (_diskProvider.FileExists(trackPath)) { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Gamearr can see but not access downloaded track {trackPath}. Likely permissions error.", "#permissions-error")); } else { // If the file doesn't exist but TrackInfo is not null then the message is coming from // ImportApprovedTracks and the file must have been removed part way through processing return(new HealthCheck(GetType(), HealthCheckResult.Error, $"File {trackPath} was removed part way though procesing.")); } } // If the previous case did not match then the failure occured in DownloadedTracksImportService, // while trying to locate the files reported by the download client var client = _downloadClientProvider.GetDownloadClients().FirstOrDefault(x => x.Definition.Name == failureMessage.DownloadClient); try { var status = client.GetStatus(); var dlpath = client?.GetItems().FirstOrDefault(x => x.DownloadId == failureMessage.DownloadId)?.OutputPath.FullPath; // If dlpath is null then there's not much useful we can report. Give a generic message so // that the user realises something is wrong. if (dlpath.IsNullOrWhiteSpace()) { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Gamearr failed to import a track. Check your logs for details.")); } if (!dlpath.IsPathValid()) { if (!status.IsLocalhost) { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} reported files in {dlpath} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and download client settings.", "#bad-remote-path-mapping")); } else if (_osInfo.IsDocker) { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} reported files in {dlpath} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and download client settings.", "#docker-bad-remote-path-mapping")); } else { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Local download client {client.Definition.Name} reported files in {dlpath} but this is not a valid {_osInfo.Name} path. Review your download client settings.", "#bad-download-client-settings")); } } if (_diskProvider.FolderExists(dlpath)) { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Gamearr can see but not access download directory {dlpath}. Likely permissions error.", "#permissions-error")); } // if it's a remote client/docker, likely missing path mappings if (_osInfo.IsDocker) { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; download client {client.Definition.Name} reported files in {dlpath} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.", "#docker-bad-remote-path-mapping")); } else if (!status.IsLocalhost) { return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote download client {client.Definition.Name} reported files in {dlpath} but this directory does not appear to exist. Likely missing remote path mapping.", "#bad-remote-path-mapping")); } else { // path mappings shouldn't be needed locally so probably a permissions issue return(new HealthCheck(GetType(), HealthCheckResult.Error, $"Download client {client.Definition.Name} reported files in {dlpath} but Gamearr cannot see this directory. You may need to adjust the folder's permissions.", "#permissions-error")); } } catch (DownloadClientException ex) { _logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name); } catch (Exception ex) { _logger.Error(ex, "Unknown error occured in RemotePathMapping HealthCheck"); } return(new HealthCheck(GetType())); } else { return(Check()); } }
public MediaInfoModel GetMediaInfo(string filename) { if (!_diskProvider.FileExists(filename)) { throw new FileNotFoundException("Media file does not exist: " + filename); } MediaInfo mediaInfo = null; try { mediaInfo = new MediaInfo(); _logger.Debug("Getting media info from {0}", filename); mediaInfo.Option("ParseSpeed", "0.0"); int open; using (var stream = _diskProvider.OpenReadStream(filename)) { open = mediaInfo.Open(stream); } if (open != 0) { int audioRuntime; int videoRuntime; int generalRuntime; //Runtime Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out videoRuntime); Int32.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "PlayTime"), out audioRuntime); Int32.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out generalRuntime); if (audioRuntime == 0 && videoRuntime == 0 && generalRuntime == 0) { mediaInfo.Option("ParseSpeed", "1.0"); using (var stream = _diskProvider.OpenReadStream(filename)) { open = mediaInfo.Open(stream); } } } if (open != 0) { int width; int height; int videoBitRate; int audioBitRate; int audioRuntime; int videoRuntime; int generalRuntime; int streamCount; int audioChannels; decimal videoFrameRate; string subtitles = mediaInfo.Get(StreamKind.General, 0, "Text_Language_List"); string scanType = mediaInfo.Get(StreamKind.Video, 0, "ScanType"); Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "Width"), out width); Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "Height"), out height); Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "BitRate"), out videoBitRate); Decimal.TryParse(mediaInfo.Get(StreamKind.Video, 0, "FrameRate"), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out videoFrameRate); //Runtime Int32.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out videoRuntime); Int32.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "PlayTime"), out audioRuntime); Int32.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out generalRuntime); string aBitRate = mediaInfo.Get(StreamKind.Audio, 0, "BitRate"); int aBindex = aBitRate.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase); if (aBindex > 0) { aBitRate = aBitRate.Remove(aBindex); } Int32.TryParse(aBitRate, out audioBitRate); Int32.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "StreamCount"), out streamCount); string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)"); int aCindex = audioChannelsStr.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase); if (aCindex > 0) { audioChannelsStr = audioChannelsStr.Remove(aCindex); } string audioLanguages = mediaInfo.Get(StreamKind.General, 0, "Audio_Language_List"); string audioProfile = mediaInfo.Get(StreamKind.Audio, 0, "Format_Profile"); int aPindex = audioProfile.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase); if (aPindex > 0) { audioProfile = audioProfile.Remove(aPindex); } Int32.TryParse(audioChannelsStr, out audioChannels); var mediaInfoModel = new MediaInfoModel { VideoCodec = mediaInfo.Get(StreamKind.Video, 0, "Codec/String"), VideoBitrate = videoBitRate, Height = height, Width = width, AudioFormat = mediaInfo.Get(StreamKind.Audio, 0, "Format"), AudioBitrate = audioBitRate, RunTime = GetBestRuntime(audioRuntime, videoRuntime, generalRuntime), AudioStreamCount = streamCount, AudioChannels = audioChannels, AudioProfile = audioProfile.Trim(), VideoFps = videoFrameRate, AudioLanguages = audioLanguages, Subtitles = subtitles, ScanType = scanType }; return(mediaInfoModel); } else { _logger.Warn("Unable to open media info from file: " + filename); } } catch (DllNotFoundException ex) { _logger.ErrorException("mediainfo is required but was not found", ex); } catch (Exception ex) { _logger.ErrorException("Unable to parse media info from file: " + filename, ex); } finally { if (mediaInfo != null) { mediaInfo.Close(); } } return(null); }
public void DeleteFile(string path, string subfolder = "") { _logger.Debug("Attempting to send '{0}' to recycling bin", path); var recyclingBin = _configService.RecycleBin; if (string.IsNullOrWhiteSpace(recyclingBin)) { _logger.Info("Recycling Bin has not been configured, deleting permanently. {0}", path); if (OsInfo.IsWindows) { _logger.Debug(_diskProvider.GetFileAttributes(path)); } _diskProvider.DeleteFile(path); _logger.Debug("File has been permanently deleted: {0}", path); } else { var fileInfo = new FileInfo(path); var destinationFolder = Path.Combine(recyclingBin, subfolder); var destination = Path.Combine(destinationFolder, fileInfo.Name); try { _logger.Debug("Creating folder [0]", destinationFolder); _diskProvider.CreateFolder(destinationFolder); } catch (IOException e) { _logger.Error(e, "Unable to create the folder '{0}' in the recycling bin for the file '{1}'", destinationFolder, fileInfo.Name); throw; } var index = 1; while (_diskProvider.FileExists(destination)) { index++; if (fileInfo.Extension.IsNullOrWhiteSpace()) { destination = Path.Combine(destinationFolder, fileInfo.Name + "_" + index); } else { destination = Path.Combine(destinationFolder, Path.GetFileNameWithoutExtension(fileInfo.Name) + "_" + index + fileInfo.Extension); } } try { _logger.Debug("Moving '{0}' to '{1}'", path, destination); _diskTransferService.TransferFile(path, destination, TransferMode.Move); } catch (IOException e) { _logger.Error(e, "Unable to move '{0}' to the recycling bin: '{1}'", path, destination); throw; } SetLastWriteTime(destination, DateTime.UtcNow); _logger.Debug("File has been moved to the recycling bin: {0}", destination); } }
private bool IsTrackFileMissing(Artist artist, TrackFile trackFile) { return(!_diskProvider.FileExists(trackFile.Path)); }
public BookFileMoveResult UpgradeBookFile(BookFile bookFile, LocalBook localBook, bool copyOnly = false) { var moveFileResult = new BookFileMoveResult(); var existingFiles = localBook.Book.BookFiles.Value; var rootFolderPath = _diskProvider.GetParentFolder(localBook.Author.Path); var rootFolder = _rootFolderService.GetBestRootFolder(rootFolderPath); var isCalibre = rootFolder.IsCalibreLibrary && rootFolder.CalibreSettings != null; var settings = rootFolder.CalibreSettings; // If there are existing book files and the root folder is missing, throw, so the old file isn't left behind during the import process. if (existingFiles.Any() && !_diskProvider.FolderExists(rootFolderPath)) { throw new RootFolderNotFoundException($"Root folder '{rootFolderPath}' was not found."); } foreach (var file in existingFiles) { var bookFilePath = file.Path; var subfolder = rootFolderPath.GetRelativePath(_diskProvider.GetParentFolder(bookFilePath)); bookFile.CalibreId = file.CalibreId; if (_diskProvider.FileExists(bookFilePath)) { _logger.Debug("Removing existing book file: {0} CalibreId: {1}", file, file.CalibreId); if (!isCalibre) { _recycleBinProvider.DeleteFile(bookFilePath, subfolder); } else { var existing = _calibre.GetBook(file.CalibreId, settings); var existingFormats = existing.Formats.Keys; _logger.Debug($"Removing existing formats {existingFormats.ConcatToString()} from calibre"); _calibre.RemoveFormats(file.CalibreId, existingFormats, settings); } } moveFileResult.OldFiles.Add(file); _mediaFileService.Delete(file, DeleteMediaFileReason.Upgrade); } if (!isCalibre) { if (copyOnly) { moveFileResult.BookFile = _bookFileMover.CopyBookFile(bookFile, localBook); } else { moveFileResult.BookFile = _bookFileMover.MoveBookFile(bookFile, localBook); } _audioTagService.WriteTags(bookFile, true); } else { var source = bookFile.Path; moveFileResult.BookFile = CalibreAddAndConvert(bookFile, settings); if (!copyOnly) { _diskProvider.DeleteFile(source); } } return(moveFileResult); }
private void ClearTargetPath(string targetPath, bool overwrite) { if (_diskProvider.FileExists(targetPath)) { if (overwrite) { _diskProvider.DeleteFile(targetPath); } else { throw new IOException(string.Format("Destination already exists [{0}]", targetPath)); } } }
private MovieFile TransferFile(MovieFile movieFile, Movie movie, string destinationFilePath, TransferMode mode) { Ensure.That(movieFile, () => movieFile).IsNotNull(); Ensure.That(movie, () => movie).IsNotNull(); Ensure.That(destinationFilePath, () => destinationFilePath).IsValidPath(); var movieFilePath = movieFile.Path ?? Path.Combine(movie.Path, movieFile.RelativePath); if (!_diskProvider.FileExists(movieFilePath)) { throw new FileNotFoundException("Movie file path does not exist", movieFilePath); } if (movieFilePath == destinationFilePath) { throw new SameFilenameException("File not moved, source and destination are the same", movieFilePath); } _diskTransferService.TransferFile(movieFilePath, destinationFilePath, mode); var oldMoviePath = movie.Path; var newMoviePath = new OsPath(destinationFilePath).Directory.FullPath.TrimEnd(Path.DirectorySeparatorChar); movie.Path = newMoviePath; //We update it when everything went well! movieFile.RelativePath = movie.Path.GetRelativePath(destinationFilePath); _updateMovieFileService.ChangeFileDateForFile(movieFile, movie); try { _mediaFileAttributeService.SetFolderLastWriteTime(movie.Path, movieFile.DateAdded); } catch (Exception ex) { _logger.Warn(ex, "Unable to set last write time"); } _mediaFileAttributeService.SetFilePermissions(destinationFilePath); if (oldMoviePath != newMoviePath && _diskProvider.FolderExists(oldMoviePath)) { //Let's move the old files before deleting the old folder. We could just do move folder, but the main file (movie file) is already moved, so eh. var files = _diskProvider.GetFiles(oldMoviePath, SearchOption.AllDirectories); foreach (var file in files) { try { var destFile = Path.Combine(newMoviePath, oldMoviePath.GetRelativePath(file)); _diskProvider.EnsureFolder(Path.GetDirectoryName(destFile)); _diskProvider.MoveFile(file, destFile); } catch (Exception e) { _logger.Warn(e, "Error while trying to move extra file {0} to new folder. Maybe it already exists? (Manual cleanup necessary!).", oldMoviePath.GetRelativePath(file)); } } if (_diskProvider.GetFiles(oldMoviePath, SearchOption.AllDirectories).Count() == 0) { _recycleBinProvider.DeleteFolder(oldMoviePath); } } //Only update the movie path if we were successfull! if (oldMoviePath != newMoviePath) { _movieService.UpdateMovie(movie); } return(movieFile); }
public MediaInfoModel GetMediaInfo(string filename) { if (!_diskProvider.FileExists(filename)) { throw new FileNotFoundException("Media file does not exist: " + filename); } // TODO: Cache media info by path, mtime and length so we don't need to read files multiple times try { _logger.Debug("Getting media info from {0}", filename); var ffprobeOutput = FFProbe.GetStreamJson(filename, ffOptions: new FFOptions { ExtraArguments = "-probesize 50000000" }); var analysis = FFProbe.AnalyseStreamJson(ffprobeOutput); if (analysis.PrimaryAudioStream?.ChannelLayout.IsNullOrWhiteSpace() ?? true) { ffprobeOutput = FFProbe.GetStreamJson(filename, ffOptions: new FFOptions { ExtraArguments = "-probesize 150000000 -analyzeduration 150000000" }); analysis = FFProbe.AnalyseStreamJson(ffprobeOutput); } var mediaInfoModel = new MediaInfoModel(); mediaInfoModel.ContainerFormat = analysis.Format.FormatName; mediaInfoModel.VideoFormat = analysis.PrimaryVideoStream?.CodecName; mediaInfoModel.VideoCodecID = analysis.PrimaryVideoStream?.CodecTagString; mediaInfoModel.VideoProfile = analysis.PrimaryVideoStream?.Profile; mediaInfoModel.VideoBitrate = analysis.PrimaryVideoStream?.BitRate ?? 0; mediaInfoModel.VideoMultiViewCount = 1; mediaInfoModel.VideoBitDepth = GetPixelFormat(analysis.PrimaryVideoStream?.PixelFormat)?.Components.Min(x => x.BitDepth) ?? 8; mediaInfoModel.VideoColourPrimaries = analysis.PrimaryVideoStream?.ColorPrimaries; mediaInfoModel.VideoTransferCharacteristics = analysis.PrimaryVideoStream?.ColorTransfer; mediaInfoModel.DoviConfigurationRecord = analysis.PrimaryVideoStream?.SideDataList?.Find(x => x.GetType().Name == nameof(DoviConfigurationRecordSideData)) as DoviConfigurationRecordSideData; mediaInfoModel.Height = analysis.PrimaryVideoStream?.Height ?? 0; mediaInfoModel.Width = analysis.PrimaryVideoStream?.Width ?? 0; mediaInfoModel.AudioFormat = analysis.PrimaryAudioStream?.CodecName; mediaInfoModel.AudioCodecID = analysis.PrimaryAudioStream?.CodecTagString; mediaInfoModel.AudioProfile = analysis.PrimaryAudioStream?.Profile; mediaInfoModel.AudioBitrate = analysis.PrimaryAudioStream?.BitRate ?? 0; mediaInfoModel.RunTime = GetBestRuntime(analysis.PrimaryAudioStream?.Duration, analysis.PrimaryVideoStream?.Duration, analysis.Format.Duration); mediaInfoModel.AudioStreamCount = analysis.AudioStreams.Count; mediaInfoModel.AudioChannels = analysis.PrimaryAudioStream?.Channels ?? 0; mediaInfoModel.AudioChannelPositions = analysis.PrimaryAudioStream?.ChannelLayout; mediaInfoModel.VideoFps = analysis.PrimaryVideoStream?.FrameRate ?? 0; mediaInfoModel.AudioLanguages = analysis.AudioStreams?.Select(x => x.Language) .Where(l => l.IsNotNullOrWhiteSpace()) .ToList(); mediaInfoModel.Subtitles = analysis.SubtitleStreams?.Select(x => x.Language) .Where(l => l.IsNotNullOrWhiteSpace()) .ToList(); mediaInfoModel.ScanType = "Progressive"; mediaInfoModel.RawStreamData = ffprobeOutput; mediaInfoModel.SchemaRevision = CURRENT_MEDIA_INFO_SCHEMA_REVISION; FFProbeFrames frames = null; // if it looks like PQ10 or similar HDR, do a frame analysis to figure out which type it is if (PqTransferFunctions.Contains(mediaInfoModel.VideoTransferCharacteristics)) { var frameOutput = FFProbe.GetFrameJson(filename, ffOptions: new () { ExtraArguments = "-read_intervals \"%+#1\" -select_streams v" }); mediaInfoModel.RawFrameData = frameOutput; frames = FFProbe.AnalyseFrameJson(frameOutput); } var streamSideData = analysis.PrimaryVideoStream?.SideDataList ?? new (); var framesSideData = frames?.Frames?.Count > 0 ? frames?.Frames[0]?.SideDataList ?? new () : new (); var sideData = streamSideData.Concat(framesSideData).ToList(); mediaInfoModel.VideoHdrFormat = GetHdrFormat(mediaInfoModel.VideoBitDepth, mediaInfoModel.VideoColourPrimaries, mediaInfoModel.VideoTransferCharacteristics, sideData); return(mediaInfoModel); } catch (Exception ex) { _logger.Error(ex, "Unable to parse media info from file: {0}", filename); } return(null); }
private EpisodeFile MoveFile(EpisodeFile episodeFile, Series series, List <Episode> episodes, string destinationFilename) { Ensure.That(episodeFile, () => episodeFile).IsNotNull(); Ensure.That(series, () => series).IsNotNull(); Ensure.That(destinationFilename, () => destinationFilename).IsValidPath(); if (!_diskProvider.FileExists(episodeFile.Path)) { throw new FileNotFoundException("Episode file path does not exist", episodeFile.Path); } if (episodeFile.Path.PathEquals(destinationFilename)) { throw new SameFilenameException("File not moved, source and destination are the same", episodeFile.Path); } var directoryName = new FileInfo(destinationFilename).DirectoryName; if (!_diskProvider.FolderExists(directoryName)) { try { _diskProvider.CreateFolder(directoryName); } catch (IOException ex) { _logger.ErrorException("Unable to create directory: " + directoryName, ex); } SetFolderPermissions(directoryName); if (!directoryName.PathEquals(series.Path)) { SetFolderPermissions(series.Path); } } _logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, destinationFilename); _diskProvider.MoveFile(episodeFile.Path, destinationFilename); episodeFile.Path = destinationFilename; _updateEpisodeFileService.ChangeFileDateForFile(episodeFile, series, episodes); try { _logger.Debug("Setting last write time on series folder: {0}", series.Path); _diskProvider.FolderSetLastWriteTimeUtc(series.Path, episodeFile.DateAdded); if (series.SeasonFolder) { var seasonFolder = Path.GetDirectoryName(destinationFilename); _logger.Debug("Setting last write time on season folder: {0}", seasonFolder); _diskProvider.FolderSetLastWriteTimeUtc(seasonFolder, episodeFile.DateAdded); } } catch (Exception ex) { _logger.WarnException("Unable to set last write time", ex); } //We should only run this on Windows if (OsInfo.IsWindows) { //Wrapped in Try/Catch to prevent this from causing issues with remote NAS boxes, the move worked, which is more important. try { _diskProvider.InheritFolderPermissions(destinationFilename); } catch (Exception ex) { if (ex is UnauthorizedAccessException || ex is InvalidOperationException) { _logger.Debug("Unable to apply folder permissions to: ", destinationFilename); _logger.DebugException(ex.Message, ex); } else { throw; } } } else { SetPermissions(destinationFilename, _configService.FileChmod); } return(episodeFile); }
private bool InstallUpdate(UpdatePackage updatePackage) { EnsureAppDataSafety(); if (OsInfo.IsWindows || _configFileProvider.UpdateMechanism != UpdateMechanism.Script) { var startupFolder = _appFolderInfo.StartUpFolder; var uiFolder = Path.Combine(startupFolder, "UI"); if (!_diskProvider.FolderWritable(startupFolder)) { throw new UpdateFolderNotWritableException("Cannot install update because startup folder '{0}' is not writable by the user '{1}'.", startupFolder, Environment.UserName); } if (!_diskProvider.FolderWritable(uiFolder)) { throw new UpdateFolderNotWritableException("Cannot install update because UI folder '{0}' is not writable by the user '{1}'.", uiFolder, Environment.UserName); } } if (_appFolderInfo.StartUpFolder.EndsWith("_output")) { _logger.ProgressDebug("Running in developer environment, not updating."); return(false); } var updateSandboxFolder = _appFolderInfo.GetUpdateSandboxFolder(); var packageDestination = Path.Combine(updateSandboxFolder, updatePackage.FileName); if (_diskProvider.FolderExists(updateSandboxFolder)) { _logger.Info("Deleting old update files"); _diskProvider.DeleteFolder(updateSandboxFolder, true); } _logger.ProgressInfo("Downloading update {0}", updatePackage.Version); _logger.Debug("Downloading update package from [{0}] to [{1}]", updatePackage.Url, packageDestination); _httpClient.DownloadFile(updatePackage.Url, packageDestination); _logger.ProgressInfo("Verifying update package"); if (!_updateVerifier.Verify(updatePackage, packageDestination)) { _logger.Error("Update package is invalid"); throw new UpdateVerificationFailedException("Update file '{0}' is invalid", packageDestination); } _logger.Info("Update package verified successfully"); _logger.ProgressInfo("Extracting Update package"); _archiveService.Extract(packageDestination, updateSandboxFolder); _logger.Info("Update package extracted successfully"); EnsureValidBranch(updatePackage); _backupService.Backup(BackupType.Update); if (OsInfo.IsNotWindows && _configFileProvider.UpdateMechanism == UpdateMechanism.Script) { InstallUpdateWithScript(updateSandboxFolder); return(true); } _logger.Info("Preparing client"); _diskTransferService.TransferFolder(_appFolderInfo.GetUpdateClientFolder(), updateSandboxFolder, TransferMode.Move); var updateClientExePath = _appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime); if (!_diskProvider.FileExists(updateClientExePath)) { _logger.Warn("Update client {0} does not exist, aborting update.", updateClientExePath); return(false); } // Set executable flag on update app if (OsInfo.IsOsx || (OsInfo.IsLinux && PlatformInfo.IsNetCore)) { _diskProvider.SetFilePermissions(updateClientExePath, "755", null); } _logger.Info("Starting update client {0}", updateClientExePath); _logger.ProgressInfo("Lidarr will restart shortly."); _processProvider.Start(updateClientExePath, GetUpdaterArgs(updateSandboxFolder)); return(true); }
public MediaInfoModel GetMediaInfo(string filename) { if (!_diskProvider.FileExists(filename)) { throw new FileNotFoundException("Media file does not exist: " + filename); } MediaInfo mediaInfo = null; // TODO: Cache media info by path, mtime and length so we don't need to read files multiple times try { mediaInfo = new MediaInfo(); _logger.Debug("Getting media info from {0}", filename); if (filename.ToLower().EndsWith(".ts")) { // For .ts files we often have to scan more of the file to get all the info we need mediaInfo.Option("ParseSpeed", "0.3"); } else { mediaInfo.Option("ParseSpeed", "0.0"); } int open; using (var stream = _diskProvider.OpenReadStream(filename)) { open = mediaInfo.Open(stream); } if (open != 0) { int audioRuntime; int videoRuntime; int generalRuntime; // Runtime int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out videoRuntime); int.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "PlayTime"), out audioRuntime); int.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out generalRuntime); // Audio Channels var audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim(); var audioChannelPositions = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions/String2"); int.TryParse(audioChannelsStr, out var audioChannels); if (audioRuntime == 0 && videoRuntime == 0 && generalRuntime == 0) { // No runtime, ask mediainfo to scan the whole file mediaInfo.Option("ParseSpeed", "1.0"); using (var stream = _diskProvider.OpenReadStream(filename)) { open = mediaInfo.Open(stream); } } else if (audioChannels > 2 && audioChannelPositions.IsNullOrWhiteSpace()) { // Some files with DTS don't have ChannelPositions unless more of the file is scanned mediaInfo.Option("ParseSpeed", "0.3"); using (var stream = _diskProvider.OpenReadStream(filename)) { open = mediaInfo.Open(stream); } } } if (open != 0) { int width; int height; int videoBitRate; int audioBitRate; int audioRuntime; int videoRuntime; int generalRuntime; int streamCount; int audioChannels; int audioChannelsOrig; int videoBitDepth; decimal videoFrameRate; int videoMultiViewCount; string subtitles = mediaInfo.Get(StreamKind.General, 0, "Text_Language_List"); string scanType = mediaInfo.Get(StreamKind.Video, 0, "ScanType"); int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "Width"), out width); int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "Height"), out height); int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "BitRate"), out videoBitRate); if (videoBitRate <= 0) { int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "BitRate_Nominal"), out videoBitRate); } decimal.TryParse(mediaInfo.Get(StreamKind.Video, 0, "FrameRate"), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out videoFrameRate); int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "BitDepth"), out videoBitDepth); int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "MultiView_Count"), out videoMultiViewCount); //Runtime int.TryParse(mediaInfo.Get(StreamKind.Video, 0, "PlayTime"), out videoRuntime); int.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "PlayTime"), out audioRuntime); int.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out generalRuntime); string aBitRate = mediaInfo.Get(StreamKind.Audio, 0, "BitRate").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim(); int.TryParse(aBitRate, out audioBitRate); int.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "StreamCount"), out streamCount); string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim(); int.TryParse(audioChannelsStr, out audioChannels); string audioChannelsStrOrig = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)_Original").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim(); int.TryParse(audioChannelsStrOrig, out audioChannelsOrig); var audioChannelPositionsText = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions"); var audioChannelPositionsTextOrig = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions_Original"); var audioChannelPositions = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions/String2"); string audioLanguages = mediaInfo.Get(StreamKind.General, 0, "Audio_Language_List"); string videoProfile = mediaInfo.Get(StreamKind.Video, 0, "Format_Profile").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim(); string audioProfile = mediaInfo.Get(StreamKind.Audio, 0, "Format_Profile").Split(new string[] { " /" }, StringSplitOptions.None)[0].Trim(); var mediaInfoModel = new MediaInfoModel { ContainerFormat = mediaInfo.Get(StreamKind.General, 0, "Format"), VideoFormat = mediaInfo.Get(StreamKind.Video, 0, "Format"), VideoCodecID = mediaInfo.Get(StreamKind.Video, 0, "CodecID"), VideoProfile = videoProfile, VideoCodecLibrary = mediaInfo.Get(StreamKind.Video, 0, "Encoded_Library"), VideoBitrate = videoBitRate, VideoBitDepth = videoBitDepth, VideoMultiViewCount = videoMultiViewCount, VideoColourPrimaries = mediaInfo.Get(StreamKind.Video, 0, "colour_primaries"), VideoTransferCharacteristics = mediaInfo.Get(StreamKind.Video, 0, "transfer_characteristics"), Height = height, Width = width, AudioFormat = mediaInfo.Get(StreamKind.Audio, 0, "Format"), AudioCodecID = mediaInfo.Get(StreamKind.Audio, 0, "CodecID"), AudioProfile = audioProfile, AudioCodecLibrary = mediaInfo.Get(StreamKind.Audio, 0, "Encoded_Library"), AudioAdditionalFeatures = mediaInfo.Get(StreamKind.Audio, 0, "Format_AdditionalFeatures"), AudioBitrate = audioBitRate, RunTime = GetBestRuntime(audioRuntime, videoRuntime, generalRuntime), AudioStreamCount = streamCount, AudioChannelsContainer = audioChannels, AudioChannelsStream = audioChannelsOrig, AudioChannelPositions = audioChannelPositions, AudioChannelPositionsTextContainer = audioChannelPositionsText, AudioChannelPositionsTextStream = audioChannelPositionsTextOrig, VideoFps = videoFrameRate, AudioLanguages = audioLanguages, Subtitles = subtitles, ScanType = scanType, SchemaRevision = CURRENT_MEDIA_INFO_SCHEMA_REVISION }; return(mediaInfoModel); } else { _logger.Warn("Unable to open media info from file: " + filename); } } catch (DllNotFoundException ex) { _logger.Error(ex, "mediainfo is required but was not found"); } catch (Exception ex) { _logger.Error(ex, "Unable to parse media info from file: {0}", filename); } finally { mediaInfo?.Close(); } return(null); }
public void CheckForCompletedItem(IDownloadClient downloadClient, TrackedDownload trackedDownload, List <History.History> grabbedHistory, List <History.History> importedHistory) { if (!_configService.EnableCompletedDownloadHandling) { return; } if (trackedDownload.DownloadItem.Status == DownloadItemStatus.Completed && trackedDownload.State == TrackedDownloadState.Downloading) { var grabbedItems = GetHistoryItems(grabbedHistory, trackedDownload.DownloadItem.DownloadClientId); if (!grabbedItems.Any() && trackedDownload.DownloadItem.Category.IsNullOrWhiteSpace()) { UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Download wasn't grabbed by drone or not in a category, ignoring download."); return; } var importedItems = GetHistoryItems(importedHistory, trackedDownload.DownloadItem.DownloadClientId); if (importedItems.Any()) { trackedDownload.State = TrackedDownloadState.Imported; UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Already added to history as imported."); } else { string downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder; string downloadItemOutputPath = trackedDownload.DownloadItem.OutputPath; if (downloadItemOutputPath.IsNullOrWhiteSpace()) { UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download doesn't contain intermediate path, ignoring download."); return; } if (!downloadedEpisodesFolder.IsNullOrWhiteSpace() && (downloadedEpisodesFolder.PathEquals(downloadItemOutputPath) || downloadedEpisodesFolder.IsParentPath(downloadItemOutputPath))) { UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Intermediate Download path inside drone factory, ignoring download."); return; } if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath)) { var importResults = _downloadedEpisodesImportService.ProcessFolder(new DirectoryInfo(trackedDownload.DownloadItem.OutputPath), trackedDownload.DownloadItem); ProcessImportResults(trackedDownload, importResults); } else if (_diskProvider.FileExists(trackedDownload.DownloadItem.OutputPath)) { var importResults = _downloadedEpisodesImportService.ProcessFile(new FileInfo(trackedDownload.DownloadItem.OutputPath), trackedDownload.DownloadItem); ProcessImportResults(trackedDownload, importResults); } else { if (grabbedItems.Any()) { var episodeIds = trackedDownload.DownloadItem.RemoteEpisode.Episodes.Select(v => v.Id).ToList(); // Check if we can associate it with a previous drone factory import. importedItems = importedHistory.Where(v => v.Data.GetValueOrDefault(DownloadTrackingService.DOWNLOAD_CLIENT_ID) == null && episodeIds.Contains(v.EpisodeId) && v.Data.GetValueOrDefault("droppedPath") != null && new FileInfo(v.Data["droppedPath"]).Directory.Name == grabbedItems.First().SourceTitle ).ToList(); if (importedItems.Count == 1) { var importedFile = new FileInfo(importedItems.First().Data["droppedPath"]); if (importedFile.Directory.Name == grabbedItems.First().SourceTitle) { trackedDownload.State = TrackedDownloadState.Imported; importedItems.First().Data[DownloadTrackingService.DOWNLOAD_CLIENT] = grabbedItems.First().Data[DownloadTrackingService.DOWNLOAD_CLIENT]; importedItems.First().Data[DownloadTrackingService.DOWNLOAD_CLIENT_ID] = grabbedItems.First().Data[DownloadTrackingService.DOWNLOAD_CLIENT_ID]; _historyService.UpdateHistoryData(importedItems.First().Id, importedItems.First().Data); UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Intermediate Download path does not exist, but found probable drone factory ImportEvent."); return; } } } UpdateStatusMessage(trackedDownload, LogLevel.Error, "Intermediate Download path does not exist: {0}", trackedDownload.DownloadItem.OutputPath); return; } } } if (_configService.RemoveCompletedDownloads && trackedDownload.State == TrackedDownloadState.Imported && !trackedDownload.DownloadItem.IsReadOnly) { try { _logger.Debug("[{0}] Removing completed download from history.", trackedDownload.DownloadItem.Title); downloadClient.RemoveItem(trackedDownload.DownloadItem.DownloadClientId); if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath)) { _logger.Debug("Removing completed download directory: {0}", trackedDownload.DownloadItem.OutputPath); _diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath, true); } else if (_diskProvider.FileExists(trackedDownload.DownloadItem.OutputPath)) { _logger.Debug("Removing completed download file: {0}", trackedDownload.DownloadItem.OutputPath); _diskProvider.DeleteFile(trackedDownload.DownloadItem.OutputPath); } trackedDownload.State = TrackedDownloadState.Removed; } catch (NotSupportedException) { UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Removing item not supported by your download client."); } } }
public void Start(string installationFolder, int processId) { _logger.Info("Installation Folder: {0}", installationFolder); _logger.Info("Updating Sonarr from version {0} to version {1}", _detectExistingVersion.GetExistingVersion(installationFolder), BuildInfo.Version); Verify(installationFolder, processId); var appType = _detectApplicationType.GetAppType(); _processProvider.FindProcessByName(ProcessProvider.SONARR_CONSOLE_PROCESS_NAME); _processProvider.FindProcessByName(ProcessProvider.SONARR_PROCESS_NAME); if (OsInfo.IsWindows) { _terminateNzbDrone.Terminate(processId); } try { _backupAndRestore.Backup(installationFolder); _backupAppData.Backup(); if (OsInfo.IsWindows) { if (_processProvider.Exists(ProcessProvider.SONARR_CONSOLE_PROCESS_NAME) || _processProvider.Exists(ProcessProvider.SONARR_PROCESS_NAME)) { _logger.Error("Sonarr was restarted prematurely by external process."); return; } } try { _logger.Info("Copying new files to target folder"); _diskTransferService.MirrorFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder); // Handle OSX package update and set executable flag on Sonarr app if (OsInfo.IsOsx) { var shimPath = Path.Combine(installationFolder, "Sonarr"); var realShimPath = Path.Combine(installationFolder, "../Sonarr"); if (installationFolder.EndsWith("/MacOS/bin") && _diskProvider.FileExists(realShimPath)) { // New MacOS App stores Sonarr binaries in MacOS/bin and has a shim in MacOS // Delete the shim in the downloaded update, we shouldn't update the shim unnecessarily _diskProvider.DeleteFile(shimPath); } else { // Old MacOS App stores Sonarr binaries in MacOS together with shell script // Make shim executable _diskProvider.SetPermissions(shimPath, "0755"); } } } catch (Exception e) { _logger.Error(e, "Failed to copy upgrade package to target folder."); _backupAndRestore.Restore(installationFolder); throw; } } finally { if (OsInfo.IsWindows) { _startNzbDrone.Start(appType, installationFolder); } else { _terminateNzbDrone.Terminate(processId); _logger.Info("Waiting for external auto-restart."); for (int i = 0; i < 5; i++) { System.Threading.Thread.Sleep(1000); if (_processProvider.Exists(ProcessProvider.SONARR_PROCESS_NAME)) { _logger.Info("Sonarr was restarted by external process."); break; } } if (!_processProvider.Exists(ProcessProvider.SONARR_PROCESS_NAME)) { _startNzbDrone.Start(appType, installationFolder); } } } }