diff --git a/README.md b/README.md index b741cd7..bceb9a0 100644 --- a/README.md +++ b/README.md @@ -46,15 +46,6 @@ We help you download at the fastest possible speed without having to wait for ea



-

- -

- -

- Supports multiple simultaneous downloads and can be increased or decreased depending on the environment. -

-
-
## 😼 Introduction to key features @@ -66,8 +57,6 @@ We help you download at the fastest possible speed without having to wait for ea ⭐ In addition to the `1ficher` link, when you directly enter a shortened `URL` such as `ouo.io`, an automatic link that bypasses `reCAPTCHA` is added. -⭐ Supports simultaneous proxy downloads using `Threading` (basic 3 experimental) - ⭐ The default download folder path is the Windows ‘Download’ folder. _Your life is short. Don't wait any longer._ @@ -115,6 +104,7 @@ _Your life is short. Don't wait any longer._ - Added exception handling when adding duplicate downloads with the same file name (processing required after actual 1fichier URL parsing) - Supports asynchronous download using `Asyncio` instead of `Threading`, which is slow when downloading simultaneously (increases speed) - In addition to the basic specification ‘1ficher’, it is necessary to check whether programs from other similar download sites are supported. +- Supports simultaneous proxy downloads using `Threading`

diff --git a/build command.txt b/build command.txt new file mode 100644 index 0000000..aa83a89 --- /dev/null +++ b/build command.txt @@ -0,0 +1 @@ +pyinstaller --windowed --noconsole --onefile --noconfirm --clean --hiddenimport=_cffi_backend --additional-hooks-dir=. --icon=core/gui/res/ico.ico --add-data "core/gui/res/*.*;res/" .\1fichier-dl.py \ No newline at end of file diff --git a/core/download/helpers.py b/core/download/helpers.py index 1a4030a..a97c004 100644 --- a/core/download/helpers.py +++ b/core/download/helpers.py @@ -126,7 +126,7 @@ def get_link_info(url: str) -> list: return [name, size] except: logging.debug(__name__+' Exception') - return None + return ['Error', '- MB'] def is_valid_link(url: str) -> bool: diff --git a/core/download/workers.py b/core/download/workers.py index 6835d5f..69ec87a 100644 --- a/core/download/workers.py +++ b/core/download/workers.py @@ -36,90 +36,115 @@ def __init__(self, actions, cached_download='', password=''): @pyqtSlot() def run(self): self.valid_links = [] - - if isinstance(self.links, str): - self.valid_links = [self.links] - else: - links = self.links.toPlainText().splitlines() - for link in links: - logging.debug('valid_links:'+str(link.strip())) - # If the shortened URL is ouo bypass, recaptcha bypass - if 'ouo.io' in link: - link = ouo_bypass(url=link)['bypassed_link'] - logging.debug('bypassed link: ' + str(link)) - - link = link.strip() - if is_valid_link(link): - if not 'https://' in link[0:8] and not 'http://' in link[0:7]: - link = f'https://{link}' - if '&' in link: - link = link.split('&')[0] - self.valid_links.append(link) - - if not self.valid_links: - self.signals.alert_signal.emit( - 'The link(s) you inserted were not valid.') + self.invalid_links = [] + + try: + if self.links.toPlainText(): + links = self.links.toPlainText().splitlines() + for link in links: + link = link.strip() + logging.debug('Processing link: ' + str(link)) + # If the shortened URL is ouo bypass, recaptcha bypass + try: + if 'ouo.io' in link: + bypassed = ouo_bypass(url=link) + link = bypassed['bypassed_link'] + logging.debug('Bypassed link: ' + str(link)) + except Exception as e: + logging.error(f"Failed to bypass ouo.io link {link}: {e}") + self.invalid_links.append(link) + continue + + # Link validation + try: + if is_valid_link(link): + if not (link.startswith('https://') or link.startswith('http://')): + link = f'https://{link}' + link = link.split('&')[0] + self.valid_links.append(link) + else: + raise ValueError(f'Invalid link format: {link}') + except ValueError as ve: + logging.warning(ve) + self.invalid_links.append(link) + self.signals.alert_signal.emit(f'Invalid link format: {link}') + continue # Continue to next link + + if len(self.invalid_links) > 0 : self.gui.hide_loading_overlay() # Reset link input window self.gui.add_btn.setEnabled(True) self.gui.links.setEnabled(True) self.gui.password.setEnabled(True) - - for link in self.valid_links: - if '/dir/' in link: - folder = requests.get(f'{link}?json=1') - folder = folder.json() - for f in folder: - link = f['link'] - info = [f['filename'], convert_size(int(f['size']))] - info.extend(['Added', None, '0 B/s', '']) - row = [] - - for val in info: - data = QStandardItem(val) - data.setFlags(data.flags() & ~Qt.ItemIsEditable) - row.append(data) - - if f['password'] == 1: - password = QStandardItem(self.password) - row.append(password) - self.gui.hide_loading_overlay() - else: - no_password = QStandardItem('No password') - no_password.setFlags(data.flags() & ~Qt.ItemIsEditable) - row.append(no_password) - - self.signals.download_signal.emit( - row, link, True, self.dl_name, self.progress) - if self.cached_download: - self.cached_downloads.remove(self.cached_download) - else: - info = get_link_info(link) - if info is not None: - is_private = True if info[0] == 'Private File' else False - info[0] = self.dl_name if self.dl_name else info[0] - info.extend(['Added', None, '0 B/s', '']) - row = [] - - for val in info: - data = QStandardItem(val) - data.setFlags(data.flags() & ~Qt.ItemIsEditable) - row.append(data) - - if is_private: - password = QStandardItem(self.password) - row.append(password) - self.gui.hide_loading_overlay() - else: - no_password = QStandardItem('No password') - no_password.setFlags(data.flags() & ~Qt.ItemIsEditable) - row.append(no_password) - - self.signals.download_signal.emit( - row, link, True, self.dl_name, self.progress) - if self.cached_download: - self.cached_downloads.remove(self.cached_download) - + # 링크 추가 문구 원복 + self.gui.add_links_complete() + else : + for link in self.valid_links: + try: + if '/dir/' in link: + folder = requests.get(f'{link}?json=1') + folder = folder.json() + for f in folder: + link = f['link'] + info = [f['filename'], convert_size(int(f['size']))] + info.extend(['Waiting', None, '0 B/s', '']) + row = [] + + for val in info: + data = QStandardItem(val) + data.setFlags(data.flags() & ~Qt.ItemIsEditable) + row.append(data) + + if f['password'] == 1: + password = QStandardItem(self.password) + row.append(password) + self.gui.hide_loading_overlay() + else: + no_password = QStandardItem('No password') + no_password.setFlags(data.flags() & ~Qt.ItemIsEditable) + row.append(no_password) + + self.signals.download_signal.emit( + row, link, True, self.dl_name, self.progress) + if self.cached_download: + self.cached_downloads.remove(self.cached_download) + else: + info = get_link_info(link) + if info is not None: + # parsing Avoid errors + if info[0] == 'Error': + self.signals.alert_signal.emit(f'We couldn\'t get the actual information for the file to download.\n{link}') + self.gui.hide_loading_overlay() + else: + is_private = True if info[0] == 'Private File' else False + info[0] = self.dl_name if self.dl_name else info[0] + info.extend(['Waiting', None, '0 B/s', '']) + row = [] + + for val in info: + data = QStandardItem(val) + data.setFlags(data.flags() & ~Qt.ItemIsEditable) + row.append(data) + + if is_private: + password = QStandardItem(self.password) + row.append(password) + self.gui.hide_loading_overlay() + else: + no_password = QStandardItem('No password') + no_password.setFlags(data.flags() & ~Qt.ItemIsEditable) + row.append(no_password) + + self.signals.download_signal.emit( + row, link, True, self.dl_name, self.progress) + if self.cached_download: + self.cached_downloads.remove(self.cached_download) + except Exception as e: + logging.error(f"Error processing link {link}: {e}") + continue + + except Exception as e: + logging.error(f"Unexpected error in run method: {e}") class DownloadWorker(QRunnable): def __init__(self, link, table_model, data, settings, dl_name=''): diff --git a/core/gui/gui.py b/core/gui/gui.py index 0f81b66..d6f480d 100644 --- a/core/gui/gui.py +++ b/core/gui/gui.py @@ -99,7 +99,7 @@ def __init__(self, gui): self.filter_thread = QThreadPool() self.download_thread = QThreadPool() # Limits concurrent downloads to 1. - self.download_thread.setMaxThreadCount(3) + self.download_thread.setMaxThreadCount(1) self.download_workers = [] self.gui = gui self.handle_init() @@ -283,19 +283,47 @@ def change_theme(self, theme=None): qdarktheme.setup_theme("dark") # self.gui.app.setPalette(dark_theme) + def get_language(self): + language = os.getenv('LANGUAGE') or 'en' # Get language from environment variable + return language + + def set_language(self, language=None): + if language: + self.gui.theme_select.setCurrentIndex(language) + + if(self.gui.theme_select.currentIndex()) : + return 'kr' + else : + return 'en' + + def load_messages(self, language): + messages = {} + messages_file = f'messages_{language}.txt' + + with open(messages_file, 'r', encoding='utf-8') as f: + for line in f: + key, value = line.strip().split(',') + messages[key] = value + + return messages + def save_settings(self): with open(abs_config('app/settings'), 'wb') as f: settings = [] - settings.append(self.gui.dl_directory_input.text()) # Download Directory - 0 + settings.append(self.gui.dl_directory_input.text()) # Theme - 1 settings.append(self.gui.theme_select.currentIndex()) - settings.append(self.gui.timeout_input.value()) # Timeout - 2 + settings.append(self.gui.timeout_input.value()) # Proxy Settings - 3 settings.append(self.gui.proxy_settings_input.text()) - # Number of multi downloads - settings.append(self.gui.thread_input.value()) + # Thread Settings - 4 + settings.append(1) + # settings.append(self.gui.thread_input.value()) + # Select language + # Lang Settings - 5 + # settings.append(self.gui.lang_select.currentIndex()) pickle.dump(settings, f) self.settings = settings self.gui.settings.hide() @@ -549,6 +577,15 @@ def settings_win(self): vbox.setAlignment(Qt.AlignTop) form_layout = QFormLayout() + # Change Lang + # form_layout.addRow(QLabel('Language:')) + + # self.lang_select = QComboBox() + # self.lang_select.addItems(['Korean', 'English']) + # self.lang_select.currentIndexChanged.connect( + # self.actions.set_language) + # form_layout.addRow(self.lang_select) + # Change Directory form_layout.addRow(QLabel('Download directory:')) @@ -594,14 +631,14 @@ def settings_win(self): form_layout_c = QFormLayout() # Timeout - form_layout_c.addRow(QLabel('Timeout (Default 30s):')) - self.timeout_input = QSpinBox() - if self.actions.settings is not None: - self.timeout_input.setValue(self.actions.settings[2]) - else: - self.timeout_input.setValue(30) + # form_layout_c.addRow(QLabel('Timeout (Default 30s):')) + # self.timeout_input = QSpinBox() + # if self.actions.settings is not None: + # self.timeout_input.setValue(self.actions.settings[2]) + # else: + # self.timeout_input.setValue(30) - form_layout_c.addRow(self.timeout_input) + # form_layout_c.addRow(self.timeout_input) # Proxy settings form_layout_c.addRow(QLabel('Enter proxy list directly:')) @@ -668,17 +705,24 @@ def add_links_complete(self): self.links.clear() def add_to_download_list(self): - # Disable the 'Add to download list' button. - self.add_btn.setText("Adding to download list...") - self.add_btn.setEnabled(False) - # It takes the entered link and converts it into a list. links_text = self.links.toPlainText() - download_links = links_text.split('\n') - self.links.setDisabled(True) - self.password.setDisabled(True) - - for link in download_links: - if link.strip(): # Append only if the line is not blank. - # Use 'add_links' to add download links. - self.actions.add_links(link) + + if(links_text) : + add_links_texts = [] + # Disable the 'Add to download list' button. + self.add_btn.setText("Adding to download list...") + self.add_btn.setEnabled(False) + + download_links = links_text.split('\n') + self.links.setDisabled(True) + self.password.setDisabled(True) + + for link in download_links: + if link.strip(): # Append only if the line is not blank. + add_links_texts.append(link) + + # Use 'add_links' to add download links. + self.actions.add_links('\n'.join(add_links_texts)) + else: + alert('Please enter a list of download links.') diff --git a/https_proxy_list.txt b/https_proxy_list.txt index 7b74c3f..ccf8796 100644 --- a/https_proxy_list.txt +++ b/https_proxy_list.txt @@ -1,4 +1,5 @@ +https://raw.githubusercontent.com/r00tee/Proxy-List/main/Https.txt +https://raw.githubusercontent.com/r00tee/Proxy-List/main/Socks5.txt https://raw.githubusercontent.com/Zaeem20/FREE_PROXIES_LIST/master/https.txt -https://raw.githubusercontent.com/roosterkid/openproxylist/main/HTTPS_RAW.txt -https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/https.txt -https://raw.githubusercontent.com/casals-ar/proxy.casals.ar/main/https +https://raw.githubusercontent.com/SevenworksDev/proxy-list/main/proxies/https.txt +https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/https.txt \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8e3f47e..2ba1341 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ -PyQt5==5.15.9 +PyQt5 lxml==4.9.3 curl_cffi==0.5.7 urllib3==2.0.4 requests beautifulsoup4 pyqtdarktheme +pyinstaller==5.13.2 #pyinstaller==5.13.0 # python -m pip uninstall pyinstalle, python ./waf all --target-arch=64bit \ No newline at end of file