diff --git a/HSRename.AppImage b/HSRename.AppImage index 8a74b04..dd9f575 100755 Binary files a/HSRename.AppImage and b/HSRename.AppImage differ diff --git a/README.md b/README.md index eea6f5c..55584f6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A **native Linux** GUI for mass renaming files. Inspired by [Bulk Rename Utility - **GUI** – Structure renames with rule panels; no command line needed. - **Preview** – See “Original → New name” for every file before committing. - **Multiple rules** – Combine rules (order: Replace → Regex → Insert → Remove → Case → Numbering → Episode renumber → Prefix/Suffix). Enable only the rules you need. -- **Episode renumbering** – Replace episode numbers (e.g. `S01E05 - Title` → `S01E01 - Title`) while keeping season and title. Start number and zero-padding configurable. +- **Episode renumbering** – Replace episode numbers (e.g. `S01E05 - Title` → `S01E01 - Title`) while keeping season and title. Multi-episode files like `S01E05-E06` become `S01E01-E02` (range length preserved). Start, step, and zero-padding configurable. - **Replace / Regex** – Plain text find/replace or full regex with capture groups. - **Insert / Remove** – Insert text at start/end/position; remove text, digits, or first/last N characters. - **Case** – Title Case, UPPER, lower, Sentence case. @@ -62,6 +62,10 @@ Sort order is the current list order (alphabetical by filename). Reorder files i 3. Enable **9. CSV mapping**, click **Browse…** and select the CSV. 4. Only rows that match a current filename in the folder are renamed; others are unchanged. You can combine with other rules (CSV is applied in rule order). +## Gear Lever (AppImage updates) + +To have [Gear Lever](https://github.com/mijorus/gearlever) see new releases when we push them and update the AppImage from there, see **[docs/GEARLEVER.md](docs/GEARLEVER.md)** for configuration (custom update URL for Gitea, or static URL). + ## AppImage (distribution) To build a portable AppImage: diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6d7de6e --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.2 diff --git a/build_appimage.sh b/build_appimage.sh index 675abba..00d55ed 100755 --- a/build_appimage.sh +++ b/build_appimage.sh @@ -54,9 +54,17 @@ else fi echo "=== Building AppImage (requires appimagetool) ===" +# Optional: set VERSION when building for release so update info is embedded (for Gear Lever etc.) +# e.g. VERSION=v1.0.1 ./build_appimage.sh +GITEA_RELEASES="http://brassnet.ddns.net:33983/Dawnsorrow/HS-Rename/releases" +UPDATE_INFO="" +if [[ -n "${VERSION:-}" ]]; then + UPDATE_INFO="-u url|${GITEA_RELEASES}/download/${VERSION}/HSRename.AppImage" +fi if command -v appimagetool &>/dev/null; then OUT="${APP_NAME}.AppImage" - appimagetool "$APPDIR" "$OUT" + # shellcheck disable=SC2086 + appimagetool $UPDATE_INFO "$APPDIR" "$OUT" echo "Done: $OUT" else echo "AppDir is ready at $APPDIR/" diff --git a/docs/GEARLEVER.md b/docs/GEARLEVER.md new file mode 100644 index 0000000..104cf44 --- /dev/null +++ b/docs/GEARLEVER.md @@ -0,0 +1,58 @@ +# Using HSRename with Gear Lever + +[Gear Lever](https://github.com/mijorus/gearlever) can manage and update the HSRename AppImage. Because releases are hosted on **Gitea** (not GitHub), use one of the options below. + +## Option 1: Custom update URL (recommended to try first) + +Gitea uses the same release path style as GitHub: +`/owner/repo/releases/download/{tag}/{filename}` + +1. Open **Gear Lever** and add the HSRename AppImage (drag & drop or **Add**). +2. Select the HSRename entry and open **Update** / **Custom update URL** (or the equivalent in your Gear Lever version). +3. Try this URL pattern (with a wildcard for the tag): + ``` + http://brassnet.ddns.net:33983/Dawnsorrow/HS-Rename/releases/download/*/HSRename.AppImage + ``` +4. If Gear Lever accepts it (e.g. field turns green or validates), it may use the Gitea releases page to resolve the latest tag and offer updates when you push new releases. +5. Use **Check for updates** / **List updates** to see if it detects new versions. + +If your Gear Lever only supports GitHub-style URLs and rejects this, use Option 2. + +## Option 2: Static URL (manual update link) + +If the wildcard URL does not work: + +1. In Gear Lever, add HSRename and set **Update** to **Static URL**. +2. Paste the **direct download URL** of the current release, for example: + - **v1.0.1:** + `http://brassnet.ddns.net:33983/Dawnsorrow/HS-Rename/releases/download/v1.0.1/HSRename.AppImage` +3. When a new version is released (e.g. v1.0.2), open the [releases page](http://brassnet.ddns.net:33983/Dawnsorrow/HS-Rename/releases), open the new release, right‑click **HSRename.AppImage** → **Copy link address**, then in Gear Lever set the Static URL to that new link and run **Update**. + +So: Gear Lever will only “see” updates if you change the Static URL to the new release’s download link when you want to update. + +## Option 3: CLI (if you use Gear Lever from the terminal) + +After adding the AppImage in Gear Lever, you can set the update URL from the command line: + +```bash +# Set custom update URL (try the wildcard pattern) +gearlever --set-update-url /path/to/HSRename.AppImage "http://brassnet.ddns.net:33983/Dawnsorrow/HS-Rename/releases/download/*/HSRename.AppImage" + +# Check for updates +gearlever --list-updates + +# Apply update +gearlever --update /path/to/HSRename.AppImage +``` + +Replace `/path/to/HSRename.AppImage` with the actual path where Gear Lever stores the AppImage. + +## Release download URL pattern + +For any release tag `vX.Y.Z`, the direct download URL is: + +``` +http://brassnet.ddns.net:33983/Dawnsorrow/HS-Rename/releases/download/vX.Y.Z/HSRename.AppImage +``` + +So when we push a new release (e.g. v1.0.2), that new tag’s URL is what Gear Lever needs (either via the wildcard in Option 1 or by pasting the new URL in Option 2). diff --git a/engine/rules.py b/engine/rules.py index c69710f..5056cd4 100644 --- a/engine/rules.py +++ b/engine/rules.py @@ -201,14 +201,14 @@ class NumberingRule(Rule): return stem, ext -# Episode renumber: match SxxExx or similar, replace episode number only, keep title +# Episode renumber: match SxxExx or SxxExx-Eyy, replace episode block; keep title @dataclass class EpisodeRenumberRule(Rule): start: int = 1 step: int = 1 padding: int = 2 - # Pattern: group 1 = prefix (e.g. "S01E"), group 2 = episode digits, group 3 = rest (title) - pattern: str = r"(.*?[Ss]\d+[Ee])(\d+)(.*)" + # Group 1 = prefix (e.g. "S01E"), 2 = first ep digits, 3 = full "-E06" or None, 4 = second ep if range, 5 = rest (title) + pattern: str = r"(.*?[Ss]\d+[Ee])(\d+)(-[Ee](\d+))?(.*)" def apply( self, @@ -224,10 +224,36 @@ class EpisodeRenumberRule(Rule): return stem, ext if not m: return stem, ext - prefix, _old_ep, rest = m.group(1), m.group(2), m.group(3) - ep_num = self.start + index * self.step - ep_str = str(ep_num).zfill(max(1, self.padding)) - new_stem = f"{prefix}{ep_str}{rest}" + prefix, old_first_s, range_dash_e, old_second_s, rest = ( + m.group(1), + m.group(2), + m.group(3), + m.group(4), + m.group(5), + ) + try: + old_first = int(old_first_s) + except ValueError: + return stem, ext + span = 1 + if old_second_s is not None: + try: + old_second = int(old_second_s) + except ValueError: + return stem, ext + span = old_second - old_first + 1 + if span < 1: + span = 1 + ep_new_first = self.start + index * self.step + pad = max(1, self.padding) + e1 = str(ep_new_first).zfill(pad) + if span <= 1: + new_stem = f"{prefix}{e1}{rest}" + else: + ep_new_last = ep_new_first + span - 1 + e2 = str(ep_new_last).zfill(pad) + # Match common style: S01E01-E02 (second block uses E again) + new_stem = f"{prefix}{e1}-E{e2}{rest}" return new_stem, ext diff --git a/gui/rule_widgets.py b/gui/rule_widgets.py index 1c13f09..cbfd317 100644 --- a/gui/rule_widgets.py +++ b/gui/rule_widgets.py @@ -285,7 +285,10 @@ class EpisodeRenumberRuleWidget(QWidget): layout.addRow("First episode number:", self.start) layout.addRow("Step:", self.step) layout.addRow("Zero-pad width:", self.padding) - info = QLabel("Matches patterns like S01E05 - Title or Show 1x03 - Title. Episode number is replaced; title is kept.") + info = QLabel( + "Matches S01E05 - Title (single) or S01E05-E06 - Title (two-part). " + "Ranges keep their length: S01E01-E02, S01E03-E04, … Order follows the file list / table sort." + ) info.setWordWrap(True) layout.addRow(info)