<?php

$requested_action = '';
if (isset($_REQUEST['action'])) {
	$requested_action = (string)$_REQUEST['action'];
} elseif (isset($action)) {
	$requested_action = (string)$action;
}

if( !defined( 'DATALIFEENGINE' ) OR ( !defined( 'LOGGED_IN' ) AND $requested_action !== 'bulkupload' ) ) {
	header( "HTTP/1.1 403 Forbidden" );
	header ( 'Location: ../../' );
	die( "Hacking attempt!" );
}

if( $requested_action !== 'bulkupload' ) {
	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		msg( "error", $lang['index_denied'], $lang['index_denied'] );
	}
}

function dle_wallpaper_clean_title( $value ) {
	$value = trim((string)$value);
	if( $value === '' ) return '';

	$value = preg_replace('~^.*[\\\\/]~', '', $value);
	$value = preg_replace('~\\.[^.]+$~', '', $value);
	$value = str_replace('_', ' ', $value);
	$value = dle_wallpaper_sanitize_title_text($value);

	return $value;
}

function dle_wallpaper_sanitize_title_text( $value ) {
	$value = (string)$value;
	$value = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
	// Replace common problematic punctuation with spaces
	$value = preg_replace('~[\[\]\(\)\{\}<>`"\'\\|/\\:;,*?=#+%]+~u', ' ', $value);
	// Keep letters/numbers/spaces and a few safe separators
	$value = preg_replace('~[^\p{L}\p{N}\s\-_.]+~u', ' ', $value);
	$value = preg_replace('/\s+/u', ' ', $value);
	return trim($value);
}

function dle_wallpaper_generate_auto_tags( $title, $type ) {
	$title = (string)$title;
	$type = (string)$type;

	$base_map = array(
		'image' => 'wallpapers',
		'audio' => 'ringtones',
		'video' => 'videos',
	);

	$tags = array();
	if (isset($base_map[$type])) {
		$tags[] = $base_map[$type];
	}

	$title = dle_wallpaper_sanitize_title_text($title);
	if ($title !== '') {
		$lower = function_exists('dle_strtolower') ? dle_strtolower($title) : mb_strtolower($title, 'UTF-8');
		$lower = str_replace(array('-', '_', '.'), ' ', $lower);
		$words = preg_split('/\s+/u', trim($lower), -1, PREG_SPLIT_NO_EMPTY);

		$stop = array(
			'the','a','an','and','or','of','to','in','on','for','with','from','by','at','is','are','be','this','that','these','those',
			'wallpaper','wallpapers','video','videos','ringtone','ringtones',
			'free','hd','uhd','4k','8k','1080p','720p','iphone','android','mobile','desktop','pc','download','new','best'
		);

		// Optional topic keywords: if present anywhere in title, include as tags.
		$topics = array('space','nature','flowers','flower','animals','animal','cars','car','anime','abstract','football','sports','music','love');
		$topic_found = array();
		foreach ($words as $w) {
			$w = trim((string)$w);
			if ($w === '') continue;
			if (in_array($w, $topics, true) && !in_array($w, $stop, true)) {
				$topic_found[] = $w;
			}
		}
		if (count($topic_found)) {
			$topic_found = array_values(array_unique($topic_found));
			foreach ($topic_found as $w) {
				$tags[] = $w;
			}
		}

		$keywords = array();
		for ($i = count($words) - 1; $i >= 0; $i--) {
			$w = trim((string)$words[$i]);
			if ($w === '') continue;
			if (preg_match('/^\d+$/', $w)) continue;
			if (function_exists('dle_strlen')) {
				if (dle_strlen($w) < 3) continue;
			} else {
				if (mb_strlen($w, 'UTF-8') < 3) continue;
			}
			if (in_array($w, $stop, true)) continue;
			$keywords[] = $w;
			if (count($keywords) >= 1) break;
		}
		$keywords = array_reverse($keywords);
		foreach ($keywords as $w) {
			$tags[] = $w;
		}
	}

	// Deduplicate case-insensitively and enforce tag length
	$out = array();
	$seen = array();
	foreach ($tags as $t) {
		$t = trim((string)$t);
		if ($t === '') continue;
		$key = function_exists('dle_strtolower') ? dle_strtolower($t) : mb_strtolower($t, 'UTF-8');
		if (isset($seen[$key])) continue;
		$seen[$key] = 1;
		if (function_exists('dle_substr')) {
			$t = dle_substr($t, 0, 100);
		} else {
			$t = mb_substr($t, 0, 100, 'UTF-8');
		}
		$out[] = $t;
		if (count($out) >= 10) break;
	}

	return implode(', ', $out);
}

function dle_wallpaper_parse_tags_list( $tags ) {
	$tags = (string)$tags;
	if( $tags === '' ) return array();
	$parts = explode(',', $tags);
	$out = array();
	$seen = array();
	foreach( $parts as $p ) {
		$p = trim((string)$p);
		if( $p === '' ) continue;
		$key = function_exists('dle_strtolower') ? dle_strtolower($p) : mb_strtolower($p, 'UTF-8');
		$key = trim($key);
		if( $key === '' ) continue;
		if( isset($seen[$key]) ) continue;
		$seen[$key] = 1;
		$out[] = $p;
	}
	return $out;
}

function dle_wallpaper_merge_tags_string( $existing, $add ) {
	$existing_list = dle_wallpaper_parse_tags_list($existing);
	$add_list = dle_wallpaper_parse_tags_list($add);
	if( !count($add_list) ) return implode(', ', $existing_list);

	$seen = array();
	$out = array();
	foreach( $existing_list as $t ) {
		$key = function_exists('dle_strtolower') ? dle_strtolower($t) : mb_strtolower($t, 'UTF-8');
		$seen[$key] = 1;
		$out[] = $t;
	}
	foreach( $add_list as $t ) {
		$key = function_exists('dle_strtolower') ? dle_strtolower($t) : mb_strtolower($t, 'UTF-8');
		if( isset($seen[$key]) ) continue;
		$seen[$key] = 1;
		$out[] = $t;
	}

	return implode(', ', $out);
}

function dle_wallpaper_get_image_url_from_xfvalue( $xfvalue ) {
	global $config;

	$xfvalue = (string)$xfvalue;
	$parts = explode('|', $xfvalue);
	$first = isset($parts[0]) ? (string)$parts[0] : '';
	$first = trim($first);

	if( !$first ) return '';

	if( preg_match('~^https?://~i', $first) ) {
		return $first;
	}

	$first = ltrim($first, '/');
	return rtrim($config['http_home_url'], '/') . '/uploads/posts/' . $first;
}

function dle_wallpaper_build_xfields_string( $fields ) {
	$fields = is_array($fields) ? $fields : [];
	if( !count($fields) ) return '';

	$filecontents = [];
	foreach( $fields as $name => $value ) {
		if( $value === null ) continue;
		$value = (string)$value;
		if( $value === '' ) continue;

		$name = (string)$name;
		$name = str_replace("|", "&#124;", $name);
		$name = str_replace("\r", "", $name);
		$name = str_replace("\n", "__NEWL__", $name);

		$value = str_replace("|", "&#124;", $value);
		$value = str_replace("\r", "", $value);
		$value = str_replace("\n", "__NEWL__", $value);

		$filecontents[] = $name . "|" . $value;
	}

	if( !count($filecontents) ) return '';
	return implode("||", $filecontents);
}

function dle_wallpaper_extract_first_image_url( $content ) {
	$content = (string)$content;
	if( $content === '' ) return '';

	if( preg_match('~\[img\]\s*([^\[]+?)\s*\[/img\]~is', $content, $m) ) {
		return trim((string)$m[1]);
	}

	if( preg_match('~<img[^>]+(?:data-src|src)\s*=\s*([\'\"])\s*([^\'\"]+?)\s*\\1~i', $content, $m) ) {
		return trim((string)$m[2]);
	}

	if( preg_match('~<img[^>]+(?:data-src|src)\s*=\s*([^\s>]+)~i', $content, $m) ) {
		return trim((string)$m[1], "\"' ");
	}

	return '';
}

function dle_wallpaper_url_to_abs_path( $url ) {
	$url = (string)$url;
	if( $url === '' ) return '';
	$path = parse_url($url, PHP_URL_PATH);
	if( !$path ) return '';
	$path = '/' . ltrim((string)$path, '/');
	return rtrim(ROOT_DIR, '/\\') . str_replace('/', DIRECTORY_SEPARATOR, $path);
}

function dle_wallpaper_try_get_medium_url( $url ) {
	global $config;
	$url = (string)$url;
	if( $url === '' ) return '';

	$path = parse_url($url, PHP_URL_PATH);
	if( !$path ) return '';
	$path = '/' . ltrim((string)$path, '/');

	// Only for wallpapers stored under uploads/public_files/wallpapers/
	if( stripos($path, '/uploads/public_files/wallpapers/') === false ) return '';
	if( stripos($path, '/uploads/public_files/wallpapers/medium/') !== false ) return $url;
	if( stripos($path, '/uploads/public_files/wallpapers/thumbs/') !== false ) {
		$path = str_ireplace('/uploads/public_files/wallpapers/thumbs/', '/uploads/public_files/wallpapers/', $path);
	}

	$dir = dirname($path);
	$base = basename($path);
	$mediumPath = rtrim($dir, '/') . '/medium/' . $base;
	$mediumAbs = rtrim(ROOT_DIR, '/\\') . str_replace('/', DIRECTORY_SEPARATOR, $mediumPath);
	if( !is_file($mediumAbs) ) return '';
	return rtrim($config['http_home_url'], '/') . $mediumPath;
}

function dle_wallpaper_parse_xfields_string( $xfields ) {
	$xfields = (string)$xfields;
	if( $xfields === '' ) return array();
	$result = array();
	$items = explode('||', $xfields);
	foreach( $items as $item ) {
		$item = (string)$item;
		if( $item === '' ) continue;
		$pair = explode('|', $item, 2);
		if( !isset($pair[0]) ) continue;
		$name = (string)$pair[0];
		$value = isset($pair[1]) ? (string)$pair[1] : '';
		$name = str_replace("&#124;", "|", $name);
		$name = str_replace("__NEWL__", "\n", $name);
		$value = str_replace("&#124;", "|", $value);
		$value = str_replace("__NEWL__", "\n", $value);
		if( $name !== '' ) $result[$name] = $value;
	}
	return $result;
}

function dle_wallpaper_bootstrap_thumb_lib() {
	$autoload = ENGINE_DIR . '/classes/composer/vendor/autoload.php';
	if( !class_exists('\\Intervention\\Image\\ImageManagerStatic', false) && is_file($autoload) ) {
		include_once $autoload;
	}

	if( !class_exists('thumbnail', false) ) {
		include_once (DLEPlugins::Check(ENGINE_DIR . '/classes/thumb.class.php'));
	}

	return class_exists('thumbnail', false) && class_exists('\\Intervention\\Image\\ImageManagerStatic');
}

if( !isset($action) ) $action = '';

