This commit is contained in:
Izalia Mae 2020-08-26 03:59:03 -04:00
parent 66561a94d9
commit 6ac080f480
31 changed files with 1584 additions and 1111 deletions

View file

@ -4,11 +4,13 @@ Web browser written in Python and Qt with PyQt5
## Features
*
* Quick access to various page functions in the status bar
* Login to a Mastodon account
* Quick access to domain permissions in the status bar
* Create new Mastodon posts without going to the webui
* Interact with Mastodon posts by right-clicking the post's url
* Control the browser via cli from a terminal
* Ability to add user scripts (basic support atm)
* Custom searches via keyword
* Remote web dev tools (localhost only atm)
note: I may add full pleroma support in the future
@ -31,7 +33,7 @@ Make sure you add Python to PATH
### Python dependencies
Windows:
pip3 install -r requirements.txt -r requirements-win.txt
Linux:
@ -48,6 +50,7 @@ Linux (pyqt via pip):
* Handle cookies
* Add folder structure for bookmarks
* Create history tab
* Add proxy support
* Ability to add new custom searches via local or remote json
* Add optional auto-updater with stable/testing channels
* Add ability to 'freeze' tabs
@ -55,9 +58,9 @@ Linux (pyqt via pip):
## Bugs
* Resetting a page search sometimes scrolls the page to the top or an element on the page
* Web notifications use the wrong icon sometimes
* Switching between PyQt versions resets cookies
* Stop and reload buttons occasionally don't get set properly after finished loading
* The nitter redirect only works when loading a url from the navbar
* Web inspector is kinda broken
* The webview is really blurry and glitchy in virtualbox with the VboxSVGA controller with 3d accel enabled
@ -68,3 +71,4 @@ Linux (pyqt via pip):
* No web notification support in QWebEngine
* Download urls with query parameters will probably produce junk names atm because QWebEngineDownloadItem.suggestedFileName() doesn't exist
* Can't login to twitch.tv and channels sometimes don't load
* Can't browse github code trees. Use right click > Open Link in New Tab to work around the issue for now

View file

@ -1,10 +1,10 @@
__application__ = 'QtWeb'
__version_info__ = (0,1,6,9)
__version_info__ = (0,1,7)
__version__ = '.'.join(str(v) for v in __version_info__)
__author__ = 'Zoey Mae'
dbversion = 202008150250
prev_dbversion = 202008052224
dbversion = 202008200718
prev_dbversion = 202008150250
import sys

View file

@ -1,17 +1,14 @@
import sys
from collections import namedtuple
from os.path import abspath, dirname, join, isdir, expanduser
from os.path import abspath, dirname, join, isdir
from os import environ, makedirs, walk
from random import randint
from . import __version__, __application__, isWindows
from .arguments import profile
from .Lib.IzzyLib import logging
from .Lib.IzzyLib.misc import randomgen
import PyQt5
from PyQt5.QtCore import QStandardPaths
from PyQt5.QtWebEngineWidgets import QWebEngineProfile
@ -41,12 +38,21 @@ variables = {
'lock': join(datapath, profile, 'lock.pid'),
'home': home,
'temp': join(temp, 'qtweb'),
'local': 'web:'
'local': 'qtweb'
}
default_vars = {
'homepage': 'https://ddg.gg',
'nitter': 'https://nitter.net',
'mhost': '127.0.0.1',
'mport': 8008
}
# Set various variables
var = namedtuple('Variables', variables.keys())(*variables.values())
default = namedtuple('Default', default_vars.keys())(*default_vars.values())
## create necessary data dirs
if not isdir(var.profilepath):
@ -55,5 +61,7 @@ if not isdir(var.profilepath):
if not isdir(var.temp):
makedirs(var.temp, exist_ok=True)
if not isdir(join(var.datapath, 'UserScripts')):
makedirs(join(var.datapath, 'UserScripts'), exist_ok=True)
environ['QTWEBENGINE_DICTIONARIES_PATH'] = join(var.resources, 'dictionaries')
#environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '127.0.0.1:8018'

View file

