├── LICENSE ├── requirements.txt ├── decoder.py ├── README.md └── encoder.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Adam Conway 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.9.3 2 | aiosignal==1.3.1 3 | attrs==23.2.0 4 | autopwn-suite==2.1.5 5 | beautifulsoup4==4.12.3 6 | blinker==1.7.0 7 | bs4==0.0.2 8 | certifi==2024.2.2 9 | charset-normalizer==3.3.2 10 | click==8.1.7 11 | contourpy==1.2.0 12 | cycler==0.12.1 13 | decorator==4.4.2 14 | discord==2.3.2 15 | discord.py==2.3.2 16 | distro==1.9.0 17 | Flask==3.0.2 18 | fonttools==4.48.1 19 | frozenlist==1.4.1 20 | idna==3.6 21 | imageio==2.34.0 22 | imageio-ffmpeg==0.4.9 23 | itsdangerous==2.1.2 24 | Jinja2==3.1.3 25 | jsonify==0.5 26 | kiwisolver==1.4.5 27 | markdown-it-py==3.0.0 28 | MarkupSafe==2.1.5 29 | matplotlib==3.8.2 30 | mdurl==0.1.2 31 | moviepy==1.0.3 32 | multidict==6.0.5 33 | numpy==1.26.4 34 | opencv-python==4.9.0.80 35 | packaging==23.2 36 | pillow==10.2.0 37 | proglog==0.1.10 38 | pygame==2.5.2 39 | Pygments==2.17.2 40 | pyparsing==3.1.1 41 | pypng==0.20220715.0 42 | python-dateutil==2.8.2 43 | python-nmap==0.7.1 44 | qrcode==7.4.2 45 | requests==2.31.0 46 | rich==13.7.0 47 | setuptools==69.1.0 48 | six==1.16.0 49 | soupsieve==2.5 50 | tqdm==4.66.2 51 | typing_extensions==4.9.0 52 | urllib3==2.1.0 53 | Werkzeug==3.0.1 54 | yarl==1.9.4 55 | -------------------------------------------------------------------------------- /decoder.py: -------------------------------------------------------------------------------- 1 | # decoder.py 2 | 3 | import cv2 4 | import numpy as np 5 | import time 6 | 7 | start_time = time.time() 8 | 9 | def video_to_frames(video_path): 10 | """Extracts frames from a video.""" 11 | cap = cv2.VideoCapture(video_path) 12 | frames = [] 13 | while cap.isOpened(): 14 | ret, frame = cap.read() 15 | if not ret: 16 | break 17 | frames.append(frame) 18 | cap.release() 19 | return frames 20 | 21 | def frames_to_binary(frames, pixel_size=10): 22 | """Converts frames to binary data.""" 23 | binary_data = "" 24 | for frame in frames: 25 | height, width = frame.shape[:2] 26 | for y in range(0, height, pixel_size): 27 | for x in range(0, width, pixel_size): 28 | pixel = frame[y:y+pixel_size, x:x+pixel_size] 29 | if np.mean(pixel) < 128: # Assuming black pixel represents '1', checking average of pixels 30 | binary_data += '1' 31 | else: 32 | binary_data += '0' 33 | return binary_data 34 | 35 | def binary_to_file(binary_data, output_file_path): 36 | """Converts binary data to a file.""" 37 | byte_array = bytearray() 38 | for i in range(0, len(binary_data), 8): 39 | byte = binary_data[i:i+8] 40 | byte_array.append(int(byte, 2)) 41 | with open(output_file_path, 'wb') as file: 42 | file.write(byte_array) 43 | 44 | # Example usage 45 | if __name__ == "__main__": 46 | video_path = 'data_video.mp4' 47 | frames = video_to_frames(video_path) 48 | binary_data = frames_to_binary(frames) 49 | binary_to_file(binary_data, 'output2.png') 50 | 51 | end_time = time.time() 52 | print(f"Decoding completed in {end_time - start_time:.2f} seconds.") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Converting files to video 3 | Cloud storage is a popular way for people to upload and share files online, but it often comes at a cost. YouTube is a service that allows for unlimited uploads, and if one could leverage video formats to convert files into video, you could theoretically share files online through YouTube videos that others can download and convert. 4 | 5 | This project turns files into videos where each pixel is read left to right, row by row, in clusters of five pixels where black represents a binary digit '1' and white represents a binary digit '0'. Each frame is a new chunk of data, where each frame at the default 5x5 pixel size can store approximately 10kb of data. At the full size of a 1080p frame, it can store approximately 0.26MB of data per frame. We do not use this though, as this is too fragile for commonly used compression algorithms. 6 | 7 | This is a proof of concept and should not be used as a legitimate replacement for Google Drive. Users should also not use this to store sensitive files. 8 | 9 | 10 | ## Execution 11 | 12 | To execute this project you'll need to install the dependencies included in the requirements.txt file. Navigate to the folder in a terminal and run the following command. 13 | 14 | ```bash 15 | pip install -r requirements.txt 16 | ``` 17 | 18 | Prepare the file you want for encoding and modify the source code of encoder.py to add the input and output files. You can also add music to the video if you wish, where the function to do so is commented out. Run: 19 | 20 | ```bash 21 | python encoder.py 22 | ``` 23 | 24 | This will take some time. Larger files require more RAM, and a 100MB file was observed to use 100GB of RAM. This is not a practical way of storing files. 25 | 26 | ## Documentation 27 | This program was written for an article on XDA-Developers by Adam Conway, Lead Technical Editor of the site. 28 | 29 | [XDA-Developers article](https://www.xda-developers.com/youtube-as-storage-data-to-video/) 30 | 31 | -------------------------------------------------------------------------------- /encoder.py: -------------------------------------------------------------------------------- 1 | # encoder.py 2 | 3 | import cv2 4 | import numpy as np 5 | import time 6 | from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_videoclips, CompositeAudioClip 7 | 8 | start_time = time.time() 9 | 10 | def file_to_binary(file_path): 11 | """Converts file content to a binary string.""" 12 | with open(file_path, 'rb') as file: 13 | content = file.read() 14 | return ''.join(format(byte, '08b') for byte in content) 15 | 16 | # Change pixel_size here 17 | def binary_to_frames(binary_data, frame_size=(1920, 1080), pixel_size=5): 18 | """Converts binary data to a list of frames.""" 19 | width, height = frame_size 20 | pixels_per_width = width // pixel_size 21 | pixels_per_height = height // pixel_size 22 | 23 | # Calculate total pixels per frame and how many frames are needed 24 | total_pixels_per_frame = pixels_per_width * pixels_per_height 25 | total_frames_needed = len(binary_data) // total_pixels_per_frame + (1 if len(binary_data) % total_pixels_per_frame else 0) 26 | 27 | frames = [] 28 | for frame_index in range(total_frames_needed): 29 | frame = np.ones((height, width, 3), np.uint8) * 255 # Start with a white frame 30 | start_index = frame_index * total_pixels_per_frame 31 | end_index = start_index + total_pixels_per_frame 32 | frame_data = binary_data[start_index:end_index] 33 | 34 | for index, bit in enumerate(frame_data): 35 | x = (index % pixels_per_width) * pixel_size 36 | y = (index // pixels_per_width) * pixel_size 37 | color = 0 if bit == '1' else 255 # Black for '1', white for '0' 38 | frame[y:y+pixel_size, x:x+pixel_size] = color 39 | 40 | frames.append(frame) 41 | return frames 42 | 43 | # Can increase FPS but risks triggering irreversible compression on YouTube 44 | def save_frames_to_video(frames, output_file='output.mp4', fps=15): 45 | """Saves frames to a video file.""" 46 | height, width = frames[0].shape[:2] 47 | out = cv2.VideoWriter(output_file, cv2.VideoWriter_fourcc(*'mp4v'), fps, (width, height)) 48 | 49 | for frame in frames: 50 | out.write(frame) 51 | out.release() 52 | 53 | def add_audio_to_video(video_path, audio_path, output_path): 54 | # Load the video clip 55 | video_clip = VideoFileClip(video_path) 56 | 57 | # Load the audio file 58 | audio_clip = AudioFileClip(audio_path) 59 | 60 | # Calculate the duration of the audio and video 61 | video_duration = video_clip.duration 62 | audio_duration = audio_clip.duration 63 | 64 | # Loop the audio if necessary 65 | if video_duration > audio_duration: 66 | # Calculate the number of loops required 67 | loop_count = int(video_duration // audio_duration) + 1 68 | # Create a composite audio clip with the audio looped 69 | audio_loops = [audio_clip] * loop_count 70 | looped_audio_clip = CompositeAudioClip(audio_loops) 71 | # Set the audio of the video clip as the looped audio, trimming to video duration 72 | final_audio_clip = looped_audio_clip.set_duration(video_clip.duration) 73 | else: 74 | # If the video is shorter than one loop of the audio, just trim the audio 75 | final_audio_clip = audio_clip.set_duration(video_clip.duration) 76 | 77 | # Set the audio of the video clip 78 | final_clip = video_clip.set_audio(final_audio_clip) 79 | 80 | # Write the result to a file 81 | final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac") 82 | 83 | # Example usage 84 | if __name__ == "__main__": 85 | file_path = 'image.png' 86 | audio_path = 'sneakysnitch.mp3' 87 | binary_data = file_to_binary(file_path) 88 | frames = binary_to_frames(binary_data) 89 | save_frames_to_video(frames, 'data_video.mp4') 90 | 91 | # Uncomment the below if you want music in your video lol 92 | #add_audio_to_video('data_video.mp4', audio_path, 'data_video_audio.mp4') 93 | 94 | end_time = time.time() 95 | print(f"Encoding completed in {end_time - start_time:.2f} seconds.") --------------------------------------------------------------------------------