Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,29 @@ multi-frame pictures refused, the EXIF thumbnail dropped), one history entry for
- `Controls/CropMath` holds the arithmetic (ratios and their portrait forms, nudging by whole pixels, the frame in
pixels, where the size chip goes, where the dial opens) and is unit tested.

## Clip window

The Cut a clip tool (`tool-clip`, first on the back of the video donut) opens `Views/ClipWindow` instead of running a
job. The window only chooses the clip; writing it is an ordinary conversion.

- `Core/Presets/ClipCutter` does every edit without the window: the parts of the clip (start, end, speed, volume,
mute), gaps left by Delete, the blade, edge trims that take back or cut away, music on A2, fades, Remove silence and
undo / redo (one step per drag). The result is a `ClipEdit`, unit tested on its own.
- `ClipWindowViewModel` holds the window's state: the overview of the whole video (`Controls/ClipTimeline`: 16
frames, the loudness, the handles) and the sequence under it (`Controls/ClipSequence`: V1 pictures, A1 the video's
sound, A2 music). Both are custom-drawn controls; their pictures and loudness are taken again only when the clip's
ends rest.
- Space plays the clip as it will be written: `Engines/Media/ClipPreview` runs the same filter graph as the output,
the pictures as raw BGRA frames over a pipe and the sound through `ffplay` (when it sits next to Filee's FFmpeg;
without it the preview is silent). FFmpeg for the window runs below normal priority.
- Save calls `ClipTool.ConversionOf`, which carries the `ClipEdit` in `MediaOptions.Clip`, so the toast, cancel, the
output rules (`{name}_clip` next to the video) and the history work as for any conversion. `FfmpegConverter`
copies one plain stretch with `-ss/-to` and `-c copy` (*Fast, no re-encoding*), or encodes the exact cut.
- Anything else is one filter graph (`MediaEncoding.ClipGraphOf`): the video is read once, `split`/`trim` per piece,
`setpts` and chained `atempo` for speed, `volume`, `color`/`anullsrc` for gaps, `concat`, the music as a second
input (`atrim`, `volume`, `afade`, `adelay`, `amix`), then `fade`/`afade`. It is encoded with the preset's quality,
size and codec.

## Spreadsheet print studio

The `tool-sheet-studio` tool opens an independent `Views/SpreadsheetPrintStudioWindow` for each dropped XLSX workbook
Expand Down
2 changes: 2 additions & 0 deletions docs/ENGINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -1417,6 +1417,8 @@ format, and FFmpeg reads what the file holds, so a `.dat` with no video in it fa
It is smaller than the "essentials" build (100 vs 115 MB download, 272 vs 329 MB on disk, because ffmpeg.exe and
ffprobe.exe share their libraries) and has a superset of its encoders. It links only Windows system DLLs; GPU
and Vulkan libraries are loaded only if present. Being over 60 MB, it is offered but not ticked on first run.
- The Cut a clip window's preview plays its sound through `ffplay` when it sits next to Filee's FFmpeg (the gyan.dev
build has it); without it, the preview plays silently. Nothing else uses ffplay.
- Container and codecs per target (`Media/MediaEncoding.cs`, every encoder checked with `ffmpeg -encoders`):

