60 changes: 60 additions & 0 deletions .yarn/patches/app-builder-lib-npm-24.8.0-51e1f5cd3f.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
diff --git a/out/macPackager.js b/out/macPackager.js
index 30787ba358acb9277a63df05c839a0caeea8ad91..a9e5efe7e442607f1a44c658cbf6f78093387f40 100644
--- a/out/macPackager.js
+++ b/out/macPackager.js
@@ -276,7 +276,7 @@ class MacPackager extends platformPackager_1.PlatformPackager {
await this.doFlat(appPath, artifactPath, masInstallerIdentity, keychainFile);
await this.dispatchArtifactCreated(artifactPath, null, builder_util_1.Arch.x64, this.computeSafeArtifactName(artifactName, "pkg", arch, true, this.platformSpecificBuildOptions.defaultArch));
}
- await this.notarizeIfProvided(appPath);
+ if (!isMas) await this.notarizeIfProvided(appPath);
return true;
}
async getOptionsForFile(appPath, isMas, customSignOptions) {
@@ -407,24 +407,36 @@ class MacPackager extends platformPackager_1.PlatformPackager {
builder_util_1.log.info({ reason: "`notarizeOptions` is explicitly set to false" }, "skipped macOS notarization");
return;
}
+
+ let options;
+ // option 1: app-specific password
const appleId = process.env.APPLE_ID;
const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD;
- if (!appleId && !appleIdPassword) {
+ // option 2: API key
+ const appleApiKey = process.env.APPLE_API_KEY;
+ const appleApiKeyId = process.env.APPLE_API_KEY_ID;
+ const appleApiIssuer = process.env.APPLE_API_ISSUER;
+ if (appleId || appleIdPassword) {
+ if (!appleId) {
+ throw new builder_util_1.InvalidConfigurationError(`APPLE_ID env var needs to be set`);
+ }
+ if (!appleIdPassword) {
+ throw new builder_util_1.InvalidConfigurationError(`APPLE_APP_SPECIFIC_PASSWORD env var needs to be set`);
+ }
+
+ options = this.generateNotarizeOptions({ appPath, appleId, appleIdPassword });
+ } else if (appleApiKey || appleApiKeyId || appleApiIssuer) {
+ options = this.generateNotarizeOptions({ appPath, appleApiKey, appleApiKeyId, appleApiIssuer });
+ } else {
// if no credentials provided, skip silently
return;
}
- if (!appleId) {
- throw new builder_util_1.InvalidConfigurationError(`APPLE_ID env var needs to be set`);
- }
- if (!appleIdPassword) {
- throw new builder_util_1.InvalidConfigurationError(`APPLE_APP_SPECIFIC_PASSWORD env var needs to be set`);
- }
- const options = this.generateNotarizeOptions(appPath, appleId, appleIdPassword);
await (0, notarize_1.notarize)(options);
+
builder_util_1.log.info(null, "notarization successful");
}
- generateNotarizeOptions(appPath, appleId, appleIdPassword) {
- const baseOptions = { appPath, appleId, appleIdPassword };
+ generateNotarizeOptions({ appPath, appleId, appleIdPassword, appleApiKey, appleApiKeyId, appleApiIssuer }) {
+ const baseOptions = { appPath, appleId, appleIdPassword, appleApiKey, appleApiKeyId, appleApiIssuer };
const options = this.platformSpecificBuildOptions.notarize;
if (typeof options === "boolean") {
const proj = {
16 changes: 8 additions & 8 deletions .yarn/plugins/@yarnpkg/plugin-licenses.cjs

Large diffs are not rendered by default.

807 changes: 0 additions & 807 deletions .yarn/releases/yarn-3.3.0.cjs

This file was deleted.

893 changes: 893 additions & 0 deletions .yarn/releases/yarn-4.0.2.cjs

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
compressionLevel: mixed

nodeLinker: node-modules

plugins:
- path: .yarn/plugins/@yarnpkg/plugin-licenses.cjs
spec: "https://raw.githubusercontent.com/mhassan1/yarn-plugin-licenses/v0.8.1/bundles/@yarnpkg/plugin-licenses.js"
- checksum: 55d54388ad171beb0a12e808e375d3c5f8632556a2eed7d03b38b8304514b07831ef7a9f9e78410287313940d107ea4634cfa2933e08459a165c432cea55bdba
path: .yarn/plugins/@yarnpkg/plugin-licenses.cjs
spec: "https://raw.githubusercontent.com/mhassan1/yarn-plugin-licenses/v0.13.1/bundles/@yarnpkg/plugin-licenses.js"

yarnPath: .yarn/releases/yarn-3.3.0.cjs
yarnPath: .yarn/releases/yarn-4.0.2.cjs
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<div align="center">
<br>
<br>
<p><a href="https://mifi.no/losslesscut/"><img src="src/icon.svg" width="130" alt="LosslessCut" /></a></p>
<p><a href="https://mifi.no/losslesscut/"><img src="src/renderer/src/icon.svg" width="120" alt="LosslessCut" /></a></p>
<p><b>LosslessCut</b></p>
The swiss army knife of lossless video/audio editing
<br>
Expand All @@ -12,7 +11,7 @@
<a href="https://mifi.no/thanks/">Thanks to my supporters</a> and everyone who purchased LosslessCut!
<br>
<br>
<p align="center"><img src="https://github.com/mifi/lossless-cut/raw/master/main_screenshot.jpg" width="600" alt="screenshot" /></p>
<p align="center"><img src="main_screenshot.jpg" width="600" alt="screenshot" /></p>
<br>
<br>
</div>
Expand All @@ -32,7 +31,7 @@ The main feature is lossless trimming and cutting of video and audio files, whic
- Remove unneeded tracks
- Replace or re-encode only some tracks
- Extract all tracks from a file (extract video, audio, subtitle, attachments and other tracks from one file into separate files)
- Batch view for fast multi-file workflow (note: no mass export yet)
- Fast multi-file workflow (note: no mass/batch export yet)
- Keyboard shortcut workflow
- Losslessly remux video/audio into a different container (file) format
- Take full-resolution snapshots from videos in JPEG/PNG format (low or high quality)
Expand All @@ -57,7 +56,8 @@ The main feature is lossless trimming and cutting of video and audio files, whic
- Customizable keyboard hotkeys
- Black scene detection, silent audio detection, and scene change detection
- Divide timeline into segments with length L or into N segments or even randomized segments!
- [Basic CLI support](cli.md)
- Speed up / slow down video or audio file ([changing FPS](https://github.com/mifi/lossless-cut/issues/1712))
- Basic [CLI](cli.md) and [HTTP API](api.md)

## Example lossless use cases

Expand All @@ -83,6 +83,7 @@ The main feature is lossless trimming and cutting of video and audio files, whic
- Losslessly split a video into one file per scene (note you probably have to shift segments, see [#330](https://github.com/mifi/lossless-cut/issues/330).)
- Cut away silent parts of an audio/video
- Split video into segments to for example respect Twitter's 140 second limit
- Annotate each segment with one or more tags, then use those tags to organize your segments or use it to create an output folder structure or hierarchy for your segments.

### Export cut times as YouTube Chapters
1. Export with Merge and "Create chapters from merged segments" enabled
Expand All @@ -102,7 +103,7 @@ First export each track as individual files. Then use Handbrake or similar to re

## Download

If you want to support my continued work on LosslessCut, and you want the advantage of a secure and simple installation process with automatic updates, consider getting it from your favorite store:
If you want to support my continued work on LosslessCut, and you want the advantage of a secure and simple installation process with automatic, stable updates, consider getting it from your favorite store:

<a href="https://apps.apple.com/app/id1505323402"><img src="mac-app-store-badge.svg" alt="Mac App Store" height="50"/></a> <a href="https://www.microsoft.com/store/apps/9P30LSR4705L?cid=storebadge&ocid=badge"><img src="ms-store-badge.svg" alt="MS badge" height="50"/></a>

Expand All @@ -112,28 +113,26 @@ For Linux these are some alternatives:

If you prefer to download the executables manually, this will of course always be free:

- Mac OS X: [Intel DMG](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-mac-x64.dmg) / [Apple Silicon DMG](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-mac-arm64.dmg)
- Mac OS X: [Intel DMG](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-mac-x64.dmg) / [Apple Silicon DMG](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-mac-arm64.dmg) (note that universal.pkg **does not work**)
- Windows: [7zip](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-win-x64.7z) (Windows 7, 8 and 8.1 is **no longer supported** after [v3.50.0](https://github.com/mifi/lossless-cut/releases/tag/v3.50.0))
- Linux: [x64 tar.bz2](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-linux-x64.tar.bz2) / [x64 AppImage](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-linux-x86_64.AppImage) / [arm64 tar.bz2](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-linux-arm64.tar.bz2) / [Raspberry Pi armv7l](https://github.com/mifi/lossless-cut/releases/latest/download/LosslessCut-linux-armv7l.tar.bz2)
- [More releases](https://github.com/mifi/lossless-cut/releases) (note that APPX for Windows and PKG for MacOS **do not work**)

If you find LosslessCut useful, I'm very thankful for [donations](https://github.com/mifi/lossless-cut#donate-).

### Difference between App Stores and GitHub download
If you find LosslessCut useful, I'm very thankful for [your support](https://github.com/mifi/lossless-cut#donate-).

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 need to prompt for output directory. LosslessCut version in the App Stores is a few versions behind the GitHub version, because I want to be sure that the new versions work perfectly before releasing in the App Stores. GitHub version can contain new, untested features and may contain some bugs. I consider the newest GitHub versions to be a public "beta" test.
**What's the difference between App Stores and GitHub download?** [Please see FAQ](issues.md#app-stores-and-github-difference)

### Nightly builds 🧪

If you want to test the very latest and greatest bleeding-edge version. 1. Go to [Actions](https://github.com/mifi/lossless-cut/actions/workflows/build.yml), 2. click on the latest *Build/release*, 3. scroll all the way down to *Artifacts*. **Note:** these builds may be totally broken!

## [Supported operating systems](./requirements.md)

## Supported formats

Since LosslessCut is based on Chromium and uses the HTML5 video player, not all FFmpeg supported formats will be supported smoothly.
The following formats/codecs should generally work: MP4, MOV, WebM, MKV, OGG, WAV, MP3, AAC, H264, Theora, VP8, VP9
For more information about supported formats / codecs, see https://www.chromium.org/audio-video.
LosslessCut uses Chromium's HTML5 video player, and not all FFmpeg supported formats and codecs are natively supported. Generally, the following formats should work: `MP4`, `MOV`, `WebM`, `Matroska`, `OGG` and `WAV`. The following audio codecs: `FLAC`, `MP3`, `Opus`, `PCM`, `Vorbis` and `AAC`. Video codecs: `H264`, `AV1`, `Theora`, `VP8`, `VP9` and `H265` (needs hardware decoder). More information about [Chromium supported formats / codecs](https://www.chromium.org/audio-video). [What's the difference between a codec and a format?](./issues.md#primer-video--audio-formats-vs-codecs)

Unsupported files can still be converted to a supported format/codec from the `File` menu. (Try the "fastest" option first.) A low quality version of the file (with/without audio) will then be created and opened in the player. The actual cut/export operation will still be performed on the original file, so it will be lossless. This allows for potentially opening any file that FFmpeg is able to decode.
Codecs and formats not listed above can still be converted to a supported format/codec from the `File` menu. (Try the `fastest` option first.) A low quality version of the file (with/without audio) will then be created and opened in the player. The actual cut/export operation will still be performed on the original file, so it will be lossless. This allows for potentially opening any file that FFmpeg is able to decode.

## Video demos

Expand Down Expand Up @@ -162,16 +161,18 @@ Unsupported files can still be converted to a supported format/codec from the `F
- Press the **Camera** button (or <kbd>C</kbd>) if you want to take a JPEG/PNG snapshot from the current time
- If you want to move the original file to trash, press the **trash** button
- For best results you may need to trial and error with another output format (Matroska takes nearly everything), change keyframe cut mode or disable some tracks (see [known issues](issues.md)).
- Press <kbd>H</kbd> to view help and all keyboard shortcuts.
- Press <kbd>SHIFT</kbd> + <kbd>/</kbd> to view all keyboard & mouse shortcuts.
- **Note:** The original video file will not be modified. Instead, a file is created file in the same directory as the original file with from/to timestamps in the file name.

## [Import / export](import-export.md)

## [Command line interface (CLI)](cli.md)
## [Command line interface (CLI)](cli.md) & [HTTP API](api.md)

## [Known issues, limitations, troubleshooting, FAQ](issues.md)

## [Contributing](developer-notes.md)

## [Known issues, limitations, troubleshooting, FAQ](issues.md)
## [Translation](translation.md)

If you have any problem or question, [please read this](issues.md) before creating an issue. I try to answer most common questions here.

Expand All @@ -184,15 +185,17 @@ This project is maintained by me alone. The project will always remain free and
## Featured

- [Featured in the Console newsletter](https://console.substack.com/p/console-93)
- [Hacker News](https://news.ycombinator.com/item?id=33969490)
- Hacker News [2024](https://news.ycombinator.com/item?id=40829494) [2022](https://news.ycombinator.com/item?id=33969490) [2020-10](https://news.ycombinator.com/item?id=24883030) [2020-01](https://news.ycombinator.com/item?id=22026412) [2016](https://news.ycombinator.com/item?id=12885585)
- Are you using LosslessCut for some interesting project and want your link here? Reach out!

<img src="https://api.star-history.com/svg?repos=mifi/lossless-cut&type=Date" alt="Star History Chart" width="400px" />

## Attributions
- App icon made by [Dimi Kazak](http://www.flaticon.com/authors/dimi-kazak "Dimi Kazak") from [www.flaticon.com](http://www.flaticon.com "Flaticon") is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/ "Creative Commons BY 3.0")
- [Lottie animation](https://lottiefiles.com/7077-magic-flow)
- Thanks to Adi Abinun for his UI sketch work, inspiration and guidance
- [Thanks to everyone for supporting](https://mifi.no/thanks/) my open source work 🙌
- Thanks to translators who helped translate the app. [You can too!](https://hosted.weblate.org/projects/losslesscut/losslesscut/)
- Thanks to translators who helped translate the app. [You can too!](translation.md)

## More software

Expand Down
48 changes: 48 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# HTTP API

LosslessCut can be controlled via a HTTP API, if it is being run with the command line option `--http-api`. See also [CLI](./cli.md). **Note that the HTTP API is experimental and may change at any time.**

## Programmatically opening a file

This must be done with [the CLI](./cli.md).

## Enabling the API

```bash
LosslessCut --http-api
```

## API endpoints

### `POST /api/shortcuts/action`

Execute a keyboard shortcut `action`, similar to the `--keyboard-action` CLI option. This is different from the CLI in that most of the actions will wait for the action to finish before responding to the HTTP request (but not all).

#### [Available keyboard actions](./cli.md#available-keyboard-actions)

#### Example

Export the currently opened file:

```bash
curl -X POST http://localhost:8080/api/shortcuts/export
```

### Batch example

Start the main LosslessCut in one terminal with the HTTP API enabled:

```bash
LosslessCut --http-api
```

Then run the script in a different terminal:

```bash
for PROJECT in /path/to/folder/with/projects/*.llc
LosslessCut $PROJECT
sleep 5 # wait for the file to open
curl -X POST http://localhost:8080/api/shortcuts/export
curl -X POST http://localhost:8080/api/shortcuts/closeCurrentFile
done
```
47 changes: 42 additions & 5 deletions cli.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
# Command line interface (CLI)

LosslessCut has limited support for automation through the CLI. Note that these examples assume that you have set up LosslessCut in your `PATH` environment. Alternatively you can run it like this:
LosslessCut has basic support for automation through the CLI. See also [HTTP API](./api.md).

```bash
LosslessCut [options] [files]
```

Note that these examples assume that you have set up the LosslessCut executable to be available in your `PATH` (command line environment). Alternatively you can run it like this:

```bash
# First navigate to the folder containing the LosslessCut app
cd /path/to/directory/containing/app
# Then run it
# On Linux:
./LosslessCut arguments
# On Windows:
Expand All @@ -18,14 +26,43 @@ LosslessCut file1.mp4 file2.mkv
```

## Override settings (experimental)
See [available settings](https://github.com/mifi/lossless-cut/blob/master/public/configStore.js). Note that this is subject to change in newer versions. ⚠️ If you specify incorrect values it could corrupt your configuration file. You may use JSON or JSON5:
See [available settings](https://github.com/mifi/lossless-cut/blob/master/src/main/configStore.ts). Note that this is subject to change in newer versions. ⚠️ If you specify incorrect values it could corrupt your configuration file. You may use JSON or JSON5. Example:
```bash
LosslessCut --settings-json '{captureFormat:"jpeg", "keyframeCut":true}'
```

## Multiple instances (experimental)
## Other options

- `--locales-path` Customise path to locales (useful for [translators](./translation.md)).
- `--disable-networking` Turn off all network requests.
- `--http-api` Start the [HTTP server with an API](./api.md) to control LosslessCut, optionally specifying a port (default `8080`).
- `--keyboard-action` Run a keyboard action (see below.)
- `--config-dir` Path to a directory where the `config.json` file will be stored and loaded from.

## Controlling a running instance (experimental)

If you have the "Allow multiple instances" setting enabled, you can control a running instance of LosslessCut from the outside, using for example a command line. You do this by issuing messages to it through the `LosslessCut` command. Currently only keyboard actions are supported, and you can open files. *Note that this is considered experimental and the API may change at any time.*

### Keyboard actions, `--keyboard-action`

Simulate a keyboard press action in an already running instance of LosslessCut. Note that the command will return immediately, so if you want to run multiple actions in a sequence, you have to `sleep` for a few seconds between the commands. Alternatively if you want to wait until an action has finished processing, you can use the [HTTP API](./api.md) instead. Note that the HTTP API does not support opening files, and it is currently not possible to wait for a file to have finished opening.

### Available keyboard actions

A list of the available action names can be found in the "Keyboard shortcuts" dialog in the app. Note that you don't have to bind them to any key before using them.

Example:

```bash
# Open a file in an already running instance
LosslessCut file.mp4
sleep 3 # hopefully the file has loaded by now
# Export the currently opened file
LosslessCut --keyboard-action export
```

### Open files in running instance

By default, only a single running instance of LosslessCut is allowed. If you start a new LosslessCut instance from the command line, it will instead pass the list of files onto the already running instance. You can override this behavior by passing this option via the CLI:
```bash
LosslessCut --allow-multiple-instances
LosslessCut file1.mp4 file2.mkv
```
84 changes: 27 additions & 57 deletions developer-notes.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,10 @@
# Contributing

## Translators

You are welcome to help translate the app at [Weblate](https://hosted.weblate.org/projects/losslesscut/losslesscut/). Weblate will automatically push translations as a Pull Request in this repo, but this PR is not merged immediately by maintainers.

Master language is english.

### Testing translations locally

To test new weblate translations you made in the app itself, you need to:
1. Download the translation for your language from Weblate: **Files -> Download translation**
2. Rename the downloaded `.json` file to: `translation.json`
3. Create a [folder structure](https://github.com/mifi/lossless-cut/tree/master/public/locales) somewhere on your computer that looks like this:
```
translations/locales/localeCode
```
You can find a list of the available [`localeCode`s here](https://github.com/mifi/lossless-cut/tree/master/public/locales). In our example we will use `nb_NO` (Norwegian) with this path:
```
/Users/mifi/Desktop/translations/locales/nb_NO
```

4. Now move your `translation.json` file into the folder:
```
/Users/mifi/Desktop/translations/locales/nb_NO/translation.json
```

5. Now run LosslessCut from the [command line](cli.md), with the special command line argument `--locales-path`. Use the path to the **folder containing the locales folder**, e.g.:
```bash
./LosslessCut --locales-path /Users/mifi/Desktop/translations
```

Now LosslessCut will use your language file.

## Development setup

This app is built using Electron.
Make sure you have at least Node v16. The app uses ffmpeg from PATH when developing.

```bash
npm install -g yarn
```

```bash
git clone https://github.com/mifi/lossless-cut.git
cd lossless-cut
Expand All @@ -53,30 +17,26 @@ Note: `yarn` may take some time to complete.

Run one of the below commands:
```bash
npm run download-ffmpeg-darwin-x64
npm run download-ffmpeg-darwin-arm64
npm run download-ffmpeg-linux-x64
npm run download-ffmpeg-win32-x64
yarn download-ffmpeg-darwin-x64
yarn download-ffmpeg-darwin-arm64
yarn download-ffmpeg-linux-x64
yarn download-ffmpeg-win32-x64
```

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

```bash
npm start
yarn dev
```

### Building for production

See:
- https://www.electron.build/
- https://github.com/mifi/lossless-cut/blob/master/.github/workflows/build.yml

## Building mas-dev (Mac App Store) build locally
## `mas-dev` (Mac App Store) local build

This will sign using the development provisioning profile:

```
npm run pack-mas-dev
yarn pack-mas-dev
```

MAS builds have some restrictions, see `isMasBuild` variable in code. In particular, any file cannot be read without the user's consent.
Expand All @@ -92,7 +52,7 @@ NOTE: when MAS (dev) build, Application Support will instead be here:
rm -rf ~/Library/Containers/no.mifi.losslesscut-mac
```

## Windows Store
## Windows Store notes

Windows store version is built as a Desktop Bridge app (with `runFullTrust` capability). This means the app has access to essentially everything the user has access to, and even `internetClient` is redundant.

Expand All @@ -106,13 +66,23 @@ For per-platform build/signing setup, see [this article](https://mifi.no/blog/au

### Release new version

- Commit changes
- If Mac App Store / Windows Store
- Checkout branch `stores`
- Merge `master` into `stores`
- `npm version ...`
- `git push && git push --tags`
- `git push --follow-tags`
- Wait for build and draft in Github actions
- Release draft at github
- Open draft in github and add Release notes
- For files `LosslessCut-mac-universal.pkg` and `LosslessCut-win-x64.appx` add prefix `-DO-NOT-DOWNLOAD`
- If intended as Github, release the draft
- If store-only release, release the draft as **pre-release**

### After release

- If Mac App Store / Windows Store
- Merge `stores` into `master`
- Bump [snap version](https://snapcraft.io/losslesscut/listing)
- `npm run scan-i18n` to get the newest Englist strings and push so weblate gets them
- `yarn scan-i18n` to get the newest English strings and push so weblate gets them

## Minimum OS version

Expand All @@ -126,7 +96,7 @@ Minimum supported OS versions for Electron. As of electron 22:
How to check the value:

```bash
npm run pack-mas-dev
yarn pack-mas-dev
cat dist/mas-dev-arm64/LosslessCut.app/Contents/Info.plist
```

Expand All @@ -152,7 +122,7 @@ Links:
- package.json

### i18n
`npm run scan-i18n`
`yarn scan-i18n`

### Licenses

Expand All @@ -165,7 +135,7 @@ npx license-checker --summary
#### Regenerate licenses file

```
npm run generate-licenses
yarn generate-licenses
#cp licenses.txt losslesscut.mifi.no/public/
```
Then deploy.
37 changes: 37 additions & 0 deletions electron.vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
import react from '@vitejs/plugin-react';


export default defineConfig({
main: {
// https://electron-vite.org/guide/dev#dependencies-vs-devdependencies
// For the main process and preload, the best practice is to externalize dependencies and only bundle our own code.
// However, until we use ESM for electron main, we need to include ESM-only deps in the bundle: (exclude from externalize)
plugins: [externalizeDepsPlugin({ exclude: ['p-map', 'execa', 'nanoid'] })],
build: {
target: 'node18.17',
sourcemap: true,
},
},
preload: {
// https://electron-vite.org/guide/dev#dependencies-vs-devdependencies
plugins: [externalizeDepsPlugin({ exclude: [] })],
build: {
target: 'node18.17',
sourcemap: true,
},
},
renderer: {
plugins: [react()],
build: {
target: 'chrome118',
sourcemap: true,
chunkSizeWarningLimit: 3e6,
},
server: {
port: 3001,
host: '127.0.0.1',
https: false,
},
},
});
2 changes: 2 additions & 0 deletions entitlements.mas.plist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
Expand Down
11 changes: 11 additions & 0 deletions expressions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Expressions

## Select segments by expression

LosslessCut has support for normal JavaScript expressions. You will be given a variable `segment` and can create an expression that returns `true` or `false`. For example to select all segments with a duration of less than 5 seconds use this expression:

```js
segment.duration < 5
```

See more examples in-app.
785 changes: 785 additions & 0 deletions ffprobe.ts

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions i18next-parser.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// eslint-disable-line unicorn/filename-case
export default {
input: ['src/**/*.{js,jsx}', 'public/*.js'],
input: ['src/renderer/**/*.{js,jsx,ts,tsx}', 'src/main/*.{js,ts}'],

output: 'public/locales/$LOCALE/$NAMESPACE.json',
output: 'locales/$LOCALE/$NAMESPACE.json',
indentation: 4,

sort: true,
Expand All @@ -15,7 +16,7 @@ export default {

defaultValue: (lng, ns, key) => key,

// Keep in sync between i18next-parser.config.js and i18n-common.js:
// Keep in sync between i18next-parser.config.js and i18nCommon.js:
keySeparator: false,
namespaceSeparator: false,
};
24 changes: 15 additions & 9 deletions import-export.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@

## Customising exported file names

When exporting multiple segments as separate files, LosslessCut offers you the ability to specify how the output files will be named in sequence. The following variables are available to customize the filenames:
When exporting multiple segments as separate files, LosslessCut offers you the ability to specify how the output files will be named in sequence using a *template string*. The template string is evaluated as a [JavaScript template string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), so you can use JavaScript syntax inside of it. The following variables are available in the template to customize the filenames:

| Variable | Output |
| -------------- | - |
| `${FILENAME}` | The original filename without the extension (e.g. `Beach Trip` for a file named `Beach Trip.mp4`)
| `${EXT}` | The extension of the file (e.g.: `.mp4`, `.mkv`)
| `${SEG_NUM}` | Number of the segment (e.g. `1`, `2` or `3`)
| `${SEG_LABEL}` | The label of the segment (e.g. `Getting_Lunch`)
| `${SEG_SUFFIX}` | If a label exists for this segment, the label will be used, prepended by `-`. Otherwise, the segment number prepended by `-seg` will be used (e.g. `-Getting_Lunch`, `-seg1`)
| `${CUT_FROM}` | The timestamp for the beginning of the segment in `hh.mm.ss.sss` format (e.g. `00.00.27.184`)
| `${CUT_TO}` | The timestamp for the ending of the segment in `hh.mm.ss.sss` format (e.g. `00.00.28.000`)
| `${SEG_TAGS.XX}` | Allows you to retrieve the tags for a given segment by name. If a tag is called foo, it can be accessed with `${SEG_TAGS.foo}`
| `${FILENAME}` | The original filename *without the extension* (e.g. `Beach Trip` for a file named `Beach Trip.mp4`).
| `${EXT}` | The extension of the file (e.g.: `.mp4`, `.mkv`).
| `${SEG_NUM}` | Number of the segment, padded string (e.g. `01`, `02` or `42`).
| `${SEG_NUM_INT}` | Number of the segment, as a raw integer (e.g. `1`, `2` or `42`). Can be used with numeric arithmetics, e.g. `${SEG_NUM_INT+100}`.
| `${EPOCH_MS}` | Number of milliseconds since epoch (e.g. `1680852771465`).
| `${SEG_LABEL}` | The label of the segment (e.g. `Getting_Lunch`).
| `${SEG_SUFFIX}` | If a label exists for this segment, the label will be used, prepended by `-`. Otherwise, the segment number prepended by `-seg` will be used (e.g. `-Getting_Lunch`, `-seg1`).
| `${CUT_FROM}` | The timestamp for the beginning of the segment in `hh.mm.ss.sss` format (e.g. `00.00.27.184`).
| `${CUT_TO}` | The timestamp for the ending of the segment in `hh.mm.ss.sss` format (e.g. `00.00.28.000`).
| `${SEG_TAGS.XX}` | Allows you to retrieve the tags for a given segment by name. If a tag is called foo, it can be accessed with `${SEG_TAGS.foo}`. Note that if the tag does not exist, it will return the text `undefined`. You can work around this as follows: `${SEG_TAGS.foo ?? ''}`

Your files must always include at least one unique identifer (such as `${SEG_NUM}` or `${CUT_FROM}`), and they should end in `${EXT}` (or else players might not recognise the files). For instance, to achieve a filename sequence of `Beach Trip - 1.mp4`, `Beach Trip - 2.mp4`, `Beach Trip - 3.mp4`, your format should read `${FILENAME} - ${SEG_NUM}${EXT}`

Expand All @@ -34,6 +36,10 @@ LosslessCut also allows importing/exporting your project (segments) in a variety
1234,,Last segment
```

### TSV

Same as CSV but `<tab>` instead.

### More formats?

See https://github.com/mifi/lossless-cut/issues/1340
127 changes: 94 additions & 33 deletions issues.md

Large diffs are not rendered by default.

295 changes: 203 additions & 92 deletions public/locales/cs/translation.json → locales/cs/translation.json

Large diffs are not rendered by default.

216 changes: 168 additions & 48 deletions public/locales/de/translation.json → locales/de/translation.json

Large diffs are not rendered by default.

130 changes: 99 additions & 31 deletions public/locales/en/translation.json → locales/en/translation.json

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
File renamed without changes.
106 changes: 59 additions & 47 deletions public/locales/fi/translation.json → locales/fi/translation.json

Large diffs are not rendered by default.

757 changes: 757 additions & 0 deletions locales/fr/translation.json

Large diffs are not rendered by default.

File renamed without changes.
41 changes: 41 additions & 0 deletions locales/hu/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"Alt": "Alt",
"All Files": "Minden fájl",
"Are you sure you want to quit?": "Valóban ki akarsz lépni?",
"Close sidebar": "Oldalsáv bezárása",
"Close current screen": "Jelenlegi képernyő bezárása",
"Codec": "Kodek",
"Help": "Súgó",
"Copy": "Másolás",
"Ctrl": "Ctrl",
"Done": "Kész",
"Exit": "Kilépés",
"Export": "Exportálás",
"XML files": "XML fájlok",
"Zoom": "Nagyítás",
"Are you sure?": "Biztos?",
"Cut": "Kivágás",
"Edit": "Szerkesztés",
"An error has occurred.": "Valami hiba lépett fel.",
"Chapters": "Fejezetek",
"Chapters only": "Csak fejezetek",
"Close": "Bezár",
"Confirm": "Megerősít",
"CSV files": "CSV fájlok",
"Check for updates on startup?": "Frissítések keresése indításkor",
"Copy to clipboard": "Másolás vágólapra",
"Default": "Alapértelmezett",
"Duration": "Hossz",
"About LosslessCut": "A LosslessCut névjegye",
"attachment": "csatolmány",
"audio": "hang",
"⌘ Cmd / ⊞ Win": "⌘ Cmd / ⊞ Win",
"Add metadata": "Metaadat hozzáadása",
"Abort": "Megszakít",
"Advanced options": "Haladó beállítások",
"All formats:": "Minden formátum:",
"Cancel": "Mégse",
"Bitrate": "Bitráta",
"Both": "Mindkettő",
"YouTube Chapters": "YouTube fejezetek"
}
File renamed without changes.
323 changes: 217 additions & 106 deletions public/locales/it/translation.json → locales/it/translation.json

Large diffs are not rendered by default.

223 changes: 167 additions & 56 deletions public/locales/ja/translation.json → locales/ja/translation.json

Large diffs are not rendered by default.

378 changes: 314 additions & 64 deletions public/locales/ko/translation.json → locales/ko/translation.json

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
80 changes: 63 additions & 17 deletions public/locales/nl/translation.json → locales/nl/translation.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"All formats:": "Alle format:",
"Always open this dialog when opening multiple files": "Vis dette vindauga kvar gong du opnar fleire filer",
"An error has occurred.": "Eit mistak har hendt.",
"Are you sure you want to close the current file?": "Lat att noverande fil?",
"Are you sure you want to close the current file?": "Lat att den noverande fila?",
"Are you sure you want to quit?": "End appen?",
"Ask about what to do when opening a new file when another file is already already open?": "Spør om kva som skal verta gjort ved opning av ei ny fil når ei anna alt er open?",
"Ask before closing": "Spør før ending",
Expand All @@ -29,7 +29,7 @@
"CUE files": "CUE-filer",
"CUE sheet file": "CUE-arkfil",
"Cancel": "Avbryt",
"Capture frame": "Tak bilete",
"Capture frame": "Ta biletet",
"Capture frame format": "Biletetaksformat",
"Change order of segment": "Brigd bitrekkjefylgd",
"Change rotation": "Snu",
Expand All @@ -44,12 +44,12 @@
"Bind new key to action": "Bind ny tast til gjerda",
"Change segment order": "Brigd bitrekkjefylgd",
"Clear working directory": "Tøm verkamappa",
"Close file and clean up": "Steng fil og reins opp",
"Close file and clean up": "Steng fila og reins opp",
"Click to toggle track inclusion when exporting": "Klikk for å slå av/på medtaking av spor under utføring",
"Close current screen": "Steng denne visinga",
"Defaults to same format as input file. You can losslessly change the file format (container) of the file with this option. Not all formats support all codecs. Matroska/MP4/MOV support the most common codecs. Sometimes it's even impossible to export to the same output format as input.": "Forvalet vert satt til same format som gjeven fil. Du kan utan tap byta filformatet (karet) til fila med dette valet. Ikkje alle format stør alle omkodarar. Matroska/MP4/MOV stør dei mest nytta omkodarane. Nokre gongar er det jamvel umogleg å utføra i same format som vert mata inn.",
"Delete source file": "Slett kjeldefil",
"Depending on your specific file/player, you may have to try different options for best results.": "Ut ifrå kva for ein fil/avspelar du nyttar, kan ulike val gje deg betre verknadar.",
"Depending on your specific file/player, you may have to try different options for best results.": "Ut ifrå kva for ei fil/avspelar du nyttar, kan ulike val gje deg betre verknadar.",
"Create fixed duration segments": "Lag bitar med fast lengd",
"Are you sure?": "Er du viss?",
"Ask about chapters": "Spør om hovudbolkar",
Expand Down Expand Up @@ -83,7 +83,7 @@
"Confirm quit": "Stadfest utgang",
"Convert current file to supported format": "Lag om noverande fil til eit stødd format",
"Convert to supported format": "Lag om til stødd format",
"Converting to supported format": "Omlagar til eit stødd format",
"Converting to supported format": "Lagar om til eit stødd format",
"Copy": "Kopier",
"Clear all segments": "Tøm alle bitar",
"Cleanup files?": "Tak bort filer?",
Expand Down
743 changes: 743 additions & 0 deletions locales/pl/translation.json

Large diffs are not rendered by default.

File renamed without changes.
256 changes: 205 additions & 51 deletions public/locales/pt_BR/translation.json → locales/pt_BR/translation.json

Large diffs are not rendered by default.

File renamed without changes.
852 changes: 852 additions & 0 deletions locales/ru/translation.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions locales/si/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"All Files": "සියළුම ගොනු",
"attachment": "ඇමුණුම",
"Both": "දෙකම",
"Change preferences": "අභිප්‍රේත සංශෝධනය",
"Auto save project file?": "ව්‍යාපෘතියේ ගොනුව ඉබේ සුරකින්න",
"Bitrate": "බිටුඅනුපා.",
"Change value": "අගය සංශෝධනය",
"audio": "ශ්‍රව්‍ය",
"Cancel": "අවලංගු",
"Capture frame": "රාමුව ග්‍රහණය"
}
686 changes: 686 additions & 0 deletions locales/sk/translation.json

Large diffs are not rendered by default.

680 changes: 680 additions & 0 deletions locales/sl/translation.json

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
469 changes: 340 additions & 129 deletions public/locales/tr/translation.json → locales/tr/translation.json

Large diffs are not rendered by default.

705 changes: 705 additions & 0 deletions locales/uk/translation.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -570,5 +570,6 @@
"Video FPS": "FPS Video",
"View and edit segment tags in JSON5 format:": "Xem và chỉnh sửa các thẻ phân đoạn ở định dạng JSON5:",
"Whether or not to sanitize output file names (sanitizing removes special characters)": "Có hay không khử sạch tên tệp đầu ra (khử sạch xóa các ký tự đặc biệt)",
"You are running version {{version}}": "Bạn đang chạy phiên bản {{version}}"
"You are running version {{version}}": "Bạn đang chạy phiên bản {{version}}",
"Shift": ""
}

Large diffs are not rendered by default.

800 changes: 800 additions & 0 deletions locales/zh_Hant/translation.json

Large diffs are not rendered by default.

Binary file modified main_screenshot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions no.mifi.losslesscut.appdata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
<url type="bugtracker">https://github.com/mifi/lossless-cut/issues</url>
<url type="donation">https://paypal.me/mifino/usd</url>
<releases>
<release version="3.61.1" date="2024-05-27"/>
<release version="3.61.0" date="2024-05-14"/>
<release version="3.60.0" date="2024-01-05"/>
<release version="3.59.1" date="2023-12-22"/>
<release version="3.59.0" date="2023-12-22"/>
<release version="3.58.0" date="2023-10-16"/>
<release version="3.57.0" date="2023-10-15"/>
<release version="3.56.0" date="2023-08-24"/>
<release version="3.55.2" date="2023-04-09"/>
<release version="3.55.1" date="2023-04-07"/>
<release version="3.55.0" date="2023-04-07"/>
<release version="3.54.0" date="2023-04-04"/>
<release version="3.53.0" date="2023-03-10"/>
<release version="3.52.0" date="2023-02-17"/>
<release version="3.51.1" date="2023-02-17"/>
Expand Down
168 changes: 91 additions & 77 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,28 @@
"productName": "LosslessCut",
"description": "The swiss army knife of lossless video/audio editing",
"copyright": "Copyright © 2021 ${author}",
"version": "3.53.0",
"main": "public/electron.js",
"version": "3.61.1",
"main": "./out/main/index.js",
"homepage": "./",
"scripts": {
"start": "concurrently -k \"npm run start:frontend\" \"npm run start:electron\"",
"start:frontend": "cross-env vite --port 3001",
"start:electron": "wait-on tcp:3001 && electron .",
"icon-gen": "mkdirp icon-build build-resources/appx && node script/icon-gen.mjs",
"download-ffmpeg-darwin-x64": "mkdirp ffmpeg/darwin-x64 && cd ffmpeg/darwin-x64 && wget https://github.com/mifi/ffmpeg-build-script/releases/download/5.1.2/ffmpeg -O ffmpeg && wget https://github.com/mifi/ffmpeg-build-script/releases/download/5.1.2/ffprobe -O ffprobe && chmod +x ffmpeg && chmod +x ffprobe",
"download-ffmpeg-darwin-arm64": "mkdirp ffmpeg/darwin-arm64 && cd ffmpeg/darwin-arm64 && wget https://github.com/mifi/ffmpeg-builds/releases/download/5.1.2/ffmpeg-darwin-arm64-v5.1.2 -O ffmpeg && wget https://github.com/mifi/ffmpeg-builds/releases/download/5.1.2/ffprobe-darwin-arm64-v5.1.2 -O ffprobe && chmod +x ffmpeg && chmod +x ffprobe",
"download-ffmpeg-linux-x64": "mkdirp ffmpeg/linux-x64 && cd ffmpeg/linux-x64 && wget https://github.com/mifi/ffmpeg-builds/releases/download/5.1.2/linux-x64-v5.1.1.tar.xz -O ffmpeg-ffprobe.xz && tar -xv --strip-components=1 -f ffmpeg-ffprobe.xz ffmpeg-5.1.1-amd64-static/ffmpeg ffmpeg-5.1.1-amd64-static/ffprobe",
"download-ffmpeg-win32-x64": "mkdirp ffmpeg/win32-x64 && cd ffmpeg/win32-x64 && npx download-cli https://github.com/mifi/ffmpeg-builds/releases/download/5.1.2/win32-x64-v5.1.2.7z --out . --filename ffmpeg-ffprobe.7z && 7z x ffmpeg-ffprobe.7z && npx shx mv ffmpeg-5.1.2-essentials_build/bin/ffmpeg.exe ./ && npx shx mv ffmpeg-5.1.2-essentials_build/bin/ffprobe.exe ./",
"build": "yarn icon-gen && vite build --outDir vite-dist",
"clean": "rimraf dist out ts-dist build-resources icon-build",
"start": "electron-vite preview",
"dev": "electron-vite dev -w",
"icon-gen": "mkdirp icon-build build-resources/appx && tsx script/icon-gen.mts",
"download-ffmpeg-darwin-x64": "mkdirp ffmpeg/darwin-x64 && cd ffmpeg/darwin-x64 && wget https://github.com/mifi/ffmpeg-build-script/releases/download/6.0-1/ffmpeg-macos-X64 -O ffmpeg && wget https://github.com/mifi/ffmpeg-build-script/releases/download/6.0-1/ffprobe-macos-X64 -O ffprobe && chmod +x ffmpeg && chmod +x ffprobe",
"download-ffmpeg-darwin-arm64": "mkdirp ffmpeg/darwin-arm64 && cd ffmpeg/darwin-arm64 && wget https://github.com/mifi/ffmpeg-build-script/releases/download/6.0-1/ffmpeg-macos-ARM64 -O ffmpeg && wget https://github.com/mifi/ffmpeg-build-script/releases/download/6.0-1/ffprobe-macos-ARM64 -O ffprobe && chmod +x ffmpeg && chmod +x ffprobe",
"download-ffmpeg-linux-x64": "mkdirp ffmpeg/linux-x64 && cd ffmpeg/linux-x64 && wget https://github.com/mifi/ffmpeg-builds/releases/download/6.0/ffmpeg-n6.0-12-ga6dc92968a-linux64-gpl-shared-6.0.tar.xz -O ffmpeg-ffprobe.xz && tar -xv -f ffmpeg-ffprobe.xz && mv ffmpeg-n6.0-12-ga6dc92968a-linux64-gpl-shared-6.0 extracted && mkdirp lib && mv extracted/bin/ffmpeg extracted/bin/ffprobe extracted/lib/lib*.so* lib",
"download-ffmpeg-win32-x64": "mkdirp ffmpeg/win32-x64 && cd ffmpeg/win32-x64 && npx download-cli https://github.com/mifi/ffmpeg-builds/releases/download/6.0/ffmpeg-n6.0-12-ga6dc92968a-win64-gpl-shared-6.0.zip --out . --filename ffmpeg-ffprobe.zip && 7z x ffmpeg-ffprobe.zip && mkdirp lib && cd ffmpeg-n6.0-12-ga6dc92968a-win64-gpl-shared-6.0/bin && npx shx mv ffmpeg.exe ffprobe.exe *.dll ../../lib",
"build": "yarn icon-gen && electron-vite build",
"tsc": "tsc --build",
"test": "vitest",
"lint": "eslint --ext .jsx --ext .js . --ext .mjs",
"pack-mac": "electron-builder --mac -m dmg",
"prepack-mac": "yarn build",
"pack-mas-dev": "electron-builder --mac -m mas-dev -c.mas.provisioningProfile=LosslessCut_Dev.provisionprofile -c.mas.identity='Apple Development: Mikael Finstad (JH4PH8B3C8)'",
"prepack-mas-dev": "yarn build",
"pack-win": "electron-builder --win",
"prepack-win": "yarn build",
"postinstall": "patch-package && electron-builder install-app-deps",
"version": "node script/postversion.mjs && git add no.mifi.losslesscut.appdata.xml",
"pack-linux": "electron-builder --linux",
"prepack-linux": "yarn build",
"lint": "eslint --ext .js,.ts,.jsx,.tsx,.mjs,.mts .",
"pack-mac": "yarn build && electron-builder --mac dmg",
"pack-mas-dev": "yarn build && electron-builder -mac mas-dev -c.mas.provisioningProfile=LosslessCut_Dev.provisionprofile -c.mas.identity='Apple Development: Mikael Finstad (JH4PH8B3C8)'",
"pack-win": "yarn build && electron-builder --win zip --x64",
"postinstall": "electron-builder install-app-deps",
"version": "tsx script/postversion.mts && git add no.mifi.losslesscut.appdata.xml",
"pack-linux": "yarn build && electron-builder --linux",
"scan-i18n": "i18next --config i18next-parser.config.mjs",
"generate-licenses": "yarn licenses generate-disclaimer > licenses.txt && echo '\n\nffmpeg is licensed under GPL v2+:\n\nhttp://www.gnu.org/licenses/old-licenses/gpl-2.0.html' >> licenses.txt"
},
Expand All @@ -43,35 +40,57 @@
"license": "GPL-2.0-only",
"devDependencies": {
"@fontsource/open-sans": "^4.5.14",
"@radix-ui/colors": "^0.1.8",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-switch": "^1.0.1",
"@tsconfig/node18": "^18.2.2",
"@tsconfig/strictest": "^2.0.2",
"@tsconfig/vite-react": "^3.0.0",
"@types/color": "^3.0.6",
"@types/css-modules": "^1.0.5",
"@types/eslint": "^8",
"@types/express": "^4.17.21",
"@types/lodash": "^4.14.202",
"@types/luxon": "^3.4.2",
"@types/morgan": "^1.9.9",
"@types/node": "18",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/sortablejs": "^1.15.0",
"@vitejs/plugin-react": "^3.1.0",
"@types/yargs-parser": "^21.0.3",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"@uidotdev/usehooks": "^2.4.1",
"@vitejs/plugin-react": "^4.3.1",
"color": "^3.1.0",
"concurrently": "^6.0.0",
"cross-env": "^7.0.3",
"csv-parse": "^4.15.3",
"csv-stringify": "^5.6.2",
"electron": "^23.1.0",
"electron-builder": "^23.6.0",
"electron-builder-notarize": "^1.4.0",
"data-uri-to-buffer": "^4.0.0",
"electron": "^27.0.0",
"electron-builder": "^24.6.4",
"electron-devtools-installer": "^3.2.0",
"eslint": "^7.32.0 || ^8.2.0",
"eslint-config-airbnb": "^19.0.4",
"electron-vite": "^2.3.0",
"eslint": "^8.2.0",
"eslint-config-mifi": "^0.0.3",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-unicorn": "^51.0.1",
"evergreen-ui": "^6.13.1",
"fast-xml-parser": "^4.0.3",
"fast-xml-parser": "^4.2.5",
"framer-motion": "^9.0.3",
"i18next-parser": "^7.6.0",
"icon-gen": "^3.0.0",
"icon-gen": "^4.0.0",
"immer": "^10.0.2",
"ky": "^0.33.1",
"luxon": "^3.3.0",
"mkdirp": "^1.0.3",
"moment": "^2.29.4",
"mousetrap": "^1.6.5",
"nanoid": "^5.0.6",
"p-map": "^5.5.0",
"patch-package": "^6.2.1",
"p-retry": "^6.2.0",
"pify": "^5.0.0",
"pretty-bytes": "^6.0.0",
"react": "^18.2.0",
Expand All @@ -82,66 +101,69 @@
"react-sortablejs": "^6.1.4",
"react-syntax-highlighter": "^15.4.3",
"react-use": "^17.4.0",
"rimraf": "^5.0.5",
"sass": "^1.77.2",
"screenfull": "^6.0.2",
"scroll-into-view-if-needed": "^2.2.28",
"sharp": "^0.30.5",
"sharp": "^0.32.6",
"smpte-timecode": "^1.2.3",
"sortablejs": "^1.13.0",
"sweetalert2": "^11.0.0",
"sweetalert2": "^11.11.0",
"sweetalert2-react-content": "^5.0.7",
"tiny-invariant": "^1.3.3",
"tsx": "^4.7.1",
"typescript": "~5.2.0",
"use-debounce": "^5.1.0",
"use-trace-update": "^1.3.0",
"uuid": "^8.3.2",
"vite": "^4.1.1",
"vitest": "^0.28.5",
"wait-on": "^7.0.1"
"vite": "^5.3.4",
"vitest": "^2.0.3"
},
"dependencies": {
"@electron/remote": "^2.0.9",
"@radix-ui/colors": "^0.1.8",
"@electron/remote": "^2.0.10",
"@octokit/core": "5",
"cue-parser": "^0.3.0",
"data-uri-to-buffer": "^4.0.0",
"electron-is-dev": "^2.0.0",
"electron-store": "5.1.1",
"electron-unhandled": "^4.0.1",
"execa": "^5.0.0",
"execa": "^8.0.1",
"express": "^4.19.2",
"express-async-handler": "^1.2.0",
"file-type": "16",
"file-url": "^3.0.0",
"fs-extra": "^8.1.0",
"github-api": "^3.2.2",
"i18next": "^22.4.10",
"i18next-fs-backend": "^2.1.1",
"json5": "^2.2.2",
"lodash": "^4.17.19",
"mime-types": "^2.1.14",
"semver": "^7.1.3",
"string-to-stream": "^1.1.1",
"strtok3": "^6.0.0",
"morgan": "^1.10.0",
"semver": "^7.6.0",
"string-to-stream": "^3.0.1",
"winston": "^3.8.1",
"yargs-parser": "^21.0.0"
},
"eslintConfig": {
"extends": "react-app"
"yargs-parser": "^21.1.1",
"zod": "^3.22.5"
},
"build": {
"directories": {
"buildResources": "build-resources"
},
"extraMetadata": {
"main": "vite-dist/electron.js"
},
"files": [
"vite-dist/**/*"
"out/**/*"
],
"asar": {
"smartUnpack": false
},
"appId": "no.mifi.losslesscut",
"artifactName": "${productName}-${os}-${arch}.${ext}",
"afterSign": "electron-builder-notarize",
"extraResources": [
{
"from": "locales",
"to": "locales"
}
],
"mac": {
"hardenedRuntime": true,
"appId": "no.mifi.losslesscut-mac",
"category": "public.app-category.productivity",
"notarize": true,
"target": [
{
"target": "mas",
Expand All @@ -160,12 +182,8 @@
],
"extraResources": [
{
"from": "ffmpeg/darwin-${arch}/ffmpeg",
"to": "ffmpeg"
},
{
"from": "ffmpeg/darwin-${arch}/ffprobe",
"to": "ffprobe"
"from": "ffmpeg/darwin-${arch}",
"to": "."
}
],
"icon": "icon-build/app.icns",
Expand Down Expand Up @@ -268,6 +286,7 @@
},
"mas": {
"hardenedRuntime": false,
"notarize": false,
"entitlements": "entitlements.mas.plist",
"entitlementsInherit": "entitlements.mas.inherit.plist",
"provisioningProfile": "LosslessCut_Mac_App_Store_provisioning_profile.provisionprofile",
Expand All @@ -283,12 +302,8 @@
],
"extraResources": [
{
"from": "ffmpeg/win32-${arch}/ffmpeg.exe",
"to": "ffmpeg.exe"
},
{
"from": "ffmpeg/win32-${arch}/ffprobe.exe",
"to": "ffprobe.exe"
"from": "ffmpeg/win32-${arch}/lib",
"to": "."
}
],
"icon": "icon-build/app.ico",
Expand Down Expand Up @@ -399,12 +414,8 @@
"executableName": "losslesscut",
"extraResources": [
{
"from": "ffmpeg/linux-${arch}/ffmpeg",
"to": "ffmpeg"
},
{
"from": "ffmpeg/linux-${arch}/ffprobe",
"to": "ffprobe"
"from": "ffmpeg/linux-${arch}/lib",
"to": "."
}
],
"category": "AudioVideo",
Expand Down Expand Up @@ -439,5 +450,8 @@
]
}
},
"packageManager": "yarn@3.3.0"
"packageManager": "yarn@4.0.2",
"resolutions": {
"app-builder-lib@24.8.0": "patch:app-builder-lib@npm%3A24.8.0#./.yarn/patches/app-builder-lib-npm-24.8.0-51e1f5cd3f.patch"
}
}
11 changes: 0 additions & 11 deletions public/constants.js

This file was deleted.

380 changes: 0 additions & 380 deletions public/locales/fr/translation.json

This file was deleted.

253 changes: 0 additions & 253 deletions public/locales/pl/translation.json

This file was deleted.

609 changes: 0 additions & 609 deletions public/locales/ru/translation.json

This file was deleted.

1 change: 0 additions & 1 deletion public/locales/sr/translation.json

This file was deleted.

3 changes: 0 additions & 3 deletions public/locales/uk/translation.json

This file was deleted.

506 changes: 0 additions & 506 deletions public/locales/zh_Hant/translation.json

This file was deleted.

33 changes: 0 additions & 33 deletions public/logger.js

This file was deleted.

5 changes: 0 additions & 5 deletions public/util.js

This file was deleted.

8 changes: 8 additions & 0 deletions requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Supported OS versions

LosslessCut is based on Electron which routinely drops support for old OS versions, and therefore LosslessCut will also do so. [More info](https://github.com/mifi/lossless-cut/discussions/1476#discussioncomment-5012521).

- v3.58.0 [dropped support](https://www.electronjs.org/docs/latest/breaking-changes#removed-macos-1013--1014-support) for MacOS 10.14 and older.
- v3.52.0 dropped support for Windows 8.1 and older.
- v3.48.2 dropped support for MacOS 10.12 and older.
- v3.48.2 dropped support for 32 bit Linux.
22 changes: 0 additions & 22 deletions script/icon-gen.mjs

This file was deleted.

32 changes: 32 additions & 0 deletions script/icon-gen.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// eslint-disable-line unicorn/filename-case
import sharp from 'sharp';
import icongenRaw from 'icon-gen';

const icongen = icongenRaw as unknown as typeof icongenRaw['default'];

const svg2png = (from: string, to: string, width: number, height: number) => sharp(from)
.png()
.resize(width, height, {
fit: sharp.fit.contain,
background: { r: 0, g: 0, b: 0, alpha: 0 },
})
.toFile(to);

const srcIcon = 'src/renderer/src/icon.svg';
// Linux:
await svg2png(srcIcon, './icon-build/app-512.png', 512, 512);

// Windows Store
await svg2png(srcIcon, './build-resources/appx/StoreLogo.png', 50, 50);
await svg2png(srcIcon, './build-resources/appx/Square150x150Logo.png', 300, 300);
await svg2png(srcIcon, './build-resources/appx/Square44x44Logo.png', 44, 44);
await svg2png(srcIcon, './build-resources/appx/Wide310x150Logo.png', 620, 300);

// MacOS:
// https://github.com/mifi/lossless-cut/issues/1820
await icongen('./src/renderer/src/icon-mac.svg', './icon-build', { icns: { sizes: [512, 1024] }, report: false });

// Windows ICO:
// https://github.com/mifi/lossless-cut/issues/778
// https://stackoverflow.com/questions/3236115/which-icon-sizes-should-my-windows-applications-icon-include
await icongen(srcIcon, './icon-build', { ico: { sizes: [16, 24, 32, 40, 48, 64, 96, 128, 256, 512] }, report: false });
8 changes: 4 additions & 4 deletions script/postversion.mjs → script/postversion.mts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { readFile, writeFile } from 'fs/promises';
import { readFile, writeFile } from 'node:fs/promises';
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
import moment from 'moment';
import { DateTime } from 'luxon';

const xmlUrl = new URL('../no.mifi.losslesscut.appdata.xml', import.meta.url);
const xmlData = await readFile(xmlUrl);

const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)));
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)) as unknown as string);

const parser = new XMLParser({ alwaysCreateTextNode: true, ignoreAttributes: false, ignoreDeclaration: false });
const xml = parser.parse(xmlData);
// console.log(xml);

const { version } = packageJson;

xml.component.releases.release = [{ '@_version': version, '@_date': moment().format('YYYY-MM-DD') }, ...xml.component.releases.release];
xml.component.releases.release = [{ '@_version': version, '@_date': DateTime.now().toISODate() }, ...xml.component.releases.release];

const builder = new XMLBuilder({ format: true, ignoreAttributes: false, suppressEmptyNode: true });
await writeFile(xmlUrl, builder.build(xml));
20 changes: 12 additions & 8 deletions script/xcrun-wrapper.mjs → script/xcrun-wrapper.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import execa from 'execa';
import { readFile } from 'fs/promises';
// eslint-disable-line unicorn/filename-case
import { execa } from 'execa';
import { readFile } from 'node:fs/promises';

// we need a wrapper script because altool tends to error out very often
// https://developer.apple.com/forums/thread/698477
Expand All @@ -8,15 +9,15 @@ import { readFile } from 'fs/promises';
const args = process.argv.slice(2);

const filePath = args[0];
const apiKey = args[1];
const apiKeyId = args[1];
const apiIssuer = args[2];
const appleId = args[3];
const bundleId = args[4];

// seems to be the same
const ascPublicId = apiIssuer;

const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)));
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)) as unknown as string);

