fixes #1419 add photos in a lounge as a temporary space

* at the end of the upload of after a maximum duration, move the photos from the lounge to their actual categories.
* do not invalidate user cache when photos are added in the lounge, thus avoiding to rebuild cache on every photo uploaded
* the lounge system activates itself only beyond 50k (by default) photo
This commit is contained in:
plegall 2021-06-11 16:35:29 +02:00
parent 4aa7a7b9bf
commit ac0d1a5b47
13 changed files with 307 additions and 7 deletions

View file

@ -1888,6 +1888,129 @@ function compare_image_tag_lists($taglist_before, $taglist_after)
return $images_to_update;
}
/**
* Instead of associating images to categories, add them in the lounge, waiting for take-off.
*
* @since 12
* @param array $images - list of image ids
* @param array $categories - list of category ids
*/
function fill_lounge($images, $categories)
{
$inserts = array();
foreach ($categories as $category_id)
{
foreach ($images as $image_id)
{
$inserts[] = array(
'image_id' => $image_id,
'category_id' => $category_id,
);
}
}
if (count($inserts))
{
mass_inserts(
LOUNGE_TABLE,
array_keys($inserts[0]),
$inserts
);
}
}
/**
* Move images from the lounge to the categories they were intended for.
*
* @since 12
* @param boolean $invalidate_user_cache
* @return int number of images moved
*/
function empty_lounge($invalidate_user_cache=true)
{
global $logger;
if (isset($conf['empty_lounge_running']))
{
list($running_exec_id, $running_exec_start_time) = explode('-', $conf['empty_lounge_running']);
if (time() - $running_exec_start_time > 60)
{
$logger->debug(__FUNCTION__.', exec='.$running_exec_id.', timeout stopped by another call to the function');
conf_delete_param('empty_lounge_running');
}
}
$exec_id = generate_key(4);
$logger->debug(__FUNCTION__.', exec='.$exec_id.', begins');
// if lounge is already being emptied, skip
$query = '
INSERT IGNORE
INTO '.CONFIG_TABLE.'
SET param="empty_lounge_running"
, value="'.$exec_id.'-'.time().'"
;';
pwg_query($query);
list($empty_lounge_running) = pwg_db_fetch_row(pwg_query('SELECT value FROM '.CONFIG_TABLE.' WHERE param = "empty_lounge_running"'));
list($running_exec_id,) = explode('-', $empty_lounge_running);
if ($running_exec_id != $exec_id)
{
$logger->debug(__FUNCTION__.', exec='.$exec_id.', skip');
return;
}
$logger->debug(__FUNCTION__.', exec='.$exec_id.' wins the race and gets the token!');
sleep(5);
$max_image_id = 0;
$query = '
SELECT
image_id,
category_id
FROM '.LOUNGE_TABLE.'
ORDER BY category_id ASC, image_id ASC
;';
$rows = query2array($query);
$images = array();
foreach ($rows as $idx => $row)
{
if ($row['image_id'] > $max_image_id)
{
$max_image_id = $row['image_id'];
}
$images[] = $row['image_id'];
if (!isset($rows[$idx+1]) or $rows[$idx+1]['category_id'] != $row['category_id'])
{
// if we're at the end of the loop OR if category changes
associate_images_to_categories($images, array($row['category_id']));
$images = array();
}
}
$query = '
DELETE
FROM '.LOUNGE_TABLE.'
WHERE image_id <= '.$max_image_id.'
;';
pwg_query($query);
if ($invalidate_user_cache)
{
invalidate_user_cache();
}
conf_delete_param('empty_lounge_running');
$logger->debug(__FUNCTION__.', exec='.$exec_id.', ends');
return count($rows);
}
/**
* Associate a list of images to a list of categories.
* The function will not duplicate links and will preserve ranks.
@ -3140,12 +3263,28 @@ SELECT path
*/
function get_orphans()
{
// exclude images in the lounge
$query = '
SELECT
image_id
FROM '.LOUNGE_TABLE.'
;';
$lounged_ids = query2array($query, null, 'image_id');
$query = '
SELECT
id
FROM '.IMAGES_TABLE.'
LEFT JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id
WHERE category_id is null
WHERE category_id is null';
if (count($lounged_ids) > 0)
{
$query .= '
AND id NOT IN ('.implode(',', $lounged_ids).')';
}
$query.= '
ORDER BY id ASC
;';

View file

@ -362,12 +362,31 @@ SELECT
pwg_activity('photo', $image_id, 'add');
}
if (!isset($conf['lounge_active']))
{
conf_update_param('lounge_active', false, true);
}
if (!$conf['lounge_active'])
{
// check if we need to use the lounge from now
list($nb_photos) = pwg_db_fetch_row(pwg_query('SELECT COUNT(*) FROM '.IMAGES_TABLE.';'));
if ($nb_photos >= $conf['lounge_activate_threshold'])
{
conf_update_param('lounge_active', true, true);
}
}
if (isset($categories) and count($categories) > 0)
{
associate_images_to_categories(
array($image_id),
$categories
);
if ($conf['lounge_active'])
{
fill_lounge(array($image_id), $categories);
}
else
{
associate_images_to_categories(array($image_id), $categories);
}
}
// update metadata from the uploaded file (exif/iptc)
@ -377,7 +396,10 @@ SELECT
}
sync_metadata(array($image_id));
invalidate_user_cache();
if (!$conf['lounge_active'])
{
invalidate_user_cache();
}
// cache a derivative
$query = '

