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.
 
 
 
 

204 lines
8.9 KiB

  1. #include "mainwindow.h"
  2. #include "crunchyrollapi.h"
  3. #include "prompt.h"
  4. #include <QApplication>
  5. #include <QDebug>
  6. #include <QDir>
  7. #include <QProcess>
  8. #include <QStyle>
  9. #include <QCommandLineParser>
  10. ///NOTE (Windows): Append '/debug/' to the working directory (in projects tab of QtCreator)
  11. int main(int argc, char *argv[])
  12. {
  13. QApplication a(argc, argv);
  14. QCommandLineParser parser;
  15. parser.setApplicationDescription(BOLD(FHEADER("\nCrunchyroll Scraper/Downloader\n"))
  16. "Running this app without arguments will open the GUI by default.\n"
  17. "To use it in the shell, please add the '-t' flag in combination with\n"
  18. "any other parameter listed below.");
  19. parser.addHelpOption();
  20. QCommandLineOption terminalOption(QStringList() << "t" << "terminal",
  21. "Run in Shell/CLI-mode instead of launching the GUI");
  22. parser.addOption(terminalOption);
  23. QCommandLineOption accountOption(QStringList() << "p" << "account",
  24. "Login using specified (premium) account; "
  25. "email and password need to be separated by a colon", "account", "[email protected]:password");
  26. parser.addOption(accountOption);
  27. QCommandLineOption proxyOption(QStringList() << "u" << "unblock",
  28. "Bypass regional blocked content (use US proxy)");
  29. parser.addOption(proxyOption);
  30. QCommandLineOption allColOption(QStringList() << "C" << "all-collections",
  31. "Skip user interaction and download all collections/seasons");
  32. parser.addOption(allColOption);
  33. QCommandLineOption onlyM3UOption(QStringList() << "m" << "only-m3u",
  34. "Only download the M3U8 HLS playlists (fast)");
  35. parser.addOption(onlyM3UOption);
  36. QCommandLineOption overwriteOption(QStringList() << "y" << "overwrite",
  37. "Overwrite video file, if it already exists");
  38. parser.addOption(overwriteOption);
  39. QCommandLineOption localeOption(QStringList() << "l" << "locale",
  40. "Set desired <locale>; some content is only downloadable in some specific locales",
  41. "locale", "enUS");
  42. parser.addOption(localeOption);
  43. QCommandLineOption queryOption(QStringList() << "q" << "query",
  44. "Set a search query; the first result will be picked",
  45. "query", "");
  46. parser.addOption(queryOption);
  47. parser.process(a);
  48. if(!parser.isSet(terminalOption)){
  49. a.setStyle("Fusion");
  50. MainWindow w;
  51. a.setPalette(w.style()->standardPalette());
  52. w.show();
  53. return a.exec();
  54. }
  55. else {
  56. //CLI-Mode
  57. bool useAccount = parser.isSet(accountOption);
  58. bool useUSproxy = parser.isSet(proxyOption);
  59. bool allCollections = parser.isSet(allColOption);
  60. bool m3uOnly = parser.isSet(onlyM3UOption);
  61. bool overwrite = parser.isSet(overwriteOption);
  62. QString query = parser.value(queryOption);
  63. QString locale = parser.value(localeOption);
  64. QStringList mailpass = parser.value(accountOption).split(":");
  65. if(mailpass.count()!=2){
  66. qDebug(FRED("Invalid account\n"
  67. "You need to join the email and password of the account seperated by a colon\n"
  68. "Example: [email protected]:password123"));
  69. qApp->exit();
  70. }
  71. QString mail = mailpass.at(0);
  72. QString pass = mailpass.at(1);
  73. CrunchyrollAPI* api = new CrunchyrollAPI();
  74. if(locale!="")api->setLocale(locale);
  75. if(useUSproxy && useAccount){
  76. if(api->newSession(useUSproxy).second.failed) return 1;
  77. if(api->doProxyLogin(mail,pass).second.failed) return 1;
  78. }
  79. else{
  80. if(api->newSession(useUSproxy).second.failed) return 1;
  81. if(useAccount)
  82. if(api->doLogin(mail,pass).second.failed) return 1;
  83. }
  84. query_t result;
  85. if(query==""){
  86. auto apiResponse = api->searchMedia(prompt("Enter a search query:"));
  87. QVector<query_t> results = apiResponse.first;
  88. if(apiResponse.second.failed)
  89. exit(1);
  90. if(results.length() == 0) exit(1);
  91. else if (results.length() == 1)
  92. result = results.at(0);
  93. else
  94. result = promptQuery(results);
  95. }
  96. else{
  97. auto apiResponse = api->searchMedia(query);
  98. QVector<query_t> results = apiResponse.first;
  99. if(apiResponse.second.failed)
  100. exit(1);
  101. if(results.length() == 0) exit(1);
  102. result = results.at(0);
  103. }
  104. auto apiResponseC = api->getCollections(result.series_id.toInt());
  105. QVector<collection_t> collections = apiResponseC.first;
  106. if(apiResponseC.second.failed)
  107. exit(1);
  108. collection_t collection;
  109. if(!allCollections){ //If we want to download all collections via cmd-parameter, we don't need to select a single collection
  110. if(collections.length() == 0)qApp->quit();
  111. else if (collections.length() == 1)
  112. collection = collections.at(0);
  113. else
  114. collection = promptCollection(collections);
  115. }
  116. QVector<meta_episode_t> meta_episodes;
  117. if(collection.collection_id == "" || allCollections){
  118. //Download all collections
  119. auto apiResponse = api->getEpisodes(result.series_id.toInt(),true);
  120. meta_episodes = apiResponse.first;
  121. if(apiResponse.second.failed)
  122. exit(1);
  123. }
  124. else{
  125. //Download one collection
  126. auto apiResponse = api->getEpisodes(collection.collection_id.toInt(),false);
  127. meta_episodes = apiResponse.first;
  128. if(apiResponse.second.failed)
  129. exit(1);
  130. }
  131. QVector<episode_t> episodes;
  132. for(meta_episode_t meta_episode:meta_episodes){
  133. auto apiResponseE = api->getEpisode(meta_episode.media_id.toInt());
  134. episode_t episode = apiResponseE.first;
  135. api->printEpisodeDetails(episode);
  136. QString dir = QString(".") + QDir::separator() + episode.seriesname + QDir::separator() + episode.collectionname + QDir::separator();
  137. QDir d(dir);
  138. if(!d.exists())
  139. d.mkpath(".");
  140. auto apiResponseM3U = api->downloadM3U(episode);
  141. QString m3u = apiResponseM3U.first;
  142. if(m3u=="" || apiResponseM3U.second.failed)
  143. break;
  144. QFile file(dir + episode.seriesname + " e" + QString::number(episode.number) + " - " + episode.name + ".m3u8");
  145. if ( file.open(QIODevice::ReadWrite) )
  146. {
  147. QTextStream stream( &file );
  148. stream << m3u;
  149. }
  150. episodes.append(episode);
  151. }
  152. qDebug(FGREEN("\nM3U8 HLS playlists have been downloaded"));
  153. if(m3uOnly)
  154. return 0;
  155. qDebug(FHEADER("Starting video download..."));
  156. for(episode_t episode:episodes){
  157. std::cout << "Downloading " << KBOLD << "#" << episode.number << " - " << episode.name.toUtf8().constData() << RST << " (" << episode.collectionname.toUtf8().constData() << ")..." << std::endl;
  158. QString dir = QString(".") + QDir::separator() + episode.seriesname + QDir::separator() + episode.collectionname + QDir::separator();
  159. QDir d(dir);
  160. if(!d.exists())
  161. d.mkpath(".");
  162. for(stream_t stream:episode.streams){
  163. QString quality = stream.quality;
  164. if(quality.toLower() == "adaptive"){
  165. QString filename(dir + episode.seriesname + " e" + QString::number(episode.number) + " - " + episode.name + ".mp4");
  166. #ifdef WIN32
  167. QString program = "ffmpeg.exe";
  168. QStringList params = QStringList() << "-i" << stream.url.toString()
  169. << "-bsf:a" << "aac_adtstoasc" << "-c" << "copy"
  170. << "-stats" << "-v" << "quiet" << filename;
  171. #else
  172. QString program = "ffmpeg";
  173. QStringList params = QStringList() << "-i" << "\"" + stream.url.toString() + "\""
  174. << "-bsf:a" << "aac_adtstoasc" << "-c" << "copy"
  175. << "-stats" << "-v" << "quiet" << "\"" + filename + "\"";
  176. #endif
  177. if(overwrite) params.append("-y");
  178. system((program + " " + params.join(" ")).toStdString().c_str());
  179. break;
  180. }
  181. }
  182. }
  183. qDebug(FGREEN("\nAll downloads finished"));
  184. return 0;
  185. }
  186. }