Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ config/
env.json
variables.json
html/allsky/configuration.json
html/allsky/images/[0-9][0-9][0-9][0-9]/
html/allsky/viewSettings/
scripts/allsky-config
scripts/functions.php
Expand Down
10 changes: 10 additions & 0 deletions config_repo/options.json.repo
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,16 @@
"booldependson" : "timelapsegenerate AND timelapseupload AND (uselocalwebsite OR useremotewebsite OR useremoteserver)"
},
{
"name" : "uploadimages",
"default" : false,
"description" : "Enable to upload all images from the day to an Allsky Website and/or remote server in images/YYYY/YYYYMMDD/ format.",
"label" : "Upload Images",
"label_prefix" : "Daily Timelapse",
"type" : "boolean",
"carryforward" : true,
"booldependson" : "timelapsegenerate AND (uselocalwebsite OR useremotewebsite OR useremoteserver)"
},
{
"name" : "keogramupload",
"default" : true,
"description" : "Enable to upload the keogram to an Allsky Website and/or remote server.",
Expand Down
202 changes: 202 additions & 0 deletions html/allsky/images/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
<?php
$configFilePrefix = "../";
include_once('../functions.php');
disableBuffering();

// Settings are now in $webSettings_array.
$homePage = v("homePage", null, $webSettings_array);
$includeGoogleAnalytics = v("includeGoogleAnalytics", false, $homePage);
$thumbnailsortorder = v("thumbnailsortorder", "descending", $homePage);
$thumbnailSizeX = v("thumbnailsizex", 100, $homePage);

// Get date from URL or use today
$selected_date = isset($_GET['date']) ? $_GET['date'] : date('Ymd');
if (!preg_match('/^\d{8}$/', $selected_date)) {
$selected_date = date('Ymd');
}

$title = "Archived Images";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" type="image/png" href="../allsky-favicon.png">
<title><?php echo $title; ?></title>

<?php
if ($includeGoogleAnalytics && file_exists("../js/analyticsTracking.js")) {
echo "<script src='../js/analyticsTracking.js'></script>";
}
?>
<link href="../font-awesome/css/all.min.css" rel="stylesheet">
<link href="../css/allsky.css" rel="stylesheet">
<style>
.date-controls {
margin: 20px 0;
padding: 15px;
background: #222;
border-radius: 5px;
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.date-input {
background: #333;
border: 1px solid #555;
color: #e0e0e0;
padding: 8px 12px;
border-radius: 4px;
font-size: 14px;
}
.btn-date {
background: #7777ff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn-date:hover {
background: #6666ee;
}
.date-info {
color: #7777ff;
font-weight: bold;
}
</style>
</head>
<body>

<?php
// Calculate dates for navigation
$date_obj = DateTime::createFromFormat('Ymd', $selected_date);
$prev_date = clone $date_obj;
$prev_date->modify('-1 day');
$next_date = clone $date_obj;
$next_date->modify('+1 day');

$year = substr($selected_date, 0, 4);
$month = substr($selected_date, 4, 2);
$day = substr($selected_date, 6, 2);
$formatted_date = "$year-$month-$day ({$date_obj->format('l')})";

// Build directory path
$dir = "./$year/$selected_date";

// Get list of images
$images = array();
if (is_dir($dir)) {
$files = glob("$dir/*.{jpg,JPG,jpeg,JPEG}", GLOB_BRACE);
foreach ($files as $file) {
$images[] = basename($file);
}

if ($thumbnailsortorder === "descending") {
arsort($images);
} else {
asort($images);
}
}

$num_images = count($images);
?>

<div class="date-controls">
<button class="btn-date" onclick="navigateToDate('<?php echo $prev_date->format('Ymd'); ?>')">
<i class="fas fa-chevron-left"></i> Previous
</button>

<input type="date"
class="date-input"
value="<?php echo $date_obj->format('Y-m-d'); ?>"
onchange="navigateToDate(this.value.replace(/-/g, ''))">

<button class="btn-date" onclick="navigateToDate('<?php echo $next_date->format('Ymd'); ?>')">
Next <i class="fas fa-chevron-right"></i>
</button>

<button class="btn-date" onclick="navigateToDate('<?php echo date('Ymd'); ?>')">
<i class="fas fa-calendar-day"></i> Today
</button>

<span class="date-info"><?php echo $formatted_date; ?> (<?php echo $num_images; ?> images)</span>
</div>

<?php
if ($num_images == 0) {
echo "<p>$back_button</p>";
echo "<div class='noImages'>No images for $formatted_date</div>";
} else {
echo "<table class='imagesHeader'>";
echo "<tr>";
echo "<td class='headerButton'>$back_button</td>";
echo "<td class='headerTitle'>$title - $formatted_date</td>";
echo "</tr>";
echo "<tr>";
echo "<td colspan='2'><div class='imagesSortOrder'>$num_images images - ";
echo ($thumbnailsortorder === "descending") ? "Sorted newest to oldest" : "Sorted oldest to newest";
echo "</div></td>";
echo "</tr>";
echo "</table>";

// Try to create thumbnails directory
$thumb_dir = "$dir/thumbnails";
$can_create_thumbs = false;
if (is_dir($thumb_dir)) {
$can_create_thumbs = true;
} else {
// Try to create with recursive flag
if (@mkdir($thumb_dir, 0775, true)) {
$can_create_thumbs = true;
}
}

echo "<div class='archived-files'>\n";

foreach ($images as $image) {
$image_path = "$dir/$image";
$thumbnail = "$thumb_dir/$image";

// Use thumbnail if exists, otherwise try to create it, or use full image
if (file_exists($thumbnail)) {
$display_image = $thumbnail;
} else if ($can_create_thumbs && make_thumb($image_path, $thumbnail, $thumbnailSizeX)) {
$display_image = $thumbnail;
flush();
} else {
// Can't create thumbnail, use full image
$display_image = $image_path;
}

// Extract time from filename (YYYYMMDD_HHMMSS.jpg)
$time = '';
if (preg_match('/\d{8}_(\d{2})(\d{2})(\d{2})/', $image, $matches)) {
$time = $matches[1] . ':' . $matches[2] . ':' . $matches[3];
}

echo "<a href='$image_path'><div class='day-container'><div class='img-text'>";
echo "<div class='image-container'>";
echo "<img src='$display_image' title='$image'/>";
echo "</div>";
echo "<div class='day-text'>$time</div>";
echo "</div></div></a>\n";
}

echo "</div>"; // archived-files
echo "<div class='archived-files-end'></div>";
echo "<div class='archived-files'><hr></div>";
}
?>

<script>
function navigateToDate(date) {
window.location.href = '?date=' + date;
}
</script>

</body>
</html>
15 changes: 14 additions & 1 deletion html/includes/days.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,17 @@ function ListDays()
$i = getValidImageNames(ALLSKY_IMAGES . "/$day/meteors", true); // true == stop after 1
$has_meteors = (count($i) > 0);
}
// Check for archived images in images/YYYY/YYYYMMDD/
$year = substr($day, 0, 4);
$archived_dir = ALLSKY_IMAGES . "/images/$year/$day";
$has_archived_images = false;
if (is_dir($archived_dir)) {
$ai = getValidImageNames($archived_dir, true); // true == stop after 1
$has_archived_images = (count($ai) > 0);
}


if (! $has_images && ! $has_timelapse && ! $has_keogram && ! $has_startrails && ! $has_meteors) {
if (! $has_images && ! $has_timelapse && ! $has_keogram && ! $has_startrails && ! $has_meteors && ! $has_archived_images) {
echo "<script>console.log('Directory \"$day\" has no images, timelapse, et.al.; ignoring.');</script>";
continue;
}
Expand All @@ -119,6 +128,10 @@ function ListDays()
echo "none";
}
echo "</td>\n";
if ($has_archived_images) {
$icon_archive = "<i class='fa fa-archive fa-{$fa_size} fa-fw' style='color:#888'></i>";
echo " <a href='index.php?page=list_images&day=$day&archived=1' title='Archived Images'>$icon_archive</a>";
}

echo "\t\t\t<td>";
if ($has_timelapse) {
Expand Down
25 changes: 20 additions & 5 deletions html/includes/images.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,22 @@ function ListImages() {
return;
}

$dir = ALLSKY_IMAGES . "/$chosen_day";
// Check if we should display archived images
$archived = getVariableOrDefault($_GET, 'archived', 0);

if ($archived == 1) {
// Display from images/YYYY/YYYYMMDD/
$year = substr($chosen_day, 0, 4);
$dir = ALLSKY_IMAGES . "/images/$year/$chosen_day";
$web_path = "/images/images/$year/$chosen_day";
$title_suffix = " (Archived)";
} else {
// Display from regular day directory
$dir = ALLSKY_IMAGES . "/$chosen_day";
$web_path = "/images/$chosen_day";
$title_suffix = "";
}

$images = getValidImageNames($dir, false); // false == get whole list
if (count($images) > 0) {
if ($imagesSortOrder === "descending") {
Expand All @@ -48,7 +63,7 @@ function ListImages() {
}

if (count($images) == 0) {
DisplayImageError("Displaying images", "There are no images for $chosen_day");
DisplayImageError("Displaying images", "There are no images for $chosen_day$title_suffix");
}

$width = getVariableOrDefault($settings_array, 'thumbnailsizex', 100);
Expand All @@ -57,8 +72,8 @@ function ListImages() {
echo '<div id="lightgallery">';
foreach ($images as $image) {

echo "<a href='/images/$chosen_day/$image' data-lg-size='1600-2400'>";
echo " <img alt='$image' width=$width height=$height src='/images/$chosen_day/thumbnails/$image' loading='lazy' decoding='async' fetchpriority='low' />";
echo "<a href='$web_path/$image' data-lg-size='1600-2400'>";
echo " <img alt='$image' width=$width height=$height src='$web_path/thumbnails/$image' loading='lazy' decoding='async' fetchpriority='low' />";
echo '</a>';

}
Expand Down Expand Up @@ -88,4 +103,4 @@ function ListImages() {

}

?>
?>
83 changes: 83 additions & 0 deletions scripts/saveImage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,89 @@ if [[ ${IMG_UPLOAD_FREQUENCY} -gt 0 ]]; then
fi
fi


# Upload archived images to remote website if enabled
if [[ ${S_uploadimages} == "true" && ${SAVE_IMAGE} == "true" && ${IMG_UPLOAD_FREQUENCY} -gt 0 ]]; then
# Check if we should upload this image based on frequency
UPLOAD_COUNTER_FILE="${ALLSKY_TMP}/image_upload_counter.txt"
if [[ ! -f ${UPLOAD_COUNTER_FILE} ]]; then
echo "0" > "${UPLOAD_COUNTER_FILE}"
fi
UPLOAD_COUNTER=$(<"${UPLOAD_COUNTER_FILE}")
UPLOAD_COUNTER=$((UPLOAD_COUNTER + 1))

if [[ ${UPLOAD_COUNTER} -ge ${IMG_UPLOAD_FREQUENCY} ]]; then
echo "0" > "${UPLOAD_COUNTER_FILE}"

# Extract year from DATE_NAME (first 4 characters: YYYYMMDD -> YYYY)
YEAR="${DATE_NAME:0:4}"
REMOTE_IMAGE_DIR="images/${YEAR}/${DATE_NAME}"
IMAGE_BASENAME="$(basename "${CURRENT_IMAGE}")"

# Try to upload the image directly
UPLOAD_SUCCESS="false"
if upload_all --remote-web "${CURRENT_IMAGE}" "${REMOTE_IMAGE_DIR}" "${IMAGE_BASENAME}" "ArchivedImage" 2>/dev/null; then
if upload_all --remote-server "${CURRENT_IMAGE}" "${REMOTE_IMAGE_DIR}" "${IMAGE_BASENAME}" "ArchivedImage" 2>/dev/null; then
UPLOAD_SUCCESS="true"
fi
fi

# If upload failed, add to queue
if [[ ${UPLOAD_SUCCESS} == "false" ]]; then
# Check RAMFS available space before saving to queue
RAMFS_AVAILABLE=$(df -k "${ALLSKY_TMP}" | tail -1 | awk '{print $4}')
RAMFS_THRESHOLD=102400 # 100MB in KB

if [[ ${RAMFS_AVAILABLE} -lt ${RAMFS_THRESHOLD} ]]; then
# Not enough space, log alert
ALERT_MSG="$(date '+%Y-%m-%d %H:%M:%S') - WARNING: RAMFS space below 100MB threshold (${RAMFS_AVAILABLE} KB available). Cannot queue image upload."
echo "${ALERT_MSG}" >> "${ALLSKY_TMP}/image_upload_alerts.log"
echo "${ALERT_MSG}" >&2

# Call allskyNotify.sh if available
if [[ -x "${ALLSKY_SCRIPTS}/allskyNotify.sh" ]]; then
"${ALLSKY_SCRIPTS}/allskyNotify.sh" "WARNING" "RAMFS space low" "${ALERT_MSG}"
fi
else
# Enough space, save to queue
QUEUE_DIR="${ALLSKY_TMP}/image_upload_queue/${YEAR}/${DATE_NAME}"
mkdir -p "${QUEUE_DIR}"
cp "${CURRENT_IMAGE}" "${QUEUE_DIR}/${IMAGE_BASENAME}"
fi
fi
else
echo "${UPLOAD_COUNTER}" > "${UPLOAD_COUNTER_FILE}"
fi

# Process the retry queue
QUEUE_BASE="${ALLSKY_TMP}/image_upload_queue"
if [[ -d ${QUEUE_BASE} ]]; then
# Find all queued images and try to upload them
while IFS= read -r -d '' QUEUED_IMAGE; do
# Extract path components: queue/YYYY/YYYYMMDD/filename.jpg
RELATIVE_PATH="${QUEUED_IMAGE#${QUEUE_BASE}/}"
QUEUE_YEAR="$(echo "${RELATIVE_PATH}" | cut -d'/' -f1)"
QUEUE_DATE="$(echo "${RELATIVE_PATH}" | cut -d'/' -f2)"
QUEUE_FILENAME="$(basename "${QUEUED_IMAGE}")"
QUEUE_REMOTE_DIR="images/${QUEUE_YEAR}/${QUEUE_DATE}"

# Try to upload
if upload_all --remote-web "${QUEUED_IMAGE}" "${QUEUE_REMOTE_DIR}" "${QUEUE_FILENAME}" "QueuedImage" 2>/dev/null; then
if upload_all --remote-server "${QUEUED_IMAGE}" "${QUEUE_REMOTE_DIR}" "${QUEUE_FILENAME}" "QueuedImage" 2>/dev/null; then
# Upload successful, remove from queue
rm -f "${QUEUED_IMAGE}"

# Remove empty directories
QUEUE_DATE_DIR="${QUEUE_BASE}/${QUEUE_YEAR}/${QUEUE_DATE}"
QUEUE_YEAR_DIR="${QUEUE_BASE}/${QUEUE_YEAR}"
[[ -d ${QUEUE_DATE_DIR} ]] && rmdir --ignore-fail-on-non-empty "${QUEUE_DATE_DIR}"
[[ -d ${QUEUE_YEAR_DIR} ]] && rmdir --ignore-fail-on-non-empty "${QUEUE_YEAR_DIR}"
fi
fi
done < <(find "${QUEUE_BASE}" -type f -name "*.jpg" -print0)
fi
fi

# If needed, upload the mini timelapse. If the upload failed above, it will likely fail below.
if [[ ${TIMELAPSE_MINI_UPLOAD_VIDEO} == "true" && ${SAVE_IMAGE} == "true" && ${RET} -eq 0 ]] ; then
FILE_TO_UPLOAD="${ALLSKY_MINITIMELAPSE_FILE}"
Expand Down