Skip to content

Expand "Find & Replace" function to Groups #553

@simkin

Description

@simkin

v0.8.19

I am currently unable to perform bulk renaming operations on Group names. While the application includes a "Find & Replace" function for channel names and titles, this functionality is missing for Groups. When managing playlists containing hundreds of groups, the lack of this feature makes standardizing group names (e.g., removing prefixes, fixing typos, or changing naming conventions) time-consuming and inefficient.

Image

I would like the existing "Find & Replace" functionality to be extended to support Groups. Ideally, this would be integrated into the current Find & Replace interface, allowing the user to select the target scope (e.g., "Channels" or "Groups"). Alternatively, a dedicated "Batch Edit Groups" tool would suffice. The feature should support standard string replacement to allow for quick cleanup of the group list.

As workaround you could do some manual editing, one by one
Or you can create a bookmark that you can trigger manually (paste the below instead of an URL):

javascript:(function() { const inputs = document.querySelectorAll('input.fi-input'); const regex = /^(?:UK\|\s*|[A-Z0-9+-]+[:|]\s*)|\s*(?:ᴿ||ᴿ||³|ˢ|HEVC|LQ|HD|UHD|4K|8K||$$.*$$).*$/i; let count = 0; inputs.forEach(input => { const oldVal = input.value; if (oldVal) { const newVal = oldVal.replace(regex, '').trim(); if (oldVal !== newVal) { input.value = newVal; input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); input.style.border = "2px solid #22c55e"; input.style.backgroundColor = "#f0fdf4"; count++; } } }); console.log(`✅ Adjusted ${count} names on this page.`); })();

Additional context
Since the logic for finding and replacing text strings already exists within the application for channel data, applying similar logic to the group data structure would align the feature set between channels and groups.

Slightly offtopic: Why not propose a REGEX string to use based on playlist analysis (or propose to clean up names automatically) based on a cleanup function?

import re
from collections import Counter

def generate_cleaning_regex(channel_list, min_prefix_occurrence=2, min_suffix_occurrence=2):
    """
    Analyzes a list of channel names and generates a Regex to clean them.
    It looks for:
    1. Prefixes ending in common delimiters (:, |, -).
    2. Common "garbage" suffixes (HD, RAW, etc).
    """
    
    # 1. Configuration: What looks like a separator?
    # We look for text ending with these characters at the start of the string
    delimiters = [':', '|', '-', ']', '>']
    
    prefixes_found = []
    suffixes_found = []
    
    # 2. Analyze the lines
    for line in channel_list:
        clean_line = line.strip()
        if not clean_line:
            continue

        # --- Analyze Prefixes ---
        # We try to split by each delimiter. If the left part is short (< 15 chars), 
        # we treat it as a potential prefix (e.g. "BE-VIP" or "US").
        for char in delimiters:
            if char in clean_line:
                parts = clean_line.split(char, 1)
                prefix_candidate = parts[0].strip()
                # If prefix is reasonably short, assume it's a tag (e.g. "US") 
                # and not part of the name (e.g. "Ant")
                if len(prefix_candidate) > 0 and len(prefix_candidate) < 15:
                    # Store the prefix including the delimiter to be exact
                    prefixes_found.append(f"{prefix_candidate}{char}")
                    break # Stop after finding the first valid delimiter

        # --- Analyze Suffixes ---
        # We look at the last "word" of the line.
        words = clean_line.split()
        if len(words) > 1:
            last_word = words[-1]
            # Heuristic: Suffixes are usually uppercase, numbers, or symbols
            # We skip long normal words to avoid deleting real names.
            if len(last_word) < 8: 
                suffixes_found.append(last_word)

    # 3. Filter results by frequency
    # Only keep patterns that appear more than 'min_occurrence' times
    prefix_counts = Counter(prefixes_found)
    suffix_counts = Counter(suffixes_found)
    
    top_prefixes = [p for p, count in prefix_counts.items() if count >= min_prefix_occurrence]
    top_suffixes = [s for s, count in suffix_counts.items() if count >= min_suffix_occurrence]

    # 4. Build the RegEx parts
    
    # Part A: The Prefix Group
    # We escape them to be safe (e.g. "|" becomes "\|") and join with OR (|)
    if top_prefixes:
        # Sort by length (longest first) to avoid partial matching issues
        top_prefixes.sort(key=len, reverse=True)
        escaped_prefixes = [re.escape(p) for p in top_prefixes]
        prefix_pattern = f"(?:{'|'.join(escaped_prefixes)})\\s*"
    else:
        # Fallback if nothing found: match standard delimiters
        prefix_pattern = r"(?:.*?[|:]\s*)?"

    # Part B: The Suffix Group
    # We explicitly look for these specific words at the end of the string
    if top_suffixes:
        escaped_suffixes = [re.escape(s) for s in top_suffixes]
        # This complex looking block ensures we match multiple suffixes (e.g. "HD RAW")
        # and ignore channel numbers (digits that aren't in our explicit garbage list)
        suffix_pattern = f"(?:\\s+(?:{'|'.join(escaped_suffixes)}|\\([^)]*\\)))+"
    else:
        suffix_pattern = r"(?:\s+(?:HD|FHD|RAW|4K))+"

    # 5. Assemble Final Regex
    # Pattern: ^ (Prefix)? (Content) (Suffix)? $
    final_regex = f"^{prefix_pattern}?(.*?){suffix_pattern}?$"
    
    return final_regex, top_prefixes, top_suffixes

# --- Test Data (Replace this with your file loading logic) ---
raw_playlist = [
    "BE-VIP: NPO 1 RAW",
    "BE-VIP: NPO 2 RAW",
    "NL: RTL 4 HD",
    "NL: SBS6 FHD",
    "US- CNN 4K",
    "UK- BBC One (Backup)",
    "MOVIE | Avatar 2 UHD",
    "MOVIE | Titanic SD",
    "Clean Name Example",
    "SPORT > ESPN 1"
]

# --- Run the Analysis ---
regex, detected_prefixes, detected_suffixes = generate_cleaning_regex(raw_playlist)

# --- Output Report ---
print(f"--- Analysis Report ---")
print(f"Found {len(detected_prefixes)} common prefixes: {detected_prefixes}")
print(f"Found {len(detected_suffixes)} common suffixes: {detected_suffixes}")
print("\n--- Suggested RegEx ---")
print(regex)

print("\n--- Testing the RegEx on data ---")
pattern = re.compile(regex)
for line in raw_playlist:
    clean = pattern.sub(r"\1", line)
    print(f"Original: '{line}'  ->  Cleaned: '{clean}'")

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions