News:

SMF - Just Installed!

Main Menu

Recent posts

#21
Announcements & Releases / 1.1.39
Last post by hitsumo - Sep 15, 2026, 04:44 PM
# Anime Tracker 1.1.39

**Release date:** 2026-09-08

No schema change. One piece of work: the thousands of addresses that render the
same list no longer look like separate pages to a search engine.

## 1. List duplicates are no longer indexed

1.1.37 answered the question "is this **entry** worth indexing?". The other half
of the same question was left open: how many **addresses** show the same
entries?

Every control on the list page adds a parameter to the address — letter filter,
sort column, direction, page number, page size, tab. A crawler clicks all of
them and treats each combination as a separate page. Measured against the live
catalog, that is **1,568 distinct (letter, page) pairs × 10 sort combinations ≈
15,700 addresses**; the six page-size values push it to ~94,000.

The measurement (8 September 2026, Search Console's 1,000-address sample):

| Share | What |
| --- | --- |
| 65% | thin stub detail page — already closed by 1.1.37; dropping them depends on a re-crawl |
| **29%** | **list duplicate** — the subject of this release |
| 5%  | page with real content |
| 1%  | chronology / series / help |

On the same day the sitemap described **564** addresses; the index held
**5,463**.

These duplicates do **not** open a new entry point: none of them answers a
search query — nobody searches for "letter H, by watch status descending, page
43". What they do is queue up in front of the entry point that already exists.
Crawl budget is fixed, and the measurement showed that only one twentieth of it
was reaching the pages we actually want indexed.

From now on the **bare list page** is indexed; the moment a filter, sort, search
or page number is supplied, the address carries `noindex, follow`. The page is
not indexed, but its **links are still followed** — pagination is the only
internal path to the whole catalog, so that part is essential.

**Nothing changed inside the site.** Filters, sorting, paging and tabs work
exactly as before; the only change is what these addresses say to a search
engine. A visitor sees no difference.

### Why `canonical` was not enough

These pages have carried a correct `canonical` since 1.1.30 — all of them point
at the bare list page. But `canonical` is a **hint**; the measurement showed
Google ignoring it and indexing the duplicates anyway. Yandex receives the same
information as `Clean-param` and does honour it.

### Why no `robots.txt` ban was added

A rule like `Disallow: /*?sort=` closes the address to **crawling**. A `noindex`
on a blocked address cannot be read, so the thousands of duplicates already in
the index would stay there. They have to be read first, then dropped.

### The same fix on the series timeline

`series_timeline.php` draws the same timeline for **every** member of a series —
five members means five addresses for one page, and the view and chain tabs
multiply that again. The `canonical` here was correct too, and again only a
hint. An address that is not the canonical one now carries `noindex, follow`.
The address the sitemap lists is indexed as before; **the sitemap output did not
change.**

## What to expect after deploying

Duplicates do **not** leave the index immediately, and the timescale is weeks:
Google has to re-crawl the address, see the `noindex`, and then drop it.

The expected curve in Search Console: **"Not indexed" goes up first** (every
dropped page moves there under "excluded by noindex tag") and "Indexed" goes
down. **That is success, not a fault.**

The thing to measure is not the page count but **impressions and clicks** in the
Performance report. The dropped addresses had close to zero impressions, so the
total is not expected to fall.

## Deliberately out of scope

- **The page number is still unbounded.** `?page=780` returns the last page of
  the list, not an empty screen. That behaviour is right for a visitor editing
  the address by hand; the search-engine cost of it is already covered by
  `noindex`.
- **The chronology page was left alone** — its only parameter is the record id,
  and that is already canonical.
- **Recent, statistics and recommendations** have been excluded from indexing
  since 1.1.30.

## Files

**New:**

```
files/migration/1.1.39/upgrade.sql   (schemaless; version stamp + rationale)
```

**Changed:**

```
files/functions/seo_helpers.php   the rule (single place)
files/index.php                   noindex, follow on a shaped address
files/series_timeline.php         noindex, follow on a non-canonical address
files/robots.php                  Clean-param list now read from one place
files/version.txt
```

No new language keys — no visible interface text changed. No new CSS or JS
either.

## Deployment note

- `files/functions/seo_helpers.php`, `files/index.php` and `files/robots.php`
  must be uploaded **together**. If the old copy of the helper stays on the
  server, the home page and `robots.txt` both fail (two new functions cannot be
  found). The reverse order is safe: helper first, then the rest — the new
  helper works with the old pages too.
- `files/series_timeline.php` calls no new function and can go on its own.
- **Nothing to do on the central catalog server.** No new field on the catalog
  wire, no manual `ALTER`, and no file under `catalog_server/` changed.
- The migration changes no schema; it only carries the version stamp and runs on
  its own.
- **On the distribution server**, the usual two steps: the published
  `version.txt` must be moved to 1.1.39 and the
  `updates/1.1.39/anime-tracker-1.1.39.zip` package published.
- Self-hosted installs are unaffected — every page is already excluded from
  indexing in that mode.
#22
Announcements & Releases / 1.1.38
Last post by hitsumo - Sep 15, 2026, 04:42 PM
# Anime Tracker 1.1.38

**Release date:** 2026-09-04

1.1.36 gave a chain a **name**. This release gives a link its **type**: you can
now write "these two entries belong together, but one is not the continuation
of the other" — and the application understands it.

## The problem

1.1.36's diagnosis said more than 1.1.36 fixed. What was missing was not only
"which track", but **"what kind of link"**.

- The *Space Adventure Cobra (1982)* film is, per AniDB, the **alternative
  version** of the TV series. The catalog could only say "linked" or "not
  linked", so the correct answer was **unsayable** — and an unsayable sentence
  looked exactly like *missing data* on screen.
- *Sailor Moon Crystal* (also an alternative version) was the opposite case:
  linked **inside** the 90s chain, so the timeline claimed a watch order that
  does not exist.

1.1.36 made both visible (by naming the tracks) but never recorded **why** they
stand apart. This release does.

## The fix: typed, orderless relations

The edit page gained a section at the bottom: **Relations**. You pick an anime
and mark the type; the form asks a single question:

> The anime you pick is this anime's ___.

| Option | Meaning |
| --- | --- |
| Alternative version | Another telling of the same story |
| Alternative setting | Same characters, a different world |
| Side story / Parent story | A side narrative and its main one (two directions) |
| Summary / Full story | A condensed retelling and the whole thing (two directions) |
| Related (other) | None of the above |

Relations appear on the detail page under **Relations**, grouped by type.

## Two ends, two different sentences

Side story and summary are **directional**: if A is B's side story, then B is
*not* A's side story — it is A's **parent** story. So the same record shows up
with two different labels on the two pages:

```
On the film's page:      Side Story   → the TV series   (would be wrong)
On the TV series' page:  Parent Story → ...
```

The correct reading: add the film as a "side story" and the series' page shows
the film under *Side Story*, while the film's page shows the series under
*Parent Story*. Alternative version, alternative setting and "other" read the
same from both ends and carry one label.

## `sequel` is deliberately absent

There is no "sequel / prequel" in the list. Watch order still lives in exactly
one place: the **Next Anime** field. If a sequel could also be stored here, two
sources could disagree about the same pair, and something would have to break
the tie. Leaving the value out makes the contradiction **impossible to enter**
rather than merely discouraged.

No type here states an order. The series timeline and the synopsis spoiler gate
are **unchanged** in this release — both still follow "Next Anime" links only.

## Two things the app refuses

**A second relation on the same pair.** Two rows are either a duplicate or a
contradiction, and there is no third case that a single, better-chosen type
cannot express. To change the type, delete the existing relation first.

**A relation between two entries already chained with "Next Anime".** That pair
would claim to be ordered and unordered at once — precisely the Sailor Moon
Crystal bug. The app refuses it and names the fix: remove the link first.

That check does not repeat the 1.1.36 rule, it **calls** it: a link is only
followed when both ends carry the same chain name. A dormant link (different
names, therefore already ignored) blocks nothing.

## Relations travel in your backup

The JSON backup carries relations. The far end is written by **identity**
(MAL / AniDB / catalog id / title) rather than by a local row number — the same
way chronology notes travel — and is resolved again on restore. A
backup-and-restore round trip no longer loses them.

Restore skips a relation in two cases and reports the count: the far end is not
present on this install, or the type is not recognised. An unrecognised type is
**not** downgraded to "other" — recording a link the app does not understand
would also block you from entering the right one later.

## Nothing changes in your existing data

The migration only creates an empty table. It converts no link and edits no
record: after the upgrade your list, your chains and your spoiler gate behave
exactly as before, and you fill in relations by hand.

## The add/edit form is now tabbed

A second, separate piece of work in the same release. The add and edit form was
one long page, and the new Relations panel made it longer still — worse, it sat
**below** the save buttons. The fields are now split across five tabs:

| Tab | What it holds |
| --- | --- |
| Basics | Title, alternative titles, media type, country, status, episode counts, dates, 18+, filler tracking, image |
| Synopsis & Genres | Synopsis text (TR/EN, catalog + personal), genres, sentences |
| Series & Relations | Series name, chain name, next anime **and the Relations panel** |
| Broadcast & Sources | Episode interval, broadcast day/time/timezone, AniDB/MAL/AnimeSchedule links |
| Personal | Watch status, watched episodes, watch dates, personal notes |

The add page has the same tabs; the only difference is that its "Series" tab
carries no Relations panel — the record does not exist yet, so one end of a
relation would be missing.

**The form is still saved in one piece.** Switching tabs submits nothing;
**Update** stays at the bottom of the page on every tab and saves *all* fields
together, whichever tab is open.

Three details:

- **If a required field is left empty on another tab**, the browser refuses to
  submit and says nothing (the field "is not focusable"). That field's tab now
  opens by itself, so the validation bubble is visible.
- **With JavaScript off** the tab bar never appears and every field is listed
  one after another, exactly as in 1.1.37.
- **The "Auto-fill" report** no longer marks a field on another tab as "(in a
  hidden section)" — the field is there, one click away. The note is kept for
  fields that really are hidden (e.g. the broadcast section that follows the
  status field).

