Skip to content

Commit

Permalink
convert-file@thigschuch: Convert images, video, and audio files (#418)
Browse files Browse the repository at this point in the history
* Convert files using Python, FFmpeg, and ImageMagick

* For now, converting some types of images, videos and audios
   * It can detect the type of file the user selected.
   * It can handle target files with same names.
  • Loading branch information
ThigSchuch authored Jun 30, 2024
1 parent 9984a7a commit 355c900
Show file tree
Hide file tree
Showing 15 changed files with 1,364 additions and 0 deletions.
3 changes: 3 additions & 0 deletions convert-file@thigschuch/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### 1.0

* Initial release
46 changes: 46 additions & 0 deletions convert-file@thigschuch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Convert File

This is a simple script to convert a file from one format to another.

## Description

It can convert:
- Images into:
`bmp`
`gif`
`heic`
`ico`
`jpeg`
`png`
`tiff`
`webp`

- Audios into:
`aac`
`aiff`
`flac`
`m4a`
`mp3`
`ogg`
`opus`
`wav`
`wma`

- Videos into:
`3gp`
`avi`
`flv`
`mkv`
`mov`
`mp4`
`webm`

## Dependencies

- [ffmpeg](https://ffmpeg.org/)
- [ImageMagick](https://imagemagick.org/index.php)
- [Python 3](https://www.python.org/)

## Screenshots

![Screenshot](./screenshots/screenshot.png)
10 changes: 10 additions & 0 deletions convert-file@thigschuch/convert-file@thigschuch.nemo_action.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Nemo Action]
_Name=Convert File
_Comment=Converts a file to a different format

Icon-Name=gtk-convert
Extensions=any;
Selection=s

Exec=<convert-file@thigschuch/action.py %F>
Dependencies=python3;ffmpeg;convert;
Empty file.
196 changes: 196 additions & 0 deletions convert-file@thigschuch/files/convert-file@thigschuch/action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#!/usr/bin/python3
import sys
from pathlib import Path
from typing import Optional, Tuple

import aui
import gi
import text
from converters import AudioConverter, Converter, ImageConverter, VideoConverter
from custom_aui import SelectDropdownDialogWindow
from gi.repository import Gtk

gi.require_version("Gtk", "3.0")
gi.require_version("Gio", "2.0")

FORMATTERS = {
"IMAGE": {
"CONVERTER": ImageConverter,
"FORMATS": (
"BMP",
"GIF",
"HEIC",
"ICO",
"JPEG",
"PNG",
"TIFF",
"WEBP",
),
"DEFAULT": "PNG",
},
"VIDEO": {
"CONVERTER": VideoConverter,
"FORMATS": (
"3GP",
"AVI",
"FLV",
"MKV",
"MOV",
"MP4",
"WEBM",
),
"DEFAULT": "MP4",
},
"AUDIO": {
"CONVERTER": AudioConverter,
"FORMATS": (
"AAC",
"AIFF",
"FLAC",
"M4A",
"MP3",
"OGG",
"OPUS",
"WAV",
"WMA",
),
"DEFAULT": "MP3",
},
}


class Action:
"""
Class representing an action to convert a file to a different format.
Attributes:
file (Path): The path to the file to be converted.
file_format_type (str): The type of the file format.
target_formats (Tuple): Available target formats for conversion.
target_format (str): The selected target format for conversion.
converter (Converter): The converter instance to perform the conversion.
Methods:
valid_file() -> bool: Checks if the file is valid for conversion.
_get_file_format_type() -> Optional[str]: Determines the file format typebased on its suffix.
_get_available_formats() -> Optional[Tuple[str]]: Retrieves available target formats based on the file format type.
_select_format() -> str: Displays a dialog window to select the target format for conversion.
When an instance of this class is created, it checks the validity of the file, determines the file format type,
retrieves available target formats, selects a target format, and performs the conversion using the selected converter.
"""

def __init__(self, file: Path):
self.file: Path = file
if not self.valid_file():
return

self.file_format_type: str = self._get_file_format_type()
self.target_formats: Tuple = self._get_available_formats()

if not self.target_formats:
aui.InfoDialogWindow(
title=text.NOT_SUPPORTED_TITLE,
message=text.NOT_SUPPORTED_LABEL,
).run()
return

self.target_format: str = self._select_format()

if not self.target_format:
return

self.converter: Converter = FORMATTERS[self.file_format_type]["CONVERTER"](
self.file, self.target_format
)
self.converter.convert()

def valid_file(self) -> bool:
"""
Checks if the file is valid for conversion.
Returns:
bool: True if the file is valid for conversion, False otherwise.
"""
if not self.file.exists() or not self.file.is_file():
aui.InfoDialogWindow(
title=text.INVALID_FILE_TITLE, message=text.INVALID_FILE_MESSAGE
).run()
return False

if not self.file.suffix:
aui.InfoDialogWindow(
title=text.NO_FILE_EXTENSION_TITLE,
message=text.NO_FILE_EXTENSION_MESSAGE,
).run()
return False

return True

def _get_file_format_type(self) -> Optional[str]:
"""
Determines the file format type based on the suffix of the file.
Returns:
Optional[str]: The file format type if it is found in the FORMATTERS dictionary, None otherwise.
"""
suffix = self.file.suffix[1:].upper()

return next(
(key for key, value in FORMATTERS.items() if suffix in value["FORMATS"]),
None,
)

def _get_available_formats(self) -> Optional[Tuple[str]]:
"""
Determines the available target formats based on the file format type.
Returns:
Optional[Tuple[str]]: A tuple of available target formats if the file format type is found in the FORMATTERS dictionary, None otherwise.
"""
return (
FORMATTERS[self.file_format_type]["FORMATS"]
if self.file_format_type
else None
)

def _select_format(self) -> str:
"""
Displays a dialog window to select the target format for conversion.
Returns:
str: The selected target format for conversion.
"""
dialog = SelectDropdownDialogWindow(
title=text.SELECT_TITLE,
label=text.SELECT_LABEL,
choices=self.target_formats,
default_choice=FORMATTERS[self.file_format_type]["DEFAULT"],
)

response = dialog.run()
target_format = dialog.get_selected() if response else None
dialog.destroy()
return target_format


def main():
"""
Main function to execute the file conversion action based on the provided command-line argument.
This function checks if there is exactly one command-line argument provided.
If so, it creates an instance of the Action class with the file path obtained from the command-line argument,
triggering the file conversion process.
Parameters:
None
Returns:
None
"""
if len(sys.argv) == 2:
Action(Path(sys.argv[1]))


if __name__ == "__main__":
main()
Loading

0 comments on commit 355c900

Please sign in to comment.