Compare commits

...

30 Commits

Author SHA1 Message Date
xkrrudah 35dd01f7fe Merge pull request '[MR]Version up to 1.0.4' (#14) from develop into main
Reviewed-on: #14
2026-05-08 14:35:59 +09:00
xkrrudah 85a52f2ae4 Version up to 1.0.4
- CHANGELOG.md
- Custom_Lib.py
- pyproject.toml
- README.md
2026-05-08 14:33:30 +09:00
xkrrudah bd18a69862 Merge pull request '[MR]Modified if modules are not install, pass' (#13) from develop into main
Reviewed-on: #13
2026-05-08 14:28:41 +09:00
xkrrudah 122f1ef235 Modified if modules are not install, pass 2026-05-08 14:26:29 +09:00
xkrrudah ca5d0a1b6e Merge pull request '[MR]Moved my_image/my_crawling.py' (#12) from develop into main
Reviewed-on: #12
2026-05-08 14:18:45 +09:00
xkrrudah 2286b76664 Moved my_image/my_crawling.py 2026-05-08 14:17:50 +09:00
xkrrudah be1ed37f65 Merge pull request '[MR]Added __init__.py' (#11) from develop into main
Reviewed-on: #11
2026-05-08 14:16:39 +09:00
xkrrudah cd61d42115 Added __init__.py 2026-05-08 14:15:58 +09:00
xkrrudah 1381575476 Merge pull request '[MR]Added pyproject.toml and CHANGELOG/README.md' (#10) from develop into main
Reviewed-on: #10
2026-05-08 14:14:42 +09:00
xkrrudah 5ec1bdd16a Added pyproject.toml and CHANGELOG/README.md 2026-05-08 14:12:47 +09:00
xkrrudah c4ad6bed2e Merge pull request '[MR]Modified .gitignore' (#9) from develop into main
Reviewed-on: #9
2026-05-08 14:11:14 +09:00
xkrrudah 7d3fea659f Modified .gitignore 2026-05-08 14:10:14 +09:00
xkrrudah 1736992a79 Merge pull request '[MR]Version up to 1.0.3' (#8) from develop into main
Reviewed-on: #8
2026-05-08 10:06:06 +09:00
xkrrudah edea91ee8c Version up to 1.0.3 2026-05-08 10:05:26 +09:00
xkrrudah d7b8fef42f Merge pull request '[MR]Added my_crawling.py' (#7) from develop into main
Reviewed-on: #7
2026-05-08 10:04:31 +09:00
xkrrudah f21db4cd33 Added my_crawling.py 2026-05-08 10:03:25 +09:00
xkrrudah f77fd3d6c9 Merge pull request '[MR]Deleted __pycache__' (#6) from develop into main
Reviewed-on: #6
2026-05-07 17:31:44 +09:00
xkrrudah 37d2505daf Merge branch 'main' into develop 2026-05-07 17:31:35 +09:00
xkrrudah 6ff12aee85 Modified .gitignore 2026-05-07 17:30:27 +09:00
xkrrudah 9983f55932 Deleted __pycache__ 2026-05-07 17:27:57 +09:00
xkrrudah cd64818e29 Merge pull request '[MR]Version up to 1.0.2' (#5) from develop into main
Reviewed-on: #5
2026-05-07 17:11:51 +09:00
xkrrudah e491e5083d Version up to 1.0.2 2026-05-07 17:11:18 +09:00
xkrrudah 92e45cd9fc Merge pull request '[MR]Modified print style' (#4) from develop into main
Reviewed-on: #4
2026-05-07 17:10:44 +09:00
xkrrudah 15049f0af2 Modified print style 2026-05-07 17:10:03 +09:00
xkrrudah e3011bd7ae Merge pull request '[MR]Added my_image version and comment' (#3) from develop into main
Reviewed-on: #3
2026-05-07 15:50:10 +09:00
xkrrudah f3b6ea43a5 Added my_image version and comment 2026-05-07 15:48:45 +09:00
xkrrudah ec9682dce9 Merge pull request '[MR]Added my_image function' (#2) from develop into main
Reviewed-on: #2
2026-05-07 15:44:36 +09:00
xkrrudah a1180534a7 Added my_image function 2026-05-07 15:43:35 +09:00
xkrrudah 1e0e149338 Merge pull request '[MR]Added my image.py' (#1) from develop into main
Reviewed-on: #1
2026-05-07 15:42:41 +09:00
xkrrudah fef5421409 Added my image.py 2026-05-07 15:41:34 +09:00
18 changed files with 697 additions and 9 deletions
+4 -2
View File
@@ -1,5 +1,7 @@
# 20260507 tak created gitignore # 20260507 tak created gitignore
.DS_Store .DS_Store
.idea/ .idea/
.venv /log
/__pycache__
/custom_lib.egg-info
.venv/
+21
View File
@@ -0,0 +1,21 @@
# Changelog
## v1.0.4
- Modified if modules are not install, pass
- Moved `my_image/my_crawling.py`
- Added `__init__.py`
- Added `pyproject.toml` and `CHANGELOG/README.md`
- Modified `.gitignore`
## v1.0.3
- Added `my_crawling.py`
- Deleted `__pycache__`
## v1.0.2
- Modified print style
- Added my_image version and comment
- Added my_image function
- Added my image.py
## v1.0.1
- First commit
+37 -7
View File
@@ -1,6 +1,6 @@
__version__ = "1.0.0" __version__ = "1.0.4"
# --- custom --- # --- required modules ---
from custom_lib import my_file_config from custom_lib import my_file_config
from custom_lib import my_logger from custom_lib import my_logger
from custom_lib import my_uuid from custom_lib import my_uuid
@@ -11,11 +11,41 @@ MODULES = [
my_uuid, my_uuid,
] ]
def print_versions(): # --- optional modules ---
for module in MODULES: OPTIONAL_MODULES = []
MISSING_MODULES = []
def _try_import_optional(module_path):
try:
module = __import__(f"custom_lib.{module_path}", fromlist=["*"])
OPTIONAL_MODULES.append(module)
return True
except ImportError:
MISSING_MODULES.append(module_path.split(".")[1])
return False
# 20260507 tak add my_image
_try_import_optional("image.my_image")
# 20260508 tak add my_crawling
_try_import_optional("crawling.my_crawling")
def print_modules():
for module in MODULES + OPTIONAL_MODULES:
version = getattr(module, "__version__", "unknown") version = getattr(module, "__version__", "unknown")
print(f"[MODULE] {module.__name__} v{version}") print(f"# [MODULE] {module.__name__} v{version}")
def print_missing_modules():
for module in MISSING_MODULES:
print(f"# [MISSING] {module}")
def init(): def init():
print(f"Custom_Lib v{__version__}") print("\n" + "#" * 60)
print_versions() print(f"# Custom_Lib v{__version__}")
print("=" * 60)
print_modules()
print_missing_modules()
print("#" * 60)
+112
View File
@@ -0,0 +1,112 @@
# Custom Lib
My Custom Python Library.
---
## Features
- File utility
- Logger
- UUID
- Image utility
- Crawling utility
---
## Requirements
- Python 3.9+
---
## Note
- Move `pyproject.toml` to the project directory.
---
## Install
### Common
```bash
python -m pip install -e .
```
### Image
```bash
python -m pip install -e .[image]
```
### Crawling
```bash
python -m pip install -e .[crawling]
```
### All
```bash
python -m pip install -e .[all]
```
---
## Usage
```python
from custom_lib import Custom_Lib as custom
custom.init()
```
---
## Modules
| Module | Description |
|----------------|------------------|
| my_file_config | File utility |
| my_logger | Logger |
| my_uuid | UUID |
| my_image | Image utility |
| my_crawling | Crawling utility |
---
## Optional Dependencies
| Option | Packages |
|---|-----------------------|
| image | Pillow, OpenCV, Nump |
| crawling | Requests, BeautifulSoup |
---
## Project Structure
```text
custom_lib/
├─ crawling/
│ └─ __init__.py
│ └─ my_crawling.py
├─ image/
│ └─ __init__.py
│ └─ my_image.py
├─ __init__.py
├─ CHANGELOG.md
├─ Custom_Lib.py
├─ my_file_config.py
├─ my_logger.py
├─ my_uuid.py
├─ pyproject.toml
└─ README.md
```
---
## Version
### v1.0.4
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
__version__ = "1.0.0"
# --- package ---
# --- crawling ---
from bs4 import BeautifulSoup
from urllib import request
def get_lxml(target_url):
request_url = request.urlopen(target_url)
bs = BeautifulSoup(request_url, "lxml", from_encoding='utf-8')
return bs
View File
Binary file not shown.
Binary file not shown.
+478
View File
@@ -0,0 +1,478 @@
__version__ = "1.0.0"
# --- package ---
# --- color ---
import webcolors
# --- image ---
from pathlib import Path
from PIL import Image, ImageSequence
import cv2
import numpy as np
from io import BytesIO
# --- base64 ---
import base64
# --- common ---
import os
from datetime import datetime
import shutil
import time
import inspect
def hex_to_rgb(hex_code):
"""Convert a hex code to RGB values."""
hex_code = hex_code.lstrip('#')
return tuple(int(hex_code[i:i+2], 16) for i in (0, 2, 4))
def rgb_to_hex(rgb_values):
"""Convert RGB values to a hex code."""
r, g, b = rgb_values
hex_code = "{:02x}{:02x}{:02x}".format(r, g, b)
return hex_code
def get_color_name(rgb_values):
"""Return the name of the closest matching color."""
min_colors = {}
for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
r_c, g_c, b_c = hex_to_rgb(key)
rd = (r_c - rgb_values[0]) ** 2
gd = (g_c - rgb_values[1]) ** 2
bd = (b_c - rgb_values[2]) ** 2
min_colors[(rd + gd + bd)] = name
return min_colors[min(min_colors.keys())]
def image_resize(import_path, import_raw_path, export_path, img, resize_target, resize_resolution):
image_ext = os.path.splitext(img)[1].split('.')[1]
move_date = datetime.now().strftime('%y%m%d%H%M%S')
img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
shutil.move(img, f"{import_path}{img_new_nm}")
image = cv2.imread(f'{import_path}{img_new_nm}')
img_width = image.shape[1]
img_height = image.shape[0]
if resize_target == 'W':
resize_height = int(img_height * (resize_resolution / img_width))
dim = (resize_resolution, resize_height)
elif resize_target == 'H':
resize_width = int(img_width * (resize_resolution / img_height))
dim = (resize_width, resize_resolution)
resized_img = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
cv2.imwrite(f"{export_path}export_{move_date}.{image_ext}", resized_img)
move_date = datetime.now().strftime('%y%m%d%H%M%S')
img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")
time.sleep(1)
return 'Success Image Resize'
def distribute_serveral_png(import_path, import_raw_path, export_path, img):
image_ext = os.path.splitext(img)[1].split('.')[1]
if image_ext == 'png':
move_date = datetime.now().strftime('%y%m%d%H%M%S')
img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
shutil.move(img, f"{import_path}{img_new_nm}")
pc_sizes = [16, 24, 32, 48, 64,
72, 80, 96, 128, 256]
and_sizes = [36, 48, 72, 96, 144]
ios_sizes = [20, 29, 40, 40, 48,
50, 55, 57, 58, 60,
60, 72, 76, 80, 87,
88, 100, 114, 120, 144,
152, 167, 172, 180, 196,
1024]
android_names = ['launcher_ldpi',
'launcher_mdpi',
'launcher_hdpi',
'launcher_xhdpi',
'launcher_xxhdpi']
ios_names = ['icon-20',
'icon-29',
'icon-20@2x',
'icon-40',
'icon-24@2x',
'icon-50',
'icon-27.5@2x',
'icon',
'icon-29@2x',
'icon-20@3x',
'icon-60',
'icon-72',
'icon-76',
'icon-40@2x',
'icon-83.5@2x',
'icon-44@2x',
'icon-50@2x',
'icon@2x',
'icon-60@2x',
'icon-72@2x',
'icon-76@2x',
'icon-29@3x',
'icon-86@2x',
'icon-60@3x',
'icon-98@2x',
'icon-1024']
pc_icon_sizes = [(x, x) for x in pc_sizes]
and_icon_sizes = [(x, x) for x in and_sizes]
ios_icon_sizes = [(x, x) for x in ios_sizes]
image = Image.open(f'{import_path}{img_new_nm}')
for size in pc_icon_sizes:
exp_img_new_nm = f"{export_path}export_{move_date}_{str(size[0])}.{image_ext}"
new_image = image.resize(size)
new_image.save(exp_img_new_nm)
new_logo_ico_filename = f"{export_path}export_{move_date}_main.ico"
new_logo_ico = image.resize(size)
new_logo_ico.save(new_logo_ico_filename, format="ICO", quality=100)
for num, label in zip(and_icon_sizes, android_names):
exp_img_new_nm = f"{export_path}export_{move_date}_{label}.{image_ext}"
new_image = image.resize(num)
new_image.save(exp_img_new_nm)
for num, label in zip(ios_icon_sizes, ios_names):
exp_img_new_nm = f"{export_path}export_{move_date}_{label}.{image_ext}"
new_image = image.resize(num)
new_image.save(exp_img_new_nm)
time.sleep(1)
shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")
return 'Success Image to Icon'
else:
return 'Only Convert .PNG'
def bake_one_big_png_to_ico(import_path, import_raw_path, export_path, img):
"""Converts one big PNG into one ICO file.
args:
sourcefile (str): Pathname of a PNG file.
targetfile (str): Pathname of the resulting ICO file.
sizes (list of int): Requested sizes of the resulting
icon file, defaults to [16, 32, 48].
Use this function if you have one big, square PNG file
and dont care about fine-tuning individual icon sizes.
Example::
sourcefile = "Path/to/high_resolution_logo_512x512.png"
targetfile = "Path/to/logo.ico"
sizes = [16, 24, 32, 48, 256]
bake_one_big_png_to_ico(sourcefile, targetfile, sizes)
"""
# if sizes is None:
# sizes = [16, 24, 32, 48, 256, 512]
# icon_sizes = [(x, x) for x in sizes]
# Image.open(sourcefile).save(targetfile, icon_sizes=icon_sizes)
image_ext = os.path.splitext(img)[1].split('.')[1]
if image_ext == 'png':
move_date = datetime.now().strftime('%y%m%d%H%M%S')
img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
shutil.move(img, f"{import_path}{img_new_nm}")
sizes = [16, 24, 32, 48, 64, 72, 80, 96, 128, 256]
icon_sizes = [(x, x) for x in sizes]
print(icon_sizes)
# img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
Image.open(f'{import_path}{img_new_nm}').save(f"{export_path}export_{move_date}.ico", icon_sizes=icon_sizes)
# shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")
time.sleep(1)
return 'Success Image to Icon'
else:
return 'Only Convert .PNG'
def bake_several_pngs_to_ico(sourcefiles, targetfile):
"""Converts several PNG files into one ICO file.
args:
sourcefiles (list of str): A list of pathnames of PNG files.
targetfile (str): Pathname of the resulting ICO file.
Use this function if you want to have fine-grained control over
the resulting icon file, providing each possible icon resolution
individually.
Example::
sourcefiles = [
"Path/to/logo_16x16.png",
"Path/to/logo_32x32.png",
"Path/to/logo_48x48.png"
]
targetfile = "Path/to/logo.ico"
bake_several_pngs_to_ico(sourcefiles, targetfile)
"""
# Write the global header
number_of_sources = len(sourcefiles)
data = bytes((0, 0, 1, 0, number_of_sources, 0))
offset = 6 + number_of_sources * 16
# Write the header entries for each individual image
for sourcefile in sourcefiles:
img = Image.open(sourcefile)
data += bytes((img.width, img.height, 0, 0, 1, 0, 32, 0, ))
bytesize = Path(sourcefile).stat().st_size
data += bytesize.to_bytes(4, byteorder="little")
data += offset.to_bytes(4, byteorder="little")
offset += bytesize
# Write the individual image data
for sourcefile in sourcefiles:
data += Path(sourcefile).read_bytes()
# Save the icon file
Path(targetfile).write_bytes(data)
# def image_to_icon(import_path, import_raw_path, export_path, img, resize_resolution):
# image_ext = os.path.splitext(img)[1].split('.')[1]
#
# if image_ext == 'png':
# move_date = datetime.now().strftime('%y%m%d%H%M%S')
#
# img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
#
# shutil.move(img, f"{import_path}{img_new_nm}")
#
# image = cv2.imread(f'{import_path}{img_new_nm}')
#
# resized_img = cv2.resize(image, (resize_resolution, resize_resolution))
#
# cv2.imwrite(f"{export_path}export_{image_ext}_{move_date}.ico", resized_img, [cv2.IMWRITE_PNG_COMPRESSION, 9])
#
# move_date = datetime.now().strftime('%y%m%d%H%M%S')
#
# img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
#
# shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")
#
# time.sleep(1)
#
# return 'Success Image to Icon'
#
# else:
# return 'Only Convert .PNG'
def image_to_extension(import_path, import_raw_path, export_path, img, extension):
image_ext = os.path.splitext(img)[1].split('.')[1]
move_date = datetime.now().strftime('%y%m%d%H%M%S')
img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
shutil.move(img, f"{import_path}{img_new_nm}")
if image_ext == 'gif':
# Load the GIF into a PIL Image object
image = Image.open(f"{import_path}{img_new_nm}")
if extension == 'png':
# Split the GIF into individual frames
frames = []
for frame in ImageSequence.Iterator(image):
frames.append(frame.copy())
# Save each frame as an individual image file
for i, frame in enumerate(frames):
move_date = datetime.now().strftime('%y%m%d%H%M%S')
frame.save(f"{export_path}export_{move_date}_{i}.{extension}", format=extension)
else:
image = image.convert("RGB")
image.save(f"{export_path}export_{move_date}.{extension}")
elif image_ext == 'png':
# Load the GIF into a PIL Image object
image = Image.open(f"{import_path}{img_new_nm}")
background = Image.new('RGB', image.size, (255, 255, 255))
background.paste(image, mask=image)
background.save(f"{export_path}export_{move_date}.{extension}")
else:
image = cv2.imread(f'{import_path}{img_new_nm}')
cv2.imwrite(f"{export_path}export_{move_date}.{extension}", image)
shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")
time.sleep(1)
return 'Success Image Extension'
def base64_to_image(import_raw_path, export_path, base64_str, txt_file, file):
try:
# ImageFile.LOAD_TRUNCATED_IMAGES = True
# Remove the "data:image/png;base64," prefix from the string
base64_string = base64_str.split(',')[1]
# Determine the length of the input string
length = len(base64_string)
# Add padding characters if necessary
if length % 4 != 0:
base64_string += '=' * (4 - length % 4)
# Decode the base64-encoded string into bytes
image_bytes = base64.b64decode(base64_string)
image = Image.open(BytesIO(image_bytes))
image_ext = base64_str.split('/')[1].split(';')[0]
move_date = datetime.now().strftime('%y%m%d%H%M%S')
if image_ext == 'gif':
if image.mode == "RGBA":
# If the image has an alpha channel, convert it to RGB mode
image = image.convert("RGB")
# Get the duration of each frame in the animation
frame_durations = []
frames = []
for frame in ImageSequence.Iterator(image):
frame_durations.append(frame.info.get("duration", 100)) # default duration is 100ms
frames.append(frame.copy())
# Calculate the speed of the animation
average_duration = sum(frame_durations) / len(frame_durations)
# if not average_duration == 0.0:
if average_duration != 0:
speed = 1 / (average_duration / 1000) # speed in frames per second
else:
speed = 0
frames[0].save(f"{export_path}export_{move_date}.{image_ext}", format="GIF", save_all=True, append_images=frames[1:], duration=speed, loop=0)
# Check if the image is a PNG with transparency
elif image_ext == "png":
# Convert the image to RGB mode to remove the alpha channel
image = image.convert("RGBA")
# Save the image to file
image.save(f"{export_path}export_{move_date}.{image_ext}")
else:
# Convert the bytes to a numpy array
image_array = np.frombuffer(image_bytes, dtype=np.uint8)
# Decode the numpy array into an OpenCV image
image = cv2.imdecode(image_array, cv2.IMREAD_UNCHANGED)
# Save the image to a file
cv2.imwrite(f"{export_path}export_{move_date}.{image_ext}", image)
file.close()
image_ext = os.path.splitext(txt_file)[1].split('.')[1]
img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
shutil.move(txt_file, f"{import_raw_path}{img_new_nm}")
time.sleep(1)
return 'Success Image Convert'
except Exception as e:
return e
def image_to_base64(import_path, import_raw_path, export_path, img):
image_ext = os.path.splitext(img)[1].split('.')[1]
move_date = datetime.now().strftime('%y%m%d%H%M%S')
img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
shutil.move(img, f"{import_path}{img_new_nm}")
if image_ext == 'gif':
image_pil = Image.open(f"{import_path}{img_new_nm}")
# Convert the image to a sequence of frames
frames = []
try:
while True:
frames.append(image_pil.copy())
image_pil.seek(len(frames)) # skip to the next frame
except EOFError:
pass # end of sequence
# Encode the frames as GIF bytes
with BytesIO() as output:
frames[0].save(output, format='GIF', save_all=True, append_images=frames[1:])
image_bytes = output.getvalue()
elif image_ext == 'png':
image = Image.open(f'{import_path}{img_new_nm}')
if image.mode != 'RGBA':
image = image.convert('RGBA')
buffer = BytesIO()
image.save(buffer, format='PNG')
# Encode the image data as a base64 string
image_bytes = buffer.getvalue()
else:
# Load the image file
image = cv2.imread(f'{import_path}{img_new_nm}')
# Convert the image to bytes
success, image_bytes = cv2.imencode(f'.{image_ext}', image)
image_bytes = image_bytes.tobytes()
# Encode the bytes as a base64 string
base64_str = base64.b64encode(image_bytes).decode()
# Create the base64-encoded image string
base64_str_dec = f"data:image/{image_ext};base64," + base64_str
shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")
time.sleep(1)
# Print the base64-encoded image string
return base64_str_dec, 'Success Base64 Convert', f"{export_path}export_{move_date}"
+33
View File
@@ -0,0 +1,33 @@
[project]
name = "custom_lib"
version = "1.0.4"
description = "My Custom Python Library"
requires-python = ">=3.9"
# --- required ---
dependencies = []
# --- optional ---
[project.optional-dependencies]
image = [
"webcolors==1.13",
"pillow==9.4.0",
"opencv-contrib-python==4.5.2.52",
"numpy<2"
]
crawling = [
"requests==2.26.0",
"beautifulsoup4==4.11.2"
]
all = [
"webcolors==1.13",
"pillow==9.4.0",
"opencv-contrib-python==4.5.2.52",
"numpy<2",
"requests==2.26.0",
"beautifulsoup4==4.11.2"
]
# --- path ---
[tool.setuptools.packages.find]
include = ["custom_lib*"]