@ -16,13 +16,12 @@ from DBUtils.PooledDB import PooledDB
from .. import dbversion, prev_dbversion
from ..config import var
from ..functions import DateTime
tables = {
'config': OrderedDict([
('id', 'INTEGER PRIMARY KEY'),
('key', 'TEXT'),
('key', 'TEXT UNIQUE'),
('value', 'TEXT')
]),
'bookmarks': OrderedDict([
@ -42,7 +41,7 @@ tables = {
('username', 'TEXT'),
('displayname', 'TEXT'),
('domain', 'TEXT'),
('fullname', 'TEXT'),
('fullname', 'TEXT UNIQUE'),
('apikey', 'TEXT'),
('tootlimit', 'INTEGER'),
('avatar', 'TEXT'),
@ -50,7 +49,7 @@ tables = {
]),
'siteoptions': OrderedDict([
('id', 'INTEGER PRIMARY KEY'),
('domain', 'TEXT'),
('domain', 'TEXT UNIQUE'),
('microphone', 'BOOLEAN DEFAULT 0'),
('notification', 'BOOLEAN DEFAULT 0'),
('camera', 'BOOLEAN DEFAULT 0'),
@ -135,6 +134,7 @@ def ParseData(table, row):
class DB():
def __init__(self, dbfile=var.database):
self.db = PooledDB(sqlite3, database=dbfile, maxconnections=50, mincached=5, maxusage=1)
self.window = None
self.cache = {}
self.resclass = {}
@ -221,7 +221,6 @@ class DB():
def fetch(self, table, rowid=None, single=True, sort=None, **kwargs):
db = self
table = table if tables.get(table) else table.replace('_migrate', '')
tablekeys = self._table_keys(table)
querysort = f'ORDER BY {sort}'
Result = self.resclass.get(table, None)
@ -385,10 +384,30 @@ def CreateDatabase():
db.CreateTable(name)
db.insert('config', {'key': 'version', 'value': dbversion})
db.insert('config', {'key': 'search', 'value': 'ddg'})
for engine in SearchEngines:
db.insert('search', engine)
darktheme = {
'name': 'Dark',
'AlternateBase': '#353535',
'Base': '#191919',
'Button': '#353535',
'ButtonText': '#ffffff',
'BrightText': '#ff0000',
'Highlight': '#2a82da',
'HighlightedText': '#000000',
'Link': '#2a82da',
'Text': '#ffffff',
'ToolTipBase': '#000000',
'ToolTipText': '#ffffff',
'Window': '#353535',
'WindowText': '#ffffff'
}
db.insert('themes', darktheme)
perm = True
backupdb = f'{var.database}.backup-{prev_dbversion}'

View file

@ -6,7 +6,9 @@ from ..Lib.IzzyLib.misc import boolean
from ..Lib.IzzyLib import logging
from .connection import db, tables, ParseData
from ..functions import psl, DateTime
from ..functions import psl
from PyQt5.QtGui import QPalette, QColor
def config(key=None, default=None):
@ -101,3 +103,24 @@ def search(keyword):
cache.store(keyword, row)
return row
def theme(name=None):
if name:
row = db.fetch('themes', name=name)
if not row:
logging.error('Cannot find color theme:', name)
return
row.palette = db.window.themes.ColorToPalette(row.asdict())
return row
else:
rows = db.fetch('themes', single=False)
for row in rows:
row.palette = db.window.themes.ColorToPalette(row.asdict())
return rows

View file

@ -22,7 +22,6 @@ from PyQt5.QtCore import QDateTime
def run(version):
global db
backupdb = var.database + f'.backup-{version}'
version_row = db.fetch('config', key='version')
version = int(version)
if not isfile(backupdb):
@ -89,4 +88,5 @@ def run(version):
db.UpVersion(202008150250)
logging.info(f'Updated database to version {dbversion} :3')

View file

@ -106,8 +106,18 @@ def tab(tabid, url, title):
tabrow = db.fetch('tabs', url=url)
if not tabrow:
result = db.insert('tabs', {'title': title, 'url': url, 'tabid': tabid})
op = 'insert'
db.insert('tabs', {'title': title, 'url': url, 'tabid': tabid})
else:
result = db.update('tabs', tabrow.id, {'title': title, 'url': url, 'tabid': tabid})
op = 'update'
tabrow.Update({'title': title, 'url': url, 'tabid': tabid})
def theme(name, palette):
row = get.theme(name)
data = db.window.themes.PaletteToColor(palette)
if row:
row.Update(data)
else:
db.insert('themes', data)

View file

@ -15,10 +15,13 @@ from PyQt5.QtWidgets import *
from PyQt5 import uic
class EditBookmark(object):
def __init__(self, title=None, url=None, row=None):
self.ui = uic.loadUi(join(var.resources, 'editbookmark.ui'))
self.ui.setWindowTitle('Add/Edit Bookmark')
class EditBookmark(QDialog):
def __init__(self, parent, title=None, url=None, row=None):
super().__init__(parent)
self.ui = uic.loadUi(join(var.resources, 'dialog-bookmark.ui'))
self.parent = parent
self.setLayout(self.ui.layout())
self.setWindowTitle('Add/Edit Bookmark')
self.row = row if row else db.fetch('bookmarks', url=url)
@ -57,19 +60,23 @@ class EditBookmark(object):
if action:
action['cmd'](*action.get('args', []), **action.get('kwargs', {}))
self.ui.deleteLater()
del self
self.close()
self.deleteLater()
del self.parent.open_dialogs['bookmarkedit']
class SiteSettings(object):
def __init__(self, host):
self.ui = uic.loadUi(join(var.resources, 'sitesettings.ui'))
class SiteSettings(QDialog):
def __init__(self, parent, host):
super().__init__(parent)
self.ui = uic.loadUi(join(var.resources, 'dialog-sitesettings.ui'))
self.setLayout(self.ui.layout())
self.parent = parent
self.domain = host
self.ui.setWindowTitle(self.domain.title())
self.setWindowTitle(self.domain.title())
self.row = get.permissions(self.domain, defaults=True)
if not self.row:
if not self.row.domain:
self.ui.reset.setEnabled(False)
else:
@ -82,6 +89,7 @@ class SiteSettings(object):
self.ui.location.setChecked(self.row.location)
self.ui.camera.setChecked(self.row.camera)
self.ui.allowhttp.setChecked(self.row.allowhttp)
self.ui.mastodon.setChecked(self.row.mastodon)
self.ui.save.clicked.connect(lambda : self._button_click('save'))
self.ui.reset.clicked.connect(lambda : self._button_click('reset'))
@ -100,7 +108,8 @@ class SiteSettings(object):
'location': self.ui.location.isChecked(),
'fullscreen': self.ui.fullscreen.isChecked(),
'adblock': self.ui.adblock.isChecked(),
'allowhttp': self.ui.allowhttp.isChecked()
'allowhttp': self.ui.allowhttp.isChecked(),
'mastodon': self.ui.mastodon.isChecked()
}
actions = {
@ -113,17 +122,20 @@ class SiteSettings(object):
if action:
action['cmd'](*action.get('args', []), **action.get('kwargs', {}))
self.close()
self.ui.deleteLater()
del self
del self.parent.open_dialogs['siteedit']
class Toot():
def __init__(self, window, mastodon, data=None, reply=None):
self.window = window
self.mastodon = mastodon
class Toot(QDialog):
def __init__(self, parent, data=None, reply=None):
super().__init__(parent)
self.ui = uic.loadUi(join(var.resources, 'dialog-toot.ui'))
self.setLayout(self.ui.layout())
self.setWindowTitle('New Toot')
self.ui = uic.loadUi(join(var.resources, 'toot.ui'))
self.ui.setWindowTitle('New Toot')
self.parent = parent
self.mastodon = parent.mastodon
self.accounts = {}
self.reply = reply
self.post = None
@ -196,7 +208,7 @@ class Toot():
def _get_post_data(self):
currentacct = self.ui.account.currentData()
data = self.window.mastodon.GetPostData(self.reply, currentacct)
data = self.mastodon.GetPostData(self.reply, currentacct)
if not data:
return
@ -253,19 +265,16 @@ class Toot():
if action:
action['cmd'](*action.get('args', []), **action.get('kwargs', {}))
self.close()
self.ui.deleteLater()
del self
def show(self):
self.ui.exec_()
del self.parent.open_dialogs['toot']
class NewDownload(QDialog):
def __init__(self, download, profile):
super().__init__()
self.ui = uic.loadUi(join(var.resources, 'newdownload.ui'))
self.ui = uic.loadUi(join(var.resources, 'dialog-download.ui'))
self.setWindowTitle('Download File: ')
self.setModal(True)
self.setFixedHeight(200)
@ -354,3 +363,35 @@ class NewDownload(QDialog):
else:
self.download.setDownloadDirectory(str(filename.parent))
self.download.setDownloadFileName(str(filename.name))
class HttpAuth(QDialog):
def __init__(self, url, authenticator, parent):
super().__init__(parent.window)
self.ui = uic.loadUi(join(var.resources, 'dialog-authentication.ui'))
self.ui.savepass.setEnabled(False)
self.setLayout(self.ui.layout())
self.webview = parent
self.url = url
self.auth = authenticator
self.username = None
self.password = None
self.result = False
text = self.ui.realm.text().format(host=url.host(), msg=self.auth.realm())
self.ui.realm.setText(text)
self.ui.login.clicked.connect(lambda : self._handle_close_button('login'))
self.ui.cancel.clicked.connect(self._handle_close_button)
def _handle_close_button(self, button=None):
if button == 'login':
self.auth.setUser(self.ui.username.text())
self.auth.setPassword(self.ui.password.text())
self.result = True
self.close()
self.deleteLater()

View file

@ -1,106 +0,0 @@
import threading, sys, os
from datetime import datetime
from os.path import join
from .Lib.IzzyLib import logging
from publicsuffixlist import PublicSuffixList
from publicsuffixlist.update import updatePSL
from . import pyqtver, debstable
from .database import db, get, put
from .functions import GetSoftware, httpclient
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
#if hasattr(sys, '_MEIPASS'):
#os.makedirs(join(sys._MEIPASS, 'publicsuffixlist'), exist_ok=True)
#with open(join(sys._MEIPASS, 'publicsuffixlist', 'public_suffix_list.dat'), 'w') as fd:
#resp = httpclient.request('GET', 'https://publicsuffix.org/list/public_suffix_list.dat')
#fd.write(resp.data.decode())
psl = PublicSuffixList()
extract = psl.privatesuffix
## blocked domains and urls (will be optional in the future)
blockedDomain = [
'doubleclick.net',
'rubiconproject.com',
'outbrain.com',
'googletagservices.com',
'amazon-adsystem.com',
'optimizely.com',
'adswizz.com'
]
blockedURL = {
'youtube.com': [
'/api/stats/ads',
'/pagead/adview',
'/pagead/lvz'
'/get_midroll/info'
],
'soundcloud.com': [
'/audio-ads'
],
'facebook.com': [
'/tr']
}
class WebFilter(QWebEngineUrlRequestInterceptor):
def __init__(self, window):
super().__init__()
self.window = window
self.threads = {}
def interceptRequest(self, info):
requrl = info.requestUrl()
reqdomain = requrl.host()
reqtopdomain = extract(reqdomain)
reqpath = requrl.path()
pageurl = info.firstPartyUrl()
pagedomain = pageurl.host()
pagetopdomain = extract(pagedomain)
pagepath = pageurl.path()
pagerow = get.permissions(pagedomain, defaults=True)
## Prevent secure pages from loading insecure content
if not pagerow.allowhttp and requrl.scheme() == 'http' and pageurl.scheme() == 'https':
logging.verbose('Blocked insecure content:', requrl.toString().split('?', 1)[0])
info.block(True)
return
## Block requests if they're in the block list
if self.window.adblock and pagerow.adblock and (reqpath in blockedURL.get(reqdomain, []) or reqdomain in blockedDomain or reqtopdomain in blockedDomain):
logging.verbose('Blocked content:', requrl.toString().split('?', 1)[0])
info.block(True)
return
## Set custom headers
info.setHttpHeader("trans-rights".encode(), "are human rights".encode())
## Check if domain is a mastodon instance
if not pagerow.domain and not pagerow.mastodon and not any(map(requrl.toString().startswith, ['about:', 'chrome://'])):
if not pagedomain:
return
if self.threads.get(pagedomain):
return
self.threads[pagedomain] = threading.Thread(target=GetSoftware, args=[pagedomain])
self.threads[pagedomain].start()
## Delete the thread object once it's finished
elif self.threads.get(pagedomain):
del self.threads[pagedomain]
## Google is a fuck and is very picky about what browsers are supported
#if 'google.com' in [pagetopdomain, reqtopdomain]:
#info.setHttpHeader('user-agent'.encode(), 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'.encode())

View file

@ -47,14 +47,19 @@ class Notifications(object):
self.New('Error', message)
def Icon(name):
def Icon(name, size=None):
filename = join(var.iconpath, name + '.svg')
if not isfile(filename):
logging.error('Invalid icon name:', name)
return QIcon(join(var.iconpath, 'missing.svg'))
icon = QIcon(join(var.iconpath, 'missing.svg'))
return QIcon(filename)
icon = QIcon(filename)
if size:
icon = QIcon(icon.pixmap(16))
return icon
def AddShortcut(key, action, widget):
@ -245,10 +250,13 @@ def Decode(byte):
## subclass QMenu because there's no way to get the number of items
class Menu(QMenu):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self, parent, title=None):
super().__init__(parent)
self.count = 0
if title:
self.setTitle(title)
def AddAction(self, name, callback):
self.addAction(name, callback)
self.count += 1
@ -312,13 +320,17 @@ def ParseSoundcloudInfo(data):
def ExceptionHandler(exctype, value, tb):
lines = [line for line in traceback.format_exception(exctype, value, tb)]
sys._excepthook(exctype, value, tb)
if lines[-1] in ['\n', '\n ']:
lines.remove(lines)
# Apparently the line above is enough to not bring down the whole browser
# Gonna keep custom handler here for now though
#lines = [line for line in traceback.format_exception(exctype, value, tb)]
error = ''.join(lines)
print(f'\n------------\n{error}------------')
#if lines[-1] in ['\n', '\n ']:
#lines.remove(lines)
#error = ''.join(lines)
#print(f'\n------------\n{error}------------')
TextCodec = QTextCodec.codecForMib(106)

View file

@ -1,26 +1,25 @@
import sys, os, time, json, signal, asyncio, tracemalloc
import sys, os, json, signal, asyncio, tracemalloc
from optparse import OptionParser
from urllib.parse import urlencode, urlparse, quote_plus
from urllib.parse import urlparse, quote_plus
from os.path import join, abspath
from base64 import b64encode, b64decode
from pathlib import Path
from .Lib.IzzyLib import logging
from .Lib.IzzyLib.misc import boolean
from . import isWindows, isLinux, debstable, dialogs, tabs, __version__
from . import isWindows, debstable, dialogs, tabs, __version__
from .urlhandlers import LocalHandler, RegisterScheme
from .webview import WebEngineView
from .profile import WebEngineProfile
from .config import var
from .database import db, get, put
from .mastodon import Mastodon
from .mbus import Client, Server
from .themes import SetupThemes, ChangeTheme
from .themes import ColorTheme
from .arguments import args
from .functions import (
Icon, AddShortcut, UserAgentGen,
ExceptionHandler, PrintMethods, Notifications, Menu
ExceptionHandler, Notifications, Menu
)
@ -42,6 +41,7 @@ from PyQt5 import uic
## Prevent errors from bringing down the whole browser
sys._excepthook = sys.excepthook
sys.excepthook = ExceptionHandler
logging.setConfig({'level': get.config('loglevel', 'INFO')})
@ -62,11 +62,15 @@ class Browser(QMainWindow):
else:
self.snapshot = None
SetupThemes(app)
if not debstable:
RegisterScheme(var.local)
db.window = self
self.jsdebug = False
self.darktheme = True if get.config('darktheme') in [1, '1'] else False
self.app = app
self.default_style = app.style().objectName().lower()
self.default_palette = app.style().standardPalette()
self.themes = ColorTheme(self)
self.clipboard = app.clipboard()
self.threadpool = QThreadPool()
self.notif = Notifications()
@ -77,7 +81,7 @@ class Browser(QMainWindow):
'y': 100,
'maximized': True
}
self.statedata.update(json.loads(get.config('state', "{}")))
## Setup empty variables for use later
@ -86,17 +90,23 @@ class Browser(QMainWindow):
self.socket = None
self.urlhover = None
self.fullscreen = False
self.shutdown = False
self.widgettabs = {}
self.open_dialogs = {}
WebEngineProfile(self)
#app.setStyle('fusion')
#ChangeTheme()
self.profile = WebEngineProfile(self)
self.local_scheme_handler = LocalHandler(self)
#logging.setConfig({'systemnotif': self.notif})
if self.darktheme:
ChangeTheme('dark')
theme = get.config('theme', 'Default')
#app.setStyle('fusion')
#self.themes.ChangeTheme()
# Re-enable when color themes are fixed
#if theme != 'Default':
#self.themes.ChangeTheme(theme)
self.ui = uic.loadUi(join(var.resources, 'window.ui'))
self.setCentralWidget(self.ui)
@ -142,14 +152,15 @@ class Browser(QMainWindow):
self.ui.toot.clicked.connect(lambda : self._open_dialog('toot'))
## Keyboard shortcuts
AddShortcut('Ctrl+L', self._focus_urlbar, self.ui.url)
AddShortcut('Ctrl+T', lambda *args: self.NewWebTab(switch=True), self.ui)
AddShortcut('Ctrl+R', lambda *args: self.ui.tabs.currentWidget().reload(), self.ui)
AddShortcut('Ctrl+W', lambda *args: self.CloseTab(), self.ui)
AddShortcut('Ctrl+N', lambda *args: self.NewWebTab('https://www.youtube.com/watch?v=dQw4w9WgXcQ', switch=True), self.ui)
AddShortcut('Ctrl+F', lambda *args: self.ui.tabs.currentWidget().searchbar.Search('toggle'), self.ui)
AddShortcut('F3', lambda *args: self.ui.tabs.currentWidget().searchbar.Search('toggle'), self.ui)
AddShortcut('F11', lambda *args: self.Fullscreen(), self.ui)
AddShortcut('Ctrl+L', self._focus_urlbar, self)
AddShortcut('Ctrl+T', lambda *args: self.NewWebTab(switch=True), self)
AddShortcut('Ctrl+R', lambda *args: self.ui.tabs.currentWidget().reload(), self)
AddShortcut('Ctrl+W', lambda *args: self.CloseTab(), self)
AddShortcut('Ctrl+N', lambda *args: self.NewWebTab('https://www.youtube.com/watch?v=dQw4w9WgXcQ', switch=True), self)
AddShortcut('Ctrl+F', lambda *args: self.ui.tabs.currentWidget().searchbar.Search('toggle'), self)
AddShortcut('F3', lambda *args: self.ui.tabs.currentWidget().searchbar.Search('toggle'), self)
AddShortcut('F5', lambda *args: self.ui.tabs.currentWidget().reload(), self)
AddShortcut('F11', lambda *args: self.Fullscreen(), self)
# keeping this here for testing purposes
#self.profile.setHttpUserAgent('Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0')
@ -162,13 +173,8 @@ class Browser(QMainWindow):
for row in db.fetch('tabs', sort='tabid', single=False):
self.NewWebTab(row.url)
try:
activetab = int(get.config('activetab', 0))
self.ui.tabs.setCurrentIndex(activetab)
except Exception as e:
print(e)
activetab = int(get.config('activetab', 0))
self.ui.tabs.setCurrentIndex(activetab)
self._prevent_empty_tabview()
## Restore the window position and size
@ -203,49 +209,14 @@ class Browser(QMainWindow):
self.ui.url.selectAll()
def _close_window(self, event=None):
lock = Path(var.lock)
opentabs = []
tabrange = list(reversed(range(0, self.ui.tabs.count())))
put.config('activetab', str(self.ui.tabs.currentIndex()))
self.SaveWinState()
for tabid in ([0] if len(tabrange) < 1 else tabrange):
widget = self.ui.tabs.widget(tabid)
if widget.windowTitle():
continue
title = widget.title()
url = widget.url().toString()
opentabs.append(url)
put.tab(tabid, url, title)
self.CloseTab(tabid, shutdown=True)
for row in db.fetch('tabs', single=False):
if row.url not in opentabs:
db.remove('tabs', row.id)
db.close()
logging.verbose('Saved state and tabs. Exiting...')
for srv in [self.server, self.socket]:
if srv:
srv.stop()
if event:
event.accept()
if lock.exists():
lock.unlink()
def _button_click(self, button):
widget = self.ui.tabs.currentWidget()
homepage = get.config('homepage', 'https://ddg.gg')
if button == 'newtab':
self.NewWebTab(switch=True)
return
if hasattr(widget, 'LoadUrl'):
actions = {
'back': {'cmd': widget.back},
@ -253,7 +224,6 @@ class Browser(QMainWindow):
'stop': {'cmd': widget.stop},
'refresh': {'cmd': widget.reload},
'home': {'cmd': widget.LoadUrl, 'args': [homepage]},
'newtab': {'cmd': self.NewWebTab, 'kwargs': {'switch': True}},
'go': {'cmd': widget.LoadUrl, 'args': [self.ui.url.text()]}
}
@ -277,8 +247,10 @@ class Browser(QMainWindow):
logging.error('No mastodon accounts')
return
widget = dialogs.Toot(self, self.mastodon)
widget.show()
widget = self.open_dialogs.get(dialog)
if widget:
widget.activateWindow()
return
webview = self.ui.tabs.currentWidget()
@ -292,6 +264,10 @@ class Browser(QMainWindow):
'siteedit': {
'cmd': dialogs.SiteSettings,
'args': [webview.url().host()]
},
'toot': {
'cmd': dialogs.Toot,
'args': [self]
}
}
@ -300,7 +276,8 @@ class Browser(QMainWindow):
if not action:
return
action['cmd'](*action.get('args', []), **action.get('kwargs', {})).ui.show()
self.open_dialogs[dialog] = action['cmd'](self, *action.get('args', []), **action.get('kwargs', {}))
self.open_dialogs[dialog].show()
def _update_webaction_buttons(self, name=None, state=None):
@ -360,6 +337,55 @@ class Browser(QMainWindow):
self.NewWebTab()
def closeEvent(self, event=None):
## Make sure this function only fires once
if self.shutdown:
return
self.shutdown = True
if event:
event.accept()
lock = Path(var.lock)
opentabs = []
tabrange = list(reversed(range(0, self.ui.tabs.count())))
put.config('activetab', str(self.ui.tabs.currentIndex()))
self.SaveWinState()
for tabid in ([0] if len(tabrange) < 1 else tabrange):
widget = self.ui.tabs.widget(tabid)
if widget.windowTitle():
continue
title = widget.title()
url = widget.url().toString()
if widget.devpage.view:
widget.devpage.closeEvent()
widget.devpage.destroy()
opentabs.append(url)
put.tab(tabid, url, title)
self.CloseTab(tabid, shutdown=True)
for row in db.fetch('tabs', single=False):
if row.url not in opentabs:
db.remove('tabs', row.id)
db.close()
logging.verbose('Saved state and tabs. Exiting...')
for srv in [self.server, self.socket]:
if srv:
srv.stop()
if lock.exists():
lock.unlink()
def SaveWinState(self, width=None, height=None, x=None, y=None):
winstate = self.windowState() in [Qt.WindowActive, Qt.WindowNoState]
maximized = self.windowState() == Qt.WindowMaximized
@ -413,10 +439,7 @@ class Browser(QMainWindow):
def NewWebTab(self, url=None, switch=False, nexttab=False):
#layout = QVBoxLayout()
webview = WebEngineView(self, url)
#layout.addWidget(webview.search)
#layout.addWidget(webview)
webview = WebEngineView(self, url if url else get.config('homepage', 'https://ddg.gg'))
if nexttab and get.config('nexttab', True):
index = self.ui.tabs.currentIndex() + 1
@ -429,6 +452,10 @@ class Browser(QMainWindow):
index = self.ui.tabs.indexOf(webview)
self.ui.tabs.setCurrentIndex(index)
if not url:
self.ui.url.setFocus()
self.ui.url.selectAll()
return webview
@ -489,6 +516,7 @@ class Browser(QMainWindow):
if not shutdown:
self._prevent_empty_tabview()
self.ui.tabs.currentWidget().setFocus()
class UrlBarEsc(QObject):
@ -523,6 +551,11 @@ class TabBar(QTabBar):
self.setDocumentMode(True)
self.window = window
self.setStyleSheet('''
QTabBar::tab:selected {font: bold;}
QTabBar::tab-bar {alignment: center;}
''')
## Set tabs to even take up full width
def tabSizeHint(self, index):
compensation = 0 #I'll need this later
@ -586,6 +619,11 @@ def main():
app.setOrganizationDomain('barkshark.xyz')
app.setWindowIcon(Icon('icon'))
if get.config('remotedebug', False):
debugport = get.config('remotedebugport', '8018')
debughost = 'still gotta sort this out'
os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = f'127.0.0.1:{remotedebugport}'
host = get.config('mhost', 'localhost')
port = get.config('mport', 8008)
@ -616,7 +654,7 @@ def main():
window = Browser(app)
window.server = Server(window)
app.aboutToQuit.connect(window._close_window)
app.aboutToQuit.connect(window.close)
if not isWindows:
window.socket = Server(window, True)

View file

@ -1,8 +1,6 @@
import json, shutil
from urllib import request
from urllib.parse import urlparse, urlencode
from urllib.error import HTTPError
from urllib.parse import urlparse
from os.path import join, isfile, basename
from os import makedirs, remove

View file

@ -1,4 +1,4 @@
import asyncio, socket, json, sys, traceback, tracemalloc, argparse, datetime, time, linecache
import asyncio, socket, json, sys, traceback, tracemalloc, argparse, datetime, linecache
from os.path import join
from pathlib import Path
@ -78,7 +78,10 @@ class Server(object):
self.loop.close()
if self.socket:
Path(self.socket).unlink()
sock = Path(self.socket)
if sock.exists():
sock.unlink()
def restart(self):
@ -136,8 +139,16 @@ class Server(object):
self.scinfo = ParseSoundcloudInfo(value)
async def search(self, *args, **kwargs):
self.GetWebview().search.Search(*args, **kwargs)
async def search(self, *args):
searchbar = self.GetWebview().searchbar
if not args:
searchbar.Search('reset')
searchbar.hide()
return
searchbar.Search(text=' '.join(args))
searchbar.show()
async def new_tab(self, *args, **kwargs):
@ -219,9 +230,8 @@ class Server(object):
def trace_top(self, num=15, key_type='lineno'):
if not self.window.snapshot:
self.trace_snapshot()
snapshot = self.window.snapshot
snapshot = snapshot.filter_traces((
snapshot = self.window.snapshot.filter_traces((
tracemalloc.Filter(False, '<frozen importlib._bootstrap>'),
tracemalloc.Filter(False, '<unknown>'),
))

View file

@ -1,4 +1,4 @@
import threading, sys, os, time
import threading, sys, os
from datetime import datetime
from os.path import join
@ -10,12 +10,12 @@ from . import pyqtver, debstable, isWindows
from . import dialogs
from .config import var
from .database import db, get, put, delete
from .functions import useragent, GetSoftware, httpclient, Decode, psl, DateTime, MD5Check
from .functions import useragent, GetSoftware, httpclient, Decode, psl, MD5Check
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtCore import QUrl, QDateTime
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from PyQt5.QtNetwork import QNetworkCookie, QNetworkCookieJar
from PyQt5.QtNetwork import QNetworkCookie
AddHeader = lambda info, k, v: info.setHttpHeader(k.encode(), v.encode())
@ -44,9 +44,6 @@ blockedURL = {
'facebook.com': [
'/tr',
'/brandlift.php'
],
'twitch.tv': [
'/service-worker.js'
]
}
@ -55,7 +52,6 @@ class WebEngineProfile(QWebEngineProfile):
def __init__(self, parent):
super().__init__(var.profile)
parent.profile = self
self.interceptor = WebFilter(parent)
self.window = parent
self.notif = parent.notif
@ -161,15 +157,6 @@ class WebEngineProfile(QWebEngineProfile):
return True
def _new_cookie(self, cookie):
if not cookie.isSessionCookie():
put.cookie(cookie)
def _rem_cookie(self, cookie):
delete.cookie(cookie)
def LoadCookies(self):
for cookie in get.cookie():
self.cookieStore().setCookie(cookie)
@ -210,23 +197,22 @@ class WebFilter(QWebEngineUrlRequestInterceptor):
return
## Check if domain is a mastodon instance
if not pagerow.domain and not pagerow.mastodon and not any(map(requrl.toString().startswith, ['about:', 'chrome://'])):
if not pagedomain:
return
if get.config('mastocheck', False):
if not pagerow.domain and not pagerow.mastodon and not any(map(requrl.toString().startswith, ['about:', 'chrome://'])):
if not pagedomain:
return
if self.threads.get(pagedomain):
return
if self.threads.get(pagedomain):
return
self.threads[pagedomain] = threading.Thread(target=GetSoftware, args=[pagedomain])
self.threads[pagedomain].start()
logging.verbose('Checking for presence of nodeinfo')
self.threads[pagedomain] = threading.Thread(target=GetSoftware, args=[pagedomain])
self.threads[pagedomain].start()
## Delete the thread object once it's finished
elif self.threads.get(pagedomain):
del self.threads[pagedomain]
## Delete the thread object once it's finished
elif self.threads.get(pagedomain):
del self.threads[pagedomain]
## Set custom headers
AddHeader(info, 'X-Trans-Rights', 'Are Human Rights')
## Google is a fuck and is very picky about what browsers are supported
#if 'google.com' in [pagetopdomain, reqtopdomain]:
#info.setHttpHeader('user-agent'.encode(), 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'.encode())
# This breaks gmx.com
#AddHeader(info, 'X-Trans-Rights', 'Are Human Rights')

View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>479</width>
<height>292</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="username"/>
</item>
<item row="2" column="1" colspan="3">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="savepass">
<property name="statusTip">
<string>Does nothing rn</string>
</property>
<property name="text">
<string>Save Password</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="login">
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>169</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="realm">
<property name="text">
<string>Authentication requested from {host}. Server message: {msg}</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>username</tabstop>
<tabstop>password</tabstop>
<tabstop>savepass</tabstop>
<tabstop>cancel</tabstop>
<tabstop>login</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>568</width>
<height>447</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="titlelabel">
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="urllabel">
<property name="text">
<string>Url</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="categorylabel">
<property name="text">
<string>Category</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="4">
<widget class="QLineEdit" name="category"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="descriptionlabel">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="3" column="2" colspan="4">
<widget class="QPlainTextEdit" name="description">
<property name="minimumSize">
<size>
<width>230</width>
<height>170</height>
</size>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="discard">
<property name="text">
<string>Cancel</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QPushButton" name="remove">
<property name="text">
<string>Remove</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="5">
<widget class="QPushButton" name="save">
<property name="text">
<string>Save</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2" colspan="4">
<widget class="QLineEdit" name="url">
<property name="maxLength">
<number>2048</number>
</property>
</widget>
</item>
<item row="0" column="2" colspan="4">
<widget class="QLineEdit" name="title"/>
</item>
<item row="4" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>228</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -1,21 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>324</width>
<height>345</height>
<height>434</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>324</width>
@ -29,15 +23,32 @@
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<property name="modal">
<bool>true</bool>
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>55</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="discard">
<property name="text">
<string>Close</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
@ -52,7 +63,7 @@
<x>0</x>
<y>0</y>
<width>316</width>
<height>304</height>
<height>393</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -179,6 +190,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mastodon">
<property name="toolTip">
<string>Is this domain a mastodon instance? Hint: Enable 'Auto-detect Mastodon instances' under advanced settings to enable auto-detection</string>
</property>
<property name="text">
<string>Mastodon Instance</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@ -196,39 +217,6 @@
</widget>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>55</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="discard">
<property name="text">
<string>Close</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="reset">
<property name="text">
<string>Reset</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="save">
<property name="text">
@ -242,23 +230,18 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="reset">
<property name="text">
<string>Reset</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>scrollArea</tabstop>
<tabstop>adblock</tabstop>
<tabstop>allowhttp</tabstop>
<tabstop>fullscreen</tabstop>
<tabstop>cookies</tabstop>
<tabstop>notifications</tabstop>
<tabstop>capture</tabstop>
<tabstop>microphone</tabstop>
<tabstop>location</tabstop>
<tabstop>camera</tabstop>
<tabstop>discard</tabstop>
<tabstop>reset</tabstop>
<tabstop>save</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -1,44 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>486</width>
<height>306</height>
<width>516</width>
<height>337</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<property name="modal">
<bool>true</bool>
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="6">
<widget class="QLabel" name="replytext">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="6">
<widget class="QLineEdit" name="warning">
<property name="placeholderText">
@ -46,57 +21,6 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="6">
<widget class="QPlainTextEdit" name="content">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustIgnored</enum>
</property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="backgroundVisible">
<bool>true</bool>
</property>
<property name="centerOnScroll">
<bool>false</bool>
</property>
<property name="placeholderText">
<string>Message body</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="count">
<property name="text">
<string>500</string>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="2">
<widget class="QComboBox" name="account"/>
</item>
<item row="3" column="3">
<widget class="QComboBox" name="privacy">
<item>
@ -121,16 +45,6 @@
</item>
</widget>
</item>
<item row="3" column="4">
<widget class="QPushButton" name="cancel">
<property name="text">
<string>Cancel</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QPushButton" name="post">
<property name="text">
@ -141,16 +55,85 @@
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>125</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="2">
<widget class="QComboBox" name="account"/>
</item>
<item row="3" column="4">
<widget class="QPushButton" name="cancel">
<property name="text">
<string>Cancel</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="6">
<widget class="QLabel" name="replytext">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="count">
<property name="text">
<string>500</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="6">
<widget class="QPlainTextEdit" name="content">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustIgnored</enum>
</property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="backgroundVisible">
<bool>false</bool>
</property>
<property name="centerOnScroll">
<bool>false</bool>
</property>
<property name="placeholderText">
<string>Message body</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>warning</tabstop>
<tabstop>content</tabstop>
<tabstop>account</tabstop>
<tabstop>privacy</tabstop>
<tabstop>cancel</tabstop>
<tabstop>post</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

Binary file not shown.

View file

@ -1,149 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>467</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="4">
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="urllabel">
<property name="text">
<string>Url</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="url">
<property name="maxLength">
<number>2048</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="categorylabel">
<property name="text">
<string>Category</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="category"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="descriptionlabel">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPlainTextEdit" name="description">
<property name="minimumSize">
<size>
<width>230</width>
<height>170</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="title"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="titlelabel">
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>198</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="discard">
<property name="text">
<string>Cancel</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="remove">
<property name="text">
<string>Remove</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="save">
<property name="text">
<string>Save</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>title</tabstop>
<tabstop>url</tabstop>
<tabstop>category</tabstop>
<tabstop>description</tabstop>
<tabstop>discard</tabstop>
<tabstop>remove</tabstop>
<tabstop>save</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>481</width>
<height>418</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QPushButton" name="resume">
<property name="text">
<string>Resume All</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="clear">
<property name="text">
<string>Clear Finished</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="pause">
<property name="text">
<string>Pause All</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="5">
<widget class="QListView" name="downloads">
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>677</width>
<height>420</height>
<width>696</width>
<height>489</height>
</rect>
</property>
<property name="windowTitle">
@ -30,7 +30,7 @@
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<widget class="QTabWidget" name="tabs">
<property name="tabPosition">
<enum>QTabWidget::West</enum>
</property>
@ -42,52 +42,6 @@
<string>General</string>
</attribute>
<layout class="QGridLayout" name="heckinfuck">
<item row="6" column="0">
<widget class="QCheckBox" name="hidetabs">
<property name="toolTip">
<string>Hide the tab bar when only one tab is open</string>
</property>
<property name="text">
<string>Auto-hide tabs</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QSpinBox" name="cachesize">
<property name="toolTip">
<string>Size in mebibytes of the web cache</string>
</property>
<property name="suffix">
<string> MB</string>
</property>
<property name="maximum">
<number>8096</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Homepage</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="homepage">
<property name="toolTip">
<string>The default web page to load in a new tab</string>
</property>
<property name="maxLength">
<number>2048</number>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
@ -95,61 +49,7 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="adblock">
<property name="toolTip">
<string>Block ads on web pages. Can be turned off for individual sites</string>
</property>
<property name="text">
<string>Enable ad blocker</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="darktheme">
<property name="toolTip">
<string>Set the browser to a dark theme. Does not affect web pages</string>
</property>
<property name="text">
<string>Use dark theme</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="selectdownloaddir">
<property name="text">
<string>Pick Directory</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cache Size</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="downloaddir">
<property name="toolTip">
<string>Default location for downloads</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="createdownloaddir">
<property name="text">
<string>Create Dir</string>
</property>
</widget>
</item>
<item row="11" column="0" colspan="4">
<item row="10" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
@ -183,19 +83,232 @@
</item>
</layout>
</item>
<item row="10" column="1" colspan="3">
<spacer name="verticalSpacer">
<item row="2" column="1">
<widget class="QLineEdit" name="downloaddir">
<property name="toolTip">
<string>Default location for downloads</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Homepage</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cache Size</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QSpinBox" name="cachesize">
<property name="toolTip">
<string>Size in mebibytes of the web cache</string>
</property>
<property name="suffix">
<string> MB</string>
</property>
<property name="maximum">
<number>8096</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="createdownloaddir">
<property name="text">
<string>Create Dir</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="homepage">
<property name="toolTip">
<string>The default web page to load in a new tab</string>
</property>
<property name="maxLength">
<number>2048</number>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="selectdownloaddir">
<property name="text">
<string>Pick Directory</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<layout class="QGridLayout" name="gridLayout_5">
<item row="3" column="0">
<widget class="QCheckBox" name="nitter">
<property name="toolTip">
<string>Redirect Twitter links to a Nitter instance</string>
</property>
<property name="text">
<string>Enable Nitter redirect</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="nitterurl">
<property name="placeholderText">
<string>ex. https://nitter.net</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="nitterreset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="hidetabs">
<property name="toolTip">
<string>Hide the tab bar when only one tab is open</string>
</property>
<property name="text">
<string>Auto-hide tabs</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="adblock">
<property name="toolTip">
<string>Block ads on web pages by denying hosts. Can be turned off for individual sites</string>
</property>
<property name="text">
<string>Enable ad blocker</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="reusetab">
<property name="toolTip">
<string>If a web page tries to create a new tab, use the current tab instead</string>
</property>
<property name="text">
<string>Reuse tabs</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="0" colspan="4">
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>638</width>
<height>219</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Soundcloud</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="screpost">
<property name="toolTip">
<string>Remove all reposts from the Stream page
Note: Reposts will still be added to the playlist, but won't be displayed</string>
</property>
<property name="text">
<string>Remove Reposts</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="appearance">
<attribute name="title">
<string>Appearance</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="themedelete">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="themenew">
<property name="text">
<string>New</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="themeedit">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="5">
<widget class="QListWidget" name="theme">
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="search">
@ -325,48 +438,13 @@
<string>Mastodon</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Domain</string>
</property>
</widget>
</item>
<item row="3" column="7">
<widget class="QPushButton" name="login">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="3" column="6">
<widget class="QPushButton" name="reset">
<property name="toolTip">
<string>Attempt to login to the account</string>
<string>Empty the form fields</string>
</property>
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
<item row="5" column="7">
<widget class="QPushButton" name="logout">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Logout of the selected account</string>
</property>
<property name="text">
<string>Logout</string>
<string>Reset</string>
</property>
</widget>
</item>
@ -380,27 +458,22 @@
</property>
</widget>
</item>
<item row="2" column="1" colspan="7">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
<item row="5" column="7">
<widget class="QPushButton" name="logout">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>E-Mail</string>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="6">
<widget class="QPushButton" name="reset">
<property name="toolTip">
<string>Empty the form fields</string>
<string>Logout of the selected account</string>
</property>
<property name="text">
<string>Reset</string>
<string>Logout</string>
</property>
</widget>
</item>
@ -472,6 +545,9 @@
</item>
<item row="5" column="6">
<widget class="QPushButton" name="mdefault">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -486,10 +562,32 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<item row="3" column="7">
<widget class="QPushButton" name="login">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Attempt to login to the account</string>
</property>
<property name="text">
<string>Password</string>
<string>Login</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Domain</string>
</property>
</widget>
</item>
@ -509,8 +607,25 @@
</property>
</widget>
</item>
<item row="2" column="1" colspan="7">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="5" column="5">
<widget class="QPushButton" name="refresh">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -525,6 +640,13 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>E-Mail</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="advanced">
@ -532,95 +654,10 @@
<string>Advanced</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QComboBox" name="mbushost">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The host MBus listens on</string>
</property>
<item>
<property name="text">
<string>127.0.0.1</string>
</property>
</item>
<item>
<property name="text">
<string>0.0.0.0</string>
</property>
</item>
<item>
<property name="text">
<string>False</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<item row="2" column="3">
<widget class="QPushButton" name="logreset">
<property name="text">
<string>MBus Host</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSpinBox" name="mbusport">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>28</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The port MBus listens on</string>
</property>
<property name="accelerated">
<bool>true</bool>
</property>
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>25565</number>
</property>
<property name="value">
<number>8008</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Stdout Log Level</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="jsdebug">
<property name="text">
<string>Send JS output to stdout</string>
<string>Reset</string>
</property>
</widget>
</item>
@ -664,26 +701,6 @@
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>MBus Port</string>
</property>
</widget>
</item>
<item row="6" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="portreset">
<property name="sizePolicy">
@ -697,6 +714,77 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSpinBox" name="mbusport">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>28</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The port MBus listens on</string>
</property>
<property name="accelerated">
<bool>true</bool>
</property>
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>25565</number>
</property>
<property name="value">
<number>8008</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="mbushost">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The host MBus listens on</string>
</property>
<item>
<property name="text">
<string>127.0.0.1</string>
</property>
</item>
<item>
<property name="text">
<string>0.0.0.0</string>
</property>
</item>
<item>
<property name="text">
<string>False</string>
</property>
</item>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="hostreset">
<property name="sizePolicy">
@ -717,12 +805,18 @@
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="logreset">
<property name="text">
<string>Reset</string>
<item row="7" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="automd5">
@ -734,6 +828,44 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>MBus Host</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Stdout Log Level</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="jsdebug">
<property name="text">
<string>Send JS output to stdout</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>MBus Port</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="mastocheck">
<property name="toolTip">
<string>Query nodeinfo on previously unvisited sites to check if the domain is a mastodon instance</string>
</property>
<property name="text">
<string>Auto-detect Mastodon instances</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
@ -741,7 +873,7 @@
</layout>
</widget>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>tabs</tabstop>
<tabstop>homepage</tabstop>
<tabstop>cachesize</tabstop>
<tabstop>downloaddir</tabstop>
@ -749,9 +881,18 @@
<tabstop>selectdownloaddir</tabstop>
<tabstop>hidetabs</tabstop>
<tabstop>adblock</tabstop>
<tabstop>darktheme</tabstop>
<tabstop>sdefault</tabstop>
<tabstop>reusetab</tabstop>
<tabstop>nitter</tabstop>
<tabstop>nitterurl</tabstop>
<tabstop>nitterreset</tabstop>
<tabstop>scrollArea</tabstop>
<tabstop>screpost</tabstop>
<tabstop>setdefault</tabstop>
<tabstop>themenew</tabstop>
<tabstop>themeedit</tabstop>
<tabstop>themedelete</tabstop>
<tabstop>theme</tabstop>
<tabstop>sdefault</tabstop>
<tabstop>snew</tabstop>
<tabstop>sdelete</tabstop>
<tabstop>sengines</tabstop>
@ -772,6 +913,8 @@
<tabstop>logreset</tabstop>
<tabstop>jsdebug</tabstop>
<tabstop>tracemalloc</tabstop>
<tabstop>automd5</tabstop>
<tabstop>mastocheck</tabstop>
</tabstops>
<resources/>
<connections/>

View file

@ -8,3 +8,23 @@ function QtWebGetInfo() {
return artist + ':::' + artistLink + ':::' + title + ':::' + titleLink + ':::' + progress + ':::' + length
}
// https://greasyfork.org/en/scripts/27012-soundcloud-hide-reposts
// ==UserScript==
// @name SoundCloud hide reposts
// @namespace http://abs.ezw.me
// @version 1.2
// @author ABS
// @description Only see new songs in your SoundCloud stream
// @match *://soundcloud.com/stream
// ==/UserScript==
function norepost(){
for (let repost of document.getElementsByClassName("soundList__item")) {
if (repost.getElementsByClassName("sc-ministats-reposts").length > 0)
repost.remove();
console.log(repost);
}
}

View file

@ -1,16 +1,15 @@
import re, json
from pathlib import Path, PurePosixPath, PureWindowsPath
from pathlib import Path
from os.path import join, isfile, abspath
from urllib.parse import urlparse
from configparser import ConfigParser
from . import isLinux, isWindows
from .config import var
from . import isWindows
from .config import var, default
from .database import db, put, get, delete
from .functions import Icon, NewDesktopEntry
from .dialogs import EditBookmark
from .themes import ChangeTheme
from .Lib.IzzyLib import logging
from .Lib.IzzyLib.misc import boolean
@ -23,7 +22,7 @@ from PyQt5 import uic
class Bookmarks(object):
def __init__(self, window):
self.ui = uic.loadUi(join(var.resources, 'bookmarktab.ui'))
self.ui = uic.loadUi(join(var.resources, 'tab-bookmarks.ui'))
self.title = 'Bookmarks'
self.url = self.title
self.window = window
@ -34,7 +33,7 @@ class Bookmarks(object):
self.ui.refresh.clicked.connect(lambda *args: self._row_action('refresh'))
self.ui.table.cellDoubleClicked.connect(lambda *args: self._row_action('edit'))
for size in [(0, 150), (1, 350), (2, 80)]:
for size in [(0, 350), (1, 200), (2, 80)]:
self.ui.table.setColumnWidth(*size)
self._populate_table()
@ -106,18 +105,24 @@ class History(object):
class Preferences(object):
def __init__(self, window):
self.ui = uic.loadUi(join(var.resources, 'preferences.ui'))
self.ui = uic.loadUi(join(var.resources, 'tab-preferences.ui'))
self.title = 'Preferences'
self.window = window
dldir = get.config('dlpath', var.downloadpath)
nitter_state = get.config('nitter', 0)
## General tab
self.ui.homepage.setText(get.config('homepage', 'http://ddg.gg'))
self.ui.downloaddir.setText(dldir)
self.ui.adblock.setChecked(int(get.config('adblock', 1)))
self.ui.hidetabs.setChecked(int(get.config('hidetabs', 1)))
self.ui.reusetab.setChecked(int(get.config('reusetab', False)))
self.ui.cachesize.setValue(int(get.config('cache', 100)))
self.ui.darktheme.setChecked(int(get.config('darktheme', 0)))
self.ui.nitter.setChecked(int(nitter_state))
self.ui.nitterurl.setText(get.config('nitterurl', 'https://nitter.net'))
self._general_set_nitterurl_state(nitter_state)
self.ui.screpost.setChecked(int(get.config('scnoreposts', False)))
if isWindows:
self.ui.setdefault.setDisabled(True)
@ -142,10 +147,7 @@ class Preferences(object):
self.ui.jsdebug.setChecked(get.config('jsdebug', False))
self.ui.tracemalloc.setChecked(get.config('tracestart', False))
self.ui.automd5.setChecked(get.config('automd5', False))
# this breaks for some reason, so keeping it disabled until I figure out what's going on
self.ui.hostreset.setToolTip('Button broken. Just set the host to "127.0.0.1"')
self.ui.hostreset.setDisabled(True)
self.ui.mastocheck.setChecked(get.config('mastocheck', False))
## General tab signals
self.ui.setdefault.clicked.connect(self._general_set_default)
@ -157,7 +159,14 @@ class Preferences(object):
self.ui.cachesize.editingFinished.connect(self._general_update_cachesize)
self.ui.hidetabs.stateChanged.connect(lambda state: self._general_handle_checkbox(state, 'hidetabs'))
self.ui.adblock.stateChanged.connect(lambda state: self._general_handle_checkbox(state, 'adblock'))
self.ui.darktheme.stateChanged.connect(lambda state: self._general_handle_checkbox(state, 'darktheme'))
self.ui.nitter.stateChanged.connect(lambda state: self._general_handle_checkbox(state, 'nitter'))
self.ui.reusetab.stateChanged.connect(lambda state: self._general_handle_checkbox(state, 'reusetab'))
self.ui.nitterurl.editingFinished.connect(self._general_update_nitter_url)
self.ui.nitterreset.clicked.connect(self._general_reset_nitter_url)
self.ui.screpost.stateChanged.connect(lambda state: self._general_handle_checkbox(state, 'scnoreposts'))
## Appearance tab signals
#self.ui.theme.itemClicked.connect(self._appearance_theme_change)
## Search engine tab signals
self.ui.snew.clicked.connect(lambda *args: self._search_button_handler('new'))
@ -175,21 +184,28 @@ class Preferences(object):
self.ui.domain.returnPressed.connect(lambda *args: self.ui.username.setFocus())
self.ui.username.returnPressed.connect(lambda *args: self.ui.password.setFocus())
self.ui.password.returnPressed.connect(self.mastodon_login)
self.ui.accounts.itemSelectionChanged.connect(self.mastodon_row_selected)
## Advanced tab signals
self.ui.mbushost.currentIndexChanged.connect(self._adv_mbushost_changed)
self.ui.mbusport.editingFinished.connect(self._adv_mbusport_changed)
self.ui.loglevel.currentIndexChanged.connect(lambda index: self._adv_changed('loglevel', index))
self.ui.hostreset.clicked.connect(lambda *args: self._adv_reset('mhost'))
self.ui.portreset.clicked.connect(lambda *args: self._adv_reset('mport'))
self.ui.hostreset.clicked.connect(lambda *args: self._adv_mbushost_changed(int(default.mhost)))
self.ui.portreset.clicked.connect(lambda *args: self._adv_mbusport_changed(default.mport))
self.ui.logreset.clicked.connect(lambda *args: self._adv_reset('loglevel'))
self.ui.jsdebug.stateChanged.connect(lambda state: self._adv_handle_checkbox('jsdebug', state))
self.ui.tracemalloc.stateChanged.connect(lambda state: self._adv_handle_checkbox('tracestart', state))
self.ui.automd5.stateChanged.connect(lambda state: self._adv_handle_checkbox('automd5', state))
self.ui.mastocheck.stateChanged.connect(lambda state: self._adv_handle_checkbox('mastocheck', state))
## Populate tables
self.mastodon_populate_table()
self.populate_search_table()
#self._appearance_populate_view()
# Remove the appearance tab until I can get it fixed
self.ui.tabs.widget(1).deleteLater()
self.ui.tabs.removeTab(1)
### General Tab ###
@ -293,10 +309,6 @@ class Preferences(object):
self.window.profile.setHttpCacheMaximumSize(1024 * 1024 * value)
def _general_homepage_reset(self, *args):
self.ui.homepage.setText(self.window.homepage)
def _general_update_homepage(self, *args):
newurl = self.ui.homepage.text()
@ -314,21 +326,81 @@ class Preferences(object):
if name == 'hidetabs':
self.window.tabbar.setAutoHide(state.real)
if name == 'adblock':
self.window.adblock = value
if name == 'darktheme':
if name == 'nitter':
value = boolean(value)
self._general_set_nitterurl_state(value)
if value:
ChangeTheme('dark')
else:
ChangeTheme()
def _general_set_nitterurl_state(self, value=None):
if not value:
value = get.config('nitter', False)
self.ui.nitterurl.setEnabled(value)
self.ui.nitterreset.setEnabled(value)
def _general_update_nitter_url(self):
url = self.ui.nitterurl.text()
if not any(map(url.startswith, ['http://', 'https://'])):
url = 'https://' + url
self.ui.nitterurl.setText(url)
if not put.config('nitterurl', url):
logging.error('Failed to update "nitterurl" config')
def _general_reset_nitter_url(self):
if not put.config('nitterurl', default.nitter):
logging.error('Failed to update "nitterurl" config')
return
self.ui.nitterurl.setText(default.nitter)
### Appearance Tab ###
def _appearance_populate_view(self):
self.ui.theme.clear()
size = QSize()
size.setHeight(24)
default = QListWidgetItem('Default')
default.setSizeHint(size)
if get.config('theme', 'Default') == 'Default':
default.setIcon(Icon('okay', 16))
self.ui.theme.addItem(default)
for theme in get.theme():
item = QListWidgetItem(theme.name)
item.setForeground(theme.palette.text())
item.setBackground(theme.palette.base())
item.setSizeHint(size)
if get.config('theme') == theme.name:
item.setIcon(Icon('okay', 16))
self.ui.theme.addItem(item)
def _appearance_theme_change(self, item):
listwidget = self.ui.theme
theme = item.text()
if put.config('theme', theme):
items = [listwidget.item(i) for i in range(listwidget.count())]
for item in items:
if item.text() != theme:
item.setIcon(QIcon())
else:
item.setIcon(Icon('okay', 16))
self.window.themes.ChangeTheme(theme)
### Search Tab ###
# todo: prefix functions with 'search'
def _search_button_handler(self, button):
table = self.ui.sengines
tablerow = table.currentRow()
@ -368,7 +440,6 @@ class Preferences(object):
return
table = self.ui.sengines
cell = table.item(x, y)
cells = [0, table.item(x, 1), table.item(x, 2), table.item(x, 3)]
data = {
@ -429,6 +500,19 @@ class Preferences(object):
logging.error('Invalid message type:', msgtype)
def mastodon_row_selected(self):
row = self.mastodon_selected_row_data()
logging.debug(row)
if not row:
self.ui.mdefault.setEnabled(False)
self.ui.logout.setEnabled(False)
else:
self.ui.mdefault.setEnabled(row['default'])
self.ui.logout.setEnabled(True)
def mastodon_selected_row_data(self):
table = self.ui.accounts
tablerow = table.currentRow() if table.currentRow() >= 0 else None
@ -436,9 +520,10 @@ class Preferences(object):
if tablerow == None:
return
cells = [0, table.item(tablerow, 1), table.item(tablerow, 2), table.item(tablerow, 3)]
cells = [table.item(tablerow, 0), table.item(tablerow, 1), table.item(tablerow, 2), table.item(tablerow, 3)]
data = {
'default': cells[0].icon().isNull(),
'displayname': cells[1].text() if cells[1] else None,
'username': cells[2].text() if cells[2] else None,
'domain': cells[3].text() if cells[3] else None
@ -450,9 +535,13 @@ class Preferences(object):
def mastodon_populate_table(self):
self.ui.accounts.setRowCount(0)
accounts = get.mastoaccount()
for widget in [self.ui.mdefault, self.ui.logout]:
widget.setEnabled(False)
#for row in db.get('mastodon', single=False):
for row in get.mastoaccount():
for row in accounts:
self.mastodon_insert_row(row)
@ -534,7 +623,7 @@ class Preferences(object):
## Advanced tab
def _adv_mbushost_changed(self, index):
new = self.ui.mbushost.itemText(index)
old = get.config('mhost')
old = get.config('mhost', default.mhost)
if old != new:
put.config('mhost', new)
@ -546,8 +635,10 @@ class Preferences(object):
self.window.server.restart()
def _adv_mbusport_changed(self):
value = self.ui.mbusport.value()
def _adv_mbusport_changed(self, value=None):
if not value:
value = self.ui.mbusport.value()
put.config('mport', value)
self.window.server.restart()
@ -574,15 +665,6 @@ class Preferences(object):
db.remove('config', row.id)
if field == 'mhost':
self.ui.mbushost.setCurrentIndex(0)
if field == 'mport':
self.ui.mbusport.setValue(8008)
if field in ['mhost', 'mport']:
self.window.server.restart()
if field == 'loglevel':
self.mbushost.setCurrentIndex(3)
logging.setConfig({'level': 'INFO'})

View file

@ -1,111 +1,74 @@
from .Lib.IzzyLib import logging
from . import isWindows
from .database import get, put
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QStyleFactory
color_themes = {}
default_style = None
current_theme = None
app = None
class ColorTheme(object):
def __init__(self, window):
self.app = window.app
self.window = window
self.default_style = self.app.style().objectName().lower()
self.default_palette = self.app.style().standardPalette()
self.appcolor = self.app.palette()
self.colors = [
'Window', 'WindowText', 'Base', 'AlternateBase', 'ToolTipBase', 'ToolTipText',
'Button', 'ButtonText', 'Text', 'Highlight', 'HighlightedText', 'Link',
'Light', 'Midlight', 'Dark', 'Mid', 'Shadow', 'LinkVisited', 'BrightText'
]
self.colors.sort()
logging.verbose('Default style:', self.default_style)
logging.verbose('Available styles:', ', '.join(QStyleFactory.keys()))
dark = {
'AlternateBase': QColor(53, 53, 53),
'Base': QColor(25, 25, 25),
'Button': QColor(53, 53, 53),
'ButtonText': Qt.white,
'BrightText': Qt.red,
'Highlight': QColor(42, 130, 218),
'HighlightedText': Qt.black,
'Link': QColor(42, 130, 218),
'Text': Qt.white,
'ToolTipBase': Qt.black,
'ToolTipText': Qt.white,
'Window': QColor(53, 53, 53),
'WindowText': Qt.white,
}
def ChangeTheme(self, theme=None):
theme = 'Default' if not theme else theme
if theme == 'Default':
palette = self.default_palette
def AddTheme(name, colors):
name = name.title()
theme = QPalette()
for colname, color in colors.items():
theme.setColor(getattr(QPalette, colname), QColor(color) if type(color) == str else color)
color_themes[name] = theme
def RemoveTheme(name):
name = name.title()
if not color_themes.get(name):
logging.error('Not a valid theme:', name)
return
del color_themes[name]
def GetTheme(name=None):
if not name:
return color_themes
name = name.title()
theme = color_themes.get(name)
if not theme:
logging.error('Not a valid theme:', name)
return
return theme
def ChangeTheme(theme=None):
global current_theme
theme = 'Default' if not theme else theme.title()
newtheme = color_themes.get(theme)
if not newtheme:
logging.error('Not a valid theme:', theme)
return
if isWindows:
if theme != 'Default':
app.setStyle('fusion')
else:
app.setStyle(default_style)
row = get.theme(theme)
logging.verbose('Changing color theme to', theme)
if not row:
logging.error('Not a valid theme:', theme)
return
current_theme = theme
app.setPalette(newtheme)
palette = row.palette
logging.verbose('Changing color theme to', theme)
put.config('theme', theme)
self.app.setPalette(palette)
if isWindows:
if theme != 'Default':
self.app.setStyle('fusion')
else:
self.app.setStyle(self.default_style)
return
def SetupThemes(appclass):
global default_style
global app
def ColorToPalette(self, data):
palette = self.app.style().standardPalette()
app = appclass
default_style = app.style().objectName().lower()
appcolor = app.palette()
for name, color in data.items():
if name in self.colors and color != None:
palette.setColor(getattr(QPalette, name), QColor(color))
colors = {}
defcolors = [
'Window', 'WindowText', 'Base', 'AlternateBase', 'ToolTipBase', 'ToolTipText', 'Button', 'ButtonText',
'BrightText', 'Light', 'Midlight', 'Dark', 'Mid', 'Shadow', 'Highlight', 'HighlightedText', 'Link', 'LinkVisited'
]
return palette
for color in defcolors:
lowercolor = color[0].lower() + color[1:]
colors[color] = getattr(appcolor, lowercolor)().color()
AddTheme('Default', colors)
AddTheme('dark', dark)
def PaletteToColor(self, palette):
data = {}
logging.verbose('Default style:', default_style)
logging.verbose('Available styles:', ', '.join(QStyleFactory.keys()))
logging.verbose('Available themes:', ', '.join(color_themes.keys()))
for color in self.colors:
data[color] = getattr(palette, color[0].lower() + ''.join(color[1:]))().color().name()
return data

View file

@ -1,49 +1,71 @@
import sys
from . import debstable
from .config import var
from .database import db, put
from .Lib.IzzyLib import logging
from PyQt5.QtCore import *
from PyQt5.QtWebEngineCore import *
def RegisterScheme(name):
scheme = QWebEngineUrlScheme(name.encode())
scheme.setFlags(QWebEngineUrlScheme.SecureScheme)
QWebEngineUrlScheme.registerScheme(scheme)
scheme = QWebEngineUrlScheme(name.encode())
scheme.setFlags(
#QWebEngineUrlScheme.LocalScheme |
QWebEngineUrlScheme.LocalAccessAllowed |
QWebEngineUrlScheme.ViewSourceAllowed
)
QWebEngineUrlScheme.registerScheme(scheme)
class LocalHandler(QWebEngineUrlSchemeHandler):
def __init__(self, webview, window):
super().__init__(window)
self.webview = webview
def __init__(self, parent):
self.window = parent
def requestStarted(self, job):
url = job.requestUrl()
host = url.host()
if not debstable:
scheme = QWebEngineUrlScheme.schemeByName(var.local.encode())
if scheme.name() == b'':
logging.critical(f'Failed to register scheme: {var.local}. Exiting...')
parent.close()
super().__init__(parent.profile)
parent.profile.installUrlSchemeHandler(var.local.encode(), self)
def _check_initiator(self, request):
initiator = request.initiator()
requrl = request.requestUrl()
if requrl.scheme() != var.local:
logging.warning(f'Blocking malicious request from {initiator.toDisplayString()} to {request_url.toDisplayString()}')
request.fail(QWebEngineUrlRequestJob.RequestDenied)
return False
return True
def requestStarted(self, request):
url = request.requestUrl()
path = url.path()
job.fail(QWebEngineUrlRequestJob.UrlNotFound)
if not self._check_initiator(request):
return
if url.host() == 'home':
mimetype = 'text/html'
data = b'<html><head><title>MERP!</title><body>UvU</body></html>'
blockedDomain = [
'doubleclick.net',
'rubiconproject.com',
'outbrain.com',
'googletagservices.com',
'amazon-adsystem.com',
'optimizely.com',
'adswizz.com'
]
else:
mimetype = 'text/html'
data = '<html><head><title>QtWeb</title><body>heck</body></html>'
blockedURL = {
'youtube.com': [
'/api/stats/ads',
'/pagead/adview',
'/pagead/lvz'
'/get_midroll/info'
],
'soundcloud.com': [
'/audio-ads'
],
'facebook.com': [
'/tr']
}
buf = QBuffer(parent=self)
buf.open(QIODevice.WriteOnly)
buf.write(data)
buf.seek(0)
buf.close()
request.reply(mimetype.encode('ascii'), buf)

View file

@ -5,12 +5,13 @@ import validators
from .Lib.IzzyLib import logging
from .Lib.IzzyLib.misc import boolean
from .Lib.IzzyLib.cache import LRUCache
from . import debstable, dialogs
from .config import var
from . import debstable, dialogs, debstable
from .config import var, default
from .database import db, get, put
from .mastodon import Mastodon
from .functions import Menu, Icon
from .functions import Menu, Icon, httpclient
from .webviewjs import JsFuncs
from PyQt5.QtCore import *
@ -40,6 +41,7 @@ class WebEngineView(QWebEngineView):
super().__init__()
self.window = window
self.jscache = LRUCache(64)
self.webview = True
self.tabs = window.ui.tabs
self.webpage = WebEnginePage(self.window.profile, self)
@ -54,15 +56,17 @@ class WebEngineView(QWebEngineView):
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._web_context_menu)
#self.loadStarted.connect(lambda *args: setattr(self, 'loading', True))
self.loadProgress.connect(self._progress_loading)
#self.loadStarted.connect(self._started_loading)
self.loadFinished.connect(self._finished_loading)
self.webpage.titleChanged.connect(self._set_tab_text)
self.webpage.iconChanged.connect(self._set_tab_icon)
self.webpage.urlChanged.connect(self._set_tab_url)
self.webpage.linkHovered.connect(self._link_hover)
self.webpage.fullScreenRequested.connect(lambda *args: self._permission_request('fullscreen', *args))
#self.webpage.fullScreenRequested.connect(lambda *args: self._permission_request('fullscreen', *args))
self.webpage.featurePermissionRequested.connect(self._feature_request)
self.webpage.authenticationRequired.connect(self._handle_auth)
self.webaction_state = {
'previous': False,
@ -79,15 +83,68 @@ class WebEngineView(QWebEngineView):
self.JsAction = JsFuncs(self)
if url != 'newtab':
self.LoadUrl(url if url else get.config('homepage', 'https://ddg.gg'))
self.LoadUrl(url if url else get.config('homepage', default.homepage))
def _progress_loading(self, progress):
current_index = self.window.ui.tabs.currentIndex()
tab_index = self.window.ui.tabs.indexOf(self)
loading = progress != 100
percent = progress / 100
self.webaction_state['stop'] = loading
self.webaction_state['reload'] = not loading
if current_index == tab_index:
#if loading:
#self.window.ui.url.setStyleSheet('background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 #1bb77b, stop: ' + str(percent) + ' #1bb77b, stop: ' + str(percent+ 0.001) + ' rgba(0, 0, 0, 0), stop: 1 white)')
#else:
#self.window.ui.url.setStyleSheet(None)
self.window._update_webaction_buttons('stop', self.webaction_state['stop'])
self.window._update_webaction_buttons('reload', self.webaction_state['reload'])
# There's a lot of code duplication here. Gonna fix that later
def _finished_loading(self, arg):
jsfile = Path(var.scriptpath, 'web', 'js', self.url().host() + '.js')
host = self.url().host()
path = self.url().path()
jsfile = Path(var.scriptpath, 'scripts', self.url().host() + '.js')
userjsfile = Path(var.datapath, 'UserScripts', self.url().host() + '.js')
jscache = self.jscache.fetch(host)
userjscache = self.jscache.fetch(host + '.User')
if jsfile.exists():
with jsfile.open('r') as fd:
self.webpage.runJavaScript(fd.read())
if jsfile.exists() and jsfile.is_file():
if jscache:
logging.verbose(f'Loading JS from cache for {host}: {jsfile}')
data = jscache
else:
logging.verbose(f'Loading JS from file for {host}: {jsfile}')
with jsfile.open('r') as fd:
data = fd.read()
self.jscache.store(host, data)
self.webpage.runJavaScript(data)
if userjsfile.exists() and userjsfile.is_file():
if userjscache:
logging.verbose(f'Loading user JS from cache for {host:} {userjsfile}')
userdata = userjscache
logging.verbose(f'Loading user JS from file for {host}: {userjsfile}')
with userjsfile.open('r') as fd:
userdata = fd.read()
self.jscache.store(host + '.User', userdata)
self.webpage.runJavaScript(userdata)
if host == 'soundcloud.com' and path.startswith('/stream') and get.config('scnoreposts', False):
self.webpage.runJavaScript('window.addEventListener("DOMNodeInserted", norepost, false);')
if get.config('reusetab', False):
self.webpage.runJavaScript('for (let link of document.getElementsByTagName("a")) {if (link.target === "_blank") link.removeAttribute("target")}')
def _set_tab_text(self, title):
@ -121,6 +178,9 @@ class WebEngineView(QWebEngineView):
action = recv_action.text().lower()
state = recv_action.isEnabled()
if action in ['stop', 'reload']:
return
self.webaction_state[action] = state
if current_index == tab_index:
@ -168,11 +228,26 @@ class WebEngineView(QWebEngineView):
self.Fullscreen(request.toggleOn())
def _handle_auth(self, location, authenticator):
url = location.toString()
login = dialogs.HttpAuth(location, authenticator, self)
login.exec_()
if login.result:
logging.verbose('Authenticated page:', url)
else:
logging.verbose('Canceled authentication for page:', url)
self.stop()
if self.window.ui.tabs.currentWidget() == self:
self.window.ui.url.setText(url)
def _web_context_menu(self, point):
cursor = QCursor().pos() #point puts the menu in the wrong location for some reason
data = self.webpage.contextMenuData()
clipboard = self.window.clipboard
mimedata = clipboard.mimeData()
currenturl = self.url()
spliturl = currenturl.path().split('/')
selected = data.selectedText()
@ -188,17 +263,10 @@ class WebEngineView(QWebEngineView):
acctcount = db.count('mastodon') > 0
mediaurl = data.mediaUrl().toString()
menu = Menu('heck', self)
menu = Menu(self, 'heck')
addItem = lambda label, name, data=None: menu.AddAction(label, lambda : self._menu_actions(name, data))
addMastoItem = lambda label, name, data=None: menu.AddAction(label, lambda : self._mastodon_action(name, data))
if mediaurl != '':
addItem('Open Media in New Tab', 'newtab', mediaurl)
addItem('Open Media in New Tab and Switch', 'newtabswitch', mediaurl)
menu.AddSeparator()
addItem('Download Media', 'download', mediaurl)
addItem('Copy Media URL', 'copy', mediaurl)
## Not sure if I wanna enable this
#if acctcount and urlismasto and urlispost:
#addItem('Reply to Post', 'reply', currenturl.toString())
@ -213,17 +281,7 @@ class WebEngineView(QWebEngineView):
addItem('Open Link in New Tab', 'newtab', linkurl)
addItem('Open Link in New Tab and Switch', 'newtabswitch', linkurl)
menu.AddSeparator()
addItem('Download Link', 'download', linkurl)
menu.AddSeparator()
if acctcount and linkispost and linkismasto:
addMastoItem('Reply to Linked Post', 'reply', linkurl)
addMastoItem('Favorite Linked Post', 'favourite', linkurl)
addMastoItem('Boost Linked Post', 'reblog', linkurl)
addMastoItem('Bookmark Linked Post', 'bookmark', linkurl)
menu.AddSeparator()
addItem('Copy Link URL', 'copyurl')
addItem('Copy Link Location', 'copyurl')
addItem('Copy Link Text', 'copy', linktext)
if selected:
@ -244,9 +302,27 @@ class WebEngineView(QWebEngineView):
if data.CanRedo:
addItem('Redo', 'Redo')
if linkurl != '':
addItem('Download Link', 'download', linkurl)
if acctcount and linkispost and linkismasto:
menu.AddSeparator()
addMastoItem('Reply to Linked Post', 'reply', linkurl)
addMastoItem('Favorite Linked Post', 'favourite', linkurl)
addMastoItem('Boost Linked Post', 'reblog', linkurl)
addMastoItem('Bookmark Linked Post', 'bookmark', linkurl)
if menu.MoreThan(0):
menu.AddSeparator()
if mediaurl != '':
addItem('Open Media in New Tab', 'newtab', mediaurl)
addItem('Open Media in New Tab and Switch', 'newtabswitch', mediaurl)
menu.AddSeparator()
addItem('Download Media', 'download', mediaurl)
addItem('Copy Media Location', 'copy', mediaurl)
menu.AddSeparator()
addItem('Inspect Page', 'inspect_page')
addItem('Inspect Element', 'inspect')
menu.AddSeparator()
@ -390,10 +466,12 @@ class WebEngineView(QWebEngineView):
twitter_paths = any(map(parsed.path.startswith, ['/settings', '/home', '/login', '/logout', '/explore', '/sessions', '/account']))
twitter_files = any(map(parsed.path.endswith, ['.js', '.jpeg', '.jpg', '.png', '.css']))
if parsed.netloc == 'twitter.com' and get.config('twiredir', True) and parsed.path != '/' and not twitter_paths and not twitter_files:
newurl = f'https://nitter.net{parsed.path}'
if parsed.netloc == 'twitter.com' and get.config('nitter', False) and parsed.path != '/' and not twitter_paths and not twitter_files:
nitterurl = get.config('nitterurl', default.nitter)
newurl = nitterurl + parsed.path
self.webpage.setUrl(QUrl(newurl))
self.setFocus()
class SearchBar(QFrame):
@ -403,7 +481,7 @@ class SearchBar(QFrame):
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 2)
layout.setContentsMargins(0, 6, 0, 0)
layout.setSpacing(4)
#layout.setSizeConstraint(QLayout.SetFixedSize)
self.setLayout(layout)
@ -418,10 +496,6 @@ class SearchBar(QFrame):
close.setIcon(Icon('close'))
for widget in [previous, next, self.search, self.sensitive, reset, close]:
#if widget == self.search:
#widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
#else:
#widget.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
self.layout().addWidget(widget)
self.search.installEventFilter(FindEsc(self))
@ -434,17 +508,18 @@ class SearchBar(QFrame):
close.clicked.connect(lambda *args: self.Search('toggle'))
def Search(self, action=None):
def Search(self, action=None, text=None):
if text:
self.search.setText(text)
text = self.search.text()
webview = self.webview
selected = webview.selectedText()
selected = self.webview.selectedText()
sensitive = self.sensitive.isChecked()
if action == 'previous':
webview.Search(text, reverse=True, sensitive=sensitive)
self.webview.Search(text, reverse=True, sensitive=sensitive)
elif action == 'reset':
webview.Search()
self.webview.Search()
self.sensitive.setChecked(False)
self.search.setText('')
@ -461,12 +536,12 @@ class SearchBar(QFrame):
self.Search()
else:
## Reset the search and focus the webview
## Reset the search and focus the window
self.Search('reset')
webview.setFocus()
self.webview.setFocus()
else:
webview.Search(text, sensitive=sensitive)
self.webview.Search(text, sensitive=sensitive)
class FindEsc(QObject):
@ -512,12 +587,14 @@ class WebEnginePage(QWebEnginePage):
def createWindow(self, wintype):
options = {'nexttab': True}
#wintypes = [
#QWebEnginePage.WebBrowserTab,
#QWebEnginePage.WebBrowserWindow,
#QWebEnginePage.WebBrowserBackgroundTab,
#QWebEnginePage.WebDialog
#]
wintypes = {
QWebEnginePage.WebBrowserTab: 'new tab',
QWebEnginePage.WebBrowserWindow: 'new window',
QWebEnginePage.WebBrowserBackgroundTab: 'new background tab',
QWebEnginePage.WebDialog: 'new dialog'
}
typename = wintypes.get(wintype, f'Invalid window type: {type(wintype)}')
if wintype != QWebEnginePage.WebDialog:
options['switch'] = True
@ -525,9 +602,30 @@ class WebEnginePage(QWebEnginePage):
return self.webview.window.NewWebTab('newtab' ,**options).webpage
def triggerAction(self, action, checked=False):
if action == QWebEnginePage.OpenLinkInNewWindow:
self.createWindow(QWebEnginePage.WebBrowserWindow)
return super(WebEnginePage, self).triggerAction(action, checked)
def acceptNavigationRequest(self, url, Type, isMainFrame):
parsed = urlparse(url.toString())
types = {
QWebEnginePage.NavigationTypeLinkClicked: 'link clicked',
QWebEnginePage.NavigationTypeTyped: 'loaded url',
QWebEnginePage.NavigationTypeFormSubmitted: 'form submission',
QWebEnginePage.NavigationTypeBackForward: 'back/forward action',
QWebEnginePage.NavigationTypeReload: 'reloaded',
QWebEnginePage.NavigationTypeOther: 'other type'
}
if not debstable:
types[QWebEnginePage.NavigationTypeRedirect] = 'redirected'
typename = types.get(Type, 'Invalid nav type')
return QWebEnginePage.acceptNavigationRequest(self, url, Type, isMainFrame)
@ -537,6 +635,7 @@ class DevPage(QMainWindow):
self.profile = profile
self.window = parent
self.view = None
self.page = None
#self.setGeometry(100, 100, 800, 600)
self.resize(800, 600)
@ -547,26 +646,25 @@ class DevPage(QMainWindow):
self.setCentralWidget(self.view)
self.setWindowTitle(f'QtWeb Inspector: {url}')
self.show()
self.activateWindow()
return self.page
def closeEvent(self, event):
def closeEvent(self, event=None):
logging.debug('closing dev tools')
#self.view.deleteLater()
#self.page.deleteLater()
#del self.view
#del self.page
event.accept()
self.view.deleteLater()
self.page.deleteLater()
self.view = None
self.page = None
def heck(self):
print('HECK!')
if event:
event.accept()
class DevWebEnginePage(QWebEnginePage):
def __init__(self, profile, parent):
super().__init__(profile)
super().__init__(profile, parent)
self.window = parent

View file

@ -4,9 +4,6 @@ from .config import var
from .Lib.IzzyLib import logging
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
class JsFuncs(object):
def __init__(self, webview):

View file

@ -4,3 +4,5 @@ publicsuffixlist
asyncqt
urllib3
dbutils
jinja2
hamlpy3