├── .github ├── CONTRIBUTING.md └── ISSUE_TEMPLATE.md ├── .gitignore ├── README.md ├── Youdao.alfredworkflow ├── icon.png ├── info.plist └── workflows.php /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **PHP Version** 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Youdao.alfredworkflow 2 | ====================== 3 | 4 | 有道翻译 workflow for Alfred v2 5 | 6 | 默认快捷键 "f",多个翻译结果,回车或直接点击将结果复制到剪切板。 7 | 8 | 通过输入"ff" 即可以在有道词典的页面中查询输入的单词。方便用户查看更详细的信息以及将单词加入生词本操作。 9 | 10 | ## [下载](https://github.com/samqiu/Youdao.alfredworkflow/releases) 11 | 12 | ## Demo 13 | 14 | 15 | ### 多个翻译结果,回车或直接点击将结果复制到剪切板。 16 | 17 | ![f](https://f.cloud.github.com/assets/290421/301401/9402b7de-95d1-11e2-9859-f267cf16642d.gif) 18 | 19 | 20 | ### 在有道词典的页面中查询输入的单词 21 | 22 | ![ff](https://cloud.githubusercontent.com/assets/1857448/4097814/71fff75a-2fe5-11e4-842d-679bdb2ad185.gif) 23 | -------------------------------------------------------------------------------- /Youdao.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samqiu/Youdao.alfredworkflow/5e2bf2f515ac1dc5c5cfbb70e02a7d21ee4f5046/Youdao.alfredworkflow -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samqiu/Youdao.alfredworkflow/5e2bf2f515ac1dc5c5cfbb70e02a7d21ee4f5046/icon.png -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | com.samqiu.workflows.youdao 7 | connections 8 | 9 | 160E3B74-C1C8-4F4F-8E6E-A64B1CDC48FE 10 | 11 | 12 | destinationuid 13 | 96159F02-1880-4FCF-9B30-02652898058D 14 | modifiers 15 | 0 16 | modifiersubtext 17 | 18 | 19 | 20 | destinationuid 21 | B831172E-379D-444D-AECE-4BA8D2A662DB 22 | modifiers 23 | 0 24 | modifiersubtext 25 | 26 | 27 | 28 | 29 | createdby 30 | samqiu 31 | description 32 | 有道翻译 33 | disabled 34 | 35 | name 36 | Youdao 37 | objects 38 | 39 | 40 | config 41 | 42 | autopaste 43 | 44 | clipboardtext 45 | {query} 46 | 47 | type 48 | alfred.workflow.output.clipboard 49 | uid 50 | 96159F02-1880-4FCF-9B30-02652898058D 51 | version 52 | 0 53 | 54 | 55 | config 56 | 57 | argumenttype 58 | 0 59 | escaping 60 | 62 61 | keyword 62 | f 63 | runningsubtext 64 | 翻译中…… 65 | script 66 | require_once('workflows.php'); 67 | 68 | $w = new Workflows(); 69 | $query = urlencode("{query}"); 70 | 71 | $url = 'http://fanyi.youdao.com/openapi.do?keyfrom=Youdao-workflow&key=1738796775&type=data&doctype=json&version=1.1&q=' . $query; 72 | $results = json_decode($w->request($url)); 73 | 74 | foreach ($results->translation as $result) { 75 | $w->result($result, $result, $result, $result, 'icon.png', ''); 76 | } 77 | 78 | if (count($results->web) > 0) { 79 | foreach ($results->web as $web) { 80 | $w->result('', implode(', ', $web->value), implode(', ', $web->value), $web->key, 'icon.png'); 81 | } 82 | } 83 | 84 | if (count($w->results()) == 0) { 85 | $w->result('', '', '没有结果', '', 'icon.png', 'no'); 86 | } 87 | 88 | echo $w->toxml(); 89 | subtext 90 | 请输入需要翻译的中英文 91 | title 92 | 有道翻译 93 | type 94 | 1 95 | withspace 96 | 97 | 98 | type 99 | alfred.workflow.input.scriptfilter 100 | uid 101 | 160E3B74-C1C8-4F4F-8E6E-A64B1CDC48FE 102 | version 103 | 0 104 | 105 | 106 | config 107 | 108 | lastpathcomponent 109 | 110 | onlyshowifquerypopulated 111 | 112 | output 113 | 0 114 | removeextension 115 | 116 | sticky 117 | 118 | text 119 | {query} 120 | title 121 | Translation result copied 122 | 123 | type 124 | alfred.workflow.output.notification 125 | uid 126 | B831172E-379D-444D-AECE-4BA8D2A662DB 127 | version 128 | 0 129 | 130 | 131 | readme 132 | 133 | uidata 134 | 135 | 160E3B74-C1C8-4F4F-8E6E-A64B1CDC48FE 136 | 137 | ypos 138 | 110 139 | 140 | 96159F02-1880-4FCF-9B30-02652898058D 141 | 142 | ypos 143 | 40 144 | 145 | B831172E-379D-444D-AECE-4BA8D2A662DB 146 | 147 | ypos 148 | 160 149 | 150 | 151 | webaddress 152 | http://samqiu.com/posts/52-youdao-workflow-for-alfred-v2 153 | 154 | 155 | -------------------------------------------------------------------------------- /workflows.php: -------------------------------------------------------------------------------- 1 | path = exec('pwd'); 31 | $this->home = exec('printf $HOME'); 32 | 33 | if ( file_exists( 'info.plist' ) ): 34 | $this->bundle = $this->get( 'bundleid', 'info.plist' ); 35 | endif; 36 | 37 | if ( !is_null( $bundleid ) ): 38 | $this->bundle = $bundleid; 39 | endif; 40 | 41 | $this->cache = $this->home. "/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/".$this->bundle; 42 | $this->data = $this->home. "/Library/Application Support/Alfred 2/Workflow Data/".$this->bundle; 43 | 44 | if ( !file_exists( $this->cache ) ): 45 | exec("mkdir '".$this->cache."'"); 46 | endif; 47 | 48 | if ( !file_exists( $this->data ) ): 49 | exec("mkdir '".$this->data."'"); 50 | endif; 51 | 52 | $this->results = array(); 53 | } 54 | 55 | /** 56 | * Description: 57 | * Accepts no parameter and returns the value of the bundle id for the current workflow. 58 | * If no value is available, then false is returned. 59 | * 60 | * @param none 61 | * @return false if not available, bundle id value if available. 62 | */ 63 | public function bundle() 64 | { 65 | if ( is_null( $this->bundle ) ): 66 | return false; 67 | else: 68 | return $this->bundle; 69 | endif; 70 | } 71 | 72 | /** 73 | * Description: 74 | * Accepts no parameter and returns the value of the path to the cache directory for your 75 | * workflow if it is available. Returns false if the value isn't available. 76 | * 77 | * @param none 78 | * @return false if not available, path to the cache directory for your workflow if available. 79 | */ 80 | public function cache() 81 | { 82 | if ( is_null( $this->bundle ) ): 83 | return false; 84 | else: 85 | if ( is_null( $this->cache ) ): 86 | return false; 87 | else: 88 | return $this->cache; 89 | endif; 90 | endif; 91 | } 92 | 93 | /** 94 | * Description: 95 | * Accepts no parameter and returns the value of the path to the storage directory for your 96 | * workflow if it is available. Returns false if the value isn't available. 97 | * 98 | * @param none 99 | * @return false if not available, path to the storage directory for your workflow if available. 100 | */ 101 | public function data() 102 | { 103 | if ( is_null( $this->bundle ) ): 104 | return false; 105 | else: 106 | if ( is_null( $this->data ) ): 107 | return false; 108 | else: 109 | return $this->data; 110 | endif; 111 | endif; 112 | } 113 | 114 | /** 115 | * Description: 116 | * Accepts no parameter and returns the value of the path to the current directory for your 117 | * workflow if it is available. Returns false if the value isn't available. 118 | * 119 | * @param none 120 | * @return false if not available, path to the current directory for your workflow if available. 121 | */ 122 | public function path() 123 | { 124 | if ( is_null( $this->path ) ): 125 | return false; 126 | else: 127 | return $this->path; 128 | endif; 129 | } 130 | 131 | /** 132 | * Description: 133 | * Accepts no parameter and returns the value of the home path for the current user 134 | * Returns false if the value isn't available. 135 | * 136 | * @param none 137 | * @return false if not available, home path for the current user if available. 138 | */ 139 | public function home() 140 | { 141 | if ( is_null( $this->home ) ): 142 | return false; 143 | else: 144 | return $this->home; 145 | endif; 146 | } 147 | 148 | /** 149 | * Description: 150 | * Returns an array of available result items 151 | * 152 | * @param none 153 | * @return array - list of result items 154 | */ 155 | public function results() 156 | { 157 | return $this->results; 158 | } 159 | 160 | /** 161 | * Description: 162 | * Convert an associative array into XML format 163 | * 164 | * @param $a - An associative array to convert 165 | * @param $format - format of data being passed (json or array), defaults to array 166 | * @return - XML string representation of the array 167 | */ 168 | public function toxml( $a=null, $format='array' ) { 169 | 170 | if ( $format == 'json' ): 171 | $a = json_decode( $a, TRUE ); 172 | endif; 173 | 174 | if ( is_null( $a ) && !empty( $this->results ) ): 175 | $a = $this->results; 176 | elseif ( is_null( $a ) && empty( $this->results ) ): 177 | return false; 178 | endif; 179 | 180 | $items = new SimpleXMLElement(""); // Create new XML element 181 | 182 | foreach( $a as $b ): // Lop through each object in the array 183 | $c = $items->addChild( 'item' ); // Add a new 'item' element for each object 184 | $c_keys = array_keys( $b ); // Grab all the keys for that item 185 | foreach( $c_keys as $key ): // For each of those keys 186 | if ( $key == 'uid' ): 187 | $c->addAttribute( 'uid', $b[$key] ); 188 | elseif ( $key == 'arg' ): 189 | $c->addAttribute( 'arg', $b[$key] ); 190 | elseif ( $key == 'type' ): 191 | $c->addAttribute( 'type', $b[$key] ); 192 | elseif ( $key == 'valid' ): 193 | if ( $b[$key] == 'yes' || $b[$key] == 'no' ): 194 | $c->addAttribute( 'valid', $b[$key] ); 195 | endif; 196 | elseif ( $key == 'autocomplete' ): 197 | $c->addAttribute( 'autocomplete', $b[$key] ); 198 | elseif ( $key == 'icon' ): 199 | if ( substr( $b[$key], 0, 9 ) == 'fileicon:' ): 200 | $val = substr( $b[$key], 9 ); 201 | $c->$key = $val; 202 | $c->$key->addAttribute( 'type', 'fileicon' ); 203 | elseif ( substr( $b[$key], 0, 9 ) == 'filetype:' ): 204 | $val = substr( $b[$key], 9 ); 205 | $c->$key = $val; 206 | $c->$key->addAttribute( 'type', 'filetype' ); 207 | else: 208 | $c->$key = $b[$key]; 209 | endif; 210 | else: 211 | $c->$key = $b[$key]; 212 | endif; 213 | endforeach; 214 | endforeach; 215 | 216 | return $items->asXML(); // Return XML string representation of the array 217 | 218 | } 219 | 220 | /** 221 | * Description: 222 | * Remove all items from an associative array that do not have a value 223 | * 224 | * @param $a - Associative array 225 | * @return bool 226 | */ 227 | private function empty_filter( $a ) { 228 | if ( $a == '' || $a == null ): // if $a is empty or null 229 | return false; // return false, else, return true 230 | else: 231 | return true; 232 | endif; 233 | } 234 | 235 | /** 236 | * Description: 237 | * Save values to a specified plist. If the first parameter is an associative 238 | * array, then the second parameter becomes the plist file to save to. If the 239 | * first parameter is string, then it is assumed that the first parameter is 240 | * the label, the second parameter is the value, and the third parameter is 241 | * the plist file to save the data to. 242 | * 243 | * @param $a - associative array of values to save 244 | * @param $b - the value of the setting 245 | * @param $c - the plist to save the values into 246 | * @return string - execution output 247 | */ 248 | public function set( $a=null, $b=null, $c=null ) 249 | { 250 | if ( is_array( $a ) ): 251 | if ( file_exists( $b ) ): 252 | if ( file_exists( $this->path.'/'.$b ) ): 253 | $b = $this->path.'/'.$b; 254 | endif; 255 | elseif ( file_exists( $this->data."/".$b ) ): 256 | $b = $this->data."/".$b; 257 | elseif ( file_exists( $this->cache."/".$b ) ): 258 | $b = $this->cache."/".$b; 259 | else: 260 | $b = $this->data."/".$b; 261 | endif; 262 | else: 263 | if ( file_exists( $c ) ): 264 | if ( file_exists( $this->path.'/'.$c ) ): 265 | $c = $this->path.'/'.$c; 266 | endif; 267 | elseif ( file_exists( $this->data."/".$c ) ): 268 | $c = $this->data."/".$c; 269 | elseif ( file_exists( $this->cache."/".$c ) ): 270 | $c = $this->cache."/".$c; 271 | else: 272 | $c = $this->data."/".$c; 273 | endif; 274 | endif; 275 | 276 | if ( is_array( $a ) ): 277 | foreach( $a as $k => $v ): 278 | exec( 'defaults write "'. $b .'" '. $k .' "'. $v .'"'); 279 | endforeach; 280 | else: 281 | exec( 'defaults write "'. $c .'" '. $a .' "'. $b .'"'); 282 | endif; 283 | } 284 | 285 | /** 286 | * Description: 287 | * Read a value from the specified plist 288 | * 289 | * @param $a - the value to read 290 | * @param $b - plist to read the values from 291 | * @return bool false if not found, string if found 292 | */ 293 | public function get( $a, $b ) { 294 | 295 | if ( file_exists( $b ) ): 296 | if ( file_exists( $this->path.'/'.$b ) ): 297 | $b = $this->path.'/'.$b; 298 | endif; 299 | elseif ( file_exists( $this->data."/".$b ) ): 300 | $b = $this->data."/".$b; 301 | elseif ( file_exists( $this->cache."/".$b ) ): 302 | $b = $this->cache."/".$b; 303 | else: 304 | return false; 305 | endif; 306 | 307 | exec( 'defaults read "'. $b .'" '.$a, $out ); // Execute system call to read plist value 308 | 309 | if ( $out == "" ): 310 | return false; 311 | endif; 312 | 313 | $out = $out[0]; 314 | return $out; // Return item value 315 | } 316 | 317 | /** 318 | * Description: 319 | * Read data from a remote file/url, essentially a shortcut for curl 320 | * 321 | * @param $url - URL to request 322 | * @param $options - Array of curl options 323 | * @return result from curl_exec 324 | */ 325 | public function request( $url=null, $options=null ) 326 | { 327 | if ( is_null( $url ) ): 328 | return false; 329 | endif; 330 | 331 | $defaults = array( // Create a list of default curl options 332 | CURLOPT_RETURNTRANSFER => true, // Returns the result as a string 333 | CURLOPT_URL => $url, // Sets the url to request 334 | CURLOPT_FRESH_CONNECT => true 335 | ); 336 | 337 | if ( $options ): 338 | foreach( $options as $k => $v ): 339 | $defaults[$k] = $v; 340 | endforeach; 341 | endif; 342 | 343 | array_filter( $defaults, // Filter out empty options from the array 344 | array( $this, 'empty_filter' ) ); 345 | 346 | $ch = curl_init(); // Init new curl object 347 | curl_setopt_array( $ch, $defaults ); // Set curl options 348 | $out = curl_exec( $ch ); // Request remote data 349 | $err = curl_error( $ch ); 350 | curl_close( $ch ); // End curl request 351 | 352 | if ( $err ): 353 | return $err; 354 | else: 355 | return $out; 356 | endif; 357 | } 358 | 359 | /** 360 | * Description: 361 | * Allows searching the local hard drive using mdfind 362 | * 363 | * @param $query - search string 364 | * @return array - array of search results 365 | */ 366 | public function mdfind( $query ) 367 | { 368 | exec('mdfind "'.$query.'"', $results); 369 | return $results; 370 | } 371 | 372 | /** 373 | * Description: 374 | * Accepts data and a string file name to store data to local file as cache 375 | * 376 | * @param array - data to save to file 377 | * @param file - filename to write the cache data to 378 | * @return none 379 | */ 380 | public function write( $a, $b ) 381 | { 382 | if ( file_exists( $b ) ): 383 | if ( file_exists( $this->path.'/'.$b ) ): 384 | $b = $this->path.'/'.$b; 385 | endif; 386 | elseif ( file_exists( $this->data."/".$b ) ): 387 | $b = $this->data."/".$b; 388 | elseif ( file_exists( $this->cache."/".$b ) ): 389 | $b = $this->cache."/".$b; 390 | else: 391 | $b = $this->data."/".$b; 392 | endif; 393 | 394 | if ( is_array( $a ) ): 395 | $a = json_encode( $a ); 396 | file_put_contents( $b, $a ); 397 | return true; 398 | elseif ( is_string( $a ) ): 399 | file_put_contents( $b, $a ); 400 | return true; 401 | else: 402 | return false; 403 | endif; 404 | } 405 | 406 | /** 407 | * Description: 408 | * Returns data from a local cache file 409 | * 410 | * @param file - filename to read the cache data from 411 | * @return false if the file cannot be found, the file data if found. If the file 412 | * format is json encoded, then a json object is returned. 413 | */ 414 | public function read( $a ) 415 | { 416 | if ( file_exists( $a ) ): 417 | if ( file_exists( $this->path.'/'.$a ) ): 418 | $a = $this->path.'/'.$a; 419 | endif; 420 | elseif ( file_exists( $this->data."/".$a ) ): 421 | $a = $this->data."/".$a; 422 | elseif ( file_exists( $this->cache."/".$a ) ): 423 | $a = $this->cache."/".$a; 424 | else: 425 | return false; 426 | endif; 427 | 428 | $out = file_get_contents( $a ); 429 | if ( !is_null( json_decode( $out ) ) ): 430 | $out = json_decode( $out ); 431 | endif; 432 | 433 | return $out; 434 | } 435 | 436 | /** 437 | * Description: 438 | * Helper function that just makes it easier to pass values into a function 439 | * and create an array result to be passed back to Alfred 440 | * 441 | * @param $uid - the uid of the result, should be unique 442 | * @param $arg - the argument that will be passed on 443 | * @param $title - The title of the result item 444 | * @param $sub - The subtitle text for the result item 445 | * @param $icon - the icon to use for the result item 446 | * @param $valid - sets whether the result item can be actioned 447 | * @param $auto - the autocomplete value for the result item 448 | * @return array - array item to be passed back to Alfred 449 | */ 450 | public function result( $uid, $arg, $title, $sub, $icon, $valid='yes', $auto=null, $type=null ) 451 | { 452 | $temp = array( 453 | 'uid' => $uid, 454 | 'arg' => $arg, 455 | 'title' => $title, 456 | 'subtitle' => $sub, 457 | 'icon' => $icon, 458 | 'valid' => $valid, 459 | 'autocomplete' => $auto, 460 | 'type' => $type 461 | ); 462 | 463 | if ( is_null( $type ) ): 464 | unset( $temp['type'] ); 465 | endif; 466 | 467 | array_push( $this->results, $temp ); 468 | 469 | return $temp; 470 | } 471 | 472 | } --------------------------------------------------------------------------------