﻿# Changelog

## [5.7.0] - 2026-09-21

### Added
- **Scrape Description** (`scripts/scrape_description.py`) — scrape deskripsi dari Pinterest search results dan simpan ke kolom `snippet` dalam format JSON
  - Progressive scroll: jika deskripsi kurang dari `min_snippet`, page_size dinaikkan +10 hingga cap `snippet_max_page_size`
  - Jika tetap kurang dari min tapi ada deskripsi, tetap disimpan
  - Skip hanya jika 0 deskripsi ditemukan
  - CLI command: `python scripts/cli.py scrape-description`
- **Related Keywords** — fetch related keywords dari Google Suggest API (`google.com/complete/search`), sama dengan logic di `Tools KW/main.go`
  - Disimpan bersama deskripsi dalam format JSON `{"related_kw": [...], "description": [...]}`
  - Related keyword diambil max 10 per keyword (configurable)
- **`fetch_related_keywords()`** di `pinscrape/v2.py` — fungsi standalone untuk fetch related keywords via Google Suggest API
- **`format_description()`** di `pinscrape/utils.py` — hapus HTML tag, normalize line ending, preserve format asli dari Pinterest
- **`get_pending_snippet_keywords()`** dan **`update_snippet()`** di `pinscrape/database.py` — fungsi database untuk snippet
- **Go Snippet Exporter** (`go-snippet/`) — exporter Go khusus untuk konten snippet (bukan AI)
  - Membaca dari kolom `snippet` (JSON) dan `images`
  - Title = `Ucwords(keyword)`
  - Layout artikel: paragraf → image → H2 (related keyword) → paragraf → image → H2 → dst
  - Related keyword diambil dari index 1+ (bukan pertama), di-random, di-Ucwords
  - Reuse template yang sama dengan Go exporter utama
  - Binary: `go-snippet/snippet-exporter.exe`
- **`EXPORT SNIPPET.bat`** — bat file standalone untuk snippet exporter
- **CLI commands baru** di `scripts/cli.py`:
  - `scrape-description` — scrape descriptions + related keywords
  - `workflow-snippet` — scrape images + scrape descriptions
  - `snippet-export` — export snippet articles + CREATE.csv
  - `snippet-deploy` — export snippet + deploy + submit
  - `full-auto-snippet` — convert + scrape + scrape-description + export + deploy + submit
- **Config baru** di `config.json`:
  - `min_snippet` (default: 3) — minimal deskripsi yang harus ditemukan per keyword
  - `snippet_max_page_size` (default: 50) — max page_size saat progressive scroll
  - `max_related_kw` (default: 10) — max related keywords per keyword
  - `suggest_hl` (default: "en") — language code untuk Google Suggest
  - `suggest_gl` (default: "US") — geo location untuk Google Suggest
- **`PinResult.description`** field di `pinscrape/models.py` — extract description dari Pinterest API response
- **`search_descriptions()`** method di `pinscrape/v2.py` — fetch descriptions tanpa images
- **Snippet column** di `check_db.py` — tampilkan statistik kolom snippet

### Changed
- **`run.bat`** — reorganize menu structure:
  - 1-5: Data operations (Convert, Scrape Images, AI Titles, AI Articles, Scrape Snippet)
  - 6-7: Delete operations
  - 8-9: Workflow (AI, Snippet)
  - 10-11: Deploy & Submit
  - 12-14: AI Export section
  - 15-17: Snippet Export section
  - 18: Exit
- **`Pinterest.search()`** di `pinscrape/v2.py` — output sekarang juga menyertakan `description` field
- **Snippet JSON format** — `ensure_ascii=False` agar karakter Unicode (emoji, aksen) tersimpan asli

## [5.6.0] - 2026-08-10

### Added
- **Status system (0/1/2)** — setiap post di database memiliki status berdasarkan kelengkapan data:
  - Status 0: `images`, `ai_title`, `ai_content` semua NULL/empty
  - Status 1: `images` ada value, `ai_title` dan `ai_content` NULL
  - Status 2: `images`, `ai_title`, `ai_content` semua ada value
