For Windows, you may have to install [7z](https://www.7-zip.org/download.html), and then put the 7z folder in your `PATH`.
### Running
### Run app in development mode
```bash
yarn dev
```
### Run all code checks and tests
Run linting, code style, types, tests etc with the following command:
```bash
yarn check
```
Most of these checks are automatically run in GitHub Actions.
### Other scripts
See [package.json](./package.json) "scripts" section.
### Contributing code
To contribute code, use [pull requests](https://github.com/mifi/lossless-cut/pulls). If you would like to contribute a lot of code, please first create an issue to check the viability of your change.
## `mas-dev` (Mac App Store) local build
This will sign using the development provisioning profile:
@ -109,14 +127,6 @@ Before releasing, consider [Maintainence chores](#maintainence-chores) first.
For per-platform build/signing setup, see [this article](https://mifi.no/blog/automated-electron-build-with-release-to-mac-app-store-microsoft-store-snapcraft/).
## Weblate
`yarn scan-i18n` to get the newest English strings and push so Weblate gets them.
Find the [latest PR](https://github.com/mifi/lossless-cut/pulls) from Weblate and **rebase+merge** it.
**Warning:** Do not squash and merge (see [here why](docs/translation.md#weblate))!
## Minimum OS version
See [requirements](docs/requirements.md).
@ -152,10 +162,13 @@ Links:
- `@electron/remote`
- `package.json` / `yarn.lock`
### i18n
```bash
yarn scan-i18n
```
### i18n strings / Weblate
Run `yarn scan-i18n` to get the newest English strings and push so Weblate gets them.
Find the [latest PR](https://github.com/mifi/lossless-cut/pulls) from Weblate and **rebase+merge** it.
**Warning:** Do not squash and merge (see [here why](docs/translation.md#weblate))!
@ -6,6 +6,8 @@ Please read the documentation before creating an issue. Thank you 🙏
- **Q:** Is there a keyboard shortcut to do X?
- **A:** First check the Keyboard shortcuts dialog. If you cannot find your shortcut there, [see this issue.](https://github.com/mifi/lossless-cut/issues/254)
- **Q:** How can I import multiple files into my project and edit them together?
- **A:** LosslessCut is not a [non-linear editor](https://github.com/mifi/lossless-cut/issues/976) (like Adobe Premiere) and it is not a linear editor (like Avidemux) - it is something in-between. You create segments on the timeline and the segments represent which portions of video will be exported. If you want more advanced workflows, you have to do it in multiple separate operations.
- **Q:** Can LosslessCut be automated using a CLI or API or do external post-processing?
- **A:** While it was never designed for advanced batching/automation, it does have a [basic CLI and a HTTP API](cli.md). More info: [#980](https://github.com/mifi/lossless-cut/issues/980) [#868](https://github.com/mifi/lossless-cut/issues/868).
- **Q:** I cannot find a particular button or function.
@ -17,7 +19,7 @@ Please read the documentation before creating an issue. Thank you 🙏
- **Q** What's the difference between the app in the Apple/Microsoft App Store vs. GitHub releases?
- **A** LosslessCut version in the App Stores is often a few versions behind the latest GitHub version, because I want to be sure that the new versions work perfectly before releasing in the App Stores. The GitHub version will contain new, untested features and may contain some bugs (even in existing functionality). I consider the newest GitHub versions to be a public "beta" test. Then, once I'm sure that the new version works well, I will release it in the App Stores as well to give a frictionless as possible experience for customers. They have exactly the same in-app features, except for a few platform limitations: Apple doesn't allow opening VOB files with App Store apps. Apple App Store apps run in a sandbox, and therefore need to prompt for output directory before allowing writing files.
- **Q**: What are all these `.llc` files that get created?
- **A***: Their purpase is to save your segments so that when you exit/re-open LosslessCut you can continue where you left off. You can turn them off in app settings. See also [#2115](https://github.com/mifi/lossless-cut/issues/2115).
- **A***: Their purpose is to save your segments so that when you exit/re-open LosslessCut you can continue where you left off. You can turn them off in app settings. See also [#2115](https://github.com/mifi/lossless-cut/issues/2115).
## Commonly requested features
@ -53,7 +55,7 @@ If you have a problem with the app or with a file, please see the [🤔 Troubles
- **Drag and drop** a video file into player or use <kbd>⌘</kbd>/<kbd>CTRL</kbd> + <kbd>O</kbd>.
- <kbd>SPACE</kbd> to play/pause or <kbd>←</kbd><kbd>→</kbd><kbd>,</kbd><kbd>.</kbd> or mouse/trackpad wheel to seek back/forth.
- Set the start/end times of the current segment by first moving the timeline cursor and then pressing <kbd>I</kbd> to set start time and <kbd>O</kbd> to set end time. You can also press hold <kbd>SHIFT</kbd> while dragging a segment with the mouse to move or resize it
- <kbd>+</kbd> to create a new segment.
- <kbd>+</kbd> to create another marker and repeat the previous step to convert it into a segment.
- <kbd>B</kbd> to split the segment at the timeline cursor.
- <kbd>BACKSPACE</kbd> to remove cutpoint/segment.
- If you create segments without an end time, it is a [marker](#markers) instead of a segment.
@ -14,23 +14,29 @@ If the video exports successfully without any error from LosslessCut, but it doe
## Cutting times are not accurate
Each segment's *start cut time* normally (but not always) will be "rounded" to the nearest **previous** keyframe. This means that you often have to move the **start cut time** to **few frames after** the desired keyframe.
Each segment's *start cut time*will normally (but not always) be "rounded" to the nearest **previous** keyframe. This means that you often have to move the **start cut time** to **few frames after** the desired keyframe.
- Lossless cutting is not an exact science. For some files, it just works. For others, you may need to trial and error to get the best cut. See [#330](https://github.com/mifi/lossless-cut/issues/330)
- Your mileage may vary when it comes to *Keyframe cut mode*. Most common video files need *Keyframe cut* enabled, but you may need to try both values. [ffmpeg](https://trac.ffmpeg.org/wiki/Seeking) also has documentation about these two seek/cut modes. In `ffmpeg`, *Keyframe cut* corresponds to `-ss`*before*`-i`.
- Try to change `avoid_negative_ts` (in export options).
- Try also to set the **start**-cutpoint a few frames **before or after** the nearest keyframe (may also solve audio sync issues).
- You may try to enable the new "Smart cut" mode to allow cutting between keyframes. However it is very experimental and may not work for many files.
- You may try to enable the experimental "Smart cut" mode to allow cutting between keyframes. However it will not work for many files.
- Currently, the only way to review the exported file (to check the actual cutpoints) is to run the export (possibly with only one segment enabled to speed up) and then manually check the output file. See also [#1887](https://github.com/mifi/lossless-cut/issues/1887)
### Cut starts from wrong keyframe
For some files, when you place segment start cutpoints at keyframes, and you export, it will instead cut from the keyframe **before** the keyframe that you wanted. This is because with some videos, ffmpeg struggles to find the nearest previous keyframe, see [#1216](https://github.com/mifi/lossless-cut/issues/1216). To workaround this, you can try to shift your segments' **start**-cutpoints forward by a few frames, so that ffmpeg correctly cuts from the *previous* keyframe. You can also enable the Export Option "Shift all start times" by +1, +2, +3 frames or so.
For some files, when you place segment start cutpoints at keyframes, and you export, it will instead cut from the keyframe **before** the keyframe that you wanted. This is because with some videos, FFmpeg struggles to find the nearest previous keyframe, see [#1216](https://github.com/mifi/lossless-cut/issues/1216). To workaround this, you can try to shift your segments' **start**-cutpoints forward by a few frames, so that ffmpeg correctly cuts from the *previous* keyframe.
- Menu: "Edit" -> "Segments" -> "Shift all segments on timeline"
- Enter `00:00:00.200` (or a larger value if it doesn't help)
- When asked about Start or End timestamps, Select **Start**
This will effectively shift all start times of segments by 6 frames (`6/30=0.2` for 30fps video).
This will effectively shift all start times of segments by 6 frames (`6/30=0.2` for 30fps video). Alternatively, to always shift start times, you can enable the Export Option "Shift all start times" by +1, +2, +3 frames or so.
In some videos (like VP9 from `yt-dlp`), some start keyframes are being ignored if LosslessCut output format is set to Matroska. A workaround is to set it to MP4 instead. See [#2804](https://github.com/mifi/lossless-cut/issues/2804).
## Start cut is ignored
Check whether your video has enough keyframes before your cutpoint. You can see keyframes on the timeline as vertical lines. You may have to zoom in to see them. If there are no keyframes, then it is most likely not possible to cut your file at the desired cutpoints losslessly. You can try to enable the Smart Cut mode, alternatively you may try to disable "keyframe cut" mode. See also (#2864)(https://github.com/mifi/lossless-cut/issues/2864).
## Cut file has same length as input
@ -85,6 +91,10 @@ LosslessCut uses the same video player that is used by Chrome. You can try to op
Some formats or codecs are not natively supported by LosslessCut's built in player, and LosslessCut will automatically use FFmpeg-assisted software decoding to playback in a lower quality. For better playback you may convert these files to a different format from the menu: *File -> Convert to supported format*. Note that this will not affect the output from LosslessCut, it is only used for playback, see [#88](https://github.com/mifi/lossless-cut/issues/88).
### Auto convert to supported format on certain timestamp
If LosslessCut automatically tries to convert your file into a supported format when you start playing/seeking and reach a certain timestamp, see issue [#2893](https://github.com/mifi/lossless-cut/issues/2893).
## Linux specific issues
- If the app crashes on startup or you get an error like `FATAL:setuid_sandbox_host.cc(157)] The SUID sandbox helper binary was found, but is not configured correctly...`, try to run it as `./lossless-cut --no-sandbox`. See [#258](https://github.com/mifi/lossless-cut/issues/258) and [#2614](https://github.com/mifi/lossless-cut/issues/2614)
"Manually input current segment's end time":"Manually input current segment's end time",
"Manually input current segment's start time":"Manually input current segment's start time",
"Markers":"Markers",
"Markers are segments without an end time and will not be exported. Convert markers to segments by setting their end time.":"Markers are segments without an end time and will not be exported. Convert markers to segments by setting their end time.",
"Markers:":"Markers:",
"Max length":"Max length",
"Maximize":"Maximize",
@ -517,6 +518,7 @@
"Open":"Open",
"Open file":"Open file",
"Open folder":"Open folder",
"Open link in browser":"Open link in browser",
"Open media from URL":"Open media from URL",
"Open next file":"Open next file",
"Open previous file":"Open previous file",
@ -742,6 +744,7 @@
"The audio track is not supported while previewing. You can convert to a supported format from the menu":"The audio track is not supported while previewing. You can convert to a supported format from the menu",
"The expression must return an object":"The expression must return an object",
"The file name template is missing {{ext}} and will result in a file without the suggested extension. This may result in an unplayable output file.":"The file name template is missing {{ext}} and will result in a file without the suggested extension. This may result in an unplayable output file.",
"The language tag (ISO 639-2 code). For example \"eng\" for English. This is used by some players to select the appropriate audio/subtitle track based on the user's language preferences.":"The language tag (ISO 639-2 code). For example \"eng\" for English. This is used by some players to select the appropriate audio/subtitle track based on the user's language preferences.",
"The last executed ffmpeg commands will show up here after you run operations. You can copy them to clipboard and modify them to your needs before running on your command line.":"The last executed ffmpeg commands will show up here after you run operations. You can copy them to clipboard and modify them to your needs before running on your command line.",
"The media file referenced by the project file you tried to open does not exist in the same directory as the project file: {{mediaFileName}}":"The media file referenced by the project file you tried to open does not exist in the same directory as the project file: {{mediaFileName}}",
"The media you tried to open does not exist":"The media you tried to open does not exist",
@ -821,6 +824,7 @@
"User interface":"User interface",
"Using built-in ffmpeg":"Using built-in ffmpeg",
"Using external ffmpeg":"Using external ffmpeg",
"Value must be shorter than total duration ({{totalDuration}})":"Value must be shorter than total duration ({{totalDuration}})",
// Used to be 5, but we recently increased to 6 because https://github.com/mifi/lossless-cut/issues/2838
// I don't remember why 5 was chosen initially, but if we don't truncate, ffmpeg can sometimes give an error when too many decimal places are used in the time argument, see:
<divstyle={{width:'100%',height:'100%',position:'absolute',cursor:'pointer'}}title={t('Open link in browser')}role="button"onClick={(e)=>{e.stopPropagation();if('targetUrl'inmifiLink&&typeofmifiLink.targetUrl==='string')electron.shell.openExternal(mifiLink.targetUrl);}}/>
description: t('The language tag (ISO 639-2 code). For example "eng" for English. This is used by some players to select the appropriate audio/subtitle track based on the user\'s language preferences.'),
// if estimated actual output length of segment is more than 1.5 times the intended segment duration, then we consider it problematic and warn the user about it.
<Dialog.Description>{t('Shift all segments on the timeline by this amount. Negative values will be shifted back, while positive value will be shifted forward in time.')}</Dialog.Description>
text: i18n.t('Shift all segments on the timeline by this amount. Negative values will be shifted back, while positive value will be shifted forward in time.'),
inputValidator:(v)=>{
constparsed=parseValue(v);
if(parsed==null)returni18n.t('Please input a valid duration. Example: {{example}}',{example: inputPlaceholder});
returnnull;
},
});
if(value==null)returnundefined;
constparsed=parseValue(value);
invariant(parsed!=null);
conststartOrEnd=awaitaskForSegmentsStartOrEnd(i18n.t('Do you want to shift the start or end timestamp by {{time}}?',{time: formatDuration({seconds: parsed,shorten: true})}));
if(startOrEnd==null)returnundefined;
return{
shiftAmount: parsed,
shiftKeys: startOrEnd,
};
}
exportasyncfunctionaskForAlignSegments() {
conststartOrEnd=awaitaskForSegmentsStartOrEnd(i18n.t('Do you want to align the segment start or end timestamps to keyframes?'));
@ -509,10 +517,10 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
// No progress if we set loglevel warning :(
// '-loglevel', 'warning',
'-ss',cutFrom.toFixed(5),// if we don't -ss before -i, seeking will be slow for long files, see https://github.com/mifi/lossless-cut/issues/126#issuecomment-1135451043
'-ss',formatFfmpegTime(cutFrom),// if we don't -ss before -i, seeking will be slow for long files, see https://github.com/mifi/lossless-cut/issues/126#issuecomment-1135451043
'-i',filePath,
'-ss','0',// If we don't do this, the output seems to start with an empty black after merging with the encoded part
'-t',(cutTo-cutFrom).toFixed(5),
'-t',formatFfmpegTime(cutTo-cutFrom),
...mapStreamsArgs,
@ -717,7 +725,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea