Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Python
__pycache__/
*.py[cod]
*.pyo
*.pyd
*.so
*.egg-info/
*.egg
venv/
.env
.DS_Store

# IDEs
.vscode/
.idea/
*.swp

# Output folders
output/
74 changes: 73 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,74 @@
#!/bin/bash
echo alias tubestore=\"cd $PWD \; python3 main.py\" >> ~/.bashrc
set -e # stop on first error

# Colors for better UX
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
RESET="\033[0m"

echo -e "${GREEN}→ Setting up TubeStore environment...${RESET}"

# Check for python3
if ! command -v python3 &>/dev/null; then
echo -e "${YELLOW}⚠️ Python3 not found. Please install Python 3.8+ first.${RESET}"
exit 1
fi

# Create venv if not exists
if [ ! -d "venv" ]; then
python3 -m venv venv
echo -e "${GREEN}✓ Virtual environment created.${RESET}"
fi

#!/bin/bash
set -e # stop on first error

# Colors for better UX
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
RESET="\033[0m"

echo -e "${GREEN}→ Setting up TubeStore environment...${RESET}"

# Check for python3
if ! command -v python3 &>/dev/null; then
echo -e "${YELLOW}⚠️ Python3 not found. Please install Python 3.8+ first.${RESET}"
exit 1
fi

# Create venv if not exists
if [ ! -d "venv" ]; then
python3 -m venv venv
echo -e "${GREEN}✓ Virtual environment created.${RESET}"
fi

# Activate venv
source venv/bin/activate

# Install dependencies
pip install --upgrade pip
pip install -r requirements.txt
echo -e "${GREEN}✓ Dependencies installed.${RESET}"

# Add alias to shell profile
ALIAS_CMD="alias tubestore=\"cd $PWD && source venv/bin/activate && python3 main.py\""
PROFILE="$HOME/.bashrc"

# Detect zsh users
if [[ "$SHELL" == *"zsh"* ]]; then
PROFILE="$HOME/.zshrc"
fi

if ! grep -Fxq "$ALIAS_CMD" "$PROFILE"; then
echo "$ALIAS_CMD" >> "$PROFILE"
echo -e "${GREEN}✓ Alias 'tubestore' added to $PROFILE.${RESET}"
else
echo -e "${YELLOW}↺ Alias 'tubestore' already exists in $PROFILE.${RESET}"
fi

echo -e "${GREEN}🎉 Installation complete! Launching TubeStore...${RESET}"

# Directly run the same command as the alias (expanded properly)
cd "$PWD"
source venv/bin/activate
python3 main.py
125 changes: 103 additions & 22 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,113 @@
import os
import videoConversion as vc
import fileConversion as fc
import os
from tkinter import Tk, filedialog

# ==========================
# HELPERS
# ==========================

def select_file(title="Select a file", filetypes=(("All files", "*.*"),)):
"""
Open a native file dialog (cross-platform: Windows, macOS, Linux).
Returns the selected path or None if canceled.
"""
root = Tk()
root.withdraw() # hide the main Tkinter window
root.update()
filename = filedialog.askopenfilename(title=title, filetypes=filetypes)
root.destroy()
return filename if filename else None


def clear():
"""Clear console screen in a cross-platform way."""
os.system("cls" if os.name == "nt" else "clear")

# ==========================
# MAIN LOOP
# ==========================

if __name__ == "__main__":
os.system("clear")
clear()
while True:
print("TubeStore - Convert your data to youtube uploadable video format\n\n")
print("1. Create video from data\n")
print("2. Retrieve data from video\n")
print("TubeStore - Convert your data to youtube-uploadable video format\n")
print("1. Create video from data")
print("2. Retrieve data from video")
print("3. Exit Program\n")
option = int(input())
os.system("clear")

try:
option = int(input("Select an option: ").strip())
except ValueError:
clear()
continue

clear()

