banner
ximu

ximu

吃饭,睡觉,做梦!
twitter

Use strm to play OneDrive direct links in Jellyfin.

cover

Preface#

Previously, I set up an anime media library using Jellyfin, but the VPS connection was somewhat unsatisfactory, and there was a risk of running out of bandwidth. So, with the great spirit of free riding free 😇, I discovered that I could use strm files to achieve direct playback of videos in OneDrive through Jellyfin, without using the server's bandwidth and traffic. I also found a conversion script on GitHub, and everything was just perfect 😜.

The principle is roughly as follows:
thought

Preparation#

  • A server with Jellyfin installed
  • A well-structured OneDrive media library
  • An Alist that has mounted the OneDrive media library
  • Some patience

Preparing the Script Runtime Environment#

Make sure that python3 and pip are installed on your server. Use the following commands to check if they are installed:

python3 --version
pip --version

If they are not installed, please follow the steps below to install them:

  1. Update the package list:

    Open the terminal and first update your package list. This ensures that you download the latest software packages. Run the following command:

    apt update
    
  2. Install Python 3:

    The Debian software repository includes installation packages for Python 3. Run the following command to install Python 3:

    apt install python3
    
  3. Install pip:

    Pip is the package manager for Python, used to install and manage Python packages. Install it using the following command:

    apt install python3-pip
    
  4. Verify the installation:

    After installation, run the following commands to verify that Python 3 and pip are correctly installed:

    python3 --version
    
    pip3 --version
    

Once you have confirmed the installation, create a virtual environment and install the script's environment dependencies within it:

  1. Create a virtual environment

    Creating a virtual environment can avoid certain conflicts 🤗

    python3 -m venv /python_env/auto_film/
    

    Please replace /python_env/auto_film/ with the path where you want to install the virtual environment.

  2. Activate the virtual environment

    After creating it, you need to activate it 🧐

    source /python_env/auto_film/bin/activate
    

    Replace /python_env/auto_film/ with the path where you just installed the virtual environment.

  3. Install dependencies

    webdavclient3 is a Python library for interacting with WebDAV servers.

    pip install webdavclient3
    

Okay, you have set up the environment required to run the script 👌

Since the script may take a long time to run, you can choose to install screen to allow the command to run in the background, so you can close the terminal window running the script 😉.

  1. Install screen

    Use the following command to install screen:

    apt-get install screen
    
  2. Start screen:

    Create a new session:

    screen -S mysession
    

    Replace mysession with your desired session name.

  3. Using the screen session:

    In the screen session, you can use the command line as usual. You can also close this terminal window, and the commands will continue to run in the background.

  4. Reconnect to a screen session:

    To reconnect to an existing screen session, use:

    screen -r mysession
    

    Where mysession is your custom session name.

Preparing the strm Script#

Save the following code as autofilm.py. The script content comes from Akimio521's GitHub project AutoFilm. Give the author a star ✨.

from webdav3.client import Client
import argparse, os, requests, time

'''
Function to traverse the WebDAV server
If depth is None, it will recursively traverse the entire WebDAV server
If depth is a positive integer, it will recursively traverse to the specified depth
If depth is 0, it will only traverse the files and folders in the current folder, without continuing to recursively traverse the next level of folders.
'''
def list_files(webdav_url, username, password, show_path, depth=None, path='', count=0, proxies=None):
    options = {
        'webdav_hostname': webdav_url,
        'webdav_login': username,
        'webdav_password': password,
        'proxies': proxies
    }

    client = Client(options)
    directory = []
    files = []
    q = 1
    while q < 15:
        try:
            items = client.list()
        except:
            print(f'Connection failed for the {q} time, retrying in {q+1} seconds...')
            q += 1
            time.sleep(q)
        else:
            if q > 1:
                print('Reconnection successful...')
            break

    if q == 15:
        print('Connection failed, please check your network settings!')
        exit()

    for item in items[1:]:
        if item[-1] == '/':
            if depth is None or depth > 0:
                subdirectory, subfiles, count = list_files(webdav_url + item, username, password, show_path, depth=None if depth is None else depth - 1, path=path+item, count=count)
                directory += [item + subitem for subitem in subdirectory]
                files += [item + subitem for subitem in subfiles]
            else:
                directory.append(item)
        else:
            files.append(item)
            count += 1
    if show_path and path:
        print(f'Current folder path: {path}')
    return directory, files, count

'''
Download function
Used for downloading 'ASS', 'SRT', 'SSA', 'NFO', 'JPG', 'PNG' files
'''   
def download_file(url, local_path, filename, total_count):
    p = 1
    while p < 10:
        try:
            print('Downloading: ' + filename)
            r = requests.get(url.replace('/dav', '/d'), proxies=proxies)
            os.makedirs(os.path.dirname(local_path), exist_ok=True)
            with open(local_path, 'wb') as f:
                f.write(r.content)
                f.close()
        except:
            print(f'Download failed for the {p} time, retrying in {p + 1} seconds...')
            p += 1
            time.sleep(p)
        else:
            if p > 1:
                print('Re-download successful!')
            print(filename + ' downloaded successfully!')
            break
        progress = int((p / 10) * 100)
        print(f'Completed {progress}%, total {total_count} files')