- **`update_all_status()`** di `pinscrape/database.py` — fungsi untuk cek dan update status seluruh row di database sesuai kriteria status 0/1/2
- **Delete AI Title Only** (`scripts/delete_ai_title.py`) — script untuk menghapus hanya `ai_title` (tidak menghapus `ai_content`), lalu auto-update status
  - CLI command: `python scripts/cli.py delete-ai-title`
  - Menu 12 di `run.bat`
- **FULL AUTO Pipeline** — workflow lengkap dari awal hingga akhir dalam satu command:
  - Scrape → AI Title → AI Article → Update Status → Generate CREATE.csv → Export (Go) → Deploy (GCS) → Submit (IndexNow)
  - Semua parameter (split_size, template, workers) diambil dari `config.json` secara otomatis
  - CLI command: `python scripts/cli.py full-auto`
  - Menu 13 di `run.bat`

### Changed
- **AI generation filter** — `ai_generator.py` sekarang hanya memproses row yang memiliki `images` (status 1), bukan semua row yang `ai_title`/`ai_content` NULL
- **`run_workflow()`** — hapus konfirmasi "Y/n", tambah step update status di akhir workflow
- **`run_full_deploy()`** — restore input interaktif untuk split size dan template dengan list template dari Go exporter (bisa pilih berdasarkan angka atau nama)
- **`run_full_auto()`** — gunakan Go exporter (bukan PHP) untuk export
- **Hapus konfirmasi** pada `delete-ai` dan workflow 5
- **Hapus `sys.exit()`** dari `cli.py` — diganti `return` agar batch file tidak terminate saat Python exit
- **`run.bat`** — tambah `call` prefix pada semua perintah `python` untuk memastikan kontrol kembali ke batch file
- **`run.bat`** — `pause` + `goto :menu` langsung di handler untuk menu 5, 9, 13 (tidak bergantung ke `:after_run`)
- **`delete_ai_content.py`** — hapus konfirmasi "y/N" saat dijalankan standalone

### Removed
- **PHP Exporter** — hapus seluruh folder `php/` (`exporter.php`, `worker.php`, `Template.php`)
- **`run_export()`** dan `get_available_templates()` dari `cli.py` — tidak lagi digunakan
- **`export` command** dari `cli.py` — export hanya via Go exporter
- **Menu Export (PHP)** dari `run.bat` — menu direnumber 1-14

### Fixed
- **Batch file force close** — `python scripts\cli.py workflow` (dan workflow lainnya) sekarang tidak menyebabkan batch file close langsung setelah selesai

## [5.5.0] - 2026-07-14

### Added
- **Anti-detection: TLS fingerprint impersonation** — ganti `requests` dengan `curl_cffi` untuk menghindari deteksi TLS fingerprint oleh Cloudflare/Akamai
  - 6 profil TLS rotation: `chrome`, `chrome110`, `chrome116`, `chrome120`, `safari15_5`, `edge99`
  - Setiap session mendapat profil TLS acak yang meniru browser nyata di level handshake
- **User-Agent rotation** — 12 profil browser realistis (Chrome, Edge, Firefox, Safari, Opera, Vivaldi di Windows/macOS/Linux)
  - Setiap request mendapat UA + `Sec-Ch-Ua` + `Sec-Ch-Ua-Platform` yang konsisten
  - Mendukung fixed UA mode (backward compatible) jika `user_agent` di-pass ke constructor
- **Exponential backoff untuk 429/403** — `_request_with_backoff()` dengan retry otomatis
  - Backoff formula: `2^attempt + random(1-5)` detik
  - Session di-reset (fresh cookies + TLS profile baru) setiap retry
  - Configurable `max_retries` (default: 3)
- **`reset_session()` method** — tutup session lama, buat baru dengan TLS profile berbeda
- **Fresh epoch per-request** — timestamp `time_epoch` di-generate ulang setiap request, bukan reuse nilai lama

### Changed
- `pinscrape/v2.py` — migrasi dari `requests.Session` ke `curl_cffi.requests.Session` dengan `impersonate`
- `pinscrape/v2.py` — sleep pattern berubah dari fixed delay ke **jitter** `random.uniform(min, max)`
  - Default: `(1.5, 4.0)` detik (configurable via `sleep_time` tuple atau float)
  - Mendukung backward compatibility: float → tambah jitter 0-1.5s, tuple → `uniform(min, max)`
