Jump to content


Photo

[Function] remoteResource() for images etc.


  • Please log in to reply
4 replies to this topic

Poll: How do you distribute media files for your scripts? (7 member(s) have cast votes)

How do you distribute media files for your scripts?

  1. I don't use media files (0 votes [0.00%])

    Percentage of vote: 0.00%

  2. Zip it! (1 votes [12.50%])

    Percentage of vote: 12.50%

  3. FileInstall, File, File (1 votes [12.50%])

    Percentage of vote: 12.50%

  4. UrlDownloadToFile (2 votes [25.00%])

    Percentage of vote: 25.00%

  5. I embed in the filestream/script/etc (PRO!) (2 votes [25.00%])

    Percentage of vote: 25.00%

  6. rRes() from now on... :) (2 votes [25.00%])

    Percentage of vote: 25.00%

Vote Guests cannot vote

#1 sumon

sumon
  • Moderators
  • 1307 posts

Posted 16 December 2011 - 11:56 PM

Ever got tired of the missing files that cause headaches when distributing your scripts? I did. So I wrote the remote resource manager.

It takes your desired file, downloads it if it has to, and loads the file(path) into a variable for later use. Default directory is A_AppsData\ScriptName\ResourceType\Resource. Very simple, very handy. Improvements or suggestions are welcome. I really like storing the data in AppsData: That way all data is "hidden" but accessible regardless of how the user moves the main exe or ahk. However, keep in mind that you shouldn't clog it up with heavy files.

Function:
/*
	< remoteResource() > (function)
	Version: 0.81
	Author: Simon Strålberg [sumon @ Autohotkey forums, simon . stralberg @ gmail . com]
	Autohotkey version: AHK_L (Unicode, x32)
		
	CHANGELOG:
	v.
		- 0.81: Added URLStatus() check (returns error if URL is invalid/file does not exist)
		- 0.8: Added HASHFile check, added cleanup
	
	REQUIREMENTS:
		- HashFile() by Deo, http://www.autohotkey.com/forum/viewtopic.php?t=71133
		- URLStatus() is included

	LICENSE: If no license documentation exists, [http]
	Script created using Autohotkey [http]
	
*/

