feat(download screen):implement download capabilities

This commit is contained in:
Benex254
2024-08-05 09:46:58 +03:00
parent b1f3953f44
commit 02fc593fe6
11 changed files with 172 additions and 49 deletions

View File

@@ -5,3 +5,17 @@ class DownloadsScreenModel(BaseScreenModel):
"""
Handles the download screen logic
"""
def update_download_progress(self, d):
print(
d["filename"],
d["downloaded_bytes"],
d["total_bytes"],
d.get("total_bytes"),
d["elapsed"],
d["eta"],
d["speed"],
d.get("percent"),
)
if d["status"] == "finished":
print("Done downloading, now converting ...")

View File

@@ -1,4 +1,10 @@
from threading import Thread
from queue import Queue
import yt_dlp
from ... import downloads_dir
from ..utils import sanitize_filename
from ..show_notification import show_notification
class MyLogger:
@@ -12,27 +18,55 @@ class MyLogger:
print(msg)
def my_hook(d):
if d["status"] == "finished":
print("Done downloading, now converting ...")
def main_progress_hook(data):
match data["status"]:
case "error":
show_notification(
"Something went wrong while downloading the video", data["filename"]
)
case "finished":
show_notification("Downloaded", data["filename"])
# URL of the file you want to download
url = "http://example.com/path/to/file.mp4"
# Options for yt-dlp
ydl_opts = {
"outtmpl": "/path/to/downloaded/file.%(ext)s", # Specify the output path and template
"logger": MyLogger(), # Custom logger
"progress_hooks": [my_hook], # Progress hook
}
# Function to download the file
def download_file(url, options):
with yt_dlp.YoutubeDL(options) as ydl:
ydl.download([url])
class YtDLPDownloader:
downloads_queue = Queue()
def _worker(self):
while True:
task, args = self.downloads_queue.get()
try:
task(*args)
except Exception as e:
show_notification("Something went wrong", f"Reason: {e}")
self.downloads_queue.task_done()
def __init__(self):
self._thread = Thread(target=self._worker)
self._thread.daemon = True
self._thread.start()
# Function to download the file
def _download_file(self, url: str, title, custom_progress_hook, silent):
anime_title = sanitize_filename(title[0])
ydl_opts = {
"outtmpl": f"{downloads_dir}/{anime_title}/{anime_title}-episode {title[1]}.%(ext)s", # Specify the output path and template
"progress_hooks": [
main_progress_hook,
custom_progress_hook,
], # Progress hook
"silent": silent,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
def download_file(self, url: str, title, custom_progress_hook, silent=True):
self.downloads_queue.put(
(self._download_file, (url, title, custom_progress_hook, silent))
)
# Call the function
download_file(url, ydl_opts)
downloader = YtDLPDownloader()

View File

@@ -1,7 +1,7 @@
import os
import shutil
from datetime import datetime
import re
# TODO: make it use color_text instead of fixed vals
# from .kivy_markup_helper import color_text
@@ -34,3 +34,62 @@ def move_file(source_path, dest_path):
return (1, path)
except Exception as e:
return (0, e)
def sanitize_filename(filename: str):
"""
Sanitize a string to be safe for use as a file name.
:param filename: The original filename string.
:return: A sanitized filename string.
"""
# List of characters not allowed in filenames on various operating systems
invalid_chars = r'[<>:"/\\|?*\0]'
reserved_names = {
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
}
# Replace invalid characters with an underscore
sanitized = re.sub(invalid_chars, "_", filename)
# Remove leading and trailing whitespace
sanitized = sanitized.strip()
# Check for reserved filenames
name, ext = os.path.splitext(sanitized)
if name.upper() in reserved_names:
name += "_file"
sanitized = name + ext
# Ensure the filename is not empty
if not sanitized:
sanitized = "default_filename"
return sanitized
# Example usage
unsafe_filename = "CON:example?file*name.txt"
safe_filename = sanitize_filename(unsafe_filename)
print(safe_filename) # Output: 'CON_example_file_name.txt'

View File

@@ -47,6 +47,8 @@
on_press: root.next_episode()
MDIconButton:
icon:"download"
on_press:
if root.current_link: app.download_anime_video(root.current_link,(root.current_title[0],root.current_episode))
MDButton:
on_press:
if root.current_link: app.play_on_mpv(root.current_link)

View File

@@ -18,11 +18,6 @@
TaskText:
size_hint_x:.8
text:color_text(root.anime_task_name,root.theme_cls.primaryColor)
TaskText:
size_hint_x:.2
# color:self.theme_cls.surfaceDimColor
theme_text_color:"Secondary"
text:color_text(root.episodes_to_download,root.theme_cls.secondaryColor)
MDIcon:
icon:"download"
text:"{} Episode: {}".format(root.file[0],root.file[1])

View File

@@ -1,13 +1,13 @@
from kivy.properties import StringProperty
from kivy.properties import StringProperty, ListProperty
from kivymd.uix.boxlayout import MDBoxLayout
# TODO: add a progress bar to show the individual progress of each task
class TaskCard(MDBoxLayout):
anime_task_name = StringProperty()
episodes_to_download = StringProperty()
file = ListProperty(("", ""))
eta = StringProperty()
def __init__(self, anime_title: str, episodes: str, *args, **kwargs):
def __init__(self, file: str, *args, **kwargs):
super().__init__(*args, **kwargs)
self.anime_task_name = f"{anime_title}"
self.episodes_to_download = f"Episodes: {episodes}"
self.file = file
# self.eta = eta
#

View File

@@ -7,7 +7,7 @@
shorten:False
markup:True
font_style: "Label"
role: "large"
role: "small"
bold:True
<DownloadsScreenView>
md_bg_color: self.theme_cls.backgroundColor
@@ -46,12 +46,12 @@
md_bg_color:self.theme_cls.secondaryContainerColor
DownloadsScreenLabel:
id:download_progress_label
size_hint_x: .6
size_hint_x: .8
text:"Try Downloading sth :)"
pos_hint: {'center_y': .5}
MDLinearProgressIndicator:
id: progress_bar
size_hint_x: .4
size_hint_x: .2
size_hint_y:None
height:"10dp"
type: "determinate"

View File

@@ -11,21 +11,31 @@ class DownloadsScreenView(BaseScreenView):
progress_bar = ObjectProperty()
download_progress_label = ObjectProperty()
def on_new_download_task(self, anime_title: str, episodes: str | None):
if not episodes:
episodes = "All"
def new_download_task(self, filename):
Clock.schedule_once(
lambda _: self.main_container.add_widget(TaskCard(anime_title, episodes))
lambda _: self.main_container.add_widget(TaskCard(filename))
)
def on_episode_download_progress(
self, current_bytes_downloaded, total_bytes, episode_info
):
percentage_completion = round((current_bytes_downloaded / total_bytes) * 100)
progress_text = f"Downloading: {episode_info['anime_title']} - {episode_info['episode']} ({format_bytes_to_human(current_bytes_downloaded)}/{format_bytes_to_human(total_bytes)})"
if (percentage_completion % 5) == 0:
self.progress_bar.value = max(min(percentage_completion, 100), 0)
self.download_progress_label.text = progress_text
def on_episode_download_progress(self, data):
percentage_completion = round(
(data.get("downloaded_bytes", 0) / data.get("total_bytes", 0)) * 100
)
speed = format_bytes_to_human(data.get("speed", 0)) if data.get("speed") else 0
progress_text = f"Downloading: {data.get('filename', 'unknown')} ({format_bytes_to_human(data.get('downloaded_bytes',0)) if data.get('downloaded_bytes') else 0}/{format_bytes_to_human(data.get('total_bytes',0)) if data.get('total_bytes') else 0})\n Elapsed: {round(data.get('elapsed',0)) if data.get('elapsed') else 0}s ETA: {data.get('eta',0) if data.get('eta') else 0}s Speed: {speed}/s"
self.progress_bar.value = max(min(percentage_completion, 100), 0)
self.download_progress_label.text = progress_text
def update_layout(self, widget):
self.user_anime_list_container.add_widget(widget)
#
# d["filename"],
# d["downloaded_bytes"],
# d["total_bytes"],
# d.get("total_bytes"),
# d["elapsed"],
# d["eta"],
# d["speed"],
# d.get("percent"),
# )

View File

@@ -15,7 +15,7 @@ if vid_path := plyer.storagepath.get_videos_dir(): # type: ignore
if not os.path.exists(downloads_dir):
os.mkdir(downloads_dir)
else:
downloads_dir = os.path.join(".", "videos")
downloads_dir = os.path.join(app_dir, "videos")
if not os.path.exists(downloads_dir):
os.mkdir(downloads_dir)

View File

@@ -10,12 +10,15 @@ from kivy.uix.screenmanager import FadeTransition, ScreenManager
from kivy.uix.settings import Settings, SettingsWithSidebar
from kivymd.app import MDApp
from fastanime.Utility.show_notification import show_notification
from . import downloads_dir
from .libs.mpv.player import mpv_player
from .Utility import (
themes_available,
user_data_helper,
)
from .Utility.downloader.downloader import downloader
from .Utility.utils import write_crash
from .View.components.media_card.components.media_popup import MediaPopup
from .View.screens import screens
@@ -141,6 +144,12 @@ class FastAnime(MDApp):
mpv_player.stop_mpv()
mpv_player.run_mpv(anime_video_url)
def download_anime_video(self, url: str, anime_title: tuple):
self.download_screen.new_download_task(anime_title)
show_notification("New Download", f"{anime_title[0]} episode: {anime_title[1]}")
progress_hook = self.download_screen.on_episode_download_progress
downloader.download_file(url, anime_title, progress_hook)
def run_app():
FastAnime().run()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB