| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/clients/client2/web32/web/tools/ |
Upload File : |
<?php
/**
* Duplikat-Prüfer & -Löscher für live_cam Posts.
*
* Findet Cams, die DOPPELT angelegt wurden. Maßgeblich ist die _vx_cam_id:
* dieselbe Cam-ID darf nur EINMAL existieren. Wird sie zweimal angelegt
* (z.B. paralleler Cron + manueller Sync), hängt WordPress an den zweiten
* Slug ein "-N" an -> name-cam-<camid>-2. Beides wird hier erkannt.
*
* Kanonisch (= bleibt erhalten) ist immer der ÄLTESTE Post (kleinste ID) je
* Cam-ID -- exakt das, was der Syncer via lookup_by_cam_id() wiederfindet.
* Alle weiteren Posts derselben Cam-ID sind Duplikate und werden gelöscht;
* dazu werden verwaiste Attachments (Thumbnail, Galerie, Avatar) mit entfernt.
*
* Aufruf (CLI braucht PHP >=7.4 MIT mysqli -> hier: php8.4):
* CLI (anzeigen): php8.4 tools/dedupe-cams.php
* CLI (löschen): php8.4 tools/dedupe-cams.php --delete
* Browser (zeigen): /tools/dedupe-cams.php?token=1fa05e8f21a19788
* Browser (lösch.): /tools/dedupe-cams.php?token=1fa05e8f21a19788&delete=1
*
* Ohne --delete / &delete=1 wird NUR angezeigt (Dry-Run) -- nichts gelöscht.
*/
define( 'VXLC_DEDUPE_TOKEN', '1fa05e8f21a19788' );
$is_cli = ( PHP_SAPI === 'cli' );
// ---- Zugriffsschutz im Browser -------------------------------------------
if ( ! $is_cli ) {
$token = isset( $_GET['token'] ) ? (string) $_GET['token'] : '';
if ( ! hash_equals( VXLC_DEDUPE_TOKEN, $token ) ) {
http_response_code( 403 );
header( 'Content-Type: text/plain; charset=utf-8' );
exit( "403 - falscher oder fehlender token\n" );
}
header( 'Content-Type: text/plain; charset=utf-8' );
}
// ---- WordPress laden ------------------------------------------------------
require __DIR__ . '/../wp-load.php';
if ( ! post_type_exists( 'live_cam' ) ) {
fwrite( STDERR, "FEHLER: Post-Type 'live_cam' nicht registriert (Plugin aktiv?).\n" );
exit( 1 );
}
$do_delete = $is_cli
? in_array( '--delete', $argv, true )
: ( isset( $_GET['delete'] ) && $_GET['delete'] === '1' );
global $wpdb;
/**
* Alle Cam-IDs sammeln, die mehr als einen Post besitzen.
* Rückgabe: [ cam_id => [ post_id, post_id, ... ] ] aufsteigend nach post_id.
*/
$rows = $wpdb->get_results(
"SELECT pm.meta_value AS cam_id, pm.post_id AS post_id, p.post_name, p.post_title, p.post_date
FROM {$wpdb->postmeta} pm
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = '_vx_cam_id'
AND p.post_type = 'live_cam'
ORDER BY CAST(pm.meta_value AS UNSIGNED) ASC, p.ID ASC",
ARRAY_A
);
$by_cam = array();
foreach ( $rows as $r ) {
$by_cam[ (string) $r['cam_id'] ][] = $r;
}
$groups = array_filter( $by_cam, function ( $posts ) {
return count( $posts ) > 1;
} );
// ---- Ausgabe ---------------------------------------------------------------
$line = str_repeat( '=', 78 );
echo "$line\n";
echo " live_cam Duplikat-Prüfer" . ( $do_delete ? ' [LÖSCHEN AKTIV]' : ' [DRY-RUN / nur anzeigen]' ) . "\n";
echo "$line\n";
echo "Gesamt live_cam Posts : " . count( $rows ) . "\n";
echo "Eindeutige Cam-IDs : " . count( $by_cam ) . "\n";
echo "Cam-IDs mit Duplikaten: " . count( $groups ) . "\n";
if ( empty( $groups ) ) {
echo "\n>> Keine Duplikate gefunden. Nichts zu tun.\n";
exit( 0 );
}
$total_dupes = 0;
$delete_ids = array(); // post_id => cam_id
echo "\nGefundene Duplikate (kanonisch = ältester Post bleibt erhalten):\n\n";
foreach ( $groups as $cam_id => $posts ) {
$keep = $posts[0]; // kleinste ID = ältester
echo "Cam-ID $cam_id:\n";
echo sprintf( " BEHALTEN #%-8d %s (%s)\n", $keep['post_id'], $keep['post_name'], $keep['post_date'] );
for ( $i = 1; $i < count( $posts ); $i++ ) {
$d = $posts[ $i ];
$has_n = preg_match( '/-cam-\d+-\d+$/', $d['post_name'] ) ? ' [-N slug]' : '';
echo sprintf( " LÖSCHEN #%-8d %s (%s)%s\n", $d['post_id'], $d['post_name'], $d['post_date'], $has_n );
$delete_ids[ (int) $d['post_id'] ] = $cam_id;
$total_dupes++;
}
echo "\n";
}
echo "Zu löschende Duplikat-Posts: $total_dupes\n";
if ( ! $do_delete ) {
echo "\n(DRY-RUN) Es wurde NICHTS gelöscht.\n";
echo "Zum Löschen erneut mit --delete (CLI) bzw. &delete=1 (Browser) aufrufen.\n";
exit( 0 );
}
// ---- Löschen ---------------------------------------------------------------
echo "\n" . str_repeat( '-', 78 ) . "\nLösche...\n";
/**
* Attachment nur löschen, wenn KEIN anderer Post es noch referenziert
* (Thumbnail, Galerie, Avatar werden per Hash dedupliziert und ggf. geteilt).
*/
$attachment_in_use = function ( $att_id, $exclude_post_id ) use ( $wpdb ) {
$att_id = (int) $att_id;
if ( $att_id <= 0 ) return true;
// Eltern-Beziehung
$cnt = (int) $wpdb->get_var( $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_parent = %d AND ID <> %d AND post_type <> 'attachment'",
$att_id, $exclude_post_id
) );
if ( $cnt > 0 ) return true;
// Referenz in Meta (_thumbnail_id, _vx_avatar_attachment, _vx_gallery_ids serialisiert)
$like = '%' . $wpdb->esc_like( (string) $att_id ) . '%';
$cnt = (int) $wpdb->get_var( $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->postmeta}
WHERE post_id <> %d
AND ( ( meta_key = '_thumbnail_id' AND meta_value = %s )
OR ( meta_key = '_vx_avatar_attachment' AND meta_value = %s )
OR ( meta_key = '_vx_gallery_ids' AND meta_value LIKE %s ) )",
$exclude_post_id, (string) $att_id, (string) $att_id, $like
) );
return $cnt > 0;
};
$deleted_posts = 0;
$deleted_atts = 0;
foreach ( $delete_ids as $post_id => $cam_id ) {
// Attachments dieses Duplikats einsammeln
$att_ids = array();
$thumb = (int) get_post_thumbnail_id( $post_id );
if ( $thumb ) $att_ids[] = $thumb;
$avatar = (int) get_post_meta( $post_id, '_vx_avatar_attachment', true );
if ( $avatar ) $att_ids[] = $avatar;
$gallery = get_post_meta( $post_id, '_vx_gallery_ids', true );
if ( is_array( $gallery ) ) {
foreach ( $gallery as $g ) { if ( (int) $g ) $att_ids[] = (int) $g; }
}
// child-attachments per post_parent
$children = get_posts( array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_parent' => $post_id,
'numberposts' => -1,
'fields' => 'ids',
) );
foreach ( $children as $c ) $att_ids[] = (int) $c;
$att_ids = array_unique( array_filter( $att_ids ) );
foreach ( $att_ids as $att_id ) {
if ( $attachment_in_use( $att_id, $post_id ) ) {
echo " ~ Attachment #$att_id noch in Benutzung -> behalten\n";
continue;
}
if ( wp_delete_attachment( $att_id, true ) ) {
$deleted_atts++;
}
}
$res = wp_delete_post( $post_id, true ); // true = endgültig, kein Papierkorb
if ( $res ) {
$deleted_posts++;
echo " - Cam-ID $cam_id: Post #$post_id gelöscht\n";
} else {
echo " ! Post #$post_id konnte NICHT gelöscht werden\n";
}
}
echo "\n" . str_repeat( '=', 78 ) . "\n";
echo "FERTIG. Gelöscht: $deleted_posts Duplikat-Posts, $deleted_atts verwaiste Attachments.\n";
echo str_repeat( '=', 78 ) . "\n";