# ------------------ OPTION 1 ------------------
if option == 1:
path = input("\nEnter file path:")
print("\nProcessing . .")
[difference,ext] = vc.binaryToVideo(fc.fileToBinary(path),path)
os.system("clear")
print("Data to Video conversion successfull!\n\n")
elif option==2:
path = input("\nEnter video path:")
print("\nProcessing . .")
print("\n👉 Drag & drop your file here or press [Enter] to browse:")
path = input("> ").strip()

# If user just pressed Enter, open file dialog
if not path:
path = select_file("Select a file to convert")

if not path:
clear()
print("[ℹ️] No file selected. Returning to menu.\n")
continue

path = path.strip('"').strip("'")

if not os.path.isfile(path):
print(f"[❌] File not found: {path}")
input("\nPress Enter to continue...")
clear()
continue

print("\nProcessing...")
[difference, ext] = vc.binaryToVideo(fc.fileToBinary(path), path)
clear()
print(f"[✅] Data-to-Video conversion successful!")
print(f"→ File saved under ./output/videos/\n")
input("Press Enter to return to menu...")
clear()

# ------------------ OPTION 2 ------------------
elif option == 2:
print("\n👉 Drag & drop your video here or press [Enter] to browse:")
path = input("> ").strip()

if not path:
path = select_file("Select a video to decode", (("Video files", "*.avi *.mp4"), ("All files", "*.*")))

if not path:
clear()
print("[ℹ️] No video selected. Returning to menu.\n")
continue

path = path.strip('"').strip("'")

if not os.path.isfile(path):
print(f"[❌] Video not found: {path}")
input("\nPress Enter to continue...")
clear()
continue

print("\nProcessing...")
fc.binaryToFile(vc.videoToBinary(path))
os.system("clear")
print("Video to Data conversion successfull!\n\n")
elif option==3:
os.system("clear")
exit()
clear()
print(f"[✅] Video-to-Data conversion successful!")
print(f"→ File saved under ./output/recovered/\n")
input("Press Enter to return to menu...")
clear()

# ------------------ EXIT ------------------
elif option == 3:
clear()
print("Bye 👋")
break

else:
pass

clear()
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
opencv-python>=4.8.0
numpy>=1.25.0
146 changes: 105 additions & 41 deletions videoConversion.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,119 @@
import cv2
import numpy as np
def binaryToVideo(cache,path):
binary_data = cache[0]
ext = cache[1]
filename = path.split("/")[-1].split(".")[-2]
array = np.array(list(binary_data)).astype(np.uint8)
res_array = array
size = len(binary_data)
print("Initial size :",size)
frame_size = (128,128)
frames = int(np.ceil(size/(frame_size[0]*frame_size[1])))
difference = int(frames*128*128 - size)
print("Difference: ",difference)
additional = np.zeros(difference,)
array = np.append(array,additional)
array = array.reshape(frames,frame_size[0],frame_size[1])
print("Array shape: ",array.shape)
array = array.astype(np.uint8)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
fps = 30
width = frame_size[0]
height = frame_size[1]
print(str(filename)+"-Video-"+str(difference)+"-"+str(ext)+".mp4")
out = cv2.VideoWriter(str(filename)+"-Video-"+str(difference)+"-"+str(ext)+".mp4",fourcc,fps,(width,height))
import os

# ==========================
# CONFIG
# ==========================
USE_MJPG = True # Use MJPG (visually lossless) for local tests. Set to False to use mp4v.
FRAME_SIZE = (128, 128)
FPS = 30

# ==========================
# HELPERS
# ==========================
def _bits_str_to_array(bits: str) -> np.ndarray:
"""
Convert a binary string ('0101...') into a NumPy array of 0/1 (uint8).
Avoids converting ASCII characters ('0'->48, '1'->49).
"""
return np.frombuffer(bits.encode("ascii"), dtype=np.uint8) - ord("0")


def _parse_meta_from_name(video_path: str):
"""
Extract metadata (difference and extension) from the video filename.
Example: name-Video-1234-pdf.mp4 -> (name, pdf, 1234)
"""
base = os.path.splitext(os.path.basename(video_path))[0] # strip .mp4/.avi
parts = base.split("-")
# Expected format: [..., "Video", difference, ext]
if len(parts) < 4 or parts[-3] != "Video":
raise ValueError(f"Filename does not contain expected metadata: {video_path}")
difference = int(parts[-2])
ext = parts[-1]
filename_wo_ext = "-".join(parts[:-3]) # supports names with hyphens
return filename_wo_ext, ext, difference

# ==========================
# MAIN FUNCTIONS
# ==========================
def binaryToVideo(cache, path):
"""
Convert a binary string into a video where each bit is represented by one pixel.
cache: [binary_string, file_extension]
path: original file path
"""
binary_data, ext = cache
filename_wo_ext = os.path.splitext(os.path.basename(path))[0]

# Convert bits to a 0/1 array
arr = _bits_str_to_array(binary_data)
size = len(arr)

width, height = FRAME_SIZE
bits_per_frame = width * height
frames = int(np.ceil(size / bits_per_frame))
difference = frames * bits_per_frame - size

if difference > 0:
arr = np.pad(arr, (0, difference), mode="constant", constant_values=0)

arr = arr.reshape(frames, height, width).astype(np.uint8)

# Ensure output folders exist
os.makedirs("output/videos", exist_ok=True)

# Define output path
if USE_MJPG:
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
out_name = os.path.join("output/videos", f"{filename_wo_ext}-Video-{difference}-{ext}.avi")
else:
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out_name = os.path.join("output/videos", f"{filename_wo_ext}-Video-{difference}-{ext}.mp4")

out = cv2.VideoWriter(out_name, fourcc, FPS, (width, height))
white = np.array([255, 255, 255], dtype=np.uint8)

for i in range(frames):
img = np.zeros((frame_size[0], frame_size[1], 3), dtype=np.uint8)
img[array[i] == 1] = [255, 255, 255]
img = np.zeros((height, width, 3), dtype=np.uint8)
mask = arr[i] == 1
img[mask] = white
out.write(img)

out.release()
return [difference,ext]
print(f"[✅] Video generated: {out_name}")
return [difference, ext]


def videoToBinary(video_path):
[ext,difference] = [video_path.split(".")[0].split("-")[-1],video_path.split(".")[0].split("-")[-2]]
binary_data = []
filename = video_path.split(".")[0]
"""
Convert a binary video back into a binary string.
Returns [filename.ext, bits]
"""
fname, ext, difference = _parse_meta_from_name(video_path)
cap = cv2.VideoCapture(video_path)
bits = []

while True:
ret,frame = cap.read()
#print(ret,frame)
ret, frame = cap.read()
if not ret:
break
frame = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
ret,frame = cv2.threshold(frame, 127, 1, cv2.THRESH_BINARY)
data = frame.astype(np.uint8).tolist()
for i in data:
binary_data.extend(i)
print("Length of binary data recovered: ",len(binary_data))
binary_data = binary_data[:0-int(difference)]
#print(array[:10],binary_data[:10])
res = ''.join(str(i) for i in binary_data)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
_, bw = cv2.threshold(gray, 127, 1, cv2.THRESH_BINARY)
bits.extend(bw.ravel().tolist())

cap.release()
return [filename+"."+ext,res]
print(f"[ℹ️] Frames read: {len(bits) // (FRAME_SIZE[0] * FRAME_SIZE[1])}")

if difference > 0:
bits = bits[:-difference]

bitstr = ''.join('1' if b else '0' for b in bits)
print(f"[✅] Binary data recovered. Length: {len(bitstr)} bits")
# Ensure output folder for recovered files exists
os.makedirs("output/recovered", exist_ok=True)

# Recovered file path (will be written later by binaryToFile)
recovered_path = os.path.join("output/recovered", f"{fname}.{ext}")
return [recovered_path, bitstr]