Added support for webm/ogv/mkv files

This commit is contained in:
farfalleflickan 2025-01-10 21:34:46 +01:00
parent 6e5a709e22
commit d4b22ce191
9 changed files with 103 additions and 51 deletions

View File

@ -37,7 +37,7 @@ Either install from source with make install OR use a pre-compiled package from
Beware, the pre-compiled `deb` file is built using the default `libcurl4-openssl-dev` backend.
# Requirements to run:
cmyflix uses libcjson(>=1.7.15), libcurl(>=7.68), imagemagick, ffmpeg and a TMDB api key. Please do also note that cmyflix searches for `mp4` files due to the usage of HTML5 and its supported formats.
cmyflix uses libcjson(>=1.7.15), libcurl(>=7.68), imagemagick, ffmpeg and a TMDB api key. Please do also note that cmyflix searches for `mp4`,`mkv`,`ogv` and `webm` files due to the usage of HTML5 and its supported formats.
# Configuration & usage:
For starters, cmyflix looks for `cmyflix.cfg` first in the same folder as the binary, then in `$HOME/.config/cmyflix/` and lastly in `/etc/cmyflix/`. Same thing applies for folder `html` and its contents.

View File

@ -10,7 +10,7 @@
#include "iofiles.h"
// Own implementation with opendir/readdir. nftw might be faster? Dunno, nftw seemed slower in tests
struct fileList *find(progConfig *conf, char *dirPath, char *searchStr, ioMode mode, bool recursive) {
struct fileList *find(progConfig *conf, char *dirPath, char **searchStrs, ioMode mode, bool recursive) {
DIR *directory = opendir(dirPath);
if (directory == NULL) {
printError("find warning", false, HYEL, "failed while opening directory \"%s\";\nError: %s;\n", dirPath, strerror(errno));
@ -46,7 +46,7 @@ struct fileList *find(progConfig *conf, char *dirPath, char *searchStr, ioMode m
list = initList(list, NULL, 0);
addData(list, path);
}
fileList *temp = find(conf, path, searchStr, mode, recursive);
fileList *temp = find(conf, path, searchStrs, mode, recursive);
if (temp != NULL) {
list = joinLists(list, temp);
}
@ -56,36 +56,41 @@ struct fileList *find(progConfig *conf, char *dirPath, char *searchStr, ioMode m
addData(newFile, fileP->d_name);
list=joinLists(newFile, list);
}
} else if (strstr(fileP->d_name, searchStr) && (mode & FI_MODE)!=0) { // if filename matches the string we looking for AND in file mode
char **fileMatch = NULL;
if ((mode & TV_MODE)!=0) { // if TV mode bit set
// if ( conf->tvDB_exists==true && (mode & CK_MODE)!=0 && strstr(conf->tvDB_str, path)!=NULL) { // if filename already in database & in 'check mode'
for (int i=0; i<conf->regexTVnum && fileMatch == NULL; i++) {
fileMatch=matchReg(path, conf->regexTVstr[i], conf->regexTVgroups);
if (fileMatch!=NULL) {
fileList *newFile=newList();
for (int j=1; j<=parseStrToInt(fileMatch[0]); j++) {
addData(newFile, fileMatch[j]);
if (j==1) {
addData(newFile, dirPath);
addData(newFile, fileP->d_name);
} else if ((mode & FI_MODE)!=0) { // if filename matches the string we looking for AND in file mode
for (char **currentStr = searchStrs; *currentStr != NULL; currentStr++) {
if (strstr(fileP->d_name, *currentStr) != NULL) {
char **fileMatch = NULL;
if ((mode & TV_MODE)!=0) { // if TV mode bit set
// if ( conf->tvDB_exists==true && (mode & CK_MODE)!=0 && strstr(conf->tvDB_str, path)!=NULL) { // if filename already in database & in 'check mode'
for (int i=0; i<conf->regexTVnum && fileMatch == NULL; i++) {
fileMatch=matchReg(path, conf->regexTVstr[i], conf->regexTVgroups);
if (fileMatch!=NULL) {
fileList *newFile=newList();
for (int j=1; j<=parseStrToInt(fileMatch[0]); j++) {
addData(newFile, fileMatch[j]);
if (j==1) {
addData(newFile, dirPath);
addData(newFile, fileP->d_name);
}
}
list = joinLists(newFile, list);
}
}
list = joinLists(newFile, list);
} else if ((mode & MO_MODE)!=0) { // if movie mode bit set...
/*
* Nothing, else works just as well
*/
} else { // if in file mode but NOT in movie/tv mode...
fileList *newFile=newList();
addData(newFile, path);
addData(newFile, dirPath);
addData(newFile, fileP->d_name);
list=joinLists(newFile, list);
}
freeStrArr(fileMatch);
break;
}
} else if ((mode & MO_MODE)!=0) { // if movie mode bit set...
/*
* Nothing, else works just as well
*/
} else { // if in file mode but NOT in movie/tv mode...
fileList *newFile=newList();
addData(newFile, path);
addData(newFile, dirPath);
addData(newFile, fileP->d_name);
list=joinLists(newFile, list);
}
freeStrArr(fileMatch);
}
tryFree(path);
}

View File

@ -9,4 +9,4 @@ typedef enum {
TV_MODE=1 << 3 // TV show mode
} ioMode;
struct fileList *find(progConfig *conf, char *dirPath, char *searchStr, ioMode mode, bool recursive);
struct fileList *find(progConfig *conf, char *dirPath, char **searchStrs, ioMode mode, bool recursive);

View File

@ -15,7 +15,7 @@ struct fileList *createMoviesDB(progConfig *conf) {
char *infoStr="building home movies' database...";
if (conf->homeMovies==false) {
infoStr="building movies' database...";
folders=find(conf, conf->MoviesPath, "", DI_MODE, false);
folders=find(conf, conf->MoviesPath, (char *[]){"", NULL}, DI_MODE, false);
}
printInfo("createMoviesDB info", true, "%s\n", infoStr);
@ -432,11 +432,25 @@ void *movieHTML(void *threadArg) {
}
printInfo("movieHTML info", true, "building HTML for \"%s\";\n", movieName);
size_t tempStrSize=strlen(MOVIE_HTML_VIDEO)+intSize(thisThread->id)*3+strlen(moviePoster)+strlen(movieFile)+strlen(movieName)*2+1;
char *tempStr=NULL;
mallocMacro(tempStr, tempStrSize, "movieHTML error");
snprintf(tempStr, tempStrSize, MOVIE_HTML_VIDEO, thisThread->id, movieName, moviePoster, movieName, thisThread->id, thisThread->id, movieFile);
size_t tempStrSize = 0;
if (strstr(movieFile, ".webm") != NULL) {
tempStrSize=strlen(MOVIE_HTML_VIDEO_WEBM)+intSize(thisThread->id)*3+strlen(moviePoster)+strlen(movieFile)+strlen(movieName)*2+1;
mallocMacro(tempStr, tempStrSize, "movieHTML error");
snprintf(tempStr, tempStrSize, MOVIE_HTML_VIDEO_WEBM, thisThread->id, movieName, moviePoster, movieName, thisThread->id, thisThread->id, movieFile);
} else if (strstr(movieFile, ".ogv") != NULL) {
tempStrSize=strlen(MOVIE_HTML_VIDEO_OGG)+intSize(thisThread->id)*3+strlen(moviePoster)+strlen(movieFile)+strlen(movieName)*2+1;
mallocMacro(tempStr, tempStrSize, "movieHTML error");
snprintf(tempStr, tempStrSize, MOVIE_HTML_VIDEO_OGG, thisThread->id, movieName, moviePoster, movieName, thisThread->id, thisThread->id, movieFile);
} else if (strstr(movieFile, ".mp4") != NULL) {
tempStrSize=strlen(MOVIE_HTML_VIDEO_MP4)+intSize(thisThread->id)*3+strlen(moviePoster)+strlen(movieFile)+strlen(movieName)*2+1;
mallocMacro(tempStr, tempStrSize, "movieHTML error");
snprintf(tempStr, tempStrSize, MOVIE_HTML_VIDEO_MP4, thisThread->id, movieName, moviePoster, movieName, thisThread->id, thisThread->id, movieFile);
} else {
tempStrSize=strlen(MOVIE_HTML_VIDEO)+intSize(thisThread->id)*3+strlen(moviePoster)+strlen(movieFile)+strlen(movieName)*2+1;
mallocMacro(tempStr, tempStrSize, "movieHTML error");
snprintf(tempStr, tempStrSize, MOVIE_HTML_VIDEO, thisThread->id, movieName, moviePoster, movieName, thisThread->id, thisThread->id, movieFile);
}
addData(thisThread->list, tempStr);
cJSON *currSub=NULL;
@ -454,7 +468,7 @@ void *movieHTML(void *threadArg) {
}
tryFree(subPath);
}
addData(thisThread->list, MOVIE_HTML_VIDEO_BOT);
addData(thisThread->list, MOVIE_HTML_VIDEO_BOTTOM);
tryFree(moviePoster);
tryFree(movieFile);
tryFree(tempStr);

View File

@ -4,11 +4,14 @@
#define MOVIE_HTML_TOP "<!DOCTYPE html>\n<html>\n<head>\n<title>cmyflix</title>\n<meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\">\n<meta name=\"description\" content=\"Daria Rostirolla\">\n<meta name=\"keywords\" content=\"HTML, CSS\">\n<meta name=\"author\" content=\"Daria Rostirolla\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<link href=\"css/movie.css\" rel=\"stylesheet\" type=\"text/css\">\n<link rel=\"icon\" type=\"image/png\" href=\"img/favicon.png\">\n</head>\n<body>\n<script type=\"text/javascript\" src=\"js/Mscript.js\"></script><div id=\"wrapper\">"
#define MOVIE_HTML_VIDEO "<div class=\"movieDiv\">\n<input id=\"A%d\" class=\"myBtn\" alt=\"%s\" onclick=\"javascript:showModal(this)\" type=\"image\" src=\"%s\" onload=\"javascript:setAlt(this, '%s')\">\n<div id=\"B%d\" class=\"modal\">\n<div class=\"modal-content\">\n<video id=\"C%d\" class=\"video_player\" controls preload=\"none\">\n<source src=\"%s\" type=\"video/mp4\">"
#define MOVIE_HTML_VIDEO "<div class=\"movieDiv\">\n<input id=\"A%d\" class=\"myBtn\" alt=\"%s\" onclick=\"javascript:showModal(this)\" type=\"image\" src=\"%s\" onload=\"javascript:setAlt(this, '%s')\">\n<div id=\"B%d\" class=\"modal\">\n<div class=\"modal-content\">\n<video id=\"C%d\" class=\"video_player\" controls preload=\"none\">\n<source src=\"%s\">"
#define MOVIE_HTML_VIDEO_WEBM "<div class=\"movieDiv\">\n<input id=\"A%d\" class=\"myBtn\" alt=\"%s\" onclick=\"javascript:showModal(this)\" type=\"image\" src=\"%s\" onload=\"javascript:setAlt(this, '%s')\">\n<div id=\"B%d\" class=\"modal\">\n<div class=\"modal-content\">\n<video id=\"C%d\" class=\"video_player\" controls preload=\"none\">\n<source src=\"%s\" type=\"video/webm\">"
#define MOVIE_HTML_VIDEO_MP4 "<div class=\"movieDiv\">\n<input id=\"A%d\" class=\"myBtn\" alt=\"%s\" onclick=\"javascript:showModal(this)\" type=\"image\" src=\"%s\" onload=\"javascript:setAlt(this, '%s')\">\n<div id=\"B%d\" class=\"modal\">\n<div class=\"modal-content\">\n<video id=\"C%d\" class=\"video_player\" controls preload=\"none\">\n<source src=\"%s\" type=\"video/mp4\">"
#define MOVIE_HTML_VIDEO_OGG "<div class=\"movieDiv\">\n<input id=\"A%d\" class=\"myBtn\" alt=\"%s\" onclick=\"javascript:showModal(this)\" type=\"image\" src=\"%s\" onload=\"javascript:setAlt(this, '%s')\">\n<div id=\"B%d\" class=\"modal\">\n<div class=\"modal-content\">\n<video id=\"C%d\" class=\"video_player\" controls preload=\"none\">\n<source src=\"%s\" type=\"video/ogg\">"
#define MOVIE_HTML_SUBS "\n<track src=\"%s\" kind=\"subtitles\" label=\"%s\">"
#define MOVIE_HTML_VIDEO_BOT "\n</video>\n<span onclick=\"javascript:hideModal()\" class=\"close\">&times;</span>\n</div>\n</div>\n</div>"
#define MOVIE_HTML_VIDEO_BOTTOM "\n</video>\n<span onclick=\"javascript:hideModal()\" class=\"close\">&times;</span>\n</div>\n</div>\n</div>"
#define MOVIE_HTML_BOT "\n<div id=\"paddingDiv\">\n</div>\n</div>\n</body>\n</html>"

View File

@ -15,7 +15,7 @@
// 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);
fileList *showFolders=find(conf, conf->TVpath, (char *[]){"", NULL}, DI_MODE, false);
if (showFolders != NULL ) {
int foldersListSize=showFolders->listSize, i=0;
@ -119,7 +119,7 @@ void *findSeasons(void *threadArg) { // runs in Show.Name folder and spawns thre
}
// 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
fileList *seasonsList = find(conf, showPath, (char *[]){"", NULL}, DI_MODE, false); // get all seasons folders
if (seasonsList!=NULL && seasonsList->data!=NULL) {
bool showHasExtras=false;
@ -276,7 +276,7 @@ void *findSeasons(void *threadArg) { // runs in Show.Name folder and spawns thre
tryFree(extrasJSONStr);
tryFree(tempShowJSONStr);
} else {
printError("findSeasons warning", false, HYEL, "please note, could not find any \".%s\" in path \"%s\"\n", videoExt, showPath);
printError("findSeasons warning", false, HYEL, "please note, could not find any in path \"%s\"\n", showPath);
}
} else {
printError("findSeasons warning", false, HYEL, "please note, path \"%s\" is empty!\n", showPath);
@ -432,9 +432,13 @@ void *findEpisodes(void *threadArg) { // find files of 'this' season
if (epName==NULL) {
if (temp->dataSize>5 && seasonExtra==false) {
if (strcmp(temp->data[6], videoExt)==0) {
epName=removeExtension(temp->data[2]);
} else {
for (char **currentStr = videoExt; *currentStr != NULL; currentStr++) {
if (strcmp(temp->data[6], *currentStr)==0) {
epName=removeExtension(temp->data[2]);
break;
}
}
if (epName==NULL) {
epName=removeExtension(temp->data[6]);
}
} else {
@ -849,12 +853,35 @@ void episodeHTML(fileList *this_show, progConfig *conf, cJSON *episode, int *cur
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));
if (strstr(this_file, ".webm") != NULL) {
tempStrSize=intSize(*uuid)+intSize(*uuidEpisode)+strlen(this_file)+strlen(SHOW_HTML_VIDEO_WEBM)+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_WEBM, *uuid, *uuidEpisode, this_file);
} else if (strstr(this_file, ".ogv") != NULL) {
tempStrSize=intSize(*uuid)+intSize(*uuidEpisode)+strlen(this_file)+strlen(SHOW_HTML_VIDEO_OGG)+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_OGG, *uuid, *uuidEpisode, this_file);
} else if (strstr(this_file, ".mp4") != NULL) {
tempStrSize=intSize(*uuid)+intSize(*uuidEpisode)+strlen(this_file)+strlen(SHOW_HTML_VIDEO_MP4)+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_MP4, *uuid, *uuidEpisode, this_file);
} else {
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);
}
snprintf(tempStr, tempStrSize, SHOW_HTML_VIDEO, *uuid, *uuidEpisode, this_file);
addData(this_show, tempStr);
cJSON *currSub=NULL;

View File

@ -26,7 +26,10 @@
#define SHOW_HTML_SPAN "<span onclick=\"javascript:hideVideoModal()\" class=\"close\">&times;</span>\n<p id=\"epTitle\">%s</p>"
#define SHOW_HTML_VIDEO "\n<video id=\"F%d_%d\" class=\"video_player\" controls preload=\"none\" onplaying=\"javascript:rezHandler()\">\n<source src=\"../%s\" type=\"video/mp4\">"
#define SHOW_HTML_VIDEO "\n<video id=\"F%d_%d\" class=\"video_player\" controls preload=\"none\" onplaying=\"javascript:rezHandler()\">\n<source src=\"../%s\">"
#define SHOW_HTML_VIDEO_WEBM "\n<video id=\"F%d_%d\" class=\"video_player\" controls preload=\"none\" onplaying=\"javascript:rezHandler()\">\n<source src=\"../%s\" type=\"video/webm\">"
#define SHOW_HTML_VIDEO_MP4 "\n<video id=\"F%d_%d\" class=\"video_player\" controls preload=\"none\" onplaying=\"javascript:rezHandler()\">\n<source src=\"../%s\" type=\"video/mp4\">"
#define SHOW_HTML_VIDEO_OGG "\n<video id=\"F%d_%d\" class=\"video_player\" controls preload=\"none\" onplaying=\"javascript:rezHandler()\">\n<source src=\"../%s\" type=\"video/ogg\">"
#define SHOW_HTML_SUBS "\n<track src=\"../%s\" kind=\"subtitles\" label=\"%s\">"

View File

@ -603,7 +603,7 @@ char *randStr(size_t size) {
char *getSubs(progConfig *conf, char *fileStr, char *filePath) {
char *epSubs=NULL;
char *fileName=removeExtension(fileStr);
fileList *subs=find(conf, filePath, fileName, FI_MODE, false);
fileList *subs=find(conf, filePath, (char *[]){fileName, NULL}, FI_MODE, false);
for (fileList *tempSub=subs; tempSub!=NULL; tempSub=tempSub->next) {
if (tempSub->dataSize>2) {

View File

@ -14,7 +14,7 @@
#define tmdbTV_ID "https://api.themoviedb.org/3/search/tv?page=1&query=" // name&api_key=blah +opts
#define tmdbTV_Opts "&language=en" //to find the ID based on name
#define tmdbImg "https://image.tmdb.org/t/p/original"
#define videoExt "mp4"
#define videoExt (char *[]){"mp4", "mkv", "ogv", "webm", NULL}
#define subExt1 "srt"
#define subExt2 "vtt"