if( $action == "addwallpaper" ) {

	$duplicateprotection = UniqIDReal(32);

	$js_array[] = "public/fileuploader/plupload/plupload.full.min.js";
	$js_array[] = "public/fileuploader/plupload/i18n/{$lang['language_code']}.js";

	if($lang['direction'] == 'rtl') $rtl_prefix ='_rtl'; else $rtl_prefix = '';
	$css_array[] = "public/fileuploader/fileuploader{$rtl_prefix}.css";

	echoheader( "<i class=\"fa fa-picture-o position-left\"></i><span class=\"text-semibold\">Bulk Wallpaper Upload</span>", "Bulk Wallpaper Upload" );

	$author = $member_id['name'];

	$categories_list = CategoryNewsSelection( 0, 0 );
	if( $config['allow_multi_category'] ) {
		$category_multiple = "class=\"categoryselect\" multiple";
		$category_size = " size=\"10\"";
	} else {
		$category_multiple = "class=\"uniform\" data-live-search=\"true\" data-none-results-text=\"{$lang['addnews_cat_fault']}\" data-width=\"350\"";
		$category_size = "";
	}

	$fix_input = ( $user_group[$member_id['user_group']]['allow_fixed'] AND $config['allow_fixed'] )
		? "<div class=\"checkbox\"><label><input class=\"icheck\" type=\"checkbox\" id=\"news_fixed\" name=\"news_fixed\" value=\"1\">{$lang['addnews_fix']}</label></div>"
		: "";

	$main_input = $user_group[$member_id['user_group']]['allow_main']
		? "<div class=\"checkbox\" id=\"opt_holder_main\"><label><input class=\"icheck\" type=\"checkbox\" id=\"allow_main\" name=\"allow_main\" value=\"1\" checked>{$lang['addnews_main']}</label></div>"
		: "";

	if( $member_id['user_group'] < 3 ) {
		$disable_index = "<div class=\"checkbox\"><label><input class=\"icheck\" type=\"checkbox\" id=\"disable_index\" name=\"disable_index\" value=\"1\">{$lang['add_disable_index']}</label></div>";
		$disable_search = "<div class=\"checkbox\"><label><input class=\"icheck\" type=\"checkbox\" id=\"disable_search\" name=\"disable_search\" value=\"1\">{$lang['cat_d_search']}</label></div>";

		if( $config['allow_yandex_dzen'] ) {
			$yandex_dzen = "<div class=\"checkbox\"><label><input class=\"icheck\" type=\"checkbox\" name=\"allow_rss_dzen\" id=\"allow_rss_dzen\" value=\"1\" checked>{$lang['allow_rss_dzen']}</label></div>";
		} else {
			$yandex_dzen = "";
		}

		if( $config['allow_rss'] ) {
			$rss_option = <<<HTML
				<div class="row mt-15" id="opt_cat_rss">
					<div class="col-sm-6" style="max-width:21.67em;">
						<div class="checkbox"><label><input class="icheck" type="checkbox" name="allow_rss" value="1" checked>{$lang['allow_rss_news']}</label></div>
					</div>
					<div class="col-sm-6">
						{$yandex_dzen}
					</div>
				</div>
HTML;
		} else {
			$rss_option = "";
		}
	} else {
		$disable_index = "";
		$disable_search = "";
		$rss_option = "";
	}

	echo <<<HTML
<div class="panel panel-default">
	<div class="panel-heading">
		<h6 class="panel-title">Bulk Wallpaper Upload</h6>
	</div>
	<form method="post" name="addwallpaper" id="addwallpaper" class="form-horizontal">
		<div class="panel-body">
			<div class="form-group">
				<label class="control-label col-sm-2">{$lang['addnews_cat']}</label>
				<div class="col-sm-10">
					<select data-placeholder="{$lang['addnews_cat_sel']}" title="{$lang['addnews_cat_sel']}" name="category[]" id="category" {$category_multiple}{$category_size} style="width:100%;max-width:24.3em;">{$categories_list}</select>
				</div>
			</div>
			<div class="form-group">
				<label class="control-label col-sm-2">{$lang['addnews_option']}</label>
				<div class="col-sm-10">
					<div class="row">
						<div class="col-sm-6" style="max-width:21.67em;">
							<div class="checkbox"><label><input class="icheck" type="checkbox" id="approve" name="approve" value="1" checked>{$lang['addnews_mod']}</label></div>
							{$main_input}
							<div class="checkbox" id="opt_holder_rating"><label><input class="icheck" type="checkbox" id="allow_rating" name="allow_rating" value="1" checked>{$lang['addnews_allow_rate']}</label></div>
						</div>
						<div class="col-sm-6">
							<div class="checkbox" id="opt_holder_comments"><label><input class="icheck" type="checkbox" id="allow_comm" name="allow_comm" value="1" checked>{$lang['addnews_comm']}</label></div>
							{$fix_input}
							{$disable_index}
							{$disable_search}
						</div>
					</div>
					{$rss_option}
				</div>
			</div>

			<div class="form-group">
				<label class="control-label col-sm-2">Files</label>
				<div class="col-sm-10">
					<div class="panel panel-default" style="margin-bottom:12px;">
						<div class="panel-heading"><h6 class="panel-title">Server ZIP inbox (bulk images)</h6></div>
						<div class="panel-body">
							<div class="alert alert-info" style="margin-bottom:10px;">
								Put a <b>.zip</b> archive into: <span class="text-semibold">uploads/public_files/bulkimg/</span><br>
								Then click <b>Refresh</b> and <b>Register &amp; Extract</b>.
							</div>
							<div class="row" style="margin-bottom:10px;">
								<div class="col-sm-6" style="max-width:28em;">
									<select class="form-control" id="bulk_zip_select"></select>
								</div>
								<div class="col-sm-6">
									<button type="button" class="btn btn-sm bg-teal" id="bulk_zip_refresh"><i class="fa fa-refresh position-left"></i>Refresh</button>
									<button type="button" class="btn btn-sm bg-teal" id="bulk_zip_register"><i class="fa fa-cloud-download position-left"></i>Register &amp; Extract</button>
									<label style="margin-left:12px;">
										<input type="checkbox" id="bulk_zip_delete" checked> Delete ZIP after unzip
									</label>
								</div>
							</div>
							<div id="bulk_zip_status" class="alert alert-info" style="display:none;"></div>
						</div>
					</div>

					<div class="alert alert-info" style="margin-bottom:12px;">
						Supported: <b>jpg, jpeg, png, gif, webp, bmp, avif</b>, <b>mp4</b>, and <b>mp3, wav, ogg, m4a, aac</b>.
					</div>
					<div id="bulk_uploader" class="xfieldsfile_uploader">
						<button type="button" class="qq-upload-button btn btn-sm bg-teal btn-raised" id="bulk_upload_button"><i class="fa fa-upload position-left"></i>Select files</button>
						<div class="qq-upload-drop-area drop-area-absolute" id="bulk_drop_area"><span>Drop files here</span></div>
						<div class="xfieldsfile_uploader_message" id="bulk_status" style="margin-top:10px;display:none;"></div>
						<div class="xfieldsfile_uploader_list" id="bulk_filelist" style="margin-top:10px;"></div>
					</div>
				</div>
			</div>

			<div class="form-group">
				<label class="control-label col-sm-2">Items</label>
				<div class="col-sm-10">
					<div class="table-responsive">
						<table class="table table-striped table-xs" id="bulk_items_table">
							<thead>
								<tr>
									<th style="width:38px;"></th>
									<th>File</th>
									<th style="width:120px;">Type</th>
									<th>Post title</th>
									<th style="width:90px;"></th>
								</tr>
							</thead>
							<tbody></tbody>
						</table>
					</div>
					<div id="bulk_items_empty" class="text-muted" style="padding:8px 0;">No items uploaded yet.</div>
					<div id="bulk_hidden_fields"></div>
				</div>
			</div>

		</div>
		<div class="panel-footer">
			<button type="submit" class="btn bg-teal btn-sm btn-raised position-left"><i class="fa fa-floppy-o position-left"></i>Create posts</button>
			<input type="hidden" name="mod" value="addwallpaper">
			<input type="hidden" name="action" value="doaddwallpaper">
			<input type="hidden" name="duplicateprotection" value="{$duplicateprotection}">
			<input type="hidden" name="user_hash" value="{$dle_login_hash}">
		</div>
	</form>
</div>

<div class="panel panel-default">
	<div class="panel-heading">
		<h6 class="panel-title">Fix blurry thumbnails (backfill XFields)</h6>
	</div>
	<div class="panel-body">
		<div class="alert alert-info" style="margin-bottom:10px;">
			This will scan existing posts and fill <b>image_url_poster</b> (and wallpaper URL fields) in <b>xfields</b>.
			Useful for old posts created before the uploader started writing xfields.
		</div>
		<button type="button" class="btn btn-sm bg-teal" id="bulk_backfill_xfields">
			<i class="fa fa-magic position-left"></i>Backfill XFields for old posts
		</button>
		<div id="bulk_backfill_status" class="alert alert-info" style="display:none;margin-top:10px;"></div>
	</div>
</div>

<div class="panel panel-default">
	<div class="panel-heading">
		<h6 class="panel-title">Backfill Tags (auto-generate wallpapers/ringtones/videos + keywords)</h6>
	</div>
	<div class="panel-body">
		<div class="alert alert-info" style="margin-bottom:10px;">
			This will scan existing posts and auto-generate tags based on media type and title keywords.
			It also syncs the <b>_tags</b> table for approved posts so counters are correct.
		</div>
		<button type="button" class="btn btn-sm bg-teal" id="bulk_backfill_tags">
			<i class="fa fa-tags position-left"></i>Backfill tags for old posts
		</button>
		<div id="bulk_backfill_tags_status" class="alert alert-info" style="display:none;margin-top:10px;"></div>
	</div>
</div>

<div class="panel panel-default">
	<div class="panel-heading">
		<h6 class="panel-title">Fix download links/counters (backfill file IDs)</h6>
	</div>
	<div class="panel-body">
		<div class="alert alert-info" style="margin-bottom:10px;">
			Downloads by ID like zmusic (<b>?do=download&id=...</b>) require storing file IDs.
			This can upgrade existing posts to store IDs for <b>mp3</b>, <b>video_url</b>, and <b>wallpaper</b>.
		</div>
		<button type="button" class="btn btn-sm bg-teal" id="bulk_backfill_audio">
			<i class="fa fa-music position-left"></i>Backfill mp3 IDs
		</button>
		<button type="button" class="btn btn-sm bg-teal" id="bulk_backfill_video" style="margin-left:6px;">
			<i class="fa fa-film position-left"></i>Backfill video IDs
		</button>
		<button type="button" class="btn btn-sm bg-teal" id="bulk_backfill_wallpaper" style="margin-left:6px;">
			<i class="fa fa-image position-left"></i>Backfill wallpaper IDs
		</button>
		<div id="bulk_backfill_audio_status" class="alert alert-info" style="display:none;margin-top:10px;"></div>
		<div id="bulk_backfill_video_status" class="alert alert-info" style="display:none;margin-top:10px;"></div>
		<div id="bulk_backfill_wallpaper_status" class="alert alert-info" style="display:none;margin-top:10px;"></div>
	</div>
</div>
<script>
<!--
jQuery(function($){
(function(){
	var userHash = "{$dle_login_hash}";
	var userId = "{$member_id['user_id']}";
	var uploadUrl = "admin.php?mod=addwallpaper&action=bulkupload";
	var itemIndex = 0;
	var deleteZipAfter = true;

	function escapeHtml(s) {
		return String(s || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#039;');
	}

	function setStatus(msg, isError) {
		var box = document.getElementById('bulk_status');
		if (!box) return;
		if (!msg) { box.style.display = 'none'; box.innerHTML = ''; return; }
		box.style.display = 'block';
		box.className = 'xfieldsfile_uploader_message ' + (isError ? 'alert alert-danger' : 'alert alert-info');
		box.innerHTML = msg;
	}

	function cleanTitle(name) {
		name = (name || '').toString();
		name = name.replace(/^.*[\\/]/, '');
		name = name.replace(/\.[^.]+$/, '');
		name = name.replace(/[`"'\u2018\u2019\u201C\u201D\(\)\[\]\{\}<>\\|\/:;,*?=#+%]+/g, ' ');
		name = name.replace(/_+/g, ' ').replace(/\s+/g, ' ').trim();
		return name;
	}

	function renderHiddenInputs() {
		var hidden = document.getElementById('bulk_hidden_fields');
		if (!hidden) return;
		hidden.innerHTML = '';

		var rows = document.querySelectorAll('#bulk_items_table tbody tr');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			var idx = row.getAttribute('data-idx');
			var skip = row.getAttribute('data-skip') === '1';
			var titleInput = row.querySelector('input[data-role="title"]');
			var titleVal = titleInput ? titleInput.value : '';
			var xfvalue = row.getAttribute('data-xfvalue') || '';
			var type = row.getAttribute('data-type') || '';
			var original = row.getAttribute('data-original') || '';

			var fields = [
				['type', type],
				['xfvalue', xfvalue],
				['title', titleVal],
				['original', original],
				['skip', skip ? '1' : '0']
			];
			for (var f = 0; f < fields.length; f++) {
				var input = document.createElement('input');
				input.type = 'hidden';
				input.name = 'items['+idx+']['+fields[f][0]+']';
				input.value = fields[f][1];
				hidden.appendChild(input);
			}
		}
	}

	function updateEmptyState() {
		var tbody = document.querySelector('#bulk_items_table tbody');
		var empty = document.getElementById('bulk_items_empty');
		if (!tbody || !empty) return;
		empty.style.display = tbody.children.length ? 'none' : 'block';
	}

	function addItemRow(item) {
		var tbody = document.querySelector('#bulk_items_table tbody');
		if (!tbody) return;

		var idx = String(itemIndex++);
		var tr = document.createElement('tr');
		tr.setAttribute('data-idx', idx);
		tr.setAttribute('data-type', item.type || '');
		tr.setAttribute('data-xfvalue', item.xfvalue || '');
		tr.setAttribute('data-original', item.original || '');
		tr.setAttribute('data-skip', '0');

		var inferredType = (item.type || '').toString();
		if (!inferredType) {
			var nameLower = ((item.original || '') + '').toLowerCase();
			var xfFirst = ((item.xfvalue || '') + '').split('|')[0].toLowerCase();
			if (nameLower.endsWith('.mp4') || xfFirst.endsWith('.mp4')) inferredType = 'video';
			else if (nameLower.match(/\.(mp3|wav|ogg|m4a|aac)$/) || xfFirst.match(/\.(mp3|wav|ogg|m4a|aac)(\?|$)/)) inferredType = 'audio';
			else if (nameLower.match(/\.(jpe?g|png|gif|webp|bmp|avif)$/) || xfFirst.match(/\.(jpe?g|png|gif|webp|bmp|avif)$/)) inferredType = 'image';
		}
		tr.setAttribute('data-type', inferredType);

		var typeLabel = (inferredType === 'video') ? 'Video' : ((inferredType === 'audio') ? 'Audio' : 'Image');
		var title = item.title || cleanTitle(item.original);

		tr.innerHTML =
			'<td><input type="checkbox" data-role="include" checked></td>' +
			'<td>' + escapeHtml(item.original || '') + '</td>' +
			'<td>' + escapeHtml(typeLabel) + '</td>' +
			'<td><input type="text" class="form-control input-sm" data-role="title" value="' + escapeHtml(title) + '"></td>' +
			'<td><button type="button" class="btn btn-xs bg-danger" data-role="remove">Remove</button></td>';

		var includeEl = tr.querySelector('input[data-role="include"]');
		if (includeEl) {
			includeEl.addEventListener('change', function(){
				tr.setAttribute('data-skip', this.checked ? '0' : '1');
				renderHiddenInputs();
			});
		}

		var titleEl = tr.querySelector('input[data-role="title"]');
		if (titleEl) {
			titleEl.addEventListener('input', function(){ renderHiddenInputs(); });
		}

		var removeEl = tr.querySelector('button[data-role="remove"]');
		if (removeEl) {
			removeEl.addEventListener('click', function(){
				tr.parentNode.removeChild(tr);
				updateEmptyState();
				renderHiddenInputs();
			});
		}

		tbody.appendChild(tr);
		updateEmptyState();
		renderHiddenInputs();
	}

	function setZipStatus(msg, isError) {
		var box = document.getElementById('bulk_zip_status');
		if (!box) return;
		if (!msg) { box.style.display = 'none'; box.innerHTML = ''; return; }
		box.style.display = 'block';
		box.className = (isError ? 'alert alert-danger' : 'alert alert-info');
		box.innerHTML = msg;
	}

	function setBackfillStatus(msg, isError) {
		var box = document.getElementById('bulk_backfill_status');
		if (!box) return;
		if (!msg) { box.style.display = 'none'; box.innerHTML = ''; return; }
		box.style.display = 'block';
		box.className = (isError ? 'alert alert-danger' : 'alert alert-info');
		box.innerHTML = msg;
	}

	function setBackfillTagsStatus(msg, isError) {
		var box = document.getElementById('bulk_backfill_tags_status');
		if (!box) return;
		if (!msg) { box.style.display = 'none'; box.innerHTML = ''; return; }
		box.style.display = 'block';
		box.className = (isError ? 'alert alert-danger' : 'alert alert-info');
		box.innerHTML = msg;
	}

	function setBackfillAudioStatus(msg, isError) {
		var box = document.getElementById('bulk_backfill_audio_status');
		if (!box) return;
		if (!msg) { box.style.display = 'none'; box.innerHTML = ''; return; }
		box.style.display = 'block';
		box.className = (isError ? 'alert alert-danger' : 'alert alert-info');
		box.innerHTML = msg;
	}

	function setBackfillVideoStatus(msg, isError) {
		var box = document.getElementById('bulk_backfill_video_status');
		if (!box) return;
		if (!msg) { box.style.display = 'none'; box.innerHTML = ''; return; }
		box.style.display = 'block';
		box.className = (isError ? 'alert alert-danger' : 'alert alert-info');
		box.innerHTML = msg;
	}

	function setBackfillWallpaperStatus(msg, isError) {
		var box = document.getElementById('bulk_backfill_wallpaper_status');
		if (!box) return;
		if (!msg) { box.style.display = 'none'; box.innerHTML = ''; return; }
		box.style.display = 'block';
		box.className = (isError ? 'alert alert-danger' : 'alert alert-info');
		box.innerHTML = msg;
	}

	function runBackfillBatch(lastId, totals) {
		totals = totals || { scanned: 0, updated: 0, skipped: 0 };
		setBackfillStatus('Processing batch...' + (lastId ? (' (lastId=' + lastId + ')') : ''), false);
		var fd = new FormData();
		fd.append('user_hash', userHash);
		if (lastId) fd.append('last_id', String(lastId));
		fd.append('scanned_total', String(totals.scanned || 0));
		fd.append('updated_total', String(totals.updated || 0));
		fd.append('skipped_total', String(totals.skipped || 0));
		fetch('admin.php?mod=addwallpaper&action=backfillxfields', { method: 'POST', body: fd, credentials: 'same-origin' })
			.then(function(r){ return r.text(); })
			.then(function(t){
				var j = null;
				try { j = JSON.parse(t); } catch(e) { j = null; }
				if (!j) { setBackfillStatus('Invalid response: ' + t.slice(0, 300), true); return; }
				if (j.error) { setBackfillStatus(j.error, true); return; }
				totals.scanned = (totals.scanned || 0) + (j.scanned || 0);
				totals.updated = (totals.updated || 0) + (j.updated || 0);
				totals.skipped = (totals.skipped || 0) + (j.skipped || 0);
				setBackfillStatus('Updated: ' + totals.updated + ', skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				if (j.next_last_id && String(j.next_last_id) !== String(lastId || '')) {
					runBackfillBatch(j.next_last_id, totals);
				} else {
					setBackfillStatus('Done. Updated: ' + totals.updated + ', skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				}
			})
			.catch(function(err){ setBackfillStatus('Request failed: ' + (err && err.message ? err.message : String(err)), true); });
	}

	var backfillBtn = document.getElementById('bulk_backfill_xfields');
	if (backfillBtn) {
		backfillBtn.addEventListener('click', function(){
			if (!confirm('Backfill XFields for old posts now? This may take some time.')) return;
			runBackfillBatch('');
		});
	}

	function runBackfillTagsBatch(lastId, totals) {
		totals = totals || { scanned: 0, updated: 0, skipped: 0 };
		setBackfillTagsStatus('Processing batch...' + (lastId ? (' (lastId=' + lastId + ')') : ''), false);
		var fd = new FormData();
		fd.append('user_hash', userHash);
		if (lastId) fd.append('last_id', String(lastId));
		fetch('admin.php?mod=addwallpaper&action=backfilltags', { method: 'POST', body: fd, credentials: 'same-origin' })
			.then(function(r){ return r.text(); })
			.then(function(t){
				var j = null;
				try { j = JSON.parse(t); } catch(e) { j = null; }
				if (!j) { setBackfillTagsStatus('Invalid response: ' + t.slice(0, 300), true); return; }
				if (j.error) { setBackfillTagsStatus(j.error, true); return; }
				totals.scanned = (totals.scanned || 0) + (j.scanned || 0);
				totals.updated = (totals.updated || 0) + (j.updated || 0);
				totals.skipped = (totals.skipped || 0) + (j.skipped || 0);
				setBackfillTagsStatus('Updated: ' + totals.updated + ', skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				if (j.next_last_id && String(j.next_last_id) !== String(lastId || '')) {
					runBackfillTagsBatch(j.next_last_id, totals);
				} else {
					setBackfillTagsStatus('Done. Updated: ' + totals.updated + ', skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				}
			})
			.catch(function(err){ setBackfillTagsStatus('Request failed: ' + (err && err.message ? err.message : String(err)), true); });
	}

	var backfillTagsBtn = document.getElementById('bulk_backfill_tags');
	if (backfillTagsBtn) {
		backfillTagsBtn.addEventListener('click', function(){
			if (!confirm('Backfill auto tags for old posts now? This may take some time.')) return;
			runBackfillTagsBatch('');
		});
	}

	function runBackfillAudioBatch(lastId, totals) {
		totals = totals || { scanned: 0, updated: 0, skipped: 0, fixed: 0, missing_file: 0 };
		setBackfillAudioStatus('Processing batch...' + (lastId ? (' (lastId=' + lastId + ')') : ''), false);
		var fd = new FormData();
		fd.append('user_hash', userHash);
		if (lastId) fd.append('last_id', String(lastId));
		fetch('admin.php?mod=addwallpaper&action=backfillaudio', { method: 'POST', body: fd, credentials: 'same-origin' })
			.then(function(r){ return r.text(); })
			.then(function(t){
				var j = null;
				try { j = JSON.parse(t); } catch(e) { j = null; }
				if (!j) { setBackfillAudioStatus('Invalid response: ' + t.slice(0, 300), true); return; }
				if (j.error) { setBackfillAudioStatus(j.error, true); return; }
				totals.scanned += (j.scanned || 0);
				totals.updated += (j.updated || 0);
				totals.skipped += (j.skipped || 0);
				totals.fixed += (j.fixed || 0);
				totals.missing_file += (j.missing_file || 0);
				setBackfillAudioStatus('Updated: ' + totals.updated + ' (fixed: ' + totals.fixed + ', missing file: ' + totals.missing_file + '), skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				if (j.next_last_id && String(j.next_last_id) !== String(lastId || '')) {
					runBackfillAudioBatch(j.next_last_id, totals);
				} else {
					setBackfillAudioStatus('Done. Updated: ' + totals.updated + ' (fixed: ' + totals.fixed + ', missing file: ' + totals.missing_file + '), skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				}
			})
			.catch(function(err){ setBackfillAudioStatus('Request failed: ' + (err && err.message ? err.message : String(err)), true); });
	}

	var backfillAudioBtn = document.getElementById('bulk_backfill_audio');
	if (backfillAudioBtn) {
		backfillAudioBtn.addEventListener('click', function(){
			if (!confirm('Backfill mp3 file IDs for old posts now? This may take some time.')) return;
			runBackfillAudioBatch('');
		});
	}

	function runBackfillVideoBatch(lastId, totals) {
		totals = totals || { scanned: 0, updated: 0, skipped: 0, fixed: 0, missing_file: 0 };
		setBackfillVideoStatus('Processing batch...' + (lastId ? (' (lastId=' + lastId + ')') : ''), false);
		var fd = new FormData();
		fd.append('user_hash', userHash);
		if (lastId) fd.append('last_id', String(lastId));
		fetch('admin.php?mod=addwallpaper&action=backfillvideo', { method: 'POST', body: fd, credentials: 'same-origin' })
			.then(function(r){ return r.text(); })
			.then(function(t){
				var j = null;
				try { j = JSON.parse(t); } catch(e) { j = null; }
				if (!j) { setBackfillVideoStatus('Invalid response: ' + t.slice(0, 300), true); return; }
				if (j.error) { setBackfillVideoStatus(j.error, true); return; }
				totals.scanned += (j.scanned || 0);
				totals.updated += (j.updated || 0);
				totals.skipped += (j.skipped || 0);
				totals.fixed += (j.fixed || 0);
				totals.missing_file += (j.missing_file || 0);
				setBackfillVideoStatus('Updated: ' + totals.updated + ' (fixed: ' + totals.fixed + ', missing file: ' + totals.missing_file + '), skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				if (j.next_last_id && String(j.next_last_id) !== String(lastId || '')) {
					runBackfillVideoBatch(j.next_last_id, totals);
				} else {
					setBackfillVideoStatus('Done. Updated: ' + totals.updated + ' (fixed: ' + totals.fixed + ', missing file: ' + totals.missing_file + '), skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				}
			})
			.catch(function(err){ setBackfillVideoStatus('Request failed: ' + (err && err.message ? err.message : String(err)), true); });
	}

	var backfillVideoBtn = document.getElementById('bulk_backfill_video');
	if (backfillVideoBtn) {
		backfillVideoBtn.addEventListener('click', function(){
			if (!confirm('Backfill video file IDs for old posts now? This may take some time.')) return;
			runBackfillVideoBatch('');
		});
	}

	function runBackfillWallpaperBatch(lastId, totals) {
		totals = totals || { scanned: 0, updated: 0, skipped: 0, fixed: 0, missing_file: 0 };
		setBackfillWallpaperStatus('Processing batch...' + (lastId ? (' (lastId=' + lastId + ')') : ''), false);
		var fd = new FormData();
		fd.append('user_hash', userHash);
		if (lastId) fd.append('last_id', String(lastId));
		fetch('admin.php?mod=addwallpaper&action=backfillwallpaper', { method: 'POST', body: fd, credentials: 'same-origin' })
			.then(function(r){ return r.text(); })
			.then(function(t){
				var j = null;
				try { j = JSON.parse(t); } catch(e) { j = null; }
				if (!j) { setBackfillWallpaperStatus('Invalid response: ' + t.slice(0, 300), true); return; }
				if (j.error) { setBackfillWallpaperStatus(j.error, true); return; }
				totals.scanned += (j.scanned || 0);
				totals.updated += (j.updated || 0);
				totals.skipped += (j.skipped || 0);
				totals.fixed += (j.fixed || 0);
				totals.missing_file += (j.missing_file || 0);
				setBackfillWallpaperStatus('Updated: ' + totals.updated + ' (fixed: ' + totals.fixed + ', missing file: ' + totals.missing_file + '), skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				if (j.next_last_id && String(j.next_last_id) !== String(lastId || '')) {
					runBackfillWallpaperBatch(j.next_last_id, totals);
				} else {
					setBackfillWallpaperStatus('Done. Updated: ' + totals.updated + ' (fixed: ' + totals.fixed + ', missing file: ' + totals.missing_file + '), skipped: ' + totals.skipped + ', scanned: ' + totals.scanned + '.', false);
				}
			})
			.catch(function(err){ setBackfillWallpaperStatus('Request failed: ' + (err && err.message ? err.message : String(err)), true); });
	}

	var backfillWallpaperBtn = document.getElementById('bulk_backfill_wallpaper');
	if (backfillWallpaperBtn) {
		backfillWallpaperBtn.addEventListener('click', function(){
			if (!confirm('Backfill wallpaper file IDs for old posts now? This may take some time.')) return;
			runBackfillWallpaperBatch('');
		});
	}

	function loadZipList() {
		setZipStatus('Loading ZIP list...', false);
		var fd = new FormData();
		fd.append('user_hash', userHash);
		fd.append('user_id', userId);
		fetch('admin.php?mod=addwallpaper&action=bulkftpziplist', { method: 'POST', body: fd, credentials: 'same-origin' })
			.then(function(r){ return r.text(); })
			.then(function(t){
				var j = null;
				try { j = JSON.parse(t); } catch(e) { j = null; }
				if (!j) { setZipStatus('Invalid response: ' + t.slice(0, 200), true); return; }
				if (j.error) { setZipStatus(j.error, true); return; }
				var sel = document.getElementById('bulk_zip_select');
				if (!sel) return;
				sel.innerHTML = '';
				var items = (j.items && Array.isArray(j.items)) ? j.items : [];
				if (!items.length) {
					var opt = document.createElement('option');
					opt.value = '';
					opt.textContent = '(No ZIP files found)';
					sel.appendChild(opt);
					setZipStatus('No ZIP files found in uploads/public_files/bulkimg/', false);
					return;
				}
				items.forEach(function(it){
					var opt = document.createElement('option');
					opt.value = it.name || '';
					opt.textContent = (it.name || '') + (it.size_h ? (' (' + it.size_h + ')') : '');
					sel.appendChild(opt);
				});
				setZipStatus('Found ' + items.length + ' ZIP file(s).', false);
			})
			.catch(function(err){ setZipStatus('Error loading ZIP list: ' + (err && err.message ? err.message : err), true); });
	}

	function registerAndExtractSelectedZip() {
		var sel = document.getElementById('bulk_zip_select');
		if (!sel) return;
		var name = sel.value || '';
		if (!name || name === '(No ZIP files found)') { setZipStatus('Select a ZIP first.', true); return; }

		var del = document.getElementById('bulk_zip_delete');
		var deleteZip = del ? (del.checked ? '1' : '0') : '1';

		setZipStatus('Registering ZIP...', false);
		var fd = new FormData();
		fd.append('user_hash', userHash);
		fd.append('zip_name', name);
		fetch('admin.php?mod=addwallpaper&action=bulkftpzipregister', { method: 'POST', body: fd, credentials: 'same-origin' })
			.then(function(r){ return r.text(); })
			.then(function(t){
				var j = null;
				try { j = JSON.parse(t); } catch(e) { j = null; }
				if (!j) { setZipStatus('Register failed: invalid JSON. ' + t.slice(0, 200), true); return; }
				if (j.error) { setZipStatus(j.error, true); return; }
				if (!j.success || !j.zip_id) { setZipStatus('Register failed', true); return; }
				setZipStatus('ZIP registered. Extracting...', false);

				var fd2 = new FormData();
				fd2.append('user_hash', userHash);
				fd2.append('zip_id', String(j.zip_id));
				fd2.append('delete_zip', deleteZip);
				fetch('admin.php?mod=addwallpaper&action=bulkextractzip', { method: 'POST', body: fd2, credentials: 'same-origin' })
					.then(function(r){ return r.text(); })
					.then(function(t2){
						var j2 = null;
						try { j2 = JSON.parse(t2); } catch(e) { j2 = null; }
						if (!j2) { setZipStatus('Extract failed: invalid JSON. ' + t2.slice(0, 200), true); return; }
						if (j2.error) { setZipStatus(j2.error, true); return; }
						if (!j2.success) { setZipStatus('Extract failed', true); return; }
						var imgs = (j2.images && Array.isArray(j2.images)) ? j2.images : [];
						if (!imgs.length) {
							setZipStatus('ZIP extracted but no supported images found (jpg/jpeg/png/gif/webp/bmp/avif).', true);
							return;
						}
						imgs.forEach(function(it){
							addItemRow({
								type: 'image',
								xfvalue: it.xfvalue || '',
								original: it.original || '',
								title: cleanTitle(it.original || '')
							});
						});
						setZipStatus('Extracted ' + imgs.length + ' image(s) from ZIP.', false);
						loadZipList();
					})
					.catch(function(err){ setZipStatus('Extract error: ' + (err && err.message ? err.message : err), true); });
			})
			.catch(function(err){ setZipStatus('Register error: ' + (err && err.message ? err.message : err), true); });
	}

	var uploader = new plupload.Uploader({
		runtimes : 'html5',
		file_data_name: 'qqfile',
		browse_button: 'bulk_upload_button',
		container: document.getElementById('bulk_uploader'),
		drop_element: document.getElementById('bulk_drop_area'),
		url: uploadUrl,
		multipart_params: {
			'user_hash': userHash,
			'user_id': userId
		},
		chunk_size: '5mb',
		filters: {
			max_file_size: '2048mb',
			mime_types: [
				{title: 'Files', extensions: 'jpg,jpeg,png,gif,webp,bmp,avif,mp4,mp3,wav,ogg,m4a,aac'}
			]
		},
		init: {
			FilesAdded: function(up, files) {
				plupload.each(files, function(file) {
					var box = document.createElement('div');
					box.id = 'bulk-upload-' + file.id;
					box.className = 'file-box';
					box.innerHTML = '<span class="qq-upload-file-status">{$lang['media_upload_st6']}</span><span class="qq-upload-file">&nbsp;'+escapeHtml(file.name)+'</span>&nbsp;<span class="qq-status"><span class="qq-upload-spinner"></span> <span class="qq-upload-size"></span></span><div class="progress"><div class="progress-bar progress-blue" style="width: 0%"><span>0%</span></div></div>';
					document.getElementById('bulk_filelist').appendChild(box);
				});
				setStatus('', false);
				up.start();
				up.refresh();
			},
			UploadProgress: function(up, file) {
				var box = document.getElementById('bulk-upload-' + file.id);
				if (!box) return;
				var sizeEl = box.querySelector('.qq-upload-size');
				var bar = box.querySelector('.progress-bar');
				if (sizeEl) sizeEl.textContent = plupload.formatSize(file.loaded) + ' {$lang['media_upload_st8']} ' + plupload.formatSize(file.origSize);
				if (bar) {
					bar.style.width = file.percent + '%';
					var span = bar.querySelector('span');
					if (span) span.textContent = file.percent + '%';
				}
			},
			FileUploaded: function(up, file, result) {
				var box = document.getElementById('bulk-upload-' + file.id);
				if (box) {
					var statusEl = box.querySelector('.qq-status');
					if (statusEl) statusEl.innerHTML = '{$lang['media_upload_st9']}';
				}

				var response = null;
				try { response = JSON.parse(result.response); } catch (e) { response = null; }
				if (!response) {
					var raw = (result && result.response) ? String(result.response) : '';
					setStatus('Upload response is not valid JSON. ' + (raw ? ('Response: ' + raw.slice(0, 200)) : ''), true);
					return;
				}
				if (response.result && response.result === 'chunk uploaded') return;
				if (response.error) { setStatus(response.error, true); return; }
				if (!response.success) return;

				addItemRow({
					type: response.type || '',
					xfvalue: response.xfvalue || '',
					original: response.original || (file ? file.name : ''),
					title: cleanTitle(response.original || (file ? file.name : ''))
				});
			},
			Error: function(up, err) {
				setStatus((err && err.message) ? err.message : 'Upload error', true);
			}
		}
	});
	uploader.init();

	var btnRefreshZip = document.getElementById('bulk_zip_refresh');
	var btnRegisterZip = document.getElementById('bulk_zip_register');
	if (btnRefreshZip) btnRefreshZip.addEventListener('click', loadZipList);
	if (btnRegisterZip) btnRegisterZip.addEventListener('click', registerAndExtractSelectedZip);
	loadZipList();

	var form = document.getElementById('addwallpaper');
	if (form) {
		form.addEventListener('submit', function(e){
			renderHiddenInputs();
		});
	}

	if ($('#category').hasClass('categoryselect') && typeof $.fn.chosen !== 'undefined') {
		$('.categoryselect').chosen({no_results_text: '{$lang['addnews_cat_fault']}' });
	}

	updateEmptyState();
})();
});
// -->
</script>
HTML;

	echofooter();
}

elseif( $action == "backfillxfields" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$auth_ok = false;

	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$last_id = isset($_REQUEST['last_id']) ? intval($_REQUEST['last_id']) : 0;
	$limit = 100;
	if( isset($_REQUEST['limit']) ) {
		$limit = intval($_REQUEST['limit']);
		if( $limit < 10 ) $limit = 10;
		if( $limit > 300 ) $limit = 300;
	}

	$scanned = 0;
	$updated = 0;
	$skipped = 0;
	$next_last_id = 0;

	// Only scan posts that likely need backfill (missing xfields poster)
	$where = "id > '{$last_id}' AND (xfields='' OR xfields NOT LIKE '%image_url_poster|%')";
	$db->query( "SELECT id, xfields, short_story, full_story FROM " . PREFIX . "_post WHERE {$where} ORDER BY id ASC LIMIT {$limit}" );
	while( $row = $db->get_row() ) {
		$scanned++;
		$postId = intval($row['id']);
		$next_last_id = $postId;

		$existing = dle_wallpaper_parse_xfields_string( isset($row['xfields']) ? $row['xfields'] : '' );
		if( isset($existing['image_url_poster']) && trim((string)$existing['image_url_poster']) !== '' ) {
			$skipped++;
			continue;
		}

		$content = (string)$row['short_story'] . "\n" . (string)$row['full_story'];
		$imageUrl = dle_wallpaper_extract_first_image_url( $content );
		$imageUrl = trim($imageUrl);
		if( !$imageUrl ) {
			$skipped++;
			continue;
		}

		$path = parse_url($imageUrl, PHP_URL_PATH);
		$path = $path ? ('/' . ltrim((string)$path, '/')) : '';
		if( $path && !preg_match('~^https?://~i', $imageUrl) ) {
			$imageUrl = rtrim($config['http_home_url'], '/') . $path;
		}

		$thumbUrl = '';
		$originalUrl = '';
		$mediumUrl = '';

		// Only backfill for our wallpaper storage, otherwise skip
		$path = parse_url($imageUrl, PHP_URL_PATH);
		$path = $path ? ('/' . ltrim((string)$path, '/')) : '';
		if( stripos($path, '/uploads/public_files/wallpapers/') === false ) {
			$skipped++;
			continue;
		}

		if( stripos($path, '/uploads/public_files/wallpapers/thumbs/') !== false ) {
			$thumbUrl = $imageUrl;
			$originalPath = str_ireplace('/uploads/public_files/wallpapers/thumbs/', '/uploads/public_files/wallpapers/', $path);
			$originalAbs = rtrim(ROOT_DIR, '/\\') . str_replace('/', DIRECTORY_SEPARATOR, $originalPath);
			if( is_file($originalAbs) ) {
				$originalUrl = rtrim($config['http_home_url'], '/') . $originalPath;
			}
		} elseif( stripos($path, '/uploads/public_files/wallpapers/medium/') !== false ) {
			$mediumUrl = $imageUrl;
			$originalPath = str_ireplace('/uploads/public_files/wallpapers/medium/', '/uploads/public_files/wallpapers/', $path);
			$originalAbs = rtrim(ROOT_DIR, '/\\') . str_replace('/', DIRECTORY_SEPARATOR, $originalPath);
			if( is_file($originalAbs) ) {
				$originalUrl = rtrim($config['http_home_url'], '/') . $originalPath;
			}
		} else {
			$originalUrl = $imageUrl;
			// try find thumb derivative
			$dir = dirname($path);
			$base = basename($path);
			$thumbPath = rtrim($dir, '/') . '/thumbs/' . $base;
			$thumbAbs = rtrim(ROOT_DIR, '/\\') . str_replace('/', DIRECTORY_SEPARATOR, $thumbPath);
			if( is_file($thumbAbs) ) {
				$thumbUrl = rtrim($config['http_home_url'], '/') . $thumbPath;
			}
		}

		// compute medium from original or imageUrl
		$mediumUrl = $mediumUrl ? $mediumUrl : ( $originalUrl ? dle_wallpaper_try_get_medium_url($originalUrl) : dle_wallpaper_try_get_medium_url($imageUrl) );

		$xfields_data = $existing;
		if( !isset($xfields_data['image_url_poster']) || trim((string)$xfields_data['image_url_poster']) === '' ) {
			$xfields_data['image_url_poster'] = $mediumUrl ? $mediumUrl : ($originalUrl ? $originalUrl : ($thumbUrl ? $thumbUrl : $imageUrl));
		}
		if( !isset($xfields_data['wallpaper']) || trim((string)$xfields_data['wallpaper']) === '' ) {
			$xfields_data['wallpaper'] = $originalUrl ? $originalUrl : ($thumbUrl ? $thumbUrl : $imageUrl);
		}
		if( $thumbUrl && (!isset($xfields_data['thumb_url_wallpaper']) || trim((string)$xfields_data['thumb_url_wallpaper']) === '') ) {
			$xfields_data['thumb_url_wallpaper'] = $thumbUrl;
		}
		if( !isset($xfields_data['image_url_wallpaper']) || trim((string)$xfields_data['image_url_wallpaper']) === '' ) {
			$xfields_data['image_url_wallpaper'] = $originalUrl ? $originalUrl : ($thumbUrl ? $thumbUrl : $imageUrl);
		}
		if( $mediumUrl && (!isset($xfields_data['medium_url_wallpaper']) || trim((string)$xfields_data['medium_url_wallpaper']) === '') ) {
			$xfields_data['medium_url_wallpaper'] = $mediumUrl;
		}

		$newXfields = dle_wallpaper_build_xfields_string( $xfields_data );
		if( $newXfields === '' ) {
			$skipped++;
			continue;
		}

		$db->query( "UPDATE " . PREFIX . "_post SET xfields='" . $db->safesql($newXfields) . "' WHERE id='{$postId}'" );
		$updated++;
	}
	$db->free();

	// Determine if there are more rows after this cursor (more reliable than $scanned >= $limit)
	$hasMore = false;
	if( $next_last_id ) {
		$exists = $db->super_query( "SELECT id FROM " . PREFIX . "_post WHERE id > '" . intval($next_last_id) . "' AND (xfields='' OR xfields NOT LIKE '%image_url_poster|%') LIMIT 1" );
		$hasMore = (isset($exists['id']) && intval($exists['id']) > 0);
	}

	die( json_encode(array(
		'success' => true,
		'scanned' => $scanned,
		'updated' => $updated,
		'skipped' => $skipped,
		'next_last_id' => $hasMore ? $next_last_id : 0,
	), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "backfilltags" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$auth_ok = false;

	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$last_id = isset($_REQUEST['last_id']) ? intval($_REQUEST['last_id']) : 0;
	$limit = 200;
	if( isset($_REQUEST['limit']) ) {
		$limit = intval($_REQUEST['limit']);
		if( $limit < 20 ) $limit = 20;
		if( $limit > 500 ) $limit = 500;
	}

	$scanned = 0;
	$updated = 0;
	$skipped = 0;
	$next_last_id = 0;

	$db->query( "SELECT id, title, tags, approve, short_story, full_story, xfields FROM " . PREFIX . "_post WHERE id > '{$last_id}' ORDER BY id ASC LIMIT {$limit}" );
	while( $row = $db->get_row() ) {
		$scanned++;
		$postId = intval($row['id']);
		$next_last_id = $postId;

		$title = isset($row['title']) ? (string)$row['title'] : '';
		$existing_tags = isset($row['tags']) ? (string)$row['tags'] : '';
		$approve = isset($row['approve']) ? intval($row['approve']) : 0;
		$content = (string)($row['short_story'] ?? '') . "\n" . (string)($row['full_story'] ?? '');

		$xf = dle_wallpaper_parse_xfields_string( isset($row['xfields']) ? $row['xfields'] : '' );

		$type = 'image';
		if( isset($xf['video_url']) && trim((string)$xf['video_url']) !== '' ) {
			$type = 'video';
		} elseif( isset($xf['mp3']) && trim((string)$xf['mp3']) !== '' ) {
			$type = 'audio';
		} else {
			if( stripos($content, '[video=') !== false || stripos($content, 'dle_video_begin') !== false || stripos($content, '<video') !== false ) {
				$type = 'video';
			} elseif( stripos($content, '[audio=') !== false || stripos($content, 'dle_audio_begin') !== false || stripos($content, '<audio') !== false ) {
				$type = 'audio';
			} else {
				$type = 'image';
			}
		}

		$auto = dle_wallpaper_generate_auto_tags($title, $type);
		if( $auto === '' ) {
			$skipped++;
			continue;
		}

		$merged = dle_wallpaper_merge_tags_string($existing_tags, $auto);
		$merged_norm = implode(',', dle_wallpaper_parse_tags_list($merged));
		$existing_norm = implode(',', dle_wallpaper_parse_tags_list($existing_tags));
		if( $merged_norm === $existing_norm ) {
			$skipped++;
			continue;
		}

		$db->query( "UPDATE " . PREFIX . "_post SET tags='" . $db->safesql($merged) . "' WHERE id='{$postId}'" );

		if( $approve ) {
			$db->query( "DELETE FROM " . PREFIX . "_tags WHERE news_id='{$postId}'" );
			$tags_arr = dle_wallpaper_parse_tags_list($merged);
			$values = array();
			foreach( $tags_arr as $t ) {
				$t = trim((string)$t);
				if( $t === '' ) continue;
				$values[] = "('" . intval($postId) . "', '" . $db->safesql($t) . "')";
			}
			if( count($values) ) {
				$db->query( "INSERT INTO " . PREFIX . "_tags (news_id, tag) VALUES " . implode(', ', $values) );
			}
		}

		$updated++;
	}
	$db->free();

	// Determine if there are more rows after this cursor (more reliable than $scanned >= $limit)
	$hasMore = false;
	if( $next_last_id ) {
		$exists = $db->super_query( "SELECT id FROM " . PREFIX . "_post WHERE id > '" . intval($next_last_id) . "' LIMIT 1" );
		$hasMore = (isset($exists['id']) && intval($exists['id']) > 0);
	}
	if( !$hasMore ) {
		clear_cache( array('tagscloud_', 'news_', 'archives_', 'calendar_', 'topnews_', 'rss', 'stats') );
	}

	die( json_encode(array(
		'success' => true,
		'scanned' => $scanned,
		'updated' => $updated,
		'skipped' => $skipped,
		'next_last_id' => $hasMore ? $next_last_id : 0,
	), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "backfillaudio" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$auth_ok = false;

	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$last_id = isset($_REQUEST['last_id']) ? intval($_REQUEST['last_id']) : 0;
	$limit = 200;
	if( isset($_REQUEST['limit']) ) {
		$limit = intval($_REQUEST['limit']);
		if( $limit < 20 ) $limit = 20;
		if( $limit > 500 ) $limit = 500;
	}

	$scanned = 0;
	$updated = 0;
	$skipped = 0;
	$fixed = 0;
	$missing_file = 0;
	$next_last_id = 0;

	// Only scan posts that have mp3 xf and look URL-only (no encoded pipe separators)
	$where = "id > '{$last_id}' AND xfields LIKE '%mp3|%' AND xfields NOT LIKE '%mp3|%&#124;%'";
	$db->query( "SELECT id, xfields FROM " . PREFIX . "_post WHERE {$where} ORDER BY id ASC LIMIT {$limit}" );
	while( $row = $db->get_row() ) {
		$scanned++;
		$postId = intval($row['id']);
		$next_last_id = $postId;

		$existing = dle_wallpaper_parse_xfields_string( isset($row['xfields']) ? $row['xfields'] : '' );
		$mp3 = isset($existing['mp3']) ? trim((string)$existing['mp3']) : '';
		if( $mp3 === '' ) { $skipped++; continue; }
		if( stripos($mp3, '|') !== false || stripos($mp3, '&#124;') !== false ) { $skipped++; continue; }

		$path = parse_url($mp3, PHP_URL_PATH);
		$path = $path ? ('/' . ltrim((string)$path, '/')) : '';
		$onserver = '';
		if( $path && stripos($path, '/uploads/public_files/') !== false ) {
			$onserver = substr($path, stripos($path, '/uploads/public_files/') + strlen('/uploads/public_files/'));
			$onserver = ltrim((string)$onserver, '/');
		}
		if( $onserver === '' ) { $skipped++; continue; }

		$file = $db->super_query( "SELECT id, size, news_id FROM " . PREFIX . "_files WHERE onserver='" . $db->safesql($onserver) . "' ORDER BY id DESC LIMIT 1" );
		$fileId = isset($file['id']) ? intval($file['id']) : 0;
		$fileSize = isset($file['size']) ? intval($file['size']) : 0;
		if( !$fileId ) {
			$missing_file++;
			$skipped++;
			continue;
		}

		$sizeText = '';
		if( $fileSize > 0 ) {
			if( function_exists('formatsize') ) {
				$sizeText = formatsize($fileSize);
			} else {
				$sizeText = (string)$fileSize;
			}
		}

		$existing['mp3'] = $mp3 . '|' . $fileId . ( $sizeText !== '' ? ('|' . $sizeText) : '' );
		$newXfields = dle_wallpaper_build_xfields_string( $existing );
		if( $newXfields === '' ) { $skipped++; continue; }

		$db->query( "UPDATE " . PREFIX . "_post SET xfields='" . $db->safesql($newXfields) . "' WHERE id='{$postId}'" );
		$updated++;
		$fixed++;

		if( isset($file['news_id']) && intval($file['news_id']) === 0 ) {
			$db->query( "UPDATE " . PREFIX . "_files SET news_id='{$postId}' WHERE id='{$fileId}' AND news_id='0'" );
		}
	}
	$db->free();

	$hasMore = false;
	if( $next_last_id ) {
		$exists = $db->super_query( "SELECT id FROM " . PREFIX . "_post WHERE id > '" . intval($next_last_id) . "' AND xfields LIKE '%mp3|%' AND xfields NOT LIKE '%mp3|%&#124;%' LIMIT 1" );
		$hasMore = (isset($exists['id']) && intval($exists['id']) > 0);
	}
	if( !$hasMore ) {
		clear_cache( array('news_', 'archives_', 'calendar_', 'topnews_', 'rss', 'stats') );
	}

	die( json_encode(array(
		'success' => true,
		'scanned' => $scanned,
		'updated' => $updated,
		'skipped' => $skipped,
		'fixed' => $fixed,
		'missing_file' => $missing_file,
		'next_last_id' => $hasMore ? $next_last_id : 0,
	), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "backfillvideo" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$auth_ok = false;

	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$last_id = isset($_REQUEST['last_id']) ? intval($_REQUEST['last_id']) : 0;
	$limit = 200;
	if( isset($_REQUEST['limit']) ) {
		$limit = intval($_REQUEST['limit']);
		if( $limit < 20 ) $limit = 20;
		if( $limit > 500 ) $limit = 500;
	}

	$scanned = 0;
	$updated = 0;
	$skipped = 0;
	$fixed = 0;
	$missing_file = 0;
	$next_last_id = 0;

	$where = "id > '{$last_id}' AND xfields LIKE '%video_url|%' AND xfields NOT LIKE '%video_url|%&#124;%'";
	$db->query( "SELECT id, xfields FROM " . PREFIX . "_post WHERE {$where} ORDER BY id ASC LIMIT {$limit}" );
	while( $row = $db->get_row() ) {
		$scanned++;
		$postId = intval($row['id']);
		$next_last_id = $postId;

		$existing = dle_wallpaper_parse_xfields_string( isset($row['xfields']) ? $row['xfields'] : '' );
		$video = isset($existing['video_url']) ? trim((string)$existing['video_url']) : '';
		if( $video === '' ) { $skipped++; continue; }
		if( stripos($video, '|') !== false || stripos($video, '&#124;') !== false ) { $skipped++; continue; }

		$path = parse_url($video, PHP_URL_PATH);
		$path = $path ? ('/' . ltrim((string)$path, '/')) : '';
		$onserver = '';
		if( $path && stripos($path, '/uploads/public_files/') !== false ) {
			$onserver = substr($path, stripos($path, '/uploads/public_files/') + strlen('/uploads/public_files/'));
			$onserver = ltrim((string)$onserver, '/');
		}
		if( $onserver === '' ) { $skipped++; continue; }

		$file = $db->super_query( "SELECT id, size, news_id FROM " . PREFIX . "_files WHERE onserver='" . $db->safesql($onserver) . "' ORDER BY id DESC LIMIT 1" );
		$fileId = isset($file['id']) ? intval($file['id']) : 0;
		$fileSize = isset($file['size']) ? intval($file['size']) : 0;
		if( !$fileId ) {
			$missing_file++;
			$skipped++;
			continue;
		}

		$sizeText = '';
		if( $fileSize > 0 ) {
			if( function_exists('formatsize') ) {
				$sizeText = formatsize($fileSize);
			} else {
				$sizeText = (string)$fileSize;
			}
		}

		$existing['video_url'] = $video . '|' . $fileId . ( $sizeText !== '' ? ('|' . $sizeText) : '' );
		$newXfields = dle_wallpaper_build_xfields_string( $existing );
		if( $newXfields === '' ) { $skipped++; continue; }

		$db->query( "UPDATE " . PREFIX . "_post SET xfields='" . $db->safesql($newXfields) . "' WHERE id='{$postId}'" );
		$updated++;
		$fixed++;

		if( isset($file['news_id']) && intval($file['news_id']) === 0 ) {
			$db->query( "UPDATE " . PREFIX . "_files SET news_id='{$postId}' WHERE id='{$fileId}' AND news_id='0'" );
		}
	}
	$db->free();

	$hasMore = false;
	if( $next_last_id ) {
		$exists = $db->super_query( "SELECT id FROM " . PREFIX . "_post WHERE id > '" . intval($next_last_id) . "' AND xfields LIKE '%video_url|%' AND xfields NOT LIKE '%video_url|%&#124;%' LIMIT 1" );
		$hasMore = (isset($exists['id']) && intval($exists['id']) > 0);
	}
	if( !$hasMore ) {
		clear_cache( array('news_', 'archives_', 'calendar_', 'topnews_', 'rss', 'stats') );
	}

	die( json_encode(array(
		'success' => true,
		'scanned' => $scanned,
		'updated' => $updated,
		'skipped' => $skipped,
		'fixed' => $fixed,
		'missing_file' => $missing_file,
		'next_last_id' => $hasMore ? $next_last_id : 0,
	), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "backfillwallpaper" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$auth_ok = false;

	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$last_id = isset($_REQUEST['last_id']) ? intval($_REQUEST['last_id']) : 0;
	$limit = 200;
	if( isset($_REQUEST['limit']) ) {
		$limit = intval($_REQUEST['limit']);
		if( $limit < 20 ) $limit = 20;
		if( $limit > 500 ) $limit = 500;
	}

	$scanned = 0;
	$updated = 0;
	$skipped = 0;
	$fixed = 0;
	$missing_file = 0;
	$next_last_id = 0;

	$where = "id > '{$last_id}' AND xfields LIKE '%wallpaper|%' AND xfields NOT LIKE '%wallpaper_file_id|%'";
	$db->query( "SELECT id, xfields FROM " . PREFIX . "_post WHERE {$where} ORDER BY id ASC LIMIT {$limit}" );
	while( $row = $db->get_row() ) {
		$scanned++;
		$postId = intval($row['id']);
		$next_last_id = $postId;

		$existing = dle_wallpaper_parse_xfields_string( isset($row['xfields']) ? $row['xfields'] : '' );
		if( isset($existing['wallpaper_file_id']) && intval($existing['wallpaper_file_id']) > 0 ) { $skipped++; continue; }

		$wallUrl = '';
		if( isset($existing['wallpaper']) && trim((string)$existing['wallpaper']) !== '' ) {
			$wallUrl = trim((string)$existing['wallpaper']);
		} elseif( isset($existing['image_url_wallpaper']) && trim((string)$existing['image_url_wallpaper']) !== '' ) {
			$wallUrl = trim((string)$existing['image_url_wallpaper']);
		}
		if( $wallUrl === '' ) { $skipped++; continue; }

		$path = parse_url($wallUrl, PHP_URL_PATH);
		$path = $path ? ('/' . ltrim((string)$path, '/')) : '';
		if( !$path || stripos($path, '/uploads/public_files/wallpapers/') === false ) { $skipped++; continue; }

		$onserver = substr($path, stripos($path, '/uploads/public_files/') + strlen('/uploads/public_files/'));
		$onserver = ltrim((string)$onserver, '/');
		$onserver = str_ireplace(array('wallpapers/thumbs/', 'wallpapers/medium/'), 'wallpapers/', $onserver);
		if( $onserver === '' ) { $skipped++; continue; }

		$file = $db->super_query( "SELECT id, size, news_id FROM " . PREFIX . "_files WHERE onserver='" . $db->safesql($onserver) . "' ORDER BY id DESC LIMIT 1" );
		$fileId = isset($file['id']) ? intval($file['id']) : 0;
		$fileSize = isset($file['size']) ? intval($file['size']) : 0;
		if( !$fileId ) {
			$missing_file++;
			$skipped++;
			continue;
		}

		$existing['wallpaper_file_id'] = $fileId;
		if( $fileSize > 0 ) {
			$existing['wallpaper_file_size'] = function_exists('formatsize') ? formatsize($fileSize) : (string)$fileSize;
		}
		$newXfields = dle_wallpaper_build_xfields_string( $existing );
		if( $newXfields === '' ) { $skipped++; continue; }

		$db->query( "UPDATE " . PREFIX . "_post SET xfields='" . $db->safesql($newXfields) . "' WHERE id='{$postId}'" );
		$updated++;
		$fixed++;

		if( isset($file['news_id']) && intval($file['news_id']) === 0 ) {
			$db->query( "UPDATE " . PREFIX . "_files SET news_id='{$postId}' WHERE id='{$fileId}' AND news_id='0'" );
		}
	}
	$db->free();

	$hasMore = false;
	if( $next_last_id ) {
		$exists = $db->super_query( "SELECT id FROM " . PREFIX . "_post WHERE id > '" . intval($next_last_id) . "' AND xfields LIKE '%wallpaper|%' AND xfields NOT LIKE '%wallpaper_file_id|%' LIMIT 1" );
		$hasMore = (isset($exists['id']) && intval($exists['id']) > 0);
	}
	if( !$hasMore ) {
		clear_cache( array('news_', 'archives_', 'calendar_', 'topnews_', 'rss', 'stats') );
	}

	die( json_encode(array(
		'success' => true,
		'scanned' => $scanned,
		'updated' => $updated,
		'skipped' => $skipped,
		'fixed' => $fixed,
		'missing_file' => $missing_file,
		'next_last_id' => $hasMore ? $next_last_id : 0,
	), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "bulkftpziplist" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$auth_ok = false;

	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$relFolder = 'public_files/bulkimg/';
	$absFolder = ROOT_DIR . '/uploads/' . $relFolder;

	if( !is_dir($absFolder) ) {
		@mkdir($absFolder, 0777, true);
		@chmod($absFolder, 0777);
		@file_put_contents($absFolder . 'index.html', '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body></body></html>');
	}

	$items = array();
	if( is_dir($absFolder) ) {
		$dh = @opendir($absFolder);
		if( $dh ) {
			while( false !== ($file = readdir($dh)) ) {
				if( $file === '.' OR $file === '..' ) continue;
				if( $file === 'index.html' ) continue;
				if( preg_match('/\.zip$/i', $file) ) {
					$full = $absFolder . $file;
					if( is_file($full) ) {
						$size = @filesize($full);
						$items[] = array(
							'name' => $file,
							'size' => $size ? intval($size) : 0,
							'size_h' => $size ? formatsize($size) : '0',
							'mtime' => @filemtime($full) ?: 0
						);
					}
				}
			}
			@closedir($dh);
		}
	}

	usort($items, function($a, $b) {
		return intval($b['mtime']) <=> intval($a['mtime']);
	});

	die( json_encode(array(
		'success' => true,
		'folder' => $relFolder,
		'items' => $items
	), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "bulkftpzipregister" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$zip_name = isset($_REQUEST['zip_name']) ? (string)$_REQUEST['zip_name'] : '';

	$auth_ok = false;
	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$zip_name = trim(str_replace(array(chr(0), "\\", "/"), '', $zip_name));
	if( !$zip_name OR !preg_match('/\.zip$/i', $zip_name) ) {
		die( json_encode(array('error' => 'Invalid ZIP name'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$relFolder = 'public_files/bulkimg/';
	$absFolder = ROOT_DIR . '/uploads/' . $relFolder;
	$absFile = $absFolder . $zip_name;
	if( !is_file($absFile) ) {
		die( json_encode(array('error' => 'ZIP not found in uploads/public_files/bulkimg/'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$onserver = 'bulkimg/' . $zip_name;
	$onserver_sql = $db->safesql($onserver);

	$existing = $db->super_query( "SELECT id, author, news_id FROM " . PREFIX . "_files WHERE onserver='{$onserver_sql}' LIMIT 1" );
	if( isset($existing['id']) AND intval($existing['id']) ) {
		if( intval($existing['news_id']) !== 0 ) {
			die( json_encode(array('error' => 'ZIP already attached to a post'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
		}
		die( json_encode(array('success' => true, 'zip_id' => intval($existing['id']) ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$sizeBytes = @filesize($absFile);
	if( !$sizeBytes ) $sizeBytes = 0;
	$md5 = @md5_file($absFile);
	if( !$md5 ) $md5 = '';
	$added_time = time();

	$db_file_name = str_replace(chr(0), '', strip_tags( html_entity_decode( $zip_name , ENT_QUOTES | ENT_HTML5, 'utf-8') ) );
	$db_file_name = preg_replace("/\s+/u", " ", $db_file_name);
	$db_file_name = $db->safesql( trim($db_file_name) );

	$db->query( "INSERT INTO " . PREFIX . "_files (news_id, name, onserver, author, date, size, checksum, driver, is_public) values ('0', '{$db_file_name}', '{$onserver_sql}', '".$db->safesql($member_id['name'])."', '{$added_time}', '".intval($sizeBytes)."', '".$db->safesql($md5)."', '0', '1')" );
	$newId = $db->insert_id();

	die( json_encode(array('success' => true, 'zip_id' => intval($newId)), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "bulkextractzip" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$zipFileId = isset($_REQUEST['zip_id']) ? intval($_REQUEST['zip_id']) : 0;
	$deleteZip = isset($_REQUEST['delete_zip']) ? intval($_REQUEST['delete_zip']) : 0;

	if( !$zipFileId ) {
		die( json_encode(array('error' => 'ZIP id is required'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !class_exists('ZipArchive') ) {
		die( json_encode(array('error' => 'PHP extension zip (ZipArchive) is not enabled on the server.'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$auth_ok = false;
	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$row = $db->super_query( "SELECT id, name, onserver, driver, is_public, author, news_id FROM " . PREFIX . "_files WHERE id='{$zipFileId}' LIMIT 1" );
	if( !isset($row['id']) OR !$row['id'] OR intval($row['news_id']) !== 0 ) {
		die( json_encode(array('error' => 'ZIP not found'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	// Only allow ZIPs from bulkimg folder
	if( stripos((string)$row['onserver'], 'bulkimg/') !== 0 ) {
		die( json_encode(array('error' => 'Invalid ZIP location'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$zipAbs = ROOT_DIR . '/uploads/public_files/' . $row['onserver'];
	if( !is_file($zipAbs) ) {
		die( json_encode(array('error' => 'ZIP file missing on disk'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$zip = new ZipArchive();
	$opened = $zip->open($zipAbs);
	if( $opened !== true ) {
		die( json_encode(array('error' => 'Unable to open ZIP'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$allowedExt = array('jpg','jpeg','png','gif','webp','bmp','avif');
	$entries = array();
	for ($i=0; $i < $zip->numFiles; $i++) {
		$stat = $zip->statIndex($i);
		if( !isset($stat['name']) ) continue;
		$name = (string)$stat['name'];
		if( substr($name, -1) === '/' ) continue;
		$extEntry = strtolower(pathinfo($name, PATHINFO_EXTENSION));
		if( !in_array($extEntry, $allowedExt, true) ) continue;
		$entries[] = $name;
	}

	$baseDir = ROOT_DIR . '/uploads/public_files/wallpapers/';
	$thumbDir = $baseDir . 'thumbs/';
	$mediumDir = $baseDir . 'medium/';
	foreach( array($baseDir, $thumbDir, $mediumDir) as $dir ) {
		if( !is_dir($dir) ) {
			@mkdir($dir, 0777, true);
			@chmod($dir, 0777);
		}
		if( !is_file($dir . 'index.html') ) {
			@file_put_contents($dir . 'index.html', '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body></body></html>');
		}
	}

	$thumbLibOk = dle_wallpaper_bootstrap_thumb_lib();

	$httpBase = rtrim($config['http_home_url'], '/') . '/uploads/public_files/wallpapers/';
	$images = array();

	foreach( $entries as $entryName ) {
		$baseName = pathinfo(str_replace('\\', '/', $entryName), PATHINFO_BASENAME);
		$extFile = strtolower(pathinfo($baseName, PATHINFO_EXTENSION));
		$nameOnly = totranslit(pathinfo($baseName, PATHINFO_FILENAME));
		$extSafe = totranslit($extFile);
		if( !$extSafe ) continue;
		if( dle_strlen($nameOnly) > 200 ) $nameOnly = dle_substr($nameOnly, 0, 200);
		if( !$nameOnly ) $nameOnly = time() . rand(1, 100);
		$finalName = $nameOnly . '.' . $extSafe;
		$targetAbs = $baseDir . $finalName;
		if( is_file($targetAbs) ) {
			$finalName = UniqIDReal() . '_' . $finalName;
			$targetAbs = $baseDir . $finalName;
		}

		$zstream = $zip->getStream($entryName);
		if( !is_resource($zstream) ) continue;
		$out = @fopen($targetAbs, 'wb');
		if( !$out ) { @fclose($zstream); continue; }
		while( !feof($zstream) ) {
			$buf = fread($zstream, 8192);
			if( $buf === false ) break;
			fwrite($out, $buf);
		}
		@fclose($zstream);
		@fclose($out);
		@chmod($targetAbs, 0666);

		if( !is_file($targetAbs) ) continue;

		// generate thumb + medium (optional)
		// For grid/card themes posters are often displayed larger; ensure generated derivatives aren't tiny.
		$t_size = isset($config['max_image']) ? intval($config['max_image']) : 0;
		$m_size = isset($config['medium_image']) ? intval($config['medium_image']) : 0;
		if( $t_size && $t_size < 400 ) $t_size = 400;
		if( $m_size && $m_size < 800 ) $m_size = 800;
		$t_seite = isset($config['t_seite']) ? intval($config['t_seite']) : 0;
		$m_seite = $t_seite;
		$make_watermark = !empty($config['allow_watermark']);

		if( $thumbLibOk ) {
			try {
				if( $t_size ) {
					$thumb = new thumbnail($targetAbs, true, false);
					if( !$thumb->error ) {
						if( $thumb->size_auto($t_size, $t_seite) ) {
							if( $make_watermark ) $thumb->insert_watermark($config['max_watermark']);
							$thumb->save('public_files/wallpapers/thumbs/' . $finalName, false);
						}
					}
					unset($thumb);
				}
				if( $m_size ) {
					$medium = new thumbnail($targetAbs, true, false);
					if( !$medium->error ) {
						if( $medium->size_auto($m_size, $m_seite) ) {
							if( $make_watermark ) $medium->insert_watermark($config['max_watermark']);
							$medium->save('public_files/wallpapers/medium/' . $finalName, false);
						}
					}
					unset($medium);
				}
				if( function_exists('gc_collect_cycles') ) {
					gc_collect_cycles();
				}
			} catch (Throwable $e) {
				// ignore thumbnail generation errors; original upload still valid
			}
		}

		$sizeBytes = @filesize($targetAbs);
		if( !$sizeBytes ) $sizeBytes = 0;
		$md5 = @md5_file($targetAbs);
		if( !$md5 ) $md5 = '';
		$added_time = time();

		$bads = array('<', '>', ':', '«', '|', '?', '*', '[', ']', ';', '|', '=', '\\', ',', '/', '#', '%', '"', "'");
		$db_file_name = str_replace(chr(0), '', strip_tags( html_entity_decode( $baseName , ENT_QUOTES | ENT_HTML5, 'utf-8') ) );
		$db_file_name = str_ireplace($bads, '', $db_file_name);
		$db_file_name = preg_replace("/\s+/u", " ", $db_file_name);
		$db_file_name = $db->safesql( trim($db_file_name) );

		$onserver = 'wallpapers/' . $finalName;
		$db->query( "INSERT INTO " . PREFIX . "_files (news_id, name, onserver, author, date, size, checksum, driver, is_public) values ('0', '{$db_file_name}', '".$db->safesql($onserver)."', '".$db->safesql($member_id['name'])."', '{$added_time}', '".intval($sizeBytes)."', '".$db->safesql($md5)."', '0', '1')" );
		$fileId = $db->insert_id();

		$originalUrl = $httpBase . $finalName;
		$thumbUrl = $httpBase . 'thumbs/' . $finalName;
		$mediumUrl = $httpBase . 'medium/' . $finalName;

		$thumbAbs = ROOT_DIR . '/uploads/public_files/wallpapers/thumbs/' . $finalName;
		$mediumAbs = ROOT_DIR . '/uploads/public_files/wallpapers/medium/' . $finalName;
		if( !is_file($thumbAbs) ) {
			$thumbUrl = $originalUrl;
		}
		if( !is_file($mediumAbs) ) {
			$mediumUrl = $originalUrl;
		}
		$xfvalue = $thumbUrl . '|' . intval($fileId) . '|' . formatsize($sizeBytes) . '|' . $originalUrl . '|' . $mediumUrl;

		$images[] = array(
			'xfvalue' => $xfvalue,
			'original' => $baseName
		);
	}

	$zip->close();

	$deleted_zip = false;
	if( $deleteZip ) {
		@unlink($zipAbs);
		$db->query("DELETE FROM " . PREFIX . "_files WHERE id='{$zipFileId}'");
		$deleted_zip = true;
	}

	die( json_encode(array('success' => true, 'type' => 'zip', 'images' => $images, 'deleted_zip' => $deleted_zip), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "bulkupload" ) {

	@header('Content-Type: application/json; charset=utf-8');

	$user_hash = isset($_REQUEST['user_hash']) ? (string)$_REQUEST['user_hash'] : '';
	$auth_ok = false;

	if ( defined('LOGGED_IN') && LOGGED_IN ) {
		if( $user_hash AND isset($dle_login_hash) AND $user_hash === $dle_login_hash ) {
			$auth_ok = true;
		}
	} else {
		$user_id = isset($_REQUEST['user_id']) ? intval($_REQUEST['user_id']) : 0;
		if( $user_id > 0 AND $user_hash ) {
			$user_row = $db->super_query( "SELECT user_id, name, user_group, password, hash, logged_ip FROM " . USERPREFIX . "_users WHERE user_id='{$user_id}' LIMIT 1" );
			if( isset($user_row['user_id']) AND intval($user_row['user_id']) === $user_id ) {
				$expected = sha1( SECURE_AUTH_KEY . $user_row['user_id'] . sha1($user_row['password']) . $user_row['hash'] );
				$match = function_exists('hash_equals') ? hash_equals($expected, $user_hash) : ($expected === $user_hash);

				if( $match ) {
					if( $config['ip_control'] == '1' AND !check_netz( $user_row['logged_ip'], $_IP ) ) {
						$auth_ok = false;
					} else {
						if( !defined('LOGGED_IN') ) {
							define( 'LOGGED_IN', true );
						}
						$member_id = is_array($member_id) ? array_merge($member_id, $user_row) : $user_row;
						$dle_login_hash = $expected;
						$auth_ok = true;
					}
				}
			}
		}
	}

	if( !$auth_ok ) {
		die( json_encode(array('error' => $lang['sess_error'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( !$user_group[$member_id['user_group']]['admin_addnews'] ) {
		die( json_encode(array('error' => $lang['index_denied'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	$originalName = '';
	if (isset($_FILES['qqfile']['name'])) {
		$originalName = (string)$_FILES['qqfile']['name'];
	}
	if( !$originalName && isset($_REQUEST['name']) ) {
		$originalName = (string)$_REQUEST['name'];
	}

	$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
	$allowed_images = array('jpg','jpeg','png','gif','webp','bmp','avif');
	$allowed_video = array('mp4');
	$allowed_audio = array('mp3','wav','ogg','m4a','aac');

	if( !in_array($ext, $allowed_images, true) && !in_array($ext, $allowed_video, true) && !in_array($ext, $allowed_audio, true) ) {
		die( json_encode(array('error' => 'Unsupported file type' ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	include_once (DLEPlugins::Check(ENGINE_DIR . '/classes/uploads/upload.class.php'));

	$temp_files_type = $user_group[$member_id['user_group']]['files_type'];
	$temp_allow_public = $user_group[$member_id['user_group']]['allow_public_file_upload'];
	$temp_allow_file_upload = $user_group[$member_id['user_group']]['allow_file_upload'];
	$temp_allow_image_upload = $user_group[$member_id['user_group']]['allow_image_upload'];
	$temp_max_file_size = $user_group[$member_id['user_group']]['max_file_size'];

	$user_group[$member_id['user_group']]['allow_public_file_upload'] = 1;
	$user_group[$member_id['user_group']]['allow_file_upload'] = 1;
	$user_group[$member_id['user_group']]['allow_image_upload'] = 1;
	$user_group[$member_id['user_group']]['max_file_size'] = 0;

	// Keep uploads separated from audio by using dedicated folders
	$_REQUEST['dle_upload_no_prefix'] = 1;

	$type = '';
	$result = '';

	if( in_array($ext, $allowed_images, true) ) {
		$user_group[$member_id['user_group']]['files_type'] = implode(',', $allowed_images);
		$_REQUEST['public_file'] = 1;
		$_REQUEST['dle_public_subdir'] = 'wallpapers';
		$_REQUEST['xfname'] = 'wallpaper_image';

		$wallpapers_abs = ROOT_DIR . '/uploads/public_files/wallpapers/';
		if( !is_dir($wallpapers_abs) ) {
			@mkdir($wallpapers_abs, 0777, true);
			@chmod($wallpapers_abs, 0777);
			@file_put_contents($wallpapers_abs . 'index.html', '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body></body></html>');
		}

		$uploader = new FileUploader('xfieldsfile', 0, $member_id['name'], 0, 0, false, false, 0, 0, false, false);
		$result = $uploader->FileUpload();
		$type = 'image';
	} elseif( in_array($ext, $allowed_audio, true) ) {
		$user_group[$member_id['user_group']]['files_type'] = implode(',', $allowed_audio);
		$_REQUEST['xfname'] = 'wallpaper_audio';
		$_REQUEST['public_file'] = 1;
		$_REQUEST['dle_public_subdir'] = 'ringtones';

		$ringtones_abs = ROOT_DIR . '/uploads/public_files/ringtones/';
		if( !is_dir($ringtones_abs) ) {
			@mkdir($ringtones_abs, 0777, true);
			@chmod($ringtones_abs, 0777);
			@file_put_contents($ringtones_abs . 'index.html', '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body></body></html>');
		}

		$uploader = new FileUploader('xfieldsaudio', 0, $member_id['name'], 0, 0, false, false, 0, 0, false, false);
		$result = $uploader->FileUpload();
		$type = 'audio';
	} else {
		$user_group[$member_id['user_group']]['files_type'] = 'mp4';
		$_REQUEST['xfname'] = 'wallpaper_video';
		$_REQUEST['public_file'] = 1;
		$_REQUEST['dle_public_subdir'] = 'videos';

		$videos_abs = ROOT_DIR . '/uploads/public_files/videos/';
		if( !is_dir($videos_abs) ) {
			@mkdir($videos_abs, 0777, true);
			@chmod($videos_abs, 0777);
			@file_put_contents($videos_abs . 'index.html', '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body></body></html>');
		}

		$uploader = new FileUploader('xfieldsvideo', 0, $member_id['name'], 0, 0, false, false, 0, 0, false, false);
		$result = $uploader->FileUpload();
		$type = 'video';
	}

	$user_group[$member_id['user_group']]['files_type'] = $temp_files_type;
	$user_group[$member_id['user_group']]['allow_public_file_upload'] = $temp_allow_public;
	$user_group[$member_id['user_group']]['allow_file_upload'] = $temp_allow_file_upload;
	$user_group[$member_id['user_group']]['allow_image_upload'] = $temp_allow_image_upload;
	$user_group[$member_id['user_group']]['max_file_size'] = $temp_max_file_size;

	$data = json_decode($result, true);
	if( !is_array($data) ) {
		die( json_encode(array('error' => $lang['upload_error_3'] ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
	}

	if( isset($data['result']) AND $data['result'] === 'chunk uploaded' ) {
		die( $result );
	}

	if( isset($data['error']) ) {
		die( $result );
	}

	// Normalize xfvalue for post creator: url|fileId|size
	if( $type === 'image' ) {
		$url = isset($data['xfvalue']) ? (string)$data['xfvalue'] : '';
		$originalUrl = $url;
		$thumbUrl = '';
		$mediumUrl = '';
		$fileId = 0;
		$sizeStr = '';
		if( isset($data['uploaded_filename']) && $data['uploaded_filename'] ) {
			// Generate thumbs/medium inside public_files/wallpapers using system thumbnail class
			$uploaded = (string)$data['uploaded_filename'];
			$baseDir = ROOT_DIR . '/uploads/public_files/wallpapers/';
			$thumbDir = $baseDir . 'thumbs/';
			$mediumDir = $baseDir . 'medium/';
			foreach( array($thumbDir, $mediumDir) as $dir ) {
				if( !is_dir($dir) ) {
					@mkdir($dir, 0777, true);
					@chmod($dir, 0777);
				}
				if( !is_file($dir . 'index.html') ) {
					@file_put_contents($dir . 'index.html', '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body></body></html>');
				}
			}

			$absOriginal = $baseDir . (string)FOLDER_PREFIX . $uploaded;
			if( $originalUrl === '' ) {
				$originalUrl = rtrim($config['http_home_url'], '/') . '/uploads/public_files/wallpapers/' . (string)FOLDER_PREFIX . $uploaded;
			}

			if( is_file($absOriginal) ) {
				$thumbLibOk = dle_wallpaper_bootstrap_thumb_lib();
				try {
					$t_size = isset($config['max_image']) ? intval($config['max_image']) : 0;
					$m_size = isset($config['medium_image']) ? intval($config['medium_image']) : 0;
					if( $t_size && $t_size < 400 ) $t_size = 400;
					if( $m_size && $m_size < 800 ) $m_size = 800;
					$t_seite = isset($config['t_seite']) ? intval($config['t_seite']) : 0;
					$m_seite = $t_seite;
					$make_watermark = !empty($config['allow_watermark']);

					if( $thumbLibOk ) {
						if( $t_size ) {
							$thumb = new thumbnail($absOriginal, true, false);
							if( !$thumb->error ) {
								if( $thumb->size_auto($t_size, $t_seite) ) {
									if( $make_watermark ) $thumb->insert_watermark($config['max_watermark']);
									$thumb->save('public_files/wallpapers/' . (string)FOLDER_PREFIX . 'thumbs/' . $uploaded, false);
								}
							}
							unset($thumb);
						}

						if( $m_size ) {
							$medium = new thumbnail($absOriginal, true, false);
							if( !$medium->error ) {
								if( $medium->size_auto($m_size, $m_seite) ) {
									if( $make_watermark ) $medium->insert_watermark($config['max_watermark']);
									$medium->save('public_files/wallpapers/' . (string)FOLDER_PREFIX . 'medium/' . $uploaded, false);
								}
							}
							unset($medium);
						}
					}
					if( function_exists('gc_collect_cycles') ) {
						gc_collect_cycles();
					}
				} catch (Throwable $e) {
					// ignore thumbnail generation errors; original upload still valid
				}
			}

			$thumbUrl = rtrim(dirname($originalUrl), '/') . '/thumbs/' . basename($originalUrl);
			$mediumUrl = rtrim(dirname($originalUrl), '/') . '/medium/' . basename($originalUrl);

			// If thumbnail generation failed for some images, avoid storing broken thumbs/medium URLs
			$thumbAbs = $baseDir . (string)FOLDER_PREFIX . 'thumbs/' . $uploaded;
			$mediumAbs = $baseDir . (string)FOLDER_PREFIX . 'medium/' . $uploaded;
			if( !is_file($thumbAbs) ) {
				$thumbUrl = $originalUrl;
			}
			if( !is_file($mediumAbs) ) {
				$mediumUrl = $originalUrl;
			}

			$sub = isset($_REQUEST['dle_public_subdir']) ? (string)$_REQUEST['dle_public_subdir'] : '';
			$sub = preg_replace('~[^a-z0-9_\-/]~i', '', $sub);
			$sub = trim($sub, '/');
			$onserver_prefix = $sub ? ($sub . '/') : '';
			$onserver = $db->safesql( $onserver_prefix . (string)FOLDER_PREFIX . (string)$data['uploaded_filename'] );
			$row = $db->super_query( "SELECT id, size FROM " . PREFIX . "_files WHERE onserver='{$onserver}' AND author='".$db->safesql($member_id['name'])."' AND news_id='0' ORDER BY id DESC LIMIT 1" );
			if( isset($row['id']) ) {
				$fileId = intval($row['id']);
				$sizeStr = formatsize( intval($row['size']) );
			}
		}
		if( !$thumbUrl ) $thumbUrl = $originalUrl;
		if( !$mediumUrl ) $mediumUrl = $originalUrl;
		$data['xfvalue'] = $thumbUrl . '|' . $fileId . '|' . $sizeStr . '|' . $originalUrl . '|' . $mediumUrl;
	}

	if( $type === 'audio' ) {
		$url = isset($data['xfvalue']) ? (string)$data['xfvalue'] : '';
		$fileId = 0;
		$sizeStr = '';

		if( isset($data['uploaded_filename']) && $data['uploaded_filename'] ) {
			$uploaded = (string)$data['uploaded_filename'];
			if( $url === '' ) {
				$url = rtrim($config['http_home_url'], '/') . '/uploads/public_files/ringtones/' . (string)FOLDER_PREFIX . $uploaded;
			}

			$sub = isset($_REQUEST['dle_public_subdir']) ? (string)$_REQUEST['dle_public_subdir'] : '';
			$sub = preg_replace('~[^a-z0-9_\-/]~i', '', $sub);
			$sub = trim($sub, '/');
			$onserver_prefix = $sub ? ($sub . '/') : '';
			$onserver = $db->safesql( $onserver_prefix . (string)FOLDER_PREFIX . (string)$data['uploaded_filename'] );
			$row = $db->super_query( "SELECT id, size FROM " . PREFIX . "_files WHERE onserver='{$onserver}' AND author='".$db->safesql($member_id['name'])."' AND news_id='0' ORDER BY id DESC LIMIT 1" );
			if( isset($row['id']) ) {
				$fileId = intval($row['id']);
				$sizeStr = formatsize( intval($row['size']) );
			}
		}

		if( $url && !preg_match('~^https?://~i', $url) ) {
			$path = ltrim($url, '/');
			if( stripos($path, 'uploads/') === 0 ) {
				$url = rtrim($config['http_home_url'], '/') . '/' . $path;
			} else {
				$url = rtrim($config['http_home_url'], '/') . '/uploads/public_files/ringtones/' . $path;
			}
		}

		$data['xfvalue'] = $url . '|' . $fileId . '|' . $sizeStr;
	}

	if( $type === 'video' ) {
		$url = isset($data['xfvalue']) ? (string)$data['xfvalue'] : '';
		$fileId = 0;
		$sizeStr = '';

		if( isset($data['uploaded_filename']) && $data['uploaded_filename'] ) {
			$uploaded = (string)$data['uploaded_filename'];
			if( $url === '' ) {
				$url = rtrim($config['http_home_url'], '/') . '/uploads/public_files/videos/' . (string)FOLDER_PREFIX . $uploaded;
			}

			$sub = isset($_REQUEST['dle_public_subdir']) ? (string)$_REQUEST['dle_public_subdir'] : '';
			$sub = preg_replace('~[^a-z0-9_\-/]~i', '', $sub);
			$sub = trim($sub, '/');
			$onserver_prefix = $sub ? ($sub . '/') : '';
			$onserver = $db->safesql( $onserver_prefix . (string)FOLDER_PREFIX . (string)$data['uploaded_filename'] );
			$row = $db->super_query( "SELECT id, size FROM " . PREFIX . "_files WHERE onserver='{$onserver}' AND author='".$db->safesql($member_id['name'])."' AND news_id='0' ORDER BY id DESC LIMIT 1" );
			if( isset($row['id']) ) {
				$fileId = intval($row['id']);
				$sizeStr = formatsize( intval($row['size']) );
			}
		}

		if( $url && !preg_match('~^https?://~i', $url) ) {
			$path = ltrim($url, '/');
			if( stripos($path, 'uploads/') === 0 ) {
				$url = rtrim($config['http_home_url'], '/') . '/' . $path;
			} else {
				$url = rtrim($config['http_home_url'], '/') . '/uploads/public_files/videos/' . $path;
			}
		}

		$data['xfvalue'] = $url . '|' . $fileId . '|' . $sizeStr;
	}

	$data['type'] = $type;
	$data['original'] = $originalName;

	die( json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
}

elseif( $action == "doaddwallpaper" ) {

	if( !$_REQUEST['user_hash'] OR $_REQUEST['user_hash'] != $dle_login_hash ) {
		msg( "error", array('javascript:history.go(-1)' => 'Bulk Wallpaper Upload', '' => $lang['addnews_error'] ), $lang['sess_error'], "javascript:history.go(-1)" );
	}

	if( !isset($_POST['duplicateprotection']) OR (isset($_SESSION['dp']['dp_'.md5($_POST['duplicateprotection'])]) AND $_SESSION['dp']['dp_'.md5($_POST['duplicateprotection'])] === true ) ) {
		msg( "error", array('javascript:history.go(-1)' => 'Bulk Wallpaper Upload', '' => $lang['addnews_error'] ), $lang['duplicate_protect'], "javascript:history.go(-1)" );
	}

	@header('X-XSS-Protection: 0;');

	$parse = new ParseFilter();

	$allow_comm = isset( $_POST['allow_comm'] ) ? intval( $_POST['allow_comm'] ) : 0;
	$approve = isset( $_POST['approve'] ) ? intval( $_POST['approve'] ) : 0;
	$allow_rating = isset( $_POST['allow_rating'] ) ? intval( $_POST['allow_rating'] ) : 0;
	$news_fixed = isset( $_POST['news_fixed'] ) ? intval( $_POST['news_fixed'] ) : 0;
	$disable_index = isset( $_POST['disable_index'] ) ? intval( $_POST['disable_index'] ) : 0;
	$disable_search = isset( $_POST['disable_search'] ) ? intval( $_POST['disable_search'] ) : 0;
	$need_pass = 0;
	$allow_rss = isset( $_POST['allow_rss'] ) ? intval( $_POST['allow_rss'] ) : 0;
	$allow_rss_dzen = isset( $_POST['allow_rss_dzen'] ) ? intval( $_POST['allow_rss_dzen'] ) : 0;
	$allowed_country = '';
	$not_allowed_country = '';
	$related_ids = '';
	$group_regel = '';

	$category = isset($_POST['category']) ? $_POST['category'] : array();
	if( !is_array($category) ) $category = array();
	if( !count($category) ) $category[] = '0';

	$category_list_arr = array();
	foreach ( $category as $value ) {
		$category_list_arr[] = intval($value);
	}

	$allow_main = 0;
	if( $user_group[$member_id['user_group']]['allow_main'] ) {
		$allow_main = isset($_POST['allow_main']) ? intval($_POST['allow_main']) : 0;
	}

	if( !$config['allow_fixed'] OR !$user_group[$member_id['user_group']]['allow_fixed'] ) {
		$news_fixed = 0;
	}

	$disable_rss_dzen = 0;

	if( $member_id['user_group'] > 2 ) {
		$disable_index = 0;
		$disable_search = 0;
		$need_pass = 0;
		$allow_rss = 1;
		$allow_rss_dzen = 1;
		$allowed_country = '';
		$not_allowed_country = '';
	}

	if( !$config['allow_rss'] ) { $allow_rss = 1; }
	if( !$config['allow_yandex_dzen'] ) { $allow_rss_dzen = 0; }
	if( !$allow_rss ) { $allow_rss_dzen = 0; }

	if($member_id['cat_add']) $allow_list = explode( ',', $member_id['cat_add'] );
	else $allow_list = explode( ',', $user_group[$member_id['user_group']]['cat_add'] );

	$mail_send = false;

	if( $member_id['cat_allow_addnews']) $allow_list_add = explode( ',', $member_id['cat_allow_addnews'] );
	else $allow_list_add = explode( ',', $user_group[$member_id['user_group']]['cat_allow_addnews'] );

	foreach ( $category_list_arr as $selected ) {
		if( $allow_list_add[0] != "all" AND !in_array( $selected, $allow_list_add ) ) {
			msg( "error", array('javascript:history.go(-1)' => 'Bulk Wallpaper Upload', '' => $lang['addnews_error'] ), $lang['news_err_41'], "javascript:history.go(-1)" );
		}
		if(isset($cat_info[$selected]['disable_main']) AND $cat_info[$selected]['disable_main']) {
			$allow_main = 0;
		}
		if(isset($cat_info[$selected]['disable_comments']) AND $cat_info[$selected]['disable_comments']) {
			$allow_comm = 1;
		}
		if(isset($cat_info[$selected]['disable_rating']) AND $cat_info[$selected]['disable_rating']) {
			$allow_rating = 0;
		}

		if( $member_id['user_group'] > 2 ) {
			if( isset($cat_info[$selected]['enable_dzen']) AND !$cat_info[$selected]['enable_dzen'] ) {
				$disable_rss_dzen++;
			}
		}
		if( $allow_list[0] != "all" AND !in_array( $selected, $allow_list ) ) {
			$approve = 0;
			$mail_send = true;
		}
	}

	if( $member_id['user_group'] > 2 ) {
		if( $disable_rss_dzen > 0 AND $disable_rss_dzen == count($category_list_arr) ) {
			$allow_rss_dzen = 0;
		}
	}

	$category_list = $db->safesql( implode( ',', $category_list_arr ) );

	if( !$user_group[$member_id['user_group']]['moderation'] ) {
		$approve = 0;
		$mail_send = true;
	}

	$items = isset($_POST['items']) && is_array($_POST['items']) ? $_POST['items'] : array();
	if( !count($items) ) {
		msg( "error", array('javascript:history.go(-1)' => 'Bulk Wallpaper Upload', '' => $lang['addnews_error'] ), "No items", "javascript:history.go(-1)" );
	}

	$author = $member_id['name'];
	$userid = $member_id['user_id'];
	$added_posts = 0;
	$skipped_items = 0;
	$invalid_items = 0;
	$invalid_missing_xfvalue = 0;
	$invalid_bad_type = 0;
	$invalid_bad_title = 0;
	$invalid_insert_failed = 0;
	$added_time = time();
	$thistime = date( "Y-m-d H:i:s", $added_time );

	foreach ($items as $item) {

		if( isset($item['skip']) && intval($item['skip']) ) { $skipped_items++; continue; }

		$type = isset($item['type']) ? trim((string)$item['type']) : '';
		$xfvalue = isset($item['xfvalue']) ? trim((string)$item['xfvalue']) : '';
		$title_raw = isset($item['title']) ? trim((string)$item['title']) : '';
		$original = isset($item['original']) ? trim((string)$item['original']) : '';

		if( !$xfvalue ) { $invalid_items++; $invalid_missing_xfvalue++; continue; }

		if( !$type ) {
			$first = explode('|', $xfvalue);
			$first = isset($first[0]) ? strtolower(trim((string)$first[0])) : '';
			$nameLower = strtolower($original);
			if( preg_match('~\.mp4($|\?)~', $first) || preg_match('~\.mp4$~', $nameLower) ) {
				$type = 'video';
			} elseif( preg_match('~\.(mp3|wav|ogg|m4a|aac)($|\?)~', $first) || preg_match('~\.(mp3|wav|ogg|m4a|aac)$~', $nameLower) ) {
				$type = 'audio';
			} elseif( preg_match('~\.(jpe?g|png|gif|webp|bmp|avif)($|\?)~', $first) || preg_match('~\.(jpe?g|png|gif|webp|bmp|avif)$~', $nameLower) ) {
				$type = 'image';
			}
		}

		if( $type !== 'image' && $type !== 'video' && $type !== 'audio' ) { $invalid_items++; $invalid_bad_type++; continue; }

		if( !$title_raw ) {
			$title_raw = dle_wallpaper_clean_title($original);
		} else {
			$title_raw = dle_wallpaper_sanitize_title_text($title_raw);
		}

		$title = $parse->process( trim( strip_tags ($title_raw) ) );
		if( !$title ) { $invalid_items++; $invalid_bad_title++; continue; }
		if( dle_strlen( $title ) > 255 ) { $invalid_items++; $invalid_bad_title++; continue; }

		$alt_name = totranslit( stripslashes( $title ), true, false, $config['translit_url'] );
		$title_db = $db->safesql( $title );
		$alt_name = $db->safesql( $alt_name );

		$catalog_url = $db->safesql( dle_substr( htmlspecialchars( strip_tags( stripslashes( trim( $_POST['catalog_url'] ?? '' ) ) ), ENT_QUOTES, 'UTF-8' ), 0, 3 ) );
		if ($config['create_catalog'] AND !$catalog_url) $catalog_url = $db->safesql( dle_substr( htmlspecialchars( strip_tags( stripslashes( trim( $title ) ) ), ENT_QUOTES, 'UTF-8' ), 0, 1 ) );

		$metatags = create_metatags($title);

		$short_story = '';
		$full_story = '';
		$imageFileId = 0;
		$imageSize = '';
		$videoFileId = 0;
		$videoSize = '';
		$audioFileId = 0;
		$audioSize = '';

		if( $type === 'image' ) {
			$parts = explode('|', $xfvalue);
			$thumbUrl = isset($parts[0]) ? trim((string)$parts[0]) : '';
			$imageFileId = isset($parts[1]) ? intval($parts[1]) : 0;
			$imageSize = isset($parts[2]) ? trim((string)$parts[2]) : '';
			$originalUrl = isset($parts[3]) ? trim((string)$parts[3]) : '';
			$mediumUrl = isset($parts[4]) ? trim((string)$parts[4]) : '';
			if( !$originalUrl ) $originalUrl = $thumbUrl;
			// Listings should use a reasonably sharp image (cards/grid often upscale posters).
			$listingUrl = $mediumUrl ? $mediumUrl : ($thumbUrl ? $thumbUrl : $originalUrl);
			if( $listingUrl ) {
				// Homepage/listings use medium (or thumb fallback)
				$short_story = "[img]{$listingUrl}[/img]";
				$full_story = "[img]{$originalUrl}[/img]";
			}
		} elseif( $type === 'video' ) {
			$parts = explode('|', $xfvalue);
			$videoUrl = isset($parts[0]) ? trim((string)$parts[0]) : '';
			$videoFileId = isset($parts[1]) ? intval($parts[1]) : 0;
			$videoSize = isset($parts[2]) ? trim((string)$parts[2]) : '';
			if( $videoUrl ) {
				$short_story = "[video={$videoUrl}]";
				$full_story = $short_story;
			}
		} else {
			$parts = explode('|', $xfvalue);
			$audioUrl = isset($parts[0]) ? trim((string)$parts[0]) : '';
			$audioFileId = isset($parts[1]) ? intval($parts[1]) : 0;
			$audioSize = isset($parts[2]) ? trim((string)$parts[2]) : '';
			if( $audioUrl ) {
				$short_story = "[audio={$audioUrl}]";
				$full_story = $short_story;
			}
		}

		// Important for themes like MovieTor: {image-1} is derived from parsed <img>,
		// so we must run DLE's BB parser before saving.
		$short_story_processed = $parse->process( $short_story );
		$full_story_processed = $parse->process( $full_story );
		$short_story_parsed = $parse->BB_Parse( $short_story_processed );
		$full_story_parsed = $parse->BB_Parse( $full_story_processed );

		// Fallback: if BB parser doesn't convert (keeps [img]), ensure we still store HTML <img>
		if( $type === 'image' ) {
			if( stripos($short_story_parsed, '[img') !== false ) {
				$imgUrl = htmlspecialchars($listingUrl, ENT_QUOTES, 'UTF-8');
				$imgAlt = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
				$short_story_parsed = "<img src=\"{$imgUrl}\" style=\"max-width:100%;\" alt=\"{$imgAlt}\">";
			}
			if( stripos($full_story_parsed, '[img') !== false ) {
				$imgUrl = htmlspecialchars($originalUrl, ENT_QUOTES, 'UTF-8');
				$imgAlt = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
				$full_story_parsed = "<img src=\"{$imgUrl}\" style=\"max-width:100%;\" alt=\"{$imgAlt}\">";
			}
		}

		$short_story_sql = $db->safesql( $short_story_parsed );
		$full_story_sql = $db->safesql( $full_story_parsed );

		$xfields_data = [];
		if( $type === 'image' ) {
			$xfields_data['image_url_poster'] = $mediumUrl ? $mediumUrl : ($originalUrl ? $originalUrl : $thumbUrl);
			$xfields_data['wallpaper'] = $originalUrl ? $originalUrl : $thumbUrl;
			$xfields_data['thumb_url_wallpaper'] = $thumbUrl;
			$xfields_data['image_url_wallpaper'] = $originalUrl ? $originalUrl : $thumbUrl;
			if( $mediumUrl ) {
				$xfields_data['medium_url_wallpaper'] = $mediumUrl;
			}
			if( $imageFileId > 0 ) {
				$xfields_data['wallpaper_file_id'] = $imageFileId;
				if( $imageSize !== '' ) {
					$xfields_data['wallpaper_file_size'] = $imageSize;
				}
			}
		} elseif( $type === 'video' ) {
			if( !empty($videoUrl) ) {
				$videoXf = $videoUrl;
				if( $videoFileId > 0 ) {
					$videoXf .= "|" . $videoFileId;
					if( $videoSize !== '' ) {
						$videoXf .= "|" . $videoSize;
					}
				}
				$xfields_data['video_url'] = $videoXf;
			}
		} elseif( $type === 'audio' ) {
			if( !empty($audioUrl) ) {
				$audioXf = $audioUrl;
				if( $audioFileId > 0 ) {
					$audioXf .= "|" . $audioFileId;
					if( $audioSize !== '' ) {
						$audioXf .= "|" . $audioSize;
					}
				}
				$xfields_data['mp3'] = $audioXf;
			}
		}

		$xfields_string = dle_wallpaper_build_xfields_string($xfields_data);
		$xfields_sql = $xfields_string ? $db->safesql($xfields_string) : '';

		$tags_string = dle_wallpaper_generate_auto_tags($title, $type);
		$tags_sql = $tags_string ? $db->safesql($tags_string) : '';

		$db->query( "INSERT INTO " . PREFIX . "_post (date, autor, short_story, full_story, xfields, title, descr, keywords, category, alt_name, allow_comm, approve, allow_main, fixed, allow_br, symbol, tags, metatitle) values ('{$thistime}', '{$author}', '{$short_story_sql}', '{$full_story_sql}', '{$xfields_sql}', '{$title_db}', '{$metatags['description']}', '{$metatags['keywords']}', '{$category_list}', '{$alt_name}', '{$allow_comm}', '{$approve}', '{$allow_main}', '{$news_fixed}', '0', '{$catalog_url}', '{$tags_sql}', '{$metatags['title']}')" );
		$id = $db->insert_id();
		if( !$id ) { $invalid_items++; $invalid_insert_failed++; continue; }

		if( $type === 'image' && $imageFileId > 0 ) {
			$db->query( "UPDATE " . PREFIX . "_files SET news_id='{$id}' WHERE id='" . intval($imageFileId) . "' AND news_id='0'" );
		}
		if( $type === 'video' && $videoFileId > 0 ) {
			$db->query( "UPDATE " . PREFIX . "_files SET news_id='{$id}' WHERE id='" . intval($videoFileId) . "' AND news_id='0'" );
		}
		if( $type === 'audio' && $audioFileId > 0 ) {
			$db->query( "UPDATE " . PREFIX . "_files SET news_id='{$id}' WHERE id='" . intval($audioFileId) . "' AND news_id='0'" );
		}

		if( $approve && $tags_string ) {
			$tags_arr = explode(',', $tags_string);
			$values = array();
			foreach( $tags_arr as $t ) {
				$t = trim((string)$t);
				if( $t === '' ) continue;
				$values[] = "('" . intval($id) . "', '" . $db->safesql($t) . "')";
			}
			if( count($values) ) {
				$db->query( "INSERT INTO " . PREFIX . "_tags (news_id, tag) VALUES " . implode(', ', $values ) );
			}
		}

		$db->query( "INSERT INTO " . PREFIX . "_post_extras (news_id, allow_rate, votes, disable_index, related_ids, access, user_id, disable_search, need_pass, allow_rss, allow_rss_dzen, allowed_country, not_allowed_country) VALUES('{$id}', '{$allow_rating}', '0', '{$disable_index}', '{$related_ids}', '{$group_regel}', '{$userid}', '{$disable_search}', '{$need_pass}', '{$allow_rss}', '{$allow_rss_dzen}', '{$allowed_country}', '{$not_allowed_country}')" );

		if( $category_list AND $approve ) {
			$cat_ids = array();
			$cat_ids_arr = explode( ",", $category_list );
			foreach ( $cat_ids_arr as $value ) {
				$cat_ids[] = "('" . $id . "', '" . trim( $value ) . "')";
			}
			if( count($cat_ids) ) {
				$db->query( "INSERT INTO " . PREFIX . "_post_extras_cats (news_id, cat_id) VALUES " . implode( ", ", $cat_ids ) );
			}
		}

		$parts = explode('|', $xfvalue);
		$fileId = isset($parts[1]) ? intval($parts[1]) : 0;
		if( $fileId ) {
			$db->query( "UPDATE " . PREFIX . "_files SET news_id='{$id}', author = '".$db->safesql($author)."' WHERE id='{$fileId}' AND author = '".$db->safesql($author)."' AND news_id = '0'" );
		}

		$db->query( "INSERT INTO " . USERPREFIX . "_admin_logs (name, date, ip, action, extras) values ('".$db->safesql($member_id['name'])."', '{$_TIME}', '{$_IP}', '1', '{$title_db}')" );

		$added_posts++;
	}

	if( !$added_posts ) {
		$total = count($items);
		$first = is_array($items) ? reset($items) : array();
		$first_type = isset($first['type']) ? (string)$first['type'] : '';
		$first_xf = isset($first['xfvalue']) ? (string)$first['xfvalue'] : '';
		$first_original = isset($first['original']) ? (string)$first['original'] : '';
		$first_title = isset($first['title']) ? (string)$first['title'] : '';
		$first_skip = isset($first['skip']) ? (string)$first['skip'] : '';
		$first_xf_prefix = dle_substr($first_xf, 0, 120);
		$sanitize = function($v) {
			$v = str_replace(array("\r", "\n", "\t"), ' ', (string)$v);
			$v = preg_replace('/\s+/u', ' ', $v);
			return htmlspecialchars($v, ENT_QUOTES, 'UTF-8');
		};
		$debug = "First item => type=".$sanitize($first_type)."; skip=".$sanitize($first_skip)."; original=".$sanitize($first_original)."; title=".$sanitize($first_title)."; xfvalue_len=".strlen($first_xf)."; xfvalue_prefix=".$sanitize($first_xf_prefix);
		$breakdown = "Invalid breakdown => missing_xfvalue={$invalid_missing_xfvalue}; bad_type={$invalid_bad_type}; bad_title={$invalid_bad_title}; insert_failed={$invalid_insert_failed}";
		msg( "error", array('javascript:history.go(-1)' => 'Bulk Wallpaper Upload', '' => $lang['addnews_error'] ), "No posts created. Items received: {$total}. Skipped: {$skipped_items}. Invalid: {$invalid_items}. {$breakdown}. {$debug}", "javascript:history.go(-1)" );
	}

	$db->query( "UPDATE " . USERPREFIX . "_users SET news_num=news_num+{$added_posts} WHERE user_id='{$userid}'" );
	clear_cache( array('news_', 'tagscloud_', 'archives_', 'calendar_', 'topnews_', 'rss', 'stats') );
	$_SESSION['dp']['dp_'.md5($_POST['duplicateprotection'])] = true;

	msg( "success", $lang['addnews_ok'], "Created {$added_posts} posts.", array('?mod=addwallpaper&action=addwallpaper' => 'Bulk Wallpaper Upload', '?mod=editnews&action=list' => $lang['add_s_3'] ) );
}

?>