View file

@ -77,6 +77,7 @@ switch ($action)
}
case 'user_cache' :
{
empty_lounge(false);
invalidate_user_cache();
break;
}

View file

@ -222,6 +222,14 @@ jQuery(document).ready(function(){
Piecon.reset();
jQuery.ajax({
url: "ws.php?format=json&method=pwg.images.emptyLounge",
type:"POST",
data: {
pwg_token: pwg_token
}
});
jQuery(".selectAlbum, .selectFiles, #permissions, .showFieldset").hide();
jQuery(".infos").append('<ul><li>'+sprintf(photosUploaded_label, uploadedPhotos.length)+'</li></ul>');

View file

@ -151,6 +151,8 @@ if (isset($conf['order_by_inside_category_custom']))
$conf['order_by_inside_category'] = $conf['order_by_inside_category_custom'];
}
check_lounge();
include(PHPWG_ROOT_PATH.'include/user.inc.php');
if (in_array( substr($user['language'],0,2), array('fr','it','de','es','pl','ru','nl','tr','da') ) )

View file

@ -433,6 +433,17 @@ $conf['session_gc_probability'] = 1;
// | debug/performance |
// +-----------------------------------------------------------------------+
// number of photos beyond which individual photos are added in the
// lounge, a temporary zone where photos wait before being "launched".
// 50k photos by default.
$conf['lounge_activate_threshold'] = 50000;
// Lounge is automatically emptied (photos are being pushed to their
// albums) when the oldest one reaches this duration. Lounge can be emptied
// before, either manually or at the end of the upload. In seconds.
// 5 minutes by default.
$conf['lounge_max_duration'] = 5*60;
// show_queries : for debug purpose, show queries and execution times
$conf['show_queries'] = false;

View file

@ -100,5 +100,7 @@ if (!defined('IMAGE_FORMAT_TABLE'))
define('IMAGE_FORMAT_TABLE', $prefixeTable.'image_format');
if (!defined('ACTIVITY_TABLE'))
define('ACTIVITY_TABLE', $prefixeTable.'activity');
if (!defined('LOUNGE_TABLE'))
define('LOUNGE_TABLE', $prefixeTable.'lounge');
?>

View file

@ -2266,4 +2266,48 @@ function safe_version_compare($a, $b, $op=null)
}
}
/**
* Checks if the lounge needs to be emptied automatically.
*
* @since 12
*/
function check_lounge()
{
global $conf;
if (!isset($conf['lounge_active']) or !$conf['lounge_active'])
{
return;
}
if (isset($_REQUEST['method']) and in_array($_REQUEST['method'], array('pwg.images.upload', 'pwg.images.uploadAsync')))
{
return;
}
// is the oldest photo in the lounge older than lounge maximum waiting time?
$query = '
SELECT
image_id,
date_available,
NOW() AS dbnow
FROM '.LOUNGE_TABLE.'
JOIN '.IMAGES_TABLE.' ON image_id = id
ORDER BY image_id ASC
LIMIT 1
;';
$voyagers = query2array($query);
if (count($voyagers))
{
$voyager = $voyagers[0];
$age = strtotime($voyager['dbnow']) - strtotime($voyager['date_available']);
if ($age > $conf['lounge_max_duration'])
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
empty_lounge();
}
}
}
?>

