You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

441 lines
16 KiB

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "crunchyrollapi.h"
  4. #include "query.h"
  5. #include "collection.h"
  6. #include "episodes.h"
  7. #include "textpopup.h"
  8. #include <QDebug>
  9. #include <QSettings>
  10. #include <QDesktopServices>
  11. #include <QMessageBox>
  12. #include <QStyleFactory>
  13. MainWindow::MainWindow(QWidget *parent) :
  14. QMainWindow(parent),
  15. ui(new Ui::MainWindow)
  16. {
  17. ui->setupUi(this);
  18. settings = new QSettings("ThePBone","CrunchyrollDownloader");
  19. loadUserdata(false);
  20. for ( const auto& i : QStyleFactory::keys() ){
  21. QAction* act = new QAction(i);
  22. ui->menuTheme->addAction(act);
  23. connect(act, &QAction::triggered, this, [act]{
  24. qDebug() << "->" << act->text();
  25. qApp->setStyle(act->text());
  26. });
  27. }
  28. connect(ui->seriesview,SIGNAL(changeButtonClicked()),this,SLOT(openSearchDlg()));
  29. connect(ui->collectionview,SIGNAL(changeButtonClicked()),this,SLOT(openCollectionDlg()));
  30. connect(ui->episodeview,SIGNAL(changeButtonClicked()),this,SLOT(openEpisodesDlg()));
  31. connect(ui->progressview,SIGNAL(isProcessingStateChanged(bool)),this,SLOT(enableDlLayout(bool)));
  32. connect(ui->localeSelection,SIGNAL(currentIndexChanged(int)),this,SLOT(updateLocale()));
  33. connect(ui->updateSession,SIGNAL(clicked()),this,SLOT(reloadBlocks()));
  34. CrunchyrollAPI* _api = new CrunchyrollAPI();
  35. api = _api;
  36. ui->progressview->reset();
  37. connect(ui->dlmode_m3u, &QRadioButton::clicked, this, [this]() {
  38. updateProgressArgs();
  39. });
  40. connect(ui->dlmode_mp4, &QRadioButton::clicked, this, [this]() {
  41. updateProgressArgs();
  42. });
  43. connect(ui->dlmode_ogg, &QRadioButton::clicked, this, [this]() {
  44. updateProgressArgs();
  45. });
  46. connect(ui->dl_outdir_box, &QGroupBox::clicked, this, [this]() {
  47. updateProgressArgs();
  48. saveUserdata();
  49. });
  50. connect(ui->dl_outdir, &QLineEdit::textEdited, this, [this]() {
  51. updateProgressArgs();
  52. saveUserdata();
  53. });
  54. connect(ui->bypassGeoblock,&QCheckBox::clicked,this,[this](){saveUserdata();});
  55. connect(ui->useAccount,&QGroupBox::clicked,this,[this](){saveUserdata();});
  56. connect(ui->acc_mail,&QLineEdit::textEdited,this,[this](){saveUserdata();});
  57. connect(ui->quality_selection, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this]() {
  58. updateProgressArgs();
  59. });
  60. api->setLocale("enUS");
  61. }
  62. MainWindow::~MainWindow()
  63. {
  64. delete ui;
  65. }
  66. void MainWindow::saveUserdata(){
  67. settings->setValue("customOutputDir", ui->dl_outdir->text());
  68. settings->setValue("accountMail", ui->acc_mail->text());
  69. settings->setValue("useCustomOutputDir", ui->dl_outdir_box->isChecked());
  70. settings->setValue("useAccount", ui->useAccount->isChecked());
  71. settings->setValue("useProxyUS", ui->bypassGeoblock->isChecked());
  72. }
  73. void MainWindow::loadUserdata(bool loadAccount){
  74. QString customOutputDir = settings->value("customOutputDir", "").toString();
  75. QString accountMail = settings->value("accountMail", "").toString();
  76. bool useCustomOutputDir = settings->value("useCustomOutputDir",false).toBool();
  77. bool useAccount = settings->value("useAccount",false).toBool();
  78. bool useProxyUS = settings->value("useProxyUS",false).toBool();
  79. ui->dl_outdir->setText(customOutputDir);
  80. if(loadAccount)ui->acc_mail->setText(accountMail);
  81. if(loadAccount)ui->useAccount->setChecked(useAccount);
  82. ui->dl_outdir_box->setChecked(useCustomOutputDir);
  83. ui->bypassGeoblock->setChecked(useProxyUS);
  84. }
  85. void MainWindow::show()
  86. {
  87. QMainWindow::show();
  88. QApplication::processEvents();
  89. if(!checkFFmpeg()){
  90. #ifdef WIN32
  91. QMessageBox::critical(this,"Critical Error","FFMpeg binary not found\n"
  92. "Please place the ffmpeg.exe executable into the working directory of this program.");
  93. #else
  94. QMessageBox::critical(this,"Critical Error","FFMpeg package not found\n"
  95. "Please install the ffmpeg package");
  96. #endif
  97. }
  98. reloadBlocks(true);
  99. //Load account mail from memory *later*
  100. //because the password is not saved,
  101. //we would get an api error when initialing a session on startup
  102. loadUserdata(true);
  103. }
  104. bool MainWindow::checkFFmpeg(){
  105. #ifdef WIN32
  106. QFile f("ffmpeg.exe");
  107. return f.exists();
  108. #elif __APPLE__
  109. return true; //do not check on macOS
  110. #else
  111. if (system("which ffmpeg > /dev/null 2>&1") == 0)
  112. return true;
  113. return false;
  114. #endif
  115. }
  116. void MainWindow::updateProgressArgs(){
  117. if(ui->dl_outdir_box->isChecked() && ui->dl_outdir->text() != "")
  118. ui->progressview->setArgs(api,currentEpisodes,ui->dlmode_m3u->isChecked(),
  119. ui->dlmode_ogg->isChecked(),ui->dl_outdir->text(),getQuality());
  120. else
  121. ui->progressview->setArgs(api,currentEpisodes,ui->dlmode_m3u->isChecked(),
  122. ui->dlmode_ogg->isChecked(),".",getQuality());
  123. }
  124. void MainWindow::openSearchDlg(){
  125. query* dlg = new query(api);
  126. if(dlg->exec()){
  127. query_t query = dlg->getSelectedQuery();
  128. if(query.series_id == "")return;
  129. //Set current series
  130. currentSeries = query;
  131. reloadSeriesBlock();
  132. reloadCollectionBlock();
  133. reloadEpisodesBlock(true);
  134. }
  135. }
  136. void MainWindow::openCollectionDlg(){
  137. if(currentSeries.series_id==""){
  138. QMessageBox::critical(this,"Error","No series selected.\nPlease select one in the box above before proceeding.");
  139. return;
  140. }
  141. collection* dlg = new collection(api,currentSeries);
  142. if(dlg->exec()){
  143. QVector<collection_t> collections = dlg->getSelectedCollections();
  144. if(collections.count() < 1)return;
  145. //Set current collections
  146. currentCollections = collections;
  147. reloadCollectionBlock();
  148. reloadEpisodesBlock(true);
  149. }
  150. }
  151. void MainWindow::openEpisodesDlg(){
  152. if(currentCollections.count() < 1){
  153. QMessageBox::critical(this,"Error","No collections selected.\nPlease select one in the box above before proceeding.");
  154. return;
  155. }
  156. episodes* dlg = new episodes(api,allEpisodes,currentEpisodes);
  157. if(dlg->exec()){
  158. QVector<meta_episode_t> episodes = dlg->getSelectedEpisodes();
  159. if(episodes.count() < 1)return;
  160. //Set current episodes
  161. currentEpisodes = episodes;
  162. reloadEpisodesBlock(true);
  163. }
  164. }
  165. void MainWindow::reloadBlocks(bool onlyLogin){
  166. ui->collectionview->setEnabled(false);
  167. ui->seriesview->setEnabled(false);
  168. ui->updateSession->setEnabled(false);
  169. ui->episodeview->setEnabled(false);
  170. ui->progressview->setEnabled(false);
  171. ui->session_box->setEnabled(false);
  172. ui->sess_status->setText("Requesting new session id...");
  173. auto r_session = api->newSession(ui->bypassGeoblock->isChecked());
  174. api_status_t status_sess = r_session.second;
  175. session_t session = r_session.first;
  176. if(status_sess.failed){
  177. QMessageBox::critical(this,status_sess.api_error ? "API Error" : "Error","An " +
  178. QString(status_sess.api_error ? "api " : "") + "error has occurred\n" + status_sess.description);
  179. ui->sess_status->setText("No session active");
  180. ui->updateSession->setEnabled(true);
  181. ui->collectionview->setEnabled(true);
  182. ui->seriesview->setEnabled(true);
  183. ui->episodeview->setEnabled(true);
  184. ui->session_box->setEnabled(true);
  185. return;
  186. }
  187. ui->sess_id->setText(session.id);
  188. ui->sess_country->setText(session.country);
  189. ui->sess_status->setText("Anonymous session");
  190. ui->sess_premium->setText("false");
  191. ui->sess_premium->setStyleSheet("*{color:red;}");
  192. if(ui->useAccount->isChecked()){
  193. if(ui->bypassGeoblock->isChecked()){
  194. ui->sess_status->setText("Logging in via proxy...");
  195. auto r_acc = api->doProxyLogin(ui->acc_mail->text(),ui->acc_pass->text());
  196. api_status_t status = r_acc.second;
  197. if(status.failed){
  198. QMessageBox::critical(this,status.api_error ? "API Error" : "Error","An " +
  199. QString(status.api_error ? "api " : "") + "error has occurred\n" + status.description);
  200. ui->sess_status->setText("Anonymous session");
  201. ui->sess_premium->setText("false");
  202. ui->sess_premium->setStyleSheet("*{color:red;}");
  203. ui->updateSession->setEnabled(true);
  204. ui->collectionview->setEnabled(true);
  205. ui->seriesview->setEnabled(true);
  206. ui->episodeview->setEnabled(true);
  207. ui->progressview->setEnabled(true);
  208. ui->session_box->setEnabled(true);
  209. return;
  210. }
  211. user_t user = r_acc.first;
  212. if(user.username=="")
  213. user.premium = false;
  214. ui->sess_premium->setText(user.premium ? "true" : "false");
  215. ui->sess_premium->setStyleSheet(user.premium ? "*{color:green;}" : "*{color:red;}");
  216. ui->sess_id->setText(user.session_id);
  217. if(user.username!="")
  218. ui->sess_status->setText("Logged in (" + user.username + ")");
  219. }else{
  220. ui->sess_status->setText("Logging in...");
  221. auto r_acc = api->doLogin(ui->acc_mail->text(),ui->acc_pass->text());
  222. api_status_t status = r_acc.second;
  223. if(status.failed){
  224. QMessageBox::critical(this,status.api_error ? "API Error" : "Error","An " +
  225. QString(status.api_error ? "api " : "") + "error has occurred\n" + status.description);
  226. ui->updateSession->setEnabled(true);
  227. ui->collectionview->setEnabled(true);
  228. ui->seriesview->setEnabled(true);
  229. ui->episodeview->setEnabled(true);
  230. ui->session_box->setEnabled(true);
  231. ui->sess_premium->setText("false");
  232. ui->sess_premium->setStyleSheet("*{color:red;}");
  233. ui->sess_status->setText("Anonymous session");
  234. return;
  235. }
  236. user_t user = r_acc.first;
  237. if(user.username=="")
  238. user.premium = false;
  239. ui->sess_premium->setText(user.premium ? "true" : "false");
  240. ui->sess_premium->setStyleSheet(user.premium ? "*{color:green;}" : "*{color:red;}");
  241. ui->sess_id->setText(user.session_id);
  242. if(user.username!="")
  243. ui->sess_status->setText("Logged in (" + user.username + ")");
  244. else
  245. ui->sess_status->setText("Anonymous session");
  246. }
  247. }
  248. else
  249. ui->sess_status->setText("Anonymous session");
  250. if(onlyLogin){
  251. ui->episodeview->setEnabled(true);
  252. ui->collectionview->setEnabled(true);
  253. ui->seriesview->setEnabled(true);
  254. ui->updateSession->setEnabled(true);
  255. ui->progressview->setEnabled(true);
  256. ui->session_box->setEnabled(true);
  257. return;
  258. }
  259. reloadSeriesBlock();
  260. reloadCollectionBlock();
  261. reloadEpisodesBlock(true);
  262. ui->session_box->setEnabled(true);
  263. ui->progressview->setEnabled(true);
  264. ui->episodeview->setEnabled(true);
  265. ui->collectionview->setEnabled(true);
  266. ui->seriesview->setEnabled(true);
  267. ui->updateSession->setEnabled(true);
  268. }
  269. void MainWindow::reloadSeriesBlock(){
  270. //Fetch collections
  271. ui->collectionview->viewScanText();
  272. auto apiResponseC = api->getCollections(currentSeries.series_id.toInt());
  273. QVector<collection_t> collections = apiResponseC.first;
  274. if(apiResponseC.second.failed){
  275. ui->seriesview->setSeries(currentSeries,"");
  276. return;
  277. }
  278. ui->seriesview->setSeries(currentSeries,QString::number(apiResponseC.first.count()) + " collections/seasons");
  279. currentCollections = apiResponseC.first;
  280. }
  281. void MainWindow::reloadCollectionBlock(){
  282. ui->collectionview->setContent(currentCollections);
  283. }
  284. void MainWindow::reloadEpisodesBlock(bool redownload){
  285. ui->collectionview->setEnabled(false);
  286. ui->seriesview->setEnabled(false);
  287. ui->episodeview->setEnabled(false);
  288. ui->progressview->setEnabled(false);
  289. ui->session_box->setEnabled(false);
  290. QVector<meta_episode_t> episodes;
  291. ui->episodeview->viewScanText();
  292. if(currentEpisodes.count() < 1){
  293. for(auto collection : currentCollections){
  294. auto apiResponse = api->getEpisodes(collection.collection_id.toInt(),false);
  295. QVector<meta_episode_t> ep = apiResponse.first;
  296. if(apiResponse.second.failed){
  297. qDebug() << apiResponse.second.description;
  298. ui->collectionview->setEnabled(true);
  299. ui->episodeview->setEnabled(true);
  300. ui->progressview->setEnabled(true);
  301. ui->seriesview->setEnabled(true);
  302. ui->session_box->setEnabled(true);
  303. return;
  304. }
  305. for(auto e:ep){
  306. e.collection = collection;
  307. episodes.push_back(e);
  308. }
  309. }
  310. currentEpisodes = episodes;
  311. }
  312. else if(redownload){
  313. for(auto collection : currentCollections){
  314. auto apiResponse = api->getEpisodes(collection.collection_id.toInt(),false);
  315. QVector<meta_episode_t> ep = apiResponse.first;
  316. if(apiResponse.second.failed){
  317. qDebug() << apiResponse.second.description;
  318. ui->episodeview->setEnabled(true);
  319. ui->progressview->setEnabled(true);
  320. ui->collectionview->setEnabled(true);
  321. ui->seriesview->setEnabled(true);
  322. ui->session_box->setEnabled(true);
  323. return;
  324. }
  325. for(auto e:ep){
  326. e.collection = collection;
  327. episodes.push_back(e);
  328. }
  329. }
  330. allEpisodes = episodes;
  331. QVector<meta_episode_t> sel;
  332. for(auto cEp:currentEpisodes){
  333. QString id = cEp.media_id;
  334. //Find episode by media id
  335. for(auto ae:allEpisodes){
  336. if(ae.media_id==id){
  337. sel.push_back(ae);
  338. break;
  339. }
  340. }
  341. }
  342. currentEpisodes = sel;
  343. }
  344. allEpisodes = episodes;
  345. ui->episodeview->setEpisodes(currentEpisodes);
  346. ui->episodeview->setEnabled(true);
  347. ui->progressview->setEnabled(true);
  348. ui->collectionview->setEnabled(true);
  349. ui->seriesview->setEnabled(true);
  350. ui->session_box->setEnabled(true);
  351. updateProgressArgs();
  352. }
  353. void MainWindow::enableDlLayout(bool b){
  354. ui->seriesview->setEnabled(!b);
  355. ui->collectionview->setEnabled(!b);
  356. ui->episodeview->setEnabled(!b);
  357. ui->locale_box->setEnabled(!b);
  358. ui->session_box->setEnabled(!b);
  359. ui->dl_box->setEnabled(!b);
  360. }
  361. void MainWindow::updateLocale(){
  362. QString text = ui->localeSelection->currentText();
  363. if(text=="English (US)")api->setLocale("enUS");
  364. else if(text=="English (UK)")api->setLocale("enGB");
  365. else if(text=="Spanish")api->setLocale("esLA");
  366. else if(text=="Spanish (Spain/EU)")api->setLocale("esES");
  367. else if(text=="Portuguese (Brazil)")api->setLocale("ptBR");
  368. else if(text=="Portuguese (Portugal)")api->setLocale("ptPT");
  369. else if(text=="French (France)")api->setLocale("frFR");
  370. else if(text=="German")api->setLocale("deDE");
  371. else if(text=="Arabic")api->setLocale("arME");
  372. else if(text=="Russian")api->setLocale("ruRU");
  373. else if(text=="Italian")api->setLocale("itIT");
  374. }
  375. quality MainWindow::getQuality(){
  376. int i = ui->quality_selection->currentIndex();
  377. return (quality)i;
  378. }
  379. void MainWindow::showCredits(){
  380. QString data = "Unable to open HTML file";
  381. QFile file(":/html/credits.html");
  382. if(!file.open(QIODevice::ReadOnly))
  383. qDebug()<<"Unable to open HTML file. Line: " << __LINE__;
  384. else
  385. data = file.readAll();
  386. file.close();
  387. TextPopup *t = new TextPopup(data);
  388. t->show();
  389. }
  390. void MainWindow::showLicense(){
  391. QString data = "Unable to open HTML file";
  392. QFile file(":/html/license.html");
  393. if(!file.open(QIODevice::ReadOnly))
  394. qDebug()<<"Unable to open HTML file. Line: " << __LINE__;
  395. else
  396. data = file.readAll();
  397. file.close();
  398. TextPopup *t = new TextPopup(data);
  399. t->show();
  400. }
  401. void MainWindow::openGithub(){
  402. QDesktopServices::openUrl(QUrl("https://github.com/ThePBone/CrunchyrollDownloader"));
  403. }