News:

SMF - Just Installed!

Main Menu

Recent posts

#11
Announcements & Releases / 1.1.44
Last post by hitsumo - Sep 30, 2026, 12:18 PM
# Anime Tracker 1.1.44

**Release date:** 2026-09-19

One piece of work: **the "Recently Updated" page is split into two tabs** —
*Episode Updates* and *Content Updates*. Which one it opens in is chosen in
List Settings; the default is **episodes**. There is one schema change
(`animes.episodes_updated_at`), local to the installation; nothing to do on
the central catalog.

## 1. Two tabs

### Why

The page listed "the five anime most recently added to or edited in the
catalog" by `animes.updated_at`. But `updated_at` is MySQL's automatic
stamp: **every** write that touches the row refreshes it. When the daily
broadcast sync counted one more episode, the anime jumped to the top of
the list — not because its content changed, but because one more episode
had aired. Two different questions were fighting over one list:

- **"What changed in the catalog?"** — a new entry, a corrected synopsis,
  an added date.
- **"Which show got a new episode?"** — the question people ask daily.

Mid-season the second buried the first completely: the page showed the
five series the sync had counted that day, and an entry edited a week
earlier was nowhere to be seen.

### What it does

| Tab | Lists | Timestamp |
| --- | --- | --- |
| **Episode Updates** | The five anime whose aired episode count changed most recently | `episodes_updated_at` (new) |
| **Content Updates** | The five anime most recently added to, or edited in, the catalog | `updated_at` |

On the episode tab every card leads with the number that changed:
**"Latest episode: 12"**. The tabs are plain links
(`recent.php?tab=episodes` / `?tab=content`); the choice in the address
applies to that view only and writes nothing persistent.

**Who writes the episode count:** the daily broadcast sync
(AnimeSchedule), the edit form and a catalog import. All three stamp the
new column **only when the number really changes**; a sync that brings
back the same number does not move the list.

**The content tab is now really about content:** episode-only writers
(the broadcast sync, the next-episode date computed on page load, the
episode step of a catalog import, the promote / demote flags for the
catalog) no longer touch `updated_at`. The edit form is a content edit
and stamps it as before; if you change only the episode count in the
form, the anime shows up on both tabs — which is correct.

### Default tab setting

List Settings → General Settings → **"Recently Updated Tab"**: episodes /
content. A personal preference that affects only you; default
**episodes**. The tabs on the page switch temporarily without overriding
it. Same pattern as the series chronology view setting (1.1.23).

### The first day after upgrading

The new column is empty on existing rows; the episode tab fills after the
first broadcast sync (once a day, on opening the main page). Until then the
tab says "No episode update recorded yet". Seeding was left out **on
purpose**: copying `updated_at` would have assumed "the last touch was an
episode increment" — and not knowing that was the whole problem.

### Side effect: sitemap `lastmod`

Since `updated_at` is now content time only, the sitemap's `lastmod` takes
the later of the two stamps, so a detail page whose episode count changed
still looks "changed" to search engines. The IndexNow announcement keeps
covering episode changes too.

## 2. Help

The "Recently Updated" section of the list-page help was rewritten to
describe the two tabs, who writes to each, and the setting; the "not to be
confused with Recently Watched" box stays as it was.

## Files

**New:**

```
files/migration/1.1.44/upgrade.sql   animes.episodes_updated_at (ADD COLUMN, re-runnable)
files/set_recent_tab_pref.php        default-tab endpoint (POST + CSRF)
```

**Changed:**

```
files/recent.php                          two tabs, per-tab query, "Latest episode" badge, empty-state text
files/list_settings.php                   "Recently Updated Tab" setting
files/functions/user_anime_helpers.php    recent_tabs() / recent_default_tab()
files/functions/animeschedule_helpers.php airedEpisodesUpdateSql(): all five episode writes from one SQL pattern
files/functions/anime_helpers.php         next-episode date writes leave updated_at alone
files/functions/seo_helpers.php           sitemap lastmod = later of the two stamps
files/edit_anime.php                      episodes_updated_at when the episode count changes
files/catalog_import.php                  episode count in its own step (own stamp), content UPDATE pure content
files/admin/admin_pending.php             promote / demote leave updated_at alone
files/robots.php                          set_recent_tab_pref.php on the disallow list
files/schema.sql                          episodes_updated_at column (fresh install)
files/lang/tr.php                         +9 keys, 1 changed (help text)
files/lang/en.php                         same
files/version.txt
```

Language file parity: 1118 = 1118. No new CSS/JS (the tab style lives in
the page).

## Deployment note

- **Nothing to do on the central catalog.** The new column is local to the
  installation; the push and catalog endpoints list their columns
  explicitly, so it never enters the wire.
- The migration runs on the first page load; a single
  `ALTER TABLE ... ADD COLUMN`, ignored if the column already exists.
  Tested as the 1.1.26 → 1.1.44 chain on an 8,113-row copy and as
  `schema.sql` + the full chain on an empty database; both clean.
- `functions/animeschedule_helpers.php`, `anime_helpers.php`,
  `user_anime_helpers.php` and `seo_helpers.php` must ship **together**
  (the whole-functions-folder rule): `recent.php` and `list_settings.php`
  call the new helpers.
- The episode tab is empty until the first sync; that is expected.
- **On the distribution server**, the usual two steps: the published
  `version.txt` must be bumped to 1.1.44 and the
  `updates/1.1.44/anime-tracker-1.1.44.zip` package published.
#12
Duyurular ve Sürümler / 1.1.44
Last post by hitsumo - Sep 30, 2026, 12:17 PM
# Anime Tracker 1.1.44

**Yayın tarihi:** 2026-09-19

Tek iş: **"Son Güncellenenler" sayfası iki sekmeye ayrıldı** — *Bölüm
Güncellenenler* ve *İçerik Güncellenenler*. Hangisiyle açılacağı Liste
Ayarları'ndan seçilir; ön tanımlı **bölüm**. Bir şema değişikliği var
(`animes.episodes_updated_at`), yalnızca kurulum-yerel; merkez katalogda
yapılacak bir şey yok.

## 1. İki sekme

### Neden

Sayfa "katalogda en son eklenen ya da düzenlenen beş anime"yi
`animes.updated_at`'e göre listeliyordu. Ama `updated_at` MySQL'in
otomatik damgası: satıra dokunan **her** yazma onu tazeler. Günlük yayın
senkronu bir bölüm daha saydığında anime listenin tepesine çıkıyordu —
içeriği değiştiği için değil, bir bölümü daha yayınlandığı için. İki
farklı soru tek listede birbirini eziyordu:

- **"Katalogda ne değişti?"** — yeni kayıt, düzeltilen konu, eklenen tarih.
- **"Hangi animenin yeni bölümü geldi?"** — günün asıl sorusu.

Sezon ortasında ikinci soru birinciyi tamamen gömüyordu: sayfa her gün
senkronun saydığı beş diziyi gösteriyor, bir hafta önce düzenlenen kayıt
görünmüyordu.

### Ne yapar

| Sekme | Ne listeler | Zaman damgası |
| --- | --- | --- |
| **Bölüm Güncellenenler** | Yayınlanan bölüm sayısı en son değişen beş anime | `episodes_updated_at` (yeni) |
| **İçerik Güncellenenler** | Kataloğa en son eklenen ya da bilgisi düzenlenen beş anime | `updated_at` |

Bölüm sekmesindeki her kartta değişen sayı öne çıkar: **"Son bölüm: 12"**.
Sekmeler düz bağlantıdır (`recent.php?tab=episodes` / `?tab=content`);
adresteki seçim o görünüm için geçerlidir, kalıcı bir şey yazmaz.

**Bölüm sayısını kim yazar:** günlük yayın senkronu (AnimeSchedule),
düzenleme formu ve katalog içe aktarımı. Üçü de yeni damgayı **yalnızca
sayı gerçekten değişince** atar; senkron aynı sayıyı yeniden getirirse
liste oynamaz.

**İçerik sekmesi artık gerçekten içerik:** yalnız-bölüm yazan yollar
(yayın senkronu, sayfa açılışında hesaplanan sonraki bölüm tarihi, katalog
içe aktarımının bölüm adımı, kataloğa alma / çıkarma işaretleri)
`updated_at`'e dokunmaz. Düzenleme formu bir içerik düzenlemesidir, eskisi
gibi damgalar; formda yalnız bölüm sayısını değiştirirseniz anime iki
sekmede de görünür — doğru olan da bu.

### Varsayılan sekme ayarı

Liste Ayarları → Genel Ayarlar → **"Son Güncellenenler Sekmesi"**: bölüm /
içerik. Kişisel tercih, yalnız sizi etkiler; ön tanımlı **bölüm**. Sayfadaki
sekmeler bu varsayılanı ezmeden geçici olarak değiştirir. Seri
kronolojisi görünümü ayarıyla (1.1.23) aynı kalıp.

### Güncelleme sonrası ilk gün

Yeni kolon var olan kayıtlarda boştur; bölüm sekmesi ilk yayın senkronundan
sonra dolar (senkron günde bir, ana sayfa açılışında). O ana kadar sekme
"Henüz kayıtlı bölüm güncellemesi yok" der. Tohumlama **bilerek** yapılmadı:
`updated_at`'i kopyalamak "son dokunuş bölüm artışıydı" varsayımı olurdu —
sorunun kendisi tam da bunun bilinmemesiydi.

### Yan etki: sitemap `lastmod`

`updated_at` artık yalnız içerik zamanı olduğundan sitemap'in `lastmod`'u
iki damganın büyüğünü alır; bölüm sayısı değişen bir detay sayfası
arama motoruna yine "değişti" görünür. IndexNow duyurusu da bölüm
değişikliğini kapsamaya devam eder.

## 2. Yardım

Liste sayfası yardımındaki "Son Güncellenenler" bölümü iki sekmeyi, kimin
buraya yazdığını ve ayarı anlatacak şekilde yeniden yazıldı; "Son
İzlenenler ile karıştırmayın" kutusu olduğu gibi duruyor.

## Dosyalar

**Yeni:**

```
files/migration/1.1.44/upgrade.sql   animes.episodes_updated_at (ADD COLUMN, yeniden çalıştırılabilir)
files/set_recent_tab_pref.php        varsayılan sekme ucu (POST + CSRF)
```

**Değişen:**

```
files/recent.php                          iki sekme, sekmeye göre sorgu, "Son bölüm" rozeti, boş durum metni
files/list_settings.php                   "Son Güncellenenler Sekmesi" ayarı
files/functions/user_anime_helpers.php    recent_tabs() / recent_default_tab()
files/functions/animeschedule_helpers.php airedEpisodesUpdateSql(): beş bölüm yazımı tek SQL kalıbından
files/functions/anime_helpers.php         sonraki bölüm tarihi yazımları updated_at'e dokunmaz
files/functions/seo_helpers.php           sitemap lastmod = iki damganın büyüğü
files/edit_anime.php                      bölüm sayısı değişince episodes_updated_at
files/catalog_import.php                  bölüm sayısı ayrı adımda (kendi damgası), içerik UPDATE'i saf içerik
files/admin/admin_pending.php             kataloğa alma / çıkarma updated_at'e dokunmaz
files/robots.php                          set_recent_tab_pref.php disallow listesinde
files/schema.sql                          episodes_updated_at kolonu (taze kurulum)
files/lang/tr.php                         +9 anahtar, 1 değişen (yardım metni)
files/lang/en.php                         aynı
files/version.txt
```

Dil dosyası paritesi: 1118 = 1118. Yeni CSS/JS yok (sekme stili sayfanın
içinde).

## Dağıtım notu

- **Merkez katalogda iş yok.** Yeni kolon kurulum-yereldir; push ve
  katalog uçları kolon listesini açık yazar, yeni kolon tele girmez.
- Migration ilk sayfa açılışında koşar; tek `ALTER TABLE ... ADD COLUMN`,
  kolon zaten varsa yok sayılır. 8.113 satırlık kopyada 1.1.26 → 1.1.44
  zinciri ve boş veritabanında `schema.sql` + tam zincir denendi; ikisi de
  temiz.