The edit page also **halved in size**: the Relations picker no longer ships a
second copy of the anime list from the server, it is cloned in the browser from
the "Next Anime" picker. Measured on a catalog of 8,000 entries: 3.9 MB → 1.9 MB.

## Changed files

**New:**

```
files/functions/relation_helpers.php   types, direction/inverse labels, rules
files/add_anime_relation.php           add endpoint
files/delete_anime_relation.php        delete endpoint
files/migration/1.1.38/upgrade.sql
```

**Changed:**

```
files/functions.php                  loads the new helper
files/edit_anime.php                 the "Relations" panel + tabs
files/add_anime.php                  tabs
files/anime_details.php              the "Relations" section
files/list_settings.php              backup export + restore
files/js/anime_form.js               tab switching, validation, option cloning
files/css/series.css                 panel and section styles
files/css/components.css             tab bar styles
files/robots.php                     the two new endpoints are disallowed
files/lang/tr.php, files/lang/en.php 37 new strings
files/schema.sql
files/version.txt
```

## Deployment note

- `files/functions.php` and `files/functions/relation_helpers.php` must be
  uploaded **together**. If the loader cannot find the file, **every page**
  dies.
- `files/edit_anime.php` and `files/anime_details.php` call the new helper, so
  they belong in the same package.
- `files/lang/*.php` too, otherwise key names show instead of text.
- `files/css/series.css`, `files/css/components.css` and `files/js/anime_form.js`
  must go with `files/version.txt`: the version stamp on those links comes from
  `version.txt`, and a stale stamp leaves the browser drawing the new page with
  the old CSS and the old script (no tabs).
- If `files/add_anime.php` / `files/edit_anime.php` land without
  `files/js/anime_form.js`, the tab bar never appears and the form falls back to
  one long list — not broken, but half the release is invisible.
- **Nothing to do on the central catalog server.** Relations, like "Next Anime"
  and "Chain name", are application-local: no new field on the catalog wire, no
  manual `ALTER`, no changed file under `catalog_server/`.
- The migration adds a single table and runs itself.
- **On the distribution server**, the usual two steps: publish `version.txt` as
  1.1.38 and the `updates/1.1.38/anime-tracker-1.1.38.zip` package.

## What comes next

The third and final step of the plan: moving sequel/prequel into the table too
and retiring the "Next Anime" field. It is on hold because today's data has no
**branching** — no entry has turned out to have two separate continuations.
#23
Announcements & Releases / 1.1.37
Last post by hitsumo - Sep 15, 2026, 04:42 PM
# Anime Tracker 1.1.37

**Release date:** 2026-09-02

No schema change. Four small, independent pieces of work: one about search
engines, two that clear the way for new languages, and one about identifying
ourselves to an outside service.

## 1. Empty entries are no longer announced to search engines

The catalog grows three ways, and two of them — moving an offline list online,
and importing from MAL/AniList — open the record as a **thin stub**: it has a
title and an identity, but no synopsis and no image. Most of the catalog is in
that state today.

Until now the sitemap's only filter was "not adult content", so every one of
those stubs went into it; since IndexNow was added, the same addresses were also
being pushed to search engines actively. With the site recently submitted to
Google, Yandex and Bing, this stopped being a theoretical problem.

From now on an entry is announced only if it has **something to offer a reader
beyond its title**: a synopsis (in either language), an image, or a chronology
marker. An episode count or a date is not enough on its own — those are
catalogue fields, not the content someone expects from a search result.

An empty entry's detail page now carries `noindex, follow`: the page is not
indexed, but its **links are still followed**, so the filled-in entries in the
same series keep being discovered through it.

**Nothing is deleted or hidden.** Inside the site everything looks and behaves
exactly as before; the only change is what gets announced outward. The moment
you add a synopsis or an image, the entry becomes indexable again on its own.

The rule is read from **one place** by the sitemap, IndexNow and the detail
page, so the three cannot drift apart.

Self-hosted installs are unaffected — every page is already excluded from
indexing in that mode.

## 2. Missing translations now fall back to English

Interface strings are looked up in order: the active language, then the fallback
language, then the key itself. The fallback used to be Turkish.

With two languages that made no difference. With a third it does: falling back
to Turkish for an Indonesian speaker is plainly worse than falling back to
English.

This is also what **makes language contributions practical**: a translator does
not have to finish the whole dictionary. Every key left untranslated falls back
to English, nothing on screen breaks, and the file can be completed a piece at a
time.

The admin dictionary was changed the same way.

## 3. AniList requests now identify themselves

Outgoing requests carry `AnimeTracker/<version> (+repository URL)`.

This is not a compliance change — the import already reads only **the user's own
list**, does not browse the catalog and does not collect metadata. The reason is
what happens if someone notices: an unidentified request looks like an unknown
bot and gets blocked silently; an identified one brings an email first.

Not added to AnimeSchedule, whose requests are already signed with an API key.

## 4. A "adding a language" section in the README

Three steps: copy `lang/en.php`, translate the values (leave the keys alone),
add the code to the allowed-languages list.

With one caution: in most strings a small mistake is harmless, but the ones that
carry **consequences** deserve a human check — the adult-content warning, delete
confirmations, the spoiler gate, and the backup/restore warnings. If those are
wrong the result is not merely awkward; a user can lose data or be shown a
spoiler.

## Changed files

**New:**

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

**Changed:**

