src/videoareawidget.h \
src/googlesuggest.h \
src/videowidget.h \
- src/flickcharm.h
+ src/flickcharm.h \
+ src/videodefinition.h
SOURCES += src/main.cpp \
src/MainWindow.cpp \
src/SearchView.cpp \
src/videoareawidget.cpp \
src/googlesuggest.cpp \
src/videowidget.cpp \
- src/flickcharm.cpp
+ src/flickcharm.cpp \
+ src/videodefinition.cpp
RESOURCES += resources.qrc
DESTDIR = build/target/
OBJECTS_DIR = build/obj/
#include "Constants.h"
#include "iconloader/qticonloader.h"
#include "global.h"
+#include "videodefinition.h"
MainWindow::MainWindow() :
mediaObject(0),
audioOutput(0),
- aboutView(0) {
-
- m_fullscreen = false;
+ aboutView(0),
+ m_fullscreen(false) {
// views mechanism
history = new QStack<QWidget*>();
actions->insert("site", siteAct);
connect(siteAct, SIGNAL(triggered()), this, SLOT(visitSite()));
- donateAct = new QAction(tr("&Donate"), this);
+ donateAct = new QAction(tr("Make a &donation"), this);
donateAct->setStatusTip(tr("Please support the continued development of %1").arg(Constants::APP_NAME));
actions->insert("donate", donateAct);
connect(donateAct, SIGNAL(triggered()), this, SLOT(donate()));
connect(volumeMuteAct, SIGNAL(triggered()), this, SLOT(volumeMute()));
addAction(volumeMuteAct);
- QAction *hdAct = new QAction(this);
- hdAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_D));
- hdAct->setIcon(createHDIcon());
- hdAct->setCheckable(true);
- actions->insert("hd", hdAct);
- connect(hdAct, SIGNAL(toggled(bool)), this, SLOT(hdMode(bool)));
- addAction(hdAct);
+ QAction *definitionAct = new QAction(this);
+ definitionAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_D));
+ /*
+ QMenu *definitionMenu = new QMenu(this);
+ foreach (QString definition, VideoDefinition::getDefinitionNames()) {
+ definitionMenu->addAction(definition);
+ }
+ definitionAct->setMenu(definitionMenu);
+ */
+ actions->insert("definition", definitionAct);
+ connect(definitionAct, SIGNAL(triggered()), SLOT(toggleDefinitionMode()));
+ addAction(definitionAct);
// common action properties
foreach (QAction *action, actions->values()) {
}
void MainWindow::createStatusBar() {
+
+ // remove ugly borders on OSX
+ // also remove excessive spacing
+ statusBar()->setStyleSheet("::item{border:0 solid} QToolBar {padding:0;spacing:0;margin:0}");
+
currentTime = new QLabel(this);
statusBar()->addPermanentWidget(currentTime);
totalTime = new QLabel(this);
statusBar()->addPermanentWidget(totalTime);
- // remove ugly borders on OSX
- // and remove some excessive padding
- statusBar()->setStyleSheet("::item{border:0 solid} "
- "QStatusBar, QToolBar, QToolButton {spacing:0;padding:0;margin:0} "
- );
-
QToolBar *toolBar = new QToolBar(this);
- int iconHeight = 24; // statusBar()->height();
- int iconWidth = 36; // iconHeight * 3 / 2;
- toolBar->setIconSize(QSize(iconWidth, iconHeight));
- toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
- toolBar->addAction(The::globalActions()->value("hd"));
+ toolBar->setToolButtonStyle(Qt::ToolButtonTextOnly);
+ toolBar->addAction(The::globalActions()->value("definition"));
statusBar()->addPermanentWidget(toolBar);
statusBar()->show();
void MainWindow::readSettings() {
QSettings settings;
restoreGeometry(settings.value("geometry").toByteArray());
- hdMode(settings.value("hd").toBool());
+ setDefinitionMode(settings.value("definition", VideoDefinition::getDefinitionNames().first()).toString());
audioOutput->setVolume(settings.value("volume", 1).toDouble());
audioOutput->setMuted(settings.value("volumeMute").toBool());
}
return;
QSettings settings;
settings.setValue("geometry", saveGeometry());
- settings.setValue("hd", The::globalActions()->value("hd")->isChecked());
settings.setValue("volume", audioOutput->volume());
settings.setValue("volumeMute", audioOutput->isMuted());
mediaView->saveSplitterState();
statusBar()->showMessage(tr("Volume is unmuted"));
}
-QPixmap MainWindow::createHDPixmap(bool enabled) {
- QPixmap pixmap = QPixmap(24,24);
- pixmap.fill(Qt::transparent);
- QPainter painter(&pixmap);
- painter.setRenderHints(QPainter::Antialiasing, true);
-
- QRect rect(0, 3, 24, 18);
-
- QPen pen;
- pen.setColor(Qt::black);
- pen.setWidth(1);
- painter.setPen(pen);
-
- if (enabled) {
- painter.setBrush(palette().highlight());
- } else {
- QLinearGradient gradient(QPointF(0, 0), QPointF(0, rect.height() / 2));
- gradient.setColorAt(0, QColor(0x6d, 0x6d, 0x6d));
- gradient.setColorAt(1, QColor(0x25, 0x25, 0x25));
- painter.setBrush(QBrush(gradient));
- }
- painter.drawRoundedRect(rect, 5, 5);
-
- if (enabled) {
- pen.setColor(palette().highlightedText().color());
- } else {
- pen.setColor(Qt::white);
- }
- painter.setPen(pen);
-
- QFont font;
- font.setPixelSize(12);
- font.setBold(true);
- painter.setFont(font);
- painter.drawText(rect, Qt::AlignCenter, "HD");
-
- return pixmap;
-}
-
-static QIcon hdOnIcon;
-static QIcon hdOffIcon;
-
-QIcon MainWindow::createHDIcon() {
- hdOffIcon.addPixmap(createHDPixmap(false));
- hdOnIcon.addPixmap(createHDPixmap(true));
- return hdOffIcon;
-}
-
-void MainWindow::hdMode(bool enabled) {
- QAction *hdAct = The::globalActions()->value("hd");
- hdAct->setChecked(enabled);
- if (enabled) {
- hdAct->setStatusTip(tr("High Definition video is enabled") + " (" + hdAct->shortcut().toString(QKeySequence::NativeText) + ")");
- } else {
- hdAct->setStatusTip(tr("High Definition video is not enabled") + " (" + hdAct->shortcut().toString(QKeySequence::NativeText) + ")");
- }
- statusBar()->showMessage(hdAct->statusTip());
+void MainWindow::setDefinitionMode(QString definitionName) {
+ QAction *definitionAct = The::globalActions()->value("definition");
+ definitionAct->setText(definitionName);
+ definitionAct->setStatusTip(tr("Maximum video definition set to %1").arg(definitionAct->text())
+ + " (" + definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
+ statusBar()->showMessage(definitionAct->statusTip());
QSettings settings;
- settings.setValue("hd", enabled);
+ settings.setValue("definition", definitionName);
}
-void MainWindow::hdIndicator(bool isHd) {
- QAction *hdAct = The::globalActions()->value("hd");
- if (isHd) {
- hdAct->setIcon(hdOnIcon);
- hdAct->setToolTip(tr("The current video is in High Definition"));
- } else {
- hdAct->setIcon(hdOffIcon);
- hdAct->setToolTip(tr("The current video is not in High Definition"));
+void MainWindow::toggleDefinitionMode() {
+ QSettings settings;
+ QString currentDefinition = settings.value("definition").toString();
+ QStringList definitionNames = VideoDefinition::getDefinitionNames();
+ int currentIndex = definitionNames.indexOf(currentDefinition);
+ int nextIndex = 0;
+ if (currentIndex != definitionNames.size() - 1) {
+ nextIndex = currentIndex + 1;
}
+ QString nextDefinition = definitionNames.at(nextIndex);
+ setDefinitionMode(nextDefinition);
}
void MainWindow::showFullscreenToolbar(bool show) {
MainWindow();
~MainWindow();
-public slots:
- void hdIndicator(bool isHd);
-
protected:
void closeEvent(QCloseEvent *);
bool eventFilter(QObject *obj, QEvent *event);
void searchFocus();
void tick(qint64 time);
void totalTimeChanged(qint64 time);
- void hdMode(bool enabled);
+ void setDefinitionMode(QString definitionName);
+ void toggleDefinitionMode();
void clearRecentKeywords();
// volume shortcuts
void readSettings();
void writeSettings();
void showWidget(QWidget*);
- QPixmap createHDPixmap(bool enabled);
- QIcon createHDIcon();
static QString formatTime(qint64 time);
// view mechanism
#include "video.h"
#include "networkaccess.h"
#include <QtNetwork>
+#include "videodefinition.h"
namespace The {
NetworkAccess* http();
Video::Video() : m_duration(0),
m_viewCount(-1),
-m_hd(false),
+definitionCode(0),
elIndex(0) { }
void Video::preloadThumbnail() {
return m_thumbnail;
}
-void Video::loadStreamUrl() {
- // if (m_streamUrl.isEmpty())
- this->scrapeStreamUrl();
- // else emit gotStreamUrl(m_streamUrl);
-}
-
static const QStringList elTypes = QStringList() << "embedded" << "vevo" << "detailpage";
-void Video::scrapeStreamUrl() {
+void Video::loadStreamUrl() {
// https://develop.participatoryculture.org/trac/democracy/browser/trunk/tv/portable/flashscraper.py
- QUrl webpage = m_webpage;
- // qDebug() << webpage.toString();
-
// Get Video ID
// youtube-dl line 428
// QRegExp re("^((?:http://)?(?:\\w+\\.)?youtube\\.com/(?:(?:v/)|(?:(?:watch(?:\\.php)?)?\\?(?:.+&)?v=)))?([0-9A-Za-z_-]+)(?(1).+)?$");
QRegExp re("^http://www\\.youtube\\.com/watch\\?v=([0-9A-Za-z_-]+).*");
- bool match = re.exactMatch(webpage.toString());
+ bool match = re.exactMatch(m_webpage.toString());
if (!match || re.numCaptures() < 1) {
- emit errorStreamUrl(QString("Cannot get video id for %1").arg(webpage.toString()));
+ emit errorStreamUrl(QString("Cannot get video id for %1").arg(m_webpage.toString()));
return;
}
videoId = re.cap(1);
- // if (!videoId) return false;
- // qDebug() << videoId;
getVideoInfo();
// Don't panic! We have a plan B.
// get the youtube video webpage
QObject *reply = The::http()->get(webpage().toString());
- connect(reply, SIGNAL(data(QByteArray)), SLOT(scrapWebPage(QByteArray)));
+ connect(reply, SIGNAL(data(QByteArray)), SLOT(scrapeWebPage(QByteArray)));
connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
// see you in scrapWebPage(QByteArray)
return;
QString videoToken = re.cap(1);
// FIXME proper decode
videoToken = videoToken.replace("%3D", "=");
+ // we'll need this in gotHeadHeaders()
+ this->videoToken = videoToken;
+
// qDebug() << "token" << videoToken;
QSettings settings;
- if (settings.value("hd").toBool())
- findHdVideo(videoToken);
- else
- standardVideoUrl(videoToken);
+ QString definitionName = settings.value("definition").toString();
+ int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
+ if (definitionCode == 18) {
+ // This is assumed always available
+ foundVideoUrl(videoToken, 18);
+ } else {
+ findVideoUrl(definitionCode);
+ }
}
-void Video::standardVideoUrl(QString videoToken) {
- QUrl videoUrl = QUrl(QString("http://www.youtube.com/get_video?video_id=")
- .append(videoId)
- .append("&t=").append(videoToken)
- .append("&eurl=&el=embedded&ps=default&fmt=18"));
- m_streamUrl = videoUrl;
- m_hd = false;
- emit gotStreamUrl(videoUrl);
-}
+void Video::foundVideoUrl(QString videoToken, int definitionCode) {
+
+ QUrl videoUrl = QUrl(QString(
+ "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=embedded&ps=default&fmt=%3"
+ ).arg(videoId, videoToken, QString::number(definitionCode)));
-void Video::hdVideoUrl(QString videoToken) {
- QUrl videoUrl = QUrl(QString("http://www.youtube.com/get_video?video_id=")
- .append(videoId)
- .append("&t=").append(videoToken)
- .append("&eurl=&el=embedded&ps=default&fmt=22"));
m_streamUrl = videoUrl;
- m_hd = true;
emit gotStreamUrl(videoUrl);
}
emit errorStreamUrl(tr("Network error: %1 for %2").arg(reply->errorString(), reply->url().toString()));
}
-void Video::scrapWebPage(QByteArray data) {
+void Video::scrapeWebPage(QByteArray data) {
QString videoHTML = QString::fromUtf8(data);
QRegExp re(".*, \"t\": \"([^\"]+)\".*");
QString videoToken = re.cap(1);
// FIXME proper decode
videoToken = videoToken.replace("%3D", "=");
+
+ // we'll need this in gotHeadHeaders()
+ this->videoToken = videoToken;
+
// qDebug() << "token" << videoToken;
QSettings settings;
- if (settings.value("hd").toBool())
- findHdVideo(videoToken);
- else
- standardVideoUrl(videoToken);
+ QString definitionName = settings.value("definition").toString();
+ int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
+ if (definitionCode == 18) {
+ // This is assumed always available
+ foundVideoUrl(videoToken, 18);
+ } else {
+ findVideoUrl(definitionCode);
+ }
}
-void Video::findHdVideo(QString videoToken) {
+void Video::gotHeadHeaders(QNetworkReply* reply) {
+ int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ // qDebug() << "gotHeaders" << statusCode;
+ if (statusCode == 200) {
+ foundVideoUrl(videoToken, definitionCode);
+ } else {
+
+ // try next (lower quality) definition
+ /*
+ QStringList definitionNames = VideoDefinition::getDefinitionNames();
+ int currentIndex = definitionNames.indexOf(currentDefinition);
+ int previousIndex = 0;
+ if (currentIndex > 0) {
+ previousIndex = currentIndex - 1;
+ }
+ if (previousIndex > 0) {
+ QString nextDefinitionName = definitionNames.at(previousIndex);
+ findVideoUrl(nextDefinitionName);
+ } else {
+ foundVideoUrl(videoToken, 18);
+ }*/
+
+
+ QList<int> definitionCodes = VideoDefinition::getDefinitionCodes();
+ int currentIndex = definitionCodes.indexOf(definitionCode);
+ int previousIndex = 0;
+ if (currentIndex > 0) {
+ previousIndex = currentIndex - 1;
+ int definitionCode = definitionCodes.at(previousIndex);
+ if (definitionCode == 18) {
+ // This is assumed always available
+ foundVideoUrl(videoToken, 18);
+ } else {
+ findVideoUrl(definitionCode);
+ }
+
+ } else {
+ foundVideoUrl(videoToken, 18);
+ }
- // we'll need this in gotHeaders()
- this->videoToken = videoToken;
+ }
+}
+
+void Video::findVideoUrl(int definitionCode) {
+ this->definitionCode = definitionCode;
- // try HD: fmt=22
- QUrl videoUrl = QUrl(QString("http://www.youtube.com/get_video?video_id=")
- .append(videoId)
- .append("&t=").append(videoToken)
- .append("&eurl=&el=embedded&ps=default&fmt=22"));
+ QUrl videoUrl = QUrl(QString(
+ "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=embedded&ps=default&fmt=%3"
+ ).arg(videoId, videoToken, QString::number(definitionCode)));
QObject *reply = The::http()->head(videoUrl);
- connect(reply, SIGNAL(finished(QNetworkReply*)), SLOT(gotHdHeaders(QNetworkReply*)));
+ connect(reply, SIGNAL(finished(QNetworkReply*)), SLOT(gotHeadHeaders(QNetworkReply*)));
// connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
- // see you in gotHeaders()
-}
+ // see you in gotHeadHeaders()
-void Video::gotHdHeaders(QNetworkReply* reply) {
- int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- // qDebug() << "gotHeaders" << statusCode;
- if (statusCode == 200) {
- hdVideoUrl(videoToken);
- } else {
- standardVideoUrl(videoToken);
- }
}
const QDateTime published() const { return m_published; }
void setPublished( QDateTime published ) { m_published = published; }
- bool isHd() const { return m_hd; }
+ bool getDefinitionCode() const { return definitionCode; }
void loadStreamUrl();
QUrl getStreamUrl() { return m_streamUrl; }
private slots:
void gotVideoInfo(QByteArray);
void errorVideoInfo(QNetworkReply*);
- void scrapWebPage(QByteArray);
- void gotHdHeaders(QNetworkReply*);
+ void scrapeWebPage(QByteArray);
+ void gotHeadHeaders(QNetworkReply*);
private:
- void scrapeStreamUrl();
void getVideoInfo();
- void findHdVideo(QString videoToken);
- void standardVideoUrl(QString videoToken);
- void hdVideoUrl(QString videoToken);
+ void findVideoUrl(int definitionCode);
+ void foundVideoUrl(QString videoToken, int definitionCode);
QString m_title;
QString m_description;
QString m_author;
- // QUrl m_authorUrl;
QUrl m_webpage;
QUrl m_streamUrl;
QImage m_thumbnail;
QList<QUrl> m_thumbnailUrls;
- // QList<QImage> m_thumbnails;
int m_duration;
QDateTime m_published;
int m_viewCount;
QString videoId;
QString videoToken;
- int m_hd;
+ int definitionCode;
// current index for the elTypes list
// needed to iterate on elTypes
--- /dev/null
+#include "videodefinition.h"
+
+QStringList VideoDefinition::getDefinitionNames() {
+ static QStringList definitionNames = QStringList() << "360p" << "720p" << "1080p";
+ return definitionNames;
+}
+
+QList<int> VideoDefinition::getDefinitionCodes() {
+ static QList<int> definitionCodes = QList<int>() << 18 << 22 << 37;
+ return definitionCodes;
+}
+
+QHash<QString, int> VideoDefinition::getDefinitions() {
+ static QHash<QString, int> definitions;
+ if (definitions.isEmpty()) {
+ definitions.insert("360p", 18);
+ definitions.insert("720p", 22);
+ definitions.insert("1080p", 37);
+ }
+ return definitions;
+}
+
+int VideoDefinition::getDefinitionCode(QString name) {
+ return VideoDefinition::getDefinitions().value(name);
+}
--- /dev/null
+#ifndef VIDEODEFINITION_H
+#define VIDEODEFINITION_H
+
+#include <QtCore>
+
+class VideoDefinition {
+
+public:
+ static QStringList getDefinitionNames();
+ static QList<int> getDefinitionCodes();
+ static QHash<QString, int> getDefinitions();
+ static int getDefinitionCode(QString name);
+
+};
+
+#endif // VIDEODEFINITION_H