diff --git a/FileParser.vb b/FileParser.vb index 69f86a1..e9e5197 100644 --- a/FileParser.vb +++ b/FileParser.vb @@ -6,17 +6,24 @@ Imports MusicFolderSyncer.Codec.CodecType Imports MusicFolderSyncer.SyncSettings.TranscodeMode Imports System.IO Imports System.Environment +Imports System.Threading #End Region Class FileParser + Implements IDisposable + Private ProcessID As Int32 + Private Shared FileTimeout As Int32 = 60 ReadOnly Property FilePath As String + Private FileLock As Boolean Private MyGlobalSyncSettings As GlobalSyncSettings Private SyncSettings As SyncSettings() + Private SourceFileStream As FileStream = Nothing #Region " New " Public Sub New(ByRef NewGlobalSyncSettings As GlobalSyncSettings, ByVal NewProcessID As Int32, ByVal NewFilePath As String, Optional NewSyncSettings As SyncSettings = Nothing) + ProcessID = NewProcessID FilePath = NewFilePath MyGlobalSyncSettings = NewGlobalSyncSettings @@ -25,10 +32,72 @@ Class FileParser Else SyncSettings = {NewSyncSettings} End If + + SourceFileStream = WaitForFile(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read, FileTimeout) + If SourceFileStream Is Nothing Then + MyLog.Write(ProcessID, "Could not get file system lock on source file: """ & FilePath.Substring(MyGlobalSyncSettings.SourceDirectory.Length) & """.", Warning) + FileLock = False + Else + FileLock = True + End If + End Sub #End Region + Public Overridable Sub Dispose() Implements IDisposable.Dispose + If Not SourceFileStream Is Nothing Then + SourceFileStream.Close() + End If + End Sub + #Region " Transfer File To Sync Folder " + Private Shared Function SafeCopy(SourceFileStream As FileStream, SyncFilePath As String) As ReturnObject + + Dim MyReturnObject As ReturnObject + + Try + If SourceFileStream Is Nothing Then Throw New Exception("Could not get file system lock on source file.") + Using NewFile As FileStream = WaitForFile(SyncFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None, FileTimeout) + If Not NewFile Is Nothing Then + SourceFileStream.CopyTo(NewFile) + MyReturnObject = New ReturnObject(True, "", Nothing) + Else + MyReturnObject = New ReturnObject(False, "Could not get file system lock on destination file.", Nothing) + End If + End Using + Catch ex As Exception + MyReturnObject = New ReturnObject(False, ex.Message, 0) + End Try + + Return MyReturnObject + + End Function + + Private Shared Function WaitForFile(fullPath As String, mode As FileMode, access As FileAccess, share As FileShare, timeoutSeconds As Int32) As FileStream + Dim msBetweenTries As Int32 = 500 + Dim numTries As Int32 = CInt(Math.Ceiling(timeoutSeconds / (msBetweenTries / 1000))) + + For count As Integer = 0 To numTries + Dim fs As FileStream = Nothing + + Try + fs = New FileStream(fullPath, mode, access, share) + + fs.ReadByte() + fs.Seek(0, SeekOrigin.Begin) + + Return fs + Catch generatedExceptionName As IOException + If fs IsNot Nothing Then + fs.Dispose() + End If + Thread.Sleep(msBetweenTries) + End Try + Next + + Return Nothing + End Function + Public Function TransferToSyncFolder() As ReturnObject Dim MyReturnObject As ReturnObject @@ -49,7 +118,8 @@ Class FileParser SyncSetting.Encoder.GetFileExtensions(0) Else Directory.CreateDirectory(Path.GetDirectoryName(SyncFilePath)) - File.Copy(FilePath, SyncFilePath, True) + Dim Result As ReturnObject = SafeCopy(SourceFileStream, SyncFilePath) + If Not Result.Success Then Throw New Exception(Result.ErrorMessage) End If Dim NewFile As New FileInfo(SyncFilePath) @@ -144,7 +214,8 @@ Class FileParser TranscodeFile(SyncFilePath, SyncSetting) Else Directory.CreateDirectory(Path.GetDirectoryName(SyncFilePath)) - File.Copy(FilePath, SyncFilePath, True) + Dim Result As ReturnObject = SafeCopy(SourceFileStream, SyncFilePath) + If Not Result.Success Then Throw New Exception(Result.ErrorMessage) End If MyLog.Write(ProcessID, "...successfully added file to sync folder.", Information) @@ -257,6 +328,11 @@ Class FileParser Private Function CheckFileTags(MyCodec As Codec, SyncSetting As SyncSettings) As Boolean + If FileLock = False Then + MyLog.Write(ProcessID, "...could not obtain file tags. Could not get file system lock on source file.", Warning) + Return False + End If + Dim TagsObject As ReturnObject = MyCodec.MatchTag(FilePath, SyncSetting.GetWatcherTags) If TagsObject.Success Then diff --git a/NewSyncWindow.xaml.vb b/NewSyncWindow.xaml.vb index ec4c4ef..711d836 100644 --- a/NewSyncWindow.xaml.vb +++ b/NewSyncWindow.xaml.vb @@ -580,9 +580,9 @@ Public Class NewSyncWindow Dim TransferResult As ReturnObject Try - Dim MyFileParser As New FileParser(MyGlobalSyncSettings, ProcessID, FilePath, SingleSyncSettings) - TransferResult = MyFileParser.TransferToSyncFolder() - + Using MyFileParser As New FileParser(MyGlobalSyncSettings, ProcessID, FilePath, SingleSyncSettings) + TransferResult = MyFileParser.TransferToSyncFolder() + End Using If TransferResult.Success Then Dim NewSize As Int64 = CType(TransferResult.MyObject, Int64) diff --git a/TrayApp.vb b/TrayApp.vb index 6f9e0e9..7b0e335 100644 --- a/TrayApp.vb +++ b/TrayApp.vb @@ -199,14 +199,14 @@ Public Class TrayApp If FilterMatch(e.Name) Then MyLog.Write(FileID, "File renamed: " & e.FullPath, Information) - Dim MyFileParser As New FileParser(UserGlobalSyncSettings, FileID, e.FullPath) - Dim Result As ReturnObject = MyFileParser.RenameInSyncFolder(e.OldFullPath) 'RenameInSyncFolder(MyFileParser, e.OldFullPath) - If Result.Success Then - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File renamed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) - Else - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File rename failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) - End If - MyFileParser = Nothing + Using MyFileParser As New FileParser(UserGlobalSyncSettings, FileID, e.FullPath) + Dim Result As ReturnObject = MyFileParser.RenameInSyncFolder(e.OldFullPath) + If Result.Success Then + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File renamed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) + Else + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File rename failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) + End If + End Using End If If Interlocked.Equals(FileID, MaxFileID) Then @@ -221,41 +221,41 @@ Public Class TrayApp 'Handles changed and new files If FilterMatch(e.Name) Then - Dim MyFileParser As New FileParser(UserGlobalSyncSettings, FileID, e.FullPath) - Select Case e.ChangeType - Case Is = IO.WatcherChangeTypes.Changed - MyLog.Write(FileID, "Source file changed: " & e.FullPath, Information) - Dim Result As ReturnObject = MyFileParser.DeleteInSyncFolder() - - If Result.Success Then - Result = MyFileParser.TransferToSyncFolder() - End If - - If Result.Success Then - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) - Else - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processing failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) - End If - Case Is = IO.WatcherChangeTypes.Created - MyLog.Write(FileID, "Source file created: " & e.FullPath, Information) - Dim Result As ReturnObject = MyFileParser.TransferToSyncFolder() - - If Result.Success Then - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) - Else - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processing failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) - End If - Case Is = IO.WatcherChangeTypes.Deleted - MyLog.Write(FileID, "Source file deleted: " & e.FullPath, Information) - Dim Result As ReturnObject = MyFileParser.DeleteInSyncFolder() - - If Result.Success Then - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File deleted:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) - Else - If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File deletion failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) - End If - End Select - MyFileParser = Nothing + Using MyFileParser As New FileParser(UserGlobalSyncSettings, FileID, e.FullPath) + Select Case e.ChangeType + Case Is = IO.WatcherChangeTypes.Changed + MyLog.Write(FileID, "Source file changed: " & e.FullPath, Information) + Dim Result As ReturnObject = MyFileParser.DeleteInSyncFolder() + + If Result.Success Then + Result = MyFileParser.TransferToSyncFolder() + End If + + If Result.Success Then + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) + Else + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processing failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) + End If + Case Is = IO.WatcherChangeTypes.Created + MyLog.Write(FileID, "Source file created: " & e.FullPath, Information) + Dim Result As ReturnObject = MyFileParser.TransferToSyncFolder() + + If Result.Success Then + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) + Else + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File processing failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) + End If + Case Is = IO.WatcherChangeTypes.Deleted + MyLog.Write(FileID, "Source file deleted: " & e.FullPath, Information) + Dim Result As ReturnObject = MyFileParser.DeleteInSyncFolder() + + If Result.Success Then + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File deleted:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Info) + Else + If Tray.Visible Then Tray.ShowBalloonTip(BalloonTime, "File deletion failed:", e.FullPath.Substring(UserGlobalSyncSettings.SourceDirectory.Length), ToolTipIcon.Error) + End If + End Select + End Using End If If Interlocked.Equals(FileID, MaxFileID) Then