```
files/functions/seo_helpers.php          the indexability rule (single source)
files/anime_details.php                  noindex, follow on a thin page
files/functions/i18n_helpers.php         fallback language TR -> EN
files/functions/anilist_import_helpers.php   User-Agent
README.md                                adding a language (TR + EN)
files/version.txt
```

## Deployment note

- `files/anime_details.php` and `files/functions/seo_helpers.php` must ship
  **together**: the page calls a new helper that does not exist in the older
  helper file. The files for the other two pieces are independent.
- **Nothing to do on the central catalog server.** No new field on the catalog
  wire, no manual `ALTER`, no file changed under `catalog_server/`.
- The migration makes no schema change; it only moves the version stamp and runs
  on its own.
- **On the distribution server**, the usual two steps: move the published
  `version.txt` to 1.1.37 and publish
  `updates/1.1.37/anime-tracker-1.1.37.zip`.
- After deployment the search engines need to re-read the sitemap; pages that
  are already indexed will take a while to drop out.
#24
Announcements & Releases / 1.1.36
Last post by hitsumo - Sep 15, 2026, 04:41 PM
# Anime Tracker 1.1.36

**Release date:** 2026-09-02

This release gives a chain a **name** — and with it, the ability to say "these
two entries are separate tellings of the same story".

## The problem

Since 1.1.25 one series name could hold several chains, but a chain itself was
not a thing: it was only the set that fell out of walking the "next in series"
links. That had three consequences.

**A chain could not be named.** The tabs read "Other Chain 1..N"; the only way
to know which was which was to open it.

**You could not say "these are separate tracks".** All you had was "are they
linked". Two real examples:

- The *Space Adventure Cobra (1982)* film is, per AniDB, an **alternative
  version** of the *Space Cobra* TV series — not its sequel. Neither was
  linked, so each showed a chain saying "1 anime" and no tab ever appeared.
- *Sailor Moon Crystal* (also an alternative version) was the opposite case:
  linked **inside** the 90s chain, right after Sailor Stars. The timeline said
  "watch Sailor Stars, then Crystal", and the spoiler gate counted all eight
  90s entries as unwatched prerequisites for Crystal.

**A one-entry track could not be expressed.** A single record linked to nothing
did not count as a chain — a correct rule, since otherwise every standalone
film sharing a series name would spawn its own tab — but it also made a record
that stands apart *on purpose*, like the Cobra film, invisible.

## The fix: membership from the name, order from the link

The add and edit screens gained one field: **Chain name (optional)**, with
autocomplete over the names already in use.

| Field | What it says |
| --- | --- |
| Series name | Which series |
| **Chain name** | **Which track inside it** |
| Next anime | The order along that track |

Entries sharing a name count as one chain and get a tab **under that name** on
the series timeline. "Other Chain 1" becomes "Crystal".

For Sailor Moon: remove Sailor Stars' "next anime" link and name the two
tracks. The result:

```
[ 90s Anime ] [ Crystal ] [ Other Chain 1 ] [ Air Date ]
```

For Cobra, without creating a single link — just names:

```
[ Alternative Version (Film) ] [ 1982 TV Series ] [ Air Date ]
```

## A named single entry is a chain

An unnamed single record still does not count — it is just a record. But a
**named** one is a deliberate statement: "this entry is its own track." The
1982 Cobra film is exactly that.

## The spoiler gate stops at the boundary too

The gate now only looks at earlier entries **on the same track**. Opening
Crystal no longer claims you should have watched the 90s run first.

One asymmetry is deliberate: an unlinked member of a named track **appears in
the list** (appended by air date), but the spoiler gate does **not** treat it
as a prerequisite. Showing a record in a list is harmless; saying "watch this
first" is a claim, and it must rest on a link you made by hand. Inventing a
missing link from air dates would turn the warning into a guess.

## Tab order changed

Chain tabs now sit together and **"Air Date" moved to the end**. With names in
play, a tab wedged between them broke the reading. Addresses and behaviour are
unchanged; only the order moved.

## Nothing changes until you name something

Every record written before 1.1.36 has an empty chain name, and empty equals
empty — so no walk shortens, and chain discovery and the spoiler gate produce
**byte-identical** results to 1.1.35. That is measured, not asserted: the
1.1.35 algorithm was kept line by line in the test suite and both versions were
run against the same data.

## Changed files

**New:**

```
files/migration/1.1.36/upgrade.sql
```

**Changed:**

```
files/functions/series_helpers.php   the one chain rule (chain_same) + membership
files/series_timeline.php            tab labels and order
files/add_anime.php                  Chain name field
files/edit_anime.php                 Chain name field
files/list_settings.php              JSON restore carries the chain name
files/lang/tr.php, files/lang/en.php form strings + help page
files/schema.sql
files/version.txt
```

## Deployment note

- `files/functions/series_helpers.php` and `files/series_timeline.php` must
  ship **together**: the page calls `chain_name_norm()`, which does not exist
  in the older helper file.
- The `files/lang/*.php` files belong in the same package, otherwise the new
  field shows a key name instead of a label.
- **Nothing to do on the central catalog server.** The chain name is
  app-local, exactly like "next in series": no new field on the catalog wire,
  no manual `ALTER`, no file changed under `catalog_server/`. A catalog sync
  does **not** overwrite the chain name.
- The migration adds one column and one index and runs on its own.
- **On the distribution server**, the usual two steps: move the published
  `version.txt` to 1.1.36 and publish
  `updates/1.1.36/anime-tracker-1.1.36.zip`.

## What comes next

A chain name lets you write down **what** a track is, but the application still
does not know the **type** of the relation: write "Alternative Version" and a
human understands, the software does not. The next release turns that into
data (sequel, alternative version, side story, summary...), aligned with AniDB's
own distinctions.
#25
Announcements & Releases / 1.1.35
Last post by hitsumo - Sep 15, 2026, 04:41 PM
# Anime Tracker 1.1.35

**Release date:** 2026-08-31

This release answers one question: **why does an anime you deleted from the
catalog come back on the next import?**

The catalog owner seeds the catalog from a public AniList list and then
prunes it by hand — deleting entries that have no AniDB counterpart. That
workflow broke in two places, and they turn out to be the same fault seen
from two sides:

1. **Nothing recorded what had been deleted.** The pruning decision lived
  only in the curator's head; a deleted row left no trace behind. Two weeks
  later there was no way to answer "did I take this one out on purpose, or
  did it never arrive?"
 
2. **The very next import brought all of them back.** A row is bucketed as
  "unmatched" *because* the catalog has no such entry — which is exactly the
  state a deletion produces. So deleting an anime made it a fresh candidate
  for re-adding.
 

There was a third face to the same fault: the dedup on catalog requests only
looked at pending rows. So **a request you rejected** was raised again on
every import; rejecting did not mean "stop asking".

## The blacklist

Deleting an anime from the catalog now records its identity on a list.
Titles on that list are **never** raised as catalog requests by an AniList or
MyAnimeList import.

The list does two jobs at once: it is the *ledger of what you deleted* and
the *gate that keeps it out*. There is a new card in the admin panel —
**Import Blacklist**.

On that page you can:

- See deleted animes newest first (who deleted them, MAL / AniDB id, any note)
- Search by title or by id
- **Remove an entry** when you change your mind — that anime may return on
  the next import
- **Block an anime by hand** that was never added in the first place (useful
  for a request you rejected)

## What the import preview shows

A new line appears under the preview summary:

> 2 of these are on the admin blacklist and will be skipped.

And the result message gains a sentence when the import finishes:

> 2 animes were skipped because they are on the admin blacklist.

Neither line is printed when the blacklist is empty or nothing was blocked.

## What the blacklist does not block

