Spaces:
Build error
Build error
Commit ·
55afdee
1
Parent(s): 673499e
Data
Browse files- Dockerfile +21 -0
- app.py +72 -0
- download_queue.json +1 -0
- extra.py +107 -0
- miyuki-0.7.7-py3-none-any.whl +0 -0
- process.py +77 -0
- requirements.txt +2 -0
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
# Install dependencies
|
| 4 |
+
RUN apt-get update && apt-get install -y \
|
| 5 |
+
ffmpeg \
|
| 6 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 7 |
+
|
| 8 |
+
# Set working directory
|
| 9 |
+
WORKDIR /app
|
| 10 |
+
|
| 11 |
+
# Copy project files
|
| 12 |
+
COPY . .
|
| 13 |
+
|
| 14 |
+
# Install Python dependencies
|
| 15 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 16 |
+
|
| 17 |
+
# Expose port for Gradio
|
| 18 |
+
EXPOSE 7860
|
| 19 |
+
|
| 20 |
+
# Run Gradio app
|
| 21 |
+
CMD ["python", "app.py"]
|
app.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import webbrowser
|
| 4 |
+
import threading
|
| 5 |
+
from process import *
|
| 6 |
+
from extra import *
|
| 7 |
+
install_miyuki()
|
| 8 |
+
|
| 9 |
+
# Load existing queue
|
| 10 |
+
download_queue = load_queue()
|
| 11 |
+
|
| 12 |
+
def open_browser():
|
| 13 |
+
webbrowser.open("http://127.0.0.1:7860")
|
| 14 |
+
|
| 15 |
+
def add_to_queue(video_url, quality):
|
| 16 |
+
if not video_url.startswith("http"):
|
| 17 |
+
return "Error: Please enter a valid URL."
|
| 18 |
+
|
| 19 |
+
if video_url in download_queue:
|
| 20 |
+
return "Warning: This URL is already in the queue."
|
| 21 |
+
|
| 22 |
+
download_queue[video_url] = {
|
| 23 |
+
"URL": video_url,
|
| 24 |
+
"Status": "Not Started",
|
| 25 |
+
"Log": ""
|
| 26 |
+
}
|
| 27 |
+
save_queue(download_queue)
|
| 28 |
+
return f"Added {video_url} to the queue."
|
| 29 |
+
|
| 30 |
+
def start_download():
|
| 31 |
+
for url, task in download_queue.items():
|
| 32 |
+
if task["Status"] == "Not Started":
|
| 33 |
+
status, log = download_file(url, "720")
|
| 34 |
+
download_queue[url]["Status"] = status
|
| 35 |
+
download_queue[url]["Log"] = log
|
| 36 |
+
save_queue(download_queue)
|
| 37 |
+
return "Downloads started."
|
| 38 |
+
|
| 39 |
+
def view_queue():
|
| 40 |
+
return [(url, data["Status"]) for url, data in download_queue.items()]
|
| 41 |
+
|
| 42 |
+
def check_status():
|
| 43 |
+
for url, task in download_queue.items():
|
| 44 |
+
if task["Status"] in ["Downloading", "Not Started"]:
|
| 45 |
+
status, log = check_task_status(url, task["Log"])
|
| 46 |
+
download_queue[url]["Status"] = status
|
| 47 |
+
download_queue[url]["Log"] = log
|
| 48 |
+
save_queue(download_queue)
|
| 49 |
+
return "Queue status updated."
|
| 50 |
+
|
| 51 |
+
with gr.Blocks() as app:
|
| 52 |
+
gr.Markdown("# Miyuki GUI Downloader")
|
| 53 |
+
|
| 54 |
+
with gr.Row():
|
| 55 |
+
video_url = gr.Textbox(label="Video URL")
|
| 56 |
+
quality = gr.Dropdown(["360", "480", "720"], label="Quality", value="360")
|
| 57 |
+
add_button = gr.Button("Add to Queue")
|
| 58 |
+
start_button = gr.Button("Start Download")
|
| 59 |
+
|
| 60 |
+
add_button.click(add_to_queue, inputs=[video_url, quality], outputs=None)
|
| 61 |
+
|
| 62 |
+
start_button.click(start_download, outputs=None)
|
| 63 |
+
|
| 64 |
+
check_button = gr.Button("Check Status")
|
| 65 |
+
check_button.click(check_status, outputs=None)
|
| 66 |
+
|
| 67 |
+
queue_list = gr.Dataframe(headers=["URL", "Status"], datatype=["str", "str"], label="Download Queue")
|
| 68 |
+
view_button = gr.Button("View Queue")
|
| 69 |
+
view_button.click(view_queue, outputs=queue_list)
|
| 70 |
+
|
| 71 |
+
threading.Thread(target=open_browser).start()
|
| 72 |
+
app.launch()
|
download_queue.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{}
|
extra.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import re
|
| 3 |
+
import subprocess
|
| 4 |
+
|
| 5 |
+
def apostrof(text):
|
| 6 |
+
# Pola untuk mencari apostrof apapun diikuti dengan huruf kapital (baik 'S, ’S, atau ‘S)
|
| 7 |
+
text = re.sub(r"([’‘'\"])S", r"\1s", text)
|
| 8 |
+
text = re.sub(r"([’‘'\"])T", r"\1t", text)
|
| 9 |
+
return text
|
| 10 |
+
|
| 11 |
+
def rename_miyuki(folder_path = "./movies_folder_miyuki", dest_folder = "./Download MissAV"):
|
| 12 |
+
if not os.path.exists(folder_path):
|
| 13 |
+
return
|
| 14 |
+
|
| 15 |
+
dest_video = os.path.join(dest_folder, "Video")
|
| 16 |
+
dest_cover = os.path.join(dest_folder, "Cover")
|
| 17 |
+
if not os.path.exists(dest_video):
|
| 18 |
+
os.makedirs(dest_video)
|
| 19 |
+
if not os.path.exists(dest_cover):
|
| 20 |
+
os.makedirs(dest_cover)
|
| 21 |
+
|
| 22 |
+
for filename in os.listdir(folder_path):
|
| 23 |
+
if filename.endswith(".mp4"):
|
| 24 |
+
file_path = os.path.join(folder_path, filename)
|
| 25 |
+
|
| 26 |
+
# Pisahkan kode dan shortname
|
| 27 |
+
name, ext = os.path.splitext(filename)
|
| 28 |
+
kode = name.split(" ")[0]
|
| 29 |
+
shortname = " ".join(name.split(" ")[1:]).rsplit(".", 1)[0]
|
| 30 |
+
|
| 31 |
+
# Buat nama baru
|
| 32 |
+
new_name = f"{kode.upper().replace('_1080P','').replace('_720P','').replace('_480P','').replace('_360P','')} {apostrof(shortname.replace('"', '').title())}"
|
| 33 |
+
new_name = new_name.strip() + ".mp4"
|
| 34 |
+
new_path = os.path.join(dest_video, new_name.replace("..", ".").replace(" .MP4 .mp4", ".mp4"))
|
| 35 |
+
|
| 36 |
+
# Rename file
|
| 37 |
+
os.rename(file_path, new_path)
|
| 38 |
+
print(f"Renamed '{filename}' to '{new_name}'")
|
| 39 |
+
|
| 40 |
+
for filename in os.listdir(folder_path):
|
| 41 |
+
if filename.endswith(".jpg"):
|
| 42 |
+
file_path = os.path.join(folder_path, filename)
|
| 43 |
+
if os.path.splitext(os.path.basename(new_path))[0] in filename.upper():
|
| 44 |
+
|
| 45 |
+
# Buat nama baru
|
| 46 |
+
new_name = new_name.replace(".mp4", " Cover.jpg")
|
| 47 |
+
new_path = os.path.join(dest_cover, new_name)
|
| 48 |
+
|
| 49 |
+
# Rename file
|
| 50 |
+
os.rename(file_path, new_path)
|
| 51 |
+
print(f"Renamed '{filename}' to '{new_name}'")
|
| 52 |
+
|
| 53 |
+
def search(string, path="."):
|
| 54 |
+
hasil = []
|
| 55 |
+
output = ""
|
| 56 |
+
|
| 57 |
+
for root, dirs, files in os.walk(path):
|
| 58 |
+
# Cek folder yang mengandung string
|
| 59 |
+
for dir_name in dirs:
|
| 60 |
+
if string.lower() in dir_name.lower():
|
| 61 |
+
hasil.append(os.path.join(root, dir_name))
|
| 62 |
+
|
| 63 |
+
# Cek file yang mengandung string
|
| 64 |
+
for file_name in files:
|
| 65 |
+
if string.lower() in file_name.lower():
|
| 66 |
+
hasil.append(os.path.join(root, file_name))
|
| 67 |
+
|
| 68 |
+
# Menampilkan hasil pencarian
|
| 69 |
+
if hasil:
|
| 70 |
+
output += "\nHasil Pencarian:"
|
| 71 |
+
for i, item in enumerate(hasil, 1):
|
| 72 |
+
output += f"\n{i}. {item}"
|
| 73 |
+
else:
|
| 74 |
+
output += "Tidak ada hasil ditemukan."
|
| 75 |
+
|
| 76 |
+
return output
|
| 77 |
+
|
| 78 |
+
def search_jav(path="."):
|
| 79 |
+
video_files = []
|
| 80 |
+
image_files = []
|
| 81 |
+
|
| 82 |
+
for root, _, files in os.walk(path):
|
| 83 |
+
for file in files:
|
| 84 |
+
if file.lower().endswith(".mp4"):
|
| 85 |
+
video_files.append(os.path.join(root, file)) # Simpan path lengkap
|
| 86 |
+
elif file.lower().endswith(".jpg"):
|
| 87 |
+
image_files.append(os.path.join(root, file))
|
| 88 |
+
|
| 89 |
+
return video_files, image_files
|
| 90 |
+
|
| 91 |
+
def run_command(command):
|
| 92 |
+
try:
|
| 93 |
+
result = subprocess.run(command, shell=True, text=True, capture_output=True)
|
| 94 |
+
if result.stdout:
|
| 95 |
+
return result.stdout # Kembalikan output jika ada
|
| 96 |
+
# Jika output kosong, lempar error
|
| 97 |
+
raise RuntimeError("Perintah tidak menghasilkan output!")
|
| 98 |
+
except Exception as e:
|
| 99 |
+
return search(command)
|
| 100 |
+
|
| 101 |
+
def install_miyuki():
|
| 102 |
+
try:
|
| 103 |
+
import miyuki
|
| 104 |
+
return "Miyuki is already installed."
|
| 105 |
+
except ImportError:
|
| 106 |
+
command = "pip install miyuki-0.7.7-py3-none-any.whl"
|
| 107 |
+
return run_command(command)
|
miyuki-0.7.7-py3-none-any.whl
ADDED
|
Binary file (13 kB). View file
|
|
|
process.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import uuid
|
| 3 |
+
import json
|
| 4 |
+
import time
|
| 5 |
+
import subprocess
|
| 6 |
+
|
| 7 |
+
static_dir = os.path.join(os.getcwd(), "static")
|
| 8 |
+
queue_file = os.path.join(os.getcwd(), "download_queue.json")
|
| 9 |
+
if not os.path.exists(static_dir):
|
| 10 |
+
os.makedirs(static_dir)
|
| 11 |
+
|
| 12 |
+
def load_queue():
|
| 13 |
+
if os.path.exists(queue_file):
|
| 14 |
+
with open(queue_file, "r") as f:
|
| 15 |
+
return json.load(f)
|
| 16 |
+
return {}
|
| 17 |
+
|
| 18 |
+
def save_queue(queue):
|
| 19 |
+
with open(queue_file, "w") as f:
|
| 20 |
+
json.dump(queue, f)
|
| 21 |
+
|
| 22 |
+
def download_file(video_url_input, quality):
|
| 23 |
+
movie_id = video_url_input.split("/")[-1]
|
| 24 |
+
log_id = uuid.uuid4()
|
| 25 |
+
log_file_path = os.path.join(static_dir, f"{log_id}.log")
|
| 26 |
+
command = f"miyuki -ffmpeg -auto {video_url_input} -quality {quality} -cover"
|
| 27 |
+
|
| 28 |
+
with open(log_file_path, "w") as log_file:
|
| 29 |
+
subprocess.Popen(
|
| 30 |
+
command, stdout=log_file, stderr=log_file, text=True, shell=True
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
log_link = f"./static/{log_id}.log"
|
| 34 |
+
return "Downloading", log_link
|
| 35 |
+
|
| 36 |
+
def check_task_status(url, log_link):
|
| 37 |
+
if not log_link:
|
| 38 |
+
return "Not Started", ""
|
| 39 |
+
|
| 40 |
+
log_file = log_link.split("/static/")[-1]
|
| 41 |
+
log_path = os.path.join(static_dir, log_file)
|
| 42 |
+
|
| 43 |
+
if os.path.exists(log_path):
|
| 44 |
+
with open(log_path, "r") as f:
|
| 45 |
+
content = f.read()
|
| 46 |
+
movie_id = url.split("/")[-1]
|
| 47 |
+
|
| 48 |
+
if f"File integrity for {movie_id}: 100.00%" in content:
|
| 49 |
+
return "Success", log_link
|
| 50 |
+
elif "Failed to fetch HTML for" in content:
|
| 51 |
+
return "Failed", log_link
|
| 52 |
+
|
| 53 |
+
return "Downloading", log_link
|
| 54 |
+
|
| 55 |
+
def clean_log_files():
|
| 56 |
+
queue = load_queue()
|
| 57 |
+
urls_to_remove = []
|
| 58 |
+
|
| 59 |
+
for url, task in queue.items():
|
| 60 |
+
if task["Status"] in ["Success", "Failed"]:
|
| 61 |
+
if task["Log"]:
|
| 62 |
+
log_file = task["Log"].split("/static/")[-1]
|
| 63 |
+
log_path = os.path.join(static_dir, log_file)
|
| 64 |
+
if os.path.exists(log_path):
|
| 65 |
+
os.remove(log_path)
|
| 66 |
+
urls_to_remove.append(url)
|
| 67 |
+
|
| 68 |
+
for url in urls_to_remove:
|
| 69 |
+
del queue[url]
|
| 70 |
+
|
| 71 |
+
save_queue(queue)
|
| 72 |
+
|
| 73 |
+
for filename in ["downloaded_urls_miyuki.txt", "ffmpeg_input_miyuki.txt", "tmp_movie_miyuki.html", "miyuki.log"]:
|
| 74 |
+
try:
|
| 75 |
+
os.remove(filename)
|
| 76 |
+
except FileNotFoundError:
|
| 77 |
+
pass
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio
|
| 2 |
+
pandas
|