View file

@ -475,6 +475,11 @@ SELECT image_id
*/
function get_computed_categories(&$userdata, $filter_days=null)
{
global $logger;
$start_time = get_moment();
$logger->debug(__FUNCTION__.' starts now');
$query = 'SELECT c.id AS cat_id, id_uppercat';
$query.= ', global_rank';
// Count by date_available to avoid count null
@ -569,6 +574,9 @@ FROM '.CATEGORIES_TABLE.' as c
}
}
}
$logger->debug(__FUNCTION__.' ends now in '.get_elapsed_time($start_time, get_moment()));
return $cats;
}

View file

@ -405,8 +405,10 @@ SELECT id, name, permalink, uppercats, global_rank, commentable
}
usort($related_categories, 'global_rank_compare');
if (empty($related_categories))
if (empty($related_categories) and !is_admin())
{
// photo might be in the lounge? or simply orphan. A standard user should not get
// info. An admin should still be able to get info.
return new PwgError(401, 'Access denied');
}
@ -2094,6 +2096,21 @@ function ws_images_checkUpload($params, $service)
return $ret;
}
/**
* API method
* Empties the lounge, where photos may wait before taking off.
* @since 12
* @param mixed[] $params
*/
function ws_images_emptyLounge($params, $service)
{
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
$ret = array('count' => empty_lounge());
return $ret;
}
/**
* API method
* add md5sum at photos, by block. Returns how md5sum were added and how many are remaining.

View file

@ -0,0 +1,26 @@
<?php
// +-----------------------------------------------------------------------+
// | This file is part of Piwigo. |
// | |
// | For copyright and license information, please view the COPYING.txt |
// | file that was distributed with this source code. |
// +-----------------------------------------------------------------------+
if (!defined('PHPWG_ROOT_PATH'))
{
die('Hacking attempt!');
}
$upgrade_description = 'add lounge table';
pwg_query('
CREATE TABLE `'.PREFIX_TABLE.'lounge` (
`image_id` mediumint(8) unsigned NOT NULL,
`category_id` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`image_id`,`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
;');
echo "\n".$upgrade_description."\n";
?>

View file

@ -260,6 +260,17 @@ CREATE TABLE `piwigo_languages` (
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
--
-- Table structure for table `piwigo_lounge`
--
DROP TABLE IF EXISTS `piwigo_lounge`;
CREATE TABLE `piwigo_lounge` (
`image_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
`category_id` smallint(5) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`image_id`,`category_id`)
) ENGINE=MyISAM;
--
-- Table structure for table `piwigo_old_permalinks`
--

9
ws.php
View file

@ -769,6 +769,15 @@ function ws_addDefaultMethods( $arr )
array('admin_only'=>true)
);
$service->addMethod(
'pwg.images.emptyLounge',
'ws_images_emptyLounge',
null,
'Empty lounge, where images may be waiting before taking off.',
$ws_functions_root . 'pwg.images.php',
array('admin_only'=>true)
);
$service->addMethod(
'pwg.images.setInfo',
'ws_images_setInfo',