# Tool is executed in the album folder, preventing it from renaming it

**URL:** https://community.mp3tag.de/t/tool-is-executed-in-the-album-folder-preventing-it-from-renaming-it/67351
**Category:** Support
**Created:** [February 1, 2025, 3:26pm UTC](https://community.mp3tag.de/t/tool-is-executed-in-the-album-folder-preventing-it-from-renaming-it/67351 "2025-02-01T15:26:15Z")
**Posts on this page:** 1
**Showing post:** 11

<div class="post-metadata">

### Author: ![Casual\_Tea](https://community.mp3tag.de/user_avatar/community.mp3tag.de/casual_tea/32/18886_2.png) [@Casual\_Tea](https://community.mp3tag.de/u/Casual_Tea)
#### Post date: [February 3, 2025, 1:04pm UTC](https://community.mp3tag.de/t/tool-is-executed-in-the-album-folder-preventing-it-from-renaming-it/67351/11 "2025-02-03T13:04:35Z")

</div>

How embarrassing, apparently I managed to bungle this test:

> [@Casual\_Tea](#):
>
> I also tested if changing the working directory within the script helps to prevent this but it did not change anything so I guess the way Mp3tag calls the tool blocks the folder until the tool is closed.

As it turns out, changing the working directory within the script is enough to not block the folder after all.

Tool:  
Name: `Folderrenamer`  
Path: `C:\Users\<User>\AppData\Local\Programs\Python\Python312\python.exe`  
Parameter:  
`D:\PythonScripts\folderrenamer.py -d "$regexp(%_folderpath%,(.*)\\,$1)" --mp3tag -s`  
☑ for all selected files

Combining this with a filter like `%track% IS 1 OR %track% IS 01` to only run it once per directory works as intended as you can see.  
 ![folderrenamer](https://community.mp3tag.de/uploads/default/original/3X/8/b/8b63c78464e70cfb34959e9cfe35c9fe86824375.gif)

Sorry for wasting your time, everyone!

In case some of you want to try my script, here's the current version.  
It requires [Python](https://www.python.org/), [ffmpeg](https://www.ffmpeg.org/) on PATH, Mp3tag on PATH and [tqdm](https://github.com/tqdm/tqdm) (`pip3 install tqdm`) and if you want to run it anywhere from the CLI instead of as an Mp3tag tool, I recommend adding the script (or the script folder) to PATH as well. [There are many guides how you can add something to PATH.](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/)

**WARNING:**  
Only use it from the CLI if you know what you're doing. Since it recursively scans and renames folders, you could very easily rename program folders that happen to have audio files in them if you execute it in the wrong directory! As a Mp3tag tool it's limited to renaming the folder of the file that you call it on.

Should someone want/need further explanations (basic usage instructions are available via `-h` or `--help`) I can add the script to github and write documentation. This is simply the version I use.

> **folderrenamer.py**
>
> ```auto
> import os
> import sys
> import subprocess
> import re
> import argparse
> from tqdm import tqdm
> from collections import defaultdict
> 
> # List of supported audio file extensions
> AUDIO_EXTENSIONS = {"flac", "mp3", "m4a", "ogg"}
> 
> def parse_arguments():
> def dir_path(path):
> if os.path.isdir(path) and path != None:
> return path
> else:
> raise argparse.ArgumentTypeError(f"readable_dir:{path} is not a valid path")
>         
> parser = argparse.ArgumentParser(description='Rename folder(s) based on the properties of contained audio files and append the format(s) in square brackets.')
> parser.add_argument('-d', '--directory',
> help='The directory that will be scanned for audio files and renamed based on their properties.', type=dir_path, default=".", const=".", nargs="?")
> parser.add_argument('--mp3tag', action='store_true',
> help='Add the renamed folders to Mp3tag. Press F5 in Mp3tag afterwards to refresh the file list.')
> parser.add_argument('-s', '--single_folder', action='store_true',
> help='Only scan a single folder for audio files and rename it.')
> 
> args: argparse.Namespace = parser.parse_args()
> 
> return args
> 
> def extract_audio_properties(file_path):
> """
> Extracts the bitrate, bit depth, sampling rate, and number of channels from an audio file using ffprobe.
> """
> try:
> result = subprocess.run(
> [
> 'ffprobe', '-v', 'error', '-select_streams', 'a:0',
> '-show_entries', 'stream=bit_rate,sample_rate,bits_per_raw_sample,channels',
> '-of', 'default=noprint_wrappers=1:nokey=1', file_path
> ],
> stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
> )
>         
> values = result.stdout.split('\n')
> bitrate = int(values[2]) // 1000 if values[2].isdigit() else None # convert from bps to kbps
> sampling_rate = int(values[0]) if values[0].isdigit() else None
> bit_depth = int(values[3]) if values[3].isdigit() else None
> channels = int(values[1]) if values[1].isdigit() else None
>         
> return bitrate, sampling_rate, bit_depth, channels
> except Exception as e:
> print(f"Error extracting properties from {file_path}: {e}")
> return None, None, None, None
> 
> def analyze_folder(folder_path):
> """
> Analyzes the audio files in a folder, collecting file types and audio properties.
> """
> properties = defaultdict(list) # Structure: {filetype: [(bitrate, sampling_rate, bit_depth, channels)]}
> mixed_formats = set()
>     
> for file in os.listdir(folder_path):
> file_ext = os.path.splitext(file)[-1][1:].lower()
>         
> if file_ext in AUDIO_EXTENSIONS:
> file_path = os.path.join(folder_path, file)
> bitrate, sampling_rate, bit_depth, channels = extract_audio_properties(file_path)
> if bitrate or sampling_rate or bit_depth or channels:
> properties[file_ext].append((bitrate, sampling_rate, bit_depth, channels))
> mixed_formats.add(file_ext.upper())
>     
> return properties, mixed_formats
> 
> def format_folder_name(base_name, properties, mixed_formats):
> """
> Generate the new folder name based on the audio properties and file types found.
> """
> if len(mixed_formats) > 2: # Don't needlessly bloat the filename with more than 2 formats
> return f"{base_name} [MIXED FORMATS]"
> 
> # Define the custom order for formats
> format_priority = {"flac": 1, "mp3": 2, "m4a": 3, "ogg": 4}
> parts = []
> 
> # Process FLAC files first, sorted by descending quality
> for filetype, prop_list in sorted(properties.items(), key=lambda x: format_priority.get(x[0], float('inf'))):
> if filetype == 'flac':
> flac_variants = []
> for bitrate, sampling_rate, bit_depth, channels in sorted(
> set(prop_list), key=lambda x: (-x[2], -x[1], -x[3] if x[3] else 0)
> ):
> variant_parts = [f"{bit_depth}", f"{sampling_rate / 1000:g}"] # Convert Hz to kHz
> if channels and channels > 2:
> channel_map = {3: "2.1", 6: "5.1", 8: "7.1"}
> variant_parts.append(channel_map.get(channels, str(channels)))
> 
> flac_variants.append("-".join(variant_parts))
> 
> parts.append(f"FLAC {', '.join(flac_variants)}")
> else:
> # Process other formats (e.g., MP3, M4A, OGG)
> bitrates = sorted(set(bitrate for bitrate, _, _, _ in prop_list if bitrate), reverse=True)
> if bitrates:
> if len(bitrates) > 1:
> all_bitrates = [bitrate for bitrate, _, _, _ in prop_list if bitrate]
> avg_bitrate = round(sum(all_bitrates) / len(all_bitrates))
> if len(bitrates) == 1:
> parts.append(f"{filetype.upper()} {bitrates[0]}")
> else:
> parts.append(f"{filetype.upper()} {bitrates[-1]}-{bitrates[0]} ⌀{avg_bitrate}")
> # Skip renaming folders that only contain CD quality FLAC files
> if parts and parts != ["FLAC 16-44.1"]:
> return f"{base_name} [{', '.join(parts)}]"
> else:
> return base_name
> 
> def main(args):
> directory = args.directory
> single_folder = args.single_folder
> mp3tag = args.mp3tag
> if mp3tag: os.chdir("..")
> match_folder = re.compile(r"^(.*) \[(?:FLAC [\d,⌀ .-]+)?(?:MP3 [\d,⌀ .-]+)?(?:M4A [\d,⌀ .-]+)?(?:OGG [\d,⌀ .-]+)?\]$|^(.*) \[MIXED FORMATS\]$")
> renamed = 0
> skipped = 0
> renamed_folders = []
> 
> """
> Recursively processes subfolders in the given base path, analyzing audio properties and renaming folders accordingly.
> """
> if not single_folder:
> with tqdm(desc="Scanning and renaming folders.", unit=" folders", ncols=100) as pbar:
> 
> for root, dirs, files in os.walk(directory, topdown=False): # Bottom-up to rename folders after processing their contents
> folder_name = os.path.basename(root)
> parent_path = os.path.dirname(root)
> 
> # Check if the folder has already been renamed
> match = re.match(match_folder, folder_name)
> base_name = match.group(1) or match.group(2) if match else folder_name
> 
> properties, mixed_formats = analyze_folder(root)
> if not mixed_formats:
> continue
> new_folder_name = format_folder_name(base_name, properties, mixed_formats)
>                 
> if new_folder_name != folder_name: # Compare to the original folder name
> new_path = os.path.join(parent_path, new_folder_name)
> try:
> os.rename(root, new_path)
> pbar.update(1)
> renamed+=1
> pbar.set_postfix({"renamed": renamed}, {"skipped": skipped})
> renamed_folders.append(os.path.abspath(new_path))
> except Exception as e:
> print(f"Error renaming folder {root} to {new_path}: {e}")
> else:
> pbar.update(1)
> skipped+=1
> pbar.set_postfix({"renamed": renamed}, {"skipped": skipped})
> 
> else:
> folder_name = os.path.basename(directory)
> parent_path = os.path.dirname(directory)
> 
> # Check if the folder has already been renamed
> match = re.match(match_folder, folder_name)
> base_name = match.group(1) or match.group(2) if match else folder_name
> 
> properties, mixed_formats = analyze_folder(directory)
> if not mixed_formats:
> print ("No music files found in the folder, exiting.")
> sys.exit()
> new_folder_name = format_folder_name(base_name, properties, mixed_formats)
>         
> if new_folder_name != folder_name: # Compare to the original folder name
> new_path = os.path.join(parent_path, new_folder_name)
> try:
> os.rename(directory, new_path)
> print(f"Renamed: {directory} -> {new_path}")
> renamed_folders.append(os.path.abspath(new_path))
> except Exception as e:
> print(f"Error renaming folder {directory} to {new_path}: {e}")
> if mp3tag and renamed_folders:
> for folder in renamed_folders:
> try:
> subprocess.run(["mp3tag", "/add", f'/fp:"{folder}"'])
> except subprocess.CalledProcessError:
> print(f"Error while adding {folder} to Mp3tag.")
> # Uncomment when you want to inspect the output
> #input("Press Enter to continue...")
> 
> if __name__ == " __main__":
> args = parse_arguments()
> try:
> main(args)
> except KeyboardInterrupt:
> print("Interrupted")
> try:
> sys.exit(130)
> except SystemExit:
> os._exit(130)
> 
> ```

---

_[View the full topic](https://community.mp3tag.de/t/tool-is-executed-in-the-album-folder-preventing-it-from-renaming-it/67351)._
