scripted upload to gitlab
This commit is contained in:
104
.gitignore
vendored
Normal file
104
.gitignore
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
*.log
|
||||
.idea/
|
||||
2
conn_settings.txt
Normal file
2
conn_settings.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
BEARER_TOKEN=hLdtXH8baCeRCtoW8ARUB0CRcfSeyOH8eocOyqQ4nmuZtXwKw2zVZcb1rwtoiMDP","accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjIyNzU1MDU4LCJ1c2VybmFtZSI6IlJpY2hhcmQxNTAiLCJhdmF0YXJVcmwiOiJzMzovL2h5cGVzcGFjZS1xdWl6L2RlZmF1bHRfYXZhdGFycy9VbnRpdGxlZC0xXzAwMDFfYmx1ZS5wbmciLCJ0b2tlbiI6ImhMZHRYSCIsInJvbGVzIjpbXSwiY2xpZW50IjoiIiwiZ3Vlc3RJZCI6bnVsbCwidiI6MSwiaWF0IjoxNTM1NDE4MjQ1LCJleHAiOjE1NDMxOTQyNDUsImlzcyI6Imh5cGVxdWl6LzEifQ.upfa3v5QCW3sl2bSt5vMyroDYuwIdExds_OiiSfCT_A
|
||||
USER_ID=21149316
|
||||
357
hq.ui
Normal file
357
hq.ui
Normal file
@@ -0,0 +1,357 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>HQApp</class>
|
||||
<widget class="QWidget" name="HQApp">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1298</width>
|
||||
<height>440</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QWidget#HQApp {
|
||||
background-color: #36399A;
|
||||
}
|
||||
QLabel { color: white; }</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>images/hqlogo.jpg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>NEXT GAME</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>$5,000 Prize</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2PM CDT</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" 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>
|
||||
<item row="5" column="0">
|
||||
<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="0" column="1" rowspan="6">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QWidget" name="GooglePanel" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QWidget#GooglePanel {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="1" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="1" column="0">
|
||||
<widget class="QProgressBar" name="progressBar_3">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Answer B</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QProgressBar" name="progressBar_2">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>63</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Answer A</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QProgressBar" name="progressBar_4">
|
||||
<property name="value">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Answer C</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>images/google.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QWidget" name="WebpagePanel" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QWidget#WebpagePanel {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
}
|
||||
QLabel { color: black; }</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>images/webpage.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Website Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="1" column="0">
|
||||
<widget class="QProgressBar" name="progressBar_5">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Answer B</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QProgressBar" name="progressBar_6">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Answer A</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QProgressBar" name="progressBar_7">
|
||||
<property name="value">
|
||||
<number>52</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Answer C</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QWidget" name="widget_3" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: white;
|
||||
border-radius: 10px;
|
||||
color: white;</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: black;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>[Q1] What is a question?</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
365
hq_gui.py
Normal file
365
hq_gui.py
Normal file
@@ -0,0 +1,365 @@
|
||||
import asyncio
|
||||
from question import QuestionHandler
|
||||
from PyQt5.QtGui import QFont, QPixmap, QPalette, QColor, QFontMetrics
|
||||
from PyQt5.QtWidgets import QWidget, QPushButton, QLabel, QProgressBar, QGridLayout
|
||||
from PyQt5.QtWidgets import QFrame, QSpacerItem, QSizePolicy
|
||||
from PyQt5.QtCore import QRect, Qt, QEasingCurve, pyqtSignal, QPropertyAnimation, QUrl
|
||||
import vlc
|
||||
import re
|
||||
|
||||
def getPropertyAnimation(self, name, duration, start, end, keyValues={}):
|
||||
animation = QPropertyAnimation(self, bytes(str.encode(name)))
|
||||
animation.setDuration(duration)
|
||||
animation.setStartValue(start)
|
||||
animation.setEndValue(end)
|
||||
for k, v in keyValues.items():
|
||||
animation.setKeyValueAt(k, v)
|
||||
return animation
|
||||
|
||||
class VideoFrame(QFrame):
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
print(event.size())
|
||||
self.setFixedWidth((300/535) * event.size().height())
|
||||
|
||||
class HQApp(QWidget):
|
||||
nextGameInfo = pyqtSignal([str, str])
|
||||
streamLive = pyqtSignal(str)
|
||||
streamStop = pyqtSignal()
|
||||
getTrivia = pyqtSignal()
|
||||
|
||||
def __init__(self, q):
|
||||
super().__init__()
|
||||
|
||||
self.q = q
|
||||
self.googleAnswers = []
|
||||
self.webpageAnswers = []
|
||||
self.animations = []
|
||||
self.streaming = False
|
||||
|
||||
self.vlc = vlc.Instance()
|
||||
self.player = self.vlc.media_player_new()
|
||||
|
||||
self.setupUI()
|
||||
self.setupSignals()
|
||||
|
||||
def setupUI(self):
|
||||
self.resize(1300, 600)#.setFixedSize(1300, 440)
|
||||
self.setObjectName('HQApp')
|
||||
self.setStyleSheet('QWidget#HQApp { background-color: #36399A; } QLabel { color: white; }')
|
||||
layout = QGridLayout(self)
|
||||
layout.setContentsMargins(9, 9, 200, 9)
|
||||
|
||||
self.hqLogo = QLabel(self)
|
||||
self.hqLogo.setPixmap(QPixmap('images/hqlogo.jpg'))
|
||||
self.hqLogo.setMaximumSize(300, 300)
|
||||
self.hqLogo.setScaledContents(True)
|
||||
layout.addWidget(self.hqLogo, 1, 0, 1, 1)
|
||||
|
||||
self.showStatus = QLabel(self)
|
||||
self.showStatus.setText('NEXT GAME')
|
||||
self.showStatus.hide()
|
||||
self.showStatus.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(self.showStatus, 2, 0, 1, 1)
|
||||
|
||||
font = QFont()
|
||||
font.setPointSize(10)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
|
||||
self.showTime = QLabel(self)
|
||||
self.showTime.setText('2PM CDT')
|
||||
self.showTime.hide()
|
||||
self.showTime.setAlignment(Qt.AlignCenter)
|
||||
self.showTime.setFont(font)
|
||||
layout.addWidget(self.showTime, 3, 0, 1, 1)
|
||||
|
||||
self.showPrize = QLabel(self)
|
||||
self.showPrize.setText('$5,000 Prize')
|
||||
self.showPrize.hide()
|
||||
self.showPrize.setAlignment(Qt.AlignCenter)
|
||||
self.showPrize.setFont(font)
|
||||
layout.addWidget(self.showPrize, 4, 0, 1, 1)
|
||||
|
||||
self.videoFrame = VideoFrame()
|
||||
self.videoFrame.resize(300, 420)
|
||||
p = self.videoFrame.palette()
|
||||
p.setColor(QPalette.Window, QColor(0, 0, 0))
|
||||
self.videoFrame.setPalette(p)
|
||||
self.videoFrame.setAutoFillBackground(True)
|
||||
self.videoFrame.hide()
|
||||
layout.addWidget(self.videoFrame, 0, 0, 6, 1)
|
||||
|
||||
topSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
bottomSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
layout.addItem(topSpacer, 0, 0, 1, 1)
|
||||
layout.addItem(bottomSpacer, 5, 0, 1, 1)
|
||||
|
||||
rlayout = QGridLayout()
|
||||
layout.addLayout(rlayout, 0, 1, 6, 1)
|
||||
|
||||
topSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
bottomSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
||||
rlayout.addItem(topSpacer, 0, 0, 1, 1)
|
||||
rlayout.addItem(bottomSpacer, 3, 0, 1, 1)
|
||||
|
||||
self.googleBG = QWidget(self)
|
||||
self.googleBG.setStyleSheet('QWidget#GooglePanel { background-color: white; border-radius: 10px; }')
|
||||
#sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||
#sizePolicy.setHorizontalStretch(1)
|
||||
#sizePolicy.setVerticalStretch(1)
|
||||
#sizePolicy.setHeightForWidth(self.googleBG.sizePolicy().hasHeightForWidth())
|
||||
#self.googleBG.setSizePolicy(sizePolicy)
|
||||
self.googleBG.setObjectName('GooglePanel')
|
||||
googleLayout = QGridLayout(self.googleBG)
|
||||
rlayout.addWidget(self.googleBG, 2, 0, 1, 1)
|
||||
|
||||
self.webpageBG = QWidget(self)
|
||||
self.webpageBG.setStyleSheet('QWidget#WebpagePanel { background-color: white; border-radius: 10px; }')
|
||||
#sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||
#sizePolicy.setHorizontalStretch(1)
|
||||
#sizePolicy.setVerticalStretch(1)
|
||||
#sizePolicy.setHeightForWidth(self.webpageBG.sizePolicy().hasHeightForWidth())
|
||||
#self.webpageBG.setSizePolicy(sizePolicy)
|
||||
self.webpageBG.setObjectName('WebpagePanel')
|
||||
webpageLayout = QGridLayout(self.webpageBG)
|
||||
rlayout.addWidget(self.webpageBG, 2, 1, 1, 1)
|
||||
|
||||
self.googleLogo = QLabel(self.googleBG)
|
||||
self.googleLogo.setPixmap(QPixmap('images/google.png'))
|
||||
self.googleLogo.setMaximumSize(300, 100)
|
||||
self.googleLogo.setScaledContents(True)
|
||||
googleLayout.addWidget(self.googleLogo, 0, 1, 1, 1)
|
||||
leftSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
rightSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
googleLayout.addItem(leftSpacer, 0, 0, 1, 1)
|
||||
googleLayout.addItem(rightSpacer, 0, 2, 1, 1)
|
||||
|
||||
self.webpageLogo = QLabel(self.webpageBG)
|
||||
self.webpageLogo.setPixmap(QPixmap('images/webpage.png'))
|
||||
self.webpageLogo.setMaximumSize(100, 100)
|
||||
self.webpageLogo.setScaledContents(True)
|
||||
self.webpageText = QLabel('Website Search', self.webpageBG)
|
||||
font = QFont()
|
||||
font.setPointSize(13)
|
||||
self.webpageText.setFont(font)
|
||||
self.webpageText.setStyleSheet('color: black;')
|
||||
webpageLayout.addWidget(self.webpageLogo, 0, 1, 1, 1)
|
||||
webpageLayout.addWidget(self.webpageText, 0, 2, 1, 1)
|
||||
leftSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
rightSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
webpageLayout.addItem(leftSpacer, 0, 0, 1, 1)
|
||||
webpageLayout.addItem(rightSpacer, 0, 3, 1, 1)
|
||||
|
||||
#self.galWidget = QWidget(self)
|
||||
#self.galWidget.setGeometry(QRect(360, 300, 341, 171))
|
||||
self.googleAnswerLayout = QGridLayout()
|
||||
self.googleAnswerLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.googleAnswerA = AnswerBar('A', self)
|
||||
self.googleAnswerB = AnswerBar('B', self)
|
||||
self.googleAnswerC = AnswerBar('C', self)
|
||||
self.googleAnswers = [self.googleAnswerA, self.googleAnswerB, self.googleAnswerC]
|
||||
for answer in self.googleAnswers:
|
||||
answer.hide()
|
||||
self.googleAnswerLayout.addWidget(self.googleAnswerA, 0, 0, 1, 1)
|
||||
self.googleAnswerLayout.addWidget(self.googleAnswerB, 1, 0, 1, 1)
|
||||
self.googleAnswerLayout.addWidget(self.googleAnswerC, 2, 0, 1, 1)
|
||||
self.googleExplanation = QLabel(
|
||||
"Google answers come straight from Google results themselves, meaning that when it works, it's often the most realiable. " \
|
||||
"If Google's confident, it may be because of it's smart card / feedback system... a definite sign that it has the correct answer.\n\nBoth answer " \
|
||||
"sources return the reverse order of its findings if the question has a negative term in it -- such as \"NOT\"," \
|
||||
" which may be inaccurate if the Google search also considers the \"NOT\" when it's returning results. Be wary."
|
||||
)
|
||||
self.googleExplanation.setStyleSheet('color: black;')
|
||||
self.googleExplanation.setWordWrap(True)
|
||||
self.googleAnswerLayout.addWidget(self.googleExplanation, 0, 0, 3, 1)
|
||||
googleLayout.addLayout(self.googleAnswerLayout, 1, 0, 1, 3)
|
||||
|
||||
#self.walWidget = QWidget(self)
|
||||
#self.walWidget.setGeometry(QRect(750, 300, 341, 171))
|
||||
self.webpageAnswerLayout = QGridLayout()
|
||||
self.webpageAnswerLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.webpageAnswerA = AnswerBar('A', self)
|
||||
self.webpageAnswerB = AnswerBar('B', self)
|
||||
self.webpageAnswerC = AnswerBar('C', self)
|
||||
self.webpageAnswers = [self.webpageAnswerA, self.webpageAnswerB, self.webpageAnswerC]
|
||||
for answer in self.webpageAnswers:
|
||||
answer.hide()
|
||||
self.webpageAnswerLayout.addWidget(self.webpageAnswerA, 0, 0, 1, 1)
|
||||
self.webpageAnswerLayout.addWidget(self.webpageAnswerB, 1, 0, 1, 1)
|
||||
self.webpageAnswerLayout.addWidget(self.webpageAnswerC, 2, 0, 1, 1)
|
||||
self.webpageExplanation = QLabel(
|
||||
"Webpage results is the plain text of the sites in the Google results split into keywords and compared " \
|
||||
"against the available answers. This can be helpful for questions that are not so straightforward, " \
|
||||
"since websites contain the complete text of what they have to say, rather than Google's small previews and compact card information. " \
|
||||
"\n\nUsually if Google " \
|
||||
"and the website search have eliminated one answer but can't decide between the other two, whichever one is " \
|
||||
"chosen by both Google and the website search is correct."
|
||||
)
|
||||
self.webpageExplanation.setStyleSheet('color: black;')
|
||||
self.webpageExplanation.setWordWrap(True)
|
||||
self.webpageAnswerLayout.addWidget(self.webpageExplanation, 0, 0, 3, 1)
|
||||
webpageLayout.addLayout(self.webpageAnswerLayout, 1, 0, 1, 4)
|
||||
|
||||
self.questionBG = QWidget(self)
|
||||
self.questionBG.setStyleSheet('background-color: white; border-radius: 10px; color: black;')
|
||||
#sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||
#sizePolicy.setHorizontalStretch(1)
|
||||
#sizePolicy.setVerticalStretch(1)
|
||||
#sizePolicy.setHeightForWidth(self.googleBG.sizePolicy().hasHeightForWidth())
|
||||
#self.googleBG.setSizePolicy(sizePolicy)
|
||||
questionLayout = QGridLayout(self.questionBG)
|
||||
rlayout.addWidget(self.questionBG, 1, 0, 1, 2)
|
||||
|
||||
font = QFont()
|
||||
font.setPointSize(20)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
self.question = QLabel(self.questionBG)
|
||||
self.question.setFont(font)
|
||||
self.question.setAlignment(Qt.AlignCenter)
|
||||
self.question.setText('HQ Trivia')
|
||||
questionLayout.addWidget(self.question, 0, 0, 1, 1)
|
||||
|
||||
self.button = QPushButton('Get Trivia', self)
|
||||
layout.addWidget(self.button, 6, 0, 1, 1)
|
||||
|
||||
#self.videoFrame.show()
|
||||
self.show()
|
||||
|
||||
for answer in self.googleAnswers + self.webpageAnswers:
|
||||
answer.setValue(0)
|
||||
|
||||
def setupSignals(self):
|
||||
self.nextGameInfo.connect(self.setNextGame)
|
||||
self.streamLive.connect(self.startStream)
|
||||
self.streamStop.connect(self.stopStream)
|
||||
#self.button.clicked.connect(self.resetCount)
|
||||
self.q.searching.connect(self.newSearch)
|
||||
self.q.googleResults.connect(self.setGoogleResults)
|
||||
self.q.pageResults.connect(self.setWebpageResults)
|
||||
|
||||
def setNextGame(self, time, prize):
|
||||
self.showTime.setText(time)
|
||||
self.showPrize.setText(prize + ' Prize')
|
||||
self.showStatus.show()
|
||||
self.showTime.show()
|
||||
self.showPrize.show()
|
||||
|
||||
def newSearch(self, qn, newQuestion, answers, formattedAnswers):
|
||||
self.animations.clear()
|
||||
|
||||
questionText = '[Q{}] {}'.format(qn, newQuestion)
|
||||
textWidth = self.questionBG.width() - 10
|
||||
textSize = 20
|
||||
while textWidth >= self.questionBG.width() - 10:
|
||||
font = QFont()
|
||||
font.setPointSize(textSize)
|
||||
font.setBold(True)
|
||||
font.setWeight(75)
|
||||
textWidth = QFontMetrics(font).width(questionText)
|
||||
textSize -= 1
|
||||
self.question.setFont(font)
|
||||
self.question.setText(questionText)
|
||||
|
||||
self.answers = {answers[0]: formattedAnswers[0], answers[1]: formattedAnswers[1], answers[2]: formattedAnswers[2]}
|
||||
|
||||
self.googleExplanation.hide()
|
||||
self.webpageExplanation.hide()
|
||||
for answer in self.googleAnswers + self.webpageAnswers:
|
||||
if answer.letter == 'A': answer.setAnswer(answers[0])
|
||||
elif answer.letter == 'B': answer.setAnswer(answers[1])
|
||||
elif answer.letter == 'C': answer.setAnswer(answers[2])
|
||||
answer.show()
|
||||
|
||||
if answer.maximum() == 0:
|
||||
answer.setValue(0)
|
||||
answer.setMaximum(1)
|
||||
else:
|
||||
animation = getPropertyAnimation(answer, 'value', 1000, answer.value(), 0)
|
||||
animation.setEasingCurve(QEasingCurve.OutQuad)
|
||||
self.animations.append(animation)
|
||||
animation.start()
|
||||
|
||||
def setGoogleResults(self, results, reverse):
|
||||
if any([type(v) == dict for v in results.values()]):
|
||||
formattedResults = {}
|
||||
for a, d in results.items():
|
||||
formattedResults[a] = sum(d.values())
|
||||
results = formattedResults
|
||||
|
||||
total = sum(results.values()) * 1000
|
||||
for answer in self.googleAnswers:
|
||||
answer.setMaximum(total)
|
||||
for orgAnswer, formatAnswer in self.answers.items():
|
||||
if answer.text() == orgAnswer:
|
||||
for rname, count in results.items():
|
||||
if re.sub('[^a-zA-Z0-9 ]', '', rname).lower() == re.sub('[^a-zA-Z0-9 ]', '', orgAnswer).lower():
|
||||
if reverse:
|
||||
if count == max(results.values()):
|
||||
count = min(results.values())
|
||||
elif count == min(results.values()):
|
||||
count = max(results.values())
|
||||
animation = getPropertyAnimation(answer, 'value', 1000, 0, count * 1000)
|
||||
animation.setEasingCurve(QEasingCurve.OutQuad)
|
||||
self.animations.append(animation)
|
||||
animation.start()
|
||||
|
||||
def setWebpageResults(self, results, reverse):
|
||||
total = sum(results.values()) * 1000
|
||||
for answer in self.webpageAnswers:
|
||||
answer.setMaximum(total)
|
||||
for orgAnswer, formatAnswer in self.answers.items():
|
||||
if answer.text() == orgAnswer:
|
||||
for rname, count in results.items():
|
||||
if re.sub('[^a-zA-Z0-9 ]', '', rname).lower() == re.sub('[^a-zA-Z0-9 ]', '', orgAnswer).lower():
|
||||
if reverse:
|
||||
if count == max(results.values()):
|
||||
count = min(results.values())
|
||||
elif count == min(results.values()):
|
||||
count = max(results.values())
|
||||
animation = getPropertyAnimation(answer, 'value', 1000, 0, count * 1000)
|
||||
animation.setEasingCurve(QEasingCurve.OutQuad)
|
||||
self.animations.append(animation)
|
||||
animation.start()
|
||||
|
||||
def startStream(self, url):
|
||||
if self.streaming:
|
||||
return
|
||||
self.media = self.vlc.media_new(url)
|
||||
self.player.set_media(self.media)
|
||||
self.player.set_hwnd(self.videoFrame.winId())
|
||||
self.streaming = True
|
||||
self.player.play()
|
||||
self.videoFrame.show()
|
||||
|
||||
def stopStream(self):
|
||||
self.streaming = False
|
||||
self.player.stop()
|
||||
self.videoFrame.hide()
|
||||
|
||||
class AnswerBar(QProgressBar):
|
||||
def __init__(self, letter, parent):
|
||||
super().__init__(parent)
|
||||
#self.setAnswer('Answer {}'.format(letter.upper()))
|
||||
self.letter = letter
|
||||
self.setAnswer('')
|
||||
self.setAlignment(Qt.AlignCenter)
|
||||
self.setCount(0, 100)
|
||||
self.setStyleSheet("QProgressBar { border: 2px solid grey; border-radius: 5px; }\n"
|
||||
"QProgressBar::chunk { background-color: #50C994; width: 1px; }")
|
||||
|
||||
def setAnswer(self, answer):
|
||||
self.setFormat(answer)
|
||||
|
||||
def setCount(self, count, total):
|
||||
self.setMaximum(total)
|
||||
self.setValue(count)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
import sys
|
||||
app = QApplication(sys.argv)
|
||||
hq = HQApp()
|
||||
hq.setupUI()
|
||||
app.exec_()
|
||||
110
hq_main.py
Normal file
110
hq_main.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import requests
|
||||
import sys
|
||||
import time
|
||||
import csv
|
||||
from ast import literal_eval
|
||||
from datetime import datetime
|
||||
from question import QuestionHandler
|
||||
|
||||
import networking
|
||||
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from hq_gui import HQApp
|
||||
from quamash import QEventLoop, QThreadExecutor
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
loop = QEventLoop(app)
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(filename="data.log", level=logging.INFO, filemode="w")
|
||||
|
||||
# Read in bearer token and user ID
|
||||
with open("conn_settings.txt", "r") as conn_settings:
|
||||
BEARER_TOKEN = conn_settings.readline().strip().split("=")[1]
|
||||
USER_ID = conn_settings.readline().strip().split("=")[1]
|
||||
|
||||
print("getting")
|
||||
main_url = f"https://api-quiz.hype.space/shows/now?type=hq&userId={USER_ID}"
|
||||
headers = {"Authorization": f"Bearer {BEARER_TOKEN}",
|
||||
"x-hq-client": "Android/1.6.1"}
|
||||
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0",
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "en-US,en;q=0.5",
|
||||
"Accept-Encoding": "gzip, deflate"}
|
||||
# "x-hq-stk": "MQ==",
|
||||
# "Connection": "Keep-Alive",
|
||||
# "User-Agent": "okhttp/3.8.0"}
|
||||
|
||||
q = QuestionHandler()
|
||||
hqApp = HQApp(q)
|
||||
|
||||
import re
|
||||
import random
|
||||
trivia = []
|
||||
with open('trivia2.csv', 'r') as csvfile:
|
||||
reader = csv.reader(csvfile)
|
||||
for row in reader:
|
||||
if row[1] == 'options': continue
|
||||
question = re.sub(r'^\d+\. ?', '', row[0].strip())
|
||||
if 'which' not in question.lower(): continue
|
||||
answers = literal_eval(row[1])
|
||||
if len(answers) != 3:
|
||||
continue
|
||||
correct = int(row[2])
|
||||
trivia.append({'q': question, 'a': answers, 'c': correct})
|
||||
i = 0
|
||||
def getTrivia():
|
||||
global i
|
||||
i += 1
|
||||
result = requests.get('https://opentdb.com/api.php?amount=1&type=multiple').json()['results'][0]
|
||||
question = result['question'].replace('"', '').replace(''', "'")
|
||||
answers = [result['correct_answer']] + result['incorrect_answers'][:2]
|
||||
loop.create_task(q.answer_question(str(i), question, answers))
|
||||
|
||||
hqApp.button.clicked.connect(getTrivia)
|
||||
#hqApp.streamLive.emit('rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov')
|
||||
#hqApp.videoFrame.raise_()
|
||||
#hqApp.button.hide()
|
||||
|
||||
while True:
|
||||
print()
|
||||
try:
|
||||
app.processEvents()
|
||||
response_data = loop.run_until_complete(
|
||||
networking.get_json_response(main_url, timeout=1.5, headers=headers))
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
print("Server response not JSON, retrying...")
|
||||
loop.run_until_complete(asyncio.sleep(1))
|
||||
continue
|
||||
|
||||
logging.info(response_data)
|
||||
|
||||
if "broadcast" not in response_data or response_data["broadcast"] is None:
|
||||
if "error" in response_data and response_data["error"] == "Auth not valid":
|
||||
raise RuntimeError("Connection settings invalid")
|
||||
else:
|
||||
print("Show not on.")
|
||||
next_time = datetime.strptime(response_data["nextShowTime"], "%Y-%m-%dT%H:%M:%S.000Z")
|
||||
now = time.time()
|
||||
offset = datetime.fromtimestamp(now) - datetime.utcfromtimestamp(now)
|
||||
|
||||
print(f"Next show time: {(next_time + offset).strftime('%Y-%m-%d %I:%M %p')}")
|
||||
print("Prize: " + response_data["nextShowPrize"])
|
||||
hqApp.nextGameInfo.emit((next_time + offset).strftime('%Y-%m-%d %I:%M %p'), response_data['nextShowPrize'])
|
||||
if hqApp.streaming:
|
||||
hqApp.streamStop.emit()
|
||||
loop.run_until_complete(asyncio.sleep(5))
|
||||
else:
|
||||
socket = response_data["broadcast"]["socketUrl"]
|
||||
stream = response_data["broadcast"]["streamUrl"]
|
||||
print(stream)
|
||||
print(f"Show active, connecting to socket at {socket}")
|
||||
if not hqApp.streaming:
|
||||
hqApp.streamLive.emit(stream)
|
||||
hqApp.videoFrame.raise_()
|
||||
loop.run_until_complete(networking.websocket_handler(q, socket, headers))
|
||||
BIN
images/google.png
Normal file
BIN
images/google.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
images/hq-100.jpg
Normal file
BIN
images/hq-100.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
images/hqlogo.jpg
Normal file
BIN
images/hqlogo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
images/webpage.png
Normal file
BIN
images/webpage.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
5
l.py
Normal file
5
l.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import PyLivestream
|
||||
|
||||
s = PyLivestream.Screenshare('stream.ini', 'youtube')
|
||||
print('going live on', s.sites)
|
||||
s.golive()
|
||||
74
networking.py
Normal file
74
networking.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
import aiohttp
|
||||
|
||||
import question
|
||||
|
||||
|
||||
async def fetch(url, session, timeout):
|
||||
try:
|
||||
async with session.get(url, timeout=timeout) as response:
|
||||
return await response.text()
|
||||
except asyncio.TimeoutError:
|
||||
print(f"Server timeout ({timeout}) to {url}")
|
||||
logging.exception(f"Server timeout ({timeout}) to {url}")
|
||||
return ""
|
||||
except Exception:
|
||||
print(f"Server error to {url}")
|
||||
logging.exception(f"Server error to {url}")
|
||||
return ""
|
||||
|
||||
async def get_responses(urls, timeout, headers):
|
||||
tasks = []
|
||||
async with aiohttp.ClientSession(headers=headers) as session:
|
||||
for url in urls:
|
||||
task = asyncio.ensure_future(fetch(url, session, timeout))
|
||||
tasks.append(task)
|
||||
|
||||
responses = await asyncio.gather(*tasks)
|
||||
return responses
|
||||
|
||||
|
||||
async def get_response(url, timeout, headers):
|
||||
async with aiohttp.ClientSession(headers=headers) as session:
|
||||
return await fetch(url, session, timeout)
|
||||
|
||||
|
||||
async def get_json_response(url, timeout, headers):
|
||||
async with aiohttp.ClientSession(headers=headers) as session:
|
||||
async with session.get(url, timeout=timeout) as response:
|
||||
return await response.json()
|
||||
|
||||
|
||||
async def websocket_handler(handler, uri, headers):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.ws_connect(uri, headers=headers, heartbeat=5) as ws:
|
||||
print("Connected")
|
||||
async for msg in ws:
|
||||
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||
message = msg.data
|
||||
message = re.sub(r"[\x00-\x1f\x7f-\x9f]", "", message)
|
||||
|
||||
message_data = json.loads(message)
|
||||
logging.info(str(message_data).encode("utf-8"))
|
||||
|
||||
if "error" in message_data and message_data["error"] == "Auth not valid":
|
||||
logging.info(message_data)
|
||||
raise RuntimeError("Connection settings invalid")
|
||||
elif message_data["type"] != "interaction":
|
||||
logging.info(message_data)
|
||||
if message_data["type"] == "question":
|
||||
question_str = message_data["question"]
|
||||
answers = [ans["text"] for ans in message_data["answers"] if ans["text"].strip() != ""]
|
||||
print("\n" * 5)
|
||||
print("Question detected.")
|
||||
print(f"Question {message_data['questionNumber']} out of {message_data['questionCount']}")
|
||||
print(question_str)
|
||||
print(answers)
|
||||
print()
|
||||
print(await handler.answer_question(message_data['questionNumber'], question_str, answers))
|
||||
|
||||
print("Socket closed")
|
||||
147
question.py
Normal file
147
question.py
Normal file
@@ -0,0 +1,147 @@
|
||||
import itertools
|
||||
import re
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from PyQt5.QtCore import pyqtSignal, QObject
|
||||
|
||||
from unidecode import unidecode
|
||||
|
||||
import search
|
||||
|
||||
punctuation_to_none = str.maketrans({key: None for key in "!\"#$%&\'()*+,-.:;<=>?@[\\]^_`{|}~<7E>"})
|
||||
punctuation_to_space = str.maketrans({key: " " for key in "!\"#$%&\'()*+,-.:;<=>?@[\\]^_`{|}~<7E>"})
|
||||
|
||||
class QuestionHandler(QObject):
|
||||
searching = pyqtSignal([str, str, list, list])
|
||||
googleResults = pyqtSignal([dict, bool])
|
||||
pageResults = pyqtSignal([dict, bool])
|
||||
|
||||
async def answer_question(self, qn, question, original_answers):
|
||||
print("Searching")
|
||||
start = time.time()
|
||||
|
||||
question = unidecode(question)
|
||||
|
||||
answers = []
|
||||
for ans in original_answers:
|
||||
ans = unidecode(ans)
|
||||
answers.append(ans.translate(punctuation_to_none))
|
||||
#answers.append(ans.translate(punctuation_to_space))
|
||||
answers = list(dict.fromkeys(answers))
|
||||
print('Possible Answers: {}'.format(answers))
|
||||
self.searching.emit(str(qn), question, original_answers, answers)
|
||||
|
||||
reverse = True
|
||||
if ("NOT" in question and '"NOT"' not in question):
|
||||
question = re.sub(r'\bNOT\b', ' ', question)
|
||||
elif ("least" in question.lower() and "at least" not in question.lower() and '"least"' not in question.lower()):
|
||||
question = re.sub(r'\bleast\b', ' most ', question)
|
||||
elif ("fewest" in question and '"fewest"' not in question):
|
||||
question = re.sub(r'\bfewest\b', ' most ', question)
|
||||
elif ("NEVER" in question and '"NEVER"' not in question):
|
||||
question = re.sub(r'\bNOT\b', ' always ', question)
|
||||
elif ("NON" in question and '"NON"' not in question):
|
||||
question = re.sub(r'\bNON', ' ', question)
|
||||
else:
|
||||
reverse = False
|
||||
|
||||
question_lower = question.lower().replace('"', '').replace("'", '"')
|
||||
question_lower = question_lower.replace('which of these', 'what').replace('which', 'what')
|
||||
|
||||
"""quoted = re.findall('"([^"]*)"', question_lower) # Get all words in quotes
|
||||
no_quote = question_lower
|
||||
for quote in quoted:
|
||||
no_quote = no_quote.replace(f"\"{quote}\"", "1placeholder1")
|
||||
|
||||
question_keywords = search.find_keywords(no_quote)
|
||||
for quote in quoted:
|
||||
question_keywords[question_keywords.index("1placeholder1")] = quote"""
|
||||
|
||||
#if 'which' in question_lower:
|
||||
#question_lower += ' ({})'.format(' OR '.join(answers))
|
||||
|
||||
print('Search: "{}"'.format(question_lower))
|
||||
smart_data, search_results = await search.search_google(question_lower, 5)
|
||||
print('Smart Data: {}'.format(smart_data))
|
||||
print('Search Results: {}'.format(search_results))
|
||||
|
||||
print('Searching smart Google data and website previews...')
|
||||
search_text = [search.clean_html(x).lower().translate(punctuation_to_none) for x in smart_data]
|
||||
counts = await self._search_method1(search_text, answers, reverse)
|
||||
if counts:
|
||||
self.googleResults.emit(counts, reverse)
|
||||
else:
|
||||
counts = await self._search_method2(search_text, answers, reverse)
|
||||
print(counts)
|
||||
if counts:
|
||||
self.googleResults.emit(counts, reverse)
|
||||
print('GOOGLE THINKS: {}'.format(min(counts, key=counts.get) if reverse else max(counts, key=counts.get)))
|
||||
else:
|
||||
self.googleResults.emit({k: 0 for k in answers}, False)
|
||||
print('GOOGLE is not sure.')
|
||||
|
||||
|
||||
print()
|
||||
print('Searching full websites provided by Google...')
|
||||
search_text = [x.translate(punctuation_to_none) for x in await search.get_clean_texts(search_results)]
|
||||
counts = await self._search_method1(search_text, answers, reverse)
|
||||
if counts:
|
||||
self.pageResults.emit(counts, reverse)
|
||||
else:
|
||||
counts = await self._search_method2(search_text, answers, reverse)
|
||||
if counts:
|
||||
self.pageResults.emit(counts, reverse)
|
||||
print('WEBSITES THINK: {}'.format(min(counts, key=counts.get) if reverse else max(counts, key=counts.get)))
|
||||
else:
|
||||
self.pageResults.emit({k: 0 for k in answers}, False)
|
||||
print('WEBSITES are not sure.')
|
||||
|
||||
|
||||
print(f"Search took {time.time() - start} seconds")
|
||||
return ""
|
||||
|
||||
async def _search_method1(self, texts, answers, reverse):
|
||||
"""
|
||||
Returns the answer with the maximum/minimum number of exact occurrences in the texts.
|
||||
:param texts: List of text to analyze
|
||||
:param answers: List of answers
|
||||
:param reverse: True if the best answer occurs the least, False otherwise
|
||||
:return: Answer that occurs the most/least in the texts, empty string if there is a tie
|
||||
"""
|
||||
print("Running method 1")
|
||||
counts = {answer.lower(): 0 for answer in answers}
|
||||
|
||||
for text in texts:
|
||||
for answer in counts:
|
||||
counts[answer] += len(re.findall(f"\\b{answer}\\b", text))
|
||||
|
||||
print(counts)
|
||||
|
||||
# If not all answers have count of 0 and the best value doesn't occur more than once, return the best answer
|
||||
best_value = min(counts.values()) if reverse else max(counts.values())
|
||||
if not all(c == 0 for c in counts.values()) and list(counts.values()).count(best_value) == 1:
|
||||
return counts
|
||||
return ""
|
||||
|
||||
async def _search_method2(self, texts, answers, reverse):
|
||||
"""
|
||||
Return the answer with the maximum/minimum number of keyword occurrences in the texts.
|
||||
:param texts: List of text to analyze
|
||||
:param answers: List of answers
|
||||
:param reverse: True if the best answer occurs the least, False otherwise
|
||||
:return: Answer whose keywords occur most/least in the texts
|
||||
"""
|
||||
print("Running method 2")
|
||||
counts = {answer: {keyword: 0 for keyword in search.find_keywords(answer)} for answer in answers}
|
||||
|
||||
for text in texts:
|
||||
for keyword_counts in counts.values():
|
||||
for keyword in keyword_counts:
|
||||
keyword_counts[keyword] += len(re.findall(f" {keyword} ", text))
|
||||
|
||||
print(counts)
|
||||
counts_sum = {answer: sum(keyword_counts.values()) for answer, keyword_counts in counts.items()}
|
||||
|
||||
if not all(c == 0 for c in counts_sum.values()):
|
||||
return counts_sum
|
||||
return ""
|
||||
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
aiohttp
|
||||
bs4
|
||||
nltk
|
||||
unidecode
|
||||
aiodns
|
||||
cchardet
|
||||
PyQt5
|
||||
git+https://github.com/TheRandomDog/quamash.git
|
||||
123
search.py
Normal file
123
search.py
Normal file
@@ -0,0 +1,123 @@
|
||||
import re
|
||||
from html import unescape
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from nltk import word_tokenize
|
||||
from nltk.corpus import stopwords
|
||||
from nltk.tag.perceptron import PerceptronTagger
|
||||
from nltk.tokenize import RegexpTokenizer
|
||||
from unidecode import unidecode
|
||||
|
||||
import networking
|
||||
|
||||
STOP = set(stopwords.words("english")) - {"most", "least"}
|
||||
tokenizer = RegexpTokenizer(r"\w+")
|
||||
tagger = PerceptronTagger()
|
||||
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/59.0",
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "en-US,en;q=0.5",
|
||||
"Accept-Encoding": "gzip, deflate"}
|
||||
GOOGLE_URL = "https://www.google.com/search?q={}&ie=utf-8&oe=utf-8&client=firefox-b-1-ab"
|
||||
|
||||
|
||||
def find_keywords(words):
|
||||
"""
|
||||
Returns the list of words given without stopwords.
|
||||
:param words: List of words
|
||||
:return: Words without stopwords
|
||||
"""
|
||||
return [w for w in tokenizer.tokenize(words.lower()) if w not in STOP]
|
||||
|
||||
|
||||
def find_nouns(text, num_words, reverse=False):
|
||||
tokens = word_tokenize(text)
|
||||
tags = tagger.tag(tokens)
|
||||
print(tags)
|
||||
|
||||
tags = tags[:num_words] if not reverse else tags[-num_words:]
|
||||
|
||||
nouns = []
|
||||
consecutive_nouns = []
|
||||
|
||||
for tag in tags:
|
||||
tag_type = tag[1]
|
||||
word = tag[0]
|
||||
|
||||
if "NN" not in tag_type and len(consecutive_nouns) > 0:
|
||||
nouns.append(" ".join(consecutive_nouns))
|
||||
consecutive_nouns = []
|
||||
elif "NN" in tag_type:
|
||||
consecutive_nouns.append(word)
|
||||
|
||||
if len(consecutive_nouns) > 0:
|
||||
nouns.append(" ".join(consecutive_nouns))
|
||||
|
||||
return nouns
|
||||
|
||||
|
||||
def get_google_links(page, num_results):
|
||||
soup = BeautifulSoup(page, "html.parser")
|
||||
results = soup.findAll("h3", {"class": "r"})
|
||||
|
||||
links = []
|
||||
for r in results:
|
||||
url = r.find("a")
|
||||
if url is not None:
|
||||
links.append(url["href"])
|
||||
links = list(dict.fromkeys(links)) # Remove duplicates while preserving order
|
||||
|
||||
return links[:num_results]
|
||||
|
||||
def get_smart_data(page):
|
||||
soup = BeautifulSoup(page, 'html.parser')
|
||||
result = (
|
||||
soup.find('div', {'class': 'Z0LcW'}) # Google Card
|
||||
or soup.find('span', {'class': 'Y0NH2b'}) # Featured Snippet
|
||||
)
|
||||
if result:
|
||||
return [result.text] + [r.text for r in soup.findAll('span', {'class': 'st'})]
|
||||
else: # Descriptions generated by Google Search results are also pretty relevant.
|
||||
return [r.text for r in soup.findAll('span', {'class': 'st'})]
|
||||
|
||||
async def search_google(question, num_results):
|
||||
"""
|
||||
Returns num_results urls from a google search of question.
|
||||
:param question: Question to search
|
||||
:param num_results: Number of results to return
|
||||
:return: List of length num_results of urls retrieved from the search
|
||||
"""
|
||||
# Could use Google's Custom Search API here, limit of 100 queries per day
|
||||
# result = service.cse().list(q=question, cx=CSE_ID, num=num_results).execute()
|
||||
# return result["items"]
|
||||
page = await networking.get_response(GOOGLE_URL.format(question), timeout=5, headers=HEADERS)
|
||||
smart = get_smart_data(page)
|
||||
return (smart, get_google_links(page, num_results))
|
||||
|
||||
|
||||
async def multiple_search(questions, num_results):
|
||||
queries = list(map(GOOGLE_URL.format, questions))
|
||||
pages = await networking.get_responses(queries, timeout=5, headers=HEADERS)
|
||||
link_list = [get_google_links(page, num_results) for page in pages]
|
||||
return link_list
|
||||
|
||||
|
||||
def clean_html(html):
|
||||
# First we remove inline JavaScript/CSS:
|
||||
cleaned = re.sub(r"(?is)<(script|style).*?>.*?(</\1>)", "", html.strip())
|
||||
# Then we remove html comments. This has to be done before removing regular
|
||||
# tags since comments can contain '>' characters.
|
||||
cleaned = re.sub(r"(?s)<!--(.*?)-->[\n]?", "", cleaned)
|
||||
# Next we can remove the remaining tags:
|
||||
cleaned = re.sub(r"(?s)<.*?>", " ", cleaned)
|
||||
# Finally, we deal with whitespace
|
||||
cleaned = re.sub(r" ", " ", cleaned)
|
||||
cleaned = re.sub(r"\n", " ", cleaned)
|
||||
cleaned = re.sub(r"\s\s+", " ", cleaned)
|
||||
|
||||
return unidecode(unescape(cleaned.strip()))
|
||||
|
||||
|
||||
async def get_clean_texts(urls, timeout=2.5, headers=HEADERS):
|
||||
responses = await networking.get_responses(urls, timeout, headers)
|
||||
|
||||
return [clean_html(r).lower() for r in responses]
|
||||
66
stream.ini
Normal file
66
stream.ini
Normal file
@@ -0,0 +1,66 @@
|
||||
[DEFAULT]
|
||||
screencap_origin: 0,0
|
||||
screencap_res: 1280x720
|
||||
screencap_fps: 30
|
||||
audiofs: 44100
|
||||
keyframe_sec: 2
|
||||
audio_bps: 128k
|
||||
preset: ultrafast
|
||||
exe: ffmpeg
|
||||
timelimit:
|
||||
|
||||
# indexed by sys.platform
|
||||
[win32]
|
||||
vcap: gdigrab
|
||||
videochan: desktop
|
||||
#vcap: dshow
|
||||
#videochan: video="UScreenCapture"
|
||||
#acap: dshow
|
||||
#audiochan: audio="Internal Microphone"
|
||||
hcam: dshow
|
||||
|
||||
# per-site config
|
||||
[youtube]
|
||||
server: rtmp://a.rtmp.youtube.com/live2/
|
||||
key: mzv1-8h3w-mamv-eedw
|
||||
|
||||
[periscope]
|
||||
video_kbps: 800
|
||||
audio_bps: 64k
|
||||
server: rtmp://va.pscp.tv:80/x/
|
||||
key: periscope.key
|
||||
|
||||
[facebook]
|
||||
server: rtmp://live-api.facebook.com:80/rtmp/
|
||||
key:
|
||||
|
||||
[restream.io]
|
||||
audio_bps: 64k
|
||||
video_kbps: 800
|
||||
server: rtmp://us-east.restream.io/live/
|
||||
key: restreamio.key
|
||||
|
||||
[twitch]
|
||||
audio_bps: 96k
|
||||
server: rtmp://live-jfk.twitch.tv/app/
|
||||
key:
|
||||
|
||||
[mixer]
|
||||
keyframe_sec: 1
|
||||
server: rtmp://ingest-wdc.mixer.com:1935/beam
|
||||
key:
|
||||
|
||||
[ustream]
|
||||
keyframe_sec: 1
|
||||
audio_bps: 128k
|
||||
server:
|
||||
key:
|
||||
|
||||
[vimeo]
|
||||
server: rtmp://rtmp.cloud.vimeo.com/
|
||||
key:
|
||||
|
||||
[file]
|
||||
video_kbps: 2000
|
||||
screencap_res: 720x420
|
||||
#audio_bps:
|
||||
1440
trivia.csv
Normal file
1440
trivia.csv
Normal file
File diff suppressed because it is too large
Load Diff
2
trivia2.csv
Normal file
2
trivia2.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
"question","options","correct_answer","game"
|
||||
" Which of these women married Ellen DeGeneres?","['k.d. Lang', 'Portia de ROSSI', 'Shannen Doherty']","1","KO Trivia"
|
||||
|
Reference in New Issue
Block a user