**An anime that is actually in the catalog** can still be added to a
member's list, even if it is blacklisted. The blacklist means "keep it out
of the catalog", not "let nobody watch it". The gate stands only in the
*unmatched* branch.

In practice this only happens for an anime that was deleted and later
re-added by hand; the admin page marks such an entry with an **"In catalog"**
badge so you can clear it up.

## Matching is by id, not by title

The list matches on `mal_id` and `anidb_id` — the stable identities the
catalog already keys on. **It does not match on title:** two unrelated shows
share a title often enough that an import which silently dropped a
legitimate anime because of an old deletion's name would be worse than the
problem being solved.

Deleting an anime that has neither id is **still recorded**, but such an
entry cannot block anything — there is no key to match against. The page
says so plainly with a **"Cannot block"** badge rather than pretending
otherwise. Adding by hand requires at least one id.

## Nothing changes on a single-user install

The blacklist is active in **multi-user (online) mode only**. A single-user
install has no shared catalog and no curator; its owner's deletions are
personal, and its import must keep bringing whatever their own list
contains.

On such an install: deleting records nothing, importing blocks nothing, and
the new page says the feature is not active here. A user syncing the catalog
is likewise unaffected by what the curator blacklisted — the list is **never
pushed** to the central server.

## Changed files

**New:**

```
files/functions/blacklist_helpers.php   (the one place the rule lives)
files/admin/admin_blacklist.php         (admin page)
files/migration/1.1.35/upgrade.sql
```

**Changed:**

```
files/functions.php                     (loads the new helper)
files/index.php                         (records a deletion)
files/list_settings.php                 (the AniList + MAL import gate)
files/admin/admin.php                   (new card)
files/lang/tr.php, files/lang/en.php    (import notices)
files/lang/admin_tr.php, admin_en.php   (admin page strings)
files/schema.sql
files/version.txt
```

## Deployment note

- `files/functions.php` and `files/functions/blacklist_helpers.php` must ship
  **together**. Without the loader line the file is never read and the delete
  and import pages will not open.
- The `files/lang/*.php` files must ship with
  `files/admin/admin_blacklist.php` and `files/list_settings.php`, otherwise
  key names show up instead of text.
- **Nothing to do on the central catalog server.** No new field on the
  catalog wire, no manual `ALTER`, and no file under `catalog_server/`
  changed.
- The migration runs on its own and creates a single table. If the files are
  uploaded before the migration has run, the app does **not** break: deleting
  still works (nothing is recorded, the reason goes to the server log),
  importing blocks nothing, and the admin page says the table could not be
  read. The migration runs by itself on the next page load and everything
  falls into place.
- **On the distribution server**, the usual two steps: the published
  `version.txt` must move to 1.1.35 — otherwise "Check for updates" still
  believes 1.1.34 is the latest — and the
  `updates/1.1.35/anime-tracker-1.1.35.zip` package must be published, or the
  "Update" button gets a 404 at the download address.
#26
Announcements & Releases / 1.1.34
Last post by hitsumo - Sep 15, 2026, 04:16 PM
# Anime Tracker 1.1.34

**Yayın tarihi:** 2026-08-26

Küçük bir sürüm; şema değişikliği yok. Üç ayrı iş var, ikisi asıl işten
sonra eklendi:

1. **Anime eklerken izleme durumu seçmek artık zorunlu değil** — asıl konu,
  hemen aşağıda.
2. **Spoiler kapısı sayfaya uyduruldu** — 1.1.33'te gelen kutu kalktı.
3. **Kurulum yedeğinin adı** — yalnızca Windows `.exe` kurulumunu ilgilendirir.

## Sorun

Anime ekleme formunda "İzleme Durumu" listesi açılışta **"Seçiniz"**
satırında duruyordu ve alan zorunlu işaretliydi. Tarayıcı bu satırı geçerli
bir cevap saymadığı için, izleme durumu seçilmeden **anime kaydedilemiyordu**;
form gönderilmiyor, ekranda "bu alanı doldurun" uyarısı çıkıyordu.

Oysa izleme durumunun boş kalması bu alanda geçerli bir durum. Katalogdan içe
aktarılan animeler tam olarak böyle, izleme durumu boş olarak listeye
giriyor; liste ve detay ekranları o kayıtları "Seçim Yapılmamış" diye
gösteriyor. Yani veri tarafı bu durumu zaten destekliyordu, yalnızca **elle
ekleme** onu yasaklıyordu.

Çıkış yolu aslında vardı: listenin **en altındaki** "Seçim Yapılmamış"
seçeneği. Ama tepedeki "Seçiniz" ile neredeyse aynı anlama geldiği için
pratikte görünmüyordu — iki satır da "henüz seçmedim" diyordu, biri kabul
ediliyor, öteki reddediliyordu.

## Ne değişti

Boş "Seçiniz" satırı kaldırıldı. **"Seçim Yapılmamış" artık listenin başında
ve açılışta seçili.**

Yani anime eklerken izleme durumuna hiç dokunmazsanız anime izleme durumu boş
olarak kaydediliyor. Listede "Seçim Yapılmamış" görünüyor, sonradan detay ya
da düzenleme ekranından istediğiniz zaman verilebiliyor. Bir durum seçmek
isteyenler için değişen bir şey yok, seçenekler aynı.

| Önce | Sonra |
| --- | --- |
| Seçiniz *(seçili, kaydettirmiyor)* | **Seçim Yapılmamış** *(seçili, kaydediyor)* |
| İzlendi | İzlendi |
| İzleniyor | İzleniyor |
| İzlenme Planlandı | İzlenme Planlandı |
| İzleme Ertelendi | İzleme Ertelendi |
| İzleme Bırakıldı | İzleme Bırakıldı |
| Seçim Yapılmamış | —   |

## Düzenleme ekranı

Anime düzenleme ekranındaki aynı liste de aynı sıraya getirildi. Orada
davranış zaten doğruydu — izleme durumu boş olan bir animeyi açtığınızda
"Seçim Yapılmamış" seçili geliyordu, o yüzden düzenlemede kimse takılmıyordu.
Değişen yalnızca sıra ve işe yaramayan "Seçiniz" satırının kalkması; iki ekran
artık aynı listeyi aynı sırada gösteriyor.

## Değişmeyenler

- **Yayın Durumu hâlâ zorunlu.** O alan animenin kendi bilgisi, kişisel bir
  tercih değil: her animenin bir yayın durumu vardır, boş bırakmak bir seçim
  değil eksik kayıttır. MyAnimeList ve AniDB bağlantılarının zorunluluğu da
  yerinde duruyor.
- **Var olan kayıtlara dokunulmadı.** Bugüne kadar seçtiğiniz izleme
  durumları olduğu gibi duruyor; toplu bir düzeltme yapılmadı.
- **Kişisel Konu, notlar, izleme tarihleri** ve geri kalan her şey aynı.

## Spoiler kapısı sayfaya uyduruldu

İkinci iş, yine ayrı bir konu. 1.1.33'te gelen spoiler kapısı, kesikli turuncu
çerçeve + krem zemin + iç boşluktan oluşan bir **kutu** içindeydi. Anime detay
sayfası ise düz satır düzenindedir: solda etiket, sağda değer, altında ince gri
bir ayraç. Kutu o düzenin içinde yamalı duruyordu — izlediğiniz "1. sezon" sade
bir satırken, izlemediğiniz "2. sezon" renkli bir pano gibi görünüyordu.

Artık kapı da sade:

- **Çerçeve, krem zemin ve iç boşluk kalktı.**
- **Uyarı metni**, sayfanın öteki küçük notlarıyla (örneğin çeviri notu) aynı
  gri ve aynı boyutta.
