Play images and video from Synology PhotoStation server

tag.php 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. <?php
  2. require_once('tag.inc.php');
  3. require_once('albumutil.php');
  4. define('PHP_CMD', '/usr/bin/php -n -d extension_dir=/lib/php/modules -d extension=syno_compiler.so -d extension=curl.so -d extension=bz2.so');
  5. class TagAPI extends WebAPI {
  6. function __construct() {
  7. parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
  8. }
  9. protected function Process()
  10. {
  11. if (!strcasecmp($this->method, "list")) {
  12. $this->TagList();
  13. } elseif (!strcasecmp($this->method, "getinfo")) {
  14. $this->GetInfo();
  15. } else {
  16. csSYNOPhotoMisc::CheckSessionTimeOut(true);
  17. if (!strcasecmp($this->method, "create")) {
  18. $this->Create();
  19. } else if (!strcasecmp($this->method, "searchplace")) {
  20. $this->SearchPlace();
  21. } else {
  22. csSYNOPhotoMisc::CheckSessionTimeOut();
  23. if (!strcasecmp($this->method, "edit")) {
  24. $this->Edit();
  25. } elseif (!strcasecmp($this->method, "delete")) {
  26. $this->Delete();
  27. } elseif (!strcasecmp($this->method, "delete_unconfirmed_tag")) {
  28. $this->DeleteUnconfirmedTag();
  29. }
  30. }
  31. }
  32. }
  33. private function GetParams_SearchPlace()
  34. {
  35. if (!isset($_REQUEST['query']) || !isset($_REQUEST['location'])) {
  36. return false;
  37. }
  38. $params = array();
  39. $params['query'] = $_REQUEST['query'];
  40. $params['location'] = $_REQUEST['location'];
  41. $params['radius'] = (isset($_REQUEST['radius'])) ? $_REQUEST['radius'] : '500';
  42. $params['format'] = $_REQUEST['format'] === 'true';
  43. return $params;
  44. }
  45. private function GetParams_Edit()
  46. {
  47. if (!isset($_REQUEST['id']) || !isset($_REQUEST['name'])) {
  48. return false;
  49. }
  50. $arr = explode('tag_', $_REQUEST['id']);
  51. if (2 !== count($arr)) {
  52. return false;
  53. }
  54. $params['id'] = (int) $arr[1];
  55. $params['name'] = $_REQUEST['name'];
  56. // for geo tag
  57. $params['place_id'] = (isset($_REQUEST['place_id'])) ? $_REQUEST['place_id'] : null;
  58. $params['reference'] = (isset($_REQUEST['reference'])) ? $_REQUEST['reference'] : null;
  59. $params['lat'] = (isset($_REQUEST['lat'])) ? $_REQUEST['lat'] : null;
  60. $params['lng'] = (isset($_REQUEST['lng'])) ? $_REQUEST['lng'] : null;
  61. $params['address'] = (isset($_REQUEST['address'])) ? $_REQUEST['address'] : null;
  62. return $params;
  63. }
  64. private function GetParams_Create()
  65. {
  66. if (!isset($_REQUEST['name']) || !isset($_REQUEST['type'])) {
  67. return false;
  68. }
  69. $params['name'] = $_REQUEST['name'];
  70. $params['type'] = $_REQUEST['type'];
  71. if (!in_array($params['type'], array('people', 'geo', 'desc'))) {
  72. return false;
  73. }
  74. // for geo tag
  75. if ('geo' === $params['type']) {
  76. $params['place_id'] = (isset($_REQUEST['place_id'])) ? $_REQUEST['place_id'] : null;
  77. $params['reference'] = (isset($_REQUEST['reference'])) ? $_REQUEST['reference'] : null;
  78. $params['lat'] = (isset($_REQUEST['lat'])) ? $_REQUEST['lat'] : null;
  79. $params['lng'] = (isset($_REQUEST['lng'])) ? $_REQUEST['lng'] : null;
  80. $params['address'] = (isset($_REQUEST['address'])) ? $_REQUEST['address'] : null;
  81. }
  82. return $params;
  83. }
  84. private function GetParams_Info()
  85. {
  86. if (!isset($_REQUEST['id'])) {
  87. return false;
  88. }
  89. $params['id'] = array();
  90. $arr = explode(',', $_REQUEST['id']);
  91. foreach ($arr as $item) {
  92. $split = explode('tag_', $item);
  93. if (2 !== count($split)) {
  94. return false;
  95. }
  96. $params['id'][] = (int) $split[1];
  97. }
  98. $params['thumbnail_status'] = isset($_REQUEST['thumbnail_status']) ? $_REQUEST['thumbnail_status'] : 'false';
  99. if (!in_array($params['thumbnail_status'], array('true', 'false'))) {
  100. return false;
  101. }
  102. $params['additional'] = explode(',', $_REQUEST['additional']);
  103. return $params;
  104. }
  105. private function GetParams_List()
  106. {
  107. if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit']) || !isset($_REQUEST['type'])) {
  108. return false;
  109. }
  110. if (!is_numeric($_REQUEST['offset']) || !is_numeric($_REQUEST['limit'])) {
  111. return false;
  112. }
  113. $params['offset'] = (int)$_REQUEST['offset'];
  114. $params['limit'] = (int)$_REQUEST['limit'];
  115. $params['type'] = explode(',', $_REQUEST['type']);
  116. foreach ($params['type'] as $type) {
  117. if (!in_array($type, array('people', 'geo', 'desc'))) {
  118. return false;
  119. }
  120. }
  121. $params['thumbnail_status'] = isset($_REQUEST['thumbnail_status']) ? $_REQUEST['thumbnail_status'] : 'false';
  122. if (!in_array($params['thumbnail_status'], array('true', 'false'))) {
  123. return false;
  124. }
  125. $params['sort_by'] = (isset($_REQUEST['sort_by'])) ? $_REQUEST['sort_by'] : 'title';
  126. if ((null !== $params['sort_by']) && !in_array($params['sort_by'], array('title'))) {
  127. return false;
  128. }
  129. $params['sort_direction'] = (isset($_REQUEST['sort_direction'])) ? $_REQUEST['sort_direction'] : 'asc';
  130. if ((null !== $params['sort_direction']) && !in_array($params['sort_direction'], array('asc', 'desc'))) {
  131. return false;
  132. }
  133. $params['unconfirm_people_tag'] = isset($_REQUEST['unconfirm_people_tag']) ? $_REQUEST['unconfirm_people_tag'] : 'false';
  134. if (!in_array($params['unconfirm_people_tag'], array('true', 'false'))) {
  135. return false;
  136. }
  137. $params['additional'] = explode(',', $_REQUEST['additional']);
  138. return $params;
  139. }
  140. private function CategoryNameToNum($name)
  141. {
  142. if (!isset($name)) {
  143. return false;
  144. }
  145. switch ($name) {
  146. case 'people':
  147. $codeNum = Label::PEOPLE;
  148. break;
  149. case 'geo':
  150. $codeNum = Label::GEO;
  151. break;
  152. case 'desc':
  153. $codeNum = Label::DESC;
  154. break;
  155. default:
  156. return false;
  157. break;
  158. }
  159. return $codeNum;
  160. }
  161. private function FormListTags($data, $params)
  162. {
  163. $extraConfig = array(
  164. 'needThumbSize' => in_array('thumb_size', $params['additional']),
  165. 'param' => $params
  166. );
  167. $tags = array();
  168. foreach ($data as $key => $value) {
  169. // 0 - people tag; 1 - geo tag; 2 - desc tag
  170. if (Label::PEOPLE === $key && !in_array('people', $params['type'])) {
  171. continue;
  172. }
  173. if (Label::GEO === $key && !in_array('geo', $params['type'])) {
  174. continue;
  175. }
  176. if (Label::DESC === $key && !in_array('desc', $params['type'])) {
  177. continue;
  178. }
  179. foreach ($data[$key] as $item) {
  180. $tag = Decorator::TagFormula($item, $extraConfig);
  181. $tags[] = $tag;
  182. }
  183. }
  184. return $tags;
  185. }
  186. private function FormInfoTags($data, $params)
  187. {
  188. $tags = array();
  189. $extraConfig = array(
  190. 'needThumbSize' => in_array('thumb_size', $params['additional']),
  191. 'param' => $params
  192. );
  193. foreach ($data as $item) {
  194. $tag = Decorator::TagFormula($item, $extraConfig);
  195. $tags[] = $tag;
  196. }
  197. return $tags;
  198. }
  199. private function AddUnConfirmTag($list, $params, &$tags)
  200. {
  201. $needThumbSize = in_array('thumb_size', $params['additional']);
  202. $arr = array();
  203. $unConfirmTag['id'] = SYNOPHOTO_UNCONFIRM_TAG_ID;
  204. $unConfirmTag['type'] = 'tag';
  205. $unConfirmTag['tag_type'] = 'people';
  206. $unConfirmTag['name'] = '';
  207. $coverPath = '';
  208. if (is_array($list)) {
  209. $pathList = array_keys($list);
  210. $coverPath = $pathList[0];
  211. }
  212. $blConversion = csSYNOPhotoMisc::IsAlbumAllowConversion(dirname($coverPath));
  213. list($orig_resolutionx, $orig_resolutiony, $blThumbRotated) = AlbumAPIUtil::GetCoverResolutionRotation($coverPath);
  214. AlbumAPIUtil::FillThumbStatus($unConfirmTag, $coverPath, $needThumbSize, $orig_resolutionx, $orig_resolutiony, $blThumbRotated, $blConversion);
  215. array_push($arr, $unConfirmTag);
  216. array_splice($tags, 0, 0, $arr);
  217. }
  218. private function SortTags($sortBy, $sortDirection, &$sortTags)
  219. {
  220. if ('title' === $sortBy) {
  221. if ('asc' === $sortDirection) {
  222. return;
  223. } else {
  224. $sortTags = array_reverse($sortTags);
  225. }
  226. }
  227. }
  228. private function TagList()
  229. {
  230. if (false === ($params = $this->GetParams_List())) {
  231. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  232. goto End;
  233. }
  234. // get category type number - people(0), geo(1), desc(2)
  235. $types = array();
  236. foreach ($params['type'] as $type) {
  237. if (false === ($num = $this->CategoryNameToNum($type))) {
  238. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  239. goto End;
  240. }
  241. array_push($types, $num);
  242. }
  243. // database query
  244. $result = Label::GetByCategoryPermission($types, isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']));
  245. $totalTags = $this->FormListTags($result, $params);
  246. // sorting
  247. $this->SortTags($params['sort_by'], $params['sort_direction'], $totalTags);
  248. // add people unconfirm tag
  249. if (in_array('people', $params['type']) && 'true' === $params['unconfirm_people_tag']) {
  250. $list = AlbumAPIUtil::GetUnConfirmItemList();
  251. if (0 < (int)$list['total']) {
  252. $this->AddUnConfirmTag($list, $params, $totalTags);
  253. }
  254. }
  255. $total = count($totalTags);
  256. // set offset and limit
  257. if (0 > $params['limit']) {
  258. $tags = array_slice($totalTags, $params['offset']);
  259. } else {
  260. $tags = array_slice($totalTags, $params['offset'], $params['limit']);
  261. }
  262. $resp['total'] = (int)$total;
  263. $offset = (0 > $params['limit']) ? $resp['total'] : $params['offset'] + $params['limit'];
  264. $resp['offset'] = ($offset > $total) ? (int)$total : (int)$offset;
  265. $resp['tags'] = $tags;
  266. $this->SetResponse($resp);
  267. End:
  268. return;
  269. }
  270. private function GetInfo()
  271. {
  272. if (false === ($params = $this->GetParams_Info())) {
  273. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  274. goto End;
  275. }
  276. // database query
  277. if (false === ($result = Label::GetByIds($params['id']))) {
  278. $this->SetError(PHOTOSTATION_TAG_GETINFO_FAIL);
  279. goto End;
  280. }
  281. // form to webapi format
  282. $tags = $this->FormInfoTags($result, $params);
  283. $resp['tags'] = $tags;
  284. $this->SetResponse($resp);
  285. End:
  286. return;
  287. }
  288. private function Create()
  289. {
  290. if (false === ($params = $this->GetParams_Create())) {
  291. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  292. goto End;
  293. }
  294. // add label parameter
  295. $label['name'] = $params['name'];
  296. $label['category'] = $this->CategoryNameToNum($params['type']);
  297. $label['info'] = '';
  298. if ('geo' === $params['type']) {
  299. $info['placeId'] = '';
  300. if (null !== $params['place_id']) {
  301. $info['placeId'] = $params['place_id'];
  302. }
  303. if (null !== $params['reference']) {
  304. $info['reference'] = $params['reference'];
  305. }
  306. if (null !== $params['lat']) {
  307. $info['lat'] = $params['lat'];
  308. }
  309. if (null !== $params['lng']) {
  310. $info['lng'] = $params['lng'];
  311. }
  312. if (null !== $params['address']) {
  313. $info['address'] = $params['address'];
  314. }
  315. if (!empty($info)) {
  316. $json = json_encode($info);
  317. $label['info'] = $json;
  318. }
  319. }
  320. $blCreateNewTag = false;
  321. if ('geo' === $params['type']) {
  322. // (name, place_id) as primary key in geo tag
  323. if (false === ($row = Label::GetGeoLabelByNamePlaceId($label['name'], $info['placeId']))) {
  324. $blCreateNewTag = true;
  325. }
  326. } elseif ('people' === $params['type'] || 'desc' === $params['type']) {
  327. // (name, category) as primary key in people, desc tag
  328. if (false === ($row = Label::GetByNameCategory($label['name'], $label['category']))) {
  329. $blCreateNewTag = true;
  330. }
  331. }
  332. if ($blCreateNewTag) {
  333. // database query
  334. if (false === ($lastinsertId = Label::Add($label['name'], $label['category'], $label['info']))) {
  335. $this->SetError(PHOTOSTATION_TAG_CREATE_FAIL);
  336. goto End;
  337. }
  338. $row = array('id' => $lastinsertId);
  339. }
  340. $resp['id'] = 'tag_'.$row['id'];
  341. $this->SetResponse($resp);
  342. End:
  343. return;
  344. }
  345. private function Edit()
  346. {
  347. if (false === ($params = $this->GetParams_Edit())) {
  348. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  349. goto End;
  350. }
  351. // get original label
  352. $id = $params['id'];
  353. if (false === ($label_objs = Label::GetByIds(array($id)))) {
  354. $this->SetError(PHOTOSTATION_TAG_EDIT_FAIL);
  355. goto End;
  356. }
  357. $result = $label_objs[$id];
  358. $origLabel = json_decode($result['info'], true);
  359. $type = $result['category'];
  360. // add label parameter
  361. $label['info'] = '';
  362. if (Label::GEO === $type) {
  363. $info['placeId'] = (null !== $params['place_id']) ? $params['place_id'] : $origLabel['placeId'];
  364. $info['reference'] = (null !== $params['reference']) ? $params['reference'] : $origLabel['reference'];
  365. $info['lat'] = (null !== $params['lat']) ? $params['lat'] : $origLabel['lat'];
  366. $info['lng'] = (null !== $params['lng']) ? $params['lng'] : $origLabel['lng'];
  367. $info['address'] = (null !== $params['address']) ? $params['address'] : $origLabel['address'];
  368. if ('' === $info['placeId'] || null === $info['placeId']) {
  369. unset($info['placeId']);
  370. }
  371. if ('' === $info['reference'] || null === $info['reference']) {
  372. unset($info['reference']);
  373. }
  374. if ('' === $info['lat'] || null === $info['lat']) {
  375. unset($info['lat']);
  376. }
  377. if ('' === $info['lng'] || null === $info['lng']) {
  378. unset($info['lng']);
  379. }
  380. if ('' === $info['address'] || null === $info['address']) {
  381. unset($info['address']);
  382. }
  383. $label['info'] = json_encode($info);
  384. }
  385. // check tag exist
  386. if (Label::isDuplicated($params['id'], $params['name'], $info['placeId'], $type)) {
  387. $this->SetError(PHOTOSTATION_TAG_HAS_EXIST);
  388. goto End;
  389. }
  390. // database query
  391. if (0 === Label::EditById($params['id'], $params['name'], $label['info'])) {
  392. $this->SetError(PHOTOSTATION_TAG_EDIT_FAIL);
  393. goto End;
  394. }
  395. // update metadata
  396. if (Label::GEO !== $type) {
  397. $image_label_objs = ItemLabel::GetPhotoLabelByLabelIds(array($params['id']));
  398. foreach ($image_label_objs as $objs) {
  399. self::OutputSingleSpace();
  400. ItemLabel::UpdatePhotoMetadata($objs['image_id'], $result['category']);
  401. }
  402. }
  403. End:
  404. return;
  405. }
  406. private function Delete()
  407. {
  408. if (false === ($params = $this->GetParams_Info())) {
  409. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  410. goto End;
  411. }
  412. foreach ($params['id'] as $id) {
  413. // To avoid cgi timeout, have to delete label one by one
  414. // Label::DeleteAllByIds support delete multilabels
  415. self::OutputSingleSpace();
  416. Label::DeleteAllByIds(array($id));
  417. }
  418. End:
  419. return;
  420. }
  421. private function DeleteUnconfirmedTag()
  422. {
  423. $unconfirmedLabelId = ItemLabel::GetUnconfirmedLabelId();
  424. Label::DeleteAllByIds($unconfirmedLabelId, false);
  425. }
  426. private function SearchPlace()
  427. {
  428. if (false === ($params = $this->GetParams_SearchPlace())) {
  429. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  430. goto End;
  431. }
  432. $query = escapeshellarg($params['query']);
  433. $location = escapeshellarg($params['location']);
  434. $radius = escapeshellarg($params['radius']);
  435. $language = escapeshellarg($_SESSION[SYNOPHOTO_ADMIN_USER]['lang']);
  436. $phppath = '/var/packages/PhotoStation/target/photo_scripts/encrypted/tag_place_search.php';
  437. $cmd = PHP_CMD." $phppath -q $query -l $location -r $radius -n $language";
  438. @exec($cmd, $output, $ret);
  439. if (0 !== $ret) {
  440. $this->SetError(PHOTOSTATION_TAG_SEARCH_FAIL);
  441. goto End;
  442. }
  443. $json = json_decode(implode('', $output), true);
  444. $searchPlaces = array();
  445. $existPlace = array();
  446. if (is_array($json['results'])) {
  447. foreach ($json['results'] as $place) {
  448. $item = array();
  449. $item['address'] = $place['formatted_address'];
  450. $item['gps'] = $place['geometry']['location'];
  451. $item['placeId'] = $place['id'];
  452. $item['name'] = $place['name'];
  453. $item['tagId'] = -1;
  454. $item['type'] = 'search';
  455. $searchPlaces[] = $item;
  456. }
  457. }
  458. $existPlace = Label::SearchGeoLabel($params['query']);
  459. if ($params['format']) {
  460. for($i=0; $i < count($existPlace); ++$i) {
  461. $existPlace[$i]['type'] = 'db';
  462. }
  463. $resp = array_merge($existPlace, $searchPlaces);
  464. } else {
  465. $resp['existPlaces'] = $existPlace;
  466. $resp['searchPlaces'] = $searchPlaces;
  467. }
  468. $this->SetResponse($resp);
  469. End:
  470. return;
  471. }
  472. protected function CheckPermission()
  473. {
  474. csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
  475. $res = true;
  476. $check = array(
  477. "edit" => "admin",
  478. "delete" => "admin",
  479. "delete_unconfirmed_tag" => "admin",
  480. "create" => "manage",
  481. "searchplace" => "manage"
  482. );
  483. if (!array_key_exists($this->method, $check)) {
  484. goto End;
  485. }
  486. $funcName = "check_".$check[$this->method];
  487. if (!method_exists($this, $funcName)) {
  488. $res = false;
  489. goto End;
  490. }
  491. $res = $this->$funcName();
  492. End:
  493. if (!$res) {
  494. $this->SetError(PHOTOSTATION_TAG_ACCESS_DENY);
  495. }
  496. return $res;
  497. }
  498. private function check_admin()
  499. {
  500. return isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user']);
  501. }
  502. private function check_manage()
  503. {
  504. if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
  505. return true;
  506. }
  507. if (0 < count($_SESSION[SYNOPHOTO_ADMIN_USER]['manageable_album'])) {
  508. return true;
  509. }
  510. return false;
  511. }
  512. }
  513. $api = new TagAPI();
  514. $api->Run();
  515. ?>