- `functions/animeschedule_helpers.php`, `anime_helpers.php`,
  `user_anime_helpers.php` ve `seo_helpers.php` **birlikte** gitmeli
  (functions/ klasörünün tamamı kuralı): `recent.php` ve
  `list_settings.php` yeni yardımcıları çağırır.
- Bölüm sekmesi ilk senkrona kadar boştur; bu beklenen davranış.
- **Dağıtım sunucusunda** her zamanki iki adım: yayımlanan `version.txt`
  1.1.44'e çekilmeli ve `updates/1.1.44/anime-tracker-1.1.44.zip` paketi
  yayımlanmalı.
#13
Duyurular ve Sürümler / 1.1.43
Last post by hitsumo - Sep 30, 2026, 12:17 PM
# Anime Tracker 1.1.43

**Yayın tarihi:** 2026-09-19

Şema değişikliği yok. Tek iş: **kurulum sayacı** — projenin, dünyada kaç
Anime Tracker kurulumu olduğunu kabaca görebilmesi. Kurulum başına **tek
bir** istek; kimlik yok, kişisel veri yok, tek satırla kapatılabilir.

## 1. Kurulum sayacı

### Neden

Proje açık kaynak ve self-host. Bu zamana kadar bir kurulumun merkezle
konuşmak zorunda olduğu **hiçbir otomatik nokta yoktu**: kişisel kurulum
merkez kataloğu çekmeyebilir, güncelleme kontrolü yalnızca düğmesine
basılınca çalışır. Yani GitHub'dan indirilip kurulan bir kopyanın var
olduğu hiçbir zaman bilinmiyordu. "Ne kadar büyüdük?" sorusunun cevabı
yoktu.

### Ne yapar

Kurulum, **yalnızca bir kez** — ana sayfanın ilk açılışından sonra —
projenin sayacına tek bir küçük istek gönderir. İçinde üç şey vardır:

| Alan | Ne  | Nereden |
| --- | --- | --- |
| `id` | Rastgele 32 karakterlik kurulum kimliği | Gönderim anında `random_bytes` ile üretilir, `settings.install_id`'ye yazılır. Adresten, veritabanından, kişiden türetilmez. |
| `v` | Kurulu sürüm | `version.txt` |
| `m` | `single` (kişisel) / `multi` (çok kullanıcılı) | `MULTI_USER_MODE` |

**Gönderilmeyenler:** site adresi, üye sayısı, anime, izleme verisi,
notlar, sunucu adı. Sayaç **IP adresini saklamaz**. Kimlik yalnızca bir
tekrarın (yeniden deneme, geri yüklenen yedek) yeni kurulum sayılmaması
içindir; `settings` tablosundaki `install_id` ve `install_ping_done`
satırlarını silen kurulum sayaçta yeni bir kurulum olur.

**Neden günlük değil, bir kez:** ilk taslak günlük "yaşıyorum" pingiydi
(toplamın yanında "son 30 günde aktif" sayısını da verirdi). Karar: en
küçük iz — kurulumda bir kez, "gerekirse genişletiriz". Kapı öyle kuruldu
ki günlüğe dönmek tek satırlık iş.

### Nasıl çalışır

- **Sayfa beklemez.** `index.php` yalnızca "ping başardı mı, bugün denendi
  mi?" diye bakar (`settings.install_ping_done` / `last_install_ping`).
  Gerekiyorsa sayfanın sonuna tek satırlık bir `fetch('install_ping.php')`
  basar; dışarı giden isteği o uç yapar, 3 saniye zaman aşımıyla. Sayaç
  sunucusu yavaş ya da kapalı olsa bile kullanıcının baktığı sayfa
  etkilenmez.
- **Başarana kadar günde en fazla bir deneme, sonra bir daha asla.** Uç,
  göndermeden **önce** günü işaretler; HTTP 200 gelince `install_ping_done`
  yazılır ve kapı kalıcı kapanır. Kurulum anında sayaç sunucusu kapalıysa
  o kurulum kaybolmaz — ertesi gün sayılır; arada hiçbir sayfa yüklemesi
  dışarı istek atmaz.
- **POST + CSRF.** Öteki durum değiştiren uçlarla aynı kalıp. İkinci istek
  (iki sekme, sayfanın JS'ini çalıştıran bir tarayıcı botu) 204 döner, hiçbir
  şey yapmaz.
- **Kapatma:** `config.php`'de `define('INSTALL_PING', false);`. Sabit yoksa
  açık sayılır (eski `config.php` dosyaları için `MULTI_USER_MODE` ile aynı
  gelenek). Yeni kurulumlarda `setup.php` ve Docker giriş betiği satırı
  `true` olarak yazar ki anahtar gözle görülsün.

### Sayılar nerede görünür

- **Yönetici paneli → "Kurulum Sayacı" kartı:** toplam, kişisel / çok
  kullanıcılı dağılımı, kurulumların hangi sürümle başladığı.
  Kart sayıları tarayıcıdan çeker (sunucu tarafında çekmek admin sayfasını
  uzak sunucuya bağımlı kılardı). Bu kurulumda sayaç kapalıysa kart bunu
  ayrıca söyler.
- **Herkese açık:** `ping.php?stats=1` yalnızca toplamları döner, kimlik
  değil. Karar: sayıda korunacak bir şey yok; ileride Hakkında sayfasında
  "N kurulum" rozeti olabilir.

### Şeffaflık

Açık kaynak bir projede "sessiz" bir sayaç olmaz. Ne gittiği dört yerde
yazılı: `README.md` (TR+EN, yeni "Kurulum sayacı" bölümü), yardım
(Senkronizasyon ve Güncelleme → yeni **Kurulum Sayacı** bölümü:
ne gönderilir, ne gönderilmez, nasıl kapatılır, neden var),
`config_example.php` ve yardımcı dosyanın başındaki açıklama. Sayı kabaca
doğrudur, kanıt değildir: sayacı kapatan görünmez, kimliğini sıfırlayan iki
kez sayılır — bu da yardımda yazıyor.

## 2. Detay sayfasında "Düzenle" yalnızca düzenleyebilene

Detay sayfası "Düzenle" düğmesini herkese basıyordu; anonim ziyaretçi tıklayınca
`edit_anime.php` giriş sayfasına yönlendiriyordu (uç zaten moderatör kapılı).
Liste sayfası aynı düğmeyi öteden beri yalnız moderatöre gösteriyordu, detay
sayfası unutulmuştu. Zararı arama motorunda görüldü: Search Console 1.034
"yönlendirmeli sayfa" biriktirmişti, hepsi `edit_anime.php?id=...`. Düğme artık
detayda da yalnız moderatör ve üstüne görünür. Kişisel kurulumda sahip her
zaman yetkili olduğundan davranış değişmez.

## 3. Seri kronolojisi sayfasının başlığında "İzleme Sırası"

Search Console, "tensei shitara slime izleme sırası" aramasında seri kronolojisi
sayfasını değil filmin detay sayfasını gösteriyordu: sayfanın başlığı "Seri
Kronolojisi" idi, arayan "izleme sırası" yazıyordu. `<title>` ve meta açıklama
artık iki ifadeyi de taşıyor — *"X İzleme Sırası - Seri Kronolojisi"* /
*"X Watch Order - Series Chronology"*; açıklamaya "hangi film hangi bölümden
sonra izlenir" eklendi. Arayüzdeki sayfa adı ve yardım metinleri değişmedi.

## Dosyalar

**Yeni:**

```
files/functions/install_ping_helpers.php   mantık; INSTALL_PING_URL sabiti
files/install_ping.php                      yerel AJAX ucu (POST + CSRF)
files/migration/1.1.43/upgrade.sql          şemasız; sürüm damgası + gerekçe
catalog_server/ping.php                     MERKEZ: kayıt + ?stats=1
```

**Değişen:**

```
files/index.php                    tetikleyici (yalnız ping gerekiyorsa basılır)
files/anime_details.php            "Düzenle" düğmesi yalnız moderatöre (§2)
files/series_timeline.php          <title> ve seo_head başlığı yeni kalıptan (§3)
files/functions.php                yeni yardımcı yükleniyor
files/admin/admin.php              "Kurulum Sayacı" kartı
files/lang/admin_tr.php            +9 anahtar
files/lang/admin_en.php            +9 anahtar
files/help/help_sync.php           yeni "Kurulum Sayacı" bölümü
files/help.php                     içindekiler (+1 satır)
files/lang/tr.php                  +12 anahtar, 1 değişen (seo.series.description_fmt)
files/lang/en.php                  aynı
files/config_example.php           INSTALL_PING açıklaması
files/setup.php, setup_en.php      üretilen config.php'ye INSTALL_PING satırı
files/schema.sql                   settings anahtar listesine install_id / last_install_ping / install_ping_done
files/robots.php                   install_ping.php disallow listesinde
files/version.txt
docker-entrypoint.sh               üretilen config.php'ye INSTALL_PING satırı
catalog_server/catalog_server_README.md   ping.php bölümü + tablo SQL'i
README.md                          "Kurulum sayacı" bölümü (TR+EN)
```

Dil dosyası paritesi: 1109 = 1109, yönetici 312 = 312. Yeni CSS/JS yok.

## Dağıtım notu

- **Merkez katalog sunucusunda iki adım:** (1) `installs` tablosunu oluştur
  (SQL `catalog_server_README.md` ve `migration/1.1.43/upgrade.sql`
  içinde), (2) `catalog_server/ping.php` dosyasını yayımla. Uç,
  `private/admin_push_config.php`'nin veritabanı bilgilerini kullanır
  (yazma yetkili kullanıcı); `private/rate_limit/` klasörünü kendisi açar.
- **Sıra önemli değil.** Uygulama önce giderse ping 404 alır, günlüğe yazar,
  ertesi gün yine dener (başarana kadar günde bir); sayfalar etkilenmez.
  Merkez önce giderse tablo boş bekler.
- Uygulama tarafında migration şema değiştirmez; `install_id` ilk ping'de
  PHP tarafından üretilir. Taze kurulum replay'i etkilenmez.
- `functions/install_ping_helpers.php` ile `functions.php` **birlikte**
  gitmeli: `functions.php` yeni dosyayı `require_once` eder, dosya yoksa
  her sayfa çöker (0.6.7'den beri geçerli "functions/ klasörünün tamamı"
  kuralı).
- Yönetici kartı sayıları `INSTALL_PING_URL?stats=1` adresinden tarayıcıda
  çeker; merkez uç yayımlanana kadar kart "Sayaç sunucusuna ulaşılamadı"
  gösterir, başka bir şey bozulmaz.
- **Dağıtım sunucusunda** her zamanki iki adım: yayımlanan `version.txt`
  1.1.43'e çekilmeli ve `updates/1.1.43/anime-tracker-1.1.43.zip` paketi
  yayımlanmalı.
- Kendi kurulumlarınız (yerel XAMPP dahil) da sayılır. Sayılmasın
  istediğiniz kopyada `INSTALL_PING`'i `false` yapın.
#14
Announcements & Releases / 1.1.43
Last post by hitsumo - Sep 30, 2026, 12:16 PM
# Anime Tracker 1.1.43

**Release date:** 2026-09-19

No schema change. One piece of work: an **install counter** — a way for the
project to see, roughly, how many Anime Tracker installations exist. **One**
request per installation; no identity, no personal data, one line to switch
off.

## 1. Install counter

### Why

The project is open source and self-hostable. Until now there was **no
automatic point** at which an install had to talk to the project: a
personal install may never pull the central catalog, and the update check
only runs when its button is pressed. A copy downloaded from GitHub and
installed somewhere was simply never known to exist. "How much have we
grown?" had no answer.

### What it does

The install sends one small request to the project counter **once only** —
after the first time the home page is opened. It carries three things:

| Field | What | From |
| --- | --- | --- |
| `id` | Random 32-character install id | Generated with `random_bytes` at send time, stored in `settings.install_id`. Not derived from the address, the database or the person. |
| `v` | Installed version | `version.txt` |
| `m` | `single` (personal) / `multi` (multi-user) | `MULTI_USER_MODE` |

**Not sent:** site address, member count, anime, watch data, notes, server
name. The counter **does not store the IP address**. The id exists only so
that a repeat (a retry, a restored backup) is not counted as a new install;
an install that deletes the `install_id` and `install_ping_done` rows from
`settings` becomes a new install on the counter.

**Why once, not daily:** the first draft was a daily heartbeat (it would
also have given "active in the last 30 days" next to the total). Decision:
the smallest possible footprint — once per install, "we extend it if we
ever need to". The gate is built so that going daily is a one-line change.

### How it works

- **The page does not wait.** `index.php` only checks "has the ping
  succeeded, was it attempted today?" (`settings.install_ping_done` /
  `last_install_ping`). If due, it prints a one-line
  `fetch('install_ping.php')` at the end of the page; that endpoint makes the
  outbound request with a 3-second timeout. A slow or dead counter server
  never affects the page the user is looking at.
