Skip to content

Commit 3dcccba

Browse files
committed
Integrate recorder
transform rcorder to OOP Signed-off-by: Skye <[email protected]>
1 parent b5b5ee7 commit 3dcccba

File tree

12 files changed

+1059
-0
lines changed

12 files changed

+1059
-0
lines changed

.pyproject.toml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
[build-system]
2+
requires = ["setuptools>=61.0", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "filemac"
7+
version = "2.0.0" # Will be overridden by version.txt in setup.py unless removed from there
8+
description = "Open source Python CLI toolkit for conversion, manipulation, analysis of files (All major file operations)"
9+
readme = "README.md"
10+
requires-python = ">=3.6"
11+
license = { file = "LICENSE" }
12+
authors = [
13+
{ name = "wambua", email = "[email protected]" },
14+
]
15+
keywords = [
16+
"file-conversion",
17+
"file-analysis",
18+
"file-manipulation",
19+
"ocr",
20+
"image-conversion",
21+
"audio_effects",
22+
"voice_shift",
23+
"pdf",
24+
"docx",
25+
]
26+
classifiers = [
27+
"Environment :: Console",
28+
"Natural Language :: English",
29+
"Operating System :: OS Independent",
30+
"Programming Language :: Python",
31+
"Programming Language :: Python :: 3 :: Only",
32+
"Programming Language :: Python :: 3.11",
33+
"Programming Language :: Python :: 3.12",
34+
"Programming Language :: Python :: 3.13",
35+
]
36+
37+
dependencies = [
38+
"argparse",
39+
"pdfminer.six",
40+
"python-docx",
41+
"python-pptx",
42+
"gTTS",
43+
"pypandoc",
44+
"fitz", # Consider replacing with "PyMuPDF" if that's what's actually used
45+
"pydub",
46+
"Pillow",
47+
"pandas",
48+
"opencv-python",
49+
"pytesseract",
50+
"PyPDF2",
51+
"pdf2docx",
52+
"requests",
53+
"moviepy",
54+
"reportlab",
55+
"numpy",
56+
"pdf2image",
57+
"openpyxl",
58+
"rich",
59+
"tqdm",
60+
"ffmpeg-python",
61+
"librosa",
62+
"python-magic",
63+
"matplotlib",
64+
"soundfile",
65+
"SpeechRecognition",
66+
"colorama",
67+
"scipy",
68+
"PyMuPDF",
69+
"pyautogui",
70+
"imageio",
71+
"pynput",
72+
"pyaudio",
73+
]
74+
75+
[project.urls]
76+
Homepage = "https://pypi.org/project/filemac/"
77+
Source = "https://github.com/skye-cyber/filemac"
78+
Issues = "https://github.com/skye-cyber/filemac/issues"
79+
80+
[project.scripts]
81+
filemac = "filemac:main"
82+
Filemac = "filemac:main"
83+
FILEMAC = "filemac:main"

filemac/exceptions/handler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class FilemacException(Exception):
2+
"""Custom filemac exception handler"""

filemac/text2word.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""Create a word document directly from a text file."""
2+
3+
from docx import Document
4+
from docx.shared import Pt, RGBColor
5+
6+
from filemac_utils.colors import foreground
7+
8+
fcl = foreground()
9+
RESET = fcl.RESET
10+
11+
12+
class StyledText:
13+
"""
14+
Args:
15+
obj-> input object (normally a formated text file)
16+
fsize ->font-size default = 12: int
17+
fstyle -> font-name default = Times New Roman: str
18+
out_obj -> output object(file) name: str
19+
Returns:
20+
None
21+
22+
Given obj -> Text file where:
23+
'#' is used to specify formarting
24+
Only three heading leavels are supported.
25+
'#' Heading1,
26+
'##' -> Heading2,
27+
'###' -> Heading3
28+
"""
29+
30+
def __init__(
31+
self, obj, out_obj=None, fsize: int = 12, fstyle: str = "Times New Roman"
32+
):
33+
self.obj = obj
34+
self.out_obj = out_obj
35+
self.fsize = fsize
36+
self.fstyle = fstyle
37+
if self.out_obj is None:
38+
self.out_obj = f"{self.obj.split('.')[0]}_filemac.docx"
39+
40+
def text_to_word(self):
41+
"""
42+
Create new document,
43+
heading_styles -> define formating
44+
Open the text file and read it line by line.
45+
For every line check whether it starts with '#' format specify , ommit the specifier and formart the line.
46+
Strip empty spaces from every line.
47+
Set body font to fstyle and font size to fsize.
48+
"""
49+
50+
print(f"{fcl.BWHITE_FG}Set Font: {fcl.CYAN_FG}{self.fsize}{RESET}")
51+
print(f"{fcl.BWHITE_FG}Set Style: {fcl.CYAN_FG}{self.fstyle}{RESET}")
52+
# Create a new Document
53+
doc = Document()
54+
55+
# Define formatting for headings and body text
56+
head_font_name = self.fstyle
57+
heading_styles = {
58+
# Heading 1
59+
1: {"font_size": Pt(18), "font_color": RGBColor(126, 153, 184)},
60+
# Heading 2
61+
2: {"font_size": Pt(16), "font_color": RGBColor(0, 120, 212)},
62+
# Heading 3
63+
3: {"font_size": Pt(14), "font_color": RGBColor(0, 120, 212)},
64+
# Heading 4
65+
4: {"font_size": Pt(13), "font_color": RGBColor(0, 120, 212)},
66+
}
67+
68+
body_font_name = "Times New Roman"
69+
body_font_size = Pt(self.fsize)
70+
body_font_color = RGBColor(0, 0, 0) # Black color
71+
72+
# Open the text file and read content
73+
with open(self.obj, "r") as file:
74+
lines = file.readlines()
75+
76+
for i, line in enumerate(lines):
77+
print(
78+
f"{fcl.BWHITE_FG}Line: {fcl.DCYAN_FG}{i}{fcl.YELLOW_FG} of {fcl.BLUE_FG}{len(lines)}{RESET}",
79+
end="\r",
80+
)
81+
# Determine heading level or body text
82+
if line.startswith("#"):
83+
level = line.count("#")
84+
level = min(level, 3) # Support up to 3 levels of headings
85+
style = heading_styles.get(level, heading_styles[1])
86+
p = doc.add_paragraph()
87+
# Remove '#' and extra space
88+
run = p.add_run(line[level + 1 :].strip())
89+
run.font.size = style["font_size"]
90+
run.font.name = head_font_name
91+
run.font.color.rgb = style["font_color"]
92+
p.style = f"Heading{level}"
93+
else:
94+
p = doc.add_paragraph()
95+
run = p.add_run(line.strip())
96+
run.font.name = body_font_name
97+
run.font.size = body_font_size
98+
run.font.color.rgb = body_font_color
99+
100+
# Save the document
101+
print("\n")
102+
doc.save(self.out_obj)
103+
print(
104+
f"{fcl.BWHITE_FG}Text file converted to Word document: {fcl.MAGENTA_FG}{self.out_obj}{RESET}"
105+
)
106+
107+
108+
if __name__ == "__main__":
109+
init = StyledText("/home/skye/Documents/FMAC/file2.txt")
110+
111+
# Call the function
112+
init.text_to_word()

filemac/video_analyzer.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""A basic/simple file analyzer"""
2+
3+
import sys
4+
import cv2
5+
import numpy as np
6+
from filemac_utils.colors import foreground
7+
import ffmpeg
8+
9+
fcl = foreground()
10+
RESET = fcl.RESET
11+
12+
13+
class SimpleAnalyzer:
14+
"""Video - video object subject for analysis
15+
return video`s: duration, total_area and frame_count"""
16+
17+
def __init__(self, video):
18+
self.video = video
19+
20+
@staticmethod
21+
def get_metadata(input_file):
22+
"""Fetch the original bitrate of the video file using ffmpeg."""
23+
try:
24+
probe = ffmpeg.probe(input_file)
25+
print(probe.get("streams")[1])
26+
bitrate = None
27+
# Iterate over the streams and find the video stream
28+
for stream in probe["streams"]:
29+
bitrate = (
30+
stream.get("bit_rate", None)
31+
if stream["codec_type"] == "video"
32+
else None
33+
)
34+
aspect_ratio = (
35+
stream.get("sample_aspect_ratio")
36+
if stream["sample_aspect_ratio"]
37+
else None
38+
)
39+
codec_name = stream.get("codec_name") if stream["codec_name"] else None
40+
channels = stream.get("channels")
41+
42+
encoder = stream.get("encoder") if stream.get("encoder") else None
43+
break
44+
return bitrate, aspect_ratio, codec_name, channels, encoder
45+
except ffmpeg.Error as e:
46+
raise
47+
print(f"Error: {e}")
48+
except Exception as e:
49+
raise
50+
print(f"Error: {e}")
51+
52+
def analyze(self):
53+
"""Read the video file/obj
54+
Increase frame count and accumulate area
55+
Calculate current frame duration
56+
Display the resulting frame"""
57+
58+
try:
59+
# Read the video file
60+
cap = cv2.VideoCapture(self.video)
61+
print(f"{fcl.BYELLOW_FG}Initializing..{RESET}")
62+
# Initialize variables
63+
# Frame rate (fps)
64+
bitrate, aspect_ratio, codec_name, channels, encoder = self.get_metadata(
65+
self.video
66+
)
67+
frame_count = 0
68+
total_area = 0
69+
duration = 0
70+
71+
print(f"{fcl.DCYAN_FG}Working on it{RESET}")
72+
while True:
73+
ret, frame = cap.read()
74+
75+
if not ret:
76+
break
77+
# Increase frame count and accumulate area
78+
frame_count += 1
79+
total_area += np.prod(frame.shape[:2])
80+
81+
# Calculate current frame duration
82+
fps = cap.get(cv2.CAP_PROP_FPS)
83+
duration += 1 / fps
84+
85+
# Display the resulting frame
86+
cv2.imshow("Frame", frame)
87+
88+
# Break the loop after pressing 'q'
89+
if cv2.waitKey(1) == ord("q"):
90+
break
91+
92+
# Release the video capture object and close all windows
93+
cap.release()
94+
cv2.destroyAllWindows()
95+
96+
# Print results
97+
# print(f"Size {fcl.BGREEN_FG}{size}{RESET}Kb")
98+
print(f"Channels: {fcl.BGREEN_FG}{channels}{RESET}")
99+
print(f"Encoder {fcl.BGREEN_FG}{encoder}{RESET}")
100+
print(f"Bitrate {fcl.BGREEN_FG}{bitrate}{RESET}")
101+
print(f"Aspect ratio{fcl.BGREEN_FG}{aspect_ratio}{RESET}")
102+
print(f"Codec name {fcl.BGREEN_FG}{codec_name}{RESET}")
103+
print(f"Total Frames: {fcl.BGREEN_FG}{frame_count}{RESET}")
104+
print(
105+
f"Average Frame Area: {fcl.BGREEN_FG}{total_area / frame_count}{RESET}"
106+
)
107+
print(f"Duration: {fcl.BGREEN_FG}{duration:.2f}{RESET} seconds")
108+
return frame_count, total_area, duration
109+
except KeyboardInterrupt:
110+
print("\nExiting")
111+
sys.exit(1)
112+
except TypeError:
113+
pass
114+
except Exception as e:
115+
print(e)
116+
sys.exit(1)
117+
118+
119+
if __name__ == "__main__":
120+
vi = SimpleAnalyzer("/home/skye/Videos/demo.mkv")
121+
# SimpleAnalyzer.get_metadata("/home/skye/Videos/demo.mkv")
122+
vi.analyze()

filemac_utils/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)