#
tokens: 2956/50000 5/5 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── LICENSE
├── pyproject.toml
├── README.md
├── src
│   └── ffmpeg_mcp
│       ├── __init__.py
│       ├── bin
│       │   └── .gitkeep
│       ├── cut_video.py
│       ├── ffmpeg.py
│       ├── server.py
│       ├── typedef.py
│       └── utils.py
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/src/ffmpeg_mcp/bin/.gitkeep:
--------------------------------------------------------------------------------

```
1 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # FFmpeg-MCP
 2 | Using ffmpeg command line to achieve an mcp server, can be very convenient, through the dialogue to achieve the local video search, tailoring, stitching, playback and other functions
 3 | 
 4 | <a href="https://glama.ai/mcp/servers/@video-creator/ffmpeg-mcp">
 5 |   <img width="380" height="200" src="https://glama.ai/mcp/servers/@video-creator/ffmpeg-mcp/badge" alt="FFmpeg-Server MCP server" />
 6 | </a>
 7 | 
 8 | ## Support Tools
 9 | The server implements the following tools: <br/>
10 | - `find_video_path`
11 |   The parameters are directory and file name, file name can be complete, or is not suffixed, recursive search in the directory, return the full path
12 | - `get_video_info`
13 |   The parameters are video path, return the video info, linkes duration/fps/codec/width/height.
14 | - `clip_video`
15 |   The parameter is the file path, start time, end time or duration, and returns the trimmed file path
16 | - `concat_videos`
17 |   The parameters are the list of files, the output path, and if the video elements in the list of files, such as width, height, frame rate, etc., are consistent, quick mode synthesis is automatically used
18 | - `play_video`
19 |   Play video/audio with ffplay, support many format, like mov/mp4/avi/mkv/3gp, video_path: video path speed: play rate loop: play count
20 | - `overlay_video`
21 |   Two video overlay. <br/>
22 |   background_video: backgroud video path <br/>
23 |   overlay_video: front video path <br/>
24 |   output_path: output video path<br/>
25 |   position: relative location<br/>
26 |   dx: x offset<br/>
27 |   dy: y offset<br/>
28 | - `scale_video`
29 |   Video scale. <br/>
30 |   video_path: in video path <br/>
31 |   width: out video width, -2 keep aspect <br/>
32 |   height: out video height, -2 keep aspect <br/>
33 |   output_path: output video path <br/>
34 | - `extract_frames_from_video`
35 |   Extract images from a video.<br/>
36 |   Parameters: <br/>
37 |   video_path (str): The path to the video.<br/>
38 |   fps (int): Extract one frame every specified number of seconds. If set to 0, extract all frames; if set to 1, extract one frame per second.<br/>
39 |   output_folder (str): The directory where the images will be saved.<br/>
40 |   format (int): The format of the extracted images; 0: PNG, 1: JPG, 2: WEBP.<br/>
41 |   total_frames (int): The maximum number of frames to extract. If set to 0, there is no limit<br/>
42 | <br/>
43 | More features are coming
44 | 
45 | ## Installation procedure
46 | 1. Download project
47 | ```
48 | git clone  https://github.com/video-creator/ffmpeg-mcp.git
49 | cd ffmpeg-mcp
50 | uv sync
51 | ```
52 | 
53 | 2. Configuration in Cline
54 | ```
55 | {
56 |   "mcpServers": {
57 |     "ffmpeg-mcp": {
58 |       "autoApprove": [],
59 |       "disabled": false,
60 |       "timeout": 60,
61 |       "command": "uv",
62 |       "args": [
63 |         "--directory",
64 |         "/Users/xxx/Downloads/ffmpeg-mcp",
65 |         "run",
66 |         "ffmpeg-mcp"
67 |       ],
68 |       "transportType": "stdio"
69 |     }
70 |   }
71 | }
72 | ```
73 | Note: the value:`/Users/XXX/Downloads/ffmpeg` in args  need to replace the actual download ffmpeg-mcp directory
74 | 
75 | ## Supported platforms
76 | Currently, only macos platforms are supported, including ARM64 or x86_64
```

