diff --git a/src/onthespot/accounts.py b/src/onthespot/accounts.py index fec9d98..f8282f1 100644 --- a/src/onthespot/accounts.py +++ b/src/onthespot/accounts.py @@ -17,6 +17,7 @@ def __init__(self, gui=False): self.gui = gui super().__init__() + def run(self): accounts = config.get('accounts') for account in accounts: diff --git a/src/onthespot/casualsnek.py b/src/onthespot/casualsnek.py index 97933ec..40cb600 100644 --- a/src/onthespot/casualsnek.py +++ b/src/onthespot/casualsnek.py @@ -12,7 +12,6 @@ def start_snake_game(win): curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK) - while True: snake = [(4, 10), (4, 9), (4, 8)] food = (random.randint(3, win.getmaxyx()[0] - 2), random.randint(1, win.getmaxyx()[1] - 3)) @@ -65,6 +64,7 @@ def start_snake_game(win): elif key == ord('q'): return + def draw_borders(win): height, width = win.getmaxyx() if width < 2 or height < 2: @@ -79,6 +79,7 @@ def draw_borders(win): if height > 1 and width > 2: win.addstr(height - 1, 0, '┗' + '━' * (width - 3) + '┛', curses.color_pair(1)) + def update_header(win, score): win.addstr(1, 2, f'Score: {score}', curses.A_BOLD) if not download_queue: @@ -89,6 +90,7 @@ def update_header(win, score): win.addstr(1, 15, item_label, curses.A_BOLD) + def display_game_over(win, score): win.clear() if score > config.get('snake_high_score', 0): diff --git a/src/onthespot/cli.py b/src/onthespot/cli.py index 585bf66..8850c2f 100644 --- a/src/onthespot/cli.py +++ b/src/onthespot/cli.py @@ -87,7 +87,6 @@ class CLI(Cmd): intro = '\033[32mWelcome to OnTheSpot. Type help or ? to list commands.\033[0m' prompt = '(OTS) ' - def do_help(self, arg): print("\033[32mAvailable commands:\033[0m") print(" help - Show this help message") @@ -141,7 +140,6 @@ def add_spotify_account_worker(): deezer_add_account(parts[2]) elif len(parts) == 2 and parts[0] == "select_account": - try: account_number = int(parts[1]) config.set_('parsing_acc_sn', account_number) @@ -151,7 +149,6 @@ def add_spotify_account_worker(): print("\033[32mInvalid account number. Please enter a valid integer.\033[0m") elif len(parts) == 2 and parts[0] == "delete_account": - try: account_number = int(parts[1]) accounts = config.get('accounts').copy() @@ -162,7 +159,6 @@ def add_spotify_account_worker(): print(f"\033[32mDeleted account number: {account_number}\033[0m") except ValueError: print("\033[32mInvalid account number. Please enter a valid integer.\033[0m") - else: print("\033[32mConfiguration options:\033[0m") print(" list_accounts") @@ -300,13 +296,11 @@ def refresh_queue(): stdscr.refresh() - def do_exit(self, arg): """Exit the CLI application.""" print("Exiting the CLI application.") os._exit(0) - if __name__ == '__main__': main() diff --git a/src/onthespot/gui/dl_progressbtn.py b/src/onthespot/gui/dl_progressbtn.py index 6918354..1d6f0b6 100644 --- a/src/onthespot/gui/dl_progressbtn.py +++ b/src/onthespot/gui/dl_progressbtn.py @@ -17,7 +17,7 @@ def __init__(self, local_id, item_metadata, pbar, copy_btn, cancel_btn, retry_bt layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(pbar) - if copy_btn != None: + if copy_btn is not None: self.copy_btn = copy_btn copy_btn.clicked.connect(self.copy_link) layout.addWidget(copy_btn) @@ -27,23 +27,25 @@ def __init__(self, local_id, item_metadata, pbar, copy_btn, cancel_btn, retry_bt self.retry_btn = retry_btn retry_btn.clicked.connect(self.retry_item) layout.addWidget(retry_btn) - if open_btn != None: + if open_btn is not None: self.open_btn = open_btn open_btn.clicked.connect(self.open_file) layout.addWidget(open_btn) - if locate_btn != None: + if locate_btn is not None: self.locate_btn = locate_btn locate_btn.clicked.connect(self.locate_file) layout.addWidget(locate_btn) - if delete_btn != None: + if delete_btn is not None: self.delete_btn = delete_btn delete_btn.clicked.connect(self.delete_file) layout.addWidget(delete_btn) self.setLayout(layout) + def copy_link(self): pyperclip.copy(self.item_metadata['item_url']) + def cancel_item(self): download_queue[self.local_id]['item_status'] = "Cancelled" download_queue[self.local_id]['gui']['status_label'].setText(self.tr("Cancelled")) @@ -51,6 +53,7 @@ def cancel_item(self): self.cancel_btn.hide() self.retry_btn.show() + def retry_item(self): download_queue[self.local_id]['item_status'] = "Waiting" download_queue[self.local_id]['gui']['status_label'].setText(self.tr("Waiting")) @@ -58,16 +61,19 @@ def retry_item(self): self.retry_btn.hide() self.cancel_btn.show() + def open_file(self): file_path = download_queue[self.local_id]['file_path'] file = os.path.abspath(file_path) open_item(file) + def locate_file(self): file_path = download_queue[self.local_id]['file_path'] file_dir = os.path.dirname(file_path) open_item(file_dir) + def delete_file(self): file_path = download_queue[self.local_id]['file_path'] file = os.path.abspath(file_path) diff --git a/src/onthespot/gui/mainui.py b/src/onthespot/gui/mainui.py index 8741967..6c3e818 100644 --- a/src/onthespot/gui/mainui.py +++ b/src/onthespot/gui/mainui.py @@ -66,6 +66,11 @@ def stop(self): class MainWindow(QMainWindow): + def closeEvent(self, event): + if config.get('close_to_tray') and get_init_tray(): + event.ignore() + self.hide() + # Remove Later def contribute(self): @@ -74,12 +79,6 @@ def contribute(self): open_item(url) - def closeEvent(self, event): - if config.get('close_to_tray') and get_init_tray(): - event.ignore() - self.hide() - - def __init__(self, _dialog, start_url=''): super(MainWindow, self).__init__() self.path = os.path.dirname(os.path.realpath(__file__)) @@ -123,9 +122,6 @@ def __init__(self, _dialog, start_url=''): # Bind button click self.bind_button_inputs() - self.__users = [] - self.last_search = None - # Set application theme self.toggle_theme_button.clicked.connect(self.toggle_theme) self.theme = config.get("theme") @@ -219,8 +215,6 @@ def bind_button_inputs(self): def set_table_props(self): - window_width = self.width() - logger.info(f"Setting table item properties {window_width}") # Sessions table #self.tbl_sessions.setSortingEnabled(True) self.tbl_sessions.horizontalHeader().setSectionsMovable(True) @@ -305,7 +299,6 @@ def session_load_done(self): def fill_account_table(self): - # Clear the table while self.tbl_sessions.rowCount() > 0: self.tbl_sessions.removeRow(0) @@ -418,7 +411,7 @@ def add_item_to_download_list(self, item, item_metadata): rows = self.tbl_dl_progress.rowCount() self.tbl_dl_progress.insertRow(rows) - if item_metadata.get('explicit', ''): # Check if the item is explicit + if item_metadata.get('explicit', ''): title = config.get('explicit_label', '') + ' ' + item_metadata['title'] else: title = item_metadata['title'] @@ -521,34 +514,12 @@ def remove_completed_from_download_list(self): "Already Exists" ): logger.info(f'Removing Row: {check_row} and mediaid: {local_id}') - - # Clear the widget in the last column before removing the row - widget = self.tbl_dl_progress.cellWidget(check_row, 0) - if widget: - widget.deleteLater() # Schedule the widget for deletion - widget = self.tbl_dl_progress.cellWidget(check_row, 1) - if widget: - widget.deleteLater() # Schedule the widget for deletion - widget = self.tbl_dl_progress.cellWidget(check_row, 2) - if widget: - widget.deleteLater() # Schedule the widget for deletion - widget = self.tbl_dl_progress.cellWidget(check_row, 3) - if widget: - widget.deleteLater() # Schedule the widget for deletion - widget = self.tbl_dl_progress.cellWidget(check_row, 4) - if widget: - widget.deleteLater() # Schedule the widget for deletion - widget = self.tbl_dl_progress.cellWidget(check_row, 5) - if widget: - widget.deleteLater() # Schedule the widget for deletion - - # Remove the row from the table self.tbl_dl_progress.removeRow(check_row) download_queue.pop(local_id) else: - check_row += 1 # Move to the next row + check_row += 1 else: - check_row += 1 # Move to the next row + check_row += 1 def cancel_all_downloads(self): @@ -732,31 +703,29 @@ def fill_search_table(self): self.inp_search_term.setText('') return + def download_btn_clicked(item_name, item_url, item_service, item_type, item_id, ): + parsing[item_id] = { + 'item_url': item_url, + 'item_service': item_service, + 'item_type': item_type, + 'item_id': item_id + } + self.__show_popup_dialog(self.tr("{0} is being parsed and will be added to the download queue shortly.").format(f"{item_type.title()}: {item_name}"), download=True) + for result in results: btn = QPushButton(self.tbl_search_results) #btn.setText(btn_text.strip()) btn.setIcon(self.get_icon('download')) - - item_url = result['item_url'] - - def download_btn_clicked(item_name, item_url, item_service, item_type, item_id, ): - parsing[item_id] = { - 'item_url': item_url, - 'item_service': item_service, - 'item_type': item_type, - 'item_id': item_id - } - self.__show_popup_dialog(self.tr("{0} is being parsed and will be added to the download queue shortly.").format(f"{item_type.title()}: {item_name}"), download=True) - + btn.setMinimumHeight(30) btn.clicked.connect(lambda x, item_name=result['item_name'], item_url=result['item_url'], item_type=result['item_type'], item_id=result['item_id'], item_service=result['item_service']: - download_btn_clicked(item_name, item_url, item_service, item_type, item_id)) + download_btn_clicked(item_name, item_url, item_service, item_type, item_id) + ) - btn.setMinimumHeight(30) service = QTableWidgetItem(result['item_service'].title()) service.setIcon(self.get_icon(result["item_service"])) diff --git a/src/onthespot/gui/minidialog.py b/src/onthespot/gui/minidialog.py index 57a944b..4c5870e 100644 --- a/src/onthespot/gui/minidialog.py +++ b/src/onthespot/gui/minidialog.py @@ -9,6 +9,7 @@ logger = get_logger('gui.minidialog') + class MiniDialog(QDialog): def __init__(self, parent=None): super(MiniDialog, self).__init__(parent) @@ -31,6 +32,7 @@ def __init__(self, parent=None): self.lb_main.mousePressEvent = self.on_label_click + def on_label_click(self, event): if event.button() == Qt.MouseButton.LeftButton: match = re.search(r"href='(https?://[^']+)'", self.lb_main.text()) @@ -40,6 +42,7 @@ def on_label_click(self, event): logger.info(f"Update URL Clicked, {match.group(1)}") open_item(match.group(1)) except Exception: + # No url in label pass def run(self, content, btn_hidden=False): diff --git a/src/onthespot/gui/settings.py b/src/onthespot/gui/settings.py index 9d1821e..decf096 100644 --- a/src/onthespot/gui/settings.py +++ b/src/onthespot/gui/settings.py @@ -15,7 +15,7 @@ def wheelEvent(self, event): def load_config(self): - # Hide Popups + # Hide Popup Settings self.group_search_items.hide() self.group_download_items.hide() @@ -32,6 +32,7 @@ def load_config(self): self.inp_login_service.insertItem(3, self.get_icon('youtube'), "") self.inp_login_service.setCurrentIndex(2) + #self.btn_reset_config.setIcon(self.get_icon('trash')) self.btn_save_config.setIcon(self.get_icon('save')) self.btn_download_root_browse.setIcon(self.get_icon('folder')) self.btn_download_tmp_browse.setIcon(self.get_icon('folder')) @@ -176,6 +177,7 @@ def load_config(self): # Store the newly created widget in the previous instance variable setattr(self, name, new_widget) + def save_config(self): # Missing Theme config.set_('language_index', self.inp_language.currentIndex()) diff --git a/src/onthespot/gui/thumb_listitem.py b/src/onthespot/gui/thumb_listitem.py index e093209..f69fb64 100644 --- a/src/onthespot/gui/thumb_listitem.py +++ b/src/onthespot/gui/thumb_listitem.py @@ -39,6 +39,7 @@ def __init__(self, label, thumb_url): self.setLayout(layout) + def on_finished(self, reply: QNetworkReply): # This method is called when the network request is completed if reply.error() == QNetworkReply.NetworkError.NoError: # Correct error checking diff --git a/src/onthespot/otsconfig.py b/src/onthespot/otsconfig.py index f019e1e..f1c82e0 100755 --- a/src/onthespot/otsconfig.py +++ b/src/onthespot/otsconfig.py @@ -17,6 +17,7 @@ def config_dir(): else: return os.path.join(os.path.expanduser("~"), ".config") + def cache_dir(): if os.name == "nt": if 'TEMP' in os.environ: @@ -223,6 +224,7 @@ def __init__(self, cfg_path=None): os.path.dirname(self.get("_log_file")), exist_ok=True ) + def get(self, key, default=None): if key in self.__config: return self.__config[key] @@ -231,6 +233,7 @@ def get(self, key, default=None): else: return default + def set_(self, key, value): if type(value) in [list, dict]: self.__config[key] = value.copy() @@ -238,6 +241,7 @@ def set_(self, key, value): self.__config[key] = value return value + def update(self): os.makedirs(os.path.dirname(self.__cfg_path), exist_ok=True) for key in list(set(self.__template_data).difference(set(self.__config))): @@ -246,6 +250,7 @@ def update(self): with open(self.__cfg_path, "w") as cf: cf.write(json.dumps(self.__config, indent=4)) + def rollback(self): with open(self.__cfg_path, "w") as cf: cf.write(json.dumps(self.__template_data, indent=4)) diff --git a/src/onthespot/parse_item.py b/src/onthespot/parse_item.py index 17d19a5..ec08539 100755 --- a/src/onthespot/parse_item.py +++ b/src/onthespot/parse_item.py @@ -63,6 +63,7 @@ def parse_url(url): 'item_id': item_id } + def parsingworker(): while True: if parsing: diff --git a/src/onthespot/search.py b/src/onthespot/search.py index ea8b139..f9f390c 100644 --- a/src/onthespot/search.py +++ b/src/onthespot/search.py @@ -10,6 +10,7 @@ logger = get_logger("search") + def get_search_results(search_term, content_types=None): if len(account_pool) <= 0: return None diff --git a/src/onthespot/utils.py b/src/onthespot/utils.py index 305927b..6f8c863 100644 --- a/src/onthespot/utils.py +++ b/src/onthespot/utils.py @@ -507,6 +507,7 @@ def fix_mp3_metadata(filename): del id3['TXXX:TCMP'] id3.save() + def add_to_m3u_file(item, item_metadata): logger.info(f"Adding {item['file_path']} to m3u") @@ -557,6 +558,7 @@ def add_to_m3u_file(item, item_metadata): else: logger.info(f"{item['file_path']} already exists in the M3U file.") + def strip_metadata(item): if os.path.isfile(os.path.abspath(item['file_path'])): target_path = os.path.abspath(item['file_path']) diff --git a/src/onthespot/web.py b/src/onthespot/web.py index d414b17..d64e6d7 100644 --- a/src/onthespot/web.py +++ b/src/onthespot/web.py @@ -51,41 +51,49 @@ def run(self): else: time.sleep(0.2) + @app.route('/items') def get_items(): with download_queue_lock: return jsonify(download_queue) + @app.route('/icons/') def serve_icons(filename): icon_path = os.path.join(config.app_root, 'resources', 'icons', filename) return send_file(icon_path) + @app.route('/download/') def download_media(local_id): return send_file(download_queue[local_id]['file_path'], as_attachment=True) + @app.route('/delete/', methods=['DELETE']) def delete_media(local_id): os.remove(download_queue[local_id]['file_path']) download_queue[local_id]['item_status'] = 'Deleted' return jsonify(success=True) + @app.route('/cancel/', methods=['POST']) def cancel_item(local_id): download_queue[local_id]['item_status'] = 'Cancelled' return jsonify(success=True) + @app.route('/retry/', methods=['POST']) def retry_item(local_id): download_queue[local_id]['item_status'] = 'Waiting' return jsonify(success=True) + @app.route('/download/', methods=['POST']) def download_file(url): parse_url(url) return jsonify(success=True) + @app.route('/clear', methods=['POST']) def clear_items(): keys_to_delete = [] @@ -100,6 +108,7 @@ def clear_items(): del download_queue[key] return jsonify(success=True) + @app.route('/download_queue') def download_queue_page(): config_path = os.path.join(config_dir(), 'onthespot', 'otsconfig.json') @@ -107,10 +116,12 @@ def download_queue_page(): config_data = json.load(config_file) return render_template('download_queue.html', config=config_data) + @app.route('/') def index(): return redirect(url_for('download_queue_page')) + @app.route('/search') def search(): config_path = os.path.join(config_dir(), 'onthespot', 'otsconfig.json') @@ -118,10 +129,12 @@ def search(): config_data = json.load(config_file) return render_template('search.html', config=config_data) + @app.route('/about') def about(): return render_template('about.html') + @app.route('/search_results') def search_results(): query = request.args.get('q', '') @@ -153,6 +166,7 @@ def settings(): config_data = json.load(config_file) return render_template('settings.html', config=config_data, account_pool=account_pool) # Render the settings.html file + @app.route('/update_settings', methods=['POST']) def update_settings(): data = request.json @@ -163,6 +177,7 @@ def update_settings(): config.update() return jsonify(success=True) + def main(): fill_account_pool = FillAccountPool() @@ -191,5 +206,6 @@ def main(): app.run(debug=True) + if __name__ == '__main__': main()