Upd horsy to 140320221219
This commit is contained in:
1 parent
c85b2be089
commit
988832acfb
25 files changed
+1458
No files matched your search
@@ -0,0 +1,32 @@
|
|||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_DelSongs(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(300, 510)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(300, 510))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(300, 510))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
|
||||||
|
self.listWidget.setGeometry(QtCore.QRect(0, 0, 300, 461))
|
||||||
|
self.listWidget.setObjectName("listWidget")
|
||||||
|
self.delButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.delButton.setGeometry(QtCore.QRect(0, 460, 300, 23))
|
||||||
|
self.delButton.setObjectName("delButton")
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "Songs manager"))
|
||||||
|
self.delButton.setText(_translate("MainWindow", "Delete selected song"))
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_ExtendedMenu(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(151, 74)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(151, 74))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(151, 74))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.prosearchButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.prosearchButton.setGeometry(QtCore.QRect(0, 0, 150, 50))
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.prosearchButton.sizePolicy().hasHeightForWidth())
|
||||||
|
self.prosearchButton.setSizePolicy(sizePolicy)
|
||||||
|
self.prosearchButton.setMaximumSize(QtCore.QSize(150, 50))
|
||||||
|
self.prosearchButton.setObjectName("prosearchButton")
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 151, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "Extended functions"))
|
||||||
|
self.prosearchButton.setText(_translate("MainWindow", "Pro search"))
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_MainBuild(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(161, 157)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(161, 157))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(161, 157))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
|
||||||
|
self.listWidget.setGeometry(QtCore.QRect(0, 0, 161, 111))
|
||||||
|
self.listWidget.setObjectName("listWidget")
|
||||||
|
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.pushButton.setGeometry(QtCore.QRect(0, 110, 161, 23))
|
||||||
|
self.pushButton.setObjectName("pushButton")
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 161, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
for file in os.listdir(os.getcwd()):
|
||||||
|
if file == "MultiMate_Player.py":
|
||||||
|
self.listWidget.addItem(file)
|
||||||
|
elif file == "MultiMate_Player.pyw":
|
||||||
|
self.listWidget.addItem(file)
|
||||||
|
elif file == "MultiMate_Player.exe":
|
||||||
|
self.listWidget.addItem(file)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "Choose main build"))
|
||||||
|
self.pushButton.setText(_translate("MainWindow", "Choose main build"))
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_MainWindow(QtWidgets.QMainWindow):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(801, 580)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(801, 580))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(801, 580))
|
||||||
|
MainWindow.setWindowIcon(QtGui.QIcon('resources/img/MultiMate.ico'))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.timeline = QtWidgets.QSlider(self.centralwidget)
|
||||||
|
self.timeline.setGeometry(QtCore.QRect(10, 490, 781, 22))
|
||||||
|
self.timeline.setPageStep(1)
|
||||||
|
self.timeline.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.timeline.setObjectName("timeline")
|
||||||
|
self.timeline.setMaximum(1000)
|
||||||
|
|
||||||
|
self.playpausebutton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.playpausebutton.setEnabled(True)
|
||||||
|
self.playpausebutton.setGeometry(QtCore.QRect(390, 520, 40, 40))
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setKerning(True)
|
||||||
|
self.playpausebutton.setFont(font)
|
||||||
|
self.playpausebutton.setStyleSheet("background-color: rgba(10, 0, 0, 0);\n"
|
||||||
|
"")
|
||||||
|
self.playpausebutton.setText("")
|
||||||
|
self.playpausebutton.setObjectName("playpausebutton")
|
||||||
|
self.playpausePicture = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
self.playpausePicture.setGeometry(QtCore.QRect(390, 520, 40, 40))
|
||||||
|
self.playpausePicture.setText("")
|
||||||
|
self.playpausePicture.setPixmap(QtGui.QPixmap("resources/img/MultiMate40x40.png"))
|
||||||
|
self.playpausePicture.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
|
||||||
|
self.playpausePicture.setObjectName("playpausePicture")
|
||||||
|
|
||||||
|
self.prevbutton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.prevbutton.setEnabled(True)
|
||||||
|
self.prevbutton.setGeometry(QtCore.QRect(340, 520, 40, 40))
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setKerning(True)
|
||||||
|
self.prevbutton.setFont(font)
|
||||||
|
self.prevbutton.setStyleSheet("background-color: rgba(10, 0, 0, 0);\n"
|
||||||
|
"")
|
||||||
|
self.prevbutton.setText("")
|
||||||
|
self.prevbutton.setObjectName("prevbutton")
|
||||||
|
self.prevPicture = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
self.prevPicture.setGeometry(QtCore.QRect(340, 520, 40, 40))
|
||||||
|
self.prevPicture.setText("")
|
||||||
|
self.prevPicture.setPixmap(QtGui.QPixmap("resources/img/prev.png"))
|
||||||
|
self.prevPicture.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
|
||||||
|
self.prevPicture.setObjectName("prevPicture")
|
||||||
|
self.nextbutton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.nextbutton.setEnabled(True)
|
||||||
|
self.nextbutton.setGeometry(QtCore.QRect(440, 520, 40, 40))
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setKerning(True)
|
||||||
|
self.nextbutton.setFont(font)
|
||||||
|
self.nextbutton.setStyleSheet("background-color: rgba(10, 0, 0, 0);\n"
|
||||||
|
"")
|
||||||
|
self.nextbutton.setText("")
|
||||||
|
self.nextbutton.setObjectName("nextbutton")
|
||||||
|
self.nextPicture = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
self.nextPicture.setGeometry(QtCore.QRect(440, 520, 40, 40))
|
||||||
|
self.nextPicture.setText("")
|
||||||
|
self.nextPicture.setPixmap(QtGui.QPixmap("resources/img/next.png"))
|
||||||
|
self.nextPicture.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
|
||||||
|
self.nextPicture.setObjectName("nextPicture")
|
||||||
|
|
||||||
|
self.speedBox = QtWidgets.QDoubleSpinBox(self.centralwidget)
|
||||||
|
self.speedBox.setGeometry(QtCore.QRect(10, 515, 62, 22))
|
||||||
|
self.speedBox.setDecimals(1)
|
||||||
|
self.speedBox.setProperty("value", 1.0)
|
||||||
|
self.speedBox.setSingleStep(0.2)
|
||||||
|
self.speedBox.setObjectName("speedBox")
|
||||||
|
self.timenow = QtWidgets.QTextEdit(self.centralwidget)
|
||||||
|
self.timenow.setGeometry(QtCore.QRect(713, 515, 81, 23))
|
||||||
|
self.timenow.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
|
||||||
|
self.timenow.setObjectName("timenow")
|
||||||
|
self.speedTextLabel = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
self.speedTextLabel.setGeometry(QtCore.QRect(25, 536, 47, 13))
|
||||||
|
self.speedTextLabel.setObjectName("speedTextLabel")
|
||||||
|
self.timeTextLabel = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
self.timeTextLabel.setGeometry(QtCore.QRect(740, 536, 47, 13))
|
||||||
|
self.timeTextLabel.setObjectName("timeTextLabel")
|
||||||
|
self.volumeDial = QtWidgets.QDial(self.centralwidget)
|
||||||
|
self.volumeDial.setGeometry(QtCore.QRect(719, 0, 81, 81))
|
||||||
|
self.volumeDial.setStyleSheet("")
|
||||||
|
self.volumeDial.setMinimum(1)
|
||||||
|
self.volumeDial.setMaximum(100)
|
||||||
|
self.volumeDial.setPageStep(1)
|
||||||
|
self.volumeDial.setProperty("value", 100)
|
||||||
|
self.volumeDial.setObjectName("volumeDial")
|
||||||
|
self.VolDialBG = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
self.VolDialBG.setGeometry(QtCore.QRect(720, 0, 81, 81))
|
||||||
|
self.VolDialBG.setText("")
|
||||||
|
self.VolDialBG.setPixmap(QtGui.QPixmap("resources/img/MultiMate80x80.png"))
|
||||||
|
self.VolDialBG.setObjectName("VolDialBG")
|
||||||
|
self.playlistsComboBox = QtWidgets.QComboBox(self.centralwidget)
|
||||||
|
self.playlistsComboBox.setGeometry(QtCore.QRect(0, 0, 231, 22))
|
||||||
|
self.playlistsComboBox.setCurrentText("")
|
||||||
|
self.playlistsComboBox.setDuplicatesEnabled(True)
|
||||||
|
self.playlistsComboBox.setObjectName("playlistsComboBox")
|
||||||
|
for playlist in os.listdir(os.getcwd()):
|
||||||
|
self.playlistsComboBox.addItem(playlist)
|
||||||
|
self.nowPlaying = QtWidgets.QTextBrowser(self.centralwidget)
|
||||||
|
self.nowPlaying.setGeometry(QtCore.QRect(10, 460, 781, 23))
|
||||||
|
self.nowPlaying.setObjectName("nowPlaying")
|
||||||
|
self.openPlaylistButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.openPlaylistButton.setGeometry(QtCore.QRect(230, 0, 75, 23))
|
||||||
|
self.openPlaylistButton.setObjectName("openPlaylistButton")
|
||||||
|
|
||||||
|
self.songList = QtWidgets.QListWidget(self.centralwidget)
|
||||||
|
self.songList.setGeometry(QtCore.QRect(0, 43, 301, 411))
|
||||||
|
self.songList.setObjectName("songList")
|
||||||
|
|
||||||
|
self.TextAllSongs = QtWidgets.QTextEdit(self.centralwidget)
|
||||||
|
self.TextAllSongs.setGeometry(QtCore.QRect(0, 20, 301, 23))
|
||||||
|
self.TextAllSongs.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
|
||||||
|
self.TextAllSongs.setObjectName("TextAllSongs")
|
||||||
|
self.toFindName = QtWidgets.QTextEdit(self.centralwidget)
|
||||||
|
self.toFindName.setGeometry(QtCore.QRect(310, 0, 331, 23))
|
||||||
|
self.toFindName.setObjectName("toFindName")
|
||||||
|
self.findSongButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.findSongButton.setGeometry(QtCore.QRect(640, 0, 75, 23))
|
||||||
|
self.findSongButton.setObjectName("findSongButton")
|
||||||
|
self.foundSongs = QtWidgets.QComboBox(self.centralwidget)
|
||||||
|
self.foundSongs.setGeometry(QtCore.QRect(310, 30, 331, 22))
|
||||||
|
self.foundSongs.setObjectName("foundSongs")
|
||||||
|
self.addThisSongButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.addThisSongButton.setGeometry(QtCore.QRect(640, 30, 75, 23))
|
||||||
|
self.addThisSongButton.setObjectName("addThisSongButton")
|
||||||
|
self.mixButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.mixButton.setGeometry(QtCore.QRect(720, 90, 75, 41))
|
||||||
|
self.mixButton.setObjectName("mixButton")
|
||||||
|
|
||||||
|
self.playlistSettingsButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.playlistSettingsButton.setGeometry(QtCore.QRect(720, 140, 75, 51))
|
||||||
|
self.playlistSettingsButton.setObjectName("playlistSettingsButton")
|
||||||
|
|
||||||
|
self.settingsButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.settingsButton.setGeometry(QtCore.QRect(720, 200, 75, 45))
|
||||||
|
self.settingsButton.setObjectName("settingsButton")
|
||||||
|
|
||||||
|
self.extendedFunctButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.extendedFunctButton.setGeometry(QtCore.QRect(720, 260, 75, 45))
|
||||||
|
self.extendedFunctButton.setObjectName("extendedFunctButton")
|
||||||
|
|
||||||
|
if platform.system() == "Darwin": # for MacOS
|
||||||
|
self.videoframe = QtWidgets.QMacCocoaViewContainer(self.centralwidget)
|
||||||
|
else:
|
||||||
|
self.videoframe = QtWidgets.QFrame(self.centralwidget)
|
||||||
|
self.videoframe.setGeometry(QtCore.QRect(310, 60, 394, 394))
|
||||||
|
self.videoframe.setObjectName("videoframe")
|
||||||
|
self.timeline.raise_()
|
||||||
|
self.playpausePicture.raise_()
|
||||||
|
self.prevPicture.raise_()
|
||||||
|
self.nextPicture.raise_()
|
||||||
|
self.speedBox.raise_()
|
||||||
|
self.nextbutton.raise_()
|
||||||
|
self.prevbutton.raise_()
|
||||||
|
self.playpausebutton.raise_()
|
||||||
|
self.timenow.raise_()
|
||||||
|
self.speedTextLabel.raise_()
|
||||||
|
self.timeTextLabel.raise_()
|
||||||
|
self.VolDialBG.raise_()
|
||||||
|
self.volumeDial.raise_()
|
||||||
|
self.playlistsComboBox.raise_()
|
||||||
|
self.nowPlaying.raise_()
|
||||||
|
self.openPlaylistButton.raise_()
|
||||||
|
self.songList.raise_()
|
||||||
|
self.TextAllSongs.raise_()
|
||||||
|
self.toFindName.raise_()
|
||||||
|
self.findSongButton.raise_()
|
||||||
|
self.foundSongs.raise_()
|
||||||
|
self.addThisSongButton.raise_()
|
||||||
|
self.mixButton.raise_()
|
||||||
|
self.playlistSettingsButton.raise_()
|
||||||
|
self.videoframe.raise_()
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 801, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
self.timer = QtCore.QTimer(self)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "MultiMate Player"))
|
||||||
|
self.speedBox.setPrefix(_translate("MainWindow", "x"))
|
||||||
|
self.speedTextLabel.setText(_translate("MainWindow", "Speed"))
|
||||||
|
self.timeTextLabel.setText(_translate("MainWindow", "Time"))
|
||||||
|
self.nowPlaying.setPlaceholderText(_translate("MainWindow", "Choose playlist and open it..."))
|
||||||
|
self.openPlaylistButton.setText(_translate("MainWindow", "Open playlist"))
|
||||||
|
self.TextAllSongs.setHtml(_translate("MainWindow",
|
||||||
|
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
|
||||||
|
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
||||||
|
"p, li { white-space: pre-wrap; }\n"
|
||||||
|
"</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:8.25pt; font-weight:400; font-style:normal;\">\n"
|
||||||
|
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Songs in playlist</p></body></html>"))
|
||||||
|
self.findSongButton.setText(_translate("MainWindow", "Find song"))
|
||||||
|
self.addThisSongButton.setText(_translate("MainWindow", "Add this song"))
|
||||||
|
self.mixButton.setText(_translate("MainWindow", "Mix"))
|
||||||
|
self.playlistSettingsButton.setText(_translate("MainWindow", "Playlist \nsettings"))
|
||||||
|
self.settingsButton.setText(_translate("MainWindow", "Settings"))
|
||||||
|
self.extendedFunctButton.setText(_translate("MainWindow", "Extended \n functions"))
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_PlaylistSettings(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(160, 97)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(160, 97))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(160, 97))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
|
||||||
|
self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 160, 80))
|
||||||
|
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
|
||||||
|
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.deletesongButton = QtWidgets.QPushButton(self.verticalLayoutWidget)
|
||||||
|
self.deletesongButton.setObjectName("deletesongButton")
|
||||||
|
self.verticalLayout.addWidget(self.deletesongButton)
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 160, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "Playlist settings"))
|
||||||
|
self.deletesongButton.setText(_translate("MainWindow", "Manage songs in playlist"))
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_ProSearch(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(750, 600)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(750, 600))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(750, 600))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
|
||||||
|
self.listWidget.setGeometry(QtCore.QRect(0, 180, 750, 361))
|
||||||
|
self.listWidget.setObjectName("listWidget")
|
||||||
|
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
|
||||||
|
self.lineEdit.setGeometry(QtCore.QRect(160, 70, 440, 30))
|
||||||
|
self.lineEdit.setStyleSheet("border-radius:\n"
|
||||||
|
" 1 px;")
|
||||||
|
self.lineEdit.setReadOnly(False)
|
||||||
|
self.lineEdit.setObjectName("lineEdit")
|
||||||
|
self.label = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
self.label.setGeometry(QtCore.QRect(285, 20, 180, 31))
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.searchButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.searchButton.setGeometry(QtCore.QRect(300, 110, 150, 30))
|
||||||
|
self.searchButton.setObjectName("searchButton")
|
||||||
|
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.pushButton.setGeometry(QtCore.QRect(300, 550, 150, 31))
|
||||||
|
self.pushButton.setObjectName("pushButton")
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 750, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "Pro search"))
|
||||||
|
self.lineEdit.setPlaceholderText(_translate("MainWindow", "Never gonna give you up"))
|
||||||
|
self.label.setText(_translate("MainWindow",
|
||||||
|
"<html><head/><body><p><span style=\" font-size:18pt;\">Advanced search</span></p></body></html>"))
|
||||||
|
self.searchButton.setText(_translate("MainWindow", "Search"))
|
||||||
|
self.pushButton.setText(_translate("MainWindow", "Add selected"))
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
from resources.lib.config import config
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_RPCsettings(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(160, 100)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(160, 100))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(160, 100))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
|
||||||
|
self.verticalLayoutWidget.setGeometry(QtCore.QRect(9, 0, 151, 80))
|
||||||
|
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
|
||||||
|
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.ShowRPCcheckBox = QtWidgets.QCheckBox(self.verticalLayoutWidget)
|
||||||
|
self.ShowRPCcheckBox.setObjectName("ShowRPCcheckBox")
|
||||||
|
self.verticalLayout.addWidget(self.ShowRPCcheckBox)
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 160, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.ShowRPCcheckBox.setChecked(config['showrpc'])
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "RPC settings"))
|
||||||
|
self.ShowRPCcheckBox.setText(_translate("MainWindow", "Show RPC"))
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_Settings(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(160, 100)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(160, 100))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(160, 100))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
|
||||||
|
self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 160, 80))
|
||||||
|
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
|
||||||
|
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
|
||||||
|
self.updateButton = QtWidgets.QPushButton(self.verticalLayoutWidget)
|
||||||
|
self.updateButton.setObjectName("updateButton")
|
||||||
|
self.verticalLayout.addWidget(self.updateButton)
|
||||||
|
|
||||||
|
self.appBuildButton = QtWidgets.QPushButton(self.verticalLayoutWidget)
|
||||||
|
self.appBuildButton.setObjectName("appBuildButton")
|
||||||
|
self.verticalLayout.addWidget(self.appBuildButton)
|
||||||
|
|
||||||
|
self.RPCButton = QtWidgets.QPushButton(self.verticalLayoutWidget)
|
||||||
|
self.RPCButton.setObjectName("RPCButton")
|
||||||
|
self.verticalLayout.addWidget(self.RPCButton)
|
||||||
|
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 160, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "Settings"))
|
||||||
|
self.updateButton.setText(_translate("MainWindow", "Upgrade player"))
|
||||||
|
self.appBuildButton.setText(_translate("MainWindow", "Choose main build"))
|
||||||
|
self.RPCButton.setText(_translate("MainWindow", "Discord RPC settings"))
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_Updater(object):
|
||||||
|
def setupUi(self, MainWindow):
|
||||||
|
MainWindow.setObjectName("MainWindow")
|
||||||
|
MainWindow.resize(160, 97)
|
||||||
|
MainWindow.setMinimumSize(QtCore.QSize(160, 97))
|
||||||
|
MainWindow.setMaximumSize(QtCore.QSize(160, 97))
|
||||||
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
|
||||||
|
self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 160, 80))
|
||||||
|
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
|
||||||
|
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
|
||||||
|
self.updateButton = QtWidgets.QPushButton(self.verticalLayoutWidget)
|
||||||
|
self.updateButton.setObjectName("updateButton")
|
||||||
|
self.verticalLayout.addWidget(self.updateButton)
|
||||||
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 160, 21))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
|
||||||
|
self.retranslateUi(MainWindow)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
|
def retranslateUi(self, MainWindow):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
MainWindow.setWindowTitle(_translate("MainWindow", "Updater"))
|
||||||
|
self.updateButton.setText(_translate("MainWindow", "Download newest"))
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import sys
|
||||||
|
from PyQt5 import QtWidgets
|
||||||
|
from resources.gui.Ui_DelSongs import Ui_DelSongs
|
||||||
|
from resources.gui.Ui_ExtendedMenu import Ui_ExtendedMenu
|
||||||
|
from resources.gui.Ui_MainBuild import Ui_MainBuild
|
||||||
|
from resources.gui.Ui_MainWindow import Ui_MainWindow
|
||||||
|
from resources.gui.Ui_PlaylistSettings import Ui_PlaylistSettings
|
||||||
|
from resources.gui.Ui_ProSearch import Ui_ProSearch
|
||||||
|
from resources.gui.Ui_RPCsettings import Ui_RPCsettings
|
||||||
|
from resources.gui.Ui_Settings import Ui_Settings
|
||||||
|
from resources.gui.Ui_Updater import Ui_Updater
|
||||||
|
|
||||||
|
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindow = QtWidgets.QMainWindow()
|
||||||
|
ui = Ui_MainWindow()
|
||||||
|
ui.setupUi(MainWindow)
|
||||||
|
|
||||||
|
appPlSet = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowPlSet = QtWidgets.QMainWindow()
|
||||||
|
uiPlSet = Ui_PlaylistSettings()
|
||||||
|
uiPlSet.setupUi(MainWindowPlSet)
|
||||||
|
|
||||||
|
appDelS = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowDelS = QtWidgets.QMainWindow()
|
||||||
|
uiDelS = Ui_DelSongs()
|
||||||
|
uiDelS.setupUi(MainWindowDelS)
|
||||||
|
|
||||||
|
appSet = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowSet = QtWidgets.QMainWindow()
|
||||||
|
uiSet = Ui_Settings()
|
||||||
|
uiSet.setupUi(MainWindowSet)
|
||||||
|
|
||||||
|
appUpd = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowUpd = QtWidgets.QMainWindow()
|
||||||
|
uiUpd = Ui_Updater()
|
||||||
|
uiUpd.setupUi(MainWindowUpd)
|
||||||
|
|
||||||
|
appExt = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowExt = QtWidgets.QMainWindow()
|
||||||
|
uiExt = Ui_ExtendedMenu()
|
||||||
|
uiExt.setupUi(MainWindowExt)
|
||||||
|
|
||||||
|
appPSearch = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowPSearch = QtWidgets.QMainWindow()
|
||||||
|
uiPSearch = Ui_ProSearch()
|
||||||
|
uiPSearch.setupUi(MainWindowPSearch)
|
||||||
|
|
||||||
|
appRPCSet = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowRPCSet = QtWidgets.QMainWindow()
|
||||||
|
uiRPCSet = Ui_RPCsettings()
|
||||||
|
uiRPCSet.setupUi(MainWindowRPCSet)
|
||||||
|
|
||||||
|
appMainBuild = QtWidgets.QApplication(sys.argv)
|
||||||
|
MainWindowMainBuild = QtWidgets.QMainWindow()
|
||||||
|
uiMainBuild = Ui_MainBuild()
|
||||||
|
uiMainBuild.setupUi(MainWindowMainBuild)
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,32 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
configfile = "resources/cfg.cfg"
|
||||||
|
|
||||||
|
if not os.path.isfile(configfile):
|
||||||
|
cfgwrite = open(configfile, 'w+')
|
||||||
|
empty = {}
|
||||||
|
json.dump(empty, cfgwrite, indent=3)
|
||||||
|
cfgwrite.close()
|
||||||
|
|
||||||
|
with open(configfile) as cfgread:
|
||||||
|
config = json.load(cfgread)
|
||||||
|
|
||||||
|
try:
|
||||||
|
config['mainbuild']
|
||||||
|
except:
|
||||||
|
if "MultiMate_Player.py" in os.listdir(os.getcwd()):
|
||||||
|
config['mainbuild'] = 'MultiMate_Player.py'
|
||||||
|
elif "MultiMate_Player.pyw" in os.listdir(os.getcwd()):
|
||||||
|
config['mainbuild'] = 'MultiMate_Player.pyw'
|
||||||
|
elif "MultiMate_Player.exe" in os.listdir(os.getcwd()):
|
||||||
|
config['mainbuild'] = 'MultiMate_Player.exe'
|
||||||
|
with open(configfile, 'w+') as cfgwrite:
|
||||||
|
json.dump(config, cfgwrite, indent=3)
|
||||||
|
|
||||||
|
try:
|
||||||
|
config['showrpc']
|
||||||
|
except:
|
||||||
|
config['showrpc'] = True
|
||||||
|
with open(configfile, 'w+') as cfgwrite:
|
||||||
|
json.dump(config, cfgwrite, indent=3)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def cls():
|
||||||
|
os.system('cls' if os.name == 'nt' else 'clear')
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from pypresence import Presence
|
||||||
|
from resources.lib.config import config
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
rpc = Presence("896669007342633000")
|
||||||
|
try:
|
||||||
|
rpc.connect()
|
||||||
|
if config['showrpc']:
|
||||||
|
rpc.update(details="Just started app", state="Nothing is beeing listened...", large_image="multimate",
|
||||||
|
start=int(time.time()))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubeSearch:
|
||||||
|
def __init__(self, search_terms: str, max_results=None):
|
||||||
|
self.search_terms = search_terms
|
||||||
|
self.max_results = max_results
|
||||||
|
self.videos = self._search()
|
||||||
|
|
||||||
|
def _search(self):
|
||||||
|
encoded_search = urllib.parse.quote_plus(self.search_terms)
|
||||||
|
BASE_URL = "https://youtube.com"
|
||||||
|
url = f"{BASE_URL}/search?q={encoded_search}"
|
||||||
|
response = requests.get(url).text
|
||||||
|
while "ytInitialData" not in response:
|
||||||
|
response = requests.get(url).text
|
||||||
|
results = self._parse_html(response)
|
||||||
|
if self.max_results is not None and len(results) > self.max_results:
|
||||||
|
return results[: self.max_results]
|
||||||
|
return results
|
||||||
|
|
||||||
|
def _parse_html(self, response):
|
||||||
|
results = []
|
||||||
|
start = (
|
||||||
|
response.index("ytInitialData")
|
||||||
|
+ len("ytInitialData")
|
||||||
|
+ 3
|
||||||
|
)
|
||||||
|
end = response.index("};", start) + 1
|
||||||
|
json_str = response[start:end]
|
||||||
|
data = json.loads(json_str)
|
||||||
|
|
||||||
|
videos = data["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"][
|
||||||
|
"sectionListRenderer"
|
||||||
|
]["contents"][0]["itemSectionRenderer"]["contents"]
|
||||||
|
|
||||||
|
for video in videos:
|
||||||
|
res = {}
|
||||||
|
if "videoRenderer" in video.keys():
|
||||||
|
video_data = video.get("videoRenderer", {})
|
||||||
|
res["id"] = video_data.get("videoId", None)
|
||||||
|
res["thumbnails"] = [thumb.get("url", None) for thumb in video_data.get("thumbnail", {}).get("thumbnails", [{}]) ]
|
||||||
|
res["title"] = video_data.get("title", {}).get("runs", [[{}]])[0].get("text", None)
|
||||||
|
res["long_desc"] = video_data.get("descriptionSnippet", {}).get("runs", [{}])[0].get("text", None)
|
||||||
|
res["channel"] = video_data.get("longBylineText", {}).get("runs", [[{}]])[0].get("text", None)
|
||||||
|
res["duration"] = video_data.get("lengthText", {}).get("simpleText", 0)
|
||||||
|
res["views"] = video_data.get("viewCountText", {}).get("simpleText", 0)
|
||||||
|
res["publish_time"] = video_data.get("publishedTimeText", {}).get("simpleText", 0)
|
||||||
|
res["url_suffix"] = video_data.get("navigationEndpoint", {}).get("commandMetadata", {}).get("webCommandMetadata", {}).get("url", None)
|
||||||
|
results.append(res)
|
||||||
|
return results
|
||||||
|
|
||||||
|
def to_dict(self, clear_cache=True):
|
||||||
|
result = self.videos
|
||||||
|
if clear_cache:
|
||||||
|
self.videos = ""
|
||||||
|
return result
|
||||||
|
|
||||||
|
def to_json(self, clear_cache=True):
|
||||||
|
result = json.dumps({"videos": self.videos})
|
||||||
|
if clear_cache:
|
||||||
|
self.videos = ""
|
||||||
|
return result
|
||||||
@@ -0,0 +1,558 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
if sys.version_info[:2] >= (3, 0):
|
||||||
|
# pylint: disable=E0611,F0401,I0011
|
||||||
|
from urllib.request import urlopen, build_opener
|
||||||
|
from urllib.error import HTTPError, URLError
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
uni, pyver = str, 3
|
||||||
|
|
||||||
|
else:
|
||||||
|
from urllib2 import urlopen, build_opener, HTTPError, URLError
|
||||||
|
from urlparse import parse_qs, urlparse
|
||||||
|
uni, pyver = unicode, 2
|
||||||
|
|
||||||
|
early_py_version = sys.version_info[:2] < (2, 7)
|
||||||
|
|
||||||
|
dbg = logging.debug
|
||||||
|
|
||||||
|
|
||||||
|
def extract_video_id(url):
|
||||||
|
""" Extract the video id from a url, return video id as str. """
|
||||||
|
idregx = re.compile(r'[\w-]{11}$')
|
||||||
|
url = str(url).strip()
|
||||||
|
|
||||||
|
if idregx.match(url):
|
||||||
|
return url # ID of video
|
||||||
|
|
||||||
|
if '://' not in url:
|
||||||
|
url = '//' + url
|
||||||
|
parsedurl = urlparse(url)
|
||||||
|
if parsedurl.netloc in ('youtube.com', 'www.youtube.com', 'm.youtube.com', 'gaming.youtube.com'):
|
||||||
|
query = parse_qs(parsedurl.query)
|
||||||
|
if 'v' in query and idregx.match(query['v'][0]):
|
||||||
|
return query['v'][0]
|
||||||
|
elif parsedurl.netloc in ('youtu.be', 'www.youtu.be'):
|
||||||
|
vidid = parsedurl.path.split('/')[-1] if parsedurl.path else ''
|
||||||
|
if idregx.match(vidid):
|
||||||
|
return vidid
|
||||||
|
|
||||||
|
err = "Need 11 character video id or the URL of the video. Got %s"
|
||||||
|
raise ValueError(err % url)
|
||||||
|
|
||||||
|
|
||||||
|
class BasePafy(object):
|
||||||
|
|
||||||
|
""" Class to represent a YouTube video. """
|
||||||
|
|
||||||
|
def __init__(self, video_url, basic=True, gdata=False,
|
||||||
|
size=False, callback=None, ydl_opts=None):
|
||||||
|
""" Set initial values. """
|
||||||
|
self.version = 1
|
||||||
|
self.videoid = extract_video_id(video_url)
|
||||||
|
self.watchv_url = "http://www.youtube.com/watch?v=%s" % self.videoid
|
||||||
|
|
||||||
|
self.callback = callback
|
||||||
|
self._have_basic = False
|
||||||
|
self._have_gdata = False
|
||||||
|
|
||||||
|
self._description = None
|
||||||
|
self._likes = None
|
||||||
|
self._dislikes = None
|
||||||
|
self._category = None
|
||||||
|
self._published = None
|
||||||
|
self._username = None
|
||||||
|
|
||||||
|
self._streams = []
|
||||||
|
self._oggstreams = []
|
||||||
|
self._m4astreams = []
|
||||||
|
self._allstreams = []
|
||||||
|
self._videostreams = []
|
||||||
|
self._audiostreams = []
|
||||||
|
|
||||||
|
self._title = None
|
||||||
|
self._rating = None
|
||||||
|
self._length = None
|
||||||
|
self._author = None
|
||||||
|
self._duration = None
|
||||||
|
self._keywords = None
|
||||||
|
self._bigthumb = None
|
||||||
|
self._viewcount = None
|
||||||
|
self._bigthumbhd = None
|
||||||
|
self._bestthumb = None
|
||||||
|
self._mix_pl = None
|
||||||
|
self.expiry = None
|
||||||
|
|
||||||
|
if basic:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
if gdata:
|
||||||
|
self._fetch_gdata()
|
||||||
|
|
||||||
|
if size:
|
||||||
|
for s in self.allstreams:
|
||||||
|
# pylint: disable=W0104
|
||||||
|
s.get_filesize()
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_basic(self):
|
||||||
|
""" Fetch basic data and streams. """
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_gdata(self):
|
||||||
|
""" Extract gdata values, fetch gdata if necessary. """
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
def _process_streams(self):
|
||||||
|
""" Create Stream object lists from internal stream maps. """
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
""" Print video metadata. Return utf8 string. """
|
||||||
|
if self._have_basic:
|
||||||
|
info = [("Title", self.title),
|
||||||
|
("Author", self.author),
|
||||||
|
("ID", self.videoid),
|
||||||
|
("Duration", self.duration),
|
||||||
|
("Rating", self.rating),
|
||||||
|
("Views", self.viewcount)]
|
||||||
|
|
||||||
|
nfo = "\n".join(["%s: %s" % i for i in info])
|
||||||
|
|
||||||
|
else:
|
||||||
|
nfo = "Pafy object: %s [%s]" % (self.videoid,
|
||||||
|
self.title[:45] + "..")
|
||||||
|
|
||||||
|
return nfo.encode("utf8", "replace") if pyver == 2 else nfo
|
||||||
|
|
||||||
|
@property
|
||||||
|
def streams(self):
|
||||||
|
""" The streams for a video. Returns list."""
|
||||||
|
if not self._streams:
|
||||||
|
self._process_streams()
|
||||||
|
|
||||||
|
return self._streams
|
||||||
|
|
||||||
|
@property
|
||||||
|
def allstreams(self):
|
||||||
|
""" All stream types for a video. Returns list. """
|
||||||
|
if not self._allstreams:
|
||||||
|
self._process_streams()
|
||||||
|
|
||||||
|
return self._allstreams
|
||||||
|
|
||||||
|
@property
|
||||||
|
def audiostreams(self):
|
||||||
|
""" Return a list of audio Stream objects. """
|
||||||
|
if not self._audiostreams:
|
||||||
|
self._process_streams()
|
||||||
|
|
||||||
|
return self._audiostreams
|
||||||
|
|
||||||
|
@property
|
||||||
|
def videostreams(self):
|
||||||
|
""" The video streams for a video. Returns list. """
|
||||||
|
if not self._videostreams:
|
||||||
|
self._process_streams()
|
||||||
|
|
||||||
|
return self._videostreams
|
||||||
|
|
||||||
|
@property
|
||||||
|
def oggstreams(self):
|
||||||
|
""" Return a list of ogg encoded Stream objects. """
|
||||||
|
if not self._oggstreams:
|
||||||
|
self._process_streams()
|
||||||
|
|
||||||
|
return self._oggstreams
|
||||||
|
|
||||||
|
@property
|
||||||
|
def m4astreams(self):
|
||||||
|
""" Return a list of m4a encoded Stream objects. """
|
||||||
|
if not self._m4astreams:
|
||||||
|
self._process_streams()
|
||||||
|
|
||||||
|
return self._m4astreams
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title(self):
|
||||||
|
""" Return YouTube video title as a string. """
|
||||||
|
if not self._title:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._title
|
||||||
|
|
||||||
|
@property
|
||||||
|
def author(self):
|
||||||
|
""" The uploader of the video. Returns str. """
|
||||||
|
if not self._author:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._author
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rating(self):
|
||||||
|
""" Rating for a video. Returns float. """
|
||||||
|
if not self._rating:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._rating
|
||||||
|
|
||||||
|
@property
|
||||||
|
def length(self):
|
||||||
|
""" Length of a video in seconds. Returns int. """
|
||||||
|
if not self._length:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._length
|
||||||
|
|
||||||
|
@property
|
||||||
|
def viewcount(self):
|
||||||
|
""" Number of views for a video. Returns int. """
|
||||||
|
if not self._viewcount:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._viewcount
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bigthumb(self):
|
||||||
|
""" Large thumbnail image url. Returns str. """
|
||||||
|
self._fetch_basic()
|
||||||
|
return self._bigthumb
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bigthumbhd(self):
|
||||||
|
""" Extra large thumbnail image url. Returns str. """
|
||||||
|
self._fetch_basic()
|
||||||
|
return self._bigthumbhd
|
||||||
|
|
||||||
|
@property
|
||||||
|
def duration(self):
|
||||||
|
""" Duration of a video (HH:MM:SS). Returns str. """
|
||||||
|
if not self._length:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
self._duration = time.strftime('%H:%M:%S', time.gmtime(self._length))
|
||||||
|
self._duration = uni(self._duration)
|
||||||
|
|
||||||
|
return self._duration
|
||||||
|
|
||||||
|
@property
|
||||||
|
def keywords(self):
|
||||||
|
""" Return keywords as list of str. """
|
||||||
|
if not self._keywords:
|
||||||
|
self._fetch_gdata()
|
||||||
|
|
||||||
|
return self._keywords
|
||||||
|
|
||||||
|
@property
|
||||||
|
def category(self):
|
||||||
|
""" YouTube category of the video. Returns string. """
|
||||||
|
if not self._category:
|
||||||
|
self._fetch_gdata()
|
||||||
|
|
||||||
|
return self._category
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
""" Description of the video. Returns string. """
|
||||||
|
if not self._description:
|
||||||
|
self._fetch_gdata()
|
||||||
|
|
||||||
|
return self._description
|
||||||
|
|
||||||
|
@property
|
||||||
|
def username(self):
|
||||||
|
""" Return the username of the uploader. """
|
||||||
|
if not self._username:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._username
|
||||||
|
|
||||||
|
@property
|
||||||
|
def published(self):
|
||||||
|
""" The upload date and time of the video. Returns string. """
|
||||||
|
if not self._published:
|
||||||
|
self._fetch_gdata()
|
||||||
|
|
||||||
|
return self._published.replace(".000Z", "").replace("T", " ")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def likes(self):
|
||||||
|
""" The number of likes for the video. Returns int. """
|
||||||
|
if not self._likes:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._likes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dislikes(self):
|
||||||
|
""" The number of dislikes for the video. Returns int. """
|
||||||
|
if not self._dislikes:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
return self._dislikes
|
||||||
|
|
||||||
|
def _getbest(self, preftype="any", ftypestrict=True, vidonly=False):
|
||||||
|
"""
|
||||||
|
Return the highest resolution video available.
|
||||||
|
|
||||||
|
Select from video-only streams if vidonly is True
|
||||||
|
"""
|
||||||
|
streams = self.videostreams if vidonly else self.streams
|
||||||
|
|
||||||
|
if not streams:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _sortkey(x, key3d=0, keyres=0, keyftype=0):
|
||||||
|
""" sort function for max(). """
|
||||||
|
key3d = "3D" not in x.resolution
|
||||||
|
keyres = int(x.resolution.split("x")[0])
|
||||||
|
keyftype = preftype == x.extension
|
||||||
|
strict = (key3d, keyftype, keyres)
|
||||||
|
nonstrict = (key3d, keyres, keyftype)
|
||||||
|
return strict if ftypestrict else nonstrict
|
||||||
|
|
||||||
|
r = max(streams, key=_sortkey)
|
||||||
|
|
||||||
|
if ftypestrict and preftype != "any" and r.extension != preftype:
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
|
return r
|
||||||
|
|
||||||
|
def getbestvideo(self, preftype="any", ftypestrict=True):
|
||||||
|
"""
|
||||||
|
Return the best resolution video-only stream.
|
||||||
|
|
||||||
|
set ftypestrict to False to return a non-preferred format if that
|
||||||
|
has a higher resolution
|
||||||
|
"""
|
||||||
|
return self._getbest(preftype, ftypestrict, vidonly=True)
|
||||||
|
|
||||||
|
def getbest(self, preftype="any", ftypestrict=True):
|
||||||
|
"""
|
||||||
|
Return the highest resolution video+audio stream.
|
||||||
|
|
||||||
|
set ftypestrict to False to return a non-preferred format if that
|
||||||
|
has a higher resolution
|
||||||
|
"""
|
||||||
|
return self._getbest(preftype, ftypestrict, vidonly=False)
|
||||||
|
|
||||||
|
def getbestaudio(self, preftype="any", ftypestrict=True):
|
||||||
|
""" Return the highest bitrate audio Stream object."""
|
||||||
|
if not self.audiostreams:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _sortkey(x, keybitrate=0, keyftype=0):
|
||||||
|
""" Sort function for max(). """
|
||||||
|
keybitrate = int(x.rawbitrate)
|
||||||
|
keyftype = preftype == x.extension
|
||||||
|
strict, nonstrict = (keyftype, keybitrate), (keybitrate, keyftype)
|
||||||
|
return strict if ftypestrict else nonstrict
|
||||||
|
|
||||||
|
r = max(self.audiostreams, key=_sortkey)
|
||||||
|
|
||||||
|
if ftypestrict and preftype != "any" and r.extension != preftype:
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
|
return r
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _content_available(cls, url):
|
||||||
|
try:
|
||||||
|
response = urlopen(url)
|
||||||
|
except HTTPError:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return response.getcode() < 300
|
||||||
|
|
||||||
|
def getbestthumb(self):
|
||||||
|
""" Return the best available thumbnail."""
|
||||||
|
if not self._bestthumb:
|
||||||
|
part_url = "http://i.ytimg.com/vi/%s/" % self.videoid
|
||||||
|
# Thumbnail resolution sorted in descending order
|
||||||
|
thumbs = ("maxresdefault.jpg",
|
||||||
|
"sddefault.jpg",
|
||||||
|
"hqdefault.jpg",
|
||||||
|
"mqdefault.jpg",
|
||||||
|
"default.jpg")
|
||||||
|
for thumb in thumbs:
|
||||||
|
url = part_url + thumb
|
||||||
|
if self._content_available(url):
|
||||||
|
return url
|
||||||
|
|
||||||
|
return self._bestthumb
|
||||||
|
|
||||||
|
def populate_from_playlist(self, pl_data):
|
||||||
|
""" Populate Pafy object with items fetched from playlist data. """
|
||||||
|
self._title = pl_data.get("title")
|
||||||
|
self._author = pl_data.get("author")
|
||||||
|
self._length = int(pl_data.get("length_seconds", 0))
|
||||||
|
self._rating = pl_data.get("rating", 0.0)
|
||||||
|
self._viewcount = "".join(re.findall(r"\d", "{0}".format(pl_data.get("views", "0"))))
|
||||||
|
self._viewcount = int(self._viewcount)
|
||||||
|
self._description = pl_data.get("description")
|
||||||
|
|
||||||
|
|
||||||
|
class BaseStream(object):
|
||||||
|
|
||||||
|
""" YouTube video stream class. """
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
""" Set initial values. """
|
||||||
|
self._itag = None
|
||||||
|
self._mediatype = None
|
||||||
|
self._threed = None
|
||||||
|
self._rawbitrate = None
|
||||||
|
self._resolution = None
|
||||||
|
self._quality = None
|
||||||
|
self._dimensions = None
|
||||||
|
self._bitrate = None
|
||||||
|
self._extension = None
|
||||||
|
self.encrypted = None
|
||||||
|
self._notes = None
|
||||||
|
self._url = None
|
||||||
|
self._rawurl = None
|
||||||
|
|
||||||
|
self._parent = parent
|
||||||
|
self._filename = None
|
||||||
|
self._fsize = None
|
||||||
|
self._active = False
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rawbitrate(self):
|
||||||
|
""" Return raw bitrate value. """
|
||||||
|
return self._rawbitrate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def threed(self):
|
||||||
|
""" Return bool, True if stream is 3D. """
|
||||||
|
return self._threed
|
||||||
|
|
||||||
|
@property
|
||||||
|
def itag(self):
|
||||||
|
""" Return itag value of stream. """
|
||||||
|
return self._itag
|
||||||
|
|
||||||
|
@property
|
||||||
|
def resolution(self):
|
||||||
|
""" Return resolution of stream as str. 0x0 if audio. """
|
||||||
|
return self._resolution
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dimensions(self):
|
||||||
|
""" Return dimensions of stream as tuple. (0, 0) if audio. """
|
||||||
|
return self._dimensions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def quality(self):
|
||||||
|
""" Return quality of stream (bitrate or resolution).
|
||||||
|
|
||||||
|
eg, 128k or 640x480 (str)
|
||||||
|
"""
|
||||||
|
return self._quality
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title(self):
|
||||||
|
""" Return YouTube video title as a string. """
|
||||||
|
return self._parent.title
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extension(self):
|
||||||
|
""" Return appropriate file extension for stream (str).
|
||||||
|
|
||||||
|
Possible values are: 3gp, m4a, m4v, mp4, webm, ogg
|
||||||
|
"""
|
||||||
|
return self._extension
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bitrate(self):
|
||||||
|
""" Return bitrate of an audio stream. """
|
||||||
|
return self._bitrate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mediatype(self):
|
||||||
|
""" Return mediatype string (normal, audio or video).
|
||||||
|
|
||||||
|
(normal means a stream containing both video and audio.)
|
||||||
|
"""
|
||||||
|
return self._mediatype
|
||||||
|
|
||||||
|
@property
|
||||||
|
def notes(self):
|
||||||
|
""" Return additional notes regarding the stream format. """
|
||||||
|
return self._notes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url(self):
|
||||||
|
""" Return the url, decrypt if required. """
|
||||||
|
return self._url
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_https(self):
|
||||||
|
""" Return https url. """
|
||||||
|
return self.url.replace("http://", "https://")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
""" Return string representation. """
|
||||||
|
out = "%s:%s@%s" % (self.mediatype, self.extension, self.quality)
|
||||||
|
return out
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
""" Cancel an active download. """
|
||||||
|
if self._active:
|
||||||
|
self._active = False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def remux(infile, outfile, quiet=False, muxer="ffmpeg"):
|
||||||
|
""" Remux audio. """
|
||||||
|
muxer = muxer if isinstance(muxer, str) else "ffmpeg"
|
||||||
|
|
||||||
|
for tool in set([muxer, "ffmpeg", "avconv"]):
|
||||||
|
cmd = [tool, "-y", "-i", infile, "-acodec", "copy", "-vn", outfile]
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(os.devnull, "w") as devnull:
|
||||||
|
subprocess.call(cmd, stdout=devnull, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
except OSError:
|
||||||
|
dbg("Failed to remux audio using %s", tool)
|
||||||
|
|
||||||
|
else:
|
||||||
|
os.unlink(infile)
|
||||||
|
dbg("remuxed audio file using %s" % tool)
|
||||||
|
|
||||||
|
if not quiet:
|
||||||
|
sys.stdout.write("\nAudio remuxed.\n")
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
logging.warning("audio remux failed")
|
||||||
|
os.rename(infile, outfile)
|
||||||
|
|
||||||
|
|
||||||
|
def get_size_done(bytesdone, progress):
|
||||||
|
_progress_dict = {'KB': 1024.0, 'MB': 1048576.0, 'GB': 1073741824.0}
|
||||||
|
return round(bytesdone/_progress_dict.get(progress, 1.0), 2)
|
||||||
|
|
||||||
|
|
||||||
|
def get_status_string(progress):
|
||||||
|
status_string = (' {:,} ' + progress + ' [{:.2%}] received. Rate: [{:4.0f} '
|
||||||
|
'KB/s]. ETA: [{:.0f} secs]')
|
||||||
|
|
||||||
|
if early_py_version:
|
||||||
|
status_string = (' {0:} ' + progress + ' [{1:.2%}] received. Rate:'
|
||||||
|
' [{2:4.0f} KB/s]. ETA: [{3:.0f} secs]')
|
||||||
|
|
||||||
|
return status_string
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
if sys.version_info[:2] >= (3, 0):
|
||||||
|
# pylint: disable=E0611,F0401,I0011
|
||||||
|
uni = str
|
||||||
|
else:
|
||||||
|
uni = unicode
|
||||||
|
|
||||||
|
import youtube_dl
|
||||||
|
|
||||||
|
from .backend_shared import BasePafy, BaseStream, remux, get_status_string, get_size_done
|
||||||
|
|
||||||
|
dbg = logging.debug
|
||||||
|
|
||||||
|
|
||||||
|
early_py_version = sys.version_info[:2] < (2, 7)
|
||||||
|
|
||||||
|
|
||||||
|
class YtdlPafy(BasePafy):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._ydl_info = None
|
||||||
|
self._ydl_opts = {'quiet': True, 'prefer_insecure': False, 'no_warnings': True}
|
||||||
|
ydl_opts = kwargs.get("ydl_opts")
|
||||||
|
if ydl_opts:
|
||||||
|
self._ydl_opts.update(ydl_opts)
|
||||||
|
super(YtdlPafy, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _fetch_basic(self):
|
||||||
|
""" Fetch basic data and streams. """
|
||||||
|
if self._have_basic:
|
||||||
|
return
|
||||||
|
|
||||||
|
with youtube_dl.YoutubeDL(self._ydl_opts) as ydl:
|
||||||
|
try:
|
||||||
|
self._ydl_info = ydl.extract_info(self.videoid, download=False)
|
||||||
|
# Turn into an IOError since that is what pafy previously raised
|
||||||
|
except youtube_dl.utils.DownloadError as e:
|
||||||
|
raise IOError(str(e).replace('YouTube said', 'Youtube says'))
|
||||||
|
|
||||||
|
if self.callback:
|
||||||
|
self.callback("Fetched video info")
|
||||||
|
|
||||||
|
self._title = self._ydl_info['title']
|
||||||
|
self._author = self._ydl_info['uploader']
|
||||||
|
self._rating = self._ydl_info['average_rating']
|
||||||
|
self._length = self._ydl_info['duration']
|
||||||
|
self._viewcount = self._ydl_info['view_count']
|
||||||
|
self._username = self._ydl_info['uploader_id']
|
||||||
|
self._category = self._ydl_info['categories'][0] if self._ydl_info['categories'] else ''
|
||||||
|
self._bestthumb = self._ydl_info['thumbnails'][0]['url']
|
||||||
|
self._bigthumb = "http://i.ytimg.com/vi/%s/mqdefault.jpg" % self.videoid
|
||||||
|
self._bigthumbhd = "http://i.ytimg.com/vi/%s/hqdefault.jpg" % self.videoid
|
||||||
|
self.expiry = time.time() + 60 * 60 * 5
|
||||||
|
|
||||||
|
self._have_basic = True
|
||||||
|
|
||||||
|
def _fetch_gdata(self):
|
||||||
|
""" Extract gdata values, fetch gdata if necessary. """
|
||||||
|
if self._have_gdata:
|
||||||
|
return
|
||||||
|
|
||||||
|
item = self._get_video_gdata(self.videoid)['items'][0]
|
||||||
|
snippet = item['snippet']
|
||||||
|
self._published = uni(snippet['publishedAt'])
|
||||||
|
self._description = uni(snippet["description"])
|
||||||
|
# Note: using snippet.get since some videos have no tags object
|
||||||
|
self._keywords = [uni(i) for i in snippet.get('tags', ())]
|
||||||
|
self._have_gdata = True
|
||||||
|
|
||||||
|
def _process_streams(self):
|
||||||
|
""" Create Stream object lists from internal stream maps. """
|
||||||
|
|
||||||
|
if not self._have_basic:
|
||||||
|
self._fetch_basic()
|
||||||
|
|
||||||
|
allstreams = [YtdlStream(z, self) for z in self._ydl_info['formats']]
|
||||||
|
self._streams = [i for i in allstreams if i.mediatype == 'normal']
|
||||||
|
self._audiostreams = [i for i in allstreams if i.mediatype == 'audio']
|
||||||
|
self._videostreams = [i for i in allstreams if i.mediatype == 'video']
|
||||||
|
self._m4astreams = [i for i in allstreams if i.extension == 'm4a']
|
||||||
|
self._oggstreams = [i for i in allstreams if i.extension == 'ogg']
|
||||||
|
self._allstreams = allstreams
|
||||||
|
|
||||||
|
|
||||||
|
class YtdlStream(BaseStream):
|
||||||
|
def __init__(self, info, parent):
|
||||||
|
super(YtdlStream, self).__init__(parent)
|
||||||
|
self._itag = info['format_id']
|
||||||
|
|
||||||
|
if (info.get('acodec') != 'none' and
|
||||||
|
info.get('vcodec') == 'none'):
|
||||||
|
self._mediatype = 'audio'
|
||||||
|
elif (info.get('acodec') == 'none' and
|
||||||
|
info.get('vcodec') != 'none'):
|
||||||
|
self._mediatype = 'video'
|
||||||
|
else:
|
||||||
|
self._mediatype = 'normal'
|
||||||
|
|
||||||
|
self._threed = info.get('format_note') == '3D'
|
||||||
|
self._rawbitrate = info.get('abr', 0) * 1024
|
||||||
|
|
||||||
|
height = info.get('height') or 0
|
||||||
|
width = info.get('width') or 0
|
||||||
|
self._resolution = str(width) + 'x' + str(height)
|
||||||
|
self._dimensions = width, height
|
||||||
|
self._bitrate = str(info.get('abr', 0)) + 'k'
|
||||||
|
self._quality = self._bitrate if self._mediatype == 'audio' else self._resolution
|
||||||
|
|
||||||
|
self._extension = info['ext']
|
||||||
|
self._notes = info.get('format_note') or ''
|
||||||
|
self._url = info.get('url')
|
||||||
|
|
||||||
|
self._info = info
|
||||||
|
|
||||||
|
def get_filesize(self):
|
||||||
|
""" Return filesize of the stream in bytes. Set member variable. """
|
||||||
|
|
||||||
|
# Faster method
|
||||||
|
if 'filesize' in self._info and self._info['filesize'] is not None:
|
||||||
|
return self._info['filesize']
|
||||||
|
|
||||||
|
# Fallback
|
||||||
|
return super(YtdlStream, self).get_filesize()
|
||||||
|
|
||||||
|
def download(self, filepath="", quiet=False, progress="Bytes",
|
||||||
|
callback=None, meta=False, remux_audio=False):
|
||||||
|
|
||||||
|
downloader = youtube_dl.downloader.http.HttpFD(ydl(),
|
||||||
|
{'http_chunk_size': 10485760})
|
||||||
|
|
||||||
|
progress_available = ["KB", "MB", "GB"]
|
||||||
|
if progress not in progress_available:
|
||||||
|
progress = "Bytes"
|
||||||
|
|
||||||
|
status_string = get_status_string(progress)
|
||||||
|
|
||||||
|
def progress_hook(s):
|
||||||
|
if s['status'] == 'downloading':
|
||||||
|
bytesdone = s['downloaded_bytes']
|
||||||
|
total = s['total_bytes']
|
||||||
|
if s.get('speed') is not None:
|
||||||
|
rate = s['speed'] / 1024
|
||||||
|
else:
|
||||||
|
rate = 0
|
||||||
|
if s.get('eta') is None:
|
||||||
|
eta = 0
|
||||||
|
else:
|
||||||
|
eta = s['eta']
|
||||||
|
|
||||||
|
progress_stats = (get_size_done(bytesdone, progress),
|
||||||
|
bytesdone*1.0/total, rate, eta)
|
||||||
|
if not quiet:
|
||||||
|
status = status_string.format(*progress_stats)
|
||||||
|
sys.stdout.write("\r" + status + ' ' * 4 + "\r")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
if callback:
|
||||||
|
callback(total, *progress_stats)
|
||||||
|
|
||||||
|
downloader._progress_hooks = [progress_hook]
|
||||||
|
|
||||||
|
if filepath and os.path.isdir(filepath):
|
||||||
|
filename = self.generate_filename(max_length=256 - len('.temp'))
|
||||||
|
filepath = os.path.join(filepath, filename)
|
||||||
|
|
||||||
|
elif filepath:
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
filepath = self.generate_filename(meta=meta, max_length=256 - len('.temp'))
|
||||||
|
|
||||||
|
infodict = {'url': self.url}
|
||||||
|
|
||||||
|
downloader.download(filepath, infodict)
|
||||||
|
print()
|
||||||
|
|
||||||
|
if remux_audio and self.mediatype == "audio":
|
||||||
|
subprocess.run(['mv', filepath, filepath + '.temp'])
|
||||||
|
remux(filepath + '.temp', filepath, quiet=quiet, muxer=remux_audio)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
Pafy = None
|
||||||
|
|
||||||
|
def new(url, basic=True, gdata=False, size=False,
|
||||||
|
callback=None, ydl_opts=None):
|
||||||
|
global Pafy
|
||||||
|
if Pafy is None:
|
||||||
|
from .backend_youtube_dl import YtdlPafy as Pafy
|
||||||
|
|
||||||
|
return Pafy(url, basic, gdata, size, callback, ydl_opts=ydl_opts)
|
||||||
Reference in new issue
Block a user