conf = $conf['batch_download']; $this->data = array( 'id' => 0, 'user_id' => $user['id'], 'date_creation' => '0000-00-00 00:00:00', 'type' => null, 'type_id' => null, 'size' => 'original', 'nb_zip' => 0, 'last_zip' => 0, 'nb_images' => 0, 'total_size' => 0, 'status' => 'new', 'estimated_total_size' => 0, // not in db ); $this->images = array(); // load specific set if (preg_match('#^[0-9]+$#', $set_id)) { $query = ' SELECT * FROM '.BATCH_DOWNLOAD_TSETS.' WHERE id = '.$set_id.' '.(!is_admin() ? 'AND user_id = '.$this->data['user_id'] : null).' ;'; $result = pwg_query($query); if (pwg_db_num_rows($result)) { $this->data = array_merge( $this->data, pwg_db_fetch_assoc($result) ); // make sure all pictures of the set exist $query = ' DELETE FROM '.BATCH_DOWNLOAD_TIMAGES.' WHERE image_id NOT IN ( SELECT id FROM '.IMAGES_TABLE.' ) ;'; pwg_query($query); $query = ' SELECT * FROM '.BATCH_DOWNLOAD_TIMAGES.' WHERE set_id = '.$this->data['id'].' ;'; $this->images = simple_hash_from_query($query, 'image_id', 'zip'); if ( $this->data['status'] != 'done' and count($this->images) != $this->data['nb_images'] ) { $this->updateParam('nb_images', count($this->images)); } } else { throw new Exception(l10n('Invalid dowload set')); } } // create a new set else if ($set_id == 'new') { if ($size != 'original') { $types = array_keys(ImageStdParams::get_defined_type_map()); if (!in_array($size, $types)) { throw new Exception(sprintf(l10n('Invalid size %s'), $size)); } } $this->data['type'] = $type; $this->data['type_id'] = $type_id; $this->data['size'] = $size; $query = ' INSERT INTO '.BATCH_DOWNLOAD_TSETS.'( user_id, date_creation, type, type_id, size, nb_zip, last_zip, nb_images, total_size, status ) VALUES( '.$this->data['user_id'].', NOW(), "'.$this->data['type'].'", "'.$this->data['type_id'].'", "'.$this->data['size'].'", 0, 0, 0, 0, "new" ) ;'; pwg_query($query); $this->data['id'] = pwg_db_insert_id(); $date = pwg_query('SELECT FROM_UNIXTIME(NOW());'); list($this->data['date_creation']) = pwg_db_fetch_row($date); if (!empty($images)) { $this->addImages($images); } } else { trigger_error('BatchDownloader::__construct, invalid input parameter', E_USER_ERROR); } } /** * updateParam * @param: string param name * @param: mixed param value */ function updateParam($name, $value) { $this->data[$name] = $value; pwg_query('UPDATE '.BATCH_DOWNLOAD_TSETS.' SET '.$name.' = "'.$value.'" WHERE id = '.$this->data['id'].';'); } /** * getParam * @param: string param name * @return: mixed param value */ function getParam($name) { return $this->data[$name]; } /** * getImages * @return: array(image_id => zip_idx) */ function getImages() { return $this->images; } /** * isInSet * @param: int image id * @return: bool */ function isInSet($image_id) { return array_key_exists($image_id, $this->images); } /** * removeImages * @param: array image ids */ function removeImages($image_ids) { if (empty($image_ids) or !is_array($image_ids)) return; foreach ($image_ids as $image_id) { unset($this->images[ $image_id ]); } $query = ' DELETE FROM '.BATCH_DOWNLOAD_TIMAGES.' WHERE set_id = '.$this->data['id'].' AND image_id IN('.implode(',', $image_ids).') ;'; pwg_query($query); $this->updateParam('nb_images', count($this->images)); } /** * addImages * @param: array image ids */ function addImages($image_ids) { if (empty($image_ids) or !is_array($image_ids)) return; $query = ' SELECT id, file FROM '.IMAGES_TABLE.' WHERE id IN('.implode(',', array_unique($image_ids)).') ;'; $images = simple_hash_from_query($query, 'id', 'file'); $inserts = array(); foreach ($images as $image_id => $file) { if ($this->isInSet($image_id)) continue; if (!in_array(get_extension($file), $this->conf['allowed_ext'])) continue; $this->images[ $image_id ] = 0; $inserts[] = array( 'set_id' => $this->data['id'], 'image_id' => $image_id, 'zip' => 0, ); } if (count($inserts)) { mass_inserts( BATCH_DOWNLOAD_TIMAGES, array('set_id','image_id','zip'), $inserts ); } $this->updateParam('nb_images', count($this->images)); } /** * clearImages */ function clearImages() { $this->images = array(); $query = ' DELETE FROM '.BATCH_DOWNLOAD_TIMAGES.' WHERE set_id = '.$this->data['id'].' ;'; pwg_query($query); } /** * getMissingDerivatives * @param: bool update, if true, update IMAGE_SIZES with FS datas * @return: array of i.php urls */ function getMissingDerivatives($update=false) { if ($this->data['size'] == 'original') { return array(); } $uid = '&b='.time(); $params = ImageStdParams::get_by_type($this->data['size']); $last_mod_time = $params->last_mod_time; $image_ids = array_keys($this->images); $to_update = $urls = $inserts = array(); global $conf; $conf['old_question_mark_in_urls'] = $conf['question_mark_in_urls']; $conf['old_php_extension_in_urls'] = $conf['php_extension_in_urls']; $conf['old_derivative_url_style'] = $conf['derivative_url_style']; $conf['question_mark_in_urls'] = $conf['php_extension_in_urls'] = true; $conf['derivative_url_style'] = 2; // images which we need to update stats if ($update) { $query = ' SELECT image_id, filemtime FROM '.IMAGE_SIZES_TABLE.' WHERE image_id IN('.implode(',', $image_ids).') AND type = "'.$this->data['size'].'" ;'; $registered = array_from_query($query, 'image_id', 'filemtime'); $to_update = array_filter($registered, create_function('$t', 'return $t<'.$last_mod_time.';')); $to_update = array_merge($to_update, array_diff($image_ids, $registered)); } $query = ' SELECT id, path, width, height, rotation FROM '.IMAGES_TABLE.' WHERE id IN('.implode(',', $image_ids).') ORDER BY id DESC ;'; $result = pwg_query($query); while ($row = pwg_db_fetch_assoc($result)) { $src_image = new SrcImage($row); // don't give representive_ext // no-image files if ($src_image->is_mimetype()) { if ($update && in_array($row['id'], $to_update)) { $inserts[ $row['id'] ] = array( 'image_id' => $row['id'], 'type' => $this->data['size'], 'width' => 0, 'height' => 0, 'filesize' => filesize(PHPWG_ROOT_PATH.$row['path'])/1024, 'filemtime' => filemtime(PHPWG_ROOT_PATH.$row['path']), ); } } // images files else { $derivative = new DerivativeImage($this->data['size'], $src_image); $filemtime = @filemtime($derivative->get_path()); $src_mtime = @filemtime(PHPWG_ROOT_PATH.$row['path']); if ($src_mtime===false) continue; if ($filemtime===false || $filemtime<$last_mod_time || $filemtime<$src_mtime) { $urls[] = $derivative->get_url().$uid; } else if ($update && in_array($row['id'], $to_update)) { $imagesize = getimagesize($derivative->get_path()); $inserts[ $row['id'] ] = array( 'image_id' => $row['id'], 'type' => $this->data['size'], 'width' => $imagesize[0], 'height' => $imagesize[1], 'filesize' => filesize($derivative->get_path())/1024, 'filemtime' => $filemtime, ); } } } if (!empty($inserts)) { $query = ' DELETE FROM '.IMAGE_SIZES_TABLE.' WHERE image_id IN('.implode(',', array_keys($inserts)).') ;'; pwg_query($query); mass_inserts( IMAGE_SIZES_TABLE, array('image_id','type','width','height','filesize','filemtime'), $inserts ); } $conf['question_mark_in_urls'] = $conf['old_question_mark_in_urls']; $conf['php_extension_in_urls'] = $conf['old_php_extension_in_urls']; $conf['derivative_url_style'] = $conf['old_derivative_url_style']; return $urls; } /** * deleteArchives */ function deleteArchives() { $zip_path = glob($this->getArchivePath('*')); if (is_array($zip_path)) { foreach ($zip_path as $file) { unlink($file); } } } /** * getFilename */ function getFilename($row, $filesize=array()) { $row['filename'] = stripslashes(get_filename_wo_extension($row['file'])); $search = array('%id%', '%filename%', '%author%', '%dimensions%'); $replace = array($row['id'], $row['filename']); $replace[2] = empty($row['author']) ? null : $row['author']; $replace[3] = empty($filesize) ? null : $filesize['width'].'x'.$filesize['height']; $filename = str_replace($search, $replace, $this->conf['file_pattern']); $filename = preg_replace( array('#_+#', '#-+#', '# +#', '#^([_\- ]+)#', '#([_\- ]+)$#'), array('_', '-', ' ', null, null), $filename ); if (empty($filename) || $filename == $this->conf['file_pattern']) { $filename = $row['id'].'_'.$row['filename']; } $filename.= '.'.get_extension($row['path']); return $filename; } /** * createNextArchive * @param: bool force all elements in one archive * @return: string zip path or false */ function createNextArchive($force_one_archive=false) { // set already downloaded (we should never be there !) if ( $this->data['status'] == 'done' or $this->data['nb_images'] == 0 ) { trigger_error('BatchDownloader::createNextArchive, the set is empty', E_USER_ERROR); } global $conf; // first zip if ($this->data['last_zip'] == 0) { $this->updateParam('status', 'download'); // limit number of elements if ($this->data['nb_images'] > $this->conf['max_elements']) { $images_ids = array_slice(array_keys($this->images), 0, $this->conf['max_elements']); $this->clearImages(); $this->addImages($images_ids); } $this->getEstimatedArchiveNumber(true); $this->updateParam('date_creation', date('Y-m-d H:i:s')); trigger_action('batchdownload_init_zip', $this->data, array_keys($this->images)); } // get next images of the set $images_to_add = array(); foreach ($this->images as $image_id => $zip_id) { if ($zip_id != 0) continue; // here are already added images array_push($images_to_add, $image_id); } if (count($images_to_add)) { $query = ' SELECT id, name, file, path, rotation, filesize, width, height, author FROM '.IMAGES_TABLE.' WHERE id IN ('.implode(',', $images_to_add).') ;'; $images_to_add = hash_from_query($query, 'id'); if ($this->data['size'] != 'original') { $query = ' SELECT image_id, filesize, width, height FROM '.IMAGE_SIZES_TABLE.' WHERE image_id IN ('.implode(',', array_keys($images_to_add)).') AND type = "'.$this->data['size'].'" ;'; $filesizes = hash_from_query($query, 'image_id'); } // open zip $this->updateParam('last_zip', $this->data['last_zip']+1); $zip_path = $this->getArchivePath(); $zip = new myZip($zip_path, $this->conf['force_pclzip']); // add images until size limit is reach, or all images are added $images_added = array(); $total_size = 0; foreach ($images_to_add as $row) { if (!file_exists(PHPWG_ROOT_PATH.$row['path'])) { $this->removeImages(array($row['id'])); continue; } if ($this->data['size'] == 'original') { $zip->addFile(PHPWG_ROOT_PATH.$row['path'], $this->getFilename($row, $row)); $total_size+= $row['filesize']; } else { $src_image = new SrcImage($row); // don't give representive_ext // no-image files if ($src_image->is_mimetype()) { $zip->addFile(PHPWG_ROOT_PATH.$row['path'], $this->getFilename($row, array())); $total_size+= $row['filesize']; } // images files else { $derivative = new DerivativeImage($this->data['size'], $src_image); $zip->addFile($derivative->get_path(), $this->getFilename($row, $filesizes[ $row['id'] ])); $total_size+= $filesizes[ $row['id'] ]['filesize']; } } array_push($images_added, $row['id']); $this->images[ $row['id'] ] = $this->data['last_zip']; if ($total_size >= $this->conf['max_size']*1024 and !$force_one_archive) break; } $this->updateParam('total_size', $this->data['total_size'] + $total_size); // archive comment $comment = 'Generated on '.date('r').' with PHP '.PHP_VERSION.' by Piwigo Batch Downloader.'; $comment.= "\n".$conf['gallery_title'].' - '.get_absolute_root_url(); if (!empty($conf['batch_download_comment'])) { $comment.= "\n\n".wordwrap(remove_accents($conf['batch_download_comment']), 60); } $zip->setArchiveComment($comment); $zip->close(); // update database $query = ' UPDATE '.BATCH_DOWNLOAD_TIMAGES.' SET zip = '.$this->data['last_zip'].' WHERE set_id = '.$this->data['id'].' AND image_id IN('.implode(',', $images_added).') ;'; pwg_query($query); // all images added ? if (count($images_to_add) == count($images_added)) { if ($this->conf['one_archive']) $this->updateParam('status', 'done'); $done = true; // over estimed $this->updateParam('nb_zip', $this->data['last_zip']); } // under estimed if (!isset($done) && $this->data['status'] != 'done' && $this->data['last_zip'] == $this->data['nb_zip']) { $this->updateParam('nb_zip', $this->data['last_zip']+1); } return $zip_path; } else { return false; } } /** * getEstimatedTotalSize * @return: int */ function getEstimatedTotalSize($force=false) { if ($this->data['status'] == 'done') return $this->data['total_size']; if ($this->data['nb_images'] == 0) return 0; if ( !empty($this->data['estimated_total_size']) and !$force ) return $this->data['estimated_total_size']; $image_ids = array_slice(array_keys($this->images), 0, $this->conf['max_elements']); if ($this->data['size'] == 'original') { $query = ' SELECT SUM(filesize) AS total FROM '.IMAGES_TABLE.' WHERE id IN ('.implode(',', $image_ids).') ;'; } else { $query = ' SELECT SUM(filesize) AS total FROM '.IMAGE_SIZES_TABLE.' WHERE image_id IN ('.implode(',', $image_ids).') ;'; } list($this->data['estimated_total_size']) = pwg_db_fetch_row(pwg_query($query)); return $this->data['estimated_total_size']; } /** * getEstimatedArchiveNumber * @return: int */ function getEstimatedArchiveNumber($force=false) { if ($this->data['status'] == 'done') return $this->data['nb_zip']; if ( !empty($this->data['nb_zip']) and !$force ) return $this->data['nb_zip']; $this->updateParam('nb_zip', ceil( $this->getEstimatedTotalSize($force) / ($this->conf['max_size']*1024) )); return $this->data['nb_zip']; } /** * getDownloadList * @return: string html */ function getDownloadList($url='') { if ($this->data['nb_images'] == 0) { return ''.l10n('No archive').''; } $root_url = get_root_url(); $out = ''; for ($i=1; $i<=$this->getEstimatedArchiveNumber(); $i++) { $out.= '