| Target | Written as |
Expand Down
60 changes: 60 additions & 0 deletions src/Filee.App/Assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,66 @@
"crop.state_done": "تم الاقتصاص",
"crop.state_skipped": "تم التخطي",
"crop.state_failed": "فشل",
"preset.clip": "قص مقطع",
"clip.start": "البداية",
"clip.end": "النهاية",
"clip.length": "المدة",
"clip.timeline": "الخط الزمني",
"clip.save": "حفظ المقطع",
"clip.save_next": "حفظ والتالي",
"clip.seconds": "{0} ث",
"clip.length_hint": "مقطع بهذه المدة من البداية",
"clip.fast": "سريع، دون إعادة ترميز",
"clip.fast_hint": "ينسخ الفيديو كما هو: ينتهي في لحظة دون فقدان الجودة، لكن المقطع يبدأ من الإطار المفتاحي الذي يسبق البداية (حتى بضع ثوانٍ قبلها). أوقفه للقص بدقة عند الإطارات المختارة (يعيد الترميز ويستغرق وقتًا أطول).",
"clip.hint_keys": "المسافة للتشغيل · C للقص · Del للاقتطاع (فراغ)، Shift+Del للضم فورًا · اسحب عبر الأجزاء أو Ctrl / Shift+نقر لتحديد عدة أجزاء، Ctrl+A للكل · اسحب حافة الجزء لاستعادته أو تقصيره · ← → بمقدار 0.1 ث، ومع Shift ثانية · S / E / P البداية أو النهاية أو المؤشر · Ctrl+Z للتراجع · Z للتكبير · Enter للحفظ · Esc للإغلاق",
"clip.playhead": "مؤشر التشغيل",
"clip.play": "تشغيل مع الصوت (المسافة)",
"clip.pause": "إيقاف مؤقت (المسافة)",
"clip.fast_needs_encode": "توجد قصّات أو سرعة أو مستوى صوت أو تلاشٍ: سيُعاد ترميز المقطع، فلا يمكن نسخه كما هو.",
"clip.cut": "قص",
"clip.cut_hint": "قص الجزء إلى قسمين عند المؤشر (C)",
"clip.delete": "حذف",
"clip.restore": "استعادة",
"clip.delete_hint": "اقتطاع الجزء المحدد مع ترك فراغ وزر لإغلاقه (Delete)؛ Shift+Delete يضم الباقي فورًا. على A1 يكتم صوت الجزء، وعلى A2 يزيل الموسيقى",
"clip.undo": "تراجع (Ctrl+Z)",
"clip.redo": "إعادة (Ctrl+Y)",
"clip.magnet": "مغناطيس",
"clip.magnet_hint": "المغناطيس (M): يلتصق المؤشر وحواف الأجزاء والموسيقى بالقصّات وبالمؤشر",
"clip.zoom": "تكبير",
"clip.zoom_hint": "تكبير الخط الزمني على المقطع أو عرض الفيديو كله (Z)",
"clip.sequence": "التسلسل: الصورة والصوت والموسيقى",
"clip.close_gap": "إغلاق الفراغ",
"clip.no_music_yet": "A2: موسيقى من ملف (موسيقى…)",
"clip.remove_music": "إزالة الموسيقى",
"clip.mute": "كتم الصوت",
"clip.unmute": "إلغاء الكتم",
"clip.mute_hint": "كتم صوت الجزء المحدد مع بقاء الصورة (Delete على A1 يفعل ذلك أيضًا)",
"clip.music": "موسيقى",
"clip.music_hint": "وضع موسيقى من ملف تحت المقطع (A2): اسحبها لتحريكها واسحب خطها لمستوى الصوت",
"clip.music_pick": "موسيقى للمقطع",
"clip.music_files": "ملفات الصوت والفيديو",
"clip.no_music": "لا يحتوي هذا الملف على صوت يصلح موسيقى",
"clip.preparing": "جارٍ التحضير…",
"clip.finding_silence": "جارٍ البحث عن الصمت…",
"clip.loading_music": "جارٍ قراءة الموسيقى…",
"clip.selected_count": "المحدد: {0}",
"clip.volume": "مستوى الصوت",
"clip.volume_hint": "مستوى صوت الجزء المحدد (0–200 %)",
"clip.speed": "السرعة",
"clip.speed_hint": "سرعة الجزء المحدد؛ تبقى طبقة الصوت كما هي",
"clip.remove_silence": "إزالة الصمت",
"clip.remove_silence_hint": "إيجاد المقاطع الصامتة في المقطع واقتطاعها (Ctrl+Z يعيدها)",
"clip.close_gaps": "إزالة الفراغات",
"clip.close_gaps_hint": "إغلاق كل الفراغات التي خلّفتها الأجزاء المقتطعة",
"clip.fade_in": "ظهور تدريجي",
"clip.fade_in_hint": "الظهور من السواد والصمت خلال ثانية",
"clip.fade_out": "تلاشٍ",
"clip.fade_out_hint": "التلاشي إلى السواد والصمت خلال ثانية",
"clip.cannot_cut": "ضع المؤشر داخل المقطع، لا عند قصّة تمامًا",
"clip.last_piece": "لا يمكن اقتطاع آخر جزء يُشغَّل",
"clip.silence_removed": "أزيل الصمت: {0}",
"clip.no_silence": "لم يُعثر على صمت",
"clip.cannot_play": "لا يمكن تشغيل المقطع",
"preset.half_size": "نصف الحجم",
"preset.grayscale": "تدرّج رمادي",
"preset.remove_background": "إزالة الخلفية",
Expand Down
60 changes: 60 additions & 0 deletions src/Filee.App/Assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,66 @@
"crop.state_done": "Cropped",
"crop.state_skipped": "Skipped",
"crop.state_failed": "Failed",
"preset.clip": "Cut a clip",
"clip.start": "Start",
"clip.end": "End",
"clip.length": "Length",
"clip.timeline": "Timeline",
"clip.save": "Save clip",
"clip.save_next": "Save & next",
"clip.seconds": "{0} s",
"clip.length_hint": "A clip of this many seconds from the start",
"clip.fast": "Fast, no re-encoding",
"clip.fast_hint": "Copies the video as it is: done in a moment and nothing is lost, but the clip begins at the key frame before the start (up to a few seconds early). Turn off to cut exactly at the chosen frames (encodes again, takes longer).",
"clip.hint_keys": "Space plays · C cuts · Del cuts out (a gap), Shift+Del closes up · drag across pieces or Ctrl / Shift+click to select several, Ctrl+A all · drag a piece's edge to trim it · ← → by 0.1 s, Shift by 1 s · S / E / P start, end or playhead · Ctrl+Z undoes · Z zooms · Enter saves · Esc closes",
"clip.playhead": "Playhead",
"clip.play": "Play with sound (Space)",
"clip.pause": "Pause (Space)",
"clip.fast_needs_encode": "Cuts, speed, volume or fades: the clip is encoded again, so it can't be copied as it is.",
"clip.cut": "Cut",
"clip.cut_hint": "Cut the piece in two at the playhead (C)",
"clip.delete": "Delete",
"clip.restore": "Put back",
"clip.delete_hint": "Cut the selected piece out, leaving a gap with a button to close it (Delete); Shift+Delete closes up at once. On A1 it mutes the piece's sound, on A2 it removes the music",
"clip.undo": "Undo (Ctrl+Z)",
"clip.redo": "Redo (Ctrl+Y)",
"clip.magnet": "Magnet",
"clip.magnet_hint": "Magnet (M): the playhead, the piece edges and the music snap to cuts and to the playhead",
"clip.zoom": "Zoom",
"clip.zoom_hint": "Zoom the timeline in on the clip, or out to the whole video (Z)",
"clip.sequence": "Sequence: pictures, sound and music",
"clip.close_gap": "Close gap",
"clip.no_music_yet": "A2: music from a file (Music…)",
"clip.remove_music": "Remove music",
"clip.mute": "Mute sound",
"clip.unmute": "Unmute",
"clip.mute_hint": "Mute the selected piece's sound; its picture plays on (Delete on A1 does it too)",
"clip.music": "Music",
"clip.music_hint": "Put music from a file under the clip (A2): drag it along, drag its line for the volume",
"clip.music_pick": "Music for the clip",
"clip.music_files": "Sound and video files",
"clip.no_music": "This file has no sound to use as music",
"clip.preparing": "Preparing…",
"clip.finding_silence": "Finding silence…",
"clip.loading_music": "Reading the music…",
"clip.selected_count": "{0} selected",
"clip.volume": "Volume",
"clip.volume_hint": "Volume of the selected piece (0–200 %)",
"clip.speed": "Speed",
"clip.speed_hint": "Speed of the selected piece; the sound keeps its pitch",
"clip.remove_silence": "Remove silence",
"clip.remove_silence_hint": "Find the silent stretches of the clip and cut them out (Ctrl+Z puts them back)",
"clip.close_gaps": "Close gaps",
"clip.close_gaps_hint": "Close every gap left by pieces cut out",
"clip.fade_in": "Fade in",
"clip.fade_in_hint": "Fade in from black and silence over 1 second",
"clip.fade_out": "Fade out",
"clip.fade_out_hint": "Fade out to black and silence over 1 second",
"clip.cannot_cut": "Put the playhead inside the clip, not right at a cut",
"clip.last_piece": "The last piece that plays can't be cut out",
"clip.silence_removed": "Silence removed: {0}",
"clip.no_silence": "No silence found",
"clip.cannot_play": "The clip can't be played",

"preset.half_size": "Half size",
"preset.grayscale": "Grayscale",
Expand Down
60 changes: 60 additions & 0 deletions src/Filee.App/Assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,66 @@
"crop.state_done": "Recortado",
"crop.state_skipped": "Omitido",
"crop.state_failed": "Error",
"preset.clip": "Cortar un clip",
"clip.start": "Inicio",
"clip.end": "Fin",
"clip.length": "Duración",
"clip.timeline": "Línea de tiempo",
"clip.save": "Guardar clip",
"clip.save_next": "Guardar y siguiente",
"clip.seconds": "{0} s",
"clip.length_hint": "Un clip de estos segundos desde el inicio",
"clip.fast": "Rápido, sin recodificar",
"clip.fast_hint": "Copia el vídeo tal cual: listo al instante y sin pérdida, pero el clip empieza en el fotograma clave anterior al inicio (hasta unos segundos antes). Desactívalo para cortar exactamente en los fotogramas elegidos (vuelve a codificar, tarda más).",
"clip.hint_keys": "Espacio reproduce · C corta · Supr quita (hueco), Mayús+Supr cierra · arrastra sobre los fragmentos o Ctrl / Mayús+clic para elegir varios, Ctrl+A todos · arrastra el borde de un fragmento para recuperar o recortar · ← → 0,1 s, Mayús 1 s · S / E / P inicio, fin o cabezal · Ctrl+Z deshace · Z acerca · Intro guarda · Esc cierra",
"clip.playhead": "Cabezal",
"clip.play": "Reproducir con sonido (Espacio)",
"clip.pause": "Pausa (Espacio)",
"clip.fast_needs_encode": "Cortes, velocidad, volumen o fundidos: el clip se vuelve a codificar, no se puede copiar tal cual.",
"clip.cut": "Cortar",
"clip.cut_hint": "Cortar el fragmento en dos en el cabezal (C)",
"clip.delete": "Eliminar",
"clip.restore": "Restaurar",
"clip.delete_hint": "Quitar el fragmento elegido dejando un hueco con un botón para cerrarlo (Supr); Mayús+Supr lo cierra al momento. En A1 silencia el fragmento, en A2 quita la música",
"clip.undo": "Deshacer (Ctrl+Z)",
"clip.redo": "Rehacer (Ctrl+Y)",
"clip.magnet": "Imán",
"clip.magnet_hint": "Imán (M): el cabezal, los bordes de los fragmentos y la música se pegan a los cortes y al cabezal",
"clip.zoom": "Zoom",
"clip.zoom_hint": "Acercar la línea de tiempo al clip o mostrar todo el vídeo (Z)",
"clip.sequence": "Secuencia: imagen, sonido y música",
"clip.close_gap": "Cerrar hueco",
"clip.no_music_yet": "A2: música de un archivo (Música…)",
"clip.remove_music": "Quitar música",
"clip.mute": "Silenciar",
"clip.unmute": "Activar sonido",
"clip.mute_hint": "Silenciar el sonido del fragmento elegido; la imagen sigue (Supr en A1 hace lo mismo)",
"clip.music": "Música",
"clip.music_hint": "Poner música de un archivo bajo el clip (A2): arrástrala, arrastra su línea para el volumen",
"clip.music_pick": "Música para el clip",
"clip.music_files": "Archivos de sonido y vídeo",
"clip.no_music": "Este archivo no tiene sonido para usar como música",
"clip.preparing": "Preparando…",
"clip.finding_silence": "Buscando silencios…",
"clip.loading_music": "Leyendo la música…",
"clip.selected_count": "{0} seleccionados",
"clip.volume": "Volumen",
"clip.volume_hint": "Volumen del fragmento elegido (0–200 %)",
"clip.speed": "Velocidad",
"clip.speed_hint": "Velocidad del fragmento elegido; el sonido mantiene su tono",
"clip.remove_silence": "Quitar silencios",
"clip.remove_silence_hint": "Buscar los tramos en silencio del clip y quitarlos (Ctrl+Z los devuelve)",
"clip.close_gaps": "Cerrar huecos",
"clip.close_gaps_hint": "Cerrar todos los huecos que dejaron los fragmentos quitados",
"clip.fade_in": "Fundido de entrada",
"clip.fade_in_hint": "Aparece desde negro y silencio en 1 segundo",
"clip.fade_out": "Fundido de salida",
"clip.fade_out_hint": "Se funde a negro y silencio en 1 segundo",
"clip.cannot_cut": "Pon el cabezal dentro del clip, no justo en un corte",
"clip.last_piece": "No se puede quitar el último fragmento que suena",
"clip.silence_removed": "Silencios quitados: {0}",
"clip.no_silence": "No se encontraron silencios",
"clip.cannot_play": "No se puede reproducir el clip",
"preset.half_size": "Reducir 50 %",
"preset.grayscale": "Grises",
"preset.remove_background": "Quitar fondo",
Expand Down
60 changes: 60 additions & 0 deletions src/Filee.App/Assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,66 @@
"crop.state_done": "Recadré",
"crop.state_skipped": "Ignoré",
"crop.state_failed": "Échec",
"preset.clip": "Couper un extrait",
"clip.start": "Début",
"clip.end": "Fin",
"clip.length": "Durée",
"clip.timeline": "Chronologie",
"clip.save": "Enregistrer l'extrait",
"clip.save_next": "Enregistrer et suivant",
"clip.seconds": "{0} s",
"clip.length_hint": "Un extrait de cette durée depuis le début",
"clip.fast": "Rapide, sans réencodage",
"clip.fast_hint": "Copie la vidéo telle quelle : c'est instantané et sans perte, mais l'extrait commence à l'image clé avant le début (jusqu'à quelques secondes plus tôt). Désactivez pour couper exactement aux images choisies (réencode, plus long).",
"clip.hint_keys": "Espace lit · C coupe · Suppr retire (laisse un vide), Maj+Suppr resserre · glissez sur les morceaux ou Ctrl / Maj+clic pour en choisir plusieurs, Ctrl+A tous · tirez le bord d'un morceau pour le rogner · ← → de 0,1 s, Maj de 1 s · S / E / P début, fin ou tête de lecture · Ctrl+Z annule · Z zoome · Entrée enregistre · Échap ferme",
"clip.playhead": "Tête de lecture",
"clip.play": "Lire avec le son (Espace)",
"clip.pause": "Pause (Espace)",
"clip.fast_needs_encode": "Coupes, vitesse, volume ou fondus : l'extrait est réencodé, il ne peut donc pas être copié tel quel.",
"clip.cut": "Couper",
"clip.cut_hint": "Couper le morceau en deux à la tête de lecture (C)",
"clip.delete": "Supprimer",
"clip.restore": "Remettre",
"clip.delete_hint": "Retirer le morceau choisi en laissant un vide avec un bouton pour le fermer (Suppr) ; Maj+Suppr resserre aussitôt. Sur A1, coupe le son du morceau ; sur A2, retire la musique",
"clip.undo": "Annuler (Ctrl+Z)",
"clip.redo": "Rétablir (Ctrl+Y)",
"clip.magnet": "Aimant",
"clip.magnet_hint": "Aimant (M) : la tête de lecture, les bords des morceaux et la musique s'accrochent aux coupes et à la tête de lecture",
"clip.zoom": "Zoom",
"clip.zoom_hint": "Zoomer la chronologie sur l'extrait, ou revenir à toute la vidéo (Z)",
"clip.sequence": "Séquence : image, son et musique",
"clip.close_gap": "Fermer le vide",
"clip.no_music_yet": "A2 : musique d'un fichier (Musique…)",
"clip.remove_music": "Retirer la musique",
"clip.mute": "Couper le son",
"clip.unmute": "Rétablir le son",
"clip.mute_hint": "Couper le son du morceau choisi ; l'image continue (Suppr sur A1 fait de même)",
"clip.music": "Musique",
"clip.music_hint": "Mettre la musique d'un fichier sous l'extrait (A2) : faites-la glisser, tirez sa ligne pour le volume",
"clip.music_pick": "Musique pour l'extrait",
"clip.music_files": "Fichiers son et vidéo",
"clip.no_music": "Ce fichier n'a pas de son à utiliser comme musique",
"clip.preparing": "Préparation…",
"clip.finding_silence": "Recherche des silences…",
"clip.loading_music": "Lecture de la musique…",
"clip.selected_count": "{0} sélectionnés",
"clip.volume": "Volume",
"clip.volume_hint": "Volume du morceau choisi (0–200 %)",
"clip.speed": "Vitesse",
"clip.speed_hint": "Vitesse du morceau choisi ; le son garde sa hauteur",
"clip.remove_silence": "Retirer les silences",
"clip.remove_silence_hint": "Trouver les passages silencieux de l'extrait et les retirer (Ctrl+Z les remet)",
"clip.close_gaps": "Fermer les vides",
"clip.close_gaps_hint": "Fermer tous les vides laissés par les morceaux retirés",
"clip.fade_in": "Fondu d'entrée",
"clip.fade_in_hint": "Apparaître depuis le noir et le silence en 1 seconde",
"clip.fade_out": "Fondu de sortie",
"clip.fade_out_hint": "Disparaître dans le noir et le silence en 1 seconde",
"clip.cannot_cut": "Placez la tête de lecture dans l'extrait, pas exactement sur une coupe",
"clip.last_piece": "Le dernier morceau qui joue ne peut pas être retiré",
"clip.silence_removed": "Silences retirés : {0}",
"clip.no_silence": "Aucun silence trouvé",
"clip.cannot_play": "Impossible de lire l'extrait",

"preset.half_size": "Taille 50 %",
"preset.grayscale": "Niveaux de gris",
Expand Down
Loading
Loading