Skip to content

Commit

Permalink
2.9
Browse files Browse the repository at this point in the history
Implemented #11
  • Loading branch information
EchterAlsFake committed Oct 16, 2023
1 parent 918d5dd commit 77f26f3
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 70 deletions.
120 changes: 87 additions & 33 deletions Porn_Fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,27 @@
import os
import random


import requests # See: https://github.com/psf/requests
import math
import src.icons

from PySide6.QtWidgets import (QVBoxLayout, QHBoxLayout, QRadioButton, QInputDialog,
QCheckBox, QPushButton, QScrollArea, QGroupBox)
QCheckBox, QPushButton, QScrollArea, QGroupBox)
from phub import Client, Quality, locals, errors # See https://github.com/Egsagon/PHUB
from phub.modules.download import default, threaded
from phub.modules.download import threaded
from hqporner_api import API # See: https://github.com/EchterAlsFake/hqporner_api
from configparser import ConfigParser # See: https://github.com/python/cpython/blob/main/Lib/configparser.py
from PySide6 import QtCore # See: https://pypi.org/project/PySide6/
from PySide6.QtCore import QSemaphore
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QTreeWidgetItem, QButtonGroup
from PySide6.QtCore import Signal, QThreadPool, QRunnable, QObject, Slot
from PySide6.QtGui import QIcon
from moviepy.editor import VideoFileClip
from src.license_agreement import Ui_Widget_License
from src.Porn_Fetch_v3 import Ui_Porn_Fetch_widget
from src.setup import setup_config_file, strip_title, logging
from cli import CLI


categories = [attr for attr in dir(locals.Category) if
not callable(getattr(locals.Category, attr)) and not attr.startswith("__")]

Expand All @@ -47,7 +46,6 @@ def update_progressbar(pos, total, progress_bar):


def help_speed():

text = """
Speed (or Delay) is the requests limit per second.
Expand Down Expand Up @@ -204,23 +202,29 @@ class DownloadThread(QRunnable):
Will be used when the 'Yes' Box is checked in the main UI.
"""

def __init__(self, video, quality, output_path):
def __init__(self, video, quality, output_path, semaphore):
super().__init__()
self.video = video
self.quality = quality
self.output_path = output_path
self.signals = DownloadProgressSignal()
self.semaphore = semaphore

def callback(self, pos, total):
self.signals.progress.emit(pos, total)

def run(self):
self.semaphore.acquire()
try:
self.video.download(downloader=threaded, display=self.callback, quality=self.quality, path=self.output_path)
self.video.download(display=self.callback, quality=self.quality, path=self.output_path,
downloader=threaded())

except OSError:
logging("OS Error in Download Thread!", level=1)
self.video.download(downloader=threaded, display=self.callback, quality=self.quality, path="os_error_fixed_title.mp4")
self.video.download(display=self.callback, quality=self.quality, path="os_error_fixed_title.mp4",
downloader=threaded())
finally:
self.semaphore.release()


class CategoryFilterWindow(QWidget):
Expand Down Expand Up @@ -298,6 +302,41 @@ def on_apply(self):
self.conf.write(configfile)


def approximately_equal(duration1, duration2, tolerance=5):
"""
Check if two durations are approximately equal within a given tolerance.
Parameters:
- duration1, duration2: int or float, durations to compare.
- tolerance: int or float, acceptable difference between the durations.
Returns:
- bool, True if durations are approximately equal, False otherwise.
"""
return abs(duration1 - duration2) <= tolerance


def check_if_video_exists(video, output_path):
if os.path.exists(output_path):
logging("Found video... checking length...")
with VideoFileClip(output_path) as clip:
existing_duration = int(clip.duration)
video_duration = video.duration.seconds

logging(f"Existing video duration: {existing_duration}")
logging(f"Video duration: {video_duration}")

if approximately_equal(existing_duration, video_duration):
logging("Video already exists, skipping download...")
return True

else:
return False

else:
return False


