Automatic custom covers for XBVR

What’s up gooners!

Just thought I’d help fellow XBVR users who are tired of not having any cover images on custom scenes downloaded from here.

Most of the files/videos I download from here are not an entire scene in its original form from the producer.
As such, in XBVR, I create custom scenes because there is no “official” source to scrape for covers to these videos.

The custom scene lacks any cover however.

I could just take a screenshot, upload that to somewhere (imgur?) and paste the URL to the cover in the custom scene. That would suck however.

I have thousands of files, so doing manual screenshots, and then manually matching the screenshot with a custom scene is absolutely out of the question.

So… What I wanted to accomplish was:

  • Automatically create thumbnails of all the files in my “Interactive” directory where I store videos from Eroscripts.
  • Match these thumbnails to the correct scenes in XBVR so that I can have an idea what videos there are to choose from :wink:

What I ended up doing is half manual and half automatic, and I’m sure there are better ways to accomplish this. I think XBVR should do this automatically themselves but… I don’t know Golang, haven’t coded in ages and just wanted to fix the issue ASAP myself, so here comes a write-up instead of a Github merge request :slight_smile:

WARNING

I have a directory called \\nas\Adult\VR\ for VR sites and \\nas\Adult\Sites\ for full length videos released by many different porn networks.
Files downloaded from Eroscripts or other places are inside \nas\Adult\Interactive\

Therefore, I know perfectly well that there will be great covers scraped from the Internet for “official videos”. However, anything in my Interactive directory lacks real covers from real studios.

This guide will help you create thumbnail covers for anything inside a specific folder and match it in XBVR - so, don’t use it on a folder where you have official movies from studios, whose covers you scrape from their site instead.

If you’re still reading, that means you assume any and all responsibility if this messes up your covers! Keep a backup of your xbvr database file and you can always revert to it!

Now, lets jump in!

1. Create the thumbnails using ffmpeg:

  • Create a bash script, name it thumbnails.sh and make it executable (chmod +x thumbnails.sh):

thumbnails.sh

#!/bin/bash

# Function to determine if an image is featureless or mostly black using ffmpeg
is_featureless_or_black() {
  local image_file="$1"
  local black_pixel_threshold=0.5  # Threshold percentage of black pixels (50%)

  # Get the black pixel percentage
  black_pixel_percentage=$(ffmpeg -i "$image_file" -vf "blackdetect=d=0.1:pix_th=0.1" -an -f null - 2>&1 | awk '/black_start/ {black+=1} END {print black/NR}')

  # Check if the black pixel percentage exceeds the threshold
  if (( $(awk -v bpp="$black_pixel_percentage" -v threshold="$black_pixel_threshold" 'BEGIN {print (bpp > threshold) ? 1 : 0}') )); then
    return 1
  fi

  # Get the mean luminance value to check if the image is featureless
  mean_luminance=$(ffmpeg -i "$image_file" -vf "format=gray,psnr" -f null - 2>&1 | awk '/average:/ { print $5 }' | tr -d '[]')

  # Check if the mean luminance value indicates a featureless image
  if (( $(awk -v ml="$mean_luminance" 'BEGIN {print (ml < 0.05) ? 1 : 0}') )); then
    return 1
  fi

  return 0
}

# Function to create a thumbnail from a video file and save it to the target directory
create_thumbnail() {
  local video_file="$1"
  local source_dir="$2"
  local target_dir="$3"
  local relative_path="${video_file#$source_dir}"
  local relative_path="${relative_path#/}"
  local thumbnail_file="${target_dir}/${relative_path%.*}.jpg"
  local screenshot_time=20  # Initial screenshot time in seconds

  if [ -f "$thumbnail_file" ]; then
    echo "Thumbnail already exists: $thumbnail_file"
    return
  fi

  mkdir -p "$(dirname "$thumbnail_file")"

  while true; do
    temp_thumbnail_file=$(mktemp /tmp/thumbnail.XXXXXX.jpg)
    ffmpeg -ss "00:00:${screenshot_time}.000" -i "$video_file" -vframes 1 "$temp_thumbnail_file" -y > /dev/null 2>&1

    if is_featureless_or_black "$temp_thumbnail_file"; then
      echo "Featureless or black frame detected at ${screenshot_time} seconds. Retrying..."
      screenshot_time=$((screenshot_time + 10))
      rm "$temp_thumbnail_file"

      # If we've tried multiple times, give up to prevent infinite loops
      if [ "$screenshot_time" -ge 60 ]; then
        echo "Unable to find a clear frame for $video_file"
        return
      fi
    else
      mv "$temp_thumbnail_file" "$thumbnail_file"
      echo "Created thumbnail: $thumbnail_file"
      return
    fi
  done
}

