Script to regenerate video previews multi threaded?

Hi All,

Is anyone aware of a script that can generate the video preview images multi threaded and using the GPU?

I have to regenerate my entire lib due to multi loss of HDDs in a raid.

My lib is large and it will take a very very long time. I have 24 cores but the process only uses 1 core, single threaded and does not use the GPU.

It seems others have asked this in before but there are no recent threads so i thought i’d ask in a recent post.

If there is no script, does anyone know how to do it on the cli and ill write a script?

[EDIT 19th April 2022]
I’ve created this script.

Posting it here plex_generate_vid_previews.py · GitHub

Thanks!

3 Likes

Just to check, how long does it take to create thumbnails for a movie? There are a few reasons this can become very slow even on fast systems. If it’s more than a few minutes per movie, something might be wrong.

This is doable but I’m not aware of anything already assembled. Using the GPU wouldn’t dramatically accelerate the process.

When Plex is extracting thumbnails you can see the command that it’s executing in the process table and in the logs. An ffmpeg equivalent is something like this:

ffmpeg -skip_frame:v nokey -i input.mkv -q 3 -filter_complex "[0:V:0] fps=fps=0.500000:round=up,scale=w=320:h=240:force_original_aspect_ratio=decrease [out]" -map "[out]" img-%06d.jpg

Then they’re assembled together into index-sd.bif files. There are a few ways to do this. Relevant search keywords that may help are index-sd.bif, Roku and BIF.

Then you’d need to put the created index-sd.bif file in the correct location for Plex. Here’s a recent discussion about that.

How plex calculates this strange *.bundle folder names

1 Like

Thanks for the pointers @Volts.

It takes roughly 4-5mins per file, i think. Do you have links on what would cause it to run slow so i can check if that’s the case for me?

I noticed when it is generating the thumbs it calls “/usr/lib/plexmediaserver/Plex Transcoder” with various args. My script will need to figure out the codec as i noticed “-codec:v hevc” as an arg.

Can i assume all the other args don’t change per file?

5 minutes is slowish for 1080p on a modern computer. You could share server logs if you want.

Plex Transcoder is Plex’s version of ffmpeg with additional special sauce. You don’t need to specify -codec:v blah because ffmpeg will autodetect the codec. If autodetection doesn’t work, neither will specifying the codec. :slight_smile:

The quality/size/aspect flags stay the same when Plex generates VPTs.

You could adjust the FPS. That can significantly speed up VPT generation and reduce VPT file size. I should have suggested this before:

Big Media folder? Make smaller video preview thumbnails!

3 Likes

@Volts I’ve managed to write a rough script and it seems to be working.

It processing time went from 3-4mins to 20-30 seconds… FFmpeg with hw accel processes file between 250x and 700x speed… Compared to the 70x using CPU. Huge speedup.

still makes me wonder why plex does not support this natively.

Does this look right to you?

  -hwaccel cuda -threads:0 1 \
  -i  "somefile.mkv"  -q 3 \
  -filter_complex \
  "[0:V:0] fps=fps=0.333333:round=up,scale=w=320:h=240:force_original_aspect_ratio=decrease [out]" -map "[out]" "/tmp/test/img-%06d.jpg"

What i find odd is if i add -threads:0 1 after the cuda, it goes from 300x speed to 700x speed.

1 Like

That’s significantly faster than I expected! Neat. I might guess that keyframe extraction just doesn’t parallelize well across multiple threads.

I wonder if there’s any way to speed up the jpeg encoding, too! I suspect that’s actually a majority of the CPU time now. At first glance I don’t think that’s accelerated in ffmpeg today.


As an alternative to assembling the .bif files and inserting them where Plex expects, you could instead create a wrapper around the Plex Transcoder process, altering only the JPG extraction step.

Here are some examples that do similar things:

GitHub - ForsakenNGS/raspi-plex-transcode: Help for manipulating the plex-media-server transcode on the raspberry pi

Feature request: use ffmpeg's `-ac` flag for downmixing - #15 by imax

@Volts i’ll share the script once it is in an ok state.

I am not sure on the jpeg encoding, I’ve never used ffmpeg before so I’m kinda guessing. I did try make it use the GPU encoding but it threw errors then i got a bit lost.

What do you mean by creating the wrapper? I thought the Plex Transcoder tool generates the jpg then somewhere in the plex server code it converts those jpgs into a bif?

Thanks

1 Like

I haven’t investigated how Plex turns the jpegs into a bif. I assume that’s correct.

If you replaced the Plex Transcoder binary with a script, you could detect when Plex is extracting jpegs, and modify the command as desired.

For all other activity, pass the commands to the original Plex Transcoder binary for processing as usual.

That way you wouldn’t have to deal with assembling jpegs into bif files, or storing them in the right place.

Check out that second link - @imax’s wrapper. When it recognizes that audio is being downmixed. (via the rematrix_maxval string), it modifies the call to the Plex Transcoder.

You might be able to just insert -hwaccel cuda!

Thinking about this, I bet Plex doesn’t use hardware acceleration for VPT extraction on purpose.

Different hardware can support different numbers of concurrent transcodes, and Plex has a slot-based budgeting system in place for transcoding.

I bet it just didn’t make sense to add these into that same framework. These happen “fast enough”, and it would be bad for a real-time transcoding request to fail just because a thumbnail extraction was using hardware resources.

Ah, i see. I am using this script as a one-off initial load… Then i am happy to let plex do the work for new files…

So prob not worth the effort creating a wrapper for what i need.

Yeah, but even with a single-threaded HW accel, the generation of the bif is much much faster.

1 Like

I’ve created a script.

See here GitHub - stevezau/plex_generate_vid_previews: This is a script to manually generate Plex preview thumbnails

1 Like

Your standard for “raw/unpolished” is pretty high.
That’s awesome.

lol, well it is only tested on my machine, required u to build a docker image for the biftool, works only with nvidia cards… but i am sure someone can figure out how to get the rest of it working for their purposes.

new script uploaded with some tweaks and bug fixed.

How did this end up working for you?


It looks like the .bif format is stupidly simple, and here’s some python you could incorporate to pack the images into the .bif directly:

https://github.com/bcl/HMS/blob/master/scripts/makebif.py

https://github.com/popmedic/roku-buildbifs/blob/master/lib/poplib/bif_builder.rb


Your script would also be a great framework to add tone-mapping for video thumbnails of HDR videos. They’re currently blah-flat gray-green.

https://forums.plex.tv/search?q=video+preview+thumbnails+hdr

That would add complexity - identifying HDR media, and determining which VPTs to reprocess.

I haven’t checked if the Plex Transcoder/ffmpeg options Plex uses for video tone-mapping would also work for VPT generation. It might also be simpler to stay in software.

Another thought - backup & restore of .bif files, adjacent to media files.

@Volts took 2-3 days to process but it seems it worked. Plex is now detecting intro’s tho, that might take another few days but not worth my time adding that to the script.

I’m not planning to do any more work on the script, it served what I need it for. Anyone else is welcome to take it and enhance it tho.

Looks like it would be easy to add the bif code for python vs using docker.

1 Like

@Volts how good is your python? I’ve never used struct before.

Getting error on these lines

    array.array('B', magic).tofile(f)
    f.write(struct.pack("<I1", version))
    f.write(struct.pack("<I1", len(images)))
    f.write(struct.pack("<I1", 1000 * PLEX_BIF_FRAME_INTERVAL))

" * {error}repeat count given without format specifier"

I think the intro detection is baked a bit deeper into Plex and has more proprietary magic involved. I haven’t tried to tease apart what it does.

Just like the famous gnome strategists:

  1. Extract first ~chunk of audio && fingerprint it
  2. ??
  3. Profit

Given the date I assume it was written for Python v2.old.

Shouldn’t the count precede the format character? I think that’s what’s throwing the error.

My guess would be just '<I'. The 1 should be redundant anyway.

I think Python 2.x ignored the trailing 1 because there was no following format character, but 3.x is stricter.