Skip to content
This repository was archived by the owner on Jun 30, 2024. It is now read-only.

Commit f9e210b

Browse files
authored
Merge pull request #27 from realshouzy/single-file-project-structure
Restructure project to a single-file format
2 parents 2956735 + 16b741a commit f9e210b

File tree

10 files changed

+182
-237
lines changed

10 files changed

+182
-237
lines changed

YTDownloader/downloader.py renamed to YTDownloader.py

Lines changed: 146 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
1-
"""Module containing all classes to download YouTube content."""
1+
"""A program to download any YouTube video or playlist."""
22
from __future__ import annotations
33

4-
__all__: tuple[str, ...] = (
5-
"YouTubeDownloader",
6-
"PlaylistDownloader",
7-
"VideoDownloader",
8-
"get_downloader",
9-
)
4+
__version__: Final[str] = "1.5.1"
5+
__title__: Final[str] = "YTDownloader"
6+
__author__: Final[str] = "realshouzy"
7+
__license__: Final[str] = "MIT"
8+
__copyright__: Final[str] = "Copyright (c) 2022-present realshouzy"
109

1110
import re
1211
import sys
1312
import webbrowser
1413
from abc import ABC, abstractmethod
1514
from concurrent.futures import ThreadPoolExecutor
1615
from pathlib import Path
17-
from typing import TYPE_CHECKING, Any, Final
16+
from typing import TYPE_CHECKING, Any, Final, NamedTuple
1817

1918
import PySimpleGUI as sg
2019
import pytube.exceptions
2120
from pytube import Playlist, YouTube
2221

23-
from YTDownloader.download_options import AUDIO, HD, LD
24-
2522
if sys.version_info >= (3, 12): # pragma: >=3.12 cover
2623
from typing import override
2724
else: # pragma: <3.12 cover
@@ -32,7 +29,35 @@
3229

3330
from pytube import Stream
3431

35-
from YTDownloader.download_options import DownloadOptions
32+
33+
class DownloadOptions(NamedTuple):
34+
"""Tuple-like class holding the download options."""
35+
36+
resolution: str | None
37+
type: str
38+
progressive: bool
39+
abr: str | None
40+
41+
42+
# defining download options
43+
LD: Final[DownloadOptions] = DownloadOptions(
44+
resolution="360p",
45+
type="video",
46+
progressive=True,
47+
abr=None,
48+
)
49+
HD: Final[DownloadOptions] = DownloadOptions(
50+
resolution="720p",
51+
type="video",
52+
progressive=True,
53+
abr=None,
54+
)
55+
AUDIO: Final[DownloadOptions] = DownloadOptions(
56+
resolution=None,
57+
type="audio",
58+
progressive=False,
59+
abr="128kbps",
60+
)
3661

3762
_YOUTUBE_PLAYLIST_URL_PATTERN: Final[re.Pattern[str]] = re.compile(
3863
r"^(?:https?:\/\/)?(?:www\.|m\.)?"
@@ -590,3 +615,113 @@ def _download_complete(
590615
self._download_window["-DOWNLOADPROGRESS-"].update(0)
591616
self._download_window["-COMPLETED-"].update("")
592617
sg.Popup("Downloaded complete")
618+
619+
620+
def create_error_window(error_name: str, message: str) -> None: # pragma: no cover
621+
"""Create an error window."""
622+
error_layout: list[list[sg.Text | sg.Button]] = [
623+
[sg.Text(f"{error_name}: {message}")],
624+
[sg.Button("Ok", key="-OK-"), sg.Button("Report", key="-REPORT-")],
625+
]
626+
627+
error_window: sg.Window = sg.Window(
628+
"Error",
629+
layout=error_layout,
630+
modal=True,
631+
)
632+
633+
# error window event loop
634+
while True:
635+
event, _ = error_window.read() # type: ignore
636+
if event in {sg.WIN_CLOSED, "-OK-"}:
637+
break
638+
if event == "-REPORT-":
639+
webbrowser.open("https://github.com/realshouzy/YTDownloader/issues")
640+
641+
error_window.close()
642+
643+
644+
# pylint: disable=R0912, W0718
645+
646+
647+
def main() -> int: # noqa: C901 # pragma: no cover
648+
"""Run the program."""
649+
exit_code: int = 0
650+
651+
sg.theme("Darkred1")
652+
653+
# defining layouts
654+
start_layout: list[list[sg.Input | sg.Button]] = [
655+
[sg.Input(key="-LINKINPUT-"), sg.Button("Submit")],
656+
]
657+
start_window: sg.Window = sg.Window("Youtube Downloader", start_layout)
658+
659+
# main event loop
660+
while True:
661+
event, values = start_window.read()
662+
if event == sg.WIN_CLOSED:
663+
break
664+
665+
if event == "Submit":
666+
try:
667+
downloader: PlaylistDownloader | VideoDownloader = get_downloader(
668+
values["-LINKINPUT-"],
669+
)
670+
downloader.create_window()
671+
672+
except pytube.exceptions.RegexMatchError as re_err:
673+
if not values["-LINKINPUT-"]:
674+
create_error_window(
675+
re_err.__class__.__name__,
676+
"Please provide link.",
677+
)
678+
else:
679+
create_error_window(re_err.__class__.__name__, "Invalid link.")
680+
681+
except pytube.exceptions.VideoPrivate as vp_err:
682+
create_error_window(vp_err.__class__.__name__, "Video is privat.")
683+
684+
except pytube.exceptions.MembersOnly as mo_err:
685+
create_error_window(
686+
mo_err.__class__.__name__,
687+
"Video is for members only.",
688+
)
689+
690+
except pytube.exceptions.VideoRegionBlocked as vgb_err:
691+
create_error_window(
692+
vgb_err.__class__.__name__,
693+
"Video is block in your region.",
694+
)
695+
696+
except pytube.exceptions.LiveStreamError as ls_err:
697+
create_error_window(
698+
ls_err.__class__.__name__,
699+
"This is an active life stream.",
700+
)
701+
702+
except pytube.exceptions.AgeRestrictedError as ar_err:
703+
create_error_window(
704+
ar_err.__class__.__name__,
705+
"This video is age restricted.",
706+
)
707+
708+
except pytube.exceptions.VideoUnavailable as vu_err:
709+
create_error_window(vu_err.__class__.__name__, "Video Unavailable.")
710+
711+
except KeyError as key_err:
712+
create_error_window(
713+
key_err.__class__.__name__,
714+
"Video or playlist is unreachable or invalid.",
715+
)
716+
717+
except Exception as err:
718+
create_error_window(err.__class__.__name__, str(err))
719+
exit_code = 1
720+
break
721+
722+
start_window.close()
723+
return exit_code
724+
725+
726+
if __name__ == "__main__":
727+
raise SystemExit(main())

YTDownloader/__init__.py

Lines changed: 0 additions & 10 deletions
This file was deleted.

YTDownloader/__main__.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

YTDownloader/download_options.py

Lines changed: 0 additions & 36 deletions
This file was deleted.

YTDownloader/error_window.py

Lines changed: 0 additions & 32 deletions
This file was deleted.

YTDownloader/main.py

Lines changed: 0 additions & 101 deletions
This file was deleted.

YTDownloader/py.typed

Whitespace-only changes.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,3 @@ plugins = ["covdefaults"]
8686
[tool.coverage.report]
8787
exclude_also = ["except Exception as err:"]
8888
fail_under = 100
89-
omit = ["YTDownloader/main.py", "YTDownloader/error_window.py"]

0 commit comments

Comments
 (0)