M: give me a linux fedora cli command that will add 's01e##' onto the beginning of each file in a directory, with ## incrementing from 1 and going up

G: Here’s a one-liner you can run in the directory with the files:

n=1; for f in *; do mv -- "$f" "s01e$(printf "%02d" $n) $f"; n=$((n+1)); done

Breakdown

Notes

Do you want me to make it sequential by sorted name (so it applies in alphabetical order), or just in raw shell glob order?

M: alphabetical order, please - taking into account that the original will have '01' instead of just '1'. i've seen that mess with alphanumeric order.

G: Got it. You’ll want to force numeric sort so that 01, 02, 10 sort properly. Here’s the adjusted command:

n=1; for f in $(ls -1v); do mv -- "$f" "s01e$(printf "%02d" $n) $f"; n=$((n+1)); done

Key Changes

⚠️ Caveat: using for f in $(ls ...) can break on filenames with spaces. If your files have spaces, safer version is:

n=1; ls -1v | while IFS= read -r f; do mv -- "$f" "s01e$(printf "%02d" $n) $f"; n=$((n+1)); done

That one is space-safe.

Want me to also add a dry run version (so you can check the new names before actually renaming)?

M: yes, please.

G: Here’s a dry-run version that just shows what would be renamed without touching the files:

n=1; ls -1v | while IFS= read -r f; do echo mv -- "$f" "s01e$(printf "%02d" $n) $f"; n=$((n+1)); done

What it does

Once you’re satisfied, just remove the echo and run the real renaming command:

n=1; ls -1v | while IFS= read -r f; do mv -- "$f" "s01e$(printf "%02d" $n) $f"; n=$((n+1)); done

Do you want me to also add a backup/undo plan in case you run it and decide you don’t like the results?

M: nice. can you please, now, give me a jellyfin syntax cheat sheet - something i can cut and paste into a txt file for quick reference

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Here you go — a compact Jellyfin naming & folder cheat sheet you can drop into a .txt file.

JELLYFIN QUICK SYNTAX CHEAT SHEET
=================================

GENERAL
- Use one top-level folder per library (Movies, TV, Music, etc.).
- Prefer ASCII characters; avoid weird punctuation in names.
- Year in parentheses: (1999)
- Zero-pad season/episode numbers: S01E02
- Specials go in Season 00.
- NFO and images sit alongside media with matching basenames.

----------------------------------
MOVIES
----------------------------------
Folder:
  Movies/
    Movie Name (Year)/
      Movie Name (Year).mkv

Multi-versions (editions / cuts):
  Movie Name (Year)/
    Movie Name (Year) - Director's Cut.mkv
    Movie Name (Year) - IMAX.mkv
    Movie Name (Year) - 4K.mkv   # resolution auto-detected, tag optional

Multi-part (stacked):
  Movie Name (Year) - Part 1.mkv
  Movie Name (Year) - Part 2.mkv
  # also works: - cd1 / - cd2 / - disk1 / - disk2

Extras (bonus features & trailers):
  Movie Name (Year)/
    extras/
      Behind the Scenes.mkv
      Deleted Scenes.mkv
      Featurette - Costumes.mkv
    trailers/
      Movie Name (Year) - Trailer.mkv
  # Suffix also works: Movie Name (Year)-trailer.mkv

NFO (metadata):
  Movie Name (Year)/
    movie.nfo
  # or: Movie Name (Year).nfo

Images:
  Movie Name (Year)/
    poster.jpg          # primary poster
    backdrop.jpg        # background/fanart
    folder.jpg          # also accepted as poster

----------------------------------
TV SERIES
----------------------------------
Folder layout:
  TV Shows/
    Series Name/
      Season 01/
        Series Name - S01E01 - Episode Title.mkv
      Season 02/
        Series Name - S02E01 - Episode Title.mkv

Shorthand (no title is fine):
  Series Name/Season 01/ S01E01.mkv

Multi-episode files:
  Series Name/Season 01/ Series Name - S01E01E02.mkv
  # or: S01E01-02

Specials:
  Series Name/Season 00/ Series Name - S00E01 - Special Title.mkv

Date-based episodes (daily shows):
  Series Name/Season 2019/ Series Name - 2019-05-20 - Title.mkv
  # YYYY-MM-DD format

Anime / absolute numbering (maps to Season 1):
  Series Name/
    Season 01/
      Series Name - 001.mkv
      Series Name - 002.mkv
  # Using S01E001 etc. also works

NFO:
  Series Name/
    tvshow.nfo
  Series Name/Season 01/
    S01E01.nfo                # or match the episode basename: Series Name - S01E01 - Title.nfo

Images:
  Series Name/
    poster.jpg
    backdrop.jpg
  Series Name/
    season01-poster.jpg
    season00-poster.jpg       # specials
  Series Name/Season 01/
    Series Name - S01E01 - Episode Title.jpg   # per-episode still (optional)

----------------------------------
SUBTITLES (External)
----------------------------------
Same basename + language code:
  Movie Name (Year).eng.srt
  Movie Name (Year).en.forced.srt
  Movie Name (Year).en.sdh.srt

For TV:
  Series Name - S01E01 - Title.eng.srt
  S01E01.eng.srt

Notes:
- Use ISO codes (en, eng, es, spa, fr, fra, etc.).
- “forced” or “sdh” may be appended before extension.

----------------------------------
AUDIODESCRIPTIONS (External, optional)
----------------------------------
  Movie Name (Year).descriptive.mp3
  # Or treat as an alternate version; embedded tracks are preferred.

----------------------------------
3D / VIDEO TAGS (Optional)
----------------------------------
  Movie Name (Year) - 3D HSBS.mkv
  Movie Name (Year) - 3D HOU.mkv
- Jellyfin usually detects formats/flags; tags are for human clarity.

----------------------------------
MUSIC
----------------------------------
Artists & Albums:
  Music/
    Artist Name/
      (Year) Album Title/
        01 - Track Title.flac
        02 - Track Title.flac

Multi-disc:
  Music/Artist/(Year) Album Title/CD1/ 01 - Track.flac
  Music/Artist/(Year) Album Title/CD2/ 01 - Track.flac

Cover Art:
  folder.jpg        # album cover inside the album folder
  artist.jpg        # in artist folder

----------------------------------
COLLECTIONS (Box Sets)
----------------------------------
- Prefer creating Collections inside Jellyfin.
- If you must mirror as folders: keep normal movie folders and use Collections in the UI.

----------------------------------
COMMON GOTCHAS
----------------------------------
- Don’t mix multiple movies in one folder (one movie per folder is safest).
- Zero-pad everything: Season 01, S01E03, 001.
- Avoid renaming to include release-group junk (Jellyfin ignores it, but it clutters).
- Keep NFO/Images next to their target media with matching basenames.

----------------------------------
POWER TIPS
----------------------------------
- Bulk rename (bash, numeric sort safe):
  n=1; ls -1v | while IFS= read -r f; do mv -- "$f" "s01e$(printf "%02d" $n) $f"; n=$((n+1)); done

- Multi-episode merge naming:
  Series - S01E01E02.mkv   # single file that contains both

- Specials:
  Put all specials in Season 00 and number S00E##.

- Date-based shows:
  Prefer Series - YYYY-MM-DD.ext (with matching Season folder by year).

If you want this tailored to your exact libraries (anime-heavy, documentaries, mixed cuts, etc.), say the word and I’ll trim/add sections accordingly.

M: okay, it may be in there, but humor me - what if an anime has season-specific ova specials?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Put season-specific OVAs in Season 00 as specials (S00E##), following the numbering used by your metadata source (usually TMDb). Then enable “Display specials within seasons they aired in” so Jellyfin shows those OVAs inside the relevant season view. If TMDb classifies an OVA as a movie, file it in your Movies library instead. citeturn0search10turn0search6turn0search4

Practical patterns

Series Name/
  Season 00/                      # all specials/OVAs live here
    Series Name - S00E07 - OVA Title.mkv    # OVA tied to Season 2, ep ~5 (per TMDb listing)
    Series Name - S00E08 - OVA 2.mkv

Mini add-on for your cheat sheet

ANIME OVAs / SEASON-SPECIFIC SPECIALS
- File OVAs in: Season 00/ as S00E## per TMDb numbering.
- Enable: Dashboard → Libraries → Display → “Display specials within seasons they aired in.”
- If TMDb lists an OVA as a Movie: store in Movies library; use a Collection to group with the series.
- Multi-ep OVA in one file: S00E07E08 (or S00E07-08).

If you want, I’ll fold this block into the cheat sheet I gave you so you can paste one file and be done.

M: i have a japanese and an english version of a show. i want to store the japanese version in the same folder - but i don't want it to actually muck with the playback structure.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Keep both in the same season folder but make the JP files alternate versions of the same episode (same SxxExx, add a version tag like (Japanese)), so Jellyfin shows a single episode with a Versions picker. If you want Jellyfin to totally ignore the JP copies, put them in a hidden subfolder (e.g., .jp/) and enable “ignore hidden files/folders” in that library.

Series Name/
  Season 01/
    Series Name - S01E01 - Title.mkv
    Series Name - S01E01 - Title (Japanese).mkv
    Series Name - S01E02 - Title.mkv
    Series Name - S01E02 - Title (Japanese).mkv

Option B — Hide from Jellyfin, just store them nearby

Series Name/
  Season 01/
    .jp/
      Series Name - S01E01 - Title (Japanese).mkv
      Series Name - S01E02 - Title (Japanese).mkv

Option C — Treat them as Extras (visible but not in episode list)

Series Name/
  Season 01/
  extras/japanese/
    S01E01 - Title (Japanese).mkv
    S01E02 - Title (Japanese).mkv

Option D — One file, two audio tracks (cleanest UX if possible)


Handy bulk rename (tag JP files with (Japanese) before extension)

From the season folder containing only the JP copies (or after selecting them via a pattern):

for f in *.mkv; do mv -- "$f" "${f%.mkv} (Japanese).mkv"; done

To move all *(Japanese).mkv into a hidden .jp stash:

mkdir -p .jp
shopt -s nullglob
mv -- *"(Japanese)".mkv .jp/

Pick A if you want a clean single-episode view with a version selector; pick B if you want them truly inert; pick C if you want them accessible but out of the episode list; pick D if you’re willing to remux for the tidiest long-term setup.

M: jellyfin isn't getting rid of old directories that don't exist anymore when i scan. so there are a bunch of double entries, because i moved everything to a single directory. any solutions beyond trashing the library and rebuilding it?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Jellyfin often leaves “phantom” items after moves. Try, in order: per-library Scan → Replace all metadata, run a couple Scheduled Tasks, use the empty-old-path trick to force a purge, and if it still won’t clear, do a targeted SQLite cleanup. citeturn2search13turn1search1turn0search2turn0search6

1) Quick UI fixes (no rebuild)

2) “Empty-old-path” trick (UI, still safe)

If you remember the old folder path(s) you moved from:

  1. Recreate that exact path as an empty folder, add it back as a temporary library (or as an extra folder in the same library).
  2. Let Jellyfin scan it (it now sees those items as empty/missing).
  3. Delete that temporary library/folder from Jellyfin.
    This forces a purge of ghosts tied to the dead path. citeturn0search2

3) Why this happens

After moves, Jellyfin detects the new files but often doesn’t remove the old entries automatically, leaving duplicates (one valid, one “missing”). It’s a long-standing behavior reported by users after intra-library moves. citeturn0search6

4) Targeted SQLite cleanup (precise, fast)

If you’re comfortable touching the DB, this is the cleanest fix. Replace /old/path with the defunct root you moved away from.

Back up and stop:

sudo systemctl stop jellyfin
sudo cp -a /var/lib/jellyfin/data/library.db /var/lib/jellyfin/data/library.db.bak.$(date +%F)

Inspect what will be removed:

sqlite3 /var/lib/jellyfin/data/library.db \
"SELECT Guid, Name, Path FROM TypedBaseItems WHERE Path LIKE '/old/path/%' LIMIT 50;"

Delete stale rows (children first, then items):

sqlite3 /var/lib/jellyfin/data/library.db <<'SQL'
PRAGMA journal_mode=WAL;
DELETE FROM MediaStreams
  WHERE ItemId IN (SELECT Guid FROM TypedBaseItems WHERE Path LIKE '/old/path/%');
DELETE FROM Chapters2
  WHERE ItemId IN (SELECT Guid FROM TypedBaseItems WHERE Path LIKE '/old/path/%');
DELETE FROM UserDatas
  WHERE ItemId IN (SELECT Guid FROM TypedBaseItems WHERE Path LIKE '/old/path/%');
DELETE FROM TypedBaseItems WHERE Path LIKE '/old/path/%';
VACUUM;
SQL

Restart:

sudo systemctl start jellyfin

Notes:

5) Preventative tips

If you want, I’ll tailor the SQLite one-liner for your exact old path(s) and Jellyfin install location.

[Attachment reference unresolved: file-system]

[Attachment reference unresolved: file-scanner]

[Attachment reference unresolved: file-paths]

[Attachment reference unresolved: file-only]

[Attachment reference unresolved: file-deleting]

M: okay, give my a cli that replaces all instances of 's01e' with 's02e', please

G: [Non-text content: thoughts; content_type=thoughts]

M: by which i mean, in the filename only.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Here are safe, filename-only renamers for Fedora (Bash). They won’t touch file contents.

Current directory only (space-safe)

shopt -s nullglob
for f in *s01e*; do
  [[ -f "$f" ]] || continue
  mv -n -- "$f" "${f//s01e/s02e}"
done

Dry run (prints what would happen)

shopt -s nullglob
for f in *s01e*; do
  [[ -f "$f" ]] || continue
  printf 'mv -- %q %q\n' "$f" "${f//s01e/s02e}"
done

Recursive (all subfolders, filename part only)

find . -type f -name '*s01e*' -print0 |
while IFS= read -r -d '' f; do
  dir=${f%/*}
  base=${f##*/}
  new=${base//s01e/s02e}
  [[ $base == "$new" ]] && continue
  mv -n -- "$f" "$dir/$new"
done

Notes:

M: and this is a command line set?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Yes—those are Bash commands you can paste into a Fedora terminal.

One-liners (current directory)

Dry run:

shopt -s nullglob; for f in *s01e*; do [[ -f "$f" ]] || continue; printf 'mv -- %q %q\n' "$f" "${f//s01e/s02e}"; done

Do it:

shopt -s nullglob; for f in *s01e*; do [[ -f "$f" ]] || continue; mv -n -- "$f" "${f//s01e/s02e}"; done

Recursive one-liner

find . -type f -name '*s01e*' -print0 | while IFS= read -r -d '' f; do d=${f%/*}; b=${f##*/}; n=${b//s01e/s02e}; [[ $b == "$n" ]] || mv -n -- "$f" "$d/$n"; done

Simpler alternative (if rename is available on Fedora)

Dry run:

rename -n s01e s02e *s01e*

Do it:

rename s01e s02e *s01e*

This rename is the util-linux variant on Fedora (literal substring replace, -n for preview).

M: cli to go recursively through a directory, removing every file matching *.nfo

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Here are safe Bash one-liners for Fedora.

Dry run (list what would be removed)

find . -type f -iname '*.nfo' -print

Delete (case-insensitive, recursive)

find . -type f -iname '*.nfo' -delete

Verbose alternative (shows each removal)

find . -type f -iname '*.nfo' -print -delete

Prompt before each delete (safer)

find . -type f -iname '*.nfo' -ok rm -- {} \;

Replace . with a specific root directory if needed, e.g. /media/library.

M: go online and find out how, in jellyfin, i can export the metadata i've used to identify shows that jellyfin's automatic method have been misidentified.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Turn on NFO saver for your TV library, then run a library scan with “Replace all metadata”. Jellyfin will write .nfo files next to your shows/episodes that include the provider IDs and other identification you chose. Those NFOs are your portable “export.” citeturn1view0turn2search9

Do this

  1. Enable NFO export
  1. Force-write NFOs for existing items
  1. Confirm they’re there (Fedora CLI)
find /path/to/TV -type f -iname '*.nfo' | head

What’s in the export

Tips that help with mis-IDs (optional)

Alternative/backup

If NFOs don’t appear

That will give you a clean, file-based export of the exact IDs and metadata you used to correct Jellyfin’s matches.

[Attachment reference unresolved: file-does]

[Attachment reference unresolved: file-info]

M: is there a quick way to get from a jellyfin entry to its entry in, say, imdb

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: There isn’t a built-in “Open on IMDb” button in the default Jellyfin web UI. Fastest paths are: (1) open Edit metadata for the item, copy the IMDb ID and paste it after https://www.imdb.com/title/, or (2) grab the IMDb ID via the Jellyfin API with a one-liner. Jellyfin uses TMDb/TVDb (not IMDb) for metadata, but it stores IMDb IDs and can read them from NFOs. citeturn4search12turn2search22turn2search12

Quick ways

A) From the UI (no plugins)

  1. Open the movie/show → ⋯ MoreEdit metadata → copy the IMDb ID shown in Provider IDs.
  2. Open https://www.imdb.com/title/<that-id>/ in your browser.
    Jellyfin’s docs and forum notes confirm IMDb isn’t a metadata source; IDs are mapped/kept for reference and in NFOs, which is why this works reliably. citeturn4search12turn2search22

B) One-liner (CLI via API)

When you’re on an item’s detail page, copy the id= GUID from the URL, then run:

# Replace SERVER, APIKEY, and ITEMGUID
curl -s "http://SERVER:8096/Items/ITEMGUID?api_key=APIKEY" \
| jq -r '.ProviderIds.Imdb | select(.) | "https://www.imdb.com/title/\(.)/"'

This prints the exact IMDb URL if the item has one. (Jellyfin exposes external IDs under ProviderIds.) citeturn5search12turn5search19

C) If you only have a TMDb ID

Open the TMDb page (from the ID you see in Edit metadata) and follow the IMDb link shown there; Jellyfin relies on TMDb/TVDb mapping rather than IMDb’s API. citeturn4search16turn4search12

D) Optional quality-of-life

Enable NFO saver for the library and refresh metadata; your items will have .nfo files with the same IMDb/TMDb IDs next to the media, so you can grab them from disk later. citeturn2search22

Note: A long-standing feature request asks for a direct IMDb link in the UI; it isn’t standard as of recent builds. citeturn2search3

[Attachment reference unresolved: file-path]

M: okay, help me out here - i think jellyfin is saving nfo files, but not to the media folders. how would i check that, and if it's true, what do i need to do to get it to appear in the appropriate relative folder?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Jellyfin only writes .nfo files next to your media when the library’s Metadata savers → Nfo option is enabled, and you run a Refresh/Scan with “Replace all metadata.” Artwork location is a separate toggle. Check for NFOs with find, verify the server’s internal metadata path, then enable NFO saver and rescan if needed. citeturn0search0turn0search4

How to check where NFOs are going

1) Look for NFOs in your media tree (Fedora CLI)

# change this to your TV root
MEDIA=/path/to/TV
find "$MEDIA" -type f -iname '*.nfo' | head -n 20

If you see files, Jellyfin is writing NFOs into your media folders as intended. If you see nothing, proceed below. Jellyfin’s intended behavior (when enabled) is to write NFOs beside the media. citeturn0search4

2) Check Jellyfin’s internal metadata directory (for artwork/cache)

Open Dashboard → Paths and note the Metadata path (Linux default is /var/lib/jellyfin/metadata). This location is for server-side metadata/artwork, not NFOs—unless you tell Jellyfin to save artwork to media folders, images will accumulate here. citeturn0search10

CLI peek:

sudo find /var/lib/jellyfin/metadata -maxdepth 3 -type f | head -n 20

If NFOs aren’t appearing next to media

A) Enable the NFO saver (per library)

Dashboard → Libraries → (⋮) Manage Library → Metadata → Metadata savers → check “Nfo” → Save. This is the switch that allows writing .nfo files. citeturn0search0

B) Force Jellyfin to actually write them

For existing items, you must kick a refresh that re-writes metadata:

C) (Optional) Put artwork in media folders too

If you also want posters/backdrops alongside the files: Manage Library → Metadata → “Save artwork into media folders.” Otherwise, artwork lives under the internal Metadata path. citeturn0search6

Common snags & fixes

That’s it. Run the find check, flip the NFO saver on, then do a Replace all metadata pass, and the .nfo files will appear alongside your shows/episodes in their relative folders. citeturn0search0turn0search4

[Attachment reference unresolved: file-does]

M: okay, i can't find the metadata info anywhere. ... give me a find command that will find all nfo files, including hidden.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: find already traverses hidden folders; this will catch all .nfo files (including hidden ones) under the current directory.

# All .nfo files, case-insensitive, including hidden dirs/files
find . -type f -iname '*.nfo' -print

Helpful variants:

# Suppress "Permission denied" noise
find . -type f -iname '*.nfo' -print 2>/dev/null

# Start from a specific media root
find "/path/to/media" -type f -iname '*.nfo' -print

# Only files that themselves are hidden (start with a dot)
find . -type f -name '.*.nfo' -print

# Follow symlinks (be careful—can loop)
find -L . -type f -iname '*.nfo' -print

# Sample first 20 results
find . -type f -iname '*.nfo' -print | head -n 20

# Count them
find . -type f -iname '*.nfo' | wc -l

# Where they cluster (count per directory)
find . -type f -iname '*.nfo' -printf '%h\n' | sort | uniq -c | sort -nr | head

If this returns nothing in your media tree, Jellyfin likely isn’t set to write NFOs to media folders or lacks write permission; enable Metadata savers → Nfo and run a Replace all metadata scan, then rerun the find command.

M: no, that's my point - i went to the path and did a search. there's nothing. a lot of images, but i haven't seen a single damn metadata entry. i'm trying to hunt down a particularly shitty piece of data; for some reason, it keeps labeling the episodes wrong, even though it has identified the show correctly.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Your media folders only have artwork because NFO saver isn’t enabled (or it hasn’t been forced to write yet). Turn on Metadata savers → Nfo for the TV library, then run a Scan → Replace all metadata on that library to generate .nfo files next to your episodes. If episodes are still wrong, fix the Display Order (Aired/DVD/Absolute) on that series and/or anchor the series with a provider ID like [tvdbid-12345]. citeturn0search0turn0search10turn3view0

Make Jellyfin write NFOs into your media folders

  1. Server Dashboard → Libraries → ⋮ on your TV library → Manage Library → Metadata

    • Check Metadata savers → Nfo.
    • (Optional) Check Save artwork into media folders if you also want posters/backdrops alongside files. citeturn0search0turn0search6
  2. Force-write NFOs for existing items

    • Library ⋮ → ScanReplace all metadata (or run a “Refresh metadata” with replace). By design, enabling NFO saver doesn’t retro-write until you do this replace pass. citeturn0search10
  3. Verify on disk (Fedora CLI)

find /path/to/TV -type f -iname '*.nfo' -print | head -n 20

If nothing appears, it’s almost always because NFO saver wasn’t enabled or Jellyfin can’t write to the media path (fix permissions for the jellyfin user).

Why the episodes look wrong (and how to fix fast)

Mislabeling usually comes from ordering mismatches (Aired vs DVD vs Absolute) or from provider differences (TMDb vs TVDB). Fix at the series level:

Quick ways to see what Jellyfin thinks (IDs & numbering)

If you don’t have NFOs yet, pull it via API so you can spot the bad mapping:

  1. Grab the series’ GUID (copy id= from its URL in the web UI).
  2. Run (replace SERVER, APIKEY, SERIESGUID):
curl -s "http://SERVER:8096/Items?ParentId=SERIESGUID&Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds,Path,IndexNumber,ParentIndexNumber" \
  -H "X-MediaBrowser-Token: APIKEY" \
| jq -r '.Items[] | [(.ParentIndexNumber|tostring), (.IndexNumber|tostring), .Name, .ProviderIds.Tvdb, .ProviderIds.Tmdb, .Path] | @tsv'

This lists Season, Episode, Title, TVDB/TMDB IDs, and Path for each episode so you can spot where numbering diverges. (The API returns ProviderIds and index numbers; auth typically uses X-MediaBrowser-Token.) citeturn2search7

If one season is consistently off

Sanity checklist

If you want, tell me the series title and whether you want Aired/DVD/Absolute, and I’ll give you the exact rename/refresh sequence that will line it up cleanly.

[Attachment reference unresolved: file-paths]

M: check online and see if there's any way to get anisearch to show mature titles in jellyfin. i've got some nsfw titles, and they exist on anisearch but won't show up in jellyfin.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Short answer: there isn’t a reliable switch to make the AniSearch plugin show 18+ (NSFW) titles in Jellyfin right now. The official AniSearch plugin for Jellyfin currently scrapes the AniSearch website (not a logged-in API), and mature pages on AniSearch are typically behind an age gate. Because the plugin doesn’t offer a login/age-confirmation flow, those entries often don’t come back in Jellyfin searches. You can see from the plugin repo that it’s a simple site scraper, and there’s even an open request asking maintainers to move to AniSearch’s API “to avoid getting banned,” which hints at the brittleness of scraping here. citeturn8view0turn17search2

What you can do instead (and what actually works)

  1. Use Shoko + Shokofin (recommended for anime libraries, including adult/hentai).
    Shoko ingests metadata directly from AniDB (which includes adult titles) and feeds it to Jellyfin via the Shokofin plugin. Shoko’s docs explicitly say adult content is supported as long as AniDB has an entry. After installing Shokofin, connect it to your Shoko Server and let it provide metadata for that library. (Important: when using Shokofin, don’t enable NFO saving in that library; Shoko warns it will break overrides.) citeturn16search5turn16search0turn19search8

  2. Prioritize other anime providers over AniSearch.
    In your Anime library’s Metadata fetchers order, put AniDB and/or AniList above AniSearch so they’re tried first. The Jellyfin docs list these plugins and the forum guidance often recommends using multiple providers with a sensible priority. citeturn12search4turn18search10

  3. For anime movies or shows that exist on TMDb:
    Make sure your TMDb provider is allowed to return adult results (there’s an “Include adult content” toggle in TMDb provider settings per library). This won’t fix AniSearch, but it will help when TMDb actually has the title. citeturn12search13

  4. Lock problem items by ID (where possible).
    Jellyfin can lock matches by putting an external ID in the folder name, e.g.
    Series Name (2018) [tmdbid-123456] or Series Name (2018) [tvdbid-654321].
    This is helpful when the title exists on TMDb/TVDB. (Jellyfin’s naming docs show the external-ID pattern.) citeturn19search0

Bottom line

If you want, I can jot down quick step-by-step for installing Shokofin and flipping your Anime library over to it.

[Attachment reference unresolved: file-title]

[Attachment reference unresolved: file-names]

M: what are the names you can use for the external ids - like the tmdbid-123456 example you gave. what servers can function that way? and what are their prefixes? then, look around online and see if anidb has a workaround in the same way; i don't have much hope but let's be thorough.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Jellyfin’s filename/folder ID tags work with IMDb, TMDb, and TVDb. Use these exact prefixes (brackets required):

Supported external-ID tags

About AniDB / AniList

Copy-paste examples

Movies/
  Perfect Blue (1997) [tmdbid-10494] [imdbid-tt0156887]/ 
    Perfect Blue (1997) [tmdbid-10494] [imdbid-tt0156887].mkv

TV Shows/
  Mononoke (2007) [tvdbid-80379] [tmdbid-12589]/
    Season 01/
      Mononoke - S01E01.mkv

These patterns are exactly what Jellyfin’s docs describe and will anchor matching reliably. citeturn0search3turn2search10

[Attachment reference unresolved: file-names]

[Attachment reference unresolved: file-naming]

M: please look and see if any of the servers i'm using or can use - anidb, anilist, anisearch, or kitsu - have nfo downloads that i could use to do things manually.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Short answer (as of Aug 30, 2025): none of AniDB, AniList, AniSearch, or Kitsu offer a native “download NFO” feature. They expose data via APIs or web pages, but NFO is a Kodi/Emby/Jellyfin format you generate yourself (or with a tool). citeturn5search4turn5search0turn6search4

What each source actually provides

Practical ways to get NFOs anyway (these work)

  1. tinyMediaManager (tMM) – scrape with AniDB (tMM supports it) and write NFOs for Jellyfin/Kodi.
    • tMM advertises NFO import/export and an AniDB scraper. citeturn0search20turn8search1turn8search14
  2. Akiba (CLI) – pulls AniDB and writes stub tvshow/episode NFOs tailored for Jellyfin. Great for absolute numbering. citeturn4view0
  3. Roll your own – Jellyfin will read valid local NFOs you place next to media (good for manual fixes). citeturn0search4
    (There are other niche generators—e.g., Dantalian uses Bangumi to create NFOs if that DB serves your titles.) citeturn6search16

Minimal “pin it by ID” NFO templates (copy/paste)

Place these next to your media; they help Jellyfin lock identification.

tvshow.nfo

<tvshow>
  <title>Series Name</title>
  <uniqueid type="tmdb" default="true">12345</uniqueid>
  <uniqueid type="imdb">tt12345678</uniqueid>
  <uniqueid type="tvdb">67890</uniqueid>
</tvshow>

EpisodeFileName.nfo

<episodedetails>
  <title>Episode Title</title>
  <season>1</season>
  <episode>1</episode>
</episodedetails>

If you want, I can give you a 2–3 step tMM or Akiba workflow tailored to your library (which source to prioritize, where to drop the NFOs, and how to refresh Jellyfin).

[Attachment reference unresolved: file-examples]

[Attachment reference unresolved: file-creator]

[Attachment reference unresolved: file-where]

M: which is considered simpler to use - tmm or akiba

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: For most people, tinyMediaManager (tMM) is simpler: install → pick scrapers → hit Scrape → it writes Jellyfin-compatible NFOs (GUI or CLI). Akiba is great if you specifically want a CLI that pulls from AniDB and outputs stub NFOs that fix absolute numbering—but it needs more setup (AniDB client IDs/tokens, first-run config, build/install). citeturn5view0turn4view0turn1view0

What “simple” looks like in practice

tinyMediaManager — easiest end-to-end

Verdict: Minimum moving parts, friendliest UX, and you can still automate later with the CLI. citeturn5view0turn4view0

Akiba — simplest if you want pure CLI + AniDB stubs

Verdict: Lean and fast once configured, but setup is more technical than tMM (registering AniDB clients, tokens, first-run config, building the binary). citeturn1view0


Recommendation

Third option (for anime-heavy/NSFW libraries)

