script 1:
import os
import shutil
import re
import logging
from PIL import Image, ExifTags

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

# ----------------------------------------------------------------------------------------------------------------------
# Image Processing Functions
# ----------------------------------------------------------------------------------------------------------------------

def is_image_readable(file_path):
    """
    Check if an image file is readable.

    Args:
        file_path (str): Path to the image file.

    Returns:
        bool: True if the image is readable, False otherwise.
    """
    try:
        with Image.open(file_path) as img:
            img.verify()
        return True
    except Exception:
        return False

def delete_unreadable_images(folder_path):
    """
    Delete unreadable image files within a folder.

    Args:
        folder_path (str): Path to the folder to scan.
    """
    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')
    deleted_files = 0
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith(supported_formats):
                file_path = os.path.join(root, file)
                if not is_image_readable(file_path):
                    try:
                        os.remove(file_path)
                        logging.info(f"Deleted unreadable image: {file_path}")
                        deleted_files += 1
                    except Exception as e:
                        logging.error(f"Error deleting {file_path}: {e}")
    logging.info(f"Total unreadable image files deleted: {deleted_files}")

def count_image_files(folder_path, extensions=('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')):
    """
    Count the number of image files in a folder.

    Args:
        folder_path (str): Path to the folder.
        extensions (tuple): Tuple of file extensions to count.

    Returns:
        int: Number of image files.
    """
    return len([f for f in os.listdir(folder_path) if f.lower().endswith(extensions)])

def delete_subfolders_with_few_images(folder_path, threshold=3):
    """
    Delete subfolders containing a number of image files less than or equal to a threshold.

    Args:
        folder_path (str): Path to the parent folder.
        threshold (int): Threshold number of images. Default is 3.
    """
    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')
    deleted_folders = 0
    for root, dirs, _ in os.walk(folder_path, topdown=False):
        for dir_name in dirs:
            subfolder_path = os.path.join(root, dir_name)
            try:
                image_count = count_image_files(subfolder_path, extensions=supported_formats)
                if image_count <= threshold:
                    shutil.rmtree(subfolder_path)
                    logging.info(f"Deleted subfolder: {subfolder_path} (contained {image_count} image files)")
                    deleted_folders += 1
            except Exception as e:
                logging.error(f"Error processing subfolder {subfolder_path}: {e}")
    logging.info(f"Total subfolders deleted: {deleted_folders}")

def trim_image_files(folder_path, target_count=4):
    """
    Trim the number of image files in subfolders to a target count.

    Args:
        folder_path (str): Path to the parent folder.
        target_count (int): Desired maximum number of images per subfolder. Default is 4.
    """

    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')

    # Sorting logic to prioritize certain files
    def file_sort_key(x):
        match = re.search(r'(\d+).(jpg|jpeg|png|bmp|gif|tiff|webp)$', x, re.IGNORECASE)
        index = int(match.group(1)) if match else 0
        return (
            not x.lower().endswith('_0.jpg'),  # Prefer files not ending with '_0'
            index,                                # Lower index preferred
            x.lower()                             # Lexicographical order
        )

    for root, _, files in os.walk(folder_path):
        image_files = [f for f in files if f.lower().endswith(supported_formats)]
        image_count = len(image_files)

        if image_count <= target_count:
            continue

        image_files.sort(key=file_sort_key, reverse=True)
        files_to_delete = image_files[target_count:]

        for file in files_to_delete:
            file_path = os.path.join(root, file)
            try:
                os.remove(file_path)
                logging.info(f"Deleted excess image: {file_path}")
            except Exception as e:
                logging.error(f"Error deleting {file_path}: {e}")

def is_image_all_white(img):
    """
    Check if an image is entirely white.

    Args:
        img (PIL.Image.Image): The image to check.

    Returns:
        bool: True if the image is all white, False otherwise.
    """
    try:
        if img.mode != 'RGB':
            img = img.convert('RGB')
        extrema = img.getextrema()
        # Extrema is a list where each item is a tuple of (min, max) for each channel
        # For white image, all channels should have min and max as 255
        return all(channel_extremum == (255, 255) for channel_extremum in extrema)
    except Exception as e:
        logging.error(f"Error checking if image is all white: {e}")
        return False

