Jump to content
Webturtle

Warnung vor knappem Speicherplatz (AutoHotkey-Script)

Recommended Posts

Webturtle
Posted (edited)

Hallo,

 

da auch andere Nutzer eine Warnung des DVBViewer Pro oder GE vermissen, wenn der Speicherplatz des Aufnahmelaufwerks knapp wird (vgl. http://www.DVBViewer.tv/forum/topic/60939-warnung-bei-tauben-aufnahmen), lade ich hier ein AutoHotkey-Script hoch, das ich als Behelfslösung geschrieben habe. Das Script wurde mit AutoHotkey Basic unter Windows XP geschrieben. Es ist quick und dirty und nicht ausführlich kommentiert.

 

Es installiert sich im Systray und zeigt nach in einem festgelegten Turnus den freien Speicherplatz an. Beim Unterschreiten wird eine Message-Box angezeigt.

Beim Rechtsklick auf das Symbol im Systray kann man sich u.a. die AutoHotkey-Hilfe anzeigen lassen,  das Script neu starten (reload) oder editieren. Beim Doppelklick darauf wird das Script im augenblicklichen Stand angezeigt. 

 

Um es zu starten, muß man AutoHotkey herunterladen und installieren (https://autohotkey.com/download/). Sicherheitshalber sollten man auch AutoHotkey Basic 1.0.* herunterladen,  falls das Script unter AutoHotkey_L 1.1.* oder 2.0-a* nicht korrekt läuft. Bei einigen Befehlen hat es Änderungen gegeben (Bei mir liefen einige Scripst nicht mehr korrekt). Mit der Installation wird auch eine englische Hilfe installiert. Man kann aber am angegebenen Ort auch eine deutsche Hilfe herunterladen. AutoHotkey bietet auch einen Macro-Recorder und die Möglichkeit, Scripte in Exe-Dateien zu kompilieren.

 

Laufwerke, Speichergrößen und Zeiten müssen im Script den eigenen Bedürfnissen angepaßt werden! Falls Fragen auftauchen bitte melden.

 

Wer das Script verbessert oder modifiziert, möge bitte seine Version auch hier zur Verfügung stellen.

 

Hier das Script:

DiskSpaceWarning.zip

 

 

Viele Grüße

 

Webturtle

Edited by Webturtle
Fehler in Script (Freespace statt Freespace_I) beseitigt

Share this post


Link to post
nuts

Vielleicht mal als Ansatz für eine etwas "schlankere" Lösung:

Es gibt für den DVBViewer ja die vbs Scripte.

Über die startrec.vbs könnte man bei jedem Aufnahmestart die Überprüfung laufen lassen und bei Bedarf eine Warnmeldung anzeigen lassen.

Über den COM-Server des DVBViewers könnte man sich sogar das jeweilige Aufnahmeverzeichnis holen, sodass man bis auf den freien Speicherplatz nichts selbst definieren müsste.

 

Share this post


Link to post
majstang
Posted (edited)

Nice @Webturtle in these times around Christmas/New Year the abundance of "not-yet-had-time-to-watch-movie" recordings Im constantly on the edge of low HDD space, so something like your script is very needed:thumbsup: With @nuts suggestions it will be awesome, but i would suggest fetching the stuff from DMS, since the COM calls wont work if DVBV is not running.

Edited by majstang

Share this post


Link to post
Webturtle

Hallo,

 

@nuts: Kennst Du eine gute Einführung oder Befehlsübersicht für vbs Scripte? Außer mit Batch-Dateien hatte ich bisher nichts mit Windows-Scripten zu tun.

 

 

Viele Grüße

 

Webturtle

 

 

 

Share this post


Link to post
nuts

Nee nicht wirklich, da ich VBS nicht unbedingt gerne verwende und leider im Moment auch keine Zeit habe da mitzuhelfen.  

Ist aber ähnlich wie VB und somit ähnlich zu Autohotkey.

 

Du könntest auch den Hauptteil in der dir vertrauten Sprache belassen (als .exe zum download bereitstellen) und nur den Aufruf in die startrec.vbs verlegen

Wie das geht ist hier beschrieben: http://de.DVBViewer.tv/wiki/Command.vbs

 

Einstellbare Parameter würde ich dann als Kommandoparameter aus der startrec.vbs an das (Haupt-)Skript übergeben.

Dann muss sich kein Anwender mit dem Programmierzeugs befassen, sondern nur die 2 Dateien (deine .exe und deine startrec.vbs) herunterladen. ;) 

Share this post


Link to post
majstang

Hallo Webturtle,

 

Here is a version of your script which you may find interesting:)

 

Regards 

majstang

 

Spoiler

#NoEnv
#Persistent
LowSpaceLimit := 10 ; Set the lowest allowed Gigabyte (GB) Disk Space limit. When breached triggers the Low Disk Space warning.  
DMSFolder := DMS_RECfolder(BringIt) ;Check for the DMS Recording folder "First one only"
WatchFolder(DMSFolder, "ReportFunction", SubTree := 0, Watch := 19) ; No recursing
; ======================================================================================================================
; Auxiliary functions
; ======================================================================================================================
loadXML(ByRef data)
{
   o := ComObjCreate("MSXML2.DOMDocument.6.0")
   o.async := false
   ; o.preserveWhiteSpace := false ; true
   o.loadXML(data)
   return o
}
; ----------------------------------------------------------------------------------------------------------------------
DisplayNode(Nodes, Indent := 0) {
   For Node In Nodes {
      If Node.nodeName != "#text"
         Text .= Spaces(Indent) . Node.nodeName ": " . Node.nodeValue . "`n"
      Else
         Text .= Spaces(Indent) . Node.nodeValue . "`n"
      If Node.hasChildNodes
         Text .= DisplayNode(Node.childNodes, Indent + 2)
   }
   Return Text
}
; ----------------------------------------------------------------------------------------------------------------------
Spaces(N) {
   Loop, % N
      S .= " "
   Return S
}	
;--Asynchronous WebRequest URLToVar function----------------------------------------------------------------------
AsyncURLToVar(URL) {
   WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
   WebRequest.Open("GET", URL, true)
   WebRequest.Send()
   WebRequest.WaitForResponse()
   Return WebRequest.ResponseText
}
;---------------------------------------------------------------------------------------------------------	
; Find DVBViewer Media Server recording folder - the first one only 
DMS_RECfolder(FindIt) {
   recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html")
   RS_Status := loadXML(recordingstatus)
   RS_RECfolder := RS_Status.selectSingleNode("/status/recfolders/folder").text
   Return RS_RECfolder
} 
;---------------------------------------------------------------------------------------------------------	
; Check Recordingstatus of the DVBViewer Media Server 
DMS_Status(CheckForStatus) {
   recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html")
   RS_Status := loadXML(recordingstatus)
   RS_StatusVar := RS_Status.selectSingleNode("/status/reccount").text
   Return RS_StatusVar
} 
;---------------------------------------------------------------------------------------------------------	
; Convert Megabytes to Gigabytes
DisplaySize(FileSize) {
   Static GB := 1024
   Return (FileSize >= GB) ? (Round(FileSize / GB)) : FileSize
}
; ==================================================================================================================================
; Function:       Notifies about changes within folders.
;                 This is a rewrite of HotKeyIt's WatchDirectory() released at
;                    http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/
; Tested with:    AHK 1.1.23.01 (A32/U32/U64)
; Tested on:      Win 10 Pro x64
; Usage:          WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]])
; Parameters:
;     Folder      -  The full qualified path of the folder to be watched.
;                    Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching.
;                    Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime.
;                    If not, it will be done internally on exit.
;     UserFunc    -  The name of a user-defined function to call on changes. The function must accept at least two parameters:
;                    1: The path of the affected folder. The final backslash is not included even if it is a drive's root
;                       directory (e.g. C:).
;                    2: An array of change notifications containing the following keys:
;                       Action:  One of the integer values specified as FILE_ACTION_... (see below).
;                                In case of renaming Action is set to FILE_ACTION_RENAMED (4).
;                       Name:    The full path of the changed file or folder.
;                       OldName: The previous path in case of renaming, otherwise not used.
;                       IsDir:   True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False.
;                    Pass the string "**DEL" to remove the directory from the list of watched folders.
;     SubTree     -  Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders).
;                    Default: False - sub-folders aren't watched.
;     Watch       -  The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_...
;                    values specified below.
;                    Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME
; Return values:
;     Returns True on success; otherwise False.
; Change history:
;     1.0.02.00/2016-11-30/just me        -  bug-fix for closing handles with the '**END' option.
;     1.0.01.00/2016-03-14/just me        -  bug-fix for multiple folders
;     1.0.00.00/2015-06-21/just me        -  initial release
; License:
;     The Unlicense -> http://unlicense.org/
; Remarks:
;     Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64)
;     folders simultaneously.
; MSDN:
;     ReadDirectoryChangesW          msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx
;     FILE_NOTIFY_CHANGE_FILE_NAME   = 1   (0x00000001) : Notify about renaming, creating, or deleting a file.
;     FILE_NOTIFY_CHANGE_DIR_NAME    = 2   (0x00000002) : Notify about creating or deleting a directory.
;     FILE_NOTIFY_CHANGE_ATTRIBUTES  = 4   (0x00000004) : Notify about attribute changes.
;     FILE_NOTIFY_CHANGE_SIZE        = 8   (0x00000008) : Notify about any file-size change.
;     FILE_NOTIFY_CHANGE_LAST_WRITE  = 16  (0x00000010) : Notify about any change to the last write-time of files.
;     FILE_NOTIFY_CHANGE_LAST_ACCESS = 32  (0x00000020) : Notify about any change to the last access time of files.
;     FILE_NOTIFY_CHANGE_CREATION    = 64  (0x00000040) : Notify about any change to the creation time of files.
;     FILE_NOTIFY_CHANGE_SECURITY    = 256 (0x00000100) : Notify about any security-descriptor change.
;     FILE_NOTIFY_INFORMATION        msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx
;     FILE_ACTION_ADDED              = 1   (0x00000001) : The file was added to the directory.
;     FILE_ACTION_REMOVED            = 2   (0x00000002) : The file was removed from the directory.
;     FILE_ACTION_MODIFIED           = 3   (0x00000003) : The file was modified.
;     FILE_ACTION_RENAMED            = 4   (0x00000004) : The file was renamed (not defined by Microsoft).
;     FILE_ACTION_RENAMED_OLD_NAME   = 4   (0x00000004) : The file was renamed and this is the old name.
;     FILE_ACTION_RENAMED_NEW_NAME   = 5   (0x00000005) : The file was renamed and this is the new name.
;     GetOverlappedResult            msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx
;     CreateFile                     msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
;     FILE_FLAG_BACKUP_SEMANTICS     = 0x02000000
;     FILE_FLAG_OVERLAPPED           = 0x40000000
; ==================================================================================================================================
WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) {
   Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}}
   Static TimerID := "**" . A_TickCount
   Static TimerFunc := Func("WatchFolder").Bind(TimerID, "")
   Static MAXIMUM_WAIT_OBJECTS := 64
   Static MAX_DIR_PATH := 260 - 12 + 1
   Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode
   Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB)
   Static SizeOfOVL := 32     ; size of the OVERLAPPED structure (64-bit)
   Static WatchedFolders := {}
   Static EventArray := []
   Static HandleArray := []
   Static WaitObjects := 0
   Static BytesRead := 0
   Static Paused := False
   ; ===============================================================================================================================
   If (Folder = "")
      Return False
   SetTimer, % TimerFunc, Off
   RebuildWaitObjects := False
   ; ===============================================================================================================================
   If (Folder = TimerID) { ; called by timer
      If (ObjCount := EventArray.Length()) && !Paused {
         ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
         While (ObjIndex >= 0) && (ObjIndex < ObjCount) {
            FolderName := WatchedFolders[ObjIndex + 1]
            D := WatchedFolders[FolderName]
            If DllCall("GetOverlappedResult", "Ptr", D.Handle, "Ptr", D.OVLAddr, "UIntP", BytesRead, "Int", True) {
               Changes := []
               FNIAddr := D.FNIAddr
               FNIMax := FNIAddr + BytesRead
               OffSet := 0
               PrevIndex := 0
               PrevAction := 0
               PrevName := ""
               Loop {
                  FNIAddr += Offset
                  OffSet := NumGet(FNIAddr + 0, "UInt")
                  Action := NumGet(FNIAddr + 4, "UInt")
                  Length := NumGet(FNIAddr + 8, "UInt") // 2
                  Name   := FolderName . "\" . StrGet(FNIAddr + 12, Length, "UTF-16")
                  IsDir  := InStr(FileExist(Name), "D") ? 1 : 0
                  If (Name = PrevName) {
                     If (Action = PrevAction)
                        Continue
                     If (Action = 1) && (PrevAction = 2) {
                        PrevAction := Action
                        Changes.RemoveAt(PrevIndex--)
                        Continue
                     }
                  }
                  If (Action = 4)
                     PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0})
                  Else If (Action = 5) && (PrevAction = 4) {
                     Changes[PrevIndex, "Name"] := Name
                     Changes[PrevIndex, "IsDir"] := IsDir
                  }
                  Else
                     PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir})
                  PrevAction := Action
                  PrevName := Name
               } Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax)
               If (Changes.Length() > 0)
                  D.Func.Call(FolderName, Changes)
               DllCall("ResetEvent", "Ptr", EventArray[D.Index])
               DllCall("ReadDirectoryChangesW", "Ptr", D.Handle, "Ptr", D.FNIAddr, "UInt", SizeOfFNI, "Int", D.SubTree
                                              , "UInt", D.Watch, "UInt", 0, "Ptr", D.OVLAddr, "Ptr", 0)
            }
            ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
            Sleep, 0
         }
      }
   }
   ; ===============================================================================================================================
   Else If (Folder = "**PAUSE") { ; called to pause/resume watching
      Paused := !!UserFunc
      RebuildObjects := Paused
   }
   ; ===============================================================================================================================
   Else If (Folder = "**END") { ; called to stop watching
      For K, D In WatchedFolders
         If K Is Not Integer
            DllCall("CloseHandle", "Ptr", D.Handle)
      For Each, Event In EventArray
         DllCall("CloseHandle", "Ptr", Event)
      WatchedFolders := {}
      EventArray := []
      Paused := False
      Return True
   }
   ; ===============================================================================================================================
   Else { ; called to add, update, or remove folders
      Folder := RTrim(Folder, "\")
      VarSetCapacity(LongPath, SizeOfLongPath, 0)
      If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", SizeOfLongPath)
         Return False
      VarSetCapacity(LongPath, -1)
      Folder := LongPath
      If (WatchedFolders[Folder]) { ; update or remove
         Handle := WatchedFolders[Folder, "Handle"]
         Index  := WatchedFolders[Folder, "Index"]
         DllCall("CloseHandle", "Ptr", Handle)
         DllCall("CloseHandle", "Ptr", EventArray[Index])
         EventArray.RemoveAt(Index)
         WatchedFolders.RemoveAt(Index)
         WatchedFolders.Delete(Folder)
         RebuildWaitObjects := True
      }
      If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Length() < MAXIMUM_WAIT_OBJECTS) {
         If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) {
            Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03
                                          , "UInt", 0x42000000, "Ptr", 0, "UPtr")
            If (Handle > 0) {
               Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0)
               Index := EventArray.Push(Event)
               WatchedFolders[Index] := Folder
               WatchedFolders[Folder] := {Func: UserFunc, Handle: Handle, Index: Index, SubTree: !!SubTree, Watch: Watch}
               WatchedFolders[Folder].SetCapacity("FNIBuff", SizeOfFNI)
               FNIAddr := WatchedFolders[Folder].GetAddress("FNIBuff")
               DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI)
               WatchedFolders[Folder, "FNIAddr"] := FNIAddr
               WatchedFolders[Folder].SetCapacity("OVLBuff", SizeOfOVL)
               OVLAddr := WatchedFolders[Folder].GetAddress("OVLBuff")
               DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL)
               NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr")
               WatchedFolders[Folder, "OVLAddr"] := OVLAddr
               DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree
                                              , "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0)
               RebuildWaitObjects := True
            }
         }
      }
      If (RebuildWaitObjects) {
         VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0)
         OffSet := &WaitObjects
         For Index, Event In EventArray
            Offset := NumPut(Event, Offset + 0, 0, "Ptr")
      }
   }
   ; ===============================================================================================================================
   If (EventArray.Length() > 0)
      SetTimer, % TimerFunc, -100
   Return (RebuildWaitObjects) ; returns True on success, otherwise False
}
; ==================================================================================================================================
; WatchFolder - Reportfunctions
; ==================================================================================================================================
ReportFunction(Directory, Changes) {
   Global DMSFolder, LowSpaceLimit
   For Each, Change In Changes {
      Action := Change.Action
      Name := Change.Name
	  ; -------------------------------------------------------------------------------------------------------------------------
      ; Action 1 (added) = TS added (recording starts) in DMS recording folder, which triggers the Drive Space check
      If (Action = 1) {
		 SplitPath, Name,, Dir, Ext,, OutDrive
		 If (Dir = DMSFolder) && (Ext = "ts") {
		    FileGetTime, Created, %Name%, C
            FileGetTime, Modified, %Name%, M
			If (DMS_Status(statusquery)) && (Created = Modified) { ; DMS has ongoing recording and file "name" is still in recording
              DriveSpaceFree, FreeSpace, % OutDrive . "\"
			  If (DisplaySize(FreeSpace) < LowSpaceLimit)
			    TrayTip, Low Disk Space, % "You are running out of disk space on Local Disk (" OutDrive "). Only " DisplaySize(FreeSpace) 
				. " GB left. Free space on this drive by deleting old or unnecessary files", 30, 2
			}
         }
      }
   }
}

 

 