- **"Yine de okumak istiyorum" / "Konuyu gizle"** artık dolgulu turuncu bir
  düğme değil, düz mavi bir metin — **altı çizgisiz**. Üzerine gelince rengi
  koyulaşıyor. Cümle içinde geçen bağlantılarda alt çizgi gerekli, çünkü düz
  metinden başka türlü ayrılmazlar; burada etiket kendi satırında tek başına
  duruyor, renk zaten tıklanabilir olduğunu söylüyor.

Kapının **işleyişi değişmedi**: aynı kural, aynı yerler, aynı tercih. Yalnızca
görünümü sayfanın geri kalanına uyduruldu. Öneriler → Sürpriz kartındaki
tanıtım metni de aynı sadeleşmeden yararlanıyor.

## Kurulum yedeğinin adı

Üçüncü iş, öncekilerle hiç ilgisi yok. Windows kurulum `.exe`'si, üzerine
kurulum yapılan bir makinede dosyaları değiştirmeden önce masaüstüne bir
veritabanı yedeği bırakır. O yedeğin adındaki tarih, `.exe` **derlenirken**
elle yazılan sabit bir değerden geliyordu.

İki sorun vardı. Birincisi kozmetik: değer elle güncellenmediği için ad,
kurulumun yapıldığı günü değil, güncellemenin unutulduğu günü gösteriyordu.
İkincisi asıl olan: ad sabit olduğu için **aynı `.exe` ile ikinci kez kurulum
yapıldığında birinci kurulumun yedeği sessizce üzerine yazılıyordu.** Yedek tam
da "kurulum ters giderse buradan dön" diye alınıyor; dönülecek noktanın
kaybolması onu amacından ediyordu.

Artık damga derleme anında değil, **kurulum anında** üretiliyor ve saati de
içeriyor:

```
at_install_backup_2026-08-26_182630.sql
```

Aynı gün yapılan iki kurulum bile ayrı dosyalara yazıyor. Elle güncellenmesi
gereken bir değer kalmadı.

Bu değişiklik yalnızca `.exe` kurulumunu ilgilendirir; sunucudaki uygulama
dosyalarıyla ve güncelleme paketiyle ilgisi yoktur.

## Değişen dosyalar

**Yeni:**

```
files/migration/1.1.34/upgrade.sql
```

**Değişen:**

```
files/add_anime.php    izleme durumu listesi + boş değerin boş kabul edilmesi
files/edit_anime.php   aynı liste düzeni
files/css/base.css     spoiler kapısı: kutu kaldırıldı, düğmeler sade
                       mavi metin (altı çizgisiz)
files/version.txt
installer.nsi          kurulum yedeğinin adı (yalnızca .exe derlemesi)
```

## Dağıtım notu

- Veritabanı şeması değişmedi; `files/migration/1.1.34/upgrade.sql` yalnızca
  sürüm damgasıdır.
- **Sunucuya giden dosyalar** (`installer.nsi` gitmez, `files/` ağacının
  dışındadır): `files/add_anime.php`, `files/edit_anime.php`,
  `files/css/base.css`, `files/version.txt` ve yeni
  `files/migration/1.1.34/` klasörü.
- Dosyaların birbirine **kod** bağımlılığı yok: biri eskide kalırsa yalnızca
  kendi sayfası eski davranışla çalışır, hiçbir sayfa bozulmaz. Yeni fonksiyon,
  yeni yardımcı ya da yeni dil anahtarı olmadığı için "eski dosya yeni
  fonksiyonu çağırır" riski bu sürümde yok.
- **Tek istisna:** `files/css/base.css` ile `files/version.txt` **birlikte**
  gitmelidir. Stil adreslerindeki sürüm damgası `version.txt`'ten üretilir;
  sürüm eskide kalırsa tarayıcı damgayı aynı görür ve önbellekteki eski stili
  kullanmaya devam eder, yani kapı hâlâ kutulu görünebilir. Sıra önemli değil,
  birlikte gitmeleri yeterli. **1.1.33 atlanıp doğrudan bu sürüme geçiliyorsa** ayrıca bir şey
  yapmaya gerek yok: `base.css`, 1.1.33'te gelen spoiler kurallarını da taşır —
  tek kopya iki sürümü birden getirir.
- Merkez katalog sunucusunda yapılacak bir şey yok; izleme durumu kişisel bir
  alandır, katalog telinde yer almaz.
- **Dağıtım sunucusunda** her zamanki iki adım: yayımlanan `version.txt`
  1.1.34'e çekilmeli — yoksa "Güncelleme Denetle" hâlâ 1.1.33'ü son sürüm
  sanar — ve `updates/1.1.34/anime-tracker-1.1.34.zip` paketi yayımlanmalı,
  yoksa "Güncelle" düğmesi indirme adresinde 404 alır.
#27
Announcements & Releases / 1.1.33
Last post by hitsumo - Sep 15, 2026, 04:14 PM
# Anime Tracker 1.1.33

**Release date:** 2026-08-25

This release does two things. The first is the main one: **the synopsis of a
later season gives away the earlier one.** The second is a much larger help
section — see the end of this page.

The summary of a second (or third, or fourth) entry in a series almost always
describes how the previous one ended. A synopsis that opens with "after X
died, the remaining crew..." tells someone who has not watched it yet nothing
useful — but it spoils the whole previous season. Opening a later season's
page just to check the episode count was enough to read it.

From now on the synopsis on such a page is not printed directly; it waits
behind a **"Let me read it anyway"** button.

## What it looks like

If any entry that comes before this one in the chain is unwatched, the
"Synopsis" row shows a box instead of the text:

> Seri S2 and 1 more entry in the chain are still unwatched — this synopsis
> may spoil earlier seasons.
>
> **[ Let me read it anyway ]**

Pressing the button expands the synopsis in place and the label turns into
"Hide synopsis". If only a single entry is missing, the wording is singular:
"Seri S2 is still unwatched — this synopsis may spoil the earlier season."

When **all** earlier entries have been watched there is no button at all; the
page looks exactly as it did in 1.1.32.

## The rule

| Situation | Result |
| --- | --- |
| Every earlier entry in the chain is watched | Synopsis shown directly |
| One of the earlier entries is unwatched | Synopsis behind the button |
| An earlier entry is **half** watched ("Watching") | Synopsis behind the button |
| You are watching / finished / dropped this anime | Synopsis shown directly |
| The anime is not part of any chain | Synopsis shown directly |

Three points were deliberate:

**All earlier entries are checked, not just the immediately preceding one.**
On the page for S3, S2 may be watched while S1 was skipped — and the synopsis
can still give S1 away. A "look at the nearest link only" rule would not have
protected that reader.

**A half-watched season does not count as watched.** A summary that describes
what happens after the point you stopped is a spoiler too.

**No guard on an anime you have started.** There is nothing left to spoil in
the synopsis of an anime you are already watching (or finished, put on hold,
or dropped) — even if you skipped the previous season, you learned it while
watching this one.

## Where it applies

- **Anime detail page** — the "Synopsis" row.
- **Recommendations → Surprise** — the 200-character teaser on the card.
  Without this the protection would leak: the detail page would hide the
  synopsis while the surprise card printed its opening sentences, and the
  spoiler is usually in the first sentence.

**The Personal Synopsis is outside the guard.** You wrote that text and only
you can see it; there is no point protecting you from your own note.

## For signed-out visitors

An anonymous visitor has no personal watch data, so nothing counts as
watched and later entries open behind the button for them as well. Someone
browsing the catalog for the first time is exactly the person who needs the
protection, and it costs one click.

## It can be turned off

**List Settings → Spoiler Guard** has a checkbox, **on** by default. With it
off no button ever appears and the synopsis is always shown directly. The
preference is per-user and affects only your own account.

## Small notes

- The box is the browser's own disclosure element (`<details>`) and uses **no
  JavaScript**: it opens with scripts disabled, it is keyboard accessible, and
  opening it costs no extra request — the text already arrived with the page.
