#include #include #include #include #include #include #include #include "tvshow.h" #include "fileList.h" #include "thread.h" #include "iofiles.h" #include "utils.h" // JSON maker struct fileList *createTVShowDB(progConfig *conf) { printInfo("createTVShowDB info", true, "building TV shows' database...\n"); fileList *showFolders=find(conf, conf->TVpath, "", DI_MODE, false); if (showFolders != NULL ) { int foldersListSize=showFolders->listSize, i=0; pthread_t *threads=NULL; mallocMacro(threads, sizeof(pthread_t)*foldersListSize, "createTVShowDB error"); threadStruct *threadObj=NULL; mallocMacro(threadObj, sizeof(threadStruct)*foldersListSize, "createTVShowDB error"); cJSON *tempJSON=NULL; // open JSON database/set up cJSON if (conf->tvDB_exists==true) { tempJSON=cJSON_Duplicate(conf->JSON_tvDB, true); } for (fileList *tempList = showFolders; tempList != NULL; tempList = tempList->next, i++) { threadObj[i].conf=conf; threadObj[i].data=tempList->data; threadObj[i].oldJSON=NULL; threadObj[i].list=NULL; threadObj[i].id=0; if (conf->tvDB_exists==true && tempJSON!=NULL) { char *tvShowName=replaceAll(tempList->data[1], ".", " "); cJSON *item=NULL; cJSON_ArrayForEach(item, tempJSON){ if (cJSON_GetObjectItem(item, "show")!=NULL && cJSON_GetStringValue(cJSON_GetObjectItem(item, "show"))!=NULL) { char *tempStr=cJSON_GetStringValue(cJSON_GetObjectItem(item, "show")); if (strcmp(tempStr, tvShowName)==0) { // match threadObj[i].id=parseStrToInt(cJSON_GetStringValue(cJSON_GetObjectItem(item, "ID"))); threadObj[i].oldJSON=cJSON_DetachItemViaPointer(tempJSON, item); break; } } else { printError("createTVShowDB warning", false, HYEL, "some errors occured while reading \"%s\", parts of the database will be rebuilt from scratch...\n", conf->dbNameTV); threadObj[i].oldJSON=NULL; conf->tvDB_exists=false; cJSON_Delete(tempJSON); tempJSON=NULL; break; } } tryFree(tvShowName); } pthread_create(&threads[i], NULL, findSeasons, (void *) &threadObj[i]); } fileList *showJSON=NULL; for (i=0; inext) { // remove temp->data[1] (which should be the tv show name), meanwhile temp->data[0] is the whole show JSON tryFree(temp->data[temp->dataSize-1]); temp->dataSize--; } char *jsonStr=fileListToJSONStr(showJSON); if (jsonStr!=NULL) { cJSON_Delete(conf->JSON_tvDB); conf->JSON_tvDB=cJSON_Parse(jsonStr); } tryFree(jsonStr); return showJSON; } else { printError("createTVShowDB warning", false, HYEL, "could not find anything in \"%s\";\n", conf->TVpath); } return NULL; } void *findSeasons(void *threadArg) { // runs in Show.Name folder and spawns threads in every season folder threadStruct *thisThread=(threadStruct *) threadArg; progConfig *conf=thisThread->conf; size_t lenStr1=strlen(thisThread->data[0]); size_t lenStr2=strlen(thisThread->data[1]); // thisThread->data[1] name of tv show top folder char *showPath=NULL; mallocMacro(showPath, lenStr1+lenStr2+2, "findSeasons error"); strlcpy(showPath, thisThread->data[0], lenStr1+1); if (showPath[lenStr1 - 1] != '/') { strlcat(showPath, "/", lenStr1+lenStr2+2); } strlcat(showPath, thisThread->data[1], lenStr1+lenStr2+2); if (thisThread->data[1][lenStr2-1] != '/') { strlcat(showPath, "/", lenStr1+lenStr2+2); } // Find all the seasons of the current tv show printInfo("findSeasons info", true, "looking for seasons' folders in: \"%s\";\n", showPath); fileList *seasonsList = find(conf, showPath, "", DI_MODE, false); // get all seasons folders if (seasonsList!=NULL && seasonsList->data!=NULL) { bool showHasExtras=false; pthread_t *threads=NULL; mallocMacro(threads, sizeof(pthread_t)*seasonsList->listSize, "findSeasons error"); threadStruct *threadObj=NULL; mallocMacro(threadObj, sizeof(threadStruct)*seasonsList->listSize, "findSeasons error"); // TV show stuff int tmdb_id=thisThread->id; char *imgURL=NULL; char *tvShowName=replaceAll(thisThread->data[1], ".", " "); if (tmdb_id==0) { tmdb_id=getShowID(conf, tvShowName); // get the ID for this show } int i=0; for (fileList *tempList=seasonsList; tempList!=NULL; tempList=tempList->next, i++) { // run thread in every season folder size_t tempPathSize=strlen(showPath)+strlen(tempList->data[1])+2; char *tempPath=NULL; // path to season mallocMacro(tempPath, tempPathSize, "findSeasons error"); strlcpy(tempPath, showPath, tempPathSize); strlcat(tempPath, tempList->data[1], tempPathSize); if (tempPath[strlen(tempPath)-1]!='/') { strlcat(tempPath, "/", tempPathSize); } if (strstr(tempPath, conf->TVextraStr)) { showHasExtras=true; } // search for episodes & stuff threadObj[i].conf=thisThread->conf; threadObj[i].oldJSON=thisThread->oldJSON; threadObj[i].list=NULL; threadObj[i].data=NULL; mallocMacro(threadObj[i].data, sizeof(char **), "findSeasons error"); size_t tempSize=strlen(tempPath)+1; threadObj[i].data[0]=NULL; mallocMacro(threadObj[i].data[0], tempSize, "findSeasons error"); strlcpy(threadObj[i].data[0], tempPath, tempSize); threadObj[i].id=tmdb_id; pthread_create(&threads[i], NULL, findEpisodes, (void *) &threadObj[i]); tryFree(tempPath); } // get TV show poster! if (thisThread->oldJSON!=NULL) { char *tempStr=cJSON_GetStringValue(cJSON_GetObjectItem(thisThread->oldJSON, "Poster")); size_t tempStrLen=strlen(tempStr)+1; if (tempStrLen>3) { imgURL=NULL; mallocMacro(imgURL, tempStrLen, "findSeasons error"); strlcpy(imgURL, tempStr, tempStrLen); } } if (imgURL==NULL && thisThread->conf->getTVposter==true && tmdb_id>0) { imgURL=getShowPoster(thisThread->conf, tmdb_id); } if (imgURL==NULL) { // if still NULL, getShowPoster didn't work... imgURL=genImage(conf->AutogenImgResizeTVCmd, conf->dTVFolder, tvShowName); /* // useless on genImage? compressed results bigger than original if (conf->compressImgTV) { imgURL=compressImg(conf->compressImgTVCmd, imgURL, true); } */ } for (int j=0; jlist=joinLists(thisThread->list, threadObj[j].list); tryFree(threadObj[j].data[0]); tryFree(threadObj[j].data); } if (thisThread->list!=NULL) { if (thisThread->list->listSize>1) { msortList(&thisThread->list, 0, true, INTEGER); } size_t numOfSeasons=seasonsList->listSize; if (showHasExtras) { numOfSeasons--; } if (imgURL==NULL) { mallocMacro(imgURL, 1, "findSeasons error"); imgURL[0]='\0'; } size_t showJSONStrSize=strlen("{\"Show\":\"\",\"ID\":\"\",\"Poster\":\"\",\"Seasons\":\"\",\"Episodes\":[],\"Extras\":[]}")+strlen(tvShowName)+intSize(tmdb_id)+strlen(imgURL)+intSize(numOfSeasons)+1; char *showJSONStr=NULL; mallocMacro(showJSONStr, showJSONStrSize, "findSeasons error"); showJSONStr[0]='\0'; snprintf(showJSONStr, showJSONStrSize, "{\"Show\":\"%s\",\"ID\":\"%d\",\"Poster\":\"%s\",\"Seasons\":\"%zu\",\"Episodes\":[", tvShowName, tmdb_id, imgURL, numOfSeasons); i=0; size_t extrasJSONStrSize=2, j=0; char *extrasJSONStr=NULL; mallocMacro(extrasJSONStr, extrasJSONStrSize, "findSeasons error"); extrasJSONStr[0]='\0'; for (fileList *tempList=thisThread->list; tempList!=NULL; tempList=tempList->next) { if (tempList->dataSize>=2 && tempList->data[1]!=NULL) { if (strstr(tempList->data[1], conf->TVextraStr)!=NULL) { // check if this list comes from a 'extras season' for (size_t k=1; kdataSize; k++) { // iterate over all files in the 'extras' folder extrasJSONStrSize+=strlen(tempList->data[k]); extrasJSONStr=realloc(extrasJSONStr, extrasJSONStrSize); if (extrasJSONStr==NULL) fatalError_abort("findSeasons error", "could not realloc;\nError: %s;\n", strerror(errno)); if (j>0) { extrasJSONStrSize++; strlcat(extrasJSONStr, ",", extrasJSONStrSize); } strlcat(extrasJSONStr, tempList->data[k], extrasJSONStrSize); j++; } } else { //list from a regular season, not extras for (size_t k=1; kdataSize; k++) { showJSONStrSize+=strlen(tempList->data[k]); showJSONStr=realloc(showJSONStr, showJSONStrSize); if (showJSONStr==NULL) fatalError_abort("findSeasons error", "could not realloc;\nError: %s;\n", strerror(errno)); if (i>0) { showJSONStrSize++; strlcat(showJSONStr, ",", showJSONStrSize); } strlcat(showJSONStr, tempList->data[k], showJSONStrSize); i++; } } } } char *tempShowJSONStr=NULL; mallocMacro(tempShowJSONStr, showJSONStrSize, "findSeasons error"); strlcpy(tempShowJSONStr, showJSONStr, showJSONStrSize); showJSONStr=realloc(showJSONStr, showJSONStrSize+extrasJSONStrSize); if (showJSONStr==NULL) { fatalError_abort("findSeasons error", "could not realloc;\nError: %s;\n", strerror(errno)); } showJSONStr[0]='\0'; snprintf(showJSONStr, showJSONStrSize+extrasJSONStrSize, "%s],\"Extras\":[%s]}", tempShowJSONStr, extrasJSONStr); freeList(thisThread->list); thisThread->list=newList(); addData(thisThread->list, showJSONStr); addData(thisThread->list, tvShowName); tryFree(threads); tryFree(threadObj); freeList(seasonsList); tryFree(imgURL); tryFree(showJSONStr); tryFree(tvShowName); tryFree(extrasJSONStr); tryFree(tempShowJSONStr); } else { printError("findSeasons warning", false, HYEL, "please note, could not find any \".%s\" in path \"%s\"\n", videoExt, showPath); } } else { printError("findSeasons warning", false, HYEL, "please note, path \"%s\" is empty!\n", showPath); } tryFree(showPath); return NULL; } void *findEpisodes(void *threadArg) { // find files of 'this' season threadStruct *thisThread = (threadStruct *) threadArg; fileList *foundFiles=NULL; ioMode mode=TV_MODE | FI_MODE; if (strstr(thisThread->data[0], thisThread->conf->TVextraStr)!=NULL) { mode=FI_MODE; } printInfo("findEpisodes info", true, "looking for episodes in: \"%s\";\n", thisThread->data[0]); foundFiles=find(thisThread->conf, thisThread->data[0], videoExt, mode, false); // search for video files in this folder if (foundFiles==NULL) { thisThread->list=NULL; } else { size_t thisShowSeason=9999999; // "No season" as start value, because of sorting if ((mode & TV_MODE)!=0) { // if mode has TV_MODE bit set then we are NOT in the folder of the extras msortList(&foundFiles, 5, true, INTEGER); // sort list by data[5] ( episode number ) if (foundFiles->dataSize>4) { // getting season & not in Season.Extras thisShowSeason=parseStrToInt(foundFiles->data[4]); } else { // could not get current season from filename, trying by getting it from folder name if (foundFiles->dataSize>1) { // this only works if folder is named using the 'Something.1' format char *tempStrPtr=strrchr(foundFiles->data[1], '.'); size_t tempStrPtrLen=strlen(tempStrPtr); char *tempStr=NULL; mallocMacro(tempStr, tempStrPtrLen, "findEpisodes error"); tempStr[0]='\0'; if (tempStrPtr[tempStrPtrLen-1]=='/') { tempStrPtrLen--; } strlcpy(tempStr, tempStrPtr+1, tempStrPtrLen); thisShowSeason=parseStrToInt(tempStr); } } } fileList *jsonObjList=newList(); unsigned int showId=thisThread->id; size_t size=intSize(thisShowSeason)+1; char *tempStr=NULL; mallocMacro(tempStr, size, "findEpisodes error"); tempStr[0]='\0'; intToStr(tempStr, thisShowSeason); addData(jsonObjList, tempStr); for (fileList *temp=foundFiles; temp!=NULL; temp=temp->next) { //iterate through episodes char *epNum=NULL, *seNum=NULL, *epName=NULL, *epSubs=NULL; bool seasonExtra=false; if (thisThread->conf->tvDB_exists && strstr(thisThread->conf->tvDB_str, temp->data[0])) { if (thisThread->oldJSON!=NULL){ cJSON *JSONcopy=cJSON_Duplicate(thisThread->oldJSON, true); cJSON *episodesArray=cJSON_GetObjectItem(JSONcopy, "episodes"); cJSON *item=NULL; if (episodesArray!=NULL) { cJSON_ArrayForEach(item, episodesArray) { if (item!=NULL) { cJSON *obj=cJSON_GetObjectItem(item, "File"); if (obj!=NULL) { char *fileStr=cJSON_GetStringValue(obj); if (fileStr!=NULL && strcmp(fileStr, temp->data[0])==0) { //match item=cJSON_DetachItemViaPointer(episodesArray, item); char *tempSeNum=cJSON_GetStringValue(cJSON_GetObjectItem(item, "Season")); char *tempEpNum=cJSON_GetStringValue(cJSON_GetObjectItem(item, "Episode")); char *tempEpName=cJSON_GetStringValue(cJSON_GetObjectItem(item, "Title")); size_t tempSize=0; tempSize=strlen(tempSeNum)+1; if (tempSize>1) { seNum=NULL; mallocMacro(seNum, tempSize, "findEpisodes error"); strlcpy(seNum, tempSeNum, tempSize); } tempSize=strlen(tempEpNum)+1; if (tempSize>1) { epNum=NULL; mallocMacro(epNum, tempSize, "findEpisodes error"); strlcpy(epNum, tempEpNum, tempSize); } tempSize=strlen(tempEpName)+1; if (tempSize>1) { epName=NULL; mallocMacro(epName, tempSize, "findEpisodes error"); strlcpy(epName, tempEpName, tempSize); } cJSON_Delete(item); break; } } } } cJSON_Delete(JSONcopy); } //} else { // printError("File \"%s\" found in tvDB_str but oldJSON==NULL\n", temp->data[0]); } } if (temp->data!=NULL && strstr(temp->data[0], thisThread->conf->TVextraStr)!=NULL) { seasonExtra=true; epName=removeExtension(temp->data[2]); // episode name is filename without extension } else { // get episodes' names /* temp->data 'format' * temp->data[0] full path to file ( ex: ../TV/Show/Season/Show.S01E01.TheOneThatIsAnExample.mp4 ) * temp->data[1] path to file's folder ( ex: ../TV/Show/Season/ ) * temp->data[2] filename ( ex: Show.S01E01.TheOneThatIsAnExample.mp4 ) now filename is split into regex groups... Default would be something like the following * temp->data[3] Show * temp->data[4] 01 * temp->data[5] 01 * temp->data[6] TheOneThatIsAnExample.mp4 */ if (temp->dataSize > 5 ) { // use regexTVgroups instead of hard coded number? See temp->data 'format' if (seNum==NULL) { size_t tempStrLen=strlen(temp->data[4])+1; seNum=NULL; mallocMacro(seNum, tempStrLen, "findEpisodes error"); strlcpy(seNum, temp->data[4], tempStrLen); } if (epNum==NULL) { size_t tempStrLen=strlen(temp->data[5])+1; epNum=NULL; mallocMacro(epNum, tempStrLen, "findEpisodes error"); strlcpy(epNum, temp->data[5], tempStrLen); } if (epNum!=NULL && seNum!=NULL && epName==NULL && thisThread->conf->getEpisodeName==true && showId!=0 ) { epName=getEpisodeName(thisThread->conf, showId, seNum, epNum, thisThread->conf->TMDBapi); } } else { // dataSize got weird, shouldn't really happen? if (temp->dataSize>0){ printError("findEpisodes warning", false, HYEL, "file \"%s\" has weird dataSize: %d;\n", temp->data[0], temp->dataSize); } } } if (thisThread->conf->createTVsubs) { // search for subs, convert srt to vtt - no matter if already in DB or not if (temp->dataSize>2) { epSubs=getSubs(thisThread->conf, temp->data[2], thisThread->data[0]); } } if (epName==NULL) { if (temp->dataSize>5 && seasonExtra==false) { if (strcmp(temp->data[6], videoExt)==0) { epName=removeExtension(temp->data[2]); } else { epName=removeExtension(temp->data[6]); } } else { epName=removeExtension(temp->data[2]); } } if (epSubs==NULL) { epSubs=NULL; mallocMacro(epSubs, 1, "findEpisodes error"); epSubs[0]='\0'; } if (seNum==NULL) { seNum=NULL; mallocMacro(seNum, 2, "findEpisodes error"); seNum[0]='0'; seNum[1]='\0'; } if (epNum==NULL) { epNum=NULL; mallocMacro(epNum, 2, "findEpisodes error"); epNum[0]='0'; epNum[1]='\0'; } char *epJSON=NULL; if (seasonExtra) { size_t epJSONStrSize=strlen("{\"Title\":\"\",\"File\":\"\",\"Subs\":[]}")+strlen(seNum)+strlen(epNum)+strlen(epName)+strlen(temp->data[0])+strlen(epSubs)+1; epJSON=NULL; mallocMacro(epJSON, epJSONStrSize, "findEpisodes error"); epJSON[0]='\0'; snprintf(epJSON, epJSONStrSize, "{\"Title\":\"%s\",\"File\":\"%s\",\"Subs\":[%s]}", epName, temp->data[0], epSubs); addData(jsonObjList, epJSON); } else if (temp->dataSize>0) { size_t epJSONStrSize=strlen("{\"Season\":\"\",\"Episode\":\"\",\"Title\":\"\",\"File\":\"\",\"Subs\":[]}")+strlen(seNum)+strlen(epNum)+strlen(epName)+strlen(temp->data[0])+strlen(epSubs)+1; epJSON=NULL; mallocMacro(epJSON, epJSONStrSize, "findEpisodes error"); epJSON[0]='\0'; snprintf(epJSON, epJSONStrSize, "{\"Season\":\"%s\",\"Episode\":\"%s\",\"Title\":\"%s\",\"File\":\"%s\",\"Subs\":[%s]}", seNum, epNum, epName, temp->data[0], epSubs); addData(jsonObjList, epJSON); } tryFree(epJSON); tryFree(epSubs); tryFree(epName); tryFree(epNum); tryFree(seNum); } tryFree(tempStr); thisThread->list=jsonObjList; } freeList(foundFiles); return NULL; } char *getShowPoster(progConfig *conf, unsigned int tmdb_id) { char *imgURL=NULL; size_t posterURLSize=strlen(tmdbSite)+strlen(tmdbTV)+intSize(tmdb_id)+strlen(tmdbP)+strlen(conf->TMDBapi)+strlen(tmdbP_Opts)+strlen(conf->prefImgLangTV)+2; char *posterURL=NULL; mallocMacro(posterURL, posterURLSize, "getShowPoster error"); posterURL[0]='\0'; snprintf(posterURL, posterURLSize, "%s%s%u%s%s%s,%s", tmdbSite, tmdbTV, tmdb_id, tmdbP, conf->TMDBapi, tmdbP_Opts, conf->prefImgLangTV); if (posterURL!=NULL) { imgURL=getPoster(posterURL, conf, conf->prefImgWidthTV, conf->prefImgRatioTV, conf->prefImgLangTV); if (imgURL==NULL) { printError("getShowPoster error", true, HRED, "failed while using URL '%s';\nRetrying without language option...\n", posterURL); posterURL[0]='\0'; snprintf(posterURL, posterURLSize, "%s%s%u%s%s", tmdbSite, tmdbTV, tmdb_id, tmdbP, conf->TMDBapi); imgURL=getPoster(posterURL, conf, conf->prefImgWidthTV, conf->prefImgRatioTV, NULL); } if (imgURL==NULL) { printError("getShowPoster error", true, HRED, "failed while using URL '%s';\n", posterURL); } else { printInfo("getShowPoster info", true, "got poster for \"%d\", URL: \"%s\";\n", tmdb_id, imgURL); if (imgURL!=NULL && conf->dTVImg) { // if image should actually be downloaded, otherwise will just link to imgURL if (checkFolder(conf->dTVFolder, true)==0) { size_t dlFileStrLen=strlen(imgURL)+strlen(conf->dTVFolder); char *dlFileName=NULL; mallocMacro(dlFileName, dlFileStrLen, "getShowPoster error"); dlFileName[0]='\0'; snprintf(dlFileName, dlFileStrLen, "%s%s", conf->dTVFolder, strrchr(imgURL, '/')+1); if (dlFile(conf, imgURL, dlFileName)==CURLE_OK) { // downloaded poster! imgURL=realloc(imgURL, dlFileStrLen+1); if (imgURL==NULL) fatalError_abort("getShowPoster error", "could not realloc;\nError: %s;\n", strerror(errno)); strlcpy(imgURL, dlFileName, dlFileStrLen); if (conf->compressImgTV) { imgURL=compressImg(conf->compressImgTVCmd, imgURL, true); } } tryFree(dlFileName); } } } tryFree(posterURL); } else { printError("getShowPoster error", false, HRED, "could not build URL request string, something went wrong...\n"); } return imgURL; } char *getEpisodeName(progConfig *conf, unsigned int showId, char *seNum, char *epNum, char *TMDBapi) { char *epName=NULL; char *season="/season/"; char *episode="/episode/"; char *apiOpts="?api_key="; size_t tempStrSize=strlen(tmdbSite)+strlen(tmdbTV)+intSize(showId)+strlen(season)+strlen(seNum)+strlen(episode)+strlen(epNum)+strlen(apiOpts)+strlen(TMDBapi)+1; char *tempStr=NULL; mallocMacro(tempStr, tempStrSize, "getEpisodeName error"); tempStr[0]='\0'; snprintf(tempStr, tempStrSize, "%s%s%u%s%s%s%s%s%s", tmdbSite, tmdbTV, showId, season, seNum, episode, epNum, apiOpts, TMDBapi); memBlock *mem=initBlock(); printInfo("getEpisodeName info", true, "finding episode name for show: \"%d\", season:\"%s\", episode: \"%s\", URL: \"%s\";\n", showId, seNum, epNum, tempStr); getRequest(conf, tempStr, mem, curlMemCb); cJSON *json_root=cJSON_Parse(mem->memory); if (json_root!=NULL) { cJSON *json_name=cJSON_DetachItemFromObject(json_root, "name"); if (json_name!=NULL){ char *tempEpName=cJSON_Print(json_name); if (tempEpName!=NULL) { // remove quotation marks at the beginning and end of string size_t tempEpNameSize=strlen(tempEpName); epName=NULL; mallocMacro(epName, tempEpNameSize+1, "getEpisodeName error"); strlcpy(epName, tempEpName+1, tempEpNameSize-1); } tryFree(tempEpName); } else { if (cJSON_GetNumberValue(cJSON_GetObjectItem(json_root, "status_code"))!=34) { printError("getEpisodeName warning", true, HYEL, " json_name==NULL - JSON received was:\n"); char *cJSONstr=cJSON_Print(json_root); printError("", true, HBLU, cJSONstr); printError("", true, HYEL, "\nrequest was: '%s%s%s';", HBLU, tempStr, HYEL); printError("", true, HYEL, "\nEND;\n%s", COLOR_RESET); tryFree(cJSONstr); } } cJSON_Delete(json_name); } else { printError("getEpisodeName warning", true, HYEL, " json_root==NULL\n"); } tryFree(tempStr); cJSON_Delete(json_root); freeBlock(mem); char *fixedEpName=replaceAll(epName, "\\\"", ""); tryFree(epName); return fixedEpName; } void createShowsHTML(progConfig *conf, fileList *list) { printInfo("createShowsHTML info", true, "building HTML for TV shows...\n"); if (list==NULL || list->dataSize==0) { fatalError_abort("createShowsHTML error", "list was NULL\n"); } if (checkFolder(conf->TVhtml, true)==-1) { fatalError_exit("createShowsHTML", "could not find \"%s\";\nExiting...\n", conf->TVhtml); } pthread_t *threads=NULL; mallocMacro(threads, sizeof(pthread_t)*list->listSize, "createShowsHTML error"); threadStruct *threadObj=NULL; mallocMacro(threadObj, sizeof(threadStruct)*list->listSize, "createShowsHTML error"); size_t i=0; for (fileList *temp=list; temp!=NULL; temp=temp->next) { threadObj[i].conf=conf; threadObj[i].data=temp->data; threadObj[i].oldJSON=NULL; threadObj[i].list=newList(); threadObj[i].id=i; pthread_create(&threads[i], NULL, showHTML, (void *) &threadObj[i]); i++; } i=0; fileList *htmlList=newList(); addData(htmlList, TV_HTML_TOP); char *htmlStr=NULL; for (fileList *temp=list; temp!=NULL; temp=temp->next) { pthread_join(threads[i], NULL); char *showFile=NULL; char *showPoster=NULL; if (checkFolder(threadObj[i].list->data[0], false)==0) { showFile=getRelativePath(conf->TVhtml, threadObj[i].list->data[0]); } else { fatalError_exit("createShowsHTML", "could not find \"%s\";\nExiting...\n", threadObj[i].list->data[0]); } if (checkFolder(threadObj[i].list->data[1], false)==0) { showPoster=getRelativePath(conf->TVhtml, threadObj[i].list->data[1]); } else { fatalError_exit("createShowsHTML", "could not find \"%s\";\nExiting...\n", threadObj[i].list->data[1]); } if (showPoster==NULL) { showPoster=NULL; mallocMacro(showPoster, 1, "createShowsHTML error"); showPoster[0]='\0'; } char *showName=threadObj[i].list->data[2]; size_t htmlStrSize=strlen(TV_HTML_FRAME)+strlen(showFile)+strlen(showPoster)+strlen(showName)*2+intSize(i)+1; htmlStr=realloc(htmlStr, htmlStrSize); if (htmlStr==NULL) { fatalError_abort("createShowsHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } snprintf(htmlStr, htmlStrSize, TV_HTML_FRAME, i, showName, showFile, showPoster, showName, i, i, i); addData(htmlList, htmlStr); freeList(threadObj[i].list); tryFree(showFile); tryFree(showPoster); i++; } addData(htmlList, TV_HTML_BOT); fileListToFile(htmlList, conf->TVhtml, "", ""); tryFree(threadObj); tryFree(threads); tryFree(htmlStr); freeList(htmlList); } void *showHTML(void *threadArg) { threadStruct *thisThread=threadArg; fileList *this_show=newList(); cJSON *myJSON=cJSON_Parse(thisThread->data[0]); if (myJSON==NULL) { printError("showHTML warning", false, HYEL, " myJSON was NULL!\tThis show's JSON is unparsable!\nJSON dump:\n%s\n", thisThread->data[0]); return NULL; } cJSON *showObj=cJSON_GetObjectItem(myJSON, "Show"); cJSON *posObj=cJSON_GetObjectItem(myJSON, "Poster"); char *showPoster=NULL; char *showName=NULL; if (posObj!=NULL && cJSON_GetStringValue(posObj)!=NULL) { showPoster=cJSON_GetStringValue(posObj); } if (showObj!=NULL && cJSON_GetStringValue(showObj)!=NULL) { showName=cJSON_GetStringValue(showObj); } if (showPoster==NULL) { showPoster=genImage(thisThread->conf->AutogenImgResizeTVCmd, thisThread->conf->dTVFolder, showName); /* // useless on genImage? compressed results bigger than original if (thisThread->conf->compressImgTV) { showPoster=compressImg(thisThread->conf->compressImgTVCmd, showPoster, true); } */ } if (showName==NULL) { fatalError_abort("showHTML error", "showName==NULL; JSON was:\n%s\n--- END ---\n", cJSON_Print(myJSON)); } int uuid=thisThread->id; cJSON *episodesArray=cJSON_GetObjectItem(myJSON, "Episodes"); cJSON *extrasArray=cJSON_GetObjectItem(myJSON, "Extras"); if (episodesArray==NULL || extrasArray==NULL) { fatalError_abort("showHTML error", " episodesArray or extrasArray were equal to NULL; JSON was:\n%s\n--- END ---\n ", cJSON_Print(myJSON)); } printInfo("showHTML info", true, "building HTML for \"%s\";\n", showName); cJSON *episode=NULL; addData(this_show, SHOW_HTML_TOP); int currSeason=0; int uuidEpisode=0; size_t tempStrSize=strlen(SHOW_HTML_SEL)+intSize(uuid)+1; //'"); currSeason=0; cJSON_ArrayForEach(episode, episodesArray) { episodeHTML(this_show, thisThread->conf, episode, &currSeason, &uuid, &uuidEpisode); } addData(this_show, "\n"); if (extrasArray!=NULL && cJSON_GetArraySize(extrasArray)>0) { currSeason++; tempStrSize=intSize(uuid)+intSize(currSeason)+strlen(SHOW_HTML_UL)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) fatalError_abort("showHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); snprintf(tempStr, tempStrSize, SHOW_HTML_UL, uuid, currSeason); addData(this_show, tempStr); cJSON *extra=NULL; cJSON_ArrayForEach(extra, extrasArray) { episodeHTML(this_show, thisThread->conf, extra, &currSeason, &uuid, &uuidEpisode); } addData(this_show, "\n"); } tempStrSize=intSize(uuid)+strlen(SHOW_HTML_BOT)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) fatalError_abort("showHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); snprintf(tempStr, tempStrSize, SHOW_HTML_BOT, uuid); addData(this_show, tempStr); char *fileName=replaceAll(showName, " ", ""); tempStrSize=strlen(fileName)+strlen(thisThread->conf->showHTMLFolder)+16; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) { fatalError_abort("showHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } if (thisThread->conf->showHTMLFolder[strlen(thisThread->conf->showHTMLFolder)-1]=='/') { snprintf(tempStr, tempStrSize, "%sTV%s.html", thisThread->conf->showHTMLFolder, fileName); } else { snprintf(tempStr, tempStrSize, "%s/TV%s.html", thisThread->conf->showHTMLFolder, fileName); } if (checkFolder(thisThread->conf->showHTMLFolder, true)==0) { fileListToFile(this_show, tempStr, NULL, NULL); addData(thisThread->list, tempStr); addData(thisThread->list, showPoster); addData(thisThread->list, fileName); } if (posObj==NULL || cJSON_GetStringValue(posObj)==NULL) { tryFree(showPoster); } tryFree(fileName); tryFree(tempStr); cJSON_Delete(myJSON); freeList(this_show); return NULL; } void episodeHTML(fileList *this_show, progConfig *conf, cJSON *episode, int *currSeason, int *uuid, int *uuidEpisode) { int this_seasonNum=0; cJSON *jsonSeason=cJSON_GetObjectItem(episode, "Season"); if (jsonSeason!=NULL) { this_seasonNum=parseStrToInt(cJSON_GetStringValue(jsonSeason)); } char *this_episodeName=cJSON_GetStringValue(cJSON_GetObjectItem(episode, "Title")); char *this_file=getRelativePath(conf->TVhtml, cJSON_GetStringValue(cJSON_GetObjectItem(episode, "File"))); cJSON *this_subs=cJSON_GetObjectItem(episode, "Subs"); size_t tempStrSize=0; char *tempStr=NULL; if (this_seasonNum>*currSeason) { if (*currSeason>0) { // new season, so close prev season list addData(this_show, "\n\n"); } (*currSeason)=this_seasonNum; tempStrSize=intSize(*uuid)+intSize(*currSeason)+strlen(SHOW_HTML_UL)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) { fatalError_abort("episodeHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } snprintf(tempStr, tempStrSize, SHOW_HTML_UL, *uuid, *currSeason); addData(this_show, tempStr); } tempStrSize=intSize(*uuid)+intSize(*uuidEpisode)+strlen(this_episodeName)+strlen(SHOW_HTML_LI)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) { fatalError_abort("episodeHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } snprintf(tempStr, tempStrSize, SHOW_HTML_LI, *uuid, *uuidEpisode, this_episodeName); addData(this_show, tempStr); tempStrSize=intSize(*uuid)+intSize(*uuidEpisode)+strlen(SHOW_HTML_DIV)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) { fatalError_abort("episodeHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } snprintf(tempStr, tempStrSize, SHOW_HTML_DIV, *uuid, *uuidEpisode); addData(this_show, tempStr); tempStrSize=strlen(this_episodeName)+strlen(SHOW_HTML_SPAN)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) { fatalError_abort("episodeHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } snprintf(tempStr, tempStrSize, SHOW_HTML_SPAN, this_episodeName); addData(this_show, tempStr); tempStrSize=intSize(*uuid)+intSize(*uuidEpisode)+strlen(this_file)+strlen(SHOW_HTML_VIDEO)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) { fatalError_abort("episodeHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } snprintf(tempStr, tempStrSize, SHOW_HTML_VIDEO, *uuid, *uuidEpisode, this_file); addData(this_show, tempStr); cJSON *currSub=NULL; cJSON_ArrayForEach(currSub, this_subs) { char *subLang=cJSON_GetStringValue(cJSON_GetObjectItem(currSub, "lang")); char *subPath=getRelativePath(conf->TVhtml, cJSON_GetStringValue(cJSON_GetObjectItem(currSub, "subFile"))); if (subLang!=NULL && subPath!=NULL) { tempStrSize=strlen(SHOW_HTML_SUBS)+strlen(subLang)+strlen(subPath)+1; tempStr=realloc(tempStr, tempStrSize); if (tempStr==NULL) { fatalError_abort("episodeHTML error", "could not realloc;\nError: %s;\n", strerror(errno)); } snprintf(tempStr, tempStrSize, SHOW_HTML_SUBS, subPath, subLang); addData(this_show, tempStr); } tryFree(subPath); } addData(this_show, SHOW_HTML_CMD); (*uuidEpisode)++; tryFree(tempStr); tryFree(this_file); } void *cleanTV(void *threadArg) { threadStruct *thisThread=threadArg; progConfig *conf=thisThread->conf; if (conf->tvDB_exists==false || conf->JSON_tvDB==NULL) { printError("cleanTV warning", false, HYEL, "no TV database to clean!\n"); } else { printInfo("cleanTV info", false, "cleaning \"%s\"...\n", conf->dbNameTV); cJSON *tempJSON=cJSON_Duplicate(conf->JSON_tvDB, true); int jsonArrSize=cJSON_GetArraySize(tempJSON); pthread_t *threads=NULL; mallocMacro(threads, sizeof(pthread_t)*jsonArrSize, "cleanTV error"); threadStruct *threadObj=NULL; mallocMacro(threadObj, sizeof(threadStruct)*jsonArrSize, "cleanTV error"); cJSON *show=NULL; int i=0; cJSON_ArrayForEach(show, tempJSON) { threadObj[i].conf=conf; threadObj[i].oldJSON=cJSON_Duplicate(show, true); pthread_create(&threads[i], NULL, cleanShow, (void *) &threadObj[i]); i++; } cJSON_Delete(tempJSON); int numThreads=i; cJSON *newJSON=cJSON_CreateArray(); for (i=0; ioldJSON=cJSON_Duplicate(newJSON, true); cJSON_Delete(newJSON); tryFree(threads); tryFree(threadObj); return NULL; } return NULL; } void *cleanShow(void *threadArg) { threadStruct *thisThread=threadArg; cJSON *show=thisThread->oldJSON; if (show!=NULL && cJSON_GetObjectItem(show, "Episodes")!=NULL) { cJSON *episodesArray=cJSON_Duplicate(cJSON_GetObjectItem(show, "Episodes"), true); cJSON *episode=NULL; int i=0, numDeleted=0; cJSON_ArrayForEach(episode, episodesArray) { if (episode!=NULL && cJSON_GetObjectItem(episode, "File")!=NULL) { char *tempStr=cJSON_GetStringValue(cJSON_GetObjectItem(episode, "File")); if (tempStr!=NULL) { if (access(tempStr, F_OK)==0) { cJSON *subsArray=cJSON_Duplicate(cJSON_GetObjectItem(episode, "Subs"), true); int j=0, subsDeleted=0; cJSON *sub=NULL; cJSON_ArrayForEach(sub, subsArray) { char *tempSubStr=cJSON_GetStringValue(cJSON_GetObjectItem(sub, "subFile")); if (tempSubStr!=NULL) { if (access(tempSubStr, F_OK)!=0) { cJSON *tempSubs=cJSON_GetObjectItem(cJSON_GetArrayItem(cJSON_GetObjectItem(show, "Episodes"), i-numDeleted), "Subs"); cJSON_DeleteItemFromArray(tempSubs, j-subsDeleted); subsDeleted++; } } j++; } cJSON_Delete(subsArray); } else { cJSON_DeleteItemFromArray(cJSON_GetObjectItem(show, "Episodes"), i-numDeleted); numDeleted++; } } } i++; } cJSON_Delete(episodesArray); } return NULL; } int getShowID(progConfig *conf, char *tvShowName) { int tmdbID=0; if (conf->getTVposter || conf->getEpisodeName) { // if poster needed -> find out tmdb_id CURL *curl=curl_easy_init(); char *escapedTVShowName=curl_easy_escape(curl, tvShowName, 0); char *apiOpts="&api_key="; size_t tempURLStrSize=strlen(tmdbTV_ID)+strlen(escapedTVShowName)+strlen(apiOpts)+strlen(conf->TMDBapi)+1; char *tempURLStr=NULL; mallocMacro(tempURLStr, tempURLStrSize, "getShowID error"); tempURLStr[0]='\0'; snprintf(tempURLStr, tempURLStrSize, "%s%s%s%s", tmdbTV_ID, escapedTVShowName, apiOpts, conf->TMDBapi); printInfo("getShowID info", true, "Searching for TMDB ID for \"%s\", URL: \"%s\";\n", tvShowName, tempURLStr); tmdbID=getTmdbID(tempURLStr, conf); if (tmdbID==0) { printError("getShowID warning", false, HYEL, "beware, could not find any ID for \"%s\";\n", tvShowName); tmdbID=-1; } curl_free(escapedTVShowName); curl_easy_cleanup(curl); } return tmdbID; }