〇yaa スクレイパー

0

有名な某サイト〇yaaをサムネイルで見やすくデザインしなおした。

このサイトを知ってる方は多いかと思いますが、なにせ文字だらけで大変

そこでbingからサムネを取得してくるように


ソフトの起動には組み込みでqtorrentが必要

import sys
import requests
import webbrowser
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QMutex, QSemaphore, QTimer, QDateTime
from PyQt5.QtGui import QImage, QPixmap, QIcon
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QTableWidget, QTableWidgetItem, QLabel, QPushButton, QComboBox, QLineEdit, QWidget, QHeaderView, QHBoxLayout, QStyleFactory, QStatusBar, QDialog, QScrollArea, QFileDialog, QAction, QMenu, QMenuBar, QMessageBox, QAbstractScrollArea, QProgressBar, QAbstractItemView
import json
from PIL import Image
import io
import os
from bs4 import BeautifulSoup
import qbittorrentapi
from datetime import datetime

MAX_THREADS = 4  # 一度に実行するスレッドの数を制限
ITEMS_PER_PAGE = 20  # 1ページあたりの表示アイテム数
ITEMS_PER_SCRAPE_PAGE = 100  # 1スクレイプページあたりの最大アイテム数

SETTINGS_FILE = "settings.json"

class ClickableLabel(QLabel):
    def __init__(self, url, full_image, parent=None):
        super().__init__(parent)
        self.url = url
        self.full_image = full_image

    def mousePressEvent(self, event):
        self.show_image_dialog(self.full_image)

    @staticmethod
    def show_image_dialog(image):
        dialog = QDialog()
        dialog.setWindowTitle("画像拡大表示")
        dialog.setGeometry(100, 100, 800, 600)  # ウィンドウサイズを設定
        layout = QVBoxLayout()
        label = QLabel()
        pixmap = QPixmap.fromImage(image)
        label.setPixmap(pixmap.scaled(pixmap.width() * 4, pixmap.height() * 4, Qt.KeepAspectRatio, Qt.SmoothTransformation))  # 2倍に拡大表示
        scroll_area = QScrollArea()
        scroll_area.setWidget(label)
        layout.addWidget(scroll_area)
        dialog.setLayout(layout)
        dialog.exec_()

class ImageLoaderThread(QThread):
    image_loaded = pyqtSignal(int, QImage, QImage, str, bool)
    increment_queue = pyqtSignal()
    decrement_queue = pyqtSignal()

    def __init__(self, row_index, query, refer_url, semaphore, category=False):
        super().__init__()
        self.row_index = row_index
        self.query = query
        self.refer_url = refer_url
        self.category = category
        self.semaphore = semaphore

    def run(self):
        self.increment_queue.emit()
        try:
            if self.category:
                response = requests.get(self.query, headers={'User-Agent': 'Mozilla/5.0'})
                if 'image' in response.headers['Content-Type']:
                    image_data = response.content
                    with Image.open(io.BytesIO(image_data)) as img:
                        # フルサイズ画像を保存
                        byte_array_full = io.BytesIO()
                        img.save(byte_array_full, format='PNG')
                        q_image_full = QImage.fromData(byte_array_full.getvalue())
                        # サムネイル画像を作成
                        img.thumbnail((150, 150))
                        byte_array_thumb = io.BytesIO()
                        img.save(byte_array_thumb, format='PNG')
                        q_image_thumb = QImage.fromData(byte_array_thumb.getvalue())
                        self.image_loaded.emit(self.row_index, q_image_thumb, q_image_full, self.refer_url, self.category)
            else:
                search_url = f"https://www.bing.com/images/search?q={self.query}"
                response = requests.get(search_url, headers={'User-Agent': 'Mozilla/5.0'})
                soup = BeautifulSoup(response.content, 'html.parser')
                img_tag = soup.find('img', class_='mimg')
                if img_tag:
                    img_url = img_tag.get('src')
                    response = requests.get(img_url, headers={'User-Agent': 'Mozilla/5.0'})
                    if 'image' in response.headers['Content-Type']:
                        image_data = response.content
                        with Image.open(io.BytesIO(image_data)) as img:
                            # フルサイズ画像を保存
                            byte_array_full = io.BytesIO()
                            img.save(byte_array_full, format='PNG')
                            q_image_full = QImage.fromData(byte_array_full.getvalue())
                            # サムネイル画像を作成
                            img.thumbnail((150, 150))
                            byte_array_thumb = io.BytesIO()
                            img.save(byte_array_thumb, format='PNG')
                            q_image_thumb = QImage.fromData(byte_array_thumb.getvalue())
                            self.image_loaded.emit(self.row_index, q_image_thumb, q_image_full, self.refer_url, self.category)
        except Exception as e:
            print(f"Error loading image for row {self.row_index}: {e}")
        finally:
            self.decrement_queue.emit()
            self.semaphore.release()  # スレッドが完了したらセマフォを解放

