Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for capturedate.cs

(view source code of capturedate.cs as plain text)

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Text.RegularExpressions;
  6.  
  7.  
  8. namespace RobvanderWoude
  9. {
  10. 	class CaptureDate
  11. 	{
  12. 		static readonly string progver = "1.05";
  13.  
  14.  
  15. 		#region Global Variables
  16.  
  17. 		static bool confirmsettimestamp = true;
  18. 		static bool filedatereplacescapturedate = false;
  19. 		static bool readabletimestamp = true;
  20. 		static bool recursive = false;
  21. 		static bool settimestamp = false;
  22. 		static bool useexiftool = false;
  23. 		static bool wildcards = false;
  24. 		static double timediffthreshold = 3600;
  25. 		static int renamecount = 0;
  26. 		static string exiftool = string.Empty;
  27. 		static string searchpattern = "*.*";
  28.  
  29. 		#endregion Global Variables
  30.  
  31.  
  32. 		static int Main( string[] args )
  33. 		{
  34. 			#region Initialize Variables
  35.  
  36. 			string startdir = Environment.CurrentDirectory; // in case no directory is specified
  37. 			string[] filespec;
  38.  
  39. 			#endregion Initialize Variables
  40.  
  41.  
  42. 			#region Parse Command Line
  43.  
  44. 			if ( args.Length == 0 )
  45. 			{
  46. 				return ShowHelp( );
  47. 			}
  48.  
  49. 			foreach ( string arg in args )
  50. 			{
  51. 				if ( arg == "/?" || arg.Length < 2 )
  52. 				{
  53. 					return ShowHelp( );
  54. 				}
  55. 				if ( arg[0] == '/' )
  56. 				{
  57. 					switch ( arg.Substring( 0, 2 ).ToUpper( ) )
  58. 					{
  59. 						case "/D":
  60. 							if ( arg.Length > 3 && arg[2] == ':' )
  61. 							{
  62. 								if ( timediffthreshold != 3600 )
  63. 								{
  64. 									return ShowHelp( "Duplicate switch: /D" );
  65. 								}
  66. 								try
  67. 								{
  68. 									timediffthreshold = Convert.ToDouble( arg.Substring( 3 ) );
  69. 								}
  70. 								catch
  71. 								{
  72. 									return ShowHelp( "Invalid value: \"{0}\"", arg );
  73. 								}
  74. 							}
  75. 							else
  76. 							{
  77. 								return ShowHelp( "Invalid value: \"{0}\"", arg );
  78. 							}
  79. 							break;
  80. 						case "/F":
  81. 							if ( filedatereplacescapturedate )
  82. 							{
  83. 								return ShowHelp( "Duplicate switch: /F" );
  84. 							}
  85. 							filedatereplacescapturedate = true;
  86. 							break;
  87. 						case "/R":
  88. 							if ( recursive )
  89. 							{
  90. 								return ShowHelp( "Duplicate switch: /R" );
  91. 							}
  92. 							recursive = true;
  93. 							break;
  94. 						case "/S":
  95. 							if ( settimestamp )
  96. 							{
  97. 								return ShowHelp( "Duplicate switch: /S" );
  98. 							}
  99. 							settimestamp = true;
  100. 							break;
  101. 						case "/T":
  102. 							if ( !readabletimestamp )
  103. 							{
  104. 								return ShowHelp( "Duplicate switch: /T" );
  105. 							}
  106. 							readabletimestamp = false;
  107. 							break;
  108. 						case "/X":
  109. 							if ( useexiftool )
  110. 							{
  111. 								return ShowHelp( "Duplicate switch: /X" );
  112. 							}
  113. 							useexiftool = true;
  114. 							break;
  115. 						case "/Y":
  116. 							if ( !confirmsettimestamp )
  117. 							{
  118. 								return ShowHelp( "Duplicate switch: /Y" );
  119. 							}
  120. 							confirmsettimestamp = false;
  121. 							break;
  122. 						default:
  123. 							return ShowHelp( "Invalid switch: \"{0}\"", arg );
  124. 					}
  125. 				}
  126. 				else
  127. 				{
  128. 					searchpattern = Path.GetFileName( arg );
  129. 					wildcards = ( searchpattern.IndexOfAny( "*?".ToCharArray( ) ) > -1 );
  130. 					startdir = Directory.GetParent( arg ).FullName;
  131. 					if ( string.IsNullOrEmpty( startdir ) && !Directory.Exists( startdir ) )
  132. 					{
  133. 						return ShowHelp( "Folder not found: \"{0}\"", startdir );
  134. 					}
  135. 					filespec = Directory.GetFiles( startdir, searchpattern );
  136. 				}
  137. 			}
  138. 			if ( !confirmsettimestamp && !settimestamp )
  139. 			{
  140. 				return ShowHelp( "/Y switch is valid only when combined with /S switch." );
  141. 			}
  142. 			if ( timediffthreshold != 3600 && !settimestamp )
  143. 			{
  144. 				return ShowHelp( "/D switch is valid only when combined with /S switch." );
  145. 			}
  146. 			if ( useexiftool )
  147. 			{
  148. 				string[] path = string.Format( "{0};{1}", startdir, Environment.GetEnvironmentVariable( "PATH" ) ).Split( ";".ToCharArray( ), StringSplitOptions.RemoveEmptyEntries );
  149. 				foreach ( string dir in path )
  150. 				{
  151. 					if ( string.IsNullOrWhiteSpace( exiftool ) && File.Exists( Path.Combine( dir, "exiftool.exe" ) ) )
  152. 					{
  153. 						exiftool = Path.Combine( dir, "exiftool.exe" );
  154. 					}
  155. 				}
  156. 				if ( string.IsNullOrWhiteSpace( exiftool ) )
  157. 				{
  158. 					return ShowHelp( "ExifTool.exe not found, which makes /X switch invalid" );
  159. 				}
  160. 			}
  161. 			if ( filedatereplacescapturedate && !( useexiftool && settimestamp ) )
  162. 			{
  163. 				return ShowHelp( "/F switch is valid only when combined with /S and /X switches." );
  164. 			}
  165.  
  166. 			#endregion Parse Command Line
  167.  
  168.  
  169. 			ProcessFolder( startdir );
  170.  
  171. 			return renamecount;
  172. 		}
  173.  
  174.  
  175. 		static string GetTimestampEXIF( string filename, bool usefiletags = true )
  176. 		{
  177. 			string photodatedelimited = string.Empty;
  178. 			string pattern = "\\b[12]\\d{3}:[01]\\d:[0-3]\\d [0-2]\\d:[0-5]\\d:[0-5]\\d(\\b|\\+)";
  179. 			Regex regexp = new Regex( pattern, RegexOptions.None );
  180.  
  181. 			// First try the standard EXIF tag (should return 1 timestamp)
  182. 			ProcessStartInfo si = new ProcessStartInfo( )
  183. 			{
  184. 				FileName = exiftool,
  185. 				Arguments = "-EXIF:DateTimeOriginal " + filename,
  186. 				UseShellExecute = false,
  187. 				RedirectStandardOutput = true,
  188. 			};
  189. 			Process proc = Process.Start( si );
  190. 			proc.WaitForExit( );
  191. 			string exif = proc.StandardOutput.ReadToEnd( );
  192. 			MatchCollection matches = regexp.Matches( exif );
  193. 			if ( matches.Count > 0 )
  194. 			{
  195. 				photodatedelimited = matches[0].Value;
  196. 			}
  197. 			else if ( usefiletags )
  198. 			{
  199. 				// If querying the standard EXIF tag fails, try the file related
  200. 				// EXIF tags (will return 3 timestamps, we need the oldest one)
  201. 				si = new ProcessStartInfo( )
  202. 				{
  203. 					FileName = exiftool,
  204. 					Arguments = "-FILE:* " + filename,
  205. 					UseShellExecute = false,
  206. 					RedirectStandardOutput = true,
  207. 				};
  208. 				proc = Process.Start( si );
  209. 				proc.WaitForExit( );
  210. 				exif = proc.StandardOutput.ReadToEnd( );
  211.  
  212. 				#region Find Earliest Date String
  213.  
  214. 				regexp = new Regex( pattern, RegexOptions.None );
  215. 				matches = regexp.Matches( exif );
  216. 				if ( matches.Count == 0 )
  217. 				{
  218. 					ShowHelp( "No capture date found in EXIF data" );
  219. 					return String.Empty;
  220. 				}
  221. 				foreach ( Match match in matches )
  222. 				{
  223. 					int compare = string.Compare( photodatedelimited, match.Value, StringComparison.Ordinal );
  224. 					if ( string.IsNullOrEmpty( photodatedelimited ) || compare > 0 )
  225. 					{
  226. 						photodatedelimited = match.Value;
  227. 					}
  228. 				}
  229.  
  230. 				#endregion Find Earliest Date String
  231. 			}
  232.  
  233. 			return photodatedelimited;
  234. 		}
  235.  
  236.  
  237. 		static void ProcessFolder( string folder )
  238. 		{
  239. 			string[] filespec = Directory.GetFiles( folder, searchpattern );
  240. 			foreach ( string filename in filespec )
  241. 			{
  242. 				if ( filedatereplacescapturedate )
  243. 				{
  244. 					int rc = SetTimestampEXIF( filename );
  245. 					if ( rc != 0 )
  246. 					{
  247. 						ShowHelp( "Returncode {0} for \"{1}\"", rc.ToString( ), filename );
  248. 					}
  249. 				}
  250. 				else
  251. 				{
  252. 					ProcessImage( filename );
  253. 				}
  254. 			}
  255. 			if ( recursive )
  256. 			{
  257. 				string[] subdirs = Directory.GetDirectories( folder );
  258. 				foreach ( string subdir in subdirs )
  259. 				{
  260. 					ProcessFolder( subdir );
  261. 				}
  262. 			}
  263. 		}
  264.  
  265.  
  266. 		static void ProcessImage( string filename )
  267. 		{
  268. 			#region Read First MB of File
  269.  
  270. 			long filesize = new FileInfo( filename ).Length;
  271. 			int buffersize = Convert.ToInt32( Math.Min( filesize, 1048576 ) ); // Buffer size is 1 MB or file size, whichever is the smallest
  272. 			StreamReader file = new StreamReader( filename );
  273. 			char[] buffer = new Char[buffersize];
  274. 			_ = file.Read( buffer, 0, buffersize - 1 ); // Read only the first 1 MB of the file (or the entire file if smaller than 1 MB)
  275. 			file.Close( );
  276. 			string header = new String( buffer );
  277.  
  278. 			if ( String.IsNullOrEmpty( header ) )
  279. 			{
  280. 				ShowHelp( "Could not open file \"{0}\"", filename );
  281. 				return;
  282. 			}
  283.  
  284. 			#endregion Read First MB of File
  285.  
  286.  
  287. 			#region Find Earliest Date String
  288.  
  289. 			string photodatedelimited = string.Empty;
  290. 			string pattern = "\\b[12]\\d{3}:[01]\\d:[0-3]\\d [0-2]\\d:[0-5]\\d:[0-5]\\d\\b";
  291. 			Regex regexp = new Regex( pattern, RegexOptions.None );
  292. 			MatchCollection matches = regexp.Matches( header );
  293. 			if ( matches.Count == 0 )
  294. 			{
  295. 				if ( useexiftool )
  296. 				{
  297. 					photodatedelimited = GetTimestampEXIF( filename );
  298. 				}
  299. 				else
  300. 				{
  301. 					ShowHelp( "No capture date found in file header" );
  302. 					return;
  303. 				}
  304. 			}
  305. 			else
  306. 			{
  307. 				foreach ( Match match in matches )
  308. 				{
  309. 					if ( string.IsNullOrEmpty( photodatedelimited ) || string.Compare( photodatedelimited, match.Value, StringComparison.Ordinal ) > 0 )
  310. 					{
  311. 						photodatedelimited = match.Value;
  312. 					}
  313. 				}
  314. 			}
  315. 			photodatedelimited = photodatedelimited.Substring( 0, 10 ).Replace( ':', '-' ) + photodatedelimited.Substring( 10 );
  316. 			string photodatenodelims = photodatedelimited.Replace( ":", "" ).Replace( "-", "" ).Replace( " ", "T" );
  317.  
  318. 			#endregion Find Earliest Date String
  319.  
  320.  
  321. 			#region Set File Timestamp
  322.  
  323. 			if ( settimestamp )
  324. 			{
  325. 				string timeformat;
  326. 				if ( readabletimestamp )
  327. 				{
  328. 					timeformat = "{0:yyyy}-{0:MM}-{0:dd} {0:HH}:{0:mm}:{0:ss}";
  329. 				}
  330. 				else
  331. 				{
  332. 					timeformat = "{0:yyyy}{0:MM}{0:dd}T{0:HH}{0:mm}{0:ss}";
  333. 				}
  334. 				DateTime currenttimestamp = File.GetLastWriteTime( filename );
  335. 				if ( DateTime.TryParse( photodatedelimited, out DateTime newtimestamp ) ) // Try parsing the new file timestamp using the capture timestamp string
  336. 				{
  337. 					if ( DateTime.Compare( currenttimestamp, newtimestamp ) == 0 ) // File and capture timestamps are equal
  338. 					{
  339. 						string photodate = photodatenodelims;
  340. 						if ( readabletimestamp )
  341. 						{
  342. 							photodate = photodatedelimited;
  343. 						}
  344. 						if ( wildcards )
  345. 						{
  346. 							Console.WriteLine( "{0}\t\"{1}\"", photodate, filename );
  347. 						}
  348. 						else
  349. 						{
  350. 							Console.WriteLine( photodate );
  351. 						}
  352. 					}
  353. 					else
  354. 					{
  355. 						double timediff = Math.Abs( ( currenttimestamp - newtimestamp ).TotalSeconds ); // Calculate absolute value of timestamps' difference in seconds
  356. 						if ( timediff > timediffthreshold ) // Ignore time differences up to threshold (default 1 hour, or value set with /D switch)
  357. 						{
  358. 							string blanks = "\n" + new String( ' ', 70 ) + new String( '\b', 70 ); // String to erase the first 70 characters on the next line
  359. 							Console.WriteLine( "Image file name        : {0}", filename );
  360. 							Console.WriteLine( "Current file timestamp : " + timeformat, currenttimestamp );
  361. 							Console.WriteLine( "Capture timestamp      : " + timeformat, newtimestamp );
  362. 							if ( confirmsettimestamp )
  363. 							{
  364. 								Console.Write( "Do you want to change the file's timestamp to the capture time? [yN] " );
  365. 								string answer = Console.ReadKey( ).KeyChar.ToString( ).ToUpper( );
  366. 								if ( answer == "Y" )
  367. 								{
  368. 									File.SetLastWriteTime( filename, newtimestamp );
  369. 									Console.CursorTop -= 1; // Move the cursor 1 line up
  370. 									Console.WriteLine( blanks + "New file timestamp     : " + timeformat, File.GetLastWriteTime( filename ) ); // Overwrite prompt with new timestamp
  371. 									renamecount += 1;
  372. 								}
  373. 								else
  374. 								{
  375. 									Console.WriteLine( "\nskipping . . ." );
  376. 								}
  377. 							}
  378. 							else
  379. 							{
  380. 								File.SetLastWriteTime( filename, newtimestamp );
  381. 								Console.CursorTop -= 1; // Move the cursor 1 line up
  382. 								Console.WriteLine( blanks + "New file timestamp     : " + timeformat, File.GetLastWriteTime( filename ) ); // Overwrite prompt with new timestamp
  383. 								renamecount += 1;
  384. 							}
  385. 						}
  386. 						else
  387. 						{
  388. 							Console.WriteLine( photodatedelimited ); // Timespans' difference is not above threshold (default 1 hour, or value set with /D switch)
  389. 						}
  390. 					}
  391. 				}
  392. 				else
  393. 				{
  394. 					ShowHelp( "Could not determine timestamp of \"{0}\"", filename );
  395. 					return;
  396. 				}
  397. 			}
  398. 			else
  399. 			{
  400. 				Console.WriteLine( "{0}\t\"{1}\"", photodatedelimited, filename );
  401. 			}
  402.  
  403. 			#endregion Set File Timestamp
  404. 		}
  405.  
  406.  
  407. 		static int SetTimestampEXIF( string filename )
  408. 		{
  409. 			DateTime filetimestamp = File.GetLastWriteTime( filename );
  410. 			string capturetimestamp = GetTimestampEXIF( filename, false );
  411. 			string timeformat;
  412. 			if ( readabletimestamp )
  413. 			{
  414. 				timeformat = "{0:yyyy}-{0:MM}-{0:dd} {0:HH}:{0:mm}:{0:ss}";
  415. 				if ( capturetimestamp.Length > 10 )
  416. 				{
  417. 					capturetimestamp = capturetimestamp.Substring( 0, 10 ).Replace( ':', '-' ) + capturetimestamp.Substring( 10 );
  418. 				}
  419. 			}
  420. 			else
  421. 			{
  422. 				timeformat = "{0:yyyy}{0:MM}{0:dd}T{0:HH}{0:mm}{0:ss}";
  423. 				if ( capturetimestamp.Length > 10 )
  424. 				{
  425. 					capturetimestamp = capturetimestamp.Replace( ":", "" ).Replace( " ", "T" );
  426. 				}
  427. 			}
  428. 			Console.WriteLine( "Image file name           : {0}", filename );
  429. 			Console.WriteLine( "Current capture timestamp : {0}", capturetimestamp );
  430. 			Console.WriteLine( "Current file timestamp    : " + timeformat, filetimestamp );
  431. 			if ( capturetimestamp == string.Format( timeformat, filetimestamp ) )
  432. 			{
  433. 				return 0;
  434. 			}
  435. 			string blanks = "\n" + new String( ' ', 70 ) + new String( '\b', 70 ); // String to erase the first 70 characters on the next line
  436. 			if ( confirmsettimestamp )
  437. 			{
  438. 				Console.Write( "Do you want to change the file's capture time to its file timestamp? [yN] " );
  439. 				string answer = Console.ReadKey( ).KeyChar.ToString( ).ToUpper( );
  440. 				if ( answer == "N" )
  441. 				{
  442. 					Console.WriteLine( "\nskipping . . ." );
  443. 					return -1;
  444. 				}
  445. 				else
  446. 				{
  447. 					Console.WriteLine( );
  448. 				}
  449. 			}
  450.  
  451. 			ProcessStartInfo si = new ProcessStartInfo( )
  452. 			{
  453. 				FileName = exiftool,
  454. 				Arguments = string.Format( "-EXIF:DateTimeOriginal=\"{0:yyyy}:{0:MM}:{0:dd} {0:HH}:{0:mm}:{0:ss}\" \"{1}\"", filetimestamp, filename ),
  455. 				UseShellExecute = false,
  456. 				RedirectStandardOutput = true,
  457. 			};
  458. 			Process proc = Process.Start( si );
  459. 			proc.WaitForExit( );
  460. 			// restore file timestamp
  461. 			if ( proc.ExitCode == 0 )
  462. 			{
  463. 				File.SetLastWriteTime( filename, filetimestamp );
  464. 				if ( confirmsettimestamp )
  465. 				{
  466. 					capturetimestamp = GetTimestampEXIF( filename, false );
  467. 					if ( readabletimestamp )
  468. 					{
  469. 						capturetimestamp = capturetimestamp.Substring( 0, 10 ).Replace( ':', '-' ) + capturetimestamp.Substring( 10 );
  470. 					}
  471. 					else
  472. 					{
  473. 						capturetimestamp = capturetimestamp.Replace( ":", "" ).Replace( " ", "T" );
  474. 					}
  475. 					Console.CursorTop -= 1; // Move the cursor 1 line up
  476. 					Console.WriteLine( blanks + "New capture timestamp     : {0}", capturetimestamp ); // Overwrite prompt with new timestamp
  477. 				}
  478. 				renamecount += 1;
  479. 			}
  480. 			return proc.ExitCode;
  481. 		}
  482.  
  483.  
  484. 		static string Today( bool usedelims = true )
  485. 		{
  486. 			string dateformat;
  487. 			if ( usedelims )
  488. 			{
  489. 				dateformat = "{0:yyyy}-{0:MM}-{0:dd} {0:HH}:{0:mm}:{0:ss}";
  490. 			}
  491. 			else
  492. 			{
  493. 				dateformat = "{0:yyyy}{0:MM}{0:dd}T{0:HH}{0:mm}{0:ss}";
  494. 			}
  495. 			return String.Format( dateformat, DateTime.Now );
  496. 		}
  497.  
  498.  
  499. 		#region Error handling
  500.  
  501. 		public static int ShowHelp( params string[] errmsg )
  502. 		{
  503. 			#region Error Message
  504.  
  505. 			if ( errmsg.Length > 0 )
  506. 			{
  507. 				List<string> errargs = new List<string>( errmsg );
  508. 				errargs.RemoveAt( 0 );
  509. 				Console.Error.WriteLine( );
  510. 				Console.ForegroundColor = ConsoleColor.Red;
  511. 				Console.Error.Write( "ERROR:\t" );
  512. 				Console.ForegroundColor = ConsoleColor.White;
  513. 				Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
  514. 				Console.ResetColor( );
  515. 			}
  516.  
  517. 			#endregion Error Message
  518.  
  519.  
  520. 			#region Help Text
  521.  
  522. 			/*
  523. 			CaptureDate,  Version 1.05
  524. 			Return the capture date and time for the specified image file
  525.  
  526. 			Usage:   CAPTUREDATE  image  [ options ]
  527.  
  528. 			Where:   image        specifies the image file(s) (wildcards allowed)
  529.  
  530. 			Options: /D:seconds   minimum Difference in seconds between current file
  531. 			                      timestamp and capture date/time; if the difference
  532. 			                      exceeds the specified number of seconds, the file
  533. 			                      timestamp will be set to the capture date/time
  534. 			                      (default: 3600 seconds = 1 hour; requires /S switch)
  535. 			         /F           set capture date/time to current File timestamp
  536. 			                      (requires /S and /X switches)
  537. 			         /R           Recursive (include subdirectories); you probably want
  538. 			                      to use wildcards for image with this option
  539. 			         /S           Set the image file timestamp to the capture date/time
  540. 			         /T           return the timestamp without "-" and ":" delimiters,
  541. 			                      e.g. 20171114T135628 instead of 2017-11-14 13:56:28
  542. 			         /X           use eXiftool by Phil Harvey (https://exiftool.org/;
  543. 			                      requires exiftool.exe in the current directory or in
  544. 			                      a directory listed in the PATH)
  545. 			         /Y           do not ask for confirmation before changing the image
  546. 			                      file's timestamp (requires /S switch)
  547.  
  548. 			Notes:   Result will be displayed on screen, e.g. 2017-11-14 13:56:28.
  549. 			         The date/time is extracted by searching for the earliest date/time
  550. 			         string in the first 1048576 bytes (1 MB) of the image file. If no
  551. 			         capture date/time is found that way, and the /X switch is used,
  552. 			         exiftool.exe is used to try and read the capture date/time from the
  553. 			         image's EXIF data. If this also fails, you can use the /F switch to
  554. 			         set a new capture date/time in EXIF equal to the file date/time.
  555. 			         With /S switch used, the timestamp is changed only if the difference
  556. 			         between the current timestamp and the capture time exceeds 1 hour or
  557. 			         the threshold set with the /D switch.
  558. 			         The program will ask for confirmation before changing the file's
  559. 			         timestamp, unless the /Y switch is used.
  560. 			         Return code ("errorlevel") is -1 in case of errors, or with /S it
  561. 			         equals the number of files renamed, or 0 otherwise.
  562.  
  563. 			Written by Rob van der Woude
  564. 			http://www.robvanderwoude.com
  565. 			*/
  566.  
  567. 			#endregion Help Text
  568.  
  569.  
  570. 			#region Display Help
  571.  
  572. 			string timestampDelimited = Today( true );
  573. 			string timestampNodelims = Today( false );
  574. 			Console.Error.WriteLine( );
  575.  
  576. 			Console.Error.WriteLine( "CaptureDate,  Version {0}", progver );
  577.  
  578. 			Console.Error.WriteLine( "Return the capture date and time for the specified image file" );
  579.  
  580. 			Console.Error.WriteLine( );
  581.  
  582. 			Console.Error.Write( "Usage:   " );
  583. 			Console.ForegroundColor = ConsoleColor.White;
  584. 			Console.Error.WriteLine( "CAPTUREDATE  image  [ options ]" );
  585. 			Console.ResetColor( );
  586.  
  587. 			Console.Error.WriteLine( );
  588.  
  589. 			Console.Error.Write( "Where:   " );
  590. 			Console.ForegroundColor = ConsoleColor.White;
  591. 			Console.Error.Write( "image" );
  592. 			Console.ResetColor( );
  593. 			Console.Error.WriteLine( "        specifies the image file(s) (wildcards allowed)" );
  594.  
  595. 			Console.Error.WriteLine( );
  596.  
  597. 			Console.Error.Write( "Options: " );
  598. 			Console.ForegroundColor = ConsoleColor.White;
  599. 			Console.Error.Write( "/D:seconds" );
  600. 			Console.ResetColor( );
  601. 			Console.Error.Write( "   minimum " );
  602. 			Console.ForegroundColor = ConsoleColor.White;
  603. 			Console.Error.Write( "D" );
  604. 			Console.ResetColor( );
  605. 			Console.Error.Write( "ifference in " );
  606. 			Console.ForegroundColor = ConsoleColor.White;
  607. 			Console.Error.Write( "seconds" );
  608. 			Console.ResetColor( );
  609. 			Console.Error.WriteLine( " between current file" );
  610.  
  611. 			Console.Error.WriteLine( "                      timestamp and capture date/time; if the difference" );
  612.  
  613. 			Console.Error.WriteLine( "                      exceeds the specified number of seconds, the file" );
  614.  
  615. 			Console.Error.WriteLine( "                      timestamp will be set to the capture date/time" );
  616.  
  617. 			Console.Error.Write( "                      (default: 3600 seconds = 1 hour; requires " );
  618. 			Console.ForegroundColor = ConsoleColor.White;
  619. 			Console.Error.Write( "/S" );
  620. 			Console.ResetColor( );
  621. 			Console.Error.WriteLine( " switch)" );
  622.  
  623. 			Console.ForegroundColor = ConsoleColor.White;
  624. 			Console.Error.Write( "         /F" );
  625. 			Console.ResetColor( );
  626. 			Console.Error.Write( "           set capture date/time to current " );
  627. 			Console.ForegroundColor = ConsoleColor.White;
  628. 			Console.Error.Write( "F" );
  629. 			Console.ResetColor( );
  630. 			Console.Error.WriteLine( "ile timestamp" );
  631.  
  632. 			Console.Error.Write( "                      (requires " );
  633. 			Console.ForegroundColor = ConsoleColor.White;
  634. 			Console.Error.Write( "/S" );
  635. 			Console.ResetColor( );
  636. 			Console.Error.Write( " and " );
  637. 			Console.ForegroundColor = ConsoleColor.White;
  638. 			Console.Error.Write( "/X" );
  639. 			Console.ResetColor( );
  640. 			Console.Error.WriteLine( " switches)" );
  641.  
  642. 			Console.ForegroundColor = ConsoleColor.White;
  643. 			Console.Error.Write( "         /R           R" );
  644. 			Console.ResetColor( );
  645. 			Console.Error.WriteLine( "ecursive (include subdirectories); you probably want" );
  646.  
  647. 			Console.Error.Write( "                      to use wildcards for " );
  648. 			Console.ForegroundColor = ConsoleColor.White;
  649. 			Console.Error.Write( "image" );
  650. 			Console.ResetColor( );
  651. 			Console.Error.WriteLine( " with this option" );
  652.  
  653. 			Console.ForegroundColor = ConsoleColor.White;
  654. 			Console.Error.Write( "         /S           S" );
  655. 			Console.ResetColor( );
  656. 			Console.Error.WriteLine( "et the image file's timestamp to the capture date/time" );
  657.  
  658. 			Console.ForegroundColor = ConsoleColor.White;
  659. 			Console.Error.Write( "         /T" );
  660. 			Console.ResetColor( );
  661. 			Console.Error.WriteLine( "           return the timestamp without \"-\" and \":\" delimiters," );
  662.  
  663. 			Console.Error.Write( "                      e.g. " );
  664. 			Console.ForegroundColor = ConsoleColor.White;
  665. 			Console.Error.Write( timestampNodelims );
  666. 			Console.ResetColor( );
  667. 			Console.Error.Write( " instead of " );
  668. 			Console.ForegroundColor = ConsoleColor.White;
  669. 			Console.Error.Write( timestampDelimited );
  670. 			Console.ResetColor( );
  671. 			Console.Error.WriteLine( "." );
  672.  
  673. 			Console.ForegroundColor = ConsoleColor.White;
  674. 			Console.Error.Write( "         /X" );
  675. 			Console.ResetColor( );
  676. 			Console.Error.Write( "           use e" );
  677. 			Console.ForegroundColor = ConsoleColor.White;
  678. 			Console.Error.Write( "X" );
  679. 			Console.ResetColor( );
  680. 			Console.Error.Write( "iftool by Phil Harvey (" );
  681. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  682. 			Console.Error.Write( "https://exiftool.org/" );
  683. 			Console.ResetColor( );
  684. 			Console.Error.WriteLine( ";" );
  685.  
  686. 			Console.Error.WriteLine( "                      requires exiftool.exe in the current directory or in" );
  687.  
  688. 			Console.Error.WriteLine( "                      a directory listed in the PATH)" );
  689.  
  690. 			Console.ForegroundColor = ConsoleColor.White;
  691. 			Console.Error.Write( "         /Y" );
  692. 			Console.ResetColor( );
  693. 			Console.Error.WriteLine( "           do not ask for confirmation before changing the image" );
  694.  
  695. 			Console.Error.Write( "                      file's timestamp (requires " );
  696. 			Console.ForegroundColor = ConsoleColor.White;
  697. 			Console.Error.Write( "/S" );
  698. 			Console.ResetColor( );
  699. 			Console.Error.WriteLine( " switch)" );
  700.  
  701. 			Console.Error.WriteLine( );
  702.  
  703. 			Console.Error.Write( "Notes:   Result will be displayed on screen, e.g. " );
  704. 			Console.ForegroundColor = ConsoleColor.White;
  705. 			Console.Error.Write( timestampDelimited );
  706. 			Console.ResetColor( );
  707. 			Console.Error.WriteLine( "." );
  708.  
  709. 			Console.Error.WriteLine( "         The date/time is extracted by searching for the earliest date/time" );
  710.  
  711. 			Console.Error.WriteLine( "         in the first 1048576 bytes (1 MB) of the image file. If no" );
  712.  
  713. 			Console.Error.Write( "         capture date/time is found that way, and the " );
  714. 			Console.ForegroundColor = ConsoleColor.White;
  715. 			Console.Error.Write( "/X" );
  716. 			Console.ResetColor( );
  717. 			Console.Error.WriteLine( " switch is used," );
  718.  
  719. 			Console.Error.WriteLine( "         exiftool.exe is used to try and read the capture date/time from the" );
  720.  
  721. 			Console.Error.Write( "         image's EXIF data. If this also fails, you can use the " );
  722. 			Console.ForegroundColor = ConsoleColor.White;
  723. 			Console.Error.Write( "/F" );
  724. 			Console.ResetColor( );
  725. 			Console.Error.WriteLine( " switch to" );
  726.  
  727. 			Console.Error.Write( "         set a " );
  728. 			Console.ForegroundColor = ConsoleColor.White;
  729. 			Console.Error.Write( "new" );
  730. 			Console.ResetColor( );
  731. 			Console.Error.WriteLine( " capture date/time in EXIF equal to the file date/time." );
  732.  
  733. 			Console.Error.Write( "         With " );
  734. 			Console.ForegroundColor = ConsoleColor.White;
  735. 			Console.Error.Write( "/S" );
  736. 			Console.ResetColor( );
  737. 			Console.Error.WriteLine( " switch used, the timestamp is changed only if the difference" );
  738.  
  739. 			Console.Error.WriteLine( "         between the current timestamp and the capture time exceeds 1 hour or" );
  740.  
  741. 			Console.Error.Write( "         the threshold set with the " );
  742. 			Console.ForegroundColor = ConsoleColor.White;
  743. 			Console.Error.Write( "/D" );
  744. 			Console.ResetColor( );
  745. 			Console.Error.WriteLine( " switch." );
  746.  
  747. 			Console.Error.WriteLine( "         The program will ask for confirmation before changing the file's" );
  748.  
  749. 			Console.Error.Write( "         timestamp, unless the " );
  750. 			Console.ForegroundColor = ConsoleColor.White;
  751. 			Console.Error.Write( "/Y" );
  752. 			Console.ResetColor( );
  753. 			Console.Error.WriteLine( " switch is used." );
  754.  
  755. 			Console.Error.Write( "         Return code (\"errorlevel\") is -1 in case of errors, or with " );
  756. 			Console.ForegroundColor = ConsoleColor.White;
  757. 			Console.Error.Write( "/S" );
  758. 			Console.ResetColor( );
  759. 			Console.Error.WriteLine( " it" );
  760.  
  761. 			Console.Error.WriteLine( "         equals the number of files renamed, or 0 otherwise." );
  762.  
  763. 			Console.Error.WriteLine( );
  764.  
  765. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  766.  
  767. 			Console.Error.WriteLine( "http://www.robvanderwoude.com" );
  768.  
  769. 			#endregion Display Help
  770.  
  771.  
  772. 			return -1;
  773. 		}
  774.  
  775. 		#endregion Error handling
  776.  
  777. 	}
  778. }

page last modified: 2024-04-16; loaded in 0.0526 seconds