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
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
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
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
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
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.