class Widget(QWidget):
"""Main UI widget. Design is loaded from the ui_main_widget.py file. Feel free to change things if you want."""

Expand Down Expand Up @@ -326,8 +365,8 @@ def __init__(self, parent=None):
self.excluded_categories_str = None
self.selected_category_value = None
self.selected_category = None
self.threadpool = QThreadPool()
self.semaphore = QSemaphore(1)
self.threadpool = QThreadPool.globalInstance()
self.semaphore = QSemaphore(4)

self.ui = Ui_Porn_Fetch_widget()
self.ui.setupUi(self)
Expand Down Expand Up @@ -377,6 +416,7 @@ def button_connectors(self):
self.ui.button_select_all.clicked.connect(self.select_all_items)
self.ui.button_unselect_all.clicked.connect(self.unselect_all_items)
self.ui.button_custom_api.clicked.connect(self.custom_api_language)
self.ui.button_download_avatar.clicked.connect(self.download_avatar)

def switch_video_page(self):
self.ui.stackedWidget_3.setCurrentIndex(0)
Expand Down Expand Up @@ -559,25 +599,29 @@ def download(self, video, progress_bar, os_error_handle=False):
logging(f"Stripped title: {title}")
output_path = f"{output_path}{title}.mp4"

try:
if self.mode:
self.download_thread = DownloadThread(video, quality, output_path)
self.download_thread.signals.progress.connect(
lambda pos, total, pb=progress_bar: update_progressbar(pos, total, pb))
self.threadpool.start(self.download_thread)
logging(msg="Started download thread...")
logging("Checking if video already exists...")

elif not self.mode:
logging(msg="Running in main thread...")
self.video.download.default(display=self.callback, quality=quality, path=output_path)
if not check_if_video_exists(video, output_path):
try:
if self.mode:
self.download_thread = DownloadThread(video, quality, output_path, self.semaphore)
self.download_thread.signals.progress.connect(
lambda pos, total, pb=progress_bar: update_progressbar(pos, total, pb))
self.threadpool.start(self.download_thread)
logging(msg="Started download thread...")

except OSError:
logging(msg="OS Error: The file name is invalid for your system. Recreating a random int name...", level=1)
self.download(video, progress_bar, os_error_handle=True)
elif not self.mode:
logging(msg="Running in main thread...")
self.video.download.default(display=self.callback, quality=quality, path=output_path)

except Exception as e:
ui_popup(text=f"An unexpected error happened. Exception: {e}")
logging(msg=e, level=1)
except OSError:
logging(msg="OS Error: The file name is invalid for your system. Recreating a random int name...",
level=1)
self.download(video, progress_bar, os_error_handle=True)

except Exception as e:
ui_popup(text=f"An unexpected error happened. Exception: {e}")
logging(msg=e, level=1)

def custom_api_language(self):
api_language = self.ui.lineedit_custom_api_language.text()
Expand All @@ -588,9 +632,6 @@ def custom_api_language(self):

ui_popup("Applied, please restart Porn Fetch")




def start_file(self):
file_path = self.ui.lineedit_file.text()
with open(file_path, "r") as file:
Expand Down Expand Up @@ -692,6 +733,20 @@ def download_thumbnail(self):
image.download(path="./")
ui_popup(f"Downloaded Thumbnail for: {url}")

def download_avatar(self):
user_url = self.ui.lineedit_user_url.text()
object = self.client.get_user(user_url)
avatar = object.avatar
name = object.name
location = self.path + name + ".jpg"
try:
avatar.download(path=location)
ui_popup(f"Downloaded Avatar for: {name} to: {location}")

except Exception as e:
ui_popup("Error downloading avatar: " + str(e))


def get_user_information(self):
user = self.ui.lineedit_user_url.text()
user_object = self.client.get_user(user)
Expand All @@ -714,10 +769,12 @@ def get_user_information(self):
profile_views = info.get("Profile Views")
videos_watched = info.get("Videos Watched")

avatar_url = user_object.avatar.url

