Jump to content

Warnung vor knappem Speicherplatz (AutoHotkey-Script)


Webturtle

Recommended Posts

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

 

P.S. 

 

Zitat

Hier die aktuelle Version:

 

DiskSpaceAlarm_1.2.zip

 

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

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.

 

Link to comment

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
Link to comment

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

 

 

 

Link to comment

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. ;) 

Link to comment

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
			}
         }
      }
   }
}

 

 

Link to comment

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

Link to comment
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: 

Link to comment

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
Link to comment
  • 1 year later...

Hallo,

 

ich habe mein ursprüngliches Autohotkey Script für Windows 10 überarbeitet. Die ursprüngliche Fassung war für XP mit Autohotkey Classic geschrieben. Sie hat keine akustische Warnung unter W 10 unterstützt. Mit der aktuellen Version des (jetzt normalen) Autohotkey funktioniert das aber.

 

DiskSpaceWarning_1.1.zip

 

Als Klang verwernde ich die Trompeten-Fanfare von Nero, die am Ende eines Brennvorgangs gespielt wird. Man kann man bei Google oder anderen Suchmaschinen nach MP3s mit Kavalerie Fanfaren suchen oder aus TV-Aufnahmen passende Klänge herausschneiden. Gut dafür eignet sich "Columbo - Des Teufels Corporal" oder auch ein Westen, bei dem sich die Kavallerie per Trompete ankündigt.

 

 

 

 

Link to comment
  • 11 months later...

Hallo,

 

ich habe das Script aufgrund der  Beiträge überarbeitet. Zunächst mußte ich erst mit einer neueren Version von AutoHotkey dafür sorgen, daß auch unter W10 eine akustische Warnung ausgegeben wird. Dann habe ich das Script vereinfacht, es wird nur noch ein Laufwerk überwacht, und das Script als Exe-Datei DiskSpaceAlarm.exe kompiliert.

 

Das Programm wird folgendermaßen aufgerufen:

 

DiskSpaceAlarm [Laufwerk] [Freier Mindestspeicher in MB] [Sounddatei]

 

Falls Leerzeichen im Namen oder Pfad der Sounddatei vorkommen, muß dieser Parameter in Anführungszeichen eingeschlossen werden.

 

z.B.

DiskSpaceAlarm C:\ 9000 "C:\Program Files\DVBViewer\Kavallerie Trompetensignal (Attacke) als MP3-Download.mp3"

 

Wenn man das Programm beenden will, muß man im Systray mit der rechten Maustaste auf das grüne Quadrat mit dem weißen H (DiskSpaceAlarm) klicken und Exit wählen.

 

Als Alarmsignal verwende ich die Fanfare von Nero,  dem Brennprogramm. Die kann ich natürlich nicht weitergeben. Ich habe daher zwei freie Fanfarensignale beigefügt. Diese dürfen nicht kommerziell verwendet werden und bei einer Weitergabe ist die Angabe der Quelle erforderlich. Ich habe dazu die exportierte Webseite dazugepackt.

 

 

 

 

Viele Grüße und viel Spaß damit

 

Webturtle

 

P.S. Das ursprüngliche Script hat mir schon oft die Aufnahmen gerettet

Edited by Webturtle
Link to comment
  • 7 months later...

Hallo,

 

ich habe DiskSpacaAlarm etwas überarbeitet. Im Alarmfall bietet dei Message-Box die Moglichkeit die erneuten Warnungen vorübergehend (1 - 60 Minuten) auszusetzen. Wenn man gerade schon dabei ist, Dateien zu verschieben, kann das ständige Fanfarensignal schon mal stören. Wenn man das Script abstellt, kann man auch vergessen es wieder aufzurufen und dann von einer vollen HD überrascht werden.

 

DiskSpaceAlarm_1.1.zip

 

 

Viele Grüße

 

Webturtle

Edited by Webturtle
Link to comment
  • 4 weeks later...

Hallo,

 

ich habe DiskSpaceAlarm noch etwas verbessert. Das Programm merkt sich jetzt die Einstellungen von Volume und Mute (Lautstärke und Ton aus/ein), schaltet dann den Ton ein und die Lautstärke auf 100 (voll). Dann spielt es den Sound ab. Der Aufruf von SoundPlay innerhalb der Datei hat nicht funktioniert, was ich auch probiert habe (bei AutoHotkey gibt es leider keinen Debugger wie bei Turbo C, mit dem man Zeile für Zeile ablaufen lassen kann). Daher wird dazu Alarm_Trumpet.exe aufgerufen und der Name der Sounddatei übergeben. Danach werden die Wert für Volume und Mute wieder eingestellt.

 

Hier die aktuelle Version:

 

DiskSpaceAlarm_1.2.zip

 

 

Viele Grüße und ein gutes neues Jahr

 

Webturtle

Edited by Webturtle
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...