{"id":42926427,"url":"https://github.com/coderefinery/ffmpeg-editlist","last_synced_at":"2026-01-30T18:09:58.965Z","repository":{"id":45045514,"uuid":"367301070","full_name":"coderefinery/ffmpeg-editlist","owner":"coderefinery","description":"Script ffmpeg video cutting","archived":false,"fork":false,"pushed_at":"2025-09-17T18:43:14.000Z","size":548,"stargazers_count":21,"open_issues_count":2,"forks_count":0,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-09-27T23:02:31.181Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coderefinery.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-05-14T08:36:56.000Z","updated_at":"2025-09-17T18:43:19.000Z","dependencies_parsed_at":"2023-01-18T16:26:42.526Z","dependency_job_id":"904985fa-ce0d-48ca-bed2-50ec88e6f13f","html_url":"https://github.com/coderefinery/ffmpeg-editlist","commit_stats":{"total_commits":72,"total_committers":2,"mean_commits":36.0,"dds":0.02777777777777779,"last_synced_commit":"441d6ab9b9af4b20348a314a5d53eb84b93b46fe"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/coderefinery/ffmpeg-editlist","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderefinery%2Fffmpeg-editlist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderefinery%2Fffmpeg-editlist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderefinery%2Fffmpeg-editlist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderefinery%2Fffmpeg-editlist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coderefinery","download_url":"https://codeload.github.com/coderefinery/ffmpeg-editlist/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderefinery%2Fffmpeg-editlist/sbom","scorecard":{"id":297515,"data":{"date":"2025-08-11","repo":{"name":"github.com/coderefinery/ffmpeg-editlist","commit":"3cc1c00c8f2c136d286eae171f9a4c0e855ff3e4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/release.yml:40","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:84: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:90: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:43: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:70: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/coderefinery/ffmpeg-editlist/test.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/release.yml:52","Warn: pipCommand not pinned by hash: .github/workflows/release.yml:53","Warn: pipCommand not pinned by hash: .github/workflows/release.yml:54","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:21","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:22","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:23","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   0 out of   6 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T19:54:41.871Z","repository_id":45045514,"created_at":"2025-08-17T19:54:41.871Z","updated_at":"2025-08-17T19:54:41.871Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28917035,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T16:37:38.804Z","status":"ssl_error","status_checked_at":"2026-01-30T16:37:37.878Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-30T18:09:58.901Z","updated_at":"2026-01-30T18:09:58.955Z","avatar_url":"https://github.com/coderefinery.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ffmpeg editlist utility\n\nOften, one wants to reprocess a video file using some basic\noperations, such as removing certain segments.  Rather than opening a\nvideo editor, it is nice to be able to define a text file (the\n**editlist**) with processing instructions, and then run it.  This\nallows collaboration in the processing, for example sharing the\neditlist file via git.\n\nThis utility takes a YAML definition of an editlist (segments to cut\nout and re-assemble into a file), and does the re-assembling using the\nffmpeg command line utility.\n\nThis is currently an alpha-level utility: it works, but expect it may\nnot exactly fit your use case without a bit of work.  Documentation is\nminimal but still needs improvement.  However, it has been used for\nseveral large events.\n\nVideo demonstration: https://youtu.be/thvMNTBJg2Y\n\nFeatures include:\n\n* YAML edit list definition.\n* Select segments to stitch together in the final video file.\n  Segments are either copied raw or re-encoded (`--reencode`).\n* Give Table of Contents times (for example, '16:45: Lesson 2 begins')\n  relative to the source video, output mapped to times in\n  the output video automatically.\n* Cover certain areas of video (for example, when an audience member\n  appears).\n* Everything scripted and non-interactive.\n* Cutting and re-splicing subtitles, too.\n* Embed subtitles, title, chapters, and description inside the final\n  .mkv file.\n\n\n\n## Installation and dependencies\n\nThis is on PyPI, may be installed with `pip install ffmpeg-editlist`.\nThe only dependency is PyYAML so it might be reasonable to install\nwith `pip install --user`.  This creates a `ffmpeg-editlist` command\nin your environment.\n\nFor usage without installing, `ffmpeg_editlist.py` works as\nstand-alone with only `pyyaml`.\n\nIt depends on the `ffmpeg` command line utility, which must be\ninstalled through the operating system.  Version requirements of\n`ffmpeg` are currently unknown.\n\nThe mkv file properties (title, description, chapters, subtitles)\nrequire mkvtoolnix to be installed.  This is used by default, but the\noption `--no-mkv-props` will disable this.\n\n\n\n## Usage\n\nStart with a directory of your videos (example: `day1-raw.mkv`).\n\nCreate an edit list file (described in next section).  A minimal\nexample is:\n\n```yaml\n- input: day1-raw.mkv\n\n- output: part1.mkv\n  title: This is the title of part1\n  description: \u003e-\n    This is the multi-line description\n    of part 1.\n  editlist:\n    - start: 00:00   # These are time segments to include\n    - 4:00: Begin exercise 1\n    - stop: 5:00\n    - start: 6:13\n    - -: Going over the exercises  # '-' means \"latest start time\".\n    - stop: 99:00\n```\n\nThe general usage is then:\n\n```\npython ffmpeg-editlist.py EDITLIST.yaml INPUT-DIR [-o OUTPUT-DIR]\n```\n\nWhere `INPUT-DIR` is the search path for input files and `OUTPUT-DIR`\n(default `.`) is the output path for files.  You can use the `--limit\nPATTERN` option to reprocess only a few videos (example: `--limit\npart1`).\n\nBecause of the way keyframes work, there may be missing segments\naround the transition points.  After you have tested that your timings\nseem reasonable, re-run with ``--reencode`` and it will do a full\nre-encoding and make a seamless videos.  The default encoding settings\nare designed to be slow but good enough for all practical purposes:\n\n```\npython ffmpeg-editlist.py EDITLIST.yaml --reencode INPUT-DIR [-o OUTPUT-DIR]\n```\n\n`OUTPUT-DIR` will get the encoded files, and `.txt` files with the\nvideo descriptions ready to upload to your video hosting site.\n\nSubtitles: The option `--srt` will make ffmpeg-editlist reprocess\nsubtitles just like video segments (cut to the segments and adjust\ntimestamps).  A `.srt` file is expected alongside each input file\n(input file's extension replaced with `.srt`), and an output is\nsimilarly placed alongside each output.  Warning: make sure that no\nsegment's declared stop time goes beyond the actual file length.  It's\npossible that there will be some weird effects around the\nbeginning/end of the segments if subtitles go beyond the start/stop.\n\nShow realtime schedule:  A `- schedule-sync: SCHEDULETIME=REALTIME`\nentry in the yaml file will allow `--show-schedule --dry-run -cq [-l\nday2]` to print the timings, translated to a real-time schedule\nsuitable as reference for future years.\n\n\n\n## Editlist definition\n\n\n### Minimal example: single file\n\n```yaml\n# Input is taken from command line argument `input`.\n- output: output.mp4\n  title: This is the title\n  description: \u003e-\n    This is the multi-line description.\n  editlist:\n    - start: 00:00   # These are time segments to include\n    - stop: 5:00\n    - start: 6:13\n    - stop: 99:00\n```\n\nRun with `python ffmpeg-editlist.py editlist.yaml input.mkv`.\n\n\n### Minimal example with multiple files\n\n```yaml\n\n- input: raw-day1.mkv\n  output: day1-part1.mkv\n  editlist:\n    - start: 1:12\n    - stop: 55:30\n\n# Previous input file is used if no new input is defined\n- output: day1-part2.mkv\n  editlist:\n    - start: 1:00:12\n    - stop: 1:54:00\n```\n\nRun with `python ffmpeg-editlist.py editlist.yaml $input_directory`.\n\n\n### Multi-file with video descriptions\n\nThis is a full example that demonstrates all features.\n\n```yaml\n\n- workshop_description: \u003e\n    If this exists, it will be appended to the bottom of every video\n    description.  For example, it can be general information about the\n    overall workshop.\n\n# This input will be used for all segments until redefined\n# Input relative to the input-dir command line argument.\n# If not given, use the raw input-dir argument as a filename.\n- input: cr-2021may-day1-obs.mov\n- schedule-sync: 00:12:20=9:00:00\n\n# A basic example\n# Output is relative to the output-dir command line argument.\n- output: day1-welcome.mp4\n  # If given, do not reencode this segment even if --reencode is given\n  # (useful for segments that start at 00:00).  'reencode: true' does\n  # nothing, this is the default and --reencode must still be\n  # specified on the command line.\n  #reencode: false\n  editlist:\n    - start: 12:20\n    - stop: 31:14\n\n# Git-intro day 1\n- output: day1-git-intro-1.mp4    # Output filename\n  title: YouTube Video Title\n  description: \u003e\n    Description of the video.\n  editlist:\n    # These pairs are times to *include*\n    - start: 31:14\n    - stop: 38:13\n    - start: 41:28\n    - stop: 1:04:45\n\n# A sample including table of contents entries.\n# You need to map times from the raw file, to the output file, in\n# order to make a clickable YouTube table of contents.\n# They are times in the\n# original video, and they are converted to the equivalent times in\n# the processed videos. They must be within the ranges above (and\n# you get a unhandled error if they aren't):\n#   segment_start \u003c= toc_time \u003c segment_stop.\n# These can be interspersed with the segment definitions.\n# Example:\n- output: day2-git-intro-2.mp4\n  editlist:\n    - start: 31:14\n    # TOC entry:\n    - 31:14: Overview of the day\n    - 33:25: Motivation to version control\n    - stop: 38:13\n    - start: 41:28\n    - -: Basics of version control        # '-' is an alias for \"last start\"\n    - 48:35: \"Exercise: record changes\"   # has a ':', so must be quoted\n    - stop: 1:04:45\n    #- 1:18:22: This will fail, timestamp after the end\n\n# This syntax is used to cover a segment of the video:\n- output: day3-has-audience-visible.mp4\n  editlist:\n  - start: 00:00\n  # Cover an area.  begin/end are clear.  w and h are width and\n  # height.  x and y are offset (left, down) from the top-left corner\n  - cover: {begin: \"1:15:29\", end: \"1:51:34\", w: 840, h: 300, x: 360}\n  - stop: 5:00\n\n\n```\n\nAlongside the `.mp4` output file, a `.mp4.info.txt` file is created\nwith these contents.  This is designed for easy copying and pasting\ninto hosting sites:\n\n```\nTitle of Video\n\nVideo description.\n\n01:53 Table of contents entry 1\n15:45 Table of contents entry 2\n...\n\n\nWorkshop description.\n```\n\n\n### Multiple inputs\n\nMultiple inputs in one segment might be useful when you are attaching\nan introduction to the main video.  Note that things might go wrong if\nthe video sizes and codecs do not align perfectly.  (TODO: does this\nwork as expected?)\n\n```yaml\n- output: output.mp4\n  editlist:\n    - input: intro.mkv\n    - start: 00:00\n    - stop: 99:00\n    - input: main.mkv\n    - start: 0:00\n    - stop: 99:00\n    - input: outro.mkv\n    - start: 0:00\n    - stop: 99:00\n\n```\n\n\n\n## See also\n\n* https://trac.ffmpeg.org/wiki/Concatenate\n* https://stackoverflow.com/q/7333232\n* Inspired by\n  https://github.com/mvdoc/budapest-fmri-data/blob/master/scripts/preprocessing-stimulus/split_movie.sh\n* mpv (https://mpv.io/) is a video player with good command line\n  interface, keyboard shortcuts, and scriptable so that you can make a\n  hotkey that copies the current time.\n  * script for the mpv video player, which can copy the time when you\n    push a button.  Might need some modification:\n    https://github.com/Kr4is/mpv-copy-time\n  * `[` and `]` change the playback speed.  `O` causes time to be\n    permanently shown.  `→` and `←` seek, `↑` and `↓` seek longer, and\n    `--hr-seek` allows seeking exactly.  `Control+→`, `Control+←`\n    allow seeking to the next/previous subtitle.\n\n\n## Status / Contributing\n\nAlpha/beta, under development, it is starting to become reusable but\nstill development is for a few use cases.  In order to use this you\nprobably have to read some code / work around some bugs since it isn't\nwell tested yet.\n\nBug reports or improvements welcome, but it is kind of a mess now.\nTest with ``pytest ffmpeg-editlist.py``, but note that main\nfunctionality is not tested right now.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoderefinery%2Fffmpeg-editlist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoderefinery%2Fffmpeg-editlist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoderefinery%2Fffmpeg-editlist/lists"}