remoteResource(Resource, URL="", Directory="", TT="Display", MD5Sum="") ; by sumon
{
	; First, a few aliases/standard values:
	static BaseURL ; Remembered between calls. HOWEVER, note that the file type is not automatically appended
	if ((Resource = "Load") or (Resource = "Init") or (Resource = "URL")) ; Used for cases where you want to set a base URL, f.ex. remoteResource("Load", "http://www.autohotkey.net/~sumon/img")
	{	
		BaseURL := URL
		return 1
	}
	
	If (!Directory)
	{
		SplitPath, Resource, , , Ext ; Extension
		If (RegExMatch(Ext, "doc|docx|html|odt|rtf|txt|pdf|html|htm"))
			Type := "Docs"
		else if (RegExMatch(Ext, "ani|bmp|cur|gif|ico|jpg|jpeg|jp2|png|raw|tga|tif|tiff"))
			Type := "Media\Images"
		else if (RegExMatch(Ext, "aiff|raw|wav|flac|wma|mp3|aac|swa|mid"))
			Type := "Media\Sounds"
		else if (RegExMatch(Ext, "ini"))
			Type := "Data"
		else
			Type := "Other"
		
		SplitPath, A_ScriptName,,,, ScriptName ; Removes extension
		Directory := A_AppData . "\" ScriptName . "\" . Type ; Store the data systematically
		If ((URL = "GetDir") or (URL = "Dir"))
			return A_AppData . "\" ScriptName ; A simple way to only retrieve the default directory
	}
	
	if (!InStr(URL, "." Ext))
		URL := BaseURL . Resource
	
	;
	If (MD5Sum)
	{
		If (HashFile(Directory . "\" . Resource) = MD5Sum) ; Check MD5 integrity of file
			return Directory . "\" . Resource
	}
	else if FileExist(Directory . "\" . Resource)
		return Directory . "\" . Resource ; Exists, return path
	else
	{
		If (URL)
		{
			If (!(URLStatus(URL) >= 200 AND URLStatus(URL) < 300)) ; If cannot connect
			{
				If (TT = "Display")
					Traytip,, Error: %URLStatus%... ,, 1
				Return URLStatus ; Error #, mostly 404
			}
			If (TT = "Display")
				Traytip,, Downloading %Resource%... ,, 1 ; Optional
			else if (TT)
				Traytip,, %TT%,, 1
				
			FileCreateDir, %Directory%
			UrlDownloadToFile, %URL%, %Directory%\%Resource%
			
			If (TT)
				TrayTip ; Clear message
			
			If (MD5Sum)
			{
				If (HashFile(Directory . "\" . Resource) != MD5Sum)
				{
					MsgBox, 48, Invalid resource, Warning! The file version (MD5) of %Resource% does not seem valid.
					return ; Return nothing
				}
			}
			
			return Directory . "\" . Resource ; File was downloaded
		}
		return 0 ; Couldn't download either
	}	
} ; Returns: 0 if could not download, 2 if HashSum is wrong on both local & online version, 404 (or error status) if file does not exist

remoteResource_Clear(Directory="", Resource="")
{
	If (Directory AND (Directory != A_Temp) AND (!Resource)) ; If directory is not A_Temp or AppData\ScriptName, confirm
	{
		MsgBox, 308, WARNING!, %Directory% and all its' files is about to deleted! Are you sure?
		IfMsgBox, No
			return 0
	}
	
	SplitPath, A_ScriptName,,,, ScriptName ; Removes extension
	Directory := (Directory)?(Directory):(A_AppData "\" ScriptName)
	Resource := (Resource)?(Resource):("All")
	If (Resource = "All")
	{
		MsgBox Removing %Directory%
		FileRemoveDir, %Directory%, 1
	}
	else
	{
		fDir := remoteResource(Resource, "Dir") ; Retrieves the default dir (AppData\ScriptName\MediaType\res.ext)
		return fDir
		If FileExist(fDir)
			FileDelete, %fDir%
	}
		
}

URLStatus(URL="") ; By PEG & gamax92, http://www.autohotkey.com/forum/viewtopic.php?p=503855#503855
{
	http:=ComObjCreate("WinHttp.WinHttpRequest.5.1")
	http.open("GET", url, false)s
	http.send()
	return http.Status
}


Sample:
; Without rRes (example)

path := "data\img\"
If (!FileExist(path . "appifyerFrame.png"))
{
	FileCreateDir, %path%
	Traytip,, Downloading appifyerFrame.png... ,, 1
	UrlDownloadToFile, http://autohotkey.net/~sumon/img/appifyerFrame.png, %path%\appifyerFrame.png
}
aFrame := path . "\appifyerFrame.png"

; With rRes

aFrame := remoteResource("appifyerFrame.png", "http://autohotkey.net/~sumon/img/appifyerFrame.png")


Demo (copy-n-paste):
;~ #Include <remoteResource> ; Uncomment if you need to

; # Example 1. Image

TrayTip, Example 1, Download an image`, then add it to a GUI,, 1
KeyWait, Enter, Up
pic := remoteResource("appifyerFrame.png", "http://www.autohotkey.net/~sumon/img/appifyerFrame.png")
Gui, Add, Pic,, %pic%
Gui, Add, Text,, Press Enter to continue (to example 1b)
Gui, Show,, Example 1

; # Example 1b. Use the image again

KeyWait, Enter, D T10
Gui, Destroy
Traytip, Example 1b, Note that the image can now be directly accessed without downloading it,, 1
Gui, Add, Pic, x10 w50 h30, %pic%
Gui, Add, Pic, xp+60 yp w50 h30, %pic%
Gui, Add, Pic, x10 yp+40 w100 h60, %pic%
Gui, Add, Text,, Press Enter to continue (to example 2)
Gui, Show,, Example 1b

KeyWait, Enter, D T10

; # Example 2. Download a sound file.

Gui, Destroy
Traytip, Example 2, Any file can be downloaded this way (f.ex. a sound file),, 1
music := remoteResource("taurus_sample.mp3", "http://www.autohotkey.net/~sumon/media/sounds/taurus_sample.mp3") ; This is from my IRL buddy DJ Taurus/Astreim, http://www.youtube.com/user/Astreim
Run %music%

Sleep 5000
TrayTip, Storage, Press Enter to see where the files are stored
KeyWait, Enter, D T15
DefaultDir := remoteResource("getDir")
Run %DefaultDir%
TrayTip, Finished, Demonstration is finished!
return

Esc:: ExitApp

/*
	Function included in demonstration, for your convenience 
*/

remoteResource(Resource, URL="", Directory="", TT="Display") ; by sumon
{
	; First, a few aliases/standard values:
		
	If (!Directory)
	{
		SplitPath, Resource, , , Ext ; Extension
		If (RegExMatch(Ext, "doc|docx|html|odt|rtf|txt|pdf|html|htm"))
			Type := "Docs"
		else if (RegExMatch(Ext, "ani|bmp|cur|gif|ico|jpg|jpeg|jp2|png|raw|tga|tif|tiff"))
			Type := "Media\Images"
		else if (RegExMatch(Ext, "aiff|raw|wav|flac|wma|mp3|aac|swa|mid"))
			Type := "Media\Sounds"
		else if (RegExMatch(Ext, "ini"))
			Type := "Data"
		else
			Type := "Other"
		
		SplitPath, A_ScriptName,,,, ScriptName ; Removes extension
		Directory := A_AppData . "\" ScriptName . "\" . Type ; Store the data systematically
		If (Resource = "GetDir")
			return A_AppData . "\" ScriptName ; A simple way to only retrieve the default directory
	}
	
	;
	If FileExist(Directory . "\" . Resource)
		return Directory . "\" . Resource ; Exists, return path
	If (!FileExist(Directory . "\" . Resource))
	{
		If (URL)
		{
			If (TT = "Display")
				Traytip,, Downloading %Resource%... ,, 1 ; Optional
			else if (TT)
				Traytip,, %TT%,, 1
				
			FileCreateDir, %Directory%
			UrlDownloadToFile, %URL%, %Directory%\%Resource%
			
			If (TT)
				TrayTip
			return Directory . "\" . Resource ; File was downloaded
		}
		return 0 ; Couldn't download either
	}	
}


#2 sumon

sumon
  • Moderators
  • 1307 posts

Posted 21 December 2011 - 03:24 PM

First: I'm planning to update this function to support file versions by checking hashsums.

Second:

I'm using this function to start creating an AHK-distributor, basically a script that we can all make use of to download, run, view etc. eachothers' scripts without having to copy/paste. I'll make a new topic when I have more code for it, I just have tried the method and it'll work. Before I make the syntax strange, I'd like to know what you think is a natural commandline input order, and if you have some advice for the name of this delivery guy, and if you have some other requested functions.

You can call it by the commandline or a custom URI, and start the request with either "AHK" or "AHK:" depending on if we decide for another name for the distributor, and if you use the commandline or an URI.

Suggested structure:

AHK Command author:Script (Options)
AHK Command Script (Options) (for "globally acknowledged" scripts, such as those from a future StdLib based upon this)

Example: AHK Run sumon:test

Options:

Directory (specifies a specific directory instead of the default %AppData%, or lib directory). Any other options suggested?

Commands:

Run to (download &) run the script.
Test to run the script, hook its' process and delete the file when the process closes. (Maybe add a hotkey for closing too).
Download to only download the script.
Upload to upload the script to your Autohotkey.net account. (will prompt for script file if you do not specify a directory)
Share (?) to fill your clipboard with a userfriendly link for sharing on f.ex. IRC (which instructs to download the client if it is not installed yet, otherwise just performs the action specified)
Lib (or StdLib, Library) to put the script in your library (update if necessary).

It might be possible to add support for performing commands not only on existing/uploaded scripts, but also parse and download a script pasted using paste.autohotkey.net. Other suggestions?

#3 sumon

sumon
  • Moderators
  • 1307 posts

Posted 08 January 2012 - 07:34 PM

Updated to 0.81. Now has MD5Sum check and URLStatus(), thanks to PEG for looking up some stuff for these functions and requesting the implementations.

#4 Cattleya

Cattleya
  • Members
  • 43 posts

Posted Yesterday, 02:13 AM

Thank you, very useful function, bookmarked! :)



#5 Wicked

Wicked
  • Members
  • 480 posts

Posted Yesterday, 07:37 PM

This is what I wrote and have been using for mine:
time_1:=a_tickcount
_download_to_var("http://google.com")
time_2:=a_tickcount
msgbox,% "First _download_to_var: " time_2-time_1 "ms."
time_1:=a_tickcount
_download_to_var("http://google.com")
time_2:=a_tickcount
msgbox,% "Second _download_to_var: " time_2-time_1 "ms."


_download_to_file(u,s){
	static r:=false,request:=comobjcreate("WinHttp.WinHttpRequest.5.1")
	if(!r||request.option(1)!=u)
		request.open("GET",u)
	request.send()
	if(request.responsetext="failed"||request.status!=200||comobjtype(request.responsestream)!=0xd)
		return false
	p:=comobjquery(request.responsestream,"{0000000c-0000-0000-C000-000000000046}")
	f:=fileopen(s,"w")
	loop{
		varsetcapacity(b,8192)
		r:=dllcall(numget(numget(p+0)+3*a_ptrsize),ptr,p,ptr,&b,uint,8192, "ptr*",c)
		f.rawwrite(&b,c)
	}until (c=0)
	objrelease(p)
	f.close()
	return request.responsetext
}

_download_to_var(u){
	static r:=false,request:=comobjcreate("WinHttp.WinHttpRequest.5.1")
	if(!r||request.option(1)!=u)
		request.open("GET",u)
	request.send()
	if(request.responsetext="failed"||request.status!=200)
		return false
	r:=true
	return request.responsetext
}
 
As you can see, if the URL hasn't changed from the last request, it doesn't reopen the connection. So, as you can see from the example, my first request takes ~250ms because it has to open. The second one only takes ~150ms. So it's good for things that require multiple hits, such as RSS feeds and such.
 
Cheers. happy.png.