connect(volumeMuteAct, SIGNAL(triggered()), this, SLOT(volumeMute()));
addAction(volumeMuteAct);
+ QAction *hdAct = new QAction(this);
+ hdAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_H));
+ hdAct->setIcon(createHDIcon());
+ hdAct->setCheckable(true);
+ actions->insert("hd", hdAct);
+ QSettings settings;
+ connect(hdAct, SIGNAL(toggled(bool)), this, SLOT(saveHdSetting(bool)));
+ addAction(hdAct);
+
// common action properties
foreach (QAction *action, actions->values()) {
// never autorepeat.
// unexperienced users tend to keep keys pressed for a "long" time
action->setAutoRepeat(false);
- action->setToolTip(action->statusTip());
+
+ // set to something more meaningful then the toolbar text
+ // HELP! how to remove tooltips altogether?!
+ if (!action->statusTip().isEmpty())
+ action->setToolTip(action->statusTip());
// show keyboard shortcuts in the status bar
if (!action->shortcut().isEmpty())
statusBar()->addPermanentWidget(totalTime);
// remove ugly borders on OSX
- statusBar()->setStyleSheet("::item{border:0 solid}");
+ // and remove some excessive padding
+ statusBar()->setStyleSheet("::item{border:0 solid} QStatusBar, QToolBar {padding:0;margin:0} QToolButton {padding:1px}");
+
+ 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"));
+ statusBar()->addPermanentWidget(toolBar);
statusBar()->show();
}
void MainWindow::readSettings() {
QSettings settings;
restoreGeometry(settings.value("geometry").toByteArray());
+ The::globalActions()->value("hd")->setChecked(settings.value("hd").toBool());
}
void MainWindow::writeSettings() {
}
*/
+
+
+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);
+ painter.setPen(pen);
+
+ if (enabled) {
+ QPalette palette;
+ 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(Qt::white);
+ } else {
+ QPalette palette;
+ pen.setColor(palette.highlightedText().color());
+ }
+ 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() {
+ // QIcon icon;
+ hdOffIcon.addPixmap(createHDPixmap(false));
+ hdOnIcon.addPixmap(createHDPixmap(true));
+ return hdOffIcon;
+}
+
+void MainWindow::saveHdSetting(bool enabled) {
+ QSettings settings;
+ settings.setValue("hd", enabled);
+ QAction *hdAct = The::globalActions()->value("hd");
+ 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) + ")");
+ }
+}
+
+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"));
+ }
+}
MainWindow();
~MainWindow();
+public slots:
+ void hdIndicator(bool isHd);
+
protected:
void closeEvent(QCloseEvent *);
void searchFocus();
void tick(qint64 time);
void totalTimeChanged(qint64 time);
+ void saveHdSetting(bool enabled);
// volume shortcuts
void volumeUp();
void readSettings();
void writeSettings();
void showWidget(QWidget*);
+ QPixmap createHDPixmap(bool enabled);
+ QIcon createHDIcon();
// view mechanism
QPointer<FaderWidget> faderWidget;
connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
// TODO handle signal in a proper slot and impl item error status
connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)));
+
video->loadStreamUrl();
// reset the timer flag
listView->scrollTo(index, QAbstractItemView::EnsureVisible);
}
+ // HD indicator
+
+ // get the Video that sent the signal
+ Video *video = static_cast<Video *>(sender());
+ if (!video) {
+ qDebug() << "Cannot get sender";
+ return;
+ }
+ bool ret = QMetaObject::invokeMethod(qApp->topLevelWidgets().first(), "hdIndicator", Qt::QueuedConnection, Q_ARG(bool, video->isHd()));
+
}
void MediaView::itemActivated(const QModelIndex &index) {
this->networkReply = networkReply;
}
-void NetworkReply::metaDataChanged() {
+void NetworkReply::finished() {
QUrl redirection = networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (redirection.isValid()) {
- qDebug() << "Redirect" << redirection;
+ // qDebug() << "Redirect!"; // << redirection;
- QNetworkReply *redirectReply = The::http()->simpleGet(redirection);
+ QNetworkReply *redirectReply = The::http()->simpleGet(redirection, networkReply->operation());
setParent(redirectReply);
networkReply->deleteLater();
networkReply = redirectReply;
- // handle redirections
- connect(networkReply, SIGNAL(metaDataChanged()),
- this, SLOT(metaDataChanged()), Qt::QueuedConnection);
-
// when the request is finished we'll invoke the target method
- connect(networkReply, SIGNAL(finished()), this, SLOT(finished()), Qt::QueuedConnection);
+ connect(networkReply, SIGNAL(finished()), this, SLOT(finished()), Qt::AutoConnection);
+ return;
}
-}
-void NetworkReply::finished() {
emit finished(networkReply);
NetworkAccess::NetworkAccess( QObject* parent) : QObject( parent ) {}
-QNetworkReply* NetworkAccess::simpleGet(QUrl url) {
+QNetworkReply* NetworkAccess::simpleGet(QUrl url, int operation) {
QNetworkAccessManager *manager = The::networkAccessManager();
QNetworkRequest request(url);
request.setRawHeader("User-Agent", Constants::USER_AGENT.toUtf8());
request.setRawHeader("Connection", "Keep-Alive");
- qDebug() << "GET" << url.toString();
- QNetworkReply *networkReply = manager->get(request);
+
+ QNetworkReply *networkReply;
+ switch (operation) {
+
+ case QNetworkAccessManager::GetOperation:
+ qDebug() << "GET" << url.toString();
+ networkReply = manager->get(request);
+ break;
+
+ case QNetworkAccessManager::HeadOperation:
+ qDebug() << "HEAD" << url.toString();
+ networkReply = manager->head(request);
+ break;
+
+ default:
+ qDebug() << "Unknown operation:" << operation;
+ return 0;
+
+ }
// error handling
connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
QNetworkReply *networkReply = simpleGet(url);
NetworkReply *reply = new NetworkReply(networkReply);
- // handle redirections
- connect(networkReply, SIGNAL(metaDataChanged()),
- reply, SLOT(metaDataChanged()), Qt::QueuedConnection);
+ // error signal
+ connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
+ reply, SLOT(requestError(QNetworkReply::NetworkError)));
+
+ // when the request is finished we'll invoke the target method
+ connect(networkReply, SIGNAL(finished()), reply, SLOT(finished()), Qt::AutoConnection);
+
+ return reply;
+
+}
+
+NetworkReply* NetworkAccess::head(const QUrl url) {
+
+ QNetworkReply *networkReply = simpleGet(url, QNetworkAccessManager::HeadOperation);
+ NetworkReply *reply = new NetworkReply(networkReply);
// error signal
connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
reply, SLOT(requestError(QNetworkReply::NetworkError)));
// when the request is finished we'll invoke the target method
- connect(networkReply, SIGNAL(finished()), reply, SLOT(finished()), Qt::QueuedConnection);
+ connect(networkReply, SIGNAL(finished()), reply, SLOT(finished()), Qt::AutoConnection);
return reply;
}
+/*** sync ***/
+
+
QNetworkReply* NetworkAccess::syncGet(QUrl url) {
working = true;
networkReply = simpleGet(url);
connect(networkReply, SIGNAL(metaDataChanged()),
- this, SLOT(syncMetaDataChanged()), Qt::QueuedConnection);
+ this, SLOT(syncMetaDataChanged()), Qt::AutoConnection);
connect(networkReply, SIGNAL(finished()),
- this, SLOT(syncFinished()), Qt::QueuedConnection);
+ this, SLOT(syncFinished()), Qt::AutoConnection);
connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(error(QNetworkReply::NetworkError)));
networkReply->deleteLater();
networkReply = manager->get(QNetworkRequest(redirection));
connect(networkReply, SIGNAL(metaDataChanged()),
- this, SLOT(metaDataChanged()), Qt::QueuedConnection);
+ this, SLOT(metaDataChanged()), Qt::AutoConnection);
connect(networkReply, SIGNAL(finished()),
- this, SLOT(finished()), Qt::QueuedConnection);
+ this, SLOT(finished()), Qt::AutoConnection);
*/
}
return;
}
+ // Ignore HEADs
+ if (networkReply->operation() == QNetworkAccessManager::HeadOperation)
+ return;
+
// report the error in the status bar
QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(qApp->topLevelWidgets().first());
if (mainWindow) mainWindow->statusBar()->showMessage(
public slots:
void finished();
- void metaDataChanged();
void requestError(QNetworkReply::NetworkError);
signals:
public:
NetworkAccess( QObject* parent=0);
- QNetworkReply* simpleGet(QUrl url);
+ QNetworkReply* simpleGet(QUrl url, int operation = QNetworkAccessManager::GetOperation);
NetworkReply* get(QUrl url);
+ NetworkReply* head(QUrl url);
QNetworkReply* syncGet(QUrl url);
QByteArray syncGetBytes(QUrl url);
QString syncGetString(QUrl url);
NetworkAccess* http();
}
-Video::Video() : m_thumbnailUrls(QList<QUrl>()) {
- m_duration = 0;
- m_viewCount = -1;
-}
+Video::Video() : m_duration(0),
+m_viewCount(-1),
+m_hd(false) { }
void Video::preloadThumbnail() {
if (m_thumbnailUrls.isEmpty()) return;
return m_thumbnail;
}
+void Video::loadStreamUrl() {
+ // if (m_streamUrl.isEmpty())
+ this->scrapeStreamUrl();
+ // else emit gotStreamUrl(m_streamUrl);
+}
+
void Video::scrapeStreamUrl() {
// https://develop.participatoryculture.org/trac/democracy/browser/trunk/tv/portable/flashscraper.py
videoToken = videoToken.replace("%3D", "=");
// qDebug() << "token" << videoToken;
+ QSettings settings;
+ if (settings.value("hd").toBool())
+ findHdVideo(videoToken);
+ else
+ standardVideoUrl(videoToken);
+
+}
+
+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::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);
}
QString videoToken = re.cap(1);
// FIXME proper decode
videoToken = videoToken.replace("%3D", "=");
- qDebug() << "token" << videoToken;
+ // qDebug() << "token" << videoToken;
+
+ QSettings settings;
+ if (settings.value("hd").toBool())
+ findHdVideo(videoToken);
+ else
+ standardVideoUrl(videoToken);
+
+}
+
+void Video::findHdVideo(QString videoToken) {
+ // we'll need this in gotHeaders()
+ this->videoToken = videoToken;
+
+ // 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=detailpage&ps=default&fmt=18"));
+ .append("&eurl=&el=embedded&ps=default&fmt=22"));
- m_streamUrl = videoUrl;
+ QObject *reply = The::http()->head(videoUrl);
+ connect(reply, SIGNAL(finished(QNetworkReply*)), SLOT(gotHdHeaders(QNetworkReply*)));
+ connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
- emit gotStreamUrl(videoUrl);
+ // see you in gotHeaders()
+}
+void Video::gotHdHeaders(QNetworkReply* reply) {
+ int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ // qDebug() << "gotHeaders" << statusCode;
+ if (statusCode == 200) {
+ hdVideoUrl(videoToken);
+ } else {
+ standardVideoUrl(videoToken);
+ }
}
const QUrl webpage() const { return m_webpage; }
void setWebpage( QUrl webpage ) { m_webpage = webpage; }
- void loadStreamUrl() {
- if (m_streamUrl.isEmpty())
- this->scrapeStreamUrl();
- else emit gotStreamUrl(m_streamUrl);
- }
-
QList<QUrl> thumbnailUrls() const { return m_thumbnailUrls; }
void addThumbnailUrl(QUrl url) {
m_thumbnailUrls << url;
const QDateTime published() const { return m_published; }
void setPublished( QDateTime published ) { m_published = published; }
+ bool isHd() const { return m_hd; }
+
+ void loadStreamUrl();
+
public slots:
void setThumbnail(QByteArray bytes);
void gotVideoInfo(QByteArray);
void errorVideoInfo(QNetworkReply*);
void scrapWebPage(QByteArray);
+ void gotHdHeaders(QNetworkReply*);
private:
void scrapeStreamUrl();
+ void findHdVideo(QString videoToken);
+ void standardVideoUrl(QString videoToken);
+ void hdVideoUrl(QString videoToken);
QString m_title;
QString m_description;
// The YouTube video id
// This is needed by the gotVideoInfo callback
QString videoId;
+
+ QString videoToken;
+ int m_hd;
};
// This is required in order to use QPointer<Video> as a QVariant