- `scripts/scrape.py` — outer retry loop sekarang pakai jitter `random.uniform(3, 8)` + `reset_session()` antar attempt
- `scripts/scrape.py` — default `sleep_time` dari `0.5` ke `(1.5, 4.0)`
- `requirements.txt` — `requests~=2.32.4` diganti `curl_cffi>=0.15.0`

### Removed
- **`requests` library** — tidak lagi digunakan, digantikan `curl_cffi`
- **`urllib3.util.retry.Retry`** — retry logic dipindah ke `_request_with_backoff()` manual

### Security
- TLS fingerprint sekarang meniru browser Chrome/Firefox/Safari asli, bukan Python urllib3
- Setiap request terlihat seperti browser berbeda di level TLS + HTTP headers + timing

## [5.4.0] - 2026-07-07

### Added
- **Go HTML Exporter** (`go/`) — alternative exporter menggunakan Go yang menghasilkan output HTML identik dengan PHP exporter
  - Goroutines + channels untuk parallel export (lebih cepat, lebih efisien RAM)
  - `bufio.Writer` (64KB buffer) untuk file I/O
  - Pure Go SQLite (`modernc.org/sqlite`) — tanpa CGO/GCC dependency
  - Single static binary (~6.5MB), bisa dijalankan tanpa runtime dependency
  - `--interactive` mode dengan prompt split size, template list, workers
  - `--list-templates` flag untuk listing template dari CLI
  - `--split-size`, `--template`, `--workers`, `--create-csv` CLI flags
  - Auto-detect project root dari lokasi executable atau working directory
  - Path resolution yang konsisten dari project root
- **24 Go templates** (`templates/*.gohtml`) — konversi dari PHP templates, output HTML identik
  - Semua template PHP yang ada tidak diubah atau dihapus
  - `renderCompleteArticle` function di Go mereplikasi logika `render_complete_article` PHP
  - `htmlEscape`, `safe`, `truncateImages`, `truncateArticles` template functions
- **`go/template_test.go`** — unit tests untuk template parsing dan rendering
- **CLI menu additions** (`run.bat`):
  - Menu 7: Export (Go - Fast) dengan interactive mode
  - Menu 10: Full Deploy Pipeline (Go) — Generate CREATE.csv + Export + Deploy + Submit
  - `full-deploy-generate` command di `scripts/cli.py` untuk generate CREATE.csv dengan split_size arg
  - `--auto` flag support untuk `deploy` dan `submit` commands di `scripts/cli.py`

### Changed
- `run.bat` — restructure dari `if-else if` parenthesized blocks ke `goto :label` pattern untuk menghindari force close
- `run.bat` — gunakan `enabledelayedexpansion` dengan `!variable!` syntax konsisten
- `scripts/cli.py` — tambah `full-deploy-generate`, `deploy --auto`, `submit --auto` commands

### Fixed
- **Nil pointer pada `.MainImage`** — template crimson.gohtml, dark-stone.gohtml, dan template lain yang akses `.MainImage.ImageURL` crash saat images ada tapi `MainImage` tidak di-set
- **Goroutine deadlock** di parallel export — `sitemapChan` dan `errChan` sekarang di-drain concurrently via goroutine terpisah
- **Schema JSON double-encoded** — ganti `html/template` ke `text/template` karena `html/template` melakukan context-aware escaping di dalam `<script>` tag

### Performance
- Go exporter dengan 4 workers: estimasi **2-3x lebih cepat** dari PHP untuk database besar
- Memory usage lebih rendah: Go GC vs PHP `2048M` memory limit
- Channel-based streaming: artikel di-process tanpa load semua ke memory sekaligus

## [5.3.1] - 2026-07-04

### Fixed
- **Worker OOM crash** — worker process tidak inherit `memory_limit` dari parent process. Tambah `ini_set('memory_limit', '2048M')` dan `set_time_limit(0)` di `php/worker.php`
- **Windows `proc_open()` compatibility** — gunakan array command format (PHP 7.4+) untuk menghindari masalah shell quoting, backslash escaping, dan path separator di Windows
- **`getopt()` unreliable untuk long options dengan hyphen** di Windows PHP 7.4 — ganti dengan manual `$argv` parsing di `php/worker.php`
- **Path separator** — normalisasi semua path ke `DIRECTORY_SEPARATOR` di `php/worker.php` dan `php/exporter.php`
- **Debug display** — perbaiki tampilan cmd di error output agar menampilkan command per-worker, bukan command terakhir dari loop