If you’d rather skip NFOs entirely and let a backend handle anime metadata (including adult titles) for Jellyfin, run Shoko Server + the Shokofin plugin. It feeds Jellyfin directly from AniDB and is documented to handle adult entries when AniDB has them. Simpler day-to-day after one-time setup. citeturn3search0turn3search1turn3search4

M: go online and find me how to install tmm on fedora, please.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Here’s the official, simplest way to install tinyMediaManager (tMM) on Fedora, straight from the docs.

Method 1 — Portable Linux tarball (recommended)

tMM v4+ ships with its own Java runtime, so you don’t need to install Java. citeturn2view0

  1. Download the Linux package
    Go to the download page and grab the Linux (amd64) Universal Package (.tar.xz). citeturn3search1

  2. Extract & run

# pick where you want it to live
mkdir -p "$HOME/apps" && cd "$HOME/apps"
# adjust the filename to the one you downloaded
tar -xJf ~/Downloads/tinyMediaManager-*-linux-amd64.tar.xz
cd tinyMediaManager
# launch
./tinyMediaManager

Linux install is literally “extract the archive and run the binary.” citeturn2view0

  1. (Optional) Create a desktop launcher
    Run this from inside the extracted tinyMediaManager folder so the Icon/Exec paths resolve correctly:
cat <<'EOM' >~/.local/share/applications/tinyMediaManager.desktop
[Desktop Entry]
Type=Application
Terminal=false
Name=tinyMediaManager
Icon=%k/tmm.png
Exec=%k/tinyMediaManager
EOM

On the official page they provide a similar snippet; it lets tMM show up in your app menu. citeturn2view0

  1. (If you’re missing MediaInfo)
    If tMM complains about MediaInfo, install it (used to read file technical data):
sudo dnf install mediainfo libmediainfo

Or grab the official builds from MediaArea if your repo version causes issues. citeturn2view0

Method 2 — Docker/Podman (optional)

There’s an official Docker image. It’s marked “experimental” and running GUI apps in containers on Fedora/Wayland needs extra setup, so most users stick to the tarball. If you want it anyway, see the image page for details. citeturn3search2

Headless / CLI usage (after installing)

tMM has a built-in command-line interface you can run from the extracted folder, useful for automations (scan/scrape/write NFOs, etc.). Docs and examples live here: citeturn0search3

# show help
./tinyMediaManager --help

# example (TV): scan, scrape new, write NFOs
./tinyMediaManager tvshow -u -n -w

If you want, tell me where you put it (e.g., /home/you/apps/tinyMediaManager) and what you want to automate, and I’ll hand you ready-to-paste CLI lines.

M: would've been nice if you'd told me it was a paid program. is it the same with the other choice, akiba?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: You’re right—sorry I didn’t flag the licensing earlier.

If you want a zero-cost path today: use tMM Free for GUI-driven NFOs or Akiba for AniDB-accurate stub NFOs, and Jellyfin will read either.

M: topic change: docker pull shokoanime/server:latest what did i just do and what's my next step? check online if you need to.

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: docker pull shokoanime/server:latest downloaded the Shoko Server image to your Fedora box. Next steps: run the container (map port 8111, mount a config folder and your anime folder), open the Web UI, finish setup, then install the Shokofin plugin in Jellyfin and point it at Shoko. citeturn0search6turn0search2

1) Run Shoko Server (Docker)

Pick where to store Shoko’s config, then launch:

# create a config dir for persistent data
mkdir -p "$HOME/.shoko"

# run shoko server
docker run -d --name shokoserver --restart unless-stopped \
  -p 8111:8111 \
  -v "$HOME/.shoko:/home/shoko/.shoko" \
  -v "/path/to/anime:/mnt/anime" \
  -v "/path/to/import:/mnt/import" \
  -e PUID=$(id -u) -e PGID=$(id -g) \
  ghcr.io/shokoanime/server:latest

Docs confirm the port and a standard docker run similar to the above. citeturn0search2turn0search8

2) Initial Shoko setup (Web UI)

  1. Visit http://localhost:8111.
  2. Create the admin user.
  3. In Settings → Import, add your mounted paths:
  4. In Settings → Metadata Sites/AniDB, add your AniDB credentials/API keys if you have them.
  5. Run a library scan; Shoko will catalog and fetch metadata.
    Shoko’s docs show the Web UI on 8111 and the general settings flow. citeturn0search2turn0search9

3) Connect Jellyfin via Shokofin

You have two install options for the plugin:

A) Plugin Catalog (recommended)

B) Manual

Configure the plugin

4) Point your Jellyfin Anime library at Shoko

5) Optional niceties

That’s the whole path from pulling the image to seeing anime in Jellyfin via Shokofin, with 8111 exposed and persistent config under ~/.shoko.

M: do i have to do all the directory stuff for the first run?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Short version: No—you don’t have to mount your media/import folders on the very first run. For a clean start, just map a persistent config folder and the port; you can add the library mounts later by recreating the container with the same config volume.

Minimal first run (keeps your setup/data)

mkdir -p "$HOME/.shoko"
docker run -d --name shokoserver --restart unless-stopped \
  -p 8111:8111 \
  -v "$HOME/.shoko:/home/shoko/.shoko:Z" \
  -e PUID=$(id -u) -e PGID=$(id -g) \
  ghcr.io/shokoanime/server:latest

Add your library paths later

When you’re ready to import, recreate the container with media mounts (config is preserved):

docker stop shokoserver && docker rm shokoserver
docker run -d --name shokoserver --restart unless-stopped \
  -p 8111:8111 \
  -v "$HOME/.shoko:/home/shoko/.shoko:Z" \
  -v "/path/to/anime:/mnt/anime:Z" \
  -v "/path/to/import:/mnt/import:Z" \
  -e PUID=$(id -u) -e PGID=$(id -g) \
  ghcr.io/shokoanime/server:latest

Then in Shoko’s Web UI: Settings → Import → add /mnt/anime (and /mnt/import if used) and run a scan.

Notes

This gives you a safe, low-friction first boot and a clean path to wire in your library afterward.

M: alright, so for this trial run, i'm trying to set up an 'import folder', but i don't know exactly what it is. i just need it to work safely. there are limited options. can you advise

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: In Shoko, an import folder is just a path Shoko scans/uses. For a safe trial, add one import folder that points to your mounted library (e.g., /mnt/anime) and set Drop Type = Destination (or Both). Disable moving/renaming so nothing gets touched, then run a scan. You can add a separate Source “drop” folder later for auto-moves. citeturn0search1turn0search4

What the choices mean (pick one now)

Safe trial setup (no file moves)

In Shoko Web UI → Settings → Import → Add Import Folder:

Then in Settings → (General/Server) or Import options:

Finally:

Extra notes (handy later)

That’s the safest “it works but touches nothing” configuration for a first run.

M: like i said, there are limited choices right now... how about /mnt/?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Yes—you can use /mnt as the import folder for this trial. Set it to Drop Type = Destination (or Both), and turn off all moving/renaming so nothing is touched. Shoko will just scan everything under /mnt recursively and build metadata.

Safe trial setup

  1. In Shoko → Settings → Import → Add Import Folder
    • Folder Path: /mnt
    • Name: mnt
    • Drop Type: Destination (or Both if you might rename in-place later)
  2. In Shoko import options, disable:
    • Move on Import
    • Move after Rename
    • Any auto-delete/cleanup options
  3. Run a Scan/Hash. Shoko will index files under /mnt without moving or renaming anything.

Why this is safe

Fedora/Docker notes

Later (when you’re ready)

M: what is the mnt directory in linux, anyway

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: /mnt is just a conventional, empty directory reserved for temporary mount points. It isn’t magical—admins create subfolders under it and mount filesystems there. If nothing is mounted, it’s just a normal folder.

What /mnt is

/mnt vs /media

Common CLI you’ll actually use

# See what is mounted anywhere (tree view)
findmnt

# See what (if anything) is mounted under /mnt
findmnt -R /mnt

# Example: mount a USB partition
sudo mkdir -p /mnt/usb
sudo mount /dev/sdb1 /mnt/usb
# ...use files...
sudo umount /mnt/usb

Persistent mount (fstab) example:

/dev/sdb1  /mnt/usb  ext4  defaults,noatime  0  2

Network share examples:

# NFS
server:/export/anime  /mnt/anime  nfs   vers=4.2,_netdev  0  0
# SMB/CIFS
//server/share       /mnt/share  cifs  credentials=/root/.smb,iocharset=utf8,_netdev  0  0

Fedora + Docker/Podman notes (relevant to your Shoko trial)

Good practices

That’s all you need to confidently use /mnt as a safe, conventional mount point for your trial and future setups.

M: okay, so here's the current situation: my stuff is on an external drive, "/run/media/david/Expansion Hole/series-animated/". i want it to stay there. first, how do i shut down shoko. then, knowing that's the directory/target/whatever they're calling it, what should i do/how do i launch shoko correctly?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: Got you. Here’s the clean, safe way to switch Shoko over to your external drive at
/run/media/david/Expansion Hole/series-animated/ and keep your data where it is.

1) Stop (and remove) any existing Shoko container

docker ps
docker stop shokoserver 2>/dev/null || true
docker rm   shokoserver 2>/dev/null || true

2) Launch Shoko with your external drive mounted read-only

Read-only is safest for a trial. SELinux on Fedora needs :Z. Quotes handle the space in “Expansion Hole”.

mkdir -p "$HOME/.shoko"

docker run -d --name shokoserver --restart unless-stopped \
  -p 8111:8111 \
  -v "$HOME/.shoko:/home/shoko/.shoko:Z" \
  -v "/run/media/david/Expansion Hole/series-animated:/mnt/anime:ro,Z" \
  -e PUID=$(id -u) -e PGID=$(id -g) \
  ghcr.io/shokoanime/server:latest

Notes

3) Verify the mount from inside the container

docker exec -it shokoserver bash -lc 'ls -la /mnt/anime | head'

If you don’t see your shows, the bind path is wrong or SELinux labeling is missing.

4) Point Shoko at the folder (no moves, no renames)

In the Shoko web UI at http://localhost:8111:

5) Later, if you decide you want writes

That’s it. You’ve shut Shoko down, relaunched with a safe read-only bind to your external drive, and you can index everything without moving a single file.