This application written in Rust serves a web application that allows users to extract clips (audio or video) from videos in their media server (Plex, Jellyfin, etc.).
This is loosely inspired from jo-nike's clipplex (https://github.com/jo-nike/clipplex). However, unlike clipplex, mediaserver-clipper does not rely on any specific mediaserver API and uses the path to the file directly. This makes it slightly less easy to use but much simpler to setup and it makes it compatible with absolutely any media server.
Features:
- File search in selected library directories (and subdirectories)
- Clip creation from any accessible file using the direct path
- Audio track selection
- Subtitle track selection for subtitle burn-in
- Video clips
- Audio-only clips (mp3)
- External share links
- File management (remove clips directly from the UI)
demo.mp4
Media Server Clipper supports two ways to select the source for a clip:
-
Enter the direct path to the file. On Plex Web, you can click the triple-dot button while watching a video and then
Get Info
in order to get the absolute path to the file. If your Plex instance is running on the same system as MediaServer Clipper or if you mounted the media volume the same way both in your Plex instance, then this path will also work for the Clipper app to access it. -
Search for files indexed by Media Server Clipper. This can only find files in the libraries that you have registered at setup (unlike direct path which can access any readable file), but it allows you to find a file by just writing a few letters.
MediaServer Clipper will automatically list audio and subtitle tracks and you can select which audio you want to use and whether or not you want to burn in subtitles in the clip.
The example shows how to use it with Plex, but because this will work with any absolute path to any video, it is possible to use it with multiple sources to clip videos from anything on the drive.
For example, if using docker, you can mount the Plex
library to /media
in the container, and some other
directory with your personal video files to /videos
for example. Then if you paste a path to
/videos/holiday_summer_2023.mp4
in the app, it will be able to clip it just like plex videos if the
path was /media/some_movie.mp4
.
You can even combine it with another app that can download videos on demand. If the directory it stores its downloads is mounted in the Clipper container, it will be able to make clips from it.
Files are indexed when the application start and a refresh of the index can be triggered using the button at the top of the search results later.
You can put several words in your search query separated by whitespace. The search engine will return any file for which the path (either the file name or any of its parent directories) contains all the words of your search. Search is case-insensitive. For example, with the following library:
library
├── An.Awesome.TV.Show.x264.EN
│ ├── S01
│ │ ├── E01.mp4
│ │ ├── E02.mp4
│ │ └── E03.mp4
│ └── S02
│ ├── E01.mp4
│ ├── E02.mp4
│ └── E03.mp4
└── The.Incredible.Series.x264.EN
├── S01
│ ├── E01.mp4
│ ├── E02.mp4
│ └── E03.mp4
└── S02
├── E01.mp4
├── E02.mp4
└── E03.mp4
Searching for awe
will return all episodes from both seasons of "An Awesome TV Show".
Searching for awe s01
will return only the season 1 episodes of "An Awesome TV Show".
Searching for awe s01 e02
will return episode s01e02 of "An Awesome TV Show".
Searching for incred s01 e02
will return episode s01e02 of "The Incredible Series".
Searching for s01 e02
will return episodes s01e02 of both shows.
All environment variables have a default and are optional, but setting these allows you to customize the behaviour of the app.
APP_NAME
: defines the name of the app on the webpage. Default isMedia Server Clipper
.OUTPUT_PATH
: defines the directory in which the app will store the clips. Default is theoutput
directory where the app is started from (/app/output
in docker container).PUBLIC_LINK_PREFIX
: defines a different base URL to the clips directory that will be used when clicking theShare
button for a clip. This is especially useful if you want to protect the MediaServer Clipper instance behind a htpassword but wish to have a public static file server to share the clips with people without giving your credentials (see docker-compose example below). Default is to link to the clip hosted by the Media Server Clipper (so theshare
andlink
buttons will do the same thing).MAX_CLIP_DURATION
: defines the maximum allowed duration (in seconds) of the clips. Default is 600 (10 minutes).MAX_QUEUE_SIZE
: number of jobs in queue after which the Clipper will reject new clip jobs. This does not count finished clips, only pending ones. Default is 4.SEARCH_DIRS
: list (comma-separated) of paths in which the search engine should be indexing files. If empty or not set, the search engine is disabled and the search field does not appear in the app. Default is empty. Example:SEARCH_DIRS=/media,/personal/videos,/jellyfin/media
SEARCH_FILE_EXTS
: list (comma-separated) of file extensions (without including the dot) that should be indexed by the search engine. If empty or not set, all files will be indexed. Default is empty (no filtering). Example:SEARCH_FILE_EXT=mp4,mkv,avi,mov
To run the app locally, install the rust toolchain (https://rustup.rs/) and run:
cargo run --release
You need to have ffmpeg
and the DejaVu
fonts installed.
Docker is the recommended approach to run this application. Use this command to run the image (this exposes it on port 9987):
docker run -d --name clipper -p 9987:8000 -v ./media:/media -v ./clips:/app/output paveyry/mediaserver-clipper:latest
plex:
#[...]
volumes:
- ./media:/media
clipper:
image: paveyry/mediaserver-clipper:latest
ports:
- 9987:8000
environment:
- SEARCH_DIRS=/media
volumes:
- ./media:/media # mount it the same way as in plex/jellyfin
- ./clips:/app/output
plex:
#[...]
volumes:
- ./media:/media
clipper: # This can be protected by a htpassword
image: paveyry/mediaserver-clipper:latest
ports:
- 9987:8000
environment:
- SEARCH_DIRS=/media
- PUBLIC_LINK_PREFIX=https://yourdomain.tld:9988 # Share links wil link to static_clips
volumes:
- ./media:/media
- ./clips:/app/output
public_clips: # This has read-only access so it can be safely exposed without auth
image: nginx
ports:
- 9988:80 # this is https://yourdomain.tld:9988 linked in PUBLIC_LINK_PREFIX
volumes:
- ./clips:/usr/share/nginx/html:ro
Note that the download
and link
buttons will still link to the clipper url, only
the share
button use the URL from PUBLIC_LINK_PREFIX
(the static_clips
nginx instance in this example)
From highest to lowest priority:
- Documentation
- Selecting clip resolution
- Feature to load videos from external sources
- Cache the clip library in the backend to avoid filesystem access at each refresh
- Better logging
- Get rid of std::thread and use rocket's tokio runtime instead