Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyPI package creation -- ffmpeg4discord 0.1.0 #15

Merged
merged 16 commits into from
Feb 23, 2024
Merged
26 changes: 0 additions & 26 deletions .github/workflows/docker-publish.yml

This file was deleted.

7 changes: 0 additions & 7 deletions Dockerfile

This file was deleted.

11 changes: 0 additions & 11 deletions conf.json

This file was deleted.

98 changes: 0 additions & 98 deletions ffmpeg4discord.py

This file was deleted.

Empty file added ffmpeg4discord/__init__.py
Empty file.
97 changes: 97 additions & 0 deletions ffmpeg4discord/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import sys
import os
from glob import glob
import webbrowser
from flask import Flask, render_template, url_for, request
import time
import threading
from pathlib import Path

sys.dont_write_bytecode = True
from ffmpeg4discord import arguments
from ffmpeg4discord.twopass import TwoPass

# get args from the command line
args = arguments.get_args()
web = args.pop("web")
port = args.pop("port")
path = Path(args["filename"]).resolve()
args["filename"] = path

# instantiate the TwoPass class
twopass = TwoPass(**args)


def twopass_loop(target_filesize: float):
while twopass.run() >= target_filesize:
print(
f"\nThe output file size ({round(twopass.output_filesize, 2)}MB) is still above the target of {target_filesize}MB.\nRestarting...\n"
)
os.remove(twopass.output_filename)

# adjust the class's target file size to set a lower bitrate for the next run
twopass.target_filesize -= 0.2

print(
f"\nSUCCESS!!\nThe smaller file ({round(twopass.output_filesize, 2)}MB) is located at {twopass.output_filename}"
)


def seconds_to_timestamp(seconds: int):
hours, remainder = divmod(seconds, 3600)
minutes, seconds = divmod(remainder, 60)

# Use f-strings to format the timestamp
timestamp = f"{hours:02d}:{minutes:02d}:{seconds:02d}"

return timestamp


def open_browser():
time.sleep(0.5)
webbrowser.open(f"http://localhost:{port}")


def main():
if web:
app = Flask(__name__, static_folder=path.parent)

@app.route("/")
def index():
return render_template(
"web.html",
filename=url_for("static", filename=path.name),
resolution=twopass.resolution,
target_filesize=twopass.target_filesize,
audio_br=twopass.audio_br,
crop=twopass.crop,
output_dir=twopass.output_dir,
)

@app.route("/encode", methods=["POST"])
def form_twopass():
# generate new times from the selection
ss = int(request.form.get("startTime"))
to = int(request.form.get("endTime"))
twopass.length = to - ss
twopass.times = {"ss": seconds_to_timestamp(ss), "to": seconds_to_timestamp(to)}
target_filesize = float(request.form.get("target_filesize"))

# update TwoPass from web form
twopass.resolution = request.form.get("resolution")
twopass.target_filesize = target_filesize
twopass.audio_br = float(request.form.get("audio_br")) * 1000
twopass.crop = request.form.get("crop")
twopass.output_dir = request.form.get("output_dir")

twopass_loop(target_filesize)

for file in glob("ffmpeg2pass*"):
os.remove(file)

return f"Your compressed video file ({round(twopass.output_filesize, 2)}MB) is located at <strong>{Path(twopass.output_filename).resolve()}</strong>"

threading.Thread(target=open_browser, name="Open Browser").start()
app.run("0.0.0.0", port=port)
else:
twopass_loop(args["target_filesize"])
10 changes: 5 additions & 5 deletions utils/arguments.py → ffmpeg4discord/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
def get_args() -> Namespace:
parser = ArgumentParser(
prog="ffmpeg4discord",
description="Video compression script.",
epilog="Compress those sick clips, boi.",
description="This script takes a video file as its input and encodes to a target file size.",
epilog="For more helpL: https://github.com/zfleeman/ffmpeg4discord\nCompress those sick clips, son!",
)
parser.add_argument("filename", help="The full file path of the file that you wish to compress.")
parser.add_argument("filename", help="The file path of the file that you wish to compress.")
parser.add_argument(
"-o",
"--output-dir",
Expand All @@ -19,7 +19,7 @@ def get_args() -> Namespace:
"--target-filesize",
default=8,
type=float,
help="The output file size in MB. Free Discord accepts a max of 8MB.",
help="The output file size in MB.",
)
parser.add_argument("-a", "--audio-br", type=float, default=96, help="Audio bitrate in kbps.")

