Skip to content

Commit

Permalink
Merge pull request #1 from Jemeni11/feat/v1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Jemeni11 authored Nov 16, 2024
2 parents 0cee16b + d30929e commit 20d65f3
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 27 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[//]: # (- **Security** in case of vulnerabilities.)


## [1.1.0] - 2024-11-15
### Added
- Dry-run mode and recursive symlink handling.
- add --update flag to check for new version.

### Changed
- Changed logo

### Removed
- Removed debug argument option, it wasn't even implemented

## [1.0.0] - 2024-10-07
- Released CrossRename


[1.1.0]: https://github.com/Jemeni11/CrossRename/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/Jemeni11/CrossRename/releases/tag/v1.0.0
62 changes: 47 additions & 15 deletions CrossRename/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from pathlib import Path
import argparse
import logging
from .utils import check_for_update

__version__ = "1.1.0"

__version__ = "1.0.0"

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s » %(message)s')
Expand Down Expand Up @@ -61,67 +63,97 @@ def sanitize_filename(filename: str) -> str:
return sanitized


def rename_file(file_path: str) -> None:
def rename_file(file_path: str, dry_run: bool = False) -> None:
directory, filename = os.path.split(file_path)
new_filename = sanitize_filename(filename)

if new_filename != filename:
new_file_path = os.path.join(directory, new_filename)
try:
os.rename(file_path, new_file_path)
logger.info(f"Renamed: {filename} -> {new_filename}")
except Exception as e:
logger.error(f"Error renaming {filename}: {str(e)}")
if dry_run:
logger.info(f"[Dry-run] Would rename: {filename} -> {new_filename}")
else:
try:
os.rename(file_path, new_file_path)
logger.info(f"Renamed: {filename} -> {new_filename}")
except Exception as e:
logger.error(f"Error renaming {filename}: {str(e)}")
else:
logger.info(f"No change needed: {filename}")


def file_search(directory: str) -> list[str]:
file_list = []
for root, _, files in os.walk(directory):
visited_paths = set()

for root, _, files in os.walk(directory, followlinks=False):
real_root = os.path.realpath(root)

if real_root in visited_paths:
logger.warning(f"Skipping recursive symlink in {root}")
continue

visited_paths.add(real_root)

for file in files:
file_list.append(os.path.join(root, file))
file_path = os.path.join(root, file)
if os.path.islink(file_path):
logger.info(f"Skipping symlink: {file_path}")
continue
file_list.append(file_path)

return file_list


def main() -> None:
try:
parser = argparse.ArgumentParser(
description="CrossRename: Harmonize file names for Linux and Windows.")
parser.add_argument("-p", "--path", help="The path to the file or directory to rename.", required=True)
parser.add_argument("-p", "--path", help="The path to the file or directory to rename.")
parser.add_argument(
"-v",
"--version",
help="Prints out the current version and quits.",
action='version',
version=f"CrossRename Version {__version__}"
)
parser.add_argument(
"-u", "--update",
help="Check if a new version is available.",
action="store_true"
)
parser.add_argument(
"-r",
"--recursive",
help="Rename all files in the directory path given and its subdirectories.",
action="store_true"
)
args = parser.parse_args()
parser.add_argument("-d", "--dry-run", help="Perform a dry run, logging changes without renaming.",
action="store_true")

args = parser.parse_args()
path = args.path
recursive = args.recursive
dry_run = args.dry_run

if args.update:
check_for_update(__version__)
sys.exit()

if path is None:
sys.exit("Please provide a path to a file or directory using the --path argument.")
sys.exit("Error: Please provide a path to a file or directory using the --path argument.")

if os.path.isfile(path):
rename_file(path)
rename_file(path, dry_run)
elif os.path.isdir(path):
if recursive:
file_list = file_search(path)
for file_path in file_list:
rename_file(file_path)
rename_file(file_path, dry_run)
else:
for item in os.listdir(path):
item_path = os.path.join(path, item)
if os.path.isfile(item_path):
rename_file(item_path)
rename_file(item_path, dry_run)
else:
sys.exit(f"Error: {path} is not a valid file or directory")
except Exception as e:
Expand Down
25 changes: 25 additions & 0 deletions CrossRename/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from urllib import request, error
import json


def parse_version(version: str):
"""Converts a version string (e.g., '1.2.3') into a tuple of integers (1, 2, 3)."""
return tuple([int(part) for part in version.split(".")])


def check_for_update(current_version: str):
"""Checks if a new version of CrossRename is available on PyPI."""
try:
url = "https://pypi.org/pypi/CrossRename/json"
with request.urlopen(url, timeout=5) as response:
data = json.load(response)
latest_version = data["info"]["version"]

if parse_version(latest_version) > parse_version(current_version):
print(f"Update available: v{latest_version}. You're on v{current_version}.")
print(f"Run `pip install --upgrade CrossRename` to update.")
else:
print(f"You're on the latest version: v{current_version}.")

except error.URLError as e:
print(f"Unable to check for updates: {e}")
33 changes: 25 additions & 8 deletions PYPI_README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ Features
- Handles both individual files and entire directories
- Supports recursive renaming of files in subdirectories
- Preserves file extensions, including compound extensions like .tar.gz
- Provides informative logging with optional debug mode
- Provides informative logging
- Provides a dry-run mode to preview renaming changes without executing them
- Skips recursive symlinks to avoid infinite loops

Installation
============
Expand All @@ -50,16 +52,17 @@ Usage

::

usage: crossrename [-h] -p PATH [-d] [-v] [-r]
usage: crossrename [-h] [-p PATH] [-v] [-u] [-r] [-d]

CrossRename: Harmonize file names for Linux and Windows.

options:
-h, --help show this help message and exit
-p PATH, --path PATH The path to the file or directory to rename.
-d, --debug Enable debug mode.
-v, --version Prints out the current version and quits.
-u, --update Check if a new version is available.
-r, --recursive Rename all files in the directory path given and its subdirectories.
-d, --dry-run Perform a dry run, logging changes without renaming.

Examples
--------
Expand All @@ -76,16 +79,30 @@ Rename all files in a directory (and its subdirectories):

crossrename -p /path/to/directory -r

Perform a dry run to preview renaming changes without executing them:

::

crossrename -p /path/to/directory -r -d


Check for an update:

::

crossrename -u


Why did I build this?
=====================

.. warning::

Im no longer dual booting. Im only using Windows 10 now. I do have
WSL2 and thats what I use for testing. I dont know if therell be
I'm no longer dual booting. I'm only using Windows 10 now. I do have
WSL2 and that's what I use for testing. I don't know if there'll be
any difference in the way the tool works on a native Linux system.

Im a dual-booter running Windows 10 and Lubuntu 22.04. One day
I'm a dual-booter running Windows 10 and Lubuntu 22.04. One day
(literally yesterday lol), while transferring a folder between the two
systems, I hit a naming roadblock. Five stubborn files refused to budge,
thanks to the quirky differences in file naming rules between Linux and
Expand All @@ -104,14 +121,14 @@ smooth, worry-free file management.
Contributing
============

Contributions are welcome! If youd like to improve CrossRename or add
Contributions are welcome! If you'd like to improve CrossRename or add
support for other operating systems (like macOS), please feel free to
submit a pull request.

Wait a minute, who are you?
===========================

Hello there! Im Emmanuel Jemeni, and while I primarily work as a
Hello there! I'm Emmanuel Jemeni, and while I primarily work as a
Frontend Developer, Python holds a special place as my first programming
language. You can find me on various platforms:

Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ when transferring files between different environments.
- Handles both individual files and entire directories
- Supports recursive renaming of files in subdirectories
- Preserves file extensions, including compound extensions like .tar.gz
- Provides informative logging with optional debug mode
- Provides informative logging
- Provides a dry-run mode to preview renaming changes without executing them
- Skips recursive symlinks to avoid infinite loops

<p align="right">(<a href="#readme-top">back to top</a>)</p>

Expand All @@ -58,16 +60,18 @@ pip install CrossRename
## Usage

```
usage: crossrename [-h] -p PATH [-d] [-v] [-r]
usage: crossrename [-h] [-p PATH] [-v] [-u] [-r] [-d]
CrossRename: Harmonize file names for Linux and Windows.
options:
-h, --help show this help message and exit
-p PATH, --path PATH The path to the file or directory to rename.
-d, --debug Enable debug mode.
-v, --version Prints out the current version and quits.
-u, --update Check if a new version is available.
-r, --recursive Rename all files in the directory path given and its subdirectories.
-d, --dry-run Perform a dry run, logging changes without renaming.
```

Expand All @@ -87,6 +91,18 @@ Rename all files in a directory (and its subdirectories ):
crossrename -p /path/to/directory -r
```

Perform a dry run to preview renaming changes without executing them:

```
crossrename -p /path/to/directory -r -d
```

Check for an update:

```
crossrename -u
```

<p align="right">(<a href="#readme-top">back to top</a>)</p>

## Why did I build this?
Expand Down
Binary file modified logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "CrossRename"
version = "1.0.0"
version = "1.1.0"
authors = [
{ name="Emmanuel C. Jemeni", email="[email protected]" }
]
Expand Down

0 comments on commit 20d65f3

Please sign in to comment.