/* * playlist: A plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * * $Id: playlist.c 0.2 2004/10/08 02:34:00 hflor Exp $ */ #include "playlist.h" #include "menuplaylists.h" #include "menucontrol.h" #include "menusetup.h" #include "i18n.h" #include "vdrtools.h" #include #include #include static const char *VERSION = "0.0.2"; static const char *DESCRIPTION = "playlist for recordings"; /* TODO option playonlynewrecords */ // Global variables that control the overall behaviour: // var pos2 tParamFile playlistconfigfile = { false, false, false, "playlist.conf", "playlist.conf" }; #ifdef HAVE_ICONPATCH tParamChar charentry = { false, false, false, 236 }; tParamChar charfolder = { false, false, false, 237 }; tParamChar charrecord = { false, false, false, 249 }; #else tParamChar charentry = { false, false, false, 'e' }; tParamChar charfolder = { false, false, false, 'f' }; tParamChar charrecord = { false, false, false, 'r' }; #endif tParamInt commandline_preference = { false, false, false, true }; tParamFile lastplaylist = { false, false, false, "" }; tParamFile mainmenu_name = { false, false, false, "Playlist" }; tParamInt mainmenu_visible = { false, false, false, true }; tParamInt confirmdelplentry = { false, false, false, true }; tParamInt okstartplay = { false, false, false, true }; tParamInt deleterecords = { false, false, false, true }; tParamFile pathplaylists = { false, false, false, "" }; tParamInt startoption = { false, false, false, 0 }; tParamInt storeplaylist = { false, false, false, 0 }; tParamInt timeoutreadrecords = { false, false, false, 60 }; tParamInt verbose = { false, false, false, false, true }; tParamInt PL_options[Option_max] ={{ false, false, false, true }, // confirmdeleterecord { false, false, false, false }, // confirmstartnewrecord { false, false, false, true }, // deletefromlist { false, false, false, false }, // deleterecord { false, false, false, true }, // jumpmark { false, false, false, PlayOptions_firstnew }, // playoption1 { false, false, false, PlayOptions_selectpos }, // playoption2 { false, false, false, false }, // playonlynew { false, false, false, true }, // searchnewrecord { false, false, false, true }}; // searchrecordsub char plugin_name[MaxFileName] = "Playlist"; char DisplayStatus[MAXOSDTEXTWIDTH] = ""; const char *NoYesDefault[NoYesDefault_max]; const char *StartOptions[Start_max]; const char *PlayOptions[PlayOptions_max]; char *FileNameCharsAllowed = NULL; char *AllCharsAllowed = NULL; char *LastSelectedRecord = NULL; bool ControlMenuIsOpen = false; #if VDRVERSNUM < 10311 cRecordings Recordings; time_t LoadTime_Recordings; #endif void DisplaySetings(void) { #define WriteSource(T) plugin_name, T.r ? 'r' : ' ', T.c ? 'c' : ' ' #define IsDefault(T) WriteSource(T), T.u == T.d ? '*' : ' ' #define IsDefaultChar(T) WriteSource(T), strcmp(T.u, T.d) ? ' ' : '*' #define BoolValue(T) IsDefault(T), T.u ? "yes" : "no" // var pos3 ExpandEnvironment(&pathplaylists); if (verbose.u) { isyslog("%s: commandline_preference = [ %c%c%c ] %s", BoolValue(commandline_preference)); isyslog("%s: mainmenu_visible = [ %c%c%c ] %s", BoolValue(mainmenu_visible)); if (mainmenu_visible.u) isyslog("%s: mainmenu_name = [ %c%c%c ] %s", IsDefaultChar(mainmenu_name), mainmenu_name.u); } #ifdef PL_Debug1 if (!verbose.u) { dsyslog("%s: commandline_preference = [ %c%c%c ] %s", BoolValue(commandline_preference)); dsyslog("%s: mainmenu_visible = [ %c%c%c ] %s", BoolValue(mainmenu_visible)); if (mainmenu_visible.u) dsyslog("%s: mainmenu_name = [ %c%c%c ] %s", IsDefaultChar(mainmenu_name), mainmenu_name.u); } #endif #undef WriteSource #undef IsDefault #undef IsDefaultChar #undef BoolValue } void ExpandEnvironment(tParamFile *FileStruc) { #ifdef PL_Debug2 dsyslog("%s: ExpandEnvironment text=%s", plugin_name, FileStruc->u); #endif char *s; char *p; strn0cpy(FileStruc->e, FileStruc->u, sizeof(FileStruc->e)); while ((s = strstr(FileStruc->e, "$(")) || (s = strstr(FileStruc->e, "${"))) { char c = *(s + 1) == '(' ? ')' : '}'; p = strchr(s, c); // search closing ) or } if (p) { *p++ = 0; *s = 0; s += 2; char *e = getenv(s); if (e) { char *buffer = NULL; asprintf(&buffer, "%s%s%s", FileStruc->e, e, p); strn0cpy(FileStruc->e, buffer, sizeof(FileStruc->e)); FREENULL(buffer); } else { esyslog("%s: environmentvariable '%s' not found path=%s", plugin_name, s, FileStruc->u); FileStruc->e[0] = 0; } } else { esyslog("%s: missing ')' after '$(' path=%s", plugin_name, FileStruc->u); FileStruc->e[0] = 0; } } while ((p = strstr(FileStruc->e, "//"))) strcpy(p, p + 1); #ifdef PL_Debug2 dsyslog("%s: ExpandEnvironment return=%s", plugin_name, FileStruc->e); #endif } // --- cPluginPlaylist ---------------------------------------------------------- cPluginPlaylist *PluginPlaylist; cPluginPlaylist::cPluginPlaylist(void) { #ifdef PL_Debug1 dsyslog("%s: cPluginPlaylist::cPluginPlaylist", plugin_name); #endif // Initialize any member variables here. // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! OSDLanguage = -1; PluginPlaylist = this; } cPluginPlaylist::~cPluginPlaylist() { #ifdef PL_Debug1 dsyslog("%s: cPluginPlaylist::~cPluginPlaylist", plugin_name); #endif // Clean up after yourself! FREENULL(FileNameCharsAllowed); FREENULL(AllCharsAllowed); FREENULL(LastSelectedRecord); PluginPlaylist = NULL; } void cPluginPlaylist::TestAndSetOSDLanguage(void) { #ifdef PL_Debug2 dsyslog("%s: cPluginPlaylist::TestAndSetOSDLanguage OSDLanguage=%d", plugin_name, Setup.OSDLanguage); #endif if (OSDLanguage != Setup.OSDLanguage) { OSDLanguage = Setup.OSDLanguage; StartOptions[Start_DisplayPL] = tr("Setup$Display PL"); StartOptions[Start_DisplayLast] = tr("Setup$Display last PL"); StartOptions[Start_PlayLast] = tr("Setup$Play last PL"); StartOptions[Start_NewEmpty] = tr("Setup$new empty PL"); NoYesDefault[NoYesDefault_no] = tr("no"); NoYesDefault[NoYesDefault_yes] = tr("yes"); NoYesDefault[NoYesDefault_defaultPlugin] = tr("OptionPL$[from Setup]"); NoYesDefault[NoYesDefault_defaultPlaylist] = tr("OptionPL$[from Playlist]"); NoYesDefault[NoYesDefault_defaultRecordDir] = tr("OptionPL$[from Folder]"); PlayOptions[PlayOptions_firstpos] = tr("OptionPL$first pos"); PlayOptions[PlayOptions_firstnew] = tr("OptionPL$first new"); PlayOptions[PlayOptions_lastplay] = tr("OptionPL$last played"); PlayOptions[PlayOptions_selectpos] = tr("OptionPL$seletced pos"); PlayOptions[PlayOptions_question] = tr("OptionPL$question"); PlayOptions[PlayOptions_defaultPlugin] = tr("OptionPL$[from Setup]"); FREENULL(FileNameCharsAllowed); asprintf(&FileNameCharsAllowed, "%s/$(){}!%%@", tr(FileNameChars)); if (!AllCharsAllowed) { AllCharsAllowed = MALLOC(char, 193); char *next = AllCharsAllowed; for (int i = 32; i <= 127; i++) *next++ = i; for (int i = 160; i <= 255; i++) *next++ = i; *next = 0; } } } bool cPluginPlaylist::ProcessArg(int argc, char *argv[]) { int c; static struct option long_options[] = { { "delete_begin", no_argument, NULL, 'b' }, { "nodelete_begin", no_argument, NULL, 'B' }, { "min_entrys", required_argument, NULL, 'd' }, { "delete_end", no_argument, NULL, 'e' }, { "nodelete_end", no_argument, NULL, 'E' }, { "holdtime_history", required_argument, NULL, 'h' }, { "visible_in_mainmenu", no_argument, NULL, 'm' }, { "hide_in_mainmenu", no_argument, NULL, 'M' }, { "mainmenu_name", required_argument, NULL, 'n' }, { "holdtime_respones", required_argument, NULL, 'r' }, { "sort_ascending", no_argument, NULL, 's' }, { "sort_descending", no_argument, NULL, 'S' }, { "verbose", no_argument, NULL, 'v' }, { "noverbose", no_argument, NULL, 'V' }, { "nosetup_commandline", no_argument, NULL, 1 }, { "ns_commandline", no_argument, NULL, 1 }, { NULL } }; if (argc >= 1) strn0cpy(plugin_name, argv[0], sizeof(plugin_name)); for (c = 1; c < argc; c++) dsyslog("%s: parameter%d=%s", plugin_name, c, argv[c]); #define Setvalue(T) T.c = true; T.u #define SetvalueChar(T) T.c = true; strn0cpy(T.u, optarg, sizeof(T.u)) while ((c = getopt_long(argc, argv, "bBd:eEh:mMn:r:sSvV", long_options, NULL)) != -1) { // var pos4 switch (c) { case 1: commandline_preference.h = true; break; /* case 'b': Setvalue(delete_b_e) |= 0x1; break; case 'B': Setvalue(delete_b_e) &= ~0x1; break; case 'd': if (isnumber(optarg)) { Setvalue(num_entrys) = atoi(optarg); Setvalue(delete_b_e) = 0x3; if (num_entrys.u > 0) break; } esyslog("%s: invalid parameter for -d option: %s", plugin_name, optarg); return false; case 'e': Setvalue(delete_b_e) |= 0x2; break; case 'E': Setvalue(delete_b_e) &= ~0x2; break; case 'h': if (isnumber(optarg)) { Setvalue(message_in_queue) = atoi(optarg); if (message_in_queue.u >=5 && message_in_queue.u <= 999) break; } esyslog("%s: invalid parameter for -h option: %s range 5-999", plugin_name, optarg); return false; case 'm': Setvalue(mainmenu_visible) = true; break; case 'M': Setvalue(mainmenu_visible) = false; break; case 'n': SetvalueChar(mainmenu_name); Setvalue(mainmenu_visible) = true; break; case 'r': if (isnumber(optarg)) { Setvalue(response_in_queue) = atoi(optarg); if (response_in_queue.u >= 2 && response_in_queue.u <= 199) break; } esyslog("%s: invalid parameter for -r option: %s range 2-199", plugin_name, optarg); return false; case 's': Setvalue(sortdirection) = true; break; case 'S': Setvalue(sortdirection) = false; break;*/ case 'v': Setvalue(verbose) = true; break; case 'V': Setvalue(verbose) = false; break; default: return false; } } #undef Setvalue #undef SetvalueChar if (optind < argc && argv[optind][0] == '@') { strn0cpy(playlistconfigfile.u, &argv[optind][1], sizeof(playlistconfigfile.u)); optind++; } return optind >= argc; } const char *cPluginPlaylist::Version(void) { return VERSION; } const char *cPluginPlaylist::Description(void) { return tr(DESCRIPTION); } const char *cPluginPlaylist::CommandLineHelp(void) { // Return a string that describes all known command line options. return " -m --visible_in_mainmenu Show the plugin in the mainmenu\n" " -M --hide_in_mainmenu Hide the plugin in the mainmenu\n" " -n Name --mainmenu_name=Name Select Name for entry in the mainmenu (set also -m)\n" " -b --delete_begin Show the delete all messages line at begin of messagelist\n" " -B --nodelete_begin Hide the delete all messages line at begin of messagelist\n" " -e --delete_end Show the delete all messages line at end of messagelist\n" " -E --nodelete_end Hide the delete all messages line at end of messagelist\n" " -d xx --min_entrys=xx minimum entrys for display delete all messages line at begin and end (include -b and -e)\n" " -h xx --holdtime_history=xx minimum time (min) for message in historyqueue (OSD-list) 5-999\n" " -r xx --holdtime_respones=xx minumum time (min) for responses in queue (readable by SAQRESP) 2-199\n" " -s --sort_ascending sortoption for messagelist (OSD)\n" " -S --sort_descending sortoption for messagelist (OSD)\n" " -v --verbose Enable more logging\n" " -V --noverbose Disable more loggig\n" " --nosetup_commandline Hide the Preferr Command Line Parameter form setup-menu\n" " --ns_commandline same as --nosetup_commandline"; // free: a c f g i j k l o p q t u v w x y z } bool cPluginPlaylist::ProcessArgs(int argc, char *argv[]) { // Implement command line argument processing here if applicable. return ProcessArg(argc, argv); } #ifdef PL_Debug1 void TestI18n(char *Test) { Setup.OSDLanguage = 0; const char *eng = tr(Test); Setup.OSDLanguage = 1; const char *deu = tr(Test); if (eng && deu && strcmp(eng, deu) == 0) dsyslog("%s: missing: %s", plugin_name, Test); } #endif bool cPluginPlaylist::Start(void) { // Start any background activities the plugin shall perform. RegisterI18n(Phrases); TestAndSetOSDLanguage(); #ifdef PL_Debug1 dsyslog("%s: cPluginPlaylist::Start playlistconfigfile=%s", plugin_name, playlistconfigfile.u); #endif char *p; char *q = strdup(ConfigDirectory("")); asprintf(&p, "%s%s%s", q, *(q + strlen(q) - 1) == '/' ? "" : "/", playlistconfigfile.u); if (!access(playlistconfigfile.u, F_OK) && !access(playlistconfigfile.u, R_OK) || !access(p, F_OK) && !access(p, R_OK)) { #define MAXARGS 100 int fargc = 1; char *fargv[MAXARGS]; char buffer[MAXPARSEBUFFER]; bool done; FILE *f; if (!access(playlistconfigfile.u, F_OK)) { f = fopen(playlistconfigfile.u, "r"); #ifdef PL_Debug2 dsyslog("%s: cPluginPlaylist::Start open playlistconfigfile=%s", plugin_name, playlistconfigfile.u); #endif } else { f = fopen(p, "r"); #ifdef PL_Debug2 dsyslog("%s: cPluginPlaylist::Start open playlistconfigfile=%s", plugin_name, p); #endif } free(p); free(q); if (!f) { esyslog("%s: ERROR: cannot open config file: [%s]%s", plugin_name, ConfigDirectory(""), playlistconfigfile.u); return false; } while (fgets(buffer, sizeof(buffer), f) > 0) { p = skipspace(stripspace(buffer)); q = NULL; done = false; while (!done) { if (!q) q = p; switch (*p) { case '\\': strcpy(p, p + 1); if (*p) p++; else { esyslog("%s: ERROR: missing character after \\", plugin_name); return false; } break; case '"': case '\'': if ((p = SkipQuote(p)) == NULL) return false; break; default: if (!*p || isspace(*p)) { done = !*p; *p = 0; if (q) { if (fargc < MAXARGS - 1) { if (*q != '#') fargv[fargc++] = strdup(q); } else { esyslog("%s: ERROR: plugin argument list too long", plugin_name); return false; } q = NULL; } } if (!done) p = *p ? p + 1 : skipspace(p + 1); } } } fclose(f); fargv[0] = strdup(plugin_name); fargv[fargc] = NULL; optind = 0; // to reset the getopt() data if (fargc > 1) if (!ProcessArg(fargc, fargv)) { esyslog("%s: ERROR: cannot parse config file: [%s]%s", plugin_name, ConfigDirectory(""), playlistconfigfile.u); return false; } while(fargc) free(fargv[--fargc]); } else { free(p); free(q); if (strcmp(playlistconfigfile.u, playlistconfigfile.d)) { esyslog("%s: ERROR: config file not found: [%s]%s", plugin_name, ConfigDirectory(""), playlistconfigfile.u); return false; } else if (verbose.u) isyslog("%s: INFO: config file not found: [%s]%s", plugin_name, ConfigDirectory(""), playlistconfigfile.u); } // default parameter --> d_ // read parameter from commandline --> c_ (data in u_) // value parameter from config-file --> s_ // read parameter from config-file --> r_ // paramater used --> u_ #define SetParam(T) { if (!T.c || (!commandline_preference.u && T.r)) T.u = (!T.c && !T.r) ? T.d : T.s; } #define SetParamFile(T) { if (!T.c || (!commandline_preference.u && T.r)) strcpy(T.u, (!T.c && !T.r) ? T.d : T.s); } commandline_preference.u = commandline_preference.r ? commandline_preference.s : commandline_preference.d; // var pos5 SetParamFile (playlistconfigfile); SetParam (charentry); SetParam (charfolder); SetParam (charrecord); SetParamFile (lastplaylist); SetParamFile (mainmenu_name); SetParam (mainmenu_visible); SetParam (confirmdelplentry); SetParam (okstartplay); SetParam (deleterecords); SetParamFile (pathplaylists); SetParam (startoption); SetParam (storeplaylist); SetParam (timeoutreadrecords); SetParam (verbose); for (int i = Option_confirmdeleterecord; i < Option_max; i++) SetParam (PL_options[i]); #undef SetParam #undef SetParamFile if (verbose.u) isyslog("%s: Start", plugin_name); DisplaySetings(); strcpy(lastplaylist.o, lastplaylist.u); return true; } const char *cPluginPlaylist::MainMenuEntry(void) { return mainmenu_visible.u ? mainmenu_name.u : NULL; } cOsdObject *cPluginPlaylist::MainMenuAction(void) { #ifdef PL_Debug1 dsyslog("%s: cPluginPlaylist::MainMenuAction", plugin_name); #endif TestAndSetOSDLanguage(); return new cControlMenu; } cMenuSetupPage *cPluginPlaylist::SetupMenu(void) { TestAndSetOSDLanguage(); return new cMenuSetupPlayList; } bool cPluginPlaylist::SetupParse(const char *Name, const char *Value) { #define SetParam(T) { T.r = true; T.s = strtol(Value, NULL, 0); } #define SetParamChar(T) { T.r = true; temp = strtol(Value, NULL, 0); T.s = temp > 255 ? temp - 256 : temp; } #define SetParamFile(T) { T.r = true; strn0cpy(T.s, Value, sizeof(T.s)); } // Parse your own setup parameters and store their values. dsyslog("%s: Setupparameter %s=%s", plugin_name, Name, Value); // var pos6 int temp; if (!strcasecmp(Name, "cahre")) SetParamChar (charentry) else if (!strcasecmp(Name, "charf")) SetParamChar (charfolder) else if (!strcasecmp(Name, "charr")) SetParamChar (charrecord) else if (!strcasecmp(Name, "commandline")) SetParam (commandline_preference) else if (!strcasecmp(Name, "lastpl")) SetParamFile (lastplaylist) else if (!strcasecmp(Name, "name")) SetParamFile (mainmenu_name) else if (!strcasecmp(Name, "visible")) SetParam (mainmenu_visible) else if (!strcasecmp(Name, "deletentry")) SetParam (confirmdelplentry) else if (!strcasecmp(Name, "okplay")) SetParam (okstartplay) else if (!strcasecmp(Name, "delete")) SetParam (deleterecords) else if (!strcasecmp(Name, "path")) SetParamFile (pathplaylists) else if (!strcasecmp(Name, "start")) SetParam (startoption) else if (!strcasecmp(Name, "storepl")) SetParam (storeplaylist) else if (!strcasecmp(Name, "timerecords")) SetParam (timeoutreadrecords) else if (!strcasecmp(Name, "verbose")) SetParam (verbose) else if (!strcasecmp(Name, "confdelete")) SetParam (PL_options[Option_confirmdeleterecord]) else if (!strcasecmp(Name, "startnew")) SetParam (PL_options[Option_confirmstartnewrecord]) else if (!strcasecmp(Name, "deletel")) SetParam (PL_options[Option_deletefromlist]) else if (!strcasecmp(Name, "deleter")) SetParam (PL_options[Option_deleterecord]) else if (!strcasecmp(Name, "jumpmark")) SetParam (PL_options[Option_jumpmark]) else if (!strcasecmp(Name, "playoption1")) SetParam (PL_options[Option_playoption1]) else if (!strcasecmp(Name, "playoption2")) SetParam (PL_options[Option_playoption2]) else if (!strcasecmp(Name, "playonlynew")) SetParam (PL_options[Option_playonlynew]) else if (!strcasecmp(Name, "searchnew")) SetParam (PL_options[Option_searchnewrecord]) else if (!strcasecmp(Name, "searchsub")) SetParam (PL_options[Option_searchrecordsub]) else return false; return true; #undef SetParam #undef SetParamChar #undef SetParamFile } const char *cPluginPlaylist::ExpandPath(const char *Filename, bool CreateDir) { static char path[MaxFileName]; char *c = strdup(ConfigDirectory("")); char *p = NULL; ExpandEnvironment(&pathplaylists); if (pathplaylists.e[0] == '/') asprintf(&p, "%s%s%s", pathplaylists.e, *(pathplaylists.e + strlen(pathplaylists.e) - 1) == '/' ? "" : "/", Filename); else asprintf(&p, "%s%s%s%s%s", c, *(c + strlen(c) - 1) == '/' ? "" : "/", pathplaylists.e, strlen(pathplaylists.e) ? *(pathplaylists.e + strlen(pathplaylists.e) - 1) == '/' ? "" : "/" : "", Filename); if (strlen(p) >= MaxFileName) esyslog("%s: max length for filename is %d this name is %d [%s]", plugin_name, MaxFileName, strlen(p), p); strn0cpy(path, p, MaxFileName); if (CreateDir) MakeDirs(path, false); free(c); free(p); #ifdef PL_Debug1 dsyslog("%s: ExpandPath Filename=%s Return=%s", plugin_name, Filename, path); #endif return &path[0]; } VDRPLUGINCREATOR(cPluginPlaylist); // Don't touch this!