commit 82d79df388f2feef53c09c368d01d1a945acacbf Author: profiteroles Date: Sun Sep 29 03:39:18 2019 -0700 0.4.0 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2172d10 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog +All notable changes to this project will be documented in this file. + +## [0.4.0] +- Initial repo version \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..123b496 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ + + +# Rollup + +**Rollup** is a tool for renaming image files from digital cameras using EXIF tags. It was originally written to improve the arbitrary filenames in the iPhone Camera Roll. + +* **⇩ [Download Latest Version](https://www.profiteroles.org/git/profiteroles/Rollup/raw/master/Rollup.app.zip)** + +## Features + +* Recursive directory scanning +* Custom rename template +* Reverse geocoding (lookup place name from GPS coordinates) + +## Screenshots + + + + +## Notes + +Running Rollup requires disabling Gatekeeper by running `sudo spctl --master-disable` in the Terminal. + +## Changelog + +https://www.profiteroles.org/git/profiteroles/Rollup/src/master/CHANGELOG.md + +## Building from source + +Building Rollup requires Platypus and Pashua + +* https://github.com/sveinbjornt/Platypus +* https://github.com/BlueM/Pashua \ No newline at end of file diff --git a/Rollup.app.zip b/Rollup.app.zip new file mode 100644 index 0000000..f19f01b Binary files /dev/null and b/Rollup.app.zip differ diff --git a/Rollup.php b/Rollup.php new file mode 100755 index 0000000..b9662a0 --- /dev/null +++ b/Rollup.php @@ -0,0 +1,157 @@ + $version) { + if(askMulti("Rollup ".$curr_version." is available (you have ".$version.")", array("Cancel","Download")) == 1) { + exec("open https://www.profiteroles.org/git/profiteroles/Rollup"); + echo "QUITAPP\n"; + } else { + die; + } + } else { + echo "\nALERT:Up-to-date|".$version." is the latest version.\n"; + die; + } + } + +function alert($string, $title = "Warning") { + echo "\nALERT:".$title."|".$string."\n"; + } + +// Build list of valid dropped files + +foreach ($argv as $target) { + + if (is_dir($target)) { + $it = new RecursiveDirectoryIterator($target); + foreach(new RecursiveIteratorIterator($it) as $file) { + if (in_array(strtolower(pathinfo($file,PATHINFO_EXTENSION)), $p['allowed'])) { + $files[] = $file->getpathname(); + } + } + } elseif (in_array(strtolower(pathinfo($target,PATHINFO_EXTENSION)), $p['allowed'])) { + $files[] = $target; + } + + } + +if (!@$files) { echo "\nALERT:No supported files|Supported filetypes: ".implode(", ",$p['allowed'])."\n"; die; } else { sort($files); } +if (count($files) > $p['limit']) { if (!ask("Really process ".count($files)." files?")) { die; } } + +// Process files + +$i = 0; + +foreach ($files as $file) { + + unset($parts, $ext, $mparts, $z, $e); + + $parts = pathinfo($file); + $ext = strtolower($parts['extension']); + + $z['%FILE'] = $parts['filename']; + $z['%EXT'] = $parts['extension']; + + if ($p['debug_verify_names']) { + $p['maxlength'] = 999; + $z['%FILE'] = substr($parts['filename'],-8,8); + echo "[VERIFY]"; + } else { + echo $parts['basename']; + } + + if (strlen($parts['basename']) > $p['maxlength']) { + + echo "-> SKIP\n"; + + } else { + + $raw = eval("return ".shell_exec($p['etbin']." -php ".escapeshellarg($file))); + if (is_array($raw[0])) { $e = $raw[0]; } else { echo "Exiftool error\n"; die; } + + $z['%DTO'] = formatDateTime($e); + $z['%DUR'] = formatDuration($e); + $z['%SW'] = formatSoftware($e,$p); + $z['%MODEL'] = formatModel($e); + $z['%PLACE'] = formatPlace($e,$p); + + if (in_array($ext,$p['pfiletypes'])) { + $newname = str_replace(array_keys($z),array_values($z),$p['pt']); + } elseif (in_array($ext,$p['vfiletypes'])) { + $newname = str_replace(array_keys($z),array_values($z),$p['vt']); + } + + $figureoutabetterwaytodothis_a = array("____","___","__"); + $figureoutabetterwaytodothis_b = array("_","_","_"); + $newname = str_replace($figureoutabetterwaytodothis_a,$figureoutabetterwaytodothis_b,$newname); + + if (!@$newname) { + echo " -> ERROR\n"; + } else { + echo " -> ".$newname."\n"; + if ($p['debug_verify_names']) { + if ($parts['basename'] != $newname) { + echo "FAIL"; + echo "\nALERT:Filename changed|".$parts['basename']." VS ".$newname;; + } else { + echo "pass"; + } + } else { + if (!@$p['dry_run']) { + rename($file,$parts['dirname']."/".$newname); + } else { + echo "\nDisable dry run to rename files!"; + } + } + } + + echo updateProgress($i,count($files)); + $i++; + + } + } + +?> diff --git a/RollupPrefs.php b/RollupPrefs.php new file mode 100755 index 0000000..7e147a2 --- /dev/null +++ b/RollupPrefs.php @@ -0,0 +1,121 @@ + ["verify_peer" => false, "verify_peer_name" => false] ]); + $result = json_decode(file_get_contents("https://maps.googleapis.com/maps/api/geocode/json?key=".$key."&latlng=0,4", false, $streamContext)); + if ($result->status == "REQUEST_DENIED") { return 0; } else { return 1; } + } + +function makeWindowString($p) { + + $conf = " + # Set window title + *.title = Preferences + *.floating = 1 + + # Allowed Filetypes + allowed.type = textfield + allowed.mandatory = 1 + allowed.label = Allowed filetypes + allowed.default = ".implode(", ",$p['allowed'])." + allowed.placeholder = jpg, jpeg, png, mov, mp4, m4v, dng, heic + allowed.width = 400 + allowed.tooltip = Process files matching these extensions + + # Photo template + pt.type = textfield + pt.mandatory = 1 + pt.label = Photo renaming template + pt.default = ".$p['pt']." + pt.placeholder = %DTO_%MODEL_%PLACE_%SW_%FILE.%EXT + pt.width = 400 + pt.tooltip = Rename photo files according to this template + + # Video template + vt.type = textfield + vt.mandatory = 1 + vt.label = Video renaming template + vt.default = ".$p['vt']." + vt.placeholder = %DTO_%DUR_%PLACE_%SW_%FILE.%EXT + vt.width = 400 + vt.tooltip = Rename video files according to this template + + # API Key + key.type = textfield + key.label = Google Geocoding API key + key.default = ".$p['key']." + key.placeholder = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + key.width = 400 + key.tooltip = An API key is required for reverse-geocoding + + # Limit + limit.type = textfield + limit.mandatory = 1 + limit.label = Max files to process without warning + limit.default = ".$p['limit']." + limit.width = 80 + + # Maxlength + maxlength.type = textfield + maxlength.mandatory = 1 + maxlength.label = Max input filename length + maxlength.tooltip = Use this value to skip files that have already been renamed + maxlength.default = ".$p['maxlength']." + maxlength.width = 80 + + # Dry run + dry_run.type = checkbox + dry_run.label = Dry run (don't rename files) + dry_run.default = ".$p['dry_run']." + + # Buttons + #gb.type = button + #gb.label = Detect Key... + cb.type = cancelbutton + db.type = defaultbutton + db.label = Save + "; + + return $conf; + + } + +// Read Prefs + +$prefs_file = "/Users/".get_current_user()."/Library/Preferences/org.profiteroles.Rollup.php"; +$p = unserialize(file_get_contents($prefs_file)); + +// Launch Pashua and parse results + +$path = __DIR__."/Pashua.app/Contents/MacOS/Pashua"; +$raw = shell_exec("echo ".escapeshellarg(makeWindowString($p))." | ".escapeshellarg($path)." - "); +$result = array(); +foreach (explode("\n", $raw) as $line) { + preg_match('/^(\w+)=(.*)$/', $line, $matches); + if (empty($matches) or empty($matches[1])) { + continue; + } + $result[$matches[1]] = $matches[2]; + } + +// User cancelled + +if (@$result['cb']) { echo 1; die; } + +// Write Prefs + +$result['allowed'] = explode(", ",$result['allowed']); +file_put_contents($prefs_file,serialize($result)); + +// Test API Key + +if (@$result['key'] && ($result['key'] != $p['key'])) { + echo testKey($result['key']); + } else { + echo 1; + } + +?> \ No newline at end of file diff --git a/functions.php b/functions.php new file mode 100755 index 0000000..28ea43a --- /dev/null +++ b/functions.php @@ -0,0 +1,292 @@ +&1"); + if (strpos($result,"canceled") !== false) { + return 0; + } else { + return 1; + } + } + +function askMulti($string, $buttons) { + $buttonstring = "buttons {\\\"".implode("\\\", \\\"",$buttons)."\\\"} default button ".count($buttons); + $result = exec("osascript -e \"display dialog \\\"".$string."\\\" ".$buttonstring."\" | cut -f2 -d':'"); + return array_search($result,$buttons); + } + +function updateProgress($num = 0, $total = 100) { + return "\nPROGRESS:".floor(($num/$total)*100)."\n"; + } + +function get_gps($gps_pos) { + +# convert: +# 40 deg 44' 49.36" N, 73 deg 56' 28.18" W +# to: +# 40.7470444,-073.9411611 + + $parts = explode(" ",str_replace(array("deg ",",","'","\""),"",$gps_pos)); + + $lat_deg = $parts[0]; + $lat_min = $parts[1]; + $lat_sec = $parts[2]; + $lat_dir = $parts[3]; + + $lon_deg = $parts[4]; + $lon_min = $parts[5]; + $lon_sec = $parts[6]; + $lon_dir = $parts[7]; + + if ($lat_dir == "N") { + $lat_sin = "+"; + } else { + $lat_sin = "-"; + } + + if ($lon_dir == "E") { + $lon_sin = "+"; + } else { + $lon_sin = "-"; + } + + $latitiude = $lat_sin.($lat_deg+($lat_min/60)+($lat_sec/3600)); + $longitude = $lon_sin.($lon_deg+($lon_min/60)+($lon_sec/3600)); + + return $latitiude.",".$longitude; + + } + +function get_formatted_place($gps,$key) { + + $streamContext = stream_context_create([ "ssl" => ["verify_peer" => false, "verify_peer_name" => false] ]); + $raw = file_get_contents("https://maps.googleapis.com/maps/api/geocode/json?key=".$key."&latlng=".$gps, false, $streamContext); + $return = json_decode($raw); + + if (!is_array($return->results[0]->address_components)) { return false; } + + foreach ($return->results[0]->address_components as $key => $value) { + $out[$value->types[0]] = $value->short_name; + } + + $country = $out['country']; + $state = $out['administrative_area_level_1']; + if ($out['locality'] == "NY" | $out['locality'] == "LA") { + $place = $out['neighborhood']; + } else { + $place = $out['locality']; + } + $addr = $out['route']; + + if ($country) { + $dirty[] = $country; + } + if ($state) { + $dirty[] = $state; + } + if ($place) { + $dirty[] = $place; + } + if ($addr) { + $dirty[] = $addr; + } + + foreach ($dirty as $item) { + $clean[] = remove_accents(str_replace(" ","_",$item)); + } + + return str_replace(array("_/_","'","\"",".",",","/"), array("-","","","","","-"), implode($clean,"-")); + + } + +/** + * Converts all accent characters to ASCII characters. + * + * If there are no accent characters, then the string given is just returned. + * + * @since 1.2.1 + * + * @param string $string Text that might have accent characters + * @return string Filtered string with replaced "nice" characters. + */ + + function reset_mbstring_encoding() { + mbstring_binary_safe_encoding( true ); + } + + function mbstring_binary_safe_encoding( $reset = false ) { + static $encodings = array(); + static $overloaded = null; + + if ( is_null( $overloaded ) ) + $overloaded = function_exists( 'mb_internal_encoding' ) && ( ini_get( 'mbstring.func_overload' ) & 2 ); + + if ( false === $overloaded ) + return; + + if ( ! $reset ) { + $encoding = mb_internal_encoding(); + array_push( $encodings, $encoding ); + mb_internal_encoding( 'ISO-8859-1' ); + } + + if ( $reset && $encodings ) { + $encoding = array_pop( $encodings ); + mb_internal_encoding( $encoding ); + } + } + + function seems_utf8( $str ) { + mbstring_binary_safe_encoding(); + $length = strlen($str); + reset_mbstring_encoding(); + for ($i=0; $i < $length; $i++) { + $c = ord($str[$i]); + if ($c < 0x80) $n = 0; // 0bbbbbbb + elseif (($c & 0xE0) == 0xC0) $n=1; // 110bbbbb + elseif (($c & 0xF0) == 0xE0) $n=2; // 1110bbbb + elseif (($c & 0xF8) == 0xF0) $n=3; // 11110bbb + elseif (($c & 0xFC) == 0xF8) $n=4; // 111110bb + elseif (($c & 0xFE) == 0xFC) $n=5; // 1111110b + else return false; // Does not match any model + for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ? + if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) + return false; + } + } + return true; + } + +function remove_accents($string) { + if ( !preg_match('/[\x80-\xff]/', $string) ) + return $string; + + if (seems_utf8($string)) { + $chars = array( + // Decompositions for Latin-1 Supplement + chr(194).chr(170) => 'a', chr(194).chr(186) => 'o', chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C', chr(195).chr(136) => 'E', chr(195).chr(137) => 'E', chr(195).chr(138) => 'E', chr(195).chr(139) => 'E', chr(195).chr(140) => 'I', chr(195).chr(141) => 'I', chr(195).chr(142) => 'I', chr(195).chr(143) => 'I', chr(195).chr(144) => 'D', chr(195).chr(145) => 'N', chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', chr(195).chr(158) => 'TH',chr(195).chr(159) => 's', chr(195).chr(160) => 'a', chr(195).chr(161) => 'a', chr(195).chr(162) => 'a', chr(195).chr(163) => 'a', chr(195).chr(164) => 'a', chr(195).chr(165) => 'a', chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c', chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', chr(195).chr(176) => 'd', chr(195).chr(177) => 'n', chr(195).chr(178) => 'o', chr(195).chr(179) => 'o', chr(195).chr(180) => 'o', chr(195).chr(181) => 'o', chr(195).chr(182) => 'o', chr(195).chr(184) => 'o', chr(195).chr(185) => 'u', chr(195).chr(186) => 'u', chr(195).chr(187) => 'u', chr(195).chr(188) => 'u', chr(195).chr(189) => 'y', chr(195).chr(190) => 'th', chr(195).chr(191) => 'y', chr(195).chr(152) => 'O', + // Decompositions for Latin Extended-A + chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', chr(197).chr(154) => 'S',chr(197).chr(155) => 's', chr(197).chr(156) => 'S',chr(197).chr(157) => 's', chr(197).chr(158) => 'S',chr(197).chr(159) => 's', chr(197).chr(160) => 'S', chr(197).chr(161) => 's', chr(197).chr(162) => 'T', chr(197).chr(163) => 't', chr(197).chr(164) => 'T', chr(197).chr(165) => 't', chr(197).chr(166) => 'T', chr(197).chr(167) => 't', chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', chr(197).chr(190) => 'z', chr(197).chr(191) => 's', + // Decompositions for Latin Extended-B + chr(200).chr(152) => 'S', chr(200).chr(153) => 's', chr(200).chr(154) => 'T', chr(200).chr(155) => 't', + // Euro Sign + chr(226).chr(130).chr(172) => 'E', + // GBP (Pound) Sign + chr(194).chr(163) => '', + // Vowels with diacritic (Vietnamese) + // unmarked + chr(198).chr(160) => 'O', chr(198).chr(161) => 'o', chr(198).chr(175) => 'U', chr(198).chr(176) => 'u', + // grave accent + chr(225).chr(186).chr(166) => 'A', chr(225).chr(186).chr(167) => 'a', chr(225).chr(186).chr(176) => 'A', chr(225).chr(186).chr(177) => 'a', chr(225).chr(187).chr(128) => 'E', chr(225).chr(187).chr(129) => 'e', chr(225).chr(187).chr(146) => 'O', chr(225).chr(187).chr(147) => 'o', chr(225).chr(187).chr(156) => 'O', chr(225).chr(187).chr(157) => 'o', chr(225).chr(187).chr(170) => 'U', chr(225).chr(187).chr(171) => 'u', chr(225).chr(187).chr(178) => 'Y', chr(225).chr(187).chr(179) => 'y', + // hook + chr(225).chr(186).chr(162) => 'A', chr(225).chr(186).chr(163) => 'a', chr(225).chr(186).chr(168) => 'A', chr(225).chr(186).chr(169) => 'a', chr(225).chr(186).chr(178) => 'A', chr(225).chr(186).chr(179) => 'a', chr(225).chr(186).chr(186) => 'E', chr(225).chr(186).chr(187) => 'e', chr(225).chr(187).chr(130) => 'E', chr(225).chr(187).chr(131) => 'e', chr(225).chr(187).chr(136) => 'I', chr(225).chr(187).chr(137) => 'i', chr(225).chr(187).chr(142) => 'O', chr(225).chr(187).chr(143) => 'o', chr(225).chr(187).chr(148) => 'O', chr(225).chr(187).chr(149) => 'o', chr(225).chr(187).chr(158) => 'O', chr(225).chr(187).chr(159) => 'o', chr(225).chr(187).chr(166) => 'U', chr(225).chr(187).chr(167) => 'u', chr(225).chr(187).chr(172) => 'U', chr(225).chr(187).chr(173) => 'u', chr(225).chr(187).chr(182) => 'Y', chr(225).chr(187).chr(183) => 'y', + // tilde + chr(225).chr(186).chr(170) => 'A', chr(225).chr(186).chr(171) => 'a', chr(225).chr(186).chr(180) => 'A', chr(225).chr(186).chr(181) => 'a', chr(225).chr(186).chr(188) => 'E', chr(225).chr(186).chr(189) => 'e', chr(225).chr(187).chr(132) => 'E', chr(225).chr(187).chr(133) => 'e', chr(225).chr(187).chr(150) => 'O', chr(225).chr(187).chr(151) => 'o', chr(225).chr(187).chr(160) => 'O', chr(225).chr(187).chr(161) => 'o', chr(225).chr(187).chr(174) => 'U', chr(225).chr(187).chr(175) => 'u', chr(225).chr(187).chr(184) => 'Y', chr(225).chr(187).chr(185) => 'y', + // acute accent + chr(225).chr(186).chr(164) => 'A', chr(225).chr(186).chr(165) => 'a', chr(225).chr(186).chr(174) => 'A', chr(225).chr(186).chr(175) => 'a', chr(225).chr(186).chr(190) => 'E', chr(225).chr(186).chr(191) => 'e', chr(225).chr(187).chr(144) => 'O', chr(225).chr(187).chr(145) => 'o', chr(225).chr(187).chr(154) => 'O', chr(225).chr(187).chr(155) => 'o', chr(225).chr(187).chr(168) => 'U', chr(225).chr(187).chr(169) => 'u', + // dot below + chr(225).chr(186).chr(160) => 'A', chr(225).chr(186).chr(161) => 'a', chr(225).chr(186).chr(172) => 'A', chr(225).chr(186).chr(173) => 'a', chr(225).chr(186).chr(182) => 'A', chr(225).chr(186).chr(183) => 'a', chr(225).chr(186).chr(184) => 'E', chr(225).chr(186).chr(185) => 'e', chr(225).chr(187).chr(134) => 'E', chr(225).chr(187).chr(135) => 'e', chr(225).chr(187).chr(138) => 'I', chr(225).chr(187).chr(139) => 'i', chr(225).chr(187).chr(140) => 'O', chr(225).chr(187).chr(141) => 'o', chr(225).chr(187).chr(152) => 'O', chr(225).chr(187).chr(153) => 'o', chr(225).chr(187).chr(162) => 'O', chr(225).chr(187).chr(163) => 'o', chr(225).chr(187).chr(164) => 'U', chr(225).chr(187).chr(165) => 'u', chr(225).chr(187).chr(176) => 'U', chr(225).chr(187).chr(177) => 'u', chr(225).chr(187).chr(180) => 'Y', chr(225).chr(187).chr(181) => 'y', + // Vowels with diacritic (Chinese, Hanyu Pinyin) + chr(201).chr(145) => 'a', + // macron + chr(199).chr(149) => 'U', chr(199).chr(150) => 'u', + // acute accent + chr(199).chr(151) => 'U', chr(199).chr(152) => 'u', + // caron + chr(199).chr(141) => 'A', chr(199).chr(142) => 'a', chr(199).chr(143) => 'I', chr(199).chr(144) => 'i', chr(199).chr(145) => 'O', chr(199).chr(146) => 'o', chr(199).chr(147) => 'U', chr(199).chr(148) => 'u', chr(199).chr(153) => 'U', chr(199).chr(154) => 'u', + // grave accent + chr(199).chr(155) => 'U', chr(199).chr(156) => 'u', + ); + + // Used for locale-specific rules + $locale = "en_US"; + + if ( 'de_DE' == $locale ) { + $chars[ chr(195).chr(132) ] = 'Ae'; $chars[ chr(195).chr(164) ] = 'ae'; $chars[ chr(195).chr(150) ] = 'Oe'; $chars[ chr(195).chr(182) ] = 'oe'; $chars[ chr(195).chr(156) ] = 'Ue'; $chars[ chr(195).chr(188) ] = 'ue'; $chars[ chr(195).chr(159) ] = 'ss'; + } elseif ( 'da_DK' === $locale ) { + $chars[ chr(195).chr(134) ] = 'Ae'; $chars[ chr(195).chr(166) ] = 'ae'; $chars[ chr(195).chr(152) ] = 'Oe'; $chars[ chr(195).chr(184) ] = 'oe'; $chars[ chr(195).chr(133) ] = 'Aa'; $chars[ chr(195).chr(165) ] = 'aa'; + } + + $string = strtr($string, $chars); + } else { + // Assume ISO-8859-1 if not UTF-8 + $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158).chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194).chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202).chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210).chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218).chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227).chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235).chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243).chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251).chr(252).chr(253).chr(255); + + $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy"; + + $string = strtr($string, $chars['in'], $chars['out']); + $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254)); + $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); + $string = str_replace($double_chars['in'], $double_chars['out'], $string); + } + + return $string; + +} + +?> \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..a3ae858 Binary files /dev/null and b/icon.png differ diff --git a/prefs.php b/prefs.php new file mode 100644 index 0000000..9496dd7 --- /dev/null +++ b/prefs.php @@ -0,0 +1 @@ +a:8:{s:7:"allowed";a:8:{i:0;s:3:"jpg";i:1;s:4:"jpeg";i:2;s:3:"png";i:3;s:3:"mov";i:4;s:3:"mp4";i:5;s:3:"m4v";i:6;s:3:"dng";i:7;s:4:"heic";}s:5:"limit";i:2000;s:9:"maxlength";i:30;s:6:"review";i:1;s:2:"pt";s:33:"%DTO_%MODEL_%PLACE_%SW_%FILE.%EXT";s:2:"vt";s:31:"%DTO_%DUR_%PLACE_%SW_%FILE.%EXT";s:3:"key";s:0:"";s:7:"dry_run";i:1;} \ No newline at end of file diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..60a2d3e --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.4.0 \ No newline at end of file