From 9e1abaa8560b96fcc753206d062250a76eb21530 Mon Sep 17 00:00:00 2001 From: DUB1401 Date: Sun, 9 Jul 2023 17:44:25 +0300 Subject: [PATCH] Add: links formatter and messages from downloader --- PornHub Downloader.py | 2 +- Settings.json | 2 +- Source/MainWindow.py | 167 ++++++++++++++++++++++++++++-------------- Source/pornhub_dl.py | 35 +++++++++ 4 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 Source/pornhub_dl.py diff --git a/PornHub Downloader.py b/PornHub Downloader.py index 0917fc5..0a8982c 100644 --- a/PornHub Downloader.py +++ b/PornHub Downloader.py @@ -30,7 +30,7 @@ } # Словарь важных значений. ComData = { - "version": "1.0.0", + "version": "1.1.0", "copyright": "Copyright © 2023. DUB1401." } diff --git a/Settings.json b/Settings.json index 23b1d07..2409616 100644 --- a/Settings.json +++ b/Settings.json @@ -2,5 +2,5 @@ "sort-by-models": false, "save-directory": "", "theme": 2, - "debug": false + "debug": true } \ No newline at end of file diff --git a/Source/MainWindow.py b/Source/MainWindow.py index 651413f..6178a65 100644 --- a/Source/MainWindow.py +++ b/Source/MainWindow.py @@ -1,4 +1,3 @@ -from genericpath import isdir from PyQt6.QtWidgets import ( QApplication, QCheckBox, @@ -13,52 +12,16 @@ QVBoxLayout ) -from PyQt6.QtCore import ( - QObject, - Qt, - QThread, - QUrl, - pyqtSignal -) - -from PyQt6.QtGui import QDesktopServices +from Source.pornhub_dl import pornhub_dl +from PyQt6.QtGui import QDesktopServices, QTextCursor +from PyQt6.QtCore import Qt,QThread, QUrl import pyperclip import shutil import json +import time import os - -# Потоковый обработчик взаимодейтсвий с библиотекой pornhub_dl. -class PornhubLibSubprocess(QObject): - - #==========================================================================================# - # >>>>> СВОЙСТВА <<<<< # - #==========================================================================================# - - # Сигнал: завершение потока. Содержит: завершающий код вызова библиотеки. - finished = pyqtSignal(int) - # Исполняемая команда. - __Command = None - - #==========================================================================================# - # >>>>> МЕТОДЫ <<<<< # - #==========================================================================================# - - # Конструктор: задаёт команду для выполнения. - def __init__(self, Command: str): - # - super().__init__() - - #---> Генерация свойств. - #==========================================================================================# - self.__Command = Command - - # Запускает выполнение команды. - def run(self): - # Выполнение команды. - ExitCode = os.system(self.__Command) - # Генерация сигнала с завершающим кодом приложения. - self.finished.emit(ExitCode) +import re # Обработчик взаимодействий с главным окном. class MainWindow(QMainWindow): @@ -71,14 +34,16 @@ class MainWindow(QMainWindow): __DownloadingThread = None # Список URL видео. __VideoLinks = list() - # Индекс обрабатываемого видео. - __VideoIndex = None + # Экземпляр приложения. + __Application = None + # Время начала загрузки. + __StartTime = None # Глобальные настройки. __Settings = None # Словарь важных значений. __ComData = None - # Экземпляр приложения. - __Application = None + # Индекс обрабатываемого видео. + __VideoIndex = 0 #==========================================================================================# # >>>>> ОБРАБОТЧИКИ СИГНАЛОВ <<<<< # @@ -104,6 +69,8 @@ def __CopyOutput(self): def __DownloadVideos(self): # Очистка содержимого псевдоконсоли. self.Output.setText("") + # Удалить повторяющиеся ссылки. + self.__RemoveRepeatedLinks() # Деактивация управляющих элементов. self.Clear.setEnabled(False) self.Download.setEnabled(False) @@ -113,8 +80,6 @@ def __DownloadVideos(self): self.__VideoLinks = list(filter(None, self.Input.toPlainText().strip().split('\n'))) # Текущая директория. CurrentDirectory = os.getcwd() - # Обнуление индекса загружаемого видео. - self.__VideoIndex = 0 # Установка текущей директории для библиотеки. os.chdir(CurrentDirectory + "\\pornhub_dl") # Настройка индикатора прогресса. @@ -124,6 +89,56 @@ def __DownloadVideos(self): # Запуск загрузчика. self.__StartDownloading() + # Форматирует поле ввода. + def __FormatInput(self): + # Получение содержимого поля ввода. + InputText = self.Input.toPlainText() + # Разбитие содержимого на отдельные строки. + InputLines = InputText.split('\n') + # Обработанные строки. + FormattedLines = list() + # Результирующие строки. + ResultLines = list() + # Результирующий текст. + ResultText = None + + # Для каждой строки. + for Line in InputLines: + # Попытаться разбить строку по вхождению протокола. + Bufer = Line.replace("https", "\nhttps").strip("\n \t") + # Сохранение разбитых строк. + FormattedLines += Bufer.split('\n') + + # Для каждой обработанной строки. + for Line in FormattedLines: + # Очистка строки от аргументов. + Line = Line.split('&')[0] + + # Если строка соответствует шаблону, то сохранить её. + if bool(re.match(r"https:\/\/rt\.pornhub\.com\/view_video\.php\?viewkey=\S+\b", Line)) == True: + ResultLines.append(Line) + + # Построение результирующего текста. + ResultText = "\n".join(ResultLines) + "\n" + + # Если результирующий текст не содержит символов. + if ResultText.strip("\n \t") == "": + # Обнулить результирующий текст. + ResultText = "" + # Деактивировать кнопку загрузки. + self.Download.setEnabled(False) + + elif self.__VideoIndex == 0: + # Активировать кнопку загрузки. + self.Download.setEnabled(True) + + # Если текст отличается, то поместить отформатированный список ссылок в поле ввода. + if ResultText != self.Input.toPlainText(): + self.Input.setText(ResultText) + + # Перемещение каретки в конец поля ввода. + self.Input.moveCursor(QTextCursor.MoveOperation.End, QTextCursor.MoveMode.MoveAnchor) + # Открывает в браузере страницу проекта на GitHub. def __OpenGitHub(self): QDesktopServices.openUrl(QUrl("https://github.com/DUB1401/PornHub-Downloader")) @@ -147,6 +162,10 @@ def __SaveSetting(self, Key: str, Value): with open("Settings.json", "w", encoding = "utf-8") as FileWrite: json.dump(Bufer, FileWrite, ensure_ascii = False, indent = '\t', separators = (",", ": ")) + # Прокручивает псевдоконсоль вниз. + def __ScrollOutputToEnd(self): + self.Output.moveCursor(QTextCursor.MoveOperation.End) + #==========================================================================================# # >>>>> МЕТОДЫ <<<<< # #==========================================================================================# @@ -186,19 +205,21 @@ def __CreateBasicUI(self): self.Download.clicked.connect(self.__DownloadVideos) self.Download.move(870, 640) self.Download.resize(200, 40) + self.Download.setEnabled(False) self.Download.setText("⬇ Download") # Создание объекта GUI: поле ввода ссылок на видео. self.Input = QTextEdit(self) self.Input.move(10, 10) self.Input.resize(850, 420) - self.Input.setPlaceholderText("Paste here links to videos or press button...") + self.Input.setPlaceholderText("Paste here links to videos") + self.Input.textChanged.connect(self.__FormatInput) # Создание объекта GUI: ссылка на GitHub. self.Link = QLabel(self) self.Link.linkActivated.connect(self.__OpenGitHub) self.Link.move(1030, 690) - self.Link.setText("GitHub") + self.Link.setText("GitHub") self.Link.adjustSize() # Создание объекта GUI: поле псевдоконсольного вывода. @@ -206,7 +227,8 @@ def __CreateBasicUI(self): self.Output.move(10, 490) self.Output.resize(850, 190) self.Output.setReadOnly(True) - self.Output.setPlaceholderText("Output logs...") + self.Output.setPlaceholderText("Output logs") + self.Output.textChanged.connect(self.__ScrollOutputToEnd) # Создание объекта GUI: кнока добавления ссылки в очередь. self.Paste = QPushButton(self) @@ -224,7 +246,7 @@ def __CreateBasicUI(self): # Создание объекта GUI: контейнер настроек. self.SettingsBox = QGroupBox(self) - self.SettingsBox.move(870, 0) + self.SettingsBox.move(870, 10) self.SettingsBox.resize(200, 120) self.SettingsBox.setAlignment(Qt.AlignmentFlag.AlignCenter) self.SettingsBox.setTitle("🔧 Settings") @@ -267,15 +289,24 @@ def __CreateSettingsGroupUI(self): SettingsLayout.addStretch() # Обрабатывает завершение загрузки видео. - def __EndDownloading(self): + def __EndDownloading(self, ExitCode: int): # Инкремент индекса загружаемого видео. self.__VideoIndex += 1 # Текущая директория. CurrentDirectory = os.getcwd() # Увеличение процента заполнение в индикаторе прогресса. self.ProgressBar.setValue(self.__VideoIndex) + + # Если загрузка завершилась успешно, то вывести в псевдоконсоль время выполнения, иначе вывести ошибку. + if ExitCode == 0: + self.Output.setText(self.Output.toPlainText() + "Done! (" + str(round(float(time.time() - self.__StartTime), 2)) + " seconds)\n") + + else: + self.Output.setText(self.Output.toPlainText() + "Error! See CMD output for more information.\n") + # Вывод в псевдоконсоль: разделитель. self.Output.setText(self.Output.toPlainText() + "==========================================================================================\n") + # Структурировать загруженные видео. self.__StructurizateDownloads() # Удаление первого в очереди URL. @@ -296,15 +327,41 @@ def __EndDownloading(self): self.Download.setEnabled(True) self.Output.setReadOnly(False) self.Paste.setEnabled(True) + # Обнуление индекса загружаемого видео. + self.__VideoIndex = 0 # Очистка поля ввода. self.Input.setText("") + # Удаляет повторяющиеся ссылки. + def __RemoveRepeatedLinks(self): + # Получение содержимого поля ввода. + InputText = self.Input.toPlainText() + # Разбитие содержимого на отдельные строки. + InputLines = InputText.split('\n') + # Удаление дубликатов ссылок. + ResultLines = [*set(InputLines)] + + # Если количество ссылок отличается от изначального. + if len(InputLines) != len(ResultLines): + # Построение результирующего текста. + ResultText = "\n".join(ResultLines) + "\n" + # Поместить отсортированный список ссылок в поле ввода. + self.Input.setText(ResultText) + # Вычисление количества удалённых повторов. + RepeatedLinksCount = len(InputLines) - len(ResultLines) + # Вывод в псевдоконсоль: количество удалённых повторов. + self.Output.setText(self.Output.toPlainText() + "Removed identical links count: " + str(RepeatedLinksCount) + " \n") + # Вывод в псевдоконсоль: разделитель. + self.Output.setText(self.Output.toPlainText() + "==========================================================================================\n") + # Обрабатывает начало загрузки видео. def __StartDownloading(self): # Текущая директория. CurrentDirectory = os.getcwd() # Директория загрузки. SaveDirectory = self.__Settings["save-directory"] + # Сохранение времени начала загрузки. + self.__StartTime = time.time() # Если остались незагруженные видео. if self.__VideoIndex < len(self.__VideoLinks): @@ -315,7 +372,7 @@ def __StartDownloading(self): # Вывод в псевдоконсоль: название видео. self.Output.setText(self.Output.toPlainText() + "Current task: " + self.__VideoLinks[self.__VideoIndex] + "\n") # Настройка и запуск обработчика библиотеки в отдельном потоке. - self.Subprocess = PornhubLibSubprocess(f"{CurrentDirectory}/pornhub_dl.py --url {CurrentLink} --dir \"{SaveDirectory}\"") + self.Subprocess = pornhub_dl(f"{CurrentDirectory}/pornhub_dl.py --url {CurrentLink} --dir \"{SaveDirectory}\"") self.Subprocess.moveToThread(self.__DownloadingThread) self.__DownloadingThread.quit() self.__DownloadingThread.started.connect(self.Subprocess.run) @@ -362,7 +419,7 @@ def __StructurizateDownloads(self): # Удалить исходную директорию с файлами. shutil.rmtree(self.__Settings["save-directory"] + "\\model") - # Конструктор. + # Конструктор: задаёт экземпляр приложения, словарь важных значений и глобальные настройки. def __init__(self, Application: QApplication, ComData: dict, Settings: dict): # super().__init__() diff --git a/Source/pornhub_dl.py b/Source/pornhub_dl.py new file mode 100644 index 0000000..ce5d6b0 --- /dev/null +++ b/Source/pornhub_dl.py @@ -0,0 +1,35 @@ +from PyQt6.QtCore import QObject, pyqtSignal + +import os + +# Потоковый обработчик взаимодейтсвий с библиотекой pornhub_dl. +class pornhub_dl(QObject): + + #==========================================================================================# + # >>>>> СВОЙСТВА <<<<< # + #==========================================================================================# + + # Сигнал: завершение потока. Содержит: завершающий код вызова библиотеки. + finished = pyqtSignal(int) + # Исполняемая команда. + __Command = None + + #==========================================================================================# + # >>>>> МЕТОДЫ <<<<< # + #==========================================================================================# + + # Конструктор: задаёт команду для выполнения. + def __init__(self, Command: str): + # + super().__init__() + + #---> Генерация свойств. + #==========================================================================================# + self.__Command = Command + + # Запускает выполнение команды. + def run(self): + # Выполнение команды. + ExitCode = os.system(self.__Command) + # Генерация сигнала с завершающим кодом приложения. + self.finished.emit(ExitCode) \ No newline at end of file