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)); donen=1 → starts the counter at 1.for f in *; do ...; done → loops over all files in the
directory.mv -- "$f" "s01e$(printf "%02d" $n) $f" → renames the
file by prefixing s01e## , where ## is
zero-padded to 2 digits.n=$((n+1)) → increments the counter each time.-- after mv makes sure weird filenames
(like ones starting with -) are handled safely.mv -- "$f" "s01e$(printf "%02d" $n)$f".mp4):
n=1; for f in *.mp4; do mv -- "$f" "s01e$(printf "%02d" $n) $f"; n=$((n+1)); doneDo 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)); donels -1v → lists files one per line,
using version sort (-v), so 01,
02, 10 sort in true numeric order instead of
01, 10, 2.s01e## with
zero-padding.⚠️ 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)); doneThat 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)); doneecho mv -- ... → prints the mv command
instead of running it.mv -- "01 Episode Title.mp4" "s01e01 01 Episode Title.mp4"
mv -- "02 Episode Title.mp4" "s01e02 02 Episode Title.mp4"
...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)); doneDo 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.
citeturn0search10turn0search6turn0search4
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
S00E## inside the season it aired with,
based on the provider’s mapping. citeturn0search6S00E##. That’s how Jellyfin fetches
the right metadata. citeturn0search16Movie Name (Year).ext, optionally link it via a
Collection with the series. citeturn0search4Series Name - S00E07E08 - OVA Title.mkv (works like any
multi-ep file). citeturn0search10ANIME 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
(Japanese) (or
- Japanese, [JP], (Eng Dub),
etc.).Series Name/
Season 01/
.jp/
Series Name - S01E01 - Title (Japanese).mkv
Series Name - S01E02 - Title (Japanese).mkv
.jp), and make
sure the library setting Ignore hidden files and
folders is on.Series Name/
Season 01/
extras/japanese/
S01E01 - Title (Japanese).mkv
S01E02 - Title (Japanese).mkv
(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"; doneTo 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. citeturn2search13turn1search1turn0search2turn0search6
If you remember the old folder path(s) you moved from:
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. citeturn0search6
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;
SQLRestart:
sudo systemctl start jellyfinNotes:
TypedBaseItems is where media rows and their
Path live; related tables like MediaStreams,
Chapters2, and UserDatas reference those
items, so delete in that order. Paths and table names are documented by
multiple community write-ups and schema dumps. citeturn3search6DELETE FROM TypedBaseItems WHERE Path LIKE '/old/path/%';
and Jellyfin rebuilds streams on next scan. Use the fuller version above
if you want a tidy DB. citeturn0search15If 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.
shopt -s nullglob
for f in *s01e*; do
[[ -f "$f" ]] || continue
mv -n -- "$f" "${f//s01e/s02e}"
doneshopt -s nullglob
for f in *s01e*; do
[[ -f "$f" ]] || continue
printf 'mv -- %q %q\n' "$f" "${f//s01e/s02e}"
donefind . -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"
doneNotes:
-n prevents overwriting if a target name already
exists; switch to -i for prompts if preferred.s01e; for other cases you
can run additional passes (e.g., S01E → S02E) if
needed.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.
Dry run:
shopt -s nullglob; for f in *s01e*; do [[ -f "$f" ]] || continue; printf 'mv -- %q %q\n' "$f" "${f//s01e/s02e}"; doneDo it:
shopt -s nullglob; for f in *s01e*; do [[ -f "$f" ]] || continue; mv -n -- "$f" "${f//s01e/s02e}"; donefind . -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"; donerename 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.
find . -type f -iname '*.nfo' -printfind . -type f -iname '*.nfo' -deletefind . -type f -iname '*.nfo' -print -deletefind . -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.”
citeturn1view0turn2search9
tvdbid,
tmdbid, imdbid) when NFO saver is enabled.
citeturn1view0find /path/to/TV -type f -iname '*.nfo' | headtvdbid, tmdbid, imdbid, etc.),
plus episode/season info and special-placement tags. These are exactly
what fixes misidentifications and can be re-imported later.
citeturn1view0Series Name [tvdbid-123456])—useful before the export step
or if you ever rebuild. citeturn0search5That 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.
citeturn4search12turn2search22turn2search12
https://www.imdb.com/title/<that-id>/ in
your browser.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.)
citeturn5search12turn5search19
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. citeturn4search16turn4search12
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.
citeturn2search22
Note: A long-standing feature request asks for a direct IMDb link in the UI; it isn’t standard as of recent builds. citeturn2search3
[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. citeturn0search0turn0search4
# change this to your TV root
MEDIA=/path/to/TV
find "$MEDIA" -type f -iname '*.nfo' | head -n 20If 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. citeturn0search4
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.
citeturn0search10
CLI peek:
sudo find /var/lib/jellyfin/metadata -maxdepth 3 -type f | head -n 20Dashboard → Libraries → (⋮) Manage Library → Metadata →
Metadata savers → check “Nfo” → Save. This is the switch that
allows writing .nfo files. citeturn0search0
For existing items, you must kick a refresh that re-writes metadata:
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. citeturn0search6
Permissions: The Jellyfin service user must be able to write to your media directories.
# See the service account name
id jellyfin
# Example: grant group write (pick a media group you and Jellyfin share)
sudo chgrp -R jellyfin /path/to/TV
sudo chmod -R g+rwX /path/to/TV
# or use ACLs:
sudo setfacl -R -m u:jellyfin:rwx /path/to/TVWithout write perms, the scan completes but no NFOs are created.
Expectations: Enabling “Nfo” does not retro-write by itself; you must run a Replace all metadata pass to create NFOs for existing entries. citeturn0search4
Artwork vs NFO: Artwork location is controlled by Save artwork into media folders; NFOs are controlled by Nfo in Metadata savers. They’re separate knobs. citeturn0search6turn0search0
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. citeturn0search0turn0search4
[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' -printHelpful 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 | headIf 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].
citeturn0search0turn0search10turn3view0
Server Dashboard → Libraries → ⋮ on your TV library → Manage Library → Metadata
Force-write NFOs for existing items
Verify on disk (Fedora CLI)
find /path/to/TV -type f -iname '*.nfo' -print | head -n 20If 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).
Mislabeling usually comes from ordering mismatches (Aired vs DVD vs Absolute) or from provider differences (TMDb vs TVDB). Fix at the series level:
Open the series → ⋮ Edit metadata → set Display order to Aired / DVD / Absolute as appropriate, then refresh that series/season. This setting determines how episodes map to your SxxExx filenames. citeturn0search2turn0search17
If specials are interleaving weirdly, toggle Dashboard → Library → Display → “Display specials within their series they aired in.” This remaps S00 items into seasons when the provider has air placement data. citeturn3view0
Anchor the series to the correct metadata record
by adding an external ID to the series folder name, e.g.:
Series Name (2015) [tvdbid-123456] or
[tmdbid-98765]. Jellyfin honors these tags and will match
accordingly. citeturn3view0
If you don’t have NFOs yet, pull it via API so you can spot the bad mapping:
id= from its URL in the web
UI).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.)
citeturn2search7
S01E01E02 if one file contains two). Supported patterns and
special handling are documented in the TV naming guide.
citeturn3view0.nfo will ever appear).
citeturn0search0turn0search10[tvdbid-…] or [tmdbid-…] to the series folder.
citeturn3view0If 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. citeturn8view0turn17search2
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.)
citeturn16search5turn16search0turn19search8
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. citeturn12search4turn18search10
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. citeturn12search13
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.) citeturn19search0
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):
[imdbid-tt12345678][tmdbid-12345][tvdbid-67890] (TV shows only)[imdbid-tt########], [tmdbid-#####],
[tvdbid-#####]. Multiple IDs may be included, e.g.
Show (2015) [imdbid-tt01234567] [tmdbid-12345].
citeturn0search0turn0search3[anidbid-#####] in names; an open plugin issue asks for
this and related docs, but it isn’t implemented.
citeturn3search14Movies/
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. citeturn0search3turn2search10
[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). citeturn5search4turn5search0turn6search4
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). citeturn5view0turn4view0turn1view0
tinyMediaManager tvshow -u -n -w
# -u scan, -n scrape new, -w rewrite NFOsVerdict: Minimum moving parts, friendliest UX, and you can still automate later with the CLI. citeturn5view0turn4view0
bun install, copy binary).write-nfo;
optional poster saving/overwrite.# one-time
akiba configure --anidb-http-client examplehttpclient/1
# per library
akiba write-nfoVerdict: Lean and fast once configured, but setup is more technical than tMM (registering AniDB clients, tokens, first-run config, building the binary). citeturn1view0
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. citeturn3search0turn3search1turn3search4
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.
tMM v4+ ships with its own Java runtime, so you don’t need to install Java. citeturn2view0
Download the Linux package
Go to the download page and grab the Linux (amd64) Universal
Package (.tar.xz). citeturn3search1
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
./tinyMediaManagerLinux install is literally “extract the archive and run the binary.” citeturn2view0
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
EOMOn the official page they provide a similar snippet; it lets tMM show up in your app menu. citeturn2view0
sudo dnf install mediainfo libmediainfoOr grab the official builds from MediaArea if your repo version causes issues. citeturn2view0
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. citeturn3search2
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: citeturn0search3
# show help
./tinyMediaManager --help
# example (TV): scan, scrape new, write NFOs
./tinyMediaManager tvshow -u -n -wIf 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.
tinyMediaManager (tMM): There’s a FREE edition and a PRO edition. The free edition already supports importing/exporting NFOs, renaming, CLI, etc. PRO is a paid subscription (~12 € / year) that unlocks extra features; you don’t need PRO just to write Jellyfin-compatible NFOs. citeturn2view0turn0search0
Akiba: Free & open-source (MIT-licensed). It’s a CLI specifically for anime that can rename and export stub NFOs from AniDB mappings. No cost, you just install/build it from the repo or use its releases. citeturn1view0
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. citeturn0search6turn0search2
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/home/shoko/.shoko is the container’s persistent data
location; mapping it to ~/.shoko keeps your DB across
restarts./mnt/anime and any
“staging/import” folder at /mnt/import.sudo firewall-cmd --add-port=8111/tcp --permanent && sudo firewall-cmd --reloadDocs confirm the port and a standard docker run similar
to the above. citeturn0search2turn0search8
/mnt/anime/mnt/import (optional)You have two install options for the plugin:
A) Plugin Catalog (recommended)
Shokofin Stablehttps://raw.githubusercontent.com/ShokoAnime/Shokofin/metadata/stable/manifest.jsonB) Manual
shoko_*.zip from releases, extract
to plugins/Shokofin, and restart. citeturn0search1Configure the plugin
http://localhost:8111 (or the Docker host IP if Jellyfin is
elsewhere)docker run
later if you prefer; the same ports/volumes apply.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.
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:Z is important on Fedora (SELinux) so the
container can write to the mapped directory.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:latestThen in Shoko’s Web UI: Settings → Import → add
/mnt/anime (and /mnt/import if used) and run a
scan.
~/.shoko.sudo firewall-cmd --add-port=8111/tcp --permanent && sudo firewall-cmd --reloadThis 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. citeturn0search1turn0search4
In Shoko Web UI → Settings → Import → Add Import Folder:
/mnt/anime (your Docker
bind-mount)animeThen in Settings → (General/Server) or Import options:
Finally:
/mnt/import) with Drop Type = Source, then
enable Move on Import so Source → Destination moves
happen. citeturn0search4That’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.
/mntmnt/mnt without moving or renaming anything./mnt just means “scan everything underneath.” If
you later split to /mnt/anime and /mnt/import,
you can change/add folders without losing the DB./mnt: your
docker run volume should include
:Z for SELinux, e.g.
-v "/path/on/host:/mnt:Z".docker exec -it shokoserver bash -lc 'ls -la /mnt | head'/mnt/anime (Destination)
and optionally add /mnt/import (Source), then enable
Move on Import if you want automated sorting.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.
/mnt is/mnt/usb, /mnt/share,
/mnt/anime)./mnt vs /media/media and (on modern desktops)
/run/media/$USER/<label> are where
desktop auto-mounters put removable drives./mnt is for
manual/admin mounts. Totally fine to use it as a
convenient anchor, especially on servers and in containers.# 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/usbPersistent 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
/mnt is just a directory in
the container filesystem. You typically
bind-mount a host path to it:
# Host → Container (SELinux-aware)
-v "/host/path/anime:/mnt/anime:Z":Z on your -v bind mounts or
pre-label the host path with chcon to avoid permission
denials./mnt (or
/mnt/anime) as an Import Folder with
Drop Type = Destination and moving/renaming
disabled, as you’re doing./mnt/anime) rather than dumping files directly in
/mnt.findmnt or mount to verify where
things really are before housekeeping or rsync work.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.
docker ps
docker stop shokoserver 2>/dev/null || true
docker rm shokoserver 2>/dev/null || trueRead-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:latestNotes
:ro,Z → mount read-only, label for SELinux.PUID/PGID match your user so the container can read
user-mounted media.sudo firewall-cmd --add-port=8111/tcp --permanent
sudo firewall-cmd --reloaddocker 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.
In the Shoko web UI at http://localhost:8111:
/mnt/animeanimero,):
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 "/run/media/david/Expansion Hole/series-animated:/mnt/anime:Z" \
-e PUID=$(id -u) -e PGID=$(id -g) \
ghcr.io/shokoanime/server:latestThat’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.