- The description the detail page gives to search engines is unchanged. That
  description is a single text published to the whole world; it does not vary
  per visitor.
- The main list page never printed a synopsis, so nothing changed there.

## The help pages grew

A second job in the same release: the help was reviewed from scratch and the
features that **had been built but never documented** were written up. Four
new sections:

- **List, Search and Filters** — the difference between the Full List and My
  List and the default-tab preference, what the search box actually scans,
  the six filters (genre, watch status, broadcast status, letter, year,
  country), where the emotion filter comes from, "Items Per Page" and the
  sort arrows, plus the "Recently Updated" page.
- **Personal Preferences** — all seven preferences from List Settings in one
  list, with the interface language and adult content (18+) explained in
  full.
- **Moving and Importing Lists** — taking and restoring a JSON backup,
  importing a MyAnimeList file, importing by AniList username (both modes
  and the preview step), and how irreversible "Clear the List" really is.
- **Membership and Contributing** — signing in, registering, requesting an
  invite, the account page, what each of the four roles can do, how an anime
  you add lands in the approval queue, and the "Suggest a correction" box.

Three sections were added to "Series and Episode Info": the **Series
Chronology page** (its tabs and the "Other Chain" headings), the **Spoiler
Guard** (the full rule above) and **Broadcast Info and Countdown**.

A few corrections as well: the `+/-` buttons are now noted as being on the
detail page too, start/finish dates joined the personal-fields list and
Country joined the catalog-fields list, sentence tags are assigned by
curators rather than "the admin", the update section is marked as visible
only on a personal install, and one internal link that led nowhere was fixed.

## Changed files

**New:**

```
files/set_spoiler_pref.php
files/help/help_list.php
files/help/help_prefs.php
files/help/help_transfer.php
files/help/help_account.php
files/migration/1.1.33/upgrade.sql
```

**Changed:**

```
files/functions/series_helpers.php   (walking the chain backwards + the guard)
files/anime_details.php              (Synopsis row)
files/recommendations.php            (Surprise card teaser)
files/list_settings.php              (Spoiler Guard checkbox)
files/css/base.css                   (the look of the box)
files/help.php                       (contents: four new groups)
files/help/help_series.php           (three new sections)
files/lang/tr.php, files/lang/en.php (128 new strings each + fixes)
files/version.txt
```

## Deployment note

- `files/functions/series_helpers.php` and the three pages
  (`anime_details.php`, `recommendations.php`, `list_settings.php`) must be
  uploaded **together**. If the helper file stays behind, those pages call a
  function that does not exist and fail to open.
- If `files/css/base.css` stays behind the guard still **works** but looks
  plain (an unstyled disclosure row); nothing is lost functionally.
- If the language files stay behind, the key name shows instead of the notice.
- If `files/set_spoiler_pref.php` is missing the guard still works; only the
  checkbox in List Settings returns 404.
- `files/help.php` and the four new help pages must be uploaded **together**;
  the table of contents links to them. A help side left behind does not
  affect the rest of the application.
- Nothing to do on the central catalog server: the schema did not change and
  the catalog wire was not touched.
- **On the distribution server**, the usual two functional steps: the
  published `version.txt` must be moved to 1.1.33 — otherwise "Check for
  updates" still thinks 1.1.32 is the latest — and the package
  `updates/1.1.33/anime-tracker-1.1.33.zip` must be published, otherwise the
  "Update" button gets a 404 at the download address.
#28
Announcements & Releases / 1.1.32
Last post by hitsumo - Sep 15, 2026, 04:13 PM
# Anime Tracker 1.1.32

**Release date:** 2026-08-24

This release answers one question: **when does a search engine find out that
a page changed?**

1.1.30 gave the site a sitemap. A sitemap answers "what exists here", and a
crawler reads it on its own schedule — days, sometimes weeks. A newly added
anime stays unknown until that read; a deleted address stays in the results
until the crawler comes back.

**IndexNow** answers the other question: *what changed just now?* It is a
notification protocol that Bing and Yandex share — you tell one endpoint and
the participants pass it on. Google does not take part, so IndexNow does not
replace the sitemap; it sits next to it.

## Nothing changed on screen

This release is for whoever runs the site, not for whoever keeps a list on
it. There is no new button, no new field, no page that looks different. And
on a personal single-user install it never engages at all: an installation
with no login already says "do not index me", so announcing its addresses
would contradict itself.

## When an announcement happens

| What you do | What is announced |
| --- | --- |
| Add an anime | The new anime's addresses |
| Edit an anime | That anime's addresses |
| Delete an anime | The removed addresses (the engine returns, sees 404, drops them) |
| Add / change / delete a chronology note | The affected chronology page |
| Approve a member suggestion | The approved anime's addresses |
| Import from the catalog | Only the records that **actually changed** |

Which addresses go out follows the sitemap's own rule — whatever the sitemap
would list: adult-flagged rows excluded, the chronology page only for an
anime that has notes, the series timeline only for the entry that represents
its series.

The list page is deliberately left out. It changes on nearly every write,
crawlers visit it often anyway, and a new anime is discovered through its own
detail address; announcing it every time would spend the budget on the page
that needs it least.

## Saving never waits for anything

The obvious implementation — call the search engine when you press save —
was wrong in three separate ways:

- It would put a third-party network round trip inside your save. A slow
  endpoint would feel like a slow form.
- A failed request would simply be lost. There is no retry, and the address
  would never be announced.
- A 500-row import would fire 500 requests, and editing one anime five times
  in a minute would fire five — which is exactly what the protocol asks
  submitters *not* to do.

So all a save does is note that an address changed. A separate scheduled job
sends what has accumulated, once an hour, in batches — the same arrangement
the aired-episode sync has used since 1.0.19. One page changed ten times in a
day is still one line, and one announcement.

What that means in practice: **there is never a moment where you have to
wonder whether to ping now.** You save as usual and the rest happens on its
own.

## Setup (once)

The feature is **off by default** and wants two lines of configuration:

1. Generate a key:
 
  ```
  php indexnow_ping.php --genkey
  ```
 
2. Put the lines it prints into `config.php`:
 
  ```php
  define('INDEXNOW_KEY', '...');
  define('SITE_URL', 'https://your-site');
  ```
 
  `SITE_URL` is **required** here. Over the web the site's address can be
  worked out from the request itself, but a command line carries no such
  information; without it every address would come out as
  `http://localhost/...` and be rejected. The script refuses to run rather
  than send those.
 
3. Check that `https://your-site/<key>.txt` returns the key as plain text.
  (That file is generated the same way `robots.txt` is — you do not have to
  place a `.txt` on disk yourself.)
 
4. Schedule the job. Hourly is plenty:
 
  ```
  0 * * * * php /path/to/indexnow_ping.php >> /var/log/anime_indexnow.log 2>&1
  ```
 

With no key defined, nothing is queued, nothing is sent, and the rest of the
application is unaffected.

## Four commands for the administrator

```
php indexnow_ping.php             send what is waiting
php indexnow_ping.php --status    configuration + queue report, sends nothing
php indexnow_ping.php --dry-run   print the next batch's addresses, sends nothing
php indexnow_ping.php --retry     clear the counters on stuck rows, then send
```

`--status` looks like this:

```
indexnow status
  mode:          online (indexable)
  key:           a1b2... (32 chars)
  site url:      https://your-site
  key file:      https://your-site/a1b2....txt
  curl:          yes
  queued:        14
  stuck:         0 (attempts >= 5; use --retry)
  last ping:     2026-08-24 09:00:00 UTC
  last count:    31
```

## If something goes wrong

