Generate Jellyfin/Emby-compatible metadata from your Stash database. Create NFO files, rename/organize video files, and export performer images - all without relying on external scrapers.
Added explicit defaults to all settings - Dry Run Mode now correctly defaults to ON for new installations
v1.2.0 Changes:
Fixed: XML escaping for special characters in titles (ampersands, quotes, etc.) - Issue #9
Fixed: Emby actor folder structure now correctly uses People/Actor Name/ instead of People/A/Actor Name/ - Issue #11
Added: Comprehensive unit tests
Breaking Change - Settings Migration:
Settings have moved from settings.ini to Stash’s native plugin settings UI. After updating:
Go to Settings → Plugins → mcMetadata
Re-enter your settings in the GUI
Delete the old settings.ini file (no longer used)
The new settings UI is easier to use and doesn’t require restarting Stash when you make changes.
Reminder - Dry Run Mode:
Dry Run Mode is ON by default. Run “Bulk Update Scenes” first to preview changes in Settings → Logs (Debug level), then disable Dry Run Mode to execute for real.
I like what this is doing. Anything that can make my library “portable” with metadata stored next to the files is a good thing in my opinion. I have a few comments.
Can this be configured to run for targeted directories? I have a large library and I get nervous about doing a large update which will move and rename all files at once. To test this plugin I had to setup a new stash instance and only loaded in one directory.
Dry run did not default to on for me with new install like your changelog says it should.
I do not see it mentioned but this copies the cover to be stored next to the nfo file. This is good but it didn’t work for me. It created a jpg file but the file was corrupted and wouldn’t open. I didn’t see any messages in log so I’m not sure what happened.
Interesting. The image issue I’ll have to look into. Target directory, no you can’t do that yet but good idea. The DRY RUN thing I’d be curious - was DRY RUN actually not enabled, or just appeared to be in the Plugin Settings? Because Stash will not allow you to provide a default value that reflects properly in the UI, so even if that Boolean is set to “true” by default in the code, the Plugin settings will show it as “false” or off until you actually toggle it on via the UI, then it will be “synced” to the correct value. A weird little shortcoming of Stash’s that is sometimes confusing. I will investigate all of these bugs against my own Stash though. Thanks for the report!
I wasn’t aware of that quirk in the UI with default values so it may have been assigned correctly as you describe. I didn’t run it before setting the toggle in the UI so I don’t know if that was the case.
Regarding images, I wonder if it’s because it’s missing API key in string? I set log to debug and I see messages like the one below, there should be string after apikey= shouldn’t there?
I wasn’t seeing the “No module named ‘stashapi’” error you mentioned but since I spun up a new instance for testing this and I use docker I realized I’m probably missing this. After install, the apikey= is still blank.
Issue 1 (Dry run default): This is a Stash UI quirk - boolean settings show as “off” visually until you toggle them, even though the code defaults to true. Once you toggle the setting once, it persists correctly.
Issues 2 & 3 (Corrupted images / blank API key): These are related. The download_image function had no error handling - if the download failed (e.g., auth error from missing API key), it would save whatever it received (often an HTML error page) as the image file, with no logging.
The fix:
Downloads to a temp file first, validates before saving
Checks HTTP status code and Content-Type header
Validates the file is actually a JPEG/PNG/WebP by checking magic bytes
Adds comprehensive error logging so you can see what went wrong
Sanitizes API key from logs for security
Once merged, if you still see image download failures, you’ll now get clear error messages in the Stash logs explaining why.
Looks a bit better. I’m still having issues with api key but for now I’ve just disabled security credentials to my test instance so that an api key isn’t needed.
Some searching online and this appears to be because /tmp and /data are different filesystems. The /data path is default path for docker to point to your library files. Looks like shutil.move() might be better option?
I’m also experiencing the corrupted image issue. Current version 1.2.3-f22a4e3 on Stash v0.30.1 (build b23b0267).
This is what I’m seeing in the logs. Only bit changed its the path and filename before the hyphen. The outputted file’s checksums, when checked on CX Explorer on Android, all show Error.
[16:59:53] [DEBUG] Downloading image from http://127.0.0.1:9999/scene/9572/screenshot?t=1770674391&apikey=***
[16:59:53] [DEBUG] Saved image to /data/my/path/scene_file_,name-poster.jpg
Rename option is disabled, Scene update hook enabled.
I see you updated this recently. The nfo and image creation is working great for me now, thank you. If I may make some more minor suggestions.
Would it be possible to have more control over the triggers on hook? I often make make updates when adding a new scene, and usually not all at once. It would be good to have the option to only write and rename the files when the scene is marked as organized instead of after every save.
Any chance of conditional tests in the rename so that text is only included if data exists for a field? For example, if you used curly brackets, {$ReleaseDate - }$Title would only include the “ - “ if a release date is set on the scene. If no release date on the scene then it only writes $Title.
This is minor but any chance of control over what fields get written to the nfo? I don’t think I need “uniqueid type” and I might choose to omit one or more of the rating fields as well.
Currently it’s exported as [scene title]-poster.jpg which will get recognized by Jellyfin. It may be nice to be able to customize the output filenames though.
I noticed you implemented the other changes I suggested in a previous post and they’ve been working great. I haven’t figured out how to use hookTriggerMode though to have it fire only on organized. Probably I’m missing something obvious.
First, thank you for this plugin, it works well and is great for jellyfin integrations. I have 3 requests and I was wondering if you’d be interested in adding any of these onto this plugin:
Ability to exclude thumbs. Specifically this tag: <thumb aspect="poster"></thumb>. I tried to put this into the exclude field in the Plugin Settings but it didn’t work. Or maybe I’m doing it wrong? I put in thumb aspect, thumb in the NFO Exclude Fields.
Ability to use <stashid></stashid> for the stashID instead of <uniqueid type="stash"></uniqueid>
ThePornDB ID integration, which for scenes looks like this: <theporndbid>scenes/25e1418b-897c-4aa1-854b-5673a1211d96</theporndbid>. You can grab the UUID from the tpdb scraped endpoint.
Any possibility of getting something like a tag that’s required for moving/generating nfo/renaming? Or being able to restrict it to specific directories? Basically, something that would let us only generate nfo/etc when we know the files are in their final location without micro managing disabling/enabling features in the settings. Just something to cut down on manual work.
Even just an option to somehow choose a folder to run ‘Bulk Update Scenes’ on would be awesome. Sorry that I missed it if there is already something for this. Appreciate your work!