I enjoy movies, like anyone else, and have accumulated many over the years, though I haven’t always acquired them through the most formal means. 😉 To organize and watch them, I usually use Plex, and I like it a lot; it’s a great platform and, in my opinion, deserves a few bucks from me. However, since they’ve been making some curious decisions lately, I started using an Emby instance more frequently for similar purposes.

Everything was going relatively well until it started going relatively poorly: I noticed that sometimes the movies or videos I store there had difficulty starting playback and/or continuing playback when I made jumps on the timeline. It wasn’t a huge problem, as it’s not common to make time jumps when watching a movie, but the issue had already caught my attention.

I first thought my hard drives were failing since this problem didn’t occur a couple of years ago when I moved my media library to them. So, I ran a couple of SMART tests, and everything seemed fine with them. Ruled out. I thought it might be a network bottleneck, but it was very unlikely since I observed the problem during local playback of the files. Additionally, I had recently upgraded my local network bandwidth to a respectable 10 Gigabit (more on this in another post in the future). Ruled out. It crossed my mind that one of the nodes (computers/servers) might be having issues when jumping from one part of the movie to another, but I didn’t see any excessive CPU usage in any case. Ruled out.

I ruled out a couple more options over days and weeks, but I noticed something interesting while deliberately trying to reproduce the error for diagnostic purposes. It didn’t matter how many minutes the jump was; whether I skipped 5 or 50 minutes, there was still a noticeable delay. More interestingly, I realized that if I didn’t hover the cursor over the timeline and jumped directly by placing it right at the point I wanted to skip to, the delay practically disappeared. Very interesting.

The problem

Reviewing the logs in the browser’s DevTools window, I noticed many requests made to the same endpoint; all started with “Thumbnail,” and then everything made sense: while scrubbing, i.e., moving the cursor over the timeline to see a small preview of what happens along it, dozens of requests were made that allowed Emby to obtain thumbnails for the preview. Thumbnails are lightweight, yes, but in the order of dozens, they significantly delay subsequent actions.

Emby requests to get thumbnails
Emby requests to get thumbnails

I couldn’t determine how many requests are made per pixel, perhaps per em, but they result in many in a short time while scrubbing and can become frustrating because the video resumes playback only when all of them (or at least a good percentage) have been completed, successfully or not.

The solution

Visiting the Emby forums, I saw that they recommended enabling the option to save thumbnails on the same disk where the content resides. It’s an option I had deliberately left disabled because I didn’t want to use space unnecessarily. Additionally, it was also recommended that each video file reside within its own directory and not have them all at the same level, as this way both the operating systems involved (Debian for the NAS and Windows for the client I was using at the time) and Emby itself would have less work finding and accessing files.

Let’s get hands on, then.

Option to save thumbnails into media folders
Option to save thumbnails into media folders

This initiated a process that, using FFmpeg, generates BIF files (created by Roku) that encapsulate snapshots taken at regular intervals throughout the video. So far, so good, but the process takes approximately 9 seconds per gigabyte on my hardware, and with several terabytes in tow, it was going to take a good few hours.

Some time later, with the process finished, I had double the files in my main movies/videos directory; each MP4 or MKV file had an analogous BIF file with the same name. If I recall correctly, they recommended not having so many files at the same level, and what I did doubled the original number—totally wrong. The good thing is that the next step would surely improve things.

I had to move each pair of files to their own folder; the bad thing is that Emby doesn’t get happy at all when files are moved. It doesn’t matter if you just put them inside another folder; it represents a change in their location, and that’s enough. The consequence of this is that Emby wouldn’t recognize the “new” files as files it already knew about, and references to the “old” ones would be invalidated, thus ruining playlists and favorites, for example.

Reading around, they recommended using NFO files because they could preserve important information about the files, and as long as they’re alongside the videos, that information will be available.

I did the test: enabled the option to save everything on the media disk in NFO format and moved a couple of movies to their respective new folders. I rescanned the folder in Emby, refreshed the metadata, but nothing—the movies in question had disappeared from the playlists they were in, as well as from favorites. The curious thing is that if I returned the files to their original location, everything went back to normal. Apparently, the correct way to remove items from these lists is by deliberately removing them through the applications or the API.

Option to use NFO as metadata format
Option to use NFO as metadata format
Option to enable NFO files as preferred metadata
Option to enable NFO files as preferred metadata