A failed submission does **not** discard the queue. A temporary problem —
rate limiting, a server error, a dropped connection — is made up for on the
next run by itself. A permanent one — a wrong key, an address that does not
match — parks those lines after five attempts so they cannot block the newer
addresses behind them; the lines are not deleted, `--status` counts them as
`stuck`, and once the cause is fixed `--retry` brings them all back.

Every failure is written to the server error log with its detail.

## Changed files

**New:**

```
files/functions/indexnow_helpers.php    (queue + submission logic)
files/indexnow_ping.php                 (the cron / command-line job)
files/indexnow_key.php                  (serves the key file)
files/migration/1.1.32/upgrade.sql
```

**Changed:**

```
files/functions.php                     (loads the new helper file)
files/functions/seo_helpers.php         (address rule for a single anime)
files/add_anime.php                     (queues after an add)
files/edit_anime.php                    (queues after an edit)
files/index.php                         (queues BEFORE a delete)
files/catalog_import.php                (queues only what really changed)
files/admin/admin_catalog_requests.php  (queues on approval)
files/add_chronology_marker.php         (a chronology page may be gained)
files/update_chronology_marker.php      (chronology content changed)
files/delete_chronology_marker.php      (the last note may be gone)
files/.htaccess                         (key file rewrite)
files/config_example.php                (INDEXNOW_KEY documentation)
files/schema.sql
files/version.txt
```

## Deployment note

- `files/functions.php` and `files/functions/indexnow_helpers.php` must ship
  **together**. Without the loader line the file is never read and every page
  that queues an address fails to open.
- `files/functions/seo_helpers.php` belongs in the same package: the address
  rule lives there, and the old file does not have that function.
- If `files/.htaccess` is not refreshed, the key file address answers 403 and
  submissions are silently rejected. The site keeps working; only IndexNow
  does nothing useful.
- **Nothing to do on the central catalog server.** Unlike 1.1.31 there is no
  new field on the catalog wire and no manual `ALTER`.
- The migration runs automatically and creates a single table.
- **On the distribution server**, the usual two functional steps: the
  published `version.txt` must be moved to 1.1.32 — otherwise "Check for
  updates" still believes 1.1.31 is the latest — and the
  `updates/1.1.32/anime-tracker-1.1.32.zip` package must be published, or the
  "Update" button gets a 404 at the download address. Open the package and
  confirm it carries the three new files.
#29
Announcements & Releases / 1.1.31
Last post by hitsumo - Sep 15, 2026, 04:02 PM
# Anime Tracker 1.1.31

**Release date:** 2026-08-24

This release answers one question: **what do you write when you do not know
the whole date?**

For older productions the sources often give no day, and sometimes no month
either — only a year survives. Until now there were two options and both were
bad:

- **Leave the field empty.** The year you did know was lost too.
- **Invent a day.** The screen then showed `01.01.1979`, and a reader took
  that to mean "1 January, not 3 November". A false precision.

You can now enter **as much of the date as is known**; the unknown part shows
up as `??`.

## What it looks like

| Known | On screen |
| --- | --- |
| Day, month, year | `08.04.2005` |
| Month and year | `??.04.2005` |
| Year only | `??.??.2005` |
| Nothing | `??.??.????` |

The rule applies to both **Release Date** and **End Date**, independently of
each other: knowing the start to the day and only the year of the end is
perfectly fine.

## How you enter it

On the add and edit anime screens each date field now starts with a dropdown:

- **Full date** — today's date picker, nothing changed (the default).
- **Month and year** — a month box and a year box appear.
- **Year only** — only the year box appears.
- **Unknown** — no box at all.

Nobody types `??` by hand; the screen shows whatever you picked. When you
switch from a full date to "Year only", the year you already typed moves into
the year box on its own — no need to type it again.

## "Unknown" and "empty" are not the same

The distinction is deliberate:

- **Leave the field empty** and the anime detail page still says **"Not
  specified"** — meaning "I have not filled this in yet".
- **Pick "Unknown"** and it says `??.??.????` — meaning "I looked, there is
  no source".

That way you can tell which record is waiting to be filled in and which one
genuinely has no source.

## No countdown on a partial date

For anime that have not started airing, the detail page counts down to the
first episode. If the day of the date is not known, that line is **not
printed at all**. Saying "42 days left" while the screen reads `??.??.2027`
would be inventing an exact number for an unknown day.

## The year filter and sorting still work

The **year filter** on the main list and the date ordering in the series
timeline work exactly as before. An anime whose year alone is known shows up
under that year's filter and sorts at the start of that year.

## "Auto-fill" now brings partial dates too

The **Auto-fill** button behind an AnimeSchedule link takes its dates from
AniList. AniList holds only the year for many older productions; until now
those records were **dropped entirely** and the field stayed empty. The known
part now lands in the form and the precision dropdown moves to the matching
option by itself.

One thing is preserved: a date you typed is never overwritten. If you entered
a full date, your day stays put even when AniList says "year only".

## In the series timeline

Dates on the "Air Date" tab use the same format (for example
`??.04.2005 – ??.06.2005`). When the start and the end point at the same
value only one of them is printed; when they differ, both are.

## Two notes about the catalog

**Precision travels with the catalog.** The curator's "only the year is
known" reaches every install through the central catalog. A catalog file
older than 1.1.31 is read as "full date", so the old behaviour is preserved
exactly.

**A gap was closed: the end date is now really synced.** The central catalog
had been *sending* the end date since 1.1.14, but the client side never
stored it — the field was silently dropped. That gap is closed.

> **Behaviour change:** on a record that comes from the catalog, an end date
> you entered by hand may be replaced by the catalog's value on the next
> sync. That rule already applied to the same record's title, status,
> synopsis and release date; the end date surviving was not a rule but an
> oversight.

## The month box and the custom dropdown

Long dropdowns (more than 8 options) are turned into a custom widget on the
desktop — that has been the case since 1.1.11, so a long list never covers
the screen. The partial-date month box has 13 options, so it gets converted
too.

The two features collided on the first attempt: the rule that hides the box
was applied to the underlying native list rather than to the widget actually
on screen. The result was that the month box stayed visible whatever
precision you picked — even on "Full date" and "Year only". The date and year
boxes are text inputs, so they are never converted and behaved correctly; the
fault showed only on the month box.

The hiding is now applied to the widget itself. The fix is general: any long
dropdown the page hides stays hidden after conversion.

## Changed files

**New:**

```
files/functions/date_precision_helpers.php
files/migration/1.1.31/upgrade.sql
```

**Changed:**

```
files/functions.php                    (loads the new helper)
files/functions/anime_helpers.php      (no countdown on a partial date)
files/functions/series_helpers.php      (new columns added to the queries)
files/functions/anilist_import_helpers.php  (AniList partial dates kept)
files/fetch_animeschedule.php          (auto-fill carries partial dates)
files/add_anime.php                    (form + save)
files/edit_anime.php                    (form + save)
files/js/anime_form.js                  (precision dropdown, field swapping)
files/js/select_enhance.js              (hidden long dropdowns stay hidden)
files/css/components.css                (layout of the date field)
files/anime_details.php                (display)
files/series_timeline.php              (Air Date tab)
files/catalog_import.php                (precision + end-date sync)
files/admin/catalog_push.php            (new columns are sent)
files/admin/admin_catalog_requests.php  (precision carried through approval)
files/list_settings.php                (backup restore + suggestion flow)
files/schema.sql
files/lang/tr.php, files/lang/en.php    (9 new strings each + help page)
files/version.txt
catalog_server/catalog.php              (new columns are published)
catalog_server/admin_push.php          (new columns are stored)
```

## Deployment notes

- `files/functions.php` and `files/functions/date_precision_helpers.php` must
  be uploaded **together**. Without the loader line the file is never read
  and every page that prints a date fails to open.
