Import product photos from live sites; link downloads and SEO meta to products
- vitec:import-product-images: attach staged photos (fileadmin/user_upload/ products/<slug>/, 116 files harvested from vitec.com and datapath.co.uk carousels) to image-less products via FAL/DataHandler; fill-up only. fix_image_reference_fieldname.php repairs the first run's references (TCA field is productimage, not the JSON key images) - vitec:link-product-downloads: match the download records imported on 2026-08-06 to products by title (doc-type/brand/role suffix stripping, alias table for live-site naming deviations, series rules for g45xx/e38xx/ chassis docs); 55 unique-match links added, add-only, idempotent - SEO meta: seo_meta_vitec.json + apply_seo_meta.php fill empty seotitle/ seometa from the live vitec.com product pages (11 products, fill-up only)"
This commit is contained in:
75
migrations/apply_seo_meta.php
Normal file
75
migrations/apply_seo_meta.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Fill empty seotitle/seometa product fields from a scraped mapping file
|
||||
* (migrations/seo_meta_vitec.json - source: the live vitec.com product
|
||||
* pages, 2026-09-04). Fill-up ONLY: fields that already hold a value are
|
||||
* never overwritten.
|
||||
*
|
||||
* Dry run: php migrations/apply_seo_meta.php
|
||||
* Write: php migrations/apply_seo_meta.php --apply
|
||||
*/
|
||||
|
||||
$apply = in_array('--apply', $argv, true);
|
||||
|
||||
$root = null;
|
||||
$dir = __DIR__;
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
if (is_file($dir . '/config/system/settings.php')) {
|
||||
$root = $dir;
|
||||
break;
|
||||
}
|
||||
$dir = dirname($dir);
|
||||
}
|
||||
if ($root === null) {
|
||||
exit("Could not locate project root above " . __DIR__ . "\n");
|
||||
}
|
||||
|
||||
$map = json_decode((string)file_get_contents(__DIR__ . '/seo_meta_vitec.json'), true);
|
||||
if (!is_array($map)) {
|
||||
exit("seo_meta_vitec.json missing or invalid.\n");
|
||||
}
|
||||
unset($map['_quelle']);
|
||||
|
||||
$settings = require $root . '/config/system/settings.php';
|
||||
$db = $settings['DB']['Connections']['Default'];
|
||||
$pdo = new PDO(
|
||||
sprintf('mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4', $db['host'] ?? 'localhost', $db['port'] ?? 3306, $db['dbname'] ?? ''),
|
||||
$db['user'] ?? '',
|
||||
$db['password'] ?? '',
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
|
||||
);
|
||||
|
||||
$select = $pdo->prepare("SELECT uid, title, seotitle, seometa FROM tx_vitec_domain_model_product WHERE deleted = 0 AND slug = ?");
|
||||
$update = $pdo->prepare("UPDATE tx_vitec_domain_model_product SET seotitle = ?, seometa = ?, tstamp = ? WHERE uid = ?");
|
||||
|
||||
$written = 0;
|
||||
foreach ($map as $slug => $meta) {
|
||||
$select->execute([$slug]);
|
||||
$product = $select->fetch();
|
||||
if (!$product) {
|
||||
echo "FEHLT $slug - kein Produkt\n";
|
||||
continue;
|
||||
}
|
||||
$newTitle = trim((string)$product['seotitle']) === '' ? trim((string)($meta['seotitle'] ?? '')) : (string)$product['seotitle'];
|
||||
$newMeta = trim((string)$product['seometa']) === '' ? trim((string)($meta['seometa'] ?? '')) : (string)$product['seometa'];
|
||||
$changes = [];
|
||||
if ($newTitle !== (string)$product['seotitle']) {
|
||||
$changes[] = 'seotitle';
|
||||
}
|
||||
if ($newMeta !== (string)$product['seometa']) {
|
||||
$changes[] = 'seometa';
|
||||
}
|
||||
if ($changes === []) {
|
||||
echo "ok {$product['title']} - beide Felder bereits gefuellt oder keine Daten\n";
|
||||
continue;
|
||||
}
|
||||
printf("%s %s (uid %d): %s\n", $apply ? 'WRITE ' : 'plan ', $product['title'], $product['uid'], implode(' + ', $changes));
|
||||
if ($apply) {
|
||||
$update->execute([$newTitle, $newMeta, time(), (int)$product['uid']]);
|
||||
}
|
||||
$written++;
|
||||
}
|
||||
printf("\n%d Produkt(e) %s.%s\n", $written, $apply ? 'geschrieben' : 'geplant', $apply ? ' Cache leeren: vendor/bin/typo3 cache:flush' : ' Mit --apply schreiben.');
|
||||
59
migrations/fix_image_reference_fieldname.php
Normal file
59
migrations/fix_image_reference_fieldname.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* One-shot repair (2026-09-04): the first vitec:import-product-images run
|
||||
* wrote its sys_file_reference rows with fieldname 'images' - the JSON key -
|
||||
* but the TCA field (and everything that queries it) is 'productimage'.
|
||||
* Re-files those references and refreshes the products' inline counter.
|
||||
*
|
||||
* Usage: php migrations/fix_image_reference_fieldname.php
|
||||
*/
|
||||
|
||||
$root = null;
|
||||
$dir = __DIR__;
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
if (is_file($dir . '/config/system/settings.php')) {
|
||||
$root = $dir;
|
||||
break;
|
||||
}
|
||||
$dir = dirname($dir);
|
||||
}
|
||||
if ($root === null) {
|
||||
exit("Could not locate project root above " . __DIR__ . "\n");
|
||||
}
|
||||
|
||||
$settings = require $root . '/config/system/settings.php';
|
||||
$db = $settings['DB']['Connections']['Default'];
|
||||
$pdo = new PDO(
|
||||
sprintf('mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4', $db['host'] ?? 'localhost', $db['port'] ?? 3306, $db['dbname'] ?? ''),
|
||||
$db['user'] ?? '',
|
||||
$db['password'] ?? '',
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
|
||||
);
|
||||
|
||||
$wrong = (int)$pdo->query(
|
||||
"SELECT COUNT(*) FROM sys_file_reference
|
||||
WHERE deleted = 0 AND tablenames = 'tx_vitec_domain_model_product' AND fieldname = 'images'"
|
||||
)->fetchColumn();
|
||||
echo "Misfiled references (fieldname 'images'): $wrong\n";
|
||||
|
||||
if ($wrong > 0) {
|
||||
$pdo->exec(
|
||||
"UPDATE sys_file_reference SET fieldname = 'productimage'
|
||||
WHERE deleted = 0 AND tablenames = 'tx_vitec_domain_model_product' AND fieldname = 'images'"
|
||||
);
|
||||
echo "Re-filed to 'productimage'.\n";
|
||||
}
|
||||
|
||||
$updated = $pdo->exec(
|
||||
"UPDATE tx_vitec_domain_model_product p
|
||||
SET p.productimage = (
|
||||
SELECT COUNT(*) FROM sys_file_reference r
|
||||
WHERE r.deleted = 0 AND r.uid_foreign = p.uid
|
||||
AND r.tablenames = 'tx_vitec_domain_model_product' AND r.fieldname = 'productimage'
|
||||
)
|
||||
WHERE p.deleted = 0"
|
||||
);
|
||||
echo "Inline counters refreshed on $updated product(s). Flush the cache: vendor/bin/typo3 cache:flush\n";
|
||||
51
migrations/seo_meta_vitec.json
Normal file
51
migrations/seo_meta_vitec.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"_quelle": "vitec.com Produktseiten, Scrape 2026-09-04. Nur Produkt-Detailseiten; Kategorieseiten-Metas (ChannelLink, 45-series, PRISM, 38-Series) bewusst ausgelassen (zu generisch). 'VITEC - '-Praefix entfernt.",
|
||||
"mgw-diamond-sdi-encoder": {
|
||||
"seotitle": "MGW Diamond - 4K and Multi-Channel SD/HD HEVC Encoder",
|
||||
"seometa": "MGW Diamond is a small, power-efficient quad channel HD or one channel 4K HEVC video encoder ideal for field-based applications. It features a powerful encoding engine with the ability to output up to eight streams simultaneously."
|
||||
},
|
||||
"mgw-ace-encoder": {
|
||||
"seotitle": "MGW Ace Encoder Compact HEVC (H.265) Hardware Encoder",
|
||||
"seometa": "MGW Ace Encoder is the world's first HEVC / H.265 hardware encoder in a professional grade portable streaming appliance. Powered by VITEC HEVC GEN2+ encoder, it delivers industry's best video quality with up to 50% bandwidth savings compared to H.264 and Ultra Low Latency streaming down to 16ms glass-to-glass."
|
||||
},
|
||||
"mgw-ace-decoder": {
|
||||
"seotitle": "MGW Ace Decoder - Professional Portable HEVC & H.264 Decoder",
|
||||
"seometa": "MGW Ace Decoder is a professional grade, high performance IP decoder supporting the bandwidth efficient HEVC/H.265 and H.264/AVC compression standards, with 4:2:2 10-bit decoding from IP or DVB-ASI, genlock support and 4K-ready 12G-SDI and HDMI outputs."
|
||||
},
|
||||
"mgw-pico-portable-encoder": {
|
||||
"seotitle": "MGW Pico Encoder - Ultra-Compact, Low Latency H.264 Encoding & Streaming Appliance",
|
||||
"seometa": "The MGW Pico Encoder is the world's smallest H.264 HD/SD portable encoding appliance. With 3G/HD/SD-SDI and Composite inputs, low power consumption and robust industrial design, the appliance is ideal for any video field-based streaming application."
|
||||
},
|
||||
"mgw-diamond-og-sdi-composite-blade-encoder": {
|
||||
"seotitle": "MGW Diamond OG - 4K and Multi-Channel SD/HD HEVC VITEC OG Encoder Card",
|
||||
"seometa": "MGW Diamond OG is a small, power-efficient quad channel HD or one channel 4K HEVC video encoder card for the openGear ecosystem, with a powerful encoding engine able to output up to eight streams simultaneously."
|
||||
},
|
||||
"mgw-diamond-og-sdi-blade-encoder": {
|
||||
"seotitle": "MGW Diamond+ OG Encoder - Multi-codec, Broadcast-grade 4K/Multichannel HD VITEC OG Encoder Card",
|
||||
"seometa": "MGW Diamond+ OG is a broadcast grade HEVC, H.264 and MPEG-2 IP encoder that is ideal for contribution or point-to-point streaming applications and compatible with the openGear ecosystem for seamless integration."
|
||||
},
|
||||
"mgw-diamond-hx-og": {
|
||||
"seotitle": "MGW Diamond-Hx OG Encoder 4K & Multi-Channel SD/HD HDMI VITEC OG Encoder Card",
|
||||
"seometa": "openGear 4K HEVC video encoder card that is ideal for IPTV distribution or Direct-to-Web applications, featuring a powerful encoding engine with the ability to deliver up to four streams simultaneously."
|
||||
},
|
||||
"mgw-ace-decoder-og-ultra-low-latency-blade-encoder": {
|
||||
"seotitle": "MGW Ace Decoder OG - Professional HEVC & H.264 openGear Decoder Card",
|
||||
"seometa": "MGW Ace Decoder OG is a professional grade, high performance IP decoder card supporting HEVC/H.265 and H.264/AVC, with 4:2:2 10-bit decoding from IP or DVB-ASI, genlock support and 4K-ready outputs for the openGear ecosystem."
|
||||
},
|
||||
"diamond-ip-og-ip-to-ip-encoder": {
|
||||
"seotitle": "Diamond-IP OG Encoder - 4K HEVC encoder with SMPTE ST2110 capture",
|
||||
"seometa": ""
|
||||
},
|
||||
"mges7000-high-density-4kuhdhd-hevc-h264-iptv-encoding-blade": {
|
||||
"seotitle": "MGES 7000 - Market Leading High Density 4K/UHD/HD HEVC & H.264 IPTV Encoding Blade",
|
||||
"seometa": "Featuring the highest density available on the market, VITEC's 4K/UHD/HD HEVC & H.264 IPTV 8-input blade offers real-time hardware encoding with secondary channel, integrated resolution and frame-rate scaling, AES 256/128-bit encryption and low latency mode."
|
||||
},
|
||||
"mges6000-broadcast-quality-hdsd-h264-quad-input-iptv-encoder": {
|
||||
"seotitle": "MGES 6000 - Broadcast-quality HD/SD H.264 Quad-Input IPTV Encoder",
|
||||
"seometa": "The MGES-6000 Blade provides real-time hardware encoding of HD and SD video for all IPTV applications, with best-in-class picture quality, a secondary low-res channel, streaming to up to seven IP destinations per port and optional AES-256/128-bit encryption."
|
||||
},
|
||||
"x-player-end-points-xp23-xp25-xp26": {
|
||||
"seotitle": "VITEC X-Player End-Points",
|
||||
"seometa": "High-performance IP video & digital signage playback devices"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user