_dir = $dir; } public function getId() { return $this->_dir->getId(); } public function getType() { return $this->_dir->getType(); } public function setType($type) { $this->_dir->setType($type); } public function getDirectory() { return $this->_dir->getDirectory(); } public function setDirectory($dir) { $this->_dir->setDirectory($dir); $this->_dir->save(); } public function setExistsFlag($flag) { $this->_dir->setExists($flag); $this->_dir->save(); } public function setWatchedFlag($flag) { $this->_dir->setWatched($flag); $this->_dir->save(); } public function getWatchedFlag() { return $this->_dir->getWatched(); } public function getExistsFlag() { return $this->_dir->getExists(); } /** * There are 2 cases where this function can be called. * 1. When watched dir was removed * 2. When some dir was watched, but it was unmounted. * * In case of 1, $userAddedWatchedDir should be true * In case of 2, $userAddedWatchedDir should be false * * When $userAddedWatchedDir is true, it will set "Watched" flag to false * otherwise, it will set "Exists" flag to true * * @param mixed $userAddedWatchedDir */ public function remove($userAddedWatchedDir = true) { $music_dir_id = $this->getId(); $sql = <<<'SQL' SELECT DISTINCT s.instance_id FROM cc_music_dirs AS md LEFT JOIN cc_files AS f ON f.directory = md.id RIGHT JOIN cc_schedule AS s ON s.file_id = f.id WHERE md.id = :musicDirId; SQL; $show_instances = Application_Common_Database::prepareAndExecute( $sql, [':musicDirId' => $music_dir_id], 'all' ); // get all the files on this dir $sql = <<<'SQL' UPDATE cc_files SET file_exists = 'f' WHERE id IN (SELECT f.id FROM cc_music_dirs AS md LEFT JOIN cc_files AS f ON f.directory = md.id WHERE md.id = :musicDirId); SQL; $affected = Application_Common_Database::prepareAndExecute( $sql, [':musicDirId' => $music_dir_id], 'all' ); // set RemovedFlag to true if ($userAddedWatchedDir) { self::setWatchedFlag(false); } else { self::setExistsFlag(false); } // $res = $this->_dir->delete(); foreach ($show_instances as $show_instance_row) { $temp_show = new Application_Model_ShowInstance($show_instance_row['instance_id']); $temp_show->updateScheduledTime(); } Application_Model_RabbitMq::PushSchedule(); } /** * Checks if p_dir1 is the ancestor of p_dir2. Returns * true if it is the ancestor, false otherwise. Note that * /home/user is considered the ancestor of /home/user. * * @param string $p_dir1 * The potential ancestor directory * @param string $p_dir2 * The potential descendent directory * * @return bool * Returns true if it is the ancestor, false otherwise */ private static function isAncestorDir($p_dir1, $p_dir2) { if (strlen($p_dir1) > strlen($p_dir2)) { return false; } return substr($p_dir2, 0, strlen($p_dir1)) == $p_dir1; } /** * Checks whether the path provided is a valid path. A valid path * is defined as not being nested within an existing watched directory, * or vice-versa. Throws a NestedDirectoryException if invalid. * * @param string $p_path * The path we want to validate */ public static function isPathValid($p_path) { $dirs = self::getWatchedDirs(); $dirs[] = self::getStorDir(); foreach ($dirs as $dirObj) { $dir = $dirObj->getDirectory(); $diff = strlen($dir) - strlen($p_path); if ($diff == 0) { if ($dir == $p_path) { throw new NestedDirectoryException(sprintf(_('%s is already watched.'), $p_path)); } } elseif ($diff > 0) { if (self::isAncestorDir($p_path, $dir)) { throw new NestedDirectoryException(sprintf(_('%s contains nested watched directory: %s'), $p_path, $dir)); } } else { // diff < 0 if (self::isAncestorDir($dir, $p_path)) { throw new NestedDirectoryException(sprintf(_('%s is nested within existing watched directory: %s'), $p_path, $dir)); } } } } /** There are 2 cases where this function can be called. * 1. When watched dir was added * 2. When some dir was watched, but it was unmounted somehow, but gets mounted again. * * In case of 1, $userAddedWatchedDir should be true * In case of 2, $userAddedWatchedDir should be false * * When $userAddedWatchedDir is true, it will set "Removed" flag to false * otherwise, it will set "Exists" flag to true * * @param $nestedWatch - if true, bypass path check, and Watched to false * @param mixed $p_path * @param mixed $p_type * @param mixed $userAddedWatchedDir */ public static function addDir($p_path, $p_type, $userAddedWatchedDir = true, $nestedWatch = false) { if (!is_dir($p_path)) { return ['code' => 2, 'error' => sprintf(_('%s is not a valid directory.'), $p_path)]; } $real_path = Application_Common_OsPath::normpath($p_path) . '/'; if ($real_path != '/') { $p_path = $real_path; } $exist_dir = self::getDirByPath($p_path); if (is_null($exist_dir)) { $temp_dir = new CcMusicDirs(); $dir = new Application_Model_MusicDir($temp_dir); } else { $dir = $exist_dir; } $dir->setType($p_type); $p_path = Application_Common_OsPath::normpath($p_path) . '/'; try { /* isPathValid() checks if path is a substring or a superstring of an * existing dir and if not, throws NestedDirectoryException */ if (!$nestedWatch) { self::isPathValid($p_path); } if ($userAddedWatchedDir) { $dir->setWatchedFlag(true); } else { if ($nestedWatch) { $dir->setWatchedFlag(false); } $dir->setExistsFlag(true); } $dir->setDirectory($p_path); return ['code' => 0]; } catch (NestedDirectoryException $nde) { $msg = $nde->getMessage(); return ['code' => 1, 'error' => "{$msg}"]; } catch (Exception $e) { return ['code' => 1, 'error' => sprintf( _('%s is already set as the current storage dir or in the' . ' watched folders list'), $p_path ), ]; } } /** There are 2 cases where this function can be called. * 1. When watched dir was added * 2. When some dir was watched, but it was unmounted somehow, but gets mounted again. * * In case of 1, $userAddedWatchedDir should be true * In case of 2, $userAddedWatchedDir should be false * * When $userAddedWatchedDir is true, it will set "Watched" flag to true * otherwise, it will set "Exists" flag to true * * @param mixed $p_path * @param mixed $userAddedWatchedDir * @param mixed $nestedWatch */ public static function addWatchedDir($p_path, $userAddedWatchedDir = true, $nestedWatch = false) { $res = self::addDir($p_path, 'watched', $userAddedWatchedDir, $nestedWatch); if ($res['code'] != 0) { return $res; } // convert "linked" files (Airtime <= 1.8.2) to watched files. $propel_link_dir = CcMusicDirsQuery::create() ->filterByType('link') ->findOne(); // see if any linked files exist. if (isset($propel_link_dir)) { // newly added watched directory object $propel_new_watch = CcMusicDirsQuery::create() ->filterByDirectory(Application_Common_OsPath::normpath($p_path) . '/') ->findOne(); // any files of the deprecated "link" type. $link_files = CcFilesQuery::create() ->setFormatter(ModelCriteria::FORMAT_ON_DEMAND) ->filterByDbDirectory($propel_link_dir->getId()) ->find(); $newly_watched_dir = $propel_new_watch->getDirectory(); foreach ($link_files as $link_file) { $link_filepath = $link_file->getDbFilepath(); // convert "link" file into a watched file. if ((strlen($newly_watched_dir) < strlen($link_filepath)) && (substr($link_filepath, 0, strlen($newly_watched_dir)) === $newly_watched_dir)) { // get the filepath path not including the watched directory. $sub_link_filepath = substr($link_filepath, strlen($newly_watched_dir)); $link_file->setDbDirectory($propel_new_watch->getId()); $link_file->setDbFilepath($sub_link_filepath); $link_file->save(); } } } $data = []; $data['directory'] = $p_path; Application_Model_RabbitMq::SendMessageToMediaMonitor('new_watch', $data); return $res; } public static function getDirByPK($pk) { $dir = CcMusicDirsQuery::create()->findPK($pk); if (!$dir) { return null; } return new Application_Model_MusicDir($dir); } public static function getDirByPath($p_path) { $dir = CcMusicDirsQuery::create() ->filterByDirectory($p_path) ->findOne(); if ($dir == null) { return null; } return new Application_Model_MusicDir($dir); } /** * Search and returns watched dirs. * * @param $exists search condition with exists flag * @param $watched search condition with watched flag */ public static function getWatchedDirs($exists = true, $watched = true) { $result = []; $dirs = CcMusicDirsQuery::create() ->filterByType('watched'); if ($exists !== null) { $dirs = $dirs->filterByExists($exists); } if ($watched !== null) { $dirs = $dirs->filterByWatched($watched); } $dirs = $dirs->find(); foreach ($dirs as $dir) { $result[] = new Application_Model_MusicDir($dir); } return $result; } public static function getStorDir() { $dir = CcMusicDirsQuery::create() ->filterByType('stor') ->findOne(); return new Application_Model_MusicDir($dir); } public static function setStorDir($p_dir) { // we want to be consistent when storing dir path. // path should always ends with trailing '/' $p_dir = Application_Common_OsPath::normpath($p_dir) . '/'; if (!is_dir($p_dir)) { return ['code' => 2, 'error' => sprintf(_('%s is not a valid directory.'), $p_dir)]; } if (Application_Model_Preference::GetImportTimestamp() + 10 > time()) { return ['code' => 3, 'error' => 'Airtime is currently importing files. Please wait until this is complete before changing the storage directory.']; } $dir = self::getStorDir(); // if $p_dir doesn't exist in DB $exist = $dir->getDirByPath($p_dir); if ($exist == null) { $dir->setDirectory($p_dir); $dirId = $dir->getId(); $data = []; $data['directory'] = $p_dir; $data['dir_id'] = $dirId; Application_Model_RabbitMq::SendMessageToMediaMonitor('change_stor', $data); return ['code' => 0]; } return ['code' => 1, 'error' => sprintf(_('%s is already set as the current storage dir or in the watched folders list.'), $p_dir), ]; } public static function getWatchedDirFromFilepath($p_filepath) { $dirs = CcMusicDirsQuery::create() ->filterByType(['watched', 'stor']) ->filterByExists(true) ->filterByWatched(true) ->find(); foreach ($dirs as $dir) { $directory = $dir->getDirectory(); if (substr($p_filepath, 0, strlen($directory)) === $directory) { return new Application_Model_MusicDir($dir); } } return null; } /** There are 2 cases where this function can be called. * 1. When watched dir was removed * 2. When some dir was watched, but it was unmounted. * * In case of 1, $userAddedWatchedDir should be true * In case of 2, $userAddedWatchedDir should be false * * When $userAddedWatchedDir is true, it will set "Watched" flag to false * otherwise, it will set "Exists" flag to true * * @param mixed $p_dir * @param mixed $userAddedWatchedDir */ public static function removeWatchedDir($p_dir, $userAddedWatchedDir = true) { // make sure that $p_dir has a trailing "/" $real_path = Application_Common_OsPath::normpath($p_dir) . '/'; if ($real_path != '/') { $p_dir = $real_path; } $dir = Application_Model_MusicDir::getDirByPath($p_dir); if (is_null($dir)) { return ['code' => 1, 'error' => sprintf(_("%s doesn't exist in the watched list."), $p_dir)]; } $dir->remove($userAddedWatchedDir); $data = []; $data['directory'] = $p_dir; Application_Model_RabbitMq::SendMessageToMediaMonitor('remove_watch', $data); return ['code' => 0]; } public static function splitFilePath($p_filepath) { $mus_dir = self::getWatchedDirFromFilepath($p_filepath); if (is_null($mus_dir)) { return null; } $length_dir = strlen($mus_dir->getDirectory()); $fp = substr($p_filepath, $length_dir); return [$mus_dir->getDirectory(), trim($fp)]; } public function unhideFiles() { $files = $this->_dir->getCcFiless(); $hid = 0; foreach ($files as $file) { ++$hid; $file->setDbHidden(false); $file->save(); } Logging::info("unhide '{$hid}' files"); } }