## [5.3.0] - 2026-07-03

### Added
- **Multi-process parallel export** via `proc_open()` — export kini dapat memanfaatkan multi-core CPU untuk rendering paralel
  - Konfigurasi `worker_count` di `config.json` (default: 4, recommended 4-6)
  - CLI override `--workers=N` pada `php exporter.php`
  - Fallback otomatis ke sequential jika `worker_count=1` atau artikel terlalu sedikit
- `php/worker.php` — standalone worker script yang menerima batch JSON, merender artikel, dan menulis `.html` + result JSON
- Pre-compute `related_articles` dan `first_articles` di main process sebelum distribusi ke workers

### Changed
- `php/exporter.php` — refactor `run()` menjadi dispatch ke `runSequential()` atau `runParallel()`
- `php/exporter.php` — tambah `runParallel()` dengan proc_open multi-process architecture
- `php/exporter.php` — tambah `preComputeRelated()` dan `preComputeFirstArticles()` untuk metadata pre-computation
- `php/exporter.php` — tambah `--workers` CLI option
- `config.json` — tambah field `worker_count`

### Performance
- Dengan 4 workers di 8-core: estimasi **3-4x lebih cepat** untuk database besar
- Zero overhead untuk `worker_count=1` (menggunakan sequential path)

## [5.2.1] - 2026-07-03

### Fixed
- **Out of Memory saat export database besar** — optimasi memory pada `php/exporter.php` dan `php/template.php` untuk mengatasi crash OOM pada database berukuran 2.5GB+

### Changed
- `php/exporter.php` — naikkan `memory_limit` dari `512M` menjadi `2048M`
- `php/exporter.php` — filter artikel kosong dipindahkan dari PHP ke SQL query di `streamArticles()` menghilangkan `json_decode` overhead di generator
- `php/exporter.php` — tambah `->finalize()` dan `->close()` eksplisit per batch di `streamArticles()` untuk me-release SQLite resources
- `php/exporter.php` — tambah `unset()` untuk intermediate data setelah `file_put_contents` di `processArticle()` agar data besar di-free lebih awal
- `php/exporter.php` — tambah `gc_collect_cycles()` setiap 100 artikel selama proses chunk
- `php/template.php` — hapus `array_merge()` dan recursive `render()` pada layout rendering. Layout kini di-include langsung karena semua variabel sudah tersedia di scope method setelah `extract()`
- `php/template.php` — tambah `unset()` setelah `extract()` untuk free array container

### Output Compatibility
- Semua perubahan **tidak mengubah output HTML** — file `.html`, `sitemap.xml`, `index.html`, dan `robots.txt` yang dihasilkan identik dengan versi sebelumnya

## [5.2.0] - 2026-07-02

### Added
- **Fallback models** untuk AI title dan article generator
  - Konfigurasi `fallback_models` di `config.json` — daftar model cadangan yang dicoba berurutan saat model utama gagal
  - Mendukung format string sederhana (`google/gemma-2-9b-it`) atau objek dengan `base_url`/`api_key` terpisah per model
  - Fungsi `build_model_chain()` di `scripts/ai_generator.py` untuk membangun rantai model dari config
- **Retry logic** per model sebelum fallback ke model berikutnya
  - Konfigurasi `max_retries` (default: 2) — jumlah percobaan ulang per model
  - Konfigurasi `retry_delay` (default: 5) — detik delay antar retry dengan pola eksponensial (`delay x attempt`)
- **Error classification** yang membedakan:
  - Rate limit (429) -> retry + fallback
  - Server error (500/502/503/504) -> retry + fallback
  - Timeout -> retry + fallback
  - Error lain (auth, invalid request) -> langsung skip ke model berikutnya tanpa retry
- **Startup info** — mencetak model chain dan pengaturan retry saat AI generation dimulai

### Changed
- `config.json` — section `ai` ditambah field `fallback_models`, `max_retries`, `retry_delay`
- `scripts/ai_generator.py` — refaktor `generate_content()` dan `process_db_ai()` untuk mendukung model chain dan retry logic