self.ui.lineedit_user_name.setText(str(user_object.name))
self.ui.lineedit_user_url.setText(str(user_object.url))
self.ui.lineedit_user_fake_breasts.setText(str(fake_breasts))
self.ui.lineedit_user_avatar.setText(str("Not implemented yet, wait vor v2.9"))
self.ui.lineedit_user_avatar.setText(str(avatar_url))
self.ui.lineedit_user_gender.setText(str(gender))
self.ui.lineedit_user_ethnicity.setText(str(ethnicity))
self.ui.lineedit_user_hair_color.setText(str(hair_color))
Expand Down Expand Up @@ -916,9 +973,6 @@ def load_user_settings(self):
self.ui.radio_api_custom.setChecked(True)
logging("Applied custom language")




if self.conf["Porn_Fetch"]["delay"] == "true":
self.ui.radio_speed_usual.setChecked(True)
self.delay = True
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

![Build](https://github.com/EchterAlsFake/Porn_Fetch/actions/workflows/python-app.yml/badge.svg)
![Build](https://github.com/EchterAlsFake/Porn_Fetch/actions/workflows/android.yml/badge.svg)
### [Download Current Version 2.8](https://github.com/EchterAlsFake/Porn_Fetch/releases/tag/2.8)
### [Development Status V2.9](https://github.com/EchterAlsFake/Porn_Fetch/blob/master/README/STATUS.md)
### [Download Current Version 2.9](https://github.com/EchterAlsFake/Porn_Fetch/releases/tag/2.9)
### [Development Status V3.0](https://github.com/EchterAlsFake/Porn_Fetch/blob/master/README/STATUS.md)
## Table of Contents

- [What is Porn Fetch?](#what-is-porn-fetch)
Expand Down Expand Up @@ -95,18 +95,18 @@ This will automatically install Python3 (if not installed) and build the project
The development for Android has a separate branch named "android."
<br>Kivy is a whole new framework for me, so I will need my time to make it look good!

### DOWNLOAD: [0.1](https://github.com/EchterAlsFake/Porn_Fetch/releases)
### DOWNLOAD: [0.2](https://github.com/EchterAlsFake/Porn_Fetch/releases)

#### Supported are Android 5-13 (in theory)

If you like to build it by yourself, use [this script](https://github.com/EchterAlsFake/Porn_Fetch/blob/master/build_android.sh)

It will install all dependencies and use the buildozer.spec from my repo.
<br> YOU NEED UBUNTU 22.04.3 FOR BUILDING AND NOTHING ELSE!!!

Minimum needed API: 21

**Requirements:**
- Internet, Read and write to External storage (your internal /emulated/0/ drive)

**Error reporting:**
- You can report errors, but please include your Android version, a detailed description of what you did and if your device is rooted, because if it is, we can get additional logs :)

**Manual Building:**
- A guide/script is in development

# iOS
### iSH
Expand Down
10 changes: 9 additions & 1 deletion README/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,12 @@ I hope that most issues are now fixed.
- API Update to PHUB v4.1 from py-3.9 branch using custom fork
- You can now just paste the URL from the clipboard
- You can now choose the output folder without needing to enter it in to the input line
- improved visual look and progressbar
- improved visual look and progressbar

# 2.9

- API updated to 4.1.3
- Added Enhancement request from #11 (Skips already downloaded videos...)
- huge Performance increase when downloading (thanks to Egsagon's threaded preset)
- Added a Semaphore (only 4 threads at once -- less overload and less CPU burning)
- Added Avatar downloading
7 changes: 2 additions & 5 deletions README/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@
- [] Update Website to v4 and host it
- [x] Work on the basic design of the Android Application
- [x] Automatically manage permissions for the Android Application
- [x] Add some logical useful things like .. / .. videos downloaded, when using multiple downloads etc...
- [] Add some logical useful things like .. / .. videos downloaded, when using multiple downloads etc...
# STATUS:

Priority:

Android App / Implementing new functions

See: [Status](https://github.com/EchterAlsFake/Porn_Fetch/blob/master/README/STATUS.md)
12 changes: 6 additions & 6 deletions README/STATUS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# 2.9
# 3.0


## TODO:

- [] Add avatar in User metadata
- [] Update Web application
- [] Using threaded download method from 4.1.2
- [x] Colorized logging
- [] Updating API to v4.1.2
- [] Working on the translations
- [x] Automatic Kivy build script (Ubuntu 22.04)
- [] Updating CLI (make a TUI from it)
- [] Fixing the script for iOS (will take its time)
- [] Building for macOS (I don't have an Apple product, so it needs some grey zones to be broken ^^)
- [] Accent colors for main UI
- [] Update Android Application
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ wget
requests
lxml
hqporner_api
hue_shift
hue_shift
moviepy
12 changes: 6 additions & 6 deletions src/Porn_Fetch_v3.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Ui_Porn_Fetch_widget(object):
def setupUi(self, Porn_Fetch_widget):
if not Porn_Fetch_widget.objectName():
Porn_Fetch_widget.setObjectName(u"Porn_Fetch_widget")
Porn_Fetch_widget.resize(1418, 595)
Porn_Fetch_widget.resize(1418, 596)
Porn_Fetch_widget.setStyleSheet(u"QWidget {\n"
"color: white;\n"
"background-color: rgb(60, 60, 60);\n"
Expand Down Expand Up @@ -1777,10 +1777,10 @@ def setupUi(self, Porn_Fetch_widget):

self.retranslateUi(Porn_Fetch_widget)

self.stackedWidget_3.setCurrentIndex(1)
self.stackedWidget_3.setCurrentIndex(2)
self.stackedWidget_2.setCurrentIndex(0)
self.stackedWidget.setCurrentIndex(0)
self.stacked_widget_metadata.setCurrentIndex(0)
self.stacked_widget_metadata.setCurrentIndex(1)


QMetaObject.connectSlotsByName(Porn_Fetch_widget)
Expand Down Expand Up @@ -1892,15 +1892,15 @@ def retranslateUi(self, Porn_Fetch_widget):
self.button_get_metadata.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Get metadata", None))
self.groupBox_6.setTitle(QCoreApplication.translate("Porn_Fetch_widget", u"User Metadata", None))
self.button_user_information.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Get Information", None))
self.button_download_avatar.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Not implemented", None))
self.button_download_avatar.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Get Avatar", None))
self.button_user_biography.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Get User's Biography", None))
self.label_user_avatar.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Avatar:", None))
self.label_user_relationship.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Relationship: ", None))
self.label_user_ethnicity.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Ethnicity: ", None))
self.label_user_name.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Name: ", None))
self.label_user_video_views.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Video views:", None))
self.label_user_hair_color.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Hair color: ", None))
self.label_user_videos_watched.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Videos watched;", None))
self.label_user_videos_watched.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Videos watched:", None))
self.label_user_profile_views.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Profile views: ", None))
self.label_user_hobbies.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Hobbies:", None))
self.label_user_interested_in.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Interested in: ", None))
Expand All @@ -1910,7 +1910,7 @@ def retranslateUi(self, Porn_Fetch_widget):
self.label_user_gender.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Gender:", None))
self.label_user_tattoos.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Tattoos:", None))
self.label_user_turn_ons.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Turn ons:", None))
self.lineedit_user_avatar.setPlaceholderText(QCoreApplication.translate("Porn_Fetch_widget", u"Not implemented yet", None))
self.lineedit_user_avatar.setPlaceholderText("")
self.label_user_url.setText(QCoreApplication.translate("Porn_Fetch_widget", u"User URL: ", None))
self.button_switch_video_metadata.setText(QCoreApplication.translate("Porn_Fetch_widget", u"Video", None))
self.button_switch_user_metadata.setText(QCoreApplication.translate("Porn_Fetch_widget", u"User", None))
Expand Down
Loading

0 comments on commit 77f26f3

Please sign in to comment.