# Export the functions so that they can be used by find
export -f create_thumbnail
export -f is_featureless_or_black

# Check if the user has provided the source and target paths as arguments
if [ "$#" -ne 2 ]; then
  echo "Usage: $0 /path/to/videos /other/path"
  exit 1
fi

# Get the source and target directory paths
source_dir="$1"
target_dir="$2"

# Ensure the target directory exists
mkdir -p "$target_dir"

# Remove trailing slash from source directory if it exists
source_dir="${source_dir%/}"

# Find all video files in the source directory and its subdirectories
find "$source_dir" -type f \( -iname "*.mp4" -o -iname "*.mkv" -o -iname "*.avi" -o -iname "*.mov" \) -exec bash -c 'create_thumbnail "$0" "$1" "$2"' {} "$source_dir" "$target_dir" \;

echo "All thumbnails created."


  • Run the script
    ./thumbnails.sh /media/adult/Interactive /mnt/nas/Thumbnails

If you have a video file in /media/adult/Subdirectory/Movie.mp4 then there will be a thumbnail named /mnt/nas/Thumbnails/Subdirectory/Movie.jpg
In my script, I’m grabbing the thumbnail from 20 seconds in. We’re also doing some checks to see if the image is just black or empty. If that’s the case, then we move up to 30 seconds and see if that’s a clearer image and grab that instead. This takes some extra time to process, but I have many videos with 5-15 second intros that I don’t want a screenshot of, but you’re free to change to any timecode you want ofc :wink:

2. Move the thumbnail files to a directory where XBVR can serve them.

Find the configuration directory of your XBVR instance. In my case, I’m using XBVR on Windows, so the path was %APPDATA%\xbvr\ or C:\Users\Username\AppData\Roaming\xbvr\

In this directory there is a directory named myfiles. Any file placed here can be accessed in a web-browser that can talk with XBVR. In my home that would mean http://192.168.1.10:8888/myfiles/. This is where we want to place the thumbnails we just generated.

If all is correct, if you have a file named Secret Kinky Movie123.mp4 in a directory called Kinks, then you should be able to find http://192.168.1.10:8888/myfiles/Kinks/Secret Kinky Movie123.jpg

Of course, the IP and port number should be substituted for your own.

3. Match the new covers to the correct scene in XBVR

Inside the configuration folder of XBVR there is an SQL database file called main.db.
This is where it can get tricky! You don’t have to know anything about SQL, but follow along!

Before doing anything with these files, make sure you close XBVR completely first. We don’t want it to be in the middle of doing anything with the database when we start messing with it.

I then downloaded “DB Browser for SQLite” because I’m on Windows.
I renamed main.db to backup.db, took a copy of the backup and named that main.db. It’s always best to have a backup of whatever the database looked like before we touched it.

I then opened main.db in DB Browser for SQLite.
First, click “Browse data” and choose the “files” table.
You get a bunch of columns, one of which is named “path”. The path is the directory the scene video file resides in.
Because we only want to create covers for anything inside my Interactive folder, we want to filter out all the videos whose path doesn’t start with \\\nas\Adult\Interactive

If the path to your cover-less videos are in /mnt/sexytime/Videos, then replace \\nas\Adult\Interactive in the script below with /mnt/sexytime/Videos.

Then we ask - what is the web address to the myfiles directory where we dumped all of the thumbnails/covers we generated?

In the example above it was http://192.168.1.10:8888/myfiles.
In the script below, replace this with whatever the value is to access your XBVR.
You’ll have to find the URL to your XBVR instance yourself :wink:

There is a tab named “Execute SQL”. Click that and paste this code:

UPDATE scenes
SET cover_url = REPLACE(REPLACE(
       REPLACE(
       REPLACE(
       REPLACE(
       REPLACE(
       REPLACE(
           REPLACE(f.path || '/' || f.filename, '\\nas\Adult\Interactive', 'http://192.168.1.10:8888/myfiles/'), 
           '.mp4', '.jpg'),
           '.mkv', '.jpg'),
           '.avi', '.jpg'),
           '.mov', '.jpg'),
           '.wmv', '.jpg'),
           '.flv', '.jpg'),
           '\', '/')
FROM files f
WHERE scenes.id = f.scene_id
  AND (f.filename LIKE '%.mp4'
       OR f.filename LIKE '%.mkv'
       OR f.filename LIKE '%.avi'
       OR f.filename LIKE '%.mov'
       OR f.filename LIKE '%.wmv'
       OR f.filename LIKE '%.flv');

Make sure you replaced the paths above to whatever is true for you!
When you feel confident it’s correct, you can execute the SQL code we just pasted by pressing the little Play button, or pressing F5 on your keyboard.

When the script is complete, you will only get something like “OK” or “Execution finished.”

Now, if you have a look in the scenes table again, and look at the column cover_url, the path should now be something like: http://192.168.1.10:8888/myfiles/Kinks/Secret Kinky Movie123.jpg

Lastly, don’t forget write the changes (save the file) and close DB Browser for SQLite.

4. Then, start XBVR again as usual
And… tada! If all worked well, you should be seeing covers on all custom scenes that contained the path to your interactive folder, while NOT touching scenes that already had covers from reputable sources :slight_smile:

Don’t come for me with guns blazing if something got f’ed up - this worked for me in my use case. Perhaps it can inspire someone else to do this properly directly in XBVR, or perhaps at least an easier method than what I ended up doing.

Anyway… hope this helped someone. :smiling_imp::sweat_drops:

5 Likes

omg i love you

another option so you dont have to copy a tb of porn is to make a symbolic link to the myfiles folder.

so here my videos are on D drive in the folder named 11. I am making a symbolic link to the xbvr\myfiles folder. This will save space and provide the same functionality.

mklink /D "C:\Users\username\AppData\Roaming\xbvr\myfiles" "D:\11"

FYI, I just updated the bash script a little…

If I ran: ./thumbnails.sh /mnt/adult/Interactive/ /mnt/adult/Thumbnails/ then the outputted files would end up /mnt/adult/Thumbnails/mnt/adult/Interactive/ (obviously wrong!)

I just wanted them to be in the root of the second parameter, i.e a subdirectory named “Kinks” should end up as /mnt/adult/Thumbs/Kinks/.

I also wanted to make sure we don’t generate new thumbnails for videos that already have a thumbnail. This way I can run this script as a cronjob and generate covers every night, and update the SQL too…

This is now fixed in the bash script above.

Nice, thanks for sharing.
I wanted to make something like that ages ago, but was too lazy :smiley:

why dont you just output the thumbnails to the myfiles directory. im confused what goes in the myfiles director.

You absolutely can output them to the myfiles directory!
It’s just for me, I have myfiles on a Windows machine, and the bash scripting on another machine… I could set it up differently of course, but I just jotted down what I did :wink:

well i mean what is supposed to be in there. The thumbnails or the videos?

Only the thumbnails…
Updated the guide to clarify.

Nice ok that worked. Do you know if theres a way to auto match scenes?

It’s like the xbvr devs are beginners and dont have 3000 items to manually match and only pay for their porn so they cannot fathom how insanely inconvenient not having thumbnails for non studio clips and manually matching everything is. lol

1 Like

I was searching for the same feature together with the possibility to create custom covers for 2D scenes as well…some time ago. I think the goal of XBVR is more like providing one platform to search and download scenes of different VR studios. With some additional automation to add custom scenes and the possibility to add 2D content it would be a quite good tool to manage my custom porn library as well. Long story short :sweat_smile: I would appreacheate any tips in regard of auto match scenes (and 2D content) as well…

You said it :smiley:

Regarding your question - what do you mean with auto-match? Match downloaded clips to something like stashdb?

The funscripts because if the video isnt a known scene then I have to sit there and click the video then go and then tell it where the funscript is. Theyre the same name and theyre always in the same folder.

I think the standard same name same folder requirement is better than any name any folder manual match. Id rather just keep them together.

1 Like

I just add the video file to a new custom scene… to make it easier, I sort files with any FPS (hide files without any frames per second). Then create new scenes for them… next time I run the scan, the scene is there, and the funscript is automatically matched to it if it has the same name.

The lack of image covers for XBVR custom videos is a problem for me too. I’ve set XBVR to generate video previews but they do not display in Heresphere, only in a web browser, for some unexplained reason.

Any chance someone can explain how to achieve the above on a Mac? Appreciated!

I have a container running XBackBone. Open video, grab screenshot of the best part so I remember, then upload to XBB, copy the raw URL, then add cover during the scene creation. Works for me as I download maybe 3-4 videos a week.