YouTube Downloader

Developed or developing a new product in PureBasic? Tell the world about it.
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Post by eddy »

It's a big update :P (~ 500 lines )
- Youtube : OK
- DailyMotion : OK
- GoogleVideo : ERROR

http://files-upload.com/files/444567/PureTube.zip

Code: Select all

;/ PureBasic 4.10b2 
; Version 2.0 

; Added : ProgressBar + Cancel Download + Size Downloaded / File Size / rate 
; Added : Cancel Button / Resize / Translation in English 

#Title="PureTube - Download "
#TitleNext=" at " 
Global VideoTitle.s, VideoId.s, Path.s 
Global DownloadURL.s, DownloadFilename.s, StopDownload, IsDownloading 

Global SourceName.s, SourceURL.s
Global IdBegin.s,IdEnd.s    
Global TitleBegin.s,TitleEnd.s  
Global UrlInit.s, UrlBegin.s, UrlEnd.s, UrlDecodeMode.s  

Enumeration 
  #Web 
  #ToolBar  
  #Home 
  #Back 
  #Forward 
  #Download 
  #Cancel 
  #StatusBar 
  #Progress
  #Folder 
  #FolderSetup
  #FolderOpen  
  #Sources
  #SourceAdd
  
  #FolderMenu
  #SourceMenu
EndEnumeration 
 
DataSection 
  Back:IncludeBinary "back.ico" 
  Forward:IncludeBinary "forward.ico" 
  Home:IncludeBinary "home.ico" 
  Save:IncludeBinary "Download.ico" 
  Stop:IncludeBinary "Cancel.ico" 
  Chemin:IncludeBinary "Path.ico"
  Options:IncludeBinary "Options.ico"
EndDataSection 

;{ Generic Functions
; For Decoding URL  
Procedure HexVal(a$) 
  a$=Trim(UCase(a$)) 
  If Asc(a$)='$' 
    a$=Trim(Mid(a$,2,Len(a$)-1)) 
  EndIf 
  
  Protected Result=0 
  Protected *adr.Byte=@a$ 
  For i=1 To Len(a$) 
    Result<<4 
    Select *adr\B 
      Case '0' 
      Case '1':Result+1 
      Case '2':Result+2 
      Case '3':Result+3 
      Case '4':Result+4 
      Case '5':Result+5 
      Case '6':Result+6 
      Case '7':Result+7 
      Case '8':Result+8 
      Case '9':Result+9 
      Case 'A':Result+10 
      Case 'B':Result+11 
      Case 'C':Result+12 
      Case 'D':Result+13 
      Case 'E':Result+14 
      Case 'F':Result+15 
      Default:i=Len(a$) 
    EndSelect 
    *adr+1 
  Next 
  ProcedureReturn Result 
EndProcedure

; For Decoding URL
Procedure.s DecodeURL(In.s)
  Protected i=FindString(In,"%",1)  
  While (i)
    In = ReplaceString(In,Mid(In,i,3),Chr(HexVal(Mid(In,i+1,2))))
    i=FindString(In,"%",1) 
  Wend 
  Protected Out.s =In
  
  ProcedureReturn Out 
EndProcedure

; For Form 
Procedure ToolBarImageButtonEx(ToolBarId,ButtonId,ImageId,ToolTip.s) 
  ToolBarImageButton(ButtonId,ImageId) 
  ToolBarToolTip(ToolBarId,ButtonId,ToolTip) 
EndProcedure 

;}

;{ Video MultiSources 
Procedure InitSourceParser(Xml.s)
  ; Read XML file
  Protected XmlID = LoadXML(#PB_Any,Xml)
  Protected XmlNode,XmlSubnode   
  
  ; Get parser parameters from XML file
  If (XmlID And XMLStatus(XmlID)=0)
    XmlNode = XMLNodeFromID(XmlID,SourceName)
    
    XmlSubnode = XMLNodeFromPath(XmlNode,"homepage")
    If (XmlSubnode)
      SourceURL = GetXMLAttribute(XmlSubnode,"url")
    EndIf
    
    XmlSubnode = XMLNodeFromPath(XmlNode,"download")
    If (XmlSubnode)
      UrlInit = GetXMLAttribute(XmlSubnode,"url")
      UrlBegin = GetXMLAttribute(XmlSubnode,"begin")
      UrlEnd = GetXMLAttribute(XmlSubnode,"end")
      UrlDecodeMode = GetXMLAttribute(XmlSubnode,"decode")
    EndIf 
    
    XmlSubnode = XMLNodeFromPath(XmlNode,"title")
    If (XmlSubnode)
      TitleBegin = GetXMLAttribute(XmlSubnode,"begin")
      TitleEnd = GetXMLAttribute(XmlSubnode,"end")
    EndIf 
    
    XmlSubnode = XMLNodeFromPath(XmlNode,"vid")
    If (XmlSubnode)
      IdBegin = GetXMLAttribute(XmlSubnode,"begin")
      IdEnd = GetXMLAttribute(XmlSubnode,"end")
    EndIf 
    
    ;FreeXML(XmlID)
  Else 
    MessageRequester("Warning!","Error reading XML!" ,#MB_ICONWARNING)
    ProcedureReturn 
  EndIf   
EndProcedure

Procedure InitSourceList(Xml.s)
  ; Get parser parameters from XML file
  Protected XmlID = LoadXML(#PB_Any,Xml)
  Protected XmlNode,XmlSubnode   
  Protected Result.s
  
  ; Create source list 
  If (XmlID And XMLStatus(XmlID)=0)
    XmlNode = RootXMLNode(XmlID)
    XmlNode = XMLNodeFromPath(XmlNode,"/sources/source[0]")
     
    CreatePopupMenu(#SourceMenu)
    MenuItemID=#SourceMenu
    Repeat  
      MenuItemID+1
      MenuItem(MenuItemID,GetXMLAttribute(XmlNode,"id"))
      XmlNode=NextXMLNode(XmlNode)
    Until (XmlNode=0)
    MenuBar()
    MenuItem(#SourceAdd,"New Source...")
    DisableMenuItem(#SourceMenu,#SourceAdd,1)

    ;FreeXML(XmlID)
  Else 
    MessageRequester("Warning!","Error reading XML!" ,#MB_ICONWARNING)
    ProcedureReturn
  EndIf   
EndProcedure
;}
  
;{ Download Video 
Procedure SaveVideoFile(VideoId.s) 
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
  ; ***************************
  ;  Windows OS code
  ; ***************************
  
  ;/ ( Downloader With progress And Status By Joakim L. Christiansen )  
  DisableToolBarButton(#ToolBar,#Cancel,#False) 
  IsDownloading+#True 
   
  Protected hInet, hURL, Bytes 
  Protected BufferLength = 2048, Buffer.s = Space(BufferLength) 
  
  Protected url.s      = DownloadURL.s 
  Protected Filename.s = DownloadFilename.s 
  Protected File 
  Protected CurrentSize, PreviousSize, FileSize, time, BytesPerSecond 
  Protected Domain.s, String.s, i, BufferLengthWas = BufferLength 
  Protected hInetCon, hHttpOpenRequest, iretval 
  
  hInet = InternetOpen_("Downloader",0,0,0,0) 
  hURL  = InternetOpenUrl_(hInet,url.s,0,0,$80000000,0) 
  
  ;Get filesize 
  Domain.s  = StringField(url.s,3,"/") 
  hInetCon = InternetConnect_(hInet,Domain.s,80,#Null,#Null,3,0,0) 
  If hInetCon 
    hHttpOpenRequest = HttpOpenRequest_(hInetCon,"HEAD",ReplaceString(url.s,"http://"+Domain.s+"/",""),#Null,#Null,0,$80000000,0) 
    If hHttpOpenRequest 
      iretval = HttpSendRequest_(hHttpOpenRequest,#Null,0,0,0) 
      If iretval 
        HttpQueryInfo_(hHttpOpenRequest,19,@Buffer.s,@BufferLength,0) ;changes the buffer length 
        String.s = PeekS(@Buffer.s,BufferLength): BufferLength = BufferLengthWas 
        If Trim(String.s) = "200" 
          HttpQueryInfo_(hHttpOpenRequest,22,@Buffer.s,@BufferLength,0) 
          String.s = PeekS(@Buffer.s,BufferLength): BufferLength = BufferLengthWas 
          If FindString(String.s,"Content-Length:",1) 
            i = FindString(String.s,"Content-Length:",1) + Len("Content-Length:") 
            String.s = Mid(String.s,i,Len(String.s)-i) 
            FileSize = Val(Trim(String.s)) 
          EndIf 
        EndIf 
      EndIf 
    EndIf 
  EndIf 
  
  ;Download file and update status 
  If hURL 
    File = CreateFile(#PB_Any,Filename.s) 
    If File 
      time = ElapsedMilliseconds() 
      SetGadgetAttribute(#Progress,#PB_ProgressBar_Maximum,FileSize) 
      While InternetReadFile_(hURL,@Buffer.s,BufferLength,@Bytes) And Bytes > 0 
        If StopDownload 
          Break 
        EndIf 
        WriteData(File,@Buffer.s,Bytes) 
        CurrentSize + Bytes 
        SetGadgetState(#Progress,CurrentSize) 
        StatusBarText(#StatusBar,0,"  "+Str(CurrentSize/1024)+"/"+Str(FileSize/1024)+"kb - "+Str(BytesPerSecond/1024)+"kb/s") 
        If time < ElapsedMilliseconds() - 1000 
          time = ElapsedMilliseconds() 
          BytesPerSecond = CurrentSize - PreviousSize 
          PreviousSize = CurrentSize 
        EndIf 
      Wend 
      CloseFile(File) 
    Else 
      MessageRequester("Warning!","Error creating file!",#MB_ICONWARNING) 
    EndIf 
    StopDownload = #False 
    IsDownloading = #False 
    SetGadgetState(#Progress,0) 
    If CurrentSize < FileSize 
      DeleteFile(Filename.s) 
    EndIf 
  Else 
    MessageRequester("Warning!","Download failed!",#MB_ICONWARNING) 
  EndIf 
  
  InternetCloseHandle_(hURL) 
  InternetCloseHandle_(hInetCon) 
  InternetCloseHandle_(hInet) 
  DisableToolBarButton(#ToolBar,#Cancel,#True) 
  StatusBarText(#StatusBar,0,"") 
  
  CompilerElse
  ; ***************************
  ;  Other OS code
  ; ***************************
  CompilerEndIf
EndProcedure 

Procedure.s GetDownloadURL() 
  ; Extract the HTML code from the WebGadget 
  Protected Source.s
  If (FindString(Source,"iframe",1))
    Source.s=GetGadgetItemText(#Web,#PB_Web_SelectedText)
  Else 
    Source.s=GetGadgetItemText(#Web,#PB_Web_HtmlCode)  
  EndIf
   
  ; Extract the VideoURL from HTML code 
  Protected i,j
  Protected Result.s
  i=FindString(Source,UrlBegin,1)+Len(UrlBegin) 
  j=FindString(Source,UrlEnd,i)  
  Result = UrlInit+Mid(Source,i,j-i)  
  
  ; Decode the VideoURL if necessary
  Select LCase(UrlDecodeMode)
    Case "url"
      Result = DecodeURL(Result)
    Default  
  EndSelect
  
  ProcedureReturn Result
EndProcedure

Procedure.s GetVideoTitle() 
  ; Extract the HTML code from the WebGadget 
  Protected Source.s=GetGadgetItemText(#Web,#PB_Web_HtmlCode)  
  
  ; Extract the VideoName from HTML code (if not use default name)
  Protected i,j
  Protected Result.s ="default_video_"+SourceName+"_"+FormatDate("%yyyy%mm%dd_%hh%ii%ss",Date())
  i=FindString(Source,"<title>",1)
  j=FindString(Source,"</title>",i)
  If (i And j)
    i+Len("<title>") 
    Source = Mid(Source,i,j-i)
    i=FindString(Source,TitleBegin,1)
    j=FindString(Source,TitleEnd,i)      
    If i=0 : i=1 : EndIf 
    If j=0 : j=Len(Source)+1 : EndIf 
    Result = Mid(Source,i,j-i) 
  EndIf 
  
  ProcedureReturn Result
EndProcedure

Procedure.s GetVideoId() 
  ; Extract the HTML code from the WebGadget 
  Protected Source.s=GetGadgetItemText(#Web,#PB_Web_HtmlCode)  
  
  ; Extract the VideoName from HTML code
  Protected i,j
  Protected Result.s =""
  If FindString(Source,"<body",1)
    i=FindString(Source,IdBegin,1)
    If (i)
      i+Len(IdBegin) 
      j=FindString(Source,IdEnd,i)  
      If (j)
        Result = Mid(Source,i,j-i)  
      EndIf
    EndIf 
  EndIf
  
  ProcedureReturn Result
EndProcedure

Procedure.s GetVideoFilename(File.s) ; Get normalized filename 
  Protected n
  Protected c.s, Out.s
  
  For n=1 To Len(File) 
    c.s=Mid(File,n,1) 
    If FindString(c,"\",1) 
      Out.s+" " 
    ElseIf FindString(c,"/",1) 
      Out.s+" " 
    ElseIf FindString(c,":",1) 
      Out.s+" " 
    ElseIf FindString(c,"?",1) 
      Out.s+" " 
    ElseIf FindString(c,"*",1) 
      Out.s+" " 
    ElseIf FindString(c,"<",1) 
      Out.s+" " 
    ElseIf FindString(c,">",1) 
      Out.s+" " 
    ElseIf FindString(c,"|",1) 
      Out.s+" " 
    ElseIf FindString(c,Chr(34),1) 
      Out.s+" " 
    Else 
      Out.s+c 
    EndIf 
  Next 
  
  ProcedureReturn Out 
EndProcedure 
;}

;{ Load Preferences
CompilerIf #PB_Compiler_Debugger
  Ini.s="D:\.............\PureTube.ini" ; for code debugging
  Xml.s="D:\.............\PureTube.xml" ; for code debugging
CompilerElse
  Ini.s=GetPathPart(ProgramFilename())+StringField(GetFilePart(ProgramFilename()),1,".")+".ini" 
  Xml.s=GetPathPart(ProgramFilename())+StringField(GetFilePart(ProgramFilename()),1,".")+".xml"  
CompilerEndIf

OpenPreferences(Ini) 
Path.s=ReadPreferenceString("Path","c:\") 
LastUrl.s=ReadPreferenceString("LastUrl","http://www.youtube.com")
IsMaximized.b=ReadPreferenceLong("IsMaximized",0)
SourceName.s =ReadPreferenceString("LastSource","YouTube")
InitSourceList(Xml)
InitSourceParser(Xml)
;}

;{ Visual Design 
OpenWindow(0,0,0,1024,768,"",#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget) 
SetWindowTitle(0,#Title+SourceName+#TitleNext+Path) 
CreateGadgetList(WindowID(0)) 
WebGadget(#Web,10,30,1024-20,768-60,LastUrl) 
CreateToolBar(#ToolBar,WindowID(0)) 
ToolBarImageButtonEx(#ToolBar,#Home,ImageID(CatchImage(#PB_Any,?Home)),RemoveString(SourceURL,"http://")) 
ToolBarImageButtonEx(#ToolBar,#Back,ImageID(CatchImage(#PB_Any,?Back)),"Back") 
ToolBarImageButtonEx(#ToolBar,#Forward,ImageID(CatchImage(#PB_Any,?Forward)),"Forward") 
ToolBarSeparator() 
ToolBarImageButtonEx(#ToolBar,#Folder,ImageID(CatchImage(#PB_Any,?Chemin)),"Download Folder") 
ToolBarImageButtonEx(#ToolBar,#Sources,ImageID(CatchImage(#PB_Any,?Options)),"Select Video Source") 
ToolBarSeparator() 
ToolBarImageButtonEx(#ToolBar,#Download,ImageID(CatchImage(#PB_Any,?Save)),"Download") 
ToolBarImageButtonEx(#ToolBar,#Cancel,ImageID(CatchImage(#PB_Any,?Stop)),"Cancel Download") 
CreateStatusBar(#StatusBar,WindowID(0)) 
AddStatusBarField(150) 
AddStatusBarField(3000) 
ProgressBarGadget(#Progress,170,5,1024-181,20,0,100, #PB_ProgressBar_Smooth) 
DisableToolBarButton(#ToolBar,#Cancel,#True) 
DisableToolBarButton(#ToolBar,#Download,#True) 
If IsMaximized : SetWindowState(0,#PB_Window_Maximize) : EndIf

CreatePopupMenu(#FolderMenu)
MenuItem(#FolderOpen,"Explore Download Folder...")
MenuBar()
MenuItem(#FolderSetup,"Select Folder...")

Procedure WebNavigation(Gadget, url.s)
  ProcedureReturn #True 
EndProcedure
SetGadgetAttribute(#Web,#PB_Web_NavigationCallback,@WebNavigation())
;} 

;{ Event Management 
Repeat 
  evt=WaitWindowEvent() 
  
  If IsThread(Tid)=0 And Len(VideoId)>0 ; If not Downloading and Video exists
    DisableToolBarButton(#ToolBar,#Download,#False) ; Enable Download  
  Else 
    DisableToolBarButton(#ToolBar,#Download,#True)  ; Disable Download 
  EndIf 
  
  Select evt 
    Case #PB_Event_SizeWindow 
      ResizeGadget(#Web,10,30,WindowWidth(0)-20,WindowHeight(0)-60) 
      ResizeGadget(#Progress,170+20,5,WindowWidth(0)-181-20,20) 
      
    Case #PB_Event_Gadget 
      Select EventGadget()
        Case #Web  
          Select EventType()
            Case #PB_EventType_TitleChange
              VideoId = ""
              StatusBarText(#StatusBar,1," "+GetGadgetText(#Web)) ; Show PageURL   
            Default 
              VideoId = GetVideoId() 
              If IsThread(Tid)=0 
                StatusBarText(#StatusBar,0," "+VideoId) ; Show VideoId
              EndIf
        EndSelect 
    EndSelect  
       
    Case #PB_Event_Menu 
      Select EventMenu() 
        Case #Home 
          SetGadgetText(#Web,SourceURL) 
        Case #Back 
          SetGadgetState(#Web,#PB_Web_Back) 
        Case #Forward 
          SetGadgetState(#Web,#PB_Web_Forward) 
          
        Case #Download 
          If Not IsDownloading And Len(VideoId)>0
            VideoTitle = GetVideoTitle()  
            DownloadFilename = Path+GetVideoFilename(VideoTitle)+".flv"
            DownloadURL = GetDownloadURL() 
            Tid = CreateThread(@SaveVideoFile(),VideoId) 
          EndIf 
          
        Case #Cancel 
          StopDownload = #True 
          IsDownloading = #False 
          SetGadgetState(#Progress,0) 
          DisableToolBarButton(#ToolBar,#Cancel,#True) 
          
        Case #Folder 
          DisplayPopupMenu(#FolderMenu,WindowID(0),WindowX(0)+82,WindowY(0)+54) ; show folder menu
        Case #FolderOpen
          SetGadgetText(#Web,Path)
        Case #FolderSetup 
          temp.s=PathRequester("Backup Video Path",Path) 
          If temp<>"" 
            Path=temp 
            SetWindowTitle(0,#Title+SourceName+#TitleNext+Path) 
          EndIf 
          
        Case #Sources
          DisplayPopupMenu(#SourceMenu,WindowID(0),WindowX(0)+106,WindowY(0)+54) ; show source menu
        Default 
          SourceName = GetMenuItemText(#SourceMenu, EventMenu())  ; new source name
          InitSourceParser(Xml)                                   ; new source parser
          ToolBarToolTip(#ToolBar,#Home,RemoveString(SourceURL,"http://"))  ; refresh toolbar 
          SetGadgetText(#Web,SourceURL)                                     ; refresh web content
      EndSelect 
  EndSelect 
  
Until evt=#PB_Event_CloseWindow 
;}

;{ Save Preferences
WritePreferenceString("Path",Path)
WritePreferenceString("LastUrl",GetGadgetText(#Web)) 
WritePreferenceString("LastSource",SourceName)  
WritePreferenceLong("IsMaximized", (#True And GetWindowState(0) = #PB_Window_Maximize))
;}

 
; jaPBe Version=3.7.7.653
; FoldLines=002E006400300000005F0000006600AC0067000000AE0170013C000001510000
; FoldLines=0172018201A601F101F301F8
; Build=48
; Language=0x0000 Language Neutral
; FirstLine=51
; CursorPosition=370
; EnableThread
; EnableXP
; UseIcon=PureTube.ico
; ExecutableFormat=Windows
; Executable=
; DontSaveDeclare
; EOF

XML examples

Code: Select all

<?xml version="1.0" encoding="iso-8859-1"?> 
<sources>
	<source id="YouTube">
		<homepage url="http://www.youtube.com" />
		<vid begin="video_id=" end="& #38;" />		
		<title begin="YouTube - " end="" />
		<download 
			url="http://cache.googlevideo.com/get_video?video_id=" 
			begin="video_id=" 
			end="& #38;" 
			decode="" />
	</source>
	<source id="DailyMotion">
		<homepage url="http://www.dailymotion.com" />
		<vid begin="key%3D" end="& #34;);" />		
		<title begin="Video " end=" - " />
		<download 
			url="http://www.dailymotion.com/get/" 
			begin="http%3A%2F%2Fwww.dailymotion.com%2Fget%2F" 
			end="& #34;);" 
			decode="url" />
	</source>	
	<source id="GoogleVideo">
		<homepage url="video.google.com" />
		<vid begin="/videoplay?docid\u003d" end="& #34;" />		
		<title begin="" end=" - " />
		<download 
			url="http://vp.video.google.com" 
			begin="http%3A%2F%2Fvp.video.google.com" 
			end="& #38;messagesUrl" 
			decode="url" />
	</source>
</sources>
Last edited by eddy on Sun Aug 19, 2007 7:09 pm, edited 1 time in total.
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Post by eddy »

TODO :
-------
- DragDrop ( + video source autodetect )
- Found a way to read multi-frames website ( eg : GoogleVideo )
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Does anyone know how to download from jibjab?
Edit: Never mind, found out. The url to the flv file is in an xml file.
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Post by eddy »

Next update :
---------------
- auto-detect source
- download through IE frames ( it will fix GoogleVideo problem )
- support : myspace or metacafe
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Thank you very much for this Droopy :)
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
jack
Addict
Addict
Posts: 1358
Joined: Fri Apr 25, 2003 11:10 pm

Post by jack »

pardon my ignorance, but how do you download a youtube video?
if I enter a url of a video all I get is a message that such videos are not available.
and what about the last download link, it's dead. :(
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Post by Inf0Byt3 »

It could be that YouTube has changed the location of the file or the specifications. They usually do this to kill usch programs so people can't download the videos (they lose bandwidth and noone clicks their ads).
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4790
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Post by Fangbeast »

Sounds right. I just tried this after fixing a few little bugs for pb4.2 and absolutely nothing showed up.

Going to fiddle with the URL analysis area unless the original author steps in. Wish me luck:):)
glops
User
User
Posts: 38
Joined: Wed Jan 16, 2008 12:53 pm
Location: France

why not use

Post by glops »

jack
Addict
Addict
Posts: 1358
Joined: Fri Apr 25, 2003 11:10 pm

Post by jack »

thanks glops, I saw that link bebore but did not know if it was any good
it actually works, bought the full versdion :)
Post Reply