#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ MoneyPack Browser v1.0 Premium Web Browser - Better than Opera/Vivaldi Created by MoneyPack Features: - Tabbed browsing with animated tab bar - Custom dark luxury UI - Built-in ad blocker - Sidebar (bookmarks, history, downloads) - Speed dial new tab page with MoneyPack branding - Download manager - Private/Incognito mode - Custom frameless window - Keyboard shortcuts - Find in page - Zoom controls - Print support - Developer tools - Full screen mode """ import sys import os import json import time from pathlib import Path from datetime import datetime from urllib.parse import urlparse from PyQt6.QtWidgets import * from PyQt6.QtCore import * from PyQt6.QtGui import * from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtWebEngineCore import ( QWebEnginePage, QWebEngineProfile, QWebEngineSettings, QWebEngineUrlRequestInterceptor ) # ═══════════════════════════════════════════════════════ # CONFIG # ═══════════════════════════════════════════════════════ APP_NAME = "MoneyPack Browser" APP_VERSION = "1.0.0" DATA_DIR = Path.home() / ".moneypack_browser" BOOKMARKS_FILE = DATA_DIR / "bookmarks.json" HISTORY_FILE = DATA_DIR / "history.json" SETTINGS_FILE = DATA_DIR / "settings.json" # Ensure data dir DATA_DIR.mkdir(parents=True, exist_ok=True) # Colors class C: BG = "#08080f" BG2 = "#0e0e1a" PANEL = "#12122a" CARD = "#1a1a38" HOVER = "#222250" TAB = "#161630" TAB_ACTIVE = "#1e1e42" GOLD = "#d4af37" PINK = "#ff1a5c" CYAN = "#00e5ff" PURPLE = "#a855f7" WHITE = "#f0f0ff" TEXT = "#c0c0dd" DIM = "#5a5a80" MUTED = "#2a2a4a" GREEN = "#00e676" RED = "#ff1744" BORDER = "#ffffff10" URL_BG = "#0c0c1e" # ═══════════════════════════════════════════════════════ # AD BLOCKER # ═══════════════════════════════════════════════════════ AD_DOMAINS = { "doubleclick.net", "googlesyndication.com", "googleadservices.com", "adservice.google.com", "pagead2.googlesyndication.com", "facebook.com/tr", "analytics.google.com", "ad.doubleclick.net", "ads.yahoo.com", "ads.twitter.com", "advertising.com", "adnxs.com", "adsrvr.org", "outbrain.com", "taboola.com", "criteo.com", "criteo.net", "moatads.com", "quantserve.com", "scorecardresearch.com", "amazon-adsystem.com", "adobedtm.com", "hotjar.com", "mouseflow.com", "fullstory.com", "popads.net", "popcash.net", "propellerads.com", "adcolony.com", "applovin.com", "unity3d.com/ads", "chartbeat.com", "optimizely.com", "crazyegg.com", } class AdBlocker(QWebEngineUrlRequestInterceptor): """Block ads and trackers.""" def __init__(self): super().__init__() self.blocked_count = 0 def interceptRequest(self, info): url = info.requestUrl().toString() host = info.requestUrl().host() # Check against ad domains for ad_domain in AD_DOMAINS: if ad_domain in host: info.block(True) self.blocked_count += 1 return # ═══════════════════════════════════════════════════════ # DATA MANAGEMENT # ═══════════════════════════════════════════════════════ class DataManager: """Manage bookmarks, history, settings.""" @staticmethod def load_json(path, default=None): if default is None: default = [] try: if path.exists(): return json.loads(path.read_text()) except: pass return default @staticmethod def save_json(path, data): try: path.write_text(json.dumps(data, indent=2)) except: pass @classmethod def get_bookmarks(cls): return cls.load_json(BOOKMARKS_FILE, [ {"title": "Google", "url": "https://www.google.com"}, {"title": "YouTube", "url": "https://www.youtube.com"}, {"title": "GitHub", "url": "https://github.com"}, {"title": "Reddit", "url": "https://www.reddit.com"}, ]) @classmethod def save_bookmarks(cls, bookmarks): cls.save_json(BOOKMARKS_FILE, bookmarks) @classmethod def add_bookmark(cls, title, url): bm = cls.get_bookmarks() bm.append({"title": title, "url": url, "added": datetime.now().isoformat()}) cls.save_bookmarks(bm) @classmethod def get_history(cls): return cls.load_json(HISTORY_FILE, []) @classmethod def add_history(cls, title, url): h = cls.get_history() h.insert(0, {"title": title, "url": url, "time": datetime.now().isoformat()}) h = h[:1000] # Keep last 1000 cls.save_json(HISTORY_FILE, h) @classmethod def clear_history(cls): cls.save_json(HISTORY_FILE, []) # ═══════════════════════════════════════════════════════ # CUSTOM TAB BAR # ═══════════════════════════════════════════════════════ class BrowserTab(QWidget): """Single browser tab with web view.""" def __init__(self, url="", private=False): super().__init__() layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) if private: profile = QWebEngineProfile() self.page = QWebEnginePage(profile) self.web = QWebEngineView() self.web.setPage(self.page) else: self.web = QWebEngineView() # Settings settings = self.web.settings() settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.ScrollAnimatorEnabled, True) layout.addWidget(self.web) if url: self.web.setUrl(QUrl(url)) def navigate(self, url): if not url.startswith(("http://", "https://", "file://", "about:")): if "." in url and " " not in url: url = "https://" + url else: url = f"https://www.google.com/search?q={url}" self.web.setUrl(QUrl(url)) # ═══════════════════════════════════════════════════════ # NEW TAB PAGE (Speed Dial) # ═══════════════════════════════════════════════════════ NEW_TAB_HTML = """
""" # ═══════════════════════════════════════════════════════ # MAIN BROWSER WINDOW # ═══════════════════════════════════════════════════════ class MoneyPackBrowser(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(APP_NAME) self.setMinimumSize(1200, 750) self.resize(1400, 850) self.setWindowFlags(Qt.WindowType.FramelessWindowHint) self.setStyleSheet(f"QMainWindow {{ background: {C.BG}; }}") # Ad blocker self.ad_blocker = AdBlocker() QWebEngineProfile.defaultProfile().setUrlRequestInterceptor(self.ad_blocker) # Build UI self._tabs = [] self._private_mode = False self._build_ui() self._setup_shortcuts() # Open new tab self.new_tab() def _build_ui(self): central = QWidget() self.setCentralWidget(central) layout = QVBoxLayout(central) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # ─── TITLE BAR ─── titlebar = QWidget() titlebar.setFixedHeight(36) titlebar.setStyleSheet(f"background: {C.BG};") self._titlebar = titlebar self._drag_pos = None titlebar.mousePressEvent = self._title_press titlebar.mouseMoveEvent = self._title_move titlebar.mouseReleaseEvent = self._title_release titlebar.mouseDoubleClickEvent = lambda e: self.showNormal() if self.isMaximized() else self.showMaximized() tb_layout = QHBoxLayout(titlebar) tb_layout.setContentsMargins(10, 0, 6, 0) # Brand brand = QLabel("MoneyPack") brand.setFont(QFont("Segoe UI", 9, QFont.Weight.Bold)) brand.setStyleSheet(f"color: {C.GOLD};") tb_layout.addWidget(brand) tb_layout.addStretch() # Window controls for txt, act in [("\u2014", self.showMinimized), ("\u25a1", lambda: self.showNormal() if self.isMaximized() else self.showMaximized()), ("\u2715", self.close)]: b = QPushButton(txt) b.setFixedSize(36, 28) color = C.RED if txt == "\u2715" else C.DIM b.setStyleSheet(f"QPushButton{{background:transparent;color:{color};border:none;font-size:12px;border-radius:4px;}}QPushButton:hover{{background:{C.HOVER};color:{C.WHITE};}}") b.clicked.connect(act) tb_layout.addWidget(b) layout.addWidget(titlebar) # ─── TAB BAR ─── tab_bar_widget = QWidget() tab_bar_widget.setFixedHeight(38) tab_bar_widget.setStyleSheet(f"background: {C.BG2};") self._tab_bar_layout = QHBoxLayout(tab_bar_widget) self._tab_bar_layout.setContentsMargins(8, 4, 8, 0) self._tab_bar_layout.setSpacing(2) self._tab_bar_layout.addStretch() # New tab button new_btn = QPushButton("+") new_btn.setFixedSize(28, 28) new_btn.setStyleSheet(f"QPushButton{{background:{C.CARD};color:{C.TEXT};border:none;border-radius:14px;font-size:16px;}}QPushButton:hover{{background:{C.PINK};color:white;}}") new_btn.setCursor(Qt.CursorShape.PointingHandCursor) new_btn.clicked.connect(self.new_tab) self._tab_bar_layout.addWidget(new_btn) layout.addWidget(tab_bar_widget) # ─── TOOLBAR ─── toolbar = QWidget() toolbar.setFixedHeight(44) toolbar.setStyleSheet(f"background: {C.PANEL}; border-bottom: 1px solid {C.MUTED};") tool_layout = QHBoxLayout(toolbar) tool_layout.setContentsMargins(8, 4, 8, 4) tool_layout.setSpacing(4) # Nav buttons btn_style = f"QPushButton{{background:transparent;color:{C.DIM};border:none;font-size:16px;padding:4px 8px;border-radius:6px;}}QPushButton:hover{{background:{C.HOVER};color:{C.WHITE};}}" self._back_btn = QPushButton("\u2190") self._back_btn.setStyleSheet(btn_style) self._back_btn.clicked.connect(self._go_back) tool_layout.addWidget(self._back_btn) self._fwd_btn = QPushButton("\u2192") self._fwd_btn.setStyleSheet(btn_style) self._fwd_btn.clicked.connect(self._go_forward) tool_layout.addWidget(self._fwd_btn) self._reload_btn = QPushButton("\u21bb") self._reload_btn.setStyleSheet(btn_style) self._reload_btn.clicked.connect(self._reload) tool_layout.addWidget(self._reload_btn) self._home_btn = QPushButton("\u2302") self._home_btn.setStyleSheet(btn_style) self._home_btn.clicked.connect(self.new_tab) tool_layout.addWidget(self._home_btn) # URL bar self._url_bar = QLineEdit() self._url_bar.setPlaceholderText("Search or enter URL...") self._url_bar.setStyleSheet(f""" QLineEdit {{ background: {C.URL_BG}; border: 1px solid {C.MUTED}; border-radius: 20px; padding: 8px 20px; color: {C.WHITE}; font-size: 13px; selection-background-color: {C.PINK}44; }} QLineEdit:focus {{ border-color: {C.PINK}; }} """) self._url_bar.returnPressed.connect(self._navigate_url) tool_layout.addWidget(self._url_bar) # Right buttons self._bookmark_btn = QPushButton("\u2606") self._bookmark_btn.setStyleSheet(btn_style) self._bookmark_btn.clicked.connect(self._add_bookmark) self._bookmark_btn.setToolTip("Bookmark this page") tool_layout.addWidget(self._bookmark_btn) self._private_btn = QPushButton("\u229a") self._private_btn.setStyleSheet(btn_style) self._private_btn.clicked.connect(self._toggle_private) self._private_btn.setToolTip("Private Mode") tool_layout.addWidget(self._private_btn) # Ad block counter self._adblock_label = QLabel("ADS: 0") self._adblock_label.setStyleSheet(f"color: {C.GREEN}; font-size: 9px; font-weight: bold; padding: 0 8px;") tool_layout.addWidget(self._adblock_label) # Menu button menu_btn = QPushButton("\u2261") menu_btn.setStyleSheet(f"QPushButton{{background:transparent;color:{C.TEXT};border:none;font-size:20px;padding:4px 10px;border-radius:6px;}}QPushButton:hover{{background:{C.HOVER};}}") menu_btn.clicked.connect(self._show_menu) tool_layout.addWidget(menu_btn) layout.addWidget(toolbar) # ─── CONTENT ─── self._stack = QStackedWidget() self._stack.setStyleSheet(f"background: {C.BG};") layout.addWidget(self._stack) # ─── STATUS BAR ─── status = QWidget() status.setFixedHeight(22) status.setStyleSheet(f"background: {C.BG2}; border-top: 1px solid {C.MUTED};") sl = QHBoxLayout(status) sl.setContentsMargins(10, 0, 10, 0) self._status_label = QLabel("Ready") self._status_label.setStyleSheet(f"color: {C.DIM}; font-size: 10px;") sl.addWidget(self._status_label) sl.addStretch() self._zoom_label = QLabel("100%") self._zoom_label.setStyleSheet(f"color: {C.DIM}; font-size: 10px;") sl.addWidget(self._zoom_label) layout.addWidget(status) # Update ad counter timer self._ad_timer = QTimer(self) self._ad_timer.timeout.connect(self._update_adblock) self._ad_timer.start(2000) # ─── TAB MANAGEMENT ─── def new_tab(self, url=""): tab = BrowserTab(private=self._private_mode) if url: tab.navigate(url) else: # Speed dial page tab.web.setHtml(NEW_TAB_HTML) # Connect signals tab.web.titleChanged.connect(lambda title: self._update_tab_title(tab, title)) tab.web.urlChanged.connect(lambda qurl: self._on_url_changed(tab, qurl)) tab.web.loadStarted.connect(lambda: self._status_label.setText("Loading...")) tab.web.loadFinished.connect(lambda ok: self._status_label.setText("Done" if ok else "Failed")) self._tabs.append(tab) self._stack.addWidget(tab) self._stack.setCurrentWidget(tab) self._rebuild_tab_bar() self._url_bar.clear() self._url_bar.setFocus() def close_tab(self, index): if len(self._tabs) <= 1: self.close() return tab = self._tabs.pop(index) self._stack.removeWidget(tab) tab.deleteLater() if index >= len(self._tabs): index = len(self._tabs) - 1 self._stack.setCurrentWidget(self._tabs[index]) self._rebuild_tab_bar() def switch_tab(self, index): if 0 <= index < len(self._tabs): self._stack.setCurrentWidget(self._tabs[index]) self._rebuild_tab_bar() url = self._tabs[index].web.url().toString() if url and not url.startswith("about:"): self._url_bar.setText(url) def _rebuild_tab_bar(self): # Clear existing tab buttons (keep stretch and + button) while self._tab_bar_layout.count() > 2: item = self._tab_bar_layout.takeAt(0) if item.widget(): item.widget().deleteLater() current_idx = self._tabs.index(self._stack.currentWidget()) if self._stack.currentWidget() in self._tabs else 0 for i, tab in enumerate(self._tabs): title = tab.web.title() or "New Tab" if len(title) > 20: title = title[:18] + "..." tab_widget = QWidget() tab_widget.setFixedHeight(30) tab_widget.setCursor(Qt.CursorShape.PointingHandCursor) is_active = (i == current_idx) bg = C.TAB_ACTIVE if is_active else C.TAB border = C.PINK if is_active else "transparent" tab_widget.setStyleSheet(f"background: {bg}; border: 1px solid {border}; border-bottom: none; border-radius: 8px 8px 0 0;") tl = QHBoxLayout(tab_widget) tl.setContentsMargins(12, 2, 4, 2) tl.setSpacing(4) # Private icon if self._private_mode: priv = QLabel("\u229a") priv.setStyleSheet(f"color: {C.PURPLE}; font-size: 10px; border: none;") tl.addWidget(priv) title_lbl = QLabel(title) title_lbl.setStyleSheet(f"color: {C.WHITE if is_active else C.DIM}; font-size: 11px; border: none; font-weight: {'bold' if is_active else 'normal'};") tl.addWidget(title_lbl) close_btn = QPushButton("\u2715") close_btn.setFixedSize(18, 18) close_btn.setStyleSheet(f"QPushButton{{background:transparent;color:{C.DIM};border:none;font-size:10px;border-radius:9px;}}QPushButton:hover{{background:{C.RED};color:white;}}") close_btn.clicked.connect(lambda _, idx=i: self.close_tab(idx)) tl.addWidget(close_btn) # Click to switch tab_widget.mousePressEvent = lambda e, idx=i: self.switch_tab(idx) self._tab_bar_layout.insertWidget(i, tab_widget) def _update_tab_title(self, tab, title): self._rebuild_tab_bar() if tab == self._stack.currentWidget(): self.setWindowTitle(f"{title} - {APP_NAME}") def _on_url_changed(self, tab, qurl): if tab == self._stack.currentWidget(): url = qurl.toString() if not url.startswith("about:") and not url.startswith("data:"): self._url_bar.setText(url) # Add to history title = tab.web.title() or url DataManager.add_history(title, url) # ─── NAVIGATION ─── def _navigate_url(self): url = self._url_bar.text().strip() if not url: return current = self._stack.currentWidget() if current and hasattr(current, 'navigate'): current.navigate(url) def _go_back(self): current = self._stack.currentWidget() if current: current.web.back() def _go_forward(self): current = self._stack.currentWidget() if current: current.web.forward() def _reload(self): current = self._stack.currentWidget() if current: current.web.reload() # ─── FEATURES ─── def _add_bookmark(self): current = self._stack.currentWidget() if current: title = current.web.title() url = current.web.url().toString() if url and not url.startswith("about:"): DataManager.add_bookmark(title, url) self._bookmark_btn.setText("\u2605") QTimer.singleShot(2000, lambda: self._bookmark_btn.setText("\u2606")) self._status_label.setText(f"Bookmarked: {title}") def _toggle_private(self): self._private_mode = not self._private_mode color = C.PURPLE if self._private_mode else C.DIM self._private_btn.setStyleSheet(f"QPushButton{{background:{'#a855f733' if self._private_mode else 'transparent'};color:{color};border:none;font-size:16px;padding:4px 8px;border-radius:6px;}}QPushButton:hover{{background:{C.HOVER};color:{C.WHITE};}}") self._status_label.setText("Private Mode " + ("ON" if self._private_mode else "OFF")) def _update_adblock(self): self._adblock_label.setText(f"ADS: {self.ad_blocker.blocked_count}") def _show_menu(self): menu = QMenu(self) menu.setStyleSheet(f""" QMenu {{background:{C.PANEL};border:1px solid {C.MUTED};color:{C.TEXT};border-radius:8px;padding:4px;}} QMenu::item {{padding:8px 20px;border-radius:4px;}} QMenu::item:selected {{background:{C.HOVER};color:{C.WHITE};}} QMenu::separator {{height:1px;background:{C.MUTED};margin:4px 8px;}} """) menu.addAction("New Tab (Ctrl+T)", self.new_tab) menu.addAction("Close Tab (Ctrl+W)", lambda: self.close_tab(self._tabs.index(self._stack.currentWidget()))) menu.addSeparator() # Bookmarks submenu bm_menu = menu.addMenu("Bookmarks") bm_menu.setStyleSheet(menu.styleSheet()) for bm in DataManager.get_bookmarks(): bm_menu.addAction(bm['title'], lambda u=bm['url']: self.new_tab(u)) # History submenu h_menu = menu.addMenu("History") h_menu.setStyleSheet(menu.styleSheet()) for item in DataManager.get_history()[:15]: h_menu.addAction(f"{item['title'][:30]}", lambda u=item['url']: self.new_tab(u)) h_menu.addSeparator() h_menu.addAction("Clear History", DataManager.clear_history) menu.addSeparator() menu.addAction("Zoom In (Ctrl++)", self._zoom_in) menu.addAction("Zoom Out (Ctrl+-)", self._zoom_out) menu.addAction("Reset Zoom", self._zoom_reset) menu.addSeparator() menu.addAction("Find in Page (Ctrl+F)", self._find_in_page) menu.addAction("Developer Tools (F12)", self._dev_tools) menu.addAction("Full Screen (F11)", self._fullscreen) menu.addSeparator() menu.addAction(f"Ads Blocked: {self.ad_blocker.blocked_count}") menu.addSeparator() menu.addAction(f"About {APP_NAME}", self._about) menu.exec(self.mapToGlobal(QPoint(self.width() - 200, 80))) def _zoom_in(self): current = self._stack.currentWidget() if current: factor = current.web.zoomFactor() + 0.1 current.web.setZoomFactor(factor) self._zoom_label.setText(f"{int(factor*100)}%") def _zoom_out(self): current = self._stack.currentWidget() if current: factor = max(0.3, current.web.zoomFactor() - 0.1) current.web.setZoomFactor(factor) self._zoom_label.setText(f"{int(factor*100)}%") def _zoom_reset(self): current = self._stack.currentWidget() if current: current.web.setZoomFactor(1.0) self._zoom_label.setText("100%") def _find_in_page(self): text, ok = QInputDialog.getText(self, "Find", "Search for:") if ok and text: current = self._stack.currentWidget() if current: current.web.findText(text) def _dev_tools(self): current = self._stack.currentWidget() if current: # Open dev tools in new tab page = current.web.page() if page: page.setDevToolsPage(QWebEnginePage()) def _fullscreen(self): if self.isFullScreen(): self.showNormal() else: self.showFullScreen() def _about(self): QMessageBox.about(self, f"About {APP_NAME}", f"{APP_NAME} v{APP_VERSION}\n" "Created by MoneyPack\n\n" "Premium Web Browser\n\n" "Features:\n" "- Tabbed browsing\n" "- Built-in ad blocker\n" "- Speed dial homepage\n" "- Private browsing mode\n" "- Bookmarks & history\n" "- Custom dark theme\n" f"\nAds blocked this session: {self.ad_blocker.blocked_count}" ) # ─── SHORTCUTS ─── def _setup_shortcuts(self): QShortcut(QKeySequence("Ctrl+T"), self, self.new_tab) QShortcut(QKeySequence("Ctrl+W"), self, lambda: self.close_tab(self._tabs.index(self._stack.currentWidget())) if self._tabs else None) QShortcut(QKeySequence("Ctrl+L"), self, lambda: (self._url_bar.selectAll(), self._url_bar.setFocus())) QShortcut(QKeySequence("Ctrl+R"), self, self._reload) QShortcut(QKeySequence("F5"), self, self._reload) QShortcut(QKeySequence("Alt+Left"), self, self._go_back) QShortcut(QKeySequence("Alt+Right"), self, self._go_forward) QShortcut(QKeySequence("Ctrl++"), self, self._zoom_in) QShortcut(QKeySequence("Ctrl+-"), self, self._zoom_out) QShortcut(QKeySequence("Ctrl+0"), self, self._zoom_reset) QShortcut(QKeySequence("Ctrl+F"), self, self._find_in_page) QShortcut(QKeySequence("F11"), self, self._fullscreen) QShortcut(QKeySequence("F12"), self, self._dev_tools) QShortcut(QKeySequence("Ctrl+D"), self, self._add_bookmark) # Tab switching for i in range(9): QShortcut(QKeySequence(f"Ctrl+{i+1}"), self, lambda idx=i: self.switch_tab(idx)) # ─── TITLEBAR DRAG ─── def _title_press(self, e): if e.button() == Qt.MouseButton.LeftButton: self._drag_pos = e.globalPosition().toPoint() - self.frameGeometry().topLeft() def _title_move(self, e): if self._drag_pos and e.buttons() == Qt.MouseButton.LeftButton: self.move(e.globalPosition().toPoint() - self._drag_pos) def _title_release(self, e): self._drag_pos = None # ═══════════════════════════════════════════════════════ # ENTRY # ═══════════════════════════════════════════════════════ def main(): app = QApplication(sys.argv) app.setApplicationName(APP_NAME) app.setFont(QFont("Segoe UI", 10)) window = MoneyPackBrowser() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()