--------------------------------------------------------------------------------
/src/ffmpeg_mcp/__init__.py:
--------------------------------------------------------------------------------

```python
1 | 
```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [project]
 2 | name = "ffmpeg-mcp"
 3 | version = "0.1.4"
 4 | description = "使用ffmpeg,对视频进行裁剪,拼接,滤镜等操作"
 5 | readme = "README.md"
 6 | requires-python = ">=3.10"
 7 | dependencies = [
 8 |     "mcp[cli]>=1.6.0"
 9 | ]
10 | classifiers = [
11 |     "Environment :: MacOS X",
12 | ]
13 | [project.urls]
14 | Homepage = "https://github.com/video-creator/ffmpeg-mcp"
15 | 
16 | [tool.poetry]
17 | packages = [
18 |     {include = "ffmpeg_mcp", from = "src"},
19 | ]
20 | 
21 | 
22 | [[tool.uv.index]]
23 | name = "testpypi"
24 | url = "https://test.pypi.org/simple/"
25 | publish-url = "https://test.pypi.org/legacy/"
26 | explicit = true
27 | 
28 | [project.scripts]
29 | ffmpeg-mcp = "ffmpeg_mcp.server:main"
30 | 
31 | 
32 | [build-system]
33 | requires = ["poetry-core>=2.0.0,<3.0.0"]
34 | build-backend = "poetry.core.masonry.api"
35 | 
36 | 
```

--------------------------------------------------------------------------------
/src/ffmpeg_mcp/typedef.py:
--------------------------------------------------------------------------------

```python
  1 | import json
  2 | class StreamDisposition:
  3 |     def __init__(self, disposition):
  4 |         self.default = disposition.get("default", 0)
  5 |         self.dub = disposition.get("dub", 0)
  6 |         self.original = disposition.get("original", 0)
  7 |         self.comment = disposition.get("comment", 0)
  8 |         self.lyrics = disposition.get("lyrics", 0)
  9 |         self.karaoke = disposition.get("karaoke", 0)
 10 |         self.forced = disposition.get("forced", 0)
 11 |         self.hearing_impaired = disposition.get("hearing_impaired", 0)
 12 |         self.visual_impaired = disposition.get("visual_impaired", 0)
 13 |         self.clean_effects = disposition.get("clean_effects", 0)
 14 |         self.attached_pic = disposition.get("attached_pic", 0)
 15 |         self.timed_thumbnails = disposition.get("timed_thumbnails", 0)
 16 |         self.non_diegetic = disposition.get("non_diegetic", 0)
 17 |         self.captions = disposition.get("captions", 0)
 18 |         self.descriptions = disposition.get("descriptions", 0)
 19 |         self.metadata = disposition.get("metadata", 0)
 20 |         self.dependent = disposition.get("dependent", 0)
 21 |         self.still_image = disposition.get("still_image", 0)
 22 |         self.multilayer = disposition.get("multilayer", 0)
 23 | 
 24 | class StreamTags:
 25 |     def __init__(self, tags):
 26 |         self.handler_name = tags.get("handler_name", "")
 27 |         self.vendor_id = tags.get("vendor_id", "")
 28 | 
 29 | class VideoStream:
 30 |     def __init__(self, stream):
 31 |         self.index = stream.get("index")
 32 |         self.codec_name = stream.get("codec_name")
 33 |         self.codec_long_name = stream.get("codec_long_name")
 34 |         self.profile = stream.get("profile")
 35 |         self.codec_type = stream.get("codec_type")
 36 |         self.codec_tag_string = stream.get("codec_tag_string")
 37 |         self.codec_tag = stream.get("codec_tag")
 38 |         self.width = stream.get("width")
 39 |         self.height = stream.get("height")
 40 |         self.coded_width = stream.get("coded_width")
 41 |         self.coded_height = stream.get("coded_height")
 42 |         self.has_b_frames = stream.get("has_b_frames")
 43 |         self.sample_aspect_ratio = stream.get("sample_aspect_ratio")
 44 |         self.display_aspect_ratio = stream.get("display_aspect_ratio")
 45 |         self.pix_fmt = stream.get("pix_fmt")
 46 |         self.level = stream.get("level")
 47 |         self.color_range = stream.get("color_range")
 48 |         self.color_space = stream.get("color_space")
 49 |         self.color_transfer = stream.get("color_transfer")
 50 |         self.color_primaries = stream.get("color_primaries")
 51 |         self.chroma_location = stream.get("chroma_location")
 52 |         self.field_order = stream.get("field_order")
 53 |         self.refs = stream.get("refs")
 54 |         self.view_ids_available = stream.get("view_ids_available", "")
 55 |         self.view_pos_available = stream.get("view_pos_available", "")
 56 |         self.id = stream.get("id")
 57 |         self.r_frame_rate = stream.get("r_frame_rate")
 58 |         self.avg_frame_rate = stream.get("avg_frame_rate")
 59 |         self.time_base = stream.get("time_base")
 60 |         self.start_pts = stream.get("start_pts")
 61 |         self.start_time = stream.get("start_time")
 62 |         self.duration_ts = stream.get("duration_ts")
 63 |         self.duration = stream.get("duration")
 64 |         self.bit_rate = stream.get("bit_rate")
 65 |         self.nb_frames = stream.get("nb_frames")
 66 |         self.extradata_size = stream.get("extradata_size")
 67 |         self.disposition = StreamDisposition(stream.get("disposition", {}))
 68 |         self.tags = StreamTags(stream.get("tags", {}))
 69 | 
 70 | class AudioStream:
 71 |     def __init__(self, stream):
 72 |         self.index = stream.get("index")
 73 |         self.codec_name = stream.get("codec_name")
 74 |         self.codec_long_name = stream.get("codec_long_name")
 75 |         self.profile = stream.get("profile")
 76 |         self.codec_type = stream.get("codec_type")
 77 |         self.codec_tag_string = stream.get("codec_tag_string")
 78 |         self.codec_tag = stream.get("codec_tag")
 79 |         self.sample_fmt = stream.get("sample_fmt")
 80 |         self.sample_rate = stream.get("sample_rate")
 81 |         self.channels = stream.get("channels")
 82 |         self.channel_layout = stream.get("channel_layout")
 83 |         self.bits_per_sample = stream.get("bits_per_sample")
 84 |         self.initial_padding = stream.get("initial_padding")
 85 |         self.id = stream.get("id")
 86 |         self.r_frame_rate = stream.get("r_frame_rate")
 87 |         self.avg_frame_rate = stream.get("avg_frame_rate")
 88 |         self.time_base = stream.get("time_base")
 89 |         self.start_pts = stream.get("start_pts")
 90 |         self.start_time = stream.get("start_time")
 91 |         self.duration_ts = stream.get("duration_ts")
 92 |         self.duration = stream.get("duration")
 93 |         self.bit_rate = stream.get("bit_rate")
 94 |         self.nb_frames = stream.get("nb_frames")
 95 |         self.extradata_size = stream.get("extradata_size")
 96 |         self.disposition = StreamDisposition(stream.get("disposition", {}))
 97 |         self.tags = StreamTags(stream.get("tags", {}))
 98 | 
 99 | class FormatContext:
100 |     def __init__(self, json_data):
101 |         data = json.loads(json_data)
102 |         streams = data["streams"]
103 |         self.video_streams = []
104 |         self.audio_streams = []
105 |         for stream in streams:
106 |             if stream["codec_type"] == "audio":
107 |                 self.audio_streams.append(AudioStream(stream))
108 |             elif stream["codec_type"] == "video": 
109 |                 self.video_streams.append(VideoStream(stream))
110 | 
```