A Fast and Safe Download Manager
#!/usr/bin/env python3
# coding: utf-8
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


import os
import warnings
import sys
import platform
import shutil
from persepolis.constants import OS

# finding os platform
os_type = platform.system()

if os_type == 'Linux' or os_type == 'FreeBSD' or os_type == 'OpenBSD':
    print(os_type + " detected!")
else:
    print('This script is only work for GNU/Linux or BSD!')
    sys.exit(1)


# finding current directory
cwd = os.path.abspath(__file__)
setup_dir = os.path.dirname(cwd)

# clearing __pycache__
src_pycache = os.path.join(setup_dir, 'persepolis', '__pycache__')
gui_pycache = os.path.join(setup_dir, 'persepolis', 'gui', '__pycache__')
scripts_pycache = os.path.join(setup_dir, 'persepolis', 'scripts', '__pycache__')
constants_pycache = os.path.join(setup_dir, 'persepolis', 'constants', '__pycache__')

for folder in [src_pycache, gui_pycache, scripts_pycache, constants_pycache]:
    if os.path.isdir(folder):
        shutil.rmtree(folder)
        print(str(folder)
              + ' is removed!')


uid = os.getuid()
if uid != 0:
    print('Run this script as root\n\
    if you want to clean unwanted files that created by setup tools')
    sys.exit(1)


# finding current directory
cwd = os.path.abspath(__file__)
setup_dir = os.path.dirname(cwd)

# clearing __pycache__
src_pycache = os.path.join(setup_dir, 'persepolis', '__pycache__')
gui_pycache = os.path.join(setup_dir, 'persepolis', 'gui', '__pycache__')
scripts_pycache = os.path.join(setup_dir, 'persepolis', 'scripts', '__pycache__')
constants_pycache = os.path.join(setup_dir, 'persepolis', 'constants', '__pycache__')

for folder in [src_pycache, gui_pycache, scripts_pycache, constants_pycache]:
    if os.path.isdir(folder):
        shutil.rmtree(folder)
        print(str(folder)
              + ' is removed!')

# clear unwanted files!
for folder in ['build', 'dist', 'root', 'persepolis.egg-info']:
    if os.path.isdir(folder):
        shutil.rmtree(folder)
        print(str(folder)
              + ' is removed!')

man_page = 'man/persepolis.1.gz'
if os.path.isfile(man_page):
    os.remove('man/persepolis.1.gz')

clear.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.


# this file is created for building persepolis with pyinstaller.
from persepolis.scripts import persepolis

persepolis.main()

persepolis/Persepolis Download Manager.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.


# import sys
# sys.path.insert(1, '.')

persepolis/__init__.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.


from persepolis.scripts import persepolis

persepolis.main()

persepolis/__main__.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


class BROWSER:
    CHROME = 'chrome'
    CHROMIUM = 'chromium'
    OPERA = 'opera'
    VIVALDI = 'vivaldi'
    FIREFOX = 'firefox'
    BRAVE = 'brave'
    CHROME_FAMILY = [CHROME, CHROMIUM, VIVALDI, OPERA, BRAVE]
    LIST = [CHROME, CHROMIUM, OPERA, VIVALDI, FIREFOX, BRAVE]

persepolis/constants/Browser.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

class OS:
    LINUX = 'Linux'
    WINDOWS = 'Windows'
    FREE_BSD = 'FreeBSD'
    OPEN_BSD = 'OpenBSD'
    OSX = DARWIN = 'Darwin'
    BSD_FAMILY = [FREE_BSD, OPEN_BSD]
    UNIX_LIKE = BSD_FAMILY + [LINUX]
    LIST = [LINUX, WINDOWS, FREE_BSD, OPEN_BSD, OSX]

persepolis/constants/Os.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


# import sys
# sys.path.insert(1, '.')

from .Os import OS
from .Browser import BROWSER

APP_NAME = "ghermez"
ORG_NAME = "ghermez_download_manager"
LONG_NAME = "Ghermez Download Manager"
VERSION = "0.0.1"
LICENSE = "GPL-v3"
REPO_LINK = "https://github.com/IamRezaMousavi/ghermez"

persepolis/constants/__init__.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


# import sys
# sys.path.insert(1, '.')

persepolis/gui/__init__.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
try:
    from PySide6.QtWidgets import QWidget, QTabWidget, QHBoxLayout, QVBoxLayout, QLabel, QTextEdit, QPushButton
    from PySide6.QtCore import Qt, QSize, QTranslator, QCoreApplication, QLocale
    from PySide6.QtGui import QIcon, QFont
    pyside6_is_installed = True
except ImportError:
    from PyQt5.QtWidgets import QWidget, QTabWidget, QHBoxLayout, QVBoxLayout, QLabel, QTextEdit, QPushButton
    from PyQt5.QtCore import Qt, QSize, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtGui import QIcon, QFont
    pyside6_is_installed = False

from persepolis.constants import APP_NAME, LONG_NAME, VERSION, REPO_LINK
from persepolis.gui import resources # noqa: F401

if pyside6_is_installed:
    try:
        from PySide6 import QtSvgWidget
        qtsvg_available = True
    except ImportError:
        qtsvg_available = False
else:
    try:
        from PyQt5 import QtSvg as QtSvgWidget
        qtsvg_available = True
    except ImportError:
        qtsvg_available = False
qtsvg_available = False


class AboutWindow_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()

        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        self.setMinimumSize(QSize(545, 375))
        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))

        verticalLayout = QVBoxLayout(self)

        self.about_tabWidget = QTabWidget(self)

        # about tab
        self.about_tab = QWidget(self)

        about_tab_horizontalLayout = QHBoxLayout(self.about_tab)

        about_tab_verticalLayout = QVBoxLayout()

        # persepolis icon
        if qtsvg_available:
            persepolis_icon_verticalLayout = QVBoxLayout()
            self.persepolis_icon = QtSvgWidget.QSvgWidget(':/persepolis.svg')
            self.persepolis_icon.setFixedSize(QSize(64, 64))

            persepolis_icon_verticalLayout.addWidget(self.persepolis_icon)
            persepolis_icon_verticalLayout.addStretch(1)

            about_tab_horizontalLayout.addLayout(persepolis_icon_verticalLayout)

        self.title_label = QLabel(self.about_tab)
        font = QFont()
        font.setBold(True)
        font.setWeight(QFont.Weight.Bold)
        self.title_label.setFont(font)
        self.title_label.setAlignment(Qt.AlignCenter)
        about_tab_verticalLayout.addWidget(self.title_label)

        self.version_label = QLabel(self.about_tab)
        self.version_label.setAlignment(Qt.AlignCenter)

        about_tab_verticalLayout.addWidget(self.version_label)

        self.site2_label = QLabel(self.about_tab)
        self.site2_label.setTextFormat(Qt.RichText)
        self.site2_label.setAlignment(Qt.AlignCenter)
        self.site2_label.setOpenExternalLinks(True)
        self.site2_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        about_tab_verticalLayout.addWidget(self.site2_label)

        self.telegram_label = QLabel(self.about_tab)
        self.telegram_label.setTextFormat(Qt.RichText)
        self.telegram_label.setAlignment(Qt.AlignCenter)
        self.telegram_label.setOpenExternalLinks(True)
        self.telegram_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        about_tab_verticalLayout.addWidget(self.telegram_label)

        self.twitter_label = QLabel(self.about_tab)
        self.twitter_label.setTextFormat(Qt.RichText)
        self.twitter_label.setAlignment(Qt.AlignCenter)
        self.twitter_label.setOpenExternalLinks(True)
        self.twitter_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        about_tab_verticalLayout.addWidget(self.twitter_label)

        about_tab_verticalLayout.addStretch(1)

        about_tab_horizontalLayout.addLayout(about_tab_verticalLayout)

        # developers_tab
        # developers
        self.developers_tab = QWidget(self)
        developers_verticalLayout = QVBoxLayout(self.developers_tab)

        self.developers_title_label = QLabel(self.developers_tab)
        font.setBold(True)
        font.setWeight(QFont.Weight.Bold)
        self.developers_title_label.setFont(font)
        self.developers_title_label.setAlignment(Qt.AlignCenter)
        developers_verticalLayout.addWidget(self.developers_title_label)

        self.name_label = QLabel(self.developers_tab)
        self.name_label.setAlignment(Qt.AlignCenter)

        developers_verticalLayout.addWidget(self.name_label)

        # contributors
        self.contributors_thank_label = QLabel(self.developers_tab)
        self.contributors_thank_label.setFont(font)
        self.contributors_thank_label.setAlignment(Qt.AlignCenter)

        developers_verticalLayout.addWidget(self.contributors_thank_label)

        self.contributors_link_label = QLabel(self.developers_tab)
        self.contributors_link_label.setTextFormat(Qt.RichText)
        self.contributors_link_label.setAlignment(Qt.AlignCenter)
        self.contributors_link_label.setOpenExternalLinks(True)
        self.contributors_link_label.setTextInteractionFlags(
            Qt.TextBrowserInteraction)
        developers_verticalLayout.addWidget(self.contributors_link_label)

        developers_verticalLayout.addStretch(1)

        # translators tab
        self.translators_tab = QWidget(self)
        translators_tab_verticalLayout = QVBoxLayout(self.translators_tab)

        # translators
        self.translators_textEdit = QTextEdit(self.translators_tab)
        self.translators_textEdit.setReadOnly(True)
        translators_tab_verticalLayout.addWidget(self.translators_textEdit)

        # License tab
        self.license_tab = QWidget(self)
        license_tab_verticalLayout = QVBoxLayout(self.license_tab)

        self.license_text = QTextEdit(self.license_tab)
        self.license_text.setReadOnly(True)

        license_tab_verticalLayout.addWidget(self.license_text)

        verticalLayout.addWidget(self.about_tabWidget)

        # buttons
        button_horizontalLayout = QHBoxLayout()
        button_horizontalLayout.addStretch(1)

        self.pushButton = QPushButton(self)
        self.pushButton.setIcon(QIcon(icons + 'ok'))
        self.pushButton.clicked.connect(self.close)

        button_horizontalLayout.addWidget(self.pushButton)

        verticalLayout.addLayout(button_horizontalLayout)

        self.setWindowTitle(QCoreApplication.translate("about_ui_tr", f"About {APP_NAME.capitalize()}"))

        # about_tab
        self.title_label.setText(QCoreApplication.translate("about_ui_tr", LONG_NAME))
        self.version_label.setText(QCoreApplication.translate("about_ui_tr", f"Version {VERSION}"))
        self.site2_label.setText(
            QCoreApplication.translate("about_ui_tr",
                                       f"<a href={REPO_LINK}>{REPO_LINK}</a>",
                                       "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!")
        )

        # self.telegram_label.setText(
        #     QCoreApplication.translate("about_ui_tr",
        #                                "<a href=https://telegram.me/persepolisdm>https://telegram.me/persepolisdm</a>",
        #                                "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!")
        # )

        # self.twitter_label.setText(
        #     QCoreApplication.translate("about_ui_tr",
        #                                "<a href=https://twitter.com/persepolisdm>https://twitter.com/persepolisdm</a>",
        #                                "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!")
        # )

        # developers_tab
        self.developers_title_label.setText(QCoreApplication.translate('about_ui_tr', 'Developers:'))

        self.name_label.setText(
            QCoreApplication.translate("about_ui_tr",
                                       "Reza Mousavi\nAliReza AmirSamimi\nMohammadreza Abdollahzadeh\nSadegh Alirezaie\nMostafa Asadi\nMohammadAmin Vahedinia\nJafar Akhondali\nH.Rostami\nEhsan Titish",  # noqa: E501
                                       "TRANSLATORS NOTE: YOU REALLY DON'T NEED TO TRANSLATE THIS PART!")
        )

        self.contributors_thank_label.setText(QCoreApplication.translate('about_ui_tr', 'Special thanks to:'))
        self.contributors_link_label.setText(
            f"<a href={REPO_LINK}/graphs/contributors>our contributors</a>")

        # License
        self.license_text.setPlainText("""
            This program is free software: you can redistribute it and/or modify
            it under the terms of the GNU General Public License as published by
            the Free Software Foundation, either version 3 of the License, or
            (at your option) any later version.

            This program is distributed in the hope that it will be useful,
            but WITHOUT ANY WARRANTY; without even the implied warranty of
            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
            GNU General Public License for more details.

            You should have received a copy of the GNU General Public License
            along with this program.  If not, see http://www.gnu.org/licenses/.
            """)

        # tabs
        self.about_tabWidget.addTab(self.about_tab, QCoreApplication.translate("about_ui_tr", f"About {APP_NAME.capitalize()}"))
        self.about_tabWidget.addTab(self.developers_tab, QCoreApplication.translate("about_ui_tr", "Developers"))
        self.about_tabWidget.addTab(self.translators_tab, QCoreApplication.translate("about_ui_tr", "Translators"))
        self.about_tabWidget.addTab(self.license_tab, QCoreApplication.translate("about_ui_tr", "License"))

        # button
        self.pushButton.setText(QCoreApplication.translate("about_ui_tr", "OK"))

persepolis/gui/about_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

try:
    from PySide6.QtWidgets import (
        QTabWidget, QDoubleSpinBox, QPushButton, QComboBox,
        QSpinBox, QVBoxLayout, QHBoxLayout, QLabel, QWidget,
        QGridLayout, QCheckBox, QFrame, QLineEdit
    )
    from PySide6.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PySide6 import QtCore
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtWidgets import (
        QTabWidget, QDoubleSpinBox, QPushButton, QComboBox,
        QSpinBox, QVBoxLayout, QHBoxLayout, QLabel, QWidget,
        QGridLayout, QCheckBox, QFrame, QLineEdit
    )
    from PyQt5.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PyQt5 import QtCore
    from PyQt5.QtGui import QIcon

from persepolis.constants import APP_NAME
from persepolis.gui import resources # noqa: F401
from persepolis.gui.customized_widgets import MyQDateTimeEdit

class AddLinkWindow_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()
        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        # get icons name
        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        self.setMinimumSize(QtCore.QSize(520, 425))
        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))

        # main layout
        window_verticalLayout = QVBoxLayout()

        # add link tab widget
        self.add_link_tabWidget = QTabWidget(self)
        window_verticalLayout.addWidget(self.add_link_tabWidget)

        # link tab
        self.link_tab = QWidget()

        link_tab_verticalLayout = QVBoxLayout(self.link_tab)
        link_tab_verticalLayout.setContentsMargins(21, 21, 21, 81)

        self.link_frame = QFrame(self.link_tab)
        self.link_frame.setFrameShape(QFrame.StyledPanel)
        self.link_frame.setFrameShadow(QFrame.Raised)

        horizontalLayout_2 = QHBoxLayout(self.link_frame)

        self.link_verticalLayout = QVBoxLayout()

        # link ->
        self.link_horizontalLayout = QHBoxLayout()
        self.link_label = QLabel(self.link_frame)
        self.link_horizontalLayout.addWidget(self.link_label)

        self.link_lineEdit = QLineEdit(self.link_frame)
        self.link_horizontalLayout.addWidget(self.link_lineEdit)

        self.link_verticalLayout.addLayout(self.link_horizontalLayout)

        horizontalLayout_2.addLayout(self.link_verticalLayout)
        link_tab_verticalLayout.addWidget(self.link_frame)

        # add change_name field ->
        self.change_name_horizontalLayout = QHBoxLayout()
        self.change_name_checkBox = QCheckBox(self.link_frame)
        self.change_name_horizontalLayout.addWidget(self.change_name_checkBox)

        self.change_name_lineEdit = QLineEdit(self.link_frame)
        self.change_name_horizontalLayout.addWidget(self.change_name_lineEdit)

        self.link_verticalLayout.addLayout(self.change_name_horizontalLayout)

        # add_category ->
        queue_horizontalLayout = QHBoxLayout()

        self.queue_frame = QFrame(self)
        self.queue_frame.setFrameShape(QFrame.StyledPanel)
        self.queue_frame.setFrameShadow(QFrame.Raised)

        add_queue_horizontalLayout = QHBoxLayout(self.queue_frame)

        self.add_queue_label = QLabel(self.queue_frame)
        add_queue_horizontalLayout.addWidget(self.add_queue_label)

        self.add_queue_comboBox = QComboBox(self.queue_frame)
        add_queue_horizontalLayout.addWidget(self.add_queue_comboBox)

        queue_horizontalLayout.addWidget(self.queue_frame)
        queue_horizontalLayout.addStretch(1)

        self.size_label = QLabel(self)
        queue_horizontalLayout.addWidget(self.size_label)

        link_tab_verticalLayout.addLayout(queue_horizontalLayout)

        link_tab_verticalLayout.addStretch(1)

        self.add_link_tabWidget.addTab(self.link_tab, '')

        # proxy tab
        self.proxy_tab = QWidget(self)

        proxy_verticalLayout = QVBoxLayout(self.proxy_tab)
        proxy_verticalLayout.setContentsMargins(21, 21, 21, 171)

        proxy_horizontalLayout = QHBoxLayout()

        self.proxy_checkBox = QCheckBox(self.proxy_tab)
        self.detect_proxy_pushButton = QPushButton(self.proxy_tab)
        self.detect_proxy_label = QLabel(self.proxy_tab)

        proxy_horizontalLayout.addWidget(self.proxy_checkBox)
        proxy_horizontalLayout.addWidget(self.detect_proxy_label)
        proxy_horizontalLayout.addWidget(self.detect_proxy_pushButton)

        proxy_verticalLayout.addLayout(proxy_horizontalLayout)

        self.proxy_frame = QFrame(self.proxy_tab)
        self.proxy_frame.setFrameShape(QFrame.StyledPanel)
        self.proxy_frame.setFrameShadow(QFrame.Raised)

        gridLayout = QGridLayout(self.proxy_frame)

        self.ip_label = QLabel(self.proxy_frame)
        gridLayout.addWidget(self.ip_label, 0, 0, 1, 1)

        self.ip_lineEdit = QLineEdit(self.proxy_frame)
        self.ip_lineEdit.setInputMethodHints(QtCore.Qt.ImhNone)
        gridLayout.addWidget(self.ip_lineEdit, 0, 1, 1, 1)

        self.port_label = QLabel(self.proxy_frame)
        gridLayout.addWidget(self.port_label, 0, 2, 1, 1)

        self.port_spinBox = QSpinBox(self.proxy_frame)
        self.port_spinBox.setMaximum(65535)
        self.port_spinBox.setSingleStep(1)
        gridLayout.addWidget(self.port_spinBox, 0, 3, 1, 1)

        self.proxy_user_label = QLabel(self.proxy_frame)
        gridLayout.addWidget(self.proxy_user_label, 2, 0, 1, 1)

        self.proxy_user_lineEdit = QLineEdit(self.proxy_frame)
        gridLayout.addWidget(self.proxy_user_lineEdit, 2, 1, 1, 1)

        self.proxy_pass_label = QLabel(self.proxy_frame)
        gridLayout.addWidget(self.proxy_pass_label, 2, 2, 1, 1)

        self.proxy_pass_lineEdit = QLineEdit(self.proxy_frame)
        self.proxy_pass_lineEdit.setEchoMode(QLineEdit.Password)
        gridLayout.addWidget(self.proxy_pass_lineEdit, 2, 3, 1, 1)

        proxy_verticalLayout.addWidget(self.proxy_frame)

        proxy_verticalLayout.addStretch(1)

        self.add_link_tabWidget.addTab(self.proxy_tab, '')

        # more options tab
        self.more_options_tab = QWidget(self)

        more_options_tab_verticalLayout = QVBoxLayout(self.more_options_tab)

        # download Username & Password ->
        download_horizontalLayout = QHBoxLayout()
        download_horizontalLayout.setContentsMargins(-1, 10, -1, -1)

        download_verticalLayout = QVBoxLayout()
        self.download_checkBox = QCheckBox(self.more_options_tab)
        download_verticalLayout.addWidget(self.download_checkBox)

        self.download_frame = QFrame(self.more_options_tab)
        self.download_frame.setFrameShape(QFrame.StyledPanel)
        self.download_frame.setFrameShadow(QFrame.Raised)

        gridLayout_2 = QGridLayout(self.download_frame)

        self.download_user_lineEdit = QLineEdit(self.download_frame)
        gridLayout_2.addWidget(self.download_user_lineEdit, 0, 1, 1, 1)

        self.download_user_label = QLabel(self.download_frame)
        gridLayout_2.addWidget(self.download_user_label, 0, 0, 1, 1)

        self.download_pass_label = QLabel(self.download_frame)
        gridLayout_2.addWidget(self.download_pass_label, 1, 0, 1, 1)

        self.download_pass_lineEdit = QLineEdit(self.download_frame)
        self.download_pass_lineEdit.setEchoMode(QLineEdit.Password)
        gridLayout_2.addWidget(self.download_pass_lineEdit, 1, 1, 1, 1)
        download_verticalLayout.addWidget(self.download_frame)
        download_horizontalLayout.addLayout(download_verticalLayout)

        # select folder ->
        self.folder_frame = QFrame(self.more_options_tab)
        self.folder_frame.setFrameShape(QFrame.StyledPanel)
        self.folder_frame.setFrameShadow(QFrame.Raised)

        gridLayout_3 = QGridLayout(self.folder_frame)

        self.download_folder_lineEdit = QLineEdit(self.folder_frame)
        gridLayout_3.addWidget(self.download_folder_lineEdit, 2, 0, 1, 1)

        self.folder_pushButton = QPushButton(self.folder_frame)
        gridLayout_3.addWidget(self.folder_pushButton, 3, 0, 1, 1)
        self.folder_pushButton.setIcon(QIcon(icons + 'folder'))

        self.folder_checkBox = QCheckBox(self.folder_frame)
        gridLayout_3.addWidget(self.folder_checkBox)

        self.folder_label = QLabel(self.folder_frame)
        self.folder_label.setAlignment(QtCore.Qt.AlignCenter)
        gridLayout_3.addWidget(self.folder_label, 1, 0, 1, 1)
        download_horizontalLayout.addWidget(self.folder_frame)
        more_options_tab_verticalLayout.addLayout(download_horizontalLayout)

        # start time ->
        time_limit_horizontalLayout = QHBoxLayout()
        time_limit_horizontalLayout.setContentsMargins(-1, 10, -1, -1)

        start_verticalLayout = QVBoxLayout()
        self.start_checkBox = QCheckBox(self.more_options_tab)
        start_verticalLayout.addWidget(self.start_checkBox)

        self.start_frame = QFrame(self.more_options_tab)
        self.start_frame.setFrameShape(QFrame.StyledPanel)
        self.start_frame.setFrameShadow(QFrame.Raised)

        horizontalLayout_5 = QHBoxLayout(self.start_frame)

        self.start_time_qDataTimeEdit = MyQDateTimeEdit(self.start_frame)
        self.start_time_qDataTimeEdit.setDisplayFormat('H:mm')
        horizontalLayout_5.addWidget(self.start_time_qDataTimeEdit)

        start_verticalLayout.addWidget(self.start_frame)
        time_limit_horizontalLayout.addLayout(start_verticalLayout)

        # end time ->
        end_verticalLayout = QVBoxLayout()

        self.end_checkBox = QCheckBox(self.more_options_tab)
        end_verticalLayout.addWidget(self.end_checkBox)

        self.end_frame = QFrame(self.more_options_tab)
        self.end_frame.setFrameShape(QFrame.StyledPanel)
        self.end_frame.setFrameShadow(QFrame.Raised)

        horizontalLayout_6 = QHBoxLayout(self.end_frame)

        self.end_time_qDateTimeEdit = MyQDateTimeEdit(self.end_frame)
        self.end_time_qDateTimeEdit.setDisplayFormat('H:mm')
        horizontalLayout_6.addWidget(self.end_time_qDateTimeEdit)

        end_verticalLayout.addWidget(self.end_frame)
        time_limit_horizontalLayout.addLayout(end_verticalLayout)

        # limit Speed ->
        limit_verticalLayout = QVBoxLayout()

        self.limit_checkBox = QCheckBox(self.more_options_tab)
        limit_verticalLayout.addWidget(self.limit_checkBox)

        self.limit_frame = QFrame(self.more_options_tab)
        self.limit_frame.setFrameShape(QFrame.StyledPanel)
        self.limit_frame.setFrameShadow(QFrame.Raised)

        horizontalLayout_4 = QHBoxLayout(self.limit_frame)

        self.limit_spinBox = QDoubleSpinBox(self.limit_frame)
        self.limit_spinBox.setMinimum(1)
        self.limit_spinBox.setMaximum(1023)
        horizontalLayout_4.addWidget(self.limit_spinBox)

        self.limit_comboBox = QComboBox(self.limit_frame)
        self.limit_comboBox.addItem("")
        self.limit_comboBox.addItem("")
        horizontalLayout_4.addWidget(self.limit_comboBox)
        limit_verticalLayout.addWidget(self.limit_frame)
        time_limit_horizontalLayout.addLayout(limit_verticalLayout)
        more_options_tab_verticalLayout.addLayout(time_limit_horizontalLayout)

        # number of connections ->
        connections_horizontalLayout = QHBoxLayout()
        connections_horizontalLayout.setContentsMargins(-1, 10, -1, -1)

        self.connections_frame = QFrame(self.more_options_tab)
        self.connections_frame.setFrameShape(QFrame.StyledPanel)
        self.connections_frame.setFrameShadow(QFrame.Raised)

        horizontalLayout_3 = QHBoxLayout(self.connections_frame)
        self.connections_label = QLabel(self.connections_frame)
        horizontalLayout_3.addWidget(self.connections_label)

        self.connections_spinBox = QSpinBox(self.connections_frame)
        self.connections_spinBox.setMinimum(1)
        self.connections_spinBox.setMaximum(16)
        self.connections_spinBox.setProperty("value", 16)
        horizontalLayout_3.addWidget(self.connections_spinBox)
        connections_horizontalLayout.addWidget(self.connections_frame)
        connections_horizontalLayout.addStretch(1)

        more_options_tab_verticalLayout.addLayout(connections_horizontalLayout)

        more_options_tab_verticalLayout.addStretch(1)

        self.add_link_tabWidget.addTab(self.more_options_tab, '')

        # advance options
        self.advance_options_tab = QWidget(self)

        advance_options_tab_verticalLayout = QVBoxLayout(self.advance_options_tab)

        # referer
        referer_horizontalLayout = QHBoxLayout()

        self.referer_label = QLabel(self.advance_options_tab)
        referer_horizontalLayout.addWidget(self.referer_label)

        self.referer_lineEdit = QLineEdit(self.advance_options_tab)
        referer_horizontalLayout.addWidget(self.referer_lineEdit)

        advance_options_tab_verticalLayout.addLayout(referer_horizontalLayout)

        # header
        header_horizontalLayout = QHBoxLayout()

        self.header_label = QLabel(self.advance_options_tab)
        header_horizontalLayout.addWidget(self.header_label)

        self.header_lineEdit = QLineEdit(self.advance_options_tab)
        header_horizontalLayout.addWidget(self.header_lineEdit)

        advance_options_tab_verticalLayout.addLayout(header_horizontalLayout)

        # user_agent
        user_agent_horizontalLayout = QHBoxLayout()

        self.user_agent_label = QLabel(self.advance_options_tab)
        user_agent_horizontalLayout.addWidget(self.user_agent_label)

        self.user_agent_lineEdit = QLineEdit(self.advance_options_tab)
        user_agent_horizontalLayout.addWidget(self.user_agent_lineEdit)

        advance_options_tab_verticalLayout.addLayout(user_agent_horizontalLayout)

        # load_cookies
        load_cookies_horizontalLayout = QHBoxLayout()

        self.load_cookies_label = QLabel(self.advance_options_tab)
        load_cookies_horizontalLayout.addWidget(self.load_cookies_label)

        self.load_cookies_lineEdit = QLineEdit(self.advance_options_tab)
        load_cookies_horizontalLayout.addWidget(self.load_cookies_lineEdit)

        advance_options_tab_verticalLayout.addLayout(load_cookies_horizontalLayout)

        advance_options_tab_verticalLayout.addStretch(1)

        self.add_link_tabWidget.addTab(self.advance_options_tab, '')

        # ok cancel download_later buttons ->
        buttons_horizontalLayout = QHBoxLayout()
        buttons_horizontalLayout.addStretch(1)

        self.download_later_pushButton = QPushButton(self)
        self.download_later_pushButton.setIcon(QIcon(icons + 'stop'))

        self.cancel_pushButton = QPushButton(self)
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))

        self.ok_pushButton = QPushButton(self)
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))

        buttons_horizontalLayout.addWidget(self.download_later_pushButton)
        buttons_horizontalLayout.addWidget(self.cancel_pushButton)
        buttons_horizontalLayout.addWidget(self.ok_pushButton)

        window_verticalLayout.addLayout(buttons_horizontalLayout)

        self.setLayout(window_verticalLayout)

        # labels ->
        self.setWindowTitle(QCoreApplication.translate("addlink_ui_tr", "Add Download Link"))

        self.link_label.setText(QCoreApplication.translate("addlink_ui_tr", "Download link: "))

        self.add_queue_label.setText(QCoreApplication.translate("addlink_ui_tr", "Add to category: "))

        self.change_name_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "Change file name: "))

        self.detect_proxy_pushButton.setText(
            QCoreApplication.translate("addlink_ui_tr", "Detect System Proxy Settings")
        )
        self.proxy_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "Proxy"))
        self.proxy_pass_label.setText(QCoreApplication.translate("addlink_ui_tr", "Proxy password: "))
        self.ip_label.setText(QCoreApplication.translate("addlink_ui_tr", "IP: "))
        self.proxy_user_label.setText(QCoreApplication.translate("addlink_ui_tr", "Proxy username: "))
        self.port_label.setText(QCoreApplication.translate("addlink_ui_tr", "Port:"))

        self.download_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "Download username and password"))
        self.download_user_label.setText(QCoreApplication.translate("addlink_ui_tr", "Download username: "))
        self.download_pass_label.setText(QCoreApplication.translate("addlink_ui_tr", "Download password: "))

        self.folder_pushButton.setText(QCoreApplication.translate("addlink_ui_tr", "Change Download Folder"))
        self.folder_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "Remember this path"))
        self.folder_label.setText(QCoreApplication.translate("addlink_ui_tr", "Download Folder: "))

        self.start_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "Start time"))
        self.end_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "End time"))

        self.limit_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "Limit speed"))
        self.limit_comboBox.setItemText(0, "KiB/s")
        self.limit_comboBox.setItemText(1, "MiB/s")

        self.connections_label.setText(QCoreApplication.translate("addlink_ui_tr", "Number of connections:"))

        self.cancel_pushButton.setText(QCoreApplication.translate("addlink_ui_tr", "Cancel"))
        self.ok_pushButton.setText(QCoreApplication.translate("addlink_ui_tr", "OK"))

        self.download_later_pushButton.setText(QCoreApplication.translate("addlink_ui_tr", "Download Later"))

        self.add_link_tabWidget.setTabText(self.add_link_tabWidget.indexOf(
            self.link_tab), QCoreApplication.translate("addlink_ui_tr", "Link"))

        self.add_link_tabWidget.setTabText(self.add_link_tabWidget.indexOf(
            self.proxy_tab), QCoreApplication.translate("addlink_ui_tr", "Proxy"))

        self.add_link_tabWidget.setTabText(self.add_link_tabWidget.indexOf(
            self.more_options_tab), QCoreApplication.translate("addlink_ui_tr", "More Options"))

        self.add_link_tabWidget.setTabText(self.add_link_tabWidget.indexOf(
            self.advance_options_tab), QCoreApplication.translate("addlink_ui_tr", "Advanced Options"))

        self.referer_label.setText(QCoreApplication.translate("addlink_ui_tr", 'Referrer: '))

        self.header_label.setText(QCoreApplication.translate("addlink_ui_tr", 'Header: '))

        self.load_cookies_label.setText(QCoreApplication.translate("addlink_ui_tr", 'Load cookies: '))

        self.user_agent_label.setText(QCoreApplication.translate("addlink_ui_tr", 'User agent: '))

persepolis/gui/addlink_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtWidgets import QCheckBox, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit
    from PySide6.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtWidgets import QCheckBox, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit
    from PyQt5.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtGui import QIcon

from persepolis.constants import APP_NAME
from persepolis.gui import resources # noqa: F401

class AfterDownloadWindow_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()

        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        icons = ':/' + str(self.persepolis_setting.value('settings/icons')) + '/'

        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))
        self.setWindowTitle(QCoreApplication.translate("after_download_ui_tr", "Persepolis Download Manager"))

        # complete_label
        window_verticalLayout = QVBoxLayout()
        window_verticalLayout.setContentsMargins(21, 21, 21, 21)

        self.complete_label = QLabel()
        window_verticalLayout.addWidget(self.complete_label)

        # file_name_label
        self.file_name_label = QLabel()
        window_verticalLayout.addWidget(self.file_name_label)

        # size_label
        self.size_label = QLabel()
        window_verticalLayout.addWidget(self.size_label)

        # link
        self.link_label = QLabel()
        window_verticalLayout.addWidget(self.link_label)

        self.link_lineEdit = QLineEdit()
        window_verticalLayout.addWidget(self.link_lineEdit)

        # save_as
        self.save_as_label = QLabel()
        window_verticalLayout.addWidget(self.save_as_label)
        self.save_as_lineEdit = QLineEdit()
        window_verticalLayout.addWidget(self.save_as_lineEdit)

        # open_pushButtun
        button_horizontalLayout = QHBoxLayout()
        button_horizontalLayout.setContentsMargins(10, 10, 10, 10)

        button_horizontalLayout.addStretch(1)
        self.open_pushButtun = QPushButton()
        self.open_pushButtun.setIcon(QIcon(icons + 'file'))
        button_horizontalLayout.addWidget(self.open_pushButtun)

        # open_folder_pushButtun
        self.open_folder_pushButtun = QPushButton()
        self.open_folder_pushButtun.setIcon(QIcon(icons + 'folder'))
        button_horizontalLayout.addWidget(self.open_folder_pushButtun)

        # ok_pushButton
        self.ok_pushButton = QPushButton()
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))
        button_horizontalLayout.addWidget(self.ok_pushButton)

        window_verticalLayout.addLayout(button_horizontalLayout)

        # dont_show_checkBox
        self.dont_show_checkBox = QCheckBox()
        window_verticalLayout.addWidget(self.dont_show_checkBox)

        window_verticalLayout.addStretch(1)

        self.setLayout(window_verticalLayout)

        # labels
        self.open_pushButtun.setText(QCoreApplication.translate("after_download_ui_tr", "  Open File  "))
        self.open_folder_pushButtun.setText(QCoreApplication.translate("after_download_ui_tr", "Open Download Folder"))
        self.ok_pushButton.setText(QCoreApplication.translate("after_download_ui_tr", "   OK   "))
        self.dont_show_checkBox.setText(QCoreApplication.translate(
            "after_download_ui_tr", "Don't show this message again."))
        self.complete_label.setText(QCoreApplication.translate("after_download_ui_tr", "<b>Download Completed!</b>"))
        self.save_as_label.setText(QCoreApplication.translate("after_download_ui_tr", "<b>Save as</b>: "))
        self.link_label.setText(QCoreApplication.translate("after_download_ui_tr", "<b>Link</b>: "))

persepolis/gui/after_download_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
try:
    from PySide6.QtWidgets import QDateTimeEdit
    from PySide6.QtCore import QSettings, Qt
except ImportError:
    from PyQt5.QtWidgets import QDateTimeEdit
    from PyQt5.QtCore import QSettings, Qt

from persepolis.constants import ORG_NAME, APP_NAME

# import persepolis_setting
persepolis_setting = QSettings(ORG_NAME, APP_NAME)

# check ui_direction RTL or LTR
ui_direction = persepolis_setting.value('ui_direction')


class MyQDateTimeEdit(QDateTimeEdit):
    def __init__(self, parent=None):
        super().__init__(parent)

        # change ui direction from rtl to ltr
        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.LeftToRight)


persepolis/gui/customized_widgets.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

try:
    from PySide6.QtWidgets import QWidget, QTextEdit, QVBoxLayout, QHBoxLayout, QPushButton
    from PySide6.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PySide6.QtGui import QIcon
    from PySide6 import QtCore
except ImportError:
    from PyQt5.QtWidgets import QWidget, QTextEdit, QVBoxLayout, QHBoxLayout, QPushButton
    from PyQt5.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtGui import QIcon
    from PyQt5 import QtCore

from persepolis.constants import APP_NAME
from persepolis.gui import resources # noqa: F401

class LogWindow_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()

        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        # finding windows_size
        self.setMinimumSize(QtCore.QSize(620, 300))
        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))

        verticalLayout = QVBoxLayout(self)
        horizontalLayout = QHBoxLayout()
        horizontalLayout.addStretch(1)

# text_edit
        self.text_edit = QTextEdit(self)
        self.text_edit.setReadOnly(True)

        verticalLayout.addWidget(self.text_edit)

# clear_log_pushButton
        self.clear_log_pushButton = QPushButton(self)
        horizontalLayout.addWidget(self.clear_log_pushButton)

# refresh_log_pushButton
        self.refresh_log_pushButton = QPushButton(self)
        self.refresh_log_pushButton.setIcon(QIcon(icons + 'refresh'))
        horizontalLayout.addWidget(self.refresh_log_pushButton)

# report_pushButton
        self.report_pushButton = QPushButton(self)
        self.report_pushButton.setIcon(QIcon(icons + 'about'))
        horizontalLayout.addWidget(self.report_pushButton)

        self.copy_log_pushButton = QPushButton(self)

# copy_log_pushButton
        self.copy_log_pushButton.setIcon(QIcon(icons + 'clipboard'))
        horizontalLayout.addWidget(self.copy_log_pushButton)

# close_pushButton
        self.close_pushButton = QPushButton(self)
        self.close_pushButton.setIcon(QIcon(icons + 'remove'))
        horizontalLayout.addWidget(self.close_pushButton)

        verticalLayout.addLayout(horizontalLayout)

# set labels

        self.setWindowTitle(QCoreApplication.translate("log_window_ui_tr", 'Persepolis Log'))
        self.close_pushButton.setText(QCoreApplication.translate("log_window_ui_tr", 'Close'))
        self.copy_log_pushButton.setText(QCoreApplication.translate("log_window_ui_tr", 'Copy Selected to Clipboard'))
        self.report_pushButton.setText(QCoreApplication.translate("log_window_ui_tr", "Report Issue"))
        self.refresh_log_pushButton.setText(QCoreApplication.translate("log_window_ui_tr", 'Refresh Log Messages'))
        self.clear_log_pushButton.setText(QCoreApplication.translate("log_window_ui_tr", 'Clear Log Messages'))

persepolis/gui/log_window_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
try:
    from PySide6.QtWidgets import (
        QHeaderView, QDoubleSpinBox, QPushButton, QComboBox,
        QMenu, QTreeView, QSplitter, QHBoxLayout, QVBoxLayout,
        QTableWidgetItem, QAbstractItemView, QToolBar, QMenuBar,
        QStatusBar, QTableWidget, QMainWindow, QWidget, QFrame, QCheckBox, QLabel
    )
    from PySide6.QtGui import QShortcut, QAction, QCursor, QIcon, QStandardItemModel
    from PySide6.QtCore import QCoreApplication, QRect, Qt, QTranslator, QLocale
except ImportError:
    from PyQt5.QtWidgets import (
        QHeaderView, QShortcut, QDoubleSpinBox, QPushButton, QComboBox,
        QMenu, QTreeView, QSplitter, QHBoxLayout, QVBoxLayout,
        QTableWidgetItem, QAbstractItemView, QToolBar, QMenuBar, QStatusBar,
        QTableWidget, QAction, QMainWindow, QWidget, QFrame, QCheckBox, QLabel
    )
    from PyQt5.QtGui import QCursor, QIcon, QStandardItemModel
    from PyQt5.QtCore import QCoreApplication, QRect, Qt, QTranslator, QLocale

from persepolis.constants import LONG_NAME
from persepolis.gui import resources # noqa: F401
from persepolis.gui.customized_widgets import MyQDateTimeEdit

# align center for items in download table
class QTableWidgetItem(QTableWidgetItem):
    def __init__(self, input):
        super().__init__(input)
        self.setTextAlignment(0x0004 | 0x0080)


class MenuWidget(QPushButton):
    def __init__(self, parent):
        super().__init__()

        self.parent = parent

        icons = ':/' + \
            str(self.parent.persepolis_setting.value('settings/icons')) + '/'

        # add support for other languages
        locale = str(self.parent.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.parent.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        # creating context menu
        self.menubar = QMenu(self)
        self.setMenu(self.menubar)
        self.setIcon(QIcon(icons + 'menu'))
        self.setStyleSheet("""QPushButton{border: none; background-color: transparent; padding: 0px}""")

        fileMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'File'))
        editMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Edit'))
        viewMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'View'))
        downloadMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Download'))
        queueMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Queue'))
        videoFinderMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Video Finder'))
        helpMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Help'))

        sortMenu = viewMenu.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Sort by'))

        videoFinderMenu.addAction(self.parent.videoFinderAddLinkAction)

        downloadMenu.addAction(self.parent.stopAllAction)

        sortMenu.addAction(self.parent.sort_file_name_Action)

        sortMenu.addAction(self.parent.sort_file_size_Action)

        sortMenu.addAction(self.parent.sort_first_try_date_Action)

        sortMenu.addAction(self.parent.sort_last_try_date_Action)

        sortMenu.addAction(self.parent.sort_download_status_Action)

        viewMenu.addAction(self.parent.trayAction)

        viewMenu.addAction(self.parent.showMenuBarAction)

        viewMenu.addAction(self.parent.showSidePanelAction)

        viewMenu.addAction(self.parent.minimizeAction)

        fileMenu.addAction(self.parent.addlinkAction)

        fileMenu.addAction(self.parent.addtextfileAction)

        downloadMenu.addAction(self.parent.resumeAction)

        downloadMenu.addAction(self.parent.pauseAction)

        downloadMenu.addAction(self.parent.stopAction)

        downloadMenu.addAction(self.parent.propertiesAction)

        downloadMenu.addAction(self.parent.progressAction)

        fileMenu.addAction(self.parent.openFileAction)

        fileMenu.addAction(self.parent.openDownloadFolderAction)

        fileMenu.addAction(self.parent.openDefaultDownloadFolderAction)

        fileMenu.addAction(self.parent.exitAction)

        editMenu.addAction(self.parent.clearAction)

        editMenu.addAction(self.parent.removeSelectedAction)

        editMenu.addAction(self.parent.deleteSelectedAction)

        queueMenu.addAction(self.parent.createQueueAction)

        queueMenu.addAction(self.parent.removeQueueAction)

        queueMenu.addAction(self.parent.startQueueAction)

        queueMenu.addAction(self.parent.stopQueueAction)

        queueMenu.addAction(self.parent.moveUpSelectedAction)

        queueMenu.addAction(self.parent.moveDownSelectedAction)

        editMenu.addAction(self.parent.preferencesAction)

        helpMenu.addAction(self.parent.aboutAction)

        helpMenu.addAction(self.parent.issueAction)

        helpMenu.addAction(self.parent.updateAction)

        helpMenu.addAction(self.parent.logAction)

        helpMenu.addAction(self.parent.helpAction)


# viewMenu submenus
# DownloadTableWidget Class adds QMenu to QTableWidget Class
class DownloadTableWidget(QTableWidget):
    def __init__(self, parent):
        super().__init__()

        # set ui direction
        ui_direction = parent.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        # creating context menu
        self.tablewidget_menu = QMenu(self)
        self.sendMenu = self.tablewidget_menu.addMenu('')
        
        # don't wrap items 
        self.setWordWrap(False)

    def contextMenuEvent(self, event):
        self.tablewidget_menu.popup(QCursor.pos())


# CategoryTreeView Class adds QMenu to QTreeView
class CategoryTreeView(QTreeView):
    def __init__(self, parent):
        super().__init__()

        # set ui direction
        ui_direction = parent.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        # creating context menu
        self.category_tree_menu = QMenu(self)

        # connecting activation  event
        self.activated.connect(parent.categoryTreeSelected)
        self.pressed.connect(parent.categoryTreeSelected)

    def contextMenuEvent(self, event):
        self.category_tree_menu.popup(QCursor.pos())


class MainWindow_Ui(QMainWindow):
    def __init__(self, persepolis_setting):
        super().__init__()
        # MainWindow
        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        self.setWindowTitle(QCoreApplication.translate("mainwindow_ui_tr", LONG_NAME))
        self.setWindowIcon(QIcon(':/ghermez.png'))

        self.centralwidget = QWidget(self)
        self.verticalLayout = QVBoxLayout(self.centralwidget)

        # enable drag and drop
        self.setAcceptDrops(True)

        # frame
        self.frame = QFrame(self.centralwidget)

        # download_table_horizontalLayout
        download_table_horizontalLayout = QHBoxLayout()
        horizontal_splitter = QSplitter(Qt.Horizontal)

        vertical_splitter = QSplitter(Qt.Vertical)

        # category_tree
        self.category_tree_qwidget = QWidget(self)
        category_tree_verticalLayout = QVBoxLayout()
        self.category_tree = CategoryTreeView(self)
        category_tree_verticalLayout.addWidget(self.category_tree)

        self.category_tree_model = QStandardItemModel()
        self.category_tree.setModel(self.category_tree_model)
        category_table_header = [QCoreApplication.translate("mainwindow_ui_tr", 'Category')]

        self.category_tree_model.setHorizontalHeaderLabels(
            category_table_header)
        self.category_tree.header().setStretchLastSection(True)

        self.category_tree.header().setDefaultAlignment(Qt.AlignCenter)

        # queue_panel
        self.queue_panel_widget = QWidget(self)

        queue_panel_verticalLayout_main = QVBoxLayout(self.queue_panel_widget)

        # queue_panel_show_button
        self.queue_panel_show_button = QPushButton(self)

        queue_panel_verticalLayout_main.addWidget(self.queue_panel_show_button)

        # queue_panel_widget_frame
        self.queue_panel_widget_frame = QFrame(self)
        self.queue_panel_widget_frame.setFrameShape(QFrame.StyledPanel)
        self.queue_panel_widget_frame.setFrameShadow(QFrame.Raised)

        queue_panel_verticalLayout_main.addWidget(
            self.queue_panel_widget_frame)

        queue_panel_verticalLayout = QVBoxLayout(self.queue_panel_widget_frame)
        queue_panel_verticalLayout_main.setContentsMargins(50, -1, 50, -1)

        # start_end_frame
        self.start_end_frame = QFrame(self)

        # start time
        start_verticalLayout = QVBoxLayout(self.start_end_frame)
        self.start_checkBox = QCheckBox(self)
        start_verticalLayout.addWidget(self.start_checkBox)

        self.start_frame = QFrame(self)
        self.start_frame.setFrameShape(QFrame.StyledPanel)
        self.start_frame.setFrameShadow(QFrame.Raised)

        start_frame_verticalLayout = QVBoxLayout(self.start_frame)

        self.start_time_qDataTimeEdit = MyQDateTimeEdit(self.start_frame)
        self.start_time_qDataTimeEdit.setDisplayFormat('H:mm')
        start_frame_verticalLayout.addWidget(self.start_time_qDataTimeEdit)

        start_verticalLayout.addWidget(self.start_frame)

        # end time
        self.end_checkBox = QCheckBox(self)
        start_verticalLayout.addWidget(self.end_checkBox)

        self.end_frame = QFrame(self)
        self.end_frame.setFrameShape(QFrame.StyledPanel)
        self.end_frame.setFrameShadow(QFrame.Raised)

        end_frame_verticalLayout = QVBoxLayout(self.end_frame)

        self.end_time_qDateTimeEdit = MyQDateTimeEdit(self.end_frame)
        self.end_time_qDateTimeEdit.setDisplayFormat('H:mm')
        end_frame_verticalLayout.addWidget(self.end_time_qDateTimeEdit)

        start_verticalLayout.addWidget(self.end_frame)

        self.reverse_checkBox = QCheckBox(self)
        start_verticalLayout.addWidget(self.reverse_checkBox)

        queue_panel_verticalLayout.addWidget(self.start_end_frame)

        # limit_after_frame
        self.limit_after_frame = QFrame(self)

        # limit_checkBox
        limit_verticalLayout = QVBoxLayout(self.limit_after_frame)
        self.limit_checkBox = QCheckBox(self)
        limit_verticalLayout.addWidget(self.limit_checkBox)

        # limit_frame
        self.limit_frame = QFrame(self)
        self.limit_frame.setFrameShape(QFrame.StyledPanel)
        self.limit_frame.setFrameShadow(QFrame.Raised)
        limit_verticalLayout.addWidget(self.limit_frame)

        limit_frame_verticalLayout = QVBoxLayout(self.limit_frame)

        # limit_spinBox
        limit_frame_horizontalLayout = QHBoxLayout()
        self.limit_spinBox = QDoubleSpinBox(self)
        self.limit_spinBox.setMinimum(1)
        self.limit_spinBox.setMaximum(1023)
        limit_frame_horizontalLayout.addWidget(self.limit_spinBox)

        # limit_comboBox
        self.limit_comboBox = QComboBox(self)
        self.limit_comboBox.addItem("")
        self.limit_comboBox.addItem("")
        limit_frame_horizontalLayout.addWidget(self.limit_comboBox)
        limit_frame_verticalLayout.addLayout(limit_frame_horizontalLayout)

        # limit_pushButton
        self.limit_pushButton = QPushButton(self)
        limit_frame_verticalLayout.addWidget(self.limit_pushButton)

        # after_checkBox
        self.after_checkBox = QCheckBox(self)
        limit_verticalLayout.addWidget(self.after_checkBox)

        # after_frame
        self.after_frame = QFrame(self)
        self.after_frame.setFrameShape(QFrame.StyledPanel)
        self.after_frame.setFrameShadow(QFrame.Raised)
        limit_verticalLayout.addWidget(self.after_frame)

        after_frame_verticalLayout = QVBoxLayout(self.after_frame)

        # after_comboBox
        self.after_comboBox = QComboBox(self)
        self.after_comboBox.addItem("")

        after_frame_verticalLayout.addWidget(self.after_comboBox)

        # after_pushButton
        self.after_pushButton = QPushButton(self)
        after_frame_verticalLayout.addWidget(self.after_pushButton)

        queue_panel_verticalLayout.addWidget(self.limit_after_frame)
        category_tree_verticalLayout.addWidget(self.queue_panel_widget)

        # keep_awake_checkBox
        self.keep_awake_checkBox = QCheckBox(self)
        queue_panel_verticalLayout.addWidget(self.keep_awake_checkBox)

        self.category_tree_qwidget.setLayout(category_tree_verticalLayout)
        horizontal_splitter.addWidget(self.category_tree_qwidget)

        # download table widget
        self.download_table_content_widget = QWidget(self)
        download_table_content_widget_verticalLayout = QVBoxLayout(
            self.download_table_content_widget)

        # download_table
        self.download_table = DownloadTableWidget(self)
        vertical_splitter.addWidget(self.download_table)

        horizontal_splitter.addWidget(self.download_table_content_widget)

        self.download_table.setColumnCount(13)
        self.download_table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.download_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.download_table.verticalHeader().hide()

        # hide column of GID and column of link.
        self.download_table.setColumnHidden(8, True)
        self.download_table.setColumnHidden(9, True)

        download_table_header = [
            QCoreApplication.translate("mainwindow_ui_tr", 'File Name'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Status'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Size'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Downloaded'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Percentage'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Connections'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Transfer Rate'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Estimated Time Left'),
            'Gid', QCoreApplication.translate("mainwindow_ui_tr", 'Link'),
            QCoreApplication.translate("mainwindow_ui_tr", 'First Try Date'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Last Try Date'),
            QCoreApplication.translate("mainwindow_ui_tr", 'Category')
        ]

        self.download_table.setHorizontalHeaderLabels(download_table_header)

        # fixing the size of download_table when window is Maximized!
        self.download_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Interactive)
        self.download_table.horizontalHeader().setStretchLastSection(True)

        horizontal_splitter.setStretchFactor(0, 3)  # category_tree width
        horizontal_splitter.setStretchFactor(1, 10)  # ratio of tables's width

        # video_finder_widget
        self.video_finder_widget = QWidget(self)
        video_finder_horizontalLayout = QHBoxLayout(self.video_finder_widget)

        self.muxing_pushButton = QPushButton(self)
        self.muxing_pushButton.setIcon(QIcon(icons + 'video_finder'))
        video_finder_horizontalLayout.addWidget(self.muxing_pushButton)
        video_finder_horizontalLayout.addSpacing(20)

        video_audio_verticalLayout = QVBoxLayout()

        self.video_label = QLabel(self)
        video_audio_verticalLayout.addWidget(self.video_label)

        self.audio_label = QLabel(self)
        video_audio_verticalLayout.addWidget(self.audio_label)
        video_finder_horizontalLayout.addLayout(video_audio_verticalLayout)

        status_muxing_verticalLayout = QVBoxLayout()

        self.video_finder_status_label = QLabel(self)
        status_muxing_verticalLayout.addWidget(self.video_finder_status_label)

        self.muxing_status_label = QLabel(self)
        status_muxing_verticalLayout.addWidget(self.muxing_status_label)
        video_finder_horizontalLayout.addLayout(status_muxing_verticalLayout)

        vertical_splitter.addWidget(self.video_finder_widget)

        download_table_content_widget_verticalLayout.addWidget(vertical_splitter)

        download_table_horizontalLayout.addWidget(horizontal_splitter)

        self.frame.setLayout(download_table_horizontalLayout)

        self.verticalLayout.addWidget(self.frame)

        self.setCentralWidget(self.centralwidget)

        # menubar
        self.menubar = QMenuBar(self)
        self.menubar.setGeometry(QRect(0, 0, 600, 24))
        self.setMenuBar(self.menubar)
        fileMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&File'))
        editMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Edit'))
        viewMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&View'))
        downloadMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Download'))
        queueMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Queue'))
        videoFinderMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'V&ideo Finder'))
        helpMenu = self.menubar.addMenu(QCoreApplication.translate("mainwindow_ui_tr", '&Help'))

        # viewMenu submenus
        sortMenu = viewMenu.addMenu(QCoreApplication.translate("mainwindow_ui_tr", 'Sort by'))

        # statusbar
        self.statusbar = QStatusBar(self)
        self.setStatusBar(self.statusbar)
        self.statusbar.showMessage(QCoreApplication.translate("mainwindow_ui_tr", "Persepolis Download Manager"))

        # toolBar
        self.toolBar2 = QToolBar(self)
        self.addToolBar(Qt.TopToolBarArea, self.toolBar2)
        self.toolBar2.setWindowTitle(QCoreApplication.translate("mainwindow_ui_tr", 'Menu'))
        self.toolBar2.setFloatable(False)
        self.toolBar2.setMovable(False)

        self.toolBar = QToolBar(self)
        self.addToolBar(Qt.TopToolBarArea, self.toolBar)
        self.toolBar.setWindowTitle(QCoreApplication.translate("mainwindow_ui_tr", 'Toolbar'))
        self.toolBar.setFloatable(False)
        self.toolBar.setMovable(False)

        #toolBar and menubar and actions
        self.persepolis_setting.beginGroup('settings/shortcuts')

        # videoFinderAddLinkAction
        self.videoFinderAddLinkAction = QAction(
            QIcon(icons + 'video_finder'), QCoreApplication.translate("mainwindow_ui_tr", 'Find Video Links...'),
            self,
            statusTip=QCoreApplication.translate(
                "mainwindow_ui_tr", 'Download video or audio from Youtube, Vimeo, etc.'
            ),
            triggered=self.showVideoFinderAddLinkWindow
        )

        self.videoFinderAddLinkAction_shortcut = QShortcut(self.persepolis_setting.value(
            'video_finder_shortcut'), self, self.showVideoFinderAddLinkWindow)

        videoFinderMenu.addAction(self.videoFinderAddLinkAction)

        # stopAllAction
        self.stopAllAction = QAction(
            QIcon(icons + 'stop_all'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop All Active Downloads'),
            self, statusTip='Stop All Active Downloads', triggered=self.stopAllDownloads
        )
        downloadMenu.addAction(self.stopAllAction)

        # sort_file_name_Action
        self.sort_file_name_Action = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'File Name'), self, triggered=self.sortByName)
        sortMenu.addAction(self.sort_file_name_Action)

        # sort_file_size_Action
        self.sort_file_size_Action = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'File Size'), self, triggered=self.sortBySize)
        sortMenu.addAction(self.sort_file_size_Action)

        # sort_first_try_date_Action
        self.sort_first_try_date_Action = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'First Try Date'), self, triggered=self.sortByFirstTry)
        sortMenu.addAction(self.sort_first_try_date_Action)

        # sort_last_try_date_Action
        self.sort_last_try_date_Action = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'Last Try Date'), self, triggered=self.sortByLastTry)
        sortMenu.addAction(self.sort_last_try_date_Action)

        # sort_download_status_Action
        self.sort_download_status_Action = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'Download Status'), self, triggered=self.sortByStatus)
        sortMenu.addAction(self.sort_download_status_Action)

        # trayAction
        self.trayAction = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'Show System Tray Icon'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Show/Hide system tray icon"),
            triggered=self.showTray
        )
        self.trayAction.setCheckable(True)
        viewMenu.addAction(self.trayAction)

        # showMenuBarAction
        self.showMenuBarAction = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'Show Menubar'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show Menubar'),
            triggered=self.showMenuBar
        )
        self.showMenuBarAction.setCheckable(True)
        viewMenu.addAction(self.showMenuBarAction)

        # showSidePanelAction
        self.showSidePanelAction = QAction(
            QCoreApplication.translate("mainwindow_ui_tr", 'Show Side Panel'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show Side Panel'),
            triggered=self.showSidePanel
        )
        self.showSidePanelAction.setCheckable(True)
        viewMenu.addAction(self.showSidePanelAction)

        # minimizeAction
        self.minimizeAction = QAction(
            QIcon(icons + 'minimize'), QCoreApplication.translate("mainwindow_ui_tr", 'Minimize to System Tray'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Minimize to System Tray"),
            triggered=self.minMaxTray
        )

        self.minimizeAction_shortcut = QShortcut(
            self.persepolis_setting.value('hide_window_shortcut'), self, self.minMaxTray)
        viewMenu.addAction(self.minimizeAction)

        # addlinkAction
        self.addlinkAction = QAction(
            QIcon(icons + 'add'),  QCoreApplication.translate("mainwindow_ui_tr", 'Add New Download Link...'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Add New Download Link"),
            triggered=self.addLinkButtonPressed
        )

        self.addlinkAction_shortcut = QShortcut(self.persepolis_setting.value(
            'add_new_download_shortcut'), self, self.addLinkButtonPressed)
        fileMenu.addAction(self.addlinkAction)

        # importText
        self.addtextfileAction = QAction(
            QIcon(icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Import Links from Text File...'),
            self, statusTip=QCoreApplication.translate(
                "mainwindow_ui_tr", 'Create a text file and put links in it, line by line!'
            ),
            triggered=self.importText
        )

        self.addtextfileAction_shortcut = QShortcut(
            self.persepolis_setting.value('import_text_shortcut'), self, self.importText)

        fileMenu.addAction(self.addtextfileAction)

        # resumeAction
        self.resumeAction = QAction(
            QIcon(icons + 'play'), QCoreApplication.translate("mainwindow_ui_tr", 'Resume Download'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Resume Download"),
            triggered=self.resumeButtonPressed
        )

        downloadMenu.addAction(self.resumeAction)

        # pauseAction
        self.pauseAction = QAction(
            QIcon(icons + 'pause'), QCoreApplication.translate("mainwindow_ui_tr", 'Pause Download'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Pause Download"),
            triggered=self.pauseButtonPressed
        )

        downloadMenu.addAction(self.pauseAction)

        # stopAction
        self.stopAction = QAction(
            QIcon(icons + 'stop'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop Download'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Stop/Cancel Download"),
            triggered=self.stopButtonPressed
        )

        downloadMenu.addAction(self.stopAction)

        # propertiesAction
        self.propertiesAction = QAction(
            QIcon(icons + 'setting'), QCoreApplication.translate("mainwindow_ui_tr", 'Properties'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Properties"),
            triggered=self.propertiesButtonPressed
        )

        downloadMenu.addAction(self.propertiesAction)

        # progressAction
        self.progressAction = QAction(
            QIcon(icons + 'window'), QCoreApplication.translate("mainwindow_ui_tr", 'Progress'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Progress"),
            triggered=self.progressButtonPressed
        )

        downloadMenu.addAction(self.progressAction)

        # openFileAction
        self.openFileAction = QAction(
            QIcon(icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Open File...'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open File...'),
            triggered=self.openFile
        )
        fileMenu.addAction(self.openFileAction)

        # openDownloadFolderAction
        self.openDownloadFolderAction = QAction(
            QIcon(icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open Download Folder'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open Download Folder'),
            triggered=self.openDownloadFolder
        )

        fileMenu.addAction(self.openDownloadFolderAction)

        # openDefaultDownloadFolderAction
        self.openDefaultDownloadFolderAction = QAction(
            QIcon(icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open Default Download Folder'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open Default Download Folder'),
            triggered=self.openDefaultDownloadFolder
        )

        fileMenu.addAction(self.openDefaultDownloadFolderAction)

        # exitAction
        self.exitAction = QAction(
            QIcon(icons + 'exit'), QCoreApplication.translate("mainwindow_ui_tr", 'Exit'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Exit"),
            triggered=self.closeAction
        )

        self.exitAction_shortcut = QShortcut(self.persepolis_setting.value('quit_shortcut'), self, self.closeAction)

        fileMenu.addAction(self.exitAction)

        # clearAction
        self.clearAction = QAction(
            QIcon(icons + 'multi_remove'), QCoreApplication.translate("mainwindow_ui_tr", 'Clear Download List'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Clear all items in download list'),
            triggered=self.clearDownloadList
        )
        editMenu.addAction(self.clearAction)

        # removeSelectedAction
        self.removeSelectedAction = QAction(
            QIcon(icons + 'remove'), QCoreApplication.translate(
                "mainwindow_ui_tr", 'Remove Selected Downloads from List'
            ),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Remove Selected Downloads from List'),
            triggered=self.removeSelected
        )

        self.removeSelectedAction_shortcut = QShortcut(
            self.persepolis_setting.value('remove_shortcut'), self, self.removeSelected)

        editMenu.addAction(self.removeSelectedAction)
        self.removeSelectedAction.setEnabled(False)

        # deleteSelectedAction
        self.deleteSelectedAction = QAction(
            QIcon(icons + 'trash'), QCoreApplication.translate("mainwindow_ui_tr", 'Delete Selected Download Files'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Delete Selected Download Files'),
            triggered=self.deleteSelected
        )

        self.deleteSelectedAction_shortcut = QShortcut(
            self.persepolis_setting.value('delete_shortcut'), self, self.deleteSelected)

        editMenu.addAction(self.deleteSelectedAction)
        self.deleteSelectedAction.setEnabled(False)

        # moveSelectedDownloadsAction
        self.moveSelectedDownloadsAction = QAction(
            QIcon(icons + 'folder'), QCoreApplication.translate(
                "mainwindow_ui_tr", 'Move Selected Download Files to Another Folder...'
            ),
            self, statusTip=QCoreApplication.translate(
                "mainwindow_ui_tr", 'Move Selected Download Files to Another Folder'
            ),
            triggered=self.moveSelectedDownloads
        )

        editMenu.addAction(self.moveSelectedDownloadsAction)
        self.moveSelectedDownloadsAction.setEnabled(False)

        # createQueueAction
        self.createQueueAction = QAction(
            QIcon(icons + 'add_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Create New Queue...'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Create new download queue'),
            triggered=self.createQueue
        )
        queueMenu.addAction(self.createQueueAction)

        # removeQueueAction
        self.removeQueueAction = QAction(
            QIcon(icons + 'remove_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Remove Queue'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Remove this queue'),
            triggered=self.removeQueue
        )
        queueMenu.addAction(self.removeQueueAction)

        # startQueueAction
        self.startQueueAction = QAction(
            QIcon(icons + 'start_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Start this queue'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Start Queue'),
            triggered=self.startQueue
        )

        queueMenu.addAction(self.startQueueAction)

        # stopQueueAction
        self.stopQueueAction = QAction(
            QIcon(icons + 'stop_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop this queue'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Stop Queue'),
            triggered=self.stopQueue
        )

        queueMenu.addAction(self.stopQueueAction)

        # moveUpSelectedAction
        self.moveUpSelectedAction = QAction(
            QIcon(icons + 'multi_up'), QCoreApplication.translate("mainwindow_ui_tr", 'Move Selected Items Up'),
            self, statusTip=QCoreApplication.translate(
                "mainwindow_ui_tr", 'Move currently selected items up by one row'
            ),
            triggered=self.moveUpSelected
        )

        self.moveUpSelectedAction_shortcut = QShortcut(self.persepolis_setting.value(
            'move_up_selection_shortcut'), self, self.moveUpSelected)

        queueMenu.addAction(self.moveUpSelectedAction)

        # moveDownSelectedAction
        self.moveDownSelectedAction = QAction(
            QIcon(icons + 'multi_down'), QCoreApplication.translate("mainwindow_ui_tr", 'Move Selected Items Down'),
            self, statusTip=QCoreApplication.translate(
                "mainwindow_ui_tr", 'Move currently selected items down by one row'
            ),
            triggered=self.moveDownSelected
        )
        self.moveDownSelectedAction_shortcut = QShortcut(self.persepolis_setting.value(
            'move_down_selection_shortcut'), self, self.moveDownSelected)

        queueMenu.addAction(self.moveDownSelectedAction)

        # preferencesAction
        self.preferencesAction = QAction(
            QIcon(icons + 'preferences'), QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'),
            triggered=self.openPreferences, menuRole=QAction.MenuRole.PreferencesRole
        )
        editMenu.addAction(self.preferencesAction)

        # aboutAction
        self.aboutAction = QAction(
            QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'About'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'About'),
            triggered=self.openAbout, menuRole=QAction.MenuRole.AboutRole
        )
        helpMenu.addAction(self.aboutAction)

        # issueAction
        self.issueAction = QAction(
            QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Report an Issue'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Report an issue'),
            triggered=self.reportIssue
        )
        helpMenu.addAction(self.issueAction)

        # updateAction
        self.updateAction = QAction(
            QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Check for Newer Version'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Check for newer release'),
            triggered=self.newUpdate
        )
        helpMenu.addAction(self.updateAction)

        # logAction
        self.logAction = QAction(
            QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Show Log File'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.showLog
        )
        helpMenu.addAction(self.logAction)

        # helpAction
        self.helpAction = QAction(
            QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Help'),
            self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.persepolisHelp
        )
        helpMenu.addAction(self.helpAction)

        self.persepolis_setting.endGroup()

        self.qmenu = MenuWidget(self)

        self.toolBar2.addWidget(self.qmenu)


# labels
        self.queue_panel_show_button.setText(QCoreApplication.translate("mainwindow_ui_tr", "Hide Options"))
        self.start_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "Start Time"))

        self.end_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "End Time"))

        self.reverse_checkBox.setText(QCoreApplication.translate(
            "mainwindow_ui_tr", "Download bottom of\n the list first"))

        self.limit_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "Limit Speed"))
        self.limit_comboBox.setItemText(0, "KiB/s")
        self.limit_comboBox.setItemText(1, "MiB/s")
        self.limit_pushButton.setText(QCoreApplication.translate("mainwindow_ui_tr", "Apply"))

        self.after_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "After download"))
        self.after_comboBox.setItemText(0, QCoreApplication.translate("mainwindow_ui_tr", "Shut Down"))

        self.keep_awake_checkBox.setText(QCoreApplication.translate("mainwindow_ui_tr", "Keep System Awake!"))
        self.keep_awake_checkBox.setToolTip(
            QCoreApplication.translate(
                "mainwindow_ui_tr", "<html><head/><body><p>This option will prevent the system from going to sleep.\
                It is necessary if your power manager is suspending the system automatically. </p></body></html>"
            )
        )

        self.after_pushButton.setText(QCoreApplication.translate("mainwindow_ui_tr", "Apply"))

        self.muxing_pushButton.setText(QCoreApplication.translate("mainwindow_ui_tr", "Start Mixing"))

        self.video_label.setText(QCoreApplication.translate("mainwindow_ui_tr", "<b>Video File Status: </b>"))
        self.audio_label.setText(QCoreApplication.translate("mainwindow_ui_tr", "<b>Audio File Status: </b>"))

        self.video_finder_status_label.setText(QCoreApplication.translate("mainwindow_ui_tr", "<b>Status: </b>"))
        self.muxing_status_label.setText(QCoreApplication.translate("mainwindow_ui_tr", "<b>Mixing status: </b>"))

persepolis/gui/mainwindow_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtGui import QPalette, QColor
    from PySide6.QtCore import Qt
except ImportError:
    from PyQt5.QtGui import QPalette, QColor
    from PyQt5.QtCore import Qt

class DarkFusionPalette(QPalette):
    def __init__(self):
        super().__init__()
        self.setColor(QPalette.Window, QColor(56, 56, 56))

        self.setColor(QPalette.WindowText, Qt.white)

        self.setColor(QPalette.Base, QColor(56, 56, 56))

        self.setColor(QPalette.AlternateBase, QColor(63, 63, 63))
        self.setColor(QPalette.ToolTipBase, Qt.white)
        self.setColor(QPalette.ToolTipText, Qt.white)

        self.setColor(QPalette.Text, Qt.white)

        self.setColor(QPalette.Button, QColor(56, 56, 56))

        self.setColor(QPalette.ButtonText, Qt.white)

        self.setColor(QPalette.BrightText, QColor(0, 128, 152))

        self.setColor(QPalette.Link, QColor(42, 130, 218))

        self.setColor(QPalette.Highlight, QColor(0, 128, 152))

        self.setColor(QPalette.HighlightedText, Qt.white)

        self.setColor(QPalette.Disabled, QPalette.Window, QColor(51, 51, 51))

        self.setColor(QPalette.Disabled, QPalette.ButtonText,
                      QColor(111, 111, 111))

        self.setColor(QPalette.Disabled, QPalette.Text, QColor(122, 118, 113))

        self.setColor(QPalette.Disabled, QPalette.WindowText,
                      QColor(122, 118, 113))

        self.setColor(QPalette.Disabled, QPalette.Base, QColor(32, 32, 32))


class LightFusionPalette(QPalette):
    def __init__(self):
        super().__init__()
        # EFF0F1 
        self.setColor(QPalette.Window, QColor(239, 240, 241))

        self.setColor(QPalette.WindowText, QColor(49, 54, 59))

        self.setColor(QPalette.Base, QColor(239, 240, 241))

        self.setColor(QPalette.AlternateBase, QColor(63, 63, 63))
        self.setColor(QPalette.ToolTipBase, QColor(49, 54, 59))
        self.setColor(QPalette.ToolTipText, QColor(49, 54, 59))

        self.setColor(QPalette.Text, QColor(49, 54, 59))

        self.setColor(QPalette.Button, QColor(239, 240, 241))

        self.setColor(QPalette.ButtonText, QColor(49, 54, 59))

        self.setColor(QPalette.BrightText, QColor(110, 197, 244))

        self.setColor(QPalette.Link, QColor(42, 130, 218))

        self.setColor(QPalette.Highlight, QColor(110, 197, 244))

        self.setColor(QPalette.HighlightedText, QColor(49, 54, 59))

        self.setColor(QPalette.Disabled, QPalette.Window, QColor(227, 227, 227))

        self.setColor(QPalette.Disabled, QPalette.ButtonText,
                      QColor(111, 111, 111))

        self.setColor(QPalette.Disabled, QPalette.Text, QColor(111, 111, 111))

        self.setColor(QPalette.Disabled, QPalette.WindowText,
                      QColor(111, 111, 111))

        self.setColor(QPalette.Disabled, QPalette.Base, QColor(227, 227, 227))


persepolis/gui/palettes.py
IamRezaMousavi/ghermez

# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtWidgets import (
        QCheckBox, QProgressBar, QFrame, QDoubleSpinBox, QComboBox,
        QWidget, QTabWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
    )
    from PySide6.QtCore import Qt, QTranslator, QCoreApplication, QLocale, QSize
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtWidgets import (
        QCheckBox, QProgressBar, QFrame, QDoubleSpinBox, QComboBox,
        QWidget, QTabWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
    )
    from PyQt5.QtCore import Qt, QTranslator, QCoreApplication, QLocale, QSize
    from PyQt5.QtGui import QIcon

from persepolis.constants import APP_NAME, LONG_NAME
from persepolis.gui import resources # noqa: F401

class ProgressWindow_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()
        self.persepolis_setting = persepolis_setting
        icons = ':/' + str(persepolis_setting.value('settings/icons')) + '/'

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)


# window
        self.setMinimumSize(QSize(595, 284))

        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))
        self.setWindowTitle(QCoreApplication.translate("progress_ui_tr", LONG_NAME))

        verticalLayout = QVBoxLayout(self)

# progress_tabWidget
        self.progress_tabWidget = QTabWidget(self)

# information_tab
        self.information_tab = QWidget()
        information_verticalLayout = QVBoxLayout(self.information_tab)

# link_label
        self.link_label = QLabel(self.information_tab)
        information_verticalLayout.addWidget(self.link_label)

# status_label
        self.status_label = QLabel(self.information_tab)
        information_verticalLayout.addWidget(self.status_label)

# downloaded_label
        self.downloaded_label = QLabel(self.information_tab)
        information_verticalLayout.addWidget(self.downloaded_label)

# rate_label
        self.rate_label = QLabel(self.information_tab)
        information_verticalLayout.addWidget(self.rate_label)

# time_label
        self.time_label = QLabel(self.information_tab)
        information_verticalLayout.addWidget(self.time_label)

# connections_label
        self.connections_label = QLabel(self.information_tab)
        information_verticalLayout.addWidget(self.connections_label)

        information_verticalLayout.addStretch(1)

# add information_tab to progress_tabWidget
        self.progress_tabWidget.addTab(self.information_tab, "")

# options_tab
        self.options_tab = QWidget()
        options_tab_verticalLayout = QVBoxLayout(self.options_tab)
        options_tab_horizontalLayout = QHBoxLayout()
#         options_tab_horizontalLayout.setContentsMargins(11, 11, 11, 11)


# limit_checkBox
        self.limit_checkBox = QCheckBox(self.options_tab)

        limit_verticalLayout = QVBoxLayout()
        limit_verticalLayout.addWidget(self.limit_checkBox)

# limit_frame
        self.limit_frame = QFrame(self.options_tab)
        self.limit_frame.setFrameShape(QFrame.StyledPanel)
        self.limit_frame.setFrameShadow(QFrame.Raised)
        limit_frame_verticalLayout = QVBoxLayout(self.limit_frame)
        limit_frame_horizontalLayout = QHBoxLayout()

# limit_spinBox
        self.limit_spinBox = QDoubleSpinBox(self.options_tab)
        self.limit_spinBox.setMinimum(1)
        self.limit_spinBox.setMaximum(1023)
        limit_frame_horizontalLayout.addWidget(self.limit_spinBox)

# limit_comboBox
        self.limit_comboBox = QComboBox(self.options_tab)
        self.limit_comboBox.addItem("")
        self.limit_comboBox.addItem("")

        limit_frame_horizontalLayout.addWidget(self.limit_comboBox)

# limit_pushButton
        self.limit_pushButton = QPushButton(self.options_tab)

        limit_frame_verticalLayout.addLayout(limit_frame_horizontalLayout)
        limit_frame_verticalLayout.addWidget(self.limit_pushButton)

        limit_verticalLayout.addWidget(self.limit_frame)

        limit_verticalLayout.setContentsMargins(11, 11, 11, 11)

        options_tab_horizontalLayout.addLayout(limit_verticalLayout)

        options_tab_verticalLayout.addLayout(options_tab_horizontalLayout)
        options_tab_verticalLayout.addStretch(1)

# after_checkBox
        self.after_checkBox = QCheckBox(self.options_tab)

        after_verticalLayout = QVBoxLayout()
        after_verticalLayout.addWidget(self.after_checkBox)

# after_frame
        self.after_frame = QFrame(self.options_tab)
        self.after_frame.setFrameShape(QFrame.StyledPanel)
        self.after_frame.setFrameShadow(QFrame.Raised)

        after_frame_verticalLayout = QVBoxLayout(self.after_frame)


# after_comboBox
        self.after_comboBox = QComboBox(self.options_tab)
        self.after_comboBox.addItem("")

        after_frame_verticalLayout.addWidget(self.after_comboBox)

# after_pushButton
        self.after_pushButton = QPushButton(self.options_tab)
        after_frame_verticalLayout.addWidget(self.after_pushButton)

        after_verticalLayout.addWidget(self.after_frame)

        after_verticalLayout.setContentsMargins(11, 11, 11, 11)
        options_tab_horizontalLayout.addLayout(after_verticalLayout)

        self.progress_tabWidget.addTab(self.options_tab, "")

        verticalLayout.addWidget(self.progress_tabWidget)


# download_progressBar
        self.download_progressBar = QProgressBar(self)
        verticalLayout.addWidget(self.download_progressBar)
        self.download_progressBar.setTextVisible(False)

# buttons
        button_horizontalLayout = QHBoxLayout()
        button_horizontalLayout.addStretch(1)

# resume_pushButton
        self.resume_pushButton = QPushButton(self)
        self.resume_pushButton.setIcon(QIcon(icons + 'play'))
        button_horizontalLayout.addWidget(self.resume_pushButton)

# pause_pushButton
        self.pause_pushButton = QPushButton(self)
        self.pause_pushButton.setIcon(QIcon(icons + 'pause'))
        button_horizontalLayout.addWidget(self.pause_pushButton)

# stop_pushButton
        self.stop_pushButton = QPushButton(self)
        self.stop_pushButton.setIcon(QIcon(icons + 'stop'))
        button_horizontalLayout.addWidget(self.stop_pushButton)

        verticalLayout.addLayout(button_horizontalLayout)

        self.progress_tabWidget.setCurrentIndex(0)
# labels
        self.link_label.setText(QCoreApplication.translate("progress_ui_tr", "Link: "))
        self.status_label.setText(QCoreApplication.translate("progress_ui_tr", "Status: "))
        self.downloaded_label.setText(QCoreApplication.translate("progress_ui_tr", "Downloaded:"))
        self.rate_label.setText(QCoreApplication.translate("progress_ui_tr", "Transfer rate: "))
        self.time_label.setText(QCoreApplication.translate("progress_ui_tr", "Estimated time left:"))
        self.connections_label.setText(QCoreApplication.translate("progress_ui_tr", "Number of connections: "))
        self.progress_tabWidget.setTabText(self.progress_tabWidget.indexOf(
            self.information_tab),  QCoreApplication.translate("progress_ui_tr", "Download Information"))
        self.limit_checkBox.setText(QCoreApplication.translate("progress_ui_tr", "Limit speed"))
        self.after_checkBox.setText(QCoreApplication.translate("progress_ui_tr", "After download"))
        self.limit_comboBox.setItemText(0, "KiB/s")
        self.limit_comboBox.setItemText(1, "MiB/s")
        self.limit_pushButton.setText(QCoreApplication.translate("progress_ui_tr", "Apply"))

        self.after_comboBox.setItemText(0,  QCoreApplication.translate("progress_ui_tr", "Shut Down"))

        self.progress_tabWidget.setTabText(
            self.progress_tabWidget.indexOf(self.options_tab),
            QCoreApplication.translate("progress_ui_tr", "Download Options")
        )
        self.resume_pushButton.setText(QCoreApplication.translate("progress_ui_tr", "Resume"))
        self.pause_pushButton.setText(QCoreApplication.translate("progress_ui_tr", "Pause"))
        self.stop_pushButton.setText(QCoreApplication.translate("progress_ui_tr", "Stop"))
        self.after_pushButton.setText(QCoreApplication.translate("progress_ui_tr", "Apply"))

persepolis/gui/progress_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtWidgets import (
        QAbstractItemView, QTableWidget, QTableWidgetItem, QCheckBox, QVBoxLayout,QHBoxLayout,
        QFrame, QWidget, QLabel, QLineEdit, QTabWidget, QSpinBox, QPushButton, QDial, QComboBox, QFontComboBox
    )
    from PySide6.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtWidgets import (
        QAbstractItemView, QTableWidget, QTableWidgetItem, QCheckBox, QVBoxLayout, QHBoxLayout,
        QFrame, QWidget, QLabel, QLineEdit, QTabWidget, QSpinBox, QPushButton, QDial, QComboBox, QFontComboBox
    )
    from PyQt5.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtGui import QIcon

from persepolis.constants import APP_NAME
from persepolis.gui.customized_widgets import MyQDateTimeEdit
from persepolis.gui import resources # noqa: F401

class KeyCapturingWindow_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()
        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))
        self.setWindowTitle(QCoreApplication.translate("setting_ui_tr", 'Preferences'))

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        global icons
        icons = ':/' + str(self.persepolis_setting.value('settings/icons')) + '/'

        window_verticalLayout = QVBoxLayout(self)

        self.pressKeyLabel = QLabel(self)
        window_verticalLayout.addWidget(self.pressKeyLabel)

        self.capturedKeyLabel = QLabel(self)
        window_verticalLayout.addWidget(self.capturedKeyLabel)

        # window buttons
        buttons_horizontalLayout = QHBoxLayout()
        buttons_horizontalLayout.addStretch(1)

        self.cancel_pushButton = QPushButton(self)
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))
        buttons_horizontalLayout.addWidget(self.cancel_pushButton)

        self.ok_pushButton = QPushButton(self)
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))
        buttons_horizontalLayout.addWidget(self.ok_pushButton)

        window_verticalLayout.addLayout(buttons_horizontalLayout)

        # labels
        self.pressKeyLabel.setText(QCoreApplication.translate("setting_ui_tr", "Press new keys"))
        self.cancel_pushButton.setText(QCoreApplication.translate("setting_ui_tr", "Cancel"))
        self.ok_pushButton.setText(QCoreApplication.translate("setting_ui_tr", "OK"))


class Setting_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()

        self.persepolis_setting = persepolis_setting

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))
        self.setWindowTitle(QCoreApplication.translate("setting_ui_tr", 'Preferences'))

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        global icons
        icons = ':/' + str(self.persepolis_setting.value('settings/icons')) + '/'

        # main layout
        window_verticalLayout = QVBoxLayout(self)

        # setting_tabWidget
        self.setting_tabWidget = QTabWidget(self)

        # download_options_tab
        self.download_options_tab = QWidget()
        download_options_tab_verticalLayout = QVBoxLayout(self.download_options_tab)
        download_options_tab_verticalLayout.setContentsMargins(21, 21, 0, 0)

        # tries
        tries_horizontalLayout = QHBoxLayout()

        self.tries_label = QLabel(self.download_options_tab)
        tries_horizontalLayout.addWidget(self.tries_label)

        self.tries_spinBox = QSpinBox(self.download_options_tab)
        self.tries_spinBox.setMinimum(1)

        tries_horizontalLayout.addWidget(self.tries_spinBox)
        download_options_tab_verticalLayout.addLayout(tries_horizontalLayout)

        # wait
        wait_horizontalLayout = QHBoxLayout()

        self.wait_label = QLabel(self.download_options_tab)
        wait_horizontalLayout.addWidget(self.wait_label)

        self.wait_spinBox = QSpinBox(self.download_options_tab)
        wait_horizontalLayout.addWidget(self.wait_spinBox)

        download_options_tab_verticalLayout.addLayout(wait_horizontalLayout)

        # time_out
        time_out_horizontalLayout = QHBoxLayout()

        self.time_out_label = QLabel(self.download_options_tab)
        time_out_horizontalLayout.addWidget(self.time_out_label)

        self.time_out_spinBox = QSpinBox(self.download_options_tab)
        time_out_horizontalLayout.addWidget(self.time_out_spinBox)

        download_options_tab_verticalLayout.addLayout(time_out_horizontalLayout)

        # connections
        connections_horizontalLayout = QHBoxLayout()

        self.connections_label = QLabel(self.download_options_tab)
        connections_horizontalLayout.addWidget(self.connections_label)

        self.connections_spinBox = QSpinBox(self.download_options_tab)
        self.connections_spinBox.setMinimum(1)
        self.connections_spinBox.setMaximum(16)
        connections_horizontalLayout.addWidget(self.connections_spinBox)

        download_options_tab_verticalLayout.addLayout(connections_horizontalLayout)

        # rpc_port
        self.rpc_port_label = QLabel(self.download_options_tab)
        self.rpc_horizontalLayout = QHBoxLayout()
        self.rpc_horizontalLayout.addWidget(self.rpc_port_label)

        self.rpc_port_spinbox = QSpinBox(self.download_options_tab)
        self.rpc_port_spinbox.setMinimum(1024)
        self.rpc_port_spinbox.setMaximum(65535)
        self.rpc_horizontalLayout.addWidget(self.rpc_port_spinbox)

        download_options_tab_verticalLayout.addLayout(
            self.rpc_horizontalLayout)

        # wait_queue
        wait_queue_horizontalLayout = QHBoxLayout()

        self.wait_queue_label = QLabel(self.download_options_tab)
        wait_queue_horizontalLayout.addWidget(self.wait_queue_label)

        self.wait_queue_time = MyQDateTimeEdit(self.download_options_tab)
        self.wait_queue_time.setDisplayFormat('H:mm')
        wait_queue_horizontalLayout.addWidget(self.wait_queue_time)

        download_options_tab_verticalLayout.addLayout(
            wait_queue_horizontalLayout)

        # don't check certificate checkBox
        self.dont_check_certificate_checkBox = QCheckBox(self.download_options_tab)
        download_options_tab_verticalLayout.addWidget(self.dont_check_certificate_checkBox)

        # change aria2 path
        aria2_path_verticalLayout = QVBoxLayout()

        self.aria2_path_checkBox = QCheckBox(self.download_options_tab)
        aria2_path_verticalLayout.addWidget(self.aria2_path_checkBox)

        aria2_path_horizontalLayout = QHBoxLayout()

        self.aria2_path_lineEdit = QLineEdit(self.download_options_tab)
        aria2_path_horizontalLayout.addWidget(self.aria2_path_lineEdit)

        self.aria2_path_pushButton = QPushButton(self.download_options_tab)
        aria2_path_horizontalLayout.addWidget(self.aria2_path_pushButton)

        aria2_path_verticalLayout.addLayout(aria2_path_horizontalLayout)

        download_options_tab_verticalLayout.addLayout(aria2_path_verticalLayout)

        download_options_tab_verticalLayout.addStretch(1)

        self.setting_tabWidget.addTab(self.download_options_tab, "")

        # save_as_tab
        self.save_as_tab = QWidget()

        save_as_tab_verticalLayout = QVBoxLayout(self.save_as_tab)
        save_as_tab_verticalLayout.setContentsMargins(20, 30, 0, 0)

        # download_folder
        self.download_folder_horizontalLayout = QHBoxLayout()

        self.download_folder_label = QLabel(self.save_as_tab)
        self.download_folder_horizontalLayout.addWidget(
            self.download_folder_label)

        self.download_folder_lineEdit = QLineEdit(self.save_as_tab)
        self.download_folder_horizontalLayout.addWidget(self.download_folder_lineEdit)

        self.download_folder_pushButton = QPushButton(self.save_as_tab)
        self.download_folder_horizontalLayout.addWidget(self.download_folder_pushButton)

        save_as_tab_verticalLayout.addLayout(self.download_folder_horizontalLayout)

        # temp_download_folder
        self.temp_horizontalLayout = QHBoxLayout()

        self.temp_download_label = QLabel(self.save_as_tab)
        self.temp_horizontalLayout.addWidget(self.temp_download_label)

        self.temp_download_lineEdit = QLineEdit(self.save_as_tab)
        self.temp_horizontalLayout.addWidget(self.temp_download_lineEdit)

        self.temp_download_pushButton = QPushButton(self.save_as_tab)
        self.temp_horizontalLayout.addWidget(self.temp_download_pushButton)

        save_as_tab_verticalLayout.addLayout(self.temp_horizontalLayout)

        # create subfolder
        self.subfolder_checkBox = QCheckBox(self.save_as_tab)
        save_as_tab_verticalLayout.addWidget(self.subfolder_checkBox)

        save_as_tab_verticalLayout.addStretch(1)

        self.setting_tabWidget.addTab(self.save_as_tab, "")

        # notifications_tab
        self.notifications_tab = QWidget()
        notification_tab_verticalLayout = QVBoxLayout(self.notifications_tab)
        notification_tab_verticalLayout.setContentsMargins(21, 21, 0, 0)

        self.enable_notifications_checkBox = QCheckBox(self.notifications_tab)
        notification_tab_verticalLayout.addWidget(self.enable_notifications_checkBox)

        self.sound_frame = QFrame(self.notifications_tab)
        self.sound_frame.setFrameShape(QFrame.StyledPanel)
        self.sound_frame.setFrameShadow(QFrame.Raised)

        verticalLayout = QVBoxLayout(self.sound_frame)

        self.volume_label = QLabel(self.sound_frame)
        verticalLayout.addWidget(self.volume_label)

        self.volume_dial = QDial(self.sound_frame)
        self.volume_dial.setProperty("value", 100)
        verticalLayout.addWidget(self.volume_dial)

        notification_tab_verticalLayout.addWidget(self.sound_frame)

        # message_notification
        message_notification_horizontalLayout = QHBoxLayout()
        self.notification_label = QLabel(self.notifications_tab)
        message_notification_horizontalLayout.addWidget(self.notification_label)

        self.notification_comboBox = QComboBox(self.notifications_tab)
        message_notification_horizontalLayout.addWidget(self.notification_comboBox)
        notification_tab_verticalLayout.addLayout(message_notification_horizontalLayout)

        notification_tab_verticalLayout.addStretch(1)

        self.setting_tabWidget.addTab(self.notifications_tab, "")

        # style_tab
        self.style_tab = QWidget()
        style_tab_verticalLayout = QVBoxLayout(self.style_tab)
        style_tab_verticalLayout.setContentsMargins(21, 21, 0, 0)

        # style
        style_horizontalLayout = QHBoxLayout()

        self.style_label = QLabel(self.style_tab)
        style_horizontalLayout.addWidget(self.style_label)

        self.style_comboBox = QComboBox(self.style_tab)
        style_horizontalLayout.addWidget(self.style_comboBox)

        style_tab_verticalLayout.addLayout(style_horizontalLayout)

        # language
        language_horizontalLayout = QHBoxLayout()

        self.lang_label = QLabel(self.style_tab)
        language_horizontalLayout.addWidget(self.lang_label)
        self.lang_comboBox = QComboBox(self.style_tab)
        language_horizontalLayout.addWidget(self.lang_comboBox)

        style_tab_verticalLayout.addLayout(language_horizontalLayout)
        language_horizontalLayout = QHBoxLayout()
        self.lang_label.setText(QCoreApplication.translate("setting_ui_tr", "Language: "))

        # color scheme
        self.color_label = QLabel(self.style_tab)
        language_horizontalLayout.addWidget(self.color_label)

        self.color_comboBox = QComboBox(self.style_tab)
        language_horizontalLayout.addWidget(self.color_comboBox)

        style_tab_verticalLayout.addLayout(language_horizontalLayout)

        # icons
        icons_horizontalLayout = QHBoxLayout()
        self.icon_label = QLabel(self.style_tab)
        icons_horizontalLayout.addWidget(self.icon_label)

        self.icon_comboBox = QComboBox(self.style_tab)
        icons_horizontalLayout.addWidget(self.icon_comboBox)

        style_tab_verticalLayout.addLayout(icons_horizontalLayout)

        self.icons_size_horizontalLayout = QHBoxLayout()
        self.icons_size_label = QLabel(self.style_tab)
        self.icons_size_horizontalLayout.addWidget(self.icons_size_label)

        self.icons_size_comboBox = QComboBox(self.style_tab)
        self.icons_size_horizontalLayout.addWidget(self.icons_size_comboBox)

        style_tab_verticalLayout.addLayout(self.icons_size_horizontalLayout)

        # font
        font_horizontalLayout = QHBoxLayout()
        self.font_checkBox = QCheckBox(self.style_tab)
        font_horizontalLayout.addWidget(self.font_checkBox)

        self.fontComboBox = QFontComboBox(self.style_tab)
        font_horizontalLayout.addWidget(self.fontComboBox)

        self.font_size_label = QLabel(self.style_tab)
        font_horizontalLayout.addWidget(self.font_size_label)

        self.font_size_spinBox = QSpinBox(self.style_tab)
        self.font_size_spinBox.setMinimum(1)
        font_horizontalLayout.addWidget(self.font_size_spinBox)

        style_tab_verticalLayout.addLayout(font_horizontalLayout)
        self.setting_tabWidget.addTab(self.style_tab, "")
        window_verticalLayout.addWidget(self.setting_tabWidget)

        # start persepolis in system tray if browser executed
        self.start_persepolis_if_browser_executed_checkBox = QCheckBox(self.style_tab)
        style_tab_verticalLayout.addWidget(self.start_persepolis_if_browser_executed_checkBox)

        # hide window if close button clicked
        self.hide_window_checkBox = QCheckBox(self.style_tab)
        style_tab_verticalLayout.addWidget(self.hide_window_checkBox)

        # Enable system tray icon
        self.enable_system_tray_checkBox = QCheckBox(self.style_tab)
        style_tab_verticalLayout.addWidget(self.enable_system_tray_checkBox)

        # after_download dialog
        self.after_download_checkBox = QCheckBox()
        style_tab_verticalLayout.addWidget(self.after_download_checkBox)

        # show_menubar_checkbox
        self.show_menubar_checkbox = QCheckBox()
        style_tab_verticalLayout.addWidget(self.show_menubar_checkbox)

        # show_sidepanel_checkbox
        self.show_sidepanel_checkbox = QCheckBox()
        style_tab_verticalLayout.addWidget(self.show_sidepanel_checkbox)

        # hide progress window
        self.show_progress_window_checkbox = QCheckBox()
        style_tab_verticalLayout.addWidget(self.show_progress_window_checkbox)

        # add persepolis to startup
        self.startup_checkbox = QCheckBox()
        style_tab_verticalLayout.addWidget(self.startup_checkbox)

        # keep system awake
        self.keep_awake_checkBox = QCheckBox()
        style_tab_verticalLayout.addWidget(self.keep_awake_checkBox)

        style_tab_verticalLayout.addStretch(1)

        # columns_tab
        self.columns_tab = QWidget()

        columns_tab_verticalLayout = QVBoxLayout(self.columns_tab)
        columns_tab_verticalLayout.setContentsMargins(21, 21, 0, 0)

        # creating checkBox for columns
        self.show_column_label = QLabel()
        self.column0_checkBox = QCheckBox()
        self.column1_checkBox = QCheckBox()
        self.column2_checkBox = QCheckBox()
        self.column3_checkBox = QCheckBox()
        self.column4_checkBox = QCheckBox()
        self.column5_checkBox = QCheckBox()
        self.column6_checkBox = QCheckBox()
        self.column7_checkBox = QCheckBox()
        self.column10_checkBox = QCheckBox()
        self.column11_checkBox = QCheckBox()
        self.column12_checkBox = QCheckBox()

        columns_tab_verticalLayout.addWidget(self.show_column_label)
        columns_tab_verticalLayout.addWidget(self.column0_checkBox)
        columns_tab_verticalLayout.addWidget(self.column1_checkBox)
        columns_tab_verticalLayout.addWidget(self.column2_checkBox)
        columns_tab_verticalLayout.addWidget(self.column3_checkBox)
        columns_tab_verticalLayout.addWidget(self.column4_checkBox)
        columns_tab_verticalLayout.addWidget(self.column5_checkBox)
        columns_tab_verticalLayout.addWidget(self.column6_checkBox)
        columns_tab_verticalLayout.addWidget(self.column7_checkBox)
        columns_tab_verticalLayout.addWidget(self.column10_checkBox)
        columns_tab_verticalLayout.addWidget(self.column11_checkBox)
        columns_tab_verticalLayout.addWidget(self.column12_checkBox)

        columns_tab_verticalLayout.addStretch(1)

        self.setting_tabWidget.addTab(self.columns_tab, '')

        # video_finder_tab
        self.video_finder_tab = QWidget()

        video_finder_layout = QVBoxLayout(self.video_finder_tab)
        video_finder_layout.setContentsMargins(21, 21, 0, 0)

        video_finder_tab_verticalLayout = QVBoxLayout()

        max_links_horizontalLayout = QHBoxLayout()

        # max_links_label
        self.max_links_label = QLabel(self.video_finder_tab)

        max_links_horizontalLayout.addWidget(self.max_links_label)

        # max_links_spinBox
        self.max_links_spinBox = QSpinBox(self.video_finder_tab)
        self.max_links_spinBox.setMinimum(1)
        self.max_links_spinBox.setMaximum(16)
        max_links_horizontalLayout.addWidget(self.max_links_spinBox)
        video_finder_tab_verticalLayout.addLayout(max_links_horizontalLayout)

        self.video_finder_dl_path_horizontalLayout = QHBoxLayout()

        self.video_finder_frame = QFrame(self.video_finder_tab)
        self.video_finder_frame.setLayout(video_finder_tab_verticalLayout)

        video_finder_tab_verticalLayout.addStretch(1)

        video_finder_layout.addWidget(self.video_finder_frame)

        self.setting_tabWidget.addTab(self.video_finder_tab, "")

        # shortcut tab
        self.shortcut_tab = QWidget()
        shortcut_tab_verticalLayout = QVBoxLayout(self.shortcut_tab)
        shortcut_tab_verticalLayout.setContentsMargins(21, 21, 0, 0)

        # shortcut_table
        self.shortcut_table = QTableWidget(self)
        self.shortcut_table.setColumnCount(2)
        self.shortcut_table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.shortcut_table.setSelectionMode(QAbstractItemView.SingleSelection)
        self.shortcut_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.shortcut_table.verticalHeader().hide()

        shortcut_table_header = [QCoreApplication.translate("setting_ui_tr", 'Action'),
                                 QCoreApplication.translate("setting_ui_tr", 'Shortcut')]

        self.shortcut_table.setHorizontalHeaderLabels(shortcut_table_header)

        shortcut_tab_verticalLayout.addWidget(self.shortcut_table)

        self.setting_tabWidget.addTab(self.shortcut_tab, QCoreApplication.translate("setting_ui_tr", "Shortcuts"))

        # Actions
        actions_list = [QCoreApplication.translate('setting_ui_tr', 'Quit'),
                        QCoreApplication.translate('setting_ui_tr', 'Minimize to System Tray'),
                        QCoreApplication.translate('setting_ui_tr', 'Remove Download Items'),
                        QCoreApplication.translate('setting_ui_tr', 'Delete Download Items'),
                        QCoreApplication.translate('setting_ui_tr', 'Move Selected Items Up'),
                        QCoreApplication.translate('setting_ui_tr', 'Move Selected Items Down'),
                        QCoreApplication.translate('setting_ui_tr', 'Add New Download Link'),
                        QCoreApplication.translate('setting_ui_tr', 'Add New Video Link'),
                        QCoreApplication.translate('setting_ui_tr', 'Import Links from Text File')]

        # add actions to the shortcut_table
        j = 0
        for action in actions_list:
            item = QTableWidgetItem(str(action))

            # align center
            item.setTextAlignment(0x0004 | 0x0080)

            # insert item in shortcut_table
            self.shortcut_table.insertRow(j)
            self.shortcut_table.setItem(j, 0, item)

            j = j + 1

        self.shortcut_table.resizeColumnsToContents()

        # window buttons
        buttons_horizontalLayout = QHBoxLayout()
        buttons_horizontalLayout.addStretch(1)

        self.defaults_pushButton = QPushButton(self)
        buttons_horizontalLayout.addWidget(self.defaults_pushButton)

        self.cancel_pushButton = QPushButton(self)
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))
        buttons_horizontalLayout.addWidget(self.cancel_pushButton)

        self.ok_pushButton = QPushButton(self)
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))
        buttons_horizontalLayout.addWidget(self.ok_pushButton)

        window_verticalLayout.addLayout(buttons_horizontalLayout)

        # set style_tab for default
        self.setting_tabWidget.setCurrentIndex(3)

        # labels and translations
        self.setWindowTitle(QCoreApplication.translate("setting_ui_tr", "Preferences"))

        self.tries_label.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr", "<html><head/><body><p>Set number of tries if download failed.</p></body></html>"
            )
        )
        self.tries_label.setText(QCoreApplication.translate("setting_ui_tr", "Number of tries: "))
        self.tries_spinBox.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr", "<html><head/><body><p>Set number of tries if download failed.</p></body></html>"
            )
        )

        self.wait_label.setToolTip(
            QCoreApplication.translate("setting_ui_tr", "<html><head/><body><p>Set the seconds to wait between retries. Download manager will  retry  downloads  when  the  HTTP  server  returns  a  503 response.</p></body></html>"))  # noqa: E501
        self.wait_label.setText(QCoreApplication.translate("setting_ui_tr", "Wait period between retries (seconds): "))
        self.wait_spinBox.setToolTip(
            QCoreApplication.translate("setting_ui_tr", "<html><head/><body><p>Set the seconds to wait between retries. Download manager will  retry  downloads  when  the  HTTP  server  returns  a  503 response.</p></body></html>"))  # noqa: E501

        self.time_out_label.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr", "<html><head/><body><p>Set timeout in seconds. </p></body></html>"
            ))
        self.time_out_label.setText(QCoreApplication.translate("setting_ui_tr", "Timeout (seconds): "))
        self.time_out_spinBox.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr", "<html><head/><body><p>Set timeout in seconds. </p></body></html>"
            ))

        self.connections_label.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr",
                "<html><head/><body><p>Using multiple connections can help speed up your download.</p></body></html>"
            ))
        self.connections_label.setText(QCoreApplication.translate("setting_ui_tr", "Number of connections: "))
        self.connections_spinBox.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr",
                "<html><head/><body><p>Using multiple connections can help speed up your download.</p></body></html>"
            ))

        self.rpc_port_label.setText(QCoreApplication.translate("setting_ui_tr", "RPC port number: "))
        self.rpc_port_spinbox.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr",
                "<html><head/><body><p> Specify a port number for JSON-RPC/XML-RPC server to listen to.\
                Possible Values: 1024 - 65535 Default: 6801 </p></body></html>"
            ))

        self.wait_queue_label.setText(QCoreApplication.translate(
            "setting_ui_tr", 'Wait period between each download in queue:'))

        self.dont_check_certificate_checkBox.setText(
            QCoreApplication.translate("setting_ui_tr", "Don't use certificate to verify the peers")
        )
        self.dont_check_certificate_checkBox.setToolTip(
                QCoreApplication.translate(
                    "setting_ui_tr",
                    "<html><head/><body><p>This option avoids SSL/TLS handshake failure.\
                    But use it at your own risk!</p></body></html>"
                ))

        self.aria2_path_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Change Aria2 default path'))
        self.aria2_path_pushButton.setText(QCoreApplication.translate("setting_ui_tr", 'Change'))
        aria2_path_tooltip = QCoreApplication.translate(
            "setting_ui_tr", "<html><head/><body><p>Attention: Wrong path may cause problems!\
            Do it carefully or don't change default setting!</p></body></html>")
        self.aria2_path_checkBox.setToolTip(aria2_path_tooltip)
        self.aria2_path_lineEdit.setToolTip(aria2_path_tooltip)
        self.aria2_path_pushButton.setToolTip(aria2_path_tooltip)

        self.setting_tabWidget.setTabText(self.setting_tabWidget.indexOf(
            self.download_options_tab),  QCoreApplication.translate("setting_ui_tr", "Download Options"))

        self.download_folder_label.setText(QCoreApplication.translate("setting_ui_tr", "Download folder: "))
        self.download_folder_pushButton.setText(QCoreApplication.translate("setting_ui_tr", "Change"))

        self.temp_download_label.setText(QCoreApplication.translate("setting_ui_tr", "Temporary download folder: "))
        self.temp_download_pushButton.setText(QCoreApplication.translate("setting_ui_tr", "Change"))

        self.subfolder_checkBox.setText(QCoreApplication.translate(
            "setting_ui_tr", "Create subfolders for Music,Videos, ... in default download folder"))

        self.setting_tabWidget.setTabText(
            self.setting_tabWidget.indexOf(self.save_as_tab),  QCoreApplication.translate("setting_ui_tr", "Save As"))

        self.enable_notifications_checkBox.setText(
            QCoreApplication.translate("setting_ui_tr", "Enable Notification Sounds"))

        self.volume_label.setText(QCoreApplication.translate("setting_ui_tr", "Volume: "))

        self.setting_tabWidget.setTabText(self.setting_tabWidget.indexOf(
            self.notifications_tab),  QCoreApplication.translate("setting_ui_tr", "Notifications"))

        self.style_label.setText(QCoreApplication.translate("setting_ui_tr", "Style: "))
        self.color_label.setText(QCoreApplication.translate("setting_ui_tr", "Color scheme: "))
        self.icon_label.setText(QCoreApplication.translate("setting_ui_tr", "Icons: "))

        self.icons_size_label.setText(QCoreApplication.translate("setting_ui_tr", "Toolbar icons size: "))

        self.notification_label.setText(QCoreApplication.translate("setting_ui_tr", "Notification type: "))

        self.font_checkBox.setText(QCoreApplication.translate("setting_ui_tr", "Font: "))
        self.font_size_label.setText(QCoreApplication.translate("setting_ui_tr", "Size: "))

        self.hide_window_checkBox.setText(QCoreApplication.translate(
            "setting_ui_tr", "Hide main window if close button clicked."))
        self.hide_window_checkBox.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr",
                "<html><head/><body><p>This feature may not work in your operating system.</p></body></html>"
            )
        )

        self.start_persepolis_if_browser_executed_checkBox.setText(
            QCoreApplication.translate('setting_ui_tr', 'If browser is opened, start Persepolis in system tray'))

        self.enable_system_tray_checkBox.setText(
            QCoreApplication.translate("setting_ui_tr", "Enable system tray icon"))

        self.after_download_checkBox.setText(
            QCoreApplication.translate("setting_ui_tr", "Show download complete dialog when download is finished"))

        self.show_menubar_checkbox.setText(QCoreApplication.translate("setting_ui_tr", "Show menubar"))
        self.show_sidepanel_checkbox.setText(QCoreApplication.translate("setting_ui_tr", "Show side panel"))
        self.show_progress_window_checkbox.setText(
            QCoreApplication.translate("setting_ui_tr", "Show download progress window"))

        self.startup_checkbox.setText(QCoreApplication.translate("setting_ui_tr", "Run Persepolis at startup"))

        self.keep_awake_checkBox.setText(QCoreApplication.translate("setting_ui_tr", "Keep system awake!"))
        self.keep_awake_checkBox.setToolTip(
            QCoreApplication.translate(
                "setting_ui_tr", "<html><head/><body><p>This option will prevent the system from going to sleep.\
                It is necessary if your power manager is suspending the system automatically. </p></body></html>"
            )
        )

        self.wait_queue_time.setToolTip(
            QCoreApplication.translate("setting_ui_tr", "<html><head/><body><p>Format HH:MM</p></body></html>"))

        self.setting_tabWidget.setTabText(
            self.setting_tabWidget.indexOf(self.style_tab),  QCoreApplication.translate("setting_ui_tr", "Preferences"))

# columns_tab
        self.show_column_label.setText(QCoreApplication.translate("setting_ui_tr", 'Show these columns:'))
        self.column0_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'File Name'))
        self.column1_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Status'))
        self.column2_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Size'))
        self.column3_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Downloaded'))
        self.column4_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Percentage'))
        self.column5_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Connections'))
        self.column6_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Transfer Rate'))
        self.column7_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Estimated Time Left'))
        self.column10_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'First Try Date'))
        self.column11_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Last Try Date'))
        self.column12_checkBox.setText(QCoreApplication.translate("setting_ui_tr", 'Category'))

        self.setting_tabWidget.setTabText(
            self.setting_tabWidget.indexOf(self.columns_tab),
            QCoreApplication.translate("setting_ui_tr", "Columns Customization")
        )

# Video Finder options tab
        self.setting_tabWidget.setTabText(self.setting_tabWidget.indexOf(
            self.video_finder_tab), QCoreApplication.translate("setting_ui_tr",  "Video Finder Options"))

        self.max_links_label.setText(
            QCoreApplication.translate("setting_ui_tr", 'Maximum number of links to capture:<br/>'
                                        '<small>(If browser sends multiple video links at a time)</small>')
        )

# window buttons
        self.defaults_pushButton.setText(QCoreApplication.translate("setting_ui_tr", "Defaults"))
        self.cancel_pushButton.setText(QCoreApplication.translate("setting_ui_tr", "Cancel"))
        self.ok_pushButton.setText(QCoreApplication.translate("setting_ui_tr", "OK"))

persepolis/gui/setting_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtWidgets import (
        QHeaderView, QPushButton, QWidget, QTabWidget, QVBoxLayout,
        QTableWidget, QAbstractItemView, QLabel, QLineEdit, QHBoxLayout,
        QSpinBox, QComboBox, QFrame, QCheckBox, QGridLayout
    )
    from PySide6.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtWidgets import (
        QHeaderView, QPushButton, QWidget, QTabWidget, QVBoxLayout,
        QTableWidget, QAbstractItemView, QLabel, QLineEdit, QHBoxLayout,
        QSpinBox, QComboBox, QFrame, QCheckBox, QGridLayout
    )
    from PyQt5.QtCore import Qt, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtGui import QIcon

from persepolis.constants import APP_NAME, LONG_NAME
from persepolis.gui import resources # noqa: F401

class TextQueue_Ui(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()

        self.persepolis_setting = persepolis_setting
        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))
        window_verticalLayout = QVBoxLayout()
        self.setLayout(window_verticalLayout)

        # queue_tabWidget
        self.queue_tabWidget = QTabWidget(self)
        window_verticalLayout.addWidget(self.queue_tabWidget)

        # links_tab
        self.links_tab = QWidget()
        links_tab_verticalLayout = QVBoxLayout(self.links_tab)

        # link table
        self.links_table = QTableWidget(self.links_tab)
        links_tab_verticalLayout.addWidget(self.links_table)

        self.links_table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.links_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.links_table.verticalHeader().hide()

        self.links_table.setColumnCount(3)
        links_table_header_labels = [
            'File Name', 'Download Link', 'dictionary']
        self.links_table.setHorizontalHeaderLabels(links_table_header_labels)
        self.links_table.setColumnHidden(2, True)

        self.links_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.links_table.horizontalHeader().setStretchLastSection(True)

        # add_queue
        add_queue_horizontalLayout = QHBoxLayout()

        self.select_all_pushButton = QPushButton(self.links_tab)
        add_queue_horizontalLayout.addWidget(self.select_all_pushButton)

        self.deselect_all_pushButton = QPushButton(self.links_tab)
        add_queue_horizontalLayout.addWidget(self.deselect_all_pushButton)

        add_queue_horizontalLayout.addStretch(1)

        self.add_queue_label = QLabel(self.links_tab)
        add_queue_horizontalLayout.addWidget(self.add_queue_label)

        self.add_queue_comboBox = QComboBox(self.links_tab)
        add_queue_horizontalLayout.addWidget(self.add_queue_comboBox)

        links_tab_verticalLayout.addLayout(add_queue_horizontalLayout)

        links_tab_verticalLayout.addStretch(1)
        self.queue_tabWidget.addTab(self.links_tab, "")

        # options_tab
        self.options_tab = QWidget()
        options_tab_verticalLayout = QVBoxLayout(self.options_tab)

        # proxy
        proxy_verticalLayout = QVBoxLayout()

        self.proxy_checkBox = QCheckBox(self.options_tab)
        proxy_verticalLayout.addWidget(self.proxy_checkBox)

        self.proxy_frame = QFrame(self.options_tab)
        self.proxy_frame.setFrameShape(QFrame.StyledPanel)
        self.proxy_frame.setFrameShadow(QFrame.Raised)

        proxy_gridLayout = QGridLayout(self.proxy_frame)

        self.ip_lineEdit = QLineEdit(self.proxy_frame)
        self.ip_lineEdit.setInputMethodHints(Qt.ImhNone)
        proxy_gridLayout.addWidget(self.ip_lineEdit, 0, 1, 1, 1)

        self.proxy_pass_label = QLabel(self.proxy_frame)
        proxy_gridLayout.addWidget(self.proxy_pass_label, 2, 2, 1, 1)

        self.proxy_pass_lineEdit = QLineEdit(self.proxy_frame)
        self.proxy_pass_lineEdit.setEchoMode(QLineEdit.Password)
        proxy_gridLayout.addWidget(self.proxy_pass_lineEdit, 2, 3, 1, 1)

        self.ip_label = QLabel(self.proxy_frame)
        proxy_gridLayout.addWidget(self.ip_label, 0, 0, 1, 1)

        self.proxy_user_lineEdit = QLineEdit(self.proxy_frame)
        proxy_gridLayout.addWidget(self.proxy_user_lineEdit, 0, 3, 1, 1)

        self.proxy_user_label = QLabel(self.proxy_frame)
        proxy_gridLayout.addWidget(self.proxy_user_label, 0, 2, 1, 1)

        self.port_label = QLabel(self.proxy_frame)
        proxy_gridLayout.addWidget(self.port_label, 2, 0, 1, 1)

        self.port_spinBox = QSpinBox(self.proxy_frame)
        self.port_spinBox.setMaximum(9999)
        self.port_spinBox.setSingleStep(1)
        proxy_gridLayout.addWidget(self.port_spinBox, 2, 1, 1, 1)
        proxy_verticalLayout.addWidget(self.proxy_frame)
        options_tab_verticalLayout.addLayout(proxy_verticalLayout)

        # download Username & Password
        download_horizontalLayout = QHBoxLayout()
        download_horizontalLayout.setContentsMargins(-1, 10, -1, -1)

        download_verticalLayout = QVBoxLayout()
        self.download_checkBox = QCheckBox(self.options_tab)
        download_verticalLayout.addWidget(self.download_checkBox)

        self.download_frame = QFrame(self.options_tab)
        self.download_frame.setFrameShape(QFrame.StyledPanel)
        self.download_frame.setFrameShadow(QFrame.Raised)

        download_gridLayout = QGridLayout(self.download_frame)

        self.download_user_lineEdit = QLineEdit(self.download_frame)
        download_gridLayout.addWidget(self.download_user_lineEdit, 0, 1, 1, 1)

        self.download_user_label = QLabel(self.download_frame)
        download_gridLayout.addWidget(self.download_user_label, 0, 0, 1, 1)

        self.download_pass_label = QLabel(self.download_frame)
        download_gridLayout.addWidget(self.download_pass_label, 1, 0, 1, 1)

        self.download_pass_lineEdit = QLineEdit(self.download_frame)
        self.download_pass_lineEdit.setEchoMode(QLineEdit.Password)
        download_gridLayout.addWidget(self.download_pass_lineEdit, 1, 1, 1, 1)
        download_verticalLayout.addWidget(self.download_frame)
        download_horizontalLayout.addLayout(download_verticalLayout)

        # select folder
        self.folder_frame = QFrame(self.options_tab)
        self.folder_frame.setFrameShape(QFrame.StyledPanel)
        self.folder_frame.setFrameShadow(QFrame.Raised)

        folder_gridLayout = QGridLayout(self.folder_frame)

        self.download_folder_lineEdit = QLineEdit(self.folder_frame)
        folder_gridLayout.addWidget(self.download_folder_lineEdit, 2, 0, 1, 1)

        self.folder_pushButton = QPushButton(self.folder_frame)
        folder_gridLayout.addWidget(self.folder_pushButton, 3, 0, 1, 1)
        self.folder_pushButton.setIcon(QIcon(icons + 'folder'))

        self.folder_checkBox = QCheckBox(self.folder_frame)
        folder_gridLayout.addWidget(self.folder_checkBox)

        self.folder_label = QLabel(self.folder_frame)
        self.folder_label.setAlignment(Qt.AlignCenter)
        folder_gridLayout.addWidget(self.folder_label, 1, 0, 1, 1)
        download_horizontalLayout.addWidget(self.folder_frame)
        options_tab_verticalLayout.addLayout(download_horizontalLayout)

        self.queue_tabWidget.addTab(self.options_tab, '')

        # limit Speed
        limit_verticalLayout = QVBoxLayout()

        self.limit_checkBox = QCheckBox(self.options_tab)
        limit_verticalLayout.addWidget(self.limit_checkBox)

        self.limit_frame = QFrame(self.options_tab)
        self.limit_frame.setFrameShape(QFrame.StyledPanel)
        self.limit_frame.setFrameShadow(QFrame.Raised)

        limit_horizontalLayout = QHBoxLayout(self.limit_frame)

        self.limit_spinBox = QSpinBox(self.limit_frame)
        self.limit_spinBox.setMinimum(1)
        self.limit_spinBox.setMaximum(1023)
        limit_horizontalLayout.addWidget(self.limit_spinBox)

        self.limit_comboBox = QComboBox(self.limit_frame)
        self.limit_comboBox.addItem("KiB/S")
        self.limit_comboBox.addItem("MiB/S")
        limit_horizontalLayout.addWidget(self.limit_comboBox)

        limit_verticalLayout.addWidget(self.limit_frame)

        limit_connections_horizontalLayout = QHBoxLayout()
        limit_connections_horizontalLayout.addLayout(limit_verticalLayout)

        # number of connections
        connections_horizontalLayout = QHBoxLayout()
        connections_horizontalLayout.setContentsMargins(-1, 10, -1, -1)

        self.connections_frame = QFrame(self.options_tab)
        self.connections_frame.setFrameShape(QFrame.StyledPanel)
        self.connections_frame.setFrameShadow(QFrame.Raised)

        horizontalLayout_3 = QHBoxLayout(self.connections_frame)
        self.connections_label = QLabel(self.connections_frame)
        horizontalLayout_3.addWidget(self.connections_label)

        self.connections_spinBox = QSpinBox(self.connections_frame)
        self.connections_spinBox.setMinimum(1)
        self.connections_spinBox.setMaximum(16)
        self.connections_spinBox.setProperty("value", 16)

        horizontalLayout_3.addWidget(self.connections_spinBox)
        connections_horizontalLayout.addWidget(self.connections_frame)

        limit_connections_horizontalLayout.addLayout(connections_horizontalLayout)

        options_tab_verticalLayout.addLayout(limit_connections_horizontalLayout)

        options_tab_verticalLayout.addStretch(1)

        # buttons
        buttons_horizontalLayout = QHBoxLayout()
        buttons_horizontalLayout.addStretch(1)
        # ok_pushButton
        self.ok_pushButton = QPushButton(self)
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))
        buttons_horizontalLayout.addWidget(self.ok_pushButton)
        # cancel_pushButton
        self.cancel_pushButton = QPushButton(self)
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))
        buttons_horizontalLayout.addWidget(self.cancel_pushButton)

        window_verticalLayout.addLayout(buttons_horizontalLayout)

        # labels
        self.setWindowTitle(QCoreApplication.translate("text_ui_tr", LONG_NAME))

        self.queue_tabWidget.setTabText(
            self.queue_tabWidget.indexOf(self.links_tab), QCoreApplication.translate("text_ui_tr", 'Links'))
        self.queue_tabWidget.setTabText(
            self.queue_tabWidget.indexOf(self.options_tab),
            QCoreApplication.translate("text_ui_tr", 'Download Options')
        )

        self.select_all_pushButton.setText(QCoreApplication.translate("text_ui_tr", 'Select All'))
        self.deselect_all_pushButton.setText(QCoreApplication.translate("text_ui_tr", 'Deselect All'))

        self.add_queue_label.setText(QCoreApplication.translate("text_ui_tr", 'Add to queue: '))

        self.proxy_checkBox.setText(QCoreApplication.translate("text_ui_tr", 'Proxy'))
        self.proxy_pass_label.setText(QCoreApplication.translate("text_ui_tr", "Proxy password: "))
        self.ip_label.setText(QCoreApplication.translate("text_ui_tr", "IP:"))
        self.proxy_user_label.setText(QCoreApplication.translate("text_ui_tr", "Proxy username: "))
        self.port_label.setText(QCoreApplication.translate("text_ui_tr", "Port:"))

        self.download_checkBox.setText(QCoreApplication.translate("text_ui_tr", "Download username and password"))
        self.download_user_label.setText(QCoreApplication.translate("text_ui_tr", "Download username: "))
        self.download_pass_label.setText(QCoreApplication.translate("text_ui_tr", "Download password: "))

        self.folder_pushButton.setText(QCoreApplication.translate("text_ui_tr", "Change Download Folder"))
        self.folder_checkBox.setText(QCoreApplication.translate("addlink_ui_tr", "Remember this path"))
        self.folder_label.setText(QCoreApplication.translate("text_ui_tr", "Download folder: "))

        self.limit_checkBox.setText(QCoreApplication.translate("text_ui_tr", "Limit speed"))

        self.connections_label.setText(QCoreApplication.translate("text_ui_tr", "Number of connections:"))

        self.ok_pushButton.setText(QCoreApplication.translate("text_ui_tr", 'OK'))
        self.cancel_pushButton.setText(QCoreApplication.translate("text_ui_tr", 'Cancel'))

persepolis/gui/text_queue_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from persepolis.gui.progress_ui import ProgressWindow_Ui

try:
    from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
    from PySide6.QtCore import QCoreApplication
except ImportError:
    from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
    from PyQt5.QtCore import QCoreApplication

class VideoFinderProgressWindow_Ui(ProgressWindow_Ui):
    def __init__(self, persepolis_setting):
        super().__init__(persepolis_setting)

        # status_tab
        self.status_tab = QWidget()
        status_tab_verticalLayout = QVBoxLayout(self.status_tab)

        # video_status_label
        self.video_status_label = QLabel(self.status_tab)
        status_tab_verticalLayout.addWidget(self.video_status_label)

        # audio_status_label
        self.audio_status_label = QLabel(self.status_tab)
        status_tab_verticalLayout.addWidget(self.audio_status_label)

        # muxing_status_label
        self.muxing_status_label = QLabel(self.status_tab)
        status_tab_verticalLayout.addWidget(self.muxing_status_label)

        self.progress_tabWidget.addTab(self.status_tab, "")

        # set status_tab as default tab
        self.progress_tabWidget.setCurrentIndex(2)

        # labels

        self.video_status_label.setText(QCoreApplication.translate(
            "video_finder_progress_ui_tr", "<b>Video file status: </b>"))
        self.audio_status_label.setText(QCoreApplication.translate(
            "video_finder_progress_ui_tr", "<b>Audio file status: </b>"))
        self.muxing_status_label.setText(QCoreApplication.translate(
            "video_finder_progress_ui_tr", "<b>Mixing status: </b>"))

        self.progress_tabWidget.setTabText(self.progress_tabWidget.indexOf(
            self.status_tab),  QCoreApplication.translate("setting_ui_tr", "Status"))

persepolis/gui/video_finder_progress_ui.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6 import QtSvg
    from PySide6.QtWidgets import QDesktopWidget, QHBoxLayout, QVBoxLayout, QLabel, QWidget
    from PySide6.QtCore import Qt, QSize, QRect, QPoint
except ImportError:
    from PyQt5 import QtSvg
    from PyQt5.QtWidgets import QDesktopWidget, QHBoxLayout, QVBoxLayout, QLabel, QWidget
    from PyQt5.QtCore import Qt, QSize, QRect, QPoint

class Windows_Notification_UI(QWidget):
    def __init__(self, parent, persepolis_setting):
        super().__init__(parent)

        self.persepolis_setting = persepolis_setting

        # set ui direction
        ui_direction = self.persepolis_setting.value('ui_direction')

        if ui_direction == 'rtl':
            self.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            self.setLayoutDirection(Qt.LeftToRight)

        # set size
        self.resize(QSize(400, 80))
        self.setFixedWidth(400)

        # show this widget as ToolTip widget
        self.setWindowFlags(Qt.ToolTip)

        # find bottom right position
        bottom_right_screen = QDesktopWidget().availableGeometry().bottomRight()

        bottom_right_notification = QRect(QPoint(0, 0), QSize(410, 120))
        bottom_right_notification.moveBottomRight(bottom_right_screen)
        self.move(bottom_right_notification.topLeft())

        # get persepolis icon path
        # icons = ':/' + \
        #     str(self.persepolis_setting.value('settings/icons')) + '/'

        notification_horizontalLayout = QHBoxLayout(self)

        # persepolis icon
        svgWidget = QtSvg.QSvgWidget(':/persepolis.svg')
        svgWidget.setFixedSize(QSize(64, 64))

        notification_horizontalLayout.addWidget(svgWidget)

        notification_verticalLayout = QVBoxLayout()

        # 2 labels for notification messages
        self.label1 = QLabel(self)
        self.label1.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.label1.setStyleSheet("font-weight: bold")
        self.label1.setWordWrap(True)

        self.label2 = QLabel(self)
        self.label2.setWordWrap(True)
        self.label2.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)

        notification_verticalLayout.addWidget(self.label1)
        notification_verticalLayout.addWidget(self.label2)
        notification_horizontalLayout.addLayout(notification_verticalLayout)

persepolis/gui/windows_notification_ui.py
IamRezaMousavi/ghermez
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""


# import sys

# sys.path.insert(1, '.')

persepolis/scripts/__init__.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
try:
    from PySide6.QtCore import Qt, QSize, QPoint, QFile, QIODevice, QTextStream
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import Qt, QSize, QPoint, QFile, QIODevice, QTextStream
    from PyQt5.QtGui import QIcon

from persepolis.gui.about_ui import AboutWindow_Ui
from persepolis.gui import resources # noqa: F401

class AboutWindow(AboutWindow_Ui):
    def __init__(self, persepolis_setting):
        super().__init__(persepolis_setting)

        self.persepolis_setting = persepolis_setting

        # setting window size and position
        size = self.persepolis_setting.value(
            'AboutWindow/size', QSize(545, 375))
        position = self.persepolis_setting.value(
            'AboutWindow/position', QPoint(300, 300))

        # read translators.txt files.
        # this file contains all translators.
        f = QFile(':/translators.txt')

        f.open(QIODevice.ReadOnly | QFile.Text)
        f_text = QTextStream(f).readAll()
        f.close()

        self.translators_textEdit.insertPlainText(f_text)



        self.resize(size)
        self.move(position)


    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'
        self.pushButton.setIcon(QIcon(icons + 'ok'))

    def closeEvent(self, event):
        # saving window size and position
        self.persepolis_setting.setValue('AboutWindow/size', self.size())
        self.persepolis_setting.setValue('AboutWindow/position', self.pos())
        self.persepolis_setting.sync()
        event.accept()

persepolis/scripts/about.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
try:
    from PySide6.QtWidgets import QApplication, QFileDialog
    from PySide6.QtCore import Qt, QPoint, QSize, QDir, QThread, Signal
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtWidgets import QApplication, QFileDialog
    from PyQt5.QtCore import Qt, QPoint, QSize, QDir, QThread
    from PyQt5.QtGui import QIcon
    from PyQt5.QtCore import pyqtSignal as Signal

from persepolis.gui.addlink_ui import AddLinkWindow_Ui
from persepolis.scripts.check_proxy import getProxy
from persepolis.scripts import spider
from persepolis.scripts import logger
from functools import partial
import os

# find file name and file size


class AddLinkSpiderThread(QThread):
    ADDLINKSPIDERSIGNAL = Signal(dict)

    def __init__(self, add_link_dictionary):
        QThread.__init__(self)
        self.add_link_dictionary = add_link_dictionary

    def run(self):
        try:
            # get file name and file size
            file_name, file_size = spider.addLinkSpider(self.add_link_dictionary)

            spider_dict = {'file_size': file_size, 'file_name': file_name}

            # emit results
            self.ADDLINKSPIDERSIGNAL.emit(spider_dict)

            # write an ERROR in log, If spider couldn't find file_name or file_size.
            if not(file_name):
                logger.sendToLog(
                    "Spider couldn't find file name", "ERROR")
            if not(file_size):
                logger.sendToLog(
                    "Spider couldn't find file size", "ERROR")
        except Exception as e:
            logger.sendToLog(
                "Spider couldn't find download information", "ERROR")
            logger.sendToLog(
                str(e), "ERROR")


class AddLinkWindow(AddLinkWindow_Ui):
    def __init__(self, parent, callback, persepolis_setting, plugin_add_link_dictionary={}):
        super().__init__(persepolis_setting)
        self.callback = callback
        self.plugin_add_link_dictionary = plugin_add_link_dictionary
        self.persepolis_setting = persepolis_setting
        self.parent = parent

        # entry initialization
        # read values from persepolis_setting
        # connections
        connections = int(
            self.persepolis_setting.value('settings/connections'))

        self.connections_spinBox.setValue(connections)

        # download_path
        download_path = str(
            self.persepolis_setting.value('settings/download_path'))

        self.download_folder_lineEdit.setText(download_path)
        self.download_folder_lineEdit.setEnabled(False)

        # enable ok button only if link_lineEdit is not empty!
        # see linkLineChanged method.
        self.ok_pushButton.setEnabled(False)
        self.download_later_pushButton.setEnabled(False)
        self.link_lineEdit.textChanged.connect(self.linkLineChanged)

        # if browsers plugin didn't send any links
        # then check clipboard for link!
        if ('link' in self.plugin_add_link_dictionary.keys()):
            # check plugin_add_link_dictionary for link!
            # "link" key-value must be checked
            self.link_lineEdit.setText(
                str(self.plugin_add_link_dictionary['link']))

        else:
            # check clipboard
            clipboard = QApplication.clipboard()
            text = clipboard.text()
            if (("tp:/" in text[2:6]) or ("tps:/" in text[2:7])):
                self.link_lineEdit.setText(str(text))

        # detect_proxy_pushButton
        self.detect_proxy_pushButton.clicked.connect(
            self.detectProxy)

        # ip_lineEdit initialization ->
        settings_ip = self.persepolis_setting.value(
            'add_link_initialization/ip', None)
        if (settings_ip):
            self.ip_lineEdit.setText(str(settings_ip))

        # proxy user lineEdit initialization ->
        settings_proxy_user = self.persepolis_setting.value(
            'add_link_initialization/proxy_user', None)
        if (settings_proxy_user):
            self.proxy_user_lineEdit.setText(str(settings_proxy_user))

        # port_spinBox initialization ->
        settings_port = self.persepolis_setting.value(
            'add_link_initialization/port', 0)

        self.port_spinBox.setValue(int(int(settings_port)))

        # download UserName initialization ->
        settings_download_user = self.persepolis_setting.value(
            'add_link_initialization/download_user', None)
        if (settings_download_user):
            self.download_user_lineEdit.setText(str(settings_download_user))

# get categories name and add them to add_queue_comboBox
        categories_list = self.parent.persepolis_db.categoriesList()
        for queue in categories_list:
            if queue != 'All Downloads':
                self.add_queue_comboBox.addItem(queue)

        self.add_queue_comboBox.setCurrentIndex(0)

        # add_queue_comboBox event
        self.add_queue_comboBox.currentIndexChanged.connect(self.queueChanged)

        # connect folder_pushButton
        self.folder_pushButton.clicked.connect(self.changeFolder)

        # connect OK and cancel download_later button ->
        self.cancel_pushButton.clicked.connect(self.close)
        self.ok_pushButton.clicked.connect(partial(
            self.okButtonPressed, download_later=False))
        self.download_later_pushButton.clicked.connect(
            partial(self.okButtonPressed, download_later=True))

        # frames and checkBoxes ->
        self.proxy_frame.setEnabled(False)
        self.proxy_checkBox.toggled.connect(self.proxyFrame)

        self.download_frame.setEnabled(False)
        self.download_checkBox.toggled.connect(self.downloadFrame)

        self.limit_frame.setEnabled(False)
        self.limit_checkBox.toggled.connect(self.limitFrame)

        self.start_frame.setEnabled(False)
        self.start_checkBox.toggled.connect(self.startFrame)

        self.end_frame.setEnabled(False)
        self.end_checkBox.toggled.connect(self.endFrame)

        self.change_name_lineEdit.setEnabled(False)
        self.change_name_checkBox.toggled.connect(self.changeName)

        # set focus to ok button
        self.ok_pushButton.setFocus()

        # check plugin_add_link_dictionary for finding file name
        # perhaps plugin sended file name in plugin_add_link_dictionary
        # for finding file name "out" key must be checked
        if ('out' in self.plugin_add_link_dictionary.keys()):
            if self.plugin_add_link_dictionary['out']:
                self.change_name_lineEdit.setText(
                    str(self.plugin_add_link_dictionary['out']))
                self.change_name_checkBox.setChecked(True)

        # get referer and header and user_agent and load_cookies in plugin_add_link_dictionary if exits.
        if ('referer' in self.plugin_add_link_dictionary):
            self.referer_lineEdit.setText(str(self.plugin_add_link_dictionary['referer']))

        if ('header' in self.plugin_add_link_dictionary):
            if str(self.plugin_add_link_dictionary['header']) != 'None':
                self.header_lineEdit.setText(str(self.plugin_add_link_dictionary['header']))

        if ('user_agent' in self.plugin_add_link_dictionary):
            self.user_agent_lineEdit.setText(str(self.plugin_add_link_dictionary['user_agent']))

        if ('load_cookies' in self.plugin_add_link_dictionary):
            self.load_cookies_lineEdit.setText((self.plugin_add_link_dictionary['load_cookies']))


# set window size and position
        size = self.persepolis_setting.value(
            'AddLinkWindow/size', QSize(520, 425))
        position = self.persepolis_setting.value(
            'AddLinkWindow/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)


# detect system proxy setting, and set ip_lineEdit and port_spinBox


    def detectProxy(self, button):
        # get system proxy information
        system_proxy_dict = getProxy()

        enable_proxy_frame = False

        # ip
        if 'http_proxy_ip' in system_proxy_dict.keys():
            self.ip_lineEdit.setText(str(system_proxy_dict['http_proxy_ip']))
            enable_proxy_frame = True

        # port
        if 'http_proxy_port' in system_proxy_dict.keys():
            self.port_spinBox.setValue(int(system_proxy_dict['http_proxy_port']))
            enable_proxy_frame = True

        # enable proxy frame if http_proxy_ip or http_proxy_port is valid.
        if enable_proxy_frame:
            self.proxy_checkBox.setChecked(True)
            self.detect_proxy_label.setText('')
        else:
            self.proxy_checkBox.setChecked(False)
            self.detect_proxy_label.setText('No proxy detected!')


# active frames if checkBoxes are checked
    def proxyFrame(self, checkBox):

        if self.proxy_checkBox.isChecked():
            self.proxy_frame.setEnabled(True)
        else:
            self.proxy_frame.setEnabled(False)

    def downloadFrame(self, checkBox):

        if self.download_checkBox.isChecked():
            self.download_frame.setEnabled(True)
        else:
            self.download_frame.setEnabled(False)

    def limitFrame(self, checkBox):

        if self.limit_checkBox.isChecked():
            self.limit_frame.setEnabled(True)
        else:
            self.limit_frame.setEnabled(False)

    def startFrame(self, checkBox):

        if self.start_checkBox.isChecked():
            self.start_frame.setEnabled(True)
        else:
            self.start_frame.setEnabled(False)

    def endFrame(self, checkBox):

        if self.end_checkBox.isChecked():
            self.end_frame.setEnabled(True)
        else:
            self.end_frame.setEnabled(False)

    def changeFolder(self, button):
        # get download_path from lineEdit
        download_path = self.download_folder_lineEdit.text()

        # open select folder dialog
        fname = QFileDialog.getExistingDirectory(
            self, 'Select a directory', download_path)

        if fname:
            # Returns pathName with the '/' separators converted
            # to separators that are appropriate for the underlying operating system.
            # On Windows, toNativeSeparators("c:/winnt/system32") returns
            # "c:\winnt\system32".
            fname = QDir.toNativeSeparators(fname)

        if os.path.isdir(fname):
            self.download_folder_lineEdit.setText(fname)

# enable when link_lineEdit is not empty and find size of file.
    def linkLineChanged(self, lineEdit):
        if str(self.link_lineEdit.text()) == '':
            self.ok_pushButton.setEnabled(False)
            self.download_later_pushButton.setEnabled(False)
        else:  # find file size

            dict = {'link': str(self.link_lineEdit.text())}

            # spider is finding file size
            new_spider = AddLinkSpiderThread(dict)
            self.parent.threadPool.append(new_spider)
            self.parent.threadPool[-1].start()
            self.parent.threadPool[-1].ADDLINKSPIDERSIGNAL.connect(
                partial(self.parent.addLinkSpiderCallBack, child=self))

            self.ok_pushButton.setEnabled(True)
            self.download_later_pushButton.setEnabled(True)

# enable change_name_lineEdit if change_name_checkBox is checked.
    def changeName(self, checkBoxes):
        if self.change_name_checkBox.isChecked():
            self.change_name_lineEdit.setEnabled(True)
        else:
            self.change_name_lineEdit.setEnabled(False)

    def queueChanged(self, combo):
        # if one of the queues selected by user , start time and end time must
        # be deactivated
        if self.add_queue_comboBox.currentIndex() != 0:
            self.start_checkBox.setCheckState(Qt.Unchecked)
            self.start_checkBox.setEnabled(False)

            self.end_checkBox.setCheckState(Qt.Unchecked)
            self.end_checkBox.setEnabled(False)

        else:
            self.start_checkBox.setEnabled(True)
            self.end_checkBox.setEnabled(True)

    def okButtonPressed(self, download_later, button=None):
        # user submitted information by pressing ok_pushButton, so get information
        # from AddLinkWindow and return them to the mainwindow with callback!

        # write user's new inputs in persepolis_setting for next time :)
        self.persepolis_setting.setValue(
            'add_link_initialization/ip', self.ip_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/port', self.port_spinBox.value())
        self.persepolis_setting.setValue(
            'add_link_initialization/proxy_user', self.proxy_user_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/download_user', self.download_user_lineEdit.text())

        # Check 'Remember path' and change default path if needed
        if self.folder_checkBox.isChecked():
            self.persepolis_setting.setValue(
                'settings/download_path', self.download_folder_lineEdit.text())

        # get proxy information
        if not(self.proxy_checkBox.isChecked()):
            ip = None
            port = None
            proxy_user = None
            proxy_passwd = None
        else:
            ip = self.ip_lineEdit.text()
            if not(ip):
                ip = None
            port = self.port_spinBox.value()
            if not(port):
                port = None
            proxy_user = self.proxy_user_lineEdit.text()
            if not(proxy_user):
                proxy_user = None
            proxy_passwd = self.proxy_pass_lineEdit.text()
            if not(proxy_passwd):
                proxy_passwd = None

        # get download username and password information
        if not(self.download_checkBox.isChecked()):
            download_user = None
            download_passwd = None
        else:
            download_user = self.download_user_lineEdit.text()
            if not(download_user):
                download_user = None
            download_passwd = self.download_pass_lineEdit.text()
            if not(download_passwd):
                download_passwd = None

        # check that if user limits download speed.
        if not(self.limit_checkBox.isChecked()):
            limit = 0
        else:
            if self.limit_comboBox.currentText() == "KiB/s":
                limit = str(self.limit_spinBox.value()) + str("K")
            else:
                limit = str(self.limit_spinBox.value()) + str("M")

        # get start time for download if user set that.
        if not(self.start_checkBox.isChecked()):
            start_time = None
        else:
            start_time = self.start_time_qDataTimeEdit.text()

        # get end time for download if user set that.
        if not(self.end_checkBox.isChecked()):
            end_time = None
        else:
            end_time = self.end_time_qDateTimeEdit.text()

        # check that if user set new name for download file.
        if self.change_name_checkBox.isChecked():
            out = str(self.change_name_lineEdit.text())
            self.plugin_add_link_dictionary['out'] = out
        else:
            out = None

        # get download link
        link = self.link_lineEdit.text()

        # get number of connections
        connections = self.connections_spinBox.value()

        # get download_path
        download_path = self.download_folder_lineEdit.text()

        # referer
        if self.referer_lineEdit.text() != '':
            referer = self.referer_lineEdit.text()
        else:
            referer = None

        # header
        if self.header_lineEdit.text() != '':
            header = self.header_lineEdit.text()
        else:
            header = None

        # user_agent
        if self.user_agent_lineEdit.text() != '':
            user_agent = self.user_agent_lineEdit.text()
        else:
            user_agent = None

        # load_cookies
        if self.load_cookies_lineEdit.text() != '':
            load_cookies = self.load_cookies_lineEdit.text()
        else:
            load_cookies = None
        # save information in a dictionary(add_link_dictionary).
        self.add_link_dictionary = {
            'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies,
            'out': out, 'start_time': start_time, 'end_time': end_time, 'link': link, 'ip': ip,
            'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd,
            'download_user': download_user, 'download_passwd': download_passwd,
            'connections': connections, 'limit_value': limit, 'download_path': download_path
        }

        # convert values to pystring
        for key in self.add_link_dictionary:
            self.add_link_dictionary[key] = str(self.add_link_dictionary[key])

        # get category of download
        category = str(self.add_queue_comboBox.currentText())

        del self.plugin_add_link_dictionary

        # return information to mainwindow
        self.callback(self.add_link_dictionary, download_later, category)

        # close window
        self.close()

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    # save size and position of window, when user closes the window.
    def closeEvent(self, event):
        self.persepolis_setting.setValue('AddLinkWindow/size', self.size())
        self.persepolis_setting.setValue('AddLinkWindow/position', self.pos())
        self.persepolis_setting.sync()
        event.accept()

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'

        self.folder_pushButton.setIcon(QIcon(icons + 'folder'))
        self.download_later_pushButton.setIcon(QIcon(icons + 'stop'))
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))

persepolis/scripts/addlink.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtCore import Qt, QSize, QPoint, QTranslator, QCoreApplication, QLocale
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import Qt, QSize, QPoint, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtGui import QIcon


from persepolis.gui.after_download_ui import AfterDownloadWindow_Ui
from persepolis.scripts import osCommands
import os


class AfterDownloadWindow(AfterDownloadWindow_Ui):
    def __init__(self, parent, dict, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.dict = dict
        self.parent = parent

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # connecting buttons
        self.open_pushButtun.clicked.connect(self.openFile)
        self.open_folder_pushButtun.clicked.connect(self.openFolder)
        self.ok_pushButton.clicked.connect(self.okButtonPressed)

        # labels
        # find gid
        gid = self.dict['gid']

        # get file_path from data base
        self.add_link_dict = self.parent.persepolis_db.searchGidInAddLinkTable(gid)
        file_path = self.add_link_dict['download_path']

        # save_as
        self.save_as_lineEdit.setText(file_path)
        self.save_as_lineEdit.setToolTip(file_path)

        # link
        link = str(self.dict['link'])
        self.link_lineEdit.setText(link)
        self.link_lineEdit.setToolTip(link)

        # file_name

        window_title = str(self.dict['file_name'])
        file_name = QCoreApplication.translate("after_download_src_ui_tr", "<b>File name</b>: ") + \
            window_title

        self.setWindowTitle(window_title)

        self.file_name_label.setText(file_name)

        # size
        size = QCoreApplication.translate("after_download_src_ui_tr", "<b>Size</b>: ") + str(self.dict['size'])
        self.size_label.setText(size)

        # disable link_lineEdit and save_as_lineEdit
        self.link_lineEdit.setEnabled(False)
        self.save_as_lineEdit.setEnabled(False)

        # set window size and position
        size = self.persepolis_setting.value(
            'AfterDownloadWindow/size', QSize(570, 290))
        position = self.persepolis_setting.value(
            'AfterDownloadWindow/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)

    def openFile(self):
        # execute file
        file_path = self.add_link_dict['download_path']

        if os.path.isfile(file_path):
            osCommands.xdgOpen(file_path)

        # close window
        self.close()

    def openFolder(self):
        # open download folder
        download_path = self.add_link_dict['download_path']

#         file_name = os.path.basename(file_path)

#         file_path_split = file_path.split(file_name)

#         del file_path_split[-1]

#         download_path = file_name.join(file_path_split)

        if os.path.isfile(download_path):
            osCommands.xdgOpen(download_path, 'folder', 'file')

        # close window
        self.close()

    def okButtonPressed(self):
        if self.dont_show_checkBox.isChecked():
            self.persepolis_setting.setValue('settings/after-dialog', 'no')
            self.persepolis_setting.sync()

        # close window
        self.close()

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        # saving window size and position
        self.persepolis_setting.setValue(
            'AfterDownloadWindow/size', self.size())
        self.persepolis_setting.setValue(
            'AfterDownloadWindow/position', self.pos())
        self.persepolis_setting.sync()
        event.accept()

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))
        self.open_folder_pushButtun.setIcon(QIcon(icons + 'folder'))
        self.open_pushButtun.setIcon(QIcon(icons + 'file'))

persepolis/scripts/after_download.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

from ghermez import determineConfigFolder
from persepolis.scripts import osCommands
from persepolis.constants import OS, BROWSER
import subprocess
import platform
import sys
import os

os_type = platform.system()

home_address = str(os.path.expanduser("~"))

# download manager config folder .
config_folder = determineConfigFolder()

# browser can be firefox or chromium or chrome


def browserIntegration(browser):
    # for GNU/Linux
    if os_type == OS.LINUX:
        # find Persepolis execution path
        # persepolis execution path
        exec_path = os.path.join(config_folder, 'persepolis_run_shell')

        # Native Messaging Hosts folder path for every browser
        if browser == BROWSER.CHROMIUM:
            native_message_folder = home_address + '/.config/chromium/NativeMessagingHosts'

        elif browser == BROWSER.CHROME:
            native_message_folder = home_address + \
                '/.config/google-chrome/NativeMessagingHosts'

        elif browser == BROWSER.FIREFOX:
            native_message_folder = home_address + \
                '/.mozilla/native-messaging-hosts'

        elif browser == BROWSER.VIVALDI:
            native_message_folder = home_address + \
                '/.config/vivaldi/NativeMessagingHosts'

        elif browser == BROWSER.OPERA:
            native_message_folder = home_address + \
                '/.config/opera/NativeMessagingHosts'

        elif browser == BROWSER.BRAVE:
            native_message_folder = home_address + \
                '/.config/BraveSoftware/Brave-Browser/NativeMessagingHosts'

    # for FreeBSD and OpenBSD
    elif os_type in OS.BSD_FAMILY:
        # find Persepolis execution path
        # persepolis execution path
        exec_path = os.path.join(config_folder, 'persepolis_run_shell')

        # Native Messaging Hosts folder path for every browser
        if browser == BROWSER.CHROMIUM:
            native_message_folder = home_address + '/.config/chromium/NativeMessagingHosts'

        elif browser == BROWSER.CHROME:
            native_message_folder = home_address + \
                '/.config/google-chrome/NativeMessagingHosts'

        elif browser == BROWSER.FIREFOX:
            native_message_folder = home_address + \
                '/.mozilla/native-messaging-hosts'
        elif browser == BROWSER.VIVALDI:
            native_message_folder = home_address + \
                '/.config/vivaldi/NativeMessagingHosts'

        elif browser == BROWSER.OPERA:
            native_message_folder = home_address + \
                '/.config/opera/NativeMessagingHosts'

        elif browser == BROWSER.BRAVE:
            native_message_folder = home_address + \
                '/.config/BraveSoftware/Brave-Browser/NativeMessagingHosts'


    # for Mac OSX
    elif os_type == OS.OSX:
        # find Persepolis execution path
        # persepolis execution path
        exec_path = os.path.join(config_folder, 'persepolis_run_shell')

        # Native Messaging Hosts folder path for every browser
        if browser == BROWSER.CHROMIUM:
            native_message_folder = home_address + \
                '/Library/Application Support/Chromium/NativeMessagingHosts'

        elif browser == BROWSER.CHROME:
            native_message_folder = home_address + \
                '/Library/Application Support/Google/Chrome/NativeMessagingHosts'

        elif browser == BROWSER.FIREFOX:
            native_message_folder = home_address + \
                '/Library/Application Support/Mozilla/NativeMessagingHosts'

        elif browser == BROWSER.VIVALDI:
            native_message_folder = home_address + \
                '/Library/Application Support/Vivaldi/NativeMessagingHosts'

        elif browser == BROWSER.OPERA:
            native_message_folder = home_address + \
                '/Library/Application Support/Opera/NativeMessagingHosts/'

        elif browser == BROWSER.BRAVE:
            native_message_folder = home_address + \
                '/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts/'

    # for MicroSoft Windows os (windows 7 , ...)
    elif os_type == OS.WINDOWS:
        # finding Persepolis execution path
        cwd = sys.argv[0]

        current_directory = os.path.dirname(cwd)

        exec_path = os.path.join(
            current_directory, 'Persepolis Download Manager.exe')

        # the execution path in json file for Windows must in form of
        # c:\\Users\\...\\Persepolis Download Manager.exe , so we need 2
        # "\" in address
        exec_path = exec_path.replace('\\', r'\\')

        if browser in BROWSER.CHROME_FAMILY:
            native_message_folder = os.path.join(
                home_address, 'AppData\Local\persepolis_download_manager', 'chrome')
        else:
            native_message_folder = os.path.join(
                home_address, 'AppData\Local\persepolis_download_manager', 'firefox')

    # WebExtension native hosts file prototype
    webextension_json_connector = {
        "name": "com.persepolis.pdmchromewrapper",
        "type": "stdio",
        "path": str(exec_path),
        "description": "Integrate Persepolis with %s using WebExtensions" % (browser)
    }

    # Add chrom* keys
    if browser in BROWSER.CHROME_FAMILY:
        webextension_json_connector["allowed_origins"] = ["chrome-extension://legimlagjjoghkoedakdjhocbeomojao/"]

    # Add firefox keys
    elif browser == BROWSER.FIREFOX:
        webextension_json_connector["allowed_extensions"] = [
            "com.persepolis.pdmchromewrapper@persepolisdm.github.io",
            "com.persepolis.pdmchromewrapper.offline@persepolisdm.github.io"
        ]

    # Build final path
    native_message_file = os.path.join(
        native_message_folder, 'com.persepolis.pdmchromewrapper.json')

    osCommands.makeDirs(native_message_folder)

    # Write NMH file
    f = open(native_message_file, 'w')
    f.write(str(webextension_json_connector).replace("'", "\""))
    f.close()

    if os_type != OS.WINDOWS:

        pipe_json = subprocess.Popen(['chmod', '+x', str(native_message_file)],
                                     stderr=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stdin=subprocess.PIPE,
                                     shell=False)

        if pipe_json.wait() == 0:
            json_done = True
        else:
            json_done = False

    else:
        native_done = None
        import winreg
        from builtins import WindowsError
        # add the key to the windows registry
        if browser in BROWSER.CHROME_FAMILY:
            try:
                # create pdmchromewrapper key under NativeMessagingHosts
                winreg.CreateKey(winreg.HKEY_CURRENT_USER,
                                 "SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.persepolis.pdmchromewrapper")
                # open a connection to pdmchromewrapper key
                gintKey = winreg.OpenKey(
                    winreg.HKEY_CURRENT_USER,
                    "SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.persepolis.pdmchromewrapper",
                    0, winreg.KEY_ALL_ACCESS
                )
                # set native_message_file as key value
                winreg.SetValueEx(gintKey, '', 0, winreg.REG_SZ, native_message_file)
                # close connection to pdmchromewrapper
                winreg.CloseKey(gintKey)

                json_done = True

            except WindowsError:

                json_done = False

        elif browser == BROWSER.FIREFOX:
            try:
                # create pdmchromewrapper key under NativeMessagingHosts for firefox
                winreg.CreateKey(winreg.HKEY_CURRENT_USER,
                                 "SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.persepolis.pdmchromewrapper")
                # open a connection to pdmchromewrapper key for firefox
                fintKey = winreg.OpenKey(
                    winreg.HKEY_CURRENT_USER,
                    "SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.persepolis.pdmchromewrapper",
                    0, winreg.KEY_ALL_ACCESS
                )
                # set native_message_file as key value
                winreg.SetValueEx(fintKey, '', 0, winreg.REG_SZ, native_message_file)
                # close connection to pdmchromewrapper
                winreg.CloseKey(fintKey)

                json_done = True

            except WindowsError:

                json_done = False

    # create persepolis_run_shell file for gnu/linux and BSD and Mac
    # firefox and chromium and ... call persepolis with Native Messaging system.
    # json file calls persepolis_run_shell file.
    if os_type in (OS.UNIX_LIKE + [OS.OSX]):
        # find available shell
        shell_list = ['/bin/bash', '/usr/local/bin/bash', '/bin/sh', '/usr/local/bin/sh', '/bin/ksh', '/bin/tcsh']

        for shell in shell_list:
            if os.path.isfile(shell):
                # define shebang
                shebang = '#!' + shell
                break

        if os_type == OS.OSX:
            # finding Persepolis execution path
            cwd = sys.argv[0]

            current_directory = os.path.dirname(cwd)

            persepolis_path = os.path.join(
                current_directory, 'Persepolis Download Manager')
        else:
            persepolis_path = 'persepolis'

        persepolis_run_shell_contents = shebang + '\n' + '"' + persepolis_path + '" "$@"'

        f = open(exec_path, 'w')
        f.writelines(persepolis_run_shell_contents)
        f.close()

        # make persepolis_run_shell executable

        pipe_native = subprocess.Popen(['chmod', '+x', exec_path],
                                       stderr=subprocess.PIPE,
                                       stdout=subprocess.PIPE,
                                       stdin=subprocess.PIPE,
                                       shell=False)

        if pipe_native.wait() == 0:
            native_done = True
        else:
            native_done = False

    return json_done, native_done

persepolis/scripts/browser_integration.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtCore import Qt, QPoint, QSize, QThread, Signal, QDir
    from PySide6.QtWidgets import QTableWidgetItem, QFileDialog
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import Qt, QPoint, QSize, QThread, QDir
    from PyQt5.QtWidgets import QTableWidgetItem, QFileDialog
    from PyQt5.QtCore import pyqtSignal as Signal
    from PyQt5.QtGui import QIcon

from persepolis.gui.text_queue_ui import TextQueue_Ui
from persepolis.scripts import logger
from persepolis.scripts import spider
from functools import partial
from copy import deepcopy
import os


# This thread finds filename
class QueueSpiderThread(QThread):
    QUEUESPIDERRETURNEDFILENAME = Signal(str)

    def __init__(self, dict):
        QThread.__init__(self)
        self.dict = dict

    def run(self):
        try:
            filename = spider.queueSpider(self.dict)
            if filename:
                self.QUEUESPIDERRETURNEDFILENAME.emit(filename)
            else:
                logger.logObj.error(
                    "Spider couldn't find download information", exc_info=True)

        except Exception as e:
            logger.logObj.error(
                "Spider couldn't find download information", exc_info=True)

            logger.logObj.error(
                str(e), exc_info=True)


class BrowserPluginQueue(TextQueue_Ui):
    def __init__(self, parent, list_of_links, callback, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.callback = callback
        self.parent = parent
        self.list_of_links = list_of_links

        global icons
        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        self.list_of_links.reverse()

        k = 1
        for dict in self.list_of_links:
            # add row to the links_table
            self.links_table.insertRow(0)

            # file_name
            if 'out' in dict:
                if dict['out']:
                    file_name = dict['out']
                else:
                    file_name = '***'
            else:
                file_name = '***'

            if file_name == '***':
                # spider finds file name
                new_spider = QueueSpiderThread(dict)
                self.parent.threadPool.append(new_spider)
                self.parent.threadPool[-1].start()
                self.parent.threadPool[-1].QUEUESPIDERRETURNEDFILENAME.connect(
                    partial(self.parent.queueSpiderCallBack, child=self, row_number=len(self.list_of_links) - k))
            k = k + 1

            item = QTableWidgetItem(file_name)
            # add checkbox to the item
            item.setFlags(Qt.ItemIsUserCheckable |
                          Qt.ItemIsEnabled)
            item.setCheckState(Qt.Checked)

            # insert file_name
            self.links_table.setItem(0, 0, item)

            # find link
            link = dict['link']
            item = QTableWidgetItem(str(link))

            # insert link
            self.links_table.setItem(0, 1, item)

# get categories name and add them to add_queue_comboBox
        categories_list = self.parent.persepolis_db.categoriesList()

        for queue in categories_list:
            if queue != 'All Downloads':
                self.add_queue_comboBox.addItem(queue)

        self.add_queue_comboBox.addItem(
            QIcon(icons + 'add_queue'), 'Create new queue')


# entry initialization
        global connections
        connections = int(
            self.persepolis_setting.value('settings/connections'))
        global download_path
        download_path = str(
            self.persepolis_setting.value('settings/download_path'))


# initialization
        self.connections_spinBox.setValue(connections)
        self.download_folder_lineEdit.setText(download_path)
        self.download_folder_lineEdit.setEnabled(False)

# ip_lineEdit initialization
        settings_ip = self.persepolis_setting.value(
            'add_link_initialization/ip', None)
        if settings_ip:
            self.ip_lineEdit.setText(str(settings_ip))

# proxy user lineEdit initialization
        settings_proxy_user = self.persepolis_setting.value(
            'add_link_initialization/proxy_user', None)
        if settings_proxy_user:
            self.proxy_user_lineEdit.setText(str(settings_proxy_user))

# port_spinBox initialization
        settings_port = self.persepolis_setting.value(
            'add_link_initialization/port', 0)

        self.port_spinBox.setValue(int(int(settings_port)))


# download UserName initialization
        settings_download_user = self.persepolis_setting.value(
            'add_link_initialization/download_user', None)
        if settings_download_user:
            self.download_user_lineEdit.setText(str(settings_download_user))


# connect folder_pushButton
        self.folder_pushButton.clicked.connect(self.changeFolder)

# connect OK and cancel button

        self.cancel_pushButton.clicked.connect(self.close)
        self.ok_pushButton.clicked.connect(self.okButtonPressed)

# connect select_all_pushButton  deselect_all_pushButton
        self.select_all_pushButton.clicked.connect(self.selectAll)

        self.deselect_all_pushButton.clicked.connect(self.deselectAll)


#frames and checkBoxes
        self.proxy_frame.setEnabled(False)
        self.proxy_checkBox.toggled.connect(self.proxyFrame)

        self.download_frame.setEnabled(False)
        self.download_checkBox.toggled.connect(self.downloadFrame)

        self.limit_frame.setEnabled(False)
        self.limit_checkBox.toggled.connect(self.limitFrame)

        # set focus to ok button
        self.ok_pushButton.setFocus()

# add_queue_comboBox event
        self.add_queue_comboBox.currentIndexChanged.connect(self.queueChanged)

# set window size and position
        size = self.persepolis_setting.value('TextQueue/size', QSize(700, 500))
        position = self.persepolis_setting.value(
            'TextQueue/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)

# this method selects all links in links_table
    def selectAll(self, button):
        for i in range(self.links_table.rowCount()):
            item = self.links_table.item(i, 0)
            item.setCheckState(Qt.Checked)

# this method unchecks all check boxes
    def deselectAll(self, button):
        for i in range(self.links_table.rowCount()):
            item = self.links_table.item(i, 0)
            item.setCheckState(Qt.Unchecked)

# this method is called, when user changes add_queue_comboBox
    def queueChanged(self, combo):
        if str(self.add_queue_comboBox.currentText()) == 'Create new queue':
            # if user want to create new queue, then call createQueue method from mainwindow(parent)
            new_queue = self.parent.createQueue(combo)

            if new_queue:
                # clear comboBox
                self.add_queue_comboBox.clear()

                # load queue list again!
                queues_list = self.parent.persepolis_db.categoriesList()
                for queue in queues_list:
                    if queue != 'All Downloads':
                        self.add_queue_comboBox.addItem(queue)

                self.add_queue_comboBox.addItem(
                    QIcon(icons + 'add_queue'), 'Create new queue')

                # finding index of new_queue and setting comboBox for it
                index = self.add_queue_comboBox.findText(str(new_queue))
                self.add_queue_comboBox.setCurrentIndex(index)
            else:
                self.add_queue_comboBox.setCurrentIndex(0)


# activate frames if checkBoxes checked
    def proxyFrame(self, checkBox):

        if self.proxy_checkBox.isChecked():
            self.proxy_frame.setEnabled(True)
        else:
            self.proxy_frame.setEnabled(False)

    def downloadFrame(self, checkBox):

        if self.download_checkBox.isChecked():
            self.download_frame.setEnabled(True)
        else:
            self.download_frame.setEnabled(False)

    def limitFrame(self, checkBox):

        if self.limit_checkBox.isChecked():
            self.limit_frame.setEnabled(True)
        else:
            self.limit_frame.setEnabled(False)

    def changeFolder(self, button):
        fname = QFileDialog.getExistingDirectory(
            self, 'Select a directory', download_path)

        if fname:
            # Returns pathName with the '/' separators converted
            # to separators that are appropriate for the underlying operating system.
            # On Windows, toNativeSeparators("c:/winnt/system32") returns
            # "c:\winnt\system32".
            fname = QDir.toNativeSeparators(fname)

        if os.path.isdir(fname):
            self.download_folder_lineEdit.setText(fname)

    def okButtonPressed(self, button):
        # write user's input data to init file
        self.persepolis_setting.setValue(
            'add_link_initialization/ip', self.ip_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/port', self.port_spinBox.value())
        self.persepolis_setting.setValue(
            'add_link_initialization/proxy_user', self.proxy_user_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/download_user', self.download_user_lineEdit.text())

        if not(self.proxy_checkBox.isChecked()):
            ip = None
            port = None
            proxy_user = None
            proxy_passwd = None
        else:
            ip = self.ip_lineEdit.text()
            if not(ip):
                ip = None
            port = self.port_spinBox.value()
            if not(port):
                port = None
            proxy_user = self.proxy_user_lineEdit.text()
            if not(proxy_user):
                proxy_user = None
            proxy_passwd = self.proxy_pass_lineEdit.text()
            if not(proxy_passwd):
                proxy_passwd = None

        if not(self.download_checkBox.isChecked()):
            download_user = None
            download_passwd = None
        else:
            download_user = self.download_user_lineEdit.text()
            if not(download_user):
                download_user = None
            download_passwd = self.download_pass_lineEdit.text()
            if not(download_passwd):
                download_passwd = None

        if not(self.limit_checkBox.isChecked()):
            limit = 0
        else:
            if self.limit_comboBox.currentText() == "KiB/s":
                limit = str(self.limit_spinBox.value()) + str("K")
            else:
                limit = str(self.limit_spinBox.value()) + str("M")

        category = str(self.add_queue_comboBox.currentText())

        connections = self.connections_spinBox.value()
        download_path = self.download_folder_lineEdit.text()

        dict = {'out': None,
                'start_time': None,
                'end_time': None,
                'link': None,
                'ip': ip,
                'port': port,
                'proxy_user': proxy_user,
                'proxy_passwd': proxy_passwd,
                'download_user': download_user,
                'download_passwd': download_passwd,
                'connections': connections,
                'limit_value': limit,
                'download_path': download_path,
                'referer': None,
                'load_cookies': None,
                'user_agent': None,
                'header': None,
                'after_download': None
                }


# find checked links in links_table

        self.list_of_links.reverse()
        self.add_link_dictionary_list = []
        i = 0
        for row in range(self.links_table.rowCount()):
            item = self.links_table.item(row, 0)

            # if item is checked
            if (item.checkState() == 2):
                # Create a copy from dict and add it to add_link_dictionary_list
                self.add_link_dictionary_list.append(
                    deepcopy(dict))

                # get link and add it to dict
                link = self.links_table.item(row, 1).text()
                self.add_link_dictionary_list[i]['link'] = str(link)

                # add file name to the dict
                self.add_link_dictionary_list[i]['out'] = self.links_table.item(
                    row, 0).text()

                input_dict = self.list_of_links[row]

                keys_list = ['referer', 'header', 'user-agent', 'load_cookies']
                for key in keys_list:
                    if key in input_dict:
                        self.add_link_dictionary_list[i][key] = dict[key]

                i = i + 1

        # reverse list
        self.add_link_dictionary_list.reverse()

        # Create callback for mainwindow
        self.callback(self.add_link_dictionary_list, category)

        # close window
        self.close()

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        self.persepolis_setting.setValue('TextQueue/size', self.size())
        self.persepolis_setting.setValue('TextQueue/position', self.pos())
        self.persepolis_setting.sync()

        event.accept()

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'

        self.folder_pushButton.setIcon(QIcon(icons + 'folder'))
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))

persepolis/scripts/browser_plugin_queue.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from persepolis.scripts.play import playNotification
from persepolis.gui import resources # noqa: F401
from persepolis.constants import OS, APP_NAME, ORG_NAME, LONG_NAME
import subprocess
import platform
import os

try:
    from PySide6.QtCore import QSettings
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import QSettings
    from PyQt5.QtGui import QIcon

# platform
os_type = platform.system()

if os_type == OS.DARWIN:
    from persepolis.scripts.mac_notification import notifyMac

elif os_type == OS.WINDOWS:
    from persepolis.scripts.windows_notification import Windows_Notification

# notifySend use notify-send program in user's system for sending notifications
# and use playNotification function in play.py file for playing sound
# notifications


def notifySend(message1, message2, time, sound, parent=None):

    if os_type == OS.LINUX:
        notifications_path = '/usr/share/sounds/freedesktop/stereo/'
    elif os_type in OS.BSD_FAMILY:
        notifications_path = '/usr/local/share/sounds/freedesktop/stereo/'
    else:
        notifications_path = ''

    if sound == 'ok':
        file = os.path.join(notifications_path, 'complete.oga')
        playNotification(str(file))

    elif sound == 'fail':
        file = os.path.join(notifications_path, 'dialog-error.oga')
        playNotification(str(file))

    elif sound == 'warning':
        file = os.path.join(notifications_path, 'bell.oga')
        playNotification(str(file))

    elif sound == 'critical':
        file = os.path.join(notifications_path, 'power-plug.oga')
        playNotification(str(file))

    elif sound == 'queue':
        file = os.path.join(notifications_path, 'dialog-information.oga')
        playNotification(str(file))

    # load settings
    persepolis_setting = QSettings(ORG_NAME, APP_NAME)

    enable_notification = persepolis_setting.value('settings/notification')

    time = str(time)
    message1 = str(message1)
    message2 = str(message2)

    # using Qt notification or Native system notification
    if enable_notification == 'QT notification':
        parent.system_tray_icon.showMessage(
            message1, message2, QIcon.fromTheme('persepolis-tray', QIcon(':/persepolis-tray.svg')), 10000
        )
    else:
        if os_type in OS.UNIX_LIKE:
            subprocess.Popen(['notify-send', '--icon', APP_NAME,
                              '--app-name', LONG_NAME,
                              '--expire-time', time,
                              message1, message2],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False)

        elif os_type == OS.OSX:
            notifyMac(LONG_NAME, message1, message2)

        elif os_type == OS.WINDOWS:
            message = Windows_Notification(parent=parent, time=time, text1=message1,
                                           text2=message2, persepolis_setting=persepolis_setting)
            message.show()

persepolis/scripts/bubble.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import re
import subprocess
import urllib
import os
from ghermez import osAndDesktopEnvironment
from persepolis.scripts import logger
from persepolis.constants import OS

# get proxy function


def getProxy():
    socks_proxy = False

    # find os and desktop environment
    os_type, desktop = osAndDesktopEnvironment()

    # destop == 'ubuntu:GNOME' to destop == 'GNOME'
    tmp = re.search(r'.*:(.*)', desktop)
    if tmp is not None:
        desktop = tmp.group(1)

    # write in log
    platform = 'platform : ' + os_type
    logger.sendToLog(platform, "INFO")

    proxy = {}
    if os_type in OS.UNIX_LIKE:
        if desktop is None:
            desktop_env_type = 'Desktop Environment not detected!'
        else:
            desktop_env_type = 'Desktop environment: ' + str(desktop)

        logger.sendToLog(desktop_env_type, "INFO")

    # check if it is KDE
    if desktop == 'KDE':
        # creat empty list for proxies
        proxysource = {}

        # user home directory path
        home_address = os.path.expanduser("~")
        # get proxy file content
        try:
            plasma_proxy_config_file_path = os.path.join(
                home_address,
                '.config',
                'kioslaverc'
            )
        except Exception:
            logger.sendToLog('no proxy file detected', 'INFO')

        # check if proxy file exists
        if os.path.isfile(plasma_proxy_config_file_path):
            # read kde plasma proxy config file
            try:
                with open(plasma_proxy_config_file_path) as proxyfile:
                    for line in proxyfile:
                        name, var = line.partition("=")[::2]
                        proxysource[name.strip()] = str(var)
            except Exception:
                logger.sendToLog('no proxy file detected', 'INFO')

            # check proxy enabled as manually
            if proxysource['ProxyType'].split('\n')[0] == '1':
                # get ftp proxy
                try:
                    proxy['ftp_proxy_port'] = proxysource['ftpProxy'].split(' ')[1].replace("/", "").replace("\n", "")
                    proxy['ftp_proxy_ip'] = proxysource['ftpProxy'].split(' ')[0].split('//')[1]
                except Exception:
                    logger.sendToLog('no manual ftp proxy detected', 'INFO')

                # get http proxy
                try:
                    proxy['http_proxy_port'] = proxysource['httpProxy'].split(' ')[1].replace("/", "").replace("\n", "")
                    proxy['http_proxy_ip'] = proxysource['httpProxy'].split(' ')[0].split('//')[1]
                except Exception:
                    logger.sendToLog('no manual http proxy detected', 'INFO')

                # get https proxy
                try:
                    proxy['https_proxy_port'] = proxysource['httpsProxy'].split(
                        ' ')[1].replace("/", "").replace("\n", "")
                    proxy['https_proxy_ip'] = proxysource['httpsProxy'].split(' ')[0].split('//')[1]
                except Exception:
                    logger.sendToLog('no manual https proxy detected', 'INFO')

                # get socks proxy
                try:
                    socks_proxy = proxysource['socksProxy'].split(' ')[0].split('//')[1]

                except Exception:
                    socks_proxy = False

            # proxy disabled
            else:
                logger.sendToLog('no manual proxy detected', 'INFO')

        # proxy file not exists
        else:
            logger.sendToLog('no proxy file detected', 'INFO')

    # Detect proxy from GNOME Desktop        
    elif desktop == 'GNOME':
        process = subprocess.run(['gsettings', 'get', 'org.gnome.system.proxy', 'mode'], stdout=subprocess.PIPE)
        mode = re.search(r'manual' , process.stdout.decode('utf-8'))
        if mode is not None:    
            try:
                process = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.system.proxy.http', 'host'], stdout=subprocess.PIPE
                )
                proxy['http_proxy_ip'] = re.search(r"\'([\w0-9\.]+)\'" , process.stdout.decode('utf-8')).group(1)
                process = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.system.proxy.http', 'port'], stdout=subprocess.PIPE
                )
                proxy['http_proxy_port'] = process.stdout.decode('utf-8')
            except Exception:
                logger.sendToLog('no http proxy detected', 'INFO')

            try:
                process = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.system.proxy.https', 'host'], stdout=subprocess.PIPE
                )
                proxy['https_proxy_ip'] = re.search(r"\'([\w0-9\.]+)\'" , process.stdout.decode('utf-8')).group(1)
                process = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.system.proxy.https', 'port'], stdout=subprocess.PIPE
                )
                proxy['https_proxy_port'] = process.stdout.decode('utf-8')
            except Exception:
                logger.sendToLog('no https proxy detected', 'INFO')

            try:
                process = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.system.proxy.ftp', 'host'], stdout=subprocess.PIPE
                )
                proxy['ftp_proxy_ip'] = re.search(r"\'([\w0-9\.]+)\'" , process.stdout.decode('utf-8')).group(1)
                process = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.system.proxy.ftp', 'port'], stdout=subprocess.PIPE
                )
                proxy['ftp_proxy_port'] = process.stdout.decode('utf-8')
            except Exception:
                logger.sendToLog('no ftp proxy detected', 'INFO')
            
            try:
                process = subprocess.run(
                    ['gsettings', 'get', 'org.gnome.system.proxy.socks', 'host'], stdout=subprocess.PIPE
                )
                # value = re.search(r"\'([\w0-9\.]+)\'" , process.stdout.decode('utf-8')).group(1)
                socks_proxy = True
            except Exception:
                socks_proxy = False
        
        else:
            logger.sendToLog('no manual proxy detected', 'INFO')

    # if it is windows,mac and other linux desktop
    else:
        # get proxies
        proxysource = urllib.request.getproxies()
        # get http proxy
        try:
            proxy['http_proxy_ip'] = proxysource['http'].split(':')[1].replace('//', '')
            proxy['http_proxy_port'] = proxysource['http'].split(':')[2].replace("/", "").replace("\n", "")
        except Exception:
            logger.sendToLog('no http proxy detected', 'INFO')

        # get https proxy
        try:
            proxy['https_proxy_ip'] = proxysource['https'].split(':')[1].replace('//', '')
            proxy['https_proxy_port'] = proxysource['https'].split(':')[2].replace("/", "").replace("\n", "")
        except Exception:
            logger.sendToLog('no https proxy detected', 'INFO')

        # get ftp proxy
        try:
            proxy['ftp_proxy_ip'] = proxysource['ftp'].split(':')[1].replace('//', '')
            proxy['ftp_proxy_port'] = proxysource['ftp'].split(':')[2].replace("/", "").replace("\n", "")
        except Exception:
            logger.sendToLog('no ftp proxy detected', 'INFO')

        # get socks proxy
        try:
            # if it is unity
            if desktop == 'Unity7':
                socks_proxy = proxysource['all'].split(':')[1].replace('//', '')
            # if it is Mac OS
            elif os_type == OS.OSX:

                validKeys = ['SOCKSEnable']

                # get proxies list using scutil command and parse it in tmp list
                mac_tmp_proxies_list = {}
                proxyList = subprocess.run(['scutil', '--proxy'], stdout=subprocess.PIPE)
                for line in proxyList.stdout.decode('utf-8').split('\n'):
                    words = line.split()
                    if len(words) == 3 and words[0] in validKeys:
                        mac_tmp_proxies_list[words[0]] = words[2]

                if mac_tmp_proxies_list['SOCKSEnable'] == '1':
                    socks_proxy = True
                else:
                    socks_proxy = False
            # others except KDE,Mac OS,gnome,unity7
            else:
                socks_proxy = proxysource['socks'].split(':')[1].replace('//', '')
        except Exception:
            socks_proxy = False

    # check if just socks proxy exists
    key_is_available = False
    key_list = ['http_proxy_ip', 'https_proxy_ip', 'ftp_proxy_ip']
    for key in key_list:
        if key in proxy.keys():
            key_is_available = True

    if not key_is_available and socks_proxy:
        # all print just for debugging
        socks_message = "persepolis and aria2 don't support socks\n\
        you must convert socks proxy to http proxy.\n\
        Please read this for more help:\n\
            https://github.com/persepolisdm/persepolis/wiki/Privoxy"
        logger.sendToLog(socks_message, 'ERROR')

    # return results
    proxy_log_message = 'proxy: ' + str(proxy)
    logger.sendToLog(proxy_log_message, 'INFO')
    return proxy

persepolis/scripts/check_proxy.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from ghermez import determineConfigFolder
from persepolis.scripts.osCommands import remove, removeDir
from persepolis.scripts.newopen import readList
import os

from ghermez import DataBase

# config_folder
config_folder = determineConfigFolder()

download_info_folder = os.path.join(config_folder, "download_info")


# download_list_file contains GID of all downloads
download_list_file = os.path.join(config_folder, "download_list_file")

# download_list_file_active for active downloads
download_list_file_active = os.path.join(
    config_folder, "download_list_file_active")

# queues_list contains queues name
queues_list_file = os.path.join(config_folder, 'queues_list')

# category_folder contains some file , and every files named with queues .
# every file contains gid of downloads for that queue
category_folder = os.path.join(config_folder, 'category_folder')

# queue_info_folder is contains queues information(start time,end
# time,limit speed , ...)
queue_info_folder = os.path.join(config_folder, "queue_info")

# single_downloads_list_file contains gid of non categorized downloads
single_downloads_list_file = os.path.join(category_folder, "Single Downloads")


# this script for compatibility between Version 2 and 3

def compatibility():
    if os.path.isfile(queues_list_file):
        persepolis_db = DataBase()

        # add categories to category_db_table in data_base
        f = open(queues_list_file)
        queues_list = f.readlines()
        f.close()

        # remove queues_list_file
        remove(queues_list_file)
    else:
        return

    category_list = ['All Downloads', 'Single Downloads']
    for line in queues_list:
        queue_name = line.strip()
        category_list.append(queue_name)

    for category in category_list:
        gid_list = []

        if category == 'All Downloads':
            category_info_file = download_list_file
        else:
            category_info_file = os.path.join(category_folder, category)

        f = open(category_info_file)
        category_info_file_list = f.readlines()
        f.close()

        for item in category_info_file_list:
            gid = item.strip()
            gid_list.append(gid)

        category_dict = {'category': category,
                         'start_time_enable': 'no',
                         'start_time': '0:0',
                         'end_time_enable': 'no',
                         'end_time': '0:0',
                         'reverse': 'no',
                         'limit_enable': 'no',
                         'limit_value': '0K',
                         'after_download': 'no',
                         'gid_list': str(gid_list)
                         }

        # add category to data_base
        if category == 'All Downloads' or category == 'Single Downloads':
            persepolis_db.updateCategoryTable([category_dict])
        else:
            persepolis_db.insertInCategoryTable(category_dict)

    # add items to download_db_table in data base
    f_download_list_file = open(download_list_file)
    download_list_file_lines = f_download_list_file.readlines()
    f_download_list_file.close()

    for line in download_list_file_lines:
        gid = line.strip()
        download_info_file = os.path.join(download_info_folder, gid)

        download_info_file_list = readList(download_info_file)
        add_link_dictionary = download_info_file_list[9]

        dict = {'file_name': download_info_file_list[0],
                'status': download_info_file_list[1],
                'size': download_info_file_list[2],
                'downloaded_size': download_info_file_list[3],
                'percent': download_info_file_list[4],
                'connections': download_info_file_list[5],
                'rate': download_info_file_list[6],
                'estimate_time_left': download_info_file_list[7],
                'gid': download_info_file_list[8],
                'link': add_link_dictionary['link'],
                'first_try_date': download_info_file_list[10],
                'last_try_date': download_info_file_list[11],
                'category': download_info_file_list[12]}

        add_link_dictionary['gid'] = download_info_file_list[8]

        if 'user-agent' in add_link_dictionary.keys():
            add_link_dictionary['user_agent'] = add_link_dictionary.pop('user-agent')

        if 'load-cookies' in add_link_dictionary.keys():
            add_link_dictionary['load_cookies'] = add_link_dictionary.pop('load-cookies')

        add_link_dictionary['limit_value'] = 0

        keys_list = ['gid',
                     'out',
                     'start_time',
                     'end_time',
                     'link',
                     'ip',
                     'port',
                     'proxy_user',
                     'proxy_passwd',
                     'download_user',
                     'download_passwd',
                     'connections',
                     'limit_value',
                     'download_path',
                     'referer',
                     'load_cookies',
                     'user_agent',
                     'header',
                     'after_download']

        for key in keys_list:
                # if a key is missed in dict,
                # then add this key to the dict and assign None value for the key.
            if key not in add_link_dictionary.keys():
                add_link_dictionary[key] = None

        # write information in data_base
        persepolis_db.insertInDownloadTable([dict])
        persepolis_db.insertInAddLinkTable([add_link_dictionary])

    # close connections
    # persepolis_db.closeConnections()
    del persepolis_db

    # remove unwanted files and folders
    for file in [download_list_file, download_list_file_active]:
        remove(file)

    for folder in [category_folder, queue_info_folder]:
        removeDir(folder)

persepolis/scripts/compatibility.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from ghermez import determineConfigFolder
from time import sleep
import sqlite3
import random
import ast
import os

# download manager config folder .
config_folder = determineConfigFolder()

# persepolis tmp folder path
persepolis_tmp = os.path.join(config_folder, 'persepolis_tmp')


# This class manages TempDB
# TempDB contains gid of active downloads in every session.
class TempDB():
    def __init__(self):
        # temp_db saves in RAM
        # temp_db_connection

        self.temp_db_connection = sqlite3.connect(':memory:', check_same_thread=False)

        # temp_db_cursor
        self.temp_db_cursor = self.temp_db_connection.cursor()

        # create a lock for data base
        self.lock = False

    # this method locks data base.
    # this is pervent accessing data base simultaneously.
    def lockCursor(self):
        while self.lock:
            rand_float = random.uniform(0, 0.5)
            sleep(rand_float)

        self.lock = True

    # temp_db_table contains gid of active downloads.

    def createTables(self):
        # lock data base
        self.lockCursor()
        self.temp_db_cursor.execute("""CREATE TABLE IF NOT EXISTS single_db_table(
                                                                                ID INTEGER,
                                                                                gid TEXT PRIMARY KEY,
                                                                                status TEXT,
                                                                                shutdown TEXT
                                                                                )""")

        self.temp_db_cursor.execute("""CREATE TABLE IF NOT EXISTS queue_db_table(
                                                                                ID INTEGER,
                                                                                category TEXT PRIMARY KEY,
                                                                                shutdown TEXT
                                                                                )""")

        self.temp_db_connection.commit()
        self.lock = False

    # insert new item in single_db_table
    def insertInSingleTable(self, gid):
        # lock data base
        self.lockCursor()
        self.temp_db_cursor.execute("""INSERT INTO single_db_table VALUES(
                                                                NULL,
                                                                '{}',
                                                                'active',
                                                                NULL)""".format(gid))

        self.temp_db_connection.commit()
        self.lock = False

    # insert new item in queue_db_table

    def insertInQueueTable(self, category):
        # lock data base
        self.lockCursor()
        self.temp_db_cursor.execute("""INSERT INTO queue_db_table VALUES(
                                                                NULL,
                                                                '{}',
                                                                NULL)""".format(category))

        self.temp_db_connection.commit()
        self.lock = False

    # this method updates single_db_table

    def updateSingleTable(self, dict):
        # lock data base
        self.lockCursor()
        keys_list = ['gid',
                     'shutdown',
                     'status'
                     ]

        for key in keys_list:
            # if a key is missed in dict,
            # then add this key to the dict and assign None value for the key.
            if key not in dict.keys():
                dict[key] = None

        # update data base if value for the keys is not None
        self.temp_db_cursor.execute("""UPDATE single_db_table SET shutdown = coalesce(:shutdown, shutdown),
                                                                status = coalesce(:status, status)
                                                                WHERE gid = :gid""", dict)

        self.temp_db_connection.commit()

        self.lock = False

    # this method updates queue_db_table
    def updateQueueTable(self, dict):
        # lock data base
        self.lockCursor()
        keys_list = ['category',
                     'shutdown']

        for key in keys_list:
            # if a key is missed in dict,
            # then add this key to the dict and assign None value for the key.
            if key not in dict.keys():
                dict[key] = None

        # update data base if value for the keys is not None
        self.temp_db_cursor.execute("""UPDATE queue_db_table SET shutdown = coalesce(:shutdown, shutdown)
                                                                WHERE category = :category""", dict)

        self.temp_db_connection.commit()

        self.lock = False

    # this method returns gid of active downloads
    def returnActiveGids(self):
        # lock data base
        self.lockCursor()

        self.temp_db_cursor.execute("""SELECT gid FROM single_db_table WHERE status = 'active'""")

        list = self.temp_db_cursor.fetchall()

        self.lock = False
        gid_list = []

        for tuple in list:
            gid = tuple[0]
            gid_list.append(gid)

        return gid_list

    # this method returns shutdown value for specific gid
    def returnGid(self, gid):
        # lock data base
        self.lockCursor()
        self.temp_db_cursor.execute("""SELECT shutdown, status FROM single_db_table WHERE gid = '{}'""".format(gid))

        list = self.temp_db_cursor.fetchall()

        self.lock = False

        tuple = list[0]

        dict = {'shutdown': str(tuple[0]),
                'status': tuple[1]}

        return dict

    # This method returns values of columns for specific category

    def returnCategory(self, category):
        # lock data base
        self.lockCursor()
        self.temp_db_cursor.execute("""SELECT shutdown FROM queue_db_table WHERE category = '{}'""".format(category))

        list = self.temp_db_cursor.fetchall()

        self.lock = False

        tuple = list[0]

        dict = {'shutdown': tuple[0]}

        return dict

    def resetDataBase(self):
        # lock data base
        self.lockCursor()

        # delete all items
        self.temp_db_cursor.execute("""DELETE FROM single_db_table""")
        self.temp_db_cursor.execute("""DELETE FROM queue_db_table""")

        # release lock
        self.lock = False

    # close connections

    def closeConnections(self):
        # lock data base
        self.lockCursor()
        self.temp_db_cursor.close()
        self.temp_db_connection.close()
        self.lock = False


# plugins.db is store links, when browser plugins are send new links.
# This class is managing plugin.db
class PluginsDB():
    def __init__(self):
        # plugins.db file path
        plugins_db_path = os.path.join(persepolis_tmp, 'plugins.db')

        # plugins_db_connection
        self.plugins_db_connection = sqlite3.connect(plugins_db_path, check_same_thread=False)

        # plugins_db_cursor
        self.plugins_db_cursor = self.plugins_db_connection.cursor()

        # create a lock for data base
        self.lock = False

    # this method locks data base.
    # this is pervent accessing data base simultaneously.
    def lockCursor(self):
        while self.lock:
            rand_float = random.uniform(0, 0.5)
            sleep(rand_float)

        self.lock = True

    # plugins_db_table contains links that sends by browser plugins.

    def createTables(self):
        # lock data base
        self.lockCursor()

        self.plugins_db_cursor.execute("""CREATE TABLE IF NOT EXISTS plugins_db_table(
                                                                                ID INTEGER PRIMARY KEY,
                                                                                link TEXT,
                                                                                referer TEXT,
                                                                                load_cookies TEXT,
                                                                                user_agent TEXT,
                                                                                header TEXT,
                                                                                out TEXT,
                                                                                status TEXT
                                                                                )""")
        self.plugins_db_connection.commit()

        # release lock
        self.lock = False

    # insert new items in plugins_db_table
    def insertInPluginsTable(self, list):
        # lock data base
        self.lockCursor()

        for dict in list:
            self.plugins_db_cursor.execute("""INSERT INTO plugins_db_table VALUES(
                                                                        NULL,
                                                                        :link,
                                                                        :referer,
                                                                        :load_cookies,
                                                                        :user_agent,
                                                                        :header,
                                                                        :out,
                                                                        'new'
                                                                            )""", dict)

        self.plugins_db_connection.commit()
        # release lock
        self.lock = False

# this method returns all new links in plugins_db_table
    def returnNewLinks(self):
        # lock data base
        self.lockCursor()

        self.plugins_db_cursor.execute("""SELECT link, referer, load_cookies, user_agent, header, out
                                            FROM plugins_db_table
                                            WHERE status = 'new'""")

        list = self.plugins_db_cursor.fetchall()

        # chang all rows status to 'old'
        self.plugins_db_cursor.execute("""UPDATE plugins_db_table SET status = 'old'
                                            WHERE status = 'new'""")

        # commit changes
        self.plugins_db_connection.commit()

        # release lock
        self.lock = False

        # create new_list
        new_list = []

        # put the information in tuples in dictionary format and add it to new_list
        for tuple in list:
            dict = {'link': tuple[0],
                    'referer': tuple[1],
                    'load_cookies': tuple[2],
                    'user_agent': tuple[3],
                    'header': tuple[4],
                    'out': tuple[5]
                    }

            new_list.append(dict)

        # return results in list format!
        # every member of this list is a dictionary.
        # every dictionary contains download information
        return new_list

    # delete old links from data base
    def deleteOldLinks(self):
        # lock data base
        self.lockCursor()

        self.plugins_db_cursor.execute("""DELETE FROM plugins_db_table WHERE status = 'old'""")
        # commit changes
        self.plugins_db_connection.commit()

        # release lock
        self.lock = False

    # close connections
    def closeConnections(self):
        # lock data base
        self.lockCursor()

        self.plugins_db_cursor.close()
        self.plugins_db_connection.close()

        # release lock
        self.lock = False


# persepolis main data base contains downloads information
# This class is managing persepolis.db
class PersepolisDB():
    def __init__(self):
        # persepolis.db file path
        persepolis_db_path = os.path.join(config_folder, 'persepolis.db')

        # persepolis_db_connection
        self.persepolis_db_connection = sqlite3.connect(persepolis_db_path, check_same_thread=False)

        # turn FOREIGN KEY Support on!
        self.persepolis_db_connection.execute('pragma foreign_keys=ON')

        # persepolis_db_cursor
        self.persepolis_db_cursor = self.persepolis_db_connection.cursor()

        # Create a lock for data base
        self.lock = False

    # this method locks data base.
    # this is pervent accessing data base simultaneously.
    def lockCursor(self):

        while self.lock:
            rand_float = random.uniform(0, 0.5)
            sleep(rand_float)

        self.lock = True

    # queues_list contains name of categories and category settings
    def createTables(self):

        # lock data base
        self.lockCursor()
        # Create category_db_table and add 'All Downloads' and 'Single Downloads' to it
        self.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS category_db_table(
                                                category TEXT PRIMARY KEY,
                                                start_time_enable TEXT,
                                                start_time TEXT,
                                                end_time_enable TEXT,
                                                end_time TEXT,
                                                reverse TEXT,
                                                limit_enable TEXT,
                                                limit_value TEXT,
                                                after_download TEXT,
                                                gid_list TEXT
                                            )""")

        # download table contains download table download items information
        self.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS download_db_table(
                                                file_name TEXT,
                                                status TEXT,
                                                size TEXT,
                                                downloaded_size TEXT,
                                                percent TEXT,
                                                connections TEXT,
                                                rate TEXT,
                                                estimate_time_left TEXT,
                                                gid TEXT PRIMARY KEY,
                                                link TEXT,
                                                first_try_date TEXT,
                                                last_try_date TEXT,
                                                category TEXT,
                                                FOREIGN KEY(category) REFERENCES category_db_table(category)
                                                ON UPDATE CASCADE
                                                ON DELETE CASCADE
                                            )""")

        # addlink_db_table contains addlink window download information
        self.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS addlink_db_table(
                                                ID INTEGER PRIMARY KEY,
                                                gid TEXT,
                                                out TEXT,
                                                start_time TEXT,
                                                end_time TEXT,
                                                link TEXT,
                                                ip TEXT,
                                                port TEXT,
                                                proxy_user TEXT,
                                                proxy_passwd TEXT,
                                                download_user TEXT,
                                                download_passwd TEXT,
                                                connections TEXT,
                                                limit_value TEXT,
                                                download_path TEXT,
                                                referer TEXT,
                                                load_cookies TEXT,
                                                user_agent TEXT,
                                                header TEXT,
                                                after_download TEXT,
                                                FOREIGN KEY(gid) REFERENCES download_db_table(gid) 
                                                ON UPDATE CASCADE 
                                                ON DELETE CASCADE 
                                            )""")

        # video_finder_db_table contains addlink window download information
        self.persepolis_db_cursor.execute("""CREATE TABLE IF NOT EXISTS video_finder_db_table(
                                                ID INTEGER PRIMARY KEY,
                                                video_gid TEXT,
                                                audio_gid TEXT,
                                                video_completed TEXT,
                                                audio_completed TEXT,
                                                muxing_status TEXT,
                                                checking TEXT,
                                                download_path TEXT,
                                                FOREIGN KEY(video_gid) REFERENCES download_db_table(gid)
                                                ON DELETE CASCADE,
                                                FOREIGN KEY(audio_gid) REFERENCES download_db_table(gid)
                                                ON DELETE CASCADE
                                            )""")

        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

        # add 'All Downloads' and 'Single Downloads' to the category_db_table if they wasn't added.
        answer = self.searchCategoryInCategoryTable('All Downloads')

        if not(answer):
            all_downloads_dict = {'category': 'All Downloads',
                                  'start_time_enable': 'no',
                                  'start_time': '0:0',
                                  'end_time_enable': 'no',
                                  'end_time': '0:0',
                                  'reverse': 'no',
                                  'limit_enable': 'no',
                                  'limit_value': '0K',
                                  'after_download': 'no',
                                  'gid_list': '[]'
                                  }

            single_downloads_dict = {'category': 'Single Downloads',
                                     'start_time_enable': 'no',
                                     'start_time': '0:0',
                                     'end_time_enable': 'no',
                                     'end_time': '0:0',
                                     'reverse': 'no',
                                     'limit_enable': 'no',
                                     'limit_value': '0K',
                                     'after_download': 'no',
                                     'gid_list': '[]'
                                     }

            self.insertInCategoryTable(all_downloads_dict)
            self.insertInCategoryTable(single_downloads_dict)

        # add default queue with the name 'Scheduled Downloads'
        answer = self.searchCategoryInCategoryTable('Scheduled Downloads')
        if not(answer):
            scheduled_downloads_dict = {'category': 'Scheduled Downloads',
                                        'start_time_enable': 'no',
                                        'start_time': '0:0',
                                        'end_time_enable': 'no',
                                        'end_time': '0:0',
                                        'reverse': 'no',
                                        'limit_enable': 'no',
                                        'limit_value': '0K',
                                        'after_download': 'no',
                                        'gid_list': '[]'
                                        }
            self.insertInCategoryTable(scheduled_downloads_dict)

    # insert new category in category_db_table
    def insertInCategoryTable(self, dict):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute("""INSERT INTO category_db_table VALUES(
                                                                            :category,
                                                                            :start_time_enable,
                                                                            :start_time,
                                                                            :end_time_enable,
                                                                            :end_time,
                                                                            :reverse,
                                                                            :limit_enable,
                                                                            :limit_value,
                                                                            :after_download,
                                                                            :gid_list
                                                                            )""", dict)
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    # insert in to download_db_table in persepolis.db

    def insertInDownloadTable(self, list):
        # lock data base
        self.lockCursor()

        for dict in list:
            self.persepolis_db_cursor.execute("""INSERT INTO download_db_table VALUES(
                                                                            :file_name,
                                                                            :status,
                                                                            :size,
                                                                            :downloaded_size,
                                                                            :percent,
                                                                            :connections,
                                                                            :rate,
                                                                            :estimate_time_left,
                                                                            :gid,
                                                                            :link,
                                                                            :first_try_date,
                                                                            :last_try_date,
                                                                            :category
                                                                            )""", dict)

        # commit changes
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

        if len(list) != 0:
            # item must be inserted to gid_list of 'All Downloads' and gid_list of category
            # find download category and gid
            category = dict['category']

            # get category_dict from data base
            category_dict = self.searchCategoryInCategoryTable(category)

            # get all_downloads_dict from data base
            all_downloads_dict = self.searchCategoryInCategoryTable('All Downloads')

            # get gid_list
            category_gid_list = category_dict['gid_list']

            all_downloads_gid_list = all_downloads_dict['gid_list']

            for dict in list:
                gid = dict['gid']

                # add gid of item to gid_list
                category_gid_list.append(gid)
                all_downloads_gid_list.append(gid)

            # update category_db_table
            self.updateCategoryTable([all_downloads_dict])
            self.updateCategoryTable([category_dict])

    # insert in addlink table in persepolis.db

    def insertInAddLinkTable(self, list):
        # lock data base
        self.lockCursor()

        for dict in list:
            # first column and after download column is NULL
            self.persepolis_db_cursor.execute("""INSERT INTO addlink_db_table VALUES(NULL,
                                                                                :gid,
                                                                                :out,
                                                                                :start_time,
                                                                                :end_time,
                                                                                :link,
                                                                                :ip,
                                                                                :port,
                                                                                :proxy_user,
                                                                                :proxy_passwd,
                                                                                :download_user,
                                                                                :download_passwd,
                                                                                :connections,
                                                                                :limit_value,
                                                                                :download_path,
                                                                                :referer,
                                                                                :load_cookies,
                                                                                :user_agent,
                                                                                :header,
                                                                                NULL
                                                                                )""", dict)
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    def insertInVideoFinderTable(self, list):
        # lock data base
        self.lockCursor()

        for dictionary in list:
            # first column is NULL
            self.persepolis_db_cursor.execute("""INSERT INTO video_finder_db_table VALUES(NULL,
                                                                                :video_gid,
                                                                                :audio_gid,
                                                                                :video_completed,
                                                                                :audio_completed,
                                                                                :muxing_status,
                                                                                :checking,
                                                                                :download_path
                                                                                )""", dictionary)
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    def searchGidInVideoFinderTable(self, gid):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute(
            """SELECT * FROM video_finder_db_table
            WHERE audio_gid = '{}' OR video_gid = '{}'""".format(str(gid), str(gid)))
        result_list = self.persepolis_db_cursor.fetchall()

        # job is done
        self.lock = False

        if result_list:
            tuple = result_list[0]
        else:
            return None

        dictionary = {'video_gid': tuple[1],
                      'audio_gid': tuple[2],
                      'video_completed': tuple[3],
                      'audio_completed': tuple[4],
                      'muxing_status': tuple[5],
                      'checking': tuple[6],
                      'download_path': tuple[7]}

        # return the results
        return dictionary

    # return download information in download_db_table with special gid.
    def searchGidInDownloadTable(self, gid):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute("""SELECT * FROM download_db_table WHERE gid = '{}'""".format(str(gid)))
        list = self.persepolis_db_cursor.fetchall()

        # job is done! open the lock
        self.lock = False

        if list:
            tuple = list[0]
        else:
            return None

        dict = {'file_name': tuple[0],
                'status': tuple[1],
                'size': tuple[2],
                'downloaded_size': tuple[3],
                'percent': tuple[4],
                'connections': tuple[5],
                'rate': tuple[6],
                'estimate_time_left': tuple[7],
                'gid': tuple[8],
                'link': tuple[9],
                'first_try_date': tuple[10],
                'last_try_date': tuple[11],
                'category': tuple[12]
                }

        # return results
        return dict

    # return all items in download_db_table
    # '*' for category, cause that method returns all items.
    def returnItemsInDownloadTable(self, category=None):
        # lock data base
        self.lockCursor()

        if category:
            self.persepolis_db_cursor.execute(
                """SELECT * FROM download_db_table WHERE category = '{}'""".format(category))
        else:
            self.persepolis_db_cursor.execute("""SELECT * FROM download_db_table""")

        rows = self.persepolis_db_cursor.fetchall()

        # job is done! open the lock
        self.lock = False

        downloads_dict = {}
        for tuple in rows:
            # change format of tuple to dictionary
            dict = {'file_name': tuple[0],
                    'status': tuple[1],
                    'size': tuple[2],
                    'downloaded_size': tuple[3],
                    'percent': tuple[4],
                    'connections': tuple[5],
                    'rate': tuple[6],
                    'estimate_time_left': tuple[7],
                    'gid': tuple[8],
                    'link': tuple[9],
                    'first_try_date': tuple[10],
                    'last_try_date': tuple[11],
                    'category': tuple[12]
                    }

            # add dict to the downloads_dict
            # gid is key and dict is value
            downloads_dict[tuple[8]] = dict

        return downloads_dict

    # this method checks existence of a link in addlink_db_table

    def searchLinkInAddLinkTable(self, link):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute("""SELECT * FROM addlink_db_table WHERE link = (?)""", (link,))
        list = self.persepolis_db_cursor.fetchall()

        # job is done! open the lock
        self.lock = False

        if list:
            return True
        else:
            return False

    # return download information in addlink_db_table with special gid.

    def searchGidInAddLinkTable(self, gid):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute("""SELECT * FROM addlink_db_table WHERE gid = '{}'""".format(str(gid)))
        list = self.persepolis_db_cursor.fetchall()

        # job is done! open the lock
        self.lock = False

        if list:
            tuple = list[0]
        else:
            return None

        dict = {'gid': tuple[1],
                'out': tuple[2],
                'start_time': tuple[3],
                'end_time': tuple[4],
                'link': tuple[5],
                'ip': tuple[6],
                'port': tuple[7],
                'proxy_user': tuple[8],
                'proxy_passwd': tuple[9],
                'download_user': tuple[10],
                'download_passwd': tuple[11],
                'connections': tuple[12],
                'limit_value': tuple[13],
                'download_path': tuple[14],
                'referer': tuple[15],
                'load_cookies': tuple[16],
                'user_agent': tuple[17],
                'header': tuple[18],
                'after_download': tuple[19]
                }

        return dict

    # return items in addlink_db_table
    # '*' for category, cause that method returns all items.

    def returnItemsInAddLinkTable(self, category=None):
        # lock data base
        self.lockCursor()

        if category:
            self.persepolis_db_cursor.execute(
                """SELECT * FROM addlink_db_table WHERE category = '{}'""".format(category))
        else:
            self.persepolis_db_cursor.execute("""SELECT * FROM addlink_db_table""")

        rows = self.persepolis_db_cursor.fetchall()

        # job is done! open the lock
        self.lock = False

        addlink_dict = {}
        for tuple in rows:
            # change format of tuple to dictionary
            dict = {'gid': tuple[1],
                    'out': tuple[2],
                    'start_time': tuple[3],
                    'end_time': tuple[4],
                    'link': tuple[5],
                    'ip': tuple[6],
                    'port': tuple[7],
                    'proxy_user': tuple[8],
                    'proxy_passwd': tuple[9],
                    'download_user': tuple[10],
                    'download_passwd': tuple[11],
                    'connections': tuple[12],
                    'limit_value': tuple[13],
                    'download_path': tuple[13],
                    'referer': tuple[14],
                    'load_cookies': tuple[15],
                    'user_agent': tuple[16],
                    'header': tuple[17],
                    'after_download': tuple[18]
                    }

            # add dict to the addlink_dict
            # gid as key and dict as value
            addlink_dict[tuple[1]] = dict

        return addlink_dict


# this method updates download_db_table

    def updateDownloadTable(self, list):
        # lock data base
        self.lockCursor()

        keys_list = ['file_name',
                     'status',
                     'size',
                     'downloaded_size',
                     'percent',
                     'connections',
                     'rate',
                     'estimate_time_left',
                     'gid',
                     'link',
                     'first_try_date',
                     'last_try_date',
                     'category'
                     ]

        for dict in list:
            for key in keys_list:
                # if a key is missed in dict,
                # then add this key to the dict and assign None value for the key.
                if key not in dict.keys():
                    dict[key] = None

            # update data base if value for the keys is not None
            self.persepolis_db_cursor.execute("""UPDATE download_db_table SET
                                            file_name = coalesce(:file_name, file_name),
                                            status = coalesce(:status, status),
                                            size = coalesce(:size, size),
                                            downloaded_size = coalesce(:downloaded_size, downloaded_size),
                                            percent = coalesce(:percent, percent),
                                            connections = coalesce(:connections, connections),
                                            rate = coalesce(:rate, rate),
                                            estimate_time_left = coalesce(:estimate_time_left, estimate_time_left),
                                            link = coalesce(:link, link),
                                            first_try_date = coalesce(:first_try_date, first_try_date),
                                            last_try_date = coalesce(:last_try_date, last_try_date),
                                            category = coalesce(:category, category)
                                            WHERE gid = :gid""", dict)

        # commit the changes
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False


# this method updates category_db_table

    def updateCategoryTable(self, list):
        # lock data base
        self.lockCursor()

        keys_list = ['category',
                     'start_time_enable',
                     'start_time',
                     'end_time_enable',
                     'end_time',
                     'reverse',
                     'limit_enable',
                     'limit_value',
                     'after_download',
                     'gid_list']

        for dict in list:

            # format of gid_list is list and must be converted to string for sqlite3
            if 'gid_list' in dict.keys():
                dict['gid_list'] = str(dict['gid_list'])

            for key in keys_list:
                # if a key is missed in dict,
                # then add this key to the dict and assign None value for the key.
                if key not in dict.keys():
                    dict[key] = None

            # update data base if value for the keys is not None
            self.persepolis_db_cursor.execute("""UPDATE category_db_table SET
                                            start_time_enable = coalesce(:start_time_enable, start_time_enable),
                                            start_time = coalesce(:start_time, start_time),
                                            end_time_enable = coalesce(:end_time_enable, end_time_enable),
                                            end_time = coalesce(:end_time, end_time),
                                            reverse = coalesce(:reverse, reverse),
                                            limit_enable = coalesce(:limit_enable, limit_enable),
                                            limit_value = coalesce(:limit_value, limit_value),
                                            after_download = coalesce(:after_download, after_download),
                                            gid_list = coalesce(:gid_list, gid_list)
                                            WHERE category = :category""", dict)

        # commit changes
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False


# this method updates addlink_db_table

    def updateAddLinkTable(self, list):
        # lock data base
        self.lockCursor()

        keys_list = ['gid',
                     'out',
                     'start_time',
                     'end_time',
                     'link',
                     'ip',
                     'port',
                     'proxy_user',
                     'proxy_passwd',
                     'download_user',
                     'download_passwd',
                     'connections',
                     'limit_value',
                     'download_path',
                     'referer',
                     'load_cookies',
                     'user_agent',
                     'header',
                     'after_download']

        for dict in list:
            for key in keys_list:
                # if a key is missed in dict,
                # then add this key to the dict and assign None value for the key.
                if key not in dict.keys():
                    dict[key] = None

            # update data base if value for the keys is not None
            self.persepolis_db_cursor.execute("""UPDATE addlink_db_table SET
                                            out = coalesce(:out, out),
                                            start_time = coalesce(:start_time, start_time),
                                            end_time = coalesce(:end_time, end_time),
                                            link = coalesce(:link, link),
                                            ip = coalesce(:ip, ip),
                                            port = coalesce(:port, port),
                                            proxy_user = coalesce(:proxy_user, proxy_user),
                                            proxy_passwd = coalesce(:proxy_passwd, proxy_passwd),
                                            download_user = coalesce(:download_user, download_user),
                                            download_passwd = coalesce(:download_passwd, download_passwd),
                                            connections = coalesce(:connections, connections),
                                            limit_value = coalesce(:limit_value, limit_value),
                                            download_path = coalesce(:download_path, download_path),
                                            referer = coalesce(:referer, referer),
                                            load_cookies = coalesce(:load_cookies, load_cookies),
                                            user_agent = coalesce(:user_agent, user_agent),
                                            header = coalesce(:header, header),
                                            after_download = coalesce(:after_download , after_download)
                                            WHERE gid = :gid""", dict)
        # commit the changes!
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    def updateVideoFinderTable(self, list):

        # lock data base
        self.lockCursor()

        keys_list = ['video_gid',
                     'audio_gid',
                     'video_completed',
                     'audio_completed',
                     'muxing_status',
                     'checking']

        for dictionary in list:
            for key in keys_list:
                # if a key is missed in dict,
                # then add this key to the dict and assign None value for the key.
                if key not in dictionary.keys():
                    dictionary[key] = None

            if dictionary['video_gid']:
                # update data base if value for the keys is not None
                self.persepolis_db_cursor.execute("""UPDATE video_finder_db_table SET
                                                video_completed = coalesce(:video_completed, video_completed),
                                                audio_completed = coalesce(:audio_completed, audio_completed),
                                                muxing_status = coalesce(:muxing_status, muxing_status),
                                                checking = coalesce(:checking, checking),
                                                download_path = coalesce(:download_path, download_path)
                                                WHERE video_gid = :video_gid""", dictionary)
            elif dictionary['audio_gid']:
                # update data base if value for the keys is not None
                self.persepolis_db_cursor.execute("""UPDATE video_finder_db_table SET
                                                video_completed = coalesce(:video_completed, video_completed),
                                                audio_completed = coalesce(:audio_completed, audio_completed),
                                                muxing_status = coalesce(:muxing_status, muxing_status),
                                                checking = coalesce(:checking, checking),
                                                download_path = coalesce(:download_path, download_path)
                                                WHERE audio_gid = :audio_gid""", dictionary)

        # commit the changes!
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    def setDefaultGidInAddlinkTable(self, gid, start_time=False, end_time=False, after_download=False):
        # lock data base
        self.lockCursor()

        # change value of start_time and end_time and after_download for special gid to NULL value
        if start_time:
            self.persepolis_db_cursor.execute("""UPDATE addlink_db_table SET start_time = NULL
                                                                        WHERE gid = '{}' """.format(gid))
        if end_time:
            self.persepolis_db_cursor.execute("""UPDATE addlink_db_table SET end_time = NULL
                                                                        WHERE gid = '{}' """.format(gid))
        if after_download:
            self.persepolis_db_cursor.execute("""UPDATE addlink_db_table SET after_download = NULL
                                                                        WHERE gid = '{}' """.format(gid))

        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    # return category information in category_db_table

    def searchCategoryInCategoryTable(self, category):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute(
            """SELECT * FROM category_db_table WHERE category = '{}'""".format(str(category)))
        list = self.persepolis_db_cursor.fetchall()

        # job is done! open the lock
        self.lock = False

        if list:
            tuple = list[0]
        else:
            return None

        # convert string to list
        gid_list = ast.literal_eval(tuple[9])

        # create a dictionary from results
        dict = {'category': tuple[0],
                'start_time_enable': tuple[1],
                'start_time': tuple[2],
                'end_time_enable': tuple[3],
                'end_time': tuple[4],
                'reverse': tuple[5],
                'limit_enable': tuple[6],
                'limit_value': tuple[7],
                'after_download': tuple[8],
                'gid_list': gid_list
                }

        # return dictionary
        return dict

    # return categories name
    def categoriesList(self):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute("""SELECT category FROM category_db_table ORDER BY ROWID""")
        rows = self.persepolis_db_cursor.fetchall()

        # create a list from categories name
        queues_list = []

        for tuple in rows:
            queues_list.append(tuple[0])

        # job is done! open the lock
        self.lock = False

        # return the list
        return queues_list

    def setDBTablesToDefaultValue(self):
        # lock data base
        self.lockCursor()

        # change start_time_enable , end_time_enable , reverse ,
        # limit_enable , after_download value to default value !
        self.persepolis_db_cursor.execute("""UPDATE category_db_table SET
                                        start_time_enable = 'no', end_time_enable = 'no',
                                        reverse = 'no', limit_enable = 'no', after_download = 'no'""")

        # change status of download to 'stopped' if status isn't 'complete' or 'error'
        self.persepolis_db_cursor.execute("""UPDATE download_db_table SET status = 'stopped' 
                                        WHERE status NOT IN ('complete', 'error')""")

        # change start_time and end_time and
        # after_download value to None in addlink_db_table!
        self.persepolis_db_cursor.execute("""UPDATE addlink_db_table SET start_time = NULL,
                                                                        end_time = NULL,
                                                                        after_download = NULL
                                                                                        """)

        # change checking value to no in video_finder_db_table
        self.persepolis_db_cursor.execute("""UPDATE video_finder_db_table SET checking = 'no'""")

        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    def findActiveDownloads(self, category=None):
        # lock data base
        self.lockCursor()

        # find download items is download_db_table with status = "downloading" or "waiting" or paused or scheduled
        if category:
            self.persepolis_db_cursor.execute("""SELECT gid FROM download_db_table
                                            WHERE (category = '{}') AND (status = 'downloading' OR status = 'waiting' 
                                            OR status = 'scheduled' OR status = 'paused')""".format(str(category)))
        else:
            self.persepolis_db_cursor.execute("""SELECT gid FROM download_db_table
                                            WHERE (status = 'downloading' OR status = 'waiting' 
                                            OR status = 'scheduled' OR status = 'paused')""")

        # create a list for returning answer
        result = self.persepolis_db_cursor.fetchall()
        gid_list = []

        for result_tuple in result:
            gid_list.append(result_tuple[0])

        # job is done! open the lock
        self.lock = False

        return gid_list

# this method returns items with 'downloading' or 'waiting' status
    def returnDownloadingItems(self):
        # lock data base
        self.lockCursor()

        # find download items is download_db_table with status = "downloading" or "waiting" or paused or scheduled
        self.persepolis_db_cursor.execute(
            """SELECT gid FROM download_db_table WHERE (status = 'downloading' OR status = 'waiting')""")

        # create a list for returning answer
        result = self.persepolis_db_cursor.fetchall()
        gid_list = []

        for result_tuple in result:
            gid_list.append(result_tuple[0])

        # job is done! open the lock
        self.lock = False

        return gid_list

# this method returns items with 'paused' status.
    def returnPausedItems(self):
        # lock data base
        self.lockCursor()

        # find download items is download_db_table with status = "downloading" or "waiting" or paused or scheduled
        self.persepolis_db_cursor.execute("""SELECT gid FROM download_db_table WHERE (status = 'paused')""")

        # create a list for returning answer
        result = self.persepolis_db_cursor.fetchall()
        gid_list = []

        for result_tuple in result:
            gid_list.append(result_tuple[0])

        # job is done! open the lock
        self.lock = False

        return gid_list

    # return all video_gids and audio_gids in video_finder_db_table
    def returnVideoFinderGids(self):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute("""SELECT video_gid, audio_gid FROM video_finder_db_table""")

        # create a list for result
        result = self.persepolis_db_cursor.fetchall()

        # job is done! open the lock
        self.lock = False

        gid_list = []
        video_gid_list = []
        audio_gid_list = []

        for result_tuple in result:
            gid_list.append(result_tuple[0])
            video_gid_list.append(result_tuple[0])

            gid_list.append(result_tuple[1])
            audio_gid_list.append(result_tuple[1])

        # job is done
        return gid_list, video_gid_list, audio_gid_list

    # This method deletes a category from category_db_table
    def deleteCategory(self, category):

        # delete gids of this category from gid_list of 'All Downloads'
        category_dict = self.searchCategoryInCategoryTable(category)
        all_downloads_dict = self.searchCategoryInCategoryTable('All Downloads')

        # get gid_list
        category_gid_list = category_dict['gid_list']
        all_downloads_gid_list = all_downloads_dict['gid_list']

        for gid in category_gid_list:
            # delete item from all_downloads_gid_list
            all_downloads_gid_list.remove(gid)

        # update category_db_table
        self.updateCategoryTable([all_downloads_dict])

        # delete category from data_base
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute(
            """DELETE FROM category_db_table WHERE category = '{}'""".format(str(category)))

        # commit changes
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False


# this method deletes all items in data_base

    def resetDataBase(self):
        # update gid_list in categories with empty gid_list
        all_downloads_dict = {'category': 'All Downloads', 'gid_list': []}
        single_downloads_dict = {'category': 'Single Downloads', 'gid_list': []}
        scheduled_downloads_dict = {'category': 'Scheduled Downloads', 'gid_list': []}

        self.updateCategoryTable([all_downloads_dict, single_downloads_dict, scheduled_downloads_dict])

        # lock data base
        self.lockCursor()

        # delete all items in category_db_table, except 'All Downloads' and 'Single Downloads'
        self.persepolis_db_cursor.execute(
            """DELETE FROM category_db_table
            WHERE category NOT IN ('All Downloads', 'Single Downloads', 'Scheduled Downloads')""")
        self.persepolis_db_cursor.execute("""DELETE FROM download_db_table""")
        self.persepolis_db_cursor.execute("""DELETE FROM addlink_db_table""")

        # commit
        self.persepolis_db_connection.commit()

        # release lock
        self.lock = False

# This method deletes a download item from download_db_table
    def deleteItemInDownloadTable(self, gid, category):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.execute("""DELETE FROM download_db_table WHERE gid = '{}'""".format(str(gid)))

        # commit changes
        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

        # delete item from gid_list in category and All Downloads
        for category_name in category, 'All Downloads':
            category_dict = self.searchCategoryInCategoryTable(category_name)

            # get gid_list
            gid_list = category_dict['gid_list']

            # delete item
            if gid in gid_list:
                gid_list.remove(gid)

                # if gid is in video_finder_db_table, both of video_gid and audio_gid must be deleted from gid_list
                video_finder_dictionary = self.searchGidInVideoFinderTable(gid)

                if video_finder_dictionary:
                    video_gid = video_finder_dictionary['video_gid']
                    audio_gid = video_finder_dictionary['audio_gid']

                    if gid == video_gid:
                        gid_list.remove(audio_gid)
                    else:
                        gid_list.remove(video_gid)

                # update category_db_table
                self.updateCategoryTable([category_dict])


# this method replaces:
# GB >> GiB
# MB >> MiB
# KB >> KiB
# Read this link for more information:
# https://en.wikipedia.org/wiki/Orders_of_magnitude_(data)
    def correctDataBase(self):

        # lock data base
        self.lockCursor()

        for units in [['KB', 'KiB'], ['MB', 'MiB'], ['GB', 'GiB']]:
            dict = {'old_unit': units[0],
                    'new_unit': units[1]}

            self.persepolis_db_cursor.execute("""UPDATE download_db_table 
                    SET size = replace(size, :old_unit, :new_unit)""", dict)
            self.persepolis_db_cursor.execute("""UPDATE download_db_table 
                    SET rate = replace(rate, :old_unit, :new_unit)""", dict)
            self.persepolis_db_cursor.execute("""UPDATE download_db_table 
                    SET downloaded_size = replace(downloaded_size, :old_unit, :new_unit)""", dict)

        self.persepolis_db_connection.commit()

        # job is done! open the lock
        self.lock = False

    # close connections

    def closeConnections(self):
        # lock data base
        self.lockCursor()

        self.persepolis_db_cursor.close()
        self.persepolis_db_connection.close()

        # job is done! open the lock
        self.lock = False

persepolis/scripts/data_base.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from persepolis.scripts.osCommands import moveFile, makeTempDownloadDir
from persepolis.scripts.useful_tools import freeSpace
from persepolis.scripts.bubble import notifySend
from persepolis.scripts import logger
from persepolis.constants import OS
import xmlrpc.client
import urllib.parse
import subprocess
import traceback
import platform
import time
import ast
import os

from persepolis.constants import ORG_NAME, APP_NAME
import ghermez
from ghermez import humanReadableSize

try:
    from PySide6.QtCore import QSettings
except ImportError:
    from PyQt5.QtCore import QSettings

# Before reading this file, please read this link!
# this link helps you to understand this codes:
# https://aria2.github.io/manual/en/html/aria2c.html#rpc-interface


home_address = os.path.expanduser("~")

os_type = platform.system()

# persepolis setting
persepolis_setting = QSettings(ORG_NAME, APP_NAME)

# host is localhost
host = 'localhost'

# get port from persepolis_setting
port = int(persepolis_setting.value('settings/rpc-port'))

# get aria2_path
aria2_path = persepolis_setting.value('settings/aria2_path')

# xml rpc
SERVER_URI_FORMAT = 'http://{}:{:d}/rpc'
server_uri = SERVER_URI_FORMAT.format(host, port)
server = xmlrpc.client.ServerProxy(server_uri, allow_none=True)

# start aria2 with RPC


def startAria():
    # return that starting is successful or not!
    answer = ghermez.startAria(port, aria2_path)

    # return result
    return answer

# check aria2 release version . Persepolis uses this function to
# check that aria2 RPC connection is available or not.


def aria2Version():
    try:
        answer = server.aria2.getVersion()
    except Exception:
        # write ERROR messages in terminal and log
        logger.sendToLog("Aria2 didn't respond!", "ERROR")
        answer = "did not respond"

    return answer

# this function sends download request to aria2


def downloadAria(gid, parent):
    # add_link_dictionary is a dictionary that contains user download request
    # information.

    # get information from data_base
    add_link_dictionary = parent.persepolis_db.searchGidInAddLinkTable(gid)

    for key in add_link_dictionary:
        if add_link_dictionary[key] in ["NULL", "None"]:
            add_link_dictionary[key] = None

    link = add_link_dictionary['link']
    ip = add_link_dictionary['ip']
    port = add_link_dictionary['port']
    proxy_user = add_link_dictionary['proxy_user']
    proxy_passwd = add_link_dictionary['proxy_passwd']
    download_user = add_link_dictionary['download_user']
    download_passwd = add_link_dictionary['download_passwd']
    connections = add_link_dictionary['connections']
    limit = str(add_link_dictionary['limit_value'])
    start_time = add_link_dictionary['start_time']
    end_time = add_link_dictionary['end_time']
    header = add_link_dictionary['header']
    out = add_link_dictionary['out']
    user_agent = add_link_dictionary['user_agent']
    cookies = add_link_dictionary['load_cookies']
    referer = add_link_dictionary['referer']

    # make header option
    header_list = []
    header_list.append("Cookie: " + str(cookies))

# convert Mega to Kilo, RPC does not Support floating point numbers.
    if limit != '0':
        limit_number = limit[:-1]
        limit_number = float(limit_number)
        limit_unit = limit[-1]
        if limit_unit == 'K':
            limit_number = round(limit_number)
        else:
            limit_number = round(1024*limit_number)
            limit_unit = 'K'
        limit = str(limit_number) + limit_unit

# create header list
    if header is not None:
        semicolon_split_header = header.split('; ')
        for i in semicolon_split_header:
            equal_split_header = i.split('=', 1)
            join_header = ':'.join(equal_split_header)
            if i != '':
                header_list.append(join_header)

    if len(header_list) == 0:
        header_list = None

# update status and last_try_date in data_base
    if start_time:
        status = "scheduled"
    else:
        status = "waiting"

    # get last_try_date
    now_date = nowDate()

    # update data_base
    dict = {'gid': gid, 'status': status, 'last_try_date': now_date}
    parent.persepolis_db.updateDownloadTable([dict])

    # create ip_port from ip and port in desired format.
    # for example "127.0.0.1:8118"
    if ip:
        ip_port = str(ip) + ":" + str(port)
    else:
        ip_port = ""

    # call startTime if start_time is available
    # startTime creates sleep loop if user set start_time
    # see startTime function for more information.
    if start_time:
        start_time_status = startTime(start_time, gid, parent)
    else:
        start_time_status = "downloading"

    if start_time_status == "scheduled":
        # read limit value again from data_base before starting download!
        # perhaps user changed this in progress bar window
        add_link_dictionary = parent.persepolis_db.searchGidInAddLinkTable(gid)
        limit = add_link_dictionary['limit_value']

        # convert Mega to Kilo, RPC does not Support floating point numbers.
        if limit != '0':
            limit_number = limit[:-1]
            limit_number = float(limit_number)
            limit_unit = limit[-1]
            if limit_unit == 'K':
                limit_number = round(limit_number)
            else:
                limit_number = round(1024*limit_number)
                limit_unit = 'K'
            limit = str(limit_number) + limit_unit


        # set start_time value to None in data_base!
        parent.persepolis_db.setDefaultGidInAddlinkTable(gid, start_time=True)

    # Find download_path_temp from persepolis_setting
    # if download_path_temp and download_path aren't in same partition on hard disk,
    # then create new temp folder in that partition.
    # Why? when download is completed, file must be moved to download_path from temp folder.
    # It helps moving speed :)
    persepolis_setting.sync()

    # Find default download_path_temp
    download_path_temp = persepolis_setting.value('settings/download_path_temp')

    # Find user's selected download_path
    download_path = add_link_dictionary['download_path'] 

    # check is download_path is existed
    if os.path.isdir(download_path):

        if os.lstat(download_path).st_dev != os.lstat(download_path_temp).st_dev:

            # Create folder and give new temp address from makeTempDownloadDir function.
            # Please checkout osCommands.py for more information.
            download_path_temp = makeTempDownloadDir(download_path) 
    else:
        # write an error in logger
        logger.sendToLog("download_path is not found!", "ERROR")

    if start_time_status != 'stopped':
        # send download request to aria2
        aria_dict = {
            'gid': gid,
            'max-tries': str(persepolis_setting.value('settings/max-tries')),
            'retry-wait': int(persepolis_setting.value('settings/retry-wait')),
            'timeout': int(persepolis_setting.value('settings/timeout')),
            'header': header_list,
            'out': out,
            'user-agent': user_agent,
            'referer': referer,
            'all-proxy': ip_port,
            'max-download-limit': limit,
            'all-proxy-user': str(proxy_user),
            'all-proxy-passwd': str(proxy_passwd),
            'http-user': str(download_user),
            'http-passwd': str(download_passwd),
            'split': '16',
            'max-connection-per-server': str(connections),
            'min-split-size': '1M',
            'continue': 'true',
            'dir': str(download_path_temp)
        }

        if str(persepolis_setting.value('settings/dont-check-certificate')) == 'yes':
            aria_dict['check-certificate'] = 'false'

        if not link.startswith("https"):
            aria_dict['http-user'] = str(download_user)
            aria_dict['http-passwd'] = str(download_passwd)

        aria_dict_copy = aria_dict.copy()
        # remove empty key[value] from aria_dict
        for aria_dict_key in aria_dict_copy.keys():
            if aria_dict_copy[aria_dict_key] in [None, 'None', '']:
                del aria_dict[aria_dict_key]

        try:
            answer = server.aria2.addUri([link], aria_dict)

            logger.sendToLog(answer + " Starts", 'INFO')
            if end_time:
                endTime(end_time, gid, parent)

        except Exception:

            # write error status in data_base
            dict = {'gid': gid, 'status': 'error'}
            parent.persepolis_db.updateDownloadTable([dict])

            # write ERROR messages in log
            logger.sendToLog("Download did not start", "ERROR")
            error_message = str(traceback.format_exc())
            logger.sendToLog(error_message, "ERROR")

            # return False!
            return False
    else:
        # if start_time_status is "stopped" it means download Canceled by user
        logger.sendToLog("Download Canceled", "INFO")

# this function returns list of download information
def tellActive():
    # get download information from aria2
    try:
        downloads_status = server.aria2.tellActive(
            ['gid', 'status', 'connections', 'errorCode', 'errorMessage', 'downloadSpeed',
             'connections', 'dir', 'totalLength', 'completedLength', 'files'])
    except Exception:
        return None, None

    download_status_list = []
    gid_list = []

    # convert download information in desired format.
    for dict in downloads_status:
        converted_info_dict = convertDownloadInformation(dict)

        # add gid to gid_list
        gid_list.append(dict['gid'])

        # add converted information to download_status_list
        download_status_list.append(converted_info_dict)

    # return results
    return gid_list, download_status_list

# this function returns download status that specified by gid!


def tellStatus(gid, parent):
    # get download status from aria2
    try:
        download_status = server.aria2.tellStatus(
            gid, ['status', 'connections', 'errorCode', 'errorMessage', 'downloadSpeed',
                  'connections', 'dir', 'totalLength', 'completedLength', 'files'])
        download_status['gid'] = str(gid)
    except Exception:
        return None

    # convert download_status in desired format
    converted_info_dict = convertDownloadInformation(download_status)


# if download has completed , then move file to the download folder
    if (converted_info_dict['status'] == "complete"):
        file_name = converted_info_dict['file_name']

        # find user preferred download_path from addlink_db_table in data_base
        add_link_dictionary = parent.persepolis_db.searchGidInAddLinkTable(gid)

        persepolis_setting.sync()

        download_path = add_link_dictionary['download_path']

        # if user specified download_path is equal to persepolis_setting download_path,
        # then subfolder must added to download path.
        if persepolis_setting.value('settings/download_path') == download_path:
            download_path = findDownloadPath(
                file_name, download_path, persepolis_setting.value('settings/subfolder'))

        # find temp download path
        file_status = str(download_status['files'])
        file_status = file_status[1:-1]
        file_status = ast.literal_eval(file_status)

        path = str(file_status['path'])

        # file_name
        file_name = urllib.parse.unquote(os.path.basename(path))

        # find file_size
        try:
            file_size = int(download_status['totalLength'])
        except ValueError:
            file_size = None

        # if file is related to VideoFinder thread, don't move it from temp folder...
        video_finder_dictionary = parent.persepolis_db.searchGidInVideoFinderTable(gid)
        if video_finder_dictionary:
            file_path = path
        else:
            file_path = downloadCompleteAction(parent, path, download_path, file_name, file_size)

        # update download_path in addlink_db_table
        add_link_dictionary['download_path'] = file_path
        parent.persepolis_db.updateAddLinkTable([add_link_dictionary])

# if an error occurred!
    if (converted_info_dict['status'] == "error"):
        # add errorMessage to converted_info_dict
        converted_info_dict['error'] = str(download_status['errorMessage'])

        # remove download from aria2
        server.aria2.removeDownloadResult(gid)

    # return results in dictionary format
    return converted_info_dict

# this function converts download information that received from aria2 in desired format.
# input format must be a dictionary.


def convertDownloadInformation(download_status):
    # find file_name
    try:
        # file_status contains name of download file and link of download file
        file_status = str(download_status['files'])
        file_status = file_status[1:-1]
        file_status = ast.literal_eval(file_status)
        path = str(file_status['path'])
        file_name = urllib.parse.unquote(os.path.basename(path))
        if not(file_name):
            file_name = None

        uris = file_status['uris']
        uri = uris[0]
        link = uri['uri']

    except Exception:

        file_name = None
        link = None

    for i in download_status.keys():
        if not(download_status[i]):
            download_status[i] = None

    # find file_size
    try:
        file_size = float(download_status['totalLength'])
    except ValueError:
        file_size = None

    # find downloaded size
    try:
        downloaded = float(download_status['completedLength'])
    except ValueError:
        downloaded = None

    # convert file_size and downloaded_size to KiB and MiB and GiB
    if (downloaded is not None and file_size is not None and file_size != 0):
        file_size_back = file_size

        # converting file_size to KiB or MiB or GiB
        size_str = humanReadableSize(file_size)
        downloaded_back = downloaded

        downloaded_str = humanReadableSize(downloaded)

        # find download percent from file_size and downloaded_size
        file_size = file_size_back
        downloaded = downloaded_back
        percent = int(downloaded * 100 / file_size)
        percent_str = str(percent) + "%"
    else:
        percent_str = None
        size_str = None
        downloaded_str = None

    # find download_speed
    try:
        download_speed = int(download_status['downloadSpeed'])
    except ValueError:
        download_speed = 0

    # convert download_speed to desired units.
    # and find estimate_time_left
    if (downloaded is not None and download_speed != 0):
        estimate_time_left = int((file_size - downloaded)/download_speed)

        # converting file_size to KiB or MiB or GiB
        download_speed_str = humanReadableSize(download_speed, 'speed') + '/s'

        eta = ""
        if estimate_time_left >= 3600:
            eta = eta + str(int(estimate_time_left/3600)) + "h"
            estimate_time_left = estimate_time_left % 3600
            eta = eta + str(int(estimate_time_left/60)) + "m"
            estimate_time_left = estimate_time_left % 60
            eta = eta + str(estimate_time_left) + "s"
        elif estimate_time_left >= 60:
            eta = eta + str(int(estimate_time_left/60)) + "m"
            estimate_time_left = estimate_time_left % 60
            eta = eta + str(estimate_time_left) + "s"
        else:
            eta = eta + str(estimate_time_left) + "s"
        estimate_time_left_str = eta

    else:
        download_speed_str = "0"
        estimate_time_left_str = None

    # find number of connections
    try:
        connections_str = str(download_status['connections'])
    except (UnicodeEncodeError, KeyError):
        connections_str = None

    # find status of download
    try:
        status_str = str(download_status['status'])
    except (UnicodeEncodeError, KeyError):
        status_str = None

    # rename active status to downloading
    if (status_str == "active"):
        status_str = "downloading"

    # rename removed status to stopped
    if (status_str == "removed"):
        status_str = "stopped"

    if (status_str == "None"):
        status_str = None

    # set 0 second for estimate_time_left_str if download is completed.
    if status_str == 'complete':
        estimate_time_left_str = '0s'

# return information in dictionary format
    download_info = {
        'gid': download_status['gid'],
        'file_name': file_name,
        'status': status_str,
        'size': size_str,
        'downloaded_size': downloaded_str,
        'percent': percent_str,
        'connections': connections_str,
        'rate': download_speed_str,
        'estimate_time_left': estimate_time_left_str,
        'link': link
    }

    return download_info

# download complete actions!
# this method is returning file_path of file in the user's download folder
# and move downloaded file after download completion.


def downloadCompleteAction(parent, path, download_path, file_name, file_size):

    # remove query from name, If file_name contains query components.
    # check if query existed.
    # query form is 1.mp3?foo=bar 2.mp3?blatz=pow 3.mp3?fizz=buzz
    file_name_split = file_name.split('.')
    file_extension = file_name_split[-1]
    file_name_without_extension = file_name_split[0]

    # if '?' in file_extension, then file_name contains query components.
    if '?' in file_extension:
        file_extension = file_extension.split('?')[0]
        file_name = '.'.join([file_name_without_extension, file_extension])

    # rename file if file already existed
    i = 1
    file_path = os.path.join(download_path, file_name)

    while os.path.isfile(file_path):
        file_name_split = file_name.split('.')
        extension_length = len(file_name_split[-1]) + 1

        new_name = file_name[0:-extension_length] + \
            '_' + str(i) + file_name[-extension_length:]
        file_path = os.path.join(download_path, new_name)
        i = i + 1

    free_space = freeSpace(download_path)

    if free_space is not None and file_size is not None:

        # compare free disk space and file_size
        if free_space >= file_size:

            # move the file to the download folder
            move_answer = moveFile(str(path), str(file_path), 'file')

            if not(move_answer):
                # write error message in log
                logger.sendToLog('Persepolis can not move file', "ERROR")
                file_path = path

        else:
            # notify user if we have insufficient disk space
            # and do not move file from temp download folder to download folder
            file_path = path
            logger.sendToLog('Insufficient disk space in download folder', "ERROR")

            # show notification
            notifySend("Insufficient disk space!", 'Please change download folder',
                       10000, 'fail', parent=parent)

    else:
        # move the file to the download folder
        move_answer = moveFile(str(path), str(file_path), 'file')

        if not(move_answer):
            logger.sendToLog('Persepolis can not move file', "ERROR")
            file_path = path

    return str(file_path)


# this function returns folder of download according to file extension
def findDownloadPath(file_name, download_path, subfolder):

    file_name_split = file_name.split('.')
    file_extension = file_name_split[-1]

    # convert extension letters to lower case
    # for example "JPG" will be converted in "jpg"
    file_extension = file_extension.lower()

    # remove query from file_extension if existed
    # if '?' in file_extension, then file_name contains query components.
    if '?' in file_extension:
        file_extension = file_extension.split('?')[0]

    # audio formats
    audio = ['act', 'aiff', 'aac', 'amr', 'ape', 'au', 'awb', 'dct', 'dss', 'dvf',
             'flac', 'gsm', 'iklax', 'ivs', 'm4a', 'm4p', 'mmf', 'mp3', 'mpc', 'msv',
             'ogg', 'oga', 'opus', 'ra', 'raw', 'sln', 'tta', 'vox', 'wav', 'wma', 'wv']

    # video formats
    video = ['3g2', '3gp', 'asf', 'avi', 'drc', 'flv', 'm4v', 'mkv', 'mng', 'mov', 'qt', 'mp4', 'm4p', 'mpg', 'mp2',
             'mpeg', 'mpe', 'mpv', 'm2v', 'mxf', 'nsv', 'ogv', 'rmvb', 'roq', 'svi', 'vob', 'webm', 'wmv', 'yuv', 'rm']

    # document formats
    document = ['doc', 'docx', 'html', 'htm', 'fb2', 'odt', 'sxw', 'pdf', 'ps', 'rtf', 'tex', 'txt', 'epub', 'pub'
                'mobi', 'azw', 'azw3', 'azw4', 'kf8', 'chm', 'cbt', 'cbr', 'cbz', 'cb7', 'cba', 'ibooks', 'djvu', 'md']

    # compressed formats
    compressed = ['a', 'ar', 'cpio', 'shar', 'LBR', 'iso', 'lbr', 'mar', 'tar', 'bz2', 'F', 'gz', 'lz', 'lzma', 'lzo',
                  'rz', 'sfark', 'sz', 'xz', 'Z', 'z', 'infl', '7z', 's7z', 'ace', 'afa', 'alz', 'apk', 'arc', 'arj',
                  'b1', 'ba', 'bh', 'cab', 'cfs', 'cpt', 'dar', 'dd', 'dgc', 'dmg', 'ear', 'gca', 'ha', 'hki', 'ice',
                  'jar', 'kgb', 'lzh', 'lha', 'lzx', 'pac', 'partimg', 'paq6', 'paq7', 'paq8', 'pea', 'pim', 'pit',
                  'qda', 'rar', 'rk', 'sda', 'sea', 'sen', 'sfx', 'sit', 'sitx', 'sqx', 'tar.gz', 'tgz', 'tar.Z',
                  'tar.bz2', 'tbz2', 'tar.lzma', 'tlz', 'uc', 'uc0', 'uc2', 'ucn', 'ur2', 'ue2', 'uca', 'uha', 'war',
                  'wim', 'xar', 'xp3', 'yz1', 'zip', 'zipx', 'zoo', 'zpaq', 'zz', 'ecc', 'par', 'par2']

    # return download_path
    if str(subfolder) == 'yes':
        if file_extension in audio:
            return os.path.join(download_path, 'Audios')

        # aria2c downloads youtube links file_name with 'videoplayback' name?!
        elif (file_extension in video) or (file_name == 'videoplayback'):
            return os.path.join(download_path, 'Videos')

        elif file_extension in document:
            return os.path.join(download_path, 'Documents')

        elif file_extension in compressed:
            return os.path.join(download_path, 'Compressed')

        else:
            return os.path.join(download_path, 'Others')
    else:
        return download_path


# shutdown aria2
def shutDown():
    try:
        answer = server.aria2.shutdown()
        logger.sendToLog("Aria2 Shutdown : " + str(answer), "INFO")
        return True
    except Exception:
        logger.sendToLog("Aria2 Shutdown Error", "ERROR")
        return False

# downloadStop stops download completely
# this function sends remove request to aria2
# and changes status of download to "stopped" in data_base


def downloadStop(gid, parent):
    # get download status from data_base
    dict = parent.persepolis_db.searchGidInDownloadTable(gid)
    status = dict['status']

    # if status is "scheduled", then download request has not been sended to aria2!
    # so no need to send stop request to aria2.
    # if status in not "scheduled" so stop request must be sended to aria2.
    if status != 'scheduled':
        try:
            # send remove download request to aria2.
            # see aria2 documentation for more information.
            answer = server.aria2.remove(gid)
            if status == 'downloading':
                server.aria2.removeDownloadResult(gid)
        except Exception:
            answer = "None"

        # write a messages in log and terminal
        logger.sendToLog(answer + " stopped", "INFO")

    # if download has not been completed yet,
    # so just chang status of download to "stopped" in data base.
    else:
        answer = 'stopped'

    if status != 'complete':
        # change start_time end_time and after_download value to None in date base
        parent.persepolis_db.setDefaultGidInAddlinkTable(gid,
                                                         start_time=True, end_time=True, after_download=True)

        # change status of download to "stopped" in data base
        dict = {'gid': gid, 'status': 'stopped'}
        parent.persepolis_db.updateDownloadTable([dict])

    return answer


# downloadPause pauses download
def downloadPause(gid):
    # see aria2 documentation for more information

    # send pause request to aria2 .
    try:
        answer = server.aria2.pause(gid)
    except Exception:
        answer = None

    logger.sendToLog(str(answer) + " paused", "INFO")
    return answer


# downloadUnpause unpauses download
def downloadUnpause(gid):
    try:
        # send unpause request to aria2
        answer = server.aria2.unpause(gid)
    except Exception:
        answer = None

    logger.sendToLog(str(answer) + " unpaused", "INFO")

    return answer

#  limitSpeed limits download speed


def limitSpeed(gid, limit):
    limit = str(limit)
# convert Mega to Kilo, RPC does not Support floating point numbers.
    if limit != '0':
        limit_number = limit[:-1]
        limit_number = float(limit_number)
        limit_unit = limit[-1]
        if limit_unit == 'K':
            limit_number = round(limit_number)
        else:
            limit_number = round(1024*limit_number)
            limit_unit = 'K'
        limit = str(limit_number) + limit_unit

    try:
        server.aria2.changeOption(gid, {'max-download-limit': limit})
        logger.sendToLog("Download speed limit  value is changed", "INFO")

    except Exception:
        logger.sendToLog("Speed limitation was unsuccessful", "ERROR")


# this function returns GID of active downloads in list format.
def activeDownloads():
    try:
        answer = server.aria2.tellActive(['gid'])
    except Exception:
        answer = []

    active_gids = []
    for i in answer:
        # extract gid from dictionary
        dict = i
        gid = dict['gid']

        # add gid to list
        active_gids.append(gid)

    # return results
    return active_gids


# This function returns data and time in string format
# for example >> 2017/09/09 , 13:12:26
def nowDate():
    date = time.strftime("%Y/%m/%d , %H:%M:%S")
    return date

# sigmaTime gets hours and minutes for input.
# and converts hours to minutes and returns summation in minutes
# input format is HH:MM


def sigmaTime(time):
    hour, minute = time.split(":")
    return (int(hour)*60 + int(minute))

# nowTime returns now time in HH:MM format!


def nowTime():
    now_time = time.strftime("%H:%M")
    return sigmaTime(now_time)

# this function creates sleep time,if user sets "start time" for download.


def startTime(start_time, gid, parent):
    # write some messages
    logger.sendToLog("Download starts at " + start_time, "INFO")

    # start_time that specified by user
    sigma_start = sigmaTime(start_time)

    # get current time
    sigma_now = nowTime()

    status = 'scheduled'

  # this loop is continuing until download time arrival!
    while sigma_start != sigma_now:
        time.sleep(2.1)
        sigma_now = nowTime()

        # check download status from data_base
        dict = parent.persepolis_db.searchGidInDownloadTable(gid)
        data_base_download_status = dict['status']

        # if data_base_download_status = stopped >> it means that user
        # canceled download and loop must break!
        if data_base_download_status == 'stopped':
            status = 'stopped'
            break
        else:
            status = 'scheduled'

# if user canceled download , then return 'stopped' and if download time arrived then return 'scheduled'!
    return status


def endTime(end_time, gid, parent):
    logger.sendToLog("End time is activated " + gid, "INFO")
    sigma_end = sigmaTime(end_time)

    # get current time
    sigma_now = nowTime()

    answer = 'end'
    # while current time is not equal to end_time, continue the loop
    while sigma_end != sigma_now:

        # get download status from data_base
        dict = parent.persepolis_db.searchGidInDownloadTable(gid)
        status = dict['status']

        # check download status
        if status == 'scheduled' or status == 'downloading' or status == 'paused' or status == 'waiting':

            # download continues!
            answer = 'continue'

        else:

            # Download completed or stopped by user
            # so break the loop
            answer = 'end'
            logger.sendToLog("Download has been finished! " + str(gid), "INFO")
            break

        # get current time
        sigma_now = nowTime()
        time.sleep(2.1)

    # Time is up!
    if answer != 'end':
        logger.sendToLog("Time is up!", "INFO")
        answer = downloadStop(gid, parent)
        i = 0
        # try to stop download 10 times
        while answer == 'None' and (i <= 9):
            time.sleep(1)
            answer = downloadStop(gid, parent)
            i = i + 1

        # If aria2c not respond, so kill it. R.I.P :))
        if (answer == 'None') and (os_type != OS.WINDOWS):

            subprocess.Popen(['killall', 'aria2c'],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False)

        # change end_time value to None in data_base
        parent.persepolis_db.setDefaultGidInAddlinkTable(gid, end_time=True)

persepolis/scripts/download.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

try:
    from PySide6.QtWidgets import QWidget, QTextEdit, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
    from PySide6.QtGui import QIcon
    from PySide6.QtCore import Qt, QSize, QSettings
except ImportError:
    from PyQt5.QtWidgets import QWidget, QTextEdit, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
    from PyQt5.QtCore import Qt, QSize, QSettings
    from PyQt5.QtGui import QIcon

from persepolis.constants import ORG_NAME, APP_NAME, LONG_NAME, REPO_LINK
from persepolis.scripts import osCommands
from persepolis.gui import resources # noqa: F401

from ghermez import DataBase

class ErrorWindow(QWidget):
    def __init__(self, text):
        super().__init__()
# finding windows_size
        self.setMinimumSize(QSize(363, 300))
        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))
        self.setWindowTitle(LONG_NAME)

        verticalLayout = QVBoxLayout(self)
        horizontalLayout = QHBoxLayout()
        horizontalLayout.addStretch(1)

        self.text_edit = QTextEdit(self)
        self.text_edit.setReadOnly(True)
        self.text_edit.insertPlainText(text)

        verticalLayout.addWidget(self.text_edit)

        self.label2 = QLabel(self)
        self.label2.setText('Reseting persepolis may solving problem.\nDo not panic!If you add your download links again,\npersepolis will resume your downloads\nPlease copy this error message and press "Report Issue" button\nand open a new issue in Github for it.\nWe answer you as soon as possible. \nreporting this issue help us to improve persepolis.\nThank you!')  # noqa: E501
        verticalLayout.addWidget(self.label2)

        self.report_pushButton = QPushButton(self)
        self.report_pushButton.setText("Report Issue")
        horizontalLayout.addWidget(self.report_pushButton)

        self.reset_persepolis_pushButton = QPushButton(self)
        self.reset_persepolis_pushButton.clicked.connect(
            self.resetPushButtonPressed)
        self.reset_persepolis_pushButton.setText('Reset Persepolis')
        horizontalLayout.addWidget(self.reset_persepolis_pushButton)

        self.close_pushButton = QPushButton(self)
        self.close_pushButton.setText('close')
        horizontalLayout.addWidget(self.close_pushButton)

        verticalLayout.addLayout(horizontalLayout)

        self.report_pushButton.clicked.connect(self.reportPushButtonPressed)
        self.close_pushButton.clicked.connect(self.closePushButtonPressed)

    def reportPushButtonPressed(self, button):
        osCommands.xdgOpen(f'{REPO_LINK}/issues')

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closePushButtonPressed(self, button):
        self.close()

    def resetPushButtonPressed(self, button):
        # create an object for DataBase
        persepolis_db = DataBase()

        # Reset data base
        persepolis_db.resetDataBase()

        # close connections
        # persepolis_db.closeConnections()
        del persepolis_db

        # Reset persepolis_setting
        persepolis_setting = QSettings(ORG_NAME, APP_NAME)
        persepolis_setting.clear()
        persepolis_setting.sync()

persepolis/scripts/error_window.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.


# THIS FILE CONTAINING SOME VARIABLES , ... THAT USING FOR INITIALIZING PERSEPOLIS

from persepolis.scripts.useful_tools import returnDefaultSettings
from persepolis.scripts.browser_integration import browserIntegration
from persepolis.constants import ORG_NAME, APP_NAME
from persepolis.scripts import osCommands
import os

import ghermez

try:
    from PySide6.QtCore import QSettings
except ImportError:
    from PyQt5.QtCore import QSettings

# initialization

# create folders
ghermez.init_create_folders()

# persepolisdm.log file contains persepolis log.
from persepolis.scripts import logger  # noqa: E402

# refresh logs!
ghermez.init_log_file()

from ghermez import DataBase, PluginsDB  # noqa: E402

# create an object for DataBase
persepolis_db = DataBase()

# create tables
persepolis_db.createTables()

# close connections
# persepolis_db.closeConnections()
del persepolis_db

# create an object for PluginsDB
plugins_db = PluginsDB()

# create tables
plugins_db.createTables()

# delete old links
plugins_db.deleteOldLinks()

# close connections
# plugins_db.closeConnections()
del plugins_db


# import persepolis_setting
# persepolis is using QSettings for saving windows size and windows
# position and program settings.

persepolis_setting = QSettings(ORG_NAME, APP_NAME)

persepolis_setting.beginGroup('settings')

default_setting_dict = returnDefaultSettings()
# this loop is checking values in persepolis_setting . if value is not
# valid then value replaced by default_setting_dict value
for key in default_setting_dict.keys():

    setting_value = persepolis_setting.value(key, default_setting_dict[key])
    persepolis_setting.setValue(key, setting_value)

# download files is downloading in temporary folder(download_path_temp)
# and then they will be moved to user download folder(download_path) after completion.
# Check that mount point is available of not!
if not(os.path.exists(persepolis_setting.value('download_path_temp'))):
    persepolis_setting.setValue('download_path_temp', default_setting_dict['download_path_temp'])

if not(os.path.exists(persepolis_setting.value('download_path'))):
    persepolis_setting.setValue('download_path', default_setting_dict['download_path'])


persepolis_setting.sync()

# this section  creates temporary download folder and download folder and
# download sub folders if they did not existed.
download_path_temp = persepolis_setting.value('download_path_temp')
download_path = persepolis_setting.value('download_path')


folder_list = [download_path_temp, download_path]

# add subfolders to folder_list if user checked subfolders check box in setting window.
if persepolis_setting.value('subfolder') == 'yes':
    for folder in ['Audios', 'Videos', 'Others', 'Documents', 'Compressed']:
        folder_list.append(os.path.join(download_path, folder))

# create folders in folder_list
for folder in folder_list:
    osCommands.makeDirs(folder)

persepolis_setting.endGroup()

# Browser integration for Firefox and chromium and google chrome
for browser in ['chrome', 'chromium', 'opera', 'vivaldi', 'firefox', 'brave']:
    json_done, native_done = browserIntegration(browser)

    log_message = browser

    if json_done:
        log_message = log_message + ': ' + 'Json file is created successfully.\n'

    else:
        log_message = log_message + ': ' + 'Json ERROR!\n'

    if native_done:
        log_message = log_message + 'persepolis executer file is created successfully.\n'

    elif native_done is False:
        log_message = log_message + ': ' + 'persepolis executer file ERROR!\n'

    logger.sendToLog(log_message)

# get locale and set ui direction
locale = str(persepolis_setting.value('settings/locale'))

# right to left languages
rtl_locale_list = ['fa_IR', 'ar']

# left to right languages
ltr_locale_list = ['en_US', 'zh_CN', 'fr_FR', 'pl_PL', 'nl_NL', 'pt_BR', 'es_ES', 'hu', 'tr', 'tr_TR']

if locale in rtl_locale_list:
    persepolis_setting.setValue('ui_direction', 'rtl')
else:
    persepolis_setting.setValue('ui_direction', 'ltr')

# compatibility
persepolis_version = float(persepolis_setting.value('version/version', 2.5))
if persepolis_version < 2.6:
    from persepolis.scripts.compatibility import compatibility
    try:
        compatibility()
    except Exception as e:

        # create an object for DataBase
        persepolis_db = DataBase()

        # create tables
        persepolis_db.resetDataBase()

        # close connections
        # persepolis_db.closeConnections()
        del persepolis_db

        # write error in log
        logger.sendToLog(
            "compatibility ERROR!", "ERROR")
        logger.sendToLog(
            str(e), "ERROR")

    persepolis_version = 2.6

if persepolis_version < 3.1:
    # create an object for DataBase
    persepolis_db = DataBase()

    # correct data base
    persepolis_db.correctDataBase()

    # close connections
    # persepolis_db.closeConnections()
    del persepolis_db

    persepolis_version = 3.1

if persepolis_version < 3.2:
    persepolis_setting.beginGroup('settings')

    for key in default_setting_dict.keys():

        setting_value = default_setting_dict[key]
        persepolis_setting.setValue(key, setting_value)

    persepolis_setting.endGroup()

    persepolis_setting.setValue('version/version', 3.2)

persepolis_setting.sync()

persepolis/scripts/initialization.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
from ghermez import determineConfigFolder
from persepolis.gui.log_window_ui import LogWindow_Ui
from persepolis.scripts import osCommands
from persepolis.constants import REPO_LINK
import os

try:
    from PySide6.QtCore import Qt, QPoint, QSize
    from PySide6.QtGui import QIcon
    from PySide6 import QtWidgets
except ImportError:
    from PyQt5.QtCore import Qt, QPoint, QSize
    from PyQt5.QtGui import QIcon
    from PyQt5 import QtWidgets

# config_folder
config_folder = determineConfigFolder()


class LogWindow(LogWindow_Ui):
    def __init__(self, persepolis_setting):
        super().__init__(persepolis_setting)

        self.persepolis_setting = persepolis_setting

        self.copy_log_pushButton.setEnabled(False)

# log file address
        self.log_file = os.path.join(str(config_folder), 'persepolisdm.log')

        f = open(self.log_file, 'r')
        f_lines = f.readlines()
        f.close()

        self.text = 'Log File:\n'
        for line in f_lines:
            self.text = self.text + str(line) + '\n'

        self.text_edit.insertPlainText(self.text)

        self.text_edit.copyAvailable.connect(
            self.copyAvailableSignalHandler)

        self.copy_log_pushButton.clicked.connect(
            self.copyPushButtonPressed)

        self.report_pushButton.clicked.connect(
            self.reportPushButtonPressed)

        self.close_pushButton.clicked.connect(
            self.closePushButtonPressed)

        self.refresh_log_pushButton.clicked.connect(
            self.refreshLogPushButtonPressed)

        self.clear_log_pushButton.clicked.connect(
            self.clearLogPushButtonPressed)

# setting window size and position
        size = self.persepolis_setting.value(
            'LogWindow/size', QSize(720, 300))
        position = self.persepolis_setting.value(
            'LogWindow/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)

        self.minimum_height = self.height()

    def clearLogPushButtonPressed(self, button):
        f = open(self.log_file, 'w')
        f.close()

        self.text = 'Log File:\n'

        self.text_edit.clear()
        self.text_edit.insertPlainText(self.text)

    def reportPushButtonPressed(self, button):
        osCommands.xdgOpen(f'{REPO_LINK}/issues')

    def closePushButtonPressed(self, button):
        self.close()

    def copyAvailableSignalHandler(self, signal):
        if signal:
            self.copy_log_pushButton.setEnabled(True)
        else:
            self.copy_log_pushButton.setEnabled(False)

    def copyPushButtonPressed(self, button):
        #         clipboard = QApplication.clipboard()
        #         clipboard.setText(self.text)
        self.text_edit.copy()

# this method is refresh log messages in text_edit
    def refreshLogPushButtonPressed(self, button):
        f = open(self.log_file, 'r')
        f_lines = f.readlines()
        f.close()

        self.text = 'Log File:\n'
        for line in f_lines:
            self.text = self.text + str(line) + '\n'

        self.text_edit.clear()
        self.text_edit.insertPlainText(self.text)

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        self.layout().setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.setMinimumSize(QSize(self.width(), self.minimum_height))
        self.resize(QSize(self.width(), self.minimum_height))

        self.persepolis_setting.setValue('LogWindow/size', self.size())
        self.persepolis_setting.setValue('LogWindow/position', self.pos())
        self.persepolis_setting.sync()
        event.accept()

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'

        self.close_pushButton.setIcon(QIcon(icons + 'remove'))
        self.copy_log_pushButton.setIcon(QIcon(icons + 'clipboard'))
        self.report_pushButton.setIcon(QIcon(icons + 'about'))
        self.refresh_log_pushButton.setIcon(QIcon(icons + 'refresh'))

persepolis/scripts/log_window.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

from ghermez import determineConfigFolder
from persepolis.constants import APP_NAME
from persepolis.scripts.osCommands import touch
import logging
import os

# config_folder
config_folder = determineConfigFolder()

# create a directory if it does not exist
if not os.path.exists(config_folder):
    os.makedirs(config_folder)

# log file address
log_file = os.path.join(str(config_folder), f'{APP_NAME}dm.log')

if not os.path.isfile(log_file):
    touch(log_file)

# define logging object
logObj = logging.getLogger(APP_NAME.capitalize())
logObj.setLevel(logging.INFO)

# don't show log in console
logObj.propagate = False

# create a file handler
handler = logging.FileHandler(log_file)
handler.setLevel(logging.INFO)
# create a logging format
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

# add the handlers to the logger
logObj.addHandler(handler)


def sendToLog(text="", type="INFO"):
    if type == "INFO":
        logObj.info(text)
    elif type == "ERROR":
        logObj.error(text)
    else:
        logObj.warning(text)

persepolis/scripts/logger.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

# native notification on mac! needs Xcode (latest version) installed and pyobjc
# library from pip
import Foundation
import objc

NSUserNotification = objc.lookUpClass('NSUserNotification')
NSUserNotificationCenter = objc.lookUpClass('NSUserNotificationCenter')


def notifyMac(title, subtitle, info_text, delay=0):
    notification = NSUserNotification.alloc().init()
    notification.setTitle_(title)
    notification.setSubtitle_(subtitle)
    notification.setInformativeText_(info_text)
    notification.setDeliveryDate_(Foundation.NSDate.dateWithTimeInterval_sinceDate_(
        delay, Foundation.NSDate.date()))
    NSUserNotificationCenter.defaultUserNotificationCenter(
    ).scheduleNotification_(notification)

persepolis/scripts/mac_notification.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

global pyside6_is_installed

try:
    from PySide6.QtWidgets import (
        QCheckBox, QLineEdit, QAbstractItemView, QFileDialog,
        QSystemTrayIcon, QMenu, QApplication, QInputDialog, QMessageBox
    )
    from PySide6.QtCore import QDir, QTime, QCoreApplication, QSize, QPoint, QThread, Signal, Qt, QTranslator, QLocale
    from PySide6.QtGui import QFont, QIcon, QStandardItem, QCursor, QAction
    from PySide6 import __version__ as PYQT_VERSION_STR
    from PySide6.QtCore import __version__ as QT_VERSION_STR
    pyside6_is_installed = True
except ImportError:
    from PyQt5.QtWidgets import (
        QCheckBox, QLineEdit, QAbstractItemView, QAction, QFileDialog,
        QSystemTrayIcon, QMenu, QApplication, QInputDialog, QMessageBox
    )
    from PyQt5.QtCore import (
        QDir, QTime, QCoreApplication, QSize, QPoint, QThread, Qt, QTranslator, QLocale, QT_VERSION_STR
    )
    from PyQt5.QtGui import QFont, QIcon, QStandardItem, QCursor
    from PyQt5.Qt import PYQT_VERSION_STR
    from PyQt5.QtCore import pyqtSignal as Signal
    pyside6_is_installed = False

from persepolis.scripts.useful_tools import muxer, freeSpace
from persepolis.scripts.video_finder_progress import VideoFinderProgressWindow
from persepolis.gui.mainwindow_ui import MainWindow_Ui, QTableWidgetItem
from persepolis.scripts.browser_plugin_queue import BrowserPluginQueue
from persepolis.scripts.after_download import AfterDownloadWindow
from persepolis.scripts.properties import PropertiesWindow
from persepolis.scripts.setting import PreferencesWindow
from persepolis.scripts.progress import ProgressWindow
from persepolis.scripts.addlink import AddLinkWindow
from persepolis.scripts.text_queue import TextQueue
from persepolis.scripts.log_window import LogWindow
from persepolis.scripts.update import checkupdate
from persepolis.scripts.shutdown import shutDown
from persepolis.scripts.about import AboutWindow
from persepolis.scripts.bubble import notifySend
from persepolis.scripts import osCommands
from persepolis.scripts import download
from persepolis.scripts import logger
from persepolis.scripts import spider
from persepolis.gui import resources # noqa: F401
from persepolis.constants import OS, APP_NAME, REPO_LINK
from functools import partial
from copy import deepcopy
from time import sleep
import urllib.parse
import subprocess
import textwrap
import random
import time
import sys
import os

from ghermez import PluginsDB, DataBase, TempDB
from ghermez import determineConfigFolder, osAndDesktopEnvironment

global youtube_dl_is_installed
try:
    from persepolis.scripts.video_finder_addlink import VideoFinderAddLink
    youtube_dl_is_installed = True
except ModuleNotFoundError:
    # if youtube_dl module is not installed:
    logger.sendToLog(
        "youtube_dl is not installed.", "ERROR")
    youtube_dl_is_installed = False

# CheckVersionsThread thread can change this variable.
global ffmpeg_is_installed
ffmpeg_is_installed = True

# The GID (or gid) is a key to manage each download. Each download will be assigned a unique GID.
# The GID is stored as 64-bit binary value in aria2. For RPC access,
# it is represented as a hex string of 16 characters (e.g., 2089b05ecca3d829).
# Normally, aria2 generates this GID for each download, but the user can
# specify GIDs manually


# shutdown_notification = 0 >> persepolis is running
# 1 >> persepolis is ready for closing(closeEvent  is called)
# 2 >> OK, let's close application!

global shutdown_notification
shutdown_notification = 0

# checking_flag : 0 >> normal situation ;
# 1 >> remove button or delete button pressed or sorting form viewMenu or ... toggled by user ;
# 2 >> check_download_info function is stopping until remove operation done ;
# 3 >> deleteFileAction is done it's job and It is called removeButtonPressed.

global checking_flag
checking_flag = 0

# when rpc connection between persepolis and aria is disconnected >>
# aria2_disconnected = 1
# aria2_disconnected = 0 >> every thing is ok :)
global aria2_disconnected
aria2_disconnected = 0

global aria_startup_answer
aria_startup_answer = None


global button_pressed_counter
button_pressed_counter = 0

global plugin_links_checked
plugin_links_checked = False

# find os platform
os_type, desktop_env = osAndDesktopEnvironment()

# config_folder
config_folder = determineConfigFolder()

download_info_folder = os.path.join(config_folder, "download_info")


# persepolis tmp folder path
persepolis_tmp = os.path.join(config_folder, 'persepolis_tmp')


# see persepolis.py file for show_window_file and plugin_ready
plugin_ready = os.path.join(persepolis_tmp, 'persepolis-plugin-ready')

show_window_file = os.path.join(persepolis_tmp, 'show-window')

# this thread checks ffmpeg availability.
# this thread checks ffmpeg and python and pyqt and qt versions and write them in log file.
# this thread writes osi type and desktop env. in log file.


class CheckVersionsThread(QThread):
    def __init__(self):
        QThread.__init__(self)

    def run(self):
        global ffmpeg_is_installed

        if os_type in OS.UNIX_LIKE:
            ffmpeg_path = 'ffmpeg'

        elif os_type == OS.OSX:

            cwd = sys.argv[0]
            current_directory = os.path.dirname(cwd)
            ffmpeg_path = os.path.join(current_directory, 'ffmpeg')

        elif os_type == OS.WINDOWS:

            cwd = sys.argv[0]
            current_directory = os.path.dirname(cwd)
            ffmpeg_path = os.path.join(current_directory, 'ffmpeg.exe')

        try:
            if os_type == OS.WINDOWS:

                # NO_WINDOW option avoids opening additional CMD in MS Windows.
                NO_WINDOW = 0x08000000
                pipe = subprocess.Popen([ffmpeg_path, '-version'],
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE,
                                        stdin=subprocess.PIPE,
                                        shell=False,
                                        creationflags=NO_WINDOW)

            else:
                pipe = subprocess.Popen(
                    [ffmpeg_path, '-version'],
                    stdout=subprocess.PIPE,
                    stdin=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    shell=False)

            if pipe.wait() == 0:
                ffmpeg_is_installed = True
                ffmpeg_output, error = pipe.communicate()
                ffmpeg_output = ffmpeg_output.decode('utf-8')

            else:
                ffmpeg_is_installed = False
                ffmpeg_output = 'ffmpeg is not installed'
        except Exception:
            ffmpeg_is_installed = False
            ffmpeg_output = 'ffmpeg is not installed'

        # wrap ffmpeg_output with width=70
        wrapper = textwrap.TextWrapper()
        ffmpeg_output = wrapper.fill(ffmpeg_output)

        ffmpeg_output = '\n**********\n'\
            + str(ffmpeg_output)\
            + '\n**********\n'

        logger.sendToLog(ffmpeg_output, "INFO")

        # log python version
        logger.sendToLog('python version: '
                         + str(sys.version))

        # log qt version
        logger.sendToLog('QT version: '
                         + str(QT_VERSION_STR))
        # log pyqt version
        if pyside6_is_installed:
            madule_str = 'PySide version: '
        else:
            madule_str = 'PyQt version: '

        logger.sendToLog(madule_str
                         + str(PYQT_VERSION_STR))

        # log os and desktop env.
        logger.sendToLog('Operating system: '
                         + os_type)

        # windows and mac haven't desktop_env
        if desktop_env:
            logger.sendToLog('Desktop env.: '
                             + str(desktop_env))

# start aria2 when Persepolis starts


class StartAria2Thread(QThread):
    ARIA2RESPONDSIGNAL = Signal(str)

    def __init__(self):
        QThread.__init__(self)

    def run(self):
        # aria_startup_answer is None when Persepolis starts! and after
        # ARIA2RESPONDSIGNAL emitting yes , then startAriaMessage function
        # changing aria_startup_answer to 'Ready'
        # ARIA2RESPONDSIGNAL have 3 conditions >>>
        # 1. no (aria didn't respond) 2. yes(aria is respond) 3.try again(Persepolis retry to connecting aria2)
        global aria_startup_answer

        # check that aria2 is running or not!
        answer = download.aria2Version()

        # if Aria2 wasn't started before, so start it!
        if answer == 'did not respond':

            # write in log file.
            logger.sendToLog("Starting Aria2", "INFO")

            # try 5 time if aria2 doesn't respond!
            for i in range(5):

                answer = download.startAria()

                if answer == 'did not respond' and i != 4:

                    signal_str = 'try again'
                    self.ARIA2RESPONDSIGNAL.emit(signal_str)
                    sleep(2)

                else:
                    break

        # if Aria2 doesn't respond to Persepolis ,ARIA2RESPONDSIGNAL is
        # emitting no
        if answer == 'did not respond':

            signal_str = 'no'

        else:

            # Aria2 is responding :)
            signal_str = 'yes'
            logger.sendToLog("Aria2 is running", "INFO")
            logger.sendToLog("Aria2 version: "
                             + answer, "INFO")

        # emit the signal
        # ARIA2RESPONDSIGNAL have 3 conditions >>>
        # 1. no (aria didn't respond) 2. yes(aria is respond) 3.try again(Persepolis retry to connecting aria2)
        self.ARIA2RESPONDSIGNAL.emit(signal_str)


# This thread checking that which row in download_table highlighted by user
class CheckSelectedRowThread(QThread):
    CHECKSELECTEDROWSIGNAL = Signal()

    def __init__(self):
        QThread.__init__(self)

    def run(self):
        while shutdown_notification == 0 and aria_startup_answer != 'ready':
            sleep(1)
        while shutdown_notification == 0:
            sleep(0.2)
            self.CHECKSELECTEDROWSIGNAL.emit()


# This thread is getting download information from aria2 and updating database
# this class is checking aria2 rpc connection! if aria rpc is not
# available , this class restarts aria!
class CheckDownloadInfoThread(QThread):
    DOWNLOAD_INFO_SIGNAL = Signal(list)
    RECONNECTARIASIGNAL = Signal(str)

    def __init__(self, parent):
        QThread.__init__(self)
        self.parent = parent

    def run(self):
        global checking_flag
        global shutdown_notification
        while True:

            # shutdown_notification = 0 >> persepolis is running
            # 1 >> persepolis is ready for closing(closeEvent called)
            # 2 >> OK, let's close application!

            # checking_flag : 0 >> normal situation ;
            # 1 >> remove button or delete button pressed or sorting form viewMenu selected by user ;
            # 2 >> check_download_info function is stopping until remove operation done ;
            # 3 >> deleteFileAction is done it's job and It is called removeButtonPressed.

            # wait until aria gets ready!(see StartAria2Thread for more information)
            while shutdown_notification == 0 and aria_startup_answer != 'ready':
                sleep(1)

            # data base is updated one time in five times.
            update_data_base = False
            update_data_base_counter = 0
            while shutdown_notification != 1:
                sleep(0.2)
                # if checking_flag is equal to 1, it means that user pressed
                # remove or delete button . so checking download information
                # must stop until removing is done! It avoids possibility of crashing!
                if checking_flag == 1:
                    # Ok loop is stopped!
                    checking_flag = 2

                    # check that when job is done!
                    while checking_flag != 0:
                        sleep(0.2)

                # lets getting downloads information from aria and putting them in download_status_list!

                # find gid of active downloads first! (get them from data base)
                # output of this method is a list of gid
                active_gid_list = self.parent.temp_db.returnActiveGids()

                # get download status of active downloads from aria2
                # download_status_list is a list that contains some dictionaries.
                # every dictionary contains download information.
                # gid_list is a list that contains gid of downloads in download_status_list.
                # see download.py file for more information.
                gid_list, download_status_list = download.tellActive()

                try:
                    for gid in active_gid_list:

                        # if gid not in gid_list, so download is completed or stopped or error occurred!
                        # because aria2 returns active downloads status with tellActive function in download.py file.
                        # and complete or stopped or errored downloads are not active downloads.
                        # so we must get download information with tellStatus function.
                        # see download.py file (tellStatus and tellActive functions) for more information.
                        # if aria doesn't not return download information with tellStatus and tellActive,
                        # then perhaps some error occurred.so download information must be in data_base.
                        if gid not in gid_list:

                            returned_dict = download.tellStatus(gid, self.parent)
                            if returned_dict:
                                download_status_list.append(returned_dict)
                                update_data_base = True
                            else:
                                # check data_base
                                returned_dict = self.parent.persepolis_db.searchGidInDownloadTable(gid)
                                download_status_list.append(returned_dict)

                                # if returned_dict in None, check for availability of RPC connection.
                                if not(returned_dict):
                                    self.reconnectAria()
                                    continue

                    if not(download_status_list):
                        download_status_list = []

                    # now we have a list that contains download information (download_status_list)
                    # lets update download table in main window and update data base!
                    # first emit a signal for updating MainWindow.
                    self.DOWNLOAD_INFO_SIGNAL.emit(download_status_list)

                    # data base is updated 1 time in 5 times.
                    if update_data_base_counter == 4:
                        update_data_base = True
                    else:
                        update_data_base_counter = update_data_base_counter + 1

                    # update data base!
                    if update_data_base:
                        self.parent.persepolis_db.updateDownloadTable(download_status_list)

                        # data base is updated 1 time in 5 times.
                        update_data_base = False
                        update_data_base_counter = -1

                except Exception:
                    # continue the loop if any error occurred.
                    self.reconnectAria()
                    continue

            # Ok exit loop! get ready for shutting down!
            shutdown_notification = 2
            break

# when rpc connection between persepolis and aria is
# disconnected then aria2_disconnected = 1
    def reconnectAria(self):
        global aria2_disconnected
        aria2_disconnected = 0
        # check aria2 availability by aria2Version function(see download.py file fore more information)
        answer = download.aria2Version()

        if answer == 'did not respond':
            # so aria2 connection in disconnected!
            # lets try to reconnect aria 5 times!
            for i in range(5):
                answer = download.startAria()  # start aria2
                if answer == 'did not respond' and i != 4:  # check answer
                    sleep(2)
                else:
                    # emit answer.
                    # if answer is 'did not respond', it means that reconnecting aria was not successful
                    self.RECONNECTARIASIGNAL.emit(str(answer))
                    break


# SpiderThread calls spider in spider.py .
# spider finds file size and file name of download file .
# spider works similar to spider in wget.
class SpiderThread(QThread):
    SPIDERSIGNAL = Signal(dict)

    def __init__(self, add_link_dictionary, parent):
        QThread.__init__(self)
        self.add_link_dictionary = add_link_dictionary
        self.parent = parent

    def run(self):
        try:
            # get file_name and file size with spider
            file_name, size = spider.spider(self.add_link_dictionary)

            # update data base
            dictionary = {'file_name': file_name, 'size': size, 'gid': self.add_link_dictionary['gid']}
            self.parent.persepolis_db.updateDownloadTable([dictionary])
            dictionary = self.parent.persepolis_db.searchGidInDownloadTable(dictionary['gid'])

            # update table in MainWindow
            self.SPIDERSIGNAL.emit(dictionary)

        except Exception:
            # write ERROR message
            logger.sendToLog(
                "Spider couldn't find download information", "ERROR")

# this thread sending download request to aria2


class DownloadLink(QThread):
    ARIA2NOTRESPOND = Signal()

    def __init__(self, gid, parent):
        QThread.__init__(self)
        self.gid = gid
        self.parent = parent

    def run(self):
        # add gid of download to the active gids in temp_db
        # or update data base , if it was existed before
        try:
            self.parent.temp_db.insertInSingleTable(self.gid)
        except Exception:
            # release lock
            self.parent.temp_db.lock = False
            dictionary = {'gid': self.gid, 'status': 'active'}
            self.parent.temp_db.updateSingleTable(dictionary)

        # if request is not successful then persepolis is checking rpc
        # connection with download.aria2Version() function
        answer = download.downloadAria(self.gid, self.parent)
        if answer is False:
            version_answer = download.aria2Version()

            if version_answer == 'did not respond':
                self.ARIA2NOTRESPOND.emit()

# Persepolis download audio and video separately and the muxing them :)
# VideoFinder do this duty for Persepolis.
# see data_base.py for understanding the code
# we have video_finder_db_table in data base. it's contains some items that helps
# VideoFinder for managing the situation.
# video_gid >> GID of video link
# audio_gid >> GID of audio link
# video_completed >> Is video downloaded completely?
# audio_completed >> Is audio downloaded completely?
# checking >> VideoFinder must checking or not!


class VideoFinder(QThread):
    VIDEOFINDERCOMPLETED = Signal(dict)

    def __init__(self, video_finder_dictionary, parent):
        QThread.__init__(self)
        self.parent = parent
        self.video_finder_dictionary = video_finder_dictionary

    # First: Download video
    # Second: Download audio
    # Third: Mux video and audio
    def run(self):
        self.video_completed = self.video_finder_dictionary['video_completed']
        self.audio_completed = self.video_finder_dictionary['audio_completed']
        self.muxing = 'no'
        self.checking = 'no'
        self.active = 'yes'

        video_gid = self.video_finder_dictionary['video_gid']
        audio_gid = self.video_finder_dictionary['audio_gid']

        # find category
        dictionary = self.parent.persepolis_db.searchGidInDownloadTable(video_gid)
        category = dictionary['category']

        # VideoFinder handles downloads by itself, if category is "Single Downloads"
        if category == 'Single Downloads':

            # create an item for this thread in temp_db if not exists!
            try:
                video_finder_plus_gid = 'video_finder_' + str(video_gid)
                self.parent.temp_db.insertInQueueTable(video_finder_plus_gid)
            except Exception:
                # release lock
                self.parent.temp_db.lock = False

            # check start time and end time
            add_link_dictionary = self.parent.persepolis_db.searchGidInAddLinkTable(video_gid)
            start_time = add_link_dictionary['start_time']
            # end_time = add_link_dictionary['end_time']

            if self.video_completed == 'no' and start_time:

                # set start time only for video and cancel start time for audio.
                # because video will downloaded first and start time must be set for first video! not second one
                self.parent.persepolis_db.setDefaultGidInAddlinkTable(audio_gid, start_time=True)

        # update checking status in data base for starting the job!
        self.checking = 'yes'
        self.video_finder_dictionary['checking'] = 'yes'

        self.parent.persepolis_db.updateVideoFinderTable([self.video_finder_dictionary])

        # if category "Single Downloads" >> manage download yourself.
        # if category is not "Single Download" >> just check the status time to time and wait until download ends!
        if self.video_completed == 'no':
            if category == "Single Downloads":

                # start video downloading
                new_download = DownloadLink(video_gid, self.parent)
                self.parent.threadPool.append(new_download)
                self.parent.threadPool[-1].start()
                self.parent.threadPool[-1].ARIA2NOTRESPOND.connect(self.parent.aria2NotRespond)

            # check the download status
            # continue loop and check the download status
            # if checking == 'no' >> problem occurred and downloading canceled.
            while self.video_completed != 'yes' and self.checking == 'yes':

                sleep(1)

        if self.video_completed == 'yes':

            if self.video_finder_dictionary['video_completed'] == 'no':

                # update data base
                self.video_finder_dictionary['video_completed'] = 'yes'

                self.parent.persepolis_db.updateVideoFinderTable([self.video_finder_dictionary])

            # video is downloaded completely!
            # let's start audio downloading
            if self.audio_completed == 'no':

                # if category "Single Downloads" >> start download yourself.
                # if category is not "Single Download" >> just check the status time to time
                if category == "Single Downloads":
                    new_download = DownloadLink(audio_gid, self.parent)
                    self.parent.threadPool.append(new_download)
                    self.parent.threadPool[-1].start()
                    self.parent.threadPool[-1].ARIA2NOTRESPOND.connect(self.parent.aria2NotRespond)

                # check the download status
                # continue loop and check the download status
                # if checking == 'no' >> problem occurred and downloading canceled.
                while self.audio_completed != 'yes' and self.checking == 'yes':
                    sleep(1)

        self.checking = 'no'

        # lets start muxing!
        if self.video_completed == 'yes' and self.audio_completed == 'yes':

            self.video_finder_dictionary['audio_completed'] = 'yes'
            self.video_finder_dictionary['checking'] = 'no'
            self.video_finder_dictionary['muxing_status'] = 'started'

            self.muxing = 'started'

            # update data base
            self.parent.persepolis_db.updateVideoFinderTable([self.video_finder_dictionary])

            # audio and video files are downloaded completely.
            # lets start muxing
            result_dictionary = muxer(self.parent, self.video_finder_dictionary)
            error_message = result_dictionary['error']
            ffmpeg_error_message = result_dictionary['ffmpeg_error_message']

            if ffmpeg_error_message:
                logger.sendToLog('ffmpeg error: ' + str(ffmpeg_error_message), 'ERROR')

            if error_message == 'no error':
                self.video_finder_dictionary['muxing_status'] = 'complete'
                self.muxing = 'complete'
            else:
                self.video_finder_dictionary['muxing_status'] = 'error'
                self.muxing = 'error'

             # update data base
            self.parent.persepolis_db.updateVideoFinderTable([self.video_finder_dictionary])

            complete_dictionary = {'error': error_message,
                                   'final_path': result_dictionary['final_path'],
                                   'final_size': result_dictionary['final_size'],
                                   'video_gid': self.video_finder_dictionary['video_gid'],
                                   'audio_gid': self.video_finder_dictionary['audio_gid'],
                                   'download_path': self.video_finder_dictionary['download_path'],
                                   'category': category}

            # emit error_message
            self.VIDEOFINDERCOMPLETED.emit(complete_dictionary)

        self.active = 'no'

        if category == 'Single Downloads':

            # check if user selected shutdown after download in progress window.
            shutdown_dict = self.parent.temp_db.returnCategory(video_finder_plus_gid)
            shutdown_status = shutdown_dict['shutdown']

            if shutdown_status == 'wait':

                # it means user want to persepolis shutdown system after download.
                # write 'shutdown' value for this category in temp_db
                shutdown_dict = {'category': video_finder_plus_gid,
                                 'shutdown': 'shutdown'}
                self.parent.temp_db.updateQueueTable(shutdown_dict)


# this thread is managing queue and sending download request to aria2
class Queue(QThread):
    # this signal emitted when download status of queue changes to stop
    REFRESHTOOLBARSIGNAL = Signal(str)

    def __init__(self, category, start_time, end_time, parent):
        QThread.__init__(self)
        self.category = str(category)
        self.parent = parent
        self.start_time = start_time
        self.end_time = end_time

    def run(self):
        self.start = True
        self.stop = False
        self.limit = False
        self.limit_changed = False
        self.after = False
        self.break_for_loop = False

        queue_counter = 0

        # this list contains gid_list of all active video finder in queue.
        video_finder_list = []

        # queue repeats 5 times!
        # and every time loads queue list again!
        # It is helps for checking new downloads in queue
        # and retrying failed downloads.
        for counter in range(5):

            # read downloads information from data base
            download_table_dict = self.parent.persepolis_db.returnItemsInDownloadTable(self.category)
            category_table_dict = self.parent.persepolis_db.searchCategoryInCategoryTable(self.category)

            gid_list = eval(category_table_dict['gid_list'])

            # sort downloads top to the bottom of the list OR bottom to the top
            if not(self.parent.reverse_checkBox.isChecked()):
                gid_list.reverse()

            # check that if user set start time
            if self.start_time and counter == 0:

                # find first download
                # set start time for first download in queue
                # status of first download must not be complete
                for gid in gid_list:

                    # get download information dictionary
                    dictionary = download_table_dict[gid]

                    # find status of download
                    status = dictionary['status']

                    if status != 'complete':
                        # We find first item! GREAT!
                        add_link_dict = {'gid': gid}

                        # set start_time for this download
                        add_link_dict['start_time'] = self.start_time

                        # write changes in data base
                        self.parent.persepolis_db.updateAddLinkTable([add_link_dict])

                        # delete add_link_dict
                        del add_link_dict

                        # job is done! break the loop
                        break

            for gid in gid_list:

                # if gid is related to video finder, so start  Video Finder thread for checking status
                # check video_finder_threads_dict, perhaps a thread started before for this gid
                if (gid in self.parent.all_video_finder_gid_list):

                    video_finder_dictionary = self.parent.persepolis_db.searchGidInVideoFinderTable(gid)

                    if video_finder_dictionary['video_gid'] not in self.parent.video_finder_threads_dict.keys():

                        # start new video finder thread
                        video_finder_gid_list = [video_finder_dictionary['video_gid'],
                                                 video_finder_dictionary['audio_gid']]

                        new_video_finder = VideoFinder(video_finder_dictionary, self.parent)
                        self.parent.threadPool.append(new_video_finder)
                        self.parent.threadPool[-1].start()
                        self.parent.threadPool[-1].VIDEOFINDERCOMPLETED.connect(self.parent.videoFinderCompleted)

                        # add thread to video_finder_threads_dict
                        self.parent.video_finder_threads_dict[video_finder_dictionary['video_gid']] = new_video_finder

                        video_finder_list.append(video_finder_gid_list)

                add_link_dict = {'gid': gid}

                # find download information
                dictionary = download_table_dict[gid]

                # if download was completed, continue the loop
                # with the next iteration of the loop!
                # We don't want to download it two times :)
                if dictionary['status'] == 'complete':
                    continue

                queue_counter = queue_counter + 1

                # change status of download to waiting
                status = 'waiting'
                dictionary['status'] = status

                if self.end_time:

                    # it means user was set end time for download
                    # set end_hour and end_minute
                    add_link_dict['end_time'] = self.end_time

                # user can set sleep time between download items in queue.
                # see preferences window!
                # find wait_queue value
                wait_queue_list = self.parent.persepolis_setting.value('settings/wait-queue')
                wait_queue_hour = int(wait_queue_list[0])
                wait_queue_minute = int(wait_queue_list[1])

                # check if user set sleep time between downloads in queue in setting window.
                # if queue_counter is 1 , it means we are in the first download item in queue.
                # and no need to wait for first item.
                if (wait_queue_hour != 0 or wait_queue_minute != 0) and queue_counter != 1:
                    now_time_hour = int(time.strftime("%H"))
                    now_time_minute = int(time.strftime("%M"))
                    now_time_second = int(time.strftime("%S"))

                    # add extra minute if we are in second half of minute
                    if now_time_second > 30:
                        now_time_minute = now_time_minute + 1

                    # hour value can not be more than 23 and minute value can not be more than 59.
                    sigma_minute = wait_queue_minute + now_time_minute
                    sigma_hour = wait_queue_hour + now_time_hour
                    if sigma_minute > 59:
                        sigma_minute = sigma_minute - 60
                        sigma_hour = sigma_hour + 1

                    if sigma_hour > 23:
                        sigma_hour = sigma_hour - 24

                    # setting sigma_hour and sigma_minute for download's start time!
                    add_link_dict['start_time'] = str(sigma_hour) + ':' + str(sigma_minute)

                # write changes in data base
                self.parent.persepolis_db.updateAddLinkTable([add_link_dict])

                # delete add_link_dict
                del add_link_dict

                # start new thread for download
                new_download = DownloadLink(gid, self.parent)
                self.parent.threadPool.append(new_download)
                self.parent.threadPool[-1].start()
                self.parent.threadPool[-1].ARIA2NOTRESPOND.connect(self.parent.aria2NotRespond)
                sleep(3)

                # limit download speed if user limited speed for previous download
                if self.limit:
                    self.limit_changed = True

                # continue loop until download has finished
                while status == 'downloading' or status == 'waiting' or status == 'paused' or status == 'scheduled':

                    sleep(1)
                    dictionary = self.parent.persepolis_db.searchGidInDownloadTable(gid)

                    status = dictionary['status']

                    if status == 'error':
                        error = 'error'
                        # write error_message in log file
                        error_message = 'Download failed - GID : '\
                            + str(gid)\
                            + '- Message : '\
                            + error

                        logger.sendToLog(error_message, 'ERROR')

                    elif status == 'complete':
                        complete_message = 'Download complete - GID : '\
                            + str(gid)

                        # write in log the complete_message
                        logger.sendToLog(complete_message, 'INFO')

                        # check that is this related to video finder thread or not.
                        if gid in self.parent.all_video_finder_gid_list:

                            # find related thread
                            for list in video_finder_list:

                                if gid in list:

                                    video_gid = list[0]

                                    if video_gid in self.parent.video_finder_threads_dict:
                                        video_finder_thread = self.parent.video_finder_threads_dict[video_gid]

                                        # check the video and audio and muxing_status
                                        if video_finder_thread.video_completed == 'yes' and \
                                        video_finder_thread.audio_completed == 'yes':

                                            # wait until end of muxing
                                            while video_finder_thread.active == 'yes':

                                                sleep(0.5)

                                break

                    if self.stop:
                        # it means user stopped queue
                        answer = download.downloadStop(gid, self.parent)

                        # if aria2 did not respond , then this function is checking
                        # for aria2 availability , and if aria2 disconnected then
                        # aria2Disconnected is executed
                        if answer == 'None':
                            version_answer = download.aria2Version()
                            if version_answer == 'did not respond':
                                self.parent.aria2Disconnected()
                        status = 'stopped'

                    if self.limit and status == 'downloading' and self.limit_changed:
                        # It means user want to limit download speed
                        # get limitation value
                        self.limit_comboBox_value = self.parent.limit_comboBox.currentText()
                        self.limit_spinBox_value = self.parent.limit_spinBox.value()
                        if self.limit_comboBox_value == "KiB/s":
                            limit = str(self.limit_spinBox_value) + str("K")
                        else:
                            limit = str(self.limit_spinBox_value) + str("M")

                        # apply limitation
                        download.limitSpeed(gid, limit)

                        # done!
                        self.limit_changed = False

                    if not(self.limit) and status == 'downloading' and self.limit_changed:
                        # speed limitation is canceled by user!
                        # cancel limitation
                        download.limitSpeed(gid, "0")

                        # done!
                        self.limit_changed = False

                # it means queue stopped at end time or user stopped queue
                if status == 'stopped':

                    for video_finder_gid_list in video_finder_list:

                        video_gid = video_finder_gid_list[0]

                        video_finder_dictionary = self.parent.persepolis_db.searchGidInVideoFinderTable(video_gid)

                        if video_finder_dictionary:

                            # tell video finder thread to stop checking
                            if video_finder_dictionary['video_completed'] == 'no' or \
                                video_finder_dictionary['audio_completed'] == 'no':

                                video_finder_dictionary['checking'] = 'no'
                                self.parent.persepolis_db.updateVideoFinderTable([video_finder_dictionary])

                                video_finder_thread = self.parent.video_finder_threads_dict[video_gid]
                                video_finder_thread.checking = 'no'

                            elif not(self.stop) and self.after and \
                                video_finder_dictionary['muxing_status'] == 'started':
                                # downloads were completed and video finder started Muxing
                                # wait until the end of muxing
                                # don't turn of the computer.
                                # video finder will be deleted from data base when muxing ended.
                                # so check data base every second

                                video_finder_thread = self.parent.video_finder_threads_dict[
                                    video_finder_dictionary['video_gid']
                                ]

                                while video_finder_thread.active == 'yes':
                                    sleep(1)

                    if self.stop and self.after:
                        # It means user activated shutdown before and now user
                        # stopped queue . so after download must be canceled
                        self.parent.after_checkBox.setChecked(False)

                    self.stop = True
                    self.limit = False
                    self.limit_changed = False

                    # it means that break outer "for" loop
                    self.break_for_loop = True

                    if str(self.parent.category_tree.currentIndex().data()) == str(self.category):
                        self.REFRESHTOOLBARSIGNAL.emit(self.category)

                    # show notification
                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", APP_NAME.capitalize()),
                               QCoreApplication.translate("mainwindow_src_ui_tr", "Queue Stopped!"),
                               10000, 'no', parent=self.parent)

                    # write message in log
                    logger.sendToLog('Queue stopped', 'INFO')

                    break

            if self.break_for_loop:
                break

        if self.start:
            # if queue finished :
            self.start = False

            # this section is sending shutdown signal to the shutdown script(if user
            # select shutdown for after download)
            if self.after:
                # shutdown aria2c
                answer = download.shutDown()

                # KILL aria2c if didn't respond. R.I.P :))
                if not(answer) and (os_type != OS.WINDOWS):

                    subprocess.Popen(['killall', 'aria2c'],
                                     stderr=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stdin=subprocess.PIPE,
                                     shell=False)

                # write 'shutdown' value for this category in temp_db
                shutdown_dict = {'category': self.category, 'shutdown': 'shutdown'}
                self.parent.temp_db.updateQueueTable(shutdown_dict)

                # show a notification about system is shutting down now!
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", 'Persepolis is shutting down'),
                           QCoreApplication.translate("mainwindow_src_ui_tr", 'your system in 20 seconds'),
                           15000, 'warning', parent=self.parent)

            # show notification for queue completion
            notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Persepolis"),
                       QCoreApplication.translate("mainwindow_src_ui_tr", 'Queue completed!'),
                       10000, 'queue', parent=self.parent)

            # write a message in log
            logger.sendToLog('Queue completed', 'INFO')

            self.stop = True
            self.limit = False
            self.limit_changed = False
            self.after = False

            if str(self.parent.category_tree.currentIndex().data()) == str(self.category):
                self.REFRESHTOOLBARSIGNAL.emit(self.category)


# CheckingThread have 2 duty!
# 1-this class is checking that if user add a link with browsers plugin.
# 2-assume that user executed program before .
# if user is clicking on persepolis icon in menu this tread emits SHOWMAINWINDOWSIGNAL
class CheckingThread(QThread):
    CHECKPLUGINDBSIGNAL = Signal()
    SHOWMAINWINDOWSIGNAL = Signal()

    def __init__(self):
        QThread.__init__(self)

    def run(self):
        global shutdown_notification
        global plugin_links_checked

# shutdown_notification = 0 >> persepolis is running
# 1 >> persepolis is ready for closing(closeEvent called)
# 2 >> OK, let's close application!
        while shutdown_notification == 0 and aria_startup_answer != 'ready':
            sleep(2)

        while shutdown_notification == 0:
            sleep(0.2)

            # it means , user clicked on persepolis icon and persepolis is
            # still running. see persepolis file for more details.
            if os.path.isfile(show_window_file):
                # OK! we catch notification! remove show_window_file now!
                osCommands.remove(show_window_file)

                # emit a signal to notify MainWindow for showing itself!
                self.SHOWMAINWINDOWSIGNAL.emit()

            # It means new browser plugin call is available!
            if os.path.isfile(plugin_ready):

                # OK! We received notification! remove plugin_ready file
                osCommands.remove(plugin_ready)

                # When checkPluginCall method considered request , then
                # plugin_links_checked is changed to True
                plugin_links_checked = False
                self.CHECKPLUGINDBSIGNAL.emit()  # notifying that we have browser_plugin request
                while plugin_links_checked is not True:  # wait for persepolis consideration!
                    sleep(0.5)


# if checking_flag is equal to 1, it means that user pressed remove or delete button or ... .
# so checking download information must be stopped until job is done!
# this thread checks checking_flag and when checking_flag changes to 2
# QTABLEREADY signal is emitted
class WaitThread(QThread):
    QTABLEREADY = Signal()

    def __init__(self):
        QThread.__init__(self)

    def run(self):
        global checking_flag
        checking_flag = 1
        while checking_flag != 2:
            sleep(0.05)
        self.QTABLEREADY.emit()

# button_pressed_counter changed if user pressed move up and move down and ... actions
# this thread is changing checking_flag to zero if button_pressed_counter
# don't change for 2 seconds


class ButtonPressedThread(QThread):
    def __init__(self):
        QThread.__init__(self)

    def run(self):
        global checking_flag
        current_button_pressed_value = deepcopy(button_pressed_counter) + 1
        while current_button_pressed_value != button_pressed_counter:
            current_button_pressed_value = deepcopy(button_pressed_counter)
            sleep(2)
# job is done!
        checking_flag = 0


class ShutDownThread(QThread):
    def __init__(self, parent, category, password=None):
        QThread.__init__(self)
        self.category = category
        self.password = password
        self.parent = parent

    def run(self):
        shutDown(self.parent, category=self.category, password=self.password)


# this thread is keeping system awake! because if system sleeps , then internet connection is disconnected!
# strategy is simple! a loop is checking mouse position every 20 seconds.
# if mouse position didn't change, cursor is moved by QCursor.setPos() (see keepAwake method) !
# so this is keeping system awake!
#
class KeepAwakeThread(QThread):

    KEEPSYSTEMAWAKESIGNAL = Signal(bool)

    def __init__(self):
        QThread.__init__(self)

    def run(self):
        while True:

            while shutdown_notification == 0 and aria_startup_answer != 'ready':
                sleep(1)

            old_cursor_array = [0, 0]
            add = True

            while shutdown_notification != 1:
                sleep(20)

                # finding cursor position
                cursor_position = QCursor.pos()
                new_cursor_array = [int(cursor_position.x()), int(cursor_position.y())]

                if new_cursor_array == old_cursor_array:
                    # So cursor position didn't change for 20 second.
                    if add:  # Moving mouse position one time +10 pixel and one time -10 pixel!
                        self.KEEPSYSTEMAWAKESIGNAL.emit(add)
                        add = False
                    else:
                        self.KEEPSYSTEMAWAKESIGNAL.emit(add)
                        add = True

                old_cursor_array = new_cursor_array

# This thread moves files to another destination.
# see moveSelectedDownloads method for more information.


class MoveThread(QThread):
    def __init__(self, parent, gid_list, new_folder_path):
        QThread.__init__(self)
        self.new_folder_path = new_folder_path
        self.parent = parent
        self.gid_list = gid_list

    def run(self):
        add_link_dict_list = []
        # move selected downloads
        # find row number for specific gid
        for gid in self.gid_list:
            # find download path
            dictionary = self.parent.persepolis_db.searchGidInAddLinkTable(gid)
            self.old_file_path = dictionary['download_path']

            # find file_name
            self.file_name = os.path.basename(self.old_file_path)

            self.move = osCommands.moveFile(self.old_file_path, self.new_folder_path)

            # if moving is not successful, notify user.
            if not(self.move):
                notifySend(str(self.file_name),
                           QCoreApplication.translate("mainwindow_src_ui_tr", 'Operation was not successful!'),
                           5000, 'warning', parent=self.parent)
            else:
                new_file_path = os.path.join(self.new_folder_path, self.file_name)
                add_link_dict = {'gid': gid,
                                 'download_path': new_file_path}

                # add add_link_dict to add_link_dict_list
                add_link_dict_list.append(add_link_dict)

        # update data base
        self.parent.persepolis_db.updateAddLinkTable(add_link_dict_list)

        # notify user that job is done!
        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Moving is"),
                   QCoreApplication.translate("mainwindow_src_ui_tr", 'finished!'),
                   5000, 'warning', parent=self.parent)


class MainWindow(MainWindow_Ui):
    def __init__(self, start_in_tray, persepolis_main, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.persepolis_main = persepolis_main
        global icons
        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'
# add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # find temp_download_folder
        global temp_download_folder
        temp_download_folder = persepolis_setting.value('settings/download_path_temp')

        # this variable is changed to True,
        # if user highlights multiple items in download_table
        self.multi_items_selected = False

        # this variable is changed to False when
        # user clicks on 'hide options' button in
        # side panel.
        # see showQueuePanelOptions method for more information.
        self.show_queue_panel = True

# system_tray_icon
        self.system_tray_icon = QSystemTrayIcon()
        self.system_tray_icon.setIcon(
            QIcon(':/ghermez.png'))

        # menu of system tray icon
        system_tray_menu = QMenu()
        system_tray_menu.addAction(self.addlinkAction)
        system_tray_menu.addAction(self.videoFinderAddLinkAction)
        system_tray_menu.addAction(self.stopAllAction)
        system_tray_menu.addAction(self.minimizeAction)
        system_tray_menu.addAction(self.exitAction)
        self.system_tray_icon.setContextMenu(system_tray_menu)

        # if system tray icon pressed:
        self.system_tray_icon.activated.connect(self.systemTrayPressed)

        # show system_tray_icon
        self.system_tray_icon.show()

        # check trayAction
        self.trayAction.setChecked(True)

        # check user preference for showing or hiding system_tray_icon
        if self.persepolis_setting.value('settings/tray-icon') != 'yes' and start_in_tray is False:
            self.minimizeAction.setEnabled(False)
            self.trayAction.setChecked(False)
            self.system_tray_icon.hide()

        # hide MainWindow if start_in_tray is equal to "yes"
        if start_in_tray:
            self.minimizeAction.setText(QCoreApplication.translate("mainwindow_src_ui_tr", 'Show main Window'))
            self.minimizeAction.setIcon(QIcon(icons + 'window'))

        # check user preference for showing or hiding menubar.
        # (It's not for mac osx or DE that have global menu like kde plasma)
        if self.persepolis_setting.value('settings/show-menubar') == 'yes':
            self.menubar.show()
            self.showMenuBarAction.setChecked(True)
            self.toolBar2.hide()
        else:
            self.menubar.hide()
            self.showMenuBarAction.setChecked(False)
            self.toolBar2.show()

        if os_type == OS.OSX:
            self.showMenuBarAction.setEnabled(False)

        # check user preferences for showing or hiding sidepanel.
        if self.persepolis_setting.value('settings/show-sidepanel') == 'yes':
            self.category_tree_qwidget.show()
            self.showSidePanelAction.setChecked(True)
        else:
            self.category_tree_qwidget.hide()
            self.showSidePanelAction.setChecked(False)

        # set message for statusbar
        self.statusbar.showMessage(QCoreApplication.translate("mainwindow_src_ui_tr", 'Please Wait...'))

        self.checkSelectedRow()

        # list of threads
        self.threadPool = []

        # start aria2
        start_aria = StartAria2Thread()
        self.threadPool.append(start_aria)
        self.threadPool[0].start()
        self.threadPool[0].ARIA2RESPONDSIGNAL.connect(self.startAriaMessage)

    # initializing
        # create an object for PluginsDB
        self.plugins_db = PluginsDB()

        # create an object for DataBase
        self.persepolis_db = DataBase()

        # create an object fo TempDB
        self.temp_db = TempDB()

        # create tables
        self.temp_db.createTables()

        # check tables in data_base, and change required values to default value.
        # see data_base.py for more information.
        self.persepolis_db.setDBTablesToDefaultValue()

        # get queues name from data base
        queues_list = self.persepolis_db.categoriesList()

        # add queues to category_tree(left side panel)
        for category_name in queues_list:
            new_queue_category = QStandardItem(category_name)
            font = QFont()
            font.setBold(True)
            new_queue_category.setFont(font)
            new_queue_category.setEditable(False)
            self.category_tree_model.appendRow(new_queue_category)

        # read from data base

        # add download items to the download_table
        # read download items from data base
        download_table_dict = self.persepolis_db.returnItemsInDownloadTable()

        # read gid_list from date base
        category_dict = self.persepolis_db.searchCategoryInCategoryTable('All Downloads')
        gid_list = eval(category_dict['gid_list'])

        keys_list = ['file_name',
                     'status',
                     'size',
                     'downloaded_size',
                     'percent',
                     'connections',
                     'rate',
                     'estimate_time_left',
                     'gid',
                     'link',
                     'first_try_date',
                     'last_try_date',
                     'category'
                     ]

        # insert items in download_table
        for gid in gid_list:
            # create new row
            self.download_table.insertRow(0)

            dict = download_table_dict[gid]
            i = 0
            for key in keys_list:
                item = QTableWidgetItem(str(dict[key]))
                self.download_table.setItem(0, i, item)
                i = i + 1

        # get video_finder gids
        self.all_video_finder_gid_list, self.all_video_finder_video_gid_list, self.all_video_finder_audio_gid_list = self.persepolis_db.returnVideoFinderGids()  # noqa: E501

        # defining some lists and dictionaries for running addlinkwindows and
        # propertieswindows and propertieswindows , ...
        self.addlinkwindows_list = []
        self.propertieswindows_list = []
        self.progress_window_list = []
        self.afterdownload_list = []
        self.text_queue_window_list = []
        self.about_window_list = []
        self.plugin_queue_window_list = []
        self.checkupdatewindow_list = []
        self.logwindow_list = []
        self.progress_window_list_dict = {}
        self.capturekeywindows_list = []

        # queue_list_dict contains queue threads >> queue_list_dict[name of queue]
        self.queue_list_dict = {}

        # this dictionary contains VideoFinder threads
        # key = video_gid and value = VideoFinder thread
        self.video_finder_threads_dict = {}

        # CheckDownloadInfoThread
        check_download_info = CheckDownloadInfoThread(self)
        self.threadPool.append(check_download_info)
        self.threadPool[1].start()
        self.threadPool[1].DOWNLOAD_INFO_SIGNAL.connect(self.checkDownloadInfo)
        self.threadPool[1].RECONNECTARIASIGNAL.connect(self.reconnectAria)

        # CheckSelectedRowThread
        check_selected_row = CheckSelectedRowThread()
        self.threadPool.append(check_selected_row)
        self.threadPool[2].start()
        self.threadPool[2].CHECKSELECTEDROWSIGNAL.connect(
            self.checkSelectedRow)

        # CheckingThread
        check_browser_plugin = CheckingThread()
        self.threadPool.append(check_browser_plugin)
        self.threadPool[3].start()
        self.threadPool[3].CHECKPLUGINDBSIGNAL.connect(self.checkPluginCall)
        self.threadPool[3].SHOWMAINWINDOWSIGNAL.connect(self.showMainWindow)

        # keepAwake
        self.ongoing_downloads = 0
        keep_awake = KeepAwakeThread()
        self.threadPool.append(keep_awake)
        self.threadPool[-1].start()
        self.threadPool[-1].KEEPSYSTEMAWAKESIGNAL.connect(self.keepAwake)

        # check if ffmpeg is installed
        check_ffmpeg_is_installed = CheckVersionsThread()
        self.threadPool.append(check_ffmpeg_is_installed)
        self.threadPool[-1].start()

        # finding number or row that user selected!
        self.download_table.itemSelectionChanged.connect(self.selectedRow)

        # if user  doubleclicks on an item in download_table , then openFile
        # function  executes
        self.download_table.itemDoubleClicked.connect(self.openFile)

        # connecting queue_panel_show_button to showQueuePanelOptions
        self.queue_panel_show_button.clicked.connect(
            self.showQueuePanelOptions)

        # connecting start_checkBox to startFrame
        self.start_checkBox.toggled.connect(self.startFrame)

        self.start_checkBox.setChecked(False)

        # connecting end_checkBox to endFrame
        self.end_checkBox.toggled.connect(self.endFrame)
        self.end_checkBox.setChecked(False)

        # connecting after_checkBox to afterFrame
        self.after_checkBox.toggled.connect(self.afterFrame)
        self.after_checkBox.setChecked(False)

        # connecting limit_checkBox to limitFrame
        self.limit_checkBox.toggled.connect(self.limitFrame)

        # connecting limit_pushButton to limitPushButtonPressed
        self.limit_pushButton.clicked.connect(self.limitPushButtonPressed)

        # connecting limit_comboBox and limit_spinBox to limitComboBoxChanged
        self.limit_comboBox.currentIndexChanged.connect(
            self.limitComboBoxChanged)
        self.limit_spinBox.valueChanged.connect(self.limitComboBoxChanged)

        # connecting after_pushButton to afterPushButtonPressed
        self.after_pushButton.clicked.connect(self.afterPushButtonPressed)

        # setting index of all downloads for category_tree
        global current_category_tree_index
        current_category_tree_index = self.category_tree_model.index(0, 0)
        self.category_tree.setCurrentIndex(current_category_tree_index)

        # this line set toolBar And Context Menu Items
        self.toolBarAndContextMenuItems('All Downloads')

        # It will be enabled after aria2 startup!(see startAriaMessage method)
        # .This line added for solving crash problems on startup
        self.category_tree_qwidget.setEnabled(False)

        # keep_awake_checkBox
        if str(self.persepolis_setting.value('settings/awake')) == 'yes':
            self.keep_awake_checkBox.setChecked(True)
        else:
            self.keep_awake_checkBox.setChecked(False)

        self.keep_awake_checkBox.toggled.connect(self.keepAwakeCheckBoxToggled)

        self.muxing_pushButton.clicked.connect(self.muxingPushButtonPressed)

        # finding windows_size
        size = self.persepolis_setting.value(
            'MainWindow/size', QSize(900, 500))
        position = self.persepolis_setting.value(
            'MainWindow/position', QPoint(300, 300))

        # setting window size
        self.resize(size)
        self.move(position)

        # download_table column size
        # column 0
        size = self.persepolis_setting.value(
            'MainWindow/column0', '300')
        self.download_table.setColumnWidth(0, int(size))
        # column 1
        size = self.persepolis_setting.value(
            'MainWindow/column1', '100')
        self.download_table.setColumnWidth(1, int(size))
        # column 2
        size = self.persepolis_setting.value(
            'MainWindow/column2', '100')
        self.download_table.setColumnWidth(2, int(size))
        # column 3
        size = self.persepolis_setting.value(
            'MainWindow/column3', '120')
        self.download_table.setColumnWidth(3, int(size))
        # column 4
        size = self.persepolis_setting.value(
            'MainWindow/column4', '100')
        self.download_table.setColumnWidth(4, int(size))
        # column 5
        size = self.persepolis_setting.value(
            'MainWindow/column5', '120')
        self.download_table.setColumnWidth(5, int(size))
        # column 6
        size = self.persepolis_setting.value(
            'MainWindow/column6', '100')
        self.download_table.setColumnWidth(6, int(size))
        # column 7
        size = self.persepolis_setting.value(
            'MainWindow/column7', '100')
        self.download_table.setColumnWidth(7, int(size))
        # column 10
        size = self.persepolis_setting.value(
            'MainWindow/column10', '200')
        self.download_table.setColumnWidth(10, int(size))
        # column 11
        size = self.persepolis_setting.value(
            'MainWindow/column11', '200')
        self.download_table.setColumnWidth(11, int(size))
        # column 12
        size = self.persepolis_setting.value(
            'MainWindow/column11', '200')
        self.download_table.setColumnWidth(12, int(size))

        # check maximizing situation in persepolis_setting
        if str(self.persepolis_setting.value('MainWindow/maximized')) == 'yes':
            self.showMaximized()

        # get columns visibility situation from persepolis_setting
        if str(self.persepolis_setting.value('settings/column0')) == 'yes':
            self.download_table.setColumnHidden(0, False)
        else:
            self.download_table.setColumnHidden(0, True)

        if str(self.persepolis_setting.value('settings/column1')) == 'yes':
            self.download_table.setColumnHidden(1, False)
        else:
            self.download_table.setColumnHidden(1, True)

        if str(self.persepolis_setting.value('settings/column2')) == 'yes':
            self.download_table.setColumnHidden(2, False)
        else:
            self.download_table.setColumnHidden(2, True)

        if str(self.persepolis_setting.value('settings/column3')) == 'yes':
            self.download_table.setColumnHidden(3, False)
        else:
            self.download_table.setColumnHidden(3, True)

        if str(self.persepolis_setting.value('settings/column4')) == 'yes':
            self.download_table.setColumnHidden(4, False)
        else:
            self.download_table.setColumnHidden(4, True)

        if str(self.persepolis_setting.value('settings/column5')) == 'yes':
            self.download_table.setColumnHidden(5, False)
        else:
            self.download_table.setColumnHidden(5, True)

        if str(self.persepolis_setting.value('settings/column6')) == 'yes':
            self.download_table.setColumnHidden(6, False)
        else:
            self.download_table.setColumnHidden(6, True)

        if str(self.persepolis_setting.value('settings/column7')) == 'yes':
            self.download_table.setColumnHidden(7, False)
        else:
            self.download_table.setColumnHidden(7, True)

        if str(self.persepolis_setting.value('settings/column10')) == 'yes':
            self.download_table.setColumnHidden(10, False)
        else:
            self.download_table.setColumnHidden(10, True)

        if str(self.persepolis_setting.value('settings/column11')) == 'yes':
            self.download_table.setColumnHidden(11, False)
        else:
            self.download_table.setColumnHidden(11, True)

        if str(self.persepolis_setting.value('settings/column12')) == 'yes':
            self.download_table.setColumnHidden(12, False)
        else:
            self.download_table.setColumnHidden(12, True)

        icons_size = int(self.persepolis_setting.value('settings/toolbar_icon_size'))
        self.toolBar.setIconSize(QSize(icons_size, icons_size))
        self.toolBar2.setIconSize(QSize(icons_size, icons_size))

        # check reverse_checkBox
        self.reverse_checkBox.setChecked(False)

# startAriaMessage function is showing some message on statusbar and
# sending notification when aria failed to start! see StartAria2Thread for
# more details
    def startAriaMessage(self, message):
        global aria_startup_answer
        if message == 'yes':
            sleep(0.5)
            self.statusbar.showMessage(QCoreApplication.translate("mainwindow_src_ui_tr", 'Ready...'))
            aria_startup_answer = 'ready'

            self.category_tree_qwidget.setEnabled(True)

        elif message == 'try again':
            self.statusbar.showMessage(
                QCoreApplication.translate("mainwindow_src_ui_tr",
                                           "Aria2 didn't respond! be patient! Persepolis tries again in 2 seconds!"))
            logger.sendToLog(
                "Aria2 didn't respond! be patient!Persepolis tries again in 2 seconds!",
                "WARNING")

        else:
            self.statusbar.showMessage(QCoreApplication.translate("mainwindow_src_ui_tr", 'Error...'))
            notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", 'Persepolis can not connect to Aria2'),
                       QCoreApplication.translate("mainwindow_src_ui_tr", 'Check your network & Restart Persepolis'),
                       10000, 'critical', parent=self)

            logger.sendToLog('Persepolis can not connect to Aria2', 'ERROR')

            self.propertiesAction.setEnabled(True)
            self.category_tree_qwidget.setEnabled(True)

    def reconnectAria(self, message):
        # this function is executing if RECONNECTARIASIGNAL is emitted by CheckingThread .
        # if message is 'did not respond' then a message(Persepolis can not connect to Aria2) shown
        # if message is not 'did not respond' , it means that reconnecting
        # Aria2 was successful.
        if message == 'did not respond':
            self.statusbar.showMessage(QCoreApplication.translate("mainwindow_src_ui_tr", 'Error...'))
            notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", 'Persepolis can not connect to Aria2'),
                       QCoreApplication.translate("mainwindow_src_ui_tr", 'Restart Persepolis'),
                       10000, 'critical', parent=self)

            logger.sendToLog('Persepolis can not connect to Aria2', 'ERROR')
        else:
            self.statusbar.showMessage(QCoreApplication.translate("mainwindow_src_ui_tr", 'Reconnecting Aria2...'))
            logger.sendToLog('Reconnecting Aria2 ...', 'INFO')

            # get items with 'downloading' or 'waiting' status from data base and restart them.
            downloading_gid_list = self.persepolis_db.returnDownloadingItems()

            for gid in downloading_gid_list:
                new_download = DownloadLink(gid, self)
                self.threadPool.append(new_download)
                self.threadPool[-1].start()
                self.threadPool[-1].ARIA2NOTRESPOND.connect(self.aria2NotRespond)

            # get download items with 'paused' status and stop them.
            paused_gid_list = self.persepolis_db.returnPausedItems()

            for gid in paused_gid_list:
                download.downloadStop(gid, self)

            self.statusbar.showMessage(
                QCoreApplication.translate("mainwindow_src_ui_tr", 'Persepolis reconnected aria2 successfully'))
            logger.sendToLog('Persepolis reconnected aria2 successfully', 'INFO')

# when this function is called , aria2_disconnected value is changing to
# 1! and it means that aria2 rpc connection disconnected.so CheckingThread
# is trying to fix it .
    def aria2Disconnected(self):
        global aria2_disconnected
        aria2_disconnected = 1


# read KeepAwakeThread for more information
    def keepAwake(self, add):

        # finding cursor position
        cursor_position = QCursor.pos()
        cursor_array = [int(cursor_position.x()), int(cursor_position.y())]

        # check user selected option.
        # don't do anything if we haven't any active downloads
        if self.persepolis_setting.value('settings/awake') == 'yes' and self.ongoing_downloads != 0:

            if add and self.keep_awake_checkBox.isChecked():  # Moving mouse position one time +1 pixel and one time -1 pixel!  # noqa: E501
                QCursor.setPos(cursor_array[0] + 1, cursor_array[1] + 1)
            else:
                QCursor.setPos(cursor_array[0] - 1, cursor_array[1] - 1)

# if keep_awake_checkBox toggled by user , this method is called.
    def keepAwakeCheckBoxToggled(self, checkbox):
        if self.keep_awake_checkBox.isChecked():
            self.persepolis_setting.setValue('settings/awake', 'yes')
            self.keep_awake_checkBox.setChecked(True)
        else:
            self.persepolis_setting.setValue('settings/awake', 'no')
            self.keep_awake_checkBox.setChecked(False)

        self.persepolis_setting.sync()


# this method updates download_table in MainWindow
#
# download_table_header = ['File Name', 'Status', 'Size', 'Downloaded', 'Percentage', 'Connections',
#                       'Transfer rate', 'Estimated time left', 'Gid', 'Link', 'First try date', 'Last try date', 'Category']  # noqa: E501

    def checkDownloadInfo(self, list):

        # number of ongoing downloads.
        # this variable helps keepAwake method.
        self.ongoing_downloads = len(list)

        systemtray_tooltip_text = 'Persepolis Download Manager'

        for dict in list:
            gid = dict['gid']

            status = dict['status']

            if status == 'complete' or status == 'error' or status == 'stopped':

                # eliminate gid from active_downloads in data base
                temp_dict = {'gid': gid,
                             'status': 'deactive'}

                self.temp_db.updateSingleTable(temp_dict)

            # add download percent to the tooltip text for persepolis system tray icon
            try:
                if status == 'downloading':
                    system_tray_file_name = dict['file_name']
                    if len(system_tray_file_name) > 20:
                        system_tray_file_name = system_tray_file_name[0:19] + '...'
                    systemtray_tooltip_text = systemtray_tooltip_text + '\n'\
                        + system_tray_file_name + ': '\
                        + dict['percent']
            except (KeyError, TypeError):
                pass

            # Is the link related to VideoFinder?
            if gid in self.all_video_finder_gid_list:

                video_finder_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)

                video_finder_thread = self.video_finder_threads_dict[video_finder_dictionary['video_gid']]
                video_finder_link = True

                # is gid related to video? or audio
                if gid == video_finder_dictionary['video_gid']:
                    video_finder_video_gid = True
                else:
                    video_finder_video_gid = False

                # if download is completed update video finder data base
                if status == 'complete':
                    if video_finder_video_gid:
                        video_finder_dictionary['video_completed'] = 'yes'
                        video_finder_thread.video_completed = 'yes'
                    else:
                        video_finder_dictionary['audio_completed'] = 'yes'
                        video_finder_thread.audio_completed = 'yes'

                    # update data base
                    self.persepolis_db.updateVideoFinderTable([video_finder_dictionary])

                # if download stopped, VideoFinder must be notified. so update data base.
                if video_finder_dictionary['checking'] == 'yes' and (status == 'error' or status == 'stopped'):

                    video_finder_dictionary['checking'] = 'no'
                    video_finder_thread.checking = 'no'

                    # update data base
                    self.persepolis_db.updateVideoFinderTable([video_finder_dictionary])

            else:
                video_finder_link = False

            if status == 'error':
                # check free space in temp_download_folder!
                # perhaps insufficient space in hard disk caused this error!
                # find free space in KiB
                free_space = freeSpace(temp_download_folder)

                # find file size
                file_size = dict['size']

                if file_size is not None:
                    if file_size[-2:] != ' B':
                        unit = file_size[-3:]
                        try:
                            if unit == 'TiB' or unit == 'GiB':
                                size_value = float(file_size[:-4])
                            else:
                                size_value = int(file_size[:-4])
                        except ValueError:
                            size_value = None
                    else:
                        unit = None

                        try:
                            size_value = int(file_size)
                        except ValueError:
                            size_value = None

                    if free_space is not None and size_value is not None:
                        if unit == 'TiB':
                            free_space = free_space/(1073741824*1024)
                            free_space = round(free_space, 2)
                        elif unit == 'GiB':
                            free_space = free_space/1073741824
                            free_space = round(free_space, 2)
                        elif unit == 'MiB':
                            free_space = int(free_space/1048576)
                        elif unit == 'KiB':
                            free_space = int(free_space/1024)
                        else:
                            free_space = int(free_space)

                        if free_space < size_value:
                            error = 'Insufficient disk space!'

                            # write error_message in log file
                            error_message = 'Download failed - GID : '\
                                + str(gid)\
                                + '- Message : '\
                                + error

                            logger.sendToLog(error_message, 'ERROR')

                            # show notification
                            notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Error: ") + error,
                                       QCoreApplication.translate("mainwindow_src_ui_tr",
                                                                  'Please change the temporary download folder'),
                                       10000, 'fail', parent=self)

            # find row of this gid in download_table!
            row = None
            for i in range(self.download_table.rowCount()):
                row_gid = self.download_table.item(i, 8).text()
                if gid == row_gid:
                    row = i
                    break

            # update download_table items
            if row is not None:
                update_list = [dict['file_name'], dict['status'], dict['size'],
                               dict['downloaded_size'], dict['percent'],
                               dict['connections'], dict['rate'], dict['estimate_time_left'],
                               dict['gid'], None, None, None, None]
                for i in range(12):

                    # update download_table cell if update_list item in not None
                    if update_list[i]:
                        text = update_list[i]
                    else:
                        text = self.download_table.item(row, i).text()

                    # create a QTableWidgetItem
                    item = QTableWidgetItem(text)

                    # set item
                    try:
                        self.download_table.setItem(row, i, item)
                    except Exception as problem:
                        logger.sendToLog(
                            "Error occurred while updating download table", "INFO")
                        logger.sendToLog(problem, "ERROR")

                # update download_table (refreshing!)
                self.download_table.viewport().update()

            # update progresswindow labels
            # check that any progress_window is available for this gid or not!
            if gid in self.progress_window_list_dict.keys():

                # find progress_window for this gid
                member_number = self.progress_window_list_dict[gid]
                progress_window = self.progress_window_list[member_number]

                # if link is related to video finder
                if video_finder_link:

                    # download percent
                    value = dict['percent']
                    if not(value):
                        value = '0%'

                    if video_finder_dictionary['video_completed'] == 'yes':
                        video_status = 'Completed'

                    elif video_finder_video_gid:
                        video_status = value + ' downloaded'

                    else:
                        video_status = 'Not completed'

                    video_status = QCoreApplication.translate(
                        "video_finder_progress_ui_tr", "<b>Video file status: </b>")\
                        + video_status

                    progress_window.video_status_label.setText(video_status)

                    if video_finder_dictionary['audio_completed'] == 'yes':
                        audio_status = 'Completed'

                    elif not(video_finder_video_gid):
                        audio_status = value + ' downloaded'

                    else:
                        audio_status = 'Not completed'

                    audio_status = QCoreApplication.translate(
                        "video_finder_progress_ui_tr", "<b>Audio file status: </b>")\
                        + audio_status

                    progress_window.audio_status_label.setText(audio_status)

                    if video_finder_dictionary['video_completed'] == 'yes' and \
                        video_finder_dictionary['audio_completed'] == 'yes':
                        muxing_status = 'Started!'
                    else:
                        muxing_status = 'Not started!'

                    muxing_status = QCoreApplication.translate("video_finder_progress_ui_tr", "<b>Muxing status: </b>")\
                        + muxing_status

                    progress_window.muxing_status_label.setText(muxing_status)

                    # tell to progress_window what gid is in progress
                    progress_window.gid = gid

                # link
                link = QCoreApplication.translate("mainwindow_src_ui_tr", "<b>Link</b>: ") + str(dict['link'])
                progress_window.link_label.setText(link)
                progress_window.link_label.setToolTip(link)

                # downloaded
                downloaded_size = dict['downloaded_size']

                if downloaded_size is None:
                    downloaded_size = 'None'

                file_size = dict['size']
                if file_size is None:
                    file_size = 'None'

                downloaded = QCoreApplication.translate("mainwindow_src_ui_tr", "<b>Downloaded</b>: ") \
                    + str(downloaded_size) \
                    + "/" \
                    + str(file_size)

                progress_window.downloaded_label.setText(downloaded)

                # Transfer rate
                rate = QCoreApplication.translate("mainwindow_src_ui_tr", "<b>Transfer rate</b>: ") \
                    + str(dict['rate'])

                progress_window.rate_label.setText(rate)

                # Estimate time left
                estimate_time_left = QCoreApplication.translate("mainwindow_src_ui_tr",
                                                                "<b>Estimated time left</b>: ") \
                                                                + str(dict['estimate_time_left'])

                progress_window.time_label.setText(estimate_time_left)

                # Connections
                connections = QCoreApplication.translate("mainwindow_src_ui_tr", "<b>Connections</b>: ") \
                    + str(dict['connections'])

                progress_window.connections_label.setText(connections)

                # progressbar
                value = dict['percent']
                file_name = str(dict['file_name'])

                if file_name != "***":
                    windows_title = '(' + str(value) + ')' + str(file_name)
                    progress_window.setWindowTitle(windows_title)
                try:
                    value = int(value[:-1])
                except (ValueError, TypeError):
                    value = 0
                progress_window.download_progressBar.setValue(value)

                # status
                progress_window.status = str(dict['status'])
                status = QCoreApplication.translate("mainwindow_src_ui_tr", "<b>Status</b>: ") + progress_window.status
                progress_window.status_label.setText(status)

                # activate/deactivate progress_window buttons according to status
                if progress_window.status == "downloading":
                    progress_window.resume_pushButton.setEnabled(False)
                    progress_window.stop_pushButton.setEnabled(True)
                    progress_window.pause_pushButton.setEnabled(True)

                elif progress_window.status == "paused":
                    progress_window.resume_pushButton.setEnabled(True)
                    progress_window.stop_pushButton.setEnabled(True)
                    progress_window.pause_pushButton.setEnabled(False)

                elif progress_window.status == "waiting":
                    progress_window.resume_pushButton.setEnabled(False)
                    progress_window.stop_pushButton.setEnabled(True)
                    progress_window.pause_pushButton.setEnabled(False)

                elif progress_window.status == "scheduled":
                    progress_window.resume_pushButton.setEnabled(False)
                    progress_window.stop_pushButton.setEnabled(True)
                    progress_window.pause_pushButton.setEnabled(False)

                # it means download has finished!
                # lets do finishing jobs!
                elif progress_window.status == "stopped" or \
                     progress_window.status == "error" or \
                     progress_window.status == "complete":

                    # close progress_window if download status is stopped or
                    # completed or error
                    # if window is related to video finder and download is completed, the don't close window
                    if (video_finder_link and progress_window.status == 'complete'):

                        # disable stop and pause and push buttons
                        progress_window.resume_pushButton.setEnabled(False)
                        progress_window.stop_pushButton.setEnabled(False)
                        progress_window.pause_pushButton.setEnabled(False)

                    else:
                        progress_window.close()

                        # eliminate window information from progress_window_list_dict
                        del self.progress_window_list_dict[gid]

                    # if download stopped:
                    if progress_window.status == "stopped":
                        # write message in log
                        stop_message = 'Download stopped - GID : '\
                            + str(gid)

                        logger.sendToLog(stop_message, 'INFO')

                        # show notification
                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Download Stopped"),
                                   str(dict['file_name']), 10000, 'no', parent=self)

                    # if download status is error!
                    elif progress_window.status == "error":

                        # get error message from dict
                        if 'error' in dict.keys():
                            error = dict['error']
                        else:
                            error = 'Error'

                        # write error_message in log file
                        error_message = 'Download failed - GID : '\
                            + str(gid)\
                            + '- Message : '\
                            + error

                        logger.sendToLog(error_message, 'ERROR')

                        # show notification
                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Error - ") + error,
                                   str(dict['file_name']),
                                   10000, 'fail', parent=self)

                    # set "None" for start_time and end_time and after_download value
                    # in data_base, because download has finished
                    self.persepolis_db.setDefaultGidInAddlinkTable(
                        gid=gid, start_time=True, end_time=True, after_download=True)

                    # THIS PART IS NOT RELATED TO VIDEO FINDER LINKS
                    # if user selects shutdown option for after download progress
                    # value of 'shutdown' in data base will changed to 'wait' for this category
                    # (see ShutDownThread and shutdown.py for more information)
                    # shutDown method will check that value in a loop.
                    # when "wait" changes to "shutdown" then shutdown.py script
                    # will shut down the system
                    shutdown_dict = self.temp_db.returnGid(gid)

                    # get shutdown value for this gid from data base
                    shutdown_status = shutdown_dict['shutdown']

                    # if status is complete or error, and user selected "shutdown after download" option:
                    if shutdown_status == 'wait':

                        # shutdown aria!
                        answer = download.shutDown()

                        # KILL aria2c in Unix like systems, if didn't respond. R.I.P :))
                        if not(answer) and (os_type != OS.WINDOWS):

                            subprocess.Popen(['killall', 'aria2c'],
                                             stderr=subprocess.PIPE,
                                             stdout=subprocess.PIPE,
                                             stdin=subprocess.PIPE,
                                             shell=False)

                        # send notification
                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", 'Persepolis is shutting down'),
                                   QCoreApplication.translate("mainwindow_src_ui_tr", 'your system in 20 seconds'),
                                   15000, 'warning', parent=self)

                        # write "shutdown" message in data base for this gid >> Shutdown system!
                        shutdown_dict = {'gid': gid, 'shutdown': 'shutdown'}

                        self.temp_db.updateSingleTable(shutdown_dict)

                    # sync persepolis_setting before checking!
                    self.persepolis_setting.sync()
                    if progress_window.status == "complete" and video_finder_link is False:
                        # write message in log file
                        complete_message = 'Download complete - GID : '\
                            + str(gid)

                        logger.sendToLog(complete_message, 'INFO')

                        # play notification
                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Download Complete"),
                                   dict['file_name'], 10000, 'ok', parent=self)

                        # check user's Preferences
                        if self.persepolis_setting.value('settings/after-dialog') == 'yes':

                            # show download complete dialog
                            afterdownloadwindow = AfterDownloadWindow(
                                self, dict, self.persepolis_setting)

                            self.afterdownload_list.append(afterdownloadwindow)

                            self.afterdownload_list[-1].show()

                            # bringing AfterDownloadWindow on top
                            self.afterdownload_list[-1].raise_()
                            self.afterdownload_list[-1].activateWindow()

        # set tooltip for system_tray_icon
        self.system_tray_icon.setToolTip(systemtray_tooltip_text)


# drag and drop for links
    def dragEnterEvent(self, droplink):

        text = str(droplink.mimeData().text())

        if ("tp:/" in text[2:6]) or ("tps:/" in text[2:7]):
            droplink.accept()
        else:
            droplink.ignore()

    def dropEvent(self, droplink):
        link_clipboard = QApplication.clipboard()
        link_clipboard.clear(mode=link_clipboard.Clipboard)
        link_string = droplink.mimeData().text()
        link_clipboard.setText(str(link_string), mode=link_clipboard.Clipboard)
        self.addLinkButtonPressed(button=link_clipboard)

    # aria2 identifies each download by the ID called GID.
    # The GID must be hex string of 16 characters,
    # thus [0-9a-zA-Z] are allowed and leading zeros must
    # not be stripped. The GID all 0 is reserved and must
    # not be used. The GID must be unique, otherwise error
    # is reported and the download is not added.
    # gidGenerator generates GID for downloads
    def gidGenerator(self):
        # this loop repeats until we have a unique GID
        while True:

            # generate a random hex value between 1152921504606846976 and 18446744073709551615
            # for download GID
            my_gid = hex(random.randint(1152921504606846976, 18446744073709551615))
            my_gid = my_gid[2:18]
            my_gid = str(my_gid)

            # check my_gid used before or not!
            category_dict = self.persepolis_db.searchCategoryInCategoryTable('All Downloads')
            gid_list = eval(category_dict['gid_list'])

            if my_gid not in gid_list:
                break

        return my_gid

    # this method returns index of all selected rows in list format
    def userSelectedRows(self):
        try:
            # Find selected rows
            rows_list = []
            rows_index = self.download_table.selectionModel().selectedRows()
            for index in rows_index:
                rows_list.append(index.row())

            # sort list by number
            rows_list.sort()
        except Exception:
            rows_list = []

        return rows_list

    # this method returns number of selected row
    # if user selected one row!
    def selectedRow(self):
        rows_list = self.userSelectedRows()
        if len(rows_list) == 0:
            return None
        else:
            return rows_list[0]

    # this method activates/deactivates QActions according to selected row!
    def checkSelectedRow(self):

        rows_list = self.userSelectedRows()

        # check if user selected multiple items
        if len(rows_list) <= 1:
            multi_items_selected = False
        else:
            multi_items_selected = True

        # if any thing changed ...
        if (multi_items_selected and not(self.multi_items_selected)) or \
            (not(multi_items_selected) and self.multi_items_selected):
            if multi_items_selected:
                self.multi_items_selected = True
            else:
                self.multi_items_selected = False

            self.selectDownloads()

        if len(rows_list) != 0:

            selected_row_return = rows_list[0]

            status = self.download_table.item(selected_row_return, 1).text()
            category = self.download_table.item(selected_row_return, 12).text()
            link = self.download_table.item(selected_row_return, 9).text()

            self.statusbar.showMessage(str(link))

            self.removeSelectedAction.setEnabled(True)
            self.deleteSelectedAction.setEnabled(True)

            if category == 'Single Downloads':
                if status == "scheduled":
                    self.resumeAction.setEnabled(False)
                    self.pauseAction.setEnabled(False)
                    self.stopAction.setEnabled(True)
                    self.propertiesAction.setEnabled(False)
                    self.progressAction.setEnabled(True)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

                elif status == "stopped" or status == "error":
                    self.stopAction.setEnabled(False)
                    self.pauseAction.setEnabled(False)
                    self.resumeAction.setEnabled(True)
                    self.propertiesAction.setEnabled(True)
                    self.progressAction.setEnabled(False)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

                elif status == "downloading":
                    self.resumeAction.setEnabled(False)
                    self.pauseAction.setEnabled(True)
                    self.stopAction.setEnabled(True)
                    self.propertiesAction.setEnabled(False)
                    self.progressAction.setEnabled(True)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

                elif status == "waiting":
                    self.stopAction.setEnabled(True)
                    self.resumeAction.setEnabled(False)
                    self.pauseAction.setEnabled(False)
                    self.propertiesAction.setEnabled(False)
                    self.progressAction.setEnabled(True)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

                elif status == "complete":
                    self.stopAction.setEnabled(False)
                    self.resumeAction.setEnabled(False)
                    self.pauseAction.setEnabled(False)
                    self.propertiesAction.setEnabled(True)
                    self.progressAction.setEnabled(False)
                    self.openDownloadFolderAction.setEnabled(True)
                    self.openFileAction.setEnabled(True)
                    self.moveSelectedDownloadsAction.setEnabled(True)

                elif status == "paused":
                    self.stopAction.setEnabled(True)
                    self.resumeAction.setEnabled(True)
                    self.pauseAction.setEnabled(False)
                    self.propertiesAction.setEnabled(False)
                    self.progressAction.setEnabled(True)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

                else:
                    self.progressAction.setEnabled(False)
                    self.resumeAction.setEnabled(False)
                    self.stopAction.setEnabled(False)
                    self.pauseAction.setEnabled(False)
                    self.propertiesAction.setEnabled(False)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

            else:
                self.resumeAction.setEnabled(True)
                self.pauseAction.setEnabled(True)
                self.stopAction.setEnabled(True)

                if status == 'complete':
                    self.propertiesAction.setEnabled(True)
                    self.progressAction.setEnabled(False)
                    self.openDownloadFolderAction.setEnabled(True)
                    self.openFileAction.setEnabled(True)
                    self.moveSelectedDownloadsAction.setEnabled(True)

                elif status == "stopped" or status == "error":
                    self.propertiesAction.setEnabled(True)
                    self.progressAction.setEnabled(False)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

                elif status == "scheduled" or status == "downloading" or status == "paused" or status == "waiting":
                    self.propertiesAction.setEnabled(False)
                    self.progressAction.setEnabled(False)
                    self.openDownloadFolderAction.setEnabled(False)
                    self.openFileAction.setEnabled(False)
                    self.moveSelectedDownloadsAction.setEnabled(False)

            # video_finder_widget
            # hide video_finder_widget if selected item is not related to video finder
            if not(self.multi_items_selected):

                gid = self.download_table.item(selected_row_return, 8).text()

                if gid in self.all_video_finder_gid_list:

                    # show widget
                    self.video_finder_widget.show()

                    # gid is related to audio or video?!
                    if gid in self.all_video_finder_video_gid_list:
                        video_gid = gid

                        # set video_label
                        # get video download's percentage
                        self.video_label.setText(
                            QCoreApplication.translate("mainwindow_ui_tr", "<b>Video file status: </b>")
                            + self.download_table.item(selected_row_return, 4).text()
                            + QCoreApplication.translate("mainwindow_ui_tr", " downloaded"))

                        # find audio information
                        # find row of audio_gid in download_table!
                        audio_gid = self.all_video_finder_audio_gid_list[self.all_video_finder_video_gid_list.index(
                            gid)]

                        row = None
                        for i in range(self.download_table.rowCount()):
                            row_gid = self.download_table.item(i, 8).text()
                            if audio_gid == row_gid:
                                row = i
                                break

                        # set audio_label
                        # get audio download's percentage
                        self.audio_label.setText(
                            QCoreApplication.translate("mainwindow_ui_tr", "<b>Audio file status: </b>")
                            + self.download_table.item(row, 4).text()
                            + QCoreApplication.translate("mainwindow_ui_tr", " downloaded"))

                    else:

                        # set audio_label
                        # get audio download's percentage
                        self.audio_label.setText(
                            QCoreApplication.translate("mainwindow_ui_tr", "<b>Audio file status: </b>")
                            + self.download_table.item(selected_row_return, 4).text()
                            + QCoreApplication.translate("mainwindow_ui_tr", " downloaded"))

                        # find video information
                        video_gid = self.all_video_finder_video_gid_list[self.all_video_finder_audio_gid_list.index(
                            gid)]

                        # find video row
                        row = None
                        for i in range(self.download_table.rowCount()):
                            row_gid = self.download_table.item(i, 8).text()
                            if video_gid == row_gid:
                                row = i
                                break

                        # set video_label
                        # get video download's percentage
                        self.video_label.setText(
                            QCoreApplication.translate("mainwindow_ui_tr", "<b>Video file status: </b>")
                            + self.download_table.item(row, 4).text()
                            + QCoreApplication.translate("mainwindow_ui_tr", " downloaded"))

                    # set activity status and muxing status
                    # show/hide muxing_pushButton
                    if video_gid in self.video_finder_threads_dict.keys():

                        # find thread
                        video_finder_thread = self.video_finder_threads_dict[video_gid]

                        # check activity
                        if video_finder_thread.active == 'yes':

                            video_finder_status = QCoreApplication.translate('mainwindow_ui_tr', 'Active')

                            # hide muxing_pushButton
                            self.muxing_pushButton.hide()

                        else:
                            video_finder_status = QCoreApplication.translate('mainwindow_ui_tr', 'Not Active')

                            if video_finder_thread.video_completed == 'yes' and \
                                video_finder_thread.audio_completed == 'yes':

                                # show muxing_pushButton
                                self.muxing_pushButton.show()

                        # check muxing status
                        muxing = video_finder_thread.muxing

                        if muxing == 'no':
                            muxing_status = QCoreApplication.translate('mainwindow_ui_tr', 'Not Active')

                        elif muxing == 'started':
                            muxing_status = QCoreApplication.translate('mainwindow_ui_tr', 'Started')

                        elif muxing == 'error':
                            muxing_status = QCoreApplication.translate('mainwindow_ui_tr', 'Error')

                        elif muxing == 'complete':
                            muxing_status = QCoreApplication.translate('mainwindow_ui_tr', 'Complete')

                    else:
                        video_finder_status = QCoreApplication.translate('mainwindow_ui_tr', 'Not Active')
                        muxing_status = QCoreApplication.translate('mainwindow_ui_tr', 'Not Active')

                        if self.download_table.item(selected_row_return, 1).text() == 'complete' and \
                            self.download_table.item(row, 1).text() == 'complete':

                            # show muxing_pushButton
                            self.muxing_pushButton.show()
                        else:
                            # hide muxing_pushButton
                            self.muxing_pushButton.hide()

                    # set labels
                    self.video_finder_status_label.setText(
                        QCoreApplication.translate("mainwindow_ui_tr", "<b>Status: </b>")
                        + video_finder_status)

                    self.muxing_status_label.setText(
                        QCoreApplication.translate("mainwindow_ui_tr", "<b>Muxing status: </b>")
                        + muxing_status)

                else:
                    # hide video_finder_widget
                    self.video_finder_widget.hide()

            else:
                # hide video_finder_widget
                self.video_finder_widget.hide()

        else:
            self.progressAction.setEnabled(False)
            self.resumeAction.setEnabled(False)
            self.stopAction.setEnabled(False)
            self.pauseAction.setEnabled(False)
            self.propertiesAction.setEnabled(False)
            self.openDownloadFolderAction.setEnabled(False)
            self.openFileAction.setEnabled(False)
            self.moveSelectedDownloadsAction.setEnabled(False)

            # hide video_finder_widget
            self.video_finder_widget.hide()

    # when user requests calls persepolis with browser plugin,
    # this method is called by CheckingThread.
    def checkPluginCall(self):
        global plugin_links_checked

        # get new links from plugins_db
        list_of_links = self.plugins_db.returnNewLinks()

        # notify that job is done!and new links can be received form plugins_db
        plugin_links_checked = True

        not_video_finder_links = []  # Store non-video_finder links to process normally.

        # get maximum of youtube,... link from persepolis_setting
        max_links = int(self.persepolis_setting.value('settings/video_finder/max_links', 3))

        # add your favorite site in this list
        # please don't add porn sites!
        supported_sites_list = [
            'youtube.com/watch',
            'aparat.com/v/',
            'vimeo.com/',
            'dailymotion.com/video',
            'https://soundcloud.com/'
        ]

        for link in list_of_links:

            # if link is on of supported_sites_list member then change
            # video_finder_supported to True value.
            video_finder_supported = False

            for supported_site in supported_sites_list:
                if supported_site in link['link']:
                    video_finder_supported = True
                    break

            # if link is on of supported_sites_list member, the open video_finder_addlink_window
            if max_links and video_finder_supported:
                max_links = max_links - 1
                self.showVideoFinderAddLinkWindow(input_dict=link)
            else:
                # if link is not on of supported_sites_list then add it to not_video_finder_links
                not_video_finder_links.append(link)

        # video_finder links also will stay here, those coming after specified max.
        list_of_links = not_video_finder_links

        # It means we have only one link in list_of_links
        if len(list_of_links) == 1:

            # this line calls pluginAddLink method and send a dictionary that contains
            # link information
            self.pluginAddLink(list_of_links[0])

        elif len(list_of_links):  # we have queue request from browser plugin # Length non-zero
            self.pluginQueue(list_of_links)

    # this method creates an addlinkwindow when user calls Persepolis with
    # browsers plugin (Single Download)

    def pluginAddLink(self, add_link_dictionary):
        # create an object for AddLinkWindow and add it to addlinkwindows_list.
        addlinkwindow = AddLinkWindow(
            self, self.callBack, self.persepolis_setting, add_link_dictionary)
        self.addlinkwindows_list.append(addlinkwindow)
        self.addlinkwindows_list[-1].show()

        # bring addlinkwindow on top
        self.addlinkwindows_list[-1].raise_()
        self.addlinkwindows_list[-1].activateWindow()

    # This method creates addlinkwindow when user presses plus button in MainWindow

    def addLinkButtonPressed(self, button=None):
        addlinkwindow = AddLinkWindow(self, self.callBack, self.persepolis_setting)
        self.addlinkwindows_list.append(addlinkwindow)
        self.addlinkwindows_list[-1].show()

    # callback of AddLinkWindow
    def callBack(self, add_link_dictionary, download_later, category):
        exists = self.persepolis_db.searchLinkInAddLinkTable(add_link_dictionary['link'])

        if exists:
            self.msgBox = QMessageBox()
            self.msgBox.setText(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                           "<b><center>This link has been added before!\
                                                            Are you sure you want to add it again?</center></b>"))
            self.msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            self.msgBox.setIcon(QMessageBox.Warning)
            reply = self.msgBox.exec_()

            # do nothing if user clicks NO
            if reply != QMessageBox.Yes:
                return

        category = str(category)
        # aria2 identifies each download by the ID called GID. The GID must be
        # hex string of 16 characters.
        # if user presses ok button on add link window , a gid generates for download.
        gid = self.gidGenerator()

        # add gid to add_link_dictionary
        add_link_dictionary['gid'] = gid

        # download_info_file_list is a list that contains ['file_name' ,
        # 'status' , 'size' , 'downloaded size' ,'download percentage' ,
        # 'number of connections' ,'Transfer rate' , 'estimate_time_left' ,
        # 'gid' , 'link' , 'first_try_date' , 'last_try_date', 'category']

        # if user or browser_plugin defined filename then file_name is valid in
        # add_link_dictionary['out']
        if add_link_dictionary['out']:
            file_name = add_link_dictionary['out']
        else:
            file_name = '***'

        # If user selected a queue in add_link window , then download must be
        # added to queue and and download must be started with queue so >>
        # download_later = True
        if str(category) != 'Single Downloads':
            download_later = True

        if not(download_later):
            status = 'waiting'
        else:
            status = 'stopped'

        # get now time and date
        date = download.nowDate()

        dict = {'file_name': file_name,
                'status': status,
                'size': '***',
                'downloaded_size': '***',
                'percent': '***',
                'connections': '***',
                'rate': '***',
                'estimate_time_left': '***',
                'gid': gid,
                'link': add_link_dictionary['link'],
                'first_try_date': date,
                'last_try_date': date,
                'category': category}

        # write information in data_base
        self.persepolis_db.insertInDownloadTable([dict])
        self.persepolis_db.insertInAddLinkTable([add_link_dictionary])

        # find selected category in left side panel
        for i in range(self.category_tree_model.rowCount()):
            category_tree_item_text = str(
                self.category_tree_model.index(i, 0).data())
            if category_tree_item_text == category:
                category_index = i
                break

        # highlight selected category in category_tree
        category_tree_model_index = self.category_tree_model.index(
            category_index, 0)

        current_category_tree_text = current_category_tree_index.data()

        self.category_tree.setCurrentIndex(category_tree_model_index)
        if current_category_tree_text != category:
            self.categoryTreeSelected(category_tree_model_index)
        else:
            # create a row in download_table for new download
            list = [file_name, status, '***', '***', '***',
                    '***', '***', '***', gid, add_link_dictionary['link'], date, date, category]
            self.download_table.insertRow(0)
            j = 0
            # add item in list to the row
            for i in list:
                item = QTableWidgetItem(i)
                self.download_table.setItem(0, j, item)
                j = j + 1

        # if user didn't press download_later_pushButton in add_link window
        # then create new qthread for new download!
        if not(download_later):
            new_download = DownloadLink(gid, self)
            self.threadPool.append(new_download)
            self.threadPool[-1].start()
            self.threadPool[-1].ARIA2NOTRESPOND.connect(self.aria2NotRespond)

            # open progress window for download.
            self.progressBarOpen(gid)

            # notify user
            # check that download scheduled or not
            if not(add_link_dictionary['start_time']):
                message = QCoreApplication.translate("mainwindow_src_ui_tr", "Download Starts")
            else:
                new_spider = SpiderThread(add_link_dictionary, self)
                self.threadPool.append(new_spider)
                self.threadPool[-1].start()
                self.threadPool[-1].SPIDERSIGNAL.connect(self.spiderUpdate)
                message = QCoreApplication.translate("mainwindow_src_ui_tr", "Download Scheduled")
            notifySend(message, '', 10000, 'no', parent=self)

        else:
            new_spider = SpiderThread(add_link_dictionary, self)
            self.threadPool.append(new_spider)
            self.threadPool[-1].start()
            self.threadPool[-1].SPIDERSIGNAL.connect(self.spiderUpdate)

    # when user presses resume button this method is called
    def resumeButtonPressed(self, button=None):

        self.resumeAction.setEnabled(False)

        # find user's selected row
        selected_row_return = self.selectedRow()

        if selected_row_return is not None:

            # find download category
            category = self.download_table.item(selected_row_return, 12).text()

            # if category is not "single downloads" , then send notification for error
            if category != "Single Downloads":
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful."),
                           QCoreApplication.translate("mainwindow_src_ui_tr",
                                                      "Please resume the following category: ") + category,
                           10000, 'fail', parent=self)
                return

            # find download gid
            gid = self.download_table.item(selected_row_return, 8).text()
            download_status = self.download_table.item(
                selected_row_return, 1).text()

            # this 'if' checks status of download before resuming! If download status
            # is 'paused' then download must be resumed and if status isn't 'paused' new
            # download thread must be created !
            if download_status == "paused":

                answer = download.downloadUnpause(gid)

                # if aria2 did not respond , then this function checks for aria2
                # availability , and if aria2 disconnected then aria2Disconnected is
                # called.
                if not(answer):
                    version_answer = download.aria2Version()
                    if version_answer == 'did not respond':
                        self.aria2Disconnected()
                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Aria2 disconnected!"),
                                   QCoreApplication.translate("mainwindow_src_ui_tr",
                                                              "Persepolis is trying to connect!be patient!"),
                                   10000, 'warning', parent=self)
                    else:
                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Aria2 did not respond!"),
                                   QCoreApplication.translate("mainwindow_src_ui_tr", "Try again!"),
                                   10000, 'warning', parent=self)

            else:

                # check if the gid is related to video finder
                if gid in self.all_video_finder_gid_list:

                    result_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)
                    if result_dictionary['checking'] == 'no':

                        # create new thread for this download
                        # see VideoFinder thread for more information
                        new_download = VideoFinder(result_dictionary, self)
                        self.threadPool.append(new_download)
                        self.threadPool[-1].start()
                        self.threadPool[-1].VIDEOFINDERCOMPLETED.connect(self.videoFinderCompleted)

                        # add thread to video_finder_threads_dict
                        self.video_finder_threads_dict[result_dictionary['video_gid']] = new_download

                    else:
                        # we already have an active tread for this download...
                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                              "Download is in progress by video finder!"),
                                   QCoreApplication.translate("mainwindow_src_ui_tr", "be patient!"),
                                   10000, 'warning', parent=self)

                        # show the download progress window
                        if gid in self.progress_window_list_dict.keys():

                            # find progress_window for this gid and show it to user
                            member_number = self.progress_window_list_dict[gid]
                            progress_window = self.progress_window_list[member_number]
                            progress_window.show()
                            progress_window.raise_()
                            progress_window.activateWindow()

                else:

                    # create new download thread
                    new_download = DownloadLink(gid, self)
                    self.threadPool.append(new_download)
                    self.threadPool[-1].start()
                    self.threadPool[-1].ARIA2NOTRESPOND.connect(self.aria2NotRespond)

                # create new progress_window
                self.progressBarOpen(gid)

    # this method called if aria2 crashed or disconnected!

    def aria2NotRespond(self):
        self.aria2Disconnected()
        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", 'Aria2 did not respond'),
                   QCoreApplication.translate("mainwindow_src_ui_tr", 'Try again'),
                   5000, 'critical', parent=self)

    # this method called if user presses stop button in MainWindow
    def stopButtonPressed(self, button=None):

        self.stopAction.setEnabled(False)
        selected_row_return = self.selectedRow()  # finding user's selected row

        if selected_row_return is not None:
            # find download category
            category = self.download_table.item(selected_row_return, 12).text()

            # if category is not "single downloads" , then send notification for error
            if category != "Single Downloads":
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful."),
                           QCoreApplication.translate("mainwindow_src_ui_tr",
                                                      "Please stop the following category: ") + category,
                           10000, 'fail', parent=self)

                return

            gid = self.download_table.item(selected_row_return, 8).text()

            # check if this gid is related to video finder
            if gid in self.all_video_finder_gid_list:

                result_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)
                video_finder_plus_gid = 'video_finder_' + str(result_dictionary['video_gid'])
                # cancel shut down progress
                dictionary = {'category': video_finder_plus_gid,
                              'shutdown': 'canceled'}

                self.temp_db.updateQueueTable(dictionary)

            else:

                # change status of shutdown in temp_db
                dictionary = {'gid': gid,
                              'shutdown': 'canceled'}

                self.temp_db.updateSingleTable(dictionary)

            answer = download.downloadStop(gid, self)

            # if aria2 did not respond , then this function is checking for aria2
            # availability , and if aria2 disconnected then aria2Disconnected is
            # executed
            if answer == 'None':
                version_answer = download.aria2Version()
                if version_answer == 'did not respond':
                    self.aria2Disconnected()
                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Aria2 disconnected!"),
                               QCoreApplication.translate("mainwindow_src_ui_tr",
                                                          "Persepolis is trying to connect!be patient!"),
                               10000, 'warning', parent=self)

    # this method called if user presses pause button in MainWindow

    def pauseButtonPressed(self, button=None):
        self.pauseAction.setEnabled(False)

        # find selected row
        selected_row_return = self.selectedRow()

        if selected_row_return is not None:
            # find download category
            category = self.download_table.item(selected_row_return, 12).text()

            # if category is not "single downloads" , then send notification for error
            if category != "Single Downloads":
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful."),
                           QCoreApplication.translate("mainwindow_src_ui_tr",
                                                      "Please stop the following category: ") + category,
                           10000, 'fail', parent=self)

                return

            # find download gid
            gid = self.download_table.item(selected_row_return, 8).text()

            # send pause request to aria2
            answer = download.downloadPause(gid)

            # if aria2 did not respond , then check aria2 availability!
            # and if aria2 disconnected then call aria2Disconnected
            if not(answer):
                version_answer = download.aria2Version()
                if version_answer == 'did not respond':
                    self.aria2Disconnected()
                    download.downloadStop(gid, self)
                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Aria2 disconnected!"),
                               QCoreApplication.translate("mainwindow_src_ui_tr",
                                                          "Persepolis is trying to connect! be patient!"),
                               10000, 'warning', parent=self)
                else:
                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Aria2 did not respond!"),
                               QCoreApplication.translate("mainwindow_src_ui_tr", "Please try again."),
                               10000, 'critical', parent=self)

    # This method called if properties button pressed by user in MainWindow
    def propertiesButtonPressed(self, button=None):
        result_dictionary = None
        self.propertiesAction.setEnabled(False)
        selected_row_return = self.selectedRow()  # finding user's selected row

        if selected_row_return is not None:
            # find gid of download
            gid = self.download_table.item(selected_row_return, 8).text()

            # check if the gid is related to video finder
            if gid in self.all_video_finder_gid_list:

                result_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)
                if result_dictionary['checking'] == 'yes':

                    # this link is in downloading queue by video finder
                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                          "Download is in progress by video finder!"),
                               QCoreApplication.translate("mainwindow_src_ui_tr", "be patient!"),
                               10000, 'warning', parent=self)

                    # show the download progress window
                    if gid in self.progress_window_list_dict.keys():

                        # find progress_window for this gid and show it to user
                        member_number = self.progress_window_list_dict[gid]
                        progress_window = self.progress_window_list[member_number]
                        progress_window.show()
                        progress_window.raise_()
                        progress_window.activateWindow()

                    return

            # creating propertieswindow
            propertieswindow = PropertiesWindow(
                self, self.propertiesCallback, gid, self.persepolis_setting, result_dictionary)
            self.propertieswindows_list.append(propertieswindow)
            self.propertieswindows_list[-1].show()

    # callBack of PropertiesWindow
    def propertiesCallback(self, add_link_dictionary, gid, category, video_finder_dictionary=None):

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(
                partial(self.propertiesCallback2, add_link_dictionary, gid, category, video_finder_dictionary))
        else:
            self.propertiesCallback2(add_link_dictionary, gid, category, video_finder_dictionary)

    def propertiesCallback2(self, add_link_dictionary, gid, category, video_finder_dictionary=None):
        # current_category_tree_text is current category that highlighted by user
        # in the left side panel
        current_category_tree_text = str(
            self.category_tree.currentIndex().data())

        selected_row_return = self.selectedRow()  # find user's selected row

        # find current category before changing
        current_category = self.download_table.item(
            selected_row_return, 12).text()

        if video_finder_dictionary:
            # add audio and video gid to the gid list
            gid_list = [video_finder_dictionary['video_gid'],
                        video_finder_dictionary['audio_gid']]

        else:
            gid_list = [gid]

        # find row of gid in gid_list!
        for gid in gid_list:

            row = None
            for i in range(self.download_table.rowCount()):
                row_gid = self.download_table.item(i, 8).text()
                if gid == row_gid:
                    row = i
                    break

            if row:
                if current_category_tree_text == 'All Downloads':
                    # update category in download_table
                    item = QTableWidgetItem(str(category))
                    self.download_table.setItem(row, 12, item)

                elif (str(current_category) != str(category)):
                    # remove row from download_table
                    self.download_table.removeRow(row)

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # This method is called if user presses "show/hide progress window" button in
    # MainWindow
    def progressButtonPressed(self, button=None):

        # find user's selected row
        selected_row_return = self.selectedRow()
        if selected_row_return is not None:
            gid = self.download_table.item(selected_row_return, 8).text()

            # if gid is in self.progress_window_list_dict , it means that progress
            # window  for this gid (for this download) is created before and it's
            # available! See progressBarOpen method for more information.
            if gid in self.progress_window_list_dict:

                # find member_number of window in progress_window_list_dict
                member_number = self.progress_window_list_dict[gid]

                # if window is visible >> hide it ,
                # and if window is hide >> make it visible!
                if self.progress_window_list[member_number].isVisible():

                    self.progress_window_list[member_number].hide()

                else:
                    self.progress_window_list[member_number].show()

            else:
                # if window is not availabile in progress_window_list_dict
                # so let's create it!
                self.progressBarOpen(gid)

    # This method creates new ProgressWindow
    def progressBarOpen(self, gid):

        dictionary = None
        # check if it's related to video finder or not
        if gid in self.all_video_finder_gid_list:

            dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)
            # it's related to video finder. so make a gid list for video and audio link!
            gid_list = [dictionary['video_gid'], dictionary['audio_gid']]

            # create a video finder progress window.
            progress_window = VideoFinderProgressWindow(self, gid_list, self.persepolis_setting)

        else:
            # create an ordinary progress_window
            progress_window = ProgressWindow(
                parent=self, gid=gid, persepolis_setting=self.persepolis_setting)

        # add progress window to progress_window_list
        self.progress_window_list.append(progress_window)
        member_number = len(self.progress_window_list) - 1

        # in progress_window_list_dict , key is gid and value is member's
        # rank(number) in progress_window_list
        if dictionary:
            self.progress_window_list_dict[dictionary['video_gid']] = member_number
            self.progress_window_list_dict[dictionary['audio_gid']] = member_number
        else:
            self.progress_window_list_dict[gid] = member_number

        # check user preferences
        # user can hide progress window in settings window.
        if str(self.persepolis_setting.value('settings/show-progress')) == 'yes':
            # show progress window
            self.progress_window_list[member_number].show()
        else:
            # hide progress window
            self.progress_window_list[member_number].hide()

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    # close event
    # when user closes application then this method is called
    def closeEvent(self, event=None):

        if str(self.persepolis_setting.value('settings/hide-window')) == 'yes':

            # set close event just for minimizing to tray
            self.minimizeAction.setText(QCoreApplication.translate("mainwindow_src_ui_tr", 'Show main Window'))
            self.minimizeAction.setIcon(QIcon(icons + 'window'))

        else:

            # close window and exit application
            self.closeAction(event)

    # close application actions is in this method (to close program completely this method must call)
    def closeAction(self, event=None):
        # save window size  and position
        self.persepolis_setting.setValue('MainWindow/size', self.size())
        self.persepolis_setting.setValue('MainWindow/position', self.pos())

        # save columns size
        self.persepolis_setting.setValue('MainWindow/column0', self.download_table.columnWidth(0))
        self.persepolis_setting.setValue('MainWindow/column1', self.download_table.columnWidth(1))
        self.persepolis_setting.setValue('MainWindow/column2', self.download_table.columnWidth(2))
        self.persepolis_setting.setValue('MainWindow/column3', self.download_table.columnWidth(3))
        self.persepolis_setting.setValue('MainWindow/column4', self.download_table.columnWidth(4))
        self.persepolis_setting.setValue('MainWindow/column5', self.download_table.columnWidth(5))
        self.persepolis_setting.setValue('MainWindow/column6', self.download_table.columnWidth(6))
        self.persepolis_setting.setValue('MainWindow/column7', self.download_table.columnWidth(7))
        self.persepolis_setting.setValue('MainWindow/column10', self.download_table.columnWidth(10))
        self.persepolis_setting.setValue('MainWindow/column11', self.download_table.columnWidth(11))
        self.persepolis_setting.setValue('MainWindow/column12', self.download_table.columnWidth(12))

        # save maximizing situation
        if self.isMaximized():
            self.persepolis_setting.setValue('MainWindow/maximized', 'yes')
        else:
            self.persepolis_setting.setValue('MainWindow/maximized', 'no')

        # sync persepolis_setting
        # make sure all settings is saved.
        self.persepolis_setting.sync()

        # hide MainWindow
        self.hide()

        # write message in log and console
        logger.sendToLog("Please wait ...", "INFO")

        # stop all downloads
        self.stopAllDownloads(event)

        # hide system_tray_icon
        self.system_tray_icon.hide()

        download.shutDown()  # shutting down Aria2
        sleep(0.5)
        global shutdown_notification  # see start of this script and see inherited QThreads

        # shutdown_notification = 0 >> persepolis running , 1 >> persepolis is
        # ready for close(closeEvent called) , 2 >> OK, let's close application!
        shutdown_notification = 1
        while shutdown_notification != 2:
            sleep(0.1)

        # close data bases connections
        for db in self.persepolis_db, self.plugins_db, self.temp_db:
            # db.closeConnections()
            del db

        QCoreApplication.instance().quit
        logger.sendToLog("Persepolis closed!", "INFO")
        sys.exit(0)

    # showTray method shows/hides persepolis's icon in system tray icon

    def showTray(self, menu=None):
        # check if user checked trayAction in menu or not
        if self.trayAction.isChecked():
            # show system_tray_icon
            self.system_tray_icon.show()

            # enable minimizeAction in menu
            self.minimizeAction.setEnabled(True)

            tray_icon = 'yes'
        else:
            # hide system_tray_icon
            self.system_tray_icon.hide()

            # disabaling minimizeAction in menu
            self.minimizeAction.setEnabled(False)

            tray_icon = 'no'

        # write changes in persepolis_setting
        self.persepolis_setting.setValue('settings/tray-icon', tray_icon)
        self.persepolis_setting.sync()

    # this method shows/hides menubar and
    # it's called when user toggles showMenuBarAction in view menu

    def showMenuBar(self, menu=None):
        # persepolis has 2 menu bar
        # 1. menubar in main window
        # 2. qmenu(see mainwindow_ui.py file for more information)
        # qmenu is in toolBar2
        # user can toggle between viewing menu1 or menu2 with showMenuBarAction

        # check if showMenuBarAction is checked or unchecked
        if self.showMenuBarAction.isChecked():
            # show menubar and hide toolBar2
            self.menubar.show()
            self.toolBar2.hide()
            show_menubar = 'yes'
        else:
            # hide menubar and show toolBar2
            self.menubar.hide()
            self.toolBar2.show()
            show_menubar = 'no'

        # writing changes to persepolis_setting
        self.persepolis_setting.setValue('settings/show-menubar', show_menubar)
        self.persepolis_setting.sync()

    # this method shows/hides left side panel
    # this method is called if user toggles showSidePanelAction in view menu

    def showSidePanel(self, menu=None):
        if self.showSidePanelAction.isChecked():
            self.category_tree_qwidget.show()
            show_sidepanel = 'yes'
        else:
            self.category_tree_qwidget.hide()
            show_sidepanel = 'no'

        # write changes to persepolis_setting
        self.persepolis_setting.setValue(
            'settings/show-sidepanel', show_sidepanel)
        self.persepolis_setting.sync()

    # when user left clicks on persepolis's system tray icon,then
    # this method is called

    def systemTrayPressed(self, click):
        if click == 3:
            self.minMaxTray(click)

    # when minMaxTray method called ,this method shows/hides main window
    def minMaxTray(self, menu=None):
        # hide MainWindow if it's visible
        # Show MainWindow if it's hided
        if self.isVisible():
            self.minimizeAction.setText(QCoreApplication.translate("mainwindow_src_ui_tr", 'Show main Window'))
            self.minimizeAction.setIcon(QIcon(icons + 'window'))
            self.hide()
        else:
            self.show()
            self.minimizeAction.setText(QCoreApplication.translate("mainwindow_src_ui_tr", 'Minimize to system tray'))
            self.minimizeAction.setIcon(QIcon(icons + 'minimize'))

    # showMainWindow shows main window in normal mode , see CheckingThread
    def showMainWindow(self):
        self.showNormal()
        self.minimizeAction.setText(QCoreApplication.translate("mainwindow_src_ui_tr", 'Minimize to system tray'))
        self.minimizeAction.setIcon(QIcon(icons + 'minimize'))

    # stopAllDownloads stops all downloads
    def stopAllDownloads(self, menu=None):

        # stop all queues
        for queue in self.queue_list_dict.values():
            queue.stop = True
            queue.start = False

        # stop single downloads
        # get active download list from data base
        active_gid_list = self.persepolis_db.findActiveDownloads('Single Downloads')

        for gid in active_gid_list:

            answer = download.downloadStop(gid, self)
            # if aria2 did not respond , then this function is checking for
            # aria2 availability , and if aria2 disconnected then
            # aria2Disconnected is executed
            if answer == 'None':
                version_answer = download.aria2Version()
                if version_answer == 'did not respond':
                    self.aria2Disconnected()

    # this method creates Preferences window

    def openPreferences(self, menu=None):
        self.preferenceswindow = PreferencesWindow(
            self, self.persepolis_setting)

        # show Preferences Window
        self.preferenceswindow.show()

    # this method is creating AboutWindow

    def openAbout(self, menu=None):
        about_window = AboutWindow(self.persepolis_setting)
        self.about_window_list.append(about_window)
        self.about_window_list[-1].show()

    # This method opens user's default download folder

    def openDefaultDownloadFolder(self, menu=None):
        # find user's default download folder from persepolis_setting
        self.persepolis_setting.sync()
        download_path = self.persepolis_setting.value('settings/download_path')

        # check that if download folder is availabile or not
        if os.path.isdir(download_path):
            # open folder
            osCommands.xdgOpen(download_path, 'folder', 'folder')
        else:
            # show error message if folder didn't existed
            notifySend(str(download_path), QCoreApplication.translate("mainwindow_src_ui_tr", 'Not Found'), 5000,
                       'warning', parent=self)

    # this method opens download folder , if download was finished
    def openDownloadFolder(self, menu=None):

        # find user's selected row
        selected_row_return = self.selectedRow()

        if selected_row_return is not None:
            # find gid
            gid = self.download_table.item(
                selected_row_return, 8).text()

            # find status
            download_status = self.download_table.item(
                selected_row_return, 1).text()

            if download_status == 'complete':

                # check if this link is related to video finder
                # don't open download folder, if download progress for video and audio aren't completed yet.
                video_finder_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)
                if video_finder_dictionary:

                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                          "Download is in progress by video finder!"),
                               QCoreApplication.translate("mainwindow_src_ui_tr", "be patient!"),
                               10000, 'warning', parent=self)

                    return

                # find download path
                dictionary = self.persepolis_db.searchGidInAddLinkTable(gid)
                download_path = dictionary['download_path']

                # check that if download_path existed
                if os.path.isfile(download_path):
                    # open file
                    osCommands.xdgOpen(download_path, 'folder', 'file')
                else:
                    # showing error message , if folder didn't existed
                    notifySend(str(download_path), QCoreApplication.translate("mainwindow_src_ui_tr", 'Not Found'),
                               5000, 'warning', parent=self)

    # this method executes(opens) download file if download's progress was finished

    def openFile(self, menu=None):
        # find user's selected row
        selected_row_return = self.selectedRow()

        if selected_row_return is not None:
            # find gid
            gid = self.download_table.item(
                selected_row_return, 8).text()

            # find status
            download_status = self.download_table.item(
                selected_row_return, 1).text()

            if download_status == 'complete':

                # check if this link is related to video finder
                # don't open download folder, if download progress for video and audio aren't completed yet.
                video_finder_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)

                if video_finder_dictionary:

                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                          "Download is in progress by video finder!"),
                               QCoreApplication.translate("mainwindow_src_ui_tr", "be patient!"),
                               10000, 'warning', parent=self)

                    return

                # find download path
                dictionary = self.persepolis_db.searchGidInAddLinkTable(gid)
                file_path = dictionary['download_path']

                if os.path.isfile(file_path):
                    # open file
                    osCommands.xdgOpen(file_path)

                else:
                    # show error message , if file was deleted or moved
                    notifySend(str(file_path), QCoreApplication.translate("mainwindow_src_ui_tr", 'Not Found'), 5000,
                               'warning', parent=self)

    # this method is called when multiple items is selected by user!
    def selectDownloads(self):

        # find highlighted item in category_tree
        current_category_tree_text = str(current_category_tree_index.data())
        self.toolBarAndContextMenuItems(current_category_tree_text)

        # change actions icon
        if self.multi_items_selected:
            self.removeSelectedAction.setIcon(QIcon(icons + 'multi_remove'))
            self.deleteSelectedAction.setIcon(QIcon(icons + 'multi_trash'))
            self.moveUpSelectedAction.setIcon(QIcon(icons + 'multi_up'))
            self.moveDownSelectedAction.setIcon(QIcon(icons + 'multi_down'))

        else:
            self.removeSelectedAction.setIcon(QIcon(icons + 'remove'))
            self.deleteSelectedAction.setIcon(QIcon(icons + 'trash'))
            self.moveUpSelectedAction.setIcon(QIcon(icons + 'up'))
            self.moveDownSelectedAction.setIcon(QIcon(icons + 'down'))

    # this method is called when user presses 'remove selected items' button
    def removeSelected(self, menu=None):

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.removeSelected2)
        else:
            self.removeSelected2()

    def removeSelected2(self):

        # find selected rows!
        gid_list = []
        for row in self.userSelectedRows():
            # get download status
            status = self.download_table.item(row, 1).text()

            # find category
            category = self.download_table.item(row, 12).text()

            if category != "Single Downloads":
                # check queue condition!
                # queue must be stopped first
                if str(category) in self.queue_list_dict.keys():
                    queue_status = self.queue_list_dict[str(category)].start
                else:
                    queue_status = False

                if queue_status:  # if queue was started
                    # show error message
                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful!"),
                               QCoreApplication.translate(
                                   "mainwindow_src_ui_tr",
                                   "Operation was not successful! Please stop the following category first: ")
                                   + category,
                               5000, 'fail', parent=self)

                    continue

            # find gid
            gid = self.download_table.item(row, 8).text()

            # check if this link is related to video finder
            if gid in self.all_video_finder_gid_list:

                video_finder_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)

                if gid in self.video_finder_threads_dict.keys():
                    # check the Video Finder tread status
                    video_finder_thread = self.video_finder_threads_dict[video_finder_dictionary['video_gid']]

                    if video_finder_thread.active == 'yes':

                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                              "Download is in progress by video finder!"),
                                   QCoreApplication.translate("mainwindow_src_ui_tr", "be patient!"),
                                   10000, 'warning', parent=self)

                        continue

                   # if Video Finder thread is not active so remove both of video and audio link.
                    else:
                        gid_list.append(video_finder_dictionary['video_gid'])
                        gid_list.append(video_finder_dictionary['audio_gid'])

                        continue

                # if Video Finder thread is not active so remove both of video and audio link.
                else:
                    gid_list.append(video_finder_dictionary['video_gid'])
                    gid_list.append(video_finder_dictionary['audio_gid'])

                    continue

            # only download items with "complete", "error" and "stopped" can be removed
            if (status == 'complete' or status == 'error' or status == 'stopped'):

                # add gid to gid_list
                gid_list.append(gid)
            else:
                # find filename
                file_name = self.download_table.item(row, 0).text()

                # show error message
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful!"),
                           QCoreApplication.translate("mainwindow_src_ui_tr",
                                                      "Please stop the following download first: ") + file_name,
                           5000, 'fail', parent=self)

        # remove duplicate items
        gid_list = set(gid_list)

        # find row number for specific gid
        for gid in gid_list:
            for i in range(self.download_table.rowCount()):
                row_gid = self.download_table.item(i, 8).text()
                if gid == row_gid:
                    row = i
                    break

            # find status
            status = self.download_table.item(row, 1).text()

            # find filename
            file_name = self.download_table.item(row, 0).text()

            # find category
            category = self.download_table.item(row, 12).text()

            # remove row from download_table
            self.download_table.removeRow(row)

            # remove download item from data base
            self.persepolis_db.deleteItemInDownloadTable(gid, category)

            # remove file of download from download temp folder
            if file_name != '***' and status != 'complete':
                file_name_path = os.path.join(
                    temp_download_folder,  str(file_name))
                osCommands.remove(file_name_path)  # remove file

                file_name_aria = file_name_path + str('.aria2')
                osCommands.remove(file_name_aria)  # remove file.aria

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # this method is called when user presses 'delete selected items'

    def deleteSelected(self, menu=None):
        # showing Warning message to the user.
        # checking persepolis_setting first!
        # perhaps user was checking "do not show this message again"
        delete_warning_message = self.persepolis_setting.value(
            'MainWindow/delete-warning', 'yes')

        if delete_warning_message == 'yes':
            self.msgBox = QMessageBox()
            self.msgBox.setText(QCoreApplication.translate("mainwindow_src_ui_tr",
                                            "<b><center>This operation will delete \
                                            downloaded files from your hard disk<br>PERMANENTLY!</center></b>"))
            self.msgBox.setInformativeText(QCoreApplication.translate(
                "mainwindow_src_ui_tr", "<center>Do you want to continue?</center>"))
            self.msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            self.msgBox.setIcon(QMessageBox.Warning)
            dont_show_checkBox = QCheckBox("don't show this message again")
            self.msgBox.setCheckBox(dont_show_checkBox)
            reply = self.msgBox.exec_()

            # if user checks "do not show this message again!", change persepolis_setting!
            if self.msgBox.checkBox().isChecked():
                self.persepolis_setting.setValue(
                    'MainWindow/delete-warning', 'no')

            # do nothing if user clicks NO
            if reply != QMessageBox.Yes:
                return

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.deleteSelected2)
        else:
            self.deleteSelected2()

    def deleteSelected2(self):
        gid_list = []
        # find selected rows!
        for row in self.userSelectedRows():
            # get download status
            status = self.download_table.item(row, 1).text()

            # find category
            category = self.download_table.item(row, 12).text()

            if category != "Single Downloads":
                # check queue condition!
                # queue must be stopped first
                if str(category) in self.queue_list_dict.keys():
                    queue_status = self.queue_list_dict[str(category)].start
                else:
                    queue_status = False

                if queue_status:  # if queue was started
                    # show error message
                    notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful!"),
                               QCoreApplication.translate("mainwindow_src_ui_tr",
                                                          "Please stop the following category first: ") + category,
                               5000, 'fail', parent=self)

                    continue

            # find gid
            gid = self.download_table.item(row, 8).text()

            # check if this link is related to video finder
            if gid in self.all_video_finder_gid_list:

                video_finder_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)

                if gid in self.video_finder_threads_dict.keys():

                    # check the Video Finder tread status
                    video_finder_thread = self.video_finder_threads_dict[video_finder_dictionary['video_gid']]

                    if video_finder_thread.active == 'yes':

                        notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                              "Download is in progress by video finder!"),
                                   QCoreApplication.translate("mainwindow_src_ui_tr", "be patient!"),
                                   10000, 'warning', parent=self)

                        continue

                    # if Video Finder thread is not active so remove both of video and audio link.
                    else:
                        gid_list.append(video_finder_dictionary['video_gid'])
                        gid_list.append(video_finder_dictionary['audio_gid'])
                        continue

                else:
                    gid_list.append(video_finder_dictionary['video_gid'])
                    gid_list.append(video_finder_dictionary['audio_gid'])
                    continue

            # only download items with "complete", "error" and "stopped" can be removed
            if (status == 'complete' or status == 'error' or status == 'stopped'):

                # add gid to gid_list
                gid_list.append(gid)

            else:
                # find filename
                file_name = self.download_table.item(row, 0).text()

                # show error message
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful!"),
                           QCoreApplication.translate("mainwindow_src_ui_tr",
                                                      "Stop the following download first: ") + file_name,
                           5000, 'fail', parent=self)

        # remove selected rows

        # remove duplicate items
        gid_list = set(gid_list)

        # find row number for specific gid
        for gid in gid_list:
            for i in range(self.download_table.rowCount()):
                row_gid = self.download_table.item(i, 8).text()
                if gid == row_gid:
                    row = i
                    break

            # find file_name
            file_name = self.download_table.item(row, 0).text()

            # find category
            category = self.download_table.item(row, 12).text()

            # find status
            status = self.download_table.item(row, 1).text()

            # if download is not completed,
            # remove downloaded file form download temp folder
            if file_name != '***' and status != 'complete':
                file_name_path = os.path.join(
                    temp_download_folder, str(file_name))

                # remove file : file_name_path
                osCommands.remove(file_name_path)

                # remove aria2 download information file : file_name_aria
                file_name_aria = file_name_path + str('.aria2')
                osCommands.remove(file_name_aria)

            # remove downloaded file, if download is completed
            if status == 'complete':

                # find download path
                dictionary = self.persepolis_db.searchGidInAddLinkTable(gid)
                if dictionary:
                    file_path = dictionary['download_path']

                    remove_answer = osCommands.remove(file_path)

                    if remove_answer == 'no':
                        notifySend(str(file_path), QCoreApplication.translate("mainwindow_src_ui_tr", 'Not Found'),
                                   5000, 'warning', parent=self)

            # remove row from download_table
            self.download_table.removeRow(row)

            # remove download item from data base
            self.persepolis_db.deleteItemInDownloadTable(gid, category)

        # telling the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # this method sorts download table by name
    def sortByName(self, menu=None):

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.sortByName2)
        else:
            self.sortByName2()

    def sortByName2(self):
        # find names and gid of downloads and save them in name_gid_dict
        # gid is key and name is value.
        gid_name_dict = {}
        for row in range(self.download_table.rowCount()):
            name = self.download_table.item(row, 0).text()
            gid = self.download_table.item(row, 8).text()
            gid_name_dict[gid] = name

        # sort names
        gid_sorted_list = sorted(gid_name_dict, key=gid_name_dict.get)

        # clear download_table and add sorted items
        self.download_table.clearContents()

        # find name of selected category
        current_category_tree_text = str(current_category_tree_index.data())

        # get download information from data base
        if current_category_tree_text == 'All Downloads':
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable()
        else:
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable(current_category_tree_text)

        j = 0

        for gid in gid_sorted_list:
            # enter download rows according to gid_sorted_list
            download_info = downloads_dict[gid]

            keys_list = ['file_name',
                         'status',
                         'size',
                         'downloaded_size',
                         'percent',
                         'connections',
                         'rate',
                         'estimate_time_left',
                         'gid',
                         'link',
                         'first_try_date',
                         'last_try_date',
                         'category'
                         ]

            i = 0
            for key in keys_list:
                item = QTableWidgetItem(download_info[key])

                # insert item in download_table
                self.download_table.setItem(j, i, item)

                i = i+1

            j = j + 1

        # save sorted list (gid_sorted_list) in data base
        category_dict = {'category': current_category_tree_text}

        # update gid_list
        gid_sorted_list.reverse()
        category_dict['gid_list'] = gid_sorted_list

        # update category_db_table
        self.persepolis_db.updateCategoryTable([category_dict])

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # this method sorts items in download_table by size
    def sortBySize(self, menu=None):

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.sortBySize2)
        else:
            self.sortBySize2()

    def sortBySize2(self):

        # find name of selected category
        current_category_tree_text = str(current_category_tree_index.data())

        # find gid and size of downloads
        gid_size_dict = {}
        for row in range(self.download_table.rowCount()):
            size_str = self.download_table.item(row, 2).text()
            gid = self.download_table.item(row, 8).text()

            # convert file size to the Byte
            try:
                size_int = float(size_str[:-3])
                size_symbol = str(size_str[-2])
                if size_symbol == 'G':
                    size = size_int * 1073741824
                elif size_symbol == 'M':
                    size = size_int * 1048576
                elif size_symbol == 'K':
                    size = size_int * 1024
                else:  # Byte
                    size = size_int
            except ValueError:
                size = 0

            # create a dictionary from gid and size of files in Bytes
            # gid as key and size as value
            gid_size_dict[gid] = size

        # sort gid_size_dict
        gid_sorted_list = sorted(
            gid_size_dict, key=gid_size_dict.get, reverse=True)

        # clear download_table by size
        self.download_table.clearContents()

        # get download information from data base
        if current_category_tree_text == 'All Downloads':
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable()
        else:
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable(current_category_tree_text)

        j = 0

        for gid in gid_sorted_list:
            # enter download rows according to gid_sorted_list
            download_info = downloads_dict[gid]

            keys_list = ['file_name',
                         'status',
                         'size',
                         'downloaded_size',
                         'percent',
                         'connections',
                         'rate',
                         'estimate_time_left',
                         'gid',
                         'link',
                         'first_try_date',
                         'last_try_date',
                         'category'
                         ]

            i = 0
            for key in keys_list:
                item = QTableWidgetItem(download_info[key])

                # insert item in download_table
                self.download_table.setItem(j, i, item)

                i = i+1

            j = j + 1

        # save sorted list (gid_sorted_list) in data base
        category_dict = {'category': current_category_tree_text}

        # update gid_list
        gid_sorted_list.reverse()

        category_dict['gid_list'] = gid_sorted_list

        # update category_db_table
        self.persepolis_db.updateCategoryTable([category_dict])

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # this method sorts download_table items with status
    def sortByStatus(self, menu=None):

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.sortByStatus2)
        else:
            self.sortByStatus2()

    def sortByStatus2(self):

        # find name of selected category
        current_category_tree_text = str(current_category_tree_index.data())

        # find gid and status of downloads
        gid_status_dict = {}
        for row in range(self.download_table.rowCount()):
            status = self.download_table.item(row, 1).text()
            gid = self.download_table.item(row, 8).text()
            # assign a number to every status
            if status == 'complete':
                status_int = 1
            elif status == 'stopped':
                status_int = 2
            elif status == 'error':
                status_int = 3
            elif status == 'downloading':
                status_int = 4
            elif status == 'waiting':
                status_int = 5
            else:
                status_int = 6

            # create a dictionary from gid and size_int of files in Bytes
            gid_status_dict[gid] = status_int

        # sort gid_status_dict
        gid_sorted_list = sorted(gid_status_dict, key=gid_status_dict.get)

        # get download information from data base
        if current_category_tree_text == 'All Downloads':
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable()
        else:
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable(current_category_tree_text)

        # clear download_table
        self.download_table.clearContents()

        j = 0

        for gid in gid_sorted_list:
            # enter download rows according to gid_sorted_list
            download_info = downloads_dict[gid]

            keys_list = ['file_name',
                         'status',
                         'size',
                         'downloaded_size',
                         'percent',
                         'connections',
                         'rate',
                         'estimate_time_left',
                         'gid',
                         'link',
                         'first_try_date',
                         'last_try_date',
                         'category'
                         ]

            i = 0
            for key in keys_list:
                item = QTableWidgetItem(download_info[key])

                # insert item in download_table
                self.download_table.setItem(j, i, item)

                i = i+1

            j = j + 1

        # save sorted list (gid_sorted_list) in data base
        category_dict = {'category': current_category_tree_text}

        # update gid_list
        gid_sorted_list.reverse()

        category_dict['gid_list'] = gid_sorted_list

        # update category_db_table
        self.persepolis_db.updateCategoryTable([category_dict])

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # this method sorts download table with date added information
    def sortByFirstTry(self, menu=None):

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.sortByFirstTry2)
        else:
            self.sortByFirstTry2()

    def sortByFirstTry2(self):
        # find gid and first try date
        gid_try_dict = {}
        for row in range(self.download_table.rowCount()):
            first_try_date = self.download_table.item(row, 10).text()
            gid = self.download_table.item(row, 8).text()

            # convert date and hour in first_try_date to a number
            # for example , first_try_date = '2016/11/05 , 07:45:38'
            # must be converted to 20161105074538
            first_try_date_splited = first_try_date.split(' , ')
            date_list = first_try_date_splited[0].split('/')
            hour_list = first_try_date_splited[1].split(':')
            date_joind = "".join(date_list)
            hour_joind = "".join(hour_list)
            date_hour_str = date_joind + hour_joind
            date_hour = int(date_hour_str)

            # create a dictionary
            # gid as key and date_hour as value
            gid_try_dict[gid] = date_hour

        # sort
        gid_sorted_list = sorted(
            gid_try_dict, key=gid_try_dict.get, reverse=True)

        # clear download_table
        self.download_table.clearContents()

        # find name of selected category
        current_category_tree_text = str(current_category_tree_index.data())

        # get download information from data base
        if current_category_tree_text == 'All Downloads':
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable()
        else:
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable(current_category_tree_text)

        j = 0

        for gid in gid_sorted_list:
            # enter download rows according to gid_sorted_list
            download_info = downloads_dict[gid]

            keys_list = ['file_name',
                         'status',
                         'size',
                         'downloaded_size',
                         'percent',
                         'connections',
                         'rate',
                         'estimate_time_left',
                         'gid',
                         'link',
                         'first_try_date',
                         'last_try_date',
                         'category'
                         ]

            i = 0
            for key in keys_list:
                item = QTableWidgetItem(download_info[key])

                # insert item in download_table
                self.download_table.setItem(j, i, item)

                i = i+1

            j = j + 1

        # save sorted list (gid_list) in data base
        category_dict = {'category': current_category_tree_text}

        # update gid_sorted_list
        gid_sorted_list.reverse()

        category_dict['gid_list'] = gid_sorted_list

        # update category_db_table
        self.persepolis_db.updateCategoryTable([category_dict])

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # this method sorts download_table with order of last modify date

    def sortByLastTry(self, menu=None):
        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.sortByLastTry2)
        else:
            self.sortByLastTry2()

    def sortByLastTry2(self):

        # create a dictionary
        # gid as key and date_hour as value
        gid_try_dict = {}

        # find gid and last try date for download items in download_table
        for row in range(self.download_table.rowCount()):
            last_try_date = self.download_table.item(row, 11).text()
            gid = self.download_table.item(row, 8).text()

            # convert date and hour in last_try_date to a number
            # for example , last_try_date = '2016/11/05 , 07:45:38'
            # must be converted to 20161105074538
            last_try_date_splited = last_try_date.split(' , ')
            date_list = last_try_date_splited[0].split('/')
            hour_list = last_try_date_splited[1].split(':')
            date_joind = "".join(date_list)
            hour_joind = "".join(hour_list)
            date_hour_str = date_joind + hour_joind
            date_hour = int(date_hour_str)

            # add gid and date_hour to gid_try_dict
            gid_try_dict[gid] = date_hour

        # sort
        gid_sorted_list = sorted(
            gid_try_dict, key=gid_try_dict.get, reverse=True)

        # clear download_table
        self.download_table.clearContents()

        # find name of selected category
        current_category_tree_text = str(current_category_tree_index.data())

        # get download information from data base
        if current_category_tree_text == 'All Downloads':
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable()
        else:
            downloads_dict = self.persepolis_db.returnItemsInDownloadTable(current_category_tree_text)

        j = 0

        for gid in gid_sorted_list:
            # enter download rows according to gid_sorted_list
            download_info = downloads_dict[gid]

            keys_list = ['file_name',
                         'status',
                         'size',
                         'downloaded_size',
                         'percent',
                         'connections',
                         'rate',
                         'estimate_time_left',
                         'gid',
                         'link',
                         'first_try_date',
                         'last_try_date',
                         'category'
                         ]

            i = 0
            for key in keys_list:
                item = QTableWidgetItem(download_info[key])

                # insert item in download_table
                self.download_table.setItem(j, i, item)

                i = i+1

            j = j + 1

        # save sorted list (gid_list) in data base
        category_dict = {'category': current_category_tree_text}

        # update gid_sorted_list
        gid_sorted_list.reverse()

        category_dict['gid_list'] = gid_sorted_list

        # update category_db_table
        self.persepolis_db.updateCategoryTable([category_dict])

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    # this method called , when user clicks on 'create new queue' button in
    # main window.

    def createQueue(self, menu=None):
        text, ok = QInputDialog.getText(
            self, 'Queue', 'Enter queue name:', text='queue')

        if not(ok):
            return None

        queue_name = str(text)
        if ok and queue_name != '' and queue_name != 'Single Downloads':
            # check queue_name if exists!
            answer = self.persepolis_db.searchCategoryInCategoryTable(queue_name)

            # show Error window if queue  before
            if answer:
                error_messageBox = QMessageBox()
                error_messageBox.setText(
                    '<b>"' + queue_name + QCoreApplication.translate("mainwindow_src_ui_tr", '</b>" already exists!'))
                error_messageBox.setWindowTitle('Error!')
                error_messageBox.exec_()
                return None

            # insert new item in category_tree
            new_queue_category = QStandardItem(queue_name)
            font = QFont()
            font.setBold(True)
            new_queue_category.setFont(font)
            new_queue_category.setEditable(False)
            self.category_tree_model.appendRow(new_queue_category)

            dict = {'category': queue_name,
                    'start_time_enable': 'no',
                    'start_time': '0:0',
                    'end_time_enable': 'no',
                    'end_time': '0:0',
                    'reverse': 'no',
                    'limit_enable': 'no',
                    'limit_value': '0K',
                    'after_download': 'no',
                    'gid_list': '[]'
                    }

            # insert new category in data base
            self.persepolis_db.insertInCategoryTable(dict)

            # highlight new category in category_tree
            # find item
            for i in range(self.category_tree_model.rowCount()):
                category_tree_item_text = str(
                    self.category_tree_model.index(i, 0).data())
                if category_tree_item_text == queue_name:
                    category_index = i
                    break
            # highlighting
            category_tree_model_index = self.category_tree_model.index(
                category_index, 0)
            self.category_tree.setCurrentIndex(category_tree_model_index)
            self.categoryTreeSelected(category_tree_model_index)

            # return queue_name
            return queue_name

    # this method creates a BrowserPluginQueue window for list of links.
    def pluginQueue(self, list_of_links):

        # create window
        plugin_queue_window = BrowserPluginQueue(
            self, list_of_links, self.queueCallback, self.persepolis_setting)
        self.plugin_queue_window_list.append(plugin_queue_window)
        self.plugin_queue_window_list[-1].show()

        # bring plugin_queue_window on top
        self.plugin_queue_window_list[-1].raise_()
        self.plugin_queue_window_list[-1].activateWindow()

    # this method is importing a text file for creating queue .
    # text file must contain links . 1 link per line!
    def importText(self, menu=None):

        # get file path
        f_path, filters = QFileDialog.getOpenFileName(
            self, 'Select the text file that contains links')

        # if path is correct:
        if os.path.isfile(str(f_path)):
            # create a text_queue_window for getting information.
            text_queue_window = TextQueue(
                self, f_path, self.queueCallback, self.persepolis_setting)

            self.text_queue_window_list.append(text_queue_window)
            self.text_queue_window_list[-1].show()

    # callback of text_queue_window and plugin_queue_window.AboutWindow
    # See importText and pluginQueue method for more information.
    def queueCallback(self, add_link_dictionary_list, category):

        download_table_dict_list = []

        # defining path of category_file
        selected_category = str(category)

        # highlight selected category in category_tree
        # first of all find category_index of item!
        for i in range(self.category_tree_model.rowCount()):
            category_tree_item_text = str(
                self.category_tree_model.index(i, 0).data())
            if category_tree_item_text == selected_category:
                category_index = i
                break

        # second: find category_tree_model_index
        category_tree_model_index = self.category_tree_model.index(
            category_index, 0)

        # third: highlight item
        self.category_tree.setCurrentIndex(category_tree_model_index)
        self.categoryTreeSelected(category_tree_model_index)

        download_table_list = []

        # get now time and date
        date = download.nowDate()

        # add dictionary of downloads to data base
        for add_link_dictionary in add_link_dictionary_list:

            # aria2 identifies each download by the ID called GID. The GID must
            # be hex string of 16 characters.
            gid = self.gidGenerator()

            add_link_dictionary['gid'] = gid

            # download_info_file_list is a list that contains ['file_name' ,
            # 'status' , 'size' , 'downloaded size' ,'download percentage' ,
            # 'number of connections' ,'Transfer rate' , 'estimate_time_left' ,
            # 'gid' , 'link' , 'first_try_date' , 'last_try_date', 'category']

            # if user or browser_plugin defined filename then file_name is valid in
            # add_link_dictionary['out']
            if add_link_dictionary['out']:
                file_name = add_link_dictionary['out']
            else:
                file_name = '***'

            download_table_list = [file_name, 'stopped', '***', '***', '***',
                                   '***', '***', '***', gid, add_link_dictionary['link'],
                                   date, date, category]

            dictionary = {'file_name': file_name,
                          'status': 'stopped',
                          'size': '***',
                          'downloaded_size': '***',
                          'percent': '***',
                          'connections': '***',
                          'rate': '***',
                          'estimate_time_left': '***',
                          'gid': gid,
                          'link': add_link_dictionary['link'],
                          'first_try_date': date,
                          'last_try_date': date,
                          'category': category}

            download_table_dict_list.append(dictionary)

            # create a row in download_table
            self.download_table.insertRow(0)
            j = 0
            for i in download_table_list:
                item = QTableWidgetItem(i)
                self.download_table.setItem(0, j, item)
                j = j + 1

            # spider is finding file size and file name
            new_spider = SpiderThread(add_link_dictionary, self)
            self.threadPool.append(new_spider)
            self.threadPool[-1].start()
            self.threadPool[-1].SPIDERSIGNAL.connect(self.spiderUpdate)

        # write information in data_base
        self.persepolis_db.insertInDownloadTable(download_table_dict_list)
        self.persepolis_db.insertInAddLinkTable(add_link_dictionary_list)

    # this method is called , when user clicks on an item in
    # category_tree (left side panel)

    def categoryTreeSelected(self, item):
        new_selection = item
        if current_category_tree_index != new_selection:
            # if checking_flag is equal to 1, it means that user pressed remove
            # or delete button or ... . so checking download information must
            # be stopped until job is done!
            if checking_flag != 2:

                wait_check = WaitThread()
                self.threadPool.append(wait_check)
                self.threadPool[-1].start()
                self.threadPool[-1].QTABLEREADY.connect(
                    partial(self.categoryTreeSelected2, new_selection))
            else:
                self.categoryTreeSelected2(new_selection)

    def categoryTreeSelected2(self, new_selection):
        global current_category_tree_index

        # clear download_table
        self.download_table.setRowCount(0)

        # old_selection_index
        old_selection_index = current_category_tree_index

        # finding name of old_selection_index
        old_category_tree_item_text = str(old_selection_index.data())

        queue_dict = {'category': old_category_tree_item_text}

        # start_checkBox
        if self.start_checkBox.isChecked():
            queue_dict['start_time_enable'] = 'yes'
        else:
            queue_dict['start_time_enable'] = 'no'

        # end_checkBox
        if self.end_checkBox.isChecked():
            queue_dict['end_time_enable'] = 'yes'
        else:
            queue_dict['end_time_enable'] = 'no'

        # start_time_qDataTimeEdit
        start_time = self.start_time_qDataTimeEdit.text()
        queue_dict['start_time'] = str(start_time)

        # end_time_qDateTimeEdit
        end_time = self.end_time_qDateTimeEdit.text()
        queue_dict['end_time'] = str(end_time)

        # reverse_checkBox
        if self.reverse_checkBox.isChecked():
            queue_dict['reverse'] = 'yes'
        else:
            queue_dict['reverse'] = 'no'

        # limit_checkBox
        if self.limit_checkBox.isChecked():
            queue_dict['limit_enable'] = 'yes'
        else:
            queue_dict['limit_enable'] = 'no'

        # limit_comboBox and limit_spinBox
        if self.limit_comboBox.currentText() == "KiB/s":
            limit = str(self.limit_spinBox.value()) + str("K")
        else:
            limit = str(self.limit_spinBox.value()) + str("M")

        queue_dict['limit_value'] = str(limit)

        # after_checkBox
        if self.after_checkBox.isChecked():
            queue_dict['after_download'] = 'yes'
        else:
            queue_dict['after_download'] = 'no'

        # if old_selection_index.data() is equal to None >> It means queue is
        # deleted! and no text (data) available for it
        if old_selection_index.data():

            # update data base
            self.persepolis_db.updateCategoryTable([queue_dict])

        # update download_table
        current_category_tree_index = new_selection

        # find category
        current_category_tree_text = str(
            self.category_tree.currentIndex().data())

        # read download items from data base
        if current_category_tree_text == 'All Downloads':
            download_table_dict = self.persepolis_db.returnItemsInDownloadTable()
        else:
            download_table_dict = self.persepolis_db.returnItemsInDownloadTable(current_category_tree_text)

        # get gid_list
        category_dict = self.persepolis_db.searchCategoryInCategoryTable(current_category_tree_text)
        gid_list = eval(category_dict['gid_list'])

        keys_list = ['file_name',
                     'status',
                     'size',
                     'downloaded_size',
                     'percent',
                     'connections',
                     'rate',
                     'estimate_time_left',
                     'gid',
                     'link',
                     'first_try_date',
                     'last_try_date',
                     'category'
                     ]

        # insert items in download_table
        for gid in gid_list:
            # create new row
            self.download_table.insertRow(0)

            dictionary = download_table_dict[gid]
            i = 0
            for key in keys_list:
                item = QTableWidgetItem(str(dictionary[key]))

                self.download_table.setItem(0, i, item)

                i = i + 1

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

        # update toolBar and tablewidget_menu items
        self.toolBarAndContextMenuItems(str(current_category_tree_text))

    # this method changes toolabr and context menu items when new item
    # highlighted by user in category_tree
    def toolBarAndContextMenuItems(self, category):

        # clear toolBar and context menus.
        # it makes them ready for adding
        # new items that suitable with new selected category.

        # clear toolBar
        self.toolBar.clear()

        # clear context menu of download_table
        self.download_table.tablewidget_menu.clear()

        # clear context menu of category_tree
        self.category_tree.category_tree_menu.clear()

        queueAction = QAction(QIcon(icons + 'add'), 'Single Downloads', self,
                              statusTip="Add to Single Downloads",
                              triggered=partial(self.addToQueue, 'Single Downloads'))

        # check if user checked selection mode
        if self.multi_items_selected:
            self.download_table.sendMenu = self.download_table.tablewidget_menu.addMenu(
                QCoreApplication.translate("mainwindow_src_ui_tr", 'Send selected downloads to'))
        else:
            self.download_table.sendMenu = self.download_table.tablewidget_menu.addMenu(
                QCoreApplication.translate("mainwindow_src_ui_tr", 'Send to'))

        # get categories list from data base
        categories_list = self.persepolis_db.categoriesList()

        # add categories name to sendMenu
        for category_name in categories_list:
            if category_name != category and category_name != 'All Downloads':
                queueAction = QAction(QIcon(icons + 'add_queue'), category_name, self,
                                      statusTip="Add to" + category_name,
                                      triggered=partial(self.addToQueue, category_name))

                self.download_table.sendMenu.addAction(queueAction)

        if category == 'All Downloads':
            # hide queue_panel_widget(lef side down panel)
            self.queue_panel_widget.hide()

            # update toolBar
            list = [self.addlinkAction, self.resumeAction, self.pauseAction,
                    self.stopAction, self.removeSelectedAction, self.deleteSelectedAction,
                    self.propertiesAction, self.progressAction, self.minimizeAction,
                    self.videoFinderAddLinkAction, self.exitAction]

            for i in list:
                self.toolBar.addAction(i)

            self.toolBar.insertSeparator(self.addlinkAction)
            self.toolBar.insertSeparator(self.resumeAction)
            self.toolBar.insertSeparator(self.removeSelectedAction)
            self.toolBar.insertSeparator(self.propertiesAction)
            self.toolBar.insertSeparator(self.videoFinderAddLinkAction)
            self.toolBar.insertSeparator(self.exitAction)
            self.toolBar.addSeparator()

            # add actions to download_table's context menu
            list = [self.openFileAction, self.openDownloadFolderAction, self.resumeAction,
                    self.pauseAction, self.stopAction, self.removeSelectedAction,
                    self.deleteSelectedAction, self.propertiesAction,
                    self.progressAction, self.moveSelectedDownloadsAction]

            for action in list:
                self.download_table.tablewidget_menu.addAction(action)

        elif category == 'Single Downloads':
            # hide queue_panel_widget
            self.queue_panel_widget.hide()
            self.queuePanelWidget(category)

            # update toolBar
            list = [self.addlinkAction, self.resumeAction, self.pauseAction,
                    self.stopAction, self.removeSelectedAction, self.deleteSelectedAction,
                    self.propertiesAction, self.progressAction, self.minimizeAction,
                    self.videoFinderAddLinkAction, self.exitAction]

            for i in list:
                self.toolBar.addAction(i)

            self.toolBar.insertSeparator(self.addlinkAction)
            self.toolBar.insertSeparator(self.resumeAction)
            self.toolBar.insertSeparator(self.removeSelectedAction)
            self.toolBar.insertSeparator(self.propertiesAction)
            self.toolBar.insertSeparator(self.videoFinderAddLinkAction)
            self.toolBar.insertSeparator(self.exitAction)
            self.toolBar.addSeparator()

            # add actions to download_table's context menu
            list = [self.openFileAction, self.openDownloadFolderAction, self.resumeAction,
                    self.pauseAction, self.stopAction, self.removeSelectedAction,
                    self.deleteSelectedAction, self.propertiesAction,
                    self.progressAction, self.moveSelectedDownloadsAction]

            for action in list:
                self.download_table.tablewidget_menu.addAction(action)

        elif (category != 'All Downloads' and category != 'Single Downloads'):
            # show queue_panel_widget
            self.queue_panel_widget.show()
            self.queuePanelWidget(category)

            # update toolBar
            list = [self.addlinkAction, self.removeSelectedAction, self.deleteSelectedAction,
                    self.propertiesAction, self.startQueueAction, self.stopQueueAction,
                    self.removeQueueAction, self.moveUpSelectedAction, self.moveDownSelectedAction,
                    self.minimizeAction, self.videoFinderAddLinkAction, self.exitAction]

            for i in list:
                self.toolBar.addAction(i)

            self.toolBar.insertSeparator(self.addlinkAction)
            self.toolBar.insertSeparator(self.removeSelectedAction)
            self.toolBar.insertSeparator(self.propertiesAction)
            self.toolBar.insertSeparator(self.startQueueAction)
            self.toolBar.insertSeparator(self.minimizeAction)
            self.toolBar.insertSeparator(self.videoFinderAddLinkAction)
            self.toolBar.insertSeparator(self.exitAction)
            self.toolBar.addSeparator()

            # add actions to download_table's context menu
            for action in [self.openFileAction, self.openDownloadFolderAction,
                           self.removeSelectedAction, self.deleteSelectedAction,
                           self.propertiesAction, self.moveSelectedDownloadsAction]:
                self.download_table.tablewidget_menu.addAction(action)

            # update category_tree_menu(right click menu for category_tree items)
            for i in self.startQueueAction, self.stopQueueAction, self.removeQueueAction:
                self.category_tree.category_tree_menu.addAction(i)

        # check queue condition
        if category != 'All Downloads' and category != 'Single Downloads':
            if str(category) in self.queue_list_dict.keys():
                queue_status = self.queue_list_dict[str(category)].start
            else:
                queue_status = False

            if queue_status:
                # if queue started before
                self.stopQueueAction.setEnabled(True)
                self.startQueueAction.setEnabled(False)
                self.removeQueueAction.setEnabled(False)
                self.moveUpSelectedAction.setEnabled(False)
                self.moveDownSelectedAction.setEnabled(False)
            else:
                # if queue didn't start
                self.stopQueueAction.setEnabled(False)
                self.startQueueAction.setEnabled(True)
                self.removeQueueAction.setEnabled(True)
                self.moveUpSelectedAction.setEnabled(True)
                self.moveDownSelectedAction.setEnabled(True)

        else:
            # if category is All Downloads  or Single Downloads
            self.stopQueueAction.setEnabled(False)
            self.startQueueAction.setEnabled(False)
            self.removeQueueAction.setEnabled(False)
            self.moveUpSelectedAction.setEnabled(False)
            self.moveDownSelectedAction.setEnabled(False)

        # add sortMenu to download_table context menu
        sortMenu = self.download_table.tablewidget_menu.addMenu(
            QCoreApplication.translate("mainwindow_src_ui_tr", 'Sort by'))
        sortMenu.addAction(self.sort_file_name_Action)

        sortMenu.addAction(self.sort_file_size_Action)

        sortMenu.addAction(self.sort_first_try_date_Action)

        sortMenu.addAction(self.sort_last_try_date_Action)

        sortMenu.addAction(self.sort_download_status_Action)

    # this method removes the queue that is selected in category_tree

    def removeQueue(self, menu=None):
        # show Warning message to user.
        # checks persepolis_setting first!
        # perhaps user was checking "do not show this message again"
        remove_warning_message = self.persepolis_setting.value(
            'MainWindow/remove-queue-warning', 'yes')

        if remove_warning_message == 'yes':
            self.remove_queue_msgBox = QMessageBox()
            self.remove_queue_msgBox.setText(QCoreApplication.translate("mainwindow_src_ui_tr",
                                        '<b><center>This operation will remove \
                                        all download items in this queue<br>from "All Downloads" list!</center></b>'))

            self.remove_queue_msgBox.setInformativeText(QCoreApplication.translate(
                "mainwindow_src_ui_tr", "<center>Do you want to continue?</center>"))
            self.remove_queue_msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            self.remove_queue_msgBox.setIcon(QMessageBox.Warning)
            dont_show_checkBox = QCheckBox("don't show this message again")
            self.remove_queue_msgBox.setCheckBox(dont_show_checkBox)
            reply = self.remove_queue_msgBox.exec_()

            # if user checks "do not show this message again!", change persepolis_setting!
            if self.remove_queue_msgBox.checkBox().isChecked():
                self.persepolis_setting.setValue(
                    'MainWindow/remove-queue-warning', 'no')

            # do nothing if user clicks NO
            if reply != QMessageBox.Yes:
                return

        # find name of queue
        current_category_tree_text = str(current_category_tree_index.data())

        if current_category_tree_text == 'Scheduled Downloads':
            error_messageBox = QMessageBox()
            error_messageBox.setText(
                QCoreApplication.translate("mainwindow_src_ui_tr", "<b>Sorry! You can't remove default queue!</b>"))
            error_messageBox.setWindowTitle('Error!')
            error_messageBox.exec_()

            return

        if current_category_tree_text != 'All Downloads' and current_category_tree_text != 'Single Downloads':

            # remove queue from category_tree
            row_number = current_category_tree_index.row()
            self.category_tree_model.removeRow(row_number)

            # delete category from data base
            self.persepolis_db.deleteCategory(current_category_tree_text)

        # highlight "All Downloads" in category_tree
        all_download_index = self.category_tree_model.index(0, 0)
        self.category_tree.setCurrentIndex(all_download_index)
        self.categoryTreeSelected(all_download_index)

    # this method starts the queue that is selected in category_tree

    def startQueue(self, menu=None):
        self.startQueueAction.setEnabled(False)

        # current_category_tree_text is the name of queue that is selected by user
        current_category_tree_text = str(current_category_tree_index.data())

        # create an item for this category in temp_db if not exists!
        try:
            self.temp_db.insertInQueueTable(current_category_tree_text)
        except Exception:
            # release lock
            self.temp_db.lock = False

        queue_info_dict = {'category': current_category_tree_text}

        # check that if user checks start_checkBox or not.
        if self.start_checkBox.isChecked():
            queue_info_dict['start_time_enable'] = 'yes'

            # read start_time value
            start_time = self.start_time_qDataTimeEdit.text()
        else:
            queue_info_dict['start_time_enable'] = 'no'
            start_time = None

        # check that if user checked end_checkBox or not.
        if self.end_checkBox.isChecked():
            queue_info_dict['end_time_enable'] = 'yes'

            # read end_time value
            end_time = self.end_time_qDateTimeEdit.text()
        else:
            queue_info_dict['end_time_enable'] = 'no'
            end_time = None

        # reverse_checkBox
        if self.reverse_checkBox.isChecked():
            queue_info_dict['reverse'] = 'yes'
        else:
            queue_info_dict['reverse'] = 'no'

        # update data base
        self.persepolis_db.updateCategoryTable([queue_info_dict])

        # create new Queue thread
        new_queue = Queue(current_category_tree_text, start_time,
                          end_time, self)

        self.queue_list_dict[current_category_tree_text] = new_queue
        self.queue_list_dict[current_category_tree_text].start()
        self.queue_list_dict[current_category_tree_text].REFRESHTOOLBARSIGNAL.connect(
            self.toolBarAndContextMenuItems)

        self.toolBarAndContextMenuItems(current_category_tree_text)

    # this method stops the queue that is selected
    # by user in the left side panel
    def stopQueue(self, menu=None):
        self.stopQueueAction.setEnabled(False)

        # current_category_tree_text is the name of queue that is selected by user
        current_category_tree_text = str(current_category_tree_index.data())

        queue = self.queue_list_dict[current_category_tree_text]
        queue.start = False
        queue.stop = True

        self.startQueueAction.setEnabled(True)

    # this method is called , when user want to add a download to a queue with
    # context menu. see also toolBarAndContextMenuItems() method

    def addToQueue(self, data, menu=None):
        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!

        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(partial(self.addToQueue2, data))
        else:
            self.addToQueue2(data)

    def addToQueue2(self, data):
        send_message = False

        # new selected category
        new_category = str(data)

        gid_list = []
        # find selected rows!
        for row in self.userSelectedRows():
            status = self.download_table.item(row, 1).text()
            category = self.download_table.item(row, 12).text()

            # check status of old category
            if category in self.queue_list_dict.keys():
                if self.queue_list_dict[category].start:
                    # It means queue is in download progress
                    status = 'downloading'

            # download must be in stopped situation.
            if (status == 'error' or status == 'stopped' or status == 'complete'):
                # find gid
                gid = self.download_table.item(row, 8).text()

                # check if this gid is related to video finder
                if gid in self.all_video_finder_gid_list:

                    video_finder_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)

                    # check the Video Finder tread status
                    if video_finder_dictionary['video_gid'] in self.video_finder_threads_dict:
                        video_finder_thread = self.video_finder_threads_dict[video_finder_dictionary['video_gid']]

                        if video_finder_thread.active == 'no':

                            # add both of video and audio links
                            gid_list.append(video_finder_dictionary['video_gid'])
                            gid_list.append(video_finder_dictionary['audio_gid'])
                            continue

                        else:

                            send_message = True
                            continue

                    else:

                        # add both of video and audio links
                        gid_list.append(video_finder_dictionary['video_gid'])
                        gid_list.append(video_finder_dictionary['audio_gid'])
                        continue

                # append gid to gid_list
                gid_list.append(gid)
            else:
                send_message = True

        # remove duplicate items
        gid_list = set(gid_list)

        # find row number for specific gid
        for gid in gid_list:
            for i in range(self.download_table.rowCount()):
                row_gid = self.download_table.item(i, 8).text()
                if gid == row_gid:
                    row = i
                    break

            # current_category = former selected category
            current_category = self.download_table.item(row, 12).text()

            if current_category != new_category:

                # write changes in data base
                dict = {'gid': gid, 'category': new_category}
                self.persepolis_db.updateDownloadTable([dict])
                self.persepolis_db.setDefaultGidInAddlinkTable(gid, start_time=True, end_time=True, after_download=True)

                # delete item from gid_list in current_category
                current_category_dict = self.persepolis_db.searchCategoryInCategoryTable(current_category)

                # get gid_list
                current_category_gid_list = eval(current_category_dict['gid_list'])

                # delete item
                current_category_gid_list = current_category_gid_list.remove(gid)

                # update category_db_table
                self.persepolis_db.updateCategoryTable([current_category_dict])

                # add item to gid_list of new_category
                # get category_dict from data base
                new_category_dict = self.persepolis_db.searchCategoryInCategoryTable(new_category)

                # get gid_list
                new_category_gid_list = eval(new_category_dict['gid_list'])

                # add gid of item to gid_list
                new_category_gid_list = new_category_gid_list.append(gid)

                # update category_db_table
                self.persepolis_db.updateCategoryTable([new_category_dict])

                # update category in download_table
                current_category_tree_text = str(current_category_tree_index.data())

                if current_category_tree_text == 'All Downloads':
                    item = QTableWidgetItem(new_category)
                    self.download_table.setItem(row, 12, item)
                else:
                    self.download_table.removeRow(row)

        if send_message:
            # notify user that transfer was unsuccessful
            notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                                                  "Some items didn't transferred successfully!"),
                       QCoreApplication.translate("mainwindow_src_ui_tr", "Please stop download progress first."),
                       5000, 'no', parent=self)

        global checking_flag
        checking_flag = 0

    # this method activates or deactivates start_frame according to situation
    def startFrame(self, checkBox):

        if self.start_checkBox.isChecked():
            self.start_frame.setEnabled(True)
        else:
            self.start_frame.setEnabled(False)

    # this method activates or deactivates end_frame according to situation
    def endFrame(self, checkBox):

        if self.end_checkBox.isChecked():
            self.end_frame.setEnabled(True)
        else:
            self.end_frame.setEnabled(False)

    # this method showing/hiding queue_panel_widget according to
    # queue_panel_show_button text

    def showQueuePanelOptions(self, button):
        if not(self.show_queue_panel):
            self.show_queue_panel = True
            self.queue_panel_widget_frame.show()
            self.queue_panel_show_button.setText(QCoreApplication.translate("mainwindow_src_ui_tr", 'Hide options'))
        else:
            self.show_queue_panel = False
            self.queue_panel_widget_frame.hide()
            self.queue_panel_show_button.setText(QCoreApplication.translate("mainwindow_src_ui_tr", 'Show options'))

    # this metode is activating after_pushButton with limit_comboBox changing
    def limitComboBoxChanged(self, connect):
        self.limit_pushButton.setEnabled(True)

    # this method activates or deactivates limit_frame according to
    # limit_checkBox situation

    def limitFrame(self, checkBox):
        if self.limit_checkBox.isChecked():
            self.limit_frame.setEnabled(True)
            self.limit_pushButton.setEnabled(True)
        else:
            self.limit_frame.setEnabled(False)

        # current_category_tree_text is the name of queue that selected by user
            current_category_tree_text = str(
                current_category_tree_index.data())

        # inform queue about changes
            if current_category_tree_text in self.queue_list_dict.keys():
                self.queue_list_dict[current_category_tree_text].limit = False
                self.queue_list_dict[current_category_tree_text].limit_changed = True

    # this method limits download speed in queue

    def limitPushButtonPressed(self, button):
        self.limit_pushButton.setEnabled(False)

        # current_category_tree_text is the name of queue that selected by user
        current_category_tree_text = str(current_category_tree_index.data())

        # informing queue about changes
        self.queue_list_dict[current_category_tree_text].limit = True
        self.queue_list_dict[current_category_tree_text].limit_changed = True

    # this method handles user's shutdown request
    def afterPushButtonPressed(self, button):
        # current_category_tree_text is the name of queue that selected by user
        current_category_tree_text = str(current_category_tree_index.data())

        self.after_pushButton.setEnabled(False)

        if os_type != OS.WINDOWS:  # For Linux and Mac OSX

            # get root password from user
            passwd, ok = QInputDialog.getText(
                self, 'PassWord', 'Please enter root password:', QLineEdit.Password)
            if ok:
                pipe = subprocess.Popen(['sudo', '-S', 'echo', 'hello'],
                                        stdout=subprocess.DEVNULL,
                                        stdin=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL,
                                        shell=False)

                pipe.communicate(passwd.encode())

                answer = pipe.wait()

                while answer != 0:

                    # ask password again!
                    passwd, ok = QInputDialog.getText(
                        self, 'PassWord', 'Wrong Password!\nPlease try again.', QLineEdit.Password)

                    if ok:
                        # checking password
                        pipe = subprocess.Popen(['sudo', '-S', 'echo', 'hello'],
                                                stdout=subprocess.DEVNULL,
                                                stdin=subprocess.PIPE,
                                                stderr=subprocess.DEVNULL,
                                                shell=False)

                        pipe.communicate(passwd.encode())

                        answer = pipe.wait()

                    else:
                        ok = False
                        break

                if ok:
                    self.queue_list_dict[current_category_tree_text].after = True

                    # send password and queue name to ShutDownThread
                    shutdown_enable = ShutDownThread(
                        self, current_category_tree_text, passwd)
                    self.threadPool.append(shutdown_enable)
                    self.threadPool[-1].start()

                else:
                    self.after_checkBox.setChecked(False)
                    self.queue_list_dict[current_category_tree_text].after = False

            else:
                self.after_checkBox.setChecked(False)
                self.queue_list_dict[current_category_tree_text].after = False

        else:  # for windows

            shutdown_enable = ShutDownThread(self, current_category_tree_text)
            self.threadPool.append(shutdown_enable)
            self.threadPool[-1].start()

    # this method activates or deactivates after_frame according to
    # after_checkBox situation

    def afterFrame(self, checkBox):
        # current_category_tree_text is the name of queue that selected by user
        current_category_tree_text = str(current_category_tree_index.data())

        if self.after_checkBox.isChecked():  # enable after_frame
            self.after_frame.setEnabled(True)
            self.after_pushButton.setEnabled(True)
        else:
            self.after_frame.setEnabled(False)  # disable after_frame

            # write 'canceled' for this category in temp_db .
            # see shutdown.py for more information
            if current_category_tree_text in self.queue_list_dict.keys():
                if self.queue_list_dict[current_category_tree_text].after:

                    shutdown_dict = {'category': current_category_tree_text,
                                     'shutdown': 'canceled'}

                    self.temp_db.updateQueueTable(shutdown_dict)

                    self.queue_list_dict[current_category_tree_text].after = False

    # this method checks that queue started or not,
    # and it shows or hides widgets in queue_panel_widget
    # according to situation and set widgets in panel.

    def queuePanelWidget(self, category):
        # update queue panel widget items
        # read queue_info_dict from data base
        queue_info_dict = self.persepolis_db.searchCategoryInCategoryTable(category)

        for key in queue_info_dict:
            if queue_info_dict[key] in ["NULL", "None"]:
                queue_info_dict[key] = None

        # check queue condition
        if str(category) in self.queue_list_dict.keys():
            queue_status = self.queue_list_dict[str(category)].start
        else:
            queue_status = False

        if queue_status:  # queue started
            self.start_end_frame.hide()
            self.limit_after_frame.show()

            # check that if user set limit speed
            limit_status = self.queue_list_dict[str(category)].limit

            # check that if user selected 'shutdown after download'
            after_status = self.queue_list_dict[str(category)].after

            if limit_status:  # It means queue's download speed limited by user
                # get limit_spinBox value and limit_comboBox value
                limit_number = self.queue_list_dict[str(
                    category)].limit_spinBox_value
                limit_unit = self.queue_list_dict[str(
                    category)].limit_comboBox_value

                # set limit_spinBox value
                self.limit_spinBox.setValue(limit_number)

                # set limit_comboBox value
                if limit_unit == 'K':
                    self.after_comboBox.setCurrentIndex(0)
                else:
                    self.after_comboBox.setCurrentIndex(1)

                # enable limit_frame
                self.limit_checkBox.setChecked(True)

            else:
                # disable limit_frame
                self.limit_checkBox.setChecked(False)

            # limit speed
                limit = str(queue_info_dict['limit_value'])

            # limit values
                limit_number = limit[0:-1]
                limit_unit = limit[-1]

            # limit_spinBox
                self.limit_spinBox.setValue(float(limit_number))

            # limit_comboBox
                if limit_unit == 'K':
                    self.limit_comboBox.setCurrentIndex(0)
                else:
                    self.limit_comboBox.setCurrentIndex(1)

            # if after_status is True,
            # it means that user was selected
            # shutdown option, after queue completed.
            if after_status:
                self.after_checkBox.setChecked(True)
            else:
                self.after_checkBox.setChecked(False)

        else:
            # so queue is stopped

            self.start_end_frame.show()
            self.limit_after_frame.hide()

            # start time
            # start_checkBox
            if queue_info_dict['start_time_enable'] == 'yes':
                self.start_checkBox.setChecked(True)
            else:
                self.start_checkBox.setChecked(False)

            hour, minute = queue_info_dict['start_time'].split(':')

            q_time = QTime(int(hour), int(minute))
            self.start_time_qDataTimeEdit.setTime(q_time)

            # end time
            # end_checkBox
            if queue_info_dict['end_time_enable'] == 'yes':
                self.end_checkBox.setChecked(True)
            else:
                self.end_checkBox.setChecked(False)

            hour, minute = queue_info_dict['end_time'].split(':')
            # set time
            q_time = QTime(int(hour), int(minute))
            self.end_time_qDateTimeEdit.setTime(q_time)

            # reverse_checkBox
            if queue_info_dict['reverse'] == 'yes':
                self.reverse_checkBox.setChecked(True)
            else:
                self.reverse_checkBox.setChecked(False)

        self.limitFrame(category)
        self.afterFrame(category)
        self.startFrame(category)
        self.endFrame(category)

    # this method opens issues page in github
    def reportIssue(self, menu=None):
        osCommands.xdgOpen(f'{REPO_LINK}/issues')

    # this method opens persepolis wiki page in github
    def persepolisHelp(self, menu=None):
        osCommands.xdgOpen(f'https://github.com/persepolisdm/persepolis/wiki')

    # this method opens update menu

    def newUpdate(self, menu=None):
        checkupdatewindow = checkupdate(
            self.persepolis_setting)
        self.checkupdatewindow_list.append(checkupdatewindow)
        self.checkupdatewindow_list[-1].show()

    # this method opens LogWindow

    def showLog(self, menu=None):
        logwindow = LogWindow(
            self.persepolis_setting)
        self.logwindow_list.append(logwindow)
        self.logwindow_list[-1].show()

    # this method is called when user pressed moveUpSelectedAction
    # this method subtituts selected  items with upper one
    def moveUpSelected(self, menu=None):

        global button_pressed_counter
        button_pressed_counter = button_pressed_counter + 1

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be stopped
        # until job is done!

        if checking_flag != 2:
            button_pressed_thread = ButtonPressedThread()
            self.threadPool.append(button_pressed_thread)
            self.threadPool[-1].start()

            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.moveUpSelected2)
        else:
            self.moveUpSelected2()

    def moveUpSelected2(self):

        # current_category_tree_text is the name of queue that selected by user
        current_category_tree_text = str(current_category_tree_index.data())

        # get gid_list from data base
        category_dict = self.persepolis_db.searchCategoryInCategoryTable(current_category_tree_text)

        gid_list = eval(category_dict['gid_list'])

        # find selected rows
        rows_list = self.userSelectedRows()

        new_rows_list = []

        # move up selected rows
        for old_row in rows_list:
            new_row = int(old_row) - 1
            old_row_items_list = []
            new_row_items_list = []

            if new_row >= 0:
                new_rows_list.append(new_row)
                # old index and new index of item in gid_list
                old_index = len(gid_list) - old_row - 1
                new_index = old_index + 1

                # subtitute items in gid_list
                gid_list[old_index], gid_list[new_index] = gid_list[new_index], gid_list[old_index]

                # subtitute items in download_table
                # read current items in download_table
                for i in range(13):
                    old_row_items_list.append(
                        self.download_table.item(old_row, i).text())

                    new_row_items_list.append(
                        self.download_table.item(new_row, i).text())

                # substituting
                for i in range(13):
                    # old row
                    item = QTableWidgetItem(new_row_items_list[i])

                    self.download_table.setItem(old_row, i, item)

                    # new row
                    item = QTableWidgetItem(old_row_items_list[i])

                    self.download_table.setItem(new_row, i, item)

        # remove highlight from old rows
        self.download_table.clearSelection()

        # Visit this link for more information
        # doc.qt.io/qt-5/qabstractitemview.html
        self.download_table.setSelectionMode(QAbstractItemView.MultiSelection)

        # Highlight newer rows
        for row in new_rows_list:
            self.download_table.selectRow(row)

        # change selection mode to the normal situation
        self.download_table.setSelectionMode(QAbstractItemView.ExtendedSelection)

        # update data base
        self.persepolis_db.updateCategoryTable([category_dict])

    # this method is called if user pressed moveDownSelected action
    # this method is substituting selected download item with lower download item
    def moveDownSelected(self, menu=None):

        global button_pressed_counter
        button_pressed_counter = button_pressed_counter + 1

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be stopped
        # until job is done!
        if checking_flag != 2:
            button_pressed_thread = ButtonPressedThread()
            self.threadPool.append(button_pressed_thread)
            self.threadPool[-1].start()

            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.moveDownSelected2)
        else:
            self.moveDownSelected2()

    def moveDownSelected2(self):

        # an old row and new row must be substituted by each other

        # find selected rows
        rows_list = self.userSelectedRows()

        # current_category_tree_text is the name of queue that selected by user
        current_category_tree_text = str(current_category_tree_index.data())

        # get gid_list from data base
        category_dict = self.persepolis_db.searchCategoryInCategoryTable(
            current_category_tree_text)

        gid_list = eval(category_dict['gid_list'])

        rows_list.reverse()

        new_rows_list = []

        # move up selected rows
        for old_row in rows_list:

            new_row = int(old_row) + 1
            if new_row < self.download_table.rowCount():
                new_rows_list.append(new_row)

                # old index and new index in gid_list
                old_index = len(gid_list) - old_row - 1
                new_index = old_index - 1

                # subtitute gids in gid_list
                gid_list[old_index], gid_list[new_index] = gid_list[new_index], gid_list[old_index]

                # subtitute items in download_table
                old_row_items_list = []
                new_row_items_list = []

                # read current items in download_table
                for i in range(13):
                    old_row_items_list.append(
                        self.download_table.item(old_row, i).text())

                    new_row_items_list.append(
                        self.download_table.item(new_row, i).text())

                # substituting
                for i in range(13):
                    # old row
                    item = QTableWidgetItem(new_row_items_list[i])

                    self.download_table.setItem(old_row, i, item)

                    # new_row
                    item = QTableWidgetItem(old_row_items_list[i])

                    self.download_table.setItem(new_row, i, item)

        # remove highlight from old rows
        self.download_table.clearSelection()

        # Visit this link for more information
        # doc.qt.io/qt-5/qabstractitemview.html
        self.download_table.setSelectionMode(QAbstractItemView.MultiSelection)

        # Highlight newer rows
        for row in new_rows_list:
            self.download_table.selectRow(row)

        # change selection mode to the normal situation
        self.download_table.setSelectionMode(QAbstractItemView.ExtendedSelection)

        # update data base
        self.persepolis_db.updateCategoryTable([category_dict])

    # this method is called if user pressed moveSelectedDownloads action
    # this method moves download files to another destination.
    def moveSelectedDownloads(self, menu=None):

        # initialize the path.
        initializing_path = self.persepolis_setting.value(
            'MainWindow/moving_path', None)

        # if initializing_path is not available, so use default download_path.
        if not(initializing_path):
            initializing_path = str(self.persepolis_setting.value('settings/download_path'))

        # open file manager and get new download path
        fname = QFileDialog.getExistingDirectory(
            self, 'Select a directory', initializing_path)

        if fname:
            # Returns pathName with the '/' separators converted to
            # separators that are appropriate for the underlying operating system.
            # On Windows, toNativeSeparators("c:/winnt/system32") returns
            # "c:\winnt\system32".
            new_folder_path = QDir.toNativeSeparators(fname)

            # save new_folder_path as initializing_path
            self.persepolis_setting.setValue(
                'MainWindow/moving_path', new_folder_path)

        else:
            return

        gid_list = []
        # find selected rows!
        for row in self.userSelectedRows():
            # get download status
            status = self.download_table.item(row, 1).text()

            # find category
            # category = self.download_table.item(row, 12).text()

            # only download items with "complete" can be moved.
            if (status == 'complete'):
                # find gid
                gid = self.download_table.item(row, 8).text()
                # add gid to gid_list
                gid_list.append(gid)

            else:
                # find filename
                file_name = self.download_table.item(row, 0).text()

                # show error message
                #TODO: no value for message2
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr",
                           message1='Operation was not successful! Following download must be completed first: '),
                           message2=file_name, time=5000, sound='fail', parent=self)

        # move files with MoveThread
        # MoveThread is created to pervent UI freezing.
        move_thread = MoveThread(self, gid_list, new_folder_path)
        self.threadPool.append(move_thread)
        self.threadPool[-1].start()

    # see browser_plugin_queue.py file

    def queueSpiderCallBack(self, filename, child, row_number):
        item = QTableWidgetItem(str(filename))

        # add checkbox to the item
        item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        if child.links_table.item(int(row_number), 0).checkState() == 2:
            item.setCheckState(Qt.Checked)
        else:
            item.setCheckState(Qt.Unchecked)

        child.links_table.setItem(int(row_number), 0, item)

    # see addlink.py file
    def addLinkSpiderCallBack(self, spider_dict, child):
        # get file_name and file_size
        file_name = spider_dict['file_name']
        file_size = spider_dict['file_size']

        if file_size:
            file_size = 'Size: ' + str(file_size)
            child.size_label.setText(file_size)

        if file_name and not(child.change_name_checkBox.isChecked()):
            child.change_name_lineEdit.setText(file_name)
            child.change_name_checkBox.setChecked(True)

    def spiderUpdate(self, dict):
        gid = dict['gid']
        row = None
        for i in range(self.download_table.rowCount()):
            row_gid = self.download_table.item(i, 8).text()
            if gid == row_gid:
                row = i
                break

        # update download_table items
        if row is not None:
            update_list = [dict['file_name'], dict['status'], dict['size'],
                           dict['downloaded_size'], dict['percent'],
                           dict['connections'], dict['rate'], dict['estimate_time_left'],
                           dict['gid'], None, None, None, None]
            for i in range(12):

                # update download_table cell if update_list item in not None
                if update_list[i]:
                    text = update_list[i]
                else:
                    text = self.download_table.item(row, i).text()

                # create a QTableWidgetItem
                item = QTableWidgetItem(text)

                # set item
                try:
                    self.download_table.setItem(row, i, item)
                except Exception as problem:
                    logger.sendToLog(
                        "Error occurred while updating download table", "INFO")
                    logger.sendToLog(problem, "ERROR")

    # this method deletes all items in data base
    def clearDownloadList(self, item):

        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(self.clearDownloadList2)
        else:
            self.clearDownloadList2()

    def clearDownloadList2(self):
        # all Downloads must be stopped by user
        gid_list = self.persepolis_db.findActiveDownloads()

        if len(gid_list) != 0:
            error_messageBox = QMessageBox()
            error_messageBox.setText(
                QCoreApplication.translate("mainwindow_src_ui_tr", 'Stop all downloads first!'))
            error_messageBox.setWindowTitle('Error!')
            error_messageBox.exec_()
            return

        # reset data base
        self.persepolis_db.resetDataBase()
        self.temp_db.resetDataBase()

        # highlight "All Downloads" in category_tree
        all_download_index = self.category_tree_model.index(0, 0)
        self.category_tree.setCurrentIndex(all_download_index)
        self.categoryTreeSelected(all_download_index)

        # clear download_table
        self.download_table.setRowCount(0)

        # tell the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0

    def showVideoFinderAddLinkWindow(self, input_dict=None, menu=None):

        # first check youtube_dl_is_installed and ffmpeg_is_installed value!
        # if youtube_dl or ffmpeg is not installed show an error message.
        if youtube_dl_is_installed and ffmpeg_is_installed:
            if not(input_dict):
                input_dict = {}
            video_finder_addlink_window = VideoFinderAddLink(
                parent=self, receiver_slot=self.videoFinderCallBack,
                settings=self.persepolis_setting, video_dict=input_dict)
            self.addlinkwindows_list.append(video_finder_addlink_window)
            video_finder_addlink_window.show()
            video_finder_addlink_window.raise_()
            video_finder_addlink_window.activateWindow()
        else:
            error_message = ''

            if not(youtube_dl_is_installed):
                error_message = QCoreApplication.translate("mainwindow_src_ui_tr", 'youtube-dl is not installed!')
                error_message = error_message + '\n'

            if not(ffmpeg_is_installed):
                error_message = error_message + \
                    QCoreApplication.translate("mainwindow_src_ui_tr", 'ffmpeg is not installed!')

            error_messageBox = QMessageBox()
            error_messageBox.setText(error_message)
            error_messageBox.setWindowTitle('Error!')
            error_messageBox.exec_()
            return

    # call back of VideoFinderAddLink window.
    def videoFinderCallBack(self, add_link_dictionary_list, download_later, category):

        # if we have only one link so we can download it like other ordinary links
        # but if we have seperated video and audio, then we must use VideoFinder thread and ...
        if len(add_link_dictionary_list) == 1:
            self.callBack(add_link_dictionary_list[0], download_later, category)
            return

        category = str(category)

        for add_link_dictionary in add_link_dictionary_list:

            # aria2 identifies each download by the ID called GID. The GID must be
            # hex string of 16 characters.
            # if user presses ok button on add link window , a gid generates for download.
            gid = self.gidGenerator()

            # add gid to add_link_dictionary
            add_link_dictionary['gid'] = gid

            # download_info_file_list is a list that contains ['file_name' ,
            # 'status' , 'size' , 'downloaded size' ,'download percentage' ,
            # 'number of connections' ,'Transfer rate' , 'estimate_time_left' ,
            # 'gid' , 'link' , 'first_try_date' , 'last_try_date', 'category']

            # if user or browser_plugin defined filename then file_name is valid in
            # add_link_dictionary['out']
            if add_link_dictionary['out']:
                file_name = add_link_dictionary['out']
            else:
                file_name = '***'

            # If user selected a queue in add_link window , then download must be
            # added to queue and and download must be started with queue so >>
            # download_later = True
            if str(category) != 'Single Downloads':
                download_later = True

            # change video status to waiting
            if not(download_later) and gid == add_link_dictionary_list[0]['gid']:
                status = 'waiting'
            else:
                status = 'stopped'

            # get now time and date
            date = download.nowDate()

            dictionary = {'file_name': file_name,
                          'status': status,
                          'size': '***',
                          'downloaded_size': '***',
                          'percent': '***',
                          'connections': '***',
                          'rate': '***',
                          'estimate_time_left': '***',
                          'gid': gid,
                          'link': add_link_dictionary['link'],
                          'first_try_date': date,
                          'last_try_date': date,
                          'category': category}

            # write information in data_base
            self.persepolis_db.insertInDownloadTable([dictionary])
            self.persepolis_db.insertInAddLinkTable([add_link_dictionary])

            # find selected category in left side panel
            for i in range(self.category_tree_model.rowCount()):
                category_tree_item_text = str(
                    self.category_tree_model.index(i, 0).data())
                if category_tree_item_text == category:
                    category_index = i
                    break

            # highlight selected category in category_tree
            category_tree_model_index = self.category_tree_model.index(
                category_index, 0)

            current_category_tree_text = current_category_tree_index.data()
            self.category_tree.setCurrentIndex(category_tree_model_index)

            if current_category_tree_text != category:
                self.categoryTreeSelected(category_tree_model_index)
            else:
                # create a row in download_table for new download
                list = [file_name, status, '***', '***', '***',
                        '***', '***', '***', gid, add_link_dictionary['link'], date, date, category]
                self.download_table.insertRow(0)
                j = 0
                # add item in list to the row
                for i in list:
                    item = QTableWidgetItem(i)
                    self.download_table.setItem(0, j, item)
                    j = j + 1

        # add video_gid and audio_gid to data base
        dictionary = {'video_gid': add_link_dictionary_list[0]['gid'],
                      'audio_gid': add_link_dictionary_list[1]['gid'],
                      'video_completed': 'no',
                      'audio_completed': 'no',
                      'muxing_status': 'no',
                      'checking': 'no',
                      'download_path': add_link_dictionary_list[0]['download_path']}

        self.persepolis_db.insertInVideoFinderTable([dictionary])

        # add video_gid and audio_gid to all_video_finder_gid_list
        self.all_video_finder_gid_list.append(dictionary['video_gid'])
        self.all_video_finder_video_gid_list.append(dictionary['video_gid'])

        self.all_video_finder_gid_list.append(dictionary['audio_gid'])
        self.all_video_finder_audio_gid_list.append(dictionary['audio_gid'])

        # if user didn't press download_later_pushButton in add_link window
        # then create new qthread for new download!
        if not(download_later):
            new_download = VideoFinder(dictionary, self)
            self.threadPool.append(new_download)
            self.threadPool[-1].start()
            self.threadPool[-1].VIDEOFINDERCOMPLETED.connect(self.videoFinderCompleted)

            # add thread to video_finder_threads_dict
            self.video_finder_threads_dict[dictionary['video_gid']] = new_download

            # open progress window for download.
            self.progressBarOpen(dictionary['video_gid'])

            # notify user
            if not(add_link_dictionary_list[0]['start_time']):
                message = QCoreApplication.translate("mainwindow_src_ui_tr", "Download Starts")  # noqa: F841
            else:
                # write name and size of download files in download's table
                for add_link_dictionary in add_link_dictionary_list:
                    new_spider = SpiderThread(add_link_dictionary, self)
                    self.threadPool.append(new_spider)
                    self.threadPool[-1].start()
                    self.threadPool[-1].SPIDERSIGNAL.connect(self.spiderUpdate)

        else:
                # write name and size of download files in download's table
            for add_link_dictionary in add_link_dictionary_list:
                new_spider = SpiderThread(add_link_dictionary, self)
                self.threadPool.append(new_spider)
                self.threadPool[-1].start()
                self.threadPool[-1].SPIDERSIGNAL.connect(self.spiderUpdate)

    # this method is called by VideoFinder thread
    # this method handles error_message
    # if video finder done it's job successfully,
    # then this method shows AfterDownloadWindow
    def videoFinderCompleted(self, complete_dictionary):
        # if checking_flag is equal to 1, it means that user pressed remove or
        # delete button or ... . so checking download information must be
        # stopped until job is done!
        if checking_flag != 2:
            wait_check = WaitThread()
            self.threadPool.append(wait_check)
            self.threadPool[-1].start()
            self.threadPool[-1].QTABLEREADY.connect(
                partial(self.videoFinderCompleted2, complete_dictionary))
        else:
            self.videoFinderCompleted2(complete_dictionary)

    def videoFinderCompleted2(self, complete_dictionary):

        # remove item from video_finder_threads_dict
        del self.video_finder_threads_dict[complete_dictionary['video_gid']]

        error_message = complete_dictionary['error']

        # close progress window
        if complete_dictionary['video_gid'] in self.progress_window_list_dict.keys():

            # find progress_window for this gid
            member_number = self.progress_window_list_dict[complete_dictionary['video_gid']]
            progress_window = self.progress_window_list[member_number]

            # close progress window
            progress_window.close()

        # download was successful
        if error_message == 'no error':

            # delete gids from all_video_finder_gid_list
            self.all_video_finder_gid_list.remove(complete_dictionary['video_gid'])
            self.all_video_finder_video_gid_list.remove(complete_dictionary['video_gid'])

            self.all_video_finder_gid_list.remove(complete_dictionary['audio_gid'])
            self.all_video_finder_audio_gid_list.remove(complete_dictionary['audio_gid'])

            # delete audio file
            # find row
            row = None
            for i in range(self.download_table.rowCount()):
                row_gid = self.download_table.item(i, 8).text()
                if complete_dictionary['audio_gid'] == row_gid:
                    row = i
                    break

            # muxing is complete
            # so remove unused files
            # find download path
            audio_add_link_dictionary = self.persepolis_db.searchGidInAddLinkTable(complete_dictionary['audio_gid'])
            video_add_link_dictionary = self.persepolis_db.searchGidInAddLinkTable(complete_dictionary['video_gid'])

            audio_file_path = audio_add_link_dictionary['download_path']
            video_file_path = video_add_link_dictionary['download_path']

            remove_answer = osCommands.remove(audio_file_path)
            remove_answer = osCommands.remove(video_file_path)  # noqa: F841

            # remove audio row from download_table
            if row is not None:
                self.download_table.removeRow(row)

            # remove download item from data base
            self.persepolis_db.deleteItemInDownloadTable(
                complete_dictionary['audio_gid'], complete_dictionary['category'])

            # file name and file size and downloaded size and download path must be changed for video
            video_add_link_dictionary['download_path'] = complete_dictionary['final_path']

            # update data base
            self.persepolis_db.updateAddLinkTable([video_add_link_dictionary])

            # get download_table_dict for video_gid
            video_download_table_dict = self.persepolis_db.searchGidInDownloadTable(complete_dictionary['video_gid'])

            video_download_table_dict['size'] = complete_dictionary['final_size']
            video_download_table_dict['downloaded_size'] = complete_dictionary['final_size']
            video_download_table_dict['file_name'] = urllib.parse.unquote(
                os.path.basename(complete_dictionary['final_path']))

            # update data base
            self.persepolis_db.updateDownloadTable([video_download_table_dict])

            # update download_table
            # find row
            row = None
            for i in range(self.download_table.rowCount()):
                row_gid = self.download_table.item(i, 8).text()
                if complete_dictionary['video_gid'] == row_gid:
                    row = i
                    break

            if row is not None:
                # create a QTableWidgetItem
                item = QTableWidgetItem(str(video_download_table_dict['file_name']))

                # set item
                self.download_table.setItem(row, 0, item)

                # create a QTableWidgetItem
                item = QTableWidgetItem(str(video_download_table_dict['size']))

                # set item
                self.download_table.setItem(row, 2, item)

                # create a QTableWidgetItem
                item = QTableWidgetItem(str(video_download_table_dict['downloaded_size']))

                # set item
                self.download_table.setItem(row, 3, item)

            # update download_table (refreshing!)
            self.download_table.viewport().update()

            if complete_dictionary['category'] == 'Single Downloads':
                # show download complete dialog
                afterdownloadwindow = AfterDownloadWindow(
                    self, video_download_table_dict, self.persepolis_setting)

                self.afterdownload_list.append(afterdownloadwindow)

                self.afterdownload_list[-1].show()

                # bringing AfterDownloadWindow on top
                self.afterdownload_list[-1].raise_()

                self.afterdownload_list[-1].activateWindow()

        elif error_message == 'not enough free space':
            # show error message
            notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Not enough free space in:"),
                       complete_dictionary['download_path'],
                       10000, 'fail', parent=self)

        elif error_message == 'ffmpeg error':
            # show error message
            notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "an error occurred"),
                       QCoreApplication.translate("mainwindow_src_ui_tr", "muxing error"),
                       10000, 'fail', parent=self)

        # telling the CheckDownloadInfoThread that job is done!
        global checking_flag
        checking_flag = 0


# this method is called, if user clicks on muxing_pushButton
    def muxingPushButtonPressed(self, button):

        # find user's selected row
        selected_row_return = self.selectedRow()

        if selected_row_return is not None:

            # find download category
            category = self.download_table.item(selected_row_return, 12).text()

            # if category is not "single downloads" , then send notification for error
            if category != "Single Downloads":
                notifySend(QCoreApplication.translate("mainwindow_src_ui_tr", "Operation was not successful."),
                           QCoreApplication.translate("mainwindow_src_ui_tr",
                                                      "Please resume the following category: ") + category,
                           10000, 'fail', parent=self)
                return

            # find download gid
            gid = self.download_table.item(selected_row_return, 8).text()
            # download_status = self.download_table.item(selected_row_return, 1).text()

            # read data from data base
            result_dictionary = self.persepolis_db.searchGidInVideoFinderTable(gid)

            # create new thread for this download
            # see VideoFinder thread for more information
            new_download = VideoFinder(result_dictionary, self)
            self.threadPool.append(new_download)
            self.threadPool[-1].start()
            self.threadPool[-1].VIDEOFINDERCOMPLETED.connect(self.videoFinderCompleted)

            # add thread to video_finder_threads_dict
            self.video_finder_threads_dict[result_dictionary['video_gid']] = new_download

            # create new progress_window
            self.progressBarOpen(gid)

    def changeIcon(self, new_icons):

        global icons
        icons = ':/' + str(new_icons) + '/'

        action_icon_dict = {self.stopAllAction: 'stop_all', self.minimizeAction: 'minimize',
                            self.addlinkAction: 'add', self.addtextfileAction: 'file',
                            self.resumeAction: 'play', self.pauseAction: 'pause', self.stopAction: 'stop',
                            self.propertiesAction: 'setting', self.progressAction: 'window',
                            self.openFileAction: 'file', self.openDownloadFolderAction: 'folder',
                            self.openDefaultDownloadFolderAction: 'folder', self.exitAction: 'exit',
                            self.createQueueAction: 'add_queue', self.removeQueueAction: 'remove_queue',
                            self.startQueueAction: 'start_queue', self.stopQueueAction: 'stop_queue',
                            self.preferencesAction: 'preferences', self.aboutAction: 'about',
                            self.issueAction: 'about', self.updateAction: 'about',
                            self.videoFinderAddLinkAction: 'video_finder', self.qmenu: 'menu'}

        for key in action_icon_dict.keys():
            key.setIcon(QIcon(icons + str(action_icon_dict[key])))

        self.selectDownloads()

persepolis/scripts/mainwindow.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import ast


# This function is writting a list in file_path in dictionary format
def writeList(file_path, list):
    dictionary = {'list': list}
    f = open(file_path, 'w')
    f.writelines(str(dictionary))
    f.close()

# This function is reading file_path and return content of file in list format


def readList(file_path, mode='dictionary'):
    f = open(file_path, 'r')
    f_string = f.readline()
    f.close()
    dictionary = ast.literal_eval(f_string.strip())
    list = dictionary['list']

    if mode == 'string':
        list[9] = str(list[9])

    return list

# this function is reading a file that contains dictionary , and extracts
# dictionary from it.


def readDict(file_path):
    f = open(file_path)
    f_lines = f.readlines()
    f.close()
    dict_str = str(f_lines[0].strip())
    return_dict = ast.literal_eval(dict_str)
    return return_dict

persepolis/scripts/newopen.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.


from persepolis.constants import OS
import subprocess
import platform
import shutil
import os

os_type = platform.system()
home_address = os.path.expanduser("~")


# this method finds file manager in linux
def findFileManager():
    pipe = subprocess.check_output(['xdg-mime',
                                    'query',
                                    'default',
                                    'inode/directory'])
    file_manager = pipe.decode('utf-8').strip().lower()

    return file_manager


def touch(file_path):
    if not(os.path.isfile(file_path)):
        f = open(file_path, 'w')
        f.close()

# xdgOpen opens files or folders
def xdgOpen(file_path, f_type='file', path='file'):

    # we have a file path and we want to open it's directory.
    # highlit(select) file in file manager after opening.
    # it's help to find file easier :)
    if f_type == 'folder' and path == 'file':
        highlight = True
    else:
        highlight = False

    # for linux and bsd
    if os_type in OS.UNIX_LIKE:

        file_manager = findFileManager()
        # check default file manager.
        # some file managers wouldn't support highlighting.
        if highlight:

            # dolphin is kde plasma's file manager
            if 'dolphin' in file_manager:

                subprocess.Popen(['dolphin',
                                  '--select', file_path],
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stdin=subprocess.PIPE,
                                 shell=False)

            # dde-file-manager is deepin's file manager
            elif 'dde-file-manager' in file_manager:

                subprocess.Popen(['dde-file-manager',
                                  '--show-item', file_path],
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stdin=subprocess.PIPE,
                                 shell=False)

            # if file manager is nautilus or nemo or pantheon-file-manager
            elif file_manager in ['org.gnome.nautilus.desktop', 'nemo.desktop', 'io.elementary.files.desktop']:

                # nautilus is gnome's file manager.
                if 'nautilus' in file_manager:
                    file_manager = 'nautilus'

                # pantheon-files is pantheon's file manager(elementary OS).
                elif 'elementary' in file_manager:
                    file_manager = 'io.elementary.files'

                # nemo is cinnamon's file manager.
                elif 'nemo' in file_manager:
                    file_manager = 'nemo'

                subprocess.Popen([file_manager,
                                  file_path],
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stdin=subprocess.PIPE,
                                 shell=False)

            else:
                # find folder path
                file_name = os.path.basename(str(file_path))

                file_path_split = file_path.split(file_name)

                del file_path_split[-1]

                folder_path = file_name.join(file_path_split)

                subprocess.Popen(['xdg-open', folder_path],
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stdin=subprocess.PIPE,
                                 shell=False)

        else:

            subprocess.Popen(['xdg-open', file_path],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False)

    # for Mac OS X
    elif os_type == OS.OSX:
        if highlight:

            subprocess.Popen(['open', '-R', file_path],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False)

        else:

            subprocess.Popen(['open', file_path],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False)

    # for MS Windows
    elif os_type == OS.WINDOWS:
        CREATE_NO_WINDOW = 0x08000000

        if highlight:
            subprocess.Popen(['explorer.exe', '/select,',  file_path],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False,
                             creationflags=CREATE_NO_WINDOW)

        else:

            subprocess.Popen(['cmd', '/C', 'start', file_path,  file_path],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False,
                             creationflags=CREATE_NO_WINDOW)

# remove file with path of file_path
def remove(file_path): 
    if os.path.isfile(file_path):
        try:
            # function returns  ok, if operation was successful
            os.remove(file_path)
            return 'ok'

        except OSError:
            # function returns this, if operation was not successful
            return 'cant'

    else:
        # function returns this , if file is not existed
        return 'no'

# removeDir removes folder : folder_path
def removeDir(folder_path):

    # check folder_path existence
    if os.path.isdir(folder_path):
        try:
            # remove folder
            shutil.rmtree(folder_path)
            return 'ok'

        except OSError:
            # return 'cant' if removing was not successful
            return 'cant'
    else:
        # return 'no' if file didn't existed
        return 'no'

# make directory 
def makeDirs(folder_path, hidden=False):

    if hidden:

        # create hidden attribute directory.
        if os_type == OS.WINDOWS:

            os.makedirs(folder_path, exist_ok=True)

            # in MS Windows "attrib +h" command hidden directory.
            CREATE_NO_WINDOW = 0x08000000
            subprocess.Popen(['attrib', '+h', folder_path],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False,
                             creationflags=CREATE_NO_WINDOW)
        else:

            # In linux and bsd a dot character must be added in the start of the directory's name
            dir_name = os.path.basename(folder_path)
            dir_name = '.' + dir_name 
            folder_path = os.path.join(os.path.dirname(folder_path), dir_name)

            os.makedirs(folder_path, exist_ok=True)

    else:
        os.makedirs(folder_path, exist_ok=True)

    return folder_path
    
# this function returns mount point
def findMountPoint(path):

    while not os.path.ismount(path):
        path = os.path.dirname(path)

    return path

# this function creates temporary download folder in mount point of given path
def makeTempDownloadDir(path):

    # if path and home_address are in the same partition,
    # create temp folder in default settings address.
    if os.lstat(path).st_dev == os.lstat(home_address):

        if os_type != OS.WINDOWS:
            download_path_temp = os.path.join(home_address, '.persepolis')
        else:
            download_path_temp = os.path.join(
                home_address, 'AppData', 'Local', 'persepolis')

        # create directory
        download_path_temp = makeDirs(download_path_temp)

    else:
        # Find mount point and create temp folder there!
        mount_point = findMountPoint(path)
        download_path_temp = os.path.join(mount_point, 'persepolis')

        # Create folder and give new temp address from makeDirs function.
        # Please checkout osCommands.py for more information.
        download_path_temp = makeDirs(download_path_temp, hidden=True)


    return download_path_temp

# move downloaded file to another destination.
def moveFile(old_file_path, new_path, new_path_type='folder'):
    # new_path_type can be file or folder
    # if it's folder so we have folder path
    # else we have new file path that includes file name
    if os.path.isfile(old_file_path):
        if new_path_type == 'folder':
            # check availability of directory
            check_path = os.path.isdir(new_path)
        else:
            check_path = True
        if check_path:
            try:
                # move file to new_path
                shutil.move(old_file_path, new_path)
                return True
            except OSError:
                return False
        else:
            return False
    else:
        return False

persepolis/scripts/osCommands.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtGui import QFont
    from PySide6.QtWidgets import QApplication, QStyleFactory
    from PySide6.QtCore import QFile, QTextStream, QCoreApplication, QSettings, Qt
except ImportError:
    from PyQt5.QtGui import QFont
    from PyQt5.QtWidgets import QApplication, QStyleFactory
    from PyQt5.QtCore import QFile, QTextStream, QCoreApplication, QSettings, Qt

from persepolis.gui import resources # noqa: F401
import traceback
from persepolis.scripts.error_window import ErrorWindow
from persepolis.gui.palettes import DarkFusionPalette, LightFusionPalette
import json
import struct
import argparse
from persepolis.scripts import osCommands
from ghermez import osAndDesktopEnvironment, determineConfigFolder
from persepolis.constants import OS, APP_NAME, LONG_NAME, ORG_NAME, VERSION
from copy import deepcopy
import sys
import os

# finding os platform
os_type, desktop_env = osAndDesktopEnvironment()

# Don't run persepolis as root!
if os_type in (OS.UNIX_LIKE + [OS.OSX]):
    uid = os.getuid()
    if uid == 0:
        print('Do not run persepolis as root.')
        sys.exit(1)


# initialization

# persepolis config_folder
config_folder = determineConfigFolder()

# persepolis tmp folder path
persepolis_tmp = os.path.join(config_folder, 'persepolis_tmp')


# if lock_file_validation == True >> not another instance running,
# else >> another instance of persepolis is running now.
global lock_file_validation

if os_type != OS.WINDOWS:
    import fcntl

# persepolis lock file
    lock_file = '/tmp/persepolis_exec_' + os.getlogin() + '.lock'

# create lock file
    fp = open(lock_file, 'w')

    try:
        fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)

        lock_file_validation = True  # Lock file created successfully!
    except IOError:
        lock_file_validation = False  # creating lock_file was unsuccessful! So persepolis is still running

else:  # for windows
    # pypiwin32 must be installed by pip
    from win32event import CreateMutex
    from win32api import GetLastError
    from winerror import ERROR_ALREADY_EXISTS

    handle = CreateMutex(None, 1, ORG_NAME)

    if GetLastError() == ERROR_ALREADY_EXISTS:
        lock_file_validation = False
    else:
        lock_file_validation = True

# run persepolis mainwindow
if lock_file_validation:
    from persepolis.scripts import initialization  # noqa: F401
    from persepolis.scripts.mainwindow import MainWindow

# set "persepolis" name for this process in linux and bsd
    if os_type in OS.UNIX_LIKE:
        try:
            from setproctitle import setproctitle
            setproctitle(APP_NAME)
        except ImportError:
            from persepolis.scripts import logger
            logger.sendToLog('setproctitle is not installed!', "ERROR")


# load persepolis_settings
persepolis_setting = QSettings(ORG_NAME, APP_NAME)


class PersepolisApplication(QApplication):
    def __init__(self, argv):
        super().__init__(argv)

    def setPersepolisStyle(self, style):
        # set style
        self.persepolis_style = style
        self.setStyle(style)

    def setPersepolisFont(self, font, font_size, custom_font):
        # font and font_size
        self.persepolis_font = font
        self.persepolis_font_size = font_size

        if custom_font == 'yes':
            self.setFont(QFont(font, font_size))
# color_scheme

    def setPersepolisColorScheme(self, color_scheme):
        self.persepolis_color_scheme = color_scheme
        if color_scheme == 'Dark Fusion':
            dark_fusion = DarkFusionPalette()
            self.setPalette(dark_fusion)
            file = QFile(":/dark_style.qss")
            file.open(QFile.ReadOnly | QFile.Text)
            stream = QTextStream(file)
            self.setStyleSheet(stream.readAll())

        elif color_scheme == 'Light Fusion':
            light_fusion = LightFusionPalette()
            self.setPalette(light_fusion)
            file = QFile(":/light_style.qss")
            file.open(QFile.ReadOnly | QFile.Text)
            stream = QTextStream(file)
            self.setStyleSheet(stream.readAll())


# create  terminal arguments
parser = argparse.ArgumentParser(description=LONG_NAME)
# parser.add_argument('chromium', nargs = '?', default = 'no',
#                     help='this switch is used for chrome native messaging in Linux and Mac')
parser.add_argument('--link', action='store', nargs=1, help='Download link.(Use "" for links)')
parser.add_argument('--referer', action='store', nargs=1,
                    help='Set an http referrer (Referer). This affects all http/https downloads. \
                    If * is given, the download URI is also used as the referrer.')
parser.add_argument('--cookie', action='store', nargs=1, help='Cookie')
parser.add_argument('--agent', action='store', nargs=1,
                    help='Set user agent for HTTP(S) downloads. \
                        Default: aria2/$VERSION, $VERSION is replaced by package version.')
parser.add_argument('--headers', action='store', nargs=1, help='Append HEADER to HTTP request header. ')
parser.add_argument('--name', action='store', nargs=1, help='The  file  name  of  the downloaded file. ')
parser.add_argument('--default', action='store_true', help='restore default setting')
parser.add_argument('--clear', action='store_true', help='Clear download list and user setting!')
parser.add_argument('--tray', action='store_true',
                    help="Persepolis is starting in tray icon. \
                        It's useful when you want to put persepolis in system's startup.")
parser.add_argument('--parent-window', action='store', nargs=1,
                    help='this switch is used for chrome native messaging in Windows')
parser.add_argument('--version', action='version', version=f'{LONG_NAME} {VERSION}')


# Clears unwanted args ( like args from Browers via NHM )
# unknown arguments (may sent by browser) will save in unknownargs.
args, unknownargs = parser.parse_known_args()

# if --execute >> yes  >>> persepolis main window  will start.
# if --execute >> no >>> persepolis started before!
browser_url = True

add_link_dictionary = {}
plugin_list = []
browser_plugin_dict = {'link': None,
                       'referer': None,
                       'load_cookies': None,
                       'user_agent': None,
                       'header': None,
                       'out': None
                       }


# This dirty trick will show Persepolis version when there are unknown args
# Unknown args are sent by Browsers for NHM
if args.parent_window or unknownargs:

    # Platform specific configuration
    if os_type == OS.WINDOWS:
        # Set the default I/O mode to O_BINARY in windows
        import msvcrt
        msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

    # Send message to browsers plugin
    message = '{"enable": true, "version": "1.85"}'.encode('utf-8')
    sys.stdout.buffer.write((struct.pack('i', len(message))))
    sys.stdout.buffer.write(message)
    sys.stdout.flush()

    text_length_bytes = sys.stdin.buffer.read(4)

    # Unpack message length as 4 byte integer.
    text_length = struct.unpack('@I', text_length_bytes)[0]

    # Read the text (JSON object) of the message.
    text = sys.stdin.buffer.read(text_length).decode("utf-8")

    if text:

        new_dict = json.loads(text)

        if 'url_links' in new_dict:

            # new_dict is sended by persepolis browser add-on.
            # new_dict['url_links'] contains some lists.
            # every list contains link information.
            for item in new_dict['url_links']:

                copy_dict = deepcopy(browser_plugin_dict)

                if 'url' in item.keys():
                    copy_dict['link'] = str(item['url'])

                    if 'header' in item.keys() and item['header'] != '':
                        copy_dict['header'] = item['header']

                    if 'referrer' in item.keys() and item['referrer'] != '':
                        copy_dict['referer'] = item['referrer']

                    if 'filename' in item.keys() and item['filename'] != '':
                        copy_dict['out'] = os.path.basename(str(item['filename']))

                    if 'useragent' in item.keys() and item['useragent'] != '':
                        copy_dict['user_agent'] = item['useragent']

                    if 'cookies' in item.keys() and item['cookies'] != '':
                        copy_dict['load_cookies'] = item['cookies']

                    plugin_list.append(copy_dict)

        else:
            browser_url = False


# persepolis --clear >> remove config_folder
if args.clear:
    from ghermez import DataBase

    # create an object for DataBase
    persepolis_db = DataBase()

    # Reset data base
    persepolis_db.resetDataBase()

    # close connections
    # persepolis_db.closeConnections()
    del persepolis_db

    # Reset persepolis_setting
    persepolis_setting.clear()
    persepolis_setting.sync()

    sys.exit(0)

# persepolis --default >> remove persepolis setting.
if args.default:
    persepolis_setting.clear()
    persepolis_setting.sync()
    print ('Persepolis restored default')
    sys.exit(0)


if args.link:
    add_link_dictionary['link'] = "".join(args.link)

# if plugins call persepolis, then just start persepolis in system tray
    args.tray = True

if args.referer:
    add_link_dictionary['referer'] = "".join(args.referer)
else:
    add_link_dictionary['referer'] = None

if args.cookie:
    add_link_dictionary['load_cookies'] = "".join(args.cookie)
else:
    add_link_dictionary['load_cookies'] = None

if args.agent:
    add_link_dictionary['user_agent'] = "".join(args.agent)
else:
    add_link_dictionary['user_agent'] = None

if args.headers:
    add_link_dictionary['header'] = "".join(args.headers)
else:
    add_link_dictionary['header'] = None

if args.name:
    add_link_dictionary['out'] = "".join(args.name)
else:
    add_link_dictionary['out'] = None

if args.tray:
    start_in_tray = True
else:
    start_in_tray = False


# when browsers plugin calls persepolis or user runs persepolis by terminal arguments,
# then persepolis creates a request file in persepolis_tmp folder and link information added to
# plugins_db.db file(see data_base.py for more information).
# persepolis mainwindow checks persepolis_tmp for
# plugins request file every 2 seconds (see CheckingThread class in mainwindow.py)
# when request received in CheckingThread, a popup window (AddLinkWindow)
# comes up and window gets additional download information
# from user (port , proxy , ...) and download starts and request file deleted

if ('link' in add_link_dictionary.keys()):
    plugin_dict = {'link': add_link_dictionary['link'],
                   'referer': add_link_dictionary['referer'],
                   'load_cookies': add_link_dictionary['load_cookies'],
                   'user_agent': add_link_dictionary['user_agent'],
                   'header': add_link_dictionary['header'],
                   'out': add_link_dictionary['out']
                   }

    plugin_list.append(plugin_dict)

if len(plugin_list) != 0:

    # import PluginsDB
    from ghermez import PluginsDB

    # create an object for PluginsDB
    plugins_db = PluginsDB()

    # add plugin_list to plugins_table in plugins.db file.
    plugins_db.insertInPluginsTable(plugin_list)

    # Job is done! close connections.
    # plugins_db.closeConnections()
    del plugins_db

    # notify that a link is added!
    plugin_ready = os.path.join(persepolis_tmp, 'persepolis-plugin-ready')
    osCommands.touch(plugin_ready)

    # start persepolis in system tray
    start_in_tray = True


# start persepolis in system tray if browser executed
# and if user select this option in preferences window.
if str(persepolis_setting.value('settings/browser-persepolis')) == 'yes' and (args.parent_window or unknownargs):
    start_persepolis_if_browser_executed = True
    start_in_tray = True
else:
    start_persepolis_if_browser_executed = False


def main():
    # if lock_file is existed , it means persepolis is still running!
    if lock_file_validation and (not((args.parent_window or unknownargs) and browser_url is False) or \
        ((args.parent_window or unknownargs) and start_persepolis_if_browser_executed)):

        # set QT_AUTO_SCREEN_SCALE_FACTOR to 1 for "high DPI displays"
        os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1'

        # run mainwindow

        # set color_scheme and style
        # see palettes.py and setting.py

        # this line is added fot fixing persepolis view in HighDpi displays
        # more information at: https://doc.qt.io/qt-5/highdpi.html
        try:
            QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
        except AttributeError:
            from persepolis.scripts import logger

            # write error_message in log file.
            logger.sendToLog('Qt.AA_EnableHighDpiScaling is not available!', "ERROR")


        # create QApplication
        persepolis_download_manager = PersepolisApplication(sys.argv)

        # setQuitOnLastWindowClosed(False) is needed to prevent persepolis exiting,
        # when it's minimized in system tray.
        persepolis_download_manager.setQuitOnLastWindowClosed(False)

        # Enable High DPI display
        try:
            if hasattr(QStyleFactory, 'AA_UseHighDpiPixmaps'):
                persepolis_download_manager.setAttribute(Qt.AA_UseHighDpiPixmaps)
        except AttributeError:
            from persepolis.scripts import logger

            # write error_message in log file.
            logger.sendToLog('Qt.AA_UseHighDpiPixmaps is not available!', "ERROR")

        # set organization name and domain and application name
        QCoreApplication.setOrganizationName(ORG_NAME)
        QCoreApplication.setApplicationName(APP_NAME)

        # Persepolis setting
        persepolis_download_manager.setting = QSettings()

        # get user's desired font and style , ... from setting
        custom_font = persepolis_download_manager.setting.value('settings/custom-font')
        font = persepolis_download_manager.setting.value('settings/font')
        font_size = int(persepolis_download_manager.setting.value('settings/font-size'))
        style = persepolis_download_manager.setting.value('settings/style')
        color_scheme = persepolis_download_manager.setting.value('settings/color-scheme')
        ui_direction = persepolis_download_manager.setting.value('ui_direction')

        # set style
        persepolis_download_manager.setPersepolisStyle(style)

        # set font
        persepolis_download_manager.setPersepolisFont(font, font_size, custom_font)

        # set color_scheme
        persepolis_download_manager.setPersepolisColorScheme(color_scheme)

        # set ui direction
        if ui_direction == 'rtl':
            persepolis_download_manager.setLayoutDirection(Qt.RightToLeft)

        elif ui_direction in 'ltr':
            persepolis_download_manager.setLayoutDirection(Qt.LeftToRight)

        # run mainwindow
        try:
            mainwindow = MainWindow(start_in_tray, persepolis_download_manager, persepolis_download_manager.setting)
            if start_in_tray:
                mainwindow.hide()
            else:
                mainwindow.show()

        except Exception:
            from persepolis.scripts import logger
            error_message = str(traceback.format_exc())

            # write error_message in log file.
            logger.sendToLog(error_message, "ERROR")

            # Reset persepolis
            error_window = ErrorWindow(error_message)
            error_window.show()

        sys.exit(persepolis_download_manager.exec_())

    elif not((args.parent_window or unknownargs)):

        # this section warns user that program is still running and no need to run it again
        # and creating a file to notify mainwindow for showing itself!
        # (see CheckingThread in mainwindow.py for more information)
        if len(plugin_list) == 0:

            show_window_file = os.path.join(persepolis_tmp, 'show-window')
            f = open(show_window_file, 'w')
            f.close()

        sys.exit(0)

persepolis/scripts/persepolis.py
IamRezaMousavi/ghermez

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtCore import QSettings
except ImportError:
    from PyQt5.QtCore import QSettings

from persepolis.scripts import logger
from persepolis.constants import OS, ORG_NAME, APP_NAME
import subprocess
import platform

os_type = platform.system()


def playNotification(file):
    # getting user setting from persepolis_setting
    persepolis_setting = QSettings(ORG_NAME, APP_NAME)

    # enabling or disabling notification sound in persepolis_setting
    enable_notification = str(persepolis_setting.value('settings/sound'))

    # volume of notification in persepolis_setting(an integer between 0 to 100)
    volume_percent = int(persepolis_setting.value('settings/sound-volume'))

# Paplay volume value must be between 0 (silent) and 65536 (100% volume)
    volume = int((65536 * volume_percent)/100)

    if enable_notification == 'yes':
        if os_type in OS.UNIX_LIKE:

            pipe = subprocess.Popen(['paplay', '--volume=' + str(volume),
                                     str(file)],
                                    stderr=subprocess.PIPE,
                                    stdout=subprocess.PIPE,
                                    stdin=subprocess.PIPE,
                                    shell=False)

            answer = pipe.wait()

            if answer != 0:
                logger.sendToLog(
                    "paplay not installed!Install it for playing sound notification", "WARNING")

        elif os_type == OS.OSX:

            pipe = subprocess.Popen(['osascript', '-e',
                                     'set', 'volume', 'alert',
                                     'volume', str(volume)],
                                    stderr=subprocess.PIPE,
                                    stdout=subprocess.PIPE,
                                    stdin=subprocess.PIPE,
                                    shell=False)

            pipe = subprocess.Popen(['osascript', '-e',
                                     'beep', '3'],
                                    stderr=subprocess.PIPE,
                                    stdout=subprocess.PIPE,
                                    stdin=subprocess.PIPE,
                                    shell=False)

        elif os_type == OS.WINDOWS:

            CREATE_NO_WINDOW = 0x08000000
            subprocess.Popen(['rundll32', 'user32.dll,MessageBeep'],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False,
                             creationflags=CREATE_NO_WINDOW)

persepolis/scripts/play.py
IamRezaMousavi/ghermez

# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtCore import Qt, QSize, QPoint, QThread, QTranslator, QCoreApplication, QLocale
    from PySide6.QtWidgets import QLineEdit, QInputDialog
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import Qt, QSize, QPoint, QThread, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtWidgets import QLineEdit, QInputDialog
    from PyQt5.QtGui import QIcon

from persepolis.gui.progress_ui import ProgressWindow_Ui
from persepolis.scripts.shutdown import shutDown
from persepolis.scripts.bubble import notifySend
from persepolis.scripts import download
from persepolis.constants import OS
import subprocess
import platform

os_type = platform.system()

class ShutDownThread(QThread):
    def __init__(self, parent, gid, password=None):
        QThread.__init__(self)
        self.gid = gid
        self.password = password
        self.parent = parent

    def run(self):
        shutDown(self.parent, gid=self.gid, password=self.password)


class ProgressWindow(ProgressWindow_Ui):
    def __init__(self, parent, gid, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.parent = parent
        self.gid = gid
        self.status = None
        self.resume_pushButton.clicked.connect(self.resumePushButtonPressed)
        self.stop_pushButton.clicked.connect(self.stopPushButtonPressed)
        self.pause_pushButton.clicked.connect(self.pausePushButtonPressed)
        self.download_progressBar.setValue(0)
        self.limit_pushButton.clicked.connect(self.limitPushButtonPressed)

        self.limit_frame.setEnabled(False)
        self.limit_checkBox.toggled.connect(self.limitCheckBoxToggled)

        self.after_frame.setEnabled(False)
        self.after_checkBox.toggled.connect(self.afterCheckBoxToggled)

        self.after_pushButton.clicked.connect(self.afterPushButtonPressed)

# add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

# check if limit speed activated by user or not
        add_link_dictionary = self.parent.persepolis_db.searchGidInAddLinkTable(gid)

        limit = str(add_link_dictionary['limit_value'])
        if limit != '0':
            limit_number = limit[:-1]
            limit_unit = limit[-1]
            self.limit_spinBox.setValue(float(limit_number))
            if limit_unit == 'K':
                self.after_comboBox.setCurrentIndex(0)
            else:
                self.after_comboBox.setCurrentIndex(1)
            self.limit_checkBox.setChecked(True)

        self.after_comboBox.currentIndexChanged.connect(self.afterComboBoxChanged)

        self.limit_comboBox.currentIndexChanged.connect(self.limitComboBoxChanged)

        self.limit_spinBox.valueChanged.connect(self.limitComboBoxChanged)

  # set window size and position
        size = self.persepolis_setting.value(
            'ProgressWindow/size', QSize(595, 274))
        position = self.persepolis_setting.value(
            'ProgressWindow/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        # save window size and position
        self.persepolis_setting.setValue('ProgressWindow/size', self.size())
        self.persepolis_setting.setValue('ProgressWindow/position', self.pos())
        self.persepolis_setting.sync()

        self.hide()

    def resumePushButtonPressed(self, button):

        if self.status == "paused":
            answer = download.downloadUnpause(self.gid)

            # if aria2 did not respond , then this function is checking for aria2
            # availability , and if aria2 disconnected then aria2Disconnected is
            # executed
            if not(answer):
                version_answer = download.aria2Version()
                if version_answer == 'did not respond':
                    self.parent.aria2Disconnected()
                    notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 disconnected!"),
                               QCoreApplication.translate("progress_src_ui_tr",
                                                          "Persepolis is trying to connect! be patient!"),
                               10000, 'warning', parent=self.parent)
                else:
                    notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 did not respond!"),
                               QCoreApplication.translate("progress_src_ui_tr", "Please try again."), 10000,
                               'warning', parent=self.parent)

    def pausePushButtonPressed(self, button):

        if self.status == "downloading":
            answer = download.downloadPause(self.gid)

            # if aria2 did not respond , then this function is checking for aria2
            # availability , and if aria2 disconnected then aria2Disconnected is
            # executed
            if not(answer):
                version_answer = download.aria2Version()
                if version_answer == 'did not respond':
                    self.parent.aria2Disconnected()
                    download.downloadStop(self.gid, self.parent)
                    notifySend("Aria2 disconnected!", "Persepolis is trying to connect! be patient!",
                               10000, 'warning', parent=self.parent)
                else:
                    notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 did not respond!"),
                               QCoreApplication.translate("progress_src_ui_tr", "Try again!"), 10000,
                               'critical', parent=self.parent)

    def stopPushButtonPressed(self, button):

        dict = {'gid': self.gid,
                'shutdown': 'canceled'}

        self.parent.temp_db.updateSingleTable(dict)

        answer = download.downloadStop(self.gid, self.parent)

        # if aria2 did not respond , then this function is checking for aria2
        # availability , and if aria2 disconnected then aria2Disconnected is
        # executed
        if answer == 'None':
            version_answer = download.aria2Version()
            if version_answer == 'did not respond':
                self.parent.aria2Disconnected()
                notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 disconnected!"),
                           QCoreApplication.translate("progress_src_ui_tr",
                                                      "Persepolis is trying to connect! be patient!"),
                           10000, 'warning', parent=self.parent)

    def limitCheckBoxToggled(self, checkBoxes):

        # user checked limit_checkBox
        if self.limit_checkBox.isChecked():
            self.limit_frame.setEnabled(True)
            self.limit_pushButton.setEnabled(True)

        # user unchecked limit_checkBox
        else:
            self.limit_frame.setEnabled(False)

            # check download status is "scheduled" or not!
            if self.status != 'scheduled':
                # tell aria2 for unlimited speed
                download.limitSpeed(self.gid, "0")
            else:
                # update limit value in data_base
                add_link_dictionary = {'gid': self.gid, 'limit_value': '0'}
                self.parent.persepolis_db.updateAddLinkTable([add_link_dictionary])

    def limitComboBoxChanged(self, connect):
        self.limit_pushButton.setEnabled(True)

    def afterComboBoxChanged(self, connect):
        self.after_pushButton.setEnabled(True)

    def afterCheckBoxToggled(self, checkBoxes):
        if self.after_checkBox.isChecked():
            self.after_frame.setEnabled(True)
        else:
            # so user canceled shutdown after download
            # write cancel value in data_base for this gid
            self.after_frame.setEnabled(False)

            dict = {'gid': self.gid,
                    'shutdown': 'canceled'}

            self.parent.temp_db.updateSingleTable(dict)

    def afterPushButtonPressed(self, button):
        self.after_pushButton.setEnabled(False)

        if os_type != OS.WINDOWS:  # For Linux and Mac OSX and FreeBSD and OpenBSD

            # get root password
            passwd, ok = QInputDialog.getText(
                self, 'PassWord', 'Please enter root password:', QLineEdit.Password)

            if ok:
                # check password is true or not!
                pipe = subprocess.Popen(['sudo', '-S', 'echo', 'hello'],
                                        stdout=subprocess.DEVNULL,
                                        stdin=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL,
                                        shell=False)

                pipe.communicate(passwd.encode())

                answer = pipe.wait()

                # Wrong password
                while answer != 0:

                    passwd, ok = QInputDialog.getText(
                        self, 'PassWord', 'Wrong Password!\nPlease try again.', QLineEdit.Password)

                    if ok:
                        # checking password
                        pipe = subprocess.Popen(['sudo', '-S', 'echo', 'hello'],
                                                stdout=subprocess.DEVNULL,
                                                stdin=subprocess.PIPE,
                                                stderr=subprocess.DEVNULL,
                                                shell=False)

                        pipe.communicate(passwd.encode())

                        answer = pipe.wait()

                    else:
                        ok = False
                        break

                if ok is not False:

                    # if user selects shutdown option after download progress,
                    # value of 'shutdown' will changed in temp_db for this gid
                    # and "wait" word will be written for this value.
                    # (see ShutDownThread and shutdown.py for more information)
                    # shutDown method will check that value in a loop .
                    # when "wait" changes to "shutdown" then shutdown.py script
                    # will shut down the system.
                    shutdown_enable = ShutDownThread(self.parent, self.gid, passwd)
                    self.parent.threadPool.append(shutdown_enable)
                    self.parent.threadPool[-1].start()

                else:
                    self.after_checkBox.setChecked(False)
            else:
                self.after_checkBox.setChecked(False)

        else:  # for Windows
            shutdown_enable = ShutDownThread(self.parent, self.gid)
            self.parent.threadPool.append(shutdown_enable)
            self.parent.threadPool[-1].start()

    def limitPushButtonPressed(self, button):
        self.limit_pushButton.setEnabled(False)
        if self.limit_comboBox.currentText() == "KiB/s":
            limit_value = str(self.limit_spinBox.value()) + str("K")
        else:
            limit_value = str(self.limit_spinBox.value()) + str("M")

    # if download was started before , send the limit_speed request to aria2 .
    # else save the request in data_base

        if self.status != 'scheduled':
            download.limitSpeed(self.gid, limit_value)
        else:
            # update limit value in data_base
            add_link_dictionary = {'gid': self.gid, 'limit_value': limit_value}
            self.parent.persepolis_db.updateAddLinkTable([add_link_dictionary])

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'

        self.resume_pushButton.setIcon(QIcon(icons + 'play'))
        self.pause_pushButton.setIcon(QIcon(icons + 'pause'))
        self.stop_pushButton.setIcon(QIcon(icons + 'stop'))

persepolis/scripts/progress.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PySide6.QtCore import Qt, QSize, QPoint, QDir, QTime, QCoreApplication
    from PySide6.QtWidgets import QLabel, QLineEdit, QFileDialog
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import Qt, QSize, QPoint, QDir, QTime, QCoreApplication
    from PyQt5.QtWidgets import QLabel, QLineEdit, QFileDialog
    from PyQt5.QtGui import QIcon

from persepolis.gui.addlink_ui import AddLinkWindow_Ui
from persepolis.scripts.check_proxy import getProxy
import os


class PropertiesWindow(AddLinkWindow_Ui):
    def __init__(self, parent, callback, gid, persepolis_setting, video_finder_dictionary=None):
        super().__init__(persepolis_setting)

        self.parent = parent
        self.persepolis_setting = persepolis_setting
        self.video_finder_dictionary = video_finder_dictionary

        self.download_later_pushButton.hide()  # hide download_later_pushButton
        self.change_name_checkBox.hide()  # hide change_name_checkBox
        self.change_name_lineEdit.hide()  # hide change_name_lineEdit

        # add new QLineEdit and QLineEdit for audio link if we have video finder links
        if self.video_finder_dictionary:

            self.link_label_2 = QLabel(self.link_frame)
            self.link_horizontalLayout.addWidget(self.link_label_2)

            self.link_lineEdit_2 = QLineEdit(self.link_frame)
            self.link_horizontalLayout.addWidget(self.link_lineEdit_2)
            self.link_lineEdit_2.textChanged.connect(self.linkLineChanged)

            self.link_label.setText(QCoreApplication.translate("addlink_ui_tr", "Video Link: "))
            self.link_label_2.setText(QCoreApplication.translate("addlink_ui_tr", "Audio Link: "))

            # gid_1 >> video_gid , gid_2 >> audio_gid
            self.gid_1 = self.video_finder_dictionary['video_gid']
            self.gid_2 = self.video_finder_dictionary['audio_gid']

        else:

            self.gid_1 = gid

        self.callback = callback

# detect_proxy_pushButton
        self.detect_proxy_pushButton.clicked.connect(
            self.detectProxy)

# connect folder_pushButton
        self.folder_pushButton.clicked.connect(self.changeFolder)
        self.download_folder_lineEdit.setEnabled(False)

        self.ok_pushButton.setEnabled(False)
        self.link_lineEdit.textChanged.connect(self.linkLineChanged)

# connect OK and cancel button

        self.cancel_pushButton.clicked.connect(self.close)
        self.ok_pushButton.clicked.connect(self.okButtonPressed)

#frames and checkBoxes
        self.proxy_frame.setEnabled(False)
        self.proxy_checkBox.toggled.connect(self.proxyFrame)

        self.download_frame.setEnabled(False)
        self.download_checkBox.toggled.connect(self.downloadFrame)

        self.limit_frame.setEnabled(False)
        self.limit_checkBox.toggled.connect(self.limitFrame)

        self.start_frame.setEnabled(False)
        self.start_checkBox.toggled.connect(self.startFrame)

        self.end_frame.setEnabled(False)
        self.end_checkBox.toggled.connect(self.endFrame)

        # get information from data base
        self.add_link_dictionary_1 = self.parent.persepolis_db.searchGidInAddLinkTable(self.gid_1)
        self.download_table_dict_1 = self.parent.persepolis_db.searchGidInDownloadTable(self.gid_1)

        if video_finder_dictionary:
            self.add_link_dictionary_2 = self.parent.persepolis_db.searchGidInAddLinkTable(self.gid_2)
            self.download_table_dict_2 = self.parent.persepolis_db.searchGidInDownloadTable(self.gid_2)

        # create a copy from add_link_dictionary for checking changes finally!
        self.add_link_dictionary_1_backup = {}
        for key in self.add_link_dictionary_1.keys():
            self.add_link_dictionary_1_backup[key] = self.add_link_dictionary_1[key]

        if video_finder_dictionary:
            self.add_link_dictionary_2_backup = {}
            for key in self.add_link_dictionary_2.keys():
                self.add_link_dictionary_2_backup[key] = self.add_link_dictionary_2[key]


# initialization
# disable folder_frame when download is complete
        if self.video_finder_dictionary:
            if self.video_finder_dictionary['video_completed'] == 'yes' or \
                self.video_finder_dictionary['audio_completed'] == 'yes':
                self.folder_frame.setEnabled(False)
        else:
            if self.download_table_dict_1['status'] == 'complete':
                self.folder_frame.setEnabled(False)


# link
        self.link_lineEdit.setText(self.add_link_dictionary_1['link'])

        if self.video_finder_dictionary:
            self.link_lineEdit_2.setText(self.add_link_dictionary_2['link'])

# ip_lineEdit initialization
        if self.add_link_dictionary_1['ip']:
            self.proxy_checkBox.setChecked(True)
            self.ip_lineEdit.setText(self.add_link_dictionary_1['ip'])
# port_spinBox initialization
            try:
                self.port_spinBox.setValue(
                    int(self.add_link_dictionary_1['port']))
            except ValueError:
                pass
# proxy user lineEdit initialization
            try:
                self.proxy_user_lineEdit.setText(
                    self.add_link_dictionary_1['proxy_user'])
            except Exception:
                pass
# proxy pass lineEdit initialization
            try:
                self.proxy_pass_lineEdit.setText(
                    self.add_link_dictionary_1['proxy_passwd'])
            except Exception:
                pass


# download UserName initialization
        if self.add_link_dictionary_1['download_user']:
            self.download_checkBox.setChecked(True)
            self.download_user_lineEdit.setText(
                self.add_link_dictionary_1['download_user'])
# download PassWord initialization
            try:
                self.download_pass_lineEdit.setText(
                    self.add_link_dictionary_1['download_passwd'])
            except Exception:
                pass

# folder_path
        try:
            self.download_folder_lineEdit.setText(
                self.add_link_dictionary_1['download_path'])
        except Exception:
            pass

# connections
        try:
            self.connections_spinBox.setValue(
                int(self.add_link_dictionary_1['connections']))
        except ValueError:
            pass

# get categories name and add them to add_queue_comboBox
        categories_list = self.parent.persepolis_db.categoriesList()
        for queue in categories_list:
            if queue != 'All Downloads':
                self.add_queue_comboBox.addItem(queue)

        # finding current queue and setting it!
        self.current_category = self.download_table_dict_1['category']

        current_category_index = self.add_queue_comboBox.findText(
            self.current_category)
        self.add_queue_comboBox.setCurrentIndex(current_category_index)


# add_queue_comboBox event
        self.add_queue_comboBox.currentIndexChanged.connect(self.queueChanged)


# limit speed
        limit = str(self.add_link_dictionary_1['limit_value'])
        if limit != '0':
            self.limit_checkBox.setChecked(True)
            limit_number = limit[0:-1]
            limit_unit = limit[-1]
            self.limit_spinBox.setValue(float(limit_number))
            if limit_unit == "K":
                self.limit_comboBox.setCurrentIndex(0)
            else:
                self.limit_comboBox.setCurrentIndex(1)

# start_time
        if self.add_link_dictionary_1['start_time']:
            # get hour and minute
            hour, minute = self.add_link_dictionary_1['start_time'].split(':')

            # set time
            q_time = QTime(int(hour), int(minute))
            self.start_time_qDataTimeEdit.setTime(q_time)

            self.start_checkBox.setChecked(True)
# end_time
        if self.add_link_dictionary_1['end_time']:
            # get hour and minute
            hour, minute = self.add_link_dictionary_1['end_time'].split(':')

            # set time
            q_time = QTime(int(hour), int(minute))
            self.end_time_qDateTimeEdit.setTime(q_time)

            self.end_checkBox.setChecked(True)

        # referer
        if self.add_link_dictionary_1['referer']:
            self.referer_lineEdit.setText(str(self.add_link_dictionary_1['referer']))

        if self.add_link_dictionary_1['header']:
            self.header_lineEdit.setText(str(self.add_link_dictionary_1['header']))

        if self.add_link_dictionary_1['user_agent']:
            self.user_agent_lineEdit.setText(str(self.add_link_dictionary_1['user_agent']))

        if self.add_link_dictionary_1['load_cookies']:
            self.load_cookies_lineEdit.setText((self.add_link_dictionary_1['load_cookies']))


# set window size and position
        size = self.persepolis_setting.value(
            'PropertiesWindow/size', QSize(520, 425))
        position = self.persepolis_setting.value(
            'PropertiesWindow/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)

# detect system proxy setting, and set ip_lineEdit and port_spinBox
    def detectProxy(self, button):
        # get system proxy information
        system_proxy_dict = getProxy()

        enable_proxy_frame = False

        # ip
        if 'http_proxy_ip' in system_proxy_dict.keys():
            self.ip_lineEdit.setText(str(system_proxy_dict['http_proxy_ip']))
            enable_proxy_frame = True

        # port
        if 'http_proxy_port' in system_proxy_dict.keys():
            self.port_spinBox.setValue(int(system_proxy_dict['http_proxy_port']))
            enable_proxy_frame = True

        # enable proxy frame if http_proxy_ip or http_proxy_port is valid.
        if enable_proxy_frame:
            self.proxy_checkBox.setChecked(True)
            self.detect_proxy_label.setText('')
        else:
            self.proxy_checkBox.setChecked(False)
            self.detect_proxy_label.setText('No proxy detected!')


# activate frames if checkBoxes checked
    def proxyFrame(self, checkBox):

        if self.proxy_checkBox.isChecked():
            self.proxy_frame.setEnabled(True)
        else:
            self.proxy_frame.setEnabled(False)

    def downloadFrame(self, checkBox):

        if self.download_checkBox.isChecked():
            self.download_frame.setEnabled(True)
        else:
            self.download_frame.setEnabled(False)

    def limitFrame(self, checkBox):

        if self.limit_checkBox.isChecked():
            self.limit_frame.setEnabled(True)
        else:
            self.limit_frame.setEnabled(False)

    def startFrame(self, checkBox):

        if self.start_checkBox.isChecked():
            self.start_frame.setEnabled(True)
        else:
            self.start_frame.setEnabled(False)

    def endFrame(self, checkBox):

        if self.end_checkBox.isChecked():
            self.end_frame.setEnabled(True)
        else:
            self.end_frame.setEnabled(False)

    def changeFolder(self, button):
        fname = QFileDialog.getExistingDirectory(self, 'Open f', '/home')

        if fname:
            # Returns pathName with the '/' separators converted to separators that are appropriate
            # for the underlying operating system.
            # On Windows, toNativeSeparators("c:/winnt/system32") returns
            # "c:\winnt\system32".
            fname = QDir.toNativeSeparators(fname)

        if os.path.isdir(fname):
            self.download_folder_lineEdit.setText(fname)

    def linkLineChanged(self, lineEdit):
        if str(self.link_lineEdit.text()) == '':
            self.ok_pushButton.setEnabled(False)
        else:
            self.ok_pushButton.setEnabled(True)

    def queueChanged(self, combo):
        # if one of the queues selected by user , start time and end time must
        # be deactivated
        if self.add_queue_comboBox.currentIndex() != 0:
            self.start_checkBox.setCheckState(Qt.Unchecked)
            self.start_checkBox.setEnabled(False)

            self.end_checkBox.setCheckState(Qt.Unchecked)
            self.end_checkBox.setEnabled(False)
        else:
            self.start_checkBox.setEnabled(True)
            self.end_checkBox.setEnabled(True)

    def okButtonPressed(self, button):
        # write user's new inputs in persepolis_setting for next time if needed
        if self.folder_checkBox.isChecked():
            self.persepolis_setting.setValue(
                'settings/download_path', self.download_folder_lineEdit.text())

        if not(self.proxy_checkBox.isChecked()):
            ip = None
            port = None
            proxy_user = None
            proxy_passwd = None
        else:
            ip = self.ip_lineEdit.text()
            if not(ip):
                ip = None
            port = str(self.port_spinBox.value())
            if not(port):
                port = None
            proxy_user = self.proxy_user_lineEdit.text()
            if not(proxy_user):
                proxy_user = None
            proxy_passwd = self.proxy_pass_lineEdit.text()
            if not(proxy_passwd):
                proxy_passwd = None

        if not(self.download_checkBox.isChecked()):
            download_user = None
            download_passwd = None
        else:
            download_user = self.download_user_lineEdit.text()
            if not(download_user):
                download_user = None
            download_passwd = self.download_pass_lineEdit.text()
            if not(download_passwd):
                download_passwd = None

        if not(self.limit_checkBox.isChecked()):
            limit = 0
        else:
            if self.limit_comboBox.currentText() == "KiB/s":
                limit = str(self.limit_spinBox.value()) + str("K")
            else:
                limit = str(self.limit_spinBox.value()) + str("M")

        if not(self.start_checkBox.isChecked()):
            start_time = None
        else:
            start_time = self.start_time_qDataTimeEdit.text()

        if not(self.end_checkBox.isChecked()):
            end_time = None
        else:
            end_time = self.end_time_qDateTimeEdit.text()

        connections = self.connections_spinBox.value()
        download_path = self.download_folder_lineEdit.text()

        # referer
        if self.referer_lineEdit.text() != '':
            referer = self.referer_lineEdit.text()
        else:
            referer = None

        # header
        if self.header_lineEdit.text() != '':
            header = self.header_lineEdit.text()
        else:
            header = None

        # user_agent
        if self.user_agent_lineEdit.text() != '':
            user_agent = self.user_agent_lineEdit.text()
        else:
            user_agent = None

        # load_cookies
        if self.load_cookies_lineEdit.text() != '':
            load_cookies = self.load_cookies_lineEdit.text()
        else:
            load_cookies = None

        self.add_link_dictionary_1['start_time'] = start_time
        self.add_link_dictionary_1['end_time'] = end_time
        self.add_link_dictionary_1['link'] = self.link_lineEdit.text()
        self.add_link_dictionary_1['ip'] = ip
        self.add_link_dictionary_1['port'] = port
        self.add_link_dictionary_1['proxy_user'] = proxy_user
        self.add_link_dictionary_1['proxy_passwd'] = proxy_passwd
        self.add_link_dictionary_1['download_user'] = download_user
        self.add_link_dictionary_1['download_passwd'] = download_passwd
        self.add_link_dictionary_1['download_path'] = download_path
        self.add_link_dictionary_1['limit_value'] = limit
        self.add_link_dictionary_1['connections'] = connections
        self.add_link_dictionary_1['referer'] = referer
        self.add_link_dictionary_1['header'] = header
        self.add_link_dictionary_1['user_agent'] = user_agent
        self.add_link_dictionary_1['load_cookies'] = load_cookies

        if self.video_finder_dictionary:
            self.add_link_dictionary_2['start_time'] = start_time
            self.add_link_dictionary_2['end_time'] = end_time
            self.add_link_dictionary_2['link'] = self.link_lineEdit_2.text()
            self.add_link_dictionary_2['ip'] = ip
            self.add_link_dictionary_2['port'] = port
            self.add_link_dictionary_2['proxy_user'] = proxy_user
            self.add_link_dictionary_2['proxy_passwd'] = proxy_passwd
            self.add_link_dictionary_2['download_user'] = download_user
            self.add_link_dictionary_2['download_passwd'] = download_passwd
            self.add_link_dictionary_2['download_path'] = download_path
            self.add_link_dictionary_2['limit_value'] = limit
            self.add_link_dictionary_2['connections'] = connections
            self.add_link_dictionary_2['referer'] = referer
            self.add_link_dictionary_2['header'] = header
            self.add_link_dictionary_2['user_agent'] = user_agent
            self.add_link_dictionary_2['load_cookies'] = load_cookies

        new_category = str(self.add_queue_comboBox.currentText())

        # it means category changed and data base must be updated.
        if new_category != self.current_category:

            self.download_table_dict_1['category'] = new_category
            # update data base
            self.parent.persepolis_db.updateDownloadTable([self.download_table_dict_1])

            if self.video_finder_dictionary:

                # category for audio and video must be same as each other
                self.download_table_dict_2['category'] = new_category
                self.parent.persepolis_db.updateDownloadTable([self.download_table_dict_2])

        # if any thing in add_link_dictionary_1 is changed,then update data base!
        for key in self.add_link_dictionary_1.keys():
            if self.add_link_dictionary_1[key] != self.add_link_dictionary_1_backup[key]:

                # update data base
                self.parent.persepolis_db.updateAddLinkTable([self.add_link_dictionary_1])

                # break the loop
                break

        # if link changed, then update download_db_table in data base
        if self.add_link_dictionary_1['link'] != self.add_link_dictionary_1_backup['link']:
            
            dictionary = {'gid': self.gid_1, 'link': self.add_link_dictionary_1['link']}
            self.parent.persepolis_db.updateDownloadTable([dictionary])

        # if any thing in add_link_dictionary_2 is changed,then update data base!
        if self.video_finder_dictionary:
            for key in self.add_link_dictionary_2.keys():
                if self.add_link_dictionary_2[key] != self.add_link_dictionary_2_backup[key]:

                    # update data base
                    self.parent.persepolis_db.updateAddLinkTable([self.add_link_dictionary_2])

                    # break the loop
                    break

            # if link changed, then update download_db_table in data base
            if self.add_link_dictionary_2['link'] != self.add_link_dictionary_2_backup['link']:
                
                dictionary = {'gid': self.gid_2, 'link': self.add_link_dictionary_2['link']}
                self.parent.persepolis_db.updateDownloadTable([dictionary])

            # if download_path was changed, then update video_finder_db_table in data base
            if self.add_link_dictionary_1['download_path'] != self.add_link_dictionary_1_backup['download_path']:
                dictionary = {'video_gid': self.gid_1,
                              'download_path': download_path}
                self.parent.persepolis_db.updateVideoFinderTable[dictionary]

        # callback to mainwindow
        self.callback(self.add_link_dictionary_1, self.gid_1, new_category, self.video_finder_dictionary)

        # close window
        self.close()

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        # save window size and position
        self.persepolis_setting.setValue('PropertiesWindow/size', self.size())
        self.persepolis_setting.setValue(
            'PropertiesWindow/position', self.pos())
        self.persepolis_setting.sync()

        event.accept()

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'

        self.folder_pushButton.setIcon(QIcon(icons + 'folder'))
        self.download_later_pushButton.setIcon(QIcon(icons + 'stop'))
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))

persepolis/scripts/properties.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
try:
    from PySide6.QtCore import Qt, QEvent, QTime, QSize, QPoint, QDir, QTranslator, QCoreApplication, QLocale
    from PySide6.QtWidgets import QFileDialog, QStyleFactory, QMessageBox, QTableWidgetItem
    from PySide6.QtGui import QFont, QKeySequence
except ImportError:
    from PyQt5.QtCore import Qt, QEvent, QTime, QSize, QPoint, QDir, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtWidgets import QFileDialog, QStyleFactory, QMessageBox, QTableWidgetItem
    from PyQt5.QtGui import QFont, QKeySequence


from persepolis.constants import OS
from persepolis.gui.setting_ui import Setting_Ui, KeyCapturingWindow_Ui
from persepolis.scripts.useful_tools import returnDefaultSettings
from persepolis.scripts import osCommands
from persepolis.scripts import startup
import platform
import sys
import os

home_address = os.path.expanduser("~")
os_type = platform.system()


class KeyCapturingWindow(KeyCapturingWindow_Ui):
    def __init__(self, callback, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.callback = callback

        self.ok_pushButton.clicked.connect(self.okPushButtonPressed)
        self.cancel_pushButton.clicked.connect(self.close)

        self.installEventFilter(self)

    def eventFilter(self, source, event):
        if event.type() == QEvent.KeyPress:
            if event.key():
                # show new keys in window
                self.capturedKeyLabel.setText(str(QKeySequence(event.modifiers() | event.key()).toString()))
        return super(KeyCapturingWindow, self).eventFilter(source, event)

    def okPushButtonPressed(self, button):
        # return new keys
        self.callback(self.capturedKeyLabel.text())
        self.close()

    def closeEvent(self, event):
        event.accept()


class PreferencesWindow(Setting_Ui):
    def __init__(self, parent, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.parent = parent
        self.grandparent = parent.persepolis_main

        self.persepolis_setting.beginGroup('settings')

        # initialization
        self.tries_spinBox.setValue(
            int(self.persepolis_setting.value('max-tries')))
        self.wait_spinBox.setValue(
            int(self.persepolis_setting.value('retry-wait')))
        self.time_out_spinBox.setValue(
            int(self.persepolis_setting.value('timeout')))
        self.connections_spinBox.setValue(
            int(self.persepolis_setting.value('connections')))
        self.rpc_port_spinbox.setValue(
            int(self.persepolis_setting.value('rpc-port')))

        if str(self.persepolis_setting.value('dont-check-certificate')) == 'yes':
            self.dont_check_certificate_checkBox.setChecked(True)
        else:
            self.dont_check_certificate_checkBox.setChecked(False)


        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # wait_queue
        wait_queue_list = self.persepolis_setting.value('wait-queue')
        try:
            q_time = QTime(int(wait_queue_list[0]), int(wait_queue_list[1]))
        except ValueError:
            q_time = QTime(0, 0)

        self.wait_queue_time.setTime(q_time)

        # change aria2 path
        self.aria2_path_pushButton.clicked.connect(self.changeAria2Path)
        self.aria2_path_checkBox.toggled.connect(self.ariaCheckBoxToggled)
        aria2_path = self.persepolis_setting.value('settings/aria2_path')

        self.aria2_path_lineEdit.setEnabled(False)
        if aria2_path is not None:
            self.aria2_path_checkBox.setChecked(True)
            self.aria2_path_lineEdit.setText(str(aria2_path))

        self.ariaCheckBoxToggled('aria2')

        if os_type in OS.UNIX_LIKE:
            for widget in self.aria2_path_checkBox, self.aria2_path_lineEdit, self.aria2_path_pushButton:
                widget.hide()

        # save_as_tab
        self.download_folder_lineEdit.setText(
            str(self.persepolis_setting.value('download_path')))
        self.temp_download_lineEdit.setText(
            str(self.persepolis_setting.value('download_path_temp')))

        # subfolder
        if str(self.persepolis_setting.value('subfolder')) == 'yes':
            self.subfolder_checkBox.setChecked(True)
        else:
            self.subfolder_checkBox.setChecked(False)

        # notifications_tab
        self.volume_label.setText(
            'Volume : ' + str(self.persepolis_setting.value('sound-volume')))
        self.volume_dial.setValue(
            int(self.persepolis_setting.value('sound-volume')))

        # set style
        # find available styles(It's depends on operating system and desktop environments).
        available_styles = QStyleFactory.keys()
        for style in available_styles:

            # 'bb10dark', 'GTK', 'gtk' styles may cause to crashing! Eliminate them!
            style_black_list = ['bb10dark', 'bb10bright', 'GTK', 'gtk', 'gtk2']

            if style not in style_black_list:
                self.style_comboBox.addItem(style)

        # System >> for system default style
        # when user select System for style section, the default system style is using.
        self.style_comboBox.addItem('System')

        current_style_index = self.style_comboBox.findText(
            str(self.persepolis_setting.value('style')))
        if current_style_index != -1:
            self.style_comboBox.setCurrentIndex(current_style_index)

        # available language
        available_language = ['en_US', 'fa_IR', 'ar', 'zh_CN', 'fr_FR', 'pl_PL',
                              'nl_NL', 'pt_BR', 'es_ES', 'hu', 'tr', 'tr_TR']
        for lang in available_language:
            self.lang_comboBox.addItem(str(QLocale(lang).nativeLanguageName()), lang)

        current_locale = self.lang_comboBox.findData(
            str(self.persepolis_setting.value('locale')))
        self.lang_comboBox.setCurrentIndex(current_locale)

        self.current_icon = self.persepolis_setting.value('icons')

        # icon size
        size = ['128', '64', '48', '32', '24', '16']

        self.icons_size_comboBox.addItems(size)
        current_icons_size_index = self.icons_size_comboBox.findText(
            str(self.persepolis_setting.value('toolbar_icon_size')))

        self.icons_size_comboBox.setCurrentIndex(current_icons_size_index)

        # call setDarkLightIcon if index is changed
        self.icons_size_comboBox.currentIndexChanged.connect(self.setDarkLightIcon)

        # set notification
        notifications = ['Native notification', 'QT notification']
        self.notification_comboBox.addItems(notifications)
        current_notification_index = self.notification_comboBox.findText(
            str(self.persepolis_setting.value('notification')))
        self.notification_comboBox.setCurrentIndex(current_notification_index)

        # set font
        font_setting = QFont()
        font_setting.setFamily(str(self.persepolis_setting.value('font')))
        self.fontComboBox.setCurrentFont(font_setting)

        self.font_size_spinBox.setValue(
            int(self.persepolis_setting.value('font-size')))

        # sound frame
        self.sound_frame.setEnabled(False)
        self.enable_notifications_checkBox.toggled.connect(self.soundFrame)
        if str(self.persepolis_setting.value('sound')) == 'yes':
            self.enable_notifications_checkBox.setChecked(True)
        else:
            self.enable_notifications_checkBox.setChecked(False)

        # connect folder buttons
        self.download_folder_lineEdit.setEnabled(False)
        self.download_folder_pushButton.clicked.connect(
            self.downloadFolderPushButtonClicked)
        self.temp_download_lineEdit.setEnabled(False)
        self.temp_download_pushButton.clicked.connect(
            self.tempDownloadPushButtonClicked)

        # dial
        self.volume_dial.setNotchesVisible(True)
        self.volume_dial.valueChanged.connect(self.dialChanged)

        # start_persepolis_if_browser_executed_checkBox
        if str(self.persepolis_setting.value('browser-persepolis')) == 'yes':
            self.start_persepolis_if_browser_executed_checkBox.setChecked(True)
        else:
            self.start_persepolis_if_browser_executed_checkBox.setChecked(False)

        # hide window
        if str(self.persepolis_setting.value('hide-window')) == 'yes':
            self.hide_window_checkBox.setChecked(True)
        else:
            self.hide_window_checkBox.setChecked(False)

        # tray icon
        if str(self.persepolis_setting.value('tray-icon')) == 'yes':
            self.enable_system_tray_checkBox.setChecked(True)
        else:
            self.enable_notifications_checkBox.setChecked(False)

        # show_menubar
        if str(self.persepolis_setting.value('show-menubar')) == 'yes':
            self.show_menubar_checkbox.setChecked(True)
        else:
            self.show_menubar_checkbox.setChecked(False)

        if platform.system() == 'Darwin':
            self.show_menubar_checkbox.setChecked(True)
            self.show_menubar_checkbox.hide()

        # show_sidepanel
        if str(self.persepolis_setting.value('show-sidepanel')) == 'yes':
            self.show_sidepanel_checkbox.setChecked(True)
        else:
            self.show_sidepanel_checkbox.setChecked(False)

        # show ProgressWindow
        if str(self.persepolis_setting.value('show-progress')) == 'yes':
            self.show_progress_window_checkbox.setChecked(True)
        else:
            self.show_progress_window_checkbox.setChecked(False)

        # after download dialog
        if str(self.persepolis_setting.value('after-dialog')) == 'yes':
            self.after_download_checkBox.setChecked(True)
        else:
            self.after_download_checkBox.setChecked(False)

        # run persepolis at startup checkBox
        if str(self.persepolis_setting.value('startup')) == 'yes':
            self.startup_checkbox.setChecked(True)
        else:
            self.startup_checkbox.setChecked(False)

        # font_checkBox
        if str(self.persepolis_setting.value('custom-font')) == 'yes':
            self.font_checkBox.setChecked(True)
        else:
            self.font_checkBox.setChecked(False)

        self.fontCheckBoxState(self.font_checkBox)

        # keep_awake_checkBox
        if str(self.persepolis_setting.value('awake')) == 'yes':
            self.keep_awake_checkBox.setChecked(True)
        else:
            self.keep_awake_checkBox.setChecked(False)

        # columns_tab
        if str(self.persepolis_setting.value('column0')) == 'yes':
            self.column0_checkBox.setChecked(True)
        else:
            self.column0_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column1')) == 'yes':
            self.column1_checkBox.setChecked(True)
        else:
            self.column1_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column2')) == 'yes':
            self.column2_checkBox.setChecked(True)
        else:
            self.column2_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column3')) == 'yes':
            self.column3_checkBox.setChecked(True)
        else:
            self.column3_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column4')) == 'yes':
            self.column4_checkBox.setChecked(True)
        else:
            self.column4_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column5')) == 'yes':
            self.column5_checkBox.setChecked(True)
        else:
            self.column5_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column6')) == 'yes':
            self.column6_checkBox.setChecked(True)
        else:
            self.column6_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column7')) == 'yes':
            self.column7_checkBox.setChecked(True)
        else:
            self.column7_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column10')) == 'yes':
            self.column10_checkBox.setChecked(True)
        else:
            self.column10_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column11')) == 'yes':
            self.column11_checkBox.setChecked(True)
        else:
            self.column11_checkBox.setChecked(False)

        if str(self.persepolis_setting.value('column12')) == 'yes':
            self.column12_checkBox.setChecked(True)
        else:
            self.column12_checkBox.setChecked(False)

        # video_finder
        try:  # Integer casting may raise exception.
            self.max_links_spinBox.setValue(int(persepolis_setting.value('video_finder/max_links', 3)))
        except ValueError:
            pass

        # shortcuts
        self.qshortcuts_list = [self.parent.exitAction_shortcut,
                                self.parent.minimizeAction_shortcut,
                                self.parent.removeSelectedAction_shortcut,
                                self.parent.deleteSelectedAction_shortcut,
                                self.parent.moveUpSelectedAction_shortcut,
                                self.parent.moveDownSelectedAction_shortcut,
                                self.parent.addlinkAction_shortcut,
                                self.parent.videoFinderAddLinkAction_shortcut,
                                self.parent.addtextfileAction_shortcut]

        self.shortcuts_list = [self.parent.exitAction_shortcut.key().toString(),
                               self.parent.minimizeAction_shortcut.key().toString(),
                               self.parent.removeSelectedAction_shortcut.key().toString(),
                               self.parent.deleteSelectedAction_shortcut.key().toString(),
                               self.parent.moveUpSelectedAction_shortcut.key().toString(),
                               self.parent.moveDownSelectedAction_shortcut.key().toString(),
                               self.parent.addlinkAction_shortcut.key().toString(),
                               self.parent.videoFinderAddLinkAction_shortcut.key().toString(),
                               self.parent.addtextfileAction_shortcut.key().toString()]

        # add shortcuts to the shortcut_table
        j = 0
        for shortcut in self.shortcuts_list:
            item = QTableWidgetItem(shortcut)

            # align center
            item.setTextAlignment(0x0004 | 0x0080)

            # insert item in shortcut_table
            self.shortcut_table.setItem(j, 1, item)

            j = j + 1

        # If user doubleclicks on a row, then run showCaptureKeyboardWindow method
        self.shortcut_table.itemDoubleClicked.connect(self.showCaptureKeyboardWindow)

        # ok cancel default button
        self.cancel_pushButton.clicked.connect(self.close)
        self.defaults_pushButton.clicked.connect(
            self.defaultsPushButtonPressed)
        self.ok_pushButton.clicked.connect(self.okPushButtonPressed)

        # font_checkBox connect
        self.font_checkBox.stateChanged.connect(self.fontCheckBoxState)

        # saving initial value of self.persepolis_setting in self.first_key_value_dict
        # at the end! in the okPushButtonPressed method,
        # first_key_value_dict will compared with second_key_value_dict.
        # if any thing changed , then a message box notify user about
        # "some changes take effect after restarting persepolis".
        self.first_key_value_dict = {}
        for member in self.persepolis_setting.allKeys():
            self.first_key_value_dict[member] = str(self.persepolis_setting.value(member))


        # if style_comboBox is changed, self.styleComboBoxChanged is called.
        self.style_comboBox.currentIndexChanged.connect(self.styleComboBoxChanged)

        self.styleComboBoxChanged()

        self.color_comboBox.currentIndexChanged.connect(self.setDarkLightIcon)

        self.persepolis_setting.endGroup()

        # setting window size and position
        size = self.persepolis_setting.value(
            'PreferencesWindow/size', QSize(578, 565))
        position = self.persepolis_setting.value(
            'PreferencesWindow/position', QPoint(300, 300))

        self.resize(size)
        self.move(position)


    # run this method if user doubleclicks on an item in shortcut_table
    def showCaptureKeyboardWindow(self):

        # show KeyCapturingWindow
        keyboard_capture_window = KeyCapturingWindow(self.callBack, self.persepolis_setting)

        self.parent.capturekeywindows_list.append(keyboard_capture_window)
        self.parent.capturekeywindows_list[-1].show()

    def callBack(self, keys):
        # do nothing if keys is empty
        if not(keys):
            return

        # check that if shortcut used before.
        if keys in self.shortcuts_list:
            self.msgBox = QMessageBox()
            self.msgBox.setText(QCoreApplication.translate("setting_src_ui_tr",
                                                           "<b><center>This shortcut has been used before!\
                    Use another one!</center></b>"))
            self.msgBox.setIcon(QMessageBox.Warning)
            reply = self.msgBox.exec_()  # noqa: F841

        # set new shortcut
        else:
            selected_row = self.shortcut_table.selectionModel().selectedRows()[0].row()

            item = QTableWidgetItem(keys)

            # align center
            item.setTextAlignment(0x0004 | 0x0080)

            # insert item in shortcut_table
            self.shortcut_table.setItem(selected_row, 1, item)

            # set keys in shortcuts_list
            self.shortcuts_list[selected_row] = keys

    # active color_comboBox only when user is select "Fusion" style.
    def styleComboBoxChanged(self, index=None):
        # clear color_comboBox
        self.color_comboBox.clear()

        # get current style
        selected_style = self.style_comboBox.currentText()

        if selected_style != 'Fusion':
            # color_comboBox item
            color_scheme = ['System']

            # add item
            self.color_comboBox.addItems(color_scheme)

            # set 'System' for color_scheme
            current_color_index = self.color_comboBox.findText('System')
            self.color_comboBox.setCurrentIndex(current_color_index)

            # disable color_comboBox
            self.color_comboBox.setEnabled(False)
        else:
            # enable color_comboBox
            self.color_comboBox.setEnabled(True)

            # color_comboBox items
            color_scheme = ['Dark Fusion', 'Light Fusion'] 

            # add items
            self.color_comboBox.addItems(color_scheme)

            current_color_index = self.color_comboBox.findText(
                str(self.persepolis_setting.value('color-scheme')))

            # it means user's preferred color_scheme is not valid in color_comboBox.
            if current_color_index == -1:
                current_color_index = 0

            self.color_comboBox.setCurrentIndex(current_color_index)

        self.setDarkLightIcon()

    # this method sets dark icons for dark color schemes
    # and light icons for light color schemes.
    def setDarkLightIcon(self, index=None):

        dark_theme = None

        # find selected style
        selected_style = self.style_comboBox.currentText()

        # clear icon_comboBox
        self.icon_comboBox.clear()

        # Papirus icons can be used with small sizes(smaller than 48)
        # get user's selected icons size
        selected_size = int(self.icons_size_comboBox.currentText())

        if selected_style == 'Fusion':
            if self.color_comboBox.currentText() == 'Dark Fusion':
                dark_theme = True
            else:
                dark_theme = False

        elif selected_style == 'Adwaita-Dark':
            dark_theme = True

        elif selected_style == 'Adwaita' or selected_style == 'macintosh':
            dark_theme = False

        if dark_theme:
            self.icon_comboBox.clear()

            if selected_size < 48:
                icons = ['Breeze-Dark', 'Papirus-Dark']
            else:
                icons = ['Breeze-Dark']
 
            self.icon_comboBox.addItems(icons)

            # current_icons_index is -1, if findText couldn't find icon index.
            current_icons_index = self.icon_comboBox.findText(
                str(self.persepolis_setting.value('icons', self.current_icon)))

            if current_icons_index == -1:
                current_icons_index = 0

            self.icon_comboBox.setCurrentIndex(current_icons_index)

        elif dark_theme is False:

            if selected_size < 48:
                icons = ['Breeze', 'Papirus', 'Papirus-Light']
            else:
                icons = ['Breeze', 'Papirus']


            self.icon_comboBox.addItems(icons)

            # current_icons_index is -1, if findText couldn't find icon index.
            current_icons_index = self.icon_comboBox.findText(
                str(self.persepolis_setting.value('icons', self.current_icon)))

            if current_icons_index == -1:
                current_icons_index = 0

            self.icon_comboBox.setCurrentIndex(current_icons_index)


        else:
            if selected_size < 48:
                icons = ['Breeze', 'Breeze-Dark', 'Papirus',
                        'Papirus-Dark', 'Papirus-Light']
            else:
                icons = ['Breeze', 'Breeze-Dark', 'Papirus']
 
            self.icon_comboBox.addItems(icons)

            # current_icons_index is -1, if findText couldn't find icon index.
            current_icons_index = self.icon_comboBox.findText(
                str(self.persepolis_setting.value('icons', self.current_icon)))

            if current_icons_index == -1:
                current_icons_index = 0

            self.icon_comboBox.setCurrentIndex(current_icons_index)


    def fontCheckBoxState(self, checkBox):

        # deactivate fontComboBox and font_size_spinBox if font_checkBox not checked!
        if self.font_checkBox.isChecked():
            self.fontComboBox.setEnabled(True)
            self.font_size_spinBox.setEnabled(True)
        else:
            self.fontComboBox.setEnabled(False)
            self.font_size_spinBox.setEnabled(False)

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):

        # saving window size and position
        self.persepolis_setting.setValue('PreferencesWindow/size', self.size())
        self.persepolis_setting.setValue(
            'PreferencesWindow/position', self.pos())
        self.persepolis_setting.sync()
        event.accept()

        if self.parent.isVisible() is False:
            self.parent.minMaxTray(event)
        self.close()

    def soundFrame(self, checkBox):

        if self.enable_notifications_checkBox.isChecked():
            self.sound_frame.setEnabled(True)
        else:
            self.sound_frame.setEnabled(False)

    def ariaCheckBoxToggled(self, checkBox):

        if self.aria2_path_checkBox.isChecked():
            self.aria2_path_pushButton.setEnabled(True)
        else:
            self.aria2_path_pushButton.setEnabled(False)

    def changeAria2Path(self, button):

        cwd = sys.argv[0]
        cwd = os.path.dirname(cwd)

        f_path, filters = QFileDialog.getOpenFileName(
            self, 'Select aria2 path', cwd)

        # if path is correct:
        if os.path.isfile(str(f_path)):
            self.aria2_path_lineEdit.setText(str(f_path))
        else:
            self.aria2_path_checkBox.setChecked(False)

    def downloadFolderPushButtonClicked(self, button):

        download_path = str(
            self.persepolis_setting.value('settings/download_path'))
        fname = QFileDialog.getExistingDirectory(
            self, 'Select a directory', download_path)

        if fname:
            # Returns pathName with the '/' separators converted to separators
            # that are appropriate for the underlying operating system.
            # On Windows, toNativeSeparators("c:/winnt/system32") returns
            # "c:\winnt\system32".
            fname = QDir.toNativeSeparators(fname)
            self.download_folder_lineEdit.setText(fname)
            self.persepolis_setting.setValue(
                'settings/download_path', str(fname))

    def tempDownloadPushButtonClicked(self, button):

        download_path_temp = str(
            self.persepolis_setting.value('settings/download_path_temp'))
        fname = QFileDialog.getExistingDirectory(
            self, 'Open f', download_path_temp)

        if fname:
            self.temp_download_lineEdit.setText(fname)
            self.persepolis_setting.setValue(
                'settings/download_path_temp', str(fname))

    def dialChanged(self, dial):

        self.volume_label.setText('Volume : ' + str(self.volume_dial.value()))

    def defaultsPushButtonPressed(self, button):

        self.persepolis_setting.beginGroup('settings')

        self.setting_dict = returnDefaultSettings()

        self.tries_spinBox.setValue(int(self.setting_dict['max-tries']))
        self.wait_spinBox.setValue(int(self.setting_dict['retry-wait']))
        self.time_out_spinBox.setValue(int(self.setting_dict['timeout']))
        self.connections_spinBox.setValue(
            int(self.setting_dict['connections']))

        self.rpc_port_spinbox.setValue(int(self.setting_dict['rpc-port']))
        self.aria2_path_lineEdit.setText('')
        self.aria2_path_checkBox.setChecked(False)

        # wait-queue
        wait_queue_list = self.setting_dict['wait-queue']
        q_time = QTime(wait_queue_list[0], wait_queue_list[1])
        self.wait_queue_time.setTime(q_time)

        # dont_check_certificate_checkBox
        self.dont_check_certificate_checkBox.setChecked(False)

        # save_as_tab
        self.download_folder_lineEdit.setText(
            str(self.setting_dict['download_path']))
        self.temp_download_lineEdit.setText(
            str(self.setting_dict['download_path_temp']))

        self.subfolder_checkBox.setChecked(True)

        # notifications_tab
        self.volume_label.setText(
            'Volume : ' + str(self.setting_dict['sound-volume']))
        self.volume_dial.setValue(int(self.setting_dict['sound-volume']))

        # set style
        current_style_index = self.style_comboBox.findText(
            str(self.setting_dict['style']))
        self.style_comboBox.setCurrentIndex(current_style_index)

        # set language
        current_locale = self.lang_comboBox.findData(
            str(self.setting_dict['locale']))
        self.lang_comboBox.setCurrentIndex(current_locale)

        # set color_scheme
        current_color_index = self.color_comboBox.findText(
            str(self.setting_dict['color-scheme']))
        self.color_comboBox.setCurrentIndex(current_color_index)

        # set icons
        current_icons_index = self.icon_comboBox.findText(
            str(self.setting_dict['icons']))
        self.icon_comboBox.setCurrentIndex(current_icons_index)

        # set icons size
        current_icons_size_index = self.icons_size_comboBox.findText(
            str(self.setting_dict['toolbar_icon_size']))
        self.icons_size_comboBox.setCurrentIndex(current_icons_size_index)

        # set notification
        current_notification_index = self.notification_comboBox.findText(
            str(self.setting_dict['notification']))
        self.notification_comboBox.setCurrentIndex(current_notification_index)

        # set font
        self.font_checkBox.setChecked(False)
        font_setting = QFont()
        font_setting.setFamily(str(self.setting_dict['font']))
        self.fontComboBox.setCurrentFont(font_setting)

        self.font_size_spinBox.setValue(int(self.setting_dict['font-size']))

        # sound frame
        self.enable_notifications_checkBox.setChecked(True)

        # start_persepolis_if_browser_executed_checkBox
        self.start_persepolis_if_browser_executed_checkBox.setChecked(True)

        # hide window
        self.hide_window_checkBox.setChecked(True)

        # tray icon
        self.enable_system_tray_checkBox.setChecked(True)

        # after_download_checkBox
        self.after_download_checkBox.setChecked(True)

        # hide menubar for linux
        if platform.system == 'Darwin':
            self.show_menubar_checkbox.setChecked(True)
        else:
            self.show_menubar_checkbox.setChecked(False)

        # show side panel
        self.show_sidepanel_checkbox.setChecked(True)

        # show progress window
        self.show_progress_window_checkbox.setChecked(True)

        # run persepolis at startup checkBox
        self.startup_checkbox.setChecked(False)

        # keep_awake_checkBox
        self.keep_awake_checkBox.setChecked(False)

        # columns_tab
        self.column0_checkBox.setChecked(True)
        self.column1_checkBox.setChecked(True)
        self.column2_checkBox.setChecked(True)
        self.column3_checkBox.setChecked(True)
        self.column4_checkBox.setChecked(True)
        self.column5_checkBox.setChecked(True)
        self.column6_checkBox.setChecked(True)
        self.column7_checkBox.setChecked(True)
        self.column10_checkBox.setChecked(True)
        self.column11_checkBox.setChecked(True)
        self.column12_checkBox.setChecked(True)

        # video finder
        self.max_links_spinBox.setValue(3)

        # shortcuts
        self.shortcuts_list = [self.setting_dict['shortcuts/quit_shortcut'],
                               self.setting_dict['shortcuts/hide_window_shortcut'],
                               self.setting_dict['shortcuts/remove_shortcut'],
                               self.setting_dict['shortcuts/delete_shortcut'],
                               self.setting_dict['shortcuts/move_up_selection_shortcut'],
                               self.setting_dict['shortcuts/move_down_selection_shortcut'],
                               self.setting_dict['shortcuts/add_new_download_shortcut'],
                               self.setting_dict['shortcuts/video_finder_shortcut'],
                               self.setting_dict['shortcuts/import_text_shortcut']]

        # add shortcuts to the shortcut_table
        j = 0
        for shortcut in self.shortcuts_list:
            item = QTableWidgetItem(shortcut)

            # align center
            item.setTextAlignment(0x0004 | 0x0080)

            # insert item in shortcut_table
            self.shortcut_table.setItem(j, 1, item)

            j = j + 1

        self.persepolis_setting.endGroup()

    def okPushButtonPressed(self, button):

        self.persepolis_setting.beginGroup('settings')

        self.persepolis_setting.setValue(
            'max-tries', self.tries_spinBox.value())
        self.persepolis_setting.setValue(
            'retry-wait', self.wait_spinBox.value())
        self.persepolis_setting.setValue(
            'timeout', self.time_out_spinBox.value())
        self.persepolis_setting.setValue(
            'connections', self.connections_spinBox.value())
        self.persepolis_setting.setValue(
            'rpc-port', self.rpc_port_spinbox.value())
        self.persepolis_setting.setValue(
            'download_path', self.download_folder_lineEdit.text())
        self.persepolis_setting.setValue(
            'download_path_temp', self.temp_download_lineEdit.text())
        self.persepolis_setting.setValue(
            'sound-volume', self.volume_dial.value())
        self.persepolis_setting.setValue(
            'notification', self.notification_comboBox.currentText())
        self.persepolis_setting.setValue(
            'wait-queue', self.wait_queue_time.text().split(':'))

        # change aria2_path
        if self.aria2_path_checkBox.isChecked():
            self.persepolis_setting.setValue('settings/aria2_path', str(self.aria2_path_lineEdit.text()))

        # don't check certificate
        if self.dont_check_certificate_checkBox.isChecked():
            self.persepolis_setting.setValue('dont-check-certificate', 'yes')
        else:
            self.persepolis_setting.setValue('dont-check-certificate', 'no')



        # changing icons

        icons = self.icon_comboBox.currentText()
        self.persepolis_setting.setValue('icons', icons)

        if icons != self.current_icon:  # it means icons changed
            for windows_list in [self.parent.logwindow_list, self.parent.about_window_list,
                                 self.parent.addlinkwindows_list, self.parent.propertieswindows_list,
                                 self.parent.afterdownload_list, self.parent.text_queue_window_list,
                                 self.parent.progress_window_list, self.parent.plugin_queue_window_list]:

                for window in windows_list:
                    window.changeIcon(icons)

            self.parent.changeIcon(icons)

        # icons size
        icons_size = self.icons_size_comboBox.currentText()
        self.persepolis_setting.setValue('toolbar_icon_size', icons_size)

        icons_size = int(icons_size)
        self.parent.toolBar.setIconSize(QSize(icons_size, icons_size))
        self.parent.toolBar2.setIconSize(QSize(icons_size, icons_size))

        # style
        style = str(self.style_comboBox.currentText())
        self.persepolis_setting.setValue('style', style)

        # language
        locale = str(self.lang_comboBox.itemData(self.lang_comboBox.currentIndex()))
        self.persepolis_setting.setValue('locale', locale)

        # color_scheme
        color_scheme = self.color_comboBox.currentText()
        self.persepolis_setting.setValue('color-scheme', color_scheme)

        # font and font size
        current_font = self.fontComboBox.currentFont()
        current_font = current_font.key()
        current_font = current_font.split(',')
        font = str(current_font[0])
        self.persepolis_setting.setValue('font', font)

        font_size = self.font_size_spinBox.value()
        self.persepolis_setting.setValue('font-size', font_size)

        if self.font_checkBox.isChecked():
            custom_font = 'yes'
        else:
            custom_font = 'no'

        self.persepolis_setting.setValue('custom-font', custom_font)

        # if user select qt notification  >> enable_system_tray icon
        if self.persepolis_setting.value('notification') == 'QT notification':
            self.enable_system_tray_checkBox.setChecked(True)

        # start_persepolis_if_browser_executed_checkBox
        if self.start_persepolis_if_browser_executed_checkBox.isChecked():
            self.persepolis_setting.setValue('browser-persepolis', 'yes')
        else:
            self.persepolis_setting.setValue('browser-persepolis', 'no')

        # hide_window_checkBox
        if self.hide_window_checkBox.isChecked():
            self.persepolis_setting.setValue('hide-window', 'yes')
        else:
            self.persepolis_setting.setValue('hide-window', 'no')

        # enable_system_tray_checkBox
        if self.enable_system_tray_checkBox.isChecked():
            self.persepolis_setting.setValue('tray-icon', 'yes')
            self.parent.system_tray_icon.show()
            self.parent.minimizeAction.setEnabled(True)
            self.parent.trayAction.setChecked(True)
        else:
            self.persepolis_setting.setValue('tray-icon', 'no')
            self.parent.system_tray_icon.hide()
            self.parent.minimizeAction.setEnabled(False)
            self.parent.trayAction.setChecked(False)

        # after_download_checkBox
        if self.after_download_checkBox.isChecked():
            self.persepolis_setting.setValue('after-dialog', 'yes')
        else:
            self.persepolis_setting.setValue('after-dialog', 'no')

        # show_menubar_checkbox
        if self.show_menubar_checkbox.isChecked():
            self.persepolis_setting.setValue('show-menubar', 'yes')
            self.parent.menubar.show()
            self.parent.toolBar2.hide()
            self.parent.showMenuBarAction.setChecked(True)

        else:
            self.persepolis_setting.setValue('show-menubar', 'no')
            self.parent.menubar.hide()
            self.parent.toolBar2.show()
            self.parent.showMenuBarAction.setChecked(False)

        # show_sidepanel_checkbox
        if self.show_sidepanel_checkbox.isChecked():
            self.persepolis_setting.setValue('show-sidepanel', 'yes')
            self.parent.category_tree_qwidget.show()
        else:
            self.persepolis_setting.setValue('show-sidepanel', 'no')
            self.parent.category_tree_qwidget.hide()

        # show_progress_window_checkbox
        if self.show_progress_window_checkbox.isChecked():
            self.persepolis_setting.setValue('show-progress', 'yes')
        else:
            self.persepolis_setting.setValue('show-progress', 'no')

        if self.startup_checkbox.isChecked():
            self.persepolis_setting.setValue('startup', 'yes')

            if not(startup.checkstartup()):  # checking existence of Persepolis in  system's startup

                startup.addstartup()  # adding Persepolis to system's startup
        else:
            self.persepolis_setting.setValue('startup', 'no')

            if startup.checkstartup():  # checking existence of Persepolis in  system's startup

                startup.removestartup()  # removing Persepolis from system's startup

        # keep_awake_checkBox
        if self.keep_awake_checkBox.isChecked():
            self.persepolis_setting.setValue('awake', 'yes')
            self.parent.keep_awake_checkBox.setChecked(True)
        else:
            self.persepolis_setting.setValue('awake', 'no')
            self.parent.keep_awake_checkBox.setChecked(False)

        # this section  creates temporary download folder and download folder and
        # download sub folders if they did not existed.

        download_path_temp = self.persepolis_setting.value(
            'download_path_temp')
        download_path = self.persepolis_setting.value('download_path')

        folder_list = [download_path_temp, download_path]

        if self.subfolder_checkBox.isChecked():
            self.persepolis_setting.setValue('subfolder', 'yes')

            for folder in ['Audios', 'Videos', 'Others', 'Documents', 'Compressed']:
                folder_list.append(os.path.join(download_path, folder))

        else:
            self.persepolis_setting.setValue('subfolder', 'no')

        for folder in folder_list:
            osCommands.makeDirs(folder)

        if self.enable_notifications_checkBox.isChecked():
            self.persepolis_setting.setValue('sound', 'yes')
        else:
            self.persepolis_setting.setValue('sound', 'no')

        # columns_tab
        if self.column0_checkBox.isChecked():
            self.persepolis_setting.setValue('column0', 'yes')
            self.parent.download_table.setColumnHidden(0, False)

            if self.parent.download_table.isColumnHidden(0):
                self.parent.download_table.setColumnWidth(0, 100)

        else:
            self.persepolis_setting.setValue('column0', 'no')
            self.parent.download_table.setColumnHidden(0, True)

        if self.column1_checkBox.isChecked():
            self.persepolis_setting.setValue('column1', 'yes')
            self.parent.download_table.setColumnHidden(1, False)

            if self.parent.download_table.isColumnHidden(1):
                self.parent.download_table.setColumnWidth(1, 100)

        else:
            self.persepolis_setting.setValue('column1', 'no')
            self.parent.download_table.setColumnHidden(1, True)

        if self.column2_checkBox.isChecked():
            self.persepolis_setting.setValue('column2', 'yes')
            self.parent.download_table.setColumnHidden(2, False)

            if self.parent.download_table.isColumnHidden(2):
                self.parent.download_table.setColumnWidth(2, 100)

        else:
            self.persepolis_setting.setValue('column2', 'no')
            self.parent.download_table.setColumnHidden(2, True)

        if self.column3_checkBox.isChecked():
            self.persepolis_setting.setValue('column3', 'yes')
            self.parent.download_table.setColumnHidden(3, False)

            if self.parent.download_table.isColumnHidden(3):
                self.parent.download_table.setColumnWidth(3, 100)

        else:
            self.persepolis_setting.setValue('column3', 'no')
            self.parent.download_table.setColumnHidden(3, True)

        if self.column4_checkBox.isChecked():
            self.persepolis_setting.setValue('column4', 'yes')
            self.parent.download_table.setColumnHidden(4, False)

            if self.parent.download_table.isColumnHidden(4):
                self.parent.download_table.setColumnWidth(4, 100)

        else:
            self.persepolis_setting.setValue('column4', 'no')
            self.parent.download_table.setColumnHidden(4, True)

        if self.column5_checkBox.isChecked():
            self.persepolis_setting.setValue('column5', 'yes')
            self.parent.download_table.setColumnHidden(5, False)

            if self.parent.download_table.isColumnHidden(5):
                self.parent.download_table.setColumnWidth(5, 100)

        else:
            self.persepolis_setting.setValue('column5', 'no')
            self.parent.download_table.setColumnHidden(5, True)

        if self.column6_checkBox.isChecked():
            self.persepolis_setting.setValue('column6', 'yes')
            self.parent.download_table.setColumnHidden(6, False)

            if self.parent.download_table.isColumnHidden(6):
                self.parent.download_table.setColumnWidth(6, 100)

        else:
            self.persepolis_setting.setValue('column6', 'no')
            self.parent.download_table.setColumnHidden(6, True)

        if self.column7_checkBox.isChecked():
            self.persepolis_setting.setValue('column7', 'yes')
            self.parent.download_table.setColumnHidden(7, False)

            if self.parent.download_table.isColumnHidden(7):
                self.parent.download_table.setColumnWidth(7, 100)

        else:
            self.persepolis_setting.setValue('column7', 'no')
            self.parent.download_table.setColumnHidden(7, True)

        if self.column10_checkBox.isChecked():
            self.persepolis_setting.setValue('column10', 'yes')
            self.parent.download_table.setColumnHidden(10, False)

            if self.parent.download_table.isColumnHidden(10):
                self.parent.download_table.setColumnWidth(10, 100)

        else:
            self.persepolis_setting.setValue('column10', 'no')
            self.parent.download_table.setColumnHidden(10, True)

        if self.column11_checkBox.isChecked():
            self.persepolis_setting.setValue('column11', 'yes')
            self.parent.download_table.setColumnHidden(11, False)

            if self.parent.download_table.isColumnHidden(11):
                self.parent.download_table.setColumnWidth(11, 100)

        else:
            self.persepolis_setting.setValue('column11', 'no')
            self.parent.download_table.setColumnHidden(11, True)

        if self.column12_checkBox.isChecked():
            self.persepolis_setting.setValue('column12', 'yes')
            self.parent.download_table.setColumnHidden(12, False)

            if self.parent.download_table.isColumnHidden(12):
                self.parent.download_table.setColumnWidth(12, 100)

        else:
            self.persepolis_setting.setValue('column12', 'no')
            self.parent.download_table.setColumnHidden(12, True)

        # shortcuts
        # set new shortcuts
        i = 0
        for qshortcut in self.qshortcuts_list:
            # set keys for QShortcut
            qshortcut.setKey(self.shortcuts_list[i])

            i = i + 1

        # video_finder
        self.persepolis_setting.setValue('video_finder/max_links', self.max_links_spinBox.value())

        # saving value of persepolis_setting in second_key_value_dict.
        self.second_key_value_dict = {}
        for member in self.persepolis_setting.allKeys():
            self.second_key_value_dict[member] = str(self.persepolis_setting.value(member))

        # comparing first_key_value_dict with second_key_value_dict
        show_message_box = False
        for key in self.first_key_value_dict.keys():
            if self.first_key_value_dict[key] != self.second_key_value_dict[key]:
                if key in ['locale', 'aria2_path', 'download_path_temp', 'download_path',
                           'custom-font', 'rpc-port', 'max-tries', 'retry-wait', 'timeout',
                           'connections', 'style', 'font', 'font-size', 'color-scheme']:
                    show_message_box = True

        # if any thing changed that needs restarting, then notify user about
        # "Some changes take effect after restarting persepolis"
        if show_message_box:
            restart_messageBox = QMessageBox()
            restart_messageBox.setText(QCoreApplication.translate(
                "setting_src_ui_tr", '<b><center>Restart Persepolis Please!</center></b>\
                <br><center>Some changes take effect after restarting Persepolis</center>'))
            restart_messageBox.setWindowTitle(QCoreApplication.translate("setting_src_ui_tr", 'Restart Persepolis!'))
            restart_messageBox.exec_()

        # applying changes
        self.persepolis_setting.endGroup()
        self.persepolis_setting.sync()

        self.close()

persepolis/scripts/setting.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from persepolis.scripts import logger
from persepolis.constants import OS
from time import sleep
import subprocess
import platform

os_type = platform.system()

def shutDown(parent, gid=None, category=None, password=None):
    # for queue >> gid = None
    # for single downloads >> category = None
    # change value of shutdown in data base
    if category is not None:
        dict = {'category': category,
                'shutdown': 'wait'}

        # update data base
        parent.temp_db.updateQueueTable(dict)
    else:
        # so we have single download
        dict = {'gid': gid,
                'shutdown': 'wait'}

        # update data base
        parent.temp_db.updateSingleTable(dict)

    shutdown_status = "wait"

    while shutdown_status == "wait":
        sleep(5)

        # get shutdown status from data_base
        if category is not None:
            dict = parent.temp_db.returnCategory(category)
        else:
            dict = parent.temp_db.returnGid(gid)

        shutdown_status = dict['shutdown']

    if shutdown_status == "shutdown":

        logger.sendToLog("Shutting down in 20 seconds", "INFO")
        sleep(20)

        if os_type == OS.LINUX:

            pipe = subprocess.Popen(['sudo', '-S', 'poweroff'],
                                    stdout=subprocess.DEVNULL,
                                    stdin=subprocess.PIPE,
                                    stderr=subprocess.DEVNULL,
                                    shell=False)

            pipe.communicate(password.encode())

        elif os_type == OS.DARWIN:

            pipe = subprocess.Popen(['sudo', '-S', 'shutdown', '-h', 'now'],
                                    stdout=subprocess.DEVNULL,
                                    stdin=subprocess.PIPE,
                                    stderr=subprocess.DEVNULL,
                                    shell=False)

            pipe.communicate(password.encode())

        elif os_type == OS.WINDOWS:

            CREATE_NO_WINDOW = 0x08000000
            subprocess.Popen(['shutdown', '-S'],
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stdin=subprocess.PIPE,
                             shell=False,
                             creationflags=CREATE_NO_WINDOW)

        elif os_type in OS.BSD_FAMILY:

            pipe = subprocess.Popen(['sudo', '-S', 'shutdown', '-p', 'now'],
                                    stdout=subprocess.DEVNULL,
                                    stdin=subprocess.PIPE,
                                    stderr=subprocess.DEVNULL,
                                    shell=False)

            pipe.communicate(password.encode())

persepolis/scripts/shutdown.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from ghermez import humanReadableSize
from requests.cookies import cookiejar_from_dict
from http.cookies import SimpleCookie
import requests


# for more information about "requests" library , please see
# http://docs.python-requests.org/en/master/


# spider function finds name of file and file size from header
def spider(add_link_dictionary):

    # get user's download request from add_link_dictionary
    link = add_link_dictionary['link']
    ip = add_link_dictionary['ip']
    port = add_link_dictionary['port']
    proxy_user = add_link_dictionary['proxy_user']
    proxy_passwd = add_link_dictionary['proxy_passwd']
    download_user = add_link_dictionary['download_user']
    download_passwd = add_link_dictionary['download_passwd']
    header = add_link_dictionary['header']
    out = add_link_dictionary['out']
    user_agent = add_link_dictionary['user_agent']
    raw_cookies = add_link_dictionary['load_cookies']
    referer = add_link_dictionary['referer']

    # define a requests session
    requests_session = requests.Session()
    if ip:
        ip_port = 'http://' + str(ip) + ":" + str(port)
        if proxy_user:
            ip_port = 'http://' + proxy_user + ':' + proxy_passwd + '@' + ip_port
        # set proxy to the session
        requests_session.proxies = {'http': ip_port}

    if download_user:
        # set download user pass to the session
        requests_session.auth = (download_user, download_passwd)

    # set cookies
    if raw_cookies:
        cookie = SimpleCookie()
        cookie.load(raw_cookies)

        cookies = {key: morsel.value for key, morsel in cookie.items()}
        requests_session.cookies = cookiejar_from_dict(cookies)

    # set referer
    if referer:
        requests_session.headers.update({'referer': referer})  # setting referer to the session

    # set user_agent
    if user_agent:
        requests_session.headers.update({'user-agent': user_agent})  # setting user_agent to the session

    # find headers
    try:
        response = requests_session.head(link)
        header = response.headers
    except Exception:
        header = {}

    filename = None
    file_size = None
    if 'Content-Disposition' in header.keys():  # checking if filename is available
        content_disposition = header['Content-Disposition']
        if content_disposition.find('filename') != -1:
            filename_splited = content_disposition.split('filename=')
            filename_splited = filename_splited[-1]

            # getting file name in desired format
            filename = filename_splited[1:-1]

    if not(filename):
        filename = link.split('/')[-1]

    # if user set file name before in add_link_dictionary['out'],
    # then set "out" for filename
    if out:
        filename = out

    # check if file_size is available
    if 'Content-Length' in header.keys():
        file_size = int(header['Content-Length'])

        # converting file_size to KiB or MiB or GiB
        file_size = humanReadableSize(file_size)

    # return results
    return filename, file_size


# this function finds and returns file name for links.
def queueSpider(add_link_dictionary):
    # get download information from add_link_dictionary
    for i in ['link', 'header', 'out', 'user_agent', 'load_cookies', 'referer']:
        if i not in add_link_dictionary:
            add_link_dictionary[i] = None

    link = add_link_dictionary['link']
    header = add_link_dictionary['header']
    user_agent = add_link_dictionary['user_agent']
    raw_cookies = add_link_dictionary['load_cookies']
    referer = add_link_dictionary['referer']

    requests_session = requests.Session()  # defining a requests Session

    if raw_cookies:  # set cookies
        cookie = SimpleCookie()
        cookie.load(raw_cookies)

        cookies = {key: morsel.value for key, morsel in cookie.items()}
        requests_session.cookies = cookiejar_from_dict(cookies)

    if referer:
        # set referer to the session
        requests_session.headers.update({'referer': referer})

    if user_agent:
        # set user_agent to the session
        requests_session.headers.update({'user-agent': user_agent})

    # find headers
    try:
        response = requests_session.head(link)
        header = response.headers
    except Exception:
        header = {}
    filename = None
    if 'Content-Disposition' in header.keys():  # checking if filename is available
        content_disposition = header['Content-Disposition']
        if content_disposition.find('filename') != -1:
            filename_splited = content_disposition.split('filename=')
            filename_splited = filename_splited[-1]
            # getting file name in desired format
            filename = filename_splited[1:-1]

    if not(filename):
        filename = link.split('/')[-1]

    return filename


def addLinkSpider(add_link_dictionary):
    # get user's download information from add_link_dictionary
    for i in ['link', 'header', 'out', 'user_agent', 'load_cookies', 'referer']:
        if i not in add_link_dictionary:
            add_link_dictionary[i] = None

    link = add_link_dictionary['link']
    header = add_link_dictionary['header']
    user_agent = add_link_dictionary['user_agent']
    raw_cookies = add_link_dictionary['load_cookies']
    referer = add_link_dictionary['referer']

    requests_session = requests.Session()  # defining a requests Session

    if raw_cookies:  # set cookies
        cookie = SimpleCookie()
        cookie.load(raw_cookies)

        cookies = {key: morsel.value for key, morsel in cookie.items()}
        requests_session.cookies = cookiejar_from_dict(cookies)

    if referer:
        # set referer to the session
        requests_session.headers.update({'referer': referer})

    if user_agent:
        # set user_agent to the session
        requests_session.headers.update({'user-agent': user_agent})

    # find headers
    try:
        response = requests_session.head(link)
        header = response.headers
    except Exception:
        header = {}

    # find file size
    file_size = None
    if 'Content-Length' in header.keys():  # checking if file_size is available
        file_size = int(header['Content-Length'])

        # converting file_size to KiB or MiB or GiB
        file_size = str(humanReadableSize(file_size))

    # find file name
    file_name = None
    if 'Content-Disposition' in header.keys():  # checking if filename is available
        content_disposition = header['Content-Disposition']
        if content_disposition.find('filename') != -1:
            filename_splited = content_disposition.split('filename=')
            filename_splited = filename_splited[-1]
            # getting file name in desired format
            file_name = str(filename_splited[1:-1])

    return file_name, file_size  # If no Content-Length ? fixed it.

persepolis/scripts/spider.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import platform
import sys
import os
from persepolis.constants import OS

home_address = os.path.expanduser("~")

# finding os_type
os_type = platform.system()

if os_type == OS.WINDOWS:
    import winreg
    # from winreg import QueryValueEx, OpenKey, SetValueEx
    from builtins import WindowsError


# check startup
def checkstartup():
    # check if it is linux
    if os_type in OS.UNIX_LIKE:
        # check if the startup exists
        if os.path.exists(home_address + "/.config/autostart/persepolis.desktop"):
            return True
        else:
            return False

    # check if it is mac
    elif os_type == OS.OSX:
        # OS X
        if os.path.exists(home_address + "/Library/LaunchAgents/com.persepolisdm.plist"):
            return True
        else:
            return False

    # check if it is Windows
    elif os_type == OS.WINDOWS:
        # try to open startup key and check persepolis value
        try:
            aKey = winreg.OpenKey(
                winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, winreg.KEY_ALL_ACCESS)
            # startupvalue = winreg.QueryValueEx(aKey, 'persepolis')
            startup = True
        except WindowsError:
            startup = False

        # Close the connection
        winreg.CloseKey(aKey)

        # if the startup enabled or disabled
        if startup:
            return True
        if not startup:
            return False

# add startup file


def addstartup():
    # check if it is linux
    if os_type in OS.UNIX_LIKE:
        entry = \
            '''[Desktop Entry]
Name=Persepolis Download Manager
Name[fa]=پرسپولیس
Comment=Download Manager
GenericName=Download Manager
GenericName[fa]=نرم افزار مدیریت بارگیری
Keywords=Internet;WWW;Web;
Terminal=false
Type=Application
Categories=Qt;Network;
StartupNotify=true
Exec=persepolis --tray
Icon=persepolis
StartupWMClass=persepolis-download-Manager
'''

        # check if the autostart directory exists & create entry
        if not os.path.exists(home_address + "/.config/autostart"):
            os.makedirs(home_address + "/.config/autostart", 0o755)
        startupfile = open(
            home_address + "/.config/autostart/persepolis.desktop", 'w+')
        startupfile.write(entry)
        os.chmod(home_address +
                 "/.config/autostart/persepolis.desktop", 0o644)
    # check if it is mac
    elif os_type == OS.OSX:
        # OS X
        cwd = sys.argv[0]
        cwd = os.path.dirname(cwd)
        entry = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>com.persepolisdm.persepolis</string>
	<key>Program</key>
	<string>''' + cwd + '''/Persepolis Download Manager</string>
	<key>ProgramArguments</key>
	<array>
		<string>--tray</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
</dict>
</plist>\n'''
        startupfile = open(
            home_address + '/Library/LaunchAgents/com.persepolisdm.plist', 'w+')
        startupfile.write(entry)
        os.system('launchctl load ' + home_address +
                  "/Library/LaunchAgents/com.persepolisdm.plist")
    # check if it is Windows
    elif os_type == OS.WINDOWS:

        # Connect to the startup path in Registry
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
                             "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, winreg.KEY_ALL_ACCESS)
        # find current persepolis exe path
        cwd = os.getcwd()
        persepolisexetray = '"' + cwd + '\Persepolis Download Manager.exe' + '"' + ' --tray'
        # add persepolis to startup
        winreg.SetValueEx(key, 'persepolis', 0,
                          winreg.REG_SZ, persepolisexetray)

        # Close connection
        winreg.CloseKey(key)

# remove startup file


def removestartup():
    # check if it is linux
    if os_type in OS.BSD_FAMILY:

        # remove it
        os.remove(home_address + "/.config/autostart/persepolis.desktop")

    # check if it is mac OS
    elif os_type == OS.OSX:
        # OS X
        if checkstartup():
            os.system('launchctl unload ' + home_address +
                      "/Library/LaunchAgents/com.persepolisdm.plist")
            os.remove(home_address +
                      "/Library/LaunchAgents/com.persepolisdm.plist")

    # check if it is Windows
    elif os_type == OS.WINDOWS:
        if checkstartup():
            # Connect to the startup path in Registry
            key = winreg.OpenKey(
                winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, winreg.KEY_ALL_ACCESS)

            # remove persepolis from startup
            winreg.DeleteValue(key, 'persepolis')

            # Close connection
            winreg.CloseKey(key)

persepolis/scripts/startup.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-


#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
try:
    from PySide6.QtCore import Qt, QDir, QPoint, QSize, QThread, Signal
    from PySide6.QtWidgets import QTableWidgetItem, QFileDialog
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import Qt, QDir, QPoint, QSize, QThread
    from PyQt5.QtWidgets import QTableWidgetItem, QFileDialog
    from PyQt5.QtCore import pyqtSignal as Signal
    from PyQt5.QtGui import QIcon

from persepolis.gui.text_queue_ui import TextQueue_Ui
from persepolis.scripts import logger
from persepolis.scripts import spider
from functools import partial
import os

# This thread finds filename


class QueueSpiderThread(QThread):
    QUEUESPIDERRETURNEDFILENAME = Signal(str)

    def __init__(self, dict):
        QThread.__init__(self)
        self.dict = dict

    def run(self):
        try:
            filename = spider.queueSpider(self.dict)
            if filename:
                self.QUEUESPIDERRETURNEDFILENAME.emit(filename)
            else:
                logger.logObj.error(
                    "Spider couldn't find download information", exc_info=True)

        except Exception as e:
            # write error in log
            logger.logObj.error(
                "Spider couldn't find download information", exc_info=True)
            logger.logObj.error(
                str(e), exc_info=True)


class TextQueue(TextQueue_Ui):
    def __init__(self, parent, file_path, callback, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.callback = callback
        self.file_path = file_path
        self.parent = parent

        global icons
        icons = ':/' + \
            str(self.persepolis_setting.value('settings/icons')) + '/'

        # read text file lines and put links in list format.
        f = open(self.file_path)
        f_links_list = f.readlines()
        f.close()

        f_links_list.reverse()

        # check links! links must be started with http or https or ftp
        link_list = []
        for link in f_links_list:
            text = link.strip()
            if ("tp:/" in text[2:6]) or ("tps:/" in text[2:7]):
                link_list.append(text)

        k = 1
        for link in link_list:
            self.links_table.insertRow(0)

            # file_name
            file_name = '***'
            dict = {'link': link}

            # spider finds file name
            new_spider = QueueSpiderThread(dict)
            self.parent.threadPool.append(new_spider)
            self.parent.threadPool[-1].start()
            self.parent.threadPool[-1].QUEUESPIDERRETURNEDFILENAME.connect(
                partial(self.parent.queueSpiderCallBack, child=self, row_number=len(link_list) - k))
            k = k + 1

            item = QTableWidgetItem(file_name)

            # add checkbox to the item
            item.setFlags(Qt.ItemIsUserCheckable |
                          Qt.ItemIsEnabled)
            item.setCheckState(Qt.Checked)

            # insert file_name
            self.links_table.setItem(0, 0, item)

            # insert link
            item = QTableWidgetItem(str(link))
            self.links_table.setItem(0, 1, item)

        # get categories name and add them to add_queue_comboBox
        categories_list = self.parent.persepolis_db.categoriesList()

        for queue in categories_list:
            if queue != 'All Downloads':
                self.add_queue_comboBox.addItem(queue)

        self.add_queue_comboBox.addItem(
            QIcon(icons + 'add_queue'), 'Create new queue')

# entry initialization

        # get values from persepolis_setting
        global connections
        connections = int(
            self.persepolis_setting.value('settings/connections'))
        global download_path
        download_path = str(
            self.persepolis_setting.value('settings/download_path'))

        self.connections_spinBox.setValue(connections)
        self.download_folder_lineEdit.setText(download_path)
        self.download_folder_lineEdit.setEnabled(False)

        # ip_lineEdit initialization
        settings_ip = self.persepolis_setting.value(
            'add_link_initialization/ip', None)
        if settings_ip:
            self.ip_lineEdit.setText(str(settings_ip))

        # proxy user lineEdit initialization
        settings_proxy_user = self.persepolis_setting.value(
            'add_link_initialization/proxy_user', None)
        if settings_proxy_user:
            self.proxy_user_lineEdit.setText(str(settings_proxy_user))

        # port_spinBox initialization
        settings_port = self.persepolis_setting.value(
            'add_link_initialization/port', 0)

        self.port_spinBox.setValue(int(int(settings_port)))

        # download UserName initialization
        settings_download_user = self.persepolis_setting.value(
            'add_link_initialization/download_user', None)
        if settings_download_user:
            self.download_user_lineEdit.setText(str(settings_download_user))

        # connect folder_pushButton
        self.folder_pushButton.clicked.connect(self.changeFolder)

        # connect OK and cancel button
        self.cancel_pushButton.clicked.connect(self.close)
        self.ok_pushButton.clicked.connect(self.okButtonPressed)

        # connect select_all_pushButton  deselect_all_pushButton
        self.select_all_pushButton.clicked.connect(self.selectAll)

        self.deselect_all_pushButton.clicked.connect(self.deselectAll)

        #frames and checkBoxes
        self.proxy_frame.setEnabled(False)
        self.proxy_checkBox.toggled.connect(self.proxyFrame)

        self.download_frame.setEnabled(False)
        self.download_checkBox.toggled.connect(self.downloadFrame)

        self.limit_frame.setEnabled(False)
        self.limit_checkBox.toggled.connect(self.limitFrame)

        # set focus to ok button
        self.ok_pushButton.setFocus()

        # add_queue_comboBox event
        self.add_queue_comboBox.currentIndexChanged.connect(self.queueChanged)

        # setting window size and position
        size = self.persepolis_setting.value('TextQueue/size', QSize(700, 500))
        position = self.persepolis_setting.value(
            'TextQueue/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)

    # this method checks all check boxes
    def selectAll(self, button):
        for i in range(self.links_table.rowCount()):
            item = self.links_table.item(i, 0)
            item.setCheckState(Qt.Checked)

    # this method deselect all check boxes
    def deselectAll(self, button):
        for i in range(self.links_table.rowCount()):
            item = self.links_table.item(i, 0)
            item.setCheckState(Qt.Unchecked)

    # this method is called, when user changes add_queue_comboBox
    def queueChanged(self, combo):
        if str(self.add_queue_comboBox.currentText()) == 'Create new queue':
            # if user want to create new queue, then call createQueue method from mainwindow(parent)
            new_queue = self.parent.createQueue(combo)

            if new_queue:
                # clear comboBox
                self.add_queue_comboBox.clear()

                # load queue list again!
                queues_list = self.parent.persepolis_db.categoriesList()
                for queue in queues_list:
                    if queue != 'All Downloads':
                        self.add_queue_comboBox.addItem(queue)

                self.add_queue_comboBox.addItem(
                    QIcon(icons + 'add_queue'), 'Create new queue')

                # finding index of new_queue and setting comboBox for it
                index = self.add_queue_comboBox.findText(str(new_queue))
                self.add_queue_comboBox.setCurrentIndex(index)
            else:
                self.add_queue_comboBox.setCurrentIndex(0)

    # activate frames if checkBoxes checked
    def proxyFrame(self, checkBox):

        if self.proxy_checkBox.isChecked():
            self.proxy_frame.setEnabled(True)
        else:
            self.proxy_frame.setEnabled(False)

    def downloadFrame(self, checkBox):

        if self.download_checkBox.isChecked():
            self.download_frame.setEnabled(True)
        else:
            self.download_frame.setEnabled(False)

    def limitFrame(self, checkBox):

        if self.limit_checkBox.isChecked():
            self.limit_frame.setEnabled(True)
        else:
            self.limit_frame.setEnabled(False)

    def changeFolder(self, button):
        fname = QFileDialog.getExistingDirectory(
            self, 'Select a directory', download_path)

        if fname:
            # Returns pathName with the '/' separators converted to
            # separators that are appropriate for the underlying operating system.
            # On Windows, toNativeSeparators("c:/winnt/system32") returns
            # "c:\winnt\system32".
            fname = QDir.toNativeSeparators(fname)

        if os.path.isdir(fname):
            self.download_folder_lineEdit.setText(fname)

    def okButtonPressed(self, button):
        # write user's input data to init file
        self.persepolis_setting.setValue(
            'add_link_initialization/ip', self.ip_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/port', self.port_spinBox.value())
        self.persepolis_setting.setValue(
            'add_link_initialization/proxy_user', self.proxy_user_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/download_user', self.download_user_lineEdit.text())

        # Check 'Remember path' and change default path if needed
        if self.folder_checkBox.isChecked():
            self.persepolis_setting.setValue(
                'settings/download_path', self.download_folder_lineEdit.text())

        if not(self.proxy_checkBox.isChecked()):
            ip = None
            port = None
            proxy_user = None
            proxy_passwd = None
        else:
            ip = self.ip_lineEdit.text()
            if not(ip):
                ip = None
            port = self.port_spinBox.value()
            if not(port):
                port = None
            proxy_user = self.proxy_user_lineEdit.text()
            if not(proxy_user):
                proxy_user = None
            proxy_passwd = self.proxy_pass_lineEdit.text()
            if not(proxy_passwd):
                proxy_passwd = None

        if not(self.download_checkBox.isChecked()):
            download_user = None
            download_passwd = None
        else:
            download_user = self.download_user_lineEdit.text()
            if not(download_user):
                download_user = None
            download_passwd = self.download_pass_lineEdit.text()
            if not(download_passwd):
                download_passwd = None

        if not(self.limit_checkBox.isChecked()):
            limit = 0
        else:
            if self.limit_comboBox.currentText() == "KiB/s":
                limit = str(self.limit_spinBox.value()) + str("K")
            else:
                limit = str(self.limit_spinBox.value()) + str("M")

        category = str(self.add_queue_comboBox.currentText())

        connections = self.connections_spinBox.value()
        download_path = self.download_folder_lineEdit.text()

        dict = {'out': None,
                'start_time': None,
                'end_time': None,
                'link': None,
                'ip': ip,
                'port': port,
                'proxy_user': proxy_user,
                'proxy_passwd': proxy_passwd,
                'download_user': download_user,
                'download_passwd': download_passwd,
                'connections': connections,
                'limit_value': limit,
                'download_path': download_path,
                'referer': None,
                'load_cookies': None,
                'user_agent': None,
                'header': None,
                'after_download': None
                }

        # find checked links in links_table
        self.add_link_dictionary_list = []
        i = 0
        for row in range(self.links_table.rowCount()):
            item = self.links_table.item(row, 0)

            # if item is checked
            if (item.checkState() == 2):
                # Create a copy from dict and add it to add_link_dictionary_list
                self.add_link_dictionary_list.append(
                    dict.copy())

                # get link and add it to dict
                link = self.links_table.item(row, 1).text()
                self.add_link_dictionary_list[i]['link'] = str(link)

                # add file name to the dict
                self.add_link_dictionary_list[i]['out'] = self.links_table.item(
                    row, 0).text()

                i = i + 1
        # reverse list
        self.add_link_dictionary_list.reverse()

        # Create callback for mainwindow
        self.callback(self.add_link_dictionary_list, category)

        # close window
        self.close()

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        self.persepolis_setting.setValue('TextQueue/size', self.size())
        self.persepolis_setting.setValue('TextQueue/position', self.pos())
        self.persepolis_setting.sync()

        event.accept()

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'

        self.folder_pushButton.setIcon(QIcon(icons + 'folder'))
        self.ok_pushButton.setIcon(QIcon(icons + 'ok'))
        self.cancel_pushButton.setIcon(QIcon(icons + 'remove'))

persepolis/scripts/text_queue.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
try:
    from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton
    from PySide6.QtCore import Qt, QSize, QPoint, QTranslator, QCoreApplication, QLocale
    from PySide6 import QtCore
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton
    from PyQt5.QtCore import Qt, QSize, QPoint, QTranslator, QCoreApplication, QLocale
    from PyQt5 import QtCore
    from PyQt5.QtGui import QIcon


from persepolis.constants import OS, APP_NAME, LONG_NAME, VERSION, REPO_LINK
from persepolis.scripts import osCommands
import platform
import requests
import ast

# finding os_type
os_type = platform.system()


class checkupdate(QWidget):
    def __init__(self, persepolis_setting):
        super().__init__()

# defining UI
        self.persepolis_setting = persepolis_setting
        # icons = ':/' + \
        #     str(self.persepolis_setting.value('settings/icons')) + '/'
        self.setWindowIcon(QIcon.fromTheme(APP_NAME, QIcon(':/ghermez.png')))

# add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        self.setWindowTitle(QCoreApplication.translate("update_src_ui_tr", 'Checking for newer version'))

        # installed version
        self.client_version = '3.20'

        # first line text
        self.update_label = QLabel(QCoreApplication.translate(
            "update_src_ui_tr", f"The newest is the best, we recommend to update {APP_NAME.capitalize()}."))
        self.update_label.setTextFormat(QtCore.Qt.RichText)
        self.update_label.setAlignment(QtCore.Qt.AlignCenter)

        # second line text
        self.version_label = QLabel(QCoreApplication.translate(
            "update_src_ui_tr", f'This is {LONG_NAME} version {VERSION}'))
        self.version_label.setAlignment(QtCore.Qt.AlignCenter)

        # release link
        self.link_label = QLabel(
            f'<a href={REPO_LINK}/releases>{REPO_LINK}/releases</a>')
        self.link_label.setAlignment(QtCore.Qt.AlignCenter)
        self.link_label.setOpenExternalLinks(True)

        # version status
        self.status_label = QLabel()
        self.status_label.setTextFormat(QtCore.Qt.RichText)
        self.status_label.setAlignment(QtCore.Qt.AlignCenter)

        # update button
        self.check_button = QPushButton(QCoreApplication.translate("update_src_ui_tr", "Check for new update"))
        self.check_button.clicked.connect(self.updateCheck)

        # verticalLayout
        vbox = QVBoxLayout()
        vbox.addWidget(self.update_label)
        vbox.addWidget(self.version_label)
        vbox.addWidget(self.link_label)
        vbox.addWidget(self.check_button)
        vbox.addWidget(self.status_label)

        # horizontalLayout
        hbox = QHBoxLayout()
        hbox.addLayout(vbox)

        # window layout
        self.setLayout(hbox)

        # window size and position
        size = self.persepolis_setting.value(
            'checkupdate/size', QSize(360, 250))
        position = self.persepolis_setting.value(
            'checkupdate/position', QPoint(300, 300))

        self.resize(size)
        self.move(position)

    # checking methode
    def updateCheck(self, button):
        self.check_button.setText(QCoreApplication.translate("update_src_ui_tr", 'Checking...'))

        try:
            # get information dictionary from github
            updatesource = requests.get('https://persepolisdm.github.io/version')

            updatesource_text = updatesource.text
            updatesource_dict = ast.literal_eval(updatesource_text)

            # get latest stable version
            server_version = updatesource_dict['version']

            # Comparison
            if float(server_version) > float(self.client_version):
                self.status_label.setText(QCoreApplication.translate(
                    "update_src_ui_tr", f'A newer {APP_NAME.capitalize()} release is available'))

                if os_type == OS.WINDOWS:
                    self.winUpdatedl()  # this function download latest release

                    # find system architect
                    if platform.architecture()[0] == '64bit':

                        osCommands.xdgOpen(updatesource_dict['win64dlurl'])

                    elif platform.architecture()[0] == '32bit':

                        osCommands.xdgOpen(updatesource_dict['win32dlurl'])

                elif os_type == OS.OSX:
                    osCommands.xdgOpen(updatesource_dict['macdlurl'])  # it will download latest release for mac

            elif float(server_version) == float(self.client_version):
                self.status_label.setText(QCoreApplication.translate(
                    "update_src_ui_tr", 'Latest version is installed :)'))

            elif float(server_version) < float(self.client_version):
                self.status_label.setText(QCoreApplication.translate("update_src_ui_tr", 'You are using beta version'))

        except Exception:
            self.status_label.setText(QCoreApplication.translate(
                "update_src_ui_tr", 'An error occurred while checking for updates.'))

        self.check_button.setText(QCoreApplication.translate("update_src_ui_tr", 'Check for new update'))

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        # saving window size and position
        self.persepolis_setting.setValue(
            'checkupdate/size', self.size())
        self.persepolis_setting.setValue(
            'checkupdate/position', self.pos())
        self.persepolis_setting.sync()
        event.accept()

persepolis/scripts/update.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
try:
    from PySide6.QtWidgets import QStyleFactory
except ImportError:
    from PyQt5.QtWidgets import QStyleFactory

from persepolis.constants import OS
import urllib.parse
import subprocess
import platform
import sys
import os

import ghermez

try:
    from persepolis.scripts import logger
    logger_availability = True
except ImportError:
    logger_availability = False

# find operating system
# os_type >> Linux or Darwin(Mac osx) or Windows(Microsoft Windows) or
# FreeBSD or OpenBSD
os_type = platform.system()

# user home address
home_address = os.path.expanduser("~")


# determine the config folder path based on the operating system
def determineConfigFolder():
    if os_type in OS.UNIX_LIKE:
        config_folder = os.path.join(
            home_address, ".config/persepolis_download_manager")
    elif os_type == OS.OSX:
        config_folder = os.path.join(
            home_address, "Library/Application Support/persepolis_download_manager")
    elif os_type == OS.WINDOWS:
        config_folder = os.path.join(
            home_address, 'AppData', 'Local', 'persepolis_download_manager')

    return config_folder

# this function returns operating system and desktop environment(for linux and bsd).


def osAndDesktopEnvironment():
    desktop_env = None
    if os_type in OS.UNIX_LIKE:
        # find desktop environment('KDE', 'GNOME', ...)
        desktop_env = os.environ.get('XDG_CURRENT_DESKTOP')

    return os_type, desktop_env


# this function converts file_size to KiB or MiB or GiB
def humanReadableSize(size, input_type='file_size'): 
    labels = ['KiB', 'MiB', 'GiB', 'TiB']
    i = -1
    if size < 1024:
        return str(size) + ' B'

    while size >= 1024:
        i += 1
        size = size / 1024
        
    if input_type == 'speed':
        j = 0
    else:
        j = 1

    if i > j:
        return str(round(size, 2)) + ' ' + labels[i]
    else:
        return str(round(size, None)) + ' ' + labels[i]

# this function converts human readable size to byte


def convertToByte(file_size):

    # if unit is not in Byte
    if file_size[-2:] != ' B':

        unit = file_size[-3:]

        # persepolis uses float type for GiB and TiB
        if unit == 'GiB' or unit == 'TiB':

            size_value = float(file_size[:-4])

        else:
            size_value = int(float(file_size[:-4]))
    else:
        unit = None
        size_value = int(float(file_size[:-3]))

    # covert them in byte
    if not(unit):
        in_byte_value = size_value

    elif unit == 'KiB':
        in_byte_value = size_value*1024

    elif unit == 'MiB':
        in_byte_value = size_value*1024*1024

    elif unit == 'GiB':
        in_byte_value = size_value*1024*1024*1024

    elif unit == 'TiB':
        in_byte_value = size_value*1024*1024*1024*1024

    return int(in_byte_value)


# this function checks free space in hard disk.
def freeSpace(dir):
    try:
        import psutil
    except ImportError:
        if logger_availability:
            logger.sendToLog("psutil in not installed!", "ERROR")

        return None

    try:
        dir_space = psutil.disk_usage(dir)
        free_space = dir_space.free
        return int(free_space)

    except Exception as e:
        # log in to the log file
        if logger_availability:
            logger.sendToLog("persepolis couldn't find free space value:\n" + str(e), "ERROR")

        return None


def returnDefaultSettings():
    # find available styles(It's depends on operating system and desktop environments).
    available_styles = QStyleFactory.keys()

    # Persepolis default setting
    default_setting_dict = ghermez.returnDefaultSettings(available_styles)

    return default_setting_dict


def muxer(parent, video_finder_dictionary):

    result_dictionary = {'error': 'no_error',
                         'ffmpeg_error_message': None,
                         'final_path': None,
                         'final_size': None}

    # find file path
    video_file_dictionary = parent.persepolis_db.searchGidInAddLinkTable(video_finder_dictionary['video_gid'])
    audio_file_dictionary = parent.persepolis_db.searchGidInAddLinkTable(video_finder_dictionary['audio_gid'])

    # find inputs and output file path for ffmpeg
    video_file_path = video_file_dictionary['download_path']
    audio_file_path = audio_file_dictionary['download_path']
    final_path = video_finder_dictionary['download_path']

    # calculate final file's size
    video_file_size = parent.persepolis_db.searchGidInDownloadTable(video_finder_dictionary['video_gid'])['size']
    audio_file_size = parent.persepolis_db.searchGidInDownloadTable(video_finder_dictionary['audio_gid'])['size']

    # convert size to byte
    video_file_size = convertToByte(video_file_size)
    audio_file_size = convertToByte(audio_file_size)

    final_file_size = video_file_size + audio_file_size

    # check free space
    free_space = freeSpace(final_path)

    if free_space:
        if final_file_size > free_space:
            result_dictionary['error'] = 'not enough free space'

        else:

            # find final file's name
            final_file_name = urllib.parse.unquote(os.path.basename(video_file_path))

            # if video's extension is 'mp4' then the final output file's extension is 'mp4'
            # if video's extension is 'webm' then the final output file's extension is 'mkv'

            file_name_split = final_file_name.split('.')
            video_extension = file_name_split[-1]

            if video_extension == 'webm':
                extension_length = len(file_name_split[-1]) + 1

                final_file_name = final_file_name[0:-extension_length] + '.mkv'

            if parent.persepolis_setting.value('settings/download_path') == final_path:
                if parent.persepolis_setting.value('settings/subfolder') == 'yes':
                    final_path = os.path.join(final_path, 'Videos')

            # rename file if file already existed
            i = 1
            final_path_plus_name = os.path.join(final_path, final_file_name)

            while os.path.isfile(final_path_plus_name):

                extension_length = len(file_name_split[-1]) + 1

                new_name = final_file_name[0:-extension_length] + \
                    '_' + str(i) + final_file_name[-extension_length:]

                final_path_plus_name = os.path.join(final_path, new_name)
                i = i + 1

            # start muxing
            if os_type in OS.UNIX_LIKE:
                pipe = subprocess.Popen(['ffmpeg', '-i', video_file_path,
                                         '-i', audio_file_path,
                                         '-c', 'copy',
                                         '-shortest',
                                         '-map', '0:v:0',
                                         '-map', '1:a:0',
                                         '-loglevel', 'error',
                                         '-strict', '-2',
                                         final_path_plus_name],
                                        stderr=subprocess.PIPE,
                                        stdout=subprocess.PIPE,
                                        stdin=subprocess.PIPE,
                                        shell=False)

            elif os_type == OS.DARWIN:
                # ffmpeg path in mac
                # ...path/Persepolis Download Manager.app/Contents/MacOS/ffmpeg
                cwd = sys.argv[0]
                current_directory = os.path.dirname(cwd)
                ffmpeg_path = os.path.join(current_directory, 'ffmpeg')

                pipe = subprocess.Popen([ffmpeg_path, '-i', video_file_path,
                                         '-i', audio_file_path,
                                         '-c', 'copy',
                                         '-shortest',
                                         '-map', '0:v:0',
                                         '-map', '1:a:0',
                                         '-loglevel', 'error',
                                         '-strict', '-2',
                                         final_path_plus_name],
                                        stderr=subprocess.PIPE,
                                        stdout=subprocess.PIPE,
                                        stdin=subprocess.PIPE,
                                        shell=False)

            elif os_type == OS.WINDOWS:
                # ffmpeg path in windows
                cwd = sys.argv[0]
                current_directory = os.path.dirname(cwd)
                ffmpeg_path = os.path.join(current_directory, 'ffmpeg.exe')

                # NO_WINDOW option avoids opening additional CMD window in MS Windows.
                NO_WINDOW = 0x08000000

                pipe = subprocess.Popen([ffmpeg_path, '-i', video_file_path,
                                         '-i', audio_file_path,
                                         '-c', 'copy',
                                         '-shortest',
                                         '-map', '0:v:0',
                                         '-map', '1:a:0',
                                         '-loglevel', 'error',
                                         '-strict', '-2',
                                         final_path_plus_name],
                                        stdout=subprocess.PIPE,
                                        stdin=subprocess.PIPE,
                                        stderr=subprocess.PIPE,
                                        shell=False,
                                        creationflags=NO_WINDOW)

            if pipe.wait() == 0:
                # muxing was finished successfully.
                result_dictionary['error'] = 'no error'

                result_dictionary['final_path'] = final_path_plus_name
                result_dictionary['final_size'] = humanReadableSize(final_file_size)

            else:
                result_dictionary['error'] = 'ffmpeg error'
                out, ffmpeg_error_message = pipe.communicate()

                result_dictionary['ffmpeg_error_message'] = ffmpeg_error_message.decode('utf-8', 'ignore')

    return result_dictionary

persepolis/scripts/useful_tools.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-
"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
try:
    from PySide6.QtWidgets import (
        QCheckBox, QPushButton, QTextEdit, QFrame, QLabel, QComboBox, QHBoxLayout, QApplication
    )
    from PySide6.QtCore import QThread, Signal, QCoreApplication, QTranslator, QLocale
except ImportError:
    from PyQt5.QtWidgets import QCheckBox, QPushButton, QTextEdit, QFrame, QLabel, QComboBox, QHBoxLayout, QApplication
    from PyQt5.QtCore import QThread, QCoreApplication, QTranslator, QLocale
    from PyQt5.QtCore import pyqtSignal as Signal

from ghermez import determineConfigFolder
from persepolis.scripts.addlink import AddLinkWindow
from persepolis.scripts import logger, osCommands
from persepolis.scripts.spider import spider
from time import time
from functools import partial
from random import random
from copy import deepcopy
import youtube_dl
import re
import os

# write youtube_dl version in log
logger.sendToLog('youtube_dl version: '
                 + str(youtube_dl.version.__version__),
                 'INFO')

# download manager config folder .
config_folder = determineConfigFolder()

# persepolis tmp folder path
persepolis_tmp = os.path.join(config_folder, 'persepolis_tmp')


class MediaListFetcherThread(QThread):
    RESULT = Signal(dict)
    cookies = '# HTTP cookie file.\n'  # We shall write it in a file when thread starts.

    def __init__(self, receiver_slot, video_dict, parent):
        super().__init__()
        self.RESULT.connect(receiver_slot)
        self.video_dict = video_dict

        self.cookie_path = os.path.join(persepolis_tmp, '.{}{}'.format(time(), random()))

        # youtube options must be added to youtube_dl_options_dict in dictionary format
        self.youtube_dl_options_dict = {'dump_single_json': True,
                                        'quiet': True,
                                        'noplaylist': True,
                                        'no_warnings': True
                                        }

        # cookies
        self.youtube_dl_options_dict['cookies'] = str(self.cookie_path)

        # referer
        if 'referer' in video_dict.keys() and video_dict['referer']:
            self.youtube_dl_options_dict['referer'] = str(video_dict['referer'])

        # user_agent
        if 'user_agent' in video_dict.keys() and video_dict['user_agent']:
            self.youtube_dl_options_dict['user-agent'] = str(video_dict['user_agent'])

        # load_cookies
        if 'load_cookies' in video_dict.keys() and video_dict['load_cookies']:
            # We need to convert raw cookies to http cookie file to use with youtube-dl.
            self.cookies = self.makeHttpCookie(video_dict['load_cookies'])

        # Proxy
        if 'ip' in video_dict.keys() and video_dict['ip']:
            try:
                # ip + port
                ip_port = 'http://{}:{}'.format(video_dict['ip'], video_dict['port'])

                if 'referer' in video_dict.keys() and video_dict['proxy_user']:
                    ip_port = 'http://{}:{}@{}'.format(video_dict['proxy_user'], video_dict['proxy_passwd'], ip_port)

                self.youtube_dl_options_dict['proxy'] = str(ip_port)
            except Exception:
                pass

        if 'download_user' in video_dict.keys() and video_dict['download_user']:
            try:
                self.youtube_dl_options_dict['username'] = str(video_dict['download_user'])
                self.youtube_dl_options_dict['password'] = str(video_dict['download_passwd'])
            except Exception:
                pass

        if 'link' in video_dict.keys() and video_dict['link']:
            self.youtube_link = str(video_dict['link'])

    def run(self):
        ret_val = {}

        try:  # Create cookie file
            cookie_file = open(self.cookie_path, 'w')
            cookie_file.write(self.cookies)
            cookie_file.close()

            ydl = youtube_dl.YoutubeDL(self.youtube_dl_options_dict)
            with ydl:
                result = ydl.extract_info(
                    self.youtube_link,
                    download=False
                )

            error = "error"  # Or comment out this line to show full stderr.
            if result:
                ret_val = result
            else:
                ret_val = {'error': str(error)}

        except Exception as ex:
            ret_val = {'error': str(ex)}
        finally:  # Delete cookie file
            try:
                osCommands.remove(self.cookie_path)

            except Exception as ex:
                logger.sendToLog(ex, "ERROR")

        self.RESULT.emit(ret_val)

    def makeHttpCookie(self, raw_cookie, host_name='.youtube.com'):
        cookies = '# HTTP cookie file.\n'
        if raw_cookie:
            try:
                raw_cookies = re.split(';\s*', str(raw_cookie))
                # Format all cookie values as netscape cookie.
                for c in raw_cookies:
                    key, val = c.split('=', 1)
                    cookies = cookies + '{}\tTRUE\t/\tFALSE\t{}\t{}\t{}\n'. \
                        format(host_name, int(time()) + 259200, key, val)  # Expires after 3 days.
            except Exception:
                pass

        return cookies


class FileSizeFetcherThread(QThread):
    FOUND = Signal(dict)

    def __init__(self, dictionary, thread_key):
        super().__init__()
        self.dictionary = dictionary
        self.key = thread_key

    def run(self):
        spider_file_size = spider(self.dictionary)[1]
        self.FOUND.emit({'thread_key': self.key,
                         'file_size': spider_file_size})


class VideoFinderAddLink(AddLinkWindow):
    running_thread = None
    threadPool = {}

    def __init__(self, parent, receiver_slot, settings, video_dict={}):
        super().__init__(parent, receiver_slot, settings, video_dict)
        self.setWindowTitle(QCoreApplication.translate("ytaddlink_src_ui_tr", 'Video Finder'))
        self.size_label.hide()

        # empty lists for no_audio and no_video and video_audio files
        self.no_audio_list = []
        self.no_video_list = []
        self.video_audio_list = []

        self.media_title = ''

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # extension_label
        self.extension_label = QLabel(self.link_frame)
        self.change_name_horizontalLayout.addWidget(self.extension_label)

        # Fetch Button
        self.url_submit_pushButtontton = QPushButton(self.link_frame)
        self.link_horizontalLayout.addWidget(self.url_submit_pushButtontton)

        # Status Box
        self.status_box_textEdit = QTextEdit(self.link_frame)
        self.status_box_textEdit.setMaximumHeight(150)
        self.link_verticalLayout.addWidget(self.status_box_textEdit)

        # Select format horizontal layout
        select_format_horizontalLayout = QHBoxLayout()

        # Selection Label
        self.select_format_label = QLabel(self.link_frame)
        select_format_horizontalLayout.addWidget(self.select_format_label)

        # Selection combobox
        self.media_comboBox = QComboBox(self.link_frame)
        self.media_comboBox.setMinimumWidth(200)
        select_format_horizontalLayout.addWidget(self.media_comboBox)

        # Duration label
        self.duration_label = QLabel(self.link_frame)
        select_format_horizontalLayout.addWidget(self.duration_label)

        self.format_selection_frame = QFrame(self)
        self.format_selection_frame.setLayout(select_format_horizontalLayout)
        self.link_verticalLayout.addWidget(self.format_selection_frame)

        # advanced_format_selection_checkBox
        self.advanced_format_selection_checkBox = QCheckBox(self)
        self.link_verticalLayout.addWidget(self.advanced_format_selection_checkBox)

        # advanced_format_selection_frame
        self.advanced_format_selection_frame = QFrame(self)
        self.link_verticalLayout.addWidget(self.advanced_format_selection_frame)

        advanced_format_selection_horizontalLayout = QHBoxLayout(self.advanced_format_selection_frame)

        # video_format_selection
        self.video_format_selection_label = QLabel(self.advanced_format_selection_frame)
        self.video_format_selection_comboBox = QComboBox(self.advanced_format_selection_frame)

        # audio_format_selection
        self.audio_format_selection_label = QLabel(self.advanced_format_selection_frame)
        self.audio_format_selection_comboBox = QComboBox(self.advanced_format_selection_frame)

        for widget in [self.video_format_selection_label,
                       self.video_format_selection_comboBox,
                       self.audio_format_selection_label,
                       self.audio_format_selection_comboBox]:
            advanced_format_selection_horizontalLayout.addWidget(widget)

        # Set Texts
        self.url_submit_pushButtontton.setText(QCoreApplication.translate("ytaddlink_src_ui_tr", 'Fetch Media List'))
        self.select_format_label.setText(QCoreApplication.translate("ytaddlink_src_ui_tr", 'Select a format'))

        self.video_format_selection_label.setText(QCoreApplication.translate("ytaddlink_src_ui_tr", 'Video format:'))
        self.audio_format_selection_label.setText(QCoreApplication.translate("ytaddlink_src_ui_tr", 'Audio format:'))

        self.advanced_format_selection_checkBox.setText(
            QCoreApplication.translate("ytaddlink_src_ui_tr", 'Advanced options'))

        # Add Slot Connections
        self.url_submit_pushButtontton.setEnabled(False)
        self.change_name_lineEdit.setEnabled(False)
        self.ok_pushButton.setEnabled(False)
        self.download_later_pushButton.setEnabled(False)

        self.format_selection_frame.setEnabled(True)
        self.advanced_format_selection_frame.setEnabled(False)
        self.advanced_format_selection_checkBox.toggled.connect(self.advancedFormatFrame)

        self.url_submit_pushButtontton.clicked.connect(self.submitClicked)

        self.media_comboBox.activated.connect(
            partial(self.mediaSelectionChanged, 'video_audio'))

        self.video_format_selection_comboBox.activated.connect(
            partial(self.mediaSelectionChanged, 'video'))

        self.audio_format_selection_comboBox.activated.connect(
            partial(self.mediaSelectionChanged, 'audio'))

        self.link_lineEdit.textChanged.disconnect(super().linkLineChanged)  # Should be disconnected.
        self.link_lineEdit.textChanged.connect(self.linkLineChangedHere)

        self.setMinimumSize(650, 480)

        self.status_box_textEdit.hide()
        self.format_selection_frame.hide()
        self.advanced_format_selection_frame.hide()
        self.advanced_format_selection_checkBox.hide()

        if 'link' in video_dict.keys() and video_dict['link']:
            self.link_lineEdit.setText(video_dict['link'])
            self.url_submit_pushButtontton.setEnabled(True)
        else:
            # check clipboard
            clipboard = QApplication.clipboard()
            text = clipboard.text()
            if (("tp:/" in text[2:6]) or ("tps:/" in text[2:7])):
                self.link_lineEdit.setText(str(text))

            self.url_submit_pushButtontton.setEnabled(True)

    def advancedFormatFrame(self, button):
        if self.advanced_format_selection_checkBox.isChecked():

            self.advanced_format_selection_frame.setEnabled(True)
            self.format_selection_frame.setEnabled(False)
            self.mediaSelectionChanged('video', int(self.video_format_selection_comboBox.currentIndex()))

        else:
            self.advanced_format_selection_frame.setEnabled(False)
            self.format_selection_frame.setEnabled(True)
            self.mediaSelectionChanged('video_audio', int(self.media_comboBox.currentIndex()))

    def getReadableSize(self, size):
        try:
            return '{:1.2f} MB'.format(int(size) / 1048576)
        except ValueError:
            return str(size)

    def getReadableDuration(self, seconds):
        try:
            seconds = int(seconds)
            hours = seconds // 3600
            seconds = seconds % 3600
            minutes = seconds // 60
            seconds = seconds % 60
            return '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
        except ValueError:
            return str(seconds)

    # Define native slots

    def urlChanged(self, value):
        if ' ' in value or value == '':
            self.url_submit_pushButtontton.setEnabled(False)
            self.url_submit_pushButtontton.setToolTip(QCoreApplication.translate(
                "ytaddlink_src_ui_tr", 'Please enter a valid video link'))
        else:
            self.url_submit_pushButtontton.setEnabled(True)
            self.url_submit_pushButtontton.setToolTip('')

    def submitClicked(self, button=None):
        # Clear media list
        self.media_comboBox.clear()
        self.format_selection_frame.hide()
        self.advanced_format_selection_checkBox.hide()
        self.advanced_format_selection_frame.hide()
        self.video_format_selection_comboBox.clear()
        self.audio_format_selection_comboBox.clear()
        self.change_name_lineEdit.clear()
        self.threadPool.clear()
        self.change_name_checkBox.setChecked(False)
        self.video_audio_list.clear()
        self.no_video_list.clear()
        self.no_audio_list.clear()
        self.url_submit_pushButtontton.setEnabled(False)
        self.status_box_textEdit.setText(QCoreApplication.translate("ytaddlink_src_ui_tr", 'Fetching Media Info...'))
        self.status_box_textEdit.show()
        self.ok_pushButton.setEnabled(False)
        self.download_later_pushButton.setEnabled(False)

        dictionary_to_send = deepcopy(self.plugin_add_link_dictionary)
        # More options
        more_options = self.collectMoreOptions()
        for k in more_options.keys():
            dictionary_to_send[k] = more_options[k]
        dictionary_to_send['link'] = self.link_lineEdit.text()

        fetcher_thread = MediaListFetcherThread(self.fetchedResult, dictionary_to_send, self)
        self.parent.threadPool.append(fetcher_thread)
        self.parent.threadPool[-1].start()

    def fileNameChanged(self, value):
        if value.strip() == '':
            self.ok_pushButton.setEnabled(False)

    def mediaSelectionChanged(self, combobox, index):
        try:
            if combobox == 'video_audio':
                if self.media_comboBox.currentText() == 'Best quality':
                    self.change_name_lineEdit.setText(self.media_title)
                    self.extension_label.setText('.' + self.no_audio_list[-1]['ext'])

                else:
                    self.change_name_lineEdit.setText(self.media_title)
                    self.extension_label.setText('.' + self.video_audio_list[index]['ext'])

                self.change_name_checkBox.setChecked(True)

            elif combobox == 'video':
                if self.video_format_selection_comboBox.currentText() != 'No video':
                    self.change_name_lineEdit.setText(self.media_title)
                    self.extension_label.setText('.' + self.no_audio_list[index - 1]['ext'])
                    self.change_name_checkBox.setChecked(True)

                else:

                    if self.audio_format_selection_comboBox.currentText() != 'No audio':
                        self.change_name_lineEdit.setText(self.media_title)
                        self.extension_label.setText('.' +
                                self.no_video_list[int(self.audio_format_selection_comboBox.currentIndex()) - 1]['ext'])

                        self.change_name_checkBox.setChecked(True)
                    else:
                        self.change_name_lineEdit.setChecked(False)

            elif combobox == 'audio':
                if self.audio_format_selection_comboBox.currentText() != 'No audio' and \
                    self.video_format_selection_comboBox.currentText() == 'No video':
                    self.change_name_lineEdit.setText(self.media_title)
                    self.extension_label.setText('.' +
                                                 self.no_video_list[index - 1]['ext'])

                    self.change_name_checkBox.setChecked(True)

                elif (self.audio_format_selection_comboBox.currentText() == 'No audio' and \
                      self.video_format_selection_comboBox.currentText() != 'No video') or \
                        (self.audio_format_selection_comboBox.currentText() != 'No audio' and \
                         self.video_format_selection_comboBox.currentText() != 'No video'):
                    self.change_name_lineEdit.setText(self.media_title)
                    self.extension_label.setText('.' +
                                self.no_audio_list[int(self.video_format_selection_comboBox.currentIndex()) - 1]['ext'])

                    self.change_name_checkBox.setChecked(True)

                elif self.audio_format_selection_comboBox.currentText() == 'No audio' and \
                    self.video_format_selection_comboBox.currentText() == 'No video':
                    self.change_name_checkBox.setChecked(False)

        except Exception as ex:
            logger.sendToLog(ex, "ERROR")

    def fetchedResult(self, media_dict):

        self.url_submit_pushButtontton.setEnabled(True)
        if 'error' in media_dict.keys():

            self.status_box_textEdit.setText('<font color="#f11">' + str(media_dict['error']) + '</font>')
            self.status_box_textEdit.show()
        else:  # Show the media list

            # add no audio and no video options to the comboboxes
            self.video_format_selection_comboBox.addItem('No video')
            self.audio_format_selection_comboBox.addItem('No audio')

            self.media_title = media_dict['title']
            if 'formats' not in media_dict.keys() and 'entries' in media_dict.keys():
                formats = media_dict['entries']
                formats = formats[0]
                media_dict['formats'] = formats['formats']
            elif 'formats' not in media_dict.keys() and 'format' in media_dict.keys():
                media_dict['formats'] = [media_dict.copy()]

            try:
                i = 0
                for f in media_dict['formats']:
                    no_audio = False
                    no_video = False
                    text = ''
                    if 'acodec' in f.keys():
                        # only video, no audio
                        if f['acodec'] == 'none':
                            no_audio = True

                        # resolution
                        if 'height' in f.keys():
                            text = text + ' ' + '{}p'.format(f['height'])

                    if 'vcodec' in f.keys():
                        #                         if f['vcodec'] == 'none' and f['acodec'] != 'none':
                        #                             continue

                        # No video, show audio bit rate
                        if f['vcodec'] == 'none':
                            text = text + '{}kbps'.format(f['abr'])
                            no_video = True

                    if 'ext' in f.keys():
                        text = text + ' ' + '.{}'.format(f['ext'])

                    if 'filesize' in f.keys() and f['filesize']:
                        # Youtube api does not supply file size for some formats, so check it.
                        text = text + ' ' + '{}'.format(self.getReadableSize(f['filesize']))

                    else:  # Start spider to find file size
                        input_dict = deepcopy(self.plugin_add_link_dictionary)

                        input_dict['link'] = f['url']
                        more_options = self.collectMoreOptions()

                        for key in more_options.keys():
                            input_dict[key] = more_options[key]

                        size_fetcher = FileSizeFetcherThread(input_dict, i)
                        self.threadPool[str(i)] = {'thread': size_fetcher, 'item_id': i}
                        self.parent.threadPool.append(size_fetcher)
                        self.parent.threadPool[-1].start()
                        self.parent.threadPool[-1].FOUND.connect(self.findFileSize)

                    # Add current format to the related comboboxes
                    if no_audio:
                        self.no_audio_list.append(f)
                        self.video_format_selection_comboBox.addItem(text)

                    elif no_video:
                        self.no_video_list.append(f)
                        self.audio_format_selection_comboBox.addItem(text)

                    else:
                        self.video_audio_list.append(f)
                        self.media_comboBox.addItem(text)

                    i = i + 1

                self.status_box_textEdit.hide()

                if 'duration' in media_dict.keys():
                    self.duration_label.setText('Duration ' + self.getReadableDuration(media_dict['duration']))

                self.format_selection_frame.show()
                self.advanced_format_selection_checkBox.show()
                self.advanced_format_selection_frame.show()
                self.ok_pushButton.setEnabled(True)
                self.download_later_pushButton.setEnabled(True)

                # if we have no options for separate audio and video, then hide advanced_format_selection...
                if len(self.no_audio_list) == 0 and len(self.no_video_list) == 0:
                    self.advanced_format_selection_checkBox.hide()
                    self.advanced_format_selection_frame.hide()

                # set index of comboboxes on best available quality.
                # we have both audio and video
                if len(self.no_audio_list) != 0 and len(self.no_video_list) != 0:
                    self.media_comboBox.addItem('Best quality')
                    self.media_comboBox.setCurrentIndex(len(self.video_audio_list))
                    self.change_name_lineEdit.setText(self.media_title)
                    self.extension_label.setText('.' + self.no_audio_list[-1]['ext'])
                    self.change_name_checkBox.setChecked(True)

                # video and audio are not separate
                elif len(self.video_audio_list) != 0:
                    self.media_comboBox.setCurrentIndex(len(self.video_audio_list) - 1)

                if len(self.no_audio_list) != 0:
                    self.video_format_selection_comboBox.setCurrentIndex(len(self.no_audio_list))

                if len(self.no_video_list) != 0:
                    self.audio_format_selection_comboBox.setCurrentIndex(len(self.no_video_list))

                # if we have only audio or we have only video then hide media_comboBox
                if len(self.video_audio_list) == 0:
                    self.media_comboBox.hide()
                    self.select_format_label.hide()

                    # only video
                    if len(self.no_video_list) != 0 and len(self.no_audio_list) == 0:
                        self.mediaSelectionChanged('video', int(self.video_format_selection_comboBox.currentIndex()))
                        self.advanced_format_selection_checkBox.setChecked(True)
                        self.advanced_format_selection_checkBox.hide()

                    # only audio
                    elif len(self.no_video_list) == 0 and len(self.no_audio_list) != 0:
                        self.mediaSelectionChanged('audio', int(self.audio_format_selection_comboBox.currentIndex()))
                        self.advanced_format_selection_checkBox.setChecked(True)
                        self.advanced_format_selection_checkBox.hide()

                    # audio and video
                    else:
                        self.mediaSelectionChanged('video_audio', int(self.media_comboBox.currentIndex()))

            except Exception as ex:
                logger.sendToLog(ex, "ERROR")

    def findFileSize(self, result):
        try:
            item_id = self.threadPool[str(result['thread_key'])]['item_id']
            if result['file_size'] and result['file_size'] != '0':
                text = self.media_comboBox.itemText(item_id)
                self.media_comboBox.setItemText(item_id, '{} - {}'.format(text, result['file_size']))
        except Exception as ex:
            logger.sendToLog(ex, "ERROR")

    def linkLineChangedHere(self, lineEdit):
        if str(lineEdit) == '':
            self.url_submit_pushButtontton.setEnabled(False)
        else:
            self.url_submit_pushButtontton.setEnabled(True)

    # This method collects additional information like proxy ip, user, password etc.
    def collectMoreOptions(self):
        options = {'ip': None, 'port': None, 'proxy_user': None, 'proxy_passwd': None, 'download_user': None,
                   'download_passwd': None}
        if self.proxy_checkBox.isChecked():
            options['ip'] = self.ip_lineEdit.text()
            options['port'] = self.port_spinBox.value()
            options['proxy_user'] = self.proxy_user_lineEdit.text()
            options['proxy_passwd'] = self.proxy_pass_lineEdit.text()
        if self.download_checkBox.isChecked():
            options['download_user'] = self.download_user_lineEdit.text()
            options['download_passwd'] = self.download_pass_lineEdit.text()

        # These info (keys) are required for spider to find file size, because spider() does not check if key exists.
        additional_info = ['header', 'load_cookies', 'user_agent', 'referer', 'out']
        for i in additional_info:
            if i not in self.plugin_add_link_dictionary.keys():
                options[i] = None
        return options

    # user submitted information by pressing ok_pushButton, so get information
    # from VideoFinderAddLink window and return them to the mainwindow with callback!
    def okButtonPressed(self, download_later, button=None):

        link_list = []
        # separate audio format and video format is selected.
        if self.advanced_format_selection_checkBox.isChecked():

            if self.video_format_selection_comboBox.currentText() == 'No video' and \
                self.audio_format_selection_comboBox.currentText() != 'No audio':

                # only audio link must be added to the link_list
                audio_link = self.no_video_list[self.audio_format_selection_comboBox.currentIndex() - 1]['url']
                link_list.append(audio_link)

            elif self.video_format_selection_comboBox.currentText() != 'No video' and \
                self.audio_format_selection_comboBox.currentText() == 'No audio':

                # only video link must be added to the link_list
                video_link = self.no_audio_list[self.video_format_selection_comboBox.currentIndex() - 1]['url']
                link_list.append(video_link)

            elif self.video_format_selection_comboBox.currentText() != 'No video' and \
                self.audio_format_selection_comboBox.currentText() != 'No audio':

                # video and audio links must be added to the link_list
                audio_link = self.no_video_list[self.audio_format_selection_comboBox.currentIndex() - 1]['url']
                video_link = self.no_audio_list[self.video_format_selection_comboBox.currentIndex() - 1]['url']
                link_list = [video_link, audio_link]

            elif self.video_format_selection_comboBox.currentText() == 'No video' and \
                self.audio_format_selection_comboBox.currentText() == 'No audio':

                # no video and audio is selected! REALLY?!. user is DRUNK! close the window! :))
                self.close()
        else:
            if self.media_comboBox.currentText() == 'Best quality':

                # the last item in no_video_list and no_audio_list are the best.
                video_link = self.no_audio_list[-1]['url']
                audio_link = self.no_video_list[-1]['url']

                link_list = [video_link, audio_link]

            else:
                audio_and_video_link = self.video_audio_list[self.media_comboBox.currentIndex()]['url']
                link_list.append(audio_and_video_link)


        # write user's new inputs in persepolis_setting for next time :)
        self.persepolis_setting.setValue(
            'add_link_initialization/ip', self.ip_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/port', self.port_spinBox.value())
        self.persepolis_setting.setValue(
            'add_link_initialization/proxy_user', self.proxy_user_lineEdit.text())
        self.persepolis_setting.setValue(
            'add_link_initialization/download_user', self.download_user_lineEdit.text())

        # get proxy information
        if not(self.proxy_checkBox.isChecked()):
            ip = None
            port = None
            proxy_user = None
            proxy_passwd = None
        else:
            ip = self.ip_lineEdit.text()
            if not(ip):
                ip = None
            port = self.port_spinBox.value()
            if not(port):
                port = None
            proxy_user = self.proxy_user_lineEdit.text()
            if not(proxy_user):
                proxy_user = None
            proxy_passwd = self.proxy_pass_lineEdit.text()
            if not(proxy_passwd):
                proxy_passwd = None

        # get download username and password information
        if not(self.download_checkBox.isChecked()):
            download_user = None
            download_passwd = None
        else:
            download_user = self.download_user_lineEdit.text()
            if not(download_user):
                download_user = None
            download_passwd = self.download_pass_lineEdit.text()
            if not(download_passwd):
                download_passwd = None

        # check that if user limits download speed.
        if not(self.limit_checkBox.isChecked()):
            limit = 0
        else:
            if self.limit_comboBox.currentText() == "KiB/s":
                limit = str(self.limit_spinBox.value()) + str("K")
            else:
                limit = str(self.limit_spinBox.value()) + str("M")

        # get start time for download if user set that.
        if not(self.start_checkBox.isChecked()):
            start_time = None
        else:
            start_time = self.start_time_qDataTimeEdit.text()

        # get end time for download if user set that.
        if not(self.end_checkBox.isChecked()):
            end_time = None
        else:
            end_time = self.end_time_qDateTimeEdit.text()

        # set name for file(s)
        if self.change_name_checkBox.isChecked():
            name = str(self.change_name_lineEdit.text())
            if name == '':
                name = 'video_finder_file'
        else:
            name = 'video_finder_file'

        # video finder always finds extension
        # but if it can't find file extension
        # use mp4 for extension.
        if str(self.extension_label.text()) == '':
            extension = '.mp4'
        else:
            extension = str(self.extension_label.text())

        # did user select separate audio and video?
        if len(link_list) == 2:
            video_name = name + extension
            audio_name = name + '.' + \
                str(self.no_video_list[self.audio_format_selection_comboBox.currentIndex() - 1]['ext'])

            name_list = [video_name, audio_name]
        else:
            name_list = [name + extension]

        # get number of connections
        connections = self.connections_spinBox.value()

        # get download_path
        download_path = self.download_folder_lineEdit.text()

        # referer
        if self.referer_lineEdit.text() != '':
            referer = self.referer_lineEdit.text()
        else:
            referer = None

        # header
        if self.header_lineEdit.text() != '':
            header = self.header_lineEdit.text()
        else:
            header = None

        # user_agent
        if self.user_agent_lineEdit.text() != '':
            user_agent = self.user_agent_lineEdit.text()
        else:
            user_agent = None

        # load_cookies
        if self.load_cookies_lineEdit.text() != '':
            load_cookies = self.load_cookies_lineEdit.text()
        else:
            load_cookies = None

        add_link_dictionary_list = []
        if len(link_list) == 1:
            # save information in a dictionary(add_link_dictionary).
            add_link_dictionary = {
                'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies,
                'out': name_list[0], 'start_time': start_time, 'end_time': end_time, 'link': link_list[0], 'ip': ip,
                'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd,
                'download_user': download_user, 'download_passwd': download_passwd,
                'connections': connections, 'limit_value': limit, 'download_path': download_path
            }

            add_link_dictionary_list.append(add_link_dictionary)

        else:
            video_add_link_dictionary = {
                'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies,
                'out': name_list[0], 'start_time': start_time, 'end_time': end_time, 'link': link_list[0], 'ip': ip,
                'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd,
                'download_user': download_user, 'download_passwd': download_passwd,
                'connections': connections, 'limit_value': limit, 'download_path': download_path
            }

            audio_add_link_dictionary = {
                'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies,
                'out': name_list[1], 'start_time': None, 'end_time': end_time, 'link': link_list[1], 'ip': ip,
                'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd,
                'download_user': download_user, 'download_passwd': download_passwd,
                'connections': connections, 'limit_value': limit, 'download_path': download_path
            }

            add_link_dictionary_list = [video_add_link_dictionary, audio_add_link_dictionary]

        # get category of download
        category = str(self.add_queue_comboBox.currentText())

        del self.plugin_add_link_dictionary

        # return information to mainwindow
        self.callback(add_link_dictionary_list, download_later, category)

        # close window
        self.close()

persepolis/scripts/video_finder_addlink.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
try:
    from PySide6.QtCore import Qt, QSize, QPoint, QThread, QTranslator, QCoreApplication, QLocale
    from PySide6.QtWidgets import QLineEdit, QInputDialog
    from PySide6.QtGui import QIcon
except ImportError:
    from PyQt5.QtCore import Qt, QSize, QPoint, QThread, QTranslator, QCoreApplication, QLocale
    from PyQt5.QtWidgets import QLineEdit, QInputDialog
    from PyQt5.QtGui import QIcon


from persepolis.constants import OS
from persepolis.gui.video_finder_progress_ui import VideoFinderProgressWindow_Ui
from persepolis.scripts.shutdown import shutDown
from persepolis.scripts.bubble import notifySend
from persepolis.scripts import download
import subprocess
import platform

os_type = platform.system()


class ShutDownThread(QThread):
    def __init__(self, parent, category, password=None):
        QThread.__init__(self)
        self.category = category
        self.password = password
        self.parent = parent

    def run(self):
        shutDown(self.parent, category=self.category, password=self.password)


class VideoFinderProgressWindow(VideoFinderProgressWindow_Ui):
    def __init__(self, parent, gid_list, persepolis_setting):
        super().__init__(persepolis_setting)
        self.persepolis_setting = persepolis_setting
        self.parent = parent

        # first item in the gid_list is related to video's link and second item is related to audio's link.
        self.gid_list = gid_list

        # this variable can be changed by checkDownloadInfo method in mainwindow.py
        # self.gid defines that which gid is downloaded.
        self.gid = gid_list[0]

        # this variable used as category name in ShutDownThread
        self.video_finder_plus_gid = 'video_finder_' + str(gid_list[0])

        # connect signals and sluts
        self.resume_pushButton.clicked.connect(self.resumePushButtonPressed)
        self.stop_pushButton.clicked.connect(self.stopPushButtonPressed)
        self.pause_pushButton.clicked.connect(self.pausePushButtonPressed)
        self.download_progressBar.setValue(0)
        self.limit_pushButton.clicked.connect(self.limitPushButtonPressed)

        self.limit_frame.setEnabled(False)
        self.limit_checkBox.toggled.connect(self.limitCheckBoxToggled)

        self.after_frame.setEnabled(False)
        self.after_checkBox.toggled.connect(self.afterCheckBoxToggled)

        self.after_pushButton.clicked.connect(self.afterPushButtonPressed)

        # add support for other languages
        locale = str(self.persepolis_setting.value('settings/locale'))
        QLocale.setDefault(QLocale(locale))
        self.translator = QTranslator()
        if self.translator.load(':/translations/locales/ui_' + locale, 'ts'):
            QCoreApplication.installTranslator(self.translator)

        # check if limit speed is activated by user or not
        add_link_dictionary = self.parent.persepolis_db.searchGidInAddLinkTable(gid_list[0])

        limit = str(add_link_dictionary['limit_value'])
        if limit != '0':
            limit_number = limit[:-1]
            limit_unit = limit[-1]
            self.limit_spinBox.setValue(float(limit_number))
            if limit_unit == 'K':
                self.after_comboBox.setCurrentIndex(0)
            else:
                self.after_comboBox.setCurrentIndex(1)
            self.limit_checkBox.setChecked(True)

        self.after_comboBox.currentIndexChanged.connect(self.afterComboBoxChanged)

        self.limit_comboBox.currentIndexChanged.connect(self.limitComboBoxChanged)

        self.limit_spinBox.valueChanged.connect(self.limitComboBoxChanged)

        # set window size and position
        size = self.persepolis_setting.value(
            'ProgressWindow/size', QSize(595, 274))
        position = self.persepolis_setting.value(
            'ProgressWindow/position', QPoint(300, 300))
        self.resize(size)
        self.move(position)

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):

        # save window size and position
        self.persepolis_setting.setValue('ProgressWindow/size', self.size())
        self.persepolis_setting.setValue('ProgressWindow/position', self.pos())
        self.persepolis_setting.sync()

        self.hide()

    def resumePushButtonPressed(self, button):
        if self.status == "paused":
            answer = download.downloadUnpause(self.gid)
            # if aria2 did not respond , then this function is checking for aria2
            # availability , and if aria2 disconnected then aria2Disconnected is
            # executed
            if not(answer):
                version_answer = download.aria2Version()
                if version_answer == 'did not respond':
                    self.parent.aria2Disconnected()
                    notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 disconnected!"),
                               QCoreApplication.translate("progress_src_ui_tr",
                                                          "Persepolis is trying to connect! be patient!"),
                               10000, 'warning', parent=self.parent)
                else:
                    notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 did not respond!"),
                               QCoreApplication.translate("progress_src_ui_tr", "Please try again."), 10000,
                               'warning', parent=self.parent)

    def pausePushButtonPressed(self, button):

        if self.status == "downloading":
            answer = download.downloadPause(self.gid)

            # if aria2 did not respond , then this function is checking for aria2
            # availability , and if aria2 disconnected then aria2Disconnected is
            # executed
            if not(answer):
                version_answer = download.aria2Version()
                if version_answer == 'did not respond':
                    self.parent.aria2Disconnected()
                    download.downloadStop(self.gid, self.parent)
                    notifySend("Aria2 disconnected!", "Persepolis is trying to connect! be patient!",
                               10000, 'warning', parent=self.parent)
                else:
                    notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 did not respond!"),
                               QCoreApplication.translate("progress_src_ui_tr", "Try again!"), 10000,
                               'critical', parent=self.parent)

    def stopPushButtonPressed(self, button):

        # cancel shut down progress
        dictionary = {'category': self.video_finder_plus_gid,
                      'shutdown': 'canceled'}

        self.parent.temp_db.updateQueueTable(dictionary)

        answer = download.downloadStop(self.gid, self.parent)

        # if aria2 did not respond , then this function is checking for aria2
        # availability , and if aria2 disconnected then aria2Disconnected is
        # executed
        if answer == 'None':
            version_answer = download.aria2Version()
            if version_answer == 'did not respond':
                self.parent.aria2Disconnected()
                notifySend(QCoreApplication.translate("progress_src_ui_tr", "Aria2 disconnected!"),
                           QCoreApplication.translate("progress_src_ui_tr",
                                                      "Persepolis is trying to connect! be patient!"),
                           10000, 'warning', parent=self.parent)

    def limitCheckBoxToggled(self, checkBoxes):

        # user checked limit_checkBox
        if self.limit_checkBox.isChecked():
            self.limit_frame.setEnabled(True)
            self.limit_pushButton.setEnabled(True)

        # user unchecked limit_checkBox
        else:
            self.limit_frame.setEnabled(False)

            # check download status is "scheduled" or not!
            for i in [0, 1]:
                gid = self.gid_list[i]
                dictionary = self.parent.persepolis_db.searchGidInDownloadTable(gid)
                status = dictionary['status']

                if status != 'scheduled':

                    # tell aria2 for unlimited speed
                    download.limitSpeed(gid, "0")

                else:
                    # update limit value in data_base
                    add_link_dictionary = {'gid': gid, 'limit_value': '0'}
                    self.parent.persepolis_db.updateAddLinkTable([add_link_dictionary])

    def limitComboBoxChanged(self, connect):

        self.limit_pushButton.setEnabled(True)

    def afterComboBoxChanged(self, connect):

        self.after_pushButton.setEnabled(True)

    def afterCheckBoxToggled(self, checkBoxes):

        if self.after_checkBox.isChecked():

            self.after_frame.setEnabled(True)
        else:

            # so user canceled shutdown after download
            # write cancel value in data_base for this gid
            dictionary = {'category': self.video_finder_plus_gid,
                          'shutdown': 'canceled'}

            self.parent.temp_db.updateQueueTable(dictionary)

    def afterPushButtonPressed(self, button):

        self.after_pushButton.setEnabled(False)

        # For Linux and Mac OSX and FreeBSD and OpenBSD
        if os_type != OS.WINDOWS:

            # get root password
            passwd, ok = QInputDialog.getText(
                self, 'PassWord', 'Please enter root password:', QLineEdit.Password)

            if ok:

                # check password is true or not!
                pipe = subprocess.Popen(['sudo', '-S', 'echo', 'hello'],
                                        stdout=subprocess.DEVNULL,
                                        stdin=subprocess.PIPE,
                                        stderr=subprocess.DEVNULL,
                                        shell=False)

                pipe.communicate(passwd.encode())

                answer = pipe.wait()

                # Wrong password
                while answer != 0:

                    passwd, ok = QInputDialog.getText(
                        self, 'PassWord', 'Wrong Password!\nPlease try again.', QLineEdit.Password)

                    if ok:

                        pipe = subprocess.Popen(['sudo', '-S', 'echo', 'hello'],
                                                stdout=subprocess.DEVNULL,
                                                stdin=subprocess.PIPE,
                                                stderr=subprocess.DEVNULL,
                                                shell=False)

                        pipe.communicate(passwd.encode())

                        answer = pipe.wait()

                    else:
                        ok = False
                        break

                if ok is not False:

                    # if user selects shutdown option after download progress,
                    # value of 'shutdown' will changed in temp_db for this progress
                    # and "wait" word will be written for this value.
                    # (see ShutDownThread and shutdown.py for more information)
                    # shutDown method will check that value in a loop .
                    # when "wait" changes to "shutdown" then shutdown.py script
                    # will shut down the system.

                    shutdown_enable = ShutDownThread(self.parent, self.video_finder_plus_gid, passwd)
                    self.parent.threadPool.append(shutdown_enable)
                    self.parent.threadPool[-1].start()

                else:
                    self.after_checkBox.setChecked(False)
            else:
                self.after_checkBox.setChecked(False)

        else:
            # for Windows
            for gid in self.gid_list:
                shutdown_enable = ShutDownThread(self.parent, self.video_finder_plus_gid)
                self.parent.threadPool.append(shutdown_enable)
                self.parent.threadPool[-1].start()

    def limitPushButtonPressed(self, button):

        self.limit_pushButton.setEnabled(False)

        if self.limit_comboBox.currentText() == "KiB/s":

            limit_value = str(self.limit_spinBox.value()) + str("K")
        else:
            limit_value = str(self.limit_spinBox.value()) + str("M")

        # if download was started before , send the limit_speed request to aria2 .
        # else save the request in data_base
        for i in [0, 1]:

            gid = self.gid_list[i]
            dictionary = self.parent.persepolis_db.searchGidInDownloadTable(gid)
            status = dictionary['status']

            if status != 'scheduled':

                download.limitSpeed(self.gid, limit_value)
            else:
                # update limit value in data_base
                add_link_dictionary = {'gid': gid, 'limit_value': limit_value}
                self.parent.persepolis_db.updateAddLinkTable([add_link_dictionary])

    def changeIcon(self, icons):
        icons = ':/' + str(icons) + '/'

        self.resume_pushButton.setIcon(QIcon(icons + 'play'))
        self.pause_pushButton.setIcon(QIcon(icons + 'pause'))
        self.stop_pushButton.setIcon(QIcon(icons + 'stop'))

persepolis/scripts/video_finder_progress.py
IamRezaMousavi/ghermez
# -*- coding: utf-8 -*-

"""
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
try:
    from PySide6.QtCore import Qt, QThread, Signal
except ImportError:
    from PyQt5.QtCore import Qt, QThread
    from PyQt5.QtCore import pyqtSignal as Signal

from persepolis.gui.windows_notification_ui import Windows_Notification_UI
from time import sleep


class TimerThread(QThread):
    TIMEISUP = Signal()

    def __init__(self, time):
        QThread.__init__(self)
        self.time = float(int(time)/1000)

    def run(self):
        sleep(self.time)
        self.TIMEISUP.emit()


class Windows_Notification(Windows_Notification_UI):
    def __init__(self, parent, time, text1, text2, persepolis_setting):
        super().__init__(parent, persepolis_setting)

        # run timer and close notification after time is up.
        timer = TimerThread(time)
        parent.threadPool.append(timer)
        parent.threadPool[-1].start()
        parent.threadPool[-1].TIMEISUP.connect(self.close)

        # set text to the labels
        self.label1.setText(str(text1))
        self.label2.setText(str(text2))

    def mousePressEvent(self, event):
        self.close()

    # close window with ESC key
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


    def closeEvent(self, event):
        event.accept()

persepolis/scripts/windows_notification.py
IamRezaMousavi/ghermez
#!/usr/bin/env python3
# coding: utf-8
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


import os
import warnings
import sys
import platform
import shutil

# finding os platform
os_type = platform.system()

if os_type == 'Linux' or os_type == 'FreeBSD' or os_type == 'OpenBSD':
    from setuptools import setup, Command, find_packages
    setuptools_available = True
    print(os_type + " detected!")
else:
    print('This script is only work for GNU/Linux or BSD!')
    sys.exit(1)

# Checking dependencies!
not_installed = ''

# PyQt5 or PySide6
try:
    import PySide6
    print('python3-pyside6 is found')
    pyside6_is_installed = True
except:
    pyside6_is_installed = False

if not(pyside6_is_installed):
    try:
        import PyQt5
        print('python3-pyqt5 is found')
    except:
        print('Error : python3-pyqt5 or pyside6 must be installed!')
        not_installed = not_installed + '(PyQt5 or PySide6) '

# python3-requests
try:
    import requests
    print('python3-requests is found!')
except:
    print('Error : requests is not installed!')
    not_installed = not_installed + 'python3-requests, '

# python3-setproctitle
try:
    import setproctitle
    print('python3-setproctitle is found!')
except:
    print("Warning: setproctitle is not installed!")
    not_installed = not_installed + 'python3-setproctitle, '

# psutil
try:
    import psutil
    print('python3-psutil is found!')
except:
    print("Warning: python3-psutil is not installed!")
    not_installed = not_installed + 'psutil, '

# youtube_dl
try:
    import youtube_dl
    print('youtube-dl is found')
except:
    print('Warning: youtube-dl is not installed!')
    not_installed = not_installed + 'youtube-dl, '

# aria2
answer = os.system('aria2c --version 1>/dev/null')
if answer != 0:
    print("Error aria2 not installed!")
    not_installed = not_installed + 'aria2c, '
else:
    print('aria2 is found!')

# libnotify-bin
answer = os.system('notify-send --version 1>/dev/null')
if answer != 0:
    print("Error libnotify-bin is not installed!")
    not_installed = not_installed + 'libnotify-bin, '
else:
    print('libnotify-bin is found!')

# paplay
answer = os.system('paplay --version 1>/dev/null')
if answer != 0:
    print("Warning: paplay not installed!You need pulseaudio for sound notifications!")
    not_installed = not_installed + 'paplay, '
else:
    print('paplay is found!')

# sound-theme-freedesktop
if os_type == 'Linux':
    notifications_path = '/usr/share/sounds/freedesktop/stereo/'
elif os_type == 'FreeBSD' or os_type == 'OpenBSD':
    notifications_path = '/usr/local/share/sounds/freedesktop/stereo/'

if os.path.isdir(notifications_path):
    print('sound-theme-freedesktop is found!')
else:
    print('Warning: sound-theme-freedesktop is not installed! you need this package for sound notifications!')
    not_installed = not_installed + 'sound-theme-freedesktop'

# ffmpeg
answer = os.system('ffmpeg -version 1>/dev/null')
if answer != 0:
    print("Warning: ffmpeg not installed!")
    not_installed = not_installed + 'ffmpeg, '
else:
    print('ffmpeg is found!')


# show warning , if dependencies not installed!
if not_installed != '':
    print('########################')
    print('####### WARNING ########')
    print('########################')
    print('Some dependencies are not installed .It causes some problems for persepolis! : \n')
    print(not_installed + '\n\n')
    print('Read this link for more information: \n')
    print('https://github.com/persepolisdm/persepolis/wiki/git-installation-instruction\n\n')
    answer = input('Do you want to continue?(y/n)')
    if answer not in ['y', 'Y', 'yes']:
        sys.exit(1)

if sys.argv[1] == "test":
    print('We have not unit test :)')
    sys.exit('0')

DESCRIPTION = 'Persepolis Download Manager'

if os_type == 'Linux':
    DATA_FILES = [
        ('/usr/share/man/man1/', ['man/persepolis.1.gz']),
        ('/usr/share/applications/', ['xdg/com.github.persepolisdm.persepolis.desktop']),
        ('/usr/share/metainfo/', ['xdg/com.github.persepolisdm.persepolis.appdata.xml']),
        ('/usr/share/icons/hicolor/scalable/apps/', ['resources/persepolis.svg']),
        ('/usr/share/icons/hicolor/scalable/apps/', ['resources/persepolis-tray.svg'])
    ]
elif os_type == 'FreeBSD' or os_type == 'OpenBSD':
    DATA_FILES = [
        ('/usr/local/share/man/man1/', ['man/persepolis.1.gz']),
        ('/usr/local/share/applications/', ['xdg/com.github.persepolisdm.persepolis.desktop']),
        ('/usr/local/share/metainfo/', ['xdg/com.github.persepolisdm.persepolis.appdata.xml']),
        ('/usr/local/share/pixmaps/', ['resources/persepolis.svg']),
        ('/usr/local/share/pixmaps/', ['resources/persepolis-tray.svg'])
    ]


# finding current directory
cwd = os.path.abspath(__file__)
setup_dir = os.path.dirname(cwd)

# clearing __pycache__
src_pycache = os.path.join(setup_dir, 'persepolis', '__pycache__')
gui_pycache = os.path.join(setup_dir, 'persepolis', 'gui', '__pycache__')
scripts_pycache = os.path.join(setup_dir, 'persepolis', 'scripts', '__pycache__')
constants_pycache = os.path.join(setup_dir, 'persepolis', 'constants', '__pycache__')

for folder in [src_pycache, gui_pycache, scripts_pycache, constants_pycache]:
    if os.path.isdir(folder):
        shutil.rmtree(folder)
        print(str(folder)
              + ' is removed!')


# Creating man page file
persepolis_man_page = os.path.join(setup_dir, 'man', 'persepolis.1')
os.system('gzip -f -k -9 "'
          + persepolis_man_page
          + '"')
print('man page file is generated!')

setup(
    name='persepolis',
    version='3.2.0',
    license='GPL3',
    description=DESCRIPTION,
    long_description=DESCRIPTION,
    include_package_data=True,
    url='https://github.com/persepolisdm/persepolis',
    author='AliReza AmirSamimi',
    author_email='alireza.amirsamimi@gmail.com',
    maintainer='AliReza AmirSamimi',
    maintainer_email='alireza.amirsamimi@gmail.com',
    packages=(
        'persepolis', 'persepolis.constants',
        'persepolis.scripts', 'persepolis.gui',
    ),
    data_files=DATA_FILES,
    entry_points={
        'console_scripts': [
            'persepolis = persepolis.__main__'
        ]
    }
)

setup.py
IamRezaMousavi/ghermez
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import sys
import os
import platform

# finding os platform
os_type = platform.system()

# Don't run persepolis as root!
if os_type == 'Linux' or os_type == 'FreeBSD' or os_type == 'OpenBSD' or os_type == 'Darwin':
    uid = os.getuid()
    if uid == 0:
        print('Do not run persepolis as root.')
        sys.exit(1)


cwd = os.path.abspath(__file__)
run_dir = os.path.dirname(cwd)
# if persepolis run in test folder
print('persepolis is running from test folder')
parent_dir = os.path.dirname(run_dir)

sys.path.insert(0, parent_dir)

from persepolis import __main__

test/test.py
IamRezaMousavi/ghermez
#!/usr/bin/env python3
# coding: utf-8
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


import platform
import glob
import os
import shutil
import sys

os_type = platform.system()

if os_type == 'Linux':
    path_list = ['/usr/share/man/man1/persepolis.1.gz',
                 '/usr/share/pixmaps/persepolis.svg',
                 '/usr/share/pixmaps/persepolis-tray.svg',
                 '/usr/share/applications/com.github.persepolisdm.persepolis.desktop',
                 '/usr/share/metainfo/com.github.persepolisdm.persepolis.appdata.xml',
                 '/usr/bin/persepolis']

elif os_type == 'FreeBSD' or os_type == 'OpenBSD':
    path_list = ['/usr/local/share/man/man1/persepolis.1.gz',
                 '/usr/local/share/pixmaps/persepolis.svg',
                 '/usr/local/share/pixmaps/persepolis-tray.svg',
                 '/usr/local/share/applications/com.github.persepolisdm.persepolis.desktop',
                 '/usr/local/share/metainfo/com.github.persepolisdm.persepolis.appdata.xml',
                 '/usr/local/bin/persepolis']


else:
    print('This script is for Linux and BSD')
    sys.exit(1)


# finding persepolis directories in /usr/lib/python3.6/site-packages/
python_version_list = ['python3.5','python3.6', 'python3.7', 'python3.8', 'python3.9']

for python_version in python_version_list:
    # for BSD
    if os_type == 'Linux':

        pattern = '/usr/lib/' + python_version + '/site-packages/persepolis*'

    elif os_type == 'FreeBSD' or os_type == 'OpenBSD':

        pattern = '/usr/local/lib/' + python_version + '/site-packages/persepolis*'

    for folder in glob.glob(pattern):
        path_list.append(folder)

print(path_list)

uid = os.getuid()
if uid != 0:
    print('run this script as root.')
    sys.exit(1)


for path in path_list:
    if os.path.exists(path):
        if os.path.isfile(path):  # if path is for file
            os.remove(path)  # removing file
        else:
            shutil.rmtree(path)  # removing folder
        print(str(path) + ' is removed!')

print('uninstallation is complete!')

uninstall.py
IamRezaMousavi/ghermez
VSVersionInfo(
  ffi=FixedFileInfo(
    filevers=(0, 0, 1, 0),
    prodvers=(0, 0, 1, 0),
    mask=0x3f,
    flags=0x0,
    OS=0x40004,
    fileType=0x1,
    subtype=0x0,
    date=(0, 0)
    ),
  kids=[
    StringFileInfo(
      [
      StringTable(
        u'040904B0',
        [StringStruct(u'CompanyName', u'Reza Mousavi'),
        StringStruct(u'FileDescription', u'A Free Download Manager GUI for Aria2'),
        StringStruct(u'FileVersion', u'0.0.1.0'),
        StringStruct(u'InternalName', u'Ghermez Download Manager'),
        StringStruct(u'LegalCopyright', u'\xa9 GNU General Public License v3.0'),
        StringStruct(u'OriginalFilename', u'Ghermez Download Manager.exe'),
        StringStruct(u'ProductName', u'Ghermez Download Manager'),
        StringStruct(u'ProductVersion', u'0.0.1.0')])
      ]),
    VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
  ]
)

version.py
IamRezaMousavi/ghermez