parser = argparse.ArgumentParser(description='Autofilm script')
parser.add_argument('--webdav_url', type=str, help='WebDAV server address', required=True)
parser.add_argument('--username', type=str, help='WebDAV account', required=True)
parser.add_argument('--password', type=str, help='WebDAV password', required=True)
parser.add_argument('--output_path', type=str, help='Output file directory', default='./Media/')
parser.add_argument('--subtitle', type=str, help='Whether to download subtitle files', choices=['true', 'false'], default='true')
parser.add_argument('--nfo', type=str, help='Whether to download NFO files', choices=['true', 'false'], default='false')
parser.add_argument('--img', type=str, help='Whether to download JPG and PNG files', choices=['true', 'false'], default='false')
parser.add_argument('--show_path', type=str, help='Whether to show folder path during traversal', choices=['true', 'false'], default='false')
parser.add_argument('--proxy', type=str, help='HTTP proxy server in the format IP:port')
args = parser.parse_args()

print('Startup parameters:')
print(f'WebDAV server address: {args.webdav_url}')
print(f'WebDAV login username: {args.username}')
print(f'WebDAV login password: {args.password}')
print(f'File output path: {args.output_path}')
print(f'Whether to download subtitles: {args.subtitle}')
print(f'Whether to download movie information: {args.nfo}')
print(f'Whether to download images: {args.img}')
print(f'Whether to show folder path during traversal: {args.show_path}')

proxies = None
if args.proxy:
    proxies = {
        'http': f'http://{args.proxy}',
        'https': f'http://{args.proxy}'
    }

directory, files, count = list_files(args.webdav_url, args.username, args.password, args.show_path, depth=None, path='', count=0, proxies=proxies)

urls = [args.webdav_url + item for item in directory + files]

download_count = 0

for url in urls:
    if url[-1] == '/':
        continue
    filename = os.path.basename(url)
    local_path = os.path.join(args.output_path, url.replace(args.webdav_url, '').lstrip('/'))
    file_ext = filename[-3:].upper()

    if file_ext in ['MP4', 'MKV', 'FLV', 'AVI', 'WMV']:
        if not os.path.exists(os.path.join(args.output_path, filename[:-3] + 'strm')):
            print('Processing: ' + filename)
            try:
                os.makedirs(os.path.dirname(local_path), exist_ok=True)
                with open(os.path.join(local_path[:-3] + 'strm'), "w", encoding='utf-8') as f:
                    f.write(url.replace('/dav', '/d'))
            except:
                print(filename + ' processing failed, the filename contains special characters, it is recommended to rename it!')
    elif args.subtitle == 'true' and file_ext in ['ASS', 'SRT', 'SSA', 'SUB']:
        if not os.path.exists(local_path):
            download_file(url, local_path, filename, count)
            download_count += 1
    elif args.nfo == 'true' and file_ext == 'NFO':
        if not os.path.exists(local_path):
            download_file(url, local_path, filename, count)
            download_count += 1
    elif args.img == 'true' and file_ext in ['JPG', 'PNG']:
        if not os.path.exists(local_path):
            download_file(url, local_path, filename, count)
            download_count += 1

    progress = int((download_count / count) * 100)
    print(f'Completed {progress}%, total {count} files')

print('Processing complete!')

Running the strm Script#

  1. Start a screen session (optional):

    Using screen allows you to close the terminal window running the script, letting it run in the background.

    screen -S autofilm
    
  2. Activate the previously set up virtual environment

    source /python_env/auto_film/bin/activate
    

    /python_env/auto_film/ is the path where you created the virtual environment earlier.

  3. Run the script

    Please ensure that all paths and filenames in the media library do not contain special characters like #《, otherwise a network error will occur.

    python3 autofilm.py --webdav_url https://alist.example.com/dav/your/media/ --username uname --password pword  --output_path /your/strm/ --nfo true --img true
    
    • Replace https://alist.example.com/dav/ with your Alist URL
    • Replace your/media/ with the path of your media library on Alist
    • Replace uname and pword with your Alist username and password
    • Replace /your/strm/ with the location where you want to store the strm files

    Some parameter explanations:

    Required parameters

    • webdav_url: WebDAV server address
    • username: WebDAV account
    • password: WebDAV password

    Optional parameters

    • output_path: Output file directory, default is a subdirectory Media in the current folder
    • subtitle: Whether to download subtitle files, default is true
    • nfo: Whether to download NFO files, default is false
    • img: Whether to download JPG and PNG image files, default is false

Okay, please drink ten cups of tea 🍵 or watch a movie 🎞, the script's running time depends on the size of your media library and server configuration, usually it will be 🤏 slow.

Jellyfin Playing strm Files#

Add a media library in Jellyfin, and select the location where you store the strm files. Jellyfin will automatically recognize and scrape them.

Try playing it, and you will find that it does not consume server bandwidth at all! 🎉

Epilogue#

Now you don't have to worry about server bandwidth, and I've made the Jellyfin server public. I'll add some old anime resources later, bye~

References

  • AutoFilm
  • [Promoting my own small project—AutoFilm](Promoting my own small project—AutoFilm)
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.