├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── resources ├── Resources.qrc ├── icons │ ├── cancel.png │ ├── convert.png │ ├── open.png │ ├── spinner.gif │ ├── web-to-webm.icns │ ├── web-to-webm.ico │ └── web-to-webm.png └── web-to-webm.rc ├── src ├── converter.cpp ├── converter.h ├── downloader.cpp ├── downloader.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── renamer.cpp ├── updater.cpp ├── updater.h ├── updater.ui ├── utilities.cpp ├── utilities.h ├── window.cpp └── window.h └── web-to-webm.pro /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | 20 | .DS_Store 21 | *.pro.user 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | 6 | before_install: 7 | - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa 8 | - sudo apt-get update -qq 9 | - sudo apt-get install qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev 10 | - sudo apt-get install qt5-default qttools5-dev-tools 11 | 12 | script: 13 | - qmake web-to-webm.pro 14 | - make 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 solusipse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web-to-webm [![Build Status](https://travis-ci.org/solusipse/web-to-webm.svg?branch=master)](https://travis-ci.org/solusipse/web-to-webm) 2 | 3 | web-to-webm is simple, cross-platform gui tool for extracting clips from any of Youtube videos (and from many other supported services) and converting them to webm format. 4 | 5 | ## Download 6 | For downloads, visit *Releases* page: [https://github.com/solusipse/web-to-webm/releases/latest](https://github.com/solusipse/web-to-webm/releases/latest). There are Windows (64 and 32 bit) and OS X (64 bit) releases. 7 | 8 | If you are on Linux, follow these steps: 9 | 10 | 1. Build web-to-webm (see `Building` section). 11 | 2. Install `youtube-dl` ([download page](http://rg3.github.io/youtube-dl/download.html)): 12 | 13 | - `sudo curl https://yt-dl.org/downloads/2015.02.10.3/youtube-dl -o /usr/local/bin/youtube-dl` 14 | - `sudo chmod a+x /usr/local/bin/youtube-dl` 15 | 16 | 3. Install `ffmpeg` via your package manager. 17 | 18 | Release page: [https://github.com/solusipse/web-to-webm/releases](https://github.com/solusipse/web-to-webm/releases). 19 | 20 | ## Usage 21 | ### Time format 22 | Format is derived from `ffmpeg`. You have to use colons as separators. There are three options: 23 | - just seconds (`ss - ss`, e.g. `5 - 35`), 24 | - minutes and seconds (`mm:ss - mm:ss`, e.g. `15:00 - 15:25`), 25 | - hours, minutes and seconds (`hh:mm:ss - hh:mm:ss`, e.g. `02:05:00 - 02:05:30`). 26 | 27 | You can mix these standards (e.g. `55 - 01:15` which is equivalent of `00:55 - 01:15` or `55 - 75`). 28 | 29 | ## Interface 30 | ![web-to-webm](http://solusipse.net/misc/webtoweb-0.7.0-screen.png) 31 | 32 | ## Building 33 | You need `QT5` to build this software. To build web-to-webm, type these commands: 34 | 35 | 1. `qmake-qt5 web-to-webm.pro`, 36 | 2. `make`. 37 | 38 | On `Windows` and `Mac OS X` it will be probably easier to use QT Creator (open .pro file to start). 39 | 40 | ## Supported sites 41 | Since web-to-webm downloading mechanism is based on `yotube-dl` it supports same services. For full list see: http://rg3.github.io/youtube-dl/supportedsites.html. 42 | 43 | ## Changelog 44 | 45 | ### v.0.7.2 46 | - lightweight mode (no player) 47 | 48 | ### v.0.7.1 49 | - asynchronous loading of video details 50 | 51 | ### v.0.7.0 52 | - higher accuracy of conversion percentage bar 53 | - fixed downloading and conversion of clips downloaded from other services than youtube 54 | - minor interface and binary detecting bugfixes 55 | - now it's possible to set custom ffmpeg parameters 56 | - autoupdate 57 | - installer for Windows 58 | - add widget for setting quality and estimating size of output video 59 | 60 | ### v.0.6.0 61 | - added support for other websites than Youtube 62 | - added to log information about output video duration 63 | - added menu option for setting youtube-dl and ffmpeg paths 64 | - added menu option for resetting settings 65 | 66 | ## License 67 | web-to-webm is MIT licensed. Icons are from Font Awesome ([license](http://fortawesome.github.io/Font-Awesome/license/)). 68 | -------------------------------------------------------------------------------- /resources/Resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/cancel.png 4 | icons/convert.png 5 | icons/open.png 6 | icons/web-to-webm.ico 7 | icons/spinner.gif 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/icons/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/web-to-webm/54892d8cf880c4ac958df521daf0e7293ccda35d/resources/icons/cancel.png -------------------------------------------------------------------------------- /resources/icons/convert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/web-to-webm/54892d8cf880c4ac958df521daf0e7293ccda35d/resources/icons/convert.png -------------------------------------------------------------------------------- /resources/icons/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/web-to-webm/54892d8cf880c4ac958df521daf0e7293ccda35d/resources/icons/open.png -------------------------------------------------------------------------------- /resources/icons/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/web-to-webm/54892d8cf880c4ac958df521daf0e7293ccda35d/resources/icons/spinner.gif -------------------------------------------------------------------------------- /resources/icons/web-to-webm.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/web-to-webm/54892d8cf880c4ac958df521daf0e7293ccda35d/resources/icons/web-to-webm.icns -------------------------------------------------------------------------------- /resources/icons/web-to-webm.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/web-to-webm/54892d8cf880c4ac958df521daf0e7293ccda35d/resources/icons/web-to-webm.ico -------------------------------------------------------------------------------- /resources/icons/web-to-webm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solusipse/web-to-webm/54892d8cf880c4ac958df521daf0e7293ccda35d/resources/icons/web-to-webm.png -------------------------------------------------------------------------------- /resources/web-to-webm.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "icons/web-to-webm.ico" -------------------------------------------------------------------------------- /src/converter.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_mainwindow.h" 2 | #include "converter.h" 3 | #include "utilities.h" 4 | #include "window.h" 5 | #include 6 | 7 | Converter::Converter(QObject *parent) : QObject(parent) { 8 | } 9 | 10 | void Converter::start() { 11 | utils.addToLog("Starting conversion."); 12 | utils.killProcesses(); 13 | 14 | utils.currentDuration = 0; 15 | 16 | QStringList arguments; 17 | arguments << "-y" << "-hide_banner"; 18 | arguments << "-i" << utils.getCurrentRawFilename(); 19 | 20 | int bt = win.ui->bitrateValue->text().toInt(); 21 | 22 | if (win.ui->menuRemoveAudio->isChecked()) 23 | arguments << "-an"; 24 | else 25 | bt -= 100; 26 | 27 | if (bt <= 0) bt = 1; 28 | QString bitrate = QString::number(bt); 29 | 30 | if (!bitrate.isEmpty()) { 31 | bitrate.append("K"); 32 | arguments << "-b:v" << bitrate; 33 | utils.addToLog("Bitrate set to: " + bitrate); 34 | } 35 | 36 | if (!utils.configGetValue("ffmpeg_params").isEmpty()) { 37 | arguments << utils.configGetValue("ffmpeg_params").split(" "); 38 | utils.addToLog("Used custom parameters: " + utils.configGetValue("ffmpeg_params")); 39 | } 40 | 41 | if (!win.ui->cutFromEdit->text().trimmed().isEmpty() && !win.ui->cutToEdit->text().trimmed().isEmpty()) { 42 | arguments << "-ss" << win.ui->cutFromEdit->text().trimmed(); 43 | arguments << "-to" << win.ui->cutToEdit->text().trimmed(); 44 | utils.addToLog("Output video length: " + (QTime(0,0,0).addSecs(utils.getTrimmedVideoDuration())).toString("hh:mm:ss")); 45 | utils.currentDuration = utils.getTrimmedVideoDuration(); 46 | } 47 | 48 | arguments << utils.getCurrentFilename(); 49 | 50 | utils.conversionProcess = new QProcess; 51 | utils.conversionProcess->setProcessChannelMode(QProcess::MergedChannels); 52 | utils.conversionProcess->start(utils.ffmpegBinaryName(), arguments); 53 | 54 | connect(utils.conversionProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(read())); 55 | connect(utils.conversionProcess, SIGNAL(finished(int)), this, SLOT(complete(int))); 56 | } 57 | 58 | void Converter::read() { 59 | QString buffer = utils.conversionProcess->readAllStandardOutput(); 60 | QString duration, progress; 61 | QTime durationTime, progressTime; 62 | QRegExp time("\\d\\d\\:\\d\\d\\:\\d\\d\\.\\d\\d"); 63 | time.indexIn(buffer); 64 | if (buffer.contains("Duration: ") && utils.currentDuration == 0) { 65 | duration = time.capturedTexts()[0]; 66 | durationTime = QTime::fromString(duration, "hh:mm:ss.z"); 67 | utils.currentDuration = QTime(0,0).secsTo(durationTime); 68 | } else { 69 | if (buffer != "") { 70 | progress = time.capturedTexts()[0]; 71 | if (progress != "") { 72 | progressTime = QTime::fromString(progress, "hh:mm:ss.z"); 73 | int percent = double((QTime(0,0).msecsTo(progressTime)) / double(utils.currentDuration*1000))*100; 74 | win.ui->conversionProgressBar->setValue(percent); 75 | } 76 | } 77 | } 78 | if (win.ui->menuFfmpegOutput->isChecked()) 79 | utils.addToLog(buffer); 80 | else 81 | utils.addToLog(buffer, false); 82 | } 83 | 84 | void Converter::complete(int code) { 85 | utils.conversionProcess->deleteLater(); 86 | utils.conversionProcess = NULL; 87 | win.updateConversionButton(); 88 | 89 | if (utils.killed) { 90 | utils.addToLog("Conversion canceled."); 91 | utils.killed = false; 92 | win.toggleConversionButton(); 93 | return; 94 | } 95 | if (code != 0) { 96 | utils.addToLog("Error on conversion. Check logs."); 97 | win.toggleConversionButton(); 98 | return; 99 | } 100 | 101 | // In case video is so short that ffmpeg do not display 102 | // conversion status 103 | win.ui->conversionProgressBar->setValue(100); 104 | 105 | win.toggleConversionButton(); 106 | win.updateConversionButton(); 107 | 108 | if (win.ui->menuRemoveRawVideo->isChecked()) 109 | utils.removeRawVideo(); 110 | 111 | utils.addToLog("Conversion complete."); 112 | utils.addToLog("Saved to: " + utils.getCurrentFilename()); 113 | 114 | if (win.ui->menuShowFile->isChecked()) 115 | utils.showFileInDirectory(); 116 | } 117 | -------------------------------------------------------------------------------- /src/converter.h: -------------------------------------------------------------------------------- 1 | #ifndef CONVERTER_H 2 | #define CONVERTER_H 3 | 4 | #include 5 | 6 | class Converter : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit Converter(QObject *parent = 0); 11 | void start(); 12 | 13 | private: 14 | QString command; 15 | 16 | public slots: 17 | void read(); 18 | void complete(int code); 19 | 20 | }; 21 | 22 | #endif // CONVERTER_H 23 | -------------------------------------------------------------------------------- /src/downloader.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_mainwindow.h" 2 | #include "downloader.h" 3 | #include "utilities.h" 4 | #include "window.h" 5 | 6 | Downloader::Downloader(QObject *parent) : QObject(parent) { 7 | } 8 | 9 | void Downloader::start(QString c, QStringList l) { 10 | utils.downloadProcess = new QProcess; 11 | utils.downloadProcess->setProcessChannelMode(QProcess::MergedChannels); 12 | utils.downloadProcess->start(c, l); 13 | 14 | connect(utils.downloadProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(read())); 15 | connect(utils.downloadProcess, SIGNAL(finished(int)), this, SLOT(complete(int))); 16 | } 17 | 18 | void Downloader::read() { 19 | QString buffer = utils.downloadProcess->readAllStandardOutput(); 20 | 21 | QRegExp regexp("\\[download\\]\\s+(\\d+)\\."); 22 | regexp.indexIn(buffer); 23 | QString percent = regexp.capturedTexts()[1]; 24 | if (percent != "") 25 | win.ui->downloadProgressBar->setValue(percent.toInt()); 26 | 27 | if (buffer.contains("has already been downloaded")) 28 | win.ui->downloadProgressBar->setValue(100); 29 | 30 | if (win.ui->menuYoutubedlOutput->isChecked()) 31 | utils.addToLog(buffer); 32 | else 33 | utils.addToLog(buffer, false); 34 | } 35 | 36 | void Downloader::complete(int code) { 37 | utils.downloadProcess->deleteLater(); 38 | utils.downloadProcess = NULL; 39 | 40 | if (utils.killed) { 41 | utils.addToLog("Downloading canceled."); 42 | utils.killed = false; 43 | win.toggleConversionButton(); 44 | return; 45 | } 46 | if (code != 0) { 47 | utils.addToLog("Download error."); 48 | win.toggleConversionButton(); 49 | return; 50 | } 51 | 52 | utils.addToLog("Download complete"); 53 | 54 | if (win.ui->menuDontConvert->isChecked()) { 55 | utils.addToLog("User decided not to convert. Downloaded video saved to: " + utils.getCurrentRawFilename()); 56 | win.toggleConversionButton(); 57 | return; 58 | } 59 | 60 | utils.convert.start(); 61 | } 62 | -------------------------------------------------------------------------------- /src/downloader.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNLOADER_H 2 | #define DOWNLOADER_H 3 | 4 | #include 5 | #include 6 | 7 | class Downloader : public QObject { 8 | Q_OBJECT 9 | 10 | public: 11 | explicit Downloader(QObject *parent = 0); 12 | void start(QString c, QStringList l); 13 | 14 | private: 15 | QString command; 16 | 17 | public slots: 18 | void read(); 19 | void complete(int code); 20 | 21 | }; 22 | 23 | 24 | #endif // DOWNLOADER_H 25 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) { 5 | QApplication a(argc, argv); 6 | MainWindow w; 7 | w.show(); 8 | 9 | return a.exec(); 10 | } 11 | -------------------------------------------------------------------------------- /src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include "downloader.h" 4 | #include "utilities.h" 5 | #include "window.h" 6 | #include "updater.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | MainWindow::MainWindow(QWidget *parent) : 17 | QMainWindow(parent), ui(new Ui::MainWindow) { 18 | 19 | ui->setupUi(this); 20 | win.ui = ui; 21 | 22 | this->setWindowTitle("web-to-webm v." VERSION); 23 | 24 | win.setTheme(); 25 | utils.setCommons(); 26 | utils.configInit(); 27 | utils.configLoadAll(); 28 | win.lockAllControls(true); 29 | win.setPlayerHtml(); 30 | win.detectMode(this); 31 | utils.addToLog("Initialised."); 32 | } 33 | 34 | MainWindow::~MainWindow() { 35 | utils.configSaveAll(); 36 | utils.killProcesses(); 37 | delete ui; 38 | } 39 | 40 | void MainWindow::on_urlEdit_returnPressed() { 41 | utils.startProcessQueue(win.ui->urlEdit->text()); 42 | } 43 | 44 | void MainWindow::on_selectSavePath_clicked() { 45 | QString filename = QFileDialog::getSaveFileName(this, 46 | tr("Select location for converted film"), QDir::currentPath(), tr("Webm (*.webm)") ); 47 | 48 | if (filename.isEmpty()) 49 | return; 50 | 51 | utils.pathChanged = true; 52 | utils.currentFileName = filename; 53 | 54 | win.setFilename(); 55 | } 56 | 57 | void MainWindow::on_startConversion_clicked() { 58 | win.updateConversionButton(); 59 | win.resetProgress(); 60 | if (ui->startConversion->isChecked()) { 61 | if (!utils.checkUrl()) 62 | return; 63 | utils.getFileName(); 64 | 65 | QStringList arguments; 66 | arguments << "-f" << utils.getVideoQuality() << "-o"; 67 | arguments << utils.getCurrentRawFilename() << utils.currentVideoUrl; 68 | 69 | utils.download.start(utils.getBinaryName(), arguments); 70 | } else { 71 | win.toggleConversionButton(); 72 | utils.killProcesses(); 73 | } 74 | } 75 | 76 | void MainWindow::on_menuAbout_triggered() { 77 | QMessageBox::about(this, "web-to-webm about", 78 | "web-to-webm " VERSION "\n\n" 79 | "This software is open source (MIT licensed). " 80 | "For more informations see:\n" 81 | "https://github.com/solusipse/web-to-webm."); 82 | } 83 | 84 | void MainWindow::on_qualityComboBox_currentIndexChanged(int index) { 85 | win.setFilename(); 86 | win.resetProgress(); 87 | utils.addToLog("Changed resolution to: " + utils.currentQualityList[index][0]); 88 | } 89 | 90 | void MainWindow::on_menuWebsite_triggered() { 91 | QDesktopServices::openUrl(QUrl("https://github.com/solusipse/web-to-webm")); 92 | } 93 | 94 | void MainWindow::on_menuDefaultDownloadPath_triggered() { 95 | QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), 96 | QDir().homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); 97 | if (!dir.isEmpty()) 98 | utils.configSetValue("default_path", dir); 99 | } 100 | 101 | void MainWindow::on_menuExit_triggered() { 102 | qApp->exit(); 103 | } 104 | 105 | void MainWindow::on_menuClearLog_triggered() { 106 | win.ui->logBox->clear(); 107 | } 108 | 109 | void MainWindow::on_menuShowLog_triggered() { 110 | QDialog dialog(this, Qt::Window); 111 | QPlainTextEdit *box = new QPlainTextEdit(&dialog); 112 | QVBoxLayout *layout = new QVBoxLayout; 113 | dialog.resize(640, 480); 114 | dialog.setWindowTitle("web-to-webm log"); 115 | layout->addWidget(box); 116 | dialog.setLayout(layout); 117 | box->setReadOnly(true); 118 | box->setPlainText(utils.log); 119 | dialog.exec(); 120 | } 121 | 122 | void MainWindow::on_menuNew_triggered() { 123 | utils.newVideo(); 124 | } 125 | 126 | void MainWindow::on_menuCustomYoutubedlPath_triggered() { 127 | bool ok; 128 | QString path = QInputDialog::getText(this, 129 | tr("Set custom youtube-dl path"), 130 | tr("youtube-dl absolute path (leave blank for default):"), QLineEdit::Normal, 131 | utils.configGetValue("youtubedl_path"), &ok); 132 | if (ok) 133 | utils.configSetValue("youtubedl_path", path); 134 | } 135 | 136 | void MainWindow::on_menuCustomFfmpegPath_triggered() { 137 | bool ok; 138 | QString path = QInputDialog::getText(this, 139 | tr("Set custom ffmpeg path"), 140 | tr("ffmpeg absolute path (leave blank for default):"), QLineEdit::Normal, 141 | utils.configGetValue("ffmpeg_path"), &ok); 142 | if (ok) 143 | utils.configSetValue("ffmpeg_path", path); 144 | } 145 | 146 | void MainWindow::on_menuResetAllSettings_triggered() { 147 | QMessageBox::StandardButton reply = QMessageBox::question(this, 148 | "Reset settings", "Are you sure you want to reset all settings?", 149 | QMessageBox::Yes|QMessageBox::No); 150 | 151 | if (reply != QMessageBox::Yes) 152 | return; 153 | 154 | QSettings settings(QSettings::IniFormat, QSettings::UserScope, "solusipse", "web-to-webm"); 155 | for (int i = 0; i < settings.allKeys().length(); i++) 156 | settings.remove(settings.allKeys()[i]); 157 | utils.configInit(); 158 | } 159 | 160 | void MainWindow::on_menuFfmpegCustomParams_triggered() { 161 | bool ok; 162 | QString path = QInputDialog::getText(this, 163 | tr("Set custom ffmpeg parameters"), 164 | tr("youtube-dl ffmpeg (e.g. -b:v 2M, leave blank for default):"), QLineEdit::Normal, 165 | utils.configGetValue("ffmpeg_params"), &ok); 166 | if (ok) 167 | utils.configSetValue("ffmpeg_params", path); 168 | } 169 | 170 | void MainWindow::on_menuUpdate_triggered() { 171 | Updater *u = new Updater(this); 172 | u->show(); 173 | } 174 | 175 | void MainWindow::on_bitrateValue_textChanged() { 176 | utils.updateBitrate(); 177 | } 178 | 179 | void MainWindow::on_cutFromEdit_textChanged() { 180 | utils.updateBitrate(); 181 | } 182 | 183 | void MainWindow::on_cutToEdit_textChanged() { 184 | utils.updateBitrate(); 185 | } 186 | 187 | void MainWindow::on_menuLtMode_triggered() { 188 | if (win.ui->player == NULL) { 189 | utils.configSetValue("light_mode", "false"); 190 | qApp->quit(); 191 | QProcess::startDetached(qApp->arguments()[0], qApp->arguments()); 192 | return; 193 | } 194 | 195 | utils.configSetValue("light_mode", "true"); 196 | win.setLightMode(this); 197 | } 198 | -------------------------------------------------------------------------------- /src/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainWindow(QWidget *parent = 0); 17 | ~MainWindow(); 18 | 19 | private slots: 20 | void on_urlEdit_returnPressed(); 21 | void on_selectSavePath_clicked(); 22 | void on_startConversion_clicked(); 23 | void on_menuAbout_triggered(); 24 | void on_qualityComboBox_currentIndexChanged(int index); 25 | void on_menuWebsite_triggered(); 26 | void on_menuDefaultDownloadPath_triggered(); 27 | void on_menuExit_triggered(); 28 | void on_menuClearLog_triggered(); 29 | void on_menuShowLog_triggered(); 30 | void on_menuNew_triggered(); 31 | void on_menuCustomFfmpegPath_triggered(); 32 | void on_menuCustomYoutubedlPath_triggered(); 33 | void on_menuResetAllSettings_triggered(); 34 | void on_menuFfmpegCustomParams_triggered(); 35 | void on_menuUpdate_triggered(); 36 | void on_bitrateValue_textChanged(); 37 | void on_cutFromEdit_textChanged(); 38 | void on_cutToEdit_textChanged(); 39 | void on_menuLtMode_triggered(); 40 | 41 | private: 42 | Ui::MainWindow *ui; 43 | }; 44 | 45 | #endif // MAINWINDOW_H 46 | -------------------------------------------------------------------------------- /src/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1024 10 | 600 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 500 22 | 0 23 | 24 | 25 | 26 | web-to-webm 27 | 28 | 29 | 30 | :/icons/web-to-webm.ico:/icons/web-to-webm.ico 31 | 32 | 33 | 34 | 35 | 36 | 37 | QFrame::StyledPanel 38 | 39 | 40 | QFrame::Sunken 41 | 42 | 43 | 44 | 12 45 | 46 | 47 | 12 48 | 49 | 50 | 12 51 | 52 | 53 | 12 54 | 55 | 56 | 57 | 58 | Url to Youtube video 59 | 60 | 61 | 62 | 63 | 64 | 65 | true 66 | 67 | 68 | Youtube video title 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 0 80 | 0 81 | 82 | 83 | 84 | 85 | 200 86 | 0 87 | 88 | 89 | 90 | QFrame::NoFrame 91 | 92 | 93 | QFrame::Raised 94 | 95 | 96 | 97 | 0 98 | 99 | 100 | 8 101 | 102 | 103 | 0 104 | 105 | 106 | 0 107 | 108 | 109 | 110 | 111 | 112 | 0 113 | 0 114 | 115 | 116 | 117 | 118 | 200 119 | 0 120 | 121 | 122 | 123 | false 124 | 125 | 126 | body{background:#333;} 127 | 128 | 129 | 130 | about:blank 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 200 140 | 0 141 | 142 | 143 | 144 | 145 | 200 146 | 16777215 147 | 148 | 149 | 150 | QFrame::StyledPanel 151 | 152 | 153 | QFrame::Raised 154 | 155 | 156 | 157 | 12 158 | 159 | 160 | 12 161 | 162 | 163 | 12 164 | 165 | 166 | 12 167 | 168 | 169 | 170 | 171 | QFrame::NoFrame 172 | 173 | 174 | QFrame::Raised 175 | 176 | 177 | 178 | 0 179 | 180 | 181 | 0 182 | 183 | 184 | 0 185 | 186 | 187 | 0 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 16 196 | 197 | 198 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 199 | 200 | 201 | Cut from 202 | 203 | 204 | 205 | 206 | 207 | 208 | 16 209 | 210 | 211 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 212 | 213 | 214 | Cut to 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | QFrame::NoFrame 225 | 226 | 227 | QFrame::Raised 228 | 229 | 230 | 231 | 0 232 | 233 | 234 | 0 235 | 236 | 237 | 0 238 | 239 | 240 | 0 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 5 249 | 250 | 251 | Bitrate (k) 252 | 253 | 254 | 255 | 256 | 257 | 258 | true 259 | 260 | 261 | Est. size 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | QFrame::NoFrame 272 | 273 | 274 | QFrame::Raised 275 | 276 | 277 | 278 | 0 279 | 280 | 281 | 0 282 | 283 | 284 | 0 285 | 286 | 287 | 0 288 | 289 | 290 | 291 | 292 | true 293 | 294 | 295 | Filename 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 0 304 | 0 305 | 306 | 307 | 308 | 309 | 35 310 | 35 311 | 312 | 313 | 314 | 315 | 35 316 | 35 317 | 318 | 319 | 320 | ... 321 | 322 | 323 | 324 | :/icons/open.png:/icons/open.png 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | QFrame::NoFrame 335 | 336 | 337 | QFrame::Raised 338 | 339 | 340 | 341 | 0 342 | 343 | 344 | 0 345 | 346 | 347 | 0 348 | 349 | 350 | 0 351 | 352 | 353 | 354 | 355 | false 356 | 357 | 358 | -1 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 35 367 | 35 368 | 369 | 370 | 371 | 372 | 35 373 | 35 374 | 375 | 376 | 377 | ... 378 | 379 | 380 | 381 | :/icons/convert.png:/icons/convert.png 382 | 383 | 384 | true 385 | 386 | 387 | false 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | Qt::Vertical 398 | 399 | 400 | QSizePolicy::Fixed 401 | 402 | 403 | 404 | 20 405 | 20 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | Downloading 414 | 415 | 416 | 417 | 418 | 419 | 420 | 0 421 | 422 | 423 | Qt::AlignCenter 424 | 425 | 426 | 427 | 428 | 429 | 430 | Conversion 431 | 432 | 433 | 434 | 435 | 436 | 437 | 100 438 | 439 | 440 | 0 441 | 442 | 443 | Qt::AlignCenter 444 | 445 | 446 | true 447 | 448 | 449 | QProgressBar::TopToBottom 450 | 451 | 452 | 453 | 454 | 455 | 456 | true 457 | 458 | 459 | Qt::ImhNone 460 | 461 | 462 | QFrame::NoFrame 463 | 464 | 465 | false 466 | 467 | 468 | true 469 | 470 | 471 | false 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 0 487 | 0 488 | 1024 489 | 22 490 | 491 | 492 | 493 | 494 | Options 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | Help 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | File 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | Log 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | true 547 | 548 | 549 | Remove audio 550 | 551 | 552 | 553 | 554 | Set default download location 555 | 556 | 557 | 558 | 559 | About 560 | 561 | 562 | 563 | 564 | true 565 | 566 | 567 | true 568 | 569 | 570 | Remove raw video 571 | 572 | 573 | 574 | 575 | true 576 | 577 | 578 | Set custom parameters for ffmpeg 579 | 580 | 581 | 582 | 583 | true 584 | 585 | 586 | Add custom parameters for youtube-dl 587 | 588 | 589 | 590 | 591 | true 592 | 593 | 594 | Set custom youtube-dl path 595 | 596 | 597 | 598 | 599 | true 600 | 601 | 602 | Set custom ffmpeg path 603 | 604 | 605 | 606 | 607 | Website 608 | 609 | 610 | 611 | 612 | true 613 | 614 | 615 | Don't convert (download only) 616 | 617 | 618 | 619 | 620 | Show full log 621 | 622 | 623 | 624 | 625 | Clear log 626 | 627 | 628 | 629 | 630 | New 631 | 632 | 633 | 634 | 635 | Exit 636 | 637 | 638 | 639 | 640 | true 641 | 642 | 643 | Open output directory after conversion 644 | 645 | 646 | 647 | 648 | true 649 | 650 | 651 | true 652 | 653 | 654 | Display youtube-dl output 655 | 656 | 657 | 658 | 659 | true 660 | 661 | 662 | true 663 | 664 | 665 | Display ffmpeg output 666 | 667 | 668 | 669 | 670 | Reset all settings 671 | 672 | 673 | 674 | 675 | false 676 | 677 | 678 | Advanced settings 679 | 680 | 681 | 682 | 683 | Check for updates 684 | 685 | 686 | 687 | 688 | true 689 | 690 | 691 | Lightweight mode 692 | 693 | 694 | 695 | 696 | 697 | 698 | QWebView 699 | QWidget 700 |
QtWebKitWidgets/QWebView
701 |
702 |
703 | 704 | 705 | 706 | 707 |
708 | -------------------------------------------------------------------------------- /src/renamer.cpp: -------------------------------------------------------------------------------- 1 | /* This program replaces old binary with new, 2 | * downloaded from update serwer. 3 | * This should be rewritten as standalone 4 | * QT app which does the whole update 5 | * procedure. 6 | */ 7 | 8 | #include 9 | 10 | #ifdef _WIN32 11 | #define SYSTEM 0 12 | #include 13 | #else 14 | #define SYSTEM 1 15 | #include 16 | #include 17 | #include 18 | #endif 19 | 20 | void log(const char *l); 21 | void windows_procedure(char *argv); 22 | void unix_procedure(char *argv); 23 | void windows_rename(); 24 | void unix_rename(); 25 | void change_directory(); 26 | 27 | int main (int argc, char *argv[]) { 28 | if (SYSTEM == 0) 29 | windows_procedure(argv[1]); 30 | else 31 | unix_procedure(argv[0]); 32 | 33 | return 0; 34 | } 35 | 36 | void log(const char *l) { 37 | FILE *f = fopen("updatelog.txt", "a"); 38 | if (f == NULL) { 39 | printf("Can't open updatelog.txt\n"); 40 | exit(1); 41 | } 42 | fprintf(f, "%s\n", l); 43 | fclose(f); 44 | 45 | printf("%s\n", l); 46 | } 47 | 48 | #ifdef _WIN32 49 | void windows_procedure(char *argv) { 50 | log("Detected WINDOWS system, waiting for main process to die."); 51 | printf("PID: %s\n", argv); 52 | DWORD dwExitCode = 259; 53 | while(1) { 54 | HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, atoi(argv)); 55 | if(GetExitCodeProcess(h, &dwExitCode)) 56 | CloseHandle(h); 57 | else 58 | break; 59 | 60 | Sleep(1000); 61 | } 62 | printf("Process is dead now. Starting update."); 63 | windows_rename(); 64 | } 65 | 66 | void windows_rename() { 67 | remove("web-to-webm.old"); 68 | 69 | int s; 70 | char o[] = "web-to-webm.exe"; 71 | char n[] = "web-to-webm.old"; 72 | s = rename(o, n); 73 | if (s == 0) 74 | log("Created backup of an old binary."); 75 | else 76 | log("Error on creating backup."); 77 | 78 | char o_[] = "update"; 79 | char n_[] = "web-to-webm.exe"; 80 | s = rename(o_ , n_); 81 | if (s == 0) 82 | log("New binary installed"); 83 | else { 84 | log("Error on installing new binary. Abort."); 85 | exit(1); 86 | } 87 | 88 | printf("Starting web-to-webm..."); 89 | 90 | STARTUPINFO si; 91 | PROCESS_INFORMATION pi; 92 | ZeroMemory(&si, sizeof(si)); 93 | si.cb = sizeof(si); 94 | ZeroMemory(&pi, sizeof(pi)); 95 | CreateProcess("web-to-webm.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 96 | 97 | log("Update complete!"); 98 | } 99 | #endif 100 | 101 | 102 | void unix_procedure(char *argv) { 103 | #ifndef _WIN32 104 | // This is necessary for app bundles on OS X 105 | char path[1024]; 106 | strncpy(path, argv, strlen(argv)-7); 107 | path[strlen(argv)-7] = NULL; 108 | chdir(path); 109 | 110 | remove("web-to-webm.old"); 111 | 112 | int s; 113 | char o[] = "web-to-webm"; 114 | char n[] = "web-to-webm.old"; 115 | s = rename(o, n); 116 | if (s == 0) 117 | log("Created backup of an old binary."); 118 | else 119 | log("Error on creating backup."); 120 | 121 | char o_[] = "update"; 122 | char n_[] = "web-to-webm"; 123 | s = rename(o_ , n_); 124 | if (s == 0) 125 | log("New binary installed"); 126 | else { 127 | log("Error on installing new binary. Abort."); 128 | exit(1); 129 | } 130 | 131 | system("chmod a+x web-to-webm"); 132 | 133 | printf("Starting web-to-webm..."); 134 | system("./web-to-webm"); 135 | #endif 136 | } 137 | 138 | -------------------------------------------------------------------------------- /src/updater.cpp: -------------------------------------------------------------------------------- 1 | #include "updater.h" 2 | #include "ui_updater.h" 3 | #include "utilities.h" 4 | 5 | #include 6 | #include 7 | 8 | /* TODO: 9 | * In the future this class needs big improvements, 10 | * such as checking checksum, xml parsing, adding 11 | * download progress bar and some archive unpacker 12 | * for updates. We'll probably need also some mirrors. 13 | */ 14 | 15 | #if _WIN32 || _WIN64 16 | #if _WIN64 17 | #define WINARCH "32" 18 | #else 19 | #define WINARCH "64" 20 | #endif 21 | #else 22 | #define WINARCH "" 23 | #endif 24 | 25 | 26 | Updater::Updater(QWidget *parent) : QDialog(parent), ui(new Ui::Updater) { 27 | ui->setupUi(this); 28 | ui->plainTextEdit->setStyleSheet("QPlainTextEdit{background:#383838; color: #888;}"); 29 | addToLog("After update restart is required, so finish your work before you start."); 30 | } 31 | 32 | Updater::~Updater() { 33 | delete ui; 34 | } 35 | 36 | void Updater::start() { 37 | addToLog("Started update"); 38 | checkForUpdates(); 39 | } 40 | 41 | void Updater::checkForUpdates() { 42 | QNetworkAccessManager *manager = new QNetworkAccessManager(); 43 | connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *))); 44 | manager->get(QNetworkRequest(QUrl("http://solusipse.net/software/web-to-webm/version.txt"))); 45 | } 46 | 47 | void Updater::replyFinished(QNetworkReply *reply) { 48 | if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 404) { 49 | addToLog("Could not get current version. Aborting."); 50 | ui->progressBar->setValue(0); 51 | return; 52 | } 53 | 54 | QString newestVersion = reply->readAll(); 55 | addToLog("Connected to update server."); 56 | incrementProgressBar(); 57 | addToLog("Your web-to-webm version is: " VERSION); 58 | if (newestVersion == VERSION) { 59 | addToLog("There are no updates."); 60 | ui->progressBar->setValue(5); 61 | return; 62 | } 63 | 64 | addToLog("Newest version is: " + newestVersion); 65 | addToLog("Downloading..."); 66 | incrementProgressBar(); 67 | 68 | QNetworkAccessManager *manager = new QNetworkAccessManager(); 69 | connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(downloadFinished(QNetworkReply *))); 70 | manager->get(QNetworkRequest(QUrl("http://solusipse.net/software/web-to-webm/" + prepareBinaryName(newestVersion)))); 71 | } 72 | 73 | void Updater::downloadFinished(QNetworkReply *reply) { 74 | if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 404) { 75 | addToLog("Could not download proper binary. Aborting."); 76 | ui->progressBar->setValue(0); 77 | return; 78 | } 79 | addToLog("Download complete."); 80 | incrementProgressBar(); 81 | QFile f(QCoreApplication::applicationDirPath() + "/update"); 82 | if (f.open(QIODevice::WriteOnly)) { 83 | f.write(reply->readAll()); 84 | f.close(); 85 | } 86 | addToLog("Saved to file."); 87 | addToLog("Application will be restarted now."); 88 | 89 | QString pid = QString::number(QCoreApplication::applicationPid()); 90 | 91 | QProcess *proc = new QProcess(this); 92 | proc->startDetached(QCoreApplication::applicationDirPath() + "/renamer " + pid); 93 | 94 | qApp->exit(); 95 | } 96 | 97 | void Updater::addToLog(QString s) { 98 | ui->plainTextEdit->appendPlainText(s); 99 | } 100 | 101 | void Updater::incrementProgressBar() { 102 | ui->progressBar->setValue(ui->progressBar->value() + 1); 103 | } 104 | 105 | QString Updater::prepareBinaryName(QString v) { 106 | if (SYSTEM == 0) 107 | return "win-" + QString(WINARCH) + "-web-to-webm-" + v; 108 | if (SYSTEM == 1) 109 | return "mac-web-to-webm-" + v; 110 | if (SYSTEM == 2) 111 | return "linux-web-to-webm-" + v; 112 | } 113 | 114 | void Updater::on_buttonStart_clicked() 115 | { 116 | utils.killProcesses(); 117 | start(); 118 | } 119 | -------------------------------------------------------------------------------- /src/updater.h: -------------------------------------------------------------------------------- 1 | #ifndef UPDATER_H 2 | #define UPDATER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class Updater; 9 | } 10 | 11 | class Updater : public QDialog 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit Updater(QWidget *parent = 0); 17 | ~Updater(); 18 | 19 | private: 20 | Ui::Updater *ui; 21 | void checkForUpdates(); 22 | void addToLog(QString s); 23 | void start(); 24 | void incrementProgressBar(); 25 | 26 | QString prepareBinaryName(QString v); 27 | 28 | public slots: 29 | void replyFinished(QNetworkReply *reply); 30 | void downloadFinished(QNetworkReply *reply); 31 | private slots: 32 | void on_buttonStart_clicked(); 33 | }; 34 | 35 | #endif // UPDATER_H 36 | -------------------------------------------------------------------------------- /src/updater.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Updater 4 | 5 | 6 | 7 | 0 8 | 0 9 | 320 10 | 240 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 320 22 | 240 23 | 24 | 25 | 26 | 27 | 320 28 | 240 29 | 30 | 31 | 32 | Checking for updates 33 | 34 | 35 | 36 | 37 | 10 38 | 10 39 | 301 40 | 181 41 | 42 | 43 | 44 | QFrame::NoFrame 45 | 46 | 47 | QFrame::Raised 48 | 49 | 50 | 51 | 0 52 | 53 | 54 | 0 55 | 56 | 57 | 0 58 | 59 | 60 | 0 61 | 62 | 63 | 64 | 65 | QFrame::NoFrame 66 | 67 | 68 | true 69 | 70 | 71 | 72 | 73 | 74 | 75 | 3 76 | 77 | 78 | 0 79 | 80 | 81 | Qt::AlignCenter 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 10 91 | 197 92 | 301 93 | 35 94 | 95 | 96 | 97 | 98 | 16777215 99 | 35 100 | 101 | 102 | 103 | Start update 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/utilities.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_mainwindow.h" 2 | #include "utilities.h" 3 | #include "window.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | Utilities utils; 13 | 14 | Utilities::Utilities() : settings("solusipse", "web-to-webm") { 15 | killed = false; 16 | } 17 | 18 | void Utilities::setCommons() { 19 | QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true); 20 | win.ui->player->settings()->setAttribute(QWebSettings::PluginsEnabled, true); 21 | } 22 | 23 | void Utilities::addToLog(QString line, bool display) { 24 | if (line.length() < 3) 25 | return; 26 | line.prepend("[" + QTime().currentTime().toString() + "] "); 27 | if (display) 28 | win.ui->logBox->appendHtml(line); 29 | 30 | line.replace("
", "").replace("", "").replace("", ""); 31 | if (!log.contains("\n")) 32 | log.append(line + "\n"); 33 | else 34 | log.append(line); 35 | } 36 | 37 | bool Utilities::checkUrl() { 38 | if (currentVideoUrl.isEmpty()) { 39 | addToLog("Add valid video first."); 40 | return false; 41 | } 42 | addToLog("
Started downloading video:
" + win.ui->urlEdit->text()); 43 | return true; 44 | } 45 | 46 | QVector< QVector > Utilities::createQualityList(QString formats) { 47 | QVector< QVector > list; 48 | QStringList formatsList = formats.split("\n"); 49 | 50 | /* 51 | * there are always clips in webm and mp4 with same resolution 52 | * since mp4 provides better quality and there is sometimes 53 | * higher quality in mp4 format available, script is excluding webm 54 | * at this moment 55 | */ 56 | 57 | for (int i = formatsList.length()-1; i >= 0 ; i--) { 58 | if (!formatsList[i].contains("webm")) 59 | if (!formatsList[i].contains("audio only")) 60 | if (!formatsList[i].contains("video only")) { 61 | QRegExp resolution("\\d{3,4}x\\d{3,4}"); 62 | resolution.indexIn(formatsList[i]); 63 | QRegExp format("\\w\\w\\w"); 64 | format.indexIn(formatsList[i]); 65 | 66 | QString strResolution = resolution.capturedTexts()[0]; 67 | QString strFormat = format.capturedTexts()[0]; 68 | 69 | if (strFormat != "" && formatsList[i].contains("unknown")) 70 | strResolution = "unknown"; 71 | 72 | if (strResolution != "" && strFormat != "") { 73 | QString strCode = formatsList[i].split(" ")[0]; 74 | QVector single; 75 | single.append(strResolution); 76 | single.append(strCode); 77 | single.append(strFormat); 78 | list.append(single); 79 | } 80 | } 81 | } 82 | return list; 83 | } 84 | 85 | QString Utilities::getVideoQuality() { 86 | return currentQualityList[win.ui->qualityComboBox->currentIndex()][1]; 87 | } 88 | 89 | QString Utilities::getFileName() { 90 | return currentID + "-" + currentQualityList[win.ui->qualityComboBox->currentIndex()][1] + "." + currentQualityList[win.ui->qualityComboBox->currentIndex()][2]; 91 | } 92 | 93 | void Utilities::killProcesses() { 94 | if (utils.downloadProcess != NULL) { 95 | killed = true; 96 | utils.downloadProcess->kill(); 97 | } 98 | if (utils.conversionProcess != NULL) { 99 | killed = true; 100 | utils.conversionProcess->kill(); 101 | } 102 | } 103 | 104 | QString Utilities::getBinaryName() { 105 | if (!configGetValue("youtubedl_path").isEmpty()) 106 | return configGetValue("youtubedl_path"); 107 | 108 | if (SYSTEM == 0) 109 | return "youtube-dl.exe"; 110 | if (SYSTEM == 1) 111 | return QCoreApplication::applicationDirPath() + "/youtube-dl"; 112 | return "youtube-dl"; 113 | } 114 | 115 | QString Utilities::ffmpegBinaryName() { 116 | if (!configGetValue("ffmpeg_path").isEmpty()) 117 | return configGetValue("ffmpeg_path"); 118 | 119 | if (SYSTEM == 0) 120 | return "ffmpeg.exe"; 121 | if (SYSTEM == 1) 122 | return QCoreApplication::applicationDirPath() + "/ffmpeg"; 123 | return "ffmpeg"; 124 | } 125 | 126 | QString Utilities::getCurrentRawFilename() { 127 | QString path = QFileInfo(getCurrentFilename()).absolutePath(); 128 | QString name = QFileInfo(getCurrentFilename()).baseName(); 129 | return path + "/" + name + "." + currentQualityList[win.ui->qualityComboBox->currentIndex()][2]; 130 | } 131 | 132 | QString Utilities::getCurrentFilename() { 133 | if (pathChanged) 134 | return currentFileName; 135 | 136 | return getDefaultFilename(); 137 | } 138 | 139 | QString Utilities::getDefaultFilename() { 140 | if (configGetValue("default_path").isEmpty()) 141 | return QDir().homePath() + "/" + QFileInfo(getFileName()).baseName() + ".webm"; 142 | return configGetValue("default_path") + "/" + QFileInfo(getFileName()).baseName() + ".webm"; 143 | } 144 | 145 | int Utilities::getTrimmedVideoDuration() { 146 | QString cutFrom = win.ui->cutFromEdit->text(); 147 | QString cutTo = win.ui->cutToEdit->text(); 148 | int time = parseTime(cutFrom).secsTo(parseTime(cutTo)); 149 | return time; 150 | } 151 | 152 | QTime Utilities::parseTime(QString s) { 153 | int occurences = s.count(":"); 154 | 155 | if (occurences == 0) { 156 | return QTime(0,0,0).addSecs(s.toInt()); 157 | } 158 | if (occurences == 1) { 159 | return QTime::fromString(s, "mm:ss"); 160 | } 161 | if (occurences == 2) { 162 | return QTime::fromString(s, "hh:mm:ss"); 163 | } 164 | 165 | return QTime(); 166 | } 167 | 168 | void Utilities::startProcessQueue(QString url) { 169 | addToLog("Loading video details."); 170 | win.setLoaderHtml(); 171 | 172 | currentVideoUrl = url; 173 | 174 | cmdsProcessQueue.clear(); 175 | // Title 176 | cmdsProcessQueue << getBinaryName() + " -e " + url; 177 | // ID 178 | cmdsProcessQueue << getBinaryName() + " --get-id " + url; 179 | // Quality list 180 | cmdsProcessQueue << getBinaryName() + " -F " + url; 181 | 182 | if (processQueue != NULL) { 183 | processQueue->blockSignals(true); 184 | processQueue->kill(); 185 | } 186 | 187 | processQueue = new QProcess; 188 | processQueue->setProcessChannelMode(QProcess::MergedChannels); 189 | processQueue->start(cmdsProcessQueue[0]); 190 | 191 | processQueue->blockSignals(false); 192 | 193 | connect(processQueue, SIGNAL(readyReadStandardOutput()), this, SLOT(readProcessQueue())); 194 | connect(processQueue, SIGNAL(finished(int)), this, SLOT(nextProcessQueue(int))); 195 | connect(processQueue, SIGNAL(error(QProcess::ProcessError)), this, SLOT(errorProcessQueue())); 196 | } 197 | 198 | void Utilities::errorProcessQueue() { 199 | addToLog("Could not open youtube-dl. Check if binary exists."); 200 | win.setPlayerHtml(); 201 | } 202 | 203 | void Utilities::newVideo() { 204 | utils.killProcesses(); 205 | win.setPlayerHtml(); 206 | win.ui->titleEdit->clear(); 207 | win.ui->urlEdit->clear(); 208 | win.reset(); 209 | win.lockAllControls(true); 210 | } 211 | 212 | void Utilities::readProcessQueue() { 213 | QString line = QString::fromLocal8Bit(processQueue->readAll()); 214 | if (cmdsProcessQueue.length() >= 1) { 215 | 216 | if (cmdsProcessQueue.length() == 3) { 217 | if (line.contains("is not a valid URL.") || line.contains("ERROR: Unable to download webpage: ")) 218 | currentVideoUrl = "error_url"; 219 | else 220 | currentTitle = line.simplified(); 221 | } 222 | if (cmdsProcessQueue.length() == 2) 223 | currentID = line.simplified(); 224 | 225 | cmdsProcessQueue.removeFirst(); 226 | } 227 | else { 228 | currentQualityList = createQualityList(line); 229 | } 230 | } 231 | 232 | void Utilities::nextProcessQueue(int c) { 233 | if (cmdsProcessQueue.length() <= 0 || currentVideoUrl == "error_url" || c != 0) { 234 | loadVideo(currentVideoUrl); 235 | return; 236 | } 237 | 238 | processQueue->kill(); 239 | processQueue = new QProcess; 240 | processQueue->start(cmdsProcessQueue[0]); 241 | 242 | connect(processQueue, SIGNAL(readyReadStandardOutput()), this, SLOT(readProcessQueue())); 243 | connect(processQueue, SIGNAL(finished(int)), this, SLOT(nextProcessQueue(int))); 244 | } 245 | 246 | void Utilities::loadVideo(QString url) { 247 | 248 | if (url == "error_url" || url.isEmpty()) { 249 | win.setPlayerHtml(); 250 | utils.addToLog("Error: provided url is incorrect."); 251 | utils.currentVideoUrl = ""; 252 | return; 253 | } 254 | 255 | killProcesses(); 256 | pathChanged = false; 257 | win.setVideoDetails(url); 258 | addToLog("Loaded video:
" + win.ui->urlEdit->text()); 259 | 260 | } 261 | 262 | void Utilities::configInit() { 263 | configSetValueIfBlank("remove_sound", "false"); 264 | configSetValueIfBlank("dont_convert", "false"); 265 | configSetValueIfBlank("remove_raw", "true"); 266 | configSetValueIfBlank("default_path", ""); 267 | configSetValueIfBlank("open_output", "false"); 268 | configSetValueIfBlank("show_ytdl_log", "true"); 269 | configSetValueIfBlank("show_ffmpeg_log", "true"); 270 | configSetValueIfBlank("youtubedl_path", ""); 271 | configSetValueIfBlank("ffmpeg_path", ""); 272 | configSetValueIfBlank("ffmpeg_params", ""); 273 | configSetValueIfBlank("light_mode", "false"); 274 | configSetValue("version", VERSION); 275 | } 276 | 277 | void Utilities::configSaveAll() { 278 | settings.setValue("remove_sound", win.ui->menuRemoveAudio->isChecked()); 279 | settings.setValue("dont_convert", win.ui->menuDontConvert->isChecked()); 280 | settings.setValue("remove_raw", win.ui->menuRemoveRawVideo->isChecked()); 281 | settings.setValue("open_output", win.ui->menuShowFile->isChecked()); 282 | settings.setValue("show_ytdl_log", win.ui->menuYoutubedlOutput->isChecked()); 283 | settings.setValue("show_ffmpeg_log", win.ui->menuFfmpegOutput->isChecked()); 284 | } 285 | 286 | void Utilities::configLoadAll() { 287 | win.ui->menuRemoveAudio->setChecked(configGetValueBool("remove_sound")); 288 | win.ui->menuDontConvert->setChecked(configGetValueBool("dont_convert")); 289 | win.ui->menuRemoveRawVideo->setChecked(configGetValueBool("remove_raw")); 290 | win.ui->menuShowFile->setChecked(configGetValueBool("open_output")); 291 | win.ui->menuYoutubedlOutput->setChecked(configGetValueBool("show_ytdl_log")); 292 | win.ui->menuFfmpegOutput->setChecked(configGetValueBool("show_ffmpeg_log")); 293 | } 294 | 295 | void Utilities::configSetValue(QString k, QString v) { 296 | settings.setValue(k, v); 297 | } 298 | 299 | void Utilities::configSetValueIfBlank(QString k, QString v) { 300 | if (!settings.contains(k)) 301 | settings.setValue(k, v); 302 | } 303 | 304 | QString Utilities::configGetValue(QString k) { 305 | return settings.value(k).toString(); 306 | } 307 | 308 | bool Utilities::configGetValueBool(QString k) { 309 | return settings.value(k).toBool(); 310 | } 311 | 312 | void Utilities::removeRawVideo() { 313 | QFile::remove(getCurrentRawFilename()); 314 | } 315 | 316 | void Utilities::showFileInDirectory() { 317 | QDesktopServices::openUrl("file:///" + QFileInfo(getCurrentFilename()).absolutePath()); 318 | } 319 | 320 | void Utilities::updateBitrate() { 321 | if (!win.ui->cutFromEdit->text().trimmed().isEmpty() && !win.ui->cutToEdit->text().trimmed().isEmpty()) { 322 | int bitrate = win.ui->bitrateValue->text().toInt(); 323 | bitrate = bitrate*utils.getTrimmedVideoDuration()/8; 324 | if (bitrate <= 0) { 325 | win.ui->estSize->setText(""); 326 | return; 327 | } 328 | win.ui->estSize->setText(QString::number(float(bitrate)/1000) + " MB"); 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H 2 | #define UTILITIES_H 3 | 4 | #include "mainwindow.h" 5 | #include "downloader.h" 6 | #include "converter.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef _WIN32 13 | #define SYSTEM 0 14 | #elif __APPLE__ 15 | #define SYSTEM 1 16 | #else 17 | #define SYSTEM 2 18 | #endif 19 | 20 | class Utilities : public QObject 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | Utilities(); 26 | 27 | QSettings settings; 28 | 29 | QString getBinaryName(); 30 | QString getVideoQuality(); 31 | QString getFileName(); 32 | QString ffmpegBinaryName(); 33 | QString getCurrentRawFilename(); 34 | QString getCurrentFilename(); 35 | QString getDefaultFilename(); 36 | 37 | bool checkUrl(); 38 | void setCommons(); 39 | void startConversionProcess(); 40 | void killProcesses(); 41 | void addToLog(QString line, bool display = true); 42 | void removeRawVideo(); 43 | void configInit(); 44 | void configSetValue(QString k, QString v); 45 | void configSetValueIfBlank(QString k, QString v); 46 | void showFileInDirectory(); 47 | void configSaveAll(); 48 | void configLoadAll(); 49 | void loadVideo(QString url); 50 | void updateBitrate(); 51 | void startProcessQueue(QString url); 52 | void newVideo(); 53 | 54 | QString configGetValue(QString k); 55 | bool configGetValueBool(QString k); 56 | 57 | int getTrimmedVideoDuration(); 58 | QTime parseTime(QString s); 59 | 60 | int currentDuration; 61 | bool pathChanged; 62 | bool killed; 63 | 64 | QString defaultFilename; 65 | QString currentID; 66 | QString currentTitle; 67 | QString currentVideoUrl; 68 | QString currentSavePath; 69 | QString currentFileName; 70 | QString log; 71 | 72 | QVector > createQualityList(QString formats); 73 | QVector > currentQualityList; 74 | 75 | QProcess *downloadProcess; 76 | QProcess *conversionProcess; 77 | QProcess *processQueue; 78 | 79 | QStringList cmdsProcessQueue; 80 | 81 | Downloader download; 82 | Converter convert; 83 | 84 | private slots: 85 | void readProcessQueue(); 86 | void nextProcessQueue(int c); 87 | void errorProcessQueue(); 88 | 89 | }; 90 | 91 | extern Utilities utils; 92 | 93 | #endif // UTILITIES_H 94 | -------------------------------------------------------------------------------- /src/window.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_mainwindow.h" 2 | #include "utilities.h" 3 | #include "window.h" 4 | 5 | #include 6 | #include 7 | 8 | Window win; 9 | 10 | Window::Window() { 11 | } 12 | 13 | void Window::setTheme() { 14 | qApp->setStyle(QStyleFactory::create("Fusion")); 15 | QPalette p = qApp->palette(); 16 | p.setColor(QPalette::Window, QColor("#333")); 17 | p.setColor(QPalette::Button, QColor("#333")); 18 | p.setColor(QPalette::Highlight, QColor("#b31217")); 19 | p.setColor(QPalette::ButtonText, QColor("#888")); 20 | p.setColor(QPalette::WindowText, QColor("#888")); 21 | p.setColor(QPalette::Dark, QColor("#333")); 22 | p.setColor(QPalette::Light, QColor("#333")); 23 | qApp->setPalette(p); 24 | setStylesheet(); 25 | } 26 | 27 | void Window::setStylesheet() { 28 | const char *css = 29 | "QPushButton:hover{background:#7d1cb4;}" 30 | "QWebView{background:#333;} QPlainTextEdit{background:#333;}" 31 | "QLineEdit{background:#888;border:none;height:35px;padding-left:10px;padding-right:10px;color:#222;}" 32 | "QProgressBar{background:#888; border:none;height:35px;color:#333;}" 33 | "QProgressBar::chunk{background:#b31217;}" 34 | "QPlainTextEdit{color:#888;}" 35 | "QComboBox{height:35px;border:none;background:#888;color:#333;}" 36 | "QComboBox::drop-down{border:none;background:#222;}" 37 | "QComboBox::drop-down::pressed{background:#b31217;}" 38 | "QComboBox QAbstractItemView{padding:15px;background:#888;}" 39 | "QMenu {background:#333;}" 40 | "QMenu::item{}" 41 | "QMenu::item::selected{background:#b31217;}" 42 | "QPushButton{border:none;background-color:#888;padding:25px;color:#333;}" 43 | "QPushButton::hover{background:#b31217;}" 44 | "QMessageBox{background:#222;}" 45 | "QToolButton{background:#222;border:none;width:35px;height:35px;}" 46 | "QToolButton:hover{background:#b31217;}" 47 | "#logBox{background:#333;border: 1px solid #3a3a3a;}" 48 | "QSpinBox{background:#888;border:none;height:35px;}"; 49 | 50 | qApp->setStyleSheet(css); 51 | } 52 | 53 | bool Window::isModeLight() { 54 | if (win.ui->player == NULL) 55 | return true; 56 | return false; 57 | } 58 | 59 | void Window::setPlayerHtml() { 60 | if (isModeLight()) { 61 | return; 62 | } 63 | 64 | const char *html = 65 | "" 67 | ""; 68 | 69 | win.ui->player->setHtml(html); 70 | } 71 | 72 | void Window::setLoaderHtml() { 73 | if (isModeLight()) { 74 | return; 75 | } 76 | 77 | const char *html = 78 | "" 80 | ""; 81 | 82 | win.ui->player->setHtml(html); 83 | } 84 | 85 | void Window::setQualityList() { 86 | win.ui->qualityComboBox->blockSignals(true); 87 | for (int i=0; i < utils.currentQualityList.size(); i++) 88 | win.ui->qualityComboBox->addItem(utils.currentQualityList[i][0]); 89 | win.ui->qualityComboBox->blockSignals(false); 90 | } 91 | 92 | void Window::setVideoDetails(QString url) { 93 | reset(); 94 | openUrlInPlayer(url); 95 | ui->titleEdit->setText(utils.currentTitle); 96 | setQualityList(); 97 | utils.currentFileName = utils.getDefaultFilename(); 98 | setFilename(); 99 | win.lockAllControls(false); 100 | } 101 | 102 | void Window::openUrlInPlayer(QString url) { 103 | if (isModeLight()) 104 | return; 105 | 106 | if (url.contains("youtu")) { 107 | win.ui->player->load("https://www.youtube.com/embed/" + utils.currentID); 108 | } 109 | else 110 | win.ui->player->load(url); 111 | } 112 | 113 | void Window::reset() { 114 | win.resetProgress(); 115 | win.ui->filenameEdit->clear(); 116 | win.ui->cutFromEdit->clear(); 117 | win.ui->cutToEdit->clear(); 118 | win.ui->qualityComboBox->blockSignals(true); 119 | win.ui->qualityComboBox->clear(); 120 | win.ui->qualityComboBox->blockSignals(false); 121 | win.ui->bitrateValue->clear(); 122 | win.ui->estSize->clear(); 123 | } 124 | 125 | void Window::resetProgress() { 126 | win.ui->downloadProgressBar->setValue(0); 127 | win.ui->conversionProgressBar->setValue(0); 128 | } 129 | 130 | void Window::lockAllControls(bool status) { 131 | win.ui->cutFromEdit->setDisabled(status); 132 | win.ui->cutToEdit->setDisabled(status); 133 | win.ui->selectSavePath->setDisabled(status); 134 | win.ui->startConversion->setDisabled(status); 135 | win.ui->bitrateValue->setDisabled(status); 136 | } 137 | 138 | void Window::setFilename() { 139 | win.ui->filenameEdit->setText(utils.getCurrentFilename()); 140 | } 141 | 142 | void Window::updateConversionButton() { 143 | QIcon icon; 144 | if (ui->startConversion->isChecked()) { 145 | icon = QIcon(":/icons/cancel.png"); 146 | } else { 147 | icon = QIcon(":/icons/convert.png"); 148 | } 149 | ui->startConversion->setIcon(icon); 150 | } 151 | 152 | void Window::toggleConversionButton() { 153 | ui->startConversion->toggle(); 154 | updateConversionButton(); 155 | } 156 | 157 | void Window::setLightMode(MainWindow *mw) { 158 | win.ui->player->deleteLater(); 159 | win.ui->player = NULL; 160 | win.ui->horizontalLayout_3->setDirection(QBoxLayout::TopToBottom); 161 | win.ui->frame_2->setMaximumWidth(QWIDGETSIZE_MAX); 162 | mw->setMinimumWidth(300); 163 | mw->setMaximumWidth(300); 164 | mw->setMaximumWidth(QWIDGETSIZE_MAX); 165 | mw->move(QApplication::desktop()->screen()->rect().center() - mw->rect().center()); 166 | } 167 | 168 | void Window::detectMode(MainWindow *mw) { 169 | if (utils.configGetValueBool("light_mode")) { 170 | win.ui->menuLtMode->setChecked(true); 171 | setLightMode(mw); 172 | } else { 173 | win.ui->menuLtMode->setChecked(false); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/window.h: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_H 2 | #define WINDOW_H 3 | 4 | #include "mainwindow.h" 5 | 6 | class Window 7 | { 8 | public: 9 | Window(); 10 | Ui::MainWindow *ui; 11 | 12 | void setQualityList(); 13 | void setVideoDetails(QString url); 14 | void reset(); 15 | void resetProgress(); 16 | void setFilename(); 17 | void lockAllControls(bool status); 18 | void setTheme(); 19 | void setStylesheet(); 20 | void updateConversionButton(); 21 | void toggleConversionButton(); 22 | void setPlayerHtml(); 23 | void setLoaderHtml(); 24 | void openUrlInPlayer(QString url); 25 | void setLightMode(MainWindow *mw); 26 | void detectMode(MainWindow *mw); 27 | bool isModeLight(); 28 | }; 29 | 30 | extern Window win; 31 | 32 | #endif // WINDOW_H 33 | -------------------------------------------------------------------------------- /web-to-webm.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2015-01-14T09:00:32 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | QT += webkit webkitwidgets 9 | 10 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 11 | 12 | VERSION = 0.7.2 13 | 14 | TARGET = web-to-webm 15 | TEMPLATE = app 16 | 17 | SOURCES += src/main.cpp\ 18 | src/mainwindow.cpp \ 19 | src/utilities.cpp \ 20 | src/downloader.cpp \ 21 | src/converter.cpp \ 22 | src/window.cpp \ 23 | src/updater.cpp 24 | 25 | HEADERS += src/mainwindow.h \ 26 | src/utilities.h \ 27 | src/downloader.h \ 28 | src/converter.h \ 29 | src/window.h \ 30 | src/updater.h 31 | 32 | FORMS += src/mainwindow.ui \ 33 | src/updater.ui 34 | 35 | DEFINES += VERSION=\\\"$$VERSION\\\" 36 | 37 | win32 { 38 | CONFIG(debug, debug|release) { 39 | renamer.commands = $(CXX) $$PWD/src/renamer.cpp && move renamer.exe debug\renamer.exe 40 | } else { 41 | renamer.commands = $(CXX) $$PWD/src/renamer.cpp && move renamer.exe release\renamer.exe 42 | } 43 | } 44 | unix:!macx { 45 | renamer.commands = $(CXX) $$PWD/src/renamer.cpp -o $$OUT_PWD/$(dir $(TARGET))renamer 46 | } 47 | macx { 48 | renamer.commands = mkdir -p $$OUT_PWD/$(dir $(TARGET)) && $(CXX) $$PWD/src/renamer.cpp -o $$OUT_PWD/$(dir $(TARGET))renamer 49 | } 50 | 51 | 52 | QMAKE_EXTRA_TARGETS += renamer 53 | POST_TARGETDEPS += renamer 54 | 55 | ICON = $${PWD}/resources/icons/web-to-webm.icns 56 | RC_FILE = $${PWD}/resources/web-to-webm.rc 57 | 58 | RESOURCES += \ 59 | resources/Resources.qrc 60 | --------------------------------------------------------------------------------