diff options
author | Matti Lehtimäki <matti.lehtimaki@gmail.com> | 2013-01-07 21:29:45 +0200 |
---|---|---|
committer | Matti Lehtimäki <matti.lehtimaki@gmail.com> | 2013-01-07 21:29:45 +0200 |
commit | c6ffcc01aa82582b119de294ff40aac915c88e19 (patch) | |
tree | ac6881e6c72f9f2511bcbff7d5d29607a9ff9711 | |
download | vdr-plugin-history-c6ffcc01aa82582b119de294ff40aac915c88e19.tar.gz vdr-plugin-history-c6ffcc01aa82582b119de294ff40aac915c88e19.tar.bz2 |
Initial commit. Version 0.0.3.v0.0.3
-rwxr-xr-x | COPYING | 340 | ||||
-rwxr-xr-x | HISTORY | 19 | ||||
-rwxr-xr-x | Makefile | 121 | ||||
-rwxr-xr-x | README | 25 | ||||
-rwxr-xr-x | config.c | 51 | ||||
-rwxr-xr-x | config.h | 30 | ||||
-rwxr-xr-x | history.c | 188 | ||||
-rwxr-xr-x | logger.c | 166 | ||||
-rwxr-xr-x | logger.h | 54 | ||||
-rwxr-xr-x | menu.c | 286 | ||||
-rwxr-xr-x | menu.h | 34 | ||||
-rwxr-xr-x | po/fi_FI.po | 47 | ||||
-rwxr-xr-x | setup_menu.c | 44 | ||||
-rwxr-xr-x | setup_menu.h | 29 | ||||
-rwxr-xr-x | tools.c | 38 | ||||
-rwxr-xr-x | tools.h | 18 |
16 files changed, 1490 insertions, 0 deletions
@@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. @@ -0,0 +1,19 @@ +VDR Plugin 'history' Revision History +------------------------------------- + +2012-10-19: Version 0.0.3 + +- Support deleting history items through menu. +- Support deleting of also target files and recordings if enabled in setup. +- Support replaying of also non-VDR files using Xineliboutput Media Player. +- Bug fixes. + +2012-10-10: Version 0.0.2 + +- Rename several classes to avoid class name clashes with other plugins. + (Thanks to Rolf Ahrenberg for help with debugging) +- Bug fixes. + +2012-10-07: Version 0.0.1 + +- Initial revision. diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..017ef51 --- /dev/null +++ b/Makefile @@ -0,0 +1,121 @@ +# +# Makefile for history, a Video Disk Recorder plugin +# + +# history +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# IMPORTANT: the presence of this macro is important for the Make.config +# file. So it must be defined, even if it is not used here! +# +PLUGIN = history + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char VERSION\[\] *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') +GITTAG = $(shell git describe --always 2>/dev/null) + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -g -O3 -Wall -Werror=overloaded-virtual -Wno-parentheses + +### The directory environment: + +VDRDIR ?= ../../.. +LIBDIR ?= ../../lib +TMPDIR ?= /tmp + +### Make sure that necessary options are included: + +include $(VDRDIR)/Make.global + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR's plugin API (taken from VDR's "config.h"): + +APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +ifneq ($(strip $(GITTAG)),) +DEFINES += -DGITVERSION='"-GIT-$(GITTAG)"' +endif + +### The object files (add further files here): + +OBJS = $(PLUGIN).o config.o logger.o menu.o setup_menu.o tools.o + +### The main target: + +all: libvdr-$(PLUGIN).so i18n + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR = po +LOCALEDIR = $(VDRDIR)/locale +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/$(PLUGIN).pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(wildcard *.c) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name='vdr-$(PLUGIN)' --package-version='$(VERSION)' --msgid-bugs-address='<see README>' -o $@ `ls $^` + +%.po: $(I18Npot) + msgmerge -U --no-wrap --no-location --backup=none -q $@ $< + @touch $@ + +$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo + @mkdir -p $(dir $@) + cp $< $@ + +.PHONY: i18n +i18n: $(I18Nmsgs) $(I18Npot) + +### Targets: + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ + @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) + +dist: $(I18Npo) clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot + +cppcheck: $(OBJS) + @cppcheck --enable=information,style,unusedFunction -v -f $(OBJS:%.o=%.c) @@ -0,0 +1,25 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: Matti Lehtimäki <matti.lehtimaki@gmail.com> + +Project's homepage: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +See the file COPYING for more information. + +Description: + +History is a VDR plugin for maintaining a replay history. + +Installation: + +cd /put/your/path/here/VDR/PLUGINS/src +tar -xzf /put/your/path/here/vdr-history-X.Y.Z.tgz +ln -s history-X.Y.Z history +cd /put/your/path/here/VDR +make +make plugins +./vdr -P history diff --git a/config.c b/config.c new file mode 100755 index 0000000..01b2fea --- /dev/null +++ b/config.c @@ -0,0 +1,51 @@ +/* + * config.c: Global configuration + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "config.h" +#include <string.h> + +/* Global instance */ +cHistorySetup HistorySetup; + +cHistorySetup::cHistorySetup() +{ + allow_delete = 1; + replay_history_size = 10; +} + +cString cHistorySetup::m_ProcessedArgs; + +bool cHistorySetup::ProcessArg(const char *Name, const char *Value) +{ + if (SetupParse(Name, Value)) { + m_ProcessedArgs = cString::sprintf("%s%s ", *m_ProcessedArgs ? *m_ProcessedArgs : " ", Name); + return true; + } + return false; +} + +bool cHistorySetup::ProcessArgs(int argc, char *argv[]) +{ + return true; +} + +bool cHistorySetup::SetupParse(const char *Name, const char *Value) +{ + const char *pt; + if (*m_ProcessedArgs && NULL != (pt = strstr(m_ProcessedArgs + 1, Name)) && + *(pt - 1) == ' ' && *(pt + strlen(Name)) == ' ') { + dsyslog("Skipping configuration entry %s=%s (overridden in command line)", Name, Value); + return true; + } + + if (!strcasecmp(Name, "ReplayHistorySize")) replay_history_size = atoi(Value); + else if (!strcasecmp(Name, "AllowDelete")) allow_delete = atoi(Value); + else + return false; + + return true; +} diff --git a/config.h b/config.h new file mode 100755 index 0000000..92b7026 --- /dev/null +++ b/config.h @@ -0,0 +1,30 @@ +/* + * config.h: Global configuration + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __HISTORY_CONFIG_H_ +#define __HISTORY_CONFIG_H_ + +#include <vdr/tools.h> + +class cHistorySetup +{ +public: + int allow_delete; + int replay_history_size; + cHistorySetup(); + bool SetupParse(const char *Name, const char *Value); + bool ProcessArgs(int argc, char *argv[]); + +protected: + bool ProcessArg(const char *Name, const char *Value); + static cString m_ProcessedArgs; +}; + +// Global instance +extern cHistorySetup HistorySetup; + +#endif //__HISTORY_CONFIG_H_ diff --git a/history.c b/history.c new file mode 100755 index 0000000..9795e82 --- /dev/null +++ b/history.c @@ -0,0 +1,188 @@ +/* + * history.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <vdr/plugin.h> +#include <vdr/i18n.h> +#include "config.h" +#include "logger.h" +#include "menu.h" +#include "setup_menu.h" + +#ifndef GITVERSION +#define GITVERSION "" +#endif + +static const char VERSION[] = "0.0.3" GITVERSION; +static const char DESCRIPTION[] = trNOOP("Show last replayed recordings"); +static const char MAINMENUENTRY[] = trNOOP("History"); + +class cPluginHistory : public cPlugin { +private: + // Add any member variables or functions you may need here. + cHistoryLogger *history_logger; +public: + cPluginHistory(void); + virtual ~cPluginHistory(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return tr(DESCRIPTION); } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Initialize(void); + virtual bool Start(void); + virtual void Stop(void); + virtual void Housekeeping(void); + virtual void MainThreadHook(void); + virtual cString Active(void); + virtual time_t WakeupTime(void); + virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); } + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); + virtual bool Service(const char *Id, void *Data = NULL); + virtual const char **SVDRPHelpPages(void); + virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode); + }; + +cPluginHistory::cPluginHistory(void) +{ + // 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! + history_logger = NULL; +} + +cPluginHistory::~cPluginHistory() +{ + // Clean up after yourself! + delete history_logger; +} + +const char *cPluginHistory::CommandLineHelp(void) +{ + // Return a string that describes all known command line options. + return NULL; +} + +bool cPluginHistory::ProcessArgs(int argc, char *argv[]) +{ + // Implement command line argument processing here if applicable. + return true; +} + +bool cPluginHistory::Initialize(void) +{ + // Initialize any background activities the plugin shall perform. + return true; +} + +bool cPluginHistory::Start(void) +{ + // Start any background activities the plugin shall perform. + history_logger = new cHistoryLogger; + history_logger->SetReplayHistoryConfigFile(AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "replay.log")); // allowed only via main thread!); + history_logger->LoadReplayHistory(); + return true; +} + +void cPluginHistory::Stop(void) +{ + // Stop any background activities the plugin is performing. +} + +void cPluginHistory::Housekeeping(void) +{ + // Perform any cleanup or other regular tasks. +} + +void cPluginHistory::MainThreadHook(void) +{ + // Perform actions in the context of the main program thread. + // WARNING: Use with great care - see PLUGINS.html! +} + +cString cPluginHistory::Active(void) +{ + // Return a message string if shutdown should be postponed + return NULL; +} + +time_t cPluginHistory::WakeupTime(void) +{ + // Return custom wakeup time for shutdown script + return 0; +} + +cOsdObject *cPluginHistory::MainMenuAction(void) +{ + // Perform the action when selected from the main VDR menu. + return new cHistoryMainMenu(history_logger); +} + +cMenuSetupPage *cPluginHistory::SetupMenu(void) +{ + // Return a setup menu in case the plugin supports one. + return new cHistorySetupMenu(); +} + +bool cPluginHistory::SetupParse(const char *Name, const char *Value) +{ + // Parse your own setup parameters and store their values. + return HistorySetup.SetupParse(Name, Value); +} + +bool cPluginHistory::Service(const char *Id, void *Data) +{ + // Handle custom service requests from other plugins + return false; +} + +const char **cPluginHistory::SVDRPHelpPages(void) +{ + static const char *HelpPages[] = { + "REPL\n" + " Show replay history.", + "CLRR\n" + " Clear replay history.", + NULL + }; + return HelpPages; +} + +cString cPluginHistory::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) +{ + if (strcasecmp(Command, "REPL") == 0) { + if (HistorySetup.replay_history_size > 0) { + cString string = ""; + cList<cHistoryRecordingItem> *replay_history = history_logger->GetReplayHistory(); + cHistoryRecordingItem *item = replay_history->First(); + while (item != NULL) { + char *time = item->GetReplayTimeString(); + string = cString::sprintf("%s%s\n", *string, time); + free(time); + cThreadLock RecordingsLock(&Recordings); + cRecording *recording = Recordings.GetByName(item->GetFilename()); + if (recording) + string = cString::sprintf("%s%s\n", *string, recording->Title(' ')); + item = (cHistoryRecordingItem *)item->Next(); + } + return string; + } + } + else if (strcasecmp(Command, "CLRR") == 0) { + if (HistorySetup.replay_history_size > 0) { + history_logger->ClearReplayHistory(); + return cString("Replay history cleared"); + } + } + else { + ReplyCode = 554; // Requested action failed + return cString("Unable to show replay history"); + } + return NULL; +} + +VDRPLUGINCREATOR(cPluginHistory); // Don't touch this! diff --git a/logger.c b/logger.c new file mode 100755 index 0000000..abd8188 --- /dev/null +++ b/logger.c @@ -0,0 +1,166 @@ +/* + * logger.c: Keep log of replayed recordings + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "logger.h" +#include <string.h> + +cHistoryRecordingItem::cHistoryRecordingItem(const char *String) +{ + filename = NULL; + name = NULL; + if (String && *String) { + char *s = strdup(String); + char *p = strchr(s, ':'); + if (p) { + replay_time = atol(s); + char *r = strchr(p, '='); + if (r) { + *r = 0; + filename = strdup(p + 1); + name = strdup(r + 1); + } + else + filename = strdup(p + 1); + } + else { + replay_time = 0; + } + free(s); + } +} + +// Use name only if not a VDR recording +cHistoryRecordingItem::cHistoryRecordingItem(const char *Name, const char *FileName) +{ + replay_time = time(NULL); + filename = FileName ? strdup(FileName) : NULL; + name = Name ? strdup(Name) : NULL; +} + +char *cHistoryRecordingItem::GetReplayTimeString() +{ + struct tm *t = localtime(&replay_time); + char *time; + if (asprintf(&time, "%02d.%02d.%02d %02d:%02d", + t->tm_mday, t->tm_mon + 1, t->tm_year % 100, + t->tm_hour, t->tm_min)) + return time; + else + return NULL; +} + +cHistoryLogger::cHistoryLogger() +{ + replay_history_filename = NULL; + replay_history = new cList<cHistoryRecordingItem>(); +} + +cHistoryLogger::~cHistoryLogger() +{ + free(replay_history_filename); + replay_history->Clear(); + delete replay_history; +} + +void cHistoryLogger::Replaying(const cControl *Control, const char *Name, const char *FileName, bool On) +{ + if (On) { + if (FileName) { + // Check if file is a VDR recording + cThreadLock RecordingsLock(&Recordings); + cRecording *recording = Recordings.GetByName(FileName); + // Use name only if not a VDR recording + replay_history->Ins(new cHistoryRecordingItem(!recording ? Name : NULL, FileName)); + if (replay_history->Count() > HistorySetup.replay_history_size) + replay_history->Del(replay_history->Last()); + StoreReplayHistory(); + } + } +} + +void cHistoryLogger::DeleteRecordingItem(int index) +{ + if (index >= 0 && index < replay_history->Count()) { + replay_history->Del(replay_history->Get(index)); + StoreReplayHistory(); + } +} + +cHistoryRecordingItem *cHistoryLogger::GetRecordingItem(int index) +{ + if (index >= 0 && index < replay_history->Count()) + return replay_history->Get(index); + return NULL; +} + +cList<cHistoryRecordingItem> *cHistoryLogger::GetReplayHistory() +{ + return replay_history; +} + +void cHistoryLogger::ClearReplayHistory() +{ + replay_history->Clear(); + if (*replay_history_filename) { + FILE *f = fopen(replay_history_filename, "w"); + if (f) { + fclose(f); + } + else { + LOG_ERROR_STR(replay_history_filename); + } + } +} + +void cHistoryLogger::LoadReplayHistory() +{ + if (*replay_history_filename && access(replay_history_filename, F_OK) == 0) { + FILE *f = fopen(replay_history_filename, "r"); + if (f) { + char *s; + int line = 0; + cReadLine ReadLine; + while ((s = ReadLine.Read(f)) != NULL) { + ++line; + if (!isempty(s)) { + replay_history->Add(new cHistoryRecordingItem(s)); + } + } + fclose(f); + } + else { + LOG_ERROR_STR(replay_history_filename); + } + } +} + +void cHistoryLogger::StoreReplayHistory() +{ + if (*replay_history_filename) { + FILE *f = fopen(replay_history_filename, "w"); + if (f) { + cHistoryRecordingItem *item = replay_history->First(); + while (item != NULL) { + if (item->GetName()) + fprintf(f, "%li:%s=%s\n", *(item->GetReplayTime()), item->GetFilename(), item->GetName()); + else + fprintf(f, "%li:%s\n", *(item->GetReplayTime()), item->GetFilename()); + item = (cHistoryRecordingItem *)item->Next(); + } + fclose(f); + } + else { + LOG_ERROR_STR(replay_history_filename); + } + } +} + +void cHistoryLogger::SetReplayHistoryConfigFile(const char *FileName) +{ + free(replay_history_filename); + replay_history_filename = strdup(FileName); +} diff --git a/logger.h b/logger.h new file mode 100755 index 0000000..2948aab --- /dev/null +++ b/logger.h @@ -0,0 +1,54 @@ +/* + * logger.h: Keep log of replayed recordings + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __HISTORY_LOGGER_H_ +#define __HISTORY_LOGGER_H_ + +#include <time.h> +#include <vdr/status.h> +#include <vdr/tools.h> +#include "config.h" + +class cHistoryRecordingItem: public cListObject +{ +protected: + char *filename; + char *name; + time_t replay_time; + +public: + cHistoryRecordingItem(const char *String); + cHistoryRecordingItem(const char *Name, const char *FileName); + ~cHistoryRecordingItem() { free(filename); free(name); } + const char *GetFilename() { return filename; } + const char *GetName() { return name; } + time_t *GetReplayTime() { return &replay_time; } + char *GetReplayTimeString(); +}; + +class cHistoryLogger : public cStatus +{ +public: + cHistoryLogger(); + ~cHistoryLogger(); + void DeleteRecordingItem(int index); + cHistoryRecordingItem *GetRecordingItem(int index); + cList<cHistoryRecordingItem> *GetReplayHistory(); + void ClearReplayHistory(); + void LoadReplayHistory(); + void StoreReplayHistory(); + void SetReplayHistoryConfigFile(const char *FileName); + +protected: + virtual void Replaying(const cControl *Control, const char *Name, const char *FileName, bool On); + +private: + char *replay_history_filename; + cList<cHistoryRecordingItem> *replay_history; +}; + +#endif //__HISTORY_LOGGER_H_ @@ -0,0 +1,286 @@ +/* + * menu.c: The actual menu implementations + */ + +#include <stdlib.h> +#include <time.h> +#include <vdr/interface.h> +#include <vdr/plugin.h> +#include <vdr/remote.h> +#include <vdr/thread.h> +#include "config.h" +#include "logger.h" +#include "menu.h" +#include "tools.h" + +enum { + TYPE_NONE, + TYPE_IMAGE, + TYPE_VIDEO, + TYPE_MUSIC +}; + +class cHistoryRecordingMenu : public cOsdMenu { +private: + const cRecording *recording; + +public: + cHistoryRecordingMenu(const cRecording *Recording); + virtual void Display(void); + virtual eOSState ProcessKey(eKeys Key); +}; + +cHistoryRecordingMenu::cHistoryRecordingMenu(const cRecording *Recording) +:cOsdMenu(trVDR("Recording info")) +{ +#if VDRVERSNUM >= 10728 + SetMenuCategory(mcRecording); +#endif + recording = Recording; + SetHelp(trVDR("Button$Play"),trVDR("Button$Rewind")); +} + +void cHistoryRecordingMenu::Display(void) +{ + cOsdMenu::Display(); + DisplayMenu()->SetRecording(recording); + if (recording->Info()->Description()) + cStatus::MsgOsdTextItem(recording->Info()->Description()); +} + +eOSState cHistoryRecordingMenu::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == osUnknown) { + switch (Key) { + case kPlay: + case kRed: cRemote::Put(Key, true); + case kOk: return osBack; + default: break; + } + } + return state; +} + +class cHistoryRecordingMenuItem : public cOsdItem +{ +private: + char *filename; + char *name; + bool active; + +public: + cHistoryRecordingMenuItem(const char *Name, const char *Filename); + ~cHistoryRecordingMenuItem() { free(filename); free(name); } + const char *GetFilename() { return filename; } + const char *GetName() { return name; } + bool IsActive() { return active; } +}; + +// Use name only if not a VDR recording +cHistoryRecordingMenuItem::cHistoryRecordingMenuItem(const char *Name, const char *Filename) +{ + active = false; + filename = Filename ? strdup(Filename) : NULL; + name = Name ? strdup(Name) : NULL; + if (filename) { + if (!name) { + cThreadLock RecordingsLock(&Recordings); + cRecording *recording = Recordings.GetByName(filename); + if (recording) { + active = true; + SetText(recording->Title('\t')); + } + else + SetText(tr("Deleted or not available")); + } + else { + active = true; + SetText(name); + } + } + else + SetText(tr("Deleted or not available")); +} + +cHistoryMainMenu::cHistoryMainMenu(cHistoryLogger *HistoryLogger) : cOsdMenu(tr("Replay history"), 9, 6, 6) +{ + history_logger = HistoryLogger; + SetMenu(); + SetHelpKeys(); + Display(); +} + +void cHistoryMainMenu::SetHelpKeys() +{ + if (history_logger->GetReplayHistory()->Count() > 0) { + int curr = Current(); + if (curr == -1) { + for (cOsdItem *item = First(); item; item = Next(item)) { + if (item->Selectable()) { + curr = item->Index(); + break; + } + } + } + cHistoryRecordingMenuItem *ri = (cHistoryRecordingMenuItem *)Get(curr); + if (ri && ri->IsActive() && !ri->GetName()) + SetHelp(trVDR("Button$Play"), trVDR("Button$Rewind"), trVDR("Button$Delete"), trVDR("Button$Info")); + else if (ri && ri->IsActive()) + SetHelp(trVDR("Button$Play"), NULL, trVDR("Button$Delete"), NULL); + else + SetHelp(NULL, NULL, trVDR("Button$Delete"), NULL); + } +} + +void cHistoryMainMenu::SetMenu() +{ + Clear(); +#if VDRVERSNUM >= 10728 + SetMenuCategory(mcRecording); +#endif + cList<cHistoryRecordingItem> *replay_history = history_logger->GetReplayHistory(); + cHistoryRecordingItem *item = replay_history->First(); + while (item != NULL) { + char *time = item->GetReplayTimeString(); + Add(new cOsdItem(time, osUnknown, false)); + free(time); + Add(new cHistoryRecordingMenuItem(item->GetName(), item->GetFilename())); + item = (cHistoryRecordingItem *)item->Next(); + } +} + +cRecording *cHistoryMainMenu::GetRecording(cHistoryRecordingMenuItem *Item) +{ + cThreadLock RecordingsLock(&Recordings); + cRecording *recording = Recordings.GetByName(Item->GetFilename()); + if (!recording) + Skins.Message(mtError, trVDR("Error while accessing recording!")); + return recording; +} + +eOSState cHistoryMainMenu::Delete() +{ + cHistoryRecordingMenuItem *ri = (cHistoryRecordingMenuItem *)Get(Current()); + if (HistorySetup.allow_delete && ri && ri->IsActive() && ((!ri->GetName() && Interface->Confirm(tr("Delete also recording?"))) || (ri->GetName() && Interface->Confirm(tr("Delete also file?"))))) { + if (!ri->GetName()) { + cRecording *recording = GetRecording(ri); + if (recording && !recording->Delete()) + Skins.Message(mtError, tr("Unable to delete recording")); + } + else + if (!RemoveFileOrDir(ri->GetFilename())) + Skins.Message(mtError, tr("Unable to delete file")); + } + history_logger->DeleteRecordingItem(Current()/2); + return osContinue; +} + +eOSState cHistoryMainMenu::Info() +{ + cHistoryRecordingMenuItem *ri = (cHistoryRecordingMenuItem *)Get(Current()); + if (ri) { + cRecording *recording = GetRecording(ri); + if (recording && recording->Info()->Title()) + return AddSubMenu(new cHistoryRecordingMenu(recording)); + } + return osContinue; +} + +eOSState cHistoryMainMenu::Play() +{ + cHistoryRecordingMenuItem *ri = (cHistoryRecordingMenuItem *)Get(Current()); + if (!ri->GetName()) { + cReplayControl::SetRecording(ri->GetFilename()); + return osReplay; + } + else { + const char *filename = ri->GetFilename(); + int type; + if (isvideo(filename)) + type = TYPE_VIDEO; + else if (ismusic(filename)) + type = TYPE_MUSIC; + else if (isimage(filename)) + type = TYPE_IMAGE; + else + type = TYPE_NONE; + switch (type) { + case TYPE_VIDEO: + cPluginManager::CallFirstService("MediaPlayer-1.0", (void *)filename); + break; + case TYPE_MUSIC: + cPluginManager::CallFirstService("MusicPlayer-1.0", (void *)filename); + break; + case TYPE_IMAGE: + cPluginManager::CallFirstService("ImagePlayer-1.0", (void *)filename); + break; + case TYPE_NONE: + default: + break; + } + return osEnd; + } +} + +eOSState cHistoryMainMenu::Rewind() +{ + cHistoryRecordingMenuItem *ri = (cHistoryRecordingMenuItem *)Get(Current()); + cRecording *recording = GetRecording(ri); + if (recording) { + cDevice::PrimaryDevice()->StopReplay(); // must do this first to be able to rewind the currently replayed recording + cResumeFile ResumeFile(ri->GetFilename(), recording->IsPesRecording()); + ResumeFile.Delete(); + return Play(); + } + return osContinue; +} + +eOSState cHistoryMainMenu::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + if (state == osUnknown) { + cHistoryRecordingMenuItem *ri = (cHistoryRecordingMenuItem *)Get(Current()); + if (ri) { + switch (int(Key)) { + case kOk: + case kPlay: + case kRed: + state = Play(); + break; + case kGreen: + if (!ri->GetName()) + state = Rewind(); + break; + case kYellow: + state = Delete(); + SetMenu(); + SetHelpKeys(); + Display(); + break; + case kBlue: + case kInfo: + if (!ri->GetName()) + state = Info(); + break; + default: + break; + } + } + } + else { + switch (int(Key)) { + case kUp: + case kDown: + case kLeft: + case kRight: + SetHelpKeys(); + Display(); + break; + default: + break; + } + } + return state; +} @@ -0,0 +1,34 @@ +/* + * setup_menu.h: Setup Menu + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __HISTORY_MENU_H +#define __HISTORY_MENU_H + +#include <vdr/menu.h> +#include <vdr/menuitems.h> +#include <vdr/recording.h> + +class cHistoryRecordingMenuItem; + +class cHistoryMainMenu : public cOsdMenu +{ +private: + cHistoryLogger *history_logger; + void SetHelpKeys(); + void SetMenu(); + eOSState Delete(); + eOSState Info(); + eOSState Play(); + eOSState Rewind(); + cRecording *GetRecording(cHistoryRecordingMenuItem *Item); + +public: + cHistoryMainMenu(cHistoryLogger *HistoryLogger); + virtual eOSState ProcessKey(eKeys Key); +}; + +#endif //__HISTORY_MENU_H diff --git a/po/fi_FI.po b/po/fi_FI.po new file mode 100755 index 0000000..5632106 --- /dev/null +++ b/po/fi_FI.po @@ -0,0 +1,47 @@ +# VDR History plugin language source file. +# Copyright (C) 2012 Matti Lehtimäki +# This file is distributed under the same license as the PACKAGE package. +# Matti Lehtimäki, 2012- +# +msgid "" +msgstr "" +"Project-Id-Version: vdr-history 0.0.3\n" +"Report-Msgid-Bugs-To: <see README>\n" +"POT-Creation-Date: 2012-10-19 22:51+0300\n" +"PO-Revision-Date: 2012-10-19 23:00+0300\n" +"Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n" +"Language-Team: Finnish <vdr@linuxtv.org>\n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Show last replayed recordings" +msgstr "Näytä viimeksi toistetut tallenteet" + +msgid "History" +msgstr "Historia" + +msgid "Deleted or not available" +msgstr "Poistettu tai ei saatavilla" + +msgid "Replay history" +msgstr "Toistohistoria" + +msgid "Delete also recording?" +msgstr "Poista myös tallenne?" + +msgid "Delete also file?" +msgstr "Poista myös tiedosto?" + +msgid "Unable to delete recording" +msgstr "Tallennetta ei voitu poistaa" + +msgid "Unable to delete file" +msgstr "Tiedostoa ei voitu poistaa" + +msgid "Allow file delete" +msgstr "Salli tiedostojen poisto" + +msgid "Replay history size" +msgstr "Toistohistorian koko" diff --git a/setup_menu.c b/setup_menu.c new file mode 100755 index 0000000..f96b470 --- /dev/null +++ b/setup_menu.c @@ -0,0 +1,44 @@ +/* + * setup_menu.c: Setup Menu + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "setup_menu.h" +#include <vdr/config.h> +#include <vdr/i18n.h> + +//--- cHistorySetupMenu ------------------------------------------------------ + +cHistorySetupMenu::cHistorySetupMenu(void) +{ + memcpy(&newconfig, &HistorySetup, sizeof(cHistorySetup)); + Set(); +} + +void cHistorySetupMenu::Set(void) +{ + Clear(); + Add(new cMenuEditBoolItem(tr("Allow file delete"), + &newconfig.allow_delete)); + Add(new cMenuEditIntItem(tr("Replay history size"), + &newconfig.replay_history_size), 0); + Display(); +} + +void cHistorySetupMenu::Store(void) +{ + memcpy(&HistorySetup, &newconfig, sizeof(cHistorySetup)); + + SetupStore("AllowDelete", HistorySetup.allow_delete); + SetupStore("ReplayHistorySize", HistorySetup.replay_history_size); + + Setup.Save(); +} + +eOSState cHistorySetupMenu::ProcessKey(eKeys Key) +{ + eOSState state = cMenuSetupPage::ProcessKey(Key); + return state; +} diff --git a/setup_menu.h b/setup_menu.h new file mode 100755 index 0000000..2b08dd2 --- /dev/null +++ b/setup_menu.h @@ -0,0 +1,29 @@ +/* + * setup_menu.h: Setup Menu + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __HISTORY_SETUP_MENU_H +#define __HISTORY_SETUP_MENU_H + +#include <vdr/menu.h> +#include <vdr/menuitems.h> +#include "config.h" + +class cHistorySetupMenu : public cMenuSetupPage +{ +private: + cHistorySetup newconfig; + +protected: + virtual void Store(void); + void Set(void); + +public: + cHistorySetupMenu(void); + virtual eOSState ProcessKey(eKeys Key); +}; + +#endif //__HISTORY_SETUP_MENU_H @@ -0,0 +1,38 @@ +/* + * tools.c: History plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "tools.h" + +// +// File type detection code adapted from RSS Reader plugin for VDR +// http://www.saunalahti.fi/~rahrenbe/vdr/rssreader/ +// by Rolf Ahrenberg +// + +bool isimage(const char *text) +{ + if (endswith(text, ".jpg") || endswith(text, ".gif") || endswith(text, ".png")) + return true; + return false; +} + +bool isvideo(const char *text) +{ + if (endswith(text, ".mpg") || endswith(text, ".avi") || endswith(text, ".ts") || + endswith(text, ".mkv") || endswith(text, ".flv") || endswith(text, ".wmv") || + endswith(text, ".mp4")) + return true; + return false; +} + +bool ismusic(const char *text) +{ + if (endswith(text, ".mp3") || endswith(text, ".wav") || endswith(text, ".ogg") || + endswith(text, ".flac")) + return true; + return false; +} @@ -0,0 +1,18 @@ +/* + * tools.h: History plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __HISTORY_TOOLS_H_ +#define __HISTORY_TOOLS_H_ + +#include <vdr/tools.h> + +bool isimage(const char *text); +bool isvideo(const char *text); +bool ismusic(const char *text); + +#endif // __HISTORY_TOOLS_H_ + |