- **At most one attempt a day until the first success, then never again.**
  The endpoint marks the day **before** sending; on HTTP 200 it writes
  `install_ping_done` and the gate closes for good. An install set up while
  the counter is down is not lost — it is counted the next day, and no page
  load in between fires an outbound request.
- **POST + CSRF.** The same pattern as every other state-changing endpoint.
  A second request (two tabs, a crawler running the page's JS) returns 204
  and does nothing.
- **Switching off:** `define('INSTALL_PING', false);` in `config.php`. A
  missing constant means on (the `MULTI_USER_MODE` convention for older
  `config.php` files). New installs get the line written as `true` by
  `setup.php` and the Docker entrypoint so the switch is visible.

### Where the numbers show

- **Admin dashboard → "Install Counter" card:** total, personal /
  multi-user split, which version each install started with. The
  card fetches from the browser (fetching server-side would make the admin
  page depend on a remote host). If the counter is off on this install, the
  card says so.
- **Public:** `ping.php?stats=1` returns totals only, never ids. Decision:
  there is nothing in the number to protect; an "N installs" badge on the
  About page may follow.

### Transparency

An open-source project does not get a "silent" counter. What goes out is
written in four places: `README.md` (TR+EN, new "Install counter" section),
the help (Sync and Updates → new **Install Counter** section: what is sent,
what is not, how to switch off, why it exists), `config_example.php`, and
the helper file's header comment. The number is roughly right, not proof:
installs that switch it off are invisible, one that resets its id is counted
twice — the help says so too.

## 2. "Edit" on the detail page only for those who can edit

The detail page showed the "Edit" button to everyone; an anonymous visitor
clicking it was redirected by `edit_anime.php` to the login page (the endpoint
has always been moderator-gated). The list page had shown the same button to
moderators only for a long time; the detail page had been missed. The damage
showed up in search: Search Console had accumulated 1,034 "page with redirect"
entries, all `edit_anime.php?id=...`. The button is now moderator-and-above on
the detail page too. On a personal install the owner is always allowed, so
nothing changes there.

## 3. "Watch Order" in the series chronology page title

For the search "tensei shitara slime izleme sırası" (watch order), Search
Console showed the movie's detail page rather than the series chronology page:
the page title said "Series Chronology", the searcher typed "watch order". The
`<title>` and meta description now carry both — *"X Watch Order - Series
Chronology"*; the description adds "which movie to watch after which episode".
The page name in the UI and the help texts are unchanged.

## Files

**New:**

```
files/functions/install_ping_helpers.php   logic; INSTALL_PING_URL constant
files/install_ping.php                      local AJAX endpoint (POST + CSRF)
files/migration/1.1.43/upgrade.sql          no schema change; version stamp + rationale
catalog_server/ping.php                     CENTRAL: record + ?stats=1
```

**Changed:**

```
files/index.php                    trigger (printed only when a ping is due)
files/anime_details.php            "Edit" button moderator-only (§2)
files/series_timeline.php          <title> and seo_head title from the new pattern (§3)
files/functions.php                loads the new helper
files/admin/admin.php              "Install Counter" card
files/lang/admin_tr.php            +9 keys
files/lang/admin_en.php            +9 keys
files/help/help_sync.php           new "Install Counter" section
files/help.php                     table of contents (+1 entry)
files/lang/tr.php                  +12 keys, 1 changed (seo.series.description_fmt)
files/lang/en.php                  same
files/config_example.php           INSTALL_PING documented
files/setup.php, setup_en.php      INSTALL_PING line in the generated config.php
files/schema.sql                   install_id / last_install_ping / install_ping_done in the settings key list
files/robots.php                   install_ping.php in the disallow list
files/version.txt
docker-entrypoint.sh               INSTALL_PING line in the generated config.php
catalog_server/catalog_server_README.md   ping.php section + table SQL
README.md                          "Install counter" section (TR+EN)
```

Language-file parity: 1109 = 1109, admin 312 = 312. No new CSS/JS.

## Deployment note

- **Two steps on the central catalog server:** (1) create the `installs`
  table (SQL in `catalog_server_README.md` and in
  `migration/1.1.43/upgrade.sql`), (2) publish `catalog_server/ping.php`.
  The endpoint uses the database credentials from
  `private/admin_push_config.php` (a user with write access) and creates
  `private/rate_limit/` itself.
- **Order does not matter.** If the application goes first, the ping gets a
  404, logs it and tries again tomorrow (once a day until it succeeds); pages
  are unaffected. If the server goes first, the table waits empty.
- The application-side migration changes no schema; `install_id` is
  generated by PHP on the first ping. The fresh-install replay is unaffected.
- `functions/install_ping_helpers.php` and `functions.php` must go up
  **together**: `functions.php` `require_once`s the new file, and every page
  fatals if it is missing (the "whole functions/ directory" rule since 0.6.7).
- The admin card fetches `INSTALL_PING_URL?stats=1` from the browser; until
  the central endpoint is published the card shows "Could not reach the
  counter server" and nothing else breaks.
- **On the distribution server**, the usual two steps: the published
  `version.txt` must be moved to 1.1.43 and the
  `updates/1.1.43/anime-tracker-1.1.43.zip` package published.
- Your own installs (local XAMPP included) are counted too. Set
  `INSTALL_PING` to `false` on any copy you do not want counted.
#15
Announcements & Releases / 1.1.42
Last post by hitsumo - Sep 30, 2026, 10:12 AM
# Anime Tracker 1.1.42

**Release date:** 2026-09-16

No schema change. One piece of work: the help did not describe what the
last three releases added — now it does. No behaviour changes; only the
help pages and the language files changed.

## 1. Help coverage, second round

1.1.33 had extended the help with four new groups. The releases after it
added three big features, and the help did not follow:

| Release | Feature | State in the help |
| --- | --- | --- |
| 1.1.38 | Typed relations ("Alternative Version", "Side Story"...) | **Absent** |
| 1.1.38 | The add / edit form was split into five tabs | The word "tab" never appeared |
| 1.1.40 | Order is a relation ("Sequel" / "Prequel"; the "Next Anime" box went away) | One sentence |
| 1.1.41 | Shared MAL / AniDB record (part number) | **Absent** |
| 1.1.15 | How chronology notes are added / edited / deleted | "What" was there, "how" was not |
| 1.1.19 / 1.1.26 | The `[[anime:...]]` link code in a synopsis and the link picker | **Absent** |
| 1.1.35 | The import blacklist | **Absent** |

This release closes those seven gaps.

### Series and Episode Info page

- **The introduction was rewritten:** anime that belong together are
  described on three layers — series name ("which family?"), relations
  ("what is one record to the other?") and chronology notes ("after which
  episode comes which film?").
- **Series Info:** where the series name is entered, what it feeds (the
  target list of the note form, the "Air Date" tab of the Series
  Chronology) and that it says nothing about order. The section name was
  corrected: the detail-page section is "Related Anime".
- **Watch Order:** the link is only followed when both ends carry the same
  chain name.
- **Chronology Notes:** what a note is attached to (the series' record; the
  target is a separate record), the three places it shows (list, active
  alert, the "Chronology" page). **New:** *Adding, Editing and Deleting a
  Chronology Note* — who may, when the form appears, what the four fields
  mean, which point in-place editing changes, deletion. **New:** *Are
  "Chronology" and "Series Chronology" the same thing?* — one looks inside
  a single series, the other at the whole franchise.
- **New section: Relations.** The six types and their definitions
  (Sequel / Prequel as the only ordering type; Alternative Version;
  Alternative Setting; Side Story / Parent Story; Summary / Full Story;
  Other). Direction: the form's single question ("the anime you picked is
  the ___ of the anime you are editing"), why the three directional types
  appear twice, why the other three have no direction. Rules: at most one
  relation per pair, no self-relation, the chain-name rule, relations
  staying on this installation and going into the backup. *Link or
  relation? — How a chain is built:* three steps and the test ("a link
  means: when you finish this, watch that"), with the Sailor Moon Crystal
  example. Box: where the "Next Anime" box went.
- **Series Chronology:** the chain tab is now described as built from
  "Sequel / Prequel" links and points at the new section.

### Fields and Personal Data page

- **New section: The Add / Edit Form — Tabs.** What each of the five tabs
  holds; that it is one form saved by one click; that a required field
  left empty on another tab makes the form jump there; that the last tab
  is remembered; why the Relations panel exists only on the edit form (it
  links two *existing* records); who sees which form.
- **New section: Shared MAL / AniDB Record.** Why it exists (the source
  and the catalog may disagree on what "one anime" is), how to use it (the
  checkbox, the part number arriving by itself, an unticked duplicate
  being refused, the box deriving from the data when editing and not being
  untickable), where it shows (the "MyAnimeList · 2/3" badge, the "Same
  Source Record" section) and what it affects (the MAL / AniList import
  rule, Auto-fill, the `[[anime:2994/2]]` synopsis link, deletion and the
  blacklist, backup and catalog).
- **New section: Linking Another Anime in a Synopsis.** What the
  `[[anime:2994|Death Note]]` code is, why it is a code and not a plain
  link (the text travels to every installation, a MAL id is the same
  everywhere; a target missing from the catalog falls back to plain text),
  the "Add anime link" button (searches the local catalog, writes the code
  at the cursor, carries the part for a shared record) and the rules when
  typing by hand.
- "Next in series" left the catalog-field list (retired in 1.1.40); the
  MAL / AniDB line links to the shared-record checkbox. New note: the chain
  name, relations and the chronology notes you added yourself are neither
  catalog fields nor personal — they are this installation's curation
  data; sync leaves them alone and they go into the backup.

### Import / Export page

- **New section: Import Blacklist.** Multi-user site only; the problem it
  solves (a deleted anime came back through imports), how it works
  (automatic on deletion, id-only matching, cuts only unmatched records,
  never blocks an anime that is in the catalog, the last-part rule for
  shared records), the admin page, and that it is not part of the backup.

### Table of contents

Five new entries: *The Add / Edit Form — Tabs*, *Linking Another Anime in
a Synopsis*, *Shared MAL / AniDB Record*, *Relations*, *Import
Blacklist*.

## 2. Corrected misinformation

The warning box in the help said **"If you added markers yourself, they
are lost after the next sync."** That was not true: importing from the
catalog only refreshes the notes that came from the catalog; **notes you
added yourself are not deleted**, and List Settings reminds you of them
with the "not in sync with the catalog" warning. The help was describing
an older version of the code. The text was replaced with the correct one.

## Files

**New:**

```
files/migration/1.1.42/upgrade.sql   (no schema change; version stamp + rationale)
```

**Changed:**

```
files/help.php                    table of contents (+5 entries)
files/help/help_series.php        +2 subsections, new "Relations" section
files/help/help_fields.php        new "Tabs", "Synopsis Link" and "Shared Record" sections
files/help/help_transfer.php      new "Blacklist" section
files/lang/tr.php                 47 new + 10 changed keys
files/lang/en.php                 same
files/version.txt
```

Language-file parity: 1098 = 1098. No new CSS/JS.

## Deployment note

- The help pages print keys from the language files. If the pages go up
  and the language files stay old, nothing crashes but key names show
  instead of text. **Upload `lang/tr.php` and `lang/en.php` first or
  together;** the other direction is harmless (extra keys bother nobody).
- The migration changes no schema; it only moves the version stamp and
  runs on its own.
- **Nothing to do on the central catalog server.**
- **On the distribution server**, the usual two steps: set the published
  `version.txt` to 1.1.42 and publish
  `updates/1.1.42/anime-tracker-1.1.42.zip`.
- Self-hosted and online installations see the same text; the role
  sentences describe both modes.
#16
Duyurular ve Sürümler / 1.1.42
Last post by hitsumo - Sep 30, 2026, 10:11 AM
# Anime Tracker 1.1.42

**Yayın tarihi:** 2026-09-16

Şema değişikliği yok. Tek iş: yardım, son üç sürümün eklediği özellikleri
anlatmıyordu — artık anlatıyor. Kod davranışında hiçbir değişiklik yok;
değişen yalnızca yardım sayfaları ve dil dosyaları.

## 1. Yardım kapsamı, ikinci tur

1.1.33 yardımı dört yeni grupla genişletmişti. Sonraki sürümler üç büyük
özellik ekledi ve yardım onları izlemedi:

| Sürüm | Özellik | Yardımdaki durumu |
| --- | --- | --- |
| 1.1.38 | Tipli ilişkiler ("Alternatif Versiyon", "Yan Hikâye"...) | **Hiç yoktu** |
| 1.1.38 | Ekleme / düzenleme formu beş sekmeye bölündü | "Sekme" kelimesi geçmiyordu |
| 1.1.40 | Sıra bir ilişkidir ("Devamı" / "Öncesi"; "Sıradaki Anime" kutusu kalktı) | Tek cümle |
| 1.1.41 | Paylaşımlı MAL / AniDB kaydı (parça numarası) | **Hiç yoktu** |
| 1.1.15 | Kronoloji notlarının nasıl eklendiği / düzenlendiği / silindiği | "Ne olduğu" vardı, "nasıl" yoktu |
| 1.1.19 / 1.1.26 | Konu içinde `[[anime:...]]` bağlantı kodu ve bağlantı seçici | **Hiç yoktu** |
| 1.1.35 | İçe aktarma kara listesi | **Hiç yoktu** |

Bu sürüm bu yedi boşluğu kapatıyor.

### Seriler ve Bölüm Bilgisi sayfası

- **Giriş yeniden yazıldı:** birbirine bağlı animeler üç katmanda tarif
  edilir — seri adı ("hangi aileden?"), ilişkiler ("iki kayıt birbirinin
  nesi?") ve kronoloji notları ("kaçıncı bölümden sonra hangi film?").
- **Seri Bilgisi:** seri adının nereden girildiği, neyi beslediği (not
  formundaki hedef listesi, Seri Kronolojisi'nin "Yayın Tarihi" sekmesi)
  ve sıra söylemediği. Bölümün adı düzeltildi: detay sayfasındaki bölüm
  "Bağlı Animeler" değil **"Bağlantılı Animeler"**.
- **İzleme Sırası:** bağın yalnızca iki uç aynı zincir adını taşıyorsa
  izlendiği eklendi.
- **Kronoloji Notları:** notun neye bağlı olduğu (dizinin kaydına; hedef
  ayrı kayıt), üç görünme yeri (liste, aktif uyarı, "Kronoloji" sayfası).
  **Yeni:** *Kronoloji Notu Ekleme, Düzenleme, Silme* — kim ekleyebilir,
  form ne zaman çıkar, dört alan ne demek, yerinde düzenlemenin hangi
  noktayı değiştirdiği, silme. **Yeni:** *"Kronoloji" ile "Seri
  Kronolojisi" aynı şey mi?* — biri tek dizinin içine, öteki serinin
  tamamına bakar.
- **Yeni bölüm: İlişkili Animeler.** Altı tür ve tanımları (Devamı /
  Öncesi sıra belirten tek tür; Alternatif Versiyon; Alternatif Kurgu; Yan
  Hikâye / Ana Hikâye; Özet / Tam Hikâye; Diğer). Yön: formun tek sorusu
  ("Seçtiğiniz anime, düzenlediğiniz animenin ___'idir"), yönü olan üç
  türün listede iki kez geçmesi, öteki üçünün yönsüzlüğü. Kurallar: bir
  çift en çok bir ilişki taşır, kendine ilişki yok, zincir adı kuralı,
  ilişkilerin bu kurulumda kalması ve yedeğe girmesi. *Bağ mı, ilişki mi?
  — Zincir nasıl kurulur:* üç adım ve ölçüt ("bağ, bunu bitirince şunu
  izle demektir"), Sailor Moon Crystal örneğiyle. Kutu: "Sıradaki Anime"
  kutusu nereye gitti.
- **Seri Kronolojisi:** zincir sekmesi artık "Devamı / Öncesi bağlarıyla"
  diye anlatılıyor ve yeni bölüme bağlanıyor.

### Alanlar ve Kişisel Veri sayfası

- **Yeni bölüm: Ekleme / Düzenleme Formu — Sekmeler.** Beş sekmenin içeriği;
  hepsinin tek form olduğu ve tek kaydetmeyle kaydedildiği; zorunlu bir
  alan başka sekmede boş kaldıysa formun o sekmeye götürdüğü; son sekmenin
  hatırlandığı; İlişkiler panelinin neden yalnızca düzenleme formunda
  olduğu (iki *var olan* kaydı bağlar); kimin hangi formu gördüğü.
- **Yeni bölüm: Paylaşımlı MAL / AniDB Kaydı.** Neden var (kaynak ile
  katalog "bir anime"de anlaşamayabilir), nasıl kullanılır (kutu, parça
  numarasının kendiliğinden gelmesi, işaretsiz kopyanın reddi, düzenlemede
  kutunun veriden türemesi ve kaldırılamaması), nerede görünür ("MyAnimeList
  · 2/3" rozeti, "Aynı Kaynak Kaydı" bölümü) ve nelere etki eder (MAL /
  AniList içe aktarma kuralı, Otomatik Doldur, `[[anime:2994/2]]` konu
  bağlantısı, silme ve kara liste, yedek ve katalog).
- **Yeni bölüm: Konuda Başka Bir Animeye Bağlantı.** `[[anime:2994|Death
  Note]]` kodunun ne olduğu, neden düz bağlantı değil kod olduğu (metin her
  kuruluma gider, MAL numarası her yerde aynı; hedef katalogda yoksa düz
  metne düşer), "Anime bağlantısı ekle" düğmesi (yerel katalogda arar, kodu
  imlece yazar, paylaşımlı kayıtta parçayı taşır) ve elle yazarken kurallar.
- Katalog alanları listesinden "sonraki seri" kalktı (1.1.40'ta emekli
  olmuştu); MAL / AniDB satırı paylaşımlı kutuya bağlanıyor. Yeni not: zincir
  adı, ilişkiler ve kendi eklediğiniz kronoloji notları ne katalog alanıdır ne
  kişisel — bu kurulumun küratör verisidir, sync dokunmaz, yedeğe girer.

### İçe / Dışa Aktarma sayfası

- **Yeni bölüm: İçe Aktarma Kara Listesi.** Yalnızca çok kullanıcılı sitede;
  çözdüğü sorun (silinen anime içe aktarımla geri geliyordu), nasıl çalıştığı
  (silmede kendiliğinden, yalnız kimlikle eşleşme, yalnız eşleşmeyen kayıtları
  keser, katalogda duran animeyi engellemez, paylaşımlı kayıtta son parça
  kuralı), yönetici sayfası ve yedeğe girmediği.

### İçindekiler

Beş yeni satır: *Ekleme / Düzenleme Formu — Sekmeler*, *Konuda Başka Bir
Animeye Bağlantı*, *Paylaşımlı MAL / AniDB Kaydı*, *İlişkili Animeler*,
*İçe Aktarma Kara Listesi*.

## 2. Düzeltilen yanlış bilgi

Yardımdaki uyarı kutusu **"Kendiniz marker eklediyseniz sync sonrası
kaybolur"** diyordu. Bu doğru değildi: katalogdan içe aktarma yalnızca
katalogdan gelen notları yeniler, **kendi eklediğiniz notlar silinmez** ve
Liste Ayarları bunları "katalogla senkronize olmayan not" uyarısıyla
hatırlatır. Yardım, kodun eski hâlini anlatıyordu. Metin doğrusuyla
değiştirildi.

## Dosyalar

**Yeni:**

```
files/migration/1.1.42/upgrade.sql   (şemasız; sürüm damgası + gerekçe kaydı)
```

**Değişen:**

```
files/help.php                    içindekiler (+5 satır)
files/help/help_series.php        +2 alt başlık, yeni "İlişkili Animeler" bölümü
files/help/help_fields.php        yeni "Sekmeler", "Konu Bağlantısı", "Paylaşımlı Kayıt" bölümleri
files/help/help_transfer.php      yeni "Kara Liste" bölümü
files/lang/tr.php                 47 yeni + 10 değişen anahtar
files/lang/en.php                 aynı
files/version.txt
```

Dil dosyası paritesi: 1098 = 1098. Yeni CSS/JS yok.

## Dağıtım notu

- Yardım sayfaları dil dosyalarındaki anahtarları basar. Sayfalar yeni gidip
  dil dosyaları eski kalırsa sayfa çökmez ama metin yerine anahtar adı
  görünür. **`lang/tr.php` ve `lang/en.php` önce ya da birlikte gitmeli;**
  ters yön zararsızdır (fazla anahtar kimseyi rahatsız etmez).
- Migration şema değiştirmez, yalnızca sürüm damgasını taşır ve kendiliğinden
  koşar.
- **Merkez katalog sunucusunda yapılacak bir şey yok.**
- **Dağıtım sunucusunda** her zamanki iki adım: yayımlanan `version.txt`
  1.1.42'ye çekilmeli ve `updates/1.1.42/anime-tracker-1.1.42.zip` paketi
  yayımlanmalı.
- Self-host ile çevrimiçi site aynı metni görür; rol cümleleri iki modu da
  anlatır.
#17
Announcements & Releases / 1.1.41
Last post by hitsumo - Sep 30, 2026, 10:04 AM
# Anime Tracker 1.1.41

**Release date:** 2026-09-13

Schema release; **a manual `ALTER` on the central catalog server is
required.** One piece of work: a MAL or AniDB record may now be **more than
one anime** in the catalog.

## 1. The problem: the sources disagree about what "one anime" is

MAL keeps *Death Note: Rewrite* as **one** two-episode record (2994). AniDB
keeps the same production as **two** one-episode records. The catalog wanted
to follow AniDB — two entries — and point both at MAL 2994, but the MAL
number was unique, so the second entry was refused with "this MAL ID already
exists". The mirror case exists too (AniDB single, MAL split), so the AniDB
number gets the same treatment.

Uniqueness was not just a table constraint. The MAL number served as an
identity key in six places, each assuming "one number = one entry": the
add/edit form, MAL and AniList list import, catalog sync (pull and push), the
JSON backup, the import blacklist and the `[[anime:...]]` synopsis links.
All of them now understand parts.

## 2. The solution: a part number

Every entry now carries a **part number** next to its MAL and AniDB numbers
(`mal_part`, `anidb_part`). Every existing entry is part 1 of its number;
uniqueness is now on the *(number, part)* pair. The world as it was is a
special case of the new one: one part per number, nothing changed.

**A checkbox on the form,** under the MAL link: *"This MAL record corresponds
to more than one anime"* (a separate one for AniDB).

- **Ticked:** the entry takes the **next free part number** for that number
  by itself (2/2, 3/3 ...). Nobody types a number.
- **Unticked:** a second entry with the same number is **still refused** —
  an accidental duplicate is still an accident. The refusal page now adds:
  *"if you mean to split on purpose, tick the box."*

The box is **not stored**; whether an entry is shared is derived from the
data (does another entry carry the same number?), so the form can never
disagree with the table. On the edit screen the box comes ticked for a
shared entry with *"(currently part 2/3)"* next to it.

**Unticking:** if other entries carry the number, the save is refused — this
entry cannot go back to being "the only one" while siblings exist; part 1 is
either this entry already or a sibling's. Separate the other parts first. If
nobody else carries the number, the entry settles to part 1: a part 2 whose
sibling was deleted heals itself on its next save.

**Why a real column and not a flag:** everything that addresses an entry by
identity (imports, sync, backup, blacklist, synopsis links) must be able to
say *which* part it means. "2994" is not enough; "2994/2" is.

## 3. Where it shows

**Anime detail.** The External Sites button reads *"MyAnimeList · 2/3"*
(shared entries only; a single-part entry looks exactly as before). And a new
section, **Same Source Record**: the other entries carrying the same MAL /
AniDB number, with their part numbers. Derived from the data; no relation is
needed. (The curator may still add a typed relation such as *alternative
version* between the parts; that is a separate statement, not required.)

**Synopsis links.** The `[[anime:2994/2|label]]` form arrived. The old
`[[anime:2994]]` keeps meaning what it always meant — part 1 — so no existing
synopsis changed meaning. The link picker writes the `/2` suffix only when
the number is shared and shows *"MAL 2994 · 2/3"* in the list; for an
unshared number the code is the bare number, as before.

## 4. The import rule

A MAL or AniList list carries **one line** per number (status + watched
count); the catalog may hold N parts for it. The count cannot be split
between parts (2/3 → one to A and one to B? two to A?), so no invented
number is ever written:

1. **The status goes to every part** (Watched / Watching / Planned /
  Dropped ...).
2. **The watched count is left alone.**
3. Two exceptions: a **0/N** line writes 0 to every part (someone who has not
  started has started no part); a **Watched** line writes each part's **own
  total** (someone who finished the whole finished every part) — status
  only where the total is unknown.
4. A separate sentence is appended to the result: *"N entries share a MAL
  identity with other entries; check their episode counts by hand."*

| MAL line | Part A (1 ep) | Part B (1 ep) |
| --- | --- | --- |
| Completed 2/2 | Watched, 1/1 | Watched, 1/1 |
| Watching 1/2 | Watching, count untouched | Watching, untouched |
| Plan to Watch 0/2 | Planned, 0 | Planned, 0 |
| Dropped 1/2 | Dropped, untouched | Dropped, untouched |

For an unshared number the import behaves exactly as it has since 1.1.1; the
rule only engages on a shared one.

## 5. Everywhere else

- **Auto-fill** (AnimeSchedule): with the box ticked the **total / aired
  episode counts are not filled** — the source counts the whole, not this
  part. One line is added to the report; the other fields (status, broadcast
  day/time, dates) still fill.
- **JSON backup** carries the parts; the identity quadruple describing the
  other end of a chronology marker or a relation carries them too. A
  pre-1.1.41 backup reads as part 1 everywhere — it restores *exactly* as it
  did before.
- **Catalog sync.** The central catalog publishes the two part fields; pull
  matches on *(number, part)*, push updates on the same pair. A wire from a
  pre-1.1.41 server reads as part 1; such a server can only hold one entry
  per number anyway, so local parts 2 and 3 count as "not in the catalog"
  and drop to *local* — the right outcome.
- **Blacklist.** A number enters the list only when its **last part** is
  deleted. Deleting part 2 of 2994 while part 1 is still in the catalog
  cannot mean "this number does not belong in the catalog". The deletion is
  still recorded (title), without a number.
- **The suggestion queue** knows no parts (one number = one suggestion); an
  approved suggestion is born as part 1. Chronology markers carried by a
  suggestion carry the other end's part.

## 6. Fresh-install fix

Found while testing this release, closed with it: a **fresh install** from
`schema.sql` replays the whole migration chain, and the chain **broke at
1.1.20** — every self-host installed from scratch since 1.1.21 silently
stayed at 1.1.19. Cause: the 1.0.6 migration creates `catalog_requests` with
`CREATE TABLE IF NOT EXISTS` and a `title_english` column; schema.sql had
already created the table (without that column), so the CREATE is skipped,
the column is never born, and 1.1.20 fails reading it. The fix lives only in
schema.sql: the column is defined there "for replay", the way the personal
columns on `animes` are; 1.1.20 reads it, 1.1.21 drops it at the end. No
migration file was touched; existing installs are unaffected. The full chain
ran on an empty database: **92 migrations, 0.5 → 1.1.41**, the column gone
from both tables at the end.

## Verification

- The migration ran on a **copy** of the local database (8,113 anime) with
  the real MigrationManager logic: 1.1.26 → 1.1.41, 15 migrations. Every row
  part 1; both composite unique keys in place. Version stamp reset to 1.1.40
  and run **a second time** (index dropped and rebuilt, same result); third
  run 0 migrations. `schema.sql` loaded into an empty database with 1.1.41
  run on top: same result.
- 37 helper-level checks: part counting, next part, add and edit decisions
  (ticked / unticked / number changed / lone survivor), the composite key
  refusing a duplicate, sibling list, badge and code generation, the five
  import-rule lines, synopsis links (`2994` → part 1, `2994/2` → part 2,
  unknown part → plain text), remaining-carrier count on delete.
- End to end (local PHP server + curl): adding with the box ticked (2994/2,
  then 2994/3); adding unticked → refusal page with the new hint; edit form
  comes ticked with *"(currently part 2/3)"*; unticking → refused; saving
  ticked keeps the part; detail page shows *"MyAnimeList · 2/3"* and the
  *Same Source Record* section (parts 1 and 3); link-picker results carry
  `2994/1`, `2994/2`, `2994/3` and badges; MAL XML import with four lines
  (Completed 2/2, Watching 1/2, Plan to Watch 0/2, Dropped 1/2) → the table
  above exactly, plus the result sentence; parts in the JSON backup; part 3
  deleted and **restored** from the backup (as part 3; the relation and
  chronology marker attached to it linked to the right end — without parts
  the marker would have resolved to part 1, i.e. itself, and been skipped);
  a backup in the pre-1.1.41 shape (part fields stripped) matched part 1 and
  created no duplicate.
- **Catalog sync against a mock center:** the `catalog_server/` files ran on
  a second local PHP server over a "center" database derived from the 1.1.26
  copy and `ALTER`ed by hand. Full push: 8,113 updated, 2 inserted (2994/2
  and 2994/3 with the right parts on the center); second push 0 inserted,
  8,115 updated. Pull into a fresh 1.1.41 install: 2 new, 8,113 updated,
  parts carried; second pull 0 new. A **pre-1.1.41 server** imitation (no
  part fields, one entry per number): part 1 updated, parts 2 and 3 dropped
  to *local*.
- In the browser: both boxes visible on the edit form, the MAL one ticked
  and labelled *"(currently part 2/3)"*; the new section on the detail page
  with its teal stripe. The pane was unstable again; the DOM was read
  instead of a screenshot.

## Files

**New:**

```
files/functions/identity_helpers.php   part rules, siblings, import rule
files/migration/1.1.41/upgrade.sql     two columns + two composite unique keys
```

**Changed:**

```
files/functions.php                     loads the new helper
files/functions/synopsis_helpers.php    [[anime:N/P]] grammar
files/add_anime.php                     boxes, part assignment, refusal hint
files/edit_anime.php                    boxes (state from data), part decision, refusal
files/anime_details.php                 "· 2/3" badge + Same Source Record section
files/anime_link_search.php             ref + badge
files/js/synopsis_link.js               writes the code from ref, shows the badge
files/js/anime_form.js                  auto-fill sends box state + report line
files/fetch_animeschedule.php           drops episode counts for a shared entry
files/list_settings.php                 backup (parts), MAL/AniList import rule
files/catalog_import.php                (number, part) matching; two fields
files/admin/catalog_push.php            two fields + id_map
files/admin/admin_catalog_requests.php  approval resolves identity part-aware
files/index.php                         delete blacklists only the last part
files/css/series.css                    stripe of the new section
files/schema.sql                        columns + composite unique keys;
                                        catalog_requests.title_english (replay)
files/lang/tr.php, files/lang/en.php    13 new keys each (parity 1051 = 1051)
files/version.txt
catalog_server/catalog.php              publishes the two fields
catalog_server/admin_push.php           (number, part) matching; two fields
```

CSS and JS changed, so the 1.1.24 version stamp applies (upload together
with `version.txt`).

## Deployment note

- **A manual `ALTER` on the central catalog server is required — order is
  critical.** The migration does not run there.
 
  1. On the center: add the `mal_part` and `anidb_part` columns and make the
    two unique keys composite. The statements are at the end of
    `files/migration/1.1.41/upgrade.sql`; check the index names with
    `SHOW INDEX FROM animes` first.
  2. Deploy the new `catalog_server/catalog.php` and `admin_push.php`.
  3. Deploy the app (the migration runs on the first page load).
  4. Full catalog push.
 
  In the wrong order: app first, and pushing a shared entry fails on the
  center with "duplicate"; center `ALTER` skipped, and the new `catalog.php`
  selects an unknown column — catalog 503, push fails.
 
- `files/functions.php` and `files/functions/identity_helpers.php` must be
  uploaded **together**; the loader line without the file takes every page
  down with "undefined function".
 
- Because the columns are **added** (not dropped), old files keep working on
  the new schema (they do not read the part; the default of 1 carries them).
  New files do not work on the old schema; the migration ships in the same
  package and runs on the first request.
 
- `files/js/anime_form.js`, `files/js/synopsis_link.js` and
  `files/css/series.css` go together with `version.txt` (stamp).
 
- **On the distribution server** the usual two steps: the published
  `version.txt` must be moved to 1.1.41 and the
  `updates/1.1.41/anime-tracker-1.1.41.zip` package published.
 
- Self-host installs run the same migration; no extra step.
#18
Duyurular ve Sürümler / 1.1.41
Last post by hitsumo - Sep 30, 2026, 10:03 AM
# Anime Tracker 1.1.41

**Yayın tarihi:** 2026-09-13

Şemalı sürüm; **merkez katalog sunucusunda elle `ALTER` gerekir.** Tek iş:
bir MAL ya da AniDB kaydı artık katalogda **birden çok anime** olabiliyor.

## 1. Sorun: kaynaklar "bir anime"nin ne olduğunda anlaşamıyor

MAL, *Death Note: Rewrite*'ı iki bölümlük **tek** kayıt olarak tutar (2994).
AniDB aynı yapımı birer bölümlük **iki** kayıt olarak tutar. Katalog AniDB
gibi iki kayıt açmak, ikisini de MAL 2994'e bağlamak istiyordu — ama MAL
numarası tekildi, ikinci kayıt "bu MAL ID zaten var" diye reddediliyordu.
Ters durum da var (AniDB tek, MAL iki); o yüzden AniDB numarası için de aynı
çözüm.

Tekillik yalnız bir tablo kısıtı değildi. MAL numarası altı yerde "bir
numara = bir kayıt" varsayımıyla kimlik anahtarı olarak kullanılıyordu:
ekleme/düzenleme formu, MAL ve AniList listesi içe aktarma, katalog senkronu
(çekme ve itme), JSON yedek, içe aktarma kara listesi ve konudaki
`[[anime:...]]` bağlantıları. Hepsi bu sürümde parçayı tanıyor.

## 2. Çözüm: parça numarası

Her kaydın MAL ve AniDB numarasının yanında bir **parça numarası** var
(`mal_part`, `anidb_part`). Var olan her kayıt kendi numarasının 1. parçası;
tekillik artık *(numara, parça)* çifti üzerinde. Yani bugüne kadarki dünya,
yeni dünyanın özel hâli: her numarada tek parça, hiçbir şey değişmedi.

**Formda bir kutu:** MAL bağlantısının altında *"Bu MAL kaydı birden çok
animeye karşılık geliyor"* (AniDB için ayrı kutu).

- **İşaretliyse** kayıt o numara için **sıradaki boş parça numarasını**
  kendisi alır (2/2, 3/3 ...). Kimse numara yazmaz.
- **İşaretli değilse** aynı numarayla ikinci kayıt **yine reddedilir** —
  kazara çift kayıt hâlâ engelleniyor. Ret sayfası artık şunu da söylüyor:
  *"bilerek bölmek istiyorsanız kutuyu işaretleyin."*

Kutu **saklanmaz**; "paylaşımlı mı" sorusunun cevabı veriden türer (aynı
numarayı taşıyan başka kayıt var mı). Böylece form ile tablo birbirinden
ayrı düşemez. Düzenleme ekranında kutu, kayıt paylaşımlıysa işaretli gelir
ve yanında *"(şu an parça 2/3)"* yazar.

**Kutunun kaldırılması:** numarayı başka kayıtlar da taşıyorsa kayıt
reddedilir — bu kayıt "tek parça"ya geri dönemez, 1 ya zaten kendisidir ya
da bir kardeştedir; önce diğer parçalar ayrılmalı. Kimse taşımıyorsa parça
1'e iner: kardeşi silinmiş bir 2. parça, bir sonraki kayıtta kendiliğinden
düzelir.

**Neden bayrak değil gerçek kolon:** kimlikle kayıt adresleyen her yer
(içe aktarma, senkron, yedek, kara liste, konu bağlantısı) *hangi* parçayı
kastettiğini söyleyebilmeli. "2994" yetmez, "2994/2" gerekir.

## 3. Nerede görünüyor

**Anime detayı.** Dış Siteler'de düğme *"MyAnimeList · 2/3"* olur (yalnız
paylaşımlı kayıtta; tek parçalı kayıtta hiçbir şey değişmez). Ayrıca yeni
bir bölüm: **Aynı Kaynak Kaydı** — aynı MAL / AniDB numarasını taşıyan öteki
kayıtlar, parça numarasıyla. Veriden türer; ilişki kurmak gerekmez. (Kürator
isterse parçalar arasına ayrıca *alternatif versiyon* gibi tipli bir ilişki
kurabilir; bu ayrı bir ifadedir, zorunlu değil.)

**Konu bağlantısı.** `[[anime:2994/2|etiket]]` yazımı geldi. Eski
`[[anime:2994]]` yazımı her zaman ne demekse o — 1. parça — demeye devam
ediyor; hiçbir mevcut konu metni anlam değiştirmedi. Bağlantı seçici,
numara paylaşımlıysa kodu `/2` ekiyle yazar ve listede *"MAL 2994 · 2/3"*
gösterir; paylaşımlı değilse kod eskisi gibi çıplak numaradır.

## 4. İçe aktarma kuralı

MAL ya da AniList listesinden bir numara için **tek satır** gelir (durum +
izlenen bölüm); katalogda o numara N parça olabilir. Bölüm sayısı parçalara
**bölünemez** (2/3 → A'ya 1 mi, B'ye 1 mi?), bu yüzden uydurma sayı
**yazılmaz**:

1. **Durum bütün parçalara** yazılır (İzlendi / İzleniyor / Planlandı /
  Bıraktı ...).
2. **Bölüm sayısına dokunulmaz.**
3. İki istisna: satır **0/N** ise parçalara 0 yazılır (başlamamış olan
  hiçbir parçaya başlamamıştır); satır **İzlendi** ise her parçaya **kendi
  toplam bölümü** yazılır (bütünü bitiren her parçayı bitirmiştir) —
  toplamı bilinmeyen parçada yalnız durum.
4. Sonuç cümlesine ayrı bir satır eklenir: *"N kayıt paylaşımlı MAL kimliği
  taşıyor; bölüm sayıları elle kontrol edilmeli."*

| MAL satırı | Parça A (1 bölüm) | Parça B (1 bölüm) |
| --- | --- | --- |
| Tamamlandı 2/2 | İzlendi, 1/1 | İzlendi, 1/1 |
| İzleniyor 1/2 | İzleniyor, bölüm dokunulmaz | İzleniyor, dokunulmaz |
| Planlandı 0/2 | Planlandı, 0 | Planlandı, 0 |
| Bıraktı 1/2 | Bıraktı, dokunulmaz | Bıraktı, dokunulmaz |

Tek parçalı kimlikte içe aktarma 1.1.1'den beri neyse odur; kural yalnız
paylaşımlı kimlikte devreye girer.

## 5. Öteki yerler

- **Otomatik Doldur** (AnimeSchedule): formdaki kutu işaretliyse **toplam /
  yayınlanan bölüm sayısı doldurulmaz** — kaynağın sayısı bütünün sayısıdır,
  bu parçanın değil. Rapora bir satır eklenir; öteki alanlar (durum, yayın
  günü/saati, tarihler) yine dolar.
- **JSON yedek** parçaları taşır; kronoloji notu ve ilişkilerin karşı ucunu
  tarif eden kimlik dörtlüsü de parçayı taşır. Eski (1.1.41 öncesi) yedek
  parçasız okunur → 1. parça. Bu, 1.1.41'den önce alınmış bir yedeğin *tam
  olarak* eskisi gibi yüklenmesi demek.
- **Katalog senkronu.** Merkez katalog iki parça alanını yayımlar; çekme
  *(numara, parça)* ile eşler, itme aynı çiftle günceller. 1.1.41 öncesi bir
  sunucudan gelen tel parçasız okunur → 1. parça; böyle bir sunucuda
  zaten her numaradan bir kayıt olabilir, yerel 2. ve 3. parçalar
  "katalogda yok" sayılıp *local*'e düşer — doğru sonuç.
- **Kara liste.** Bir numara ancak **son parçası** silinince deftere girer.
  2994'ün 2. parçası silinirken 1. parçası hâlâ katalogdaysa "bu numara
  kataloğa ait değil" cümlesi yanlış olurdu. Silme kaydı yine tutulur
  (başlık); numarasız kalır.
- **Öneri kuyruğu** parça bilmez (bir numara = bir öneri); onaylanan öneri
  1. parça olarak doğar. Öneriyle taşınan kronoloji notları karşı ucun
    parçasını taşır.

## 6. Sıfırdan kurulum düzeltmesi

Bu sürümün testinde bulundu, bu sürümle kapandı: `schema.sql`'den yapılan
**taze kurulum** migration zincirini baştan koşar ve zincir **1.1.20'de
kırılıyordu** — 1.1.21'den beri sıfırdan kuran her self-host sessizce
1.1.19'da kalıyordu. Sebep: 1.0.6 migration'ı `catalog_requests` tablosunu
`CREATE TABLE IF NOT EXISTS` ile ve `title_english` kolonuyla yaratır;
schema.sql tabloyu daha önce (o kolon olmadan) yarattığı için CREATE atlanır,
kolon hiç doğmaz, 1.1.20 onu okurken düşer. Düzeltme yalnız schema.sql'de:
kolon, `animes`'teki kişisel kolonlar gibi "replay için" tanımlı; 1.1.20
okur, 1.1.21 sonunda düşürür. Migration dosyalarına dokunulmadı; var olan
kurulumlar etkilenmez. Boş bir veritabanında tam zincir koşturuldu:
**92 migration, 0.5 → 1.1.41**, kolon sonda iki tablodan da düşmüş.

## Doğrulama

- Migration, yerel veritabanının bir **kopyasında** (8.113 anime) gerçek
  MigrationManager mantığıyla koşuldu: 1.1.26 → 1.1.41, 15 migration. Bütün
  satırlar parça 1; iki bileşik tekil anahtar yerinde. Sürüm damgası 1.1.40'a
  geri alınıp **ikinci kez** koşuldu (index düşüp yeniden kuruldu, sonuç
  aynı); üçüncü koşu 0 migration. `schema.sql` boş bir veritabanına yüklenip
  üzerine 1.1.41 koşuldu: aynı sonuç.
- Yardımcı düzeyinde 37 kontrol: parça sayımı, sıradaki parça, ekleme ve
  düzenleme kararları (işaretli / işaretsiz / numara değişti / yalnız kalan
  parça), bileşik tekil anahtarın çift kaydı reddetmesi, kardeş listesi,
  rozet ve kod üretimi, içe aktarma kuralının beş satırı, konu bağlantısı
  (`2994` → 1. parça, `2994/2` → 2. parça, bilinmeyen parça → düz metin),
  silmede kalan taşıyıcı sayımı.
- Uçtan uca (yerel PHP sunucusu + curl): kutu işaretliyken ekleme (2994/2,
  sonra 2994/3); işaretsiz ekleme → ret sayfası + yeni ipucu; düzenlemede
  kutunun işaretli ve *"(şu an parça 2/3)"* gelmesi; işareti kaldırma → ret;
  işaretli kaydetme → parça korunur; detay sayfasında *"MyAnimeList · 2/3"*
  ve *Aynı Kaynak Kaydı* bölümü (parça 1 ve 3); bağlantı seçici sonuçlarında
  `2994/1`, `2994/2`, `2994/3` ve rozetler; MAL XML içe aktarma dört
  satırla (Tamamlandı 2/2, İzleniyor 1/2, Planlandı 0/2, Bıraktı 1/2) →
  yukarıdaki tablo birebir + sonuç cümlesi; JSON yedekte parçalar; 3. parça
  silinip yedekten **geri geldi** (parça 3 olarak; ona bağlı ilişki ve
  kronoloji notu da doğru uca bağlandı — parçasız olsaydı not 1. parçaya,
  yani kendisine düşüp atlanırdı); 1.1.41 öncesi biçimindeki yedek (parça
  alanları silinmiş) 1. parçayla eşleşti, çift kayıt üretmedi.
- **Katalog senkronu, sahte merkezle:** `catalog_server/` dosyaları yerel
  ikinci bir PHP sunucusunda, 1.1.26 kopyasından türetilmiş ve elle
  `ALTER`lanmış bir "merkez" veritabanıyla çalıştırıldı. Tam itme: 8.113
  güncellendi, 2 eklendi (2994/2, 2994/3 merkezde doğru parçayla); ikinci
  itme 0 eklendi, 8.115 güncellendi. Taze bir 1.1.41 kurulumuna çekme: 2
  yeni, 8.113 güncellendi, parçalar taşındı; ikinci çekme 0 yeni. **1.1.41
  öncesi sunucu** taklidi (parça alanları yok, her numaradan tek kayıt):
  1. parça güncellendi, 2. ve 3. parça *local*'e düştü.
- Tarayıcıda: düzenleme formunda iki kutu görünür, MAL kutusu işaretli ve
  etiketi *"(şu an parça 2/3)"*; detay sayfasında yeni bölüm turkuaz
  şeritle. Panel yine kararsızdı; ekran görüntüsü yerine DOM okundu.

## Dosyalar

**Yeni:**

```
files/functions/identity_helpers.php   parça kuralları, kardeşler, içe aktarma kuralı
files/migration/1.1.41/upgrade.sql     iki kolon + iki bileşik tekil anahtar
```

**Değişen:**

```
files/functions.php                     yeni yardımcının yüklenmesi
files/functions/synopsis_helpers.php    [[anime:N/P]] yazımı
files/add_anime.php                     kutular, parça atama, ret ipucu
files/edit_anime.php                    kutular (durum veriden), parça kararı, ret
files/anime_details.php                 "· 2/3" rozeti + Aynı Kaynak Kaydı bölümü
files/anime_link_search.php             ref + rozet
files/js/synopsis_link.js               kodu ref'ten yazar, rozeti gösterir
files/js/anime_form.js                  Otomatik Doldur'a kutu durumu + rapor satırı
files/fetch_animeschedule.php           paylaşımlıda bölüm sayıları düşer
files/list_settings.php                 yedek (parçalar), MAL/AniList içe aktarma kuralı
files/catalog_import.php                (numara, parça) eşleme; iki alan
files/admin/catalog_push.php            iki alan + id_map
files/admin/admin_catalog_requests.php  onayda kimlik çözümü parça duyarlı
files/index.php                         silmede kara liste yalnız son parçada
files/css/series.css                    yeni bölümün şeridi
files/schema.sql                        kolonlar + bileşik tekil anahtarlar;
                                        catalog_requests.title_english (replay)
files/lang/tr.php, files/lang/en.php    13'er yeni anahtar (parite 1051 = 1051)
files/version.txt
catalog_server/catalog.php              iki alan yayımlanıyor
catalog_server/admin_push.php           (numara, parça) eşleme; iki alan
```

CSS ve JS değiştiği için 1.1.24'ün sürüm damgası devreye girer
(`version.txt` ile birlikte yüklenmeli).

## Dağıtım notu

- **Merkez katalog sunucusunda elle `ALTER` gerekir — sıra kritik.**
  Migration orada çalışmaz.
 
  1. Merkezde: `mal_part` ve `anidb_part` kolonları eklenir, iki tekil
    anahtar bileşik yapılır. Komutlar
    `files/migration/1.1.41/upgrade.sql` dosyasının sonunda; önce
    `SHOW INDEX FROM animes` ile index adlarına bakılır.
  2. `catalog_server/catalog.php` ve `admin_push.php` yeni sürümü dağıtılır.
  3. Uygulama dağıtılır (migration ilk sayfa açılışında koşar).
  4. Tam katalog itme.
 
  Ters sırada: önce uygulama giderse parçalı bir kaydın itilmesi merkezde
  "duplicate" ile düşer; merkez `ALTER` atlanırsa yeni `catalog.php`
  bilinmeyen kolonu seçmeye çalışır ve katalog 503 verir, itme düşer.
 
- `files/functions.php` ile `files/functions/identity_helpers.php`
  **birlikte** yüklenmelidir; yükleyici satırı olup dosya yoksa her sayfa
  "undefined function" ile düşer.
 
- Kolon **eklendiği** için eski dosyalar yeni şemada çalışmaya devam eder
  (parçayı okumazlar, varsayılan 1 onları taşır). Yeni dosyalar eski şemada
  çalışmaz; migration dosyalarla aynı pakette gider ve ilk istekte koşar.
 
- `files/js/anime_form.js`, `files/js/synopsis_link.js`,
  `files/css/series.css` ile `version.txt` birlikte gitmeli (damga).
 
- **Dağıtım sunucusunda** her zamanki iki adım: yayımlanan `version.txt`
  1.1.41'e çekilmeli ve `updates/1.1.41/anime-tracker-1.1.41.zip` paketi
  yayımlanmalı.
 
- Self-host kurulumlar aynı migration'ı koşar; ek adım yok.
#19
Announcements & Releases / 1.1.40
Last post by hitsumo - Sep 30, 2026, 10:01 AM
# Anime Tracker 1.1.40

**Release date:** 2026-09-12

Schema release; nothing to do on the central catalog. One piece of work: the
watch order is now a **relation type**. The "Next Anime" box and the
`next_in_series` column behind it are retired; the order lives in the relations
table introduced in 1.1.38, as **Sequel / Prequel**. This is the third and last
step of the road laid out in 1.1.36: chain name (1.1.36) → typed relations
(1.1.38) → order into the table (1.1.40).

## 1. "Sequel" / "Prequel" — the order is a relation too

Since 1.1.38 the link between two entries lived in two places: the orderless
types (alternative version, side story, ...) in the **Relations** panel, the
watch order in the **Next Anime** box of the main form. `sequel` was
deliberately left out of the relation types back then, so that two sources
could never disagree about the same pair. But the split had its own cost:

- **The order never made it into a backup.** `next_in_series` was a local row
  number and every install numbers its rows differently; restoring a JSON
  backup silently dropped every chain link. Relations, on the other hand, have
  travelled by identity quadruple (MAL / AniDB / catalog id / title) since
  1.1.38.
- **The same question was asked on two screens.** The answer to "how are these
  two related?" depended on which box you looked at.
- **The column was single-valued.** An entry could have one sequel, while it
  could already have several prequels.

Now there is one place. Two new choices in the Relations panel:

| Choice | Meaning |
| --- | --- |
| **Sequel** (watched after this one) | Exactly what the old "Next Anime" box did |
| **Prequel** (watched before this one) | The same link, entered from the other end |

An entry you mark as "Sequel" on one end shows up as "Prequel" on the other —
the same way side story / parent story already worked.

**The "one place for the order" rule is kept; only the place moved.** The
1.1.38 contradiction argument still holds and is now enforced by a different
mechanism: **a pair carries at most one relation.** "A is B's sequel" and "A is
B's alternative version" cannot both be entered. The 1.1.38 error "these two are
linked with Next Anime" is therefore gone — the situation it guarded against can
no longer arise.

### Your existing links move by themselves

The migration turns every "Next Anime" link in the catalog into a **Sequel**
relation in one pass. Nothing to do by hand. Two exceptions:

- A link pointing at its own entry (the app already refused those) is skipped.
- If the pair already has a relation, the link is skipped and the relation
  stays. That was only possible for a **dormant** link: 1.1.38 allowed a
  relation on top of a link that was never followed because the chain names
  differed. The type you entered by hand wins.

**Dormant links are converted too.** A link that the timeline ignored because
the chain names differed is not deleted; it becomes a Sequel relation. The
series timeline and the spoiler guard still **ignore** it (the chain-name rule
is unchanged), but the **Related Anime** section on the detail page **shows**
it under "Sequel" / "Prequel" — that section is the raw list of every recorded
link. A migration deleting data you entered would have been wrong; if you do
not want it, one click in the edit panel removes it.

### What did not change

- **The chain-name rule (1.1.36) stands.** A link is followed only when both
  ends carry the same chain name. The chain tab of the series timeline, the
  "Other Chain" tabs and the synopsis spoiler guard give **exactly** the
  1.1.39 result on unnamed data — measured, not claimed (below).
- **The "Next Up" card on the detail page** is still there. It now reads the
  relations table and obeys the chain-name rule: a "sequel" on another track
  does not appear in the card, only in the Related Anime section.
- **Nothing goes to the central catalog.** `next_in_series` never did;
  relations do not either.

### Branching

With the column gone an entry may have several sequels. No new rule was
written: the "first same-named neighbour" rule that predecessors have followed
since 1.1.25 now applies forward as well. Which branch is drawn is decided by
the **curator**, not the program — through the chain name. A branch that is
not followed still appears in the list if it carries a name (membership comes
from the name, 1.1.36).

### Side benefit: chain links are now in the backup

The JSON backup carries Sequel relations; on restore the far end is resolved by
the identity quadruple. Restoring the same backup twice creates no new rows. A
1.1.38 install does not recognise the "sequel" rows of a 1.1.40 backup and
**skips** them — it does not misfile them as "other" (1.1.38 was written that
way on purpose).

## 2. Edit screen

- The **Next Anime box is gone** from the "Series & Relations" tab; the tab
  keeps series name, chain name and the Relations panel.
- The target list of the Relations panel is now **rendered on the server**. In
  1.1.38 it arrived empty and the browser cloned it from the Next Anime box
  (rendering the same list twice would have doubled the page). With the box
  gone it is the only list on the page; the cloning JavaScript was removed.
- The panel hint and form hint were rewritten; the help entry "Next in Series"
  became "Watch Order (Sequel / Prequel)" and says how it is set.

## Verification

- The migration ran on a **copy** of the local database (8,113 anime, 14
  links) with the real MigrationManager logic: 1.1.26 → 1.1.40, 14 migrations.
  12 links converted; 1 self-link and 1 dormant link that already had a
  relation were skipped — exactly as intended. The version stamp was reset to
  1.1.39 and the migration ran a **second time**: no new rows, identical table
  dump; a third run applied 0 migrations.
- **Side-by-side comparison:** the 1.1.36 `series_helpers.php` (the last one
  that changed) was run against the copy in its 1.1.39 state, the new one
  against the same copy in its 1.1.40 state — chain discovery, timeline rows,
  chain start and spoiler predecessor lists for 24 series were dumped to JSON
  and the `diff` is **empty**.
- 31 helper-level checks: type order, labels from both ends, choice parsing,
  endpoint direction (Sequel → from = picked, Prequel → from = edited), the old
  `chain` error code falling back to the generic sentence, the "Next Up" card,
  a dormant link converted but not followed, a pair with an existing relation
  skipped, the self-link dropped, **branching** (two unnamed sequels → lowest
  id; a named branch skipped), walks terminating under a **transitive cycle**
  (A→B→...→A), the spoiler gate.
- End to end (local PHP server + curl): detail page with "Next Up" card +
  Prequel/Sequel groups + "Series Chronology" button; spoiler gate ("... and 1
  more entry in the chain"); adding a Sequel (`exists` refusal), adding a
  Prequel (direction correct), deleting; TR and EN views; adding and editing an
  anime (INSERT/UPDATE without the column); an 11-link chain on the series
  timeline and an unlinked member of a named chain appended at the end; JSON
  export with 12 `sequel` rows and no `next_in_series` key; a deleted Sequel
  link **coming back** from the backup, and a second restore creating nothing.
- Browser: the detail page was seen at low resolution (Next Up card, Series
  Chronology button, Prequel/Sequel groups stacked, layout intact). The browser
  pane was unstable again; no high-resolution capture.

## Files

**New:**

```
files/migration/1.1.40/upgrade.sql   sequel into the enum; convert links; drop the column
```

**Changed:**

```
files/functions/relation_helpers.php  sequel type, Prequel label, choices;
                                      chain-conflict check removed
files/functions/series_helpers.php    walks read the table
                                      (seriesChainNeighbours / seriesChainStep);
                                      validateNextInSeries removed
files/edit_anime.php                  Next Anime box removed; target list
                                      server-rendered; column out of UPDATE
files/add_anime.php                   column out of INSERT
files/anime_details.php               "Next Up" card + chronology button from
                                      relations
files/add_anime_relation.php          chain-conflict refusal removed
files/catalog_import.php              column out of INSERT
files/list_settings.php               comments only
files/series_timeline.php             comments only
files/set_series_timeline_mode.php    comments only
files/anime_link_search.php           comments only
files/help/help_series.php            comments only
files/js/anime_form.js                option-cloning block removed
files/lang/tr.php, files/lang/en.php  4 new keys, 3 keys removed,
                                      6 texts updated (parity 1038 = 1038)
files/schema.sql                      column/FK/index removed; enum
files/version.txt
```

No new CSS. The JS changed, so the 1.1.24 version stamp applies (upload
together with `version.txt`).

## Deployment note

- **Because the column is dropped, all files must go up together.** A stale
  file left on the server fails with "Unknown column next_in_series":
  `edit_anime.php` (saving), `add_anime.php` (adding), `catalog_import.php`
  (**catalog sync**), `anime_details.php` (detail),
  `functions/series_helpers.php` (series page and spoiler gate). A stale
  `functions/relation_helpers.php` does not crash but labels Sequel rows as
  "Other Relation".
- The migration runs by itself on the first page load and is **re-runnable**:
  it re-adds the column before reading it (ignored if present) and drops it at
  the end; a second run after a partial one creates no rows.
- **Nothing to do on the central catalog server.** No new field on the wire,
  no manual `ALTER`, no file under `catalog_server/` changed. If the central
  server's own table still has a `next_in_series` column it can stay; no client
  reads it.
- **On the distribution server**, the usual two steps: set the published
  `version.txt` to 1.1.40 and publish `updates/1.1.40/anime-tracker-1.1.40.zip`.
- Self-host installs run the same migration; no extra step.
#20
Duyurular ve Sürümler / 1.1.40
Last post by hitsumo - Sep 30, 2026, 10:00 AM
# Anime Tracker 1.1.40

**Yayın tarihi:** 2026-09-12

Şemalı sürüm; merkez katalogda elle işlem yok. Tek iş: izleme sırası artık bir
**ilişki türü**. "Sıradaki Anime" kutusu ve arkasındaki `next_in_series` kolonu
emekli oldu; sıra, 1.1.38'de gelen ilişkiler tablosunda **Devamı / Öncesi**
olarak tutuluyor. 1.1.36'da kararlaştırılan üç adımlık yolun üçüncü ve son
adımı: zincir adı (1.1.36) → tipli ilişkiler (1.1.38) → sıra tabloya (1.1.40).

## 1. "Devamı" / "Öncesi" — sıra da bir ilişki

1.1.38'den beri iki kaydın bağı iki ayrı yerde duruyordu: alternatif versiyon,
yan hikâye gibi sırasız türler **İlişkiler** panelinde, izleme sırası ise ana
formdaki **Sıradaki Anime** kutusunda. `sequel` o sürümde bilerek ilişki
türlerine konmamıştı — iki kaynak aynı çift hakkında çelişmesin diye. Ama
ikilik kendi bedelini taşıyordu:

- **Sıra yedeğe girmiyordu.** `next_in_series` yerel bir kayıt numarasıydı ve
  her kurulum kayıtlarını farklı numaralandırır; JSON yedeğini geri yüklediğinizde
  zincir bağları sessizce kayboluyordu. İlişkiler ise 1.1.38'den beri kimlik
  dörtlüsüyle (MAL / AniDB / katalog kimliği / başlık) taşınıyor.
- **Aynı soru iki ekrandan soruluyordu.** "Bu ikisinin bağı ne?" sorusunun
  cevabı, hangi kutuya baktığınıza göre değişiyordu.
- **Kolon tekildi.** Bir kaydın tek devamı olabiliyordu; öncülü ise zaten
  birden çok olabiliyordu.

Artık tek yer var. İlişkiler panelinde iki yeni seçenek:

| Seçenek | Anlamı |
| --- | --- |
| **Devamı** (bundan sonra izlenir) | Eski "Sıradaki Anime" kutusuyla birebir aynı şey |
| **Öncesi** (bundan önce izlenir) | Aynı bağ, karşı uçtan girilmiş |

Bir uçtan "Devamı" dediğiniz kayıt, öteki uçta kendiliğinden "Öncesi" olarak
görünür — tıpkı yan hikâye / ana hikâye çiftinde olduğu gibi.

**"Sıra tek yerde" kuralı korundu, yeri değişti.** 1.1.38'in çelişki gerekçesi
bugün de geçerli ve başka bir mekanizmayla sağlanıyor: **bir çift en çok bir
ilişki taşır.** "A, B'nin devamıdır" ile "A, B'nin alternatif versiyonudur"
aynı anda yazılamaz. 1.1.38'in "Sıradaki Anime ile bağlı çifte ilişki
kurulamaz" hatası bu yüzden kalktı — koruduğu durum artık oluşamıyor.

### Mevcut bağlarınız kendiliğinden taşınır

Migration, katalogtaki her "Sıradaki Anime" bağını tek seferde bir **Devamı**
ilişkisine çevirir. Elle yapılacak bir şey yok. İki istisna:

- Bir kaydın kendisini işaret eden bağ (uygulama zaten engelliyordu) atlanır.
- Aynı çift için zaten bir ilişki tanımlıysa bağ atlanır, ilişki kalır. Bu
  yalnızca **uyuyan** bir bağ için mümkündü: zincir adları farklı olduğu için
  zaten izlenmeyen bir bağın üstüne 1.1.38 ilişki girilmesine izin veriyordu.
  Elle yazdığınız tür kazanır.

**Uyuyan bağlar da çevrilir.** Zincir adları farklı olduğu için izlenmeyen bir
bağ silinmez, "Devamı" ilişkisine dönüşür. Seri kronolojisi ve spoiler
koruması onu eskisi gibi **yine izlemez** (zincir adı kuralı aynen geçerli),
ama detay sayfasındaki **İlişkili Animeler** bölümü onu "Devamı" / "Öncesi"
başlığı altında **gösterir** — o bölüm kayıtlı her bağın ham listesidir.
Migration'ın sizin girdiğiniz bir veriyi kendi başına silmesi doğru olmazdı;
istemiyorsanız düzenleme ekranındaki panelden tek tıkla silersiniz.

### Ne değişmedi

- **Zincir adı kuralı (1.1.36) aynen duruyor.** Bir bağ yalnızca iki ucun
  zincir adı aynıysa izlenir. Seri kronolojisinin zincir sekmesi, "Diğer
  Zincir" sekmeleri ve konu spoiler koruması adsız veride 1.1.39 ile **birebir
  aynı** sonucu verir — bu iddia değil ölçüm (aşağıda).
- **Detay sayfasındaki "Sıradaki" kutusu** yerinde. Artık ilişkiler tablosundan
  okunuyor ve zincir adı kuralına uyuyor: başka bir hattaki "devam" bu kutuda
  görünmez, yalnız İlişkili Animeler bölümünde görünür.
- **Merkez kataloga hiçbir şey gitmiyor.** `next_in_series` gitmiyordu,
  ilişkiler de gitmiyor.

### Dallanma

Kolon gidince bir kaydın birden çok devamı olabilir. Yeni bir kural yazılmadı:
öncüller için 1.1.25'ten beri geçerli olan "aynı adlı ilk komşu" kuralı ileri
yöne de uygulandı. Hangi dalın çizileceğine program değil **kurator** karar
verir — zincir adıyla. İzlenmeyen dal, adı varsa yine listeye girer (adlı
zincirde üyelik addan gelir, 1.1.36).

### Yan kazanç: zincir bağları artık yedekte

JSON yedeği "Devamı" ilişkilerini de taşıyor; geri yüklemede karşı uç kimlik
dörtlüsüyle bulunuyor. Aynı yedeği ikinci kez yüklemek yeni satır üretmiyor.
1.1.38 sürümüne ait bir kurulum, 1.1.40'tan gelen bir yedekteki "Devamı"
satırlarını tanımaz ve **atlar** — "diğer" diye yanlış kaydetmez (1.1.38 bunu
baştan böyle yazmıştı).

## 2. Düzenleme ekranı

- "Seri ve İlişkiler" sekmesinden **Sıradaki Anime kutusu kalktı**; sekmede
  seri adı, zincir adı ve İlişkiler paneli duruyor.
- İlişkiler panelindeki hedef listesi artık **sunucuda basılıyor**. 1.1.38'de
  bu liste boş geliyor ve tarayıcı onu Sıradaki Anime kutusundan kopyalıyordu
  (aynı listeyi iki kez basmak sayfayı ikiye katlardı). Kutu gidince sayfadaki
  tek liste bu oldu; kopyalayan JavaScript de kaldırıldı.
- Panel ipucu ve form ipucu yeni duruma göre yazıldı; yardımdaki "Sonraki Seri"
  maddesi "İzleme Sırası (Devamı / Öncesi)" oldu ve nasıl kurulduğunu söylüyor.

## Doğrulama

- Migration, yerel veritabanının bir **kopyasında** (8.113 anime, 14 bağ)
  gerçek MigrationManager mantığıyla koşuldu: 1.1.26 → 1.1.40, 14 migration.
  12 bağ çevrildi; kendini işaret eden 1 bağ ve ilişkisi zaten olan 1 uyuyan
  bağ atlandı — tam beklendiği gibi. Sürüm damgası 1.1.39'a geri alınıp
  **ikinci kez** koşuldu: yeni satır yok, tablo dökümü aynı; üçüncü koşu 0
  migration.
- **Birebir karşılaştırma:** 1.1.36'nın (son değişen) `series_helpers.php`'si
  1.1.39 durumundaki kopya üzerinde, yenisi 1.1.40 durumundaki aynı kopya
  üzerinde koşturuldu — 24 seri için zincir keşfi, çizelge satırları, zincir
  başı ve spoiler öncül listeleri JSON'a döküldü ve `diff` **boş**.
- Yardımcı düzeyinde 31 kontrol: tür sırası, etiketler iki yönden, seçenek
  ayrıştırma, uç yönü (Devamı → from=seçilen, Öncesi → from=düzenlenen), eski
  `chain` hata kodunun genel cümleye düşmesi, "Sıradaki" kartı, uyuyan bağın
  çevrilip izlenmemesi, ilişkisi olan çiftin atlanması, kendini işaret eden
  bağın düşmesi, **dallanma** (iki adsız devam → küçük kimlik; adlı dal atlanır),
  **geçişli döngü** (A→B→...→A) altında yürüyüşlerin sonlanması, spoiler kapısı.
- Uçtan uca (yerel PHP sunucusu + curl): detay sayfasında "Sıradaki" kartı +
  Öncesi/Devamı grupları + "Seri Kronolojisi" düğmesi; spoiler kapısı ("... ve
  zincirdeki 1 kayıt daha henüz izlenmedi"); Devamı ekleme (`exists` reddi),
  Öncesi ekleme (yön doğru), silme; TR ve EN görünüm; anime ekleme ve düzenleme
  (kolon olmadan INSERT/UPDATE); seri kronolojisinde 11 halkalık zincir ve adlı
  zincire bağsız üyenin sona eklenmesi; JSON dışa aktarımda 12 `sequel` satırı
  ve `next_in_series` anahtarının olmaması; silinen bir Devamı bağının yedekten
  **geri gelmesi**, aynı yedeğin ikinci yüklemede satır üretmemesi.
- Tarayıcıda: detay sayfası düşük çözünürlükte görüldü (Sıradaki kartı, Seri
  Kronolojisi düğmesi, Öncesi/Devamı grupları alt alta, düzen bozulmamış).
  Tarayıcı paneli yine kararsızdı; yüksek çözünürlüklü görüntü alınamadı.

## Dosyalar

**Yeni:**

```
files/migration/1.1.40/upgrade.sql   enum'a sequel; bağları çevir; kolonu düşür
```

**Değişen:**

```
files/functions/relation_helpers.php  sequel türü, Öncesi etiketi, seçenekler;
                                      zincir çelişkisi kontrolü kaldırıldı
files/functions/series_helpers.php    yürüyüşler tabloyu okur
                                      (seriesChainNeighbours / seriesChainStep);
                                      validateNextInSeries kaldırıldı
files/edit_anime.php                  Sıradaki Anime kutusu kalktı; hedef listesi
                                      sunucuda; UPDATE'ten kolon çıktı
files/add_anime.php                   INSERT'ten kolon çıktı
files/anime_details.php               "Sıradaki" kartı + kronoloji düğmesi
                                      ilişkilerden
files/add_anime_relation.php          zincir çelişkisi reddi kalktı
files/catalog_import.php              INSERT'ten kolon çıktı
files/list_settings.php               yalnız yorumlar
files/series_timeline.php             yalnız yorumlar
files/set_series_timeline_mode.php    yalnız yorumlar
files/anime_link_search.php           yalnız yorumlar
files/help/help_series.php            yalnız yorumlar
files/js/anime_form.js                seçenek klonlama bloğu kaldırıldı
files/lang/tr.php, files/lang/en.php  4 yeni anahtar, 3 anahtar kaldırıldı,
                                      6 metin güncellendi (parite 1038 = 1038)
files/schema.sql                      kolon/FK/index kaldırıldı; enum
files/version.txt
```

Yeni CSS yok. JS değiştiği için 1.1.24'ün sürüm damgası devreye girer
(`version.txt` ile birlikte yüklenmeli).

## Dağıtım notu

- **Kolon düştüğü için dosyaların tamamı birlikte yüklenmelidir.** Eski bir
  dosya sunucuda kalırsa "Unknown column next_in_series" ile düşer:
  `edit_anime.php` (kayıt), `add_anime.php` (ekleme), `catalog_import.php`
  (**katalog senkronu**), `anime_details.php` (detay),
  `functions/series_helpers.php` (seri sayfası ve spoiler kapısı). Eski
  `functions/relation_helpers.php` kalırsa çökmez ama Devamı satırlarını
  "Diğer İlişki" diye gösterir.
- Migration ilk sayfa açılışında kendiliğinden koşar ve **yeniden
  çalıştırılabilir**: kolonu okumadan önce geri ekler (varsa yok sayılır),
  sonda düşürür; yarım kalan bir koşunun ardından ikinci koşu yeni satır
  üretmez.
- **Merkez katalog sunucusunda yapılacak bir şey yok.** Katalog telinde yeni
  alan yok, elle `ALTER` gerekmiyor, `catalog_server/` altındaki hiçbir dosya
  değişmedi. Merkez sunucunun kendi tablosunda `next_in_series` kolonu varsa
  orada durabilir; hiçbir istemci onu okumuyor.
- **Dağıtım sunucusunda** her zamanki iki adım: yayımlanan `version.txt`
  1.1.40'a çekilmeli ve `updates/1.1.40/anime-tracker-1.1.40.zip` paketi
  yayımlanmalı.
- Self-host kurulumlar aynı migration'ı koşar; ek adım yok.