class DownloadManagerWindow(QMainWindow):
    def __init__(self, qb):
        super().__init__()

        self.qb = qb
        self.setWindowTitle("ダウンロード進行状況")
        self.setGeometry(150, 150, 600, 400)

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QVBoxLayout(self.central_widget)

        self.table = QTableWidget()
        self.table.setColumnCount(4)
        self.table.setHorizontalHeaderLabels(['名前', '進行状況', '操作', '日付'])
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.table.customContextMenuRequested.connect(self.show_context_menu)
        self.layout.addWidget(self.table)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_progress)
        self.timer.start(1000)  # 1秒ごとに更新

    def update_progress(self):
        try:
            for torrent in self.qb.torrents_info():
                row_count = self.table.rowCount()
                for row in range(row_count):
                    if self.table.item(row, 0).text() == torrent.name:
                        progress_bar = self.table.cellWidget(row, 1)
                        if progress_bar:
                            progress_bar.setValue(int(torrent.progress * 100))
                        if torrent.state == 'uploading':
                            pause_button = self.table.cellWidget(row, 2).findChild(QPushButton)
                            if pause_button:
                                pause_button.setText('再開')
                        break
                else:
                    self.add_torrent(torrent)
        except Exception as e:
            print(f"Error updating progress: {e}")

    def add_torrent(self, torrent):
        row_count = self.table.rowCount()
        self.table.insertRow(row_count)

        self.table.setItem(row_count, 0, QTableWidgetItem(torrent.name))
        progress_bar = QProgressBar()
        progress_bar.setValue(int(torrent.progress * 100))
        self.table.setCellWidget(row_count, 1, progress_bar)

        pause_button = QPushButton('一時停止')
        pause_button.clicked.connect(lambda: self.toggle_torrent(torrent))
        widget = QWidget()
        layout = QHBoxLayout(widget)
        layout.addWidget(pause_button)
        layout.setAlignment(Qt.AlignCenter)
        widget.setLayout(layout)
        self.table.setCellWidget(row_count, 2, widget)

        self.table.setItem(row_count, 3, QTableWidgetItem(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

    def toggle_torrent(self, torrent):
        if torrent.state == 'uploading' or torrent.state == 'stalledUP':
            self.qb.torrents_pause(torrent_hashes=torrent.hash)
        else:
            self.qb.torrents_resume(torrent_hashes=torrent.hash)

    def show_context_menu(self, pos):
        context_menu = QMenu(self)
        open_folder_action = context_menu.addAction("フォルダを開く")
        delete_action = context_menu.addAction("削除")
        pause_action = context_menu.addAction("一時停止/再開")

        action = context_menu.exec_(self.table.viewport().mapToGlobal(pos))
        if action == open_folder_action:
            self.open_folder()
        elif action == delete_action:
            self.delete_torrent()
        elif action == pause_action:
            self.pause_resume_torrent()

    def open_folder(self):
        current_row = self.table.currentRow()
        if current_row >= 0:
            torrent_name = self.table.item(current_row, 0).text()
            for torrent in self.qb.torrents_info():
                if torrent.name == torrent_name:
                    os.startfile(torrent.save_path)
                    break

    def delete_torrent(self):
        current_row = self.table.currentRow()
        if current_row >= 0:
            torrent_name = self.table.item(current_row, 0).text()
            for torrent in self.qb.torrents_info():
                if torrent.name == torrent_name:
                    self.qb.torrents_delete(torrent_hashes=torrent.hash)
                    self.table.removeRow(current_row)
                    break

    def pause_resume_torrent(self):
        current_row = self.table.currentRow()
        if current_row >= 0:
            torrent_name = self.table.item(current_row, 0).text()
            for torrent in self.qb.torrents_info():
                if torrent.name == torrent_name:
                    if torrent.state == 'uploading' or torrent.state == 'stalledUP':
                        self.qb.torrents_pause(torrent_hashes=torrent.hash)
                    else:
                        self.qb.torrents_resume(torrent_hashes=torrent.hash)
                    break

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Nyaa.si スクレイパー")
        self.setGeometry(100, 100, 1200, 900)  # ウィンドウのサイズを調整
        self.setWindowIcon(QIcon("avatar-U99s-Q.png")) 
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QVBoxLayout(self.central_widget)

        self.query_input = QLineEdit()
        self.query_input.setPlaceholderText("検索")
        self.query_input.returnPressed.connect(self.search)  # Enterキーで検索
        self.layout.addWidget(self.query_input)

        self.category_combo = QComboBox()
        self.category_combo.addItems([
            'すべてのカテゴリー',  # All categories
            '本',       # Books
            'アニメ',   # Anime
            'MP3',     # MP3
            'FLAC',    # FLAC
            '実写番組',  # Live Action
            '字幕番組',  # Subtitled Live Action
            'ソフトウェア', # Software
            'ゲーム'    # Games
        ])
        self.layout.addWidget(self.category_combo)

        self.search_button = QPushButton("検索")
        self.search_button.setStyleSheet("background-color: #337ab7; color: white; padding: 10px 24px; font-size: 16px;")
        self.search_button.clicked.connect(self.search)
        self.layout.addWidget(self.search_button)

        self.table = QTableWidget()
        self.table.setColumnCount(9)
        self.table.setHorizontalHeaderLabels(['カテゴリー', '画像', '名前', 'サイズ', '日付', 'シーダー', 'リーチャー', '完了', 'マグネットリンク'])
        self.table.horizontalHeader().sectionClicked.connect(self.header_clicked)
        self.table.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
        self.layout.addWidget(self.table)

        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.table.setColumnWidth(0, 100)  # Category
        self.table.setColumnWidth(1, 150)  # Image
        self.table.setColumnWidth(2, 300)  # Name
        self.table.setColumnWidth(3, 100)  # Size
        self.table.setColumnWidth(4, 150)  # Date
        self.table.setColumnWidth(5, 80)   # Seeders
        self.table.setColumnWidth(6, 80)   # Leechers
        self.table.setColumnWidth(7, 100)  # Completed
        self.table.setColumnWidth(8, 150)  # Magnet Link
        self.table.verticalHeader().setDefaultSectionSize(50)  # 行の高さを設定

        self.pagination_layout = QHBoxLayout()
        self.prev_button = QPushButton("前へ")
        self.prev_button.setStyleSheet("background-color: #337ab7; color: white; padding: 10px 24px; font-size: 16px;")
        self.prev_button.clicked.connect(self.prev_page)
        self.next_button = QPushButton("次へ")
        self.next_button.setStyleSheet("background-color: #337ab7; color: white; padding: 10px 24px; font-size: 16px;")
        self.next_button.clicked.connect(self.next_page)
        self.page_combo = QComboBox()
        self.page_combo.addItems([str(i) for i in range(1, 101)])
        self.page_combo.currentIndexChanged.connect(self.page_changed)
        self.pagination_layout.addWidget(self.prev_button)
        self.pagination_layout.addWidget(self.page_combo)
        self.pagination_layout.addWidget(self.next_button)
        self.layout.addLayout(self.pagination_layout)

        self.current_page = 1
        self.current_subpage = 1

        self.threads = []
        self.queue_count = 0
        self.mutex = QMutex()
        self.semaphore = QSemaphore(MAX_THREADS)  # セマフォを使用してスレッドの数を制限

        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.update_queue_count()

        self.setStyle(QStyleFactory.create('Fusion'))
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f5f5f5;
            }
            QLineEdit, QComboBox, QTableWidget {
                font-size: 16px;
                padding: 5px;
            }
            QTableWidget::item {
                padding: 10px;
            }
            QLabel, QPushButton {
                font-size: 16px;
            }
            QPushButton {
                background-color: #337ab7;
                color: white;
                padding: 10px 24px;
                font-size: 16px;
            }
        """)

        self.sort_order = Qt.AscendingOrder

        self.download_folders = {
            'すべてのカテゴリー': '',
            '本': '',       # Books
            'アニメ': '',   # Anime
            'MP3': '',     # MP3
            'FLAC': '',    # FLAC
            '実写番組': '',  # Live Action
            '字幕番組': '',  # Subtitled Live Action
            'ソフトウェア': '', # Software
            'ゲーム': ''    # Games
        }
        self.load_settings()  # 設定を読み込む

        self.qb = qbittorrentapi.Client(host='localhost', port=8080)  # qBittorrentに接続
        try:
            self.qb.auth_log_in(username='admin', password='あなたの')  # 認証
        except qbittorrentapi.LoginFailed as e:
            print(f'ログインに失敗しました: {e}')
            sys.exit(1)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_progress)
        self.timer.start(1000)  # 1秒ごとに更新

        self.download_torrents = {}  # ダウンロード中のトレントを管理する辞書

        self.create_menu()

    def create_menu(self):
        menu_bar = QMenuBar(self)
        self.setMenuBar(menu_bar)

        file_menu = QMenu("ファイル", self)
        menu_bar.addMenu(file_menu)

        download_list_action = QAction("ダウンロードリスト", self)
        download_list_action.triggered.connect(self.show_download_manager)
        file_menu.addAction(download_list_action)

        folder_action = QAction("ダウンロードフォルダ設定", self)
        folder_action.triggered.connect(self.show_folder_dialog)
        file_menu.addAction(folder_action)

        settings_menu = QMenu("設定", self)
        menu_bar.addMenu(settings_menu)

        items_per_page_action = QAction("表示アイテム数", self)
        items_per_page_action.triggered.connect(self.show_items_per_page_dialog)
        settings_menu.addAction(items_per_page_action)

        login_action = QAction("ログイン設定", self)
        login_action.triggered.connect(self.show_login_dialog)
        settings_menu.addAction(login_action)

        options_menu = QMenu("オプション", self)
        menu_bar.addMenu(options_menu)

        open_qbittorrent_action = QAction("qBittorrentを開く", self)
        open_qbittorrent_action.triggered.connect(lambda: webbrowser.open('http://localhost:8080'))
        options_menu.addAction(open_qbittorrent_action)

    def show_login_dialog(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("ログイン情報の設定")
        layout = QVBoxLayout()

        username_input = QLineEdit()
        username_input.setPlaceholderText("ユーザー名")
        layout.addWidget(username_input)

        password_input = QLineEdit()
        password_input.setPlaceholderText("パスワード")
        password_input.setEchoMode(QLineEdit.Password)
        layout.addWidget(password_input)

        save_button = QPushButton("保存")
        save_button.clicked.connect(lambda: self.save_login_info(username_input.text(), password_input.text(), dialog))
        layout.addWidget(save_button)

        dialog.setLayout(layout)
        dialog.exec_()

    def save_login_info(self, username, password, dialog):
        try:
            self.qb.auth_log_in(username=username, password=password)
            self.status_bar.showMessage("ログイン情報が保存されました。")
            dialog.accept()
        except qbittorrentapi.LoginFailed as e:
            self.status_bar.showMessage(f"ログインに失敗しました: {e}")

    def show_folder_dialog(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("ダウンロードフォルダ設定")
        layout = QVBoxLayout()

        for category in self.download_folders.keys():
            folder_input_layout = QHBoxLayout()
            folder_label = QLabel(category)
            folder_input_layout.addWidget(folder_label)
            folder_button = QPushButton("フォルダを選択")
            folder_button.clicked.connect(lambda _, cat=category: self.select_folder(cat))
            folder_input_layout.addWidget(folder_button)
            layout.addLayout(folder_input_layout)

        save_button = QPushButton("保存")
        save_button.clicked.connect(lambda: self.save_folders(dialog))
        layout.addWidget(save_button)

        dialog.setLayout(layout)
        dialog.exec_()

    def select_folder(self, category):
        folder = QFileDialog.getExistingDirectory(self, f"{category}のフォルダを選択")
        if folder:
            self.download_folders[category] = folder
            self.status_bar.showMessage(f"{category}のフォルダ: {folder}")

    def save_folders(self, dialog):
        with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
            json.dump(self.download_folders, f, ensure_ascii=False, indent=4)
        self.status_bar.showMessage("フォルダ設定が保存されました。")
        dialog.accept()

    def load_settings(self):
        if os.path.exists(SETTINGS_FILE):
            with open(SETTINGS_FILE, 'r', encoding='utf-8') as f:
                self.download_folders = json.load(f)
            self.status_bar.showMessage("設定を読み込みました。")

    def show_items_per_page_dialog(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("表示アイテム数の設定")
        layout = QVBoxLayout()

        items_per_page_input = QLineEdit()
        items_per_page_input.setPlaceholderText("表示アイテム数を入力")
        layout.addWidget(items_per_page_input)

        save_button = QPushButton("保存")
        save_button.clicked.connect(lambda: self.save_items_per_page(items_per_page_input.text(), dialog))
        layout.addWidget(save_button)

        dialog.setLayout(layout)
        dialog.exec_()

    def save_items_per_page(self, items_per_page, dialog):
        global ITEMS_PER_PAGE
        try:
            ITEMS_PER_PAGE = int(items_per_page)
            self.status_bar.showMessage("表示アイテム数が設定されました。")
            dialog.accept()
        except ValueError:
            self.status_bar.showMessage("無効な入力です。")

    def show_download_manager(self):
        self.download_manager_window = DownloadManagerWindow(self.qb)
        self.download_manager_window.show()

    def update_queue_count(self):
        self.status_bar.showMessage(f"画像読み込み中: {self.queue_count}")

    def increment_queue(self):
        self.mutex.lock()
        self.queue_count += 1
        self.update_queue_count()
        self.mutex.unlock()

    def decrement_queue(self):
        self.mutex.lock()
        self.queue_count -= 1
        self.update_queue_count()
        self.mutex.unlock()

    def search(self):
        self.current_page = 1
        self.current_subpage = 1
        self.load_page()

    def load_page(self):
        query = self.query_input.text()
        category_map = {
            'すべてのカテゴリー': '0_0',
            '本': '3_3',
            'アニメ': '1_4',
            'MP3': '2_2',
            'FLAC': '2_1',
            '実写番組': '4_4',
            '字幕番組': '4_1',
            'ソフトウェア': '6_1',
            'ゲーム': '6_2'
        }
        category = category_map[self.category_combo.currentText()]

        all_data = self.scrape_nyaa(query=query, category=category, page=self.current_page)
        start_index = (self.current_subpage - 1) * ITEMS_PER_PAGE
        end_index = start_index + ITEMS_PER_PAGE
        data = all_data[start_index:end_index]

        self.table.setRowCount(0)
        self.images = []

        for i, row in enumerate(data):
            self.show_result(i, row)

        for i, row in enumerate(data):
            if row[0]:  # カテゴリーアイコンのロード
                category_icon_url = f"https://nyaa.si/static/img/icons/nyaa/{category}.png"
                self.load_image_async(i, category_icon_url, row[0], category=True)
            if row[9]:
                self.load_image_async(i, row[9], row[1])

    def prev_page(self):
        if self.current_subpage > 1:
            self.current_subpage -= 1
            self.load_page()
        elif self.current_page > 1:
            self.current_page -= 1
            self.current_subpage = (ITEMS_PER_SCRAPE_PAGE // ITEMS_PER_PAGE)  # Set to last subpage
            self.page_combo.setCurrentIndex(self.current_page - 1)
            self.load_page()

    def next_page(self):
        if self.current_subpage < (ITEMS_PER_SCRAPE_PAGE // ITEMS_PER_PAGE):
            self.current_subpage += 1
            self.load_page()
        elif self.current_page < 100:
            self.current_page += 1
            self.current_subpage = 1  # Reset to first subpage
            self.page_combo.setCurrentIndex(self.current_page - 1)
            self.load_page()

    def page_changed(self):
        self.current_page = int(self.page_combo.currentText())
        self.current_subpage = 1  # Reset to first subpage
        self.load_page()

    def scrape_nyaa(self, query='', category='', page=1):
        try:
            url = f'https://nyaa.si/?c={category}&p={page}&q={query}'
            response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
            soup = BeautifulSoup(response.content, 'html.parser')

            table = soup.find('table', class_='torrent-list')
            rows = table.find_all('tr', class_='default') if table else []

            data = []
            for row in rows:
                cells = row.find_all('td')
                if len(cells) > 0:
                    magnet_link = cells[2].find_all('a')[1]['href'] if len(cells[2].find_all('a')) > 1 else ''
                    data.append([
                        cells[0].find('img')['alt'] if cells[0].find('img') else '',  # カテゴリー名
                        '',  # Placeholder for image
                        cells[1].get_text(strip=True),  # 名前
                        cells[3].get_text(strip=True),  # サイズ
                        cells[4].get_text(strip=True),  # 日付
                        cells[5].get_text(strip=True),  # シーダー
                        cells[6].get_text(strip=True),  # リーチャー
                        cells[7].get_text(strip=True),  # 完了
                        magnet_link,  # マグネットリンク
                        cells[1].get_text(strip=True)  # 画像検索用の名前
                    ])
            return data
        except Exception as e:
            print(f"Error during scraping: {e}")
            return []

    def show_result(self, row_index, data):
        self.table.insertRow(row_index)
        for col_index, item in enumerate(data[:9]):  # データの数を9に変更
            if col_index == 1:  # 画像列を飛ばす
                continue
            self.table.setItem(row_index, col_index, QTableWidgetItem(item))
        self.table.setItem(row_index, 1, QTableWidgetItem("読み込み中..."))

        magnet_button = QPushButton("ダウンロード")
        magnet_button.clicked.connect(lambda _, link=data[8], row=row_index: self.open_magnet(link, row))
        self.table.setCellWidget(row_index, 8, magnet_button)

    def open_magnet(self, link, row):
        category = self.category_combo.currentText()
        download_folder = self.download_folders.get(category, '')
        if download_folder:
            self.download_torrent(link, download_folder, row)
        else:
            webbrowser.open(link)

    def download_torrent(self, magnet_link, download_folder, row):
        try:
            self.qb.torrents_add(urls=magnet_link, save_path=download_folder)
            torrent_info = self.qb.torrents_info(urls=magnet_link)
            if torrent_info:
                torrent_hash = torrent_info[0].hash
                self.download_torrents[torrent_hash] = row
                self.status_bar.showMessage("ダウンロードを開始しました。")
            else:
                self.status_bar.showMessage("トレント情報の取得に失敗しました。")
        except Exception as e:
            self.status_bar.showMessage(f"ダウンロードに失敗しました: {e}")

    def update_progress(self):
        try:
            for torrent in self.qb.torrents_info():
                if torrent.hash in self.download_torrents:
                    if torrent.progress == 1.0 and torrent.state not in ('pausedDL', 'pausedUP'):
                        self.qb.torrents_pause(torrent_hashes=torrent.hash)
                        self.status_bar.showMessage("ダウンロードが完了し、一時停止しました。")
                    else:
                        row = self.download_torrents[torrent.hash]
                        progress_bar = self.table.cellWidget(row, 8)
                        if isinstance(progress_bar, QProgressBar):
                            progress_bar.setValue(int(torrent.progress * 100))
        except Exception as e:
            print(f"Error updating progress: {e}")

    def load_image_async(self, row_index, query, refer_url, category=False):
        self.semaphore.acquire()  # セマフォを取得してスレッドの実行を制限
        thread = ImageLoaderThread(row_index, query, refer_url, self.semaphore, category)
        thread.image_loaded.connect(self.display_image)
        thread.increment_queue.connect(self.increment_queue)
        thread.decrement_queue.connect(self.decrement_queue)
        self.threads.append(thread)
        thread.start()

    def display_image(self, row_index, q_image_thumb, q_image_full, refer_url, category):
        pixmap = QPixmap.fromImage(q_image_thumb)
        label = ClickableLabel(refer_url, q_image_full)
        label.setPixmap(pixmap)
        if category:
            self.table.setCellWidget(row_index, 0, label)  # カテゴリーアイコンの列
        else:
            self.table.setCellWidget(row_index, 1, label)  # 画像の列

    def closeEvent(self, event):
        for thread in self.threads:
            thread.quit()
            thread.wait()
        event.accept()

    def header_clicked(self, index):
        if index == 5:  # シーダー列
            self.sort_order = Qt.DescendingOrder if self.sort_order == Qt.AscendingOrder else Qt.AscendingOrder
            self.table.sortItems(index, self.sort_order)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle(QStyleFactory.create('Fusion'))
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

返事を書く

Please enter your comment!
Please enter your name here

CAPTCHA