Share this post


Link to post
Webturtle

Hello majstang,

 

your script is very interesting!:thumbsup:   Or with other words: Whow or humpf! It seems to be objectorientated. Is this vbs? Last time I've written some programs was with Turbo C. So for me it could be klingon as well.

 

 

Regards

 

Webturtle

Share this post


Link to post
nuts

It's autohotkey isnt it? ;) 

Share this post


Link to post
majstang
37 minutes ago, Webturtle said:

your script is very interesting!:thumbsup:   Or with other words: Whow or humpf! It seems to be objectorientated. Is this vbs? Last time I've written some programs was with Turbo C. So for me it could be klingon as well.

Yes, it uses both function objects and arrays. All the code is snippets taken from a program I have been working on for years. WatchFolder is the backbone of that program and I intentionally did leave out watching of the NAS (you had in your original script as second DMS recording folder (it can watch all DMS folders if you like)) cuz I thought maybe you wanna add it on your own? If not I will do it for you (it might be a bit tricky if you are not scripting that much). Watchfolder is the replacement for @nuts VBS idea to catch when a recording starts which then triggers the low disk space check. Its a failsafe method really. So no VBS used. I dont even know if the DVBViewer recording.vbs is invoked when a DMS recording is started. It might be, if DVBViewer is running, but if DVBV is not running the low disk space check could fail if using the vbs.  

 

Other than that focus on the ReportFunction if you wanna add the sleep and goto label stuff you were using for the second TrayTip (repeat of warning) and the MsgBox, but I recommend using real timers instead.

 

Best regards

majstang

 

1 hour ago, nuts said:

It's autohotkey isnt it? ;) 

:rotfl: 

Share this post


Link to post
majstang

Ok, script now retrieves ALL DMS recording folders and checks for low disk space (warning) when a recording is started and located to the corresponding drive.

NAS UNC paths untested.

 

regards

majstang

 

Spoiler

#NoEnv
#Persistent
Global RS_RECfolderArray := []
LowSpaceLimit := 10 ; Set the lowest allowed Gigabyte (GB) Disk Space limit. When breached triggers the Low Disk Space warning.  
DMS_RECfolder(BringIt) ;Check for the DMS Recording folders
for key, value in RS_RECfolderArray
  WatchFolders .= (A_Index > 1 ? "|" : "") value "" ; Watching subfolders is not needed for what this script does. Add a star "*"  
Loop, Parse, WatchFolders,|                         ; between the last double quotes if wanting watched subfolders 
{
   SubTree := (SubStr(A_LoopField, StrLen(A_LoopField)) = "*")
   WatchFolder(RTrim(A_LoopField, "*"), "ReportFunction", SubTree, Watch := 19)
}
; ======================================================================================================================
; Auxiliary functions
; ======================================================================================================================
loadXML(ByRef data)
{
   o := ComObjCreate("MSXML2.DOMDocument.6.0")
   o.async := false
   ; o.preserveWhiteSpace := false ; true
   o.loadXML(data)
   return o
}
; ----------------------------------------------------------------------------------------------------------------------
DisplayNode(Nodes, Indent := 0) {
   For Node In Nodes {
      If Node.nodeName != "#text"
         Text .= Spaces(Indent) . Node.nodeName ": " . Node.nodeValue . "`n"
      Else
         Text .= Spaces(Indent) . Node.nodeValue . "`n"
      If Node.hasChildNodes
         Text .= DisplayNode(Node.childNodes, Indent + 2)
   }
   Return Text
}
; ----------------------------------------------------------------------------------------------------------------------
Spaces(N) {
   Loop, % N
      S .= " "
   Return S
}	
;--Asynchronous WebRequest URLToVar function----------------------------------------------------------------------
AsyncURLToVar(URL) {
   WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
   WebRequest.Open("GET", URL, true)
   WebRequest.Send()
   WebRequest.WaitForResponse()
   Return WebRequest.ResponseText
}
;---------------------------------------------------------------------------------------------------------	
; Find DVBViewer Media Server recording folder - the first one only 
DMS_RECfolder(FindIt) {
   recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html")
   RS_Status := loadXML(recordingstatus)
   foldernodes := RS_Status.selectSingleNode("/status/recfolders/folder")
   for folder in ( foldernodes.parentNode.selectNodes("folder"), folders := "" )
     RS_RECfolderArray.Push(folder.text)
} 
;---------------------------------------------------------------------------------------------------------	
; Check Recordingstatus of the DVBViewer Media Server 
DMS_Status(CheckForStatus) {
   recordingstatus := AsyncURLToVar("http://127.0.0.1:8089/api/status2.html")
   RS_Status := loadXML(recordingstatus)
   RS_StatusVar := RS_Status.selectSingleNode("/status/reccount").text
   Return RS_StatusVar
} 
;---------------------------------------------------------------------------------------------------------	
; Convert Megabytes to Gigabytes
DisplaySize(FileSize) {
   Static GB := 1024
   Return (FileSize >= GB) ? (Round(FileSize / GB)) : FileSize
}
; ==================================================================================================================================
; Function:       Notifies about changes within folders.
;                 This is a rewrite of HotKeyIt's WatchDirectory() released at
;                    http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/
; Tested with:    AHK 1.1.23.01 (A32/U32/U64)
; Tested on:      Win 10 Pro x64
; Usage:          WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]])
; Parameters:
;     Folder      -  The full qualified path of the folder to be watched.
;                    Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching.
;                    Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime.
;                    If not, it will be done internally on exit.
;     UserFunc    -  The name of a user-defined function to call on changes. The function must accept at least two parameters:
;                    1: The path of the affected folder. The final backslash is not included even if it is a drive's root
;                       directory (e.g. C:).
;                    2: An array of change notifications containing the following keys:
;                       Action:  One of the integer values specified as FILE_ACTION_... (see below).
;                                In case of renaming Action is set to FILE_ACTION_RENAMED (4).
;                       Name:    The full path of the changed file or folder.
;                       OldName: The previous path in case of renaming, otherwise not used.
;                       IsDir:   True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False.
;                    Pass the string "**DEL" to remove the directory from the list of watched folders.
;     SubTree     -  Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders).
;                    Default: False - sub-folders aren't watched.
;     Watch       -  The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_...
;                    values specified below.
;                    Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME
; Return values:
;     Returns True on success; otherwise False.
; Change history:
;     1.0.02.00/2016-11-30/just me        -  bug-fix for closing handles with the '**END' option.
;     1.0.01.00/2016-03-14/just me        -  bug-fix for multiple folders
;     1.0.00.00/2015-06-21/just me        -  initial release
; License:
;     The Unlicense -> http://unlicense.org/
; Remarks:
;     Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64)
;     folders simultaneously.
; MSDN:
;     ReadDirectoryChangesW          msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx
;     FILE_NOTIFY_CHANGE_FILE_NAME   = 1   (0x00000001) : Notify about renaming, creating, or deleting a file.
;     FILE_NOTIFY_CHANGE_DIR_NAME    = 2   (0x00000002) : Notify about creating or deleting a directory.
;     FILE_NOTIFY_CHANGE_ATTRIBUTES  = 4   (0x00000004) : Notify about attribute changes.
;     FILE_NOTIFY_CHANGE_SIZE        = 8   (0x00000008) : Notify about any file-size change.
;     FILE_NOTIFY_CHANGE_LAST_WRITE  = 16  (0x00000010) : Notify about any change to the last write-time of files.
;     FILE_NOTIFY_CHANGE_LAST_ACCESS = 32  (0x00000020) : Notify about any change to the last access time of files.
;     FILE_NOTIFY_CHANGE_CREATION    = 64  (0x00000040) : Notify about any change to the creation time of files.
;     FILE_NOTIFY_CHANGE_SECURITY    = 256 (0x00000100) : Notify about any security-descriptor change.
;     FILE_NOTIFY_INFORMATION        msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx
;     FILE_ACTION_ADDED              = 1   (0x00000001) : The file was added to the directory.
;     FILE_ACTION_REMOVED            = 2   (0x00000002) : The file was removed from the directory.
;     FILE_ACTION_MODIFIED           = 3   (0x00000003) : The file was modified.
;     FILE_ACTION_RENAMED            = 4   (0x00000004) : The file was renamed (not defined by Microsoft).
;     FILE_ACTION_RENAMED_OLD_NAME   = 4   (0x00000004) : The file was renamed and this is the old name.
;     FILE_ACTION_RENAMED_NEW_NAME   = 5   (0x00000005) : The file was renamed and this is the new name.
;     GetOverlappedResult            msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx
;     CreateFile                     msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
;     FILE_FLAG_BACKUP_SEMANTICS     = 0x02000000
;     FILE_FLAG_OVERLAPPED           = 0x40000000
; ==================================================================================================================================
WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) {
   Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}}
   Static TimerID := "**" . A_TickCount
   Static TimerFunc := Func("WatchFolder").Bind(TimerID, "")
   Static MAXIMUM_WAIT_OBJECTS := 64
   Static MAX_DIR_PATH := 260 - 12 + 1
   Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode
   Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB)
   Static SizeOfOVL := 32     ; size of the OVERLAPPED structure (64-bit)
   Static WatchedFolders := {}
   Static EventArray := []
   Static HandleArray := []
   Static WaitObjects := 0
   Static BytesRead := 0
   Static Paused := False
   ; ===============================================================================================================================
   If (Folder = "")
      Return False
   SetTimer, % TimerFunc, Off
   RebuildWaitObjects := False
   ; ===============================================================================================================================
   If (Folder = TimerID) { ; called by timer
      If (ObjCount := EventArray.Length()) && !Paused {
         ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
         While (ObjIndex >= 0) && (ObjIndex < ObjCount) {
            FolderName := WatchedFolders[ObjIndex + 1]
            D := WatchedFolders[FolderName]
            If DllCall("GetOverlappedResult", "Ptr", D.Handle, "Ptr", D.OVLAddr, "UIntP", BytesRead, "Int", True) {
               Changes := []
               FNIAddr := D.FNIAddr
               FNIMax := FNIAddr + BytesRead
               OffSet := 0
               PrevIndex := 0
               PrevAction := 0
               PrevName := ""
               Loop {
                  FNIAddr += Offset
                  OffSet := NumGet(FNIAddr + 0, "UInt")
                  Action := NumGet(FNIAddr + 4, "UInt")
                  Length := NumGet(FNIAddr + 8, "UInt") // 2
                  Name   := FolderName . "\" . StrGet(FNIAddr + 12, Length, "UTF-16")
                  IsDir  := InStr(FileExist(Name), "D") ? 1 : 0
                  If (Name = PrevName) {
                     If (Action = PrevAction)
                        Continue
                     If (Action = 1) && (PrevAction = 2) {
                        PrevAction := Action
                        Changes.RemoveAt(PrevIndex--)
                        Continue
                     }
                  }
                  If (Action = 4)
                     PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0})
                  Else If (Action = 5) && (PrevAction = 4) {
                     Changes[PrevIndex, "Name"] := Name
                     Changes[PrevIndex, "IsDir"] := IsDir
                  }
                  Else
                     PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir})
                  PrevAction := Action
                  PrevName := Name
               } Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax)
               If (Changes.Length() > 0)
                  D.Func.Call(FolderName, Changes)
               DllCall("ResetEvent", "Ptr", EventArray[D.Index])
               DllCall("ReadDirectoryChangesW", "Ptr", D.Handle, "Ptr", D.FNIAddr, "UInt", SizeOfFNI, "Int", D.SubTree
                                              , "UInt", D.Watch, "UInt", 0, "Ptr", D.OVLAddr, "Ptr", 0)
            }
            ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
            Sleep, 0
         }
      }
   }
   ; ===============================================================================================================================
   Else If (Folder = "**PAUSE") { ; called to pause/resume watching
      Paused := !!UserFunc
      RebuildObjects := Paused
   }
   ; ===============================================================================================================================
   Else If (Folder = "**END") { ; called to stop watching
      For K, D In WatchedFolders
         If K Is Not Integer
            DllCall("CloseHandle", "Ptr", D.Handle)
      For Each, Event In EventArray
         DllCall("CloseHandle", "Ptr", Event)
      WatchedFolders := {}
      EventArray := []
      Paused := False
      Return True
   }
   ; ===============================================================================================================================
   Else { ; called to add, update, or remove folders
      Folder := RTrim(Folder, "\")
      VarSetCapacity(LongPath, SizeOfLongPath, 0)
      If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", SizeOfLongPath)
         Return False
      VarSetCapacity(LongPath, -1)
      Folder := LongPath
      If (WatchedFolders[Folder]) { ; update or remove
         Handle := WatchedFolders[Folder, "Handle"]
         Index  := WatchedFolders[Folder, "Index"]
         DllCall("CloseHandle", "Ptr", Handle)
         DllCall("CloseHandle", "Ptr", EventArray[Index])
         EventArray.RemoveAt(Index)
         WatchedFolders.RemoveAt(Index)
         WatchedFolders.Delete(Folder)
         RebuildWaitObjects := True
      }
      If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Length() < MAXIMUM_WAIT_OBJECTS) {
         If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) {
            Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03
                                          , "UInt", 0x42000000, "Ptr", 0, "UPtr")
            If (Handle > 0) {
               Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0)
               Index := EventArray.Push(Event)
               WatchedFolders[Index] := Folder
               WatchedFolders[Folder] := {Func: UserFunc, Handle: Handle, Index: Index, SubTree: !!SubTree, Watch: Watch}
               WatchedFolders[Folder].SetCapacity("FNIBuff", SizeOfFNI)
               FNIAddr := WatchedFolders[Folder].GetAddress("FNIBuff")
               DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI)
               WatchedFolders[Folder, "FNIAddr"] := FNIAddr
               WatchedFolders[Folder].SetCapacity("OVLBuff", SizeOfOVL)
               OVLAddr := WatchedFolders[Folder].GetAddress("OVLBuff")
               DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL)
               NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr")
               WatchedFolders[Folder, "OVLAddr"] := OVLAddr
               DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree
                                              , "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0)
               RebuildWaitObjects := True
            }
         }
      }
      If (RebuildWaitObjects) {
         VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0)
         OffSet := &WaitObjects
         For Index, Event In EventArray
            Offset := NumPut(Event, Offset + 0, 0, "Ptr")
      }
   }
   ; ===============================================================================================================================
   If (EventArray.Length() > 0)
      SetTimer, % TimerFunc, -100
   Return (RebuildWaitObjects) ; returns True on success, otherwise False
}
; ==================================================================================================================================
; WatchFolder - Reportfunctions
; ==================================================================================================================================
ReportFunction(Directory, Changes) {
   Global LowSpaceLimit
   For Each, Change In Changes {
      Action := Change.Action
      Name := Change.Name
	  ; -------------------------------------------------------------------------------------------------------------------------
      ; Action 1 (added) = TS added (recording starts) in DMS recording folder, which triggers the Drive Space check
      If (Action = 1) {
	    SplitPath, Name,, Dir, Ext,, OutDrive
		for key, value in RS_RECfolderArray {
          DMSFolder := value
		  If (Dir = DMSFolder) && (Ext = "ts") {
		   FileGetTime, Created, %Name%, C
		   FileGetTime, Modified, %Name%, M
		   If (DMS_Status(statusquery)) && (Created = Modified) { ; DMS has ongoing recording and file "name" is still in recording
		    DriveSpaceFree, FreeSpace, % OutDrive . "\"
			If (DisplaySize(FreeSpace) < LowSpaceLimit)
			  TrayTip, Low Disk Space, % "You are running out of disk space on Local Disk (" OutDrive "). Only " DisplaySize(FreeSpace) 
			  . " GB left. Free space on this drive by deleting old or unnecessary files", 30, 2
		  }
         }
		}
      }
   }
}

 

 

Edited by majstang

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×