- The two forms (`add_anime.php`, `edit_anime.php`) and
  `files/js/anime_form.js` must also travel together: an old script does not
  know the new precision dropdown and the field swapping will not work. The
  server side still saves correctly, you would just see all three boxes at
  once.
- `files/js/select_enhance.js`, `files/js/anime_form.js` and
  `files/css/components.css` must travel together; if only one of them stays
  old, the month box remains on screen whatever precision is picked.
- If the language files stay old, the key name shows up instead of the option
  labels — harmless but ugly.
- **Two `ALTER` statements have to be run by hand on the central catalog
  server.** Migrations do not run there; a push made before the columns exist
  will fail. Order: `ALTER` on the central server first, then the application
  deployment, then the push. The commands are written at the top of
  `files/migration/1.1.31/upgrade.sql`.
- **On the distribution server** the usual two functional steps apply: the
  published `version.txt` must be moved to 1.1.31 — otherwise "Check for
  updates" still thinks 1.1.30 is the latest — and the
  `updates/1.1.31/anime-tracker-1.1.31.zip` package must be published, or the
  "Update" button gets a 404 at the download address.
#30
Announcements & Releases / 1.1.30
Last post by hitsumo - Sep 15, 2026, 04:01 PM
# Anime Tracker 1.1.30

**Release date:** 2026-08-23

This release introduces the site to search engines — **and explicitly hides
the installations that should never have been introduced to them.** Both are
sides of the same job, so they were done together.

Until now a page `<head>` carried nothing but a title: no description, no
canonical link, no sharing tags. There was no `robots.txt` and no
`sitemap.xml`. So there was no way to tell a search engine "these pages
exist" — and no way to tell it "never look here".

## Privacy first: for people who self-host

**Personal installations are now closed to search engines.** In single-user
(self-host) mode the application has no login — that is by design, whoever
opens the page is the single user. Someone running a personal install on
their own server was, until now, not only publicly reachable but
**indexable**. That was a privacy bug, not an SEO gap.

Indexing now follows the mode:

| Installation | Behaviour |
| --- | --- |
| **Single-user (self-host)** | Every page says "do not index"; `robots.txt` blocks everything; no sitemap is published |
| **Multi-user (online)** | Pages are indexable; the sitemap is published |

No new setting was added — the decision is tied to the existing
`MULTI_USER_MODE` switch. A personal install should be private without its
owner having to discover a checkbox. Anyone who wants the opposite sets that
switch to `true` in `config.php`.

## The sitemap (`sitemap.xml`)

There is now a `sitemap.xml` address, generated fresh on every request. It
lists:

- the main list page, the about page and the six help pages,
- **the detail page of every anime entry**,
- the **watch order** page of anime that have chronology markers,
- **one** series chronology page per series.

The real reason for a sitemap is paging: the list shows 10 entries per page
by default, so as the catalog grows, the entries deep in it become
practically unreachable by following links. The sitemap names every entry
directly.

**The sitemap splits itself when the catalog grows.** Past 2000 entries it
produces a sitemap index plus chunks instead of one file — the standard
structure search engines expect — and it happens automatically.

**Adult-flagged entries are not listed.** The detail page already hides them
behind an opt-in preference, so an anonymous visitor (and a crawler) only
sees a neutral notice. Listing an address that answers with a placeholder is
worse than not listing it.

## `robots.txt`

It exists now, and its content follows the mode. On a multi-user install the
public pages stay open while login, registration, account, editing, admin
and every "does something when opened" address are closed off. The sitemap
address is announced here too.

There is a separate section for Yandex: the sorting and filter parameters of
the list page are declared with `Clean-param`, so dozens of addresses of the
same list are not mistaken for separate pages.

## Tags added to the page heads

Every page gained a **description**, a **canonical** link and **sharing
tags** (Open Graph / Twitter card). In practice:

- **In search results** a meaningful description now appears under the
  title. On anime detail pages it is built from the **catalog synopsis**.
- **When you paste a link** into WhatsApp, Discord or a forum, you get a
  card with the title, the description and the **poster** instead of a bare
  address.

**Your personal synopsis never appears in these descriptions.** They are
built from the catalog synopsis only; the personal one is your own note,
while a meta description is published to the whole world.

### Canonical: one address per piece of content

- **List page:** sorting, filter, search and page-number parameters all
  point back at the bare list address.
- **Series chronology:** this page draws the same series identically for
  every member, so one page would have had many addresses. One address per
  series is now the official one.
- **Watch order:** the view mode (broadcast order / story order) may appear
  in the address, but the official address is a single one.

### Three pages left out of search

**Recently Updated**, **Statistics** and **What Should I Watch?** are
deliberately kept out of search results: all three change on every visit,
the statistics describe one person's list, and the "surprise" mode returns a
different anime on every request. Links on those pages are still followed —
so crawling effort goes to the detail pages instead.

## A small but real fix

Opening a detail page with a non-existent anime number said "not found"
while the server answered **"everything is fine"**. A search engine reads
that as "this page exists" and treats the one-line error as content. It now
returns a proper "not found" response. Nothing changes on screen.

## Configuration: `SITE_URL` (optional)

Search engines want absolute addresses; the application links relatively
everywhere. The address is now reconstructed from the request itself, so
**you do not have to do anything** — an install in a subdirectory produces
correct addresses too.

The one exception: behind a reverse proxy or a CDN the incoming request may
not reveal the site's real address. In that case `SITE_URL` can be set in
`config.php`. It sits in the example configuration file with its
explanation, commented out by default. Existing `config.php` files keep
working untouched.

## After the release (not code)

For the sitemap to be worth anything it has to be announced: Google Search
Console, Bing Webmaster (the same registration covers DuckDuckGo, Ecosia and
Yahoo) and Yandex Webmaster. All three are given the **same** sitemap
address; no engine needs its own file. If site ownership is verified with a
**DNS TXT record**, no file has to be added to the site at all — that is the
cleanest route.

## Changed files

**New:**

```
files/functions/seo_helpers.php
files/sitemap.php
files/robots.php
files/migration/1.1.30/upgrade.sql (version stamp only)
```

**Changed:**

```
files/.htaccess                    (/robots.txt and /sitemap.xml rewrites)
files/functions.php                (loads the new helper)
files/config_example.php           (optional SITE_URL)
files/index.php                    (meta + canonical)
files/anime_details.php            (meta + canonical + "not found" response)
files/chronology.php               (meta + canonical)
files/series_timeline.php          (meta + one canonical per series)
files/about.php, files/help.php    (meta + canonical)
files/help/help_basics.php, help_fields.php, help_sync.php,
files/help/help_discovery.php, help_series.php, help_timezone.php
files/recent.php, files/statistics.php, files/recommendations.php
                                   (no-index tag)
files/lang/tr.php, files/lang/en.php  (11 new strings each)
files/version.txt
```

## Deployment notes

- `files/functions.php` and `files/functions/seo_helpers.php` must be
  uploaded **together**. If only the pages are updated they call a function
  that does not exist and the page will not open.
- If `files/.htaccess` stays old, `/robots.txt` will not work (the server
  blocks the `.txt` extension across the board); `robots.php` and
  `sitemap.php` still open under their own names.
- If the language files stay old, the key name shows up instead of the
  description text — harmless but ugly.
- The database schema did not change, and nothing has to be done by hand on
  the central catalog server.
- **On the distribution server** the usual two functional steps apply: the
  published `version.txt` must be moved to 1.1.30 — otherwise "Check for
  updates" still thinks 1.1.29 is the latest — and the
  `updates/1.1.30/anime-tracker-1.1.30.zip` package must be published, or
  the "Update" button gets a 404 at the download address.
- One thing to confirm after upload: multi-user mode must be on in the live
  `config.php`, otherwise the site closes itself to search engines.