Expand All @@ -31,7 +31,7 @@ def get_args() -> Namespace:
parser.add_argument("--config", help="JSON file containing the run's configuration")

# web
parser.add_argument("--web", action=BooleanOptionalAction)
parser.add_argument("--web", action=BooleanOptionalAction, help="Launch The Ephemeral Web UI in your browser.")
parser.add_argument("-p", "--port", type=int, default=5333, help="Local port for the Flask application.")

return vars(parser.parse_args())
File renamed without changes.
8 changes: 3 additions & 5 deletions twopass/twopass.py → ffmpeg4discord/twopass.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,9 @@ def run(self) -> float:
:return: the output file's size
"""

self.output_filename = (
self.output_dir
+ "small_"
+ self.filename.stem.replace(" ", "_")
+ datetime.strftime(datetime.now(), "_%Y%m%d%H%M%S.mp4")
self.output_filename = str(
Path(self.output_dir)
/ ("small_" + self.filename.stem.replace(" ", "_") + datetime.strftime(datetime.now(), "_%Y%m%d%H%M%S.mp4"))
)

# generate run parameters
Expand Down
78 changes: 78 additions & 0 deletions ffmpeg4discord/windows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# THIS SCRIPT ONLY SUPPORTS WINDOWS AT THE MOMENT
import sys
import os
from urllib.request import urlretrieve
import zipfile
from pathlib import Path
import shutil
import logging

logging.getLogger().setLevel(logging.INFO)

path = os.environ.get("PATH", "")
target = Path(sys.executable).parent / "Scripts"
target.mkdir(parents=True, exist_ok=True)

if str(target) not in path:
logging.warning(
"""
The directory we are installing ffmpeg into is not in your
system's PATH. Windows will not be able to find ffmpeg when
trying to run ffmpeg4discord. Please ensure that you installed
Python with the "Add Python to PATH" option selected.

More information: https://docs.python.org/3/using/windows.html#finding-the-python-executable
"""
)


def copy_directory_contents(source, target):
source_path = Path(source)
target_path = Path(target)

# Iterate over files in the source directory
for item in source_path.iterdir():
# Construct target item path
target_item = target_path / item.name

# If the item is a file, copy it to the target directory
if item.is_file():
shutil.copy2(item, target_item)
print(f"Copied file: {item} -> {target_item}")


def download_with_progress(url, save_path):
def report(block_num, block_size, total_size):
downloaded = block_num * block_size
downloaded_mb = downloaded / (1024 * 1024)
total_size_mb = total_size / (1024 * 1024)
progress = min(downloaded / total_size, 1.0)
percent = round(progress * 100, 2)
print(f"\rDownloaded {downloaded_mb:.2f}/{total_size_mb:.2f} MB ({percent}%)", end="")

save_dir = Path(save_path).parent
save_dir.mkdir(parents=True, exist_ok=True)

urlretrieve(url, save_path, reporthook=report)
print("\nDownload complete!")


def install():
print("Downloading ffmpeg to ffmpeg.zip...")
download_with_progress("https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip", "ffmpeg/ffmpeg.zip")

print("\nUnzipping ffmpeg.zip...")
with zipfile.ZipFile("ffmpeg/ffmpeg.zip", "r") as zip_ref:
zip_ref.extractall("ffmpeg/")
source = Path(f"ffmpeg/{zip_ref.namelist()[0]}/bin").resolve()
print("Done!\n")

copy_directory_contents(source=source, target=target)
shutil.rmtree("ffmpeg/")

print("\nffmpeg installation complete.")
print("\nOpen a new Terminal window and type ffmpeg and hit enter to verify that the installation succeeded.")


if __name__ == "__main__":
install()
Loading