-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* initial web work * adding arg * beginning path work * html work * webform functionality * output filename bug * glob to remove log files * Update README.md * PR comments * better output
- Loading branch information
Showing
6 changed files
with
272 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,98 @@ | ||
import sys | ||
import os | ||
from glob import glob | ||
import webbrowser | ||
from flask import Flask, render_template, url_for, request | ||
from random import randint | ||
import time | ||
import threading | ||
from pathlib import Path | ||
|
||
sys.dont_write_bytecode = True | ||
from utils.arguments import get_args | ||
from twopass import TwoPass | ||
|
||
|
||
# get args from the command line | ||
args = get_args() | ||
web = args.pop("web") | ||
path = Path(args["filename"]).resolve() | ||
args["filename"] = path | ||
|
||
# instantiate the TwoPass class and save our target file size for comparison in the loop | ||
# instantiate the TwoPass class | ||
twopass = TwoPass(**args) | ||
end_fs = args["target_filesize"] | ||
|
||
while twopass.run() >= end_fs: | ||
|
||
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"\nThe output file size ({round(twopass.output_filesize, 2)}MB) is still above the target of {end_fs}MB.\nRestarting...\n" | ||
f"\nSUCCESS!!\nThe smaller file ({round(twopass.output_filesize, 2)}MB) is located at {twopass.output_filename}" | ||
) | ||
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}") | ||
|
||
|
||
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>" | ||
|
||
port = randint(5000, 6000) | ||
threading.Thread(target=open_browser, name="Open Browser").start() | ||
app.run("0.0.0.0", port=port) | ||
else: | ||
twopass_loop(args["target_filesize"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
ffmpeg-python | ||
pyperclip | ||
flask |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
<!doctype html> | ||
<html lang="en" data-bs-theme="dark"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>The Ephemeral Web UI for ffmpeg4discord</title> | ||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> | ||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css"> | ||
</head> | ||
<body> | ||
<div class="container"> | ||
<div class="row mt-3"> | ||
<video id="myVideo" preload="auto" controls> | ||
<source src="{{ filename }}" type="video/mp4"> | ||
Your browser does not support the video tag. | ||
</video> | ||
<p class="text-muted"><em>Drag the video playhead, and use the buttons below to set the Start and End times.</em></p> | ||
</div> | ||
<form id="encodeForm" action="/encode" method="post"> | ||
<div class="row mb-3"> | ||
<div class="col"> | ||
<button type="button" class="btn btn-outline-primary" onclick="setTime('start')"><span class="bi-skip-start-fill"></span> Set Start Time</button> | ||
</div> | ||
<div class="col"> | ||
<button type="button" class="btn btn-outline-primary" onclick="setTime('end')">Set End Time <span class="bi-skip-end-fill"></span></button> | ||
</div> | ||
</div> | ||
<div class="row mb-3"> | ||
<div class="col"> | ||
<label for="startTime" class="form-label padding-top">Start Time:</label> | ||
<input class="form-range" type="range" name="startTime" id="startTime" min="0" defaultValue="0" step="1" max="0"> | ||
<span id="startTimeLabel">0</span> | ||
</div> | ||
<div class="col"> | ||
<label for="endTime" class="form-label">End Time:</label> | ||
<input class="form-range" type="range" name="endTime" id="endTime" min="0" defaultValue="0" step="1" max="0"> | ||
<span id="endTimeLabel">0</span> | ||
</div> | ||
</div> | ||
<div class="row"> | ||
<div class="col"> | ||
<button type="button" class="btn btn-outline-primary" onclick="selectVideoPart()"><span class="bi-play"></span> Preview Selection</button> | ||
</div> | ||
</div> | ||
<hr /> | ||
<div class="row mb-3"> | ||
<h3>Output Parameters</h3> | ||
<div class="col"> | ||
<label for="resolution" class="form-label">Resolution</label> | ||
<input class="form-control" name="resolution" id="resolution" type="text" value="{{ resolution }}" /> | ||
</div> | ||
<div class="col"> | ||
<label for="target_filesize" class="form-label">Target File Size (MB)</label> | ||
<input class="form-control" name="target_filesize" id="target_filesize" type="text" value="{{ target_filesize }}" /> | ||
</div> | ||
<div class="col"> | ||
<label for="audio_br" class="form-label">Audio Bitrate (kbps)</label> | ||
<input class="form-control" name="audio_br" id="audio_br" type="text" value="{{ audio_br/1000 }}" /> | ||
</div> | ||
<div class="col"> | ||
<label for="crop" class="form-label">Crop</label> | ||
<input class="form-control" name="crop" id="crop" type="text" value="{{ crop }}" /> | ||
</div> | ||
<div class="col"> | ||
<label for="output_dir" class="form-label">Output Directory</label> | ||
<input class="form-control" name="output_dir" id="output_dir" type="text" value="{{ output_dir }}" /> | ||
</div> | ||
</div> | ||
<div class="row mb-3"> | ||
<div class="col"> | ||
<button id="spinButton" onclick="startSpin()" class="submission btn btn-primary btn-lg" type="submit"> | ||
<span class="bi-film"></span> Encode | ||
</button> | ||
<span id="spinIcon" class="spinner-border spinner-border-sm" role="status" aria-hidden="true" hidden></span> | ||
</div> | ||
</div> | ||
</form> | ||
</div> | ||
<script> | ||
const video = document.getElementById('myVideo'); | ||
const startTimeInput = document.getElementById('startTime'); | ||
const endTimeInput = document.getElementById('endTime'); | ||
const startTimeLabel = document.getElementById('startTimeLabel'); | ||
const endTimeLabel = document.getElementById('endTimeLabel'); | ||
|
||
video.addEventListener('loadedmetadata', () => { | ||
// Set the maximum value of the input elements to the video duration | ||
startTimeInput.max = video.duration; | ||
endTimeInput.max = video.duration; | ||
}); | ||
|
||
startTimeInput.addEventListener('input', updateStartTimeLabel); | ||
endTimeInput.addEventListener('input', updateEndTimeLabel); | ||
|
||
function updateStartTimeLabel() { | ||
startTimeLabel.textContent = startTimeInput.value + ' seconds'; | ||
} | ||
|
||
function updateEndTimeLabel() { | ||
endTimeLabel.textContent = endTimeInput.value + ' seconds'; | ||
} | ||
|
||
function setTime(type) { | ||
const currentTime = Math.floor(video.currentTime); | ||
|
||
if (type === 'start') { | ||
startTimeInput.value = currentTime; | ||
startTimeLabel.textContent = currentTime + ' seconds'; | ||
} else if (type === 'end') { | ||
endTimeInput.value = currentTime; | ||
endTimeLabel.textContent = currentTime + ' seconds'; | ||
} | ||
} | ||
|
||
function selectVideoPart() { | ||
const startTime = parseInt(startTimeInput.value); | ||
const endTime = parseInt(endTimeInput.value); | ||
|
||
if (!isNaN(startTime) && !isNaN(endTime) && startTime < endTime) { | ||
video.currentTime = startTime; | ||
video.play(); | ||
|
||
setTimeout(() => { | ||
video.pause(); | ||
video.currentTime = endTime; | ||
}, (endTime - startTime) * 1000); | ||
} else { | ||
alert('Please enter valid start and end times.'); | ||
} | ||
} | ||
|
||
function startSpin() { | ||
// Get the icon element | ||
var icon = document.getElementById('spinIcon'); | ||
icon.hidden = false; | ||
|
||
// Disable the button temporarily to prevent multiple clicks | ||
document.getElementById('spinButton').disabled = true; | ||
document.getElementById('encodeForm').submit(); | ||
|
||
} | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters