pax_global_header00006660000000000000000000000064151427114560014520gustar00rootroot0000000000000052 comment=0118b4129c3601c04c7483f2fd04b604d4a2a980 vdr-plugin-epgsearch-2.4.6/000077500000000000000000000000001514271145600155575ustar00rootroot00000000000000vdr-plugin-epgsearch-2.4.6/.gitignore000066400000000000000000000004641514271145600175530ustar00rootroot00000000000000# directories .vscode/ .settings/ doc/ html/ man/ # supplemental files .cproject .dependencies* .doc* .project apply-patches.sh *.BASE *.LOCAL *.REMOTE *~ *.bak *.code-workspace *.new *.old *.orig *.rej *.sav *.tgz *.tmp # build results MANUAL MANUAL.DE README README.DE createcats *.a *.mo *.o *.pot *.so vdr-plugin-epgsearch-2.4.6/COPYING000066400000000000000000000431061514271145600166160ustar00rootroot00000000000000 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. Copyright (C) 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. , 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. vdr-plugin-epgsearch-2.4.6/HISTORY000066400000000000000000002347071514271145600166600ustar00rootroot00000000000000VDR Plugin 'epgsearch' Revision History --------------------------------------- 2026-02-10 - Set version to 2.4.6 - Adaptation to vdr 2.7.8 - fix time_t usage for 32bit systems (Soeren Moch) - added svdrpclient.c needed for previous commit 2026-01-04 - Reworked svdrclient.h (get rid of namespace, new svdrpclient.c) - check response when using external svdrp - New method to check svdrp ready during startup 2025-12-15 - set version to 2.4.5 - set version of quickepgsearch epgsearchonly conflictcheckonly to 2.4.5 - new Variable "Aux" allows access to auxiliary field of EPG - removed deprecated call to MainThreadHook(void) - add column with "time of last recording" to search timer results - Search for repeats in EPG menu will ignore (episodes) in title - Add general searchtimer config to limit days of timer creation - Add current searchtimer id to some log messages 2025-12-04 - removed 'HISTORY.DE', as no more changes have been recorded since years - revised German translation w.r.t. typos, readability, and style - changed menus to use 'tr()' string constants, where possible - fixed typos in 'tr()' strings (and other code locations accidentally found) - fixed wrong pairs of 'new' operator and 'free()' (instead of 'delete' operator) - fixed some memory leaks, like multiple 'strdup()' assignments with just one 'free()' - fixed some buffer operations without (correct) capacity checks - simplified 'free()' and 'delete' by skipping unnecessary null-pointer test - consolidated encoding and decoding of special characters ':' and '|' - removed further 'using std' directives missed last time - removed orphan 'options' in searches and blacklists (see Version 0.9.15) - aligned capabilities (parameter sets) of searches, templates, and blacklists - sorted class attributes to have related variables close to each other - revised constructors and copy methods to use the same sequence of member access - fixed some issues encountered during rework, like template fields not copied - supplied some missing parameter ranges in menus, where reasonable - rejected duplicate channel-groups names (menu: rename; SVDRP: NEWC, RENC) - added parental rating as new search parameter - added flexible matching criteria for content descriptors - refactored missing EPG categories into category matching mode - refactored some identifiers with hard-to-conceive semantics - revised 'AddHelp()' to consider menu context, not just position - revised man pages ('doc-src' files) to comply to POD syntax - updated manuals to cover new (or yet undocumented) features, observing 'man-pages(7)' guidelines - added missing German man pages. i.e., 'createcats' and 'epgsearch(4)' - disabled insertion of color codes for pages in 'doc' directory - added 'Q' macros to 'Makefile' for more silent builds - revised detection of 'pin' plugin in build space - added POD check to the creation of man files 2025-09-26 - Makefile: make WITHOUT_xxx variables effective (madmartin) 2025-07-25 - replace Matches(time,true) with CalcStartStopTime 2025-06-27 - delete contentfilter if not set again in EDIS - changes in Ext::Parse provided by Stefan Hofmann - updated corresponding Manual entries - more error checking - avoid crash - evaluating return code - ignore error lines in epgsearch.conf - read rest - forgot README in .gitignore 2025-06-22 thanks to Stefan Hofmann: - get rid of 'using' directives, always use namespace prefix 'std' 2025-05-15 - provide timer in SetItemEvent with vdr 2.7.5 thanks to Stefan Hofmann: - remove compiler warnings - some small fixes and annotations - avoid string overflows 2025-03-20 fixes: - fix timerdone processing if EPG has changed - prevent crash in vdr 2.7.4 2024-09-13; Version 2.4.3 fixes: - fix deprecated funtion GetEvent() 2024-04-07 fixes: - add missing std::namespace (amair) 2024-01-31 fixes: - fix Makefile for coreutils >= 9.4 (kfb77) - add missing free (kfb77) 2023-06-08 fixes: - removed subtitle limit (firefly) - fix title containing more than one colon (firefly) 2023-05-28 fixes: - Changed wenig to Wenig in German messages - Avoid division by zero if StopTime-Starttime=0 2023-01-09; Version 2.4.2 new: - Use perlregex-library pcre2 if available fixes: - timers not marked as done if 2 recordings stop same time - remove dead code - update spanish translation - fix memory leaks - do not lose recordingtime at change to winter time (kfb77) 2022-02-01 new: - Evaluate vdr's Errorcount (vdr > 2.5.x) New config "Allowed Errors" - Allow missing timer-Object in Recording-Done-Processing (vdr > 2.5.x compatibility) 2021-12-10 fixes: - Wrong device selection for conflict check - compareSubtitle supports more than 2 values 2021-05-24; Version 2.4.1 fixes: - Fixed conflictcheck for encrypted channels with internal CAMs - Fixed compiling with gcc11 - Updated deprecated calls to SetItemEvent - Temporally(?) added #define DISABLE_TEMPLATES_COLLIDING_WITH_STL for compatibility with vdr 2.5.4 2021-04-12 new: - Improved handling of remote timers - Replace auto_ptr with unique_ptr for c++11 (kfb77@vdr-portal.de) - Delay threads after pluginstart 10 secs (configurable) - Changed fgets to allow compiling with clang - Clarified "avoid repeats". Forced subtitle-comparison is named "yes" again New option "allow empty" fixes: - Fixed several lock sequence errors - Fix utf-8 encoding in docs and manpages (kfb77 and seahawk1986@vdr-portal.de) - Fixed possible format overflow - Fixed displaying NAME in generated manpages thanks to etobi 2018-04-16; Version 2.4.0 new: - Create man pages only once. by jasminj@vdr-portal.de - Remove useless member "useEpisode" from cBlacklist by jasminj@vdr-portal.de - Remove useless member "useEpisode" from cBlacklist by jasminj@vdr-portal.de - Remove useless "LOCK_CHANNELS_READ" in cBlacklist run by jasminj@vdr-portal.de - Change shell in docsrc2*.sh to /bin/bash, suggested by TomJoad@vdr-portal.de - Added operator to cListObject derived classes by jasminj@vdr-portal.de - Add patches/vdr.epgsearch-exttimeredit-2.3.5.diff by Dietmar Spingler - Adapt new makefile style of VDR 2.3.6 by TomJoad@vdr-portal.de - Use vdr timerids instead of index by TomJoad@vdr-portal.de - Fix many lock sequences by TomJoad@vdr-portal.de - New vdr.epgsearch-exttimeredit-2.3.6.diff (thx to Claus Muus) - Add conflict check for remote timers by Johann Friedrichs - Search timers should create only local timers - Use namespace for svdrpclient - Add icon for inactive timer - Use separate thread for recdone processing - get rid of some casts - unified indentation - Preserve timerflags in searchtimerupdate 2017-05-xx; Version 2.3.1 - development release new: - Commit vdr-2.3.2-epgsearch-1.0.1.beta5~git20150715_v2.diff by fnu@vdr-portal.de based on inputs from kamel5, mini73 & TomJoad @ vdr-portal.de => http://www.vdr-portal.de/board17-developer/board97-vdr-core/p1284612-produktive-problem-und-pluginl%C3%B6sungen-f%C3%BCr-vdr-2-3-2-und-h%C3%B6her/#post1284612 - Commit 0004-Added-patches-vdr.epgsearch-exttimeredit-2.3.3.diff.diff by jasminj@vdr-portal.de => http://www.vdr-portal.de/board17-developer/board21-vdr-plugins/p1289938-epgsearch-f%C3%BCr-vdr-2-3-x/#post1289938 - Commit 0005-epgsearch-inactive-records-v2.diff by jasminj@vdr-portal.de => http://www.vdr-portal.de/board17-developer/board21-vdr-plugins/p1289983-epgsearch-f%C3%BCr-vdr-2-3-x/#post1289983 - Commit 0001-Fix-warning-in-pending_notifications.c.diff by jasminj@vdr-portal.de - Commit 0002-Fixed-warnings-in-man-page-generation.diff by jasminj@vdr-portal.de => http://www.vdr-portal.de/board17-developer/board21-vdr-plugins/p1289704-epgsearch-f%C3%BCr-vdr-2-3-x/#post1289704 - Commit 0003-Use-HandleRemoteTimerModifications-instead-of-Handle.diff by jasminj@vdr-portal.de => http://www.vdr-portal.de/board17-developer/board21-vdr-plugins/p1289764-epgsearch-f%C3%BCr-vdr-2-3-x/#post1289764 - Commit menu_category_recsdone.diff by tomas & louis @vdr-portal.de http://www.vdr-portal.de/board1-news/board2-vdr-news/p1271995-skindesigner-1-0-0-neuer-default-skin-estuary4vdr/#post1271995 - Commit 0001-revert-seperate-status-thread.diff by TomJoad@vdr-portal.de - Commit 0002-fix-incorrect-lock-sequences.diff by TomJoad@vdr-portal.de - Commit 0003-revert-now-obsolete-pointer-params.diff by TomJoad@vdr-portal.de - Commit 0004-some-small-fixes.diff by TomJoad@vdr-portal.de http://www.vdr-portal.de/board17-developer/board21-vdr-plugins/p1291452-epgsearch-f%C3%BCr-vdr-2-3-x/#post1291452 - Drop legacy code prior VDR 2.3.x - Commit 0001-fixed-unresponsive-vdr.diff by TomJoad@vdr-portal.de - Commit 0002-removed-variableduplications.diff by TomJoad@vdr-portal.de - Commit 0003-more-cleanups.diff by TomJoad@vdr-portal.de - Commit 0004-Remote-timers-in-menu-myedittimer.diff by TomJoad@vdr-portal.de http://www.vdr-portal.de/board17-developer/board21-vdr-plugins/p1291771-epgsearch-f%C3%BCr-vdr-2-3-x/#post1291771 - Commit fixblacklist_handling.diff by TomJoad@vdr-portal.de http://www.vdr-portal.de/board16-video-disk-recorder/board99-distributionen/board107-mld/p1292085-epgsearch-in-verbindung-mit-epgd-epg2vdr-live-absturz-unter-vdr-2-3-4/#post1292085 - Commit epgsearch_min_max_from_stl.diff by jasminj@vdr-portal.de http://www.vdr-portal.de/board17-developer/board21-vdr-plugins/p1292145-epgsearch-f%C3%BCr-vdr-2-3-x/#post1292145 2013-03-xx; Version 1.0.1 - maintenance release new: - new Makefile style as introduced in vdr-1.7.36, the old Makefile still exists as Makefile.before.1.7.36 for previous vdr builds. Many thanks to Copperhead, Stefan Hofmann and Christopher Reimer for their work. - implement device bonding in conflict checker, thanks to Joachim Wilke for providing a patch - new service interface "Epgsearch-enablesearchtimers-v1.0" to switch the search timers background update on/off. Updated the sample in source/vdr-epgsearchclient-0.0.2.tgz to use the new service. - new format specifier like %02i in the epgsearchcats.conf, thanks to Joe_D for providing a patch ('man 5 epgsearchcats.conf' for more information). - in the menu 'recordings done' you can now toggle the blue key to 'Orphaned' to show recordings with vanished search timers. - when channel separators are displayed the filling '-' can cause problems in the output of graphlcd. Set PLUGIN_EPGSEARCH_SEP_ITEMS=--- in your Make.config to avoid this. Most skins (except classic, st:tng) will translate '---' into a single line anyway (Feature #857, thanks to 'KeineAhnung' for providing a patch) - search timers are now processed by their descending timer priority. Search timers with the same priority are sorted by their search term. Thanks to Anonym for providing a patch - new bugtracker at http://projects.vdr-developer.org/projects/plg-epgsearch - thanks to Tobias Grimm - dropped old code for vdr < 1.6.0, thanks to Ville Skyttä for whole pile of patches - updated MainMenuHooks patch to 1.0.1 - czech translation, thanks to Radek Stastny fixes: - fixed a memory leak, thanks to Sundararaj Reel for the patch - fixed a crash when editing blacklists 2011-09-11; Version 1.0.0 new: - supports vdr-1.6.0 to vdr-1.7.21 - avoid repeats with new 'compare date' entry: compare two events by their date to ignore repeats within the same day, week or month - global blacklists: blacklists for search timers can now be global to exclude generally unwanted events (like on my double SD/HD channels). Default mode for search timers is now 'only global', but can be set to 'none' to ignore also global blacklists. - new setup variable epgsearch.ConflCheckCmd (no gui for this, so edit setup.conf!), that allows executing a command for each timer causing a conflict, see the MANUAL for details. - vdr-1.7.15 has changed the SVDRP default port to 6419, please update this in epgsearch's setup menu too! - there is now an official git repository for epgsearch, that contains the latest development. First usage: git clone git://projects.vdr-developer.org/vdr-plugin-epgsearch.git Keep up-to-date with: git pull Web-git: http://projects.vdr-developer.org/git/?p=vdr-plugin-epgsearch.git many thanks to the maintainers of projects.vdr-developer.org, especially Tobias Grimm - directory entries from VDR's folders.conf are now also read and offered in the directory selection of the timer menu. - Search timer support for content descriptors as introduced in vdr-1.7.11. This lets you search for broadcasts by their type, like "Movie/Drama", "Documentation",... - Search timers now have a new action "Announce and switch". This announces the event via OSD right before it starts and lets you switch to its channel with 'Ok'. Switch timers now have the same option. - in addition to the announcements via OSD new events can now also be reported by mail. To do so, there's a new search timer action "Announce by mail". You also have to update your mail template file epgsearchupdmail.templ (s. the updated html sample in the conf directory and/or read the MANUAL '13. Email notification'). - The time in hours between the search timer mails can now be configured in the setup to avoid flooding your inbox. epgsearch buffers the contents of the pending mails in the new file pendingnotifications.conf. - New setup option to check if there is EPG content for the next x hours. If not you get warned by OSD and/or mail (Setup -> Search and search timers), suggested by Andreas Mair. - new internal variables: * %day%, %month% and %year% which return the numeric day, month and year (with century) of an event * %chgrp% returns the VDR channel group name corresponding to an event * %liveeventid% returns the encoded event ID as used in the frontend 'live' to add direct links e.g. in the search timer mails (see sample conf/epgsearchupdmail-html.templ). * %timer.liveid% returns the encoded timer ID as used in the frontend 'live' to add direct links e.g. in the search timer mails. * %date_iso% and %date_iso_now% return the (current) date in 'YYYY-MM-DD' format, suggested by Andreas Mair. * %search.series% returns 1 or 0 depending on the flag "series recording" of a search and can be used in the directory entry of a search or its depending variables. - new command 'connect' within internal variables: with this command you can connect to a TCP service, pass data and assign the result to a variable. See the MANUAL for details. - new command 'length' within internal variables: this returns the length of the given arguments - in memory to pat: french translation update, thanks to Patrice Staudt - italian translation update, thanks to Diego Pierotto - finnish translation update, thanks to Rolf Ahrenberg and Ville Skyttä - new lithuanian translation, thanks to Valdemaras Pipiras - new slovak translation, thanks to Milan Hrala - new SVDRP command 'MENU [NOW|PRG|SUM]' to call one of the main OSD menus or the summary of the current event. If any epgsearch menu is open a subsequent SVDRP call will close it again. - changed the maximum number of days for a timer conflict check from 99 to 14 - patch by Jörg Wendel for new graphtft patch - Two events with both empty episode names are now handled different within the feature 'Avoid repeats'. This will result in more recordings, but ensures not to miss one only because of a buggy EPG. - searchtimers: if a timers filename results in an empty string or contains "!^invalid^!" it will be skipped for programming now. - Avoid repeats: The option 'Yes' for 'Compare subtitle' is now replaced with 'if present'. With this setting epgsearch will classify two events only as equal if their episode names match and are not empty. - epgsearch now uses the shutdown handler (introduced in vdr 1.5.1) to prevent a search timer update to be interrupted. - the SVDRP command UPDS for triggering search timer updates has now a new option 'SCAN' that will execute an EPG scan before the search timer update actually starts. - when deleting a search timer now its list of created timers is also cleared, suggested by Sundararaj Reel - moved 'file' and 'directory' to the top in the timer edit menu, since at least in my case its the most common entry to change, e.g. to select a folder for the recording. - auto enable Wareagle icons if VDRSymbols font is used (can be overwritten with WarEagleIcons=0 in epgsearchmenu.conf), suggested by Ronny Kornexl - the correct content encoding for mail notifications is now automatically detected - epgsearch now autodetects an installed pin plugin or graphtft, also the optional libraries libpcre und libtre (can be turned off by commenting AUTOCONFIG in the Makefile) - new patch vdr.epgsearch-exttimeredit.diff: this patch against VDR integrates epgsearch's timer edit menu to VDR's timer menu, thanks S:oren@vdr-portal for providing it. - some speed enhancements, thanks to Tobias Bratfisch for providing patches - if the VPS time differs from the start time of an event the VPS marker is now 'v' instead of 'V' - the first run of the background threads (searchtimer, switchtimer, conflict check) is now triggered by the first call to cPlugin::MainThreadHook instead of waiting 20s after VDRs startup. - The update check for manual timers does now ignore timers whose start or stop time was edited by the user. - default path to sendmail is now '/usr/sbin/sendmail' and can be configured in the Makefile, thanks to Ville Skyttä for providing a patch. - externally triggered search timer updates (via service interface or SVDRP) will now automatically activate the setting "Use search timers" in the setup. - epgsearch now also checks the recordings length among the correct start and stop time of a timer when testing for complete recordings. 98% and more are handled as complete. - switch timers are now also visible in all search results EPG menus - Avoid repeats: in 'compare summary' one can now adjust the required match, default is 90%. - if there is no subtitle the filename now defaults to YYYY.MM.DD-HH.MM-Weekday to ease sorting in some frontends, thanks to Dominic Evans for providing a patch - should now compile in FreeBSD, thanks to Juergen Lock for providing a patch - new SVDRP command UPDT to reload the list of search timers in epgsearch.conf fixes: - fixed a crash when pressing 'Ok' in an empty timers done menu - fixed a crash when using the progressbar and events with 0 duration exist, thanks to egal@vdrportal - when an incomplete recording triggers a search timer update for a search timer with 'avoid repeats' now running events are ignored for the repeats. - now using cCondWait::Wait instead of cCondWait:SleepMs to avoid shutdown problems, thanks to e9hack@vdrportal for providing a patch - fixed line breaks in SVDRP command LSTT, thanks to Andreas Mair for providing a patch - improved response time when canceling the search timer thread - fixed a segfault occurring when navigating to a userdefined epg menu that has expired in the meantime, thanks to Mike Constabel for reporting - the day selection menu in the timer edit menu was hidden - if an event was matched by multiple search timers with 'announce only' setting, it was also listed more than once. Thanks to Andreas Mair for reporting. - fixed a bug in search timer announcements concerning the display of the corresponding search timer - fixed some memory leaks, thanks to Bittor Corl for providing a patch - some fixes to compile with gcc-4.4, thanks to firefly@vdrportal for providing a patch - fixed wrong man section of some man pages, thanks to Ville Skyttä for providing a patch - some fixes regarding libtre includes, thanks to Ville Skyttä for providing a patch - fix UTF8-character handling in timer file names, thanks to Rolf Ahrenberg for providing a patch - fixed a crash when adding huge episode names to the timer's file name, thanks to Lari Tuononen for reporting - make sure only one regular expression library is linked against epgsearch, thanks to Ville Skyttä for providing a patch. - some providers have strange EPG time changes only by some seconds. epgsearch now ignores changes less than 60s and does not touch the corresponding timer or report by mail, thanks to cmichel@mantis for reporting - possible fix of the old problem with a crash in libpcre, thanks to Andreas Cz. for the patch and to Stefan Bauer for pointing me to it. - fix a crash when toggling between with/without subtitle in timer info view for events that have (very) long "short" texts, thanks to Ville Skyttä for providing a patch. - fixed file descriptor handling when there are errors in SVDRP communication, thanks to Teemu Rantanen for providing a patch. - fixed the service interface for switchtimers because of wrong parameter handling, thanks to gnapheus@vdrportal for reporting. - fixed case insensitve searching when Utf-8 characters are involved, thanks to Ville Skyttä for reporting - fixed check of complete VPS recordings, thanks to durchflieger@vdr-portal for providing a patch. 2008-04-29: Version 0.9.24 new: - support for vdr-1.6.x/1.7.x - speedup in searching and search timer updates (about 25%) - speedup in EPG menues, thanks to patch authors from http://www.open7x0.org - support for VDRSymbols font (activate it with 'WarEagle=1' in epgsearchmenu.conf) - the EPG command 'Search in recordings' now evaluates the info.vdr instead of the recordings path name and also does fuzzy searching, suggested by Mase@vdrportal - search timers with action 'switch only' and switch timers now have an additional option 'unmute sound' which unmutes audio at the event, if it was off, suggested by Michael Brückner - the timer update notification mails supports now an additional variable %timer.modreason%, that holds the reason for a timer update in plain text (see epgsearchupdmail(-html).templ for a sample) - support for a conf.d mechanism (s. MANUAL -> 14. The conf.d subdirectory), suggested by Mike Constabel. - new SVDRP command 'LSCC' that returns the results of a timer conflict check. See the MANUAL for details about the format of the result list. - new patch against VDR (vdr-1.5.17-progressbar-support-0.0.1.diff) that adds support for graphical progressbars in skins classic and st:tng, thanks to zulu@vdrportal - '0' in the menu of done recordings now toggles the display of the episode name only - the favorites menu can now also be displayed after 'Overview - Now' via setup, suggested by Bittor Corl - menu of recordings done now sorts the date top down - support for new info key behaviour in vdr-1.5.13 - changes for builtin graphtft-patch (when using VDR-extension-patch you need > v.37) - updated the timercmd-patch for vdr-1.5.12 (patches/timercmd-0.1_1.5.12.diff) - added patches/README.patches to describe the existing patches - update of 'undoneepgsearch.sh', a script to remove recordings from the done file via reccmds.conf, thanks to Viking@vdrportal. - update of finnish translation, thanks to Rolf Ahrenberg - full spanish translation, many thanks to agusmir, dragondefuego, GenaroL, lopezm and nachofr from todopvr.com, and especially to bittor - update of italian translation, thanks to Diego Pierotto - update of dutch translation, thanks to carel@bugtracker - the setup option "No announcements when replaying" is now ignored, if the search timer update was triggered manually, suggested by Andreas Mair. fixes: - shifting the time display: the start time now only gets displayed in 'Overview - Now' instead of a progressbar, if there's not already a start time in the menu template ('%time%') as in the default template, thanks to Getty@vdrportal for reporting - fixed some issues regarding GPL, thanks to Thomas Günther for reporting - fixed a crash when no EPG is present, thanks to Petri Helin for providing a patch - the default value for maximum duration is now '23:59' to avoid problems with external tools, thanks to ralf-naujokat@bugtracker for reporting (bug-id #371) - after an EPG change of less then 10 minutes epgsearch modified a timer instead of creating a new one. This caused problems with events with less then 10 minutes. The tolerance is therefore now min(, 10 min). Thanks to Janne Liimatainen for reporting. - fixed some translations, thanks to ramirez@vdrportal for reporting - speed improvement when scrolling through EPG menus, thanks to ramirez@vdrportal for reporting - fixed a crash, when SIGINT is signaled while background threads startup - channel group separators in 'Overview Now/Next/...' are now hidden if they are empty like in ':@30', thanks to Ulf Kiener for providing a patch - fixed a crash in the setup of the addon plugins when VDR is patched with the ext-patch and active LIEMIKUUTIO - 'avoid repeats' combined with 'pause on ... recordings' created to less timers, thanks to spockele@vdrportal for reporting - fixed some compiler warnings with g++ 4.3 - fine-tuning fuzzy matching of descriptions, thanks to Alf Fahland for providing a patch - fixed a problem with pipes in the pattern of blacklists, thanks to Andreas Mair for reporting. - fixed labeling the green/yellow color keys after toggling the keys in menu 'Schedule', thanks to Andreas Mair for reporting. - fixed evaluation of compiler flags like WITHOUT_EPGSEARCHONLY,... 2007-09-02: Version 0.9.23 new: - full support for new i18n system in vdr>=1.5.7, but keeps fully backwards compatible - when using extended EPG categories one can now also compare by value, e.g. to search for events after some year. Therefore new searchmodes (10 which means '<', 11 which means '<=', ...) were introduced in the file epgsearchcats.conf. Example: # 'until year' 3|Year|until year||11 # 'from year' 12|Year|from year||13 One could use this also to search for a specific season of a series, if this data is part of the EPG. See the section in the documentation for a complete list of all search modes. - The edit menu for search timers has now a switch 'Ignore missing categories' for extended EPG categories. If set to 'Yes' this tells epgsearch that a missing EPG category should not exclude an event from the results. Caution: Using this without any other criterions could flood your timers. - Search timers have now an 'auto delete' feature. The edit menu therefor provides: * after x recordings, or * after x days after first recording Only complete recordings are counted. The deletion of the search timer is executed directly after the end of the corresponding recording - new action "Create a copy" in menu Search/Actions to create and edit a copy of the current search, suggested by Michael Brückner. - the option "use as search timer" has now a third value 'user defined' besides 'yes' and 'no', that allows specifying a time margin where the search timer is active, suggested by jo01@vdrportal - the progressbar now displays the start time instead of an empty bar, when shifting to future events, thanks to zulu@vdrportal for providing a patch. - menu "show timers created" shows now summary for the timers done with 'Ok'. Also the shortcuts '1..9' for the EPG-commands are available. - built-in pin-plugin patch (no more need to patch epgsearch). Activate it by compiling with 'USE_PINPLUGIN' in VDR's Make.config (as already done in VDR extension patch) - built-in graphtft-plugin patch (no more need to patch epgsearch). Activate it by compiling with 'USE_GRAPHTFT' in VDR's Make.config (as already done in VDR extension patch) - update of finnish translation, thanks to Rolf Ahrenberg - update of french translation, thanks to Patrice Staudt - added file README.Translators with notes for translators fixes: - fixed a bug in timer creation after navigating through the summary menu, thanks to Rolf Ahrenberg for reporting - Label "Record" or "Timer" in menu summary fixed with respect to existing timer. - fixed switch timers in the case when EPG has changed, thanks to Juergen Urban for providing a patch - fixed some compiler warnings in g++-4.2, thanks to Michael Brückner for reporting - added "," to the allowed characters for a search timer, thanks to Mike Constabel for reporting. 2007-05-27: Version 0.9.22 new: - new option in timer conflict check "When a recording starts": This performs a conflict check when any recording starts and informs about it via OSD if the conflict is within the next 2h. So also timers not created with epgsearch are checked, the same for instant recordings. - new service interface "Epgsearch-services-v1.0": This allows other plugins to access and manage many components of epgsearch as searchtimers, setup values,... and execute tasks like searching for repeats (Have a look at services.h for more information). - new project homepage: http://winni.vdr-developer.org/epgsearch, many thanks to Thomas Keil, sorry, only german at the moment ;) - update for finnish translation, thanks to Rolf Ahrenberg fixes: - complete rewrite of the summary menu and its navigation to avoid problems with skins and fix some other bugs. - when testing for repeats now also the comparison with the done-file only checks the alphanumeric portions of title and episode - fixed another issue with quotes when calling commands for EPG events - some license changes and additions for integration in Debian repositories - fixed a bug in displayed time, when using user defined times and shifting with FRew/FFwd, thanks to Torsten Weigelt for reporting - the aux info about the channel was not added in 'one press timer creation'. Thanks to Rolf Ahrenberg for providing a patch. 2007-04-29: Version 0.9.21 new: - support for the new MainMenuHooksPatch. This replaces the vdr-replace-schedulemenu patch. The new patch is used by other plugins too, so from now on only one patch is needed. The old patch is still supported, but this will be removed in the next releases. - announcements of broadcasts via OSD have been completely redesigned. Instead of displaying them event by event, you get now "x new broadcast(s) found! Show them?". Pressing 'Ok' shows a menu of all events with the usual functions as in other EPG menus. With "Edit" you can modify the announce settings (like "announce again: yes/no" or announce again after day x). - timer conflict notifications via OSD can now be suppressed while replaying. Nevertheless, if a conflict comes up within the next 2 hours the message gets still displayed. - all addon plugins (epgsearchonly, quickepgsearch, conflictcheckonly) now have a setup option to toggle the main menu entry on/off, thanks to Tobias Grimm for providing a patch. - channel name in aux info of manual timers, thanks to Rolf Ahrenberg for providing a patch. - new script undoneepgsearch.sh to undo a timer within the recordings menu, more info at: http://www.vdr-portal.de/board/thread.php?postid=574109#post574109 Thanks to the author Christian Jacobsen - update for french translation, thanks to Patrice Staudt - update for finnish translation, thanks to Rolf Ahrenberg - menu 'timer conflicts details' now also shows the date of the conflict in menu title, suggested by Rusk@vdrportal. - the password for mail authentication is now hidden in the OSD, i.e. represented with '***...' - the setup for mail notifications now also has a "Send to" address, because some providers don't allow the same address for sender and recipient. If "Send to" is not set, epgsearch automatically uses the sender address as recipient. - when testing for repeats (with feature 'Avoid repeats') epgsearch now only compares the alphanumeric portions of title and episode and ignores the case too. Suggested by chello@vdrportal. - thanks to Rolf Ahrenberg for finnish translation update - added '&' to the set of valid chars for search terms fixes: - the tags stored in the aux info of the timers created with search timers conform now to correct XML syntax (no capitals, no blanks). E.g. "Search timer" will be changed to "searchtimer". So don't wonder if the first search timer update will modify all existent timers created with search timers. Thanks to Rolf Ahrenberg for reporting. - update of recordingdone.sh because of the previous changes, thanks to Mike Constabel for providing a patch - fixed a segfault in the case of misconfigured extended EPG categories. - scrolling text items now works again, if your skin supports this. Thanks to ufauser@vdrportal for reporting. - setup option "Use search timers" now always gets automatically enabled, if one toggles a search to 'active' or manually starts a search timer update. - fixed handling of double quotes in title/episode when passed to userdefined EPG commands, thanks to Mike Constabel for reporting. - fixed menu handling in search timer templates menu, thanks to wombel@vdrportal for reporting. 2007-01-30: Version 0.9.20 new: - support for vdr-1.5.0. Note: the CA support in the timer conflict check is highly experimental because I have no CAMs to test it! - epgsearch can now also modify manual timers according to EPG changes. When using epgsearch's own timer edit menu you can select between * no check * by event ID * by channel/time Please refer to the README (1.4.4 Setup/Timer Programming/Default timer check method) for further notes. - the timespan used in the favorites menu can now be adjusted via setup. Default is 24h. - Event announcements: If the user presses one of the keys 'Ok', '0', ... '9' while the announcement of an event is displayed, he will be asked if further announcements of this event should be disabled for ever (user hit '0' or 'Ok') or for the next 'x' days (user hit '1' to '9'). After pressing 'Ok' again, this setting will be stored. - With the new setup option "No announcements when replaying" you can now suppress any announcements of broadcasts when you currently replay anything. Suggested by Andreas Mair. - Besides the done file for recordings there is now a done file for timers (timersdone.conf). This allows deleting timers without getting them again with the next search timer update. With the new menu 'Show timers created' in Search/Actions one can edit this list. The whole feature can be disabled in the search timers setup. Note: the done list is only populated with timers that were newly created with this release. - new addon plugin 'quickepgsearch': it creates a main menu entry 'Quick search' and can be used to search for any event in the EPG. Include it with '-Pquickepgsearch' , suggested by SurfaceCleanerZ@vdrportal. - new setup option "Limit channels from 1 to" to speed up epgsearchs call. If the current channel is above the limit, all channels will be displayed. Suggested by Uwe@vdrportal. - new SVDRP commands: * 'LSRD' returns a list of all recording directories used in existing recordings, timers, search timers or as specified in epgsearchdirs.conf * 'LSTT [ID]' to list all search templates, or the one with the passed ID (format is the same as epgsearch.conf) * 'NEWT ' to add a new search template REMARK: the value of element ID is ignored. epgsearch will always assign the next free ID * 'DELT ' to delete the search template with ID * 'EDIT ' to modify an existing search template * 'DEFT [ID]' returns the ID of the default search template. When passing an ID it activates the corresponding template as default. - modified SVDRP commands: * 'DELS [ID] [DELT]' now also deletes created timers for search with ID when passing the optional 'DELT'. - you can now also use sendmail for mail delivery besides the script sendemail.pl (-> setup), suggested by Patrick Cernko - new variables: * '%timespan%' to be used in menus or mails. It will be replaced with the timespan from now to the begin of an event (e.g. 'in 15m'). It is now used in the default menu template of the favorites menu, where the date field was removed. * '%length%' for the length of a broadcast in seconds. - thanks to Rolf Ahrenberg for finnish translation update fixes: - manually created timers are now neither deleted nor modified by any search timers. - the addon conflictcheckonly was compiled even if WITHOUT_CONFLICTCHECKONLY was defined in the Makefile, thanks to Ronny Kornexl for reporting. - search term and directory support now the characters 'ß' (only for language german) and '@'. - 'avoid repeats' had a bug (in some special cases) that created a timer for any later repeat instead of the first event. - a search with "Use day of week" AND "Use time" where "Start before" is after midnight now also matches events on the next day til "Start before". E.g. "Start after 22:00" and "Start before 03:00" on monday will also match an event at 01:00 at tuesday. - already recording timers are now modified only, if the timers stop time has changed (e.g. by an EPG update), thanks to Mike Constabel for reporting. - fixed a bug in evaluation of user variables, thanks to Mike Constabel for reporting and remote debugging - fixed a bug in conflict mail notification concerning timers without assigned events 2006-10-27: Version 0.9.19 new: - if search results of different searches intersect, now only the search that initially created a corresponding timer may modify it. - new variables: * '%search.query%' to be used in the recording directory of a search timer. Will be substituted to the query of a search timer. * '%videodir%' VDR video directory (e.g. /video) * '%plugconfdir%' VDR plugin config directory (e.g. /etc/vdr/plugins) * '%epgsearchdir%' epgsearchs config directory (e.g. /etc/vdr/plugins/epgsearch) - the syntax of the 'system' command within a user variable has changed to be more flexible. It's now: %uservar%=system(/path/to/script[, parameters]) where the optional 'parameters' can now be any expression using other variables except directly using conditional expressions or other system calls. - update for french translation, thanks to Patrice Staudt fixes: - VPS timers created by search timers are now always updated to their VPS time, even if the user has changed start/stop time. - after editing the setup any menu templates defined in epgsearchmenu.conf were reset to defaults, thanks to s.krueger@vdrportal for reporting. - existing VDR serial timers are not modified anymore to regular timers by the search timer update. 2006-10-01: Version 0.9.18 new: - IMPORTANT!!! PLEASE READ THIS!: epgsearch expects its conf files now in a separate configuration directory 'epgsearch' and not in the general plugin configuration directory as with previous versions! Please create this directory in your plugins configuration directory and move all epgsearch-files to it before running this version. Like this: mkdir /etc/vdr/plugins/epgsearch mv /etc/vdr/plugins/epgsearch*.* /etc/vdr/plugins/epgsearch mv /etc/vdr/plugins/.epgsearch* /etc/vdr/plugins/epgsearch This is only important if you don't already use epgsearchs '--config' or '-c' parameter. - new 'Favorites menu' besides 'Now' and 'Next': This menu can show a list of all your favorite broadcasts for the next 24h. To enable it activate 'Show favorites menu' in the setup. To let epgsearch know what your favorites are create/edit some searches and enable 'Use in favorites menu'. - new setup option for timer conflict check: 'After each timer programming'. This performs a conflict check after each manual timer programming and - if the new/modified timer is involved in a conflict - pops up an OSD message about it. - new email notifications about search timer updates or timer conflicts with a customizable mail content. Please refer to the MANUAL section 'Email notification' for further details. - epgsearch has now a set of man pages. A very BIG thanks to Mike Constabel (vejoun@vdrportal) who did the complete job of rewriting/correcting and formatting the current docs. The man pages cover the README's, MANUAL and a documentation of all conf's that epgsearch uses. Available for german and english. Install them with 'make install-doc' in epgsearch's src-directory. - you can now define your own variables to be used in the default recordings directory, search timer recording directory or your own menu templates. Since this is quite complex please refer to the MANUAL section 'User defined variables'. Just an example to see what is possible: # Weekday, Date, Time %DateStr%=%time_w% %date% %time% # Themes or Subtitle or Date %ThemesSubtitleDate1%=%Subtitle% ? %Subtitle% : %DateStr% %ThemesSubtitleDate%=%Themes% ? %Themes% : %ThemesSubtitleDate1% # Calls this script to get a recording path %DocuScript%=system(doku.pl,%Title%,%Subtitle%,%Episode%,%Themes%,%Category%,%Genre%) %Docu%=%DocuScript% - key 'Ok' can now also be configured to switch to a channel instead of displaying the summary, suggested by malachay@vdrportal. - if a timer was created by a search timer then epgsearch's own timer edit menu now displays the name of the search timer in a non selectable item at the bottom of the menu. - when an event is announced via search timers you can now create a timer for it, when you press the red key while the announce is visible, suggested by Andreas Mair - if search timers are disabled by setup then creating/editing a search timer now automatically activates them in setup, suggested by Andreas Mair. - you can now use more than one menu template for search results, please refer to the MANUAL section "Customizing the EPG menus". - the check for a complete recording now also accepts short breaks (< 2s, e.g. a channel pid change) - new SVDRP commands: * LSTC [channel group name] list all channel groups or if given the one with name 'group name' * NEWC create a new channel group, format as in epgsearchchangrps.conf * EDIC modify an existing channel group, format as in epgsearchchangrps.conf * DELC delete an existing channel group * RENC rename an existing channel group * 'LSTB [ID]' to list all blacklists, or the one with the passed ID (format is the same as epgsearchblacklists.conf, see MANUAL) * 'NEWB ' to add a new blacklist REMARK: the value of element ID is ignored. epgsearch will always assign the next free ID * 'DELB ' to delete the blacklist with ID * 'EDIB ' to modify an existing blacklist * 'QRYS < ID(s) >' to get the results for a search with the given ID. Multiple IDs can also be passed and have to be separated with '|'. * 'QRYS ' to get the results for a search with the given search settings. * 'QRYF [hours]' to get the results for the favorites menu. The optional parameter specifies the number of hours to evaluate and defaults to 24h. * 'LSTE [ID] to get the extended EPG categories defined in epgsearchcats.conf or the one with the given ID. * 'MODS ID ON|OFF' turns on/off the option 'Use as search timer'. * 'SETP option' returns the current value of the setup option (see MANUAL). - epgsearch has now a bug tracking system (german) and a mailing list (english) http://www.vdr-developer.org/mantisbt http://www.vdr-developer.org/mailman/listinfo/epgsearch thanks to the providers of developer.org - new service interface "Epgsearch-switchtimer-v1.0" to access and manage switch timers, thanks to Dirk Leber for his extension. - update for french translation (including setup help!), thanks to Patrice Staudt - thanks to Rolf Ahrenberg for finnish translation update - thanks to Mike Constabel for providing a HTML update mail template (conf/epgsearchupdmail-html.templ) fixes: - the first day of a repeating timer is now taken into consideration in the timer conflict check, thanks to zimuland@vdrportal for reporting - fixed search timer update for VPS timers, thanks to Chello and oholler@vdrportal for reporting. - fixed handling of the sign '|' when changing the search type of a search timer, thanks to Chello@vdrportal for reporting - changed some default search mode values in the epgsearchcats.conf samples for multiple selection - removed extra line feeds in SVDRP response of LSTS, thanks to Andreas Mair for reporting - channel criterion 'no pay tv' in search timers was ignored, thanks to Andreas Mair for reporting - fixed initial min/max values for search criterion 'duration', thanks to Mike Constabel for reporting - fixed a bug when selecting the conflict check 'after each search timer update' in setup, thanks to Ronny Kornexl for reporting - some changes for thread safeness - added '--remove-destination' to the Makefile as introduced in vdr-1.4.2-3 2006-08-07: Version 0.9.17d (maintenance release) fixes: - fixed a wrong usage of 'cPlugin::ConfigDirectory' in search timer thread, thanks to Udo Richter and Petri Hintukainen for reporting 2006-06-12: Version 0.9.17c fixes: - fixed a problem with multiple selection of extended epg category values, thanks to Christian Jacobsen for reporting - another compilation fix for linvdr, thanks to toxic-tonic@vdrportal 2006-06-10: Version 0.9.17b fixes: - fixed some problems with repeating timers in timer conflict check, thanks to Peter Juenger for reporting - fixed a crash in timer conflict check when there was an active recording on a device provided by a plugin (e.g. pvr, streamdev), thanks to Gerhard Steiner for reporting. 2006-06-06: Version 0.9.17a fixes: - menu 'timer conflicts' automatically returned if there where periodic timers, thanks to lostinspc@vdrportal - fixed some compiling problems with gcc-2.95 and gcc-4.1.0, thanks to ferdi03@vdrportal, smudo81@vdrportal, toxic-tonic@vdrportal 2006-06-05: Version 0.9.17 - dropped support for vdr < 1.3.46 :-( - changed logic in jumping through menus with user-defined times: you can now also access times on next day, if they are less then 20 hours in the future. Thanks to egal@vdrportal and CKone@vdrportal. - epgsearch has now its own timer conflict check which can be customized by setup regarding relevant priorities, the time span to check or the relevant conflict duration. There is also a conflict manager in Search/Actions that can be used to resolve conflicts. Many thanks to Mike Constabel for heavy testing ;-) - if there is a timer conflicts notification via OSD, you can now press 'Ok' to go directly to the timer conflicts overview. - if you like to have a separate main menu entry for the timer conflict check use '-P conflictcheckonly' which comes as 'mini'-plugin with epgsearch and simply calls epgsearch's conflict menu. conflictcheckonly has a setup option to display an info about the last conflict check directly in its main menu entry. - setup is now completely restructured and has now something like an 'online' help. - menu 'switch timers' now also displays the channel number - input logic for extended EPG categories changed to allow multiple values. - added script 'timercmds-auxinfo.sh' that displays the aux info (e.g. the search timer responsible for the timer) in VDR's timer menu (requires the timercmd patch), thanks to the author Mike Constabel - user-defined EPG times with empty description get now their associated time as description, suggested by Thiemo Gehrke - new service interface "Epgsearch-conflictmenu-v1.0" to call epgsearch's conflict menu from other plugins. - new service interface "Epgsearch-lastconflictinfo-v1.0" to get an info about the last conflict check result. - epgsearchmenu.conf is not more reloaded with every plugin call, since this is only useful when testing the conf file. To activate the permanent reload again, pass the new start parameter '-r' or '--reloadmenuconf' in your runvdr - thanks to Rolf Ahrenberg for finnish translation update - added HISTORY.DE (german HISTORY) fixes: - loading epgsearchmenu.conf now falls back to menu defaults, if the conf file contains any errors. - fixed handling of '%' in LSTS in SVDRP for vdr > 1.3.44, thanks to Mike Constabel - fixed loading epgsearchmenu.conf for epgsearchonly and service interface "Epgsearch-searchmenu-v1.0", thanks to Daniel Dorau for reporting. - fixed display update in menu 'Search templates', thanks to Mike Constabel - fixed a bug when deleting associated timers of a search timer when there are no timers present, thanks to Patrick Koppen for reporting. - hopefully fixed a bug when adding a search entry to templates, thanks to Patrick Koppen for reporting. 2006-04-18: Version 0.9.16 (maintenance release) fixes: - the display of 'Overview - Now' was broken, when using progressbar and channel numbers in default menu look. Thanks to Gerhard Steiner for reporting this one. - when using small OSD fonts, the progressbars where flickering because of 1 pixel line height. Thanks to holymoly and TomG@vdrportal. - support for the APIVERSION define as introduced in vdr-1.3.47 2006-04-14: Version 0.9.15 new: - the EPG menus can now be customized with the file epgsearchmenu.conf (sample file in 'conf' subdirectory), e.g. the entry: MenuWhatsOnNow=%chnr%:3|%progrt2s%:5| %time% %t_status%:8|%category%:6| %title% ~ %subtitle%:35 creates a menu line starting with the channel number, followed by a progress bar in text2skin style, the start time, the timer status, the EPG category (like "movie") and finally the title and subtitle. You can customize each menu ('What's on now, next, other times, schedule and search results with a separate line). Please refer to the MANUAL for more information. - IMPORTANT change to search timers: epgsearch now removes timers, that are not needed anymore (if vdr >= 1.3.44). These are: * timers, that are no more valid because of EPG changes. This should avoid the double recordings after an EPG change. * timers, that do not match the search criterions after a change of the search timer. So you don't have to remove them manually anymore. If the user has modified start or stop of such a timer, it will be kept. - new search mode 'fuzzy' in searches. This performs a fuzzy search applying an algorithm like it's used in agrep. The error tolerance can be set with the edit field 'Tolerance'. - added the 'one press' timer creation feature in EPG menus as introduced in vdr-1.3.38 with one small difference: If the event is already running or about to start within MarginStart + 2 minutes, the timer edit menu will be popup to allow editing the file/directory entry, because this cannot be changed when the recording immediately starts. Else, the timer is created immediately. Can be turned off in setup. - with the red button in epgsearch's own timer edit menu one can now delete an existing timer, suggested by Rolf Ahrenberg. - if the main menu entry is not hidden, its name can now be set by setup. (Note: when changing the name to something different than the default, it is no more dependent on the OSD language) - changed the default main menu entry from 'Search' to 'Program guide' - some people requested a separate main menu entry for epgsearch's search menu. Therefore I've added a little plugin called 'epgsearchonly' that simply calls epgsearch's search menu. Its main menu entry is 'Search'. Compilation of epgsearchonly is automatically done, but can be switched off in the Makefile when uncommenting the line #WITHOUT_EPGSEARCHONLY=1. To use it simply put '-P epgsearchonly' to your VDR start script. (requires >= vdr-1.3.30) - new service interface "Epgsearch-searchmenu-v1.0" to call epgsearch's search menu from other plugins. - new action 'Delete created timers?' for search entries to delete all timers created from the current search timer. Does not affect recording timers. - When deleting a search, you can now decide if all timers created from this search should also be deleted. - If the user modifies the start/stop time of a timer created by epgsearch this timer will not be changed anymore(only vdr >= 1.3.44 ) - If an event is currently recorded, the status flag in the EPG menus is now 'R' instead of 'T' - Support for wareagle-icon-patch (activate it by adding the line 'WarEagleIcons=1' to epgsearchmenu.conf) - Progressbar in Now/Next beautified when using setup option 'graphical', inspired by Nordlichts-EPG-Plugin - new setup option to show/hide radio channels in the EPG menus, suggested by Patrice Staudt. - new service interface "Epgsearch-searchresults-v1.0", that returns a result list of events matching a given search, suggested by Andreas Brugger - removed some special setup options, because their function can also be realized with a customized menu now: * "show progress bar in 'Next'", in default menu now off * "display width for progressbar", in default menu now 4 * "show short channel name", in default now always short * "Show episode text in schedules", now always displayed - update for finnish translation, thanks to Rolf Ahrenberg - aux field is now formatted in full XML style (if vdr >= 1.3.44). As a result of this, an updated recordingdone.sh is provided in the 'scripts' subdirectory. Thanks to Mike Constabel for providing a patch. - search timer update now always runs in low priority, suggested by Christian Jacobsen - new SVDRP command 'FIND'. This allows searching an event and returns a list of results consisting of 'NEWT' lines, that can be used to immediately create a timer for a result. Suggested by Lari Tuononen fixes: - thanks to Darren Salt for patches for g++ 4.1 and some things concerning thread safeness and code review - fixed some memory leaks - fixed calling user defined EPG commands, thanks to rzndl@vdrportal - fixed blacklist match for search timers, thanks to Christian Jacobsen - make sure that the episode name of a timer is less then MAX_SUBTITLE_LENGTH, thanks to Mike Constabel - speedup for search timer update 2006-03-05: Version 0.9.14a fixes: - EPG command 'mark as already recorded' crashed, thanks to Mike Constabel for reporting this 2006-03-04: Version 0.9.14 new: - when disabling/enabling a search timer you can now decide if the associated timers should also be disabled/enabled. - new built-in EPG command 'Create blacklist' to create a blacklist from any EPG menu. - the summary of an entry in the menu 'recordings done' has now an additional button 'Aux info', that displays the content of the aux field. - update for the script recordingdone.sh to work also with vdr-1.3.44, thanks to Mike Constabel for his changes. - conversion scripts for upgrade to vdr-1.3.44: Because of the different handling of auxiliary info (summary -> aux) some features of epgsearch won't work with recordings made before vdr-1.3.44. This applies to the done-Feature and to the automatic deletion of recordings. Without using these scripts you will simply have some more recordings. So you actually don't have to apply them, but when using them, all works fine. Thanks to Mike Constable for providing them. * new script convert_info_vdr.pl. This converts the info file of recordings made before vdr-1.3.44 and moves epgsearch aux data to the new aux field. * new script convert_epgsearchdone_data.pl. This converts the epgsearchdone.data file adding an 'aux' section to it. - update for script mtdone2epgsearchdone.sh, thanks to Christian Jacobsen - update for french translation, thanks to Patrice Staudt - update for finnish translation, thanks to Rolf Ahrenberg fixes: - done-feature was broken with vdr-1.3.44, thanks to Mike Constabel for reporting this - another fix of high cpu load in search timer thread, thanks to sledgehammer@vdrportal for reporting this - automatic linking after extracting the tarball removed again, requested by distribution maintainers. - fixed some compiler warnings when using -Wformat=2, thanks to Darren Salt for reporting and providing a patch 2006-02-28: Version 0.9.13 new: - support for vdr-1.3.44: works now, but: because of the summary/aux changes in vdr-1.3.44 the additional info about the search timer, that triggered a timer/recording, or the recording channel is currently not available. This info is now stored in the new aux field of timers/recordings. - blacklists: you can now maintain one or more blacklists and select them within a search or search timer. A blacklist works like a normal search and defines events that should be ignored in a search or search timer. Blacklists can be managed from setup or with the new search action 'Show blacklists' or directly when editing a search. Within a search you can select one or more or all blacklists. If any search result is also contained in a black list result set it will be skipped. - the switchtimer background thread is now only active, while there are any active switch timers. - when you unpack the tar file, the necessary link is now automatically created (like text2skin) - the result list of repeats of an event now omits the currently running event, suggested by Ronny Kornexl - thanks for beta testing to Mike Constabel - update for finnish translation, thanks to Rolf Ahrenberg fixes: - the setup option 'Add episode to manual timers' was not stored 2006-02-12: Version 0.9.12 new: - new feature 'pause when ... recordings exist" for search timers. If the given number (>0) of recordings exist, then the search timer will not create further timers. After deleting one or more recordings new timers are generated again. fixes: - fixed a crash when surfing to a channel without epg in schedule menu 2006-02-08: Version 0.9.11a fixes: - high cpu load fixed 2006-02-07: Version 0.9.11 new: - support for vdr-1.3.42 - new feature switch timers: add an event with the new epg command 'Add to switch list' to the switch list and epgsearch will switch the channel one minute before the event starts. The current content of the switch list can be controlled within the search menu with the new action 'Show switch list'. If there is a switch timer for an event its menu item is labeled with 'S', if there is no real timer. - switch timers also for search timers: a search timer can now be configured to 'switch only'. Its search results are then added to the switch list and epgsearch will switch to the found events one minute before they start. There will be no real timer for such a search timer. - search criterion 'Use channel' has now an additional value 'only FTA' to search in non encrypted channels only - new setup option 'Show day separators' for menu 'Schedule', that displays an additional line at day break. - epgsearch has now a SVDRP interface with the following commands (requires vdr > 1.3.30): * 'LSTS [ID]' to list all searches, or the one with the passed ID (format is the same as epgsearch.conf, see MANUAL) * 'NEWS ' to add a new search REMARK: the value of element ID is ignored. epgsearch will always assign the next free ID * 'DELS ' to delete the search with ID * 'EDIS ' to modify an existing search * 'UPDS [OSD]' to update the search timers. Passing the optional keyword 'OSD' pops up an OSD message after the update has finished. * 'UPDD' to reload the file epgsearchdone.data, e.g. after an external tool has modified it. * 'SETS ' to activate or cancel the search timer background thread. - since setup gets too big it has now the modes 'Standard' and 'Extended'(toggled with key 'red'), suggested by Rolf Ahrenberg - update for finnish translation, thanks to Rolf Ahrenberg - update for italian translation, thanks to reelbox users - update for french translation, thanks to Patrice Staudt - new dutch translation, thanks to reelbox users - using 127.0.0.1 instead of resolving 'localhost' for SVDRP, suggested by Thiemo Gehrke - added script recordingdone.sh for your reccmds.conf to add a recording to epgsearch's done file, thanks to Patrice Staudt, Christian Jacobsen and Mike Constabel - added script mtdone2epgsearchdone.sh to move recordings from mastertimer's done file to epgsearch's done file, thanks to Christian Jacobsen. - thanks to Mike Constabel and Andreas Brugger for testing and bug reports fixes: - fixed a bug after using templates, thanks to Christian Jacobsen 2006-01-15: Version 0.9.10 new: - update of all schedule and search result menus after creating/changing a timer, as introduced in vdr-1.3.38 - button 'Record' now labeled with 'Record' or 'Timer' depending on existing timer for the event, as introduced in vdr-1.3.38 - epgsearch's own timer edit menu now displays the recording device number for a currently recording timer - new setup option: 'Add episode to manual timers' affects adding the subtitle to the timer file in manual timer programming. The setting 'smart' skips the subtitle if the event has more that 80 min. - search edit menu for search timers changed for future extensions: 'Action' now selects the job to do for the search results, currently 'record' (default) or 'announce only' - thanks to Rolf Ahrenberg for finnish translation update fixes: - in vdr-1.3.38 some translations were broken - updated the timercmds-patches to work with vdr-1.3.38 2006-01-08: Version 0.9.9 new: - support for vdr-1.3.38 - search templates: the settings of a search are often quite the same as already done with other searches. You can now create templates that can be selected when editing a search (with key blue). A template can also be set as default. A new search will then get the default settings automatically. - extended setup with 'search templates' to manage the new templates - new action 'Use as template' to copy a search to the templates - setup option to display the subtitle in the schedules, thanks to Darren Salt for his patch - extension of feature 'Delete recordings after ... days' with 'Keep ... recordings'. This will asure, that last x recordings will not be deleted, even if they are expired. fixes: - the search timer feature 'Delete recordings after ... days' also deleted recordings that were edited. fixed now. 2005-11-27: Version 0.9.8 new: ATTENTION: this version converts epgsearch.conf (the file that stores the searches) to a NEW FORMAT. A backup of the old file is stored as epgsearch.conf.bak. If you switch back to an older version you have to move the backup file back to epgsearch.conf. - new feature 'avoid repeats' for search timers, aka 'done' feature (please read the README before using it!). A special thanks to Mike Constabel (vejoun@vdrportal) for many hours of testing and bug reports. * timer preview * recognition of broken recordings * fuzzy event comparison - the menu of search results for a search timer with 'avoid repeats' has an additional mode for key 'blue' to preview the timers, that will be programmed with it. - new setup option to display channels without EPG in 'What's on...', idea from 'Nordlicht's EPG plugin' - new setup option to show channel separators in 'What's on...', idea from 'Nordlicht's EPG plugin' - new feature 'Delete recordings after ... days'. This removes a recording automatically after the given days. It's a nice thing for e.g. news recordings (like Tagesschau), that are only interesting for a few days - the timer summary now contains additional information about the event: event title, subtitle (if present) at the top, the name and ID of the search timer, that triggered the timer, and also the recording channel (after the real summary) at the bottom - the summary of timers that are manually programmed now also contains info about title, subtitle (if present) and the recording channel - 'Execute' in searches menu has been replaced with 'Actions' that list a menu consisting of * Execute search * Use as search timer on/off * Trigger search timer update * Show recordings done You can also use the short keys to trigger the corresponding action without opening this menu - introduced a logfile (default file is plugin-configdir/epgsearch.log, but can be specified with commandline option '-l /path/to/logfile'. Commandline option '-v n' specifies the verbose level of the logfile. n=0 means no logging, allowed values are n=0(default),1(standard),2(extended),3(for debug). - setup has now a default recording directory entry, that is used in manual timer programming. It supports also entries like '%Category%~%Genre%'. The directory gets automatically replaced with the values of the event (but only if all variables were replaced). - depending on the calling menu the event menu (summary) now labels the buttons green/yellow with the previous/next channel or the start of the previous/next event, suggested by Andreas Brugger - the channel number is now also listed in menu 'search results', if the setup option 'show channel numbers' is set, suggested by Gerhard Steiner. - short keys (1,2,...) to EPG commands are now also available in the EPG summary menu, suggested by Frank Krömmelbein - if VDR version >= 1.3.31 then setup has now an option to choose between epgsearch's timer edit menu and VDR's built-in menu (useful when using a patched VDR timer edit menu, suggested by Frank Krömmelbein) - if a subtitle is present the menu for manual timer programming now always adds the subtitle to the timer file as default, suggested by Gerhard Steiner - optimized the splitting of the directory and file entry when re-editing a timer in manual programming, thanks to Andreas Brugger - the EPG menus now always display a subtitle in the form 'title ~ subtitle' if present - new service 'Epgsearch-updatesearchtimers-v1.0' to trigger a search timer update - if a search timer update is triggered from the actions menu then an osd message is displayed when it has finished (also when using the service 'Epgsearch-updatesearchtimers-v1.0' optional) - the menu of search timers now displays the number of (active) searchtimers in its title - now checking if manual timer programming has failed and display an osd message in that case (needs vdr >= 1.3.30 to work) - thanks to Rolf Ahrenberg for updating the finnish translation - thanks to Mike Constabel for corrections in READMEs and MANUAL fixes: - epgsearch now exits to main menu when not called directly via a short key, thanks to Andreas Brugger - fixed editing menu search term in english translation - fixed sorting search timer list after editing a search timer, thanks to Mike Constabel - fixed menu navigation with respect to empty schedules, thanks to Darren Salt for his patch - fixed misaligned search entries in searches menu, thanks to Andreas Brugger 2005-09-04: Version 0.9.7 new: - changes in the events summary now also trigger a timer update fixes: - fixed a memory leak in search timers with regular expressions, thanks to TomG@vdrportal - fixed channel name display in color buttons when a channel separator exists, thanks to viking@vdrportal 2005-08-27: Version 0.9.6 new: - search criterion 'use channel' extended by 'channel groups'. Channel groups can be managed with their own menu and allow a much more flexible way to select channels for the search procedure. - epgsearch now exposes its extended timer edit menu and searching the epg as services to other plugins (s. MANUAL or contact me, if you are interested) - if a timer is programmed for an event (searchtimer or manual programming) then the summary is now also passed to the timer (allows displaying the summary pressing 'ok' in timer menu), thanks to TomG@vdrportal - after toggling the color buttons with '0' in the schedule menu the yellow/green button now move to the schedule of the previous/next channel - added a patch from Uwe/egal@vdrportal that replaces vdr's standard schedule menu with the epgsearch plugin. Also big thanks to Uwe for the changes in epgsearch. After patching VDR you have to activate the patch in epgsearch's setup. - after editing a new search, its entry now gets correctly sorted in the list of all searches, thanks to Mike Constabel - added Gerhard Steiner's timercmd patch for vdr >= 1.3.25 - a 'back' in the start menu now always closes the osd (should solve some trouble with the submenu patch) - events without title are now ignored, regardless of the search criteria, thanks to Mike Constabel - new EPG variable '%channel%', that can be used in directory entries and gets replaced with the channels name - recording margins in search timers can now be negative (allows intentional cutting the begin/end of recording), suggested by Mike Constabel - extended EPG categories are now compared case insensitive - updated finnish translation, thanks to Rolf Ahrenberg fixes: - '.epgsearchrc' now too is expected in the config path specified with -c (if given), thanks to Ronny Kornexl - fixed programming of timers with empty file and directory, thanks to Andreas Brugger - fixed search timer programming in case of midnight between start and stop time - fixed directory selection when listing of dirs is empty, thanks to harvey@vdrportal - (de)activating a searchtimer with '0' did not store, thanks to Mike Constabel - fixed menu navigation (green-yellow-yellow-green displayed the summary instead of 'whats on now'), thanks to Mike Constabel 2005-07-02: Version 0.9.5 - epgsearch has now its own menu for standard timer editing with the following extensions (timer programming is based on SVDRP, so this should be configured!): * additional item 'directory' with selection of existing directories with key 'blue' (also supports EPG variables like "%category%" when using ext. EPG info) * item 'file' can be extended with subtitle (if present) pressing key 'blue' * item 'file' and 'directory' can be reset to standard pressing key 'yellow' * when editing weekdays then item 'day' can be customized with a submenu for arbitrary weekdays selection - menu 'userdefined days of week' for search timers now starts with 'Monday' instead of 'Sunday' - menu 'Select directory' now lists distinct items of the following sources: * current recording directories * current timer directories * directories used in search timers * directories specified in epgsearchdirs.conf key 'yellow' can be used to change the depth of the directory listing. - Progressbar in 'Next' can now be switched on/off with its own setup item. - support for language dependent commands for EPG (default loading uses epgsearchcmds.conf, if there is no epgsearchcmds-XXX.conf, where XXX is the language code as shown in i18n.c, see MANUAL) - directly calling a command with key '1'..'9' will now go back to the calling menu after execution instead of displaying menu 'commands' - added a commandline option '-c' or '--config' to specify the plugins own config directory (option). - added commandline help for 'vdr --help - thanks again to Mike Constabel (vejoun@vdrportal) for great beta testing and suggestions, and to Rolf Ahrenberg for finnish translation. 2005-05-24: Version 0.9.4 - support for extended EPG info (categories) in search timers (please refer to the README 'Using extended EPG info'). - added a submenu to select an existing directory when editing a search timer. This menu can also be extended with entries in the file epgsearchdirs.conf - usage of variables like "%Genre%" or "%Category% in the directory entry of a search timer. These are replaced with the current extended EPG info when a timer is created. - key '0' in search menu now toggles the flag 'use as search timer' - fixed compilation for vdr < 1.3.18 (thanks to TomG@vdrportal) - updated script rememberevent.sh (thanks to Ronny Kornexl for complete rewrite and extensions) - added script epg2master-timer.sh for the commands menu, that adds an event to master-timer, thanks to the author Christian Jacobsen / Viking (vdrportal) - some changes to compile with bigpatch - removed font patch for progressbar, since runtime patch seems to work fine - added a script timerrep.sh to search the repeats of a timer from timer menu (requires the timercmds.conf-patch from Gerhard Steiner, see dir patches) - updated/rearranged README(.DE) and added also a MANUAL (only english) for detailed info about some topics, since README gets too big. - Added a description of the format of epgsearch.conf to MANUAL - fixed a bug with buttons display in search timer edit menu - a special thanks to Mike Constabel (vejoun@vdrportal) for great beta testing and suggestions, and to Rolf Ahrenberg for finnish translation. 2005-04-14: Version 0.9.3 - implemented direct SVDRP communication (no more need for svdrpsend. For now, the old external method can still be used when you pass it with -f as parameter, if you omit -f the internal method is used) - added buttons '<<' and '>>' in detailed epg view (summary) to jump to the previous/next event of the menu behind (schedule, now, next, ..., search results) - new feature to use epgsearch for searching from other plugins or scripts (see the README 'Usage from other plugins or scripts') - shifting displayed time with FF, FR or by toggling with '0' is now also available in schedule menu - added a setup option to set a default for handling pay tv channels when searching (does not affect search timers) - fixed day check in searchtimer scan with respect to changes in vdr-1.3.23 - changed detection of existing timers to reduce superfluous timer modifications. - added channel name and subtitle to parameters for custom commands - small change in input logic of 'from channel' when editing a search entry - updated screen shots on project homepage 2005-03-20: Version 0.9.2 - update for vdr-1.3.23 2005-03-19: Version 0.9.1 - french translation update, thanks to Patrice Staudt - fixed a problem with priority and lifetime when modifying a timer 2005-03-19: Version 0.9.0 - new search mode 'regular expressions' (POSIX(default) or Perl compatible by compiler switch HAVE_PCREPOSIX in the Makefile, see README - Installation) - new search mode 'match exactly' to simplify matching of short search terms - new option for search timers: 'only annouce' will not add a timer, but display an OSD message about the found event - new progressbar in 'what's on next', that displays the time span to the next event (0 minutes(=100%) to max. 30 minutes(=0%)) - timer conficts can now be checked in the background and are displayed via OSD after each search timer scan (requires a recent timeline plugin running) - key blue in search results now toggles between "only FTA" and "all channels" (to hide results from pay tv channels) - removed some superfluous search timer modifications - finnish translation, thanks to Rolf Ahrenberg - updated README and added a description of the search process - shifted version number to allow subversions 2005-02-15: Version 0.0.8 - added support for VPS in search timers - channels of search timers are now stored with ID rather than number (avoids editing your searches after channel modifications) - corrections in the english version, thanks to Darren Salt - fixed a bug in criterion duration - fixed channel selection when shifting displayed time, thanks to Ronny Kornexl - put some space between progressbar and event info (TtV*) for text2skin 1.0 - fixed compilaton problems in vdr-1.3.10 - switch display in search results to 'episode name only' with key yellow - fixed width of start time, when displaying channel number, thanks to Gerhard Steiner 2005-01-22: Version 0.0.7 - Start menu can now be setup between 'schedule' and 'now' - runtime patch for progressbar (no need to patch vdr anymore, experimental) - Added lifetime, priority and margins for start and stop for searchtimers - Added new font-patch for vdr-1.3.18 - new: italian translation, thanks to Sean Carlos - french translation, thanks to Patrice Staudt - finnish translation by Rolf Ahrenberg - search term can now be empty to search for anything, that matches other criterions searchtimers - Fixed editing of criterion 'to channel', thanks to gunnar@vdrportal 2005-01-11: Version 0.0.6 - now compiles also with vdr-1.3.18 - Search criterion 'channel' can now be a range of channels (thanks to gunnar@vdrportal) - Setup option 'short channel names' for display in 'now', 'next',... (only available for >= vdr-1.3.15, thanks to dido@vdrportal) - french translation, thanks to Patrice Staudt - added external trigger to force a search scan (e.g. at shutdown, thanks to Ronny Kornexl) - fixed display of color keys while editing search term and directory (thanks to Ronny Kornexl) - recordings found with 'search in recordings' can now be replayed from result list - fixed menu switching now-program-now (thanks to dido@vdrportal) 2005-01-03: Version 0.0.5 - time in 'now', 'next',... can now be shifted with FastFwd and FastRew or with Green/Yellow when toggling with '0' (see README) - search results now display 't' ,'T', if there is already a timer for an entry, thanks to LordPSI@vdrportal. - translation of new things to finnish (as always thanks to Rolf Ahrenberg) - sorting by channel in repeatings should work now, thanks to LordPSI@vdrportal. - fixed searching in mode 'one word', thanks to LordPSI@vdrportal - fixed key blue and red in an empty search list, thanks to Ronny Kornexl - fixed compilation problem with vdr-1.3.10, thanks to rockclimber@vdrportal - fixed some function calls to thread safeness (thanks to ?) - fixed graphical progressbar, thanks to white_@vdrportal - rewritten the README's 2004-11-30: Version 0.0.4 - New feature 'search timers' (like vdradmins 'auto timers', see the readme, thanks to Rolf Ahrenberg for idea and code review) - Progressbar can now be set to graphical bars in setup (you have to apply a vdr-patch for this, see README, thanks to Uwe/egal from vdrportal for creating the needed fonts) - Added builtin command to commands menu to create a search from epg entry - Added builtin command to commands menu to switch to channel of selected entry - Added builtin command to commands menu to search the current epg entry in recordings (thanks to Adreas Kool for his patch) - Key "Blue" can now also be customized by setup (switch/search) - Key '0' toggles key mapping of red and blue key (thanks to Roman (Uatschitchun) from vdrportal for his idea) - Added 'day(s) of week' as search criterion - Executing a command on an epg entry now only closes command menu, not the whole plugin - Display of channel number in "what's on" controlled by setup - fixed column display to prevent cutting of channel name - fixed selection of current channel in "what's on now" (thanks to Leo from vdrportal for reporting this) - button 'ok' in empty search results jumps back to search list 2004-11-08: Version 0.0.3 - Added an ASCII-based progress bar to the 'what's on now' view of the schedules. (Usage and display width customizable by setup). - Added up to 4 user defined times besides "now" and "next" (you can use this for example for "primetime" or "late night") - Renamed plugin main entry from "EPG search" to "Search" - Added duration to search criterions - Added commands menu in 'search results' list - Behaviour of red key is now the same in 'search results' as in main menu of the plugin. - Updated history and readme's 2004-09-11: Version 0.0.2 - New feature for executing commands on epg items (see README) - Added script epg2autotimer.sh to create autotimers from epg - Added finnish translation (thanks to Rolf Ahrenberg, also for code review) - Behaviour of red key can now be switched by setup form "Standard" to "Commands". - code is now more structured - BUGFIX: Blue key for switching in summary should work now (thanks to Sir James Lachtveel and others for reporting this one) 2004-08-15: Version 0.0.1 - Initial revision. vdr-plugin-epgsearch-2.4.6/INSTALL000066400000000000000000000030641514271145600166130ustar00rootroot00000000000000Theres nothing special about it. Install it like any other standard plugin (unpack, soft link, make plugins). Optional libraries: ------------------- libpcre ------- For support of Perl compatible regular expressions in a search you have to use libpcre: simply edit the plugins Makefile and uncomment '#REGEXLIB = pcre2' to 'REGEXLIB = pcre2' or append 'REGEXLIB=pcre2' to your 'make plugins' call. pcre2 is the newer maintained library from pcre.org. You can also use pcre which refers to the old perl compatible library. (you will need libpcre2-dev installed (or libpcre3-dev which is the older lib)) HINT: if all compiles well, but after starting VDR you get: ERROR: /usr/lib/libpcreposix.so.0: undefined symbol: pcre_free). update libpcre from www.pcre.org and recompile the plugin. There seems to be a problem with PCRE on some systems, that produce a crash when using regexp. Til now, I could not find the reason. So perhaps dont use REGEXLIB=pcre, if you don't really need it. libtre ------ epgsearch has a fuzzy search algorithm. Unfortunately it's restricted to search patterns with a maximum of 31 characters. This results in empty results if more than 31 characters are used. To avoid this limitation you can use the TRE package (http://laurikari.net/tre/). Install tre (on debian: apt-get install tre-agrep libtre4 libtre-dev) and activate epgsearch's support for it in the Makefile by uncommenting '#REGEXLIB = pcre' to 'REGEXLIB = tre' or append it to your 'make plugins' call. After recompiling epgsearch will now use an algorithm similar to 'agrep' with no limits. vdr-plugin-epgsearch-2.4.6/INSTALL.DE000066400000000000000000000020061514271145600170750ustar00rootroot00000000000000Hier gibt es nichts besonderes. Bitte wie jedes andere Standard-Plugin installieren (entpacken, soft link, make plugins). Eine kleine Besonderheit: Wer lieber mit Perl compatible regular expressions in der Suche arbeitet, sollte einfach im Makefile des Plugins '#REGEXLIB = pcre' in 'REGEXLIB = pcre' ändern oder einfach REGEXLIB=pcre an den 'make plugins'-Aufruf anhängen. Hierzu muss pcreposix installiert sein, das in der libpcre von www.pcre.org enthalten ist. Dies sollte aber in den meisten Distributionen bereits enthalten sein. HINWEIS: wenn alles normal kompiliert, aber beim Start von VDR folgendes kommt: ERROR: /usr/lib/libpcreposix.so.0: undefined symbol: pcre_free). dann bitte libpcre von www.pcre.org updaten und das Plugin neu kompilieren. Scheinbar gibt es auf manchen Systemen Probleme mit PCRE, die sich in einem Crash äußern, wenn man reg. Ausdrücke benutzt. Da ich die Ursache bisher nicht finden konnte, empfehle ich deshalb auf REGEXLIB=pcre zu verzichten, wenn es nicht unbedingt notwendig ist. vdr-plugin-epgsearch-2.4.6/Makefile000066400000000000000000000272241514271145600172260ustar00rootroot00000000000000# # Makefile for epgsearch, a Video Disk Recorder plugin # # Christian Wieninger cwieninger at gmx.de # # Adapted to the new VDR makefile environment by Copperhead, # refined by Stefan Hofmann # ### ------------ ### CONFIG START ### ### to change an option just edit the value: 0 => false, 1 => true ### edit one of these lines to '1', if you don't want the addon epgsearchonly, ### conflictcheckonly or quickepgsearch WITHOUT_EPGSEARCHONLY = 0 WITHOUT_CONFLICTCHECKONLY = 0 WITHOUT_QUICKSEARCH = 0 ### edit this to '0' if you don't want epgsearch to auto config itself AUTOCONFIG = 1 ### if AUTOCONFIG is not active you can manually enable the ### optional modules or patches for other plugins ifeq ($(AUTOCONFIG),0) # if you want to use Perl compatible regular expressions (PCRE) or libtre for # unlimited fuzzy searching, uncomment this and set the value to pcre/pcre2 # or tre - also have a look at INSTALL for further notes on this #REGEXLIB = pcre2 # uncomment this to enable support for the pin plugin. #USE_PINPLUGIN = 1 # uncomment this to enable support for the graphtft plugin. #USE_GRAPHTFT = 1 endif ### the sendmail executable to use when epgsearch is configured to use the ### sendmail method for sending mail SENDMAIL = /usr/sbin/sendmail ### ### CONFIG END ### do not edit below this line if you don't know what you do ;-) ### ------------------------------------------------------------- PLUGIN = epgsearch MAINMENUSHORTCUT = epgsearchmainmenushortcut PLUGIN2 = epgsearchonly PLUGIN3 = conflictcheckonly PLUGIN4 = quickepgsearch ### 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') ### The directory environment: # Use package data if installed...otherwise assume we're under the VDR source directory: PKG_CONFIG ?= pkg-config PKGCFG = $(if $(VDRDIR),$(shell $(PKG_CONFIG) --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." $(PKG_CONFIG) --variable=$(1) vdr)) LIBDIR = $(call PKGCFG,libdir) LOCDIR = $(call PKGCFG,locdir) MANDIR = $(call PKGCFG,mandir) CONFDIR = $(call PKGCFG,configdir) BINDIR = $(call PKGCFG,bindir) # TMPDIR ?= /tmp PLGCFG = $(call PKGCFG,plgcfg) -include $(PLGCFG) ### The compiler options: export CFLAGS = $(call PKGCFG,cflags) export CXXFLAGS = $(call PKGCFG,cxxflags) ### configuring modules ifeq ($(AUTOCONFIG),1) ifeq (exists, $(shell $(PKG_CONFIG) libpcre2-posix && echo exists)) REGEXLIB = pcre2 else ifeq (exists, $(shell $(PKG_CONFIG) libpcre && echo exists)) REGEXLIB = pcre else ifeq (exists, $(shell $(PKG_CONFIG) tre && echo exists)) REGEXLIB = tre endif ifeq (exists, $(shell test -e ../pin -o -e ../*plugin-pin && echo exists)) USE_PINPLUGIN = 1 endif ifeq (exists, $(shell test -e ../graphtft -o -e ../*plugin-graphtft && echo exists)) USE_GRAPHTFT = 1 endif ifeq (exists, $(shell test -e ../graphtftng -o -e ../*plugin-graphtftng && echo exists)) USE_GRAPHTFT = 1 endif endif ### The version number of VDR's plugin API: APIVERSION = $(call PKGCFG,apiversion) ### The name of the distribution archive: ARCHIVE = $(PLUGIN)-$(VERSION) PACKAGE = vdr-$(ARCHIVE) ### Includes and Defines (add further entries here): INCLUDES += DEFINES += ifneq ($(SENDMAIL),) DEFINES += -DSENDMAIL='"$(SENDMAIL)"' endif ### The object files (add further files here): ALL = libvdr-$(PLUGIN).so createcats INSTALL-LIB = install-$(PLUGIN) ifeq ($(WITHOUT_EPGSEARCHONLY), 0) ALL += libvdr-$(PLUGIN2).so INSTALL-LIB += install-$(PLUGIN2) endif ifeq ($(WITHOUT_CONFLICTCHECKONLY), 0) ALL += libvdr-$(PLUGIN3).so INSTALL-LIB += install-$(PLUGIN3) endif ifeq ($(WITHOUT_QUICKSEARCH), 0) ALL += libvdr-$(PLUGIN4).so INSTALL-LIB += install-$(PLUGIN4) endif OBJS = afuzzy.o blacklist.o changrp.o confdloader.o conflictcheck.o conflictcheck_thread.o distance.o $(PLUGIN).o epgsearchcats.o epgsearchcfg.o epgsearchext.o epgsearchsetup.o epgsearchsvdrp.o epgsearchtools.o mail.o md5.o menu_announcelist.o menu_blacklistedit.o menu_blacklists.o menu_commands.o menu_conflictcheck.o menu_deftimercheckmethod.o menu_dirselect.o menu_event.o menu_favorites.o menu_main.o menu_myedittimer.o menu_quicksearch.o menu_recsdone.o menu_search.o menu_searchactions.o menu_searchedit.o menu_searchresults.o menu_searchtemplate.o menu_switchtimers.o menu_templateedit.o menu_timersdone.o menu_whatson.o noannounce.o pending_notifications.o rcfile.o recdone.o recdone_thread.o recstatus.o searchtimer_thread.o services.o svdrpclient.o switchtimer.o switchtimer_thread.o templatefile.o timer_thread.o timerdone.o timerstatus.o uservars.o varparser.o ifeq ($(REGEXLIB), pcre2) LIBS += $(shell pcre2-config --libs-posix) INCLUDE += $(shell pcre2-config --cflags) DEFINES += -DHAVE_PCRE2POSIX else ifeq ($(REGEXLIB), pcre) LIBS += $(shell pcre-config --libs-posix) INCLUDE += $(shell pcre-config --cflags) DEFINES += -DHAVE_PCREPOSIX else ifeq ($(REGEXLIB), tre) LIBS += -L$(shell $(PKG_CONFIG) --variable=libdir tre) $(shell $(PKG_CONFIG) --libs tre) DEFINES += -DHAVE_LIBTRE INCLUDE += $(shell $(PKG_CONFIG) --cflags tre) endif ifdef USE_PINPLUGIN DEFINES += -DUSE_PINPLUGIN endif ifdef USE_GRAPHTFT DEFINES += -DUSE_GRAPHTFT endif ifdef CFLC DEFINES += -DCFLC endif ifdef DEBUG_CONFL DEFINES += -DDEBUG_CONFL endif ### length of the filling '-' in the channel separators, defaults to ### "----------------------------------------" ### overwrite this with PLUGIN_EPGSEARCH_SEP_ITEMS=--- ### to avoid problems with graphlcd ifdef PLUGIN_EPGSEARCH_SEP_ITEMS DEFINES += -DMENU_SEPARATOR_ITEMS='"$(PLUGIN_EPGSEARCH_SEP_ITEMS)"' endif OBJS2 = mainmenushortcut.o epgsearchonly.o LIBS2 = OBJS3 = mainmenushortcut.o conflictcheckonly.o LIBS3 = OBJS4 = mainmenushortcut.o quickepgsearch.o LIBS4 = ### The main target: all: $(ALL) i18n docs ### Implicit rules: %.o: %.c @echo CC $@ $(Q)$(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN)"' $(INCLUDES) -o $@ $< mainmenushortcut.o: mainmenushortcut.c @echo CC $@ $(Q)$(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(MAINMENUSHORTCUT)"' $(INCLUDES) -o $@ $< epgsearchonly.o: epgsearchonly.c @echo CC $@ $(Q)$(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN2)"' $(INCLUDES) -o $@ $< conflictcheckonly.o: conflictcheckonly.c @echo CC $@ $(Q)$(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN3)"' $(INCLUDES) -o $@ $< quickepgsearch.o: quickepgsearch.c @echo CC $@ $(Q)$(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN4)"' $(INCLUDES) -o $@ $< # Dependencies: MAKEDEP = $(CXX) -MM -MG DEPFILE = .dependencies $(DEPFILE): Makefile @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) $(OBJS2:%.o=%.c) $(OBJS3:%.o=%.c) $(OBJS4:%.o=%.c)> $@ ifneq ($(MAKECMDGOALS),clean) -include $(DEPFILE) endif DEPFILE_DOC = .dependencies_doc DEPFILE_stmp = .doc_stmp $(DEPFILE_DOC): Makefile @echo DEP @rm -f $(DEPFILE_DOC) $(Q)./docsrc2man.sh --depend $(DEPFILE_stmp) > $(DEPFILE_DOC) ifneq ($(MAKECMDGOALS),clean) -include $(DEPFILE_DOC) endif ### Internationalization (I18N): PODIR = po I18Npo = $(wildcard $(PODIR)/*.po) I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) I18Npot = $(PODIR)/$(PLUGIN).pot %.mo: %.po @echo MO $@ $(Q)msgfmt -c -o $@ $< $(I18Npot): $(wildcard *.c) @echo GT$@ $(Q)xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='' -o $@ `ls $^` %.po: $(I18Npot) @echo PO $@ $(Q)msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< @touch $@ $(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo @echo IN $@ $(Q)install -D -m644 $< $@ .PHONY: i18n i18n: $(I18Nmo) $(I18Npot) install-i18n: $(I18Nmsgs) ### Targets: libvdr-$(PLUGIN).so: $(OBJS) @echo LD $@ $(Q)$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@ libvdr-$(PLUGIN2).so: $(OBJS2) @echo LD $@ $(Q)$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS2) $(LIBS2) -o $@ libvdr-$(PLUGIN3).so: $(OBJS3) @echo LD $@ $(Q)$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS3) $(LIBS3) -o $@ libvdr-$(PLUGIN4).so: $(OBJS4) @echo LD $@ $(Q)$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS4) $(LIBS4) -o $@ createcats: createcats.o Makefile @echo LD $@ $(Q)$(CXX) $(CXXFLAGS) $(LDFLAGS) createcats.o -o $@ $(DEPFILE_stmp): $(Q)./docsrc2man.sh && ./docsrc2html.sh $(Q)ln -sf ./doc/en/epgsearch.4.txt MANUAL $(Q)ln -sf ./doc/de/epgsearch.4.txt MANUAL.DE $(Q)ln -sf ./doc/en/epgsearch.1.txt README $(Q)ln -sf ./doc/de/epgsearch.1.txt README.DE @rm -f $(DEPFILE_stmp) @date > $(DEPFILE_stmp) docs: $(DEPFILE_stmp) install-$(PLUGIN): libvdr-$(PLUGIN).so @echo IN $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN).so.$(APIVERSION) $(Q)install -D libvdr-$(PLUGIN).so $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN).so.$(APIVERSION) install-$(PLUGIN2): libvdr-$(PLUGIN2).so @echo IN $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN2).so.$(APIVERSION) $(Q)install -D libvdr-$(PLUGIN2).so $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN2).so.$(APIVERSION) install-$(PLUGIN3): libvdr-$(PLUGIN3).so @echo IN $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN3).so.$(APIVERSION) $(Q)install -D libvdr-$(PLUGIN3).so $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN3).so.$(APIVERSION) install-$(PLUGIN4): libvdr-$(PLUGIN4).so @echo IN $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN4).so.$(APIVERSION) $(Q)install -D libvdr-$(PLUGIN4).so $(DESTDIR)$(LIBDIR)/libvdr-$(PLUGIN4).so.$(APIVERSION) install-conf: @echo IN $(DESTDIR)$(CONFDIR)/plugins/$(PLUGIN)/conf.d $(Q)mkdir -p $(DESTDIR)$(CONFDIR)/plugins/$(PLUGIN)/conf.d $(Q)for file in `cd conf; ls -1`; do\ if ! [ -e $(DESTDIR)$(CONFDIR)/plugins/$(PLUGIN)/$$file ] ; then\ cp -p conf/$$file $(DESTDIR)$(CONFDIR)/plugins/$(PLUGIN);\ fi\ done install-doc: docs @echo IN $(DESTDIR)$(MANDIR) $(Q)mkdir -p $(DESTDIR)$(MANDIR)/man1 $(Q)mkdir -p $(DESTDIR)$(MANDIR)/man4 $(Q)mkdir -p $(DESTDIR)$(MANDIR)/man5 $(Q)mkdir -p $(DESTDIR)$(MANDIR)/de/man1 $(Q)mkdir -p $(DESTDIR)$(MANDIR)/de/man4 $(Q)mkdir -p $(DESTDIR)$(MANDIR)/de/man5 $(Q)cp man/en/*1.gz $(DESTDIR)$(MANDIR)/man1/ $(Q)cp man/en/*4.gz $(DESTDIR)$(MANDIR)/man4/ $(Q)cp man/en/*5.gz $(DESTDIR)$(MANDIR)/man5/ $(Q)cp man/de/*1.gz $(DESTDIR)$(MANDIR)/de/man1/ $(Q)cp man/de/*4.gz $(DESTDIR)$(MANDIR)/de/man4/ $(Q)cp man/de/*5.gz $(DESTDIR)$(MANDIR)/de/man5/ install-bin: createcats @echo IN $(DESTDIR)$(BINDIR)/createcats $(Q)mkdir -p $(DESTDIR)$(BINDIR) $(Q)cp createcats $(DESTDIR)$(BINDIR) install: install-lib install-i18n install-conf install-doc install-bin install-lib: $(INSTALL-LIB) dist: docs clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE)/doc-src @-rm -rf $(TMPDIR)/$(ARCHIVE)/html @-rm -rf $(TMPDIR)/$(ARCHIVE)/docsrc2man.sh @-rm -rf $(TMPDIR)/$(ARCHIVE)/docsrc2html.sh @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @ln -sf README.git README @echo Distribution package created as $(PACKAGE).tgz distfull: docs clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @ln -sf README.git README @echo complete distribution package created as $(PACKAGE).tgz clean: @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot @-rm -f $(OBJS) $(OBJS2) $(OBJS3) $(OBJS4) $(DEPFILE) *.so *.tgz core* createcats createcats.o pod2*.tmp @-find . \( -name "*~" -o -name "#*#" \) -print0 | xargs -0r rm -f @-rm -rf doc html man @-rm -f MANUAL MANUAL.DE README README.DE @-rm -f $(DEPFILE_stmp) $(DEPFILE_DOC) vdr-plugin-epgsearch-2.4.6/Makefile.before.1.7.36000066400000000000000000000221311514271145600212120ustar00rootroot00000000000000# # Makefile for epgsearch, a Video Disk Recorder plugin # # Christian Wieninger cwieninger at gmx.de # ### ------------ ### CONFIG START ### ### to change an option just edit the value: 0 => false, 1 => true ### edit one of these lines to '1', if you don't want the addon epgsearchonly, ### conflictcheckonly or quickepgsearch WITHOUT_EPGSEARCHONLY=0 WITHOUT_CONFLICTCHECKONLY=0 WITHOUT_QUICKSEARCH=0 ### edit this to '0' if you don't want epgsearch to auto config itself AUTOCONFIG=1 ### if AUTOCONFIG is not active you can manually enable the ### optional modules or patches for other plugins ifeq ($(AUTOCONFIG),0) # if you want to use Perl compatible regular expressions (PCRE) or libtre for # unlimited fuzzy searching, uncomment this and set the value to pcre or tre # also have a look at INSTALL for further notes on this #REGEXLIB = pcre # uncomment this to enable support for the pin plugin. #USE_PINPLUGIN = 1 # uncomment this to enable support for the graphtft plugin. #USE_GRAPHTFT = 1 endif ### the sendmail executable to use when epgsearch is configured to use the ### sendmail method for sending mail SENDMAIL = /usr/sbin/sendmail ### ### CONFIG END ### do not edit below this line if you don't know what you do ;-) ### ------------------------------------------------------------- PLUGIN = epgsearch MAINMENUSHORTCUT = epgsearchmainmenushortcut PLUGIN2 = epgsearchonly PLUGIN3 = conflictcheckonly PLUGIN4 = quickepgsearch ### 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') ### The C++ compiler and options: CXX ?= g++ CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses -Wno-format-y2k ### The directory environment: #DVBDIR = ../../../../DVB VDRDIR = ../../.. LIBDIR = ../../lib TMPDIR = /tmp ### auto configuring modules ifeq ($(AUTOCONFIG),1) ifeq (exists, $(shell pkg-config libpcre && echo exists)) REGEXLIB = pcre else ifeq (exists, $(shell pkg-config tre && echo exists)) REGEXLIB = tre endif ifeq (exists, $(shell test -e ../pin && echo exists)) USE_PINPLUGIN = 1 endif ifeq (exists, $(shell test -e ../graphtft && echo exists)) USE_GRAPHTFT = 1 endif endif ### Make sure that necessary options are included: -include $(VDRDIR)/Make.global ### Allow user defined options to overwrite defaults: -include $(VDRDIR)/Make.config ALL = libvdr-$(PLUGIN).so createcats ifeq ($(WITHOUT_EPGSEARCHONLY), 0) ALL += libvdr-$(PLUGIN2).so endif ifeq ($(WITHOUT_CONFLICTCHECKONLY), 0) ALL += libvdr-$(PLUGIN3).so endif ifeq ($(WITHOUT_QUICKSEARCH), 0) ALL += libvdr-$(PLUGIN4).so endif ### The version number of VDR (taken from VDR's "config.h"): VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') APIVERSION = $(shell grep 'define APIVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') ifeq ($(strip $(APIVERSION)),) APIVERSION = $(VDRVERSION) endif ### The name of the distribution archive: ARCHIVE = $(PLUGIN)-$(VERSION) PACKAGE = vdr-$(ARCHIVE) ### Includes and Defines (add further entries here): INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include #INCLUDES += -I$(VDRDIR)/include EPGSEARCH_DEFINES += -D_GNU_SOURCE ifneq ($(SENDMAIL),) EPGSEARCH_DEFINES += -DSENDMAIL='"$(SENDMAIL)"' endif DEFINES += $(EPGSEARCH_DEFINES) ### The object files (add further files here): OBJS = afuzzy.o blacklist.o changrp.o confdloader.o conflictcheck.o conflictcheck_thread.o distance.o $(PLUGIN).o epgsearchcats.o epgsearchcfg.o epgsearchext.o epgsearchsetup.o epgsearchsvdrp.o epgsearchtools.o mail.o md5.o menu_announcelist.o menu_blacklistedit.o menu_blacklists.o menu_commands.o menu_conflictcheck.o menu_deftimercheckmethod.o menu_dirselect.o menu_event.o menu_favorites.o menu_main.o menu_myedittimer.o menu_quicksearch.o menu_recsdone.o menu_search.o menu_searchactions.o menu_searchedit.o menu_searchresults.o menu_searchtemplate.o menu_switchtimers.o menu_templateedit.o menu_timersdone.o menu_whatson.o noannounce.o pending_notifications.o rcfile.o recdone.o recstatus.o searchtimer_thread.o services.o switchtimer.o switchtimer_thread.o templatefile.o timer_thread.o timerdone.o timerstatus.o uservars.o varparser.o ifeq ($(REGEXLIB), pcre) LIBS += $(shell pcre-config --libs-posix) #LIBS += -L/usr/lib -lpcreposix -lpcre INCLUDE += $(shell pcre-config --cflags) DEFINES += -DHAVE_PCREPOSIX else ifeq ($(REGEXLIB), tre) LIBS += -L$(shell pkg-config --variable=libdir tre) $(shell pkg-config --libs tre) #LIBS += -L/usr/lib -ltre DEFINES += -DHAVE_LIBTRE INCLUDE += $(shell pkg-config --cflags tre) endif ifdef USE_PINPLUGIN DEFINES += -DUSE_PINPLUGIN endif ifdef USE_GRAPHTFT DEFINES += -DUSE_GRAPHTFT endif ifdef CFLC DEFINES += -DCFLC endif ifdef DEBUG_CONFL DEFINES += -DDEBUG_CONFL endif ifdef PLUGIN_EPGSEARCH_MAX_SUBTITLE_LENGTH DEFINES += -DMAX_SUBTITLE_LENGTH='$(PLUGIN_EPGSEARCH_MAX_SUBTITLE_LENGTH)' endif ### length of the filling '-' in the channel separators, defaults to ### "----------------------------------------" ### overwrite this with PLUGIN_EPGSEARCH_SEP_ITEMS=--- in your Make.config ### to avoid problems with graphlcd ifdef PLUGIN_EPGSEARCH_SEP_ITEMS DEFINES += -DMENU_SEPARATOR_ITEMS='"$(PLUGIN_EPGSEARCH_SEP_ITEMS)"' endif OBJS2 = mainmenushortcut.o epgsearchonly.o LIBS2 = OBJS3 = mainmenushortcut.o conflictcheckonly.o LIBS3 = OBJS4 = mainmenushortcut.o quickepgsearch.o LIBS4 = ### The main target: all: $(ALL) i18n ### Implicit rules: %.o: %.c $(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN)"' $(INCLUDES) $< mainmenushortcut.o: mainmenushortcut.c $(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(MAINMENUSHORTCUT)"' $(INCLUDES) $< epgsearchonly.o: epgsearchonly.c $(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN2)"' $(INCLUDES) $< conflictcheckonly.o: conflictcheckonly.c $(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN3)"' $(INCLUDES) $< quickepgsearch.o: quickepgsearch.c $(CXX) $(CXXFLAGS) -c $(DEFINES) -DPLUGIN_NAME_I18N='"$(PLUGIN4)"' $(INCLUDES) $< # Dependencies: MAKEDEP = $(CXX) -MM -MG DEPFILE = .dependencies $(DEPFILE): Makefile @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) $(OBJS2:%.o=%.c) $(OBJS3:%.o=%.c) $(OBJS4:%.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 *.[ch]) xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP -kI18nTranslate --msgid-bugs-address='' -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) $(LIBS) -o $@ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) libvdr-$(PLUGIN2).so: $(OBJS2) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS2) $(LIBS2) -o $@ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) libvdr-$(PLUGIN3).so: $(OBJS3) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS3) $(LIBS3) -o $@ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) libvdr-$(PLUGIN4).so: $(OBJS4) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS4) $(LIBS4) -o $@ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) createcats: createcats.o Makefile $(CXX) $(CXXFLAGS) $(LDFLAGS) createcats.o -o $@ dist: docs clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE)/doc-src @-rm -rf $(TMPDIR)/$(ARCHIVE)/html @-rm -rf $(TMPDIR)/$(ARCHIVE)/docsrc2man.sh @-rm -rf $(TMPDIR)/$(ARCHIVE)/docsrc2html.sh @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @ln -sf README.git README @echo Distribution package created as $(PACKAGE).tgz distfull: docs clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @ln -sf README.git README @echo complete distribution package created as $(PACKAGE).tgz docs: @./docsrc2man.sh @./docsrc2html.sh @ln -sf ./doc/en/epgsearch.4.txt MANUAL @ln -sf ./doc/en/epgsearch.1.txt README @ln -sf ./doc/de/epgsearch.1.txt README.DE install-doc: docs @mkdir -p $(MANDIR)/man1 @mkdir -p $(MANDIR)/man4 @mkdir -p $(MANDIR)/man5 @mkdir -p $(MANDIR)/de/man1 @mkdir -p $(MANDIR)/de/man5 @cp man/en/*1.gz $(MANDIR)/man1/ @cp man/en/*4.gz $(MANDIR)/man4/ @cp man/en/*5.gz $(MANDIR)/man5/ @cp man/de/*1.gz $(MANDIR)/de/man1/ @cp man/de/*5.gz $(MANDIR)/de/man5/ clean: @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot @-rm -f $(OBJS) $(OBJS2) $(OBJS3) $(OBJS4) $(DEPFILE) *.so *.tgz core* createcats createcats.o pod2*.tmp @-find . \( -name "*~" -o -name "#*#" \) -print0 | xargs -0r rm -f @-rm -rf doc html man vdr-plugin-epgsearch-2.4.6/README.Translators000066400000000000000000000017701514271145600207570ustar00rootroot00000000000000--------------------------------------------------- Building and maintaining translations for EPGSearch --------------------------------------------------- If your language is not yet supported in EPGSearch or any updates are needed it would be great if you could give your support for this. To do so please follow these steps: 1. Check if there is a current beta version of EPGSearch at http://winni.vdr-developer.org/epgsearch/downloads/beta If not please use the latest known release. 2. Copy the original po file for your language to any backup destination. Now edit the original po file for your language in the po subdirectory. All entries with 'msgstr ""' should be filled with your translation. 3. To test your translations, simply recompile EPGSearch and check your changes. 4. When done please send me your po file or even better a patch for it, created like cd po diff -Nru /path-to-backup-po-file/x.po x.po > epgsearch_x.diff Many thanks for your support Christian vdr-plugin-epgsearch-2.4.6/README.git000066400000000000000000000005151514271145600172220ustar00rootroot00000000000000Note: This is a dummy README needed by some maintainers that use the git repository to fetch epgsearch. In the official or beta tar balls it is replaced by a symbolic link to ./doc/en/epgsearch.1.txt which is not part of the git repository since it is built from the sources (via 'make docs'). If its in the way, just remove it. vdr-plugin-epgsearch-2.4.6/afuzzy.c000066400000000000000000000172521514271145600172620ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include #include #include "afuzzy.h" static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy); /****************************************************************************** FUNCTION afuzzy_init() Initialization of the fuzzy search routine. This applies to the consequent calls of the afuzzy_CheckRTR (whole string matching) and afuzzy_CheckSUB (substring match) routines. afuzzy_init() should be called for each new pattern or error length. The search is case sensitive ARGUMENTS: p Pattern kerr Number of possible errors. Shouldn't exceed pattern length UseFilter Use agrep filter algorithm that speeds up search. fuzzy pointer to the structure that will be later passes to Check* (the first 6 elements should be NULLs for the first call) RETURN VALUE: none ALGORITHM see. the article on agrep algorithms. The only change is accounting transpositions as one edit operation . ******************************************************************************/ void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy) { int cnt, p_len, i, l, d, m; char PatFilter[sizeof(Uint) * 8 + 1]; fuzzy->k = kerr; m = strlen(p); fuzzy->FilterSet = 0; memset(fuzzy->Map, 0 , sizeof(fuzzy->Map)); if (fuzzy->S) free(fuzzy->S); if (fuzzy->R) free(fuzzy->R); if (fuzzy->R1) free(fuzzy->R1); if (fuzzy->RP) free(fuzzy->RP); if (fuzzy->RI) free(fuzzy->RI); if (fuzzy->FilterS) free(fuzzy->FilterS); fuzzy->FilterS = NULL; fuzzy->S = (Uint *)calloc(m + 1, sizeof(Uint)); fuzzy->R = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); fuzzy->R1 = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); fuzzy->RI = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); fuzzy->RP = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); for (i = 0, cnt = 0; i < m; i++) { l = fuzzy->Map[(unsigned char)p[i]]; if (!l) { l = fuzzy->Map[(unsigned char)p[i]] = ++cnt; fuzzy->S[l] = 0; } fuzzy->S[l] |= 1 << i; } for (d = 0; d <= fuzzy->k; d++) fuzzy->RI[d] = (1 << d) - 1; fuzzy->mask_ok = (1 << (m - 1)); fuzzy->r_size = sizeof(Uint) * (fuzzy->k + 1); p_len = m; if (p_len > (int) sizeof(Uint) * 8) p_len = (int) sizeof(Uint) * 8; /* If k is zero then no filter is needed! */ if (fuzzy->k && (p_len >= 2 * (fuzzy->k + 1))) { if (UseFilter) { fuzzy->FilterSet = 1; memset(fuzzy->FilterMap, 0 , sizeof(fuzzy->FilterMap)); fuzzy->FilterS = (Uint *)calloc(m + 1, sizeof(Uint)); /* Not let's fill the interleaved pattern */ int dd = p_len / (fuzzy->k + 1); p_len = dd * (fuzzy->k + 1); for (i = 0, cnt = 0; i < dd; i++) for (int j = 0; j < fuzzy->k + 1; j++, cnt++) PatFilter[cnt] = (unsigned char)p[j * dd + i]; PatFilter[p_len] = 0; for (i = 0, cnt = 0; i < p_len; i++) { l = fuzzy->FilterMap[(unsigned char)PatFilter[i]]; if (!l) { l = fuzzy->FilterMap[(unsigned char)PatFilter[i]] = ++cnt; fuzzy->FilterS[l] = 0; } fuzzy->FilterS[l] |= 1 << i; } fuzzy->filter_ok = 0; for (i = p_len - fuzzy->k - 1; i <= p_len - 1; i++) /* k+1 times */ fuzzy->filter_ok |= 1 << i; /* k+1 first bits set to 1 */ fuzzy->filter_shift = (1 << (fuzzy->k + 2)) - 1; } } } /****************************************************************************** FUNCTION afuzzy_free() Cleaning up after previous afuzzy_init() call. ARGUMENTS: fuzzy pointer to the afuzzy parameters structure RETURN VALUE: none ******************************************************************************/ void afuzzy_free(AFUZZY *fuzzy) { if (fuzzy->S) { free(fuzzy->S); fuzzy->S = NULL; } if (fuzzy->R) { free(fuzzy->R); fuzzy->R = NULL; } if (fuzzy->R1) { free(fuzzy->R1); fuzzy->R1 = NULL; } if (fuzzy->RP) { free(fuzzy->RP); fuzzy->RP = NULL; } if (fuzzy->RI) { free(fuzzy->RI); fuzzy->RI = NULL; } if (fuzzy->FilterS) { free(fuzzy->FilterS); fuzzy->FilterS = NULL; } } /****************************************************************************** FUNCTION afuzzy_CheckSUB() Perform a fuzzy pattern substring matching. afuzzy_init() should be called previously to initialize the pattern and error length. Positive result means that some part of the string given matches the pattern with no more than afuzzy->k errors (1 error = 1 letter replacement or transposition) ARGUMENTS: t the string to test fuzzy pointer to the afuzzy parameters structure RETURN VALUE: 0 - no match > 0 - strings match ALGORITHM ???????????????? ******************************************************************************/ int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy) { char c; int j, d; /* For eficciency this case should be little bit optimized */ if (!fuzzy->k) { Uint R = 0, R1; for (j = 0; (c = t[j]) != '\0'; j++) { R1 = (((R << 1) | 1) & fuzzy->S[fuzzy->Map[(unsigned char)c]]); R = R1; if (R1 & fuzzy->mask_ok) return 1; } /* end for (int j = 0 ... */ return 0; } if (fuzzy->FilterSet && !afuzzy_checkFLT(t, fuzzy)) return 0; memcpy(fuzzy->R, fuzzy->RI, fuzzy->r_size); /* R = RI */ for (j = 0; (c = t[j]); j++) { for (d = 0; d <= fuzzy->k; d++) { fuzzy->R1[d] = (((fuzzy->R[d] << 1) | 1) & fuzzy->S[fuzzy->Map[(unsigned char)c]]); if (d > 0) fuzzy->R1[d] |= ((fuzzy->R[d - 1] | fuzzy->R1[d - 1]) << 1) | 1 | fuzzy->R[d - 1]; } if (fuzzy->R1[fuzzy->k] & fuzzy->mask_ok) return j; memcpy(fuzzy->R, fuzzy->R1, fuzzy->r_size); } /* end for (int j = 0 ... */ return 0; } static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy) { Uint FilterR = 0; Uint FilterR1; int j; for (j = 0; t[j] != '\0'; j++) { FilterR1 = (((FilterR << (fuzzy->k + 1)) | fuzzy->filter_shift) & fuzzy->FilterS[fuzzy->FilterMap[(unsigned char)t[j]]]); if (FilterR1 & fuzzy->filter_ok) return 1; FilterR = FilterR1; } /* end for (int j = 0 ... */ return 0; } vdr-plugin-epgsearch-2.4.6/afuzzy.h000066400000000000000000000034711514271145600172650ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #ifndef _AFUZZY_H #define _AFUZZY_H #include // source from: /* Leonid Boitsov 2002. (itman@narod.ru) C version of Stas Namin. This code is a GPL software and is distributed under GNU public licence without any warranty. */ typedef unsigned int Uint; #define MaxPatSize (sizeof(Uint) * 8) typedef struct { Uint *R, *R1, *RP, *S, *RI; Uint *FilterS; int Map[256]; int FilterMap[256]; int k; Uint mask_ok; Uint filter_ok; Uint filter_shift; int r_size; int FilterSet; } AFUZZY; void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy); void afuzzy_free(AFUZZY *fuzzy); int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy); #endif vdr-plugin-epgsearch-2.4.6/blacklist.c000066400000000000000000000733641514271145600177100ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include "epgsearchtools.h" #include "blacklist.h" #include "epgsearchcats.h" #include #include "menu_dirselect.h" #include "changrp.h" #include "menu_search.h" #include "menu_searchedit.h" #include "menu_searchresults.h" #include #include cBlacklists Blacklists; // -- cBlacklist ----------------------------------------------------------------- cBlacklist::cBlacklist(void) { buffer = NULL; ID = -1; *search = 0; useTime = false; startTime = 0000; stopTime = 2359; useChannel = false; { LOCK_CHANNELS_READ; channelMin = Channels->GetByNumber(cDevice::CurrentChannel()); channelMax = channelMin; } channelGroup = NULL; useCase = false; mode = 0; useTitle = true; useSubtitle = true; useDescription = true; useDuration = false; minDuration = 0; maxDuration = 130; useDayOfWeek = false; DayOfWeek = 0; useExtEPGInfo = false; catvalues = (char**) malloc(SearchExtCats.Count() * sizeof(char*)); cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { catvalues[index] = (char*)malloc(MaxFileName); *catvalues[index] = 0; SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } extEPGInfoMatchingMode = 0; fuzzyTolerance = 1; isGlobal = 0; useContentsFilter = false; contentsCategoryMatchingMode = 0; contentsCharacteristicsMatchingMode = 0; useParentalRating = false; minParentalRating = 0; maxParentalRating = 18; } cBlacklist::~cBlacklist(void) { free(buffer); if (catvalues) { cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { free(catvalues[index]); SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } free(catvalues); } } cBlacklist& cBlacklist::operator= (const cBlacklist &Blacklist) { ID = Blacklist.ID; strcpy(search, Blacklist.search); useTime = Blacklist.useTime; startTime = Blacklist.startTime; stopTime = Blacklist.stopTime; useChannel = Blacklist.useChannel; channelMin = Blacklist.channelMin; channelMax = Blacklist.channelMax; free(channelGroup); channelGroup = Blacklist.channelGroup ? strdup(Blacklist.channelGroup) : NULL; useCase = Blacklist.useCase; mode = Blacklist.mode; useTitle = Blacklist.useTitle; useSubtitle = Blacklist.useSubtitle; useDescription = Blacklist.useDescription; useDuration = Blacklist.useDuration; minDuration = Blacklist.minDuration; maxDuration = Blacklist.maxDuration; useDayOfWeek = Blacklist.useDayOfWeek; DayOfWeek = Blacklist.DayOfWeek; useExtEPGInfo = Blacklist.useExtEPGInfo; cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { *catvalues[index] = 0; strcpy(catvalues[index], Blacklist.catvalues[index]); SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } extEPGInfoMatchingMode = Blacklist.extEPGInfoMatchingMode; fuzzyTolerance = Blacklist.fuzzyTolerance; isGlobal = 0; useContentsFilter = Blacklist.useContentsFilter; contentsFilter = Blacklist.contentsFilter; contentsCategoryMatchingMode = Blacklist.contentsCategoryMatchingMode; contentsCharacteristicsMatchingMode = Blacklist.contentsCharacteristicsMatchingMode; useParentalRating = Blacklist.useParentalRating; minParentalRating = Blacklist.minParentalRating; maxParentalRating = Blacklist.maxParentalRating; return *this; } void cBlacklist::CopyFromTemplate(const cSearchExt* templ) { useTime = templ->useTime; startTime = templ->startTime; stopTime = templ->stopTime; useChannel = templ->useChannel; channelMin = templ->channelMin; channelMax = templ->channelMax; free(channelGroup); channelGroup = templ->channelGroup ? strdup(templ->channelGroup) : NULL; useCase = templ->useCase; mode = templ->mode; useTitle = templ->useTitle; useSubtitle = templ->useSubtitle; useDescription = templ->useDescription; useDuration = templ->useDuration; minDuration = templ->minDuration; maxDuration = templ->maxDuration; useDayOfWeek = templ->useDayOfWeek; DayOfWeek = templ->DayOfWeek; useExtEPGInfo = templ->useExtEPGInfo; cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { strcpy(catvalues[index], templ->catvalues[index]); SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } extEPGInfoMatchingMode = templ->extEPGInfoMatchingMode; fuzzyTolerance = templ->fuzzyTolerance; isGlobal = 0; useContentsFilter = templ->useContentsFilter; contentsFilter = templ->contentsFilter; contentsCategoryMatchingMode = templ->contentsCategoryMatchingMode; contentsCharacteristicsMatchingMode = templ->contentsCharacteristicsMatchingMode; useParentalRating = templ->useParentalRating; minParentalRating = templ->minParentalRating; maxParentalRating = templ->maxParentalRating; } bool cBlacklist::operator< (const cListObject &ListObject) { cBlacklist *BL = (cBlacklist *)&ListObject; return strcasecmp(search, BL->search) < 0; } const char *cBlacklist::ToText(void) { char tmp_Start[5] = ""; char tmp_Stop[5] = ""; char tmp_minDuration[5] = ""; char tmp_maxDuration[5] = ""; char* tmp_search = encodeSpecialCharacters(search); char* tmp_chanSel = NULL; char* tmp_catvalues = NULL; if (useTime) { sprintf(tmp_Start, "%04d", startTime); sprintf(tmp_Stop, "%04d", stopTime); } if (useDuration) { sprintf(tmp_minDuration, "%04d", minDuration); sprintf(tmp_maxDuration, "%04d", maxDuration); } if (useChannel == 1) { if (channelMin->Number() < channelMax->Number()) msprintf(&tmp_chanSel, "%s|%s", CHANNELSTRING(channelMin), CHANNELSTRING(channelMax)); else msprintf(&tmp_chanSel, "%s", CHANNELSTRING(channelMin)); } if (useChannel == 2) { int channelGroupNr = ChannelGroups.GetIndex(channelGroup); if (channelGroupNr == -1) { LogFile.eSysLog("channel group %s does not exist!", channelGroup); useChannel = 0; } else tmp_chanSel = strdup(channelGroup); } if (useExtEPGInfo) { cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { char* catvalue = NULL; if (msprintf(&catvalue, "%s", catvalues[index]) != -1) { char* temp = catvalue; catvalue = encodeSpecialCharacters(catvalue, true); free(temp); if (index == 0) msprintf(&tmp_catvalues, "%d#%s", SearchExtCat->id, catvalue); else { temp = tmp_catvalues; msprintf(&tmp_catvalues, "%s|%d#%s", tmp_catvalues, SearchExtCat->id, catvalue); free(temp); } free(catvalue); } SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } } free(buffer); msprintf(&buffer, "%d:%s:%d:%s:%s:%d:%s:%d:%d:%d:" "%d:%d:%d:%s:%s:%d:%d:%d:%s:%d:" // 1..20 "%d:%d:%s:%d:%d:%d:%d:%d", // 21..28 ID, // 1 tmp_search, useTime, tmp_Start, tmp_Stop, useChannel, (useChannel > 0 && useChannel < 3) ? tmp_chanSel : "0", useCase, mode, useTitle, useSubtitle, // 11 useDescription, useDuration, tmp_minDuration, tmp_maxDuration, useDayOfWeek, DayOfWeek, useExtEPGInfo, useExtEPGInfo ? tmp_catvalues : "", fuzzyTolerance, extEPGInfoMatchingMode, // 21 isGlobal, contentsFilter.c_str(), contentsCategoryMatchingMode, contentsCharacteristicsMatchingMode, useParentalRating, minParentalRating, maxParentalRating); free(tmp_search); free(tmp_chanSel); free(tmp_catvalues); return buffer; } bool cBlacklist::Parse(const char *s) { char *line; char *pos; char *pos_next; int parameter = 1; int valuelen; char value[MaxFileName]; contentsFilter = ""; pos = line = strdup(s); pos_next = pos + strlen(pos); if (*pos_next == '\n') *pos_next = 0; while (*pos) { while (*pos == ' ') pos++; if (*pos) { if (*pos != ':') { pos_next = strchr(pos, ':'); if (!pos_next) pos_next = pos + strlen(pos); valuelen = pos_next - pos + 1; if (valuelen > MaxFileName) valuelen = MaxFileName; strn0cpy(value, pos, valuelen); pos = pos_next; switch (parameter) { case 1: ID = atoi(value); break; case 2: { char* s = decodeSpecialCharacters(value); strn0cpy(search, s, sizeof(search)); free(s); } break; case 3: useTime = atoi(value); break; case 4: startTime = atoi(value); break; case 5: stopTime = atoi(value); break; case 6: useChannel = atoi(value); break; case 7: if (useChannel == 0) { channelMin = NULL; channelMax = NULL; } else if (useChannel == 1) { int minNum = 0, maxNum = 0; int fields = sscanf(value, "%d-%d", &minNum, &maxNum); if (fields == 0) { // stored with ID #ifdef __FreeBSD__ char *channelMinbuffer = MALLOC(char, 32); char *channelMaxbuffer = MALLOC(char, 32); int channels = sscanf(value, "%31[^|]|%31[^|]", channelMinbuffer, channelMaxbuffer); #else char *channelMinbuffer = NULL; char *channelMaxbuffer = NULL; int channels = sscanf(value, "%m[^|]|%m[^|]", &channelMinbuffer, &channelMaxbuffer); #endif LOCK_CHANNELS_READ; channelMin = Channels->GetByChannelID(tChannelID::FromString(channelMinbuffer), true, true); if (!channelMin) { LogFile.eSysLog("ERROR: channel %s not defined", channelMinbuffer); channelMin = channelMax = NULL; useChannel = 0; } if (channels == 1) channelMax = channelMin; else { channelMax = Channels->GetByChannelID(tChannelID::FromString(channelMaxbuffer), true, true); if (!channelMax) { LogFile.eSysLog("ERROR: channel %s not defined", channelMaxbuffer); channelMin = channelMax = NULL; useChannel = 0; } } free(channelMinbuffer); free(channelMaxbuffer); } } else if (useChannel == 2) channelGroup = strdup(value); break; case 8: useCase = atoi(value); break; case 9: mode = atoi(value); break; case 10: useTitle = atoi(value); break; case 11: useSubtitle = atoi(value); break; case 12: useDescription = atoi(value); break; case 13: useDuration = atoi(value); break; case 14: minDuration = atoi(value); break; case 15: maxDuration = atoi(value); break; case 16: useDayOfWeek = atoi(value); break; case 17: DayOfWeek = atoi(value); break; case 18: useExtEPGInfo = atoi(value); break; case 19: if (!ParseExtEPGValues(value)) { LogFile.eSysLog("ERROR reading ext. EPG values - 1"); free(line); return false; } break; case 20: fuzzyTolerance = atoi(value); break; case 21: extEPGInfoMatchingMode = atoi(value); break; case 22: isGlobal = atoi(value); break; case 23: // no need for replacing special characters, as these would cause the check to fail value[MaxFileName - 1] = '\0'; // simulate strn0cpy() contentsFilter = value; useContentsFilter = !contentsFilter.empty(); break; case 24: contentsCategoryMatchingMode = atoi(value); break; case 25: contentsCharacteristicsMatchingMode = atoi(value); break; case 26: useParentalRating = atoi(value); break; case 27: minParentalRating = atoi(value); break; case 28: maxParentalRating = atoi(value); break; default: break; } //switch } parameter++; } if (*pos) pos++; } //while free(line); return (parameter >= 19) ? true : false; } bool cBlacklist::ParseExtEPGValues(const char *s) { char *line; char *pos; char *pos_next; int valuelen; char value[MaxFileName]; pos = line = strdup(s); pos_next = pos + strlen(pos); if (*pos_next == '\n') *pos_next = 0; while (*pos) { while (*pos == ' ') pos++; if (*pos) { if (*pos != '|') { pos_next = strchr(pos, '|'); if (!pos_next) pos_next = pos + strlen(pos); valuelen = pos_next - pos + 1; if (valuelen > MaxFileName) valuelen = MaxFileName; strn0cpy(value, pos, valuelen); pos = pos_next; if (!ParseExtEPGEntry(value)) { LogFile.eSysLog("ERROR reading ext. EPG value: %s", value); free(line); return false; } } } if (*pos) pos++; } //while free(line); return true; } bool cBlacklist::ParseExtEPGEntry(const char *s) { char *line; char *pos; char *pos_next; int parameter = 1; int valuelen; char value[MaxFileName]; int currentid = -1; pos = line = strdup(s); pos_next = pos + strlen(pos); if (*pos_next == '\n') *pos_next = 0; while (*pos) { while (*pos == ' ') pos++; if (*pos) { if (*pos != '#') { pos_next = strchr(pos, '#'); if (!pos_next) pos_next = pos + strlen(pos); valuelen = pos_next - pos + 1; if (valuelen > MaxFileName) valuelen = MaxFileName; strn0cpy(value, pos, valuelen); pos = pos_next; switch (parameter) { case 1: { currentid = atoi(value); int index = SearchExtCats.GetIndexFromID(currentid); if (index > -1) strcpy(catvalues[index], ""); } break; case 2: if (currentid > -1) { int index = SearchExtCats.GetIndexFromID(currentid); if (index > -1) { char* s = decodeSpecialCharacters(value, true); strn0cpy(catvalues[index], s, MaxFileName); free(s); } } break; default: break; } //switch } parameter++; } if (*pos) pos++; } //while free(line); return (parameter >= 2) ? true : false; } bool cBlacklist::Save(FILE *f) { return fprintf(f, "%s\n", ToText()) > 0; } const cEvent * cBlacklist::GetEventByBlacklist(const cSchedule *schedule, const cEvent *Start, int MarginStop) { const cEvent *pe = NULL; const cEvent *p1 = NULL; if (Start) p1 = schedule->Events()->Next(Start); else p1 = schedule->Events()->First(); time_t tNow = time(NULL); char* szTest = NULL; char* searchText = strdup(search); if (!useCase) ToLower(searchText); int searchStart = 0, searchStop = 0; if (useTime) { searchStart = startTime; searchStop = stopTime; if (searchStop < searchStart) searchStop += 2400; } int minSearchDuration = 0; int maxSearchDuration = 0; if (useDuration) { minSearchDuration = minDuration / 100 * 60 + minDuration % 100; maxSearchDuration = maxDuration / 100 * 60 + maxDuration % 100; } for (const cEvent *p = p1; p; p = schedule->Events()->Next(p)) { // ignore events without title if (!p->Title() || strlen(p->Title()) == 0) continue; free(szTest); msprintf(&szTest, "%s%s%s%s%s", (useTitle ? p->Title() : ""), (useSubtitle || useDescription) ? "~" : "", (useSubtitle ? p->ShortText() : ""), useDescription ? "~" : "", (useDescription ? p->Description() : "")); if (tNow < p->EndTime() + MarginStop * 60) { if (!useCase) ToLower(szTest); if (useTime) { time_t tEvent = p->StartTime(); struct tm tmEvent; localtime_r(&tEvent, &tmEvent); int eventStart = tmEvent.tm_hour * 100 + tmEvent.tm_min; int eventStart2 = tmEvent.tm_hour * 100 + tmEvent.tm_min + 2400; if ((eventStart < searchStart || eventStart > searchStop) && (eventStart2 < searchStart || eventStart2 > searchStop)) continue; } if (useDuration) { int duration = p->Duration() / 60; if (minSearchDuration > duration || maxSearchDuration < duration) continue; } if (useDayOfWeek) { time_t tEvent = p->StartTime(); struct tm tmEvent; localtime_r(&tEvent, &tmEvent); if (DayOfWeek >= 0 && DayOfWeek != tmEvent.tm_wday) continue; if (DayOfWeek < 0) { int iFound = 0; for (int i = 0; i < 7; i++) if (abs(DayOfWeek) & (int)pow(2, i) && i == tmEvent.tm_wday) { iFound = 1; break; } if (!iFound) continue; } } if (strlen(szTest) > 0) { if (!MatchesSearchMode(szTest, searchText, mode, " ,;|~", fuzzyTolerance)) continue; } if (useParentalRating) { int rating = p->ParentalRating(); if (minParentalRating > rating || maxParentalRating < rating) continue; } if (useContentsFilter && !MatchesContentsFilter(p)) continue; if (useExtEPGInfo && !MatchesExtEPGInfo(p)) continue; pe = p; break; } } free(szTest); free(searchText); return pe; } // returns a pointer array to the matching search results cSearchResults* cBlacklist::Run(cSearchResults* pSearchResults, int MarginStop) { LogFile.Log(3, "start search for blacklist '%s'", search); LOCK_CHANNELS_READ; LOCK_SCHEDULES_READ; const cSchedule *Schedule = Schedules->First(); while (Schedule) { const cChannel* channel = Channels->GetByChannelID(Schedule->ChannelID(), true, true); if (!channel) { Schedule = Schedules->Next(Schedule); continue; } if (useChannel == 1 && channelMin && channelMax) { // caution: reordering the channels via OSD can yield unexpected results if (channelMin->Number() > channel->Number() || channelMax->Number() < channel->Number()) { Schedule = Schedules->Next(Schedule); continue; } } if (useChannel == 2 && channelGroup) { cChannelGroup* group = ChannelGroups.GetGroupByName(channelGroup); if (!group || !group->ChannelInGroup(channel)) { Schedule = Schedules->Next(Schedule); continue; } } if (useChannel == 3) { if (channel->Ca() >= CA_ENCRYPTED_MIN) { Schedule = Schedules->Next(Schedule); continue; } } const cEvent *pPrevEvent = NULL; do { const cEvent* event = GetEventByBlacklist(Schedule, pPrevEvent, MarginStop); pPrevEvent = event; if (event && Channels->GetByChannelID(event->ChannelID(), true, true)) { if (!pSearchResults) pSearchResults = new cSearchResults; pSearchResults->Add(new cSearchResult(event, this)); } } while (pPrevEvent); Schedule = Schedules->Next(Schedule); } LogFile.Log(3, "found %d event(s) for blacklist '%s'", pSearchResults ? pSearchResults->Count() : 0, search); return pSearchResults; } bool cBlacklist::MatchesExtEPGInfo(const cEvent* e) { if (!e || !e->Description()) return false; bool matching = extEPGInfoMatchingMode != 2; cSearchExtCat* SearchExtCat = SearchExtCats.First(); while (SearchExtCat) { char* value = NULL; int index = SearchExtCats.GetIndexFromID(SearchExtCat->id); if (index > -1) value = catvalues[index]; if (value && SearchExtCat->searchmode >= 10 && atol(value) == 0) // numerical value != 0 ? value = NULL; if (value && *value) { char* testvalue = GetExtEPGValue(e, SearchExtCat); if (!testvalue) { if (extEPGInfoMatchingMode == 0) return false; } else { // compare case insensitive char* valueLower = strdup(value); ToLower(valueLower); ToLower(testvalue); bool valueMatches = MatchesSearchMode(testvalue, valueLower, SearchExtCat->searchmode, ",;|~", fuzzyTolerance); free(testvalue); free(valueLower); if (extEPGInfoMatchingMode != 2) matching &= valueMatches; else matching |= valueMatches; } } SearchExtCat = SearchExtCats.Next(SearchExtCat); } return matching; } bool cBlacklist::HasContentID(int contentID) { for (unsigned int i = 0; i < contentsFilter.size(); i += 2) { std::string hexContentID = contentsFilter.substr(i, 2); if (hexContentID.size() != 2) return false; std::istringstream iss(hexContentID); int tmpContentID = 0; if (!(iss >> std::noshowbase >> std::hex >> tmpContentID)) return false; if (contentID == tmpContentID) return true; } return false; } void cBlacklist::SetContentsFilter(const int* contentDescriptorFlags) { // create the hex array of content descriptor IDs std::ostringstream oss; for (unsigned int i = 0; contentDescriptorFlags && i <= CONTENT_DESCRIPTOR_MAX; i++) { if (contentDescriptorFlags[i]) { oss << std::hex << std::noshowbase << i; } } contentsFilter = oss.str(); } bool cBlacklist::MatchesContentsFilter(const cEvent* e) { if (!e || !e->Contents(0)) return false; // ensure match with VDR settings constexpr unsigned int contentIdentifiersPerGroup = 16; // check if each content filter ID is contained in the events descriptors; // at least one entry within a content group must match, and all non-empty // groups must have at least one matching entry unsigned int checkedGroups = 0; // set bit indicates that group is contained in contentsFilter unsigned int checkedCharacteristics = 0; unsigned int matchingGroups = 0; // set bit indicates that at least a matching content ID within the group unsigned int matchingCharacteristics = 0; for (unsigned int i = 0; i < contentsFilter.size(); i += 2) { std::string hexContentID = contentsFilter.substr(i, 2); if (hexContentID.size() != 2) return false; std::istringstream iss(hexContentID); int searchContentID = 0; if (!(iss >> std::hex >> searchContentID)) return false; unsigned int gid = searchContentID / contentIdentifiersPerGroup; unsigned int cid = searchContentID % contentIdentifiersPerGroup; int c = 0, eventContentID = 0; bool found = false; while ((eventContentID = e->Contents(c++)) > 0) { if (gid == 0xB) { // special characteristics use their own matching mode checkedCharacteristics |= 1 << cid; if (eventContentID == searchContentID) { matchingCharacteristics |= 1 << cid; found = true; // to prevent from unintended exit break; } } else { checkedGroups |= 1 << gid; if (eventContentID == searchContentID) { if (contentsCategoryMatchingMode == 1) { // any descriptor satisfies the matching mode return true; } matchingGroups |= 1 << gid; found = true; break; } } } if (contentsCategoryMatchingMode == 0 && !found) { // only all descriptors satisfy the matching mode return false; } } // check special characteristics w.r.t. matching mode bool characteristicsIsMatching; if (contentsCharacteristicsMatchingMode == 0) { // must be exact match characteristicsIsMatching = checkedCharacteristics == matchingCharacteristics; } else { // one match satisfies, provided that some characteristics was selected at all characteristicsIsMatching = !checkedCharacteristics || matchingCharacteristics; } // some selected descriptor within each touched group return characteristicsIsMatching && (contentsCategoryMatchingMode == 0 || checkedGroups == matchingGroups); } // -- cBlacklists ---------------------------------------------------------------- int cBlacklists::GetNewID() { int newID = -1; cMutexLock BlacklistLock(this); cBlacklist *l = (cBlacklist *)First(); while (l) { newID = std::max(newID, l->ID); l = (cBlacklist *)l->Next(); } return newID + 1; } cBlacklist* cBlacklists::GetBlacklistFromID(int ID) { if (ID == -1) return NULL; cMutexLock BlacklistLock(this); cBlacklist *l = (cBlacklist *)First(); while (l) { if (l->ID == ID) return l; l = (cBlacklist *)l->Next(); } return NULL; } bool cBlacklists::Exists(const cBlacklist* Blacklist) { cMutexLock BlacklistLock(this); cBlacklist *l = (cBlacklist*)First(); while (l) { if (l == Blacklist) return true; l = (cBlacklist*)l->Next(); } return false; } vdr-plugin-epgsearch-2.4.6/blacklist.h000066400000000000000000000075721514271145600177130ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #ifndef __EPGSEARCHBL_H #define __EPGSEARCHBL_H #include #include class cSearchExt; class cSearchResults; class cBlacklist : public cListObject { friend class cMenuEditSearchExt; private: // the previously static buffer would bear the risk of race conditions // between class instances or at the service interface char* buffer; public: // items follow the sequence of epgsearchblacklists.conf(5), but arrange // dependent items next to each other int ID; char search[MaxFileName]; int useTime; int startTime; int stopTime; int useChannel; const cChannel *channelMin; const cChannel *channelMax; char* channelGroup; int useCase; int mode; int useTitle; int useSubtitle; int useDescription; int useDuration; int minDuration; int maxDuration; int useDayOfWeek; int DayOfWeek; int useExtEPGInfo; char** catvalues; int extEPGInfoMatchingMode; int fuzzyTolerance; int isGlobal; int useContentsFilter; // just for OSD handling std::string contentsFilter; int contentsCategoryMatchingMode; int contentsCharacteristicsMatchingMode; int useParentalRating; int minParentalRating; int maxParentalRating; public: cBlacklist(void); virtual ~cBlacklist(void); cBlacklist& operator= (const cBlacklist&); virtual bool operator< (const cListObject &ListObject); const char *Search(void) { return search; } int StartTime(void) { return startTime; } int StopTime(void) { return stopTime; } int UseChannel(void) { return useChannel; } const cChannel *ChannelMin(void) { return channelMin; } const cChannel *ChannelMax(void) { return channelMax; } const cEvent * GetEventByBlacklist(const cSchedule *schedules, const cEvent *Start, int MarginStop = 0); bool MatchesExtEPGInfo(const cEvent* e); const char *ToText(void); bool Parse(const char *s); bool ParseExtEPGValues(const char *s); bool ParseExtEPGEntry(const char *s); bool Save(FILE *f); cSearchResults* Run(cSearchResults* pSearchResults = NULL, int MarginStop = 0); void CopyFromTemplate(const cSearchExt* templ); bool HasContentID(int contentID); void SetContentsFilter(const int* contentDescriptorFlags); bool MatchesContentsFilter(const cEvent* e); }; class cBlacklists : public cConfig, public cMutex { public: int GetNewID(void); cBlacklist* GetBlacklistFromID(int ID); bool Exists(const cBlacklist* Blacklist); }; class cBlacklistObject: public cListObject { public: cBlacklist* blacklist; cBlacklistObject(cBlacklist* Blacklist) : blacklist(Blacklist) {} }; extern cBlacklists Blacklists; #endif vdr-plugin-epgsearch-2.4.6/changrp.c000066400000000000000000000367011514271145600173540ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include "changrp.h" #include "epgsearchtools.h" #include "epgsearchcfg.h" #include "epgsearchext.h" #include // -- cChannelGroup ----------------------------------------------------------------- cChannelGroup::cChannelGroup(void) { strcpy(name, ""); channels.Clear(); } cChannelGroup::~cChannelGroup(void) { channels.Clear(); } bool cChannelGroup::Parse(const char *s) { char *line; char *pos; char *pos_next; int parameter = 1; int valuelen; #define MAXVALUELEN (10 * MaxFileName) char value[MAXVALUELEN]; pos = line = strdup(s); pos_next = pos + strlen(pos); if (*pos_next == '\n') *pos_next = 0; while (*pos) { while (*pos == ' ') pos++; if (*pos) { if (*pos != '|') { pos_next = strchr(pos, '|'); if (!pos_next) pos_next = pos + strlen(pos); valuelen = pos_next - pos + 1; if (valuelen > MAXVALUELEN) { LogFile.eSysLog("entry '%s' is too long. Will be truncated!", pos); valuelen = MAXVALUELEN; } strn0cpy(value, pos, valuelen); pos = pos_next; switch (parameter) { case 1: strcpy(name, value); break; default: { #ifdef __FreeBSD__ char *channelbuffer = MALLOC(char, 32); int numChannels = sscanf(value, "%31[^|]", channelbuffer); #else char *channelbuffer = NULL; int numChannels = sscanf(value, "%m[^|]", &channelbuffer); #endif if (numChannels == 1) { LOCK_CHANNELS_READ; const cChannel* channel = Channels->GetByChannelID(tChannelID::FromString(channelbuffer), true, true); if (channel) { cChannelGroupItem* channelitem = new cChannelGroupItem(channel); channels.Add(channelitem); } } free(channelbuffer); } break; } //switch } parameter++; } if (*pos) pos++; } //while free(line); return (parameter >= 1) ? true : false; } const char *cChannelGroup::ToText(void) { char* channelbuffer = NULL; cChannelGroupItem* ChannelGroupItem = channels.First(); int index = 0; while (ChannelGroupItem) { const cChannel* channel = ChannelGroupItem->channel; if (index++ == 0) channelbuffer = strdup(CHANNELSTRING(channel)); else { char* temp = channelbuffer; msprintf(&channelbuffer, "%s|%s", channelbuffer, CHANNELSTRING(channel)); free(temp); } ChannelGroupItem = channels.Next(ChannelGroupItem); } char* buffer = NULL; msprintf(&buffer, "%s|%s", name, channelbuffer); free(channelbuffer); return buffer; } int* cChannelGroup::CreateChannelSel() { LOCK_CHANNELS_READ; int* channelSel = (int*) malloc(Channels->Count() * sizeof(int)); const cChannel* channel = Channels->First(); int index = 0; while (channel) { if (channel->GroupSep()) { channel = Channels->Next(channel); continue; } channelSel[index] = 0; cChannelGroupItem* channelInGroup = channels.First(); while (channelInGroup) { if (channel == channelInGroup->channel) { channelSel[index] = 1; break; } channelInGroup = channels.Next(channelInGroup); } index++; channel = Channels->Next(channel); } return channelSel; } void cChannelGroup::CreateChannelList(int* channelSel) { channels.Clear(); LOCK_CHANNELS_READ; const cChannel* channel = Channels->First(); int index = 0; while (channel) { if (!channel->GroupSep()) { if (channelSel[index] == 1) channels.Add(new cChannelGroupItem(channel)); index++; } channel = Channels->Next(channel); } } bool cChannelGroup::Save(FILE *f) { return fprintf(f, "%s\n", ToText()) > 0; } bool cChannelGroup::ChannelInGroup(const cChannel* channel) { cChannelGroupItem* channelInGroup = channels.First(); while (channelInGroup) { if (channel == channelInGroup->channel) return true; channelInGroup = channels.Next(channelInGroup); } return false; } // -- cChannelGroups ----------------------------------------------------------------- int cChannelGroups::GetIndex(char* channelGroup) { if (!channelGroup) return -1; cChannelGroup* ChannelGroup = First(); int index = 0; while (ChannelGroup) { if (strcmp(channelGroup, ChannelGroup->name) == 0) return index; index++; ChannelGroup = Next(ChannelGroup); } return -1; } cChannelGroup* cChannelGroups::GetGroupByName(const char* channelGroup) { if (!channelGroup) return NULL; cChannelGroup* ChannelGroup = First(); while (ChannelGroup) { if (strcmp(channelGroup, ChannelGroup->name) == 0) return ChannelGroup; ChannelGroup = Next(ChannelGroup); } return NULL; } cSearchExt* cChannelGroups::Used(cChannelGroup* group) { if (!group) return NULL; if (SearchExts.Count() == 0) SearchExts.Load(AddDirectory(CONFIGDIR, "epgsearch.conf")); cMutexLock SearchExtsLock(&SearchExts); cSearchExt *SearchExt = SearchExts.First(); while (SearchExt) { if (SearchExt->useChannel == 2 && strcmp(SearchExt->channelGroup, group->name) == 0) return SearchExt; SearchExt = SearchExts.Next(SearchExt); } return NULL; } const char** cChannelGroups::CreateMenuitemsList() { const char** menuitemsChGr = new const char*[ChannelGroups.Count() + 1]; cChannelGroup* ChannelGroup = First(); menuitemsChGr[0] = ""; int index = 1; while (ChannelGroup) { menuitemsChGr[index++] = ChannelGroup->name; ChannelGroup = Next(ChannelGroup); } return menuitemsChGr; } // -- cMenuChannelGroupItem ----------------------------------------------------------------- cMenuChannelGroupItem::cMenuChannelGroupItem(cChannelGroup* Group) { group = Group; Set(); } void cMenuChannelGroupItem::Set(void) { cString channelbuffer; cChannelGroupItem* channelInGroup = group->channels.First(); int channelNr, chIntBegin = -1, chIntEnd = -1, chLast = -1; while (channelInGroup) { channelNr = channelInGroup->channel->Number(); if (chIntBegin == -1) chIntBegin = channelNr; if (chLast == channelNr - 1) chIntEnd = channelNr; else { chIntEnd = chLast; if (chIntBegin == chIntEnd) channelbuffer = cString::sprintf("%s %d", *channelbuffer ? *channelbuffer : "", chIntBegin); else if (chIntEnd != -1) channelbuffer = cString::sprintf("%s %d-%d", *channelbuffer ? *channelbuffer : "", chIntBegin, chIntEnd); chIntBegin = chIntEnd = channelNr; } chLast = channelNr; channelInGroup = group->channels.Next(channelInGroup); if (!channelInGroup) { if (chLast == chIntBegin) channelbuffer = cString::sprintf("%s %d", *channelbuffer ? *channelbuffer : "", chIntBegin); else channelbuffer = cString::sprintf("%s %d-%d", *channelbuffer ? *channelbuffer : "", chIntBegin, chLast); } } SetText(cString::sprintf("%s\t%s", group->name, *channelbuffer ? *channelbuffer : "")); } // --- cMenuChannelGroups ---------------------------------------------------------- cMenuChannelGroups::cMenuChannelGroups(char** GroupName) : cOsdMenu(tr("Channel groups"), 20) { SetMenuCategory(mcSetupPlugins); groupSel = -1; groupName = GroupName; if (groupName && *groupName) groupSel = ChannelGroups.GetIndex(*groupName); cChannelGroup* ChannelGroup = ChannelGroups.First(); int index = 0; while (ChannelGroup) { Add(new cMenuChannelGroupItem(ChannelGroup), (index == groupSel ? true : false)); ChannelGroup = ChannelGroups.Next(ChannelGroup); index++; } if (groupName && *groupName) SetHelp(trVDR("Button$Edit"), trVDR("Button$New"), trVDR("Button$Delete"), tr("Button$Select")); else SetHelp(trVDR("Button$Edit"), trVDR("Button$New"), trVDR("Button$Delete"), NULL); Sort(); Display(); } cChannelGroup *cMenuChannelGroups::CurrentGroup(void) { cMenuChannelGroupItem *item = (cMenuChannelGroupItem *)Get(Current()); return item ? item->group : NULL; } eOSState cMenuChannelGroups::New(void) { if (HasSubMenu()) return osContinue; return AddSubMenu(new cMenuEditChannelGroup(new cChannelGroup, true)); } eOSState cMenuChannelGroups::Delete(void) { cChannelGroup *curGroup = CurrentGroup(); if (curGroup) { cSearchExt* search = ChannelGroups.Used(curGroup); if (search) { cString Message = cString::sprintf("%s %s", tr("Channel group used by:"), search->search); INFO(Message); return osContinue; } if (Interface->Confirm(tr("Edit$Delete group?"))) { ChannelGroups.Del(curGroup); ChannelGroups.Save(); cOsdMenu::Del(Current()); Display(); } } return osContinue; } eOSState cMenuChannelGroups::ProcessKey(eKeys Key) { int GroupNumber = HasSubMenu() ? Count() : -1; eOSState state = cOsdMenu::ProcessKey(Key); if (state == osUnknown) { if (HasSubMenu()) return osContinue; switch (Key) { case kRed: if (CurrentGroup()) state = AddSubMenu(new cMenuEditChannelGroup(CurrentGroup())); else state = osContinue; break; case kGreen: state = New(); break; case kYellow: state = Delete(); break; case kOk: case kBlue: if (groupName && *groupName) { free(*groupName); *groupName = strdup(CurrentGroup()->name); return osBack; } default: break; } } if (GroupNumber >= 0 && !HasSubMenu() && ChannelGroups.Get(GroupNumber)) { // a newly created group was confirmed with Ok cChannelGroup* group = ChannelGroups.Get(GroupNumber); Add(new cMenuChannelGroupItem(group), true); Display(); } return state; } // --- cMenuEditChannelGroup -------------------------------------------------------- cMenuEditChannelGroup::cMenuEditChannelGroup(cChannelGroup *Group, bool New) : cOsdMenu(tr("Edit channel group"), 30) { SetMenuCategory(mcSetupPlugins); group = Group; channelSel = group->CreateChannelSel(); strcpy(name, group->name); addIfConfirmed = New; if (group) Set(); } cMenuEditChannelGroup::~cMenuEditChannelGroup() { free(channelSel); } void cMenuEditChannelGroup::Set() { int current = Current(); Clear(); Add(new cMenuEditStrItem(tr("Group name"), name, sizeof(group->name), trVDR(FileNameChars))); LOCK_CHANNELS_READ; // TODO THIS MAY LOCK Channels A LONG TIME! const cChannel* channel = Channels->First(); int index = 0; while (channel) { if (channel->GroupSep()) { channel = Channels->Next(channel); continue; } Add(new cMenuEditBoolItem(CHANNELNAME(channel), &channelSel[index++], trVDR("no"), trVDR("yes"))); channel = Channels->Next(channel); } SetCurrent(Get(current)); } eOSState cMenuEditChannelGroup::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); const char* ItemText = Get(Current())->Text(); if (strlen(ItemText) > 0 && strstr(ItemText, tr("Group name")) != ItemText) SetHelp(tr("Button$Invert selection"), tr("Button$All yes"), tr("Button$All no"), NULL); else if (!InEditMode(ItemText, tr("Group name"), name)) SetHelp(NULL, NULL, NULL, NULL); if (state == osUnknown) { switch (Key) { case kOk: if (strlen(name) == 0) { ERROR(tr("Group name is empty!")); return osContinue; } if ((addIfConfirmed || strcmp(name, group->name)) && ChannelGroups.GetGroupByName(name)) { ERROR(tr("Group name already exists!")); return osContinue; } { bool saveSearchExts = false; if (strcmp(group->name, name) != 0 && !addIfConfirmed) { // if group name changed, update searches cMutexLock SearchExtsLock(&SearchExts); cSearchExt *SearchExt = SearchExts.First(); while (SearchExt) { if (SearchExt->useChannel == 2 && SearchExt->channelGroup && strcmp(SearchExt->channelGroup, group->name) == 0) { free(SearchExt->channelGroup); SearchExt->channelGroup = strdup(name); } SearchExt = SearchExts.Next(SearchExt); } saveSearchExts = true; // save them after groups are saved! } strcpy(group->name, name); group->CreateChannelList(channelSel); if (addIfConfirmed) ChannelGroups.Add(group); ChannelGroups.Save(); if (saveSearchExts) SearchExts.Save(); } addIfConfirmed = false; return osBack; break; case kRed: case kGreen: case kYellow: { LOCK_CHANNELS_READ; const cChannel* channel = Channels->First(); int index = 0; while (channel) { if (channel->GroupSep()) { channel = Channels->Next(channel); continue; } channelSel[index] = (Key == kGreen ? 1 : (Key == kRed ? 1 - channelSel[index] : 0)); index++; channel = Channels->Next(channel); } Set(); Display(); return osContinue; } default: break; } } return state; } vdr-plugin-epgsearch-2.4.6/changrp.h000066400000000000000000000063741514271145600173640ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #ifndef __EPGSEARCHCHANGRP_H #define __EPGSEARCHCHANGRP_H #include #include #include class cSearchExt; // --- cChannelGroupItem -------------------------------------------------------- class cChannelGroupItem : public cListObject { public: const cChannel* channel; public: cChannelGroupItem(const cChannel* ch) : channel(ch) {} }; // --- cChannelGroup -------------------------------------------------------- class cChannelGroup : public cListObject { public: char name[MaxFileName]; cList channels; public: cChannelGroup(void); virtual ~cChannelGroup(void); bool Parse(const char *s); const char *ToText(void); bool Save(FILE *f); int* CreateChannelSel(); void CreateChannelList(int*); bool ChannelInGroup(const cChannel*); }; // --- cChannelGroups -------------------------------------------------------- class cChannelGroups : public cConfig { private: public: cChannelGroups(void) {} ~cChannelGroups(void) {} int GetIndex(char* channelGroup); cChannelGroup* GetGroupByName(const char* channelGroup); cSearchExt* Used(cChannelGroup*); const char** CreateMenuitemsList(); }; extern cChannelGroups ChannelGroups; // --- cMenuChannelGroupItem ---------------------------------------------------------- class cMenuChannelGroupItem : public cOsdItem { private: public: cChannelGroup* group; cMenuChannelGroupItem(cChannelGroup*); void Set(void); }; // --- cMenuChannelGroups -------------------------------------------------------- class cMenuChannelGroups : public cOsdMenu { private: cChannelGroup *CurrentGroup(void); eOSState New(void); eOSState Delete(void); int groupSel; char** groupName; protected: virtual eOSState ProcessKey(eKeys Key); public: cMenuChannelGroups(char** groupName = NULL); }; // --- cMenuEditChannelGroup -------------------------------------------------------- class cMenuEditChannelGroup : public cOsdMenu { private: cChannelGroup *group; bool addIfConfirmed; char name[MaxFileName]; int* channelSel; public: cMenuEditChannelGroup(cChannelGroup *group, bool New = false); ~cMenuEditChannelGroup(); void Set(); virtual eOSState ProcessKey(eKeys Key); }; #endif vdr-plugin-epgsearch-2.4.6/conf/000077500000000000000000000000001514271145600165045ustar00rootroot00000000000000vdr-plugin-epgsearch-2.4.6/conf/epgsearchcats.conf-epgdata000066400000000000000000000043161514271145600235760ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # This is just a template based on your current epg.data. Please edit! # Perhaps a category or its value list should be removed. Also the # 'name in menu' should be adjusted to your language. # The order of items determines the order listed in epgsearch. It does not # depend on the ID, which is used by epgsearch. # Format: # ID|category name|name in menu|values separated by ',' (option)|searchmode (option) # - 'ID' should be a unique positive integer # (changing the id later on will force you to reedit your search timers!) # - 'category name' is the name in your epg.data # - 'name in menu' is the name displayed in epgsearch. # - 'values' is an optional list of possible values # - 'searchmode' is an optional parameter specifying the mode of search: # text comparison: # 0 - the whole term must appear as substring # 1 - all single words (delimiters are ',', ';', '|' or '~') # must exist as substrings. This is the default search mode. # 2 - at least one word (delimiters are ',', ';', '|' or '~') # must exist as substring. # 3 - matches exactly # 4 - regular expression # numerical comparison: # 10 - less # 11 - less or equal # 12 - greater # 13 - greater or equal # 14 - equal # 15 - not equal # ----------------------------------------------------------------------------- 1|Category|Kategorie|Information,Kinder,Musik,Serie,Show,Spielfilm,Sport|3 2|Genre|Genre|Abenteuer,Action,Boxen,Comedy,Dokumentarfilm,Drama,Erotik,Familien-Show,Fantasy,Fussball,Geschichte,Gesellschaft,Gesundheit,Gymnastik,Handball,Heimat,Humor,Jazz,Kinderfilme,Kindernachrichten,Kinderserien,Klassik,Krankenhaus,Krimi,Kultur,Kurzfilm,Motor+Verkehr,Motorsport,Musik,Mystery,Nachrichten,Natur,Politik,Radsport,Ratgeber,Reise,Rock,Romantik/Liebe,Science Fiction,Soap,Spielshows,Talkshows,Tennis,Thriller,Verschiedenes,Volksmusik,Wassersport,Western,Wintersport,Wirtschaft,Wissen,Zeichentrick|2 3|Format|Video-Format|16:9,4:3|2 4|Audio|Audio|Dolby Surround,Stereo|2 5|Year|Jahr||0 6|Cast|Besetzung||2 7|Director|Regisseur||2 8|Moderation|Moderation||2 9|Rating|Bewertung|Tagestip,Tip|2 10|FSK|FSK|6,12,16,18|2 vdr-plugin-epgsearch-2.4.6/conf/epgsearchcats.conf-tvm2vdr-hoerzu000066400000000000000000000047011514271145600251050ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # This is just a template based on your current epg.data. Please edit! # Perhaps a category or its value list should be removed. Also the # 'name in menu' should be adjusted to your language. # The order of items determines the order listed in epgsearch. It does not # depend on the ID, which is used by epgsearch. # Format: # ID|category name|name in menu|values separated by ',' (option)|searchmode (option) # - 'ID' should be a unique positive integer # (changing the id later on will force you to reedit your search timers!) # - 'category name' is the name in your epg.data # - 'name in menu' is the name displayed in epgsearch. # - 'values' is an optional list of possible values # - 'searchmode' is an optional parameter specifying the mode of search: # text comparison: # 0 - the whole term must appear as substring # 1 - all single words (delimiters are ',', ';', '|' or '~') # must exist as substrings. This is the default search mode. # 2 - at least one word (delimiters are ',', ';', '|' or '~') # must exist as substring. # 3 - matches exactly # 4 - regular expression # numerical comparison: # 10 - less # 11 - less or equal # 12 - greater # 13 - greater or equal # 14 - equal # 15 - not equal # ----------------------------------------------------------------------------- 1|Category|Kategorie|Information,Kinder,Musik,Serie,Show,Spielfilm,Sport|3 2|Genre|Genre|Abenteuer,Action,Boxen,Comedy,Dokumentarfilm,Drama,Erotik,Familien-Show,Fantasy,Fussball,Geschichte,Gesellschaft,Gesundheit,Gymnastik,Handball,Heimat,Humor,Jazz,Kinderfilme,Kindernachrichten,Kinderserien,Klassik,Krankenhaus,Krimi,Kultur,Kurzfilm,Motor+Verkehr,Motorsport,Musik,Mystery,Nachrichten,Natur,Politik,Radsport,Ratgeber,Reise,Rock,Romantik/Liebe,Science Fiction,Soap,Spielshows,Talkshows,Tennis,Thriller,Verschiedenes,Volksmusik,Wassersport,Western,Wintersport,Wirtschaft,Wissen,Zeichentrick|2 3|Format|Video-Format|16:9,4:3|2 4|Audio|Audio|Dolby Surround,Dolby,Hoerfilm,Stereo|2 5|Year|Jahr||0 6|Cast|Besetzung||2 7|Director|Regisseur||2 8|Moderation|Moderation||2 9|Rating|Bewertung|Groartig besonders wertvoll,Groartig wertvoll,Groartig,Gelungen besonders wertvoll,Gelungen wertvoll,Gelungen,Annehmbar besonders wertvoll,Annehmbar wertvoll,besonders wertvoll,Annehmbar,Schwach|3 10|FSK|FSK|6,12,16,18|3 11|Country|Land||0 12|Episode|Episode||0 vdr-plugin-epgsearch-2.4.6/conf/epgsearchcats.conf-tvm2vdr-tvmovie000066400000000000000000000102001514271145600252510ustar00rootroot00000000000000# ----------------------------------------------------------------------------- # This is just a template based on your current epg.data. Please edit! # Perhaps a category or its value list should be removed. Also the # 'name in menu' should be adjusted to your language. # The order of items determines the order listed in epgsearch. It does not # depend on the ID, which is used by epgsearch. # Format: # ID|category name|name in menu|values separated by ',' (option)|searchmode (option) # - 'ID' should be a unique positive integer # (changing the id later on will force you to reedit your search timers!) # - 'category name' is the name in your epg.data # - 'name in menu' is the name displayed in epgsearch. # - 'values' is an optional list of possible values # - 'searchmode' is an optional parameter specifying the mode of search: # text comparison: # 0 - the whole term must appear as substring # 1 - all single words (delimiters are ',', ';', '|' or '~') # must exist as substrings. This is the default search mode. # 2 - at least one word (delimiters are ',', ';', '|' or '~') # must exist as substring. # 3 - matches exactly # 4 - regular expression # numerical comparison: # 10 - less # 11 - less or equal # 12 - greater # 13 - greater or equal # 14 - equal # 15 - not equal # ----------------------------------------------------------------------------- 1|Category|Category|Film,Kultur,Serie,Show,Spielfilm,Sport|2 2|Genre|Genre|Abenteuer,Abenteuerkomdie,Action,Actiondrama,Action-Fantasy,Actionkomdie,Actionkrimi,Actionthriller,Animations,Anime,Ansprache,Arzt,Auslandsmagazin,Auslandsreportage,Automagazin,Ballett,Bericht,Bibelverfilmung,Bildungsprogramm,Biographie,Boulevardmagazin,Boxen,Bchermagazin,Brgersendung,Chart,Chronik,Clip,Clips,Comedy,Daily Soap,Dauerwerbesendung,Detektiv,Diskussion,Doku,Dokudrama,Doku-Drama,Dokumentar,Dokumentation,Doku-Soap,Drama,Dramedy,Ehedrama,Ehekomdie,Erotik,Erotikthriller,Familien,Familienchronik,Familiendrama,Familienkomdie,Fantasy,Fantasyabenteuer,Fantasykomdie,Fernseh,Festakt,Frauenmagazin,Frhmagazin,Fuball,Gala,Gehrlosenmagazin,Gerichts,Gesellschaftsdrama,Gesellschaftskomdie,Gesellschaftssatire,Gesprch,Gesundheitsmagazin,Gottesdienst,Groteske,Gruselkomdie,Heimat,Heimatmelodram,Historien,Historiendrama,Horror,Horrorkomdie,Horrorthriller,Impressionen,Informationen,Interview,Jazz,Jugend,Jugenddrama,Jugendmagazin,Jugendmemoiren,Justizdrama,Kabarett,Kammermusik,Katastrophen,Kinder,Kinderabenteuer,Kinder<80><94>Magazin,Kinderkomdie,Kinderkrimi,Kindermagazin,Kinder-Magazin,Kinomagazin,Klassik,Koch,Komdie,Konzert,Kriegs,Kriegsdrama,Krimi,Krimiabenteuer,Krimidrama,Krimikomdie,Kriminalmagazin,Kriminal-Magazin,Kulinarisches,Kulturmagazin,Kurz,Liebes,Liebeskomdie,Lifestylemagazin,Literaturverfilmung,Lotterie,Lustspiel,Magazin,Mrchen,Medienmagazin,Melodram,Militrsatire,Mittagsmagazin,Motorsport,Musical,Musicalverfilmung,Musik,Musikkomdie,Musikmagazin,Mystery,Mythen-Doku,Nachrichten,Nachrichtenmagazin,Naturdokumentation,nn,kokrimi,Oper,Politdrama,Politmagazin,Polit-Report,Politthriller,Polizei,Portrt,Prsentation,Predigt,Preisverleihung,Programminformation,Psychothriller,Puppen,Puppentrick,Quiz,Ratgeber,Reisedokumentation,Reisemagazin,Reisereportage,Reitsport,Religion,Reportage,Rock,Romanze,Sammelsendung,Satire,Satiremagazin,Schwank,schwarze Komdie,Science-fiction,Science-fiction-Film,Science-Fiction-Horror,Science-fiction-Komdie,Sitcom,Sitzung,Sketch,Slapstickkomdie,Sonstiges,Spiel,Sport,Sportmagazin,Sportsendung,Sprachkurs,Talk,Teenagerkomdie,Telenovela,Tennis,Theater,Thriller,Tier,Tier-doku,Tierdokumentation,Tiermagazin,Tragikomdie,Trick,Turnen,Umzug,Unterhaltung,Unterhaltungs,Unterhaltungsmusik,Verkaufs,Verkehrsmagazin,Volksmusik,Volkstheater,Vortrag,Weltmusik,Werbung,Western,Westerndrama,Wetter,Wirtschaftsmagazin,Wissenschaftsmagazin,Wochenmagazin,Zeichentrick,Zusammenfassung|2 3|Format|Format|4:3,16:9|3 4|Audio|Audio|Dolby,DolbyDigital, - O-Ton,Stereo,Stereo - O-Ton,Surround,Zweikanal|3 5|Year|Jahr||0 6|Cast|Darsteller||2 7|Director|Regisseur||2 8|Rating|Bewertung|TagesTipp,Tipp,TopTipp|2 9|FSK|FSK|3,4,5,6,7,8,9,12,16,18|3 vdr-plugin-epgsearch-2.4.6/conf/epgsearchconflmail.templ000066400000000000000000000043731514271145600234040ustar00rootroot00000000000000######################################################################### # This is a sample template for email notifications about timer conflicts. # # There are 4 sections to be defined: # - "subject" to be used as mail subject # - "mailbody" the body of the mail (put %conflicts% in the place where the # list of conflicts should appear!) # - "conflictsat" the description of one time where one or more conflicts # exists (put %confltimers% in the place where the list of conflict # timers should appear!) # - "confltimer" the description of one conflicting timer # # close each section with the correspondig end tag! ######################################################################### ######################################################################### # here's the subject definition (no CR!) ######################################################################### [epgsearch] timer conflict info: %conflict.count% conflicting timers! ######################################################################### # here's the body definition ######################################################################### information about conflicting timers ---------------------------------------------------------------------- check at: %datenow% %timenow% conflicts: %conflict.count% ---------------------------------------------------------------------- %conflict.conflicts% Have fun! ######################################################################### # here's the conflictsat definition ######################################################################### the following timers have a conflict at: %conflict.date% %conflict.time% %conflict.confltimers% ######################################################################### # here's the definition of a conflict timer ######################################################################### Title: %title% ~ %subtitle% Start/End: %time_w% %date% %time%-%timeend% (Timer: %timer.start%-%timer.stop%) Channel: %chlng% (%chnr%) File: %timer.file% Search: %timer.search% (%timer.searchid%) ---------------------------------------------------------------------- vdr-plugin-epgsearch-2.4.6/conf/epgsearchmenu.conf000066400000000000000000000030331514271145600222000ustar00rootroot00000000000000######################################################### # sample file for epgsearchmenu.conf # # The following variables exist: # # %time% - start time in format HH:MM # %date% - start date in format TT.MM.YY # %datesh% - start date in format TT.MM. # %time_w% - weekday name # %time_d% - start day in format TT # %title% - title # %subtitle% - subtitle # %t_status% - timer status ('T', 't', 'R') # %v_status% - VPS status # %r_status% - running status # %status% - complete status, the same as # '%t_status%%v_status%%r_status%' # %colon% - the sign ':' # %% - a value from the extended EPG categories, specified in # epgsearchcats.conf, like %genre% or %category% # # for the 'Whats on...' menus there is also: # # %chnr% - channel number # %chsh% - the short channel name # %chlng% - the 'normal' channel name # %progr% - graphical progress bar # %progrT2S% - progress bar in text2skin style # ######################################################### MenuWhatsOnNow= %chnr%:4|%progrt2s%:5| %time% %t_status%:8|%category%:6| %title% ~ %subtitle%:35 MenuWhatsOnNext=%chnr%:4|%time% %t_status%:8|%category%:8| %title% ~ %subtitle%:35 MenuWhatsOnElse=%chnr%:4|%time% %t_status%:8|%category%:8| %title% ~ %subtitle%:35 MenuSchedule=%time% %t_status%:8|%genre%:14| %title% ~ %subtitle%:35 MenuSearchResults=%chsh%:12|%datesh%:6|%time%:6|%t_status%:2|%title% ~ %subtitle%:35 # WarEagleIcons=1 vdr-plugin-epgsearch-2.4.6/conf/epgsearchupdmail-html.templ000066400000000000000000000160411514271145600240300ustar00rootroot00000000000000######################################################################### # This is a sample template for email notifications about timer changes # (NOTE: This is a sample for a HTML mail, but you could use only text # as well) # # There are 3 sections that have to be defined: # - "subject" to be used as mail subject # - "mailbody" the body of the mail: # put %update.newtimers% in the place where the list of new timers should # appear. The same for %update.modtimers%, %update.deltimers% and # %update.newevents& for the list of changed or deleted timers and event # announcements. # - "timer" the description of one timer and "event" with the description of # one event. This section is used to display one timer within a timer list, # e.g. in %update.newtimers%. The same for "event". # # All sections are optional, e.g. if you don't use event announcements you # can drop "%update.newevents%" in the mailbody and the "event" section. But # of course you should have at least a mailbody ;-) # # close each section with the correspondig end tag! # Please have a look at the MANUAL for the available variables. ######################################################################### # # Version: 0.4 Date: 20.09.2009 # # Author: Mike Constabel # Christian Wieninger # ######################################################################### ######################################################################### # here's the mail's subject definition (no CR!) ######################################################################### [epgsearch] update info: %update.countnewtimers% new / %update.countmodtimers% modified / %update.countdeltimers% deleted timers / %update.countnewevents% new events / ######################################################################### # here's the mail's body definition ######################################################################### Information about search timer update
Information about search timer update
update at:%datenow% %timenow%
new timers:%update.countnewtimers%
modified timers:%update.countmodtimers%
deleted timers:%update.countdeltimers%
new events to report:%update.countnewevents%

Top New timers
%update.newtimers%

Top Modified timers
%update.modtimers%

Top Deleted timers
%update.deltimers%

Top New events to report
%update.newevents%

Have fun!

############################################################################## # here's the timer definition, which is used to display information about # a timer within one of the timer lists. You can use any variables that refer # to an event or timer. (Please note, that a timer may have no event assigned # to it, resulting in an empty variable substitution!). You can also use 'user # defined' variables from epgsearchuservars.conf. ##############################################################################
Title:
Subtitle:%subtitle%
Start/End:%time_w% %date% %time%-%timeend% (Timer: %timer.start%-%timer.stop%)
Channel:%timer.chlng% (%timer.chnr%)
File:%timer.file%
Search:%timer.search% (%timer.searchid%)
Modification:%timer.modreason%
Summary:%htmlsummary%

############################################################################## # here's the event definition, which is used to display information about # an event in the announcement list. You can use any variables that refer # to an event. You can also use 'user defined' variables from # epgsearchuservars.conf. ##############################################################################
Title:
Subtitle:%subtitle%
Start/End:%time_w% %date% %time%-%timeend%
Channel:%chlng% (%chnr%)
Search:%search% (%searchid%)
Summary:%htmlsummary%

vdr-plugin-epgsearch-2.4.6/conf/epgsearchupdmail.templ000066400000000000000000000101771514271145600230720ustar00rootroot00000000000000######################################################################### # This is a sample template for email notifications about timer changes # and/or events announcements # (NOTE: This is a sample for a text mail, but you could use HTML as well) # # There are 3 sections that have to be defined: # - "subject" to be used as mail subject # - "mailbody" the body of the mail: # put %update.newtimers% in the place where the list of new timers should # appear. The same for %update.modtimers%, %update.deltimers% and # %update.newevents& for the list of changed or deleted timers and event # announcements. # - "timer" the description of one timer and "event" with the description of # one event. This section is used to display one timer within a timer list, # e.g. in %update.newtimers%. The same for "event". # # All sections are optional, e.g. if you don't use event announcements you # can drop "%update.newevents%" in the mailbody and the "event" section. But # of course you should have at least a mailbody ;-) # # close each section with the correspondig end tag! # Please have a look at the MANUAL for the available variables. ######################################################################### ######################################################################### # here's the mail's subject definition (no CR!) ######################################################################### [epgsearch] update info: %update.countnewtimers% new / %update.countmodtimers% modified / %update.countdeltimers% deleted timers / %update.countnewevents% new events ######################################################################### # here's the mail's body definition ######################################################################### information about search timer update ---------------------------------------------------------------------- update at: %datenow% %timenow% new timers: %update.countnewtimers% modified timers: %update.countmodtimers% deleted timers: %update.countdeltimers% new events: %update.countnewevents% the following timers have been added: ---------------------------------------------------------------------- %update.newtimers% the following timers have been modified: ---------------------------------------------------------------------- %update.modtimers% the following timers have been deleted: ---------------------------------------------------------------------- %update.deltimers% the following events where found: ---------------------------------------------------------------------- %update.newevents% Have fun! ############################################################################## # here's the timer definition, which is used to display information about # a timer within one of the timer lists. You can use any variables that refer # to an event or timer. (Please note, that a timer may have no event assigned # to it, resulting in an empty variable substitution!). You can also use 'user # defined' variables from epgsearchuservars.conf. ############################################################################## Title: %title% ~ %subtitle% Start/End: %time_w% %date% %time%-%timeend% (Timer: %timer.start%-%timer.stop%) Channel: %timer.chlng% (%timer.chnr%) File: %timer.file% Modification: %timer.modreason% Search: %timer.search% (%timer.searchid%) Summary: %summary% ---------------------------------------------------------------------- ############################################################################## # here's the event definition, which is used to display information about # an event within the list of event announcements. You can use any variables # that refer to an event. You can also use 'user defined' variables from # epgsearchuservars.conf. ############################################################################## Title: %title% ~ %subtitle% Start/End: %time_w% %date% %time%-%timeend% Channel: %chlng% (%chnr%) Search: %search% (%searchid%) Summary: %summary% ---------------------------------------------------------------------- vdr-plugin-epgsearch-2.4.6/confdloader.c000066400000000000000000000111141514271145600202010ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include #include "confdloader.h" #include "log.h" #include "uservars.h" #include "menu_dirselect.h" #include "templatefile.h" #include "epgsearchcats.h" // --------------------------- // Loads all files in the conf.d subdirectory of and // applies found settings. // --------------------------- bool cConfDLoader::Load() { const std::string dirPath(AddDirectory(CONFIGDIR, "conf.d")); LogFile.Log(2, "loading entries in %s", dirPath.c_str()); cReadDir d(dirPath.c_str()); struct dirent* e; std::string parent(".."); std::string current("."); int count = 0; bool success = true; while ((e = d.Next())) { std::string direntry = e->d_name; if ((current == direntry) || (parent == direntry) || (direntry[direntry.size() - 1] == '~')) { continue; } /* Check if entry is a directory: I do not rely on e->d_type here because on some systems it is always DT_UNKNOWN. Also testing for DT_DIR does not take into account symbolic links to directories. */ struct stat buf; if ((stat((dirPath + "/" + e->d_name).c_str(), &buf) != 0) || (S_ISDIR(buf.st_mode))) { continue; } success &= LoadFile((dirPath + "/" + e->d_name).c_str()); count++; } LogFile.Log(2, "loaded %d entries in %s", count, dirPath.c_str()); return success; } // --------------------------- // Each file has the form // // [
] // // ... // [
] // ... // // where section is one: // // epgsearchuservars // epgsearchdirs // epgsearchmenu // epgsearchcats // // corresponds to the entries in the related conf files. // --------------------------- bool cConfDLoader::LoadFile(const char *FileName) { if (FileName && access(FileName, F_OK) == 0) { LogFile.Log(1, "loading %s", FileName); FILE *f = fopen(FileName, "r"); if (f) { char *s; cReadLine ReadLine; std::string section; while ((s = ReadLine.Read(f)) != NULL) { char *p = strchr(s, '#'); if (p) *p = 0; stripspace(s); if (!isempty(s)) { if (*s == '[' && *(s + strlen(s) - 1) == ']') // Section? section = s; else { if (EqualsNoCase(section, "[epgsearchuservars]")) cUserVarLine::Parse(s); if (EqualsNoCase(section, "[epgsearchdirs]")) { cDirExt* D = new cDirExt; if (D && D->Parse(s)) ConfDDirExts.Add(D); else delete D; } if (EqualsNoCase(section, "[epgsearchmenu]")) { cTemplLine T; if (T.Parse(s)) cTemplFile::Parse(T.Name(), T.Value()); } if (EqualsNoCase(section, "[epgsearchcats]")) { cSearchExtCat* cat = new cSearchExtCat; if (cat && cat->Parse(s)) SearchExtCats.Add(cat); else delete cat; } } } } fclose(f); } return true; } else { LOG_ERROR_STR(FileName); return false; } } vdr-plugin-epgsearch-2.4.6/confdloader.h000066400000000000000000000022601514271145600202100ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #ifndef VDR_EPGSEARCH_CONFDLOADER_INC #define VDR_EPGSEARCH_CONFDLOADER_INC class cConfDLoader { public: cConfDLoader() {} bool Load(); bool LoadFile(const char *FileName); }; #endif vdr-plugin-epgsearch-2.4.6/conflictcheck.c000066400000000000000000001134011514271145600205220ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include #include "conflictcheck.h" #include "epgsearchcfg.h" /*#include */ #include "conflictcheck_thread.h" #include "recstatus.h" #include "timerstatus.h" #include "uservars.h" #include #define FULLMATCH 1000 #define EPGLIMITBEFORE (1 * 3600) // Time in seconds before a timer's start time and #define EPGLIMITAFTER (1 * 3600) // after its stop time within which EPG events will be taken into consideration. // --- cConflictCheckTimerObj -------------------------------------------------------- cConflictCheckTimerObj::cConflictCheckTimerObj(cTimer* Timer, time_t Start, time_t Stop, int Device, int OrigIndex) : cTimerObj(Timer), start(Start), stop(Stop), device(Device), origIndex(OrigIndex), conflCheckTime(NULL), concurrentTimers(NULL), ignore(false) { event = Timer->Event(); recDuration = 0; lastRecStart = 0; lastRecStop = 0; } cConflictCheckTimerObj::~cConflictCheckTimerObj() { // conflict checks works on a copy of a timer, so delete it again DELETENULL(timer); } int cConflictCheckTimerObj::Compare(const cListObject &ListObject) const { cConflictCheckTimerObj *p = (cConflictCheckTimerObj *)&ListObject; long diff = start - p->start; if (diff == 0) diff = p->timer->Priority() - timer->Priority(); if (diff == 0) diff = origIndex - p->origIndex; return diff; } const cEvent* cConflictCheckTimerObj::Event() { if (timer->Event()) return timer->Event(); else if (!event) event = SetEventFromSchedule(); return event; } const cEvent* cConflictCheckTimerObj::SetEventFromSchedule() { LOCK_SCHEDULES_READ; if (!Schedules) return NULL; const cSchedule *Schedule = Schedules->GetSchedule(timer->Channel()); if (Schedule && Schedule->Events()->First()) { const cEvent *Event = NULL; if (timer->HasFlags(tfVps) && Schedule->Events()->First()->Vps()) { // VPS timers only match if their start time exactly matches the event's VPS time: for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) { if (e->StartTime()) { // changes from vdr 2.4 applied int overlap = 0; if (Matches(e, &overlap) == tmFull) { Event = e; if (overlap > FULLMATCH) { break; // take the first matching event } } } } } else { // Normal timers match the event they have the most overlap with: int Overlap = 0; // Set up the time frame within which to check events: #if APIVERSNUM > 30008 time_t startTime, stopTime; timer->CalcStartStopTime(startTime,stopTime); time_t TimeFrameBegin = startTime - EPGLIMITBEFORE; time_t TimeFrameEnd = stopTime + EPGLIMITAFTER; #else timer->Matches(0, true); time_t TimeFrameBegin = start - EPGLIMITBEFORE; time_t TimeFrameEnd = stop + EPGLIMITAFTER; #endif for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) { if (e->EndTime() < TimeFrameBegin) continue; // skip events way before the timer starts if (e->StartTime() > TimeFrameEnd) break; // the rest is way after the timer ends int overlap = 0; Matches(e, &overlap); if (overlap && overlap >= Overlap) { if (Event && overlap == Overlap && e->Duration() <= Event->Duration()) continue; // if overlap is the same, we take the longer event Overlap = overlap; Event = e; } } } return Event; } return NULL; } int cConflictCheckTimerObj::Matches(const cEvent *Event, int *Overlap) const { // Overlap is the percentage of the Event's duration that is covered by // this timer (based on FULLMATCH for finer granularity than just 100). // To make sure a VPS timer can be distinguished from a plain 100% overlap, // it gets an additional 100 added, and a VPS event that is actually running // gets 200 added to the FULLMATCH. if (timer->Channel()->GetChannelID() == Event->ChannelID()) { bool UseVps = timer->HasFlags(tfVps) && Event->Vps(); #if APIVERSNUM > 30008 time_t startTime, stopTime; timer->CalcStartStopTime(startTime,stopTime); #else timer->Matches(UseVps ? Event->Vps() : Event->StartTime(), true); #endif int overlap = 0; if (UseVps) // use modifications since vdr 2.4 #if APIVERSNUM > 30008 if (startTime == Event->Vps()) { #else if (start == Event->Vps()) { #endif overlap = FULLMATCH; if (Event->IsRunning()) overlap += 200; else if (Event->RunningStatus() != SI::RunningStatusNotRunning) overlap += 100; } else { #if APIVERSNUM > 30008 if (startTime <= Event->StartTime() && Event->EndTime() <= stopTime) #else if (start <= Event->StartTime() && Event->EndTime() <= stop) #endif overlap = FULLMATCH; #if APIVERSNUM > 30008 else if (stopTime <= Event->StartTime() || Event->EndTime() <= startTime) #else else if (stop <= Event->StartTime() || Event->EndTime() <= start) #endif overlap = 0; else #if APIVERSNUM > 30008 overlap = (std::min(stopTime, Event->EndTime()) - std::max(startTime, Event->StartTime())) * FULLMATCH / std::max(Event->Duration(), 1); #else overlap = (std::min(stop, Event->EndTime()) - std::max(start, Event->StartTime())) * FULLMATCH / std::max(Event->Duration(), 1); #endif } if (Overlap) *Overlap = overlap; return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone; } return tmNone; } // --- cConflictCheck ------------------------------------------------------- cConflictCheck::cConflictCheck() { evaltimeList = NULL; timerList = NULL; failedList = NULL; relevantConflicts = 0; numConflicts = 0; devices = NULL; localConflicts = !(EPGSearchConfig.RemoteConflictCheck && Setup.SVDRPPeering); InitDevicesInfo(); } cConflictCheck::~cConflictCheck() { if (failedList && (failedList != evaltimeList)) { // if no local active timers but remote failed // we have a new list failedList->Clear(); DELETENULL(failedList); } if (evaltimeList) { evaltimeList->Clear(); DELETENULL(evaltimeList); } if (timerList) { timerList->Clear(); DELETENULL(timerList); } delete [] devices; } void cConflictCheck::InitDevicesInfo() { delete [] devices; devices = new cConflictCheckDevice[MAXDEVICES]; #ifdef DEBUG_CONFL numDevices = 4; for (int i = 0; i < numDevices; i++) { devices[i].devicenr = i; devices[i].device = NULL; } #else numDevices = cDevice::NumDevices(); for (int i = 0; i < numDevices; i++) devices[i].device = cDevice::GetDevice(i); #endif BondDevices(Setup.DeviceBondings); } void cConflictCheck::BondDevices(const char *Bondings) { LogFile.Log(3, "Bond Devices"); if (Bondings) { cSatCableNumbers SatCableNumbers(MAXDEVICES, Bondings); int* array = SatCableNumbers.Array(); for (int i = 0; i < SatCableNumbers.Size(); i++) { for (int j = 0; j < SatCableNumbers.Size(); j++) { if (array[i] > 0 && array[i] == array[j] && i != j) { LogFile.Log(3, "Bond devices %i and %i.", i + 1, j + 1); devices[i].bondedDevices.push_back(&(devices[j])); } } } } LogFile.Log(3, "Bond Devices done."); } void cConflictCheck::Check() { if (failedList && (failedList != evaltimeList)) DELETENULL(failedList); if (evaltimeList) DELETENULL(evaltimeList); if (timerList) DELETENULL(timerList); relevantConflicts = 0; numConflicts = 0; LogFile.Log(3, "check only local conflicts = %s", GetLocal() ? "yes" : "no"); timerList = CreateCurrentTimerList(); if (timerList) evaltimeList = CreateEvaluationTimeList(timerList); if (evaltimeList) failedList = CreateConflictList(evaltimeList, timerList); if ((!localConflicts) && timerList) failedList = CreateRemoteConflictList(timerList, failedList); if (failedList) for (cConflictCheckTime* checkTime = failedList->First(); checkTime; checkTime = failedList->Next(checkTime)) { LogFile.Log(2, "result of conflict check for %s:", DAYDATETIME(checkTime->evaltime)); std::set::iterator it; for (it = checkTime->failedTimers.begin(); it != checkTime->failedTimers.end(); ++it) { if (!localConflicts) LogFile.Log(2, "timer '%s' (%s, channel %s) %s%s failed", (*it)->timer->File(), DAYDATETIME((*it)->timer->StartTime()), CHANNELNAME((*it)->timer->Channel()), (*it)->timer->Remote() ? "@" : "", (*it)->timer->Remote() ? (*it)->timer->Remote() : ""); else LogFile.Log(2, "timer '%s' (%s, channel %s) failed", (*it)->timer->File(), DAYDATETIME((*it)->timer->StartTime()), CHANNELNAME((*it)->timer->Channel())); } } if (numConflicts > 0 && gl_timerStatusMonitor) gl_timerStatusMonitor->SetConflictCheckAdvised(); } cList* cConflictCheck::CreateCurrentTimerList() { LogFile.Log(3, "current timer list creation started"); cList* CurrentTimerList = NULL; // collect single event timers time_t tMax = 0; LOCK_TIMERS_READ; const cTimer* ti = NULL; for (ti = Timers->First(); ti; ti = Timers->Next(ti)) { tMax = std::max(tMax, ti->StartTime()); if (!ti->HasFlags(tfActive)) continue; if (localConflicts && ti->Remote()) continue; if (ti->StopTime() - ti->StartTime() == 0) continue; // avoid division by zero in computing recPart if (!ti->IsSingleEvent()) continue; // already recording? int deviceNr = -1; if (ti->Local()) // we check devices only for local timers deviceNr = gl_recStatusMonitor->TimerRecDevice(ti) - 1; // create a copy of this timer cTimer* clone = new cTimer(*ti); clone->SetEvent(ti->Event()); cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(clone, ti->StartTime(), ti->StopTime(), deviceNr, ti->Id()); if (deviceNr >= 0) { devices[deviceNr].recTimers.insert(timerObj); timerObj->lastRecStart = ti->StartTime(); } LogFile.Log(3, "add timer '%s' (%s, channel %s) for conflict check", ti->File(), DAYDATETIME(ti->StartTime()), CHANNELNAME(ti->Channel())); if (deviceNr >= 0) LogFile.Log(3, "timer already recording since %s on device %d", DAYDATETIME(ti->StartTime()), deviceNr + 1); if (!CurrentTimerList) CurrentTimerList = new cList; CurrentTimerList->Add(timerObj); } // collect repeating timers from now until the date of the timer with tMax time_t maxCheck = time(NULL) + std::min(14, EPGSearchConfig.checkMaxDays) * SECSINDAY; tMax = std::max(tMax, maxCheck); for (ti = Timers->First(); ti; ti = Timers->Next(ti)) { if (ti->IsSingleEvent()) continue; if (localConflicts && ti->Remote()) continue; time_t day = time(NULL); while (day < tMax) { if (ti->DayMatches(day)) { time_t Start = cTimer::SetTime(day, cTimer::TimeToInt(ti->Start())); int deviceNr = -1; if (Start < time(NULL)) { #ifndef DEBUG_CONFL if (ti->Local() && ti->Recording()) deviceNr = gl_recStatusMonitor->TimerRecDevice(ti) - 1; #else if (Start + ti->StopTime() - ti->StartTime() > time(NULL)) deviceNr = 0; #endif if (deviceNr == -1) { // currently not recording, skip it day += SECSINDAY; continue; } } else if (Start < ti->StartTime()) { day += SECSINDAY; continue; } // create a copy of this timer cTimer* clone = new cTimer(*ti); clone->SetEvent(ti->Event()); cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(clone, Start, Start + ti->StopTime() - ti->StartTime(), deviceNr, ti->Id()); LogFile.Log(3, "add timer '%s' (%s, channel %s) for conflict check", ti->File(), DAYDATETIME(Start), CHANNELNAME(ti->Channel())); if (deviceNr >= 0) { LogFile.Log(3, "timer already recording since %s on device %d", DAYDATETIME(Start), deviceNr + 1); devices[deviceNr].recTimers.insert(timerObj); timerObj->lastRecStart = Start; } if (!CurrentTimerList) CurrentTimerList = new cList; CurrentTimerList->Add(timerObj); } day += SECSINDAY; } } if (CurrentTimerList) CurrentTimerList->Sort(); LogFile.Log(3, "current timer list created"); return CurrentTimerList; } // create a list of all times that have to be checked cList* cConflictCheck::CreateEvaluationTimeList(cList* TimerList) { LogFile.Log(3, "create check time list"); cList* EvalTimeList = NULL; for (cConflictCheckTimerObj* TimerObj = TimerList->First(); TimerObj; TimerObj = TimerList->Next(TimerObj)) { if (!TimerObj->timer->HasFlags(tfActive)) continue; if (TimerObj->timer->Remote()) continue; // here we check local timers only if (!EvalTimeList) EvalTimeList = new cList; cConflictCheckTime* checkTime = NULL; // add all timer start times for (cConflictCheckTime* checkTimeTest = EvalTimeList->First(); checkTimeTest; checkTimeTest = EvalTimeList->Next(checkTimeTest)) { if (checkTimeTest->evaltime == TimerObj->start) { checkTime = checkTimeTest; break; } } if (!checkTime) { checkTime = new cConflictCheckTime(TimerObj->start); EvalTimeList->Add(checkTime); } checkTime->startingTimers.insert(TimerObj); // add all timer stop times checkTime = NULL; for (cConflictCheckTime* checkTimeTest = EvalTimeList->First(); checkTimeTest; checkTimeTest = EvalTimeList->Next(checkTimeTest)) { if (checkTimeTest->evaltime == TimerObj->stop) { checkTime = checkTimeTest; break; } } if (!checkTime) { checkTime = new cConflictCheckTime(TimerObj->stop); EvalTimeList->Add(checkTime); } checkTime->stoppingTimers.insert(TimerObj); } if (EvalTimeList) EvalTimeList->Sort(); LogFile.Log(3, "create check time list - done"); return EvalTimeList; } // this return a list of all conflicts cList* cConflictCheck::CreateConflictList(cList* EvalTimeList, cList* TimerList) { LogFile.Log(3, "create conflict list"); maxCheck = time(NULL) + std::min(14, EPGSearchConfig.checkMaxDays) * SECSINDAY; // check each time for (cConflictCheckTime* checkTime = EvalTimeList->First(); checkTime; checkTime = EvalTimeList->Next(checkTime)) { int Conflicts = ProcessCheckTime(checkTime); if (Conflicts > 0) { // if there were conflicts do a retry as VDR would do a few seconds after the conflict LogFile.Log(3, "retry check time %s", DAYDATETIME(checkTime->evaltime)); int OldConflicts = Conflicts; while (true) { Conflicts = ProcessCheckTime(checkTime); if (Conflicts == OldConflicts) break; // no change after retry? OldConflicts = Conflicts; }; } } nextRelevantConflictDate = 0; for (cConflictCheckTime* checkTime = EvalTimeList->First(); checkTime;) { // clear the list cConflictCheckTime* checkTimeNext = EvalTimeList->Next(checkTime); if (checkTime->failedTimers.empty()) EvalTimeList->Del(checkTime); else { bool allTimersIgnored = true; std::set::iterator it; for (it = checkTime->failedTimers.begin(); it != checkTime->failedTimers.end(); ++it) { numConflicts++; if (!(*it)->ignore) { if (!nextRelevantConflictDate) nextRelevantConflictDate = checkTime->evaltime; else nextRelevantConflictDate = std::min(nextRelevantConflictDate, checkTime->evaltime); relevantConflicts++; allTimersIgnored = false; break; } } if (allTimersIgnored) checkTime->ignore = true; } checkTime = checkTimeNext; } // store for external access cConflictCheckThread::m_cacheNextConflict = nextRelevantConflictDate; cConflictCheckThread::m_cacheRelevantConflicts = relevantConflicts; cConflictCheckThread::m_cacheTotalConflicts = numConflicts; LogFile.Log(3, "create conflict list - done"); return EvalTimeList; } cList* cConflictCheck::CreateRemoteConflictList(cList* TimerList, cList* failedList) { LogFile.Log(3, "add remote conflicts to list"); bool foundRemote = false; cStringList RemoteHosts; // check if we have any Remote timers RemoteHosts.Clear(); for (cConflictCheckTimerObj* TimerObj = TimerList->First(); TimerObj; TimerObj = TimerList->Next(TimerObj)) { if (!TimerObj->timer->HasFlags(tfActive)) continue; if (TimerObj->timer->Remote()) { if (RemoteHosts.Find(TimerObj->timer->Remote()) < 0) { foundRemote = true; RemoteHosts.Append(strdup(TimerObj->timer->Remote())); } } } if (!foundRemote) { LogFile.Log(3, "no remote timers to add"); return failedList; } RemoteHosts.Sort(); cStringList Response; // for all RemoteHosts for (int i = 0; i < RemoteHosts.Size(); i++) { Response.Clear(); if (ExecSVDRPCommand(RemoteHosts[i], "PLUG epgsearch LSCC REL", &Response)) { for (int j = 0; j < Response.Size(); j++) { const char *s = Response[j]; int Code = SVDRPCode(s); if (Code == 901) { LogFile.Log(3, "conflictcheck %s no remote conflicts found", RemoteHosts[i]); continue; } else if (Code != 900) { LogFile.Log(2, "Invalid remote response %d %s", Code, SVDRPValue(s)); break; } else if (const char* line = SVDRPValue(s)) { LogFile.Log(2, "remote conflictcheck line %s", line); int Id, recPart; char rest[256]; intmax_t evaltime; sscanf(line, "%jd:%d|%s", &evaltime, &Id, rest); cConflictCheckTime* checkTime = new cConflictCheckTime((time_t)evaltime); if (!failedList) failedList = new cList; LogFile.Log(2, "added remote checkTime %s to failedList", DAYDATETIME(evaltime)); failedList->Add(checkTime); if (!nextRelevantConflictDate) nextRelevantConflictDate = checkTime->evaltime; else nextRelevantConflictDate = std::min(nextRelevantConflictDate, checkTime->evaltime); numConflicts++; // find TimerObj with id Id in timerList cConflictCheckTimerObj* failedTimer = NULL; bool foundfT = false; for (failedTimer = TimerList->First(); failedTimer; failedTimer = TimerList->Next(failedTimer)) { if (failedTimer->timer->Id() == Id) { foundfT = true; break; } } if (!foundfT) { LogFile.Log(2, "remote failed Timer disappeared"); continue; } LogFile.Log(2, "create remote failedTimer with Id %d", Id); failedTimer->conflCheckTime = checkTime; failedTimer->origIndex = Id; sscanf(rest, "%d|%s", &recPart, rest); failedTimer->recDuration = ((failedTimer->stop - failedTimer->start) * recPart / 100); cConflictCheckTimerObj* concurrentTimer = NULL; while (strlen(rest) > 0) { int n = sscanf(rest, "%d#%s", &Id, rest); if (n < 2) { if (sscanf(rest, "%d", &Id) <= 0) { LogFile.Log(2, "error scanning rest of line %s", rest); break; } *rest = 0; // TODO : possible ?? } // find TimerObj itcc for with Id in timerList bool foundcT = false; for (concurrentTimer = TimerList->First(); concurrentTimer; concurrentTimer = TimerList->Next(concurrentTimer)) { if (concurrentTimer->timer->Id() == Id) { foundcT = true; break; } } if (!foundcT) { LogFile.Log(2, "remote concurrent Timer disappeared"); continue; } if (!failedTimer->concurrentTimers) failedTimer->concurrentTimers = new std::set; LogFile.Log(2, "insert remote Id %d into concurrentTimers", concurrentTimer->timer->Id()); failedTimer->concurrentTimers->insert(concurrentTimer); } // while concurrent Timers LogFile.Log(2, "insert Id %d into checkTime->failedTimers", failedTimer->timer->Id()); checkTime->failedTimers.insert(failedTimer); relevantConflicts++; } else LogFile.Log(2, "got Code %d, but no Value from %s", Code, RemoteHosts[i]); } // received response } else { LogFile.Log(2, "ExecSVDRPCommand failed for %s", RemoteHosts[i]); } } // for all RemoteHosts cConflictCheckThread::m_cacheNextConflict = nextRelevantConflictDate; cConflictCheckThread::m_cacheTotalConflicts = numConflicts; cConflictCheckThread::m_cacheRelevantConflicts = relevantConflicts; LogFile.Log(3, "add remote conflicts done"); return failedList; } // checks for conflicts at one special time int cConflictCheck::ProcessCheckTime(cConflictCheckTime* checkTime) { if (!checkTime) return 0; LogFile.Log(3, "check time %s", DAYDATETIME(checkTime->evaltime)); LogFile.Log(3, "detach stopping timers"); int Conflicts = 0; // detach all stopping timers from their devices std::set::iterator it; for (it = checkTime->stoppingTimers.begin(); it != checkTime->stoppingTimers.end(); ++it) if ((*it) && (*it)->device >= 0) { LogFile.Log(3, "detach device %d from timer '%s' (%s, channel %s) at %s", ((*it)->device) + 1, (*it)->timer->File(), DAYDATETIME((*it)->start), CHANNELNAME((*it)->timer->Channel()), DAYDATETIME(checkTime->evaltime)); devices[(*it)->device].recTimers.erase(*it); (*it)->lastRecStop = checkTime->evaltime; if ((*it)->lastRecStart > 0 && (*it)->lastRecStart < (*it)->lastRecStop) { (*it)->recDuration += (*it)->lastRecStop - (*it)->lastRecStart; (*it)->lastRecStart = 0; if (((*it)->stop - (*it)->start - (*it)->recDuration) < EPGSearchConfig.checkMinDuration * 60) (*it)->ignore = true; } } LogFile.Log(3, "add pending timers"); // if we have pending timers add them to the current start list for (it = pendingTimers.begin(); it != pendingTimers.end(); ++it) { if ((*it) && (*it)->stop > checkTime->evaltime) checkTime->startingTimers.insert(*it); } pendingTimers.clear(); LogFile.Log(3, "attach starting timers"); // handle starting timers for (it = checkTime->startingTimers.begin(); it != checkTime->startingTimers.end(); ++it) { bool NeedsDetachReceivers = false; if (!(*it) || (*it)->device >= 0) continue; // already has a device int device = GetDevice(*it, &NeedsDetachReceivers); if (device >= 0) { // device will be attached? if (NeedsDetachReceivers) { // but needs to detach all others? // disable running timers std::set::iterator it2 = devices[device].recTimers.begin(); for (; it2 != devices[device].recTimers.end(); ++it2) { LogFile.Log(3, "stopping timer '%s' (%s, channel %s) at %s on device %d because of higher priority", (*it2)->timer->File(), DAYDATETIME((*it2)->start), CHANNELNAME((*it2)->timer->Channel()), DAYDATETIME(checkTime->evaltime), device + 1); AddConflict((*it2), checkTime, pendingTimers); Conflicts++; } devices[device].recTimers.clear(); } devices[device].recTimers.insert(*it); (*it)->device = device; (*it)->lastRecStart = checkTime->evaltime; LogFile.Log(3, "recording timer '%s' (%s, channel %s) at %s on device %d", (*it)->timer->File(), DAYDATETIME((*it)->start), CHANNELNAME((*it)->timer->Channel()), DAYDATETIME(checkTime->evaltime), device + 1); } else { AddConflict((*it), checkTime, pendingTimers); Conflicts++; } } LogFile.Log(3, "check time %s - done", DAYDATETIME(checkTime->evaltime)); return Conflicts; } eModuleStatus cConflictCheck::CamSlotModuleStatus(cCamSlot *CamSlot) { if (!CamSlot) return msNone; if ((int)camSlotStatusArray.size() != CamSlots.Count()) for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) camSlotStatusArray.push_back(CamSlot->ModuleStatus()); if (CamSlot->Index() < (int)camSlotStatusArray.size()) return camSlotStatusArray[CamSlot->Index()]; else return msNone; } int cConflictCheck::GetDevice(cConflictCheckTimerObj* TimerObj, bool* NeedsDetachReceivers) { int Priority = TimerObj->timer->Priority(); const cChannel* Channel = TimerObj->timer->Channel(); // Collect the current priorities of all CAM slots that can decrypt the channel: int selDevice = -1; int NumCamSlots = CamSlots.Count(); int SlotPriority[NumCamSlots]; int NumUsableSlots = 0; bool InternalCamNeeded = false; if (Channel->Ca() >= CA_ENCRYPTED_MIN) { for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) { SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used if (CamSlotModuleStatus(CamSlot) == msReady) { if (CamSlot->ProvidesCa(Channel->Caids())) { if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->MasterSlotNumber())) { SlotPriority[CamSlot->Index()] = CamSlot->Priority(); NumUsableSlots++; } } } } #ifdef CFLC int NumUsableSlots = 1; #endif if (!NumUsableSlots) InternalCamNeeded = true; // no CAM is able to decrypt this channel } if (NeedsDetachReceivers) *NeedsDetachReceivers = false; uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) { if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY) continue; // there is no CAM available in this slot for (int i = 0; i < numDevices; i++) { if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != devices[i].CardIndex() + 1) continue; // a specific card was requested, but not this one bool HasInternalCam = devices[i].HasInternalCam(); if (InternalCamNeeded && !HasInternalCam) continue; // no CAM is able to decrypt this channel and the device uses vdr handled CAMs if (NumUsableSlots && !HasInternalCam && !CamSlots.Get(j)->Assign(devices[i].device, true)) continue; // CAM slot can't be used with this device bool ndr; if (devices[i].ProvidesChannel(Channel, Priority, &ndr)) { // this device is basically able to do the job if (NumUsableSlots && !HasInternalCam && devices[i].CamSlot() && devices[i].CamSlot() != CamSlots.Get(j)) ndr = true; // using a different CAM slot requires detaching receivers // Put together an integer number that reflects the "impact" using // this device would have on the overall system. Each condition is represented // by one bit in the number (or several bits, if the condition is actually // a numeric value). The sequence in which the conditions are listed corresponds // to their individual severity, where the one listed first will make the most // difference, because it results in the most significant bit of the result. uint32_t imp = 0; // prefer the primary device for live viewing if we don't need to detach existing receivers imp <<= 1; ; // use receiving devices if we don't need to detach existing receivers imp <<= 1; imp |= !devices[i].Receiving() || ndr; // avoid devices that are receiving imp <<= 1; imp |= devices[i].Receiving(); // do we have GetClippedNumProvidedSystems ??? uses MaxNumProvidedSystems in vdr since V1.7 !! // but should not be needed imp <<= 5; // headroom for 31 Systems int ProvidedSystems=devices[i].NumProvidedSystems(); if (ProvidedSystems <= 0) // invalid return ProvidedSystems = 1; imp |= std::min(ProvidedSystems,31); // avoid cards which support multiple delivery systems // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used) imp <<= 8; imp |= std::min(std::max(devices[i].Priority() + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used) imp <<= 8; imp |= std::min(std::max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // avoid devices if we need to detach existing receivers imp <<= 1; imp |= ndr; // avoid the primary device imp <<= 1; imp |= devices[i].IsPrimaryDevice(); // avoid cards with Common Interface for FTA channels imp <<= 1; imp |= (NumUsableSlots || InternalCamNeeded) ? 0 : devices[i].HasCi(); // avoid full featured cards imp <<= 1; imp |= devices[i].HasDecoder(); // prefer CAMs that are known to decrypt this channel imp <<= 1; imp |= (NumUsableSlots && !HasInternalCam) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlots.Get(j)->MasterSlotNumber()) : 0; if (imp < Impact) { // This device has less impact than any previous one, so we take it. Impact = imp; selDevice = i; if (NeedsDetachReceivers) *NeedsDetachReceivers = ndr; } } } if (!NumUsableSlots) break; // no CAM necessary, so just one loop over the devices } return selDevice; } void cConflictCheck::AddConflict(cConflictCheckTimerObj* TimerObj, cConflictCheckTime* CheckTime, std::set& pendingTimers) { for (cConflictCheckTimerObj* concTimer = timerList->First(); concTimer; concTimer = timerList->Next(concTimer)) { if (concTimer->timer->Remote()) continue; // ignore overlapping remote timers if (concTimer->start >= TimerObj->stop) continue; if (concTimer->stop <= TimerObj->start) continue; if (!TimerObj->concurrentTimers) TimerObj->concurrentTimers = new std::set; TimerObj->concurrentTimers->insert(concTimer); } TimerObj->ignore = (TimerObj->timer->Priority() < EPGSearchConfig.checkMinPriority) || TimerObj->start > maxCheck; CheckTime->concurrentRecs.insert(TimerObj); pendingTimers.insert(TimerObj); TimerObj->lastRecStop = CheckTime->evaltime; if (TimerObj->lastRecStart > 0 && TimerObj->lastRecStart < TimerObj->lastRecStop) { TimerObj->recDuration += TimerObj->lastRecStop - TimerObj->lastRecStart; TimerObj->lastRecStart = 0; if ((TimerObj->stop - TimerObj->start - TimerObj->recDuration) < EPGSearchConfig.checkMinDuration * 60) TimerObj->ignore = true; } TimerObj->device = -1; if (!TimerObj->conflCheckTime) TimerObj->conflCheckTime = CheckTime; else return; CheckTime->failedTimers.insert(TimerObj); LogFile.Log(3, "conflict found for timer '%s' (%s, channel %s)", TimerObj->timer->File(), DAYDATETIME(TimerObj->start), CHANNELNAME(TimerObj->timer->Channel())); } bool cConflictCheck::TimerInConflict(const cTimer* timer) { if (!failedList) return false; for (cConflictCheckTime* checkTime = failedList->First(); checkTime; checkTime = failedList->Next(checkTime)) { std::set::iterator it; for (it = checkTime->failedTimers.begin(); it != checkTime->failedTimers.end(); ++it) { if (!(*it)->ignore) { std::set::iterator it2; if ((*it)->concurrentTimers) { LOCK_TIMERS_READ; for (it2 = (*it)->concurrentTimers->begin(); it2 != (*it)->concurrentTimers->end(); ++it2) { if ((*it2)->OrigTimer(Timers) == timer) return true; } } } } } return false; } void cConflictCheck::EvaluateConflCheckCmd() { if (strlen(EPGSearchConfig.conflCheckCmd) > 0) { LogFile.Log(2, "evaluating conflict check command '%s'", EPGSearchConfig.conflCheckCmd); for (cConflictCheckTime* ct = failedList->First(); ct; ct = failedList->Next(ct)) { if (ct->ignore) continue; std::set::iterator it; for (it = ct->failedTimers.begin(); it != ct->failedTimers.end(); ++it) if ((*it) && !(*it)->ignore) { std::string result = EPGSearchConfig.conflCheckCmd; LOCK_TIMERS_READ; if (!(*it)->OrigTimer(Timers)) { LogFile.Log(3, "timer has disappeared meanwhile"); continue; } else LogFile.Log(3, "evaluating conflict check command for timer '%s' (%s, channel %s)", (*it)->timer->File(), DAYDATETIME((*it)->start), CHANNELNAME((*it)->timer->Channel())); if ((*it)->Event()) { cVarExpr varExprEvent(result); result = varExprEvent.Evaluate((*it)->Event()); } cVarExpr varExprTimer(result); result = varExprTimer.Evaluate((*it)->timer); cVarExpr varExpr(result); varExpr.Evaluate(); } } } } vdr-plugin-epgsearch-2.4.6/conflictcheck.h000066400000000000000000000267521514271145600205430ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #ifndef __EPGSEARCHCONFLCH_H #define __EPGSEARCHCONFLCH_H #include "epgsearchtools.h" #include #include #include #include #define DO_MULTIPLE_RECORDINGS 1 #define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1 class cConflictCheckTime; class TimerObjSort; // --- cConflictCheckTimerObj -------------------------------------------------------- class cConflictCheckTimerObj : public cTimerObj { const cEvent* event; public: time_t start; time_t stop; int device; int origIndex; int recDuration; time_t lastRecStart; time_t lastRecStop; cConflictCheckTime* conflCheckTime; std::set* concurrentTimers; bool ignore; cConflictCheckTimerObj(cTimer* Timer, time_t Start, time_t Stop, int Device = -1, int OrigIndex = -1); ~cConflictCheckTimerObj(); int Compare(const cListObject &ListObject) const; const cEvent* Event(); const cEvent* SetEventFromSchedule(); int Matches(const cEvent *Event, int *Overlap) const; const cTimer* OrigTimer(const cTimers* timers) const { return timers->GetById(timer->Id(), timer->Remote()); } cTimer* OrigTimer(cTimers* timers) { return timers->GetById(timer->Id(), timer->Remote()); } }; class TimerObjSort { public: bool operator()(const cConflictCheckTimerObj* a, const cConflictCheckTimerObj* b) const { return (a->Compare(*b) < 0); } }; // --- cConflictCheckTime -------------------------------------------------------- class cConflictCheckTime : public cListObject { public: time_t evaltime; std::set startingTimers; std::set stoppingTimers; std::set failedTimers; std::set concurrentRecs; bool ignore; cConflictCheckTime(time_t EvalTime) : evaltime(EvalTime), ignore(false) {} int Compare(const cListObject &ListObject) const { cConflictCheckTime *p = (cConflictCheckTime *)&ListObject; return evaltime - p->evaltime; } }; // --- cConflictCheckDevice -------------------------------------------------------- // This class tries to emulate the behaviour of a DVB device // NOTE: The case device == NULL is only for debugging purposes class cConflictCheckDevice { public: std::set recTimers; cDevice* device; int devicenr; std::vector bondedDevices; cConflictCheckDevice() { device = NULL; devicenr = 0; } int Priority() const { int prio = -1; for (std::set::iterator it = recTimers.begin(); it != recTimers.end(); ++it) prio = std::max(prio, (*it)->timer->Priority()); return prio; }; bool HasInternalCam(void) { if (device) return device->HasInternalCam(); else return false; } int NumProvidedSystems(void) const { if (device) return device->NumProvidedSystems(); else return 1; } int CardIndex(void) const { if (device) return device->CardIndex(); else return devicenr; } bool Receiving() const { return !recTimers.empty(); } bool IsTunedTo(const cChannel* Channel) const { for (std::set::iterator it = recTimers.begin(); it != recTimers.end(); ++it) if ((*it)->timer->Channel()->Source() == Channel->Source() && (*it)->timer->Channel()->Transponder() == Channel->Transponder()) return true; return false; } bool HasDecoder() const { if (device) return device->HasDecoder(); else return (devicenr == 3); } bool HasCi() const { if (device) return device->HasCi(); else return (devicenr == 3); } bool IsPrimaryDevice() const { if (device) return device->IsPrimaryDevice(); else return (devicenr == 3); } bool ProvidesSource(int Source) const { if (device) return device->ProvidesSource(Source); else { // int type = Source & cSource::st_Mask; // if (devicenr == 0) return type == cSource::stCable; // if (devicenr > 0) return type == cSource::stTerr; // return false; return true; } } cCamSlot *CamSlot(void) const { if (device) return device->CamSlot(); else return NULL; } int Ca() const { for (std::set::iterator it = recTimers.begin(); it != recTimers.end(); ++it) return (*it)->timer->Channel()->Ca(); return 0; } bool HasPid(int Pid) const { return true; } bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const { bool result = false; bool hasPriority = Priority < 0 || Priority > this->Priority(); bool needsDetachReceivers = false; if (ProvidesSource(Channel->Source())) { result = hasPriority; if (Priority >= 0 && Receiving()) { if (IsTunedTo(Channel)) { if ((Channel->Vpid() && !HasPid(Channel->Vpid())) || (Channel->Apid(0) && !HasPid(Channel->Apid(0)))) { #ifdef DO_MULTIPLE_RECORDINGS if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) { if (CamSlot()->CanDecrypt(Channel)) result = true; else needsDetachReceivers = true; } else if (!IsPrimaryDevice()) result = true; #ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE else result = Priority >= 0; #endif #endif } else result = !IsPrimaryDevice() || Priority >= 0; } else needsDetachReceivers = true; } } if (result) { if (!BondingOk(Channel)) { // This device is bonded, so we need to check the priorities of the others: for (size_t i = 0; i < bondedDevices.size(); i++) { if (bondedDevices[i]->Priority() >= Priority) { LogFile.Log(3, "Attached receiver to bonded device %i has higher priority.", bondedDevices[i]->CardIndex() + 1); result = false; break; } } if (result) LogFile.Log(3, "Bonding ok, but detaches receiver on device %i.", CardIndex()); else LogFile.Log(3, "Bonding not okay on device %i.", CardIndex()); needsDetachReceivers = Receiving(); } else { LogFile.Log(3, "Bonding ok on device %i.", CardIndex()); } } if (NeedsDetachReceivers) *NeedsDetachReceivers = needsDetachReceivers; return result; } bool BondingOk(const cChannel *Channel) const { if (bondedDevices.empty()) return true; LogFile.Log(3, "Checking for bonding constraints on device %i", CardIndex() + 1); cString BondingParams = GetBondingParams(Channel); for (size_t i = 0; i < bondedDevices.size(); i++) { // bonding not okay, if a bonded devices records on another polarization or freq. band if (!bondedDevices[i]->recTimers.empty()) { if (strcmp(BondingParams, GetBondingParams((*bondedDevices[i]->recTimers.begin())->timer->Channel())) != 0) { LogFile.Log(3, "Bonded device %i has receiver attached. Not safe to use device.", bondedDevices[i]->CardIndex() + 1); return false; } else { LogFile.Log(3, "Bonded device %i has receiver attached but its safe.", bondedDevices[i]->CardIndex() + 1); } } else { LogFile.Log(3, "Bonded device %i has no receivers attached - ok.", bondedDevices[i]->CardIndex() + 1); } } return true; } cString GetBondingParams(const cChannel *Channel) const { //copied from cDVBTuner cDvbTransponderParameters dtp(Channel->Parameters()); if (Setup.DiSEqC) { if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL)) return diseqc->Commands(); } else { bool ToneOff = Channel->Frequency() < Setup.LnbSLOF; bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R'; return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V'); } return ""; } }; // --- cConflictCheck -------------------------------------------------------- class cConflictCheck { cList* timerList; cList* evaltimeList; cList* failedList; std::set pendingTimers; cConflictCheckDevice *devices; int numDevices; time_t maxCheck; std::vector camSlotStatusArray; public: int relevantConflicts; int numConflicts; bool localConflicts; time_t nextRelevantConflictDate; cConflictCheck(); ~cConflictCheck(); void InitDevicesInfo(); void Check(); void BondDevices(const char* bondings); void SetLocal() { localConflicts = true; } bool GetLocal() { return localConflicts; } cList* CreateCurrentTimerList(); cList* CreateEvaluationTimeList(cList*); cList* CreateConflictList(cList*, cList* timerList); cList* CreateRemoteConflictList(cList* timerList, cList* failedList); int GetDevice(cConflictCheckTimerObj* TimerObj, bool *NeedsDetachReceivers); cList* GetFailed() { return failedList; } cList* GetTimers() { return timerList; } void AddConflict(cConflictCheckTimerObj* TimerObj, cConflictCheckTime* Checktime, std::set& pendingTimers); int ProcessCheckTime(cConflictCheckTime* checkTime); bool TimerInConflict(const cTimer*); void EvaluateConflCheckCmd(); eModuleStatus CamSlotModuleStatus(cCamSlot *CamSlot); }; #endif vdr-plugin-epgsearch-2.4.6/conflictcheck_thread.c000066400000000000000000000140641514271145600220560ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include "conflictcheck_thread.h" #include "epgsearchcfg.h" #include "mail.h" #include "epgsearch.h" #define CONFLICTCHECK_NICE 19 cConflictCheckThread *cConflictCheckThread::m_Instance = NULL; time_t cConflictCheckThread::m_cacheNextConflict = 0; int cConflictCheckThread::m_cacheRelevantConflicts = 0; int cConflictCheckThread::m_cacheTotalConflicts = 0; bool cConflictCheckThread::m_runOnce = false; bool cConflictCheckThread::m_forceUpdate = false; extern bool VDR_readyafterStartup; cConflictCheckThread::cConflictCheckThread(cPluginEpgsearch* thePlugin) : cThread("EPGSearch: conflictcheck") { m_plugin = thePlugin; m_Active = false; m_lastUpdate = time(NULL); m_runOnce = false; m_forceUpdate = false; } cConflictCheckThread::~cConflictCheckThread() { if (m_Active) Stop(); } void cConflictCheckThread::Init(cPluginEpgsearch* thePlugin, bool runOnce) { if (EPGSearchConfig.checkTimerConflictsAfterUpdate || EPGSearchConfig.conflictCheckIntervall == 0) { if (!runOnce) return; m_runOnce = true; } if (m_Instance == NULL) { m_Instance = new cConflictCheckThread(thePlugin); m_Instance->Start(); } else if (runOnce) m_forceUpdate = true; // force an update, because thread is already running } void cConflictCheckThread::Exit(void) { if (m_Instance != NULL) { m_Instance->Stop(); DELETENULL(m_Instance); } } void cConflictCheckThread::Stop(void) { m_Active = false; Wait.Signal(); Cancel(6); } void cConflictCheckThread::Action(void) { SetPriority(CONFLICTCHECK_NICE); m_Active = true; // let VDR do its startup if (!m_runOnce) { if (!VDR_readyafterStartup) { LogFile.Log(2, "ConflictCheckThread: waiting for VDR to become ready..."); WaitVDRReady(); } if (EPGSearchConfig.delayThreads > 0) LogFile.Log(2, "ConflictCheckThread: startup delayed %d seconds", EPGSearchConfig.delayThreads); cCondWait::SleepMs(EPGSearchConfig.delayThreads * 1000); } time_t nextUpdate = time(NULL); while (m_Active && Running()) { time_t now = time(NULL); if (now >= nextUpdate || m_forceUpdate) { m_forceUpdate = false; { LOCK_TIMERS_WRITE; Timers->SetExplicitModify(); } LogFile.iSysLog("timer conflict check started"); cConflictCheck conflictCheck; conflictCheck.Check(); time_t nextConflict = 0; if (conflictCheck.relevantConflicts > 0) { cString msgfmt = ""; if (conflictCheck.relevantConflicts == 1) msgfmt = cString::sprintf(tr("timer conflict at %s! Show it?"), *DateTime(conflictCheck.nextRelevantConflictDate)); else msgfmt = cString::sprintf(tr("%d timer conflicts! First at %s. Show them?"), conflictCheck.relevantConflicts, *DateTime(conflictCheck.nextRelevantConflictDate)); bool doMessage = EPGSearchConfig.noConflMsgWhileReplay == 0 || !cDevice::PrimaryDevice()->Replaying() || conflictCheck.nextRelevantConflictDate - now < 2 * 60 * 60; if (doMessage && SendMsg(msgfmt, true, 7, mtWarning) == kOk) { m_plugin->showConflicts = true; cRemote::CallPlugin("epgsearch"); } if (EPGSearchConfig.sendMailOnConflicts) { cMailConflictNotifier mailNotifier; mailNotifier.SendConflictNotifications(conflictCheck); } conflictCheck.EvaluateConflCheckCmd(); } // store for external access cConflictCheckThread::m_cacheNextConflict = conflictCheck.nextRelevantConflictDate; cConflictCheckThread::m_cacheRelevantConflicts = conflictCheck.relevantConflicts; cConflictCheckThread::m_cacheTotalConflicts = conflictCheck.numConflicts; LogFile.iSysLog("timer conflict check finished"); m_lastUpdate = time(NULL); int Intervall = EPGSearchConfig.conflictCheckIntervall; if (nextConflict > 0 && EPGSearchConfig.conflictCheckWithinLimit > 0 && nextConflict - time(NULL) < EPGSearchConfig.conflictCheckWithinLimit * 60) Intervall = EPGSearchConfig.conflictCheckIntervall2; nextUpdate = long(m_lastUpdate / 60) * 60 + (Intervall * 60); } if (m_Active && Running()) Wait.Wait(2000); // to avoid high system load if time%30==0 ????????????????????? // no waiting in the while loop if m_runOnce is true while (Running() && m_Active && time(NULL) % 30 != 0 && !m_runOnce) // sync heart beat to a multiple of 5secs Wait.Wait(1000); }; m_Active = false; // always false at this point LogFile.iSysLog("Leaving conflict check thread"); } vdr-plugin-epgsearch-2.4.6/conflictcheck_thread.h000066400000000000000000000034031514271145600220560ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #ifndef VDR_CONFLICTCHECK_THREAD_H #define VDR_CONFLICTCHECK_THREAD_H #include #include "conflictcheck.h" #include "epgsearch.h" class cConflictCheckThread: public cThread { private: bool m_Active; time_t m_lastUpdate; cPluginEpgsearch* m_plugin; static bool m_runOnce; static bool m_forceUpdate; cCondWait Wait; protected: virtual void Action(void); void Stop(void); public: static cConflictCheckThread *m_Instance; static time_t m_cacheNextConflict; static int m_cacheRelevantConflicts; static int m_cacheTotalConflicts; cConflictCheckThread(cPluginEpgsearch* thePlugin); virtual ~cConflictCheckThread(); static void Init(cPluginEpgsearch* thePlugin, bool runOnce = false); static void Exit(void); }; #endif vdr-plugin-epgsearch-2.4.6/conflictcheckonly.c000066400000000000000000000066341514271145600214350ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include #include #include "services.h" #include "mainmenushortcut.h" static const char VERSION[] = "2.4.5"; static const char DESCRIPTION[] = trNOOP("Direct access to epgsearch's conflict check menu"); static const char MAINMENUENTRY[] = trNOOP("Timer conflicts"); static const char SETUPTEXT[] = trNOOP("Conflict info in main menu"); cString DateTime(time_t t) { char buffer[32]; if (t == 0) { time(&t); } struct tm tm_r; tm *tm = localtime_r(&t, &tm_r); snprintf(buffer, sizeof(buffer), "%02d.%02d. %02d:%02d", tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min); return buffer; } class cPluginConflictcheckonly: public cMainMenuShortcut { private: char *_menuText; public: cPluginConflictcheckonly(); ~cPluginConflictcheckonly(); virtual const char *Version() { return VERSION; } virtual const char *Description() { return I18nTranslate(DESCRIPTION, I18nEpgsearch); } virtual bool Initialize(); virtual cOsdObject *MainMenuAction() { return GetEpgSearchMenu("Epgsearch-conflictmenu-v1.0"); } protected: virtual const char *SetupText() { return I18nTranslate(SETUPTEXT, I18nEpgsearch); } virtual const char *MainMenuText(void); }; cPluginConflictcheckonly::cPluginConflictcheckonly(): _menuText(NULL) { } cPluginConflictcheckonly::~cPluginConflictcheckonly() { free(_menuText); } const char *cPluginConflictcheckonly::MainMenuText(void) { const char *menuText = I18nTranslate(MAINMENUENTRY, I18nEpgsearch); cPlugin *epgSearchPlugin = cPluginManager::GetPlugin("epgsearch"); if (epgSearchPlugin) { Epgsearch_lastconflictinfo_v1_0 *serviceData = new Epgsearch_lastconflictinfo_v1_0; if (epgSearchPlugin->Service("Epgsearch-lastconflictinfo-v1.0", serviceData)) { if (serviceData->relevantConflicts > 0) { free(_menuText); if (asprintf(&_menuText, "%s (%d, %s: %s)", menuText, serviceData->relevantConflicts, I18nTranslate(trNOOP("next"), I18nEpgsearch), *DateTime(serviceData->nextConflict))) menuText = _menuText; } } delete serviceData; } return menuText; } bool cPluginConflictcheckonly::Initialize(void) { return cMainMenuShortcut::Initialize(); } VDRPLUGINCREATOR(cPluginConflictcheckonly); // Don't touch this! vdr-plugin-epgsearch-2.4.6/createcats.c000066400000000000000000000270151514271145600200460ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include #include #include #include #define MINAPPEARANCE 100 // the minimum appearance of a category #define MAXVALUES 60 // the maximum of values for a category #define MAXNAMELENGTH 30 // the maximum length of a category name or value // some helping stuff copied from VDR sources #define KILOBYTE(n) ((n) * 1024) #define MAXPARSEBUFFER KILOBYTE(10) #ifdef __FreeBSD__ #ifdef isnumber #undef isnumber #endif #endif bool isnumber(const char *s) { if (!*s) return false; while (*s) { if (!isdigit(*s)) return false; s++; } return true; } // --- cReadLine ------------------------------------------------------------- class cReadLine { private: char buffer[MAXPARSEBUFFER]; public: cReadLine() { buffer[0] = 0; } char *Read(FILE *f); }; char *cReadLine::Read(FILE *f) { if (fgets(buffer, sizeof(buffer), f) != 0) { int l = strlen(buffer) - 1; if (l >= 0 && buffer[l] == '\n') buffer[l] = 0; return buffer; } return NULL; } char *skipspace(const char *s) { while (*s && isspace(*s)) s++; return (char *)s; } int comparevalue(const void *arg1, const void *arg2) { char* value1 = *(char**) arg1; char* value2 = *(char**) arg2; return strcmp(value1, value2); } // --- cCat ------------------------------------------------------------- class cCat { public: int appeared; char name[MAXPARSEBUFFER]; int numvalues; char** values; cCat(char* n) : appeared(0), numvalues(0), values(NULL) { strcpy(name, n); } void addvalue(char* value) { if (valueexists(value)) return; char* newvalue = (char*) malloc(sizeof(char) * (strlen(value) + 1)); strcpy(newvalue, value); char **tmp = (char**) realloc(values, sizeof(char*) * (numvalues + 1)); if (tmp) { values = tmp; values[numvalues++] = newvalue; } else { free(newvalue); } } bool valueexists(char* value) { for (int i = 0; i < numvalues; i++) if (strcmp(values[i], value) == 0) return true; return false; } void sort() { qsort(values, numvalues, sizeof(char*), comparevalue); } }; int comparecat(const void *arg1, const void *arg2) { cCat* cat1 = *(cCat**) arg1; cCat* cat2 = *(cCat**) arg2; if (cat1->appeared == cat2->appeared) return 0; if (cat1->appeared < cat2->appeared) return 1; else return -1; } // --- cCats ------------------------------------------------------------- class cCats { private: int numcats; cCat** cats; public: cCats(): numcats(0), cats(NULL) {} int num() { return numcats; } cCat* add(char* name) { cCat* newCat = new cCat(name); cCat **tmp = (cCat**) realloc(cats, sizeof(cCat*) * (numcats + 1)); if (tmp) { cats = tmp; cats[numcats++] = newCat; return newCat; } else { delete newCat; return NULL; } } cCat* get(int i) { if (i >= 0 && i < numcats) return cats[i]; else return NULL; } cCat* exists(char* name) { for (int i = 0; i < numcats; i++) if (strcmp(cats[i]->name, name) == 0) return cats[i]; return NULL; } void sort() { for (int i = 0; i < numcats; i++) cats[i]->sort(); qsort(cats, numcats, sizeof(cCat*), comparecat); } }; int main(int argc, char *argv[]) { FILE* f = NULL; cCats catlist; unsigned int minappearance = MINAPPEARANCE; unsigned int maxvalues = MAXVALUES; unsigned int maxlength = MAXNAMELENGTH; static const struct option long_options[] = { { "minappearance", required_argument, NULL, 'm' }, { "maxvalues", required_argument, NULL, 'v' }, { "maxlength", required_argument, NULL, 'l' }, { "help", no_argument, NULL, 'h' }, { NULL, no_argument, NULL, 0 } }; int c; while ((c = getopt_long(argc, argv, "m:v:l:h", long_options, NULL)) != -1) { switch (c) { case 'm': if (isnumber(optarg)) { minappearance = atoi(optarg); break; } fprintf(stderr, "invalid parameter minappearance: %s\n", optarg); return 2; break; case 'v': if (isnumber(optarg)) { maxvalues = atoi(optarg); break; } fprintf(stderr, "invalid parameter maxvalues: %s\n", optarg); return 2; break; case 'l': if (isnumber(optarg)) { maxlength = atoi(optarg); break; } fprintf(stderr, "invalid parameter maxlength: %s\n", optarg); return 2; break; case 'h': printf("usage: createcats [OPTIONS] /path_to/epg.data\n\n"); printf("-m N, --minappearance=N the minimum number of occurrences for a category\n"); printf(" to be accepted\n"); printf("-v N, --maxvalues=N values of a category are omitted if they exceed\n"); printf(" this number\n"); printf("-l N, --maxlength=N the maximum length of a text to be accepted\n"); printf(" as a category value\n"); printf("-h, --help this help\n\n"); return 0; default: break; } } if (argc < 2) { fprintf(stderr, "ERROR: please pass your epg.data\nusage: createcats epg.data\n"); return 1; } f = fopen(argv[argc - 1], "r"); if (f == NULL) { fprintf(stderr, "ERROR: could not open: %s\n", argv[1]); return 1; } char *s; cReadLine ReadLine; while ((s = ReadLine.Read(f)) != NULL) { if (*s == 'D') { s = strchr(s, '|'); // jump to possibly first category if (!s) continue; s++; char *pstrSearchToken; char *pstrSearch = strdup(s); pstrSearchToken = strtok(pstrSearch, "|"); while (pstrSearchToken) { // must have a ':' char* szPos = NULL; if ((szPos = strchr(pstrSearchToken, ':')) == NULL) { pstrSearchToken = strtok(NULL, "|"); continue; } char catname[MAXPARSEBUFFER] = ""; char catvalue[MAXPARSEBUFFER] = ""; strncpy(catname, pstrSearchToken, szPos - pstrSearchToken); catname[szPos - pstrSearchToken] = 0; strcpy(catvalue, skipspace(szPos + 1)); cCat* cat = catlist.exists(catname); if (!cat && strlen(catname) < maxlength) // accept only names up to 30 chars cat = catlist.add(catname); if (cat) { cat->appeared++; if (strlen(catvalue) < maxlength) // accept only values up to 30 chars cat->addvalue(catvalue); } pstrSearchToken = strtok(NULL, "|"); } free(pstrSearch); } } fclose(f); catlist.sort(); f = fopen("epgsearchcats.conf", "w"); if (f == NULL) { fprintf(stderr, "ERROR: could not open outputfile\n"); return 1; } fprintf(f, "# -----------------------------------------------------------------------------\n"); fprintf(f, "# This is just a template based on your current epg.data. Please edit!\n"); fprintf(f, "# Perhaps a category or its value list should be removed. Also the\n"); fprintf(f, "# 'name in menu' should be adjusted to your language.\n"); fprintf(f, "# The order of items determines the order listed in epgsearch. It does not\n"); fprintf(f, "# depend on the ID, which is used by epgsearch.\n"); fprintf(f, "# Format:\n"); fprintf(f, "# ID|category name|name in menu|values separated by ',' (option)|searchmode(option)\n"); fprintf(f, "# - 'ID' should be a unique positive integer\n"); fprintf(f, "# (changing the id later on will force you to reedit your search timers!)\n"); fprintf(f, "# - 'category name' is the name in your epg.data\n"); fprintf(f, "# - 'name in menu' is the name displayed in epgsearch.\n"); fprintf(f, "# - 'values' is an optional list of possible values\n"); fprintf(f, "# if you omit the list, the entry turns to an edit field in epgsearch,\n"); fprintf(f, "# else it's a list of items to select from\n"); fprintf(f, "# - 'searchmode' is an optional parameter specifying the mode of search:\n"); fprintf(f, "# text comparison:\n"); fprintf(f, "# 0 - the whole term must appear as substring\n"); fprintf(f, "# 1 - all single words (delimiters are ',', ';', '|' or '~')\n"); fprintf(f, "# must exist as substrings. This is the default search mode.\n"); fprintf(f, "# 2 - at least one word (delimiters are ',', ';', '|' or '~')\n"); fprintf(f, "# must exist as substring.\n"); fprintf(f, "# 3 - matches exactly\n"); fprintf(f, "# 4 - regular expression\n"); fprintf(f, "# numerical comparison:\n"); fprintf(f, "# 10 - less\n"); fprintf(f, "# 11 - less or equal\n"); fprintf(f, "# 12 - greater\n"); fprintf(f, "# 13 - greater or equal\n"); fprintf(f, "# 14 - equal\n"); fprintf(f, "# 15 - not equal\n"); fprintf(f, "# -----------------------------------------------------------------------------\n\n"); int id = 1; for (int i = 0; i < catlist.num(); i++) { cCat* cat = catlist.get(i); if (cat->appeared > (int)minappearance && cat->numvalues > 1) { // accept only category, that have at least 2 values and appear more than MINAPPEARANCE timers fprintf(f, "# '%s' found %d times with %d different values %s\n", cat->name, cat->appeared, cat->numvalues, cat->numvalues >= (int)maxvalues ? "(values omitted, too much)" : ""); fprintf(f, "%d|%s|%s|", id++, cat->name, cat->name); for (int j = 0; cat->numvalues < (int)maxvalues && j < cat->numvalues; j++) fprintf(f, "%s%s", cat->values[j], (j == cat->numvalues - 1 ? "" : ",")); fprintf(f, "|1\n\n"); } } fclose(f); printf("epgsearchcats.conf created!\n"); return 0; } vdr-plugin-epgsearch-2.4.6/distance.c000066400000000000000000000103751514271145600175230ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ //--------------------------------------------------- // Levenshtein Distance // by Michael Gilleland, Merriam Park Software // // source: // http://www.merriampark.com/ld.htm#CPLUSPLUS // //--------------------------------------------------- #include "distance.h" #include #ifdef __FreeBSD__ #include #else #include #endif #include //**************************** // Get minimum of three values //**************************** int Distance::Minimum(int a, int b, int c) { int mi; mi = a; if (b < mi) { mi = b; } if (c < mi) { mi = c; } return mi; } //************************************************** // Get a pointer to the specified cell of the matrix //************************************************** int *Distance::GetCellPointer(int *pOrigin, int col, int row, int nCols) { return pOrigin + col + (row * (nCols + 1)); } //***************************************************** // Get the contents of the specified cell in the matrix //***************************************************** int Distance::GetAt(int *pOrigin, int col, int row, int nCols) { int *pCell; pCell = GetCellPointer(pOrigin, col, row, nCols); return *pCell; } //******************************************************* // Fill the specified cell in the matrix with the value x //******************************************************* void Distance::PutAt(int *pOrigin, int col, int row, int nCols, int x) { int *pCell; pCell = GetCellPointer(pOrigin, col, row, nCols); *pCell = x; } //***************************** // Compute Levenshtein distance //***************************** int Distance::LD(char const *s, char const *t, int maxLength) { int *d; // pointer to matrix int n; // length of s int m; // length of t int i; // iterates through s int j; // iterates through t char s_i; // ith character of s char t_j; // jth character of t int cost; // cost int result; // result int cell; // contents of target cell int above; // contents of cell immediately above int left; // contents of cell immediately to left int diag; // contents of cell immediately above and to left int sz; // number of cells in matrix // Step 1 n = std::min((int)strlen(s), maxLength); m = std::min((int)strlen(t), maxLength); if (n == 0) { return m; } if (m == 0) { return n; } sz = (n + 1) * (m + 1) * sizeof(int); d = (int *) malloc(sz); // Step 2 for (i = 0; i <= n; i++) { PutAt(d, i, 0, n, i); } for (j = 0; j <= m; j++) { PutAt(d, 0, j, n, j); } // Step 3 for (i = 1; i <= n; i++) { s_i = s[i - 1]; // Step 4 for (j = 1; j <= m; j++) { t_j = t[j - 1]; // Step 5 if (s_i == t_j) { cost = 0; } else { cost = 1; } // Step 6 above = GetAt(d, i - 1, j, n); left = GetAt(d, i, j - 1, n); diag = GetAt(d, i - 1, j - 1, n); cell = Minimum(above + 1, left + 1, diag + cost); PutAt(d, i, j, n, cell); } } // Step 7 result = GetAt(d, n, m, n); free(d); return result; } vdr-plugin-epgsearch-2.4.6/distance.h000066400000000000000000000031321514271145600175210ustar00rootroot00000000000000/* -*- c++ -*- Copyright (C) 2004-2013 Christian Wieninger 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 Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ //--------------------------------------------------- // Levenshtein Distance // by Michael Gilleland, Merriam Park Software // // source: // http://www.merriampark.com/ld.htm#CPLUSPLUS // //--------------------------------------------------- #ifndef _DISTANCE_INC_ #define _DISTANCE_INC_ class Distance { public: int LD(char const *s, char const *t, int maxLength); private: int Minimum(int a, int b, int c); int *GetCellPointer(int *pOrigin, int col, int row, int nCols); int GetAt(int *pOrigin, int col, int row, int nCols); void PutAt(int *pOrigin, int col, int row, int nCols, int x); }; #endif vdr-plugin-epgsearch-2.4.6/doc-src/000077500000000000000000000000001514271145600171115ustar00rootroot00000000000000vdr-plugin-epgsearch-2.4.6/doc-src/de/000077500000000000000000000000001514271145600175015ustar00rootroot00000000000000vdr-plugin-epgsearch-2.4.6/doc-src/de/createcats.1.txt000066400000000000000000000107421514271145600225230ustar00rootroot00000000000000=encoding utf8 =for syntax specification: https://perldoc.perl.org/perlpod =head1 NAME F – Generierung von F anhand der EPG-Daten des VDR =head1 SYNTAX B [OPTIONEN] F =head1 BESCHREIBUNG Dieses Tool wird mit EPGSearch ausgeliefert und sollte nach seiner Kompilierung im Verzeichnis des Plugins vorliegen. Es hilft beim Erstellen der Datei F aus den EPG-Daten des VDR, falls die Beispieldateien im Verzeichnis F nicht den Anforderungen entsprechen sollten. Der Aufruf erfolgt üblicherweise mit: =over 4 createcats /Pfad_zu/epg.data =back Das Tool verwendet die VDR-Datei F als Argument und durchsucht sie nach geeigneten EPG-Informationen. Diese Informationen bestehen jeweils aus einem Tupel mit einem Kategorienamen und einem entsprechenden Wert, beispielsweise C, am Zeilenanfang. In F wird ein Zeilenumbruch mittels C<|> kodiert. Erzeugt wird die Datei F, die in das Konfigurationsverzeichnis des Plugins kopiert werden sollte. Vor der produktiven Nutzung sind wohl einige Anpassungen erforderlich, da selten alle Einträge der Datei als erweiterte EPG-Informationen geeignet sein dürften. =head1 OPTIONEN Die vollständige Liste von Argumenten umfasst: =over 4 =item B<−h>, B<−−help> Ausgabe eines knappen Hilfetextes. =item B<−l> I, B<−−maxlength>=I Maximale Länge eines Textes, um ihn als Kategoriewert zu akzeptieren. Wenn die Länge eines Werts größer als I ist, wird dieser Wert nicht in die Werteliste aufgenommen. =item B<−m> I, B<−−minappearance>=I Die Mindesthäufigkeit einer Kategorie, um akzeptiert zu werden. Das Tool ermittelt, wie oft eine Kategorie in den EPG-Daten verwendet wird. Ist dieser Wert kleiner als I, wird entsprechende Kategorie nicht in die Datei F übernommen. =item B<−v> I, B<−−maxvalues>=I Die Werte einer Kategorie bleiben unberücksichtigt, wenn sie die festgelegte Anzahl überschreiten. Ist die Anzahl unterschiedlicher Werte für eine Kategorie größer als I, wird für die entsprechende Kategorie keine Werteliste erstellt. Die Kategorie wird stattdessen durch einen String repräsentiert, der mit beliebigem Text gefüllt werden kann. Andernfalls wird die Kategorie durch ein Menüelement zur Auswahl von Kategoriewerten aus der Liste abgebildet. =back B Die besten Ergebnisse erzielt man, wenn die aktuellen EPG-Daten möglichst umfangreich sind. Beim Herunterladen von EPG-Daten von einer externen Quelle sollte vor Ausführung von F eine Aktualisierung erfolgen. =head1 AUTOREN (Man-Pages) Ursprünglich erstellt von Mike Constabel . Überarbeitet und an die aktuellen Features von EPGSearch adaptiert durch die derzeitigen Maintainer. =head1 PROJEKTSEITE Das Plugin wird als Projekt auf GitHub geführt: L =head1 FEHLER MELDEN Fehlerberichte sowie Feature-Anfragen können über den Bugtracker des Projekts eingespeist werden: L =head1 COPYRIGHT und LIZENZ Copyright © 2004-2010 Christian Wieninger Copyright © 2011-2025 TomJoad (VDR-Portal) et al. Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren, entweder gemäß Version 2 der Lizenz oder (nach Ihrer Option) jeder späteren Version. Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, dass es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der GNU General Public License. Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm erhalten haben. Falls nicht, schreiben Sie an die Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Oder rufen Sie in Ihrem Browser L auf. Der ursprüngliche Autor kann über L erreicht werden. Die aktuellen Maintainer können über die Projektseite auf GitHub (siehe oben) erreicht werden. Der MD5-Code ist abgeleitet aus dem Message-Digest-Algorithmus MD5 von RSA Data Security, Inc. =head1 SIEHE AUCH B(1), B(4), B(5) vdr-plugin-epgsearch-2.4.6/doc-src/de/epgsearch.1.txt000066400000000000000000001505061514271145600223510ustar00rootroot00000000000000=encoding utf8 =for syntax specification: https://perldoc.perl.org/perlpod =head1 NAME F – Suchtimer und Ersatz für das Standard-Programm-Menü des VDR =head1 BESCHREIBUNG EPGSearch kann als Ersatz für das übliche Programm-Menü des VDR verwendet werden. Es sieht zwar genauso aus, erweitert es aber um einige zusätzliche Funktionen: =over 2 =item * Im Hauptmenü kann die normale Programmübersicht des VDR durch einen erweiterten Programmführer ersetzt werden. =item * Der Programmführer erlaubt den Wechsel zwischen den nächsten und den gerade laufenden Sendungen. =item * Neben den zeitnahen Sendungen erlaubt der Programmführer die Anzeige von Sendungen für bis zu vier benutzerdefinierte Zeiten sowie eine Übersicht selbstdefinierter Favoriten. =item * Verschiebung der im Programmführer angezeigten Zeit per Tastendruck (bspw. um 30 Minuten). =item * Fortschrittsanzeige in der Programmübersicht für die laufenden und die nächsten Sendungen. =item * Integrierte Befehle, um etwa Wiederholungen von Sendungen anzuzeigen oder Suchtimer für diese anzulegen. Zudem lassen sich eigene Befehle einbinden, um beispielsweise VDRAdmin-Autotimer zu erstellen. =item * Suche im EPG: Anlegen von wiederverwendbaren Abfragen, die auch als Suchtimer verwendet werden können. =item * Suchtimer: Hintergrundsuche nach zutreffenden Sendungen und Anlegen entsprechender Timer (ähnlich der Autotimer von VDRAdmin) oder Benachrichtigung über Sendungen per OSD. =item * Unterstützung von Inhaltskennungen und erweiterten EPG-Informationen in Suchtimern. =item * Vermeidung von mehrfachen Aufnahmen der gleichen Sendung: =over 2 =item – Timer-Vorschau =item – Erkennung unbrauchbarer Aufnahmen =item – Unscharfer Vergleich von Sendungen =back Z<> =item * Erweiterung des Timer-Editieren-Menüs um Verzeichnisse, flexible Auswahl von Wochentagen und Ergänzung von Untertiteln ("Episodennamen"). =item * Hintergrundüberwachung auf Timer-Konflikte in Verbindung mit einem Timer-Konfliktmanager. =item * Benachrichtigung über erkannte Timer-Konflikte per OSD. =item * Detaillierte Anzeige der Timer-Konflikte und Unterstützung bei deren Auflösung. =item * E-Mail-Benachrichtigungen über Suchtimer-Aktualisierungen und Timer-Konflikte. =back Teile der Quelltexte basieren auf dem F-Patch von Gerhard Steiner, der die Erlaubnis gab, diese zu verwenden. Danke für seine Arbeit! =head1 OPTIONEN =over 4 =item B<−f> I, B<−−svdrpsendcmd>=I Pfad zu F für externe SVDRP-Kommunikation. Standard ist interne Kommunikation, weshalb diese Option normalerweise nicht erforderlich ist. =item B<−c> I, B<−−config>=I Festlegung eines eigenen Konfigurationsverzeichnisses für alle EPGSearch-Dateien (Standard ist FPlugin-KonfigurationsverzeichnisE/epgsearch>). =item B<−l> I, B<−−logfile>=I Festlegung eines abweichenden Dateipfades für das Log-File von EPGSearch. Standard ist F im Konfigurationsverzeichnis von EPGSearch. =item -B I, B<−−verbose>=I Detailgrad für das Log-File. Der Wert 0 bedeutet kein Logging. Weitere Werte sind 1 (allgemeine Meldungen), 2 (detaillierte Meldungen), 3 (für Debug-Zwecke). =item B<−r>, B<−−reloadmenuconf> Bewirkt ein Neuladen der Datei F bei jedem Plugin-Aufruf im OSD. Dies kann für das Testen eines selbst angepassten Menü-Layouts praktisch sein. =item B<−m> I, B<−−mailcmd>=I Externes Kommando für den E-Mail-Versand. Als Standard wird F benutzt. Wenn ein abweichendes Kommando oder Skript verwendet werden soll, muss sichergestellt sein, dass das gleiche Parameter-Interface verwendet wird wie bei F. =back =head1 INHALT =over 3 =item 1. Beschreibung =over 4 =item 1.1 Menü C =item 1.2 Menü C =over 6 =item 1.2.1 Menü C =item 1.2.2 Menü C =back =item 1.3 Erweitertes C und C =item 1.4 Menü C =over 6 =item 1.4.1 Allgemein =item 1.4.2 EPG-Menüs =item 1.4.3 Benutzerdefinierte EPG-Zeiten =item 1.4.4 Timer-Programmierung =item 1.4.5 Suche und Suchtimer =item 1.4.6 Timer-Konfliktprüfung =item 1.4.7 E-Mail-Benachrichtigung =back =back =item 2. Suchtimer =over 4 =item 2.1 Wiederholungen vermeiden im Detail =item 2.2 Wie funktioniert der Vergleichstest zweier Sendungen? =item 2.3 Wie und wo wird der Vergleichstest eingesetzt? =over 6 =item 2.3.1 Wie verwenden? =item 2.3.2 Wenn es nicht richtig funktioniert =back =back =item 3. Verwendung der Suche durch andere Plugins oder Skripte =item 4. Verwendung erweiterter EPG-Informationen =item 5. Ersetzen des Standard-Programm-Menüs =item 6. Add-ons =back =head1 1. Beschreibung Auf den ersten Blick sieht EPGSearch wie das Programm-Menü des VDR aus. Ein Tastendruck auf C<0> schaltet die Farbtasten um, so dass weitere Funktionen erreicht werden können (die vorgegebene Zuweisung kann per Setup angepasst werden). =head2 1.1 Menü Befehle Dieses Menü zeigt Befehle an, die auf den ausgewählten Menüeintrag angewandt werden können. Es gibt acht vorgegebene Befehle: =over 4 =item B Zeigt weitere Sendungen mit gleichem Titel. =item B Legt einen Timer zum Aufnehmen der Sendung an. =item B Schaltet zum Kanal der Sendung um. =item B Schaltet zum Suchmenü und erzeugt eine Suche mit dem Namen der aktuellen Sendung als Suchbegriff (um eine manuelle Erfassung zu vermeiden). =item B Durchsucht die Aufnahmen nach einer Sendung gleichen Namens. =item B Übernimmt die ausgewählte Sendung in die Datei F und weist EPGSearch an, diese Sendung nicht aufzunehmen, falls der zugehörige Suchtimer auf C gestellt ist. Ein bereits erzeugter Timer wird beim nächsten Suchtimer-Update automatisch gelöscht. =item B Zum Bearbeiten der Umschaltliste. Wenn eine Sendung in der Umschaltliste enthalten ist, wird kurz vor Beginn eine Ankündigung eingeblendet und dann umgeschaltet. Um die gesamte Umschaltliste einzusehen, bitte C » C » C aufrufen. =item B Eine Ausschlussliste wird verwendet, um bestimmte Sendungen bei der Verwendung von Suchtimern zu ignorieren. Ein Suchtimer kann beliebige Ausschlusslisten verwenden. =back Man kann eigene Befehle hinzufügen, indem man die Datei F im Konfigurationsverzeichnis von EPGSearch editiert. Eine Beispieldatei mit exemplarischen Skripten liegt dem Plugin bei (siehe Unterverzeichnis F; sie stammt von C – Dank an die Autoren). Das Format der Datei ist identisch zur Datei F oder F des VDR; siehe auch B(5). Wenn ein Befehl ausgeführt wird, werden folgende Parameter übergeben: =over 4 =item B<$1> Titel des Programmeintrags. =item B<$2> Startzeitpunkt des Programmeintrags in I-Notation (Sekunden seit dem 01.01.1970, 00:00 UTC), wie auch beim Shutdown-Skript. =item B<$3> Endzeitpunkt des Programmeintrags in I-Notation (Sekunden seit dem 01.01.1970, 00:00 UTC). =item B<$4> Kanalnummer des Programmeintrags. =item B<$5> Vollständiger Kanalname des Programmeintrags. =item B<$6> Untertitel des Programmeintrags oder ein leerer String, falls nicht vorhanden. =back Zum Ausführen eines Befehls aus dem Hauptmenü ohne Öffnen des Befehlsmenüs genügt es, die zugehörige Nummer des Befehls zu drücken. =head2 1.2 Menü C Hier kann man eine Suche im EPG erzeugen, editieren, löschen und ausführen. Bedienung und Verhalten ist ähnlich wie im Timer-Menü des VDR. =head3 1.2.1 Menü C Das Meiste in diesem Menü ist selbsterklärend. Deshalb nur einige Anmerkungen zu ausgewählten Elementen: =over 4 =item B Der Suchbegriff. Bei der Suche nach mehreren Worten, sind diese mit Leerzeichen zu trennen. Lässt man den Suchbegriff leer (in Verbindung mit Suchmodus C), wird alles akzeptiert. Das kann praktisch sein, um etwa alles zu suchen, was zu einer bestimmten Zeit auf einem bestimmten Sender kommt. Mit Taste C kann man auch eine Vorlage für eine Suche übernehmen. Falls eine Vorlage als Standard definiert wurde, wird bei einer neuen Suche automatisch der Inhalt der Standard-Vorlage verwendet. B Die unscharfe Suche ist auf 32 Zeichen begrenzt! =item B Für die Suche stehen mehrere Varianten zur Auswahl: =over 4 =item I Sucht nach dem entsprechenden Ausdruck innerhalb eines EPG-Eintrags. =item I Erfordert, dass jedes Wort im EPG-Eintrag vorkommt. =item I Erfordert, dass zumindest eines der Worte im EPG-Eintrag enthalten ist. =item I Vergleicht den gesamten Suchbegriff mit dem EPG-Eintrag. Dies ist vor allem praktisch bei kurzen Titeln, wie etwa "Alf". =item I Erlaubt die Angabe eines regulären Ausdrucks zur Suche. Umschließende Schrägstriche (C) sind nicht erforderlich. Wenn im F des Plugins (was standardmäßig der Fall ist) die automatische Konfiguration aktiviert ist (kein C<#> vor C), werden bevorzugt Perl-kompatible reguläre Ausdrücke (PCRE) verwendet; dies erfordert, dass F oder F installiert wurden. Alternativ werden POSIX-konforme, erweiterte reguläre Ausdrücke genutzt, die auch näherungsweise Vergleiche erlauben; dies erfordert die Bibliothek F. Ohne automatische Konfiguration muss im F des Plugins lediglich C<#REGEXLIB = pcre2> in C geändert, die gewünschte Bibliothek für reguläre Ausdrücke eingesetzt und das Plugin neu kompiliert werden. Die benötigte Bibliothek muss vorher gegebenenfalls mit Hilfe der Paketverwaltung des Systems noch installiert. =item I Vergleicht den Suchbegriff anhand des Levenshtein-Distanz-Algorithmus. Der Wert C legt fest, wie groß die Abweichung sein darf. =back Eine Beschreibung des Suchprozesses findet sich in B(4). =item B Einige Provider liefern Kennungen für den Inhalt einer Sendung, wie etwa C, C und dergleichen. Solche Kennungen können ab VDR 1.7.11 zur Suche ausgewählt werden. Dabei ist auch eine Mehrfachauswahl möglich, wobei die Art der Übereinstimmung für jede Suche individuell einstellbar ist. Standardmäßig müssen alle ausgewählten Kennungen übereinstimmen (UND-Verknüpfung); doch kann auch festgelegt werden, dass schon eine einzige Übereinstimmung genügt (ODER-Verknüpfung). Da ähnliche Inhaltskennungen in Gruppen gegliedert sind, lässt sich alternativ festlegen, dass mindestens eine Übereinstimmung in jeder genutzten Gruppe vorliegen muss (ODER-Verknüpfung innerhalb einer Gruppe, UND-Verknüpfung zwischen den Gruppen). Besondere Merkmale von Sendungen (wie etwa Originalsprache oder schwarzweiß) werden zudem in einer von diesem Ähnlichkeitsschema abweichenden Gruppe von Inhaltskennungen geführt. Für diese Gruppe lässt sich getrennt festlegen, ob standardmäßig alle gewählten Merkmale übereinstimmen müssen (UND-Verknüpfung) oder nur eines davon (ODER-Verknüpfung). =item B Diese Option ist nur verfügbar, sofern erweiterte EPG-Informationen konfiguriert wurden; siehe C weiter unten. =item B Standardmäßig müssen alle gewählten Kategorien im EPG enthalten sein und den angegebenen Werten entsprechen. Wenn C gewählt ist, verhindert dies, dass eine Sendung aus dem Suchergebnis ausgeschlossen wird, nur weil eine gewählte Kategorie im EPG nicht vorhanden ist. Alternativ kann per C festlegt werden, dass schon die Übereinstimmung einer einzigen Kategorie ausreichend soll. B Ohne weiter eingrenzende Suchkriterien können die beiden alternativen Einstellungen zu einer Flut von Timern führen. =item B Sucht nur im angegebenen Kanalbereich, der hinterlegten Kanalgruppe oder in frei empfangbaren Sendern (FTA-Sender). Kanalgruppen, wie etwa Sportsender oder Pay-TV-Kanäle, können anhand eines Untermenüs erstellt und verwaltet werden, das mittels der Taste C aufgerufen wird. B Nach einer Änderung der Kanalreihenfolge sollten die Bereichseinstellungen von Suchtimern unbedingt kontrolliert werden! =item B Neben einzelnen Wochentagen kann auch eine benutzerdefinierte Auswahl getroffen werden, um etwa nur montags und freitags zu suchen. Die benutzerdefinierte Auswahl findet sich am Ende der Liste der Wochentage (C, C, ..., C, benutzerdefiniert) und kann über die Taste C bearbeitet werden. =item B Ausschlusslisten können benutzt werden, um unerwünschte Sendungen auszuschließen. Hier können entweder nur globale bzw. eine, mehrere oder alle Ausschlusslisten selektiert werden. Falls ein Suchergebnis auch in einer der gewählten Ausschlusslisten erscheint, wird es verworfen. =item B Nur verfügbar, wenn im Setup aktiviert. Mit dieser Option kann eine Suche zur Verwendung im Favoritenmenü markiert werden. Dieses Menü listet alle Suchergebnisse von Suchen mit dieser Option. =item B Nur verfügbar, wenn mehr als eine Menüvorlage für Suchergebnisse in F angegeben wurde. Mit dieser Option kann ein anderes Layout für die Suchergebnisse dieser Suche gewählt werden. =item B Falls C, sucht das Plugin im Hintergrund nach passenden Sendungen und erzeugt für jede gefundene einen Timer (im Setup muss dazu C aktiviert sein). Bei der Einstellung C lässt sich über die Taste C ein Zeitfenster einstellen, in dem der Suchtimer aktiv sein soll. =item B Die Standardaktion ist C, also das Erzeugen eines Timers für jedes Suchergebnis. Man kann aber auch festlegen, dass nur eine Ankündigung der Sendung per OSD vorgenommen werden soll, sobald diese gefunden wird. Eine weitere Möglichkeit ist C, womit automatisch eine Minute vor Beginn der Sendung auf deren Kanal gewechselt wird. Ebenso kann mit C die Sendung vor ihrem Beginn angekündigt werden und mit der Taste C daraufhin zum entsprechenden Kanal gewechselt werden. Weitere Aktionen sind C und C. =item B Falls C, werden die Aufzeichnungen in einem Ordner mit dem Titel als Serienname gespeichert, wobei der Untertitel als Episodenname dient. Falls ein Untertitel fehlt, werden ersatzweise Datum und Uhrzeit der Sendung als Episodenname verwendet. =item B Hier kann man ein Verzeichnis angeben, in dem die Aufzeichnung gespeichert wird, wie etwa C. Mit der Taste C kann ein Verzeichnis gewählt werden, das bereits bei anderen Sucheinträgen verwendet wird. Die Liste kann außerdem durch Einträge in der Datei F erweitert werden. Sie enthält jeweils ein Verzeichnis pro Zeile ohne den vorausgehenden Pfad des Video-Verzeichnisses; siehe auch B(4). Wenn man erweiterte EPG-Informationen von einem Provider erhält, können im Verzeichnis-Eintrag auch Variablen wie C<%Genre%> oder C<%Category%> verwendet werden. Diese werden durch die vorgefundenen erweiterten EPG-Informationen ersetzt, sobald ein Timer erzeugt wird. Siehe auch "Verwendung von Variablen im Verzeichniseintrag eines Suchtimers" in B(4). =item B Manche Aufzeichnungen sollen nur ein paar Tage existieren, wie etwa Nachrichten. Mit diesem Feature kann man EPGSearch anweisen, dass es die Aufzeichnungen automatisch nach einer bestimmten Zahl von Tagen löschen soll. =item B Wenn die angegebene Anzahl von Aufzeichnungen existiert, pausiert EPGSearch mit dem Erzeugen neuer Timer. Erst nach Löschen einer oder mehrerer dieser Aufzeichnungen wird wieder nach neuen Sendungen gesucht. =item B Wenn man keine Wiederholungen aufnehmen will, versucht dieses Feature festzustellen, ob eine Sendung bereits programmiert oder aufgezeichnet wurde und überspringt diese dann. Bitte vor Verwendung weiter unten den Abschnitt "Wiederholungen vermeiden im Detail" lesen. =item B Will man eine gewisse Anzahl von Wiederholungen einer Sendung erlauben, kann dies hier hinterlegt werden. =item B Falls Wiederholungen nur innerhalb einer anzugebenden Anzahl von Tagen erlaubt werden sollen, kann dies hier eingestellt werden. Der Wert 0 entspricht einer unbegrenzten Anzahl von Aufnahmen. =item B Legt fest, ob beim Test, ob eine Sendung identisch ist, der Titel verglichen werden soll. =item B Legt fest, ob beim Test, ob eine Sendung identisch ist, auch der Untertitel verglichen werden soll. Bei C stuft EPGSearch zwei Sendungen nur dann als identisch ein, wenn die Untertitel gleich sind und nicht leer. Bei C können Sendungen auch identisch sein, wenn beide Untertitel leer sind. Im Normalfall sollte dann besser C eingestellt werden. =item B Legt fest, ob beim Test, ob eine Sendung identisch ist, auch die Inhaltsbeschreibung verglichen werden soll. Zum Vergleich zweier Inhaltsbeschreibungen werden zunächst alle Teile aus den Inhalten entfernt, die Kategorienangaben gleichen könnten; die verbleibenden Texte werden dann miteinander verglichen. Genügt der anhand des Levenshtein-Distanz-Algorithmus ermittelte Prozentsatz an Übereinstimmung dem in der folgenden Option angegebenen Wert, werden die Beschreibungen als gleich betrachtet. =item B Die notwendige Übereinstimmung zweier Inhaltsbeschreibungen in Prozent. =item B Manchmal wird eine Sendung häufig innerhalb einer gewissen Zeitspanne (Tag, Woche, Monat usw.) wiederholt, ohne dass sich die einzelnen Sendungen anhand des EPG-Inhalts unterscheiden lassen. Die Sendezeit ist somit die einzig verwertbare Information. Für den Vergleich kann hier die entsprechende Zeitspanne auswählt werden, um die Wiederholungen zu ignorieren. =item B Über die Schaltfläche C kann angegeben werden, welche der erweiterten EPG-Kategorien ebenfalls miteinander verglichen werden sollen. Wie schon bei den Untertiteln gilt eine Sendung als unterschiedlich, wenn sie keine entsprechende Kategorie aufweist. =item B Jeder Suchtimer kann für diese Parameter eigene Einstellungen haben. Die Voreinstellung wird im Setup vorgenommen. =item B Nutzt VPS, falls im VDR-Setup aktiv und die gefundene Sendung über VPS-Daten verfügt. =item B Zum automatischen Löschen eines Suchtimers anhand folgender Bedingungen: =over 2 =item * nach ... Aufnahmen =item * nach ... Tagen nach der ersten Aufnahme =back Gezählt werden dabei nur erfolgreiche Aufnahmen. Das Löschen erfolgt direkt nach dem Ende der entsprechenden Aufnahme. =back Um den Status C zu ändern, ohne das Menü zu öffnen, kann die Taste C<2> verwendet werden. Dies ruft direkt den zweiten Befehl im Befehlsmenü auf. =head3 1.2.2 Menü C Dieses Menü zeigt die Suchergebnisse an. Ein C sagt aus, dass es zu diesem Eintrag bereits einen Timer gibt, ein C, dass es nur teilweise aufgenommen wird, also wie im Standard-Programm-Menü. =head2 1.3 Erweitertes C und C Im Setup können bis zu vier zusätzliche Zeiten als Erweiterung zu C und C angegeben werden, um die Auswahl für die Taste C zu erweitern, beispielsweise C, C oder C. Bereits verstrichene Zeiten werden übersprungen, sodass man etwa am Abend kein C mehr angeboten bekommt. Liegt aber ein Zeitpunkt nicht mehr als 20 Stunden in der Zukunft, wird das Menü des nächsten Tages angezeigt. In diesem Menü kann die aktuell angezeigte Zeit durch Drücken auf C und C nach hinten oder vorne verschoben werden. Falls diese Tasten auf der Fernbedienung nicht existieren, kann die Funktion durch Umschalten mit C<0> erreicht werden. Die Tasten C und C wechseln dann zu CE> und CE>. Das Umschalten kann über das Setup angepasst werden. Zudem lässt sich ein Fortschrittsbalken ins Menü C / C einblenden. Des Weiteren kann in den Einstellungen ein Favoriten-Menü zugeschaltet werden. Für eine Suche lässt sich mittels C festgelegen, dass die Suchergebnisse dort angezeigt werden sollen. Das Favoriten-Menü zeigt Sendungen der (standardmäßig) nächsten 24 Stunden in chronologischer Reihenfolge. =head2 1.4 Menü C =head3 1.4.1 Allgemein =over 4 =item B Damit wird der Eintrag C im Hauptmenü ausgeblendet. B Wenn das Plugin der Taste C zugeordnet ist, dann bewirkt das Ausblenden, dass wieder das VDR-Standardmenü aufgerufen wird. Siehe unten, wie sich das vermeiden lässt. =item B Falls nicht ausgeblendet, kann hier der Name des Hauptmenü-Eintrags hinterlegt werden. Vorgabe ist C. B Wenn der Name von der Standardeinstellung abweicht, ist der Hauptmenü-Eintrag nicht mehr mit der gewählten OSD-Sprache verknüpft. Setzt man den Namen aber wieder auf die Standardeinstellung zurück oder löscht ihn, ist die Verknüpfung mit der OSD-Sprache wieder gegeben. =item B Auswahl von C oder C als Startmenü. =back =head3 1.4.2 EPG-Menüs =over 4 =item B Hier kann das Verhalten der Taste C festgelegt werden. Man kann damit die Inhaltsangabe anzeigen oder zum entsprechenden Sender wechseln. B Die Funktion der Taste C (C, C oder C) hängt von dieser Einstellung ab. =item B Legt fest, ob man C (Standardeinstellung) oder C als Vorbelegung möchte. =item B Legt fest, ob man C (Standardeinstellung) oder C als Vorbelegung möchte. =item B Im Menü C kann ein Fortschrittsbalken eingeblendet werden, der die bereits verstrichene Zeit der laufenden Sendung anzeigt. =item B Auswählen, um eine führende Kanalnummer vor jedem EPG-Eintrag anzuzeigen. =item B Zur Anzeige einer Trennzeile zwischen Kanalgruppen im Menü C<Übersicht - Jetzt> und den anderen Menüs von Programmübersichten. =item B Zur Anzeige einer Trennzeile zwischen Sendungen unterschiedlicher Tage im Menü C. =item B Erlaubt die Anzeige von Radiokanälen. =item B Bei einer sehr großen Kanalliste lässt sich der Menüaufbau mit dieser Einstellung durch eine Einschränkung der angezeigten Kanäle beschleunigen. Mit C<0> wird das Limit aufgehoben. Wenn der aktuelle Kanal über dem Limit liegt, wird das Limit ignoriert, sodass wieder alle Kanäle angezeigt werden. =item B Falls C, wird nach Drücken von C sofort ein Timer angelegt. Andernfalls erscheint stattdessen das Timer-Editieren-Menü. =item B Zur Anzeige von Programmen ohne EPG, um auf diese umschalten oder einen Timer programmieren zu können. =item B In den Menüs C und C sowie den Menüs mit benutzerdefinierten Zeiten kann der angezeigte Zeitpunkt durch drücken von C bzw. C verschoben werden. Die Sprungweite in Minuten kann hier festgelegt werden. =item B Auf C setzen, falls die Tasten C bzw. C auf der Fernbedienung nicht vorhanden sind. In entsprechenden Menüs werden damit nach Drücken der Taste C<0> die Tasten C und C beispielsweise auf CE> und CE> umgeschaltet. =item B Das Favoriten-Menü kann dazu verwendet werden, eine Liste von bevorzugten Sendungen anzuzeigen, die innerhalb der nächsten Stunden laufen. Je nach Einstellung erscheint dieses Menü vor oder nach den EPG-Menüs mit benutzerdefinierten Zeiten. Die Auswahl von Sendungen wird durch Setzen der Option C innerhalb einer Suche vorgenommen. =item B Mit diesem Wert wird die Zeitspanne eingestellt, für die Favoriten angezeigt werden sollen. =back =head3 1.4.3 Benutzerdefinierte EPG-Zeiten =over 4 =item B Bis zu vier benutzerdefinierte Zeiten können zu C und C hinzugefügt werden. =item B Name der benutzerdefinierten Zeit, etwa C, C oder C. =item B Uhrzeit, ab der die benutzerdefinierte Zeitspanne beginnt. =back =head3 1.4.4 Timer-Programmierung =over 4 =item B Beim normalen Programmieren eines Timers verwendet EPGSearch ein erweitertes Timer-Editieren-Menü, das einen Verzeichniseintrag, benutzerdefinierte Wochentage und die Vervollständigung um Untertitel anbietet. Falls man einen gepatchten VDR verwendet, der ebenfalls ein erweitertes Timer-Editieren-Menü-Menü anbietet und lieber dieses verwenden möchte, dann diese Option auf C setzen. =item B Dieser Eintrag wird beim normalen Programmieren eines Timers verwendet. Die Nutzung von EPG-Variablen ist ebenfalls möglich, etwa C. Wird das Timer-Editieren-Menü aufgerufen, versucht EPGSearch, alle Variablen durch die Werte aus der Beschreibung der Sendung zu ersetzen. Konnten nicht alle ersetzt werden, bleibt der Verzeichniseintrag leer. =item B Beim manuellen Programmieren eines Timers kann EPGSearch den Dateinamen automatisch um den Untertitel ergänzen. Dies bewirkt, dass die spätere Aufnahme in einem nach dem Titel benannten "Serienverzeichnis" mit dem Untertitel als Episodenname gespeichert wird. Hier legt man fest, ob bzw. wie die Ergänzung erfolgen soll: C versucht anhand der Länge einer Sendung festzustellen, ob ein Untertitel als Episodenname sinnvoll ist. Ist die Sendung länger als 80 Minuten, wird kein Episodenname ergänzt. =item B Manuell angelegte Timer können auf Änderungen im EPG überprüft werden. Hier kann die Standardeinstellung für die Prüfmethode je Kanal hinterlegt werden. Folgende Prüfmethoden existieren: =over 4 =item I Z<> =item I Geprüft wird anhand einer Kennung, die durch den Sender vergeben wird. =item I Geprüft wird anhand der Sendung, die am besten zur Dauer der ursprünglichen Sendung passt. =back Nicht alle Sender liefern eine vernünftige Sendungskennung. Deshalb kann hier die Standardeinstellung für jeden Kanal einzeln gesetzt werden. Bei der Programmierung eines manuellen Timers wird diese im Timer-Editieren-Menü vorgegeben, falls das Menü von EPGSearch benutzt wird. =back =head3 1.4.5 Suche und Suchtimer =over 4 =item B Falls C, untersucht das Plugin im Hintergrund die EPG-Daten und erzeugt Timer, falls passende Einträge gefunden werden. Dies betrifft nur Sucheinträge, die mit C markiert sind. Suchtimer werden immer lokal erzeugt, auch wenn ein anderer Default-Host für Aufnahmen definiert ist. =item B Das Intervall in Minuten, in dem die Hintergrundsuche vorgenommen wird. =item B Falls nicht der Standard-SVDRP-Port 6419 (Port 2001 vor VDR 1.7.15) verwendet wird, bitte hier anpassen, damit die Suchtimer funktionieren. =item B Der Start des Suchtimer-Update-Threads und der folgenden Konfliktprüfung kann um 0..300 Sekunden ab C verzögert werden. Standardmäßig sind dies 10 Sekunden. =item B Legen die entsprechenden Voreinstellungen für neu erzeugte Suchtimer fest. =item B Erlaubte Fehler in einer Aufnahme, bevor sie als unvollständig gekennzeichnet wird (verfügbar seit VDR 2.5.4) =item B Zum Unterdrücken von Sendungsankündigungen während einer aktiven Wiedergabe. =item B EPGSearch merkt sich standardmäßig, welche Timer bereits durch Suchtimer angelegt wurden, und programmiert diese nicht erneut, wenn sie gelöscht wurden. Zum Abschalten dieses Verhaltens bitte C wählen. =item B Falls das EPG von externen Anbietern bezogen wird, kann es vorkommen, dass der Abruf fehlschlägt und somit Aufzeichnungen wegen fehlender EPG-Daten verlorengehen. Hiermit kann geprüft werden, ob für die vorgegebene Zahl von Stunden bei den gewünschten Sendern ein EPG vorhanden ist. Ein Wert von C<0> deaktiviert die Prüfung. =item B Falls C, erscheint die Warnung als OSD-Einblendung. =item B Falls ja, wird die Warnung per E-Mail versandt. Hierfür bitte das E-Mail-Konto unter C konfigurieren. =item B Begrenzt die Erzeugung von Timern auf einen Zeitraum in Tagen. Ein Wert von C<0> deaktiviert die Begrenzung. =item B Hier die Kanalgruppe auswählen, für welche die Prüfung durchgeführt werden soll. Diese gegebenenfalls zuvor unter C anlegen. =item B Auf C setzen, wenn der Suche nach Wiederholungen keine Sendungen von PayTV-Sendern gewünscht sind. =item B Hier können Suchvorlagen verwaltet werden, die beim Anlegen neuer Suchen und Suchtimer verwendbar sind. =item B Hier können Ausschlusslisten verwaltet werden. Diese können innerhalb einer Suche verwendet werden, um unerwünschte Sendungen zu vermeiden. Eine Ausschlussliste kann auch als global gekennzeichnet werden. Da die Standardeinstellung beim Suchtimer für die Option C auf C steht, lassen sich damit unerwünschte Sendungen von allen Suchtimern unkompliziert ausschließen. B Falls bei einem Suchtimer für C die Option C gewählt ist, hat eine globale Ausschlussliste keine Auswirkung. Ebenso werden globale Ausschlusslisten bei der Suche nach Wiederholungen über das OSD ignoriert. =item B Verwaltet die Kanalgruppen, die als Suchkriterium in einer Suche verwendet werden können. Die Verwaltung ist auch im Editieren-Menü einer Suche möglich. =back B Wenn die EPG-Daten von einer externen Quelle bezogen werden, sollte dafür gesorgt werden, dass die Suchtimer-Updates während des EPG-Updates abgeschaltet sind. Der Grund hierfür ist, dass EPGSearch alle Timer löscht, denen keine Sendungen zugeordnet sind. Diese Situation kann auftreten, solange die neuen EPG-Daten vom VDR eingelesen werden. Die Abschaltung der Updates geht am einfachsten mit dem SVDRP-Befehl C im EPG-Update-Skript: =over 4 svdrpsend PLUG epgsearch SETS off epg-updates-laden svdrpsend PLUG epgsearch SETS on =back =head3 1.4.6 Timer-Konfliktprüfung =over 4 =item B Falls ein Timer fehlschlagen sollte, dessen Priorität unter dem angegebenen Wert liegt, wird darauf nicht per OSD-Nachricht hingewiesen und der Konflikt wird als irrelevant in der Konfliktübersicht angezeigt. =item B Falls ein Konflikt nicht länger als die angegebene Zahl von Minuten dauert, wird darauf nicht per OSD-Nachricht hingewiesen und der Konflikt wird als irrelevant in der Konfliktübersicht angezeigt. =item B Hier kann der Zeitraum für die Konfliktprüfung angegeben werden. =item B Falls SVDRP-Peering aktiv ist und C gewählt ist, werden auch Konflikte bei Remote-Timern überprüft. Dazu muss am entsprechenden Remote-Rechner das Plugin EGPSearch ebenfalls aktiviert sein. Default ist C. =item B Dies bewirkt eine Konfliktprüfung nach jeder manuellen Timer-Programmierung und erzeugt eine OSD-Nachricht, falls der neue bzw. geänderte Timer in einen Konflikt involviert ist. =item B Auf C setzen, wenn die Konfliktprüfung beim Beginn einer jeden Aufnahme erfolgen soll. Im Falle eines Konflikts wird dann sofort eine OSD-Meldung angezeigt. Diese erscheint aber nur, wenn der Konflikt innerhalb der nächsten zwei Stunden auftritt. =item B Hier kann eingestellt werden, ob eine Konfliktprüfung nach jedem Suchtimer-Update erfolgen soll. Andernfalls greifen die nachfolgend beschriebenen Einstellungen. =item B Gibt an, in welchem Intervall im Hintergrund eine automatische Konfliktprüfung erfolgen soll. Bei relevanten Konflikten erfolgt eine Benachrichtigung per OSD. Mit C<0> wird diese Funktion deaktiviert. =item B Wenn ein Konflikt in der angegebenen Zahl von Minuten eintritt, soll das nachfolgend angegebene Prüfintervall verwendet werden. =over 4 =item B Um einen kurz bevorstehenden Konflikt nicht zu übersehen, kann hier ein kürzeres Prüfintervall eingestellt werden. =back =item B Bitte auf C setzen, wenn während einer Wiedergabe keine OSD-Benachrichtigungen über Timer-Konflikte gewünscht sind. Die Benachrichtigung erfolgt trotzdem, wenn der nächste Konflikt innerhalb der nächsten zwei Stunden auftritt. =back Bitte ebenfalls den Abschnitt "Working with the timer-conflict menu" in B(4) berücksichtigen. =head3 1.4.7 E-Mail-Benachrichtigung Hierzu bitte sicherstellen, dass F in einem Verzeichnis von C<$PATH> liegt und F und F im Konfigurationsverzeichnis von EPGSearch vorliegen. =over 4 =item B Diese Option aktivieren, wenn eine E-Mail-Benachrichtigung gewünscht wird, sobald der Suchtimer-Hintergrund-Thread =over 2 =item • neue Timer angelegt hat; =item • vorhandene Timer geändert hat; =item • Timer gelöscht hat, weil diese wegen EPG-Änderungen oder anderen Benutzeraktionen nicht mehr gültig sind. =back Hierfür muss ebenfalls die Option C im Suchtimer-Setup aktiv sein. =item B Für Benachrichtigungen zu Suchtimern kann hier angegeben werden, welchen Mindestabstand in Stunden die E-Mails haben sollen. Sobald die entsprechende Zeit verstrichen ist, wird eine E-Mail nach dem nächsten Suchtimer-Update versandt. Der Wert C<0> bewirkt einen sofortigen E-Mail-Versand. =item B Diese Option aktivieren, wenn eine E-Mail-Benachrichtigung bei Timer-Konflikten gewünscht wird. Es werden nur Konflikte gemeldet, die gemäß den Einstellungen zur Timer-Konfliktprüfung relevant sind. Neue Benachrichtigungen werden nur versandt, wenn sich bei den Konflikten Veränderungen ergeben haben. B Hierfür muss in den Einstellungen der Timer-Konfliktprüfung die Option C oder C aktiviert sein. =item B Hier die vollständige (!) E-Mail-Adresse hinterlegen, an welche die Nachrichten versandt werden sollen. B Einige Provider (wie etwa Arcor) erlauben keine identischen E-Mail-Adressen für Sender und Empfänger. =item B Zur Auswahl stehen: =over 4 =item I Ein einfaches Skript, das auch auf Systemen ohne konfigurierten Mailserver den Versand von E-Mails erlaubt. Das Skript wird mit EPGSearch ausgeliefert und sollte in einem Verzeichnis von C<$PATH> liegen. =item I Setzt ein korrekt aufgesetztes Mailsystem voraus. =back =item B Hier die volle (!) E-Mail-Adresse hinterlegen, von der die Nachricht versandt werden soll. =item B Der Name des SMTP-Servers, über den der E-Mail-Versand erfolgen soll. =item B Bitte C wählen, wenn das E-Mail-Konto eine SMTP-Authentifizierung für den E-Mail-Versand benötigt. =item B Hier bitte den Benutzernamen angeben, falls das E-Mail-Konto eine Authentifizierung erfordert. =item B Hier bitte das Passwort angeben, falls das E-Mail-Konto eine Authentifizierung erfordert. B Das Passwort wird im Klartext gespeichert. Es liegt in der eigenen Verantwortung, dafür Sorge zu tragen, dass das System sicher ist und dass unautorisierten Personen kein Zugriff auf VDR-Konfigurationsdateien möglich ist. =back Nach Eingabe der E-Mail-Kontoeinstellungen bitte mittels C prüfen, ob alles funktioniert. Wenn mit F gearbeitet wird, sollte am Ende der Testausgabe etwas wie C erscheinen. Eine Testfunktion gibt es für C leider nicht. Bitte ebenfalls den Abschnitt C in B(4) berücksichtigen. =head1 2. Suchtimer Die Suchtimer entsprechen in etwa den Autotimern von VDRAdmin, benötigen jedoch kein externes Programm. Beim Anlegen einer Suche kann man festlegen, ob diese als Suchtimer verwendet werden soll. Das Plugin sucht dann im Hintergrund in bestimmten Zeitabständen (siehe C) nach passenden Sendungen und erzeugt Timer für die gefundenen Sendungen. Sollen keine Timer angelegt, sondern die Sendungen nur gemeldet werden, dann bitte C einstellen. Gerade für Serien ist dies sehr praktisch, weshalb es in der Suche die Option C gibt. In diesem Fall wird ein Timer mit zusätzlichem Episodennamen angelegt. Die Aufnahme wird dann in einem Ordner mit Titel als Seriennamen gespeichert, wobei der Untertitel als Episodenname dient. Falls ein Untertitel fehlt, werden ersatzweise Datum und Uhrzeit der Sendung als Episodenname verwendet. Die Suchtimer-Funktion muss außerdem im Setup aktiviert werden. Falls für SVDRP nicht der Standardport verwendet wird, den gewünschten Port bitte ebenfalls im Setup eintragen. Um eine Hintergrund-Suche manuell anzustoßen, genügt die Ausführung von: =over 4 touch /etc/vdr/plugins/epgsearch/.epgsearchupdate =back Diese Anweisung kann ebenfalls Teil des shutdown-Skripts sein. Dort sollte man dann aber noch eine Verzögerung von ein paar Sekunden vorsehen, damit das Plugin ausreichend Zeit hat, den Scan zu beenden. Mehr Infos zu Suchtimern finden sich in B(4) unter "Description of the search process" und "How do Search Timers work?". =head2 2.1 Wiederholungen vermeiden im Detail Nicht immer lässt sich durch entsprechende Suchkriterien vermeiden, dass auch Timer für Wiederholungen erzeugt werden. Dieser Abschnitt erläutert, wie die Option C eines Suchtimers funktioniert. Um möglichst keine Duplikate von Sendungen zu erhalten, versucht das Feature C vor dem Programmieren einer Sendung zu prüfen, ob eine gleiche Sendung schon einmal aufgenommen wurde oder ob ein Timer existiert, der die gleiche (nicht dieselbe!) Sendung aufzeichnet. Ist dies der Fall, wird kein Timer für die zu überprüfende Sendung erzeugt. =head2 2.2 Wie funktioniert der Vergleichstest zweier Sendungen? Für den Test auf Gleichheit zweier Sendungen gibt es viele Einstellmöglichkeiten bei Suchtimern. Man kann wählen, ob Titel, Untertitel, Beschreibung und bestimmte Kategorien innerhalb der Beschreibung einer Sendung mit den jeweiligen Angaben einer anderen Sendung verglichen werden sollen. Der Vergleich prüft die einzelnen Angaben immer auf vollständige Übereinstimmung, einschließlich der Schreibweise. Die Beschreibung einer Sendung stellt jedoch eine Ausnahme dar: Hier wird zunächst alles aus dem Text eliminiert, was eine erweiterte Kategorie darstellen könnte, wie etwa C. Eine solche erweiterte Kategorie entspricht einer Zeile, die mit bis zu 40 Zeichen beginnt, von einem C<:> gefolgt wird und dann maximal weitere 60 Zeichen umfasst. Hintergrund für diese Filterung sind oft vorhandene Bewertungen wie C, die bei der Wiederholung vielfach aber nicht mehr enthalten sind. Der verbleibende Text wird nun zunächst in der Länge verglichen. Ist der Unterschied größer als 90 Prozent, wird die Beschreibung als unterschiedlich gewertet. Andernfalls erfolgt eine Prüfung per Levenshtein-Distanz-Algorithmus (LD), der einen Fuzzy-Textvergleich durchführt. Die Beschreibung wird als gleich akzeptiert, wenn LD eine Übereinstimmung von mehr als 90 Prozent meldet. Da dieser Algorithmus ziemlich laufzeitintensiv ist (Komplexität I), sollte nach Möglichkeit nicht nur C als einziges Vergleichskriterium ausgewählt werden, sondern am besten immer nur in Kombination mit anderen Kriterien. =head2 2.3 Wie und wo wird der Vergleichstest eingesetzt? Wie zuvor erwähnt, wird bei einem Suchtimer-Update für Suchtimer mit diesem Feature zusätzlich geprüft, ob eine Sendung bereits irgendwann schon aufgezeichnet wurde oder ob in der Timer-Liste ein Timer vorhanden ist, der die gleiche Sendung aufzeichnen würde. Nach jeder Aufnahme, die durch einen Suchtimer mit C erzeugt wurde, werden alle Angaben zu dieser Sendung der Datei F gespeichert. Mittels C in den C des Menüs C kann man sich alle Sendungen, die ein solcher Timer bisher aufgenommen hat, anzeigen lassen und diese auch bearbeiten. In die Datei fließen nur Aufnahmen ein, die bezüglich der Timer-Angaben korrekt begonnen haben und auch beendet wurden. Das heißt, dass unvollständige Aufnahmen nicht registriert werden und somit beim nächsten Suchtimer-Update automatisch ein neuer Timer für diese Sendung erzeugt wird, falls eine Wiederholung gefunden wurde. Seit VDR 2.5.4 werden auch Aufnahmen mit Fehlern nicht registriert. Möchte man eine bestimmte Anzahl von Aufnahmefehlern zulassen, kann der Wert für C entsprechend gesetzt werden. =head3 2.3.1 Wie verwenden? Man sieht, dass das ganze Feature stark von der Qualität und dem Umfang des verwendeten EPGs abhängt. Hat man einen entsprechenden Suchtimer angelegt, ist es sinnvoll, erst einmal zu prüfen, ob er sich wie gewünscht verhält. Dazu gibt es für solche Timer im Menü C auf der Taste C die zusätzliche Belegung C. Sendungen, die noch keinen Timer haben (C), für die aber einer aufgrund des Features beim nächsten Suchtimer-Update programmiert werden würde, haben dort ein C

stehen. B Möchte man aufgrund von Konflikten einen bereits programmierten Timer nicht verwenden, sollte dieser im Timer-Menü deaktiviert werden. Beim nächsten Suchtimer-Update wird dann einfach die nächstmögliche Wiederholung programmiert, falls vorhanden. =head3 2.3.2 Wenn es nicht richtig funktioniert Damit bei Verwendung dieses Features besser nachvollziehbar ist, warum Timer erzeugt wurden oder warum nicht, steht für EPGSearch ein eigenes Log-File zur Verfügung. Startet man EPGSearch mit einem Log-Level von 2 oder höher (C<−v 2>), werden beim Suchtimer-Update in der Datei F weiterführende Informationen abgelegt. Siehe auch die Kommandozeilen-Optionen weiter oben. =head1 3. Verwendung der Suche durch andere Plugins oder Skripte Siehe B(4). =head1 4. Verwendung erweiterter EPG-Informationen Einige EPG-Provider liefern zusätzliche EPG-Informationen, wie die Art der Sendung, das Video- und Audio-Format, die Besetzung usw. in der Beschreibung einer Sendung. B Dies hat nichts mit den Inhaltskategorien zu tun, die gemäß DIN EN 300 468, Tabelle 18, als zusätzliche Daten ausgeliefert und seit VDR 1.7.11 unterstützt werden. Leider liefern nicht alle Provider solche Daten bzw. setzen die Kennungen korrekt. Deshalb gibt es den Ansatz der "erweiterten EPG-Informationen", der versucht, diese Information aus der Inhaltsbeschreibung zu extrahieren. Mit Tools wie F oder F können derartige EPG-Inhalte in den VDR importiert werden. Anhand solcher Daten lässt sich beispielsweise relativ einfach eine Suche erzeugen, die alle Tagestipps findet, die in 16:9 ausgestrahlt werden. Dazu durchsucht EPGSearch die Beschreibung einer Sendung nach Zeilen, die mit den Namen von Kategorien beginnen, gefolgt von C<:> und einem Leerzeichen, für die im Suchtimer ein oder mehrere Werte festgelegt sind. Die Suche berücksichtigt Groß- und Kleinschreibung sowohl bei den Kategorienamen als auch bei deren Werten. Um Informationen dieser Art in Suchtimern verwenden zu können, müssen anhand der Datei F im Konfigurationsverzeichnis von EPGSearch entsprechende Einstellungen vorliegen. Zeilen dieser Datei haben folgendes Format: =over 4 ID|Kategorienname[,Format]|Bezeichnung im Menü|Kategorienwerte|Suchmodus =back Die einzelnen Felder einer Zeile haben folgende Bedeutung: =over 4 =item 1 – B Integer mit einem positiven Wert. B Ändert man später die Kennung, müssen die Suchtimer angepasst werden! =item 2 – B String mit dem Namen der EPG-Kategorie laut EPG-Provider, etwa C. Wenn ein optionales C angegeben ist, wird der Kategoriewert als Ganzzahl interpretiert und gemäß der Formatangabe dargestellt (siehe C für anwendbare Formatangaben). =item 3 – B String mit dem Namen der EPG-Kategorie in Auswahlmenüs von EPGSearch. =item 4 – B String mit einer optionalen, kommaseparierten Liste von Werten für die EPG-Kategorie. =item 5 – B Optionale Enumeration mit den folgenden Werten: =over 4 B =over 4 =item 0 = der gesamte Begriff muss enthalten sein =item 1 = jedes Wort (durch eines der Zeichen C<,;|~> getrennt) muss enthalten sein; dies ist die Standardeinstellung =item 2 = mindestens ein Wort (durch eines der Zeichen C<,;|~> getrennt) muss enthalten sein =item 3 = exakte Übereinstimmung =item 4 = regulärer Ausdruck =back B =over 4 =item 10 = kleiner =item 11 = kleiner oder gleich =item 12 = größer =item 13 = größer oder gleich =item 14 = gleich =item 15 = ungleich =back =back =back Beispieldateien finden sich im Verzeichnis F von EPGSearch. Oft genügt es, eine (weitgehend) passende Vorlage als F ins Konfigurationsverzeichnis des Plugins zu kopieren und gegebenenfalls ein paar kleinere Anpassungen vorzunehmen. Zur Nutzung der EPG-Kategorien muss man den VDR danach lediglich neu starten und das Editieren-Menü eines Suchtimers aufrufen. Weil das Aufsetzen einer neuen F aber ziemlich mühselig ist, wird F als kleines Tool mitgeliefert, das den Großteil der Arbeit übernimmt. Es findet sich im Binärverzeichnis des VDR ind wird folgendermaßen aufgerufen: =over 4 createcats /epg.data =back Das Tool scannt die vorhandenen EPG-Informationen und versucht, daraus die erweiterten Informationen zu extrahieren. Das Ergebnis ist eine neue Datei F, die aber noch bearbeitet werden muss, weil sicher nicht alles genau passt. Danach muss die Datei ins Konfigurationsverzeichnis des Plugins kopiert werden. Siehe auch B(1) für weitergehende Informationen zu seiner Verwendung. =head1 5. Ersetzen des Standard-Programm-Menüs Um das Plugin als Ersatz für das über die Taste C aufgerufene Standard-Menü C des VDR zu verwenden, genügt es, die Zeile: =over 4 Green @epgsearch =back in die Datei F einzufügen. Falls kein weiterer Menüeintrag C im Hauptmenü erscheinen soll, kann der Eintrag des Plugins über die Einstellungen verborgen werden. =head1 6. Add-ons Mit EPGSearch werden zwei weitere Mini-Plugins ausgeliefert. Beide Plugins erfordern, dass EPGSearch ebenfalls installiert ist (EPGSearch kann aber aus dem Hauptmenü ausgeblendet werden): =over 4 =item B> Werden nur die Suchfunktionen bzw. Suchtimer von EPGSearch benötigt oder lediglich ein eigener Hauptmenüeintrag für die Suche gewünscht, kann dies mit diesem Plugin erreicht werden. Es erzeugt einen Hauptmenüeintrag C, der direkt in das Menü C führt. Die Aktivierung erfolgt im VDR-Startskript mit C<−Pepgsearchonly>. =item B> Die Timer-Konfliktprüfung kann ebenfalls als eigener Hauptmenüeintrag angelegt werden. Über eine Option in den Einstellungen lässt sich auch das Ergebnis der letzten Konfliktprüfung direkt im Hauptmenü anzeigen. Die Aktivierung erfolgt im VDR-Startskript mit C<−Pconflictcheckonly>. =back =head1 DATEIEN =over 4 =item B> Enthält die Suchtimer; siehe B(5). =item B> Enthält die Ausschlusslisten; siehe B(5). =item B> Enthält die Kategorien für die erweiterten EPG-Informationen; siehe B(5). =item B> Enthält die Kanalgruppen; siehe B(5). =item B> Enthält Befehle ähnlich der Datei F, die auf EPG-Einträge angewandt werden können; siehe B(5). =item B> Enthält Pfade, die beim Bearbeiten eines Suchtimers ausgewählt werden können; siehe B(5). =item B> Enthält die Liste erledigter Suchtimer; siehe B(5). =item B> Enthält die vom Benutzer gewählte Konfiguration der OSD-Menüdarstellung; siehe B(5). =item B> Enthält die Umschalttimer; siehe B(5). =item B> Enthält die Vorlagen für Suchtimer; siehe B(5). =item B> Enthält die benutzerspezifischen Variablen; siehe B(5). =back =head1 HINWEISE Die ausführliche Beschreibung der internen Funktionen des Plugins findet sich in B(4). =head1 AUTOREN (Man-Pages) Ursprünglich erstellt von Mike Constabel . Überarbeitet und an die aktuellen Features von EPGSearch adaptiert durch die derzeitigen Maintainer. =head1 PROJEKTSEITE Das Plugin wird als Projekt auf GitHub geführt: L =head1 FEHLER MELDEN Fehlerberichte sowie Feature-Anfragen können über den Bugtracker des Projekts eingespeist werden: L =head1 COPYRIGHT und LIZENZ Copyright © 2004-2010 Christian Wieninger Copyright © 2011-2025 TomJoad (VDR-Portal) et al. Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren, entweder gemäß Version 2 der Lizenz oder (nach Ihrer Option) jeder späteren Version. Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, dass es Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der GNU General Public License. Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm erhalten haben. Falls nicht, schreiben Sie an die Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Oder rufen Sie in Ihrem Browser L auf. Der ursprüngliche Autor kann über L erreicht werden. Die aktuellen Maintainer können über die Projektseite auf GitHub (siehe oben) erreicht werden. Der MD5-Code ist abgeleitet aus dem Message-Digest-Algorithmus MD5 von RSA Data Security, Inc. =head1 SIEHE AUCH B(4), B(5), B(5), B(5), B(5), B(5), B(5), B(5), B(5), B(5), B(5) vdr-plugin-epgsearch-2.4.6/doc-src/de/epgsearch.4.txt000066400000000000000000001767371514271145600223720ustar00rootroot00000000000000=encoding utf8 =for syntax specification: https://perldoc.perl.org/perlpod =head1 NAME F – Suchtimer und Ablösung des VDR-Programm-Menüs =head1 ÜBERSICHT Da die frühere F-Datei zu umfangreich geworden ist, dient diese Man-Page als Ersatz und erklärt einige Funktionen und Verhaltensweisen im Detail. Es handelt sich also nicht um ein Handbuch, sondern eher um eine erweiterte F-Datei. =head1 INHALT =over 4 =item 1. Verwendung von Variablen im Verzeichniseintrag eines Suchtimers =item 2. Das Format der Datei F =item 3. Ablauf des Suchvorgangs =item 4. Wie funktionieren Suchtimer? =item 5. Wie stößt man ein Timer-Update an? =item 6. Quellen für das Menü C =item 7. Sprachabhängige Kommandos für die Programmübersicht =item 8. Nutzung von anderen Plugins oder Skripten =item 9. Die SVDRP-Schnittstelle =item 10. Anpassung von EPG-Menüs =item 11. Nutzung des Timer-Konfliktmenüs =item 12. Benutzerdefinierte Variablen =item 13. Benachrichtigung per E-Mail =item 14. Das Verzeichnis F =back =head1 1. Verwendung von Variablen im Verzeichniseintrag eines Suchtimers Bei der Verwendung erweiterter EPG-Informationen können Variablen Teil des Verzeichniseintrags eines Suchtimers sein. Diese Variablen haben immer die Syntax C<%Variable%>. Der Name einer Variablen entspricht dem internen Namen der erweiterten EPG-Information, wie er in der Datei F angegeben ist. Beispiele finden Sie im Unterverzeichnis F des Plugins. Beispiel: =over 4 1|Category|Kategorie|Film,Kultur,Serie,Show,Spielfilm,Sport|3 =back Die Kategorie mit der ID C<1> hat den internen Namen C. Sie können sie daher mit C<%Category%> referenzieren. Variablennamen berücksichtigen keine Groß- und Kleinschreibung. Beispielhafte Verzeichniseinträge könnten so aussehen: =over 4 Meine Filme~%Category% Kinderfilme~%category% %CATEGORY%~%genre% =back Daneben gibt es drei weitere Variablen: =over 4 =item B<%Title%> Der Titel der Sendung. =item B<%Subtitle%> Der Untertitel der Sendung. =item B<%ChLng%> Der Name des Kanals, auf dem die Sendung läuft. =back Wenn weder C<%Title%> noch C<%Subtitle%> angegeben ist oder in einer eingefügten benutzerdefinierten Variable vorkommt, wird der Titel beim Erstellen eines Timers automatisch an den Verzeichniseintrag angefügt. Wenn im Suchtimer zudem C auf C gesetzt ist, wird auch der Untertitel noch angefügt. Der Verzeichniseintrag: =over 4 %Category%~%Genre%~%Title%~%Subtitle% =back ist also gleichwertig zu: =over 4 %Category%~%Genre% =back wenn C auf C steht. B Titel und Untertitel werden nicht automatisch angefügt, wenn eine der Variablen C<%Title%> oder C<%Subtitle%> im Verzeichniseintrag enthalten ist. Dadurch können Verzeichniseinträge wie dieser erstellt werden: =over 4 %Category%~%Genre%~%Title%~%Episode%~%Subtitle% =back Des Weiteren stehen noch die folgenden Variablen aus dem Suchtimer zur Verfügung: =over 4 =item B<%Search.Query%> Der Suchbegriff des Suchtimers. =item B<%Search.Series%> Liefert C<1>, wenn C auf C steht, sonst C<0>. =back Siehe auch B(5). =head1 2. Das Format der Datei F Jede Zeile der Datei repräsentiert eine Suche oder einen Suchtimer und umfasst die folgenden Felder: =over 4 =item B<1 – Eindeutige Kennung (ID)> Integer mit einem positiven Wert. =item B<2 – Suchbegriff ("Abfrage")> String zur Festlegung des Suchbegriffs. =item B<3 – Uhrzeit verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<4 – Start nach [HHMM]> Integer mit HH = 0..23, MM = 0..59; andere Werte können zu undefiniertem Verhalten führen. =item B<5 – Start vor [HHMM]> Integer mit HH = 0..23, MM = 0..59; andere Werte können zu undefiniertem Verhalten führen. =item B<6 – Kanal verwenden> Enumeration mit den Werten 0 = nein, 1 = Bereich, 2 = Kanalgruppe, 3 = ohne PayTV. =item B<7 – Kanalauswahl> =over 4 =item wenn 'Kanal verwenden' = 1: String mit C oder C|C; die Kanalkennungen müssen der VDR-Notation entsprechen (bspw. C). B Nach einer Änderung der Kanalreihenfolge sollten die Bereichseinstellungen von Suchtimern unbedingt kontrolliert werden! =item wenn 'Kanal verwenden' = 2: String mit dem Namen einer zuvor erstellten Kanalgruppe. =back =item B<8 – Groß/Kleinschreibung beachten> Flag mit den Werten 0 = nein, 1 = ja. =item B<9 – Suchmodus> Enumeration mit den folgenden Werten: =over 4 =item 0 = Ausdruck =item 1 = alle Worte =item 2 = ein Wort =item 3 = exakt =item 4 = regulärer Ausdruck =item 5 = unscharf (Toleranz im Feld 42; nicht verfügbar für erweiterte EPG-Kategorien) =back Siehe auch "3. Ablauf des Suchvorgangs" bezüglich der Frage, wie sich diese Einstellungen auf die Suche auswirken. =item B<10 – Titel verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<11 – Untertitel verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<12 – Beschreibung verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<13 – Dauer verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<14 – Minimale Dauer [HHMM]> Integer mit HH = 0..23, MM = 0..59; andere Werte können zu undefiniertem Verhalten führen. =item B<15 – Maximale Dauer [HHMM]> Integer mit HH = 0..23, MM = 0..59; andere Werte können zu undefiniertem Verhalten führen. =item B<16 – Als Suchtimer verwenden> Enumeration den folgenden Werten: =over 4 =item 0 = nein =item 1 = ja =item 2 = Zeitspanne (Felder 48 und 49) =back Z<> =item B<17 – Wochentag verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<18 – Wochentag> Integer im Wertebereich von −127 bis 6. Die Bedeutung ist zweigeteilt: =over 4 =item wenn I ≥ 0: ein einzelner Wochentag Enumeration mit den Werten 0 = Sonntag, 1 = Montag, ..., 6 = Samstag. =item wenn I < 0: eine Auswahl von Wochentagen Integer, in dem die Bits von −I die Wochentage darstellen. Die Bitmasken ergeben sich aus der Verschiebung von 0x01 um den Enumerationswert eines Wochentages: Z<> =over 4 =item 0x01 = Sonntag =item 0x02 = Montag =item 0x04 = Dienstag =item 0x08 = Mittwoch =item 0x10 = Donnerstag =item 0x20 = Freitag =item 0x40 = Samstag =back Um die gewünschten Wochentage zu kodieren, muss der aus den ODER-verknüpften Bitmasken resultierende Wert negiert werden. Beispiel: −67 (negiert: 0b01000011 = 0x40 | 0x02 | 0x01) kodiert Samstag, Montag und Sonntag =back =item B<19 – Serienaufnahme> Flag mit den Werten 0 = nein, 1 = ja. =item B<20 – Aufnahmeverzeichnis> String mit dem Namen eines Verzeichnisses unterhalb des Video-Verzeichnisses des VDR, jedoch ohne den Pfad zu diesem Video-Verzeichnis. =item B<21 – Priorität der Aufzeichnung> Integer im Wertebereich von 0 bis 99. =item B<22 – Lebensdauer der Aufzeichnung [Tage]> Integer im Wertebereich von 0 bis 99. =item B<23 – Vorlauf zum Timer-Beginn [Minuten]> Integer mit einem positiven Wert. =item B<24 – Nachlauf am Timer-Ende [Minuten]> Integer mit einem positiven Wert. =item B<25 – VPS verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<26 – Aktion> Enumeration mit den folgenden Werten: =over 4 =item 0 = aufnehmen =item 1 = per OSD ankündigen (kein Timer) =item 2 = nur umschalten (kein Timer) =item 3 = per OSD ankündigen und umschalten (kein Timer) =item 4 = per E-Mail ankündigen =item 5 = deaktiviert =back Z<> =item B<27 – Erweiterte EPG-Informationen verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<28 – Erweiterte EPG-Kategoriedaten> Liste der erweiterten EPG-Kategoriedaten, jeweils dargestellt durch ein Tupel aus: =over 4 =item 28.1 – Kennung Eindeutige Kennung der erweiterten EPG-Kategorie, wie in F angegeben. =item 28.2 – Kategoriedaten Kommaseparierte Liste von Werten, wie für die EPG-Kategorie in F angegeben. Leerzeichen vor und nach Kategoriewerten werden ignoriert. Ist in einem Wert ein C<:> enthalten, wie etwa in C<16:9>, muss es als C kodiert werden. =back Das Zeichen C<|> trennt benachbarte Kategorientupel, während C<#> die Kennungen und Kategorienamen trennt. Beispiel: C<1#Film, Serie|2#Horror|8#16!^colon^!9> =item B<29 – Wiederholungen vermeiden> Flag mit den Werten 0 = nein, 1 = ja. =item B<30 – Erlaubte Wiederholungen> Integer im Wertebereich von 0 bis 99. =item B<31 – Titel vergleichen (für den Test auf Wiederholungen)> Flag mit den Werten 0 = nein, 1 = ja. =item B<32 – Untertitel vergleichen (für den Test auf Wiederholungen)> Enumeration mit den Werten 0 = nein, 1 = ja, 2 = falls vorhanden. =item B<33 – Beschreibung vergleichen (für den Test auf Wiederholungen)> Flag mit den Werten 0 = nein, 1 = ja. =item B<34 – Kategorien vergleichen (für den Test auf Wiederholungen)> Integer, in dem jedes Bit eine erweiterte EPG-Kategorie repräsentiert. Die erste in F angegebene EPG-Kategorie wird durch Bit 0 (0x01) dargestellt, die zweite durch Bit 1 (0x02) usw. Ein Wert ungleich Null aktiviert den Vergleich. B Die Kennungen der erweiterten EPG-Kategorien (Feld 1 eines EPG-Kategorieeintrags) sind für die Kodierung des Bitfelds nicht von Bedeutung. Änderungen der EPG-Kategorien erfordern gegebenenfalls eine Aktualisierung von Suchtimern. =item B<35 – Nur innerhalb von ... Tagen> Integer im Wertebereich von 0 bis 999. =item B<36 – Aufzeichnungen nach ... Tagen löschen> Integer im Wertebereich von 0 bis 999. =item B<37 – Bis zu ... Aufzeichnungen behalten> Integer im Wertebereich von 0 bis 999. =item B<38 – Umschalten ... Minuten vor Start> Integer im Wertebereich von 0 bis 99; nur relevant für 'Aktion' = 2. =item B<39 – Pause, wenn ... Aufzeichnungen existieren> Integer im Wertebereich von 0 bis 999. =item B<40 – Ausschlusslisten verwenden> Enumeration mit den Werten 0 = nur globale, 1 = Auswahl, 2 = alle, 3 = keine. =item B<41 – Genutzte Ausschlusslisten> Liste mit Kennungen von Ausschlusslisten, jeweils per C<|> getrennt; nur relevant für 'Ausschlusslisten verwenden' = 1. =item B<42 – Unschärfetoleranz> Integer im Wertebereich von 1 bis 9, der als Schwellwert für die unscharfe Suche dient; nur relevant für 'Suchmodus' = 5. =item B<43 – Im Favoriten-Menü verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<44 – Layout der Suchergebnisse im Favoriten-Menü> Integer, der auf eine in F spezifizierte Vorlage für Suchergebnisse verweist. Der Wert ist der Index (beginnend bei 0) innerhalb aller Einträge mit dem Präfix C; Details finden sich unter "Anpassen der EPG-Menüs" in B(4). B Dieses Feld ist nur relevant, wenn mehrere Vorlagen für Suchergebnisse vorliegen. =item B<45 – Suchtimer automatisch löschen> Enumeration mit den folgenden Werten: =over 4 =item 0 = nicht löschen =item 1 = nach einer Anzahl von Aufnahmen löschen (Feld 46) =item 2 = nach einer Anzahl von Tagen nach der ersten Aufnahme löschen (Feld 47) =back Z<> =item B<46 – Nach ... Aufnahmen löschen> Integer im Wertebereich von 0 bis 999; nur relevant für 'Suchtimer automatisch löschen' = 1. =item B<47 – Nach ... Tagen nach erster Aufnahme löschen> Integer im Wertebereich von 0 bis 999; nur relevant für 'Suchtimer automatisch löschen' = 2. =item B<48 – Erster Tag (als Suchtimer verwenden ab)> Datum und Zeit in I-Notation (Sekunden seit dem 01.01.1970, 00:00 UTC), ab wann der Suchtimer aktiv ist; ein Wert von 0 deaktiviert die Prüfung. B Jeder beliebige I-Wert ist zulässig. Liegt die Zeit innerhalb eines Tages (anstelle von Mitternacht), bleibt der Timer inaktiv, bis diese Zeit erreicht ist. =item B<49 – Letzter Tag (als Suchtimer verwenden bis)> Datum und Zeit in I-Notation (Sekunden seit dem 01.01.1970, 00:00 UTC), bis wann der Suchtimer aktiv ist; ein Wert von 0 deaktiviert die Prüfung. B Jeder beliebige I-Wert ist zulässig. Liegt die Zeit innerhalb eines Tages (anstelle von Mitternacht), bleibt der Timer aktiv, bis diese Zeit erreicht ist. =item B<50 – Übereinstimmung von erweiterten EPG-Kategorien> Enumeration mit den folgenden Werten: =over 4 =item 0 = alle Kategorien =item 1 = alle außer fehlende Kategorien =item 2 = mindestens eine Kategorie =back Erlaubt die Berücksichtigung von Sendungen mit fehlenden oder nur teilweise übereinstimmenden Kategorien. Ohne andere ausreichend einschränkende Kriterien könnte dies jedoch zu einer enormen Anzahl von Suchergebnissen führen. =item B<51 – Ton anschalten> Flag mit den Werten 0 = nein, 1 = ja; nur relevant für 'Aktion' = 2 oder 3. =item B<52 – Minimale Übereinstimmung in Prozent> Integer im Wertebereich von 1 bis 100, der festlegt, wie groß die erforderliche Übereinstimmung beim Vergleich der Beschreibungen von Sendungen mindestens sein muss, um Wiederholungen zu vermeiden; nur relevant für 'Beschreibung vergleichen' = 1. =item B<53 – Inhaltskennungen> String mit zu prüfenden Inhaltskennungen. Eine Inhaltskennung ist ein Wert von 0 bis 255 (siehe DIN EN 300 468, Tabelle 18), der als zweistellige Hexadezimalzahl kodiert ist. Das obere Nibble (Bits [7..4]) repräsentiert eine Gruppe von Inhalten, das untere Nibble (Bits [3..0]) eine Kennung für einen bestimmten Inhalt innerhalb dieser Gruppe. Die erste Kennung einer Gruppe mit dem Wert 0 ist üblicherweise eine allgemeine Charakterisierung dieser Gruppe (bspw. 0x10 = Film, Drama), wohingegen die Werte 1..15 eine untergeordnete, feinere Charakterisierung darstellen (wie 0x11 = Detektiv, Thriller; 0x14 = Komödie). Die Inhaltsgruppe 11 (besondere Merkmale) entspricht jedoch nicht diesem Schema (wie etwa ersichtlich anhand von 0xB0 = Originalsprache; 0xB1 = schwarzweiß), weshalb für deren Inhaltskennungen eine eigene Art der Übereinstimmung gewählt werden kann. Die zu prüfenden Inhaltskennungen sind die Verkettung ihrer zweistelligen Hexadezimalzahlen ohne Trennzeichen. B Die Inhaltskennungen 17 (Detektiv, Thriller), 20 (Komödie) und 176 (Originalsprache) werden als C<1114B0> kodiert. Die Felder 55 (Inhaltskategorien) und 56 (besondere Merkmale) legen fest, wann eine Sendung den ausgewählten Inhaltskennungen entspricht. Ein leerer String bewirkt, dass Inhaltskennungen nicht geprüft werden. =item B<54 – Zeitpunkt vergleichen (für den Test auf Wiederholungen)> Enumeration mit den Werten 0 = nein, 1 = gleicher Tag, 2 = gleiche Woche, 3 = gleicher Monat. =item B<55 – Übereinstimmung von Inhaltskategorien> Enumeration mit den folgenden Werten: =over 4 =item 0 = alle gewählten Kennungen =item 1 = eine der gewählten Kennungen =item 2 = eine gewählte Kennung je Gruppe =back Die Optionen 0 und 1 prüfen über alle Kategoriengruppen hinweg, verwenden aber dennoch die Einstellung aus Feld 56 zur Überprüfung der besonderen Merkmale. Daher können Kombinationen wie 'UND' für die Kategoriengruppen und 'ODER' innerhalb der besonderen Merkmale auftreten und umgekehrt. Option 2 hingegen verwendet einen zweistufigen Ansatz: Zunächst werden die Kategoriengruppen mit ausgewählten Inhaltskennungen geprüft (Gruppen ohne ausgewählte Inhaltskennungen entfallen). Eine Kategoriengruppe stimmt überein, wenn eine Sendung mindestens eine der für die Gruppe gewählten Inhaltskennungen enthält ('eine gewählte', ODER), mit Ausnahme der Gruppe der besonderen Merkmale, die stets die Einstellung aus Feld 56 verwendet. Abschließend müssen alle geprüften Kategoriengruppen übereinstimmen ('je Gruppe', UND). Nur relevant, wenn 'Inhaltskennungen' nicht leer ist. =item B<56 – Übereinstimmung besonderer Inhaltsmerkmale> Enumeration mit den Werten 0 = alle gewählten Kennungen, 1 = eine der gewählten Kennungen; nur relevant, wenn 'Inhaltskennungen' nicht leer ist. =item B<57 – Altersfreigabe verwenden> Flag mit den Werten 0 = nein, 1 = ja. =item B<58 – Minimale Altersfreigabe> Integer im Wertebereich von 0 bis 18. =item B<59 – Maximale Altersfreigabe> Integer im Wertebereich von 0 bis 18. =back Der Eintrag eines Suchtimers wird als gültig betrachtet, wenn er mindestens die ersten 11 Felder abdeckt. Die Felder ("Parameter") eines Suchtimers sind durch C<:> getrennt. Daher wird ein C<:> in Strings, wie etwa dem Suchbegriff oder dem Aufnahmeverzeichnis, durch C<|> kodiert. Sollte ein C<|> ebenfalls Teil des Strings sein, wie beispielsweise in regulären Ausdrücken, wird es durch C kodiert (was zwar unschön, aber aus Gründen der Abwärtskompatibilität erforderlich ist). Felder, die von einem anderen Feld abhängen und aufgrund der Einstellung dieses Feldes belanglos sind, können (und sollten) leer gelassen werden. Beispielsweise kann ein Suchtimer ohne Zeitbeschränkung die Felder 'Start nach' und 'Start vor' leer lassen: =over 4 1:Meine Lieblingsserie: 0::: 0::0:0 ^^^^ =back Darüber hinaus werden führende und nachfolgende Leerzeichen um einen Feldwert herum verworfen. Dies bedeutet jedoch, dass Strings nicht mit Leerzeichen beginnen oder enden können. B Strings in Feldern dürfen nicht in einfachen oder doppelten Anführungszeichen eingeschlossen werden, auch wenn dies in der Beschreibung so dargestellt sein sollte. Diese Zeichen wurden bei der Kompilierung dieser Man-Page eingefügt. Siehe auch B(5). =head1 3. Ablauf des Suchvorgangs Zunächst wird für jede Sendung ein temporärer Suchtext erstellt, in dem Titel, Untertitel und Beschreibung durch C<~> getrennt sind: =over 4 Title~Untertitel~Beschreibung =back Abhängig von den Einstellungen von C, C und C werden die entsprechenden Komponenten entweder eingefügt oder bleiben leer. Wenn C nicht gesetzt ist, werden Suchtext und Suchbegriff in Kleinbuchstaben umgewandelt. Je nach Suchmodus wird der Suchbegriff dann im Suchtext gesucht: =over 4 =item B Stimmt überein, wenn der Suchbegriff an einer beliebigen Stelle im Suchttext gefunden wird. =item B =item B Zunächst wird der Suchbegriff in einzelne Worte zerlegt, die durch eines der Zeichen C<,;|~> oder Leerzeichen getrennt sind. Die Suche ist erfolgreich, wenn mindestens ein Wort oder alle Worte im Suchtext vorkommen. =item B Stimmt überein, wenn Suchbegriff und Suchtext identisch sind. =item B Die Suche erfolgt anhand eines regulären Ausdrucks. Hierfür werden zwei Standards unterstützt: erweiterte POSIX- und Perl-kompatible reguläre Ausdrücke (PCRE) (siehe F). B Anders als etwa in Perl, sind führende und abschließende C als Begrenzungszeichen für den Suchbegriff nicht erforderlich. =item B Vergleicht den Suchbegriff anhand des Levenshtein-Distanz-Algorithmus. Der Wert des Feldes C bestimmt, wie groß die Abweichung sein darf. B Der Suchbegriff ist bei unscharfer Suche auf 32 Zeichen begrenzt. =back War die Suche bisher erfolgreich, werden die weiteren Kriterien (Startzeit, Dauer, Wochentag etc.) geprüft. =head1 4. Wie funktionieren Suchtimer? Bei jedem Update sortiert das Plugin die Suchtimer zunächst nach Timer-Priorität (absteigend) und Suchbegriff, bevor es nach neuen Treffern sucht. Wird ein neuer Treffer gefunden, wird ein neuer Timer erstellt. Bei Serienaufnahmen wird der Untertitel an das Aufnahmeverzeichnis angefügt. Viele Anbieter befüllen den Untertitel allerdings erst wenige Tage vor der Sendung. Ist dieser nicht verfügbar, verwendet das Plugin vorübergehend Datum und Uhrzeit anstelle des Untertitels und ersetzt diese, sobald der Untertitel verfügbar ist. Start- und Endzeitpunkte von Sendungen variieren oft leicht. Um eine Vielzahl von Timern für die gleiche Sendung zu vermeiden, prüft das Plugin, ob sich die Start- und Stoppzeit eines anderen Timers um nicht mehr als 10 Minuten unterscheiden. Bei einer Dauer von weniger als 10 Minuten wird stattdessen die Dauer verglichen. Bei einer Übereinstimmung wird ein vorhandener Timer angepasst, andernfalls wird ein neuer Timer erstellt; inaktive Timer werden nicht aktualisiert. Manuell vorgenommene Einstellungen von Priorität oder Lebensdauer werden bei Updates nicht verändert. Wenn C auf die Variante "Ankündigen (kein Timer)" eingestellt ist, wird kein Timer erstellt. Stattdessen erfolgt eine Benachrichtigung über die Sendung: Bei jedem Suchvorgang wird eine OSD-Meldung angezeigt, jedoch nur, wenn für das Ereignis kein Timer vorhanden war. Wenn Timer erstellt oder geändert wurden, wird gemäß dem eingestellten Zeitplan eine E-Mail versendet. =head1 5. Wie stößt man ein Timer-Update an? Die Aktualisierung der Suchtimer erfolgt in einem eigenen Thread. Eine Aktualisierung kann auf verschiedene Weise ausgelöst werden: =over 4 =item B Die Suchtimer werden einige Sekunden nach dem Start des VDR aktualisiert. Nach Abschluss der Aktualisierung legt die Setup-Option C fest, in welchen Intervallen weitere Aktualisierungen durchgeführt werden sollen. =item B Der Thread überwacht die Datei C<.epgsearchupdate> im Konfigurationsverzeichnis des Plugins. Nach der Ausführung von: =over 4 touch /pfad_zur_datei/.epgsearchupdate =back wird eine Aktualisierung durchgeführt. Dies bietet eine einfache Möglichkeit, beispielsweise beim Ausführen eines Skripts eine Aktualisierung anzustoßen. =item B Der Aufruf von C » C oder das Drücken von C<3> im Menü C stößt ebenfalls eine Aktualisierung an. =item B Der Dienst C kann über die Service-Schnittstelle des VDR von anderen Plugins genutzt werden. Der Aufrufer kann eine OSD-Benachrichtigung anfordern, sobald die Aktualisierung abgeschlossen ist. =back =head1 6. Quellen für das Menü C Dieses Menü zeigt Verzeichnisse an, die sowohl für Suchtimer als auch für normale Timer verwendet werden können. Die angezeigten Elemente stammen aus folgenden Quellen: =over 2 =item * aktuelle Aufnahmeverzeichnisse =item * in Timern genutzte Verzeichnisse =item * in Suchtimern genutzte Verzeichnisse =item * in F definierte Verzeichnisse, siehe B(5) =item * Verzeichnisse aus der Datei F des VDR =back Das Menü führt diese Verzeichnisse zusammen und zeigt jedes Verzeichnis nur einmal an. Mit der Taste C lässt sich die Ebene der angezeigten Verzeichnisse ändern. Verzeichniseinträge, die EPG-Kategorievariablen wie C<%Genre%> enthalten, werden immer vor anderen Verzeichniseinträgen angezeigt. Sie sind zudem nicht von der Ebene abhängig, sondern werden immer mit ihrem vollständigen Verzeichnispfad angezeigt. Wird dieses Menü über das Timer-Bearbeitungsmenü aufgerufen und enthält der ausgewählte Eintrag bereits die Variablen C<%Title%> oder C<%Subtitle>, wird der Eintrag C des Timers gelöscht, da Titel oder Untertitel bereits im Eintrag C enthalten sind. Die Liste der Verzeichnisse kann auch über den SVDRP-Befehl C abgerufen werden. =head1 7. Sprachabhängige Kommandos für die Programmübersicht Wenn eine sprachabhängige Befehlsliste gewünscht wird, kann die Datei F in die entsprechende OSD-Sprache übersetzt und unter dem Dateinamen FLOCE.conf> gespeichert werden. Dabei entspricht FLOCE> dem Sprachcode aus F: =over 4 { "eng,dos", "deu,ger", "slv", "ita", "dut,nla,nld", "por", "fra,fre", "nor", "fin,smi", "pol", "esl,spa", "ell,gre", "sve,swe", "rom,rum", "hun", "cat,cln", "rus", "hrv", "est", "dan" // die vollständige Liste // findet sich im Quellcode } =back Wenn für eine Sprache mehrere Codes verfügbar sind (bspw. C), kann ein beliebiger davon verwendet werden. Liegt eine Datei entsprechend der im VDR eingestellten OSD-Sprache vor, wird diese geladen. Existiert eine solche jedoch nicht, wird stattdessen versucht, die Datei F zu laden. Siehe auch B(5). =head1 8. Nutzung von anderen Plugins oder Skripten Die EPG-Suche und andere Funktionen können von anderen Plugins oder Skripten genutzt werden. Es gibt zwei Ansätze: =head2 8.1. Kommandodatei (vorzugsweise per Skript) Das Plugin überwacht, ob in seinem Konfigurationsverzeichnis die Datei F<.epgsearchrc> vorhanden ist. Diese kann die folgenden Zeilen beinhalten: =over 4 Search=Suchbegriff SearchMode=x // 0 = Ausdruck, 1 = alle (Standard), 2 = ein Wort, 3 = exakt, 4 = reg. Ausdruck ChannelNr=x // sucht auf einem bestimmen Kanal, wenn angegeben UseTitle=x // 1 (Standard) oder 0 UseSubtitle=x // 1 (Standard) oder 0 UseDescr=x // 1 (Standard) oder 0 =back Beim Start sucht EPGSearch nach dieser Datei und gibt, falls gefunden, die Suchergebnisse für die darin angegebene Suche zurück. Danach wird die Datei gelöscht. Später kann über die SVDRP-Schnittstelle eine weitere Suche gestartet werden. Angenommen, die Taste C wurde EPGSearch zugewiesen (siehe B(1), Abschnitt 5), kann EPGSearch einfach über F aufgerufen werden: =over 4 svdrpsend HITK green =back Ein Beispielskript F, das nach Wiederholungen einer Aufnahme sucht, befindet sich im Unterverzeichnis F von EPGSearch. =head2 8.2 Plugin-Schnittstelle (aus anderen Plugins heraus) Ein Plugin kann mit nur wenigen Codezeilen zwei Funktionen von EPGSearch direkt aufrufen: =over 2 =item * Suche im EPG und Anzeige der Ergebnisse =item * Aufruf des erweiterten Timer-Editieren-Menüs =back Ein einfaches Beispiel-Plugin im Verzeichnis F von EPGSearch (F) demonstriert die Nutzung. =head1 9. Die SVDRP-Schnittstelle EPGSearch implementiert eine SVDRP-Schnittstelle, auf die folgendermaßen zugegriffen werden kann: =over 4 svdrpsend PLUG epgsearch LSTS =back In den folgenden Abschnitten werden die an der SVDRP-Schnittstelle verfügbaren Befehle beschrieben. =head2 9.1 Suche und Suchtimer =over 4 =item B [I] Ruft alle Sucheinträge oder nur den mit der angegebenen I ab. Das Format der zurückgegebenen Daten entspricht dem von F (siehe oben). =item B I Legt einen neuen Sucheintrag an. Das Datenformat von I entspricht dem von F (siehe oben). Die eindeutige Kennung im ersten Feld wird ignoriert. EPGSearch weist einem neuen Sucheintrag immer die nächste freie Kennung zu und gibt diese in der Antwort bekannt: =over 4 svdrpsend PLUG epgsearch NEWS "0:Mustersuche:::::::::" 220 HostName SVDRP VideoDiskRecorder 2.7.7; ... 900 search 'Mustersuche' (with new ID 6) added 221 HostName closing connection =back =item B I Ändert einen bestehenden Sucheintrag. Das Datenformat von I entspricht dem von F (siehe oben). Die Kennung im ersten Feld gibt den zu ändernden Sucheintrag an. B Ein leeres Feld (C<::>) wird ignoriert. Die Werte der Felder C, C und C werden jedoch gelöscht, wenn keine Werte angegeben wurden. =item B I ON|OFF Aktiviert (C) oder deaktiviert (C) die Option C für den Sucheintrag mit der angegebenen I. =item B I Löscht den Sucheintrag mit der angegebenen I. =item B ON|OFF Startet (C) oder stoppt (C) den im Hintergrund laufenden Suchtimer-Thread. =item B [OSD] Aktualisiert die Suchtimer. Mit dem optionalen Schlüsselwort C wird eine OSD-Meldung nach Abschluss der Aktualisierung angezeigt. =item B Liest die Datei F erneut ein, beispielsweise nachdem sie von einem externen Tool geändert wurde. =item B Liest die Datei F erneut ein, beispielsweise nachdem sie von einem externen Tool geändert wurde. =item B I Sucht im EPG nach Sendungen. Das Datenformat von I entspricht dem von F (siehe oben). Für jede gefundene Sendung wird eine Zeile im Format des VDR-Befehls C zurückgegeben. Diese beinhaltet die folgenden Felder: =over 4 Timer : // 1 = neuen Timer anlegen Kanal : // Kanalnummer des VDR Datum : // Datum, an dem die Sendung läuft [JJJJ-MM-TT] Beginn : // Beginn der Sendung [HHMM] Ende : // Ende der Sendung [HHMM] Priorität : // Priorität bei Aufnahme Lebensdauer : // Lebensdauer bei Aufnahme Datei : // Datei-Eintrag des Timers =back Beispiel: =over 4 svdrpsend PLUG epgsearch FIND "0:The Rookie:::::::::" 220 HostName SVDRP VideoDiskRecorder 2.7.7; ... 900 NEWT 1:39:2025-08-07:1753:1845:50:99:The Rookie: 221 HostName closing connection =back Ein Suchergebnis kann somit unmittelbar zur Programmierung eines Timers für die entsprechende Sendung verwendet werden. B Wenn keine VPS-Kennung vorliegt, beinhalten Datum sowie Start- und Stoppzeit die in den Parametern angegebenen Zeiten für Vor- und Nachlauf. =item B I[|I|...] Führt die Suche mit der angegebenen I aus. Weitere IDs können, jeweils mit C<|> als Trennzeichen, angefügt werden. Für jede gefundene Sendung wird eine Zeile in folgendem Format zurückgegeben: =over 4 Suche : // Eindeutige Kennung der Suche Sendung : // Eindeutige Kennung der Sendung im VDR Titel : // Titel der Sendung, wobei C<:> durch C<|> ersetzt ist Untertitel : // Untertitel der Sendung, wobei C<:> durch C<|> ersetzt ist Beginn : // Beginn der Sendung in Epoch-Notation (Sekunden seit dem 01.01.1970, 00:00 UTC) Ende : // Ende der Sendung in Epoch-Notation (Sekunden seit dem 01.01.1970, 00:00 UTC) Kanal : // Kanalkennung im VDR-Format (bspw. S19.2E-1-1101-28106) Start : // Startzeitpunkt des Timers in Epoch-Notation; nur gültig wenn 'Timer' > 0 Stop : // Stoppzeit des Timers in Epoch-Notation; nur gültig wenn 'Timer' > 0 Datei : // Datei-Eintrag des Timers; nur gültig wenn 'Timer' > 0 Timer : // 0 = ohne Timer, 1 = aktiver Timer, ... (Timer-Flags des VDR) =back Beispiel: =over 4 svdrpsend PLUG epgsearch QRYS 0 220 HostName SVDRP VideoDiskRecorder 2.7.7; ... 900 0:3124:The Rookie:Jagdfieber:1756901400:1756903800:S19.2E-1-1011-11130:1756901280:1756904400:The Rookie~Jagdfieber:1 221 HostName closing connection =back =item B I Ruft die Ergebnisse einer Suche anhand der angegebenen I ab. Das Datenformat von I entspricht dem von von F (siehe oben), wobei die eindeutige Kennung im ersten Feld ignoriert wird. =item B [I] Ruft die Ergebnisse für das Favoriten-Menü ab; das Datenformat entspricht dem von C. Der optionale Parameter gibt die Anzahl der Zeitspanne in Stunden an (standardmäßig 24 Stunden). =item B

[NOW|PRG|SUM] Ruft eines der Menüs von EPGSearch oder die Beschreibung der aktuellen Sendung auf: =over 2 =item * NOW – Was läuft jetzt? =item * PRG – Programmübersicht =item * SUM – Beschreibung der Sendung =back B Das Menü wird nur angezeigt, wenn zu diesem Zeitpunkt kein anderes Menü geöffnet ist. Andernfalls wird das aktuell angezeigte Menü geschlossen. Daher ist dieser Befehl nicht vollständig deterministisch. =back =head2 9.2 Kanalgruppen =over 4 =item B [I] Ruft alle Kanalgruppen oder nur die mit I bezeichnete ab. Das Format der zurückgegebenen Daten entspricht dem von F. =item B I Erstellt eine neue Kanalgruppe. Das Datenformat von I entspricht dem von F. =item B I Ändert eine bestehende Kanalgruppe. Das Datenformat von I entspricht dem von F. Der Name im ersten Feld gibt die zu ändernde Kanalgruppe an. =item B I|I Benennt eine bestehende Kanalgruppe um. =item B I Löscht eine bestehende Kanalgruppe. =back =head2 9.3 Ausschlusslisten =over 4 =item B [I] Ruft alle Ausschlusslisten oder nur die mit der angegebenen I ab. Das Format der zurückgegebenen Daten entspricht dem von F. =item B I Legt eine neue Ausschlussliste an. Das Datenformat von I entspricht dem von F. Die eindeutige Kennung im ersten Feld wird ignoriert. EPGSearch weist einer neuen Ausschlussliste immer die nächste freie Kennung zu und gibt diese in der Antwort bekannt: =over 4 svdrpsend PLUG epgsearch NEWB "0:Musterliste:::::::::::::::::" 220 HostName SVDRP VideoDiskRecorder 2.7.7; ... 900 blacklist 'Musterliste' (with new ID 3) added 221 HostName closing connection =back =item B I Löscht die Ausschlussliste mit der angegeben I. =item B I Ändert eine bestehende Ausschlussliste. Das Datenformat von I entspricht dem von F. Der Name im ersten Feld gibt die zu ändernde Ausschlussliste an. B Ein leeres Feld (C<::>) wird ignoriert. Die Werte der Felder C und C werden jedoch gelöscht, wenn keine Werte angegeben wurden. =back =head2 9.4 Suchvorlagen =over 4 =item B [I] Ruft alle Suchvorlagen oder nur die mit der angegebenen I ab. Das Format der zurückgegebenen Daten entspricht dem von F (siehe oben). =item B I Legt eine neue Suchvorlage an. Das Datenformat von I entspricht dem von F (siehe oben). Die eindeutige Kennung im ersten Feld wird ignoriert. EPGSearch weist einer neuen Suchvorlage immer die nächste freie Kennung zu und gibt diese in der Antwort bekannt: =over 4 svdrpsend PLUG epgsearch NEWT "0:Mustervorlage:::::::::" 220 HostName SVDRP VideoDiskRecorder 2.7.7; ... 900 search template 'Mustervorlage' (with new ID 4) added 221 HostName closing connection =back =item B I Ändert eine bestehende Suchvorlage. Das Datenformat von I entspricht dem von F (siehe oben). Die Kennung im ersten Feld gibt die zu ändernde Suchvorlage an. B Ein leeres Feld (C<::>) wird ignoriert. Die Werte der Felder C und C werden jedoch gelöscht, wenn keine Werte angegeben wurden. =item B I Löscht die Suchvorlage mit der angegebenen I. =item B [I] Gibt die eindeutige Kennung (ID) der Standardsuchvorlage zurück. Bei Angabe einer I wird die entsprechende Suchvorlage als neue Standardvorlage festgelegt. =back =head2 9.5 Erweiterte EPG-Kategorien =over 4 =item B [I] Ruft alle in F definierten EPG-Kategorien oder nur die mit der angegebenen I ab. Das Format der zurückgegebenen Daten entspricht dem von F. =back =head2 9.6 Timer-Konflikte =over 4 =item B [REL] Ruft die aktuellen Timer-Konflikte ab. Mit der Option C werden nur relevante Konflikte aufgeführt. Die Ergebnisliste sieht ähnlich aus wie für das folgende Beispiel zweier Timer-Konflikte zu einer bestimmten Zeit: =over 4 1190232780:152|30|50#152#45:45|10|50#152#45 =back C<1190232780> ist der Zeitpunkt des Konflikts in I-Notation (Sekunden seit dem 01.01.1970, 00:00 UTC). Diesem folgt die Liste der Timer, die miteinander in Konflikt stehen. C<152|30|50#152#45> ist die Beschreibung des ersten konfliktbehafteten Timers: =over 2 =item * C<152> ist die ID des Timers, mit der er auch im VDR-Kommando C geführt wird =item * C<30> ist der Prozentsatz des Timers, der aufgenommen würde (0..100) =item * C<50#152#45> ist die Liste aller von diesem Konflikt betroffenen Timer =back C<45|10|50#152#45> beschreibt einen weiteren Konflikt für diesen Zeitpunkt. =back =head2 9.7 Verschiedenes =over 4 =item B Ruft alle Verzeichnisse ab, die in Aufzeichnungen, Timern und Suchtimern verwendet werden oder in F definiert sind. =item B [I