Clearly, I needed another way to preserve my favorites and lists. I also noticed that Emby seems to assign a unique identifier to each video but based on an index that increments as new files are added, which means that, by Emby considering the moved files as new, they also didn’t retain their IDs and obtained a new one.

The best I could think of was to resolve it through the names of the movies/videos. I was almost 100% sure that none would be repeated and that they would basically function as a unique identifier, but Emby doesn’t think the same way, so I would have to use its API to search by name and thus be able to reach the real ID (the new one) to be able to add the files back to favorites.

Something to keep in mind is that when searching through the API or GUI, Emby doesn’t return a single result even if there’s an exact match, so we have to refine the search. A simple string comparison should be sufficient.

import requests
import json

EMBY_IP = ""
API_KEY = ""
USER_ID = "" # In the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
PARENT_ID = "" # In case the videos need to be filtered by library

HEADERS = {"X-Emby-Token": API_KEY}

def get_favorites():
    url = f"{EMBY_IP}/Users/{USER_ID}/Items?Recursive=true&IsFavorite=true&ParentId={PARENT_ID}&IncludeItemTypes=Movie"
    response = requests.get(url, headers=HEADERS)

    if response.status_code == 200:
        items = response.json().get("Items", [])
        return [item["Name"] for item in items]
    else:
        print("Error fetching favorites:", response.text)
        return {}

def search_new_item_id(name):
    url = f"{EMBY_IP}/emby/Items?Recursive=true&SearchTerm={name}"
    response = requests.get(url, headers=HEADERS)

    if response.status_code == 200:
        items = response.json().get("Items", [])

        matching_items = [item for item in items if item["Name"].strip().lower() == name.strip().lower()]

        if matching_items:
            return matching_items[0]["Id"]
    return None

def re_add_favorites(old_favorites):
    for name in old_favorites:
        new_id = search_new_item_id(name)
        if new_id:
            url = f"{EMBY_IP}/Users/{USER_ID}/FavoriteItems/{new_id}"
            response = requests.post(url, headers=HEADERS)

            if response.status_code == 200:
                print(f"Re-added favorite: {name}")
            else:
                print(f"Failed to re-add {name}: {response.text}")
        else:
            print(f"Could not find new ID for: {name}")

if __name__ == "__main__":
    print("Fetching current favorites...")
    old_favorites = get_favorites()

    input("Move your files now and press Enter when done...")

    print("Re-adding favorites with new paths...")
    re_add_favorites(old_favorites)


This script retrieves the names of the favorites, waits for them to be moved, and once it's told to continue, it locates them and re-adds them.

But now the files come in trios, since we have the media file, the thumbnail file (BIF), and the metadata file (NFO), and moving them manually into their respective new folders can be tedious. It’s tedious.

To solve this, I used FileBot, which is apparently quite popular among users on the Emby and Plex forums for organizing files—moving, copying, deleting, and more. The downside is that it’s paid software, and worse, it’s a subscription. But considering that it’s not very expensive (priced at $6 per year at the time of writing this), that I’ll probably need to perform these kinds of operations regularly in the future, and that I was too lazy to write another script, I decided to give this community-proven tool a shot.

Original files
Original files
Files in their new directories
Files in their new directories

Using the “File to Folder Name” preset, we’re able to create a shared folder for the three files and make that their new home. In the example, there are no other types of files, but as long as they all have exactly the same name (excluding the extension, of course), they’ll be placed into the same directory.

Final thoughts

With the above changes, the performance improvement is noticeable in some areas and significant in others. For instance, when scanning video libraries, it seems to run a bit faster; when it comes to scrubbing, thumbnail previews on the timeline now appear almost instantly, and there’s hardly any wait time when jumping to a different point, no matter how it’s done. The results seem quite satisfying.

For some day… hopefully

  • Write a script to perform the same process but for playlists. It shouldn’t be complicated; it’s quite similar to the code we already have and could be adapted with little trouble.
  • While I can use FileBot on as many machines as I want, regardless of the platform (it supports Docker, Ubuntu, macOS, Windows, and even CentOS), it would be wise to eventually replace it with a basic script. For now, I’ve already paid for it and will use the program for these tasks, but for what I need just a few lines of code should be enough.