def delete_blank_images(folder_path):
    """
    Delete images that are entirely white.

    Args:
        folder_path (str): Path to the folder to scan.
    """
    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')
    deleted_files = 0
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith(supported_formats):
                file_path = os.path.join(root, file)
                try:
                    with Image.open(file_path) as img:
                        img = apply_exif_orientation(img)
                        if is_image_all_white(img):
                            os.remove(file_path)
                            logging.info(f"Deleted blank (all white) image: {file_path}")
                            deleted_files += 1
                except Exception as e:
                    logging.error(f"Error processing {file_path} for blank check: {e}")
    logging.info(f"Total blank images deleted: {deleted_files}")

# ----------------------------------------------------------------------------------------------------------------------
# HTML Processing Functions
# ----------------------------------------------------------------------------------------------------------------------

def delete_small_html_folders(folder_path, min_char_count=6000):
    """
    Delete folders containing .html files with content less than a minimum character count.

    Args:
        folder_path (str): Path to the parent folder.
        min_char_count (int): Minimum character count required. Default is 6000.
    """
    for dirpath, _, filenames in os.walk(folder_path, topdown=False):
        delete_folder = False

        for filename in filenames:
            if filename.lower().endswith('.html'):
                file_path = os.path.join(dirpath, filename)
                try:
                    with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
                        content = file.read()
                    if len(content) < min_char_count:
                        delete_folder = True
                        break
                except Exception as e:
                    logging.error(f"Error reading {file_path}: {e}")

        if delete_folder:
            try:
                shutil.rmtree(dirpath)
                logging.info(f"Deleted folder due to small HTML content: {dirpath}")
            except Exception as e:
                logging.error(f"Error deleting folder {dirpath}: {e}")

def remove_blank_lines(file_path):
    """
    Remove blank lines from a file.

    Args:
        file_path (str): Path to the file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            lines = file.readlines()

        non_blank_lines = [line.rstrip() for line in lines if line.strip()]

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write('\n'.join(non_blank_lines))
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def remove_img_tags(file_path):
    """
    Remove all <img> tags from an HTML file.

    Args:
        file_path (str): Path to the HTML file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        # Remove img tags using regex
        cleaned_content = re.sub(r'<img\b[^>]>', '', content, flags=re.IGNORECASE)

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(cleaned_content)
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def replace_h1_tag(file_path):
    """
    Replace <h1 style="text-align: center;"> with <h1> in an HTML file.

    Args:
        file_path (str): Path to the HTML file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        modified_content = content.replace('<h1 style="text-align: center;">', '<h1>')

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(modified_content)
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def capitalize_first_letter_in_tags(content):
    """
    Capitalize the first letter of text inside HTML tags.

    Args:
        content (str): The HTML content as a string.

    Returns:
        str: Modified content with capitalized text.
    """
    # Function to capitalize text inside tags
    def capitalize_match(match):
        tag = match.group(1)
        text = match.group(2).strip()
        if text:
            return f"<{tag}>{text[0].upper()}{text[1:]}</{tag}>"
        else:
            return match.group(0)

    # Regex pattern to match tags and their content
    pattern = r'<([a-z0-9]+)(?:\s+[^>])?>([^<]+)</\1>'
    modified_content = re.sub(pattern, capitalize_match, content, flags=re.IGNORECASE)
    return modified_content

def process_html_files(folder_path):
    """
    Process all HTML files in a folder, applying several cleaning functions.

    Args:
        folder_path (str): Path to the folder.
    """
    for subdir, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(subdir, file)
                process_html_file(file_path)

def combine_html_tags_into_same_line(content):
    """
    Combine specific HTML tags into the same line by removing unnecessary line breaks.

    Args:
    content (str): The HTML content as a string.

    Returns:
    str: Modified content with specific tags combined into the same line.
    """
    # Replace '>' followed by any whitespace and newline followed by '<' with '> <'
    content = re.sub(r'>\s\n\s*<', '> <', content)
    return content

def process_html_file(file_path):
    """
    Apply processing functions to an HTML file.

    Args:
        file_path (str): Path to the HTML file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        # Remove <img> tags
        content = re.sub(r'<img\b[^>]*>', '', content, flags=re.IGNORECASE)

        # Replace <h1 style="text-align: center;"> with <h1>
        content = content.replace('<h1 style="text-align: center;">', '<h1>')

        # Capitalize first letter inside tags
        content = capitalize_first_letter_in_tags(content)

        # Combine <blockquote> lines
        content = combine_blockquote_lines_in_content(content)

        # Remove blank lines
        content = '\n'.join([line for line in content.splitlines() if line.strip()])

        # Combine specific HTML tags into the same line
        content = combine_html_tags_into_same_line(content)

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(content)

        logging.info(f"Processed HTML file: {file_path}")
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def combine_blockquote_lines_in_content(content):
    """
    Combine broken <blockquote> lines in HTML content.

    Args:
        content (str): HTML content as a string.

    Returns:
        str: Modified content with combined blockquotes.
    """
    lines = content.splitlines()
    combined_lines = []
    skip_next = False

    for i, line in enumerate(lines):
        if skip_next:
            skip_next = False
            continue

        line_stripped = line.strip()

        # Check for opening <blockquote> without closing tag
        if '<blockquote>' in line_stripped and '</blockquote>' not in line_stripped:
            if i + 1 < len(lines):
                next_line = lines[i + 1].strip()
                combined_line = line_stripped + ' ' + next_line
                combined_lines.append(combined_line)
                skip_next = True
            else:
                combined_lines.append(line_stripped)
        # Check for closing </blockquote> without opening tag
        elif '</blockquote>' in line_stripped and '<blockquote>' not in line_stripped:
            if combined_lines:
                combined_lines[-1] += ' ' + line_stripped
            else:
                combined_lines.append(line_stripped)
        else:
            combined_lines.append(line_stripped)

    return '\n'.join(combined_lines)

def remove_h1_strong_tags(folder_path):
    """
    Remove <h1><strong> and </strong></h1> tags from the first line of HTML files.

    Args:
        folder_path (str): Path to the folder.
    """
    for subdir, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(subdir, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as file_obj:
                        lines = file_obj.readlines()

                    if lines:
                        lines[0] = lines[0].replace('<h1><strong>', '').replace('</strong></h1>', '')

                    with open(file_path, 'w', encoding='utf-8') as file_obj:
                        file_obj.writelines(lines)

                    logging.info(f"Removed <h1><strong> tags in: {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def add_xem_them_to_html(folder_path):
    """
    Add the string "Xem thêm: " at the end of each HTML file.

    Args:
        folder_path (str): Path to the folder.
    """
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(root, file)
                try:
                    # Ensure we append to the file
                    with open(file_path, 'a', encoding='utf-8') as f:
                        f.write('\n\nXem thêm: ')
                    logging.info(f"Added 'Xem thêm: ' to {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def remove_quotes_from_html(folder_path):
    """
    Remove all quotation marks from HTML files.

    Args:
        folder_path (str): Path to the folder.
    """
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()

                    # Remove all quotation marks
                    content = content.replace('"', '').replace('“', '').replace('”', '')

                    with open(file_path, 'w', encoding='utf-8') as f:
                        f.write(content)

                    logging.info(f"Removed quotation marks from: {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def capitalize_after_colon_in_html(folder_path):
    """
    Capitalize the first letter after a colon in HTML files.

    Args:
        folder_path (str): Path to the folder.
    """
    pattern = re.compile(r'(: )([a-z])')

    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()

                    # Capitalize first letter after ": "
                    modified_content = pattern.sub(lambda m: m.group(1) + m.group(2).upper(), content)

                    if content != modified_content:
                        with open(file_path, 'w', encoding='utf-8') as f:
                            f.write(modified_content)
                        logging.info(f"Capitalized text after colon in: {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

# ----------------------------------------------------------------------------------------------------------------------
# Image Processing Functions (Continued)
# ----------------------------------------------------------------------------------------------------------------------

def convert_and_resize_images(folder_path, target_width=1200):
    """
    Convert all images to JPG and resize them to a target width while maintaining aspect ratio.

    Args:
        folder_path (str): Path to the folder containing images.
        target_width (int): The target width for the resized images. Default is 1200 pixels.
    """
    # Define supported image formats excluding '.jpg' and '.jpeg'
    supported_formats = ('.png', '.bmp', '.gif', '.tiff', '.webp')

    for root, _, files in os.walk(folder_path):
        for file in files:
            file_lower = file.lower()
            file_path = os.path.join(root, file)

            try:
                # Handle .jpg and .jpeg files
                if file_lower.endswith(('.jpg', '.jpeg')):
                    with Image.open(file_path) as img:
                        img = apply_exif_orientation(img)

                        # Resize if necessary (regardless of size)
                        if img.width != target_width:
                            ratio = target_width / float(img.width)
                            new_size = (target_width, int(img.height * ratio))
                            img = img.resize(new_size, Image.LANCZOS)
                            img.save(file_path, format='JPEG', quality=85)
                            logging.info(f"Resized image: {file_path}")
                        else:
                            logging.info(f"Image already at target size: {file_path}")

                # Handle other supported formats
                elif file_lower.endswith(supported_formats):
                    with Image.open(file_path) as img:
                        img = apply_exif_orientation(img)

                        # Convert to RGB if necessary
                        if img.mode in ("RGBA", "P", "LA"):
                            img = img.convert("RGB")
                            logging.debug(f"Converted image mode to RGB: {file_path}")

                        # Resize if necessary (regardless of size)
                        if img.width != target_width:
                            ratio = target_width / float(img.width)
                            new_size = (target_width, int(img.height * ratio))
                            img = img.resize(new_size, Image.LANCZOS)
                            logging.info(f"Resized image: {file_path}")

                        # Save as JPG
                        new_file_path = os.path.splitext(file_path)[0] + '.jpg'
                        img.save(new_file_path, 'JPEG', quality=85)
                        logging.info(f"Converted and saved image as JPG: {new_file_path}")

                    # Remove the original file
                    os.remove(file_path)
                    logging.info(f"Removed original image: {file_path}")

                else:
                    logging.debug(f"Unsupported file format, skipping: {file_path}")

            except Exception as e:
                logging.error(f"Error processing {file_path}: {e}")

def apply_exif_orientation(img):
    """
    Apply EXIF orientation to the image to ensure correct dimensions.

    Args:
        img (PIL.Image.Image): The image to be oriented.

    Returns:
        PIL.Image.Image: The oriented image.
    """
    try:
        exif = img._getexif()
        if exif is not None:
            for key, value in exif.items():
                tag = ExifTags.TAGS.get(key, key)
                if tag == 'Orientation':
                    orientation = value
                    break
            else:
                orientation = None

            if orientation == 3:
                img = img.rotate(180, expand=True)
                logging.debug("Applied EXIF orientation: rotated 180 degrees")
            elif orientation == 6:
                img = img.rotate(270, expand=True)
                logging.debug("Applied EXIF orientation: rotated 270 degrees")
            elif orientation == 8:
                img = img.rotate(90, expand=True)
                logging.debug("Applied EXIF orientation: rotated 90 degrees")
    except Exception as e:
        logging.warning(f"Could not apply EXIF orientation: {e}")
    return img

# ----------------------------------------------------------------------------------------------------------------------
# Main Execution Flow
# ----------------------------------------------------------------------------------------------------------------------

def main(folder_path):
    """
    Main function to execute all processing steps.

    Args:
        folder_path (str): Path to the folder to process.
    """

    logging.info("Starting image processing...")

    # Step 1: Delete unreadable image files
    delete_unreadable_images(folder_path)

    # Step 2: Delete blank (all white) images
    delete_blank_images(folder_path)

    # Step 3: Delete subfolders with few images
    delete_subfolders_with_few_images(folder_path, threshold=3)

    # Step 4: Trim image files in remaining subfolders
    trim_image_files(folder_path, target_count=4)

    logging.info("Image processing completed.")

    logging.info("Starting HTML processing...")

    # Step 5: Delete small HTML folders
    delete_small_html_folders(folder_path, min_char_count=6000)

    # Step 6: Process HTML files
    process_html_files(folder_path)

    # Step 7: Additional HTML modifications
    # Uncomment the following lines if needed
    # add_xem_them_to_html(folder_path)
    # remove_h1_strong_tags(folder_path)
    # remove_quotes_from_html(folder_path)
    capitalize_after_colon_in_html(folder_path)

    # Step 8: Convert and resize images
    convert_and_resize_images(folder_path, target_width=1200)

    logging.info("HTML processing completed.")

if __name__ == "__main__":
    # Specify the folder path here
    folder_path = r'C:\Users\anhb3\Desktop\long\test\Thời trang quyến rũ'
    main(folder_path)

script 2:
import os
import csv
import re


def read_image_html_from_csv(csv_file_path):
    image_html_dict = {}
    with open(csv_file_path, 'r', encoding='utf-8') as file:
        csv_reader = csv.reader(file)
        for row in csv_reader:
            if len(row) == 2:
                folder_name = row[0].strip()
                if folder_name not in image_html_dict:
                    image_html_dict[folder_name] = []
                image_html_dict[folder_name].append(row[1].strip())
    return image_html_dict


def read_links_from_txt(txt_file_path):
    with open(txt_file_path, 'r', encoding='utf-8') as file:
        return [line.strip() for line in file if line.strip()]


def insert_image_html(html_content, image_html_lines):
    content_length = len(html_content)
    insertion_points = [
        int(content_length * 0.125),
        int(content_length * 0.375),
        int(content_length * 0.625),
        int(content_length * 0.875)
    ]

    for i, point in enumerate(insertion_points):
        start_of_line = html_content.rfind('\n', 0, point)
        end_of_line = html_content.find('\n', point)

        if start_of_line == -1:
            start_of_line = 0
        if end_of_line == -1:
            end_of_line = content_length

        if point - start_of_line <= end_of_line - point:
            insertion_index = start_of_line + 1
        else:
            insertion_index = end_of_line + 1

        if i < len(image_html_lines):
            html_content = html_content[:insertion_index] + image_html_lines[i] + '\n' + html_content[insertion_index:]

    return html_content


def insert_link_at_end(html_content, link):
    last_xem_them = html_content.rfind("Xem thêm: ")
    if last_xem_them != -1:
        insertion_point = last_xem_them + len("Xem thêm: ")
        return html_content[:insertion_point] + link + html_content[insertion_point:]
    else:
        return html_content.rstrip() + "\nXem thêm: " + link


def insert_formatted_title(html_content):
    # Find the title
    title_match = re.search(r'<h1 style="text-align: Center;"><strong>(.*?)</strong></h1>', html_content)
    if not title_match:
        return html_content  # No title found, return unchanged content

    title = title_match.group(1)

    # Prepare the formatted title
    if '?' in title:
        formatted_title = f'<h2><strong>{title} Bài viết dưới đây sẽ giải thích cho bạn:</strong></h2>'
    else:
        formatted_title = f'<h2><strong>{title} là gì? Bài viết dưới đây sẽ giải thích cho bạn:</strong></h2>'

    # Find the first </blockquote> and insert the formatted title after it
    blockquote_end = html_content.find('</blockquote>')
    if blockquote_end != -1:
        insertion_point = html_content.find('\n', blockquote_end) + 1
        return html_content[:insertion_point] + formatted_title + '\n' + html_content[insertion_point:]
    else:
        return html_content  # No </blockquote> found, return unchanged content


def insert_title_in_blockquote(html_content):
    # Find the title
    title_match = re.search(r'<h1 style="text-align: Center;"><strong>(.*?)</strong></h1>', html_content)
    if not title_match:
        return html_content  # No title found, return unchanged content

    title = title_match.group(1)

    # Find the first <blockquote> tag
    blockquote_start = html_content.find('<blockquote>')
    if blockquote_start == -1:
        return html_content  # No <blockquote> found, return unchanged content

    # Insert the title followed by " - " just after the opening <blockquote> tag
    insertion_point = blockquote_start + len('<blockquote>')
    return html_content[:insertion_point] + f'{title} - ' + html_content[insertion_point:]


def process_files(csv_file_path, html_folder_path, links_file_path):
    image_html_dict = read_image_html_from_csv(csv_file_path)
    links = read_links_from_txt(links_file_path)
    link_index = 0

    all_html_files = []
    for root, dirs, files in os.walk(html_folder_path):
        for file in files:
            if file.endswith('.html'):
                all_html_files.append(os.path.join(root, file))
    all_html_files.sort()

    for file_path in all_html_files:
        folder_name = os.path.basename(os.path.dirname(file_path))

        with open(file_path, 'r', encoding='utf-8') as f:
            html_content = f.read()

        # Insert title in blockquote
        html_content = insert_title_in_blockquote(html_content)

        # Insert formatted title
        html_content = insert_formatted_title(html_content)

        # Insert image HTML if the folder name matches
        if folder_name in image_html_dict:
            html_content = insert_image_html(html_content, image_html_dict[folder_name])

        # Insert link at the end
        if link_index < len(links):
            html_content = insert_link_at_end(html_content, links[link_index])
            link_index += 1

        # Save the modified content
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(html_content)

        # Reset link index if we've used all links
        if link_index >= len(links):
            link_index = 0

# Usage
csv_file_path = r'C:\Users\anhb3\Desktop\long\chen_anh.csv'
html_folder_path = r'C:\Users\anhb3\Desktop\long\Game MOBA Việt'
links_file_path = r'C:\Users\anhb3\Desktop\long\di_link.txt'

process_files(csv_file_path, html_folder_path, links_file_path)

combine these two scripts into a single script.