Spaces:
Sleeping
Sleeping
Upload 12 files
Browse files- genz/Dockerfile +33 -0
- genz/__pycache__/checksum.cpython-310.pyc +0 -0
- genz/__pycache__/decode_video.cpython-310.pyc +0 -0
- genz/__pycache__/encode.cpython-310.pyc +0 -0
- genz/__pycache__/youtube_decode.cpython-310.pyc +0 -0
- genz/checksum.py +12 -0
- genz/convert.py +62 -0
- genz/decode/usage.md +114 -0
- genz/decode_video.py +103 -0
- genz/encode.py +125 -0
- genz/usage.md +114 -0
- genz/youtube_decode.py +46 -0
genz/Dockerfile
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use an official Python runtime as a parent image
|
2 |
+
FROM python:3.10-slim-buster
|
3 |
+
|
4 |
+
# Set the working directory in the Docker image
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Install system dependencies
|
8 |
+
RUN apt-get update && apt-get install -y \
|
9 |
+
libgl1 \
|
10 |
+
libglib2.0-0 \
|
11 |
+
libzbar0 \
|
12 |
+
ffmpeg \
|
13 |
+
git \
|
14 |
+
&& rm -rf /var/lib/apt/lists/*
|
15 |
+
|
16 |
+
RUN pip install uv
|
17 |
+
|
18 |
+
# Clone the repository
|
19 |
+
RUN git clone --depth 1 --branch master https://github.com/karaketir16/file2video.git /app
|
20 |
+
|
21 |
+
# Copy the current directory contents into the container at /app
|
22 |
+
#COPY . /app
|
23 |
+
|
24 |
+
RUN uv venv
|
25 |
+
|
26 |
+
# Install any needed packages specified in requirements.txt
|
27 |
+
RUN uv pip install --no-cache-dir -r requirements.txt
|
28 |
+
|
29 |
+
WORKDIR /data
|
30 |
+
|
31 |
+
# Run file2video.py when the container launches
|
32 |
+
ENTRYPOINT ["/app/.venv/bin/python", "/app/file2video.py", "--docker"]
|
33 |
+
CMD []
|
genz/__pycache__/checksum.cpython-310.pyc
ADDED
Binary file (526 Bytes). View file
|
|
genz/__pycache__/decode_video.cpython-310.pyc
ADDED
Binary file (2.69 kB). View file
|
|
genz/__pycache__/encode.cpython-310.pyc
ADDED
Binary file (3.5 kB). View file
|
|
genz/__pycache__/youtube_decode.cpython-310.pyc
ADDED
Binary file (1.15 kB). View file
|
|
genz/checksum.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import hashlib
|
2 |
+
|
3 |
+
def checksum(large_file):
|
4 |
+
"""Calculate MD5 checksum for file."""
|
5 |
+
md5_object = hashlib.md5()
|
6 |
+
block_size = 128 * md5_object.block_size
|
7 |
+
with open(large_file, 'rb') as a_file:
|
8 |
+
chunk = a_file.read(block_size)
|
9 |
+
while chunk:
|
10 |
+
md5_object.update(chunk)
|
11 |
+
chunk = a_file.read(block_size)
|
12 |
+
return md5_object.hexdigest()
|
genz/convert.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from encode import create_video
|
2 |
+
from decode_video import decode
|
3 |
+
from youtube_decode import youtube_decode
|
4 |
+
import argparse
|
5 |
+
import sys
|
6 |
+
|
7 |
+
def enc_file(source_file, output_video):
|
8 |
+
print (f"Encoding {source_file} to {output_video}")
|
9 |
+
create_video(source_file, output_video)
|
10 |
+
|
11 |
+
def dec_video(source_video, destination_folder, docker_mode):
|
12 |
+
print (f"Decoding {source_video} to {destination_folder}")
|
13 |
+
decode(source_video, destination_folder)
|
14 |
+
|
15 |
+
def y_decode(video_url, destination_folder, docker_mode):
|
16 |
+
print (f"Decoding {video_url} to {destination_folder}")
|
17 |
+
youtube_decode(video_url, destination_folder)
|
18 |
+
|
19 |
+
def main():
|
20 |
+
|
21 |
+
# First, check if '--docker' is in the command line arguments
|
22 |
+
docker_mode = '--docker' in sys.argv
|
23 |
+
if docker_mode:
|
24 |
+
sys.argv.remove('--docker') # Remove it so it doesn't interfere with the main parser
|
25 |
+
|
26 |
+
docker_usage = """\
|
27 |
+
docker run --rm -v $(pwd):/data karaketir16/file2video [-h] [--encode source_file output_video]
|
28 |
+
[--decode source_video destination_folder]
|
29 |
+
[--youtube-decode youtube_url destination_folder]"""
|
30 |
+
|
31 |
+
if docker_mode:
|
32 |
+
parser = argparse.ArgumentParser(description="Program to encode files into videos and decode videos back to files.", usage=docker_usage)
|
33 |
+
else:
|
34 |
+
parser = argparse.ArgumentParser(description="Program to encode files into videos and decode videos back to files.")
|
35 |
+
|
36 |
+
# Optional argument for encoding
|
37 |
+
parser.add_argument("--encode", nargs=2, metavar=('source_file', 'output_video'),
|
38 |
+
help="Encode a file into a video: source_file output_video.mp4")
|
39 |
+
|
40 |
+
# Optional argument for decoding
|
41 |
+
parser.add_argument("--decode", nargs=2, metavar=('source_video', 'destination_folder'),
|
42 |
+
help="Decode a video to a file: source_video.mp4 destination_folder")
|
43 |
+
|
44 |
+
# Optional argument for YouTube video decoding
|
45 |
+
parser.add_argument("--youtube-decode", nargs=2, metavar=('youtube_url', 'destination_folder'),
|
46 |
+
help="Decode a video from a YouTube URL to a file: 'youtube_url' destination_folder")
|
47 |
+
|
48 |
+
|
49 |
+
args = parser.parse_args()
|
50 |
+
|
51 |
+
# Check which command is used and call the corresponding function
|
52 |
+
if args.encode:
|
53 |
+
enc_file(*args.encode)
|
54 |
+
elif args.decode:
|
55 |
+
dec_video(*args.decode, docker_mode)
|
56 |
+
elif args.youtube_decode:
|
57 |
+
y_decode(*args.youtube_decode, docker_mode)
|
58 |
+
else:
|
59 |
+
parser.print_help()
|
60 |
+
|
61 |
+
if __name__ == "__main__":
|
62 |
+
main()
|
genz/decode/usage.md
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
python file2video.py --encode go.txt out.mp4
|
2 |
+
python file2video.py --decode out.mp4 ./
|
3 |
+
```python
|
4 |
+
import cv2
|
5 |
+
import sys
|
6 |
+
import yt_dlp
|
7 |
+
import os
|
8 |
+
from datetime import datetime
|
9 |
+
|
10 |
+
from decode_video import decode_video
|
11 |
+
|
12 |
+
def youtube_decode(src, dest_folder):
|
13 |
+
base_filename = "downloaded_video"
|
14 |
+
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
15 |
+
output_file = f"{dest_folder}/{base_filename}_{timestamp}.mp4"
|
16 |
+
|
17 |
+
# Create a yt-dlp configuration to download the video
|
18 |
+
ydl_opts = {
|
19 |
+
'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
|
20 |
+
'outtmpl': output_file,
|
21 |
+
'noplaylist': True,
|
22 |
+
'quiet': True,
|
23 |
+
'merge_output_format': 'mp4', # Ensure the output is mp4 if separate streams are downloaded
|
24 |
+
}
|
25 |
+
|
26 |
+
# Use yt-dlp to download the video
|
27 |
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
28 |
+
ydl.download([src])
|
29 |
+
|
30 |
+
# Check if the file is downloaded and exists
|
31 |
+
if os.path.exists(output_file):
|
32 |
+
# Open the downloaded video file using OpenCV
|
33 |
+
cap = cv2.VideoCapture(output_file)
|
34 |
+
|
35 |
+
# Read and process the video
|
36 |
+
decode_video(cap, dest_folder)
|
37 |
+
else:
|
38 |
+
print("Failed to download video.")
|
39 |
+
sys.exit(1)
|
40 |
+
|
41 |
+
if __name__ == '__main__':
|
42 |
+
if len(sys.argv) < 3:
|
43 |
+
print("Usage: python video2file.py \"https://video_url\" destination_folder")
|
44 |
+
sys.exit(1)
|
45 |
+
|
46 |
+
src = sys.argv[1]
|
47 |
+
dest_folder = sys.argv[2]
|
48 |
+
youtube_decode(src, dest_folder)
|
49 |
+
|
50 |
+
```
|
51 |
+
```python
|
52 |
+
from encode import create_video
|
53 |
+
from decode_video import decode
|
54 |
+
from youtube_decode import youtube_decode
|
55 |
+
import argparse
|
56 |
+
import sys
|
57 |
+
|
58 |
+
def enc_file(source_file, output_video):
|
59 |
+
print (f"Encoding {source_file} to {output_video}")
|
60 |
+
create_video(source_file, output_video)
|
61 |
+
|
62 |
+
def dec_video(source_video, destination_folder, docker_mode):
|
63 |
+
print (f"Decoding {source_video} to {destination_folder}")
|
64 |
+
decode(source_video, destination_folder)
|
65 |
+
|
66 |
+
def y_decode(video_url, destination_folder, docker_mode):
|
67 |
+
print (f"Decoding {video_url} to {destination_folder}")
|
68 |
+
youtube_decode(video_url, destination_folder)
|
69 |
+
|
70 |
+
def main():
|
71 |
+
|
72 |
+
# First, check if '--docker' is in the command line arguments
|
73 |
+
docker_mode = '--docker' in sys.argv
|
74 |
+
if docker_mode:
|
75 |
+
sys.argv.remove('--docker') # Remove it so it doesn't interfere with the main parser
|
76 |
+
|
77 |
+
docker_usage = """\
|
78 |
+
docker run --rm -v $(pwd):/data karaketir16/file2video [-h] [--encode source_file output_video]
|
79 |
+
[--decode source_video destination_folder]
|
80 |
+
[--youtube-decode youtube_url destination_folder]"""
|
81 |
+
|
82 |
+
if docker_mode:
|
83 |
+
parser = argparse.ArgumentParser(description="Program to encode files into videos and decode videos back to files.", usage=docker_usage)
|
84 |
+
else:
|
85 |
+
parser = argparse.ArgumentParser(description="Program to encode files into videos and decode videos back to files.")
|
86 |
+
|
87 |
+
# Optional argument for encoding
|
88 |
+
parser.add_argument("--encode", nargs=2, metavar=('source_file', 'output_video'),
|
89 |
+
help="Encode a file into a video: source_file output_video.mp4")
|
90 |
+
|
91 |
+
# Optional argument for decoding
|
92 |
+
parser.add_argument("--decode", nargs=2, metavar=('source_video', 'destination_folder'),
|
93 |
+
help="Decode a video to a file: source_video.mp4 destination_folder")
|
94 |
+
|
95 |
+
# Optional argument for YouTube video decoding
|
96 |
+
parser.add_argument("--youtube-decode", nargs=2, metavar=('youtube_url', 'destination_folder'),
|
97 |
+
help="Decode a video from a YouTube URL to a file: 'youtube_url' destination_folder")
|
98 |
+
|
99 |
+
|
100 |
+
args = parser.parse_args()
|
101 |
+
|
102 |
+
# Check which command is used and call the corresponding function
|
103 |
+
if args.encode:
|
104 |
+
enc_file(*args.encode)
|
105 |
+
elif args.decode:
|
106 |
+
dec_video(*args.decode, docker_mode)
|
107 |
+
elif args.youtube_decode:
|
108 |
+
y_decode(*args.youtube_decode, docker_mode)
|
109 |
+
else:
|
110 |
+
parser.print_help()
|
111 |
+
|
112 |
+
if __name__ == "__main__":
|
113 |
+
main()
|
114 |
+
```
|
genz/decode_video.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import json
|
3 |
+
import os
|
4 |
+
import sys
|
5 |
+
import base64
|
6 |
+
import logging
|
7 |
+
from pyzbar import pyzbar
|
8 |
+
from tqdm import tqdm
|
9 |
+
from multiprocessing import Pool, cpu_count
|
10 |
+
from checksum import checksum
|
11 |
+
|
12 |
+
# Setup basic logging
|
13 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
14 |
+
|
15 |
+
def apply_preprocessing(frame):
|
16 |
+
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
17 |
+
_, threshold = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY)
|
18 |
+
return threshold
|
19 |
+
|
20 |
+
def read_barcode(preprocessed_frame):
|
21 |
+
barcodes = pyzbar.decode(preprocessed_frame)
|
22 |
+
for barcode in barcodes:
|
23 |
+
barcode_info = barcode.data.decode('utf-8')
|
24 |
+
return True, barcode_info
|
25 |
+
return False, None
|
26 |
+
|
27 |
+
def process_frame(frame):
|
28 |
+
preprocessed_frame = apply_preprocessing(frame)
|
29 |
+
success, data = read_barcode(preprocessed_frame)
|
30 |
+
if not success:
|
31 |
+
logging.warning("Failed to read QR code")
|
32 |
+
return None # Return None if no barcode is found
|
33 |
+
return data
|
34 |
+
|
35 |
+
def decode_video(cap, dest_folder):
|
36 |
+
if not os.path.exists(dest_folder):
|
37 |
+
os.makedirs(dest_folder)
|
38 |
+
|
39 |
+
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
40 |
+
pbar = tqdm(total=total_frames, desc="Processing Frames")
|
41 |
+
|
42 |
+
ret, first_frame = cap.read()
|
43 |
+
if not ret:
|
44 |
+
logging.error("Cannot read first frame")
|
45 |
+
return
|
46 |
+
|
47 |
+
metadata = process_frame(first_frame)
|
48 |
+
if metadata is None:
|
49 |
+
logging.error("No QR code in first frame; cannot proceed")
|
50 |
+
return
|
51 |
+
meta_data = json.loads(metadata)
|
52 |
+
dest = os.path.join(dest_folder, meta_data["Filename"])
|
53 |
+
file = open(dest, "wb")
|
54 |
+
|
55 |
+
# Start worker processes
|
56 |
+
num_workers = cpu_count()
|
57 |
+
|
58 |
+
with Pool(num_workers) as pool:
|
59 |
+
while cap.isOpened():
|
60 |
+
frames = []
|
61 |
+
done = False
|
62 |
+
for _ in range(num_workers):
|
63 |
+
ret, frame = cap.read()
|
64 |
+
if ret:
|
65 |
+
frames.append(frame)
|
66 |
+
else:
|
67 |
+
done = True
|
68 |
+
break
|
69 |
+
|
70 |
+
datas = pool.map(process_frame, frames)
|
71 |
+
|
72 |
+
pbar.update(len(frames))
|
73 |
+
|
74 |
+
for data in datas:
|
75 |
+
file.write(base64.b64decode(data))
|
76 |
+
|
77 |
+
if done:
|
78 |
+
break
|
79 |
+
|
80 |
+
|
81 |
+
file.close()
|
82 |
+
cap.release()
|
83 |
+
pbar.close()
|
84 |
+
|
85 |
+
logging.info("Verify file integrity")
|
86 |
+
md5_sum = checksum(dest)
|
87 |
+
assert md5_sum == meta_data["Filehash"], "Data corrupted"
|
88 |
+
logging.info("File integrity verified: ")
|
89 |
+
logging.info(dest)
|
90 |
+
|
91 |
+
def decode(src, dest_folder):
|
92 |
+
cap = cv2.VideoCapture(src)
|
93 |
+
decode_video(cap, dest_folder)
|
94 |
+
|
95 |
+
if __name__ == '__main__':
|
96 |
+
if len(sys.argv) < 3:
|
97 |
+
logging.error("Usage: python script.py source_file.mp4 destination_folder")
|
98 |
+
sys.exit(1)
|
99 |
+
src = sys.argv[1]
|
100 |
+
dest_folder = sys.argv[2]
|
101 |
+
|
102 |
+
decode(src, dest_folder)
|
103 |
+
|
genz/encode.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from itertools import islice
|
2 |
+
import os
|
3 |
+
import sys
|
4 |
+
import base64
|
5 |
+
import math
|
6 |
+
import json
|
7 |
+
import qrcode
|
8 |
+
import numpy as np
|
9 |
+
from multiprocessing import Pool, cpu_count
|
10 |
+
import av
|
11 |
+
from PIL import Image
|
12 |
+
from tqdm import tqdm
|
13 |
+
|
14 |
+
from checksum import checksum
|
15 |
+
|
16 |
+
# Constants
|
17 |
+
chunk_size = 500 # Adjust this as per the data capacity of QR codes and your specific requirements
|
18 |
+
frame_rate = 20.0
|
19 |
+
width = 1080
|
20 |
+
height = 1080
|
21 |
+
|
22 |
+
def read_in_chunks(file_object, chunk_size=1024):
|
23 |
+
"""Generator to read a file piece by piece."""
|
24 |
+
while True:
|
25 |
+
data = file_object.read(chunk_size)
|
26 |
+
if not data:
|
27 |
+
break
|
28 |
+
yield data
|
29 |
+
|
30 |
+
def create_qr(data_str, box_size=10, error_correction_level=qrcode.constants.ERROR_CORRECT_L):
|
31 |
+
"""Generate a QR code as a NumPy array from data string with specified box size and error correction level."""
|
32 |
+
qr = qrcode.QRCode(
|
33 |
+
version=1,
|
34 |
+
error_correction=error_correction_level,
|
35 |
+
box_size=box_size,
|
36 |
+
border=4,
|
37 |
+
)
|
38 |
+
qr.add_data(data_str)
|
39 |
+
img = qr.make_image(fill_color="black", back_color="white").convert('RGB')
|
40 |
+
pil_img = img.resize((width, height), Image.Resampling.NEAREST) # Use NEAREST for less interpolation blur
|
41 |
+
return np.array(pil_img)
|
42 |
+
|
43 |
+
def process_chunk(data):
|
44 |
+
"""Encode data chunk into QR and return as image."""
|
45 |
+
return create_qr(base64.b64encode(data).decode('ascii'))
|
46 |
+
|
47 |
+
def encode_and_write_frames(frames, stream, container):
|
48 |
+
"""Encode frames and write to video container."""
|
49 |
+
for frame in frames:
|
50 |
+
video_frame = av.VideoFrame.from_ndarray(frame, format='rgb24')
|
51 |
+
for packet in stream.encode(video_frame):
|
52 |
+
container.mux(packet)
|
53 |
+
|
54 |
+
def create_video(src, dest, read_file_lazy = False):
|
55 |
+
"""Create video from source file using PyAV."""
|
56 |
+
md5_checksum = checksum(src)
|
57 |
+
file_stats = os.stat(src)
|
58 |
+
file_size = file_stats.st_size
|
59 |
+
chunk_count = math.ceil(file_size / chunk_size)
|
60 |
+
|
61 |
+
pbar = tqdm(total=chunk_count, desc="Generating Frames")
|
62 |
+
|
63 |
+
meta_data = {
|
64 |
+
"Filename": os.path.basename(src),
|
65 |
+
"ChunkCount": chunk_count,
|
66 |
+
"Filehash": md5_checksum,
|
67 |
+
"ConverterUrl": "https://github.com/karaketir16/file2video",
|
68 |
+
"ConverterVersion": "python_v1"
|
69 |
+
}
|
70 |
+
|
71 |
+
first_frame_data = json.dumps(meta_data, indent=4)
|
72 |
+
first_frame = create_qr(first_frame_data)
|
73 |
+
|
74 |
+
# Open output file
|
75 |
+
container = av.open(dest, mode='w')
|
76 |
+
stream = container.add_stream('h264', rate=frame_rate)
|
77 |
+
stream.width = width
|
78 |
+
stream.height = height
|
79 |
+
stream.pix_fmt = 'yuv420p'
|
80 |
+
stream.options = {'crf': '40'}
|
81 |
+
|
82 |
+
# Write the first frame
|
83 |
+
video_frame = av.VideoFrame.from_ndarray(first_frame, format='rgb24')
|
84 |
+
for packet in stream.encode(video_frame):
|
85 |
+
container.mux(packet)
|
86 |
+
|
87 |
+
# Process chunks in batches using multiprocessing
|
88 |
+
with open(src, 'rb') as f, Pool(cpu_count()) as pool:
|
89 |
+
entire_file = []
|
90 |
+
if not read_file_lazy:
|
91 |
+
entire_file = f.read()
|
92 |
+
i = 0
|
93 |
+
while True:
|
94 |
+
|
95 |
+
chunks = []
|
96 |
+
|
97 |
+
if read_file_lazy:
|
98 |
+
chunks = list(islice(read_in_chunks(f, chunk_size), cpu_count()))
|
99 |
+
else:
|
100 |
+
for _ in range(cpu_count()):
|
101 |
+
chunks.append(entire_file[i: i + chunk_size])
|
102 |
+
if not chunks[-1]: #empty
|
103 |
+
chunks.pop()
|
104 |
+
break
|
105 |
+
i = i + chunk_size
|
106 |
+
if not chunks:
|
107 |
+
break
|
108 |
+
frames = pool.map(process_chunk, chunks)
|
109 |
+
encode_and_write_frames(frames, stream, container)
|
110 |
+
pbar.update(len(frames))
|
111 |
+
|
112 |
+
pbar.close()
|
113 |
+
|
114 |
+
# Finalize the video file
|
115 |
+
for packet in stream.encode():
|
116 |
+
container.mux(packet)
|
117 |
+
container.close()
|
118 |
+
|
119 |
+
if __name__ == '__main__':
|
120 |
+
if len(sys.argv) < 3:
|
121 |
+
print("Usage: python file2video.py source_file output_file.mp4")
|
122 |
+
sys.exit(1)
|
123 |
+
src = sys.argv[1]
|
124 |
+
dest = sys.argv[2]
|
125 |
+
create_video(src, dest)
|
genz/usage.md
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
python file2video.py --encode go.txt out.mp4
|
2 |
+
python file2video.py --decode out.mp4 ./
|
3 |
+
```python
|
4 |
+
import cv2
|
5 |
+
import sys
|
6 |
+
import yt_dlp
|
7 |
+
import os
|
8 |
+
from datetime import datetime
|
9 |
+
|
10 |
+
from decode_video import decode_video
|
11 |
+
|
12 |
+
def youtube_decode(src, dest_folder):
|
13 |
+
base_filename = "downloaded_video"
|
14 |
+
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
15 |
+
output_file = f"{dest_folder}/{base_filename}_{timestamp}.mp4"
|
16 |
+
|
17 |
+
# Create a yt-dlp configuration to download the video
|
18 |
+
ydl_opts = {
|
19 |
+
'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
|
20 |
+
'outtmpl': output_file,
|
21 |
+
'noplaylist': True,
|
22 |
+
'quiet': True,
|
23 |
+
'merge_output_format': 'mp4', # Ensure the output is mp4 if separate streams are downloaded
|
24 |
+
}
|
25 |
+
|
26 |
+
# Use yt-dlp to download the video
|
27 |
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
28 |
+
ydl.download([src])
|
29 |
+
|
30 |
+
# Check if the file is downloaded and exists
|
31 |
+
if os.path.exists(output_file):
|
32 |
+
# Open the downloaded video file using OpenCV
|
33 |
+
cap = cv2.VideoCapture(output_file)
|
34 |
+
|
35 |
+
# Read and process the video
|
36 |
+
decode_video(cap, dest_folder)
|
37 |
+
else:
|
38 |
+
print("Failed to download video.")
|
39 |
+
sys.exit(1)
|
40 |
+
|
41 |
+
if __name__ == '__main__':
|
42 |
+
if len(sys.argv) < 3:
|
43 |
+
print("Usage: python video2file.py \"https://video_url\" destination_folder")
|
44 |
+
sys.exit(1)
|
45 |
+
|
46 |
+
src = sys.argv[1]
|
47 |
+
dest_folder = sys.argv[2]
|
48 |
+
youtube_decode(src, dest_folder)
|
49 |
+
|
50 |
+
```
|
51 |
+
```python
|
52 |
+
from encode import create_video
|
53 |
+
from decode_video import decode
|
54 |
+
from youtube_decode import youtube_decode
|
55 |
+
import argparse
|
56 |
+
import sys
|
57 |
+
|
58 |
+
def enc_file(source_file, output_video):
|
59 |
+
print (f"Encoding {source_file} to {output_video}")
|
60 |
+
create_video(source_file, output_video)
|
61 |
+
|
62 |
+
def dec_video(source_video, destination_folder, docker_mode):
|
63 |
+
print (f"Decoding {source_video} to {destination_folder}")
|
64 |
+
decode(source_video, destination_folder)
|
65 |
+
|
66 |
+
def y_decode(video_url, destination_folder, docker_mode):
|
67 |
+
print (f"Decoding {video_url} to {destination_folder}")
|
68 |
+
youtube_decode(video_url, destination_folder)
|
69 |
+
|
70 |
+
def main():
|
71 |
+
|
72 |
+
# First, check if '--docker' is in the command line arguments
|
73 |
+
docker_mode = '--docker' in sys.argv
|
74 |
+
if docker_mode:
|
75 |
+
sys.argv.remove('--docker') # Remove it so it doesn't interfere with the main parser
|
76 |
+
|
77 |
+
docker_usage = """\
|
78 |
+
docker run --rm -v $(pwd):/data karaketir16/file2video [-h] [--encode source_file output_video]
|
79 |
+
[--decode source_video destination_folder]
|
80 |
+
[--youtube-decode youtube_url destination_folder]"""
|
81 |
+
|
82 |
+
if docker_mode:
|
83 |
+
parser = argparse.ArgumentParser(description="Program to encode files into videos and decode videos back to files.", usage=docker_usage)
|
84 |
+
else:
|
85 |
+
parser = argparse.ArgumentParser(description="Program to encode files into videos and decode videos back to files.")
|
86 |
+
|
87 |
+
# Optional argument for encoding
|
88 |
+
parser.add_argument("--encode", nargs=2, metavar=('source_file', 'output_video'),
|
89 |
+
help="Encode a file into a video: source_file output_video.mp4")
|
90 |
+
|
91 |
+
# Optional argument for decoding
|
92 |
+
parser.add_argument("--decode", nargs=2, metavar=('source_video', 'destination_folder'),
|
93 |
+
help="Decode a video to a file: source_video.mp4 destination_folder")
|
94 |
+
|
95 |
+
# Optional argument for YouTube video decoding
|
96 |
+
parser.add_argument("--youtube-decode", nargs=2, metavar=('youtube_url', 'destination_folder'),
|
97 |
+
help="Decode a video from a YouTube URL to a file: 'youtube_url' destination_folder")
|
98 |
+
|
99 |
+
|
100 |
+
args = parser.parse_args()
|
101 |
+
|
102 |
+
# Check which command is used and call the corresponding function
|
103 |
+
if args.encode:
|
104 |
+
enc_file(*args.encode)
|
105 |
+
elif args.decode:
|
106 |
+
dec_video(*args.decode, docker_mode)
|
107 |
+
elif args.youtube_decode:
|
108 |
+
y_decode(*args.youtube_decode, docker_mode)
|
109 |
+
else:
|
110 |
+
parser.print_help()
|
111 |
+
|
112 |
+
if __name__ == "__main__":
|
113 |
+
main()
|
114 |
+
```
|
genz/youtube_decode.py
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import sys
|
3 |
+
import yt_dlp
|
4 |
+
import os
|
5 |
+
from datetime import datetime
|
6 |
+
|
7 |
+
from decode_video import decode_video
|
8 |
+
|
9 |
+
def youtube_decode(src, dest_folder):
|
10 |
+
base_filename = "downloaded_video"
|
11 |
+
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
12 |
+
output_file = f"{dest_folder}/{base_filename}_{timestamp}.mp4"
|
13 |
+
|
14 |
+
# Create a yt-dlp configuration to download the video
|
15 |
+
ydl_opts = {
|
16 |
+
'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
|
17 |
+
'outtmpl': output_file,
|
18 |
+
'noplaylist': True,
|
19 |
+
'quiet': True,
|
20 |
+
'merge_output_format': 'mp4', # Ensure the output is mp4 if separate streams are downloaded
|
21 |
+
}
|
22 |
+
|
23 |
+
# Use yt-dlp to download the video
|
24 |
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
25 |
+
ydl.download([src])
|
26 |
+
|
27 |
+
# Check if the file is downloaded and exists
|
28 |
+
if os.path.exists(output_file):
|
29 |
+
# Open the downloaded video file using OpenCV
|
30 |
+
cap = cv2.VideoCapture(output_file)
|
31 |
+
|
32 |
+
# Read and process the video
|
33 |
+
decode_video(cap, dest_folder)
|
34 |
+
else:
|
35 |
+
print("Failed to download video.")
|
36 |
+
sys.exit(1)
|
37 |
+
|
38 |
+
if __name__ == '__main__':
|
39 |
+
if len(sys.argv) < 3:
|
40 |
+
print("Usage: python video2file.py \"https://video_url\" destination_folder")
|
41 |
+
sys.exit(1)
|
42 |
+
|
43 |
+
src = sys.argv[1]
|
44 |
+
dest_folder = sys.argv[2]
|
45 |
+
youtube_decode(src, dest_folder)
|
46 |
+
|