From: Phil Date: 2002-07-04T04:14:07+09:00 Subject: Re: rubycam.rb v 1.7 CGI webcam script for use with cqcam, etc... Want to clarify that some redundancy existed in that post - the relevant portion follows. On Wed, 19 Jun 2002 16:54:21 -0700, Phil wrote: > I don't want to mess with Sourceforge, so consider this my form of > dissemination in case the server disappears some time. Please forward > mods to the email address listed under AUTHOR below. PS: New to ruby > when I wrote it - now have RSI - probably not going to make it very > slick. ;-) > > ------------- > > #!/usr/bin/env ruby > > ######################################################################## > ## VERSION: $Id: rubycam.rb,v 1.7 2002/03/22 20:08:52 rubycam Exp $ ## > ## AUTHOR: Phil Voris ## ## LICENSE: Ruby > License - must include this header comment block ## > http://www.ruby-lang.org/en/LICENSE.txt ## > ## DESCRIPTION: > ## This CGI webcam script attempts to conserve resources by not ## > creating images more often than the number of seconds indicated by ## > the refresh setting. Maximum flexibility is given by allowing the ## > administrator to set the image-producing command line. The ## defaults > are arranged to use cqcam ## (http://www.cs.duke.edu/~reynolds/cqcam/). > ## > ## REQUIRES: ruby > ## ruby modules: net/smtp (for error reporting), ftools ## > rb2html.rb (for viewing source) ## some image-grabber, > such as cqcam ## optionally, some image manipulation software > ## > ## USAGE: > ## * Install under cgi-bin or wherever cgi executables are allowed. ## * > Configure the global config section below. ## * Configure the default > config section below. ## * Optionally create one or more config files to > call in the PATH_INFO. ## * Ensure that img_file_web_directory exists > and is writable by the ## web server ## * [See end of script for > recommendations for config and calling from ## html.] > ## > ## TO DO: > ## It would be nice to add caching. That is, deliver a header ## > indicating that the page (the image) hasn't changed until it ## actually > does. I attempted this, but found that ## HTTP_IF_MODIFIED_SINCE didn't > appear in the environment. I then ## attempted to use a session to > track if the use had seen the page ## with the current image - however > sessions weren't quite working for ## me - perhaps someone else can > resolve this issue. > ######################################################################## > > > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # GLOBAL > CONFIGURATION - EDIT HERE > # > # These options must be set > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > > ## allow options from URL (true / false) use_config_files = true > > ## img file directory [relative to DOC_ROOT] img_file_web_directory = > '/img/cam' > > ## configuration files directory > config_dir = './config' > > ## directory where rb2html and associated files may be found ## (used > for /view) > rb2html_directory = './rb2html-1.0' > > ## file base-name > lock_filename = 'rubycam.lock' > > ## maximum age to keep a lock file (seconds, must exceed refresh time) > max_lockfile_age = 500 > > ## shows a little (R) which links to the source. Specify the target ## > frame ('_new', '_self', etc) or nil to not display it. > source_link_target = '_new' > > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # END > GLOBAL CONFIGURATION > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # > PER-CAM CONFIGURATION - SET DEFAULTS HERE # - MAY > MODIFY IN CONFIG FILES # > # Anything listed in this section may be set - as below - in a # > separate file. > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > > ## default_cqcam_opts > cmd_line = 'cqcam -r -q 25 -s 1 -j' > > ## filename for the images > img_filename = 'plain.jpg' > > ## admin email address > admin_email_address = 'webmaster@localhost' > > ## page title > title = 'Rubycam' > > ## background (color) > bgcolor = 'black' > > ## text (color) > text_color = 'yellow' > > ## browser refresh length (in seconds) refresh = 60 > > ## img_background_color (color | nil to disable globally) > img_background_color = nil > > ## randomize transparency_background_color (true / false) > use_randomize_img_background_color = false > > ## randomize transparency_background_color (true / false) > use_randomize_text_color = false > > ## match text and img backgrounds - useful for randomization ## (true / > false) > text_color_matches_img_background_color = false > > ## color for the source link, if it appears (color) source_link_color = > 'darkred' > > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # END > PER-CAM CONFIGURATION > #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > > > > > > > ########################################## ### PROCESS URL OPTIONS FROM > PATH_INFO ### ########################################## ## If the > script is called from a URL with path info equal to 'view' or ## 'get' > the contents of the script will be displayed. 'get' results ## in > text/plain output while view - relying on rb2html - output color- ## > coded html output with line numbers. path_info = > ENV['PATH_INFO'].untaint > > if path_info == '/get' > ## Print source > puts( "Content-type: text/plain\n\n" ) IO::foreach( > ENV['SCRIPT_FILENAME'].untaint ) { |l| > puts l > } > exit > elsif path_info == '/view' > ## Print formatted source > puts( "Content-type: text/html\n\n" ) rb2html_executable = > rb2html_directory + '/rb2html.rb' if File::file?( rb2html_executable > ) > begin > htmlpipe = IO::popen( 'ruby ' + rb2html_executable + ' ' + > ENV['SCRIPT_FILENAME'].untaint ) > puts( htmlpipe.readlines() ) > rescue SystemCallError > ## Mail admin if any errors - if you don't want mail, change ## > the admin address. > require 'net/smtp' > smtp = Net::SMTP::new( "localhost" ) > smtp.start > body = "Error:\nruby " + rb2html_executable + ' ' + > ENV['SCRIPT_FILENAME'].untaint > smtp.sendmail( body, 'rubycam_user@localhost', > admin_email_address ) smtp.finish > end > exit > else > puts( "Content-type: text/plain\n\n" ) puts( "Error: cannot execute > " + rb2html_executable ) exit > end > elsif path_info != nil > if not File::file?( config_dir + path_info ) > ## Print error > puts( "Content-type: text/plain\n\n" ) puts( "Error: invalid config > file: '" + config_dir + path_info + > "'\n" + > "Set the PATH_INFO (in the url) to a valid config file name." ) > exit > elsif path_info.index( '../' ) != nil > ## Print error > puts( "Content-type: text/plain\n\n" ) puts( "Error: invalid config > file: '" + config_dir + path_info + > "'\n" + > "'../' not permitted in config file pathname." ) > exit > elsif not File::readable?( config_dir + path_info ) > ## Print error > puts( "Content-type: text/plain\n\n" ) puts( "Error: unreadable > config file: '" + config_dir + path_info + > "'" ) > exit > else > ## Process config file > config_file_string = '' > File::open( config_dir + path_info ).readlines.each do |line| > config_file_string.concat( line ) > end > eval config_file_string > end > end > > > ########################### > ### DETERMINE FILE INFO ### > ########################### > > ## First, we determine the colors to be used for text (the image time) > ## and for the background of the image (not the page bgcolor). > color_hash = { > 0 => 'white', > 1 => 'red', > 2 => 'orange', > 3 => 'yellow', > 4 => 'green', > 5 => 'blue', > 6 => 'purple', > 7 => 'lightgreen', > 8 => 'pink', > 9 => 'lightyellow', > 10 => 'lightblue', > 11 => 'lightgray', > 12 => 'gray' > } > } > ## If img_background_color is set to nil, then no image background ## > color options will apply. > if img_background_color != nil and > use_randomize_img_background_color > img_background_color = color_hash[rand(13)] > end > > ## If randomization is used for text and image, then the image ## > background color is used for both. if use_randomize_text_color and > not text_color_matches_img_background_color > text_color = color_hash[rand(13)] > elsif ( img_background_color != nil ) and > text_color_matches_img_background_color > text_color = img_background_color > end > > img_file_webpath = img_file_web_directory + '/' + img_filename > img_file_pathname = ENV['DOCUMENT_ROOT'] + img_file_webpath > img_file_pathname.untaint > img_file_tmp_pathname = img_file_pathname + '.tmp' > > ## Time > time_now = Time::now > > ## Mtime > if File::exist?( img_file_pathname ) > img_file_mtime = File::mtime( img_file_pathname ) img_file_mtime_int > = img_file_mtime.to_i img_file_mtime_string = img_file_mtime.to_s > img_file_age = time_now.to_i - img_file_mtime_int > end > > ########################### > ### LOCKFILE CODE ### > ########################### > > ## Lock file name is global - there is only one camera and it can take > ## only one picture at a time. The lockfile is set during this process. > lock_file_pathname = ENV['DOCUMENT_ROOT'] + img_file_web_directory + '/' > + > lock_filename > lock_file_pathname.untaint > > ## If the lockfile somehow gets stale, it will be removed. locked = > false > if File::exist?( lock_file_pathname ) > if ( File::mtime( lock_file_pathname ) < ( time_now - ( > max_lockfile_age ) ) ) > File::unlink( lock_file_pathname ) > else > locked = true > end > end > > ########################### > ### EXECUTE ### > ########################### > > ## If there's no image, or it's an old one, and the camera is free, ## > then we take a new picture. > if ( ( not File::exist?( img_file_pathname ) ) or > ( img_file_age >= refresh ) ) and > ( not locked ) > > lockfile = File::new( lock_file_pathname, mode='w' ) > > begin > ## Execute > img_file = File::open( img_file_tmp_pathname, mode='w').write( > IO::popen( cmd_line ).read() ) > require 'ftools' > File::mv( img_file_tmp_pathname, img_file_pathname ) File::unlink( > lock_file_pathname ) > img_file_mtime = Time::now > img_file_mtime_string = img_file_mtime.to_s > rescue SystemCallError > ## Mail admin if any errors - if you don't want mail, change ## the > admin address. > require 'net/smtp' > smtp = Net::SMTP::new( "localhost" ) > smtp.start > body = "Subject: Error with rubycam\n" + > "Failed to execute:\n" + cmd_line + "\n\nSee logs for details." > smtp.sendmail( body, 'rubycam_user@localhost', admin_email_address > ) smtp.finish > File::unlink( lock_file_pathname ) > end > > end > > ########################### > ### HTML ### > ########################### > > puts( "Content-type: text/html\n" + > 'Last-Modified: ' + img_file_mtime_string + "\n" + 'Expires: ' + ( > img_file_mtime + refresh ).to_s + "\n\n" ) > > puts( '' + title + '' + > '' ) > > puts( '' ) > > puts( '
> if img_background_color != nil > puts( ' bgcolor="' + img_background_color + '"' ) > end > > puts( '>

' ) > > puts( '

' + img_file_mtime_string + '' ) > > if source_link_target != nil > puts( ' target="' + source_link_target + '">®' ) > end > > puts( '

' ) > > > > ########################### > ### CALLING FROM HTML ### > ########################### > ## > ## Include the following in your page head: ## ## > ## > ## Call within body of page as: > ## > ## > ## > > ########################### > ### CONFIG FILE # 1 ### > ########################### > ## greyjpeg.conf - produces a grey jpeg # cmd_line = 'cqcam -s 1 | > ppmtojpeg --progressive --greyscale --optimize --quality=33 > --comment="Made with Rubycam"' # img_filename = 'grey.jpg' # title = > 'Greycam' > # refresh = 60 > > ########################### > ### CONFIG FILE # 2 ### > ########################### > ## transgif.conf - produces a transparent gif with weird effects # > cmd_line = 'cqcam -s 1 | ppmquant 2 | ppmtogif -transparent grey > -interlace -sort -comment="Made with Rubycam"' # img_filename = > 'transparent.gif' > # title = 'Psychedelicam' > # refresh = 60 > # img_background_color = 'red' > # use_randomize_img_background_color = true # use_randomize_text_color > = true > # text_color_matches_img_background_color = true > > > ## EOF