console.log('Using version', packageJson.version);

Expand Down Expand Up @@ -54,13 +55,13 @@ Example JSON response:
*/

async function runAttempt() {
// const xcrunArgs = ['altool', '--list-apps', '--output-format', 'json', '--apiKey', apiKey, '--apiIssuer', apiIssuer];
// const xcrunArgs = ['altool', '--list-apps', '--output-format', 'json', '--apiKey', apiKeyId, '--apiIssuer', apiIssuer];

const xcrunArgs = [
'altool',
'--output-format', 'json',
'--upload-package', filePath, '--type', 'macos',
'--apiKey', apiKey, '--apiIssuer', apiIssuer,
'--apiKey', apiKeyId, '--apiIssuer', apiIssuer,
'--asc-public-id', ascPublicId,
'--apple-id', appleId,
'--bundle-id', bundleId,
Expand All @@ -73,8 +74,11 @@ async function runAttempt() {
console.log('stdout', stdout);
return false;
} catch (err) {
if (err.exitCode === 1 && err.stdout) {
const errorJson = JSON.parse(err.stdout);
if (err instanceof Error && 'exitCode' in err && err.exitCode === 1 && 'stdout' in err && err.stdout && typeof err.stdout === 'string') {
const errorJson = JSON.parse(err.stdout) as unknown;
if (!(errorJson != null && typeof errorJson === 'object' && 'product-errors' in errorJson && Array.isArray(errorJson['product-errors']))) {
throw new TypeError('Invalid JSON');
}
const productErrors = errorJson['product-errors'];
// Unable to authenticate
if (productErrors.some((error) => error.code === -19209)) {
Expand Down
2,437 changes: 0 additions & 2,437 deletions src/App.jsx

This file was deleted.

55 changes: 0 additions & 55 deletions src/Canvas.jsx

This file was deleted.

109 changes: 0 additions & 109 deletions src/CanvasPlayer.js

This file was deleted.

47 changes: 0 additions & 47 deletions src/NoFileLoaded.jsx

This file was deleted.

323 changes: 0 additions & 323 deletions src/SegmentList.jsx

This file was deleted.

489 changes: 0 additions & 489 deletions src/StreamsSelector.jsx

This file was deleted.

235 changes: 0 additions & 235 deletions src/components/ConcatDialog.jsx

This file was deleted.

10 changes: 0 additions & 10 deletions src/components/HighlightedText.jsx

This file was deleted.

Loading