You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							2712 lines
						
					
					
						
							88 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							2712 lines
						
					
					
						
							88 KiB
						
					
					
				| #!/bin/sh | |
|  | |
| # pr-auto-timer - automatic TV timer creation for neutrino | |
| 
 | |
| # Copyright (C) 2012-2013  Patrick Reinhardt, pr-cs <at> reinhardtweb.de | |
| #               2013-2014  nadine | |
| #                    2014  LtCmdrLuke | |
| #               2013-2017  NI-Team | |
| # | |
| # 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 St, Fifth Floor, Boston, MA 02110, USA | |
| 
 | |
| # Related files: | |
| #	Configuration:		/var/tuxbox/config/pr-auto-timer.conf | |
| #	Timer rules:		/var/tuxbox/config/pr-auto-timer.rules | |
| #	Neutrino plugin:	/lib/tuxbox/plugins/pr-auto-timer.{sh,cfg} | |
| 
 | |
| VERSION=0.40a | |
| 
 | |
| # Changelog: | |
| # | |
| # 0.40a by tewsbso (NI-Team) | |
| #	-fix msgbox-calls for larger OSD-resolutions | |
| # | |
| # 0.40 by vanhofen (NI-Team) | |
| #	-whitespace cleanup | |
| #	-reformatting awk-blocks (just for my readability) | |
| #	-change some german words in code | |
| #	-search-code in find_show_by_regex() fundamentally revised | |
| #	-find_show_by_start_sec() now returns same format as find_show_by_regex() | |
| # | |
| # 0.39 by vanhofen (NI-Team) | |
| #	-allow to search for every show; just filter by include/exclude expressions | |
| #	 To search for every show that contains "Mikrokosmos" in EPG-data use: | |
| #		Das Erste HD;*;*,+Mikrokosmos;R | |
| # | |
| # 0.38b by tewsbso (NI-Team) | |
| #	-find correct Sky Cinema channel | |
| #	 add exclude channels: Action Comedy Emotion Family Hits Nostalgie | |
| # | |
| # 0.38a by tewsbo (NI-Team) | |
| #	-Bugfix: New movies on Sky Cinema; align to new format on website again | |
| # | |
| # 0.38 by tewsbo (NI-Team) | |
| #	-Bugfix: New movies on Sky Cinema; align to new format on website | |
| # | |
| # 0.37b by vanhofen (NI-Team) | |
| #	-align to new controlapi calls | |
| # | |
| # 0.37a by vanhofen (NI-Team) | |
| #	-bugfix for: find correct Sky Cinema channel | |
| # | |
| # 0.37 by vanhofen (NI-Team) | |
| #	-find correct Sky Cinema channel | |
| #	-add more Sky Cinema channels | |
| #		(in conf-file: SKY_CINEMA="SD|HD|1|1HD|24|24HD[;DOW[,TIMESPAN]][;RECDIR]") | |
| # | |
| # 0.36a by nadine | |
| #	-Fixed IIClude Filter One Word (~) | |
| # | |
| # 0.36 by LtCmdrLuke | |
| #	-Bugfix: Empty timestamp in some rare cases causes and error on the console (just cosmetic) | |
| #	-Bugfix: Fixed log message in dry-run mode about amount of broken records (just cosmetic) | |
| #	-Bugfix: Detecting the EXTRA_TIME Before recording is broken in 2.7 (maybe also in 2.6?) due to the introduction of a new value | |
| #	 ZAPTO_EXTRA_TIME in timerd.conf. This is fixed now. | |
| # | |
| # 0.35 by LtCmdrLuke | |
| #	-Bugfix: The path in $ME.show_history is now correctly updated when the index is re-generated and old files have been moved | |
| #	-Bugfix/Feature: Should now also work when box is in radio mode. When starting, pr-auto-timer switches the box to tv and afterwards back. | |
| #	-Bugfix/workaround: Added a simple function to convert the most common html-entities occurring in titles (" & and '). | |
| #	 This function uses only shell replacement and is kind of inefficient. Furthermore for full conversion we would need 'recode' or even | |
| #	 better, neutrino[HD] should be consistent when delivering data in EPG and record-xml. | |
| #	-New feature: Added possibility to execute arbitrary pre- and post-actions. This feature can be activated by giving arbitrary commands | |
| #	 in the new settings in $ME.conf: PRE_ACTION and POST_ACTION. "PRE_ACTION" is executed after initializations, but before processing | |
| #	 any rules and "POST_ACTION" is executed after processing all rules, before exiting. Updated also $ME.conf.template to reflect changes. | |
| # | |
| # 0.34 by LtCmdrLuke | |
| #	-Added command line option "-p|--post-action [x]" to override any configured post-actions with the optional argument x. | |
| #	 This is helpful, when you configured the box to perform e.g. a reboot after running pr-auto-timer and you need it to | |
| #	 avoid doing exactly that when you're testing the script. | |
| #	-Using command line option -d|--dry-run also prevents any post-action | |
| #	-New option BROKEN_RECORD_HANDLING which controls the behavior when detecting broken records while creating the history index; | |
| #	 see $ME.conf.template for full explanation and possible settings | |
| #	-Bugfix when cleaning the history file (if there where more events starting in exactly the same second, only one of them survived the cleaning) | |
| #	-Automatically detect and upgrade .show_history prior to 0.33 - no need for any manual intervention. | |
| #	-Restructured code, i.e. grouped functions into sections - code is getting bigger and therefore harder to find things. Sections help.. | |
| # | |
| # 0.33 by LtCmdrLuke | |
| #	-Added possibility to limit the size of $ME.show_history by setting MAX_HISTORY_ENTRIES in the conf file. Default is 1000. | |
| #	-Due to the above change, the format of $ME.show_history has changed, there it is necessary to delete the $ME.show_history and let | |
| #	 pr-auto-timer generate a new one from your files on the disk. If you have manual entries, they must be added again in the new file. | |
| #	 Use '1500000000' for unknown timestamps (somewhere in the future) and 'x' for unknown filenames. | |
| #	-Updated pr-auto-timer.conf.template to reflect this change | |
| #	-Improved log-readability by printing human-readable time strings in the most important messages. | |
| # | |
| # 0.32 by LtCmdrLuke | |
| #	-Added general handling logic for command-line options, call pr-auto-timer -h for an overview | |
| #	-Major changes in the logic for duplicate prevention | |
| #	 * The additional script gen_show_history is no longer needed - the functionality is fully integrated in pr-auto-timer | |
| #	 * The gen_show_history.sh-functionality is now accessible with the following command line options: | |
| #	   * -g|--gen-show-history   Generate and print the index from your previous recordings | |
| #	   * -t|--print-timer-index  Generate and print the future timer index. | |
| #	   * -d|--dry-run            Perform a dry-run, i.e. do not add/remove any timers. (provide first!) | |
| #	 * New config settings (updated also $ME.conf.template to reflect changes) | |
| #	   * AUTOGEN_SHOW_HISTORY=1 - automatically searches for previously recorded files, when the D-flag is used | |
| #	   * MYRECORDPATHS - a semicolon separated list of paths to search for previously recorded files | |
| #	 * Future timers and already existing records are separated now: When using the D-flag and a new timer is added, it is also added to a | |
| #	   internal future timer index. Therefore manually deleted timers or failed recordings will no longer be detected as duplicates. This | |
| #	   feature works by reading all timers from the box upon first usage of the D-flag. For each timer, than the EPg is searched for the | |
| #	   necessary data about the show to compare with new timers. | |
| #	-Bugfix: Now the entire info2 is searched for a given regular expression - until now lines between newlines not starting or ending | |
| #	 with info2 have been omitted | |
| #	-Minor | |
| #	 * Added some quotation to protect special characters from shell in some special situations | |
| #	 * Re-ordered some code, for better readability and logical reasons | |
| # | |
| # 0.31 by LtCmdrLuke | |
| #	-Improved handling of .show_history for duplicate detection: | |
| #	  * show are stored in $ME.show_history if and only if the D-flag is present | |
| #	  * removed the N-flag, since the above change makes it obsolete | |
| #	  * This feature is still not final; handling the size of .show_history and automatic integration of already recorded stuff (i.e. obsoleting gen_show_history) is still missing | |
| #	-Bugfix: Added check if $HISTORY_FILE exists before trying to read it. | |
| #	-Improved duplicate detection (hopefully) as follows: | |
| #	  Until now, only the EPG-title and info1 was used to detect the same show again. However, some channels do not set info1 to anything meaningful. | |
| #	  In this case, pr-auto-timer now uses the first 30 characters (INFO2_CHARACTERS) of info2 to differentiate between shows. Obviously this fails, if the channel EPG | |
| #	  is changed by the provider for the repetition. | |
| #	-Minor improvement of the log-readability | |
| #	-Updated the .rules.template files to reflect the newly added flags since 0.30 (W & M) | |
| #	-Minor cleanup: | |
| #	  * Renamed ignore_empty_info --> allow_empty_info (reflects better its purpose) | |
| #	  * Rewrote parse_flags in a cleaner way (now allows for a log message if unknown flag was used) | |
| # | |
| # 0.30 by nadine | |
| #	-Maximale Anzahl an parallelen Aufnahmen parametrierbar zwischen 1 bis 8. Standard:	MAX_RECORDS=8 | |
| #	-History wird bei leerer Kurzbeschreibung (info1) ignoriert. Abschaltbar für z.B. Filme durch Flag 'M' | |
| #	-Neues Flag 'N' hinzugefügt. Ist dieses angegeben, werden Duplicate und Flag 'D' ignoriert. Die Aufnahme kommt dann auch nicht ins History-File | |
| #	-Neues Flag 'W' hinzugefügt. Da werden ungeachtet der Einstellung von DEL_TIMER_REFRESH Timer wiederholt hinzugefügt. | |
| # | |
| # 0.29 by LtCmdrLuke | |
| #	-Extended check for max tuners with transponder information, i.e. now max tuners allows currently for an unlimited number | |
| #	 of recordings, as long as only at most MAX_TUNERS transponders are needed. The number of concurrent recordings could also be limited | |
| #	 in future versions - the necessary logic for counting the concurrent recordings is now implicitly available. | |
| #	 Before adding a timer, pr-auto-timer calculates the needed number of transponders and also the max number of concurrent recordings | |
| #	 as if the new timer would have been added. Check the logfile to see if timers got rejected because there aren't enough free tuners. | |
| #	-Bugfix: define another "ret" (in prevent_timer_by_timespan) as local to prevent side-effects (same effect as the bug wich was fixed in 027) | |
| #	-Bugfix: Quotation added to the log-message in parse_dowgroup, to prevent ugly log message | |
| #	-Commented unnecessary log-message in parse_dowgroup | |
| #	-Added a function to get the transponder-id (tsid) from the service id (sid) | |
| # | |
| # 0.28 by nadine | |
| #	-Möglichkeit für die Angabe mehrerer Wochentage | |
| #	 Um die Tagesschau nur Montags, Mittwochs und Freitags um 20:00 Uhr aufzuzeichnen | |
| #		Das Erste HD;MonWedFri,20:00-21:00;Tagesschau | |
| # | |
| # 0.27 by LtCmdrLuke | |
| #	-added check if there are enough tuners available before setting a new timer. The number of available tuners can be set | |
| #	 in the pr-auto-timer.conf with the settings MAX_TUNERS. It defaults to 2. | |
| #	 If you set this value to a 1 lower, than actually available, your timer recordings will never interfere with your normal | |
| #	 TV watching. Obviously, if there are to few tuners available, or too many shows at the same time, those cannot be recorded. | |
| #	 However, if the show is repeated, auto-timer should find the repetition and record it then. | |
| #	-code cleanup: | |
| #		-renamed some functions to better reflect their purpose for better readability): | |
| #			* show_already_recorded --> find_already_recorded | |
| #			* add_timer2 --> add_historic_timer | |
| #			* find_timer2 --> find_historic_timer | |
| #			* find_timer --> find_neutrino_timer | |
| #		-declared some function variables local to prevent nasty side-effects (all shell variables are global unless explicitly | |
| #		 defined local) - this fixes a small bug which lead to adding the show to the show_history, even though the timer was not | |
| #		 added because it was previously deleted | |
| #		-clarified some log messages | |
| # | |
| # 0.26 by LtCmdrLuke | |
| #	-prevent recording of repetitions by adding a log of shows which are already covered by timers. The log is located unter | |
| #	 /var/tuxbox/config/pr-auto-timer.show_history. Currently the description (epg-title) and info1 (usually the episode title | |
| #	 for tv shows) is used to detect already recorded stuff. Each line of this files works as a ALL word exclude rule. | |
| #	-prevent recording can be activated with the flag D: | |
| #		NatGeo HD;*;Brain Games;D | |
| #		                        ^ | |
| #		                        | | |
| #		                        D=prevent Duplicates | |
| # | |
| # 0.25 by vanhofen (NI-Team) | |
| #	-get TV channels in radio mode | |
| #	-initialize DEL_TIMER_REFRESH=2 (off) | |
| #	-Add possibility to ignore EXTRA_TIME_START|END | |
| #		Das Erste HD;*;Tagesschau;I | |
| #		                          ^ | |
| #		                          | | |
| #		                          I=Ignore extra time additions. | |
| # | |
| # 0.24 by vanhofen (NI-Team) | |
| #	-change 'date' calls to align to older busybox-versions in originalimage | |
| #	-add helper function to simulate the nonexisting dos2unix in originalimage | |
| # | |
| # 0.23 by nadine | |
| #	-Shutdown hinzugefügt | |
| #		Wenn EPGscan nicht gefunden wird, gibt es folgende Optionen: | |
| #		END_SHUT_DOWN:[0]-lässt die Box an;1-fährt in Standby;2-fährt in DeepStandBy;3-fährt in PowerOff;4-startet neu | |
| #		SHUT_DOWN_ALSO_DAY:[0] - führt SHUT_DOWN nur zwischen 00:00 und 06:00 Uhr durch; 1 - führt SHUT_DOWN immer durch | |
| # | |
| # 0.22 by nadine | |
| #	-add_timer2 prüft nun, ob der Eintrag schon besteht. | |
| #	-Exclude ergänzt auf JEDES Wort. Wenn eines dieser Wörter gefunden wird, wird die Sendung nicht aufgezeichnet. | |
| #	 Für Serien, dessen Folgen nur einen kurzen Titel haben. So kann man vorhandene nacheinander angeben. | |
| #		Romance TV;*,04:00-18:15;Traumschiff,-Oman Bali Malaysia Vegas Savannah | |
| #	-Include ergänzt auf JEDES Wort. Wenn eines dieser Wörter gefunden wird, wird die Sendung aufgezeichnet. | |
| #	 Für Serien, dessen Folgen nur einen kurzen Titel haben. So kann man vorhandene nacheinander angeben. | |
| #		Romance TV;*,04:00-18:15;Traumschiff,~Oman Bali Malaysia Vegas Savannah | |
| #	-Weitere Paramater in der Config Datei werden ausgewertet (Für Funktion aus V0.21 (gelöschte Timer werden nicht mehr gesetzt) | |
| #		MAX_TMR2_COUNT=250 # Behält max. 250 Einträge in der Datei /var/tuxbox/config/pr-auto-timer.tmr beim aufräumen (Standard:250) | |
| #		DEL_TIMER_REFRESH=2 #Schaltet die Funktion ganz ab. (Standard:0) | |
| #		DEL_TIMER_REFRESH=1 #Führt im Hintergrund die Datei weiter, um nach wiedereinschalten sofort arbeiten zu können. (Standard:0) | |
| #	-Include Filter werden ignoriert, wenn keine Beschreibung gefunden wurde | |
| # | |
| # 0.21 by nadine | |
| #	-Groß- Kleinschreibung bei Sendungstitel egal | |
| #	-Include Wörter ermöglicht. Diese können ANSTATT der Exclude Wörter genutzt werden. Statt des ! kommt ein + | |
| #		Das Erste HD;*;Tagesschau,+include | |
| #	-einmal gesetzte Timer werden nicht nochmal gesetzt, auch wenn diese zwischenzeitlich manuell gelöscht wurden | |
| #	 sollen alle wieder gesetzt werden, dann die Datei /var/tuxbox/config/pr-auto-timer.tmr löschen | |
| #	 Einträge, dessen Stopzeit vor der aktuellen Zeit liegen, werden automatisch wieder entfernt, um die Dateigröße | |
| #	 nicht unnötig ansteigen zu lassen | |
| # | |
| # 0.20 by vanhofen (NI-Team) | |
| #	-new movies at "Sky Cinema" can be recorded at spec. days/times | |
| #		(in conf-file: SKY_CINEMA="HD|SD|1|24[;DOW[,TIMESPAN]][;RECDIR]") | |
| #		RECDIR is optional; default is the neutrino setting | |
| # | |
| # 0.19 by vanhofen (NI-Team) | |
| #	-fix function to trim strings | |
| #	-fix function to get channels from bouquet | |
| # | |
| # 0.18 by vanhofen (NI-Team) | |
| #	-move pr-auto-timer from /var/plugins to /lib/tuxbox/plugins | |
| #	-show pr-auto-timer as a script plugin in neutrino | |
| #	-reformatting awk-blocks (just for my readability) | |
| #	-remove variables to binarys | |
| #	-search for shows and informations word by word | |
| #	-ignore case in exclusion words | |
| #	-search for exclusion words in detailed informations too | |
| #	-add possibility to record all new movies at "Sky Cinema" | |
| #	-support for external rules-file | |
| #		(in conf-file: RULE_FILE_EXT=/path/to/file) | |
| # | |
| # 0.17 by vanhofen (NI-Team) | |
| #	-accept day groups "Weekday" and "Weekend" | |
| #	-small code optimizations | |
| #	-accept uppercase flags only | |
| #	-Add possible exlusion expressions | |
| #		FOX HD;*;LOST,!Special | |
| # | |
| #	-Support for alternative recording directorys | |
| #		NOTE: flag-section can be empty, but MUST be defined! | |
| #		Sky Cinema HD;*;Ice Age;;/mnt/rec | |
| # | |
| #	-Add possibility to create only the timer for the first show | |
| #		Das Erste HD;*;Tagesschau;F | |
| #		                          ^ | |
| #		                          | | |
| #		                          F=Create first timer only. | |
| # | |
| #	-Add possibility to deactivate an entry in rules-file on success (Danke, Patrick!) | |
| #		Sky Cinema HD;*;Ice Age;O | |
| #		                        ^ | |
| #		                        | | |
| #		                        O=Create first timer only and deactivate rule | |
| # | |
| #		NOTE: Define multiple flags by separating them with commas | |
| #		Sky Cinema HD;*;Ice Age;O,Z | |
| # | |
| # 0.16 by vanhofen (NI-Team) | |
| #	-Whitespace cleanup | |
| #	-Ignore case at search for channel-name and timers | |
| #	-Add timespan-support | |
| # | |
| # 0.15 by Patrick Reinhardt | |
| #	-Documentation | |
| #	-Invalid channel names (including those with wrong upper/lower case) now produce a warning message in the log. (Danke, nadine) | |
| #	-Added debugging option DEBUG_DISPLAYLOG | |
| #	-License is now GPLv2 | |
| # | |
| # 0.14 by Patrick Reinhardt | |
| #	-License is now GPL | |
| #	-Documentation | |
| #	-Code cleanup & optimizations | |
| # | |
| # 0.13 by Patrick Reinhardt | |
| #	-Updated debugging options | |
| #	-Code optimizations | |
| #	-Fixed special character handling in find_timer, remove_overlapping_timer | |
| #	-Fixed a bug in remove_overlapping_timer | |
| # | |
| # 0.12 by Patrick Reinhardt | |
| #	-EPG data is now acquired using the channel id rather than the channel name as a workaround for a neutrino query string bug. (Danke, tewsbo) | |
| # | |
| # 0.11 by Patrick Reinhardt | |
| #	-Code cleanup & optimizations | |
| #	-Fixed zap timers (Danke, tewsbo) | |
| #	-Default log path changed to /tmp | |
| #	-LOG_FILE can now be overridden in the configuration file (Setting to 'off' disables logging. Default if unset or invalid: /tmp/pr-auto-timer_DATE.log | |
| #	-RULE_FILE can now be overridden in the configuration file. Default if unset or invalid: /var/tuxbox/config/pr-auto-timer.rules | |
| # | |
| # 0.10 by Patrick Reinhardt | |
| #	-Bugfix in the comments :) | |
| #	-Added possibility to add zapto timers. | |
| #		Das Erste HD;Mon;Pinguin;Z | |
| #		                         ^ | |
| #		                         | | |
| #		                         Z=Create zap timer instead of record. | |
| #		Backwards compatibility is ensured, i. e. the old format will continue to work | |
| # | |
| # 0.09 by Patrick Reinhardt | |
| #	-Cleaned up unused code | |
| #	-Several bugfixes in the changelog :) | |
| #	-Check if script is already running | |
| #	-Added command line option '--menu'/'-m' which displays a popup in Neutrino | |
| # | |
| # 0.08 by Patrick Reinhardt | |
| #	-Any web server configuration should be supported now. (Danke, vanhofen) | |
| #	-Handle non existing timerd.conf. (Danke, vanhofen) | |
| #	-Timers file (pr-auto-timer.conf) moved to /var/tuxbox/config/pr-auto-timer.rules | |
| #	-Config (MAX_DIFF_MINS) moved to new config file /var/tuxbox/config/pr-auto-timer.conf | |
| #	-Removed warning if log path does not exist | |
| # | |
| # 0.07 by Patrick Reinhardt | |
| #	-Cleaned up unused code | |
| #	-It is now possible to handle whole bouquets rather than single channels | |
| #	-EPG data is now cached | |
| #	-Added a warning message in case the log path does not exist. | |
| #	-Minor optimizations (Danke, vanhofen) | |
| # | |
| # 0.06 by Patrick Reinhardt | |
| #	-Fixed day of week filter (Danke, fred_feuerstein) | |
| #	-RECORD_CORRECTION_MINS removed, recording times now corrected using the values of the tuxbox menu. | |
| #	-Reformatted log. | |
| 
 | |
| NAME="Auto-Timer" | |
| ME=${0##*/} | |
| 
 | |
| CONFIG_FILE=/var/tuxbox/config/$ME.conf | |
| PID_FILE=/var/run/$ME.pid | |
| 
 | |
| TMR2_TEMPFILE=/tmp/$ME.tmr | |
| TMR2_FILE=/var/tuxbox/config/$ME.tmr | |
| 
 | |
| EXIT_SIGNAL=1 | |
| EXIT_NO_RULE_FILE=2 | |
| EXIT_ALREADY_RUNNING=3 | |
| 
 | |
| NEUTRINO_CONF="/var/tuxbox/config/neutrino.conf" | |
| 
 | |
| TIMERD_EVENT_TYPE_ZAPTO=3 | |
| TIMERD_EVENT_TYPE_RECORD=5 | |
| 
 | |
| # How many characters of the EPG-info2 field should be (at most) | |
| # considered, if info1 is empty or the same as the title | |
| INFO2_CHARACTERS=30 | |
| 
 | |
| # The current version for entries in the $ME.show_history-file | |
| EVENT_INDEX_VERSION=1 | |
| 
 | |
| # The limit under which a recorded transport stream is considered broken, given in kb | |
| BROKEN_FILE_SIZE_LIMIT=2 | |
| 
 | |
| #Debugging options | |
| #Do not remove temporary directory: | |
| DEBUG_TEMP=0 | |
| #Do not add or remove any timers: | |
| DEBUG_DRY_RUN=0 | |
| #Output display log to STDOUT: | |
| DEBUG_DISPLAYLOG=0 | |
| 
 | |
| # Default values for variables from the config file | |
| HISTORY_FILE=/var/tuxbox/config/$ME.show_history | |
| MYRECORDPATHS=std::neutrino | |
| DEL_TIMER_REFRESH=2 | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Helper functions" | |
| 
 | |
| signal_handler() { | |
| 	#Handle INT, TERM signals and clean up. | |
| 	log "Caught signal. Cleaning up." | |
| 	cleanup | |
| 	set +f | |
| 	log "done." | |
| 	exit $EXIT_SIGNAL | |
| } | |
| 
 | |
| cleanup() { | |
| 	local result | |
| 
 | |
| 	# first set the box back to the previous mode (in case it was in radio before starting) | |
| 	if [ "$box_mode" == "radio" ]; then | |
| 		log "\tcleanup: Switching box back to $box_mode .. " | |
| 		result=$(wget -q -O - $webserver_url/control/setmode?$box_mode | dos2unix) | |
| 		if [ "$result" != "ok" ]; then | |
| 			log "\tcleanup: Could not switch box to $box_mode: $result" | |
| 		else | |
| 			log "\tcleanup: Successfully switched box to $box_mode (Result: $result)." | |
| 		fi | |
| 		# set also the standby-mode back, since switching to radio deactivates standby... | |
| 		if [ "$box_standby" == "on" ]; then | |
| 			result=$(wget -q -O - $webserver_url/control/standby?on | dos2unix) | |
| 			if [ "$result" != "ok" ]; then | |
| 				log "\tcleanup: Could not reactivate standby: $result" | |
| 			else | |
| 				log "\tcleanup: Successfully reactivated standby after mode switching (Result: $result)." | |
| 			fi | |
| 		fi | |
| 	fi | |
| 
 | |
| 	#Remove temporary directory | |
| 	if [ $DEBUG_TEMP == 0 ]; then | |
| 		log "\tcleanup: Removing directory '$temp_dir'..." | |
| 		rm -rf $temp_dir 2>/dev/null | |
| 	fi | |
| 
 | |
| 	# now remove also the pid-file | |
| 	rm -rf $PID_FILE 2>/dev/null | |
| } | |
| 
 | |
| log() { | |
| 	#Log message to log file | |
| 	#$*: Log message | |
| 	if [ "$LOG_FILE" != "" ]; then | |
| 		echo -e $(date +'%F %H:%M:%S') [$$]: "$*" >> $LOG_FILE | |
| 	fi | |
| } | |
| 
 | |
| displaylog() { | |
| 	#Log message to display log (--menu-mode) | |
| 	#$*: Log message | |
| 	# why? if [ $opt_menu ]; then | |
| 		echo -e "$@" >> $displaylogfile | |
| 	# fi | |
| } | |
| 
 | |
| trim() { | |
| 	# helper function to strip unnecessary spaces from string | |
| 	echo "$@" | sed 's/^ *//g;s/ *$//g;s/ \{1,\}/ /g' | |
| } | |
| 
 | |
| dos2unix() { | |
| 	# helper function to simulate dos2unix | |
| 	sed 's/\r$//' | |
| } | |
| 
 | |
| begin_ifs_block() { | |
| 	#Backup IFS (input field separator) to restore it after parsing arguments | |
| 	IFS_SAVE=$IFS | |
| 	set -f | |
| } | |
| 
 | |
| end_ifs_block() { | |
| 	#Restore (input field separator) IFS after parsing arguments | |
| 	IFS=$IFS_SAVE | |
| 	set +f | |
| } | |
| 
 | |
| url_encode() { | |
| 	#http://rosettacode.org/wiki/URL_encoding#AWK | |
| 	echo $*|awk ' | |
| 		BEGIN { | |
| 			for (i = 0; i <= 255; i++) | |
| 				ord[sprintf("%c", i)] = i | |
| 		} | |
|  | |
| 		function escape(str, c, len, res) { | |
| 			len = length(str) | |
| 			res = "" | |
| 			for (i = 1; i <= len; i++) { | |
| 				c = substr(str, i, 1); | |
| 				if (c ~ /[0-9A-Za-z]/) | |
| 					res = res c | |
| 				else | |
| 					res = res "%" sprintf("%02X", ord[c]) | |
| 			} | |
| 			return res | |
| 		} | |
|  | |
| 		{ print escape($0) } | |
| 	' | |
| } | |
| 
 | |
| html2text() { | |
| 	# Helper function to convert html encoding (&value;) into their characters | |
| 	# Only the most common html entities are converted. For full conversion | |
| 	# we would need 'recode' | |
| 	# Reads html encoding (can be multiple lines) from std input and | |
| 	# returns the same text ASCII encoded. | |
| 	while read line; do | |
| 		res=${line//"/\"} | |
| 		res=${res//'/\'} | |
| 		res=${res//&/\&} | |
| 		#res=${res//>/\>} | |
| 		#res=${res//</\<} | |
| 		# ...other entites, but use as few as possible, since this is inefficient. | |
| 		echo "$res" | |
| 	done | |
| } | |
| 
 | |
| get_corrected_start_stop_times() { | |
| 	#Calculate corrected start/stop times using the tuxbox values (EXTRA_ etc.) | |
| 	#$1: start secs | |
| 	#$2: stop secs | |
| 	#$3: timerd event type | |
| 	#returns: $start_sec_corrected $stop_sec_corrected | |
| 	if [ $3 = $TIMERD_EVENT_TYPE_ZAPTO ]; then | |
| 		start_sec_corrected=$(( $1 - $zap_correction_secs_before )) | |
| 		stop_sec_corrected=0 | |
| 	else | |
| 		if [ "$ignore_record_correction" = "false" ]; then | |
| 			start_sec_corrected=$(( $1 - $record_correction_secs_before )) | |
| 			stop_sec_corrected=$(( $2 + $record_correction_secs_after )) | |
| 		else | |
| 			start_sec_corrected=$1 | |
| 			stop_sec_corrected=$2 | |
| 		fi | |
| 	fi | |
| 	echo $start_sec_corrected $stop_sec_corrected | |
| } | |
| 
 | |
| get_tsid_from_cid() { | |
| 	# $1: channel_id | |
| 	#returns: id of the transponder | |
| 	echo ${1:4:4} | |
| } | |
| 
 | |
| get_setting() | |
| { | |
| 	test -e $NEUTRINO_CONF && \ | |
| 	grep "^${1}=" $NEUTRINO_CONF | cut -d'=' -f2 | |
| } | |
| 
 | |
| scale2res() | |
| { | |
| 	value=${1:-0} | |
| 
 | |
| 	if [ $osd_resolution -eq 1 ]; then | |
| 		echo -ne $value | awk '{ printf("%04.0f", $1 + $1/2) }' | |
| 	else | |
| 		echo -ne $value | |
| 	fi | |
| } | |
| 
 | |
| #END SECTION "Helper functions" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Initialization" | |
| 
 | |
| init_config() { | |
| 	#Parse config file (default: /var/tuxbox/config/pr-auto-timer.conf) | |
| 	if [ -e $CONFIG_FILE ]; then | |
| 		source $CONFIG_FILE 2>/dev/null | |
| 	fi | |
| 
 | |
| 
 | |
| 	# Initialize the logfile first, so we can write to it... | |
| 	if [ ! -d "${LOG_FILE%/*}" ]; then | |
| 		case $LOG_FILE in | |
| 			[oO][fF][fF]) | |
| 				LOG_FILE='' | |
| 				;; | |
| 			*) | |
| 				LOG_FILE=/tmp/${ME}_$(date +'%F').log | |
| 				;; | |
| 		esac | |
| 	fi | |
| 	echo -e "\n\n========================== New log started at $(date) ======================================" >> $LOG_FILE | |
| 
 | |
| 	# Check the settings and reset them to default if unset or invalid | |
| 	case $MAX_DIFF_MINS in | |
| 		''|*[!0-9]*) | |
| 			MAX_DIFF_MINS=10 | |
| 			log "\tinit_config: MAX_DIFF_MINS not found or invalid. Setting default MAX_DIFF_MINS=$MAX_DIFF_MINS." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $MAX_TMR2_COUNT in | |
| 		''|*[!0-9]*) | |
| 			MAX_TMR2_COUNT=250 | |
| 			log "\tinit_config: MAX_TMR2_COUNT not found or invalid. Setting default MAX_TMR2_COUNT=$MAX_TMR2_COUNT." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $END_SHUT_DOWN in | |
| 		''|*[!0-9]*) | |
| 			END_SHUT_DOWN=0 | |
| 			log "\tinit_config: END_SHUT_DOWN not found or invalid. Setting default END_SHUT_DOWN=$END_SHUT_DOWN." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $SHUT_DOWN_ALSO_DAY in | |
| 		''|*[!0-9]*) | |
| 			SHUT_DOWN_ALSO_DAY=0 | |
| 			log "\tinit_config: SHUT_DOWN_ALSO_DAY not found or invalid. Setting default SHUT_DOWN_ALSO_DAY=$SHUT_DOWN_ALSO_DAY." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $MAX_TUNERS in | |
| 		''|*[!0-9]*) | |
| 			MAX_TUNERS=2 | |
| 			log "\tinit_config: MAX_TUNERS not found or invalid. Setting default MAX_TUNERS=$MAX_TUNERS." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $MAX_RECORDS in | |
| 		''|[!1-8]) | |
| 			MAX_RECORDS=8 | |
| 			log "\tinit_config: MAX_RECORDS not found or invalid. Setting default MAX_RECORDS=$MAX_RECORDS." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $AUTOGEN_SHOW_HISTORY in | |
| 		''|[!0-1]) | |
| 			AUTOGEN_SHOW_HISTORY=1 | |
| 			log "\tinit_config: AUTOGEN_SHOW_HISTORY not found or invalid. Setting default AUTOGEN_SHOW_HISTORY=$AUTOGEN_SHOW_HISTORY." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $BROKEN_RECORD_HANDLING in | |
| 		''|[!0-2]) | |
| 			BROKEN_RECORD_HANDLING=1 | |
| 			log "\tinit_config: BROKEN_RECORD_HANDLING not found or invalid. Setting default BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $MYRECORDPATHS in | |
| 		'') | |
| 			MYRECORDPATHS=std::neutrino | |
| 			log "\tinit_config: MYRECORDPATHS not found or invalid. Setting default MYRECORDPATHS=std::neutrino." | |
| 		;; | |
| 	esac | |
| 
 | |
| 	case $MAX_HISTORY_ENTRIES in | |
| 		''|*[!0-9]*) | |
| 			MAX_HISTORY_ENTRIES=1000 | |
| 			log "\tinit_config: MAX_HISTORY_ENTRIES not found or invalid. Setting default MAX_HISTORY_ENTRIES=$MAX_HISTORY_ENTRIES." | |
| 		;; | |
| 	esac | |
| 
 | |
| 
 | |
| 	if [ ! -e "$RULE_FILE" ]; then | |
| 		RULE_FILE=/var/tuxbox/config/$ME.rules | |
| 		if [ ! -e "$RULE_FILE" ]; then | |
| 			echo "ERROR: Rules file '$RULE_FILE' does not exist! Exiting." | |
| 			log "ERROR: Rules file '$RULE_FILE' does not exist! Exiting." | |
| 			exit $EXIT_NO_RULE_FILE | |
| 		fi | |
| 	fi | |
| 
 | |
| 	# support for external rule-file | |
| 	if [ ! -e "$RULE_FILE_EXT" ]; then | |
| 		RULE_FILE_EXT="" | |
| 	fi | |
| 
 | |
| 
 | |
| 	# Prepare some internal variables | |
| 	RULE_FILES="$RULE_FILE $RULE_FILE_EXT" | |
| 	max_diff_secs=$(( $MAX_DIFF_MINS \* 60 )) | |
| } | |
| 
 | |
| init_temp() { | |
| 	#Init temporary directory to cache EPG data etc. | |
| 	temp_dir=$(mktemp -dt $ME.XXXXXX) | |
| 	log "\tinit_temp: Created temporary directory '$temp_dir'." | |
| } | |
| 
 | |
| init_displaylog() { | |
| 	#Init display log file used for --menu-mode | |
| 	displaylogfile="$temp_dir/display.log" | |
| } | |
| 
 | |
| init_settings() { | |
| 	#Init Tuxbox settings (record/zap extra time) | |
| 	record_correction_secs_before=0 | |
| 	record_correction_secs_after=0 | |
| 	zap_correction_secs_before=0 | |
| 	neutrino_rec_dir="" | |
| 
 | |
| 	if [ -f /var/tuxbox/config/timerd.conf ]; then | |
| 		record_correction_secs_before=$(egrep "^EXTRA_TIME_START" /var/tuxbox/config/timerd.conf | cut -d'=' -f2) | |
| 		record_correction_secs_after=$(egrep "^EXTRA_TIME_END" /var/tuxbox/config/timerd.conf | cut -d'=' -f2) | |
| 	fi | |
| 
 | |
| 	if [ -f /var/tuxbox/config/neutrino.conf ]; then | |
| 		zap_correction_secs_before=$(( $(egrep zapto_pre_time /var/tuxbox/config/neutrino.conf | cut -d'=' -f2) * 60 )) | |
| 		neutrino_rec_dir=$(egrep network_nfs_recordingdir /var/tuxbox/config/neutrino.conf | cut -d'=' -f2) | |
| 	fi | |
| 
 | |
| 	log "\tinit_settings: record_correction_secs_before=$record_correction_secs_before, record_correction_secs_after=$record_correction_secs_after" | |
| 	log "\tinit_settings: zap_correction_secs_before=$zap_correction_secs_before" | |
| 	log "\tinit_settings: neutrino_rec_dir=$neutrino_rec_dir" | |
| 
 | |
| 	osd_resolution=$(get_setting "osd_resolution") | |
| } | |
| 
 | |
| init_webserver() { | |
| 	#Build web server URL for API access including authentication | |
| 	#returns: web server base URL, e. g. http://user:pass@localhost | |
| 	webserver_url=$(awk ' | |
| 		BEGIN { | |
| 			FS="="; | |
| 		} | |
|  | |
| 		/^WebsiteMain.port/ { | |
| 			port=$2; | |
| 		} | |
|  | |
| 		/^mod_auth.authenticate=/ { | |
| 			auth=$2; | |
| 		} | |
|  | |
| 		/^mod_auth.username=/ { | |
| 			username=$2; | |
| 		} | |
|  | |
| 		/^mod_auth.password=/ { | |
| 			password=$2; | |
| 		} | |
|  | |
| 		END { | |
| 			if ( auth == "true" ) { | |
| 				printf("http://%s:%s@localhost:%i\n", username, password, port); | |
| 			} | |
| 			else { | |
| 				printf("http://localhost:%i\n", port); | |
| 			} | |
| 		} | |
| 	' /var/tuxbox/config/nhttpd.conf) | |
| } | |
| 
 | |
| #END SECTION "Initialization" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Command line options handling" | |
| 
 | |
| parse_options() { | |
| 	#Parse pr-auto-timer command line arguments | |
| 
 | |
| 	local option | |
| 
 | |
| 	while [ $# -gt 0 ] | |
| 	do | |
| 		option=$1 | |
| 		shift | |
| 
 | |
| 		case "$option" in | |
| 			-m|--menu) | |
| 				opt_menu=true | |
| 			;; | |
| 			-g|--gen-show-history) | |
| 				log "pr-auto-timer V$VERSION generating/updating your event history index file ($HISTORY_FILE)" | |
| 				init_settings | |
| 				AUTOGEN_SHOW_HISTORY=1 | |
| 				generate_history_index | |
| 				if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 					clean_history_file | |
| 				else | |
| 					log "DEBUG DRY RUN: Index created, but NOT updating your index file - printing the result on console instead" | |
| 					echo -e "The following events are already available and will\nbe ignored when planing new timers with the D-flag:" | |
| 					echo -e "---------8><-------------" | |
| 					cat $HISTORY_IDX_TMP_FILE | |
| 					echo -e "---------8><-------------" | |
| 				fi | |
| 				cleanup | |
| 				log "pr-auto-timer V$VERSION is done and exiting happily." | |
| 				exit 0 | |
| 			;; | |
| 			-d|--dry-run) | |
| 				log "\tparse_options: Debug dry-run activated on command line." | |
| 				DEBUG_DRY_RUN=1 | |
| 			;; | |
| 			-p|--post-action) | |
| 				if [ -n "$1" ] && [ ! "${1:0:1}" == "-" ] && [ $1 -ge 0 ] && [ $1 -le 4 ]; then | |
| 					log "\tparse_options: post-actions overridden on command line with END_SHUT_DOWN=$1" | |
| 					END_SHUT_DOWN=$1 | |
| 					shift | |
| 				else | |
| 					log "\tparse_options: post-actions deactivated on command line." | |
| 					END_SHUT_DOWN=0 | |
| 				fi | |
| 			;; | |
| 			-t|--print-timer-index) | |
| 				log "pr-auto-timer V$VERSION generating future timer index." | |
| 				init_settings | |
| 				init_webserver | |
| 				generate_timer_index | |
| 				echo -e "The following events are currently programmed to be recorded and will\nbe ignored when planing new timers with the D-flag:" | |
| 				echo -e "---------8><-------------" | |
| 				cat $TIMER_IDX_TMP_FILE | |
| 				echo -e "---------8><-------------" | |
| 				cleanup | |
| 				log "pr-auto-timer V$VERSION is done." | |
| 				exit 0 | |
| 			;; | |
| 			-h|--help) | |
| 				usage | |
| 				cleanup | |
| 				exit 0 | |
| 			;; | |
| 			*) | |
| 				echo "Unknown command line option '$option'. What did you want to do? Exiting!" | |
| 				usage | |
| 				cleanup | |
| 				exit 1 | |
| 			;; | |
| 		esac | |
| 	done | |
| } | |
| 
 | |
| usage() { | |
| 	# Print short usage message on screen | |
| 	echo -e "Usage: $ME [options]" | |
| 	echo -e "Valid options are:" | |
| 	echo -e "\t-m|--menu\t\tShow menu in neutrino." | |
| 	echo -e "\t-g|--gen-show-history\tGenerate and print the index from your previous recordings (and exit)." | |
| 	echo -e "\t-t|--print-timer-index\tGenerate and print the future timer index (and exit)." | |
| 	echo -e "\t-p|--post-action [x]\tOverride configured post-action with the given argument x=[0..4].\n\t\t\t\tIf no argument is given the post-action is deactivated, i.e x=0." | |
| 	echo -e "\t-d|--dry-run\t\tPerform a dry-run, i.e. do not add/remove any timers. (provide first!)" | |
| 	echo -e "\t-h|--help\t\tPrint this help and exit." | |
| } | |
| 
 | |
| #END SECTION "Command line options handling" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Rule processing" | |
| 
 | |
| parse_regexes() { | |
| 	#Parse search regexes | |
| 	regex=$(trim "${regex}") | |
| 	xregex=$(trim ${xregex}) | |
| 	xregex_option="" | |
| 	if [ ${#xregex} -le 1 ]; then | |
| 		xregex="" | |
| 	# tricking shell with an uppercase X | |
| 	# to avoid interpreting +, ~, ! and - as an operator | |
| 	# and trim again | |
| 	elif [ "X${xregex:0:1}" = "X+" ]; then | |
| 		xregex="${xregex:1}" | |
| 		xregex=$(trim ${xregex}) | |
| 		xregex_option="INCLUDE_ALL" | |
| 	elif [ "X${xregex:0:1}" = "X~" ]; then | |
| 		xregex="${xregex:1}" | |
| 		xregex=$(trim ${xregex}) | |
| 		xregex_option="INCLUDE_ONE" | |
| 	elif [ "X${xregex:0:1}" = "X!" ]; then | |
| 		xregex="${xregex:1}" | |
| 		xregex=$(trim ${xregex}) | |
| 		xregex_option="EXCLUDE_ALL" | |
| 	elif [ "X${xregex:0:1}" = "X-" ]; then | |
| 		xregex="${xregex:1}" | |
| 		xregex=$(trim ${xregex}) | |
| 		xregex_option="EXCLUDE_ONE" | |
| 	else | |
| 		regex="$regex , $xregex" # no +, ~, ! or - found; add to regex again (the comma too) | |
| 		xregex="" | |
| 	fi | |
| } | |
| 
 | |
| parse_timespan() { | |
| 	#Parse possible timespan | |
| 	timespan="$1" | |
| 
 | |
| 	if [ "$timespan" = "" ]; then | |
| 		timespan="*" | |
| 		return | |
| 	fi | |
| 
 | |
| 	#split timespan | |
| 	begin_ifs_block | |
| 		IFS='-' | |
| 		set -- $timespan | |
| 		timespan_start=$1 | |
| 		timespan_stop=$2 | |
| 	end_ifs_block | |
| 
 | |
| 	#split timespan_start | |
| 	begin_ifs_block | |
| 		IFS=':' | |
| 		set -- $timespan_start | |
| 		timespan_start_h=$1 | |
| 		timespan_start_m=$2 | |
| 	end_ifs_block | |
| 
 | |
| 	#split timespan_stop | |
| 	begin_ifs_block | |
| 		IFS=':' | |
| 		set -- $timespan_stop | |
| 		timespan_stop_h=$1 | |
| 		timespan_stop_m=$2 | |
| 	end_ifs_block | |
| 
 | |
| 	e=0 | |
| 	#check format hh:mm | |
| 	echo $timespan_start | grep -q '^[0-9][0-9]:[0-9][0-9]' | |
| 		e=$(($e+$?)) | |
| 	echo $timespan_stop | grep -q '^[0-9][0-9]:[0-9][0-9]' | |
| 		e=$(($e+$?)) | |
| 	#check hours | |
| 	if [ "$timespan_start_h" -gt "23" -o "$timespan_stop_h" -gt "23" ]; then | |
| 		e=$(($e+1)) | |
| 	fi | |
| 	#check minutes | |
| 	if [ "$timespan_start_m" -gt "59" -o "$timespan_stop_m" -gt "59" ]; then | |
| 		e=$(($e+1)) | |
| 	fi | |
| 	#finally we use the date-command to check the full string | |
| 	date -d $timespan_start +%s 2>/dev/null 1>/dev/null | |
| 		e=$(($e+$?)) | |
| 	date -d $timespan_stop +%s 2>/dev/null 1>/dev/null | |
| 		e=$(($e+$?)) | |
| 
 | |
| 	if [ $e -ne 0 ]; then | |
| 		log "\tparse_timespan: Error while checking! timespan disabled!" | |
| 		timespan="*" | |
| 	else | |
| 		timespan_start_s=$(date -d $timespan_start +%s) | |
| 		timespan_stop_s=$(date -d $timespan_stop +%s) | |
| 		if [ $timespan_start_s -gt $timespan_stop_s ]; then | |
| 			timespan_over_midnight=true | |
| 		else | |
| 			timespan_over_midnight=false | |
| 		fi | |
| 	fi | |
| } | |
| 
 | |
| parse_flags() { | |
| 	#Parse flags of entries in rule file | |
| 	event_type=$TIMERD_EVENT_TYPE_RECORD | |
| 	first_match=false | |
| 	only_once=false | |
| 	ignore_record_correction=false | |
| 	prevent_duplicates=false | |
| 	ignore_timer_refresh=false | |
| 	allow_empty_info=false | |
| 
 | |
| 	# split flags with the separator "," | |
| 	begin_ifs_block | |
| 		IFS=',' | |
| 		for flag in $1; do | |
| 			#log "\tmain: Found flag $flag!" | |
| 			case $flag in | |
| 				Z) | |
| 					event_type=$TIMERD_EVENT_TYPE_ZAPTO | |
| 				;; | |
| 				R) | |
| 					event_type=$TIMERD_EVENT_TYPE_RECORD | |
| 				;; | |
| 				F) | |
| 					first_match=true | |
| 				;; | |
| 				O) | |
| 					only_once=true | |
| 					first_match=true | |
| 				;; | |
| 				I) | |
| 					ignore_record_correction=true | |
| 				;; | |
| 				D) | |
| 					prevent_duplicates=true | |
| 				;; | |
| 				W) | |
| 					ignore_timer_refresh=true | |
| 				;; | |
| 				M) | |
| 					allow_empty_info=true | |
| 				;; | |
| 				*) | |
| 					log "\tparse_flags: WARNING - Unknown flag $flag found; Ignoring!" | |
| 				;; | |
| 			esac | |
| 		done | |
| 	end_ifs_block | |
| } | |
| 
 | |
| parse_recdir() { | |
| 	#Parse recording directory | |
| 	if [ $event_type = $TIMERD_EVENT_TYPE_ZAPTO ]; then | |
| 		recdir="" | |
| 	else | |
| 		recdir=$(url_encode "$recdir") | |
| 	fi | |
| } | |
| 
 | |
| parse_dowgroup() { | |
| 	#Check for weekday or weekend and set var dowgroup | |
| 	local result _dow | |
| 	dowgroup=$(echo "$dowfilter" | awk '{print tolower($0)}') | |
| 	_dow=$(echo $dow | awk '{print tolower($0)}') | |
| 	if [ "$dowgroup" = "weekday" ]; then | |
| 		dowgroup="montuewedthufri" | |
| 	fi | |
| 	if [ "$dowgroup" = "weekend" ]; then | |
| 		dowgroup="satsun" | |
| 	fi | |
| 	result=$(awk -v _dowgroup="$dowgroup" -v _dow="$_dow" 'BEGIN {print match(_dowgroup, _dow); exit 0 }') | |
| 	if [ $result -gt 0 ]; then | |
| 		dowgroup=true | |
| 	fi | |
| 	#log "\t\tparse_dowgroup: dowgroup='$dowgroup', dow='$_dow'" | |
| } | |
| 
 | |
| #END SECTION "Rule processing" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Retrieve & Search EPG" | |
| 
 | |
| get_channel_id_by_channel_name() { | |
| 	#$1: channel name (ignore case) | |
| 	#returns: id of the first(!) matching channel | |
| 	temp_file="$temp_dir/channellist" | |
| 	if [ ! -e "$temp_file" ]; then | |
| 		wget -qO "$temp_file" "$webserver_url/control/channellist" | |
| 	fi | |
| 	awk -v channel="$1" ' | |
| 		/^[0-9a-f]{16} / { | |
| 			if (tolower($0) == tolower($1 " " channel)) { | |
| 				print $1; | |
| 				exit 0; | |
| 			} | |
| 		} | |
| 	' $temp_file | |
| 	#Nooooooooooooo! This wouldn't have been necessary if query string handling worked in Neutrino... | |
| } | |
| 
 | |
| get_channel_xml() { | |
| 	#Fetch EPG data in XML for a specified channel. | |
| 	#$1: channel name | |
| 	#returns: XML EPG data | |
| 	channel_id=$(get_channel_id_by_channel_name "$1") | |
| 	if [ "$channel_id" != "" ]; then | |
| 		temp_file="$temp_dir/$channel_id" | |
| 		if [ ! -e "$temp_file" ]; then | |
| 			wget -qO "$temp_file" "$webserver_url/control/epg?xml=true&details=true&channelid=$channel_id" | |
| 		fi | |
| 		cat "$temp_file" | |
| 		#log "\t\tget_channel_xml: Retrieved EPG from channel '$1' (CID: $channel_id TSID: $(get_tsid_from_cid "$channel_id")) " | |
| 	else | |
| 		log "\t\tget_channel_xml: !!! WARNING: Channel '$1' was NOT found. Skipping !!!" | |
| 	fi | |
| } | |
| 
 | |
| get_channels_from_bouquet() { | |
| 	#$1: bouquet number | |
| 	#returns: channel names | |
| 	_channels=$(wget -qO - "$webserver_url/control/getbouquet?bouquet=${1}&mode=TV" | dos2unix) | |
| 	echo "$_channels" | cut -d" " -f3- | |
| } | |
| 
 | |
| find_show() { | |
| 	#Find a show by (exact) channel name or bouquet number and RegEx(!) for show name. | |
| 	#$1: channel | |
| 	#$2: show name regex | |
| 	#$3: extended regex | |
| 	#$4: extended regex option | |
| 	#returns: list of found shows in the format 'CHANNEL_ID|START_SEC|STOP_SEC|CHANNEL_NAME|DESCRIPTION|INFO1|INFO2' | |
| 	BC="$1" | |
| 	case ${BC:0:1} in | |
| 		"*") | |
| 			if [ "$1" = "*" ]; then | |
| 				bouquet=1 | |
| 			else | |
| 				bouquet=${BC:1} | |
| 			fi | |
| 			log "\t\tfind_show: Bouquet: $bouquet" | |
| 			channels=$(get_channels_from_bouquet $bouquet) | |
| 			echo -e "$channels" | while read channel; do | |
| 				find_show_by_regex "$channel" "$2" "$3" "$4" | |
| 			done | |
| 			;; | |
| 		*) | |
| 			find_show_by_regex "$1" "$2" "$3" "$4" | |
| 			;; | |
| 	esac | |
| } | |
| 
 | |
| find_show_by_regex() { | |
| 	#Find a show by (exact) channel name and RegEx(!) for show name. | |
| 	#$1: channel name | |
| 	#$2: show name regex | |
| 	#$3: extended regex | |
| 	#$4: extended regex option | |
| 	#returns: list of found shows in the format 'CHANNEL_ID|START_SEC|STOP_SEC|CHANNEL_NAME|DESCRIPTION|INFO1|INFO2' | |
| 	local xml result matches | |
| 
 | |
| 	xml=$(get_channel_xml "$1") | |
| 	result=$(echo -ne "$xml"|awk ' | |
| 		BEGIN { | |
| 			false=0; | |
| 			true=1; | |
| 			matches=0; | |
| 		} | |
|  | |
| 		END { | |
| 			exit (matches == 0); | |
| 		} | |
|  | |
| 		/<prog>/ { | |
| 			channel_id	= ""; | |
| 			start_sec	= ""; | |
| 			stop_sec	= ""; | |
| 			description	= ""; | |
| 			info1		= ""; | |
| 			info2_1		= ""; | |
| 			info2_2		= ""; | |
| 			info2_open	= 0; | |
| 		} | |
|  | |
| 		/<channel_id>[0-9a-z]+<\/channel_id>/ { | |
| 			channel_id	= gensub(/<\/?[^>]+>/, "", "g"); | |
| 		} | |
|  | |
| 		/<start_sec>[0-9]+<\/start_sec>/ { | |
| 			start_sec	= gensub(/<\/?[^>]+>/, "", "g"); | |
| 		} | |
|  | |
| 		/<stop_sec>[0-9]+<\/stop_sec>/ { | |
| 			stop_sec	= gensub(/<\/?[^>]+>/, "", "g"); | |
| 		} | |
|  | |
| 		/<info1>.*<\/info1>/ { | |
| 			info1		= gensub(/(<info1><!\[CDATA\[|\]\]><\/info1>)/, "", "g"); | |
| 		} | |
|  | |
| 		/<info2>.*/ { | |
| 			info2_1		= gensub(/(<info2><!\[CDATA\[|\]\]><\/info2>)/, "", "g"); | |
| 			info2_open	= 1; | |
| 		} | |
|  | |
| 		/.*<\/info2>/ { | |
| 			info2_2		= gensub(/(<info2><!\[CDATA\[|\]\]><\/info2>)/, "", "g"); | |
| 			info2_open	= 0; | |
| 		} | |
|  | |
| 		# This 'captures' also lines inbetween newlines for info2 | |
| 		!/.*info2.*/ { | |
| 			if (info2_open) | |
| 			{ | |
| 				info2_1=info2_1 " " $0 | |
| 			} | |
| 		} | |
|  | |
| 		/<description><!\[CDATA\[.*\]\]><\/description>/ { | |
| 			description	= gensub(/(<description><!\[CDATA\[|\]\]><\/description>)/, "", "g"); | |
| 		} | |
|  | |
| 		/<\/prog>/ { | |
| 			if (info2_1 == info2_2) | |
| 			{ | |
| 				info2 = info2_1; | |
| 			} | |
| 			else | |
| 			{ | |
| 				info2 = info2_1 " " info2_2; | |
| 			} | |
|  | |
| 			# ignore case in fulldescription | |
| 			fulldescription	= tolower(description); | |
|  | |
| 			if (length(info1) > 0) | |
| 			{ | |
| 				fulldescription	= tolower(fulldescription " " info1); | |
| 			} | |
|  | |
| 			if (length(info2) > 0) | |
| 			{ | |
| 				fulldescription	= tolower(fulldescription " " info2); | |
| 			} | |
|  | |
| 			info_length = length(info1) + length(info2); | |
|  | |
| 			find_type = "" | |
| 			include_regex = "" | |
| 			exclude_regex = "" | |
| 			if (match(extended_regex_option, "INCLUDE")) | |
| 			{ | |
| 				find_type = "include" | |
| 				include_regex = tolower(extended_regex); | |
| 			} | |
| 			else if (match(extended_regex_option, "EXCLUDE")) | |
| 			{ | |
| 				find_type = "exclude" | |
| 				exclude_regex = tolower(extended_regex); | |
| 			} | |
| 			include_regex = tolower(include_regex); | |
| 			exclude_regex = tolower(exclude_regex); | |
|  | |
| 			find_option = "" | |
| 			if (match(extended_regex_option, "ALL")) | |
| 			{ | |
| 				find_option = "all"; | |
| 			} | |
| 			else if (match(extended_regex_option, "ONE")) | |
| 			{ | |
| 				find_option = "one"; | |
| 			} | |
|  | |
| 			# split into arrays | |
| 			split(show_regex,	show_array); | |
| 			split(include_regex,	include_array); | |
| 			split(exclude_regex,	exclude_array); | |
|  | |
| 			show_found = false; | |
|  | |
| 			show_counter = 0; | |
| 			show_matches = 0; | |
|  | |
| 			if (show_regex == "*") | |
| 			{ | |
| 				# no need to search; simply grab the show | |
| 				show_counter = 1; | |
| 				show_matches = 1; | |
| 			} | |
| 			else | |
| 			{ | |
| 				for (i in show_array) | |
| 				{ | |
| 					# search for every regex/word from show_regex in description only | |
| 					show_counter++; | |
| 					if (match(string tolower(description), string tolower(show_array[i]))) | |
| 					{ | |
| 						show_matches++; | |
| 					} | |
| 				} | |
| 			} | |
|  | |
| 			if ( (show_matches > 0) && (show_counter == show_matches) ) | |
| 			{ | |
| 				# all search words or "*" found | |
| 				show_found = true; | |
|  | |
| 				if (find_type == "exclude") | |
| 				{ | |
| 					exclude_counter = 0; | |
| 					exclude_matches = 0; | |
|  | |
| 					for (i in exclude_array) | |
| 					{ | |
| 						# search for every regex/word from exclude_regex in fulldescription | |
| 						exclude_counter++; | |
| 						if (match(fulldescription, exclude_array[i])) | |
| 						{ | |
| 							exclude_matches++; | |
| 						} | |
| 					} | |
|  | |
| 					if (find_option = "all") | |
| 					{ | |
| 						if ( (exclude_matches > 0) && (exclude_counter == exclude_matches) ) | |
| 						{ | |
| 							# all exclude words found; mark this show as "false" | |
| 							show_found = false; | |
| 						} | |
| 					} | |
| 					else if (find_option = "one") | |
| 					{ | |
| 						if ( (exclude_matches > 0) && (exclude_counter >= 1) ) | |
| 						{ | |
| 							# one exclude word found; mark this show as "false" | |
| 							show_found = false; | |
| 						} | |
| 					} | |
| 				} | |
| 				else if (find_type == "include") | |
| 				{ | |
| 					include_counter = 0; | |
| 					include_matches = 0; | |
|  | |
| 					for (i in include_array) | |
| 					{ | |
| 						# search for every regex/word from include_regex in fulldescription | |
| 						include_counter++; | |
| 						if (match(fulldescription, include_array[i])) | |
| 						{ | |
| 							include_matches++; | |
| 						} | |
| 					} | |
|  | |
| 					if (find_option = "all") | |
| 					{ | |
| 						if ( (include_counter > 0) && (include_counter != include_matches) ) | |
| 						{ | |
| 							# not all include words found; mark this show as "false" | |
| 							show_found = false; | |
| 						} | |
| 					} | |
| 					else if (find_option = "one") | |
| 					{ | |
| 						if ( (include_counter > 0) && (include_matches < 1) ) | |
| 						{ | |
| 							# not any include word found; mark this show as "false" | |
| 							show_found = false; | |
| 						} | |
| 					} | |
| 				} | |
| 			} | |
|  | |
| 			if (show_found == true) | |
| 			{ | |
| 				matches++; | |
| 				printf("%s|%i|%i|%s|%s|%s|%s\n", channel_id, start_sec, stop_sec, channel_name, description, info1, info2); | |
| 			} | |
| 		} | |
| 	' channel_name="$1" show_regex="$2" extended_regex="$3" extended_regex_option="$4") | |
| 
 | |
| 	if [ $? -eq 0 ]; then | |
| 		matches=$(echo "$result"|wc -l) | |
| 		log "\tfind_show_by_regex: Found $matches matches for regex '$2' (extended regex '$3', option '$4') on channel '$1'." | |
| 		echo -e "$result" | |
| 		#log "\t\tfind_show_by_regex: Result: '$result'" | |
| 		return 0 | |
| 	else | |
| 		log "\tfind_show_by_regex: Found NO matches for regex '$2' (extended regex '$3', option '$4') on channel '$1'." | |
| 		return 1 | |
| 	fi | |
| } | |
| 
 | |
| find_show_by_start_sec() { | |
| 	#Find a show by (exact) channel name and start time. | |
| 	#$1: channel name | |
| 	#$2: start time in seconds | |
| 	#returns: list of found shows in the format 'CHANNEL_ID|START_SEC|STOP_SEC|CHANNEL_NAME|DESCRIPTION|INFO1|INFO2' | |
| 	xml=$(get_channel_xml "$1") | |
| 	result=$(echo -ne "$xml"|awk ' | |
| 		BEGIN { | |
| 			matches=0; | |
| 		} | |
|  | |
| 		END { | |
| 			exit (matches == 0); | |
| 		} | |
|  | |
| 		/<prog>/ { | |
| 			channel_id	= ""; | |
| 			start_sec	= ""; | |
| 			stop_sec	= ""; | |
| 			description	= ""; | |
| 			info1		= ""; | |
| 			info2_1		= ""; | |
| 			info2_2		= ""; | |
| 			info2_open	= 0; | |
| 		} | |
|  | |
| 		/<channel_id>[0-9a-z]+<\/channel_id>/ { | |
| 			channel_id	= gensub(/<\/?[^>]+>/, "", "g"); | |
| 		} | |
|  | |
| 		/<start_sec>[0-9]+<\/start_sec>/ { | |
| 			start_sec	= gensub(/<\/?[^>]+>/, "", "g"); | |
| 		} | |
|  | |
| 		/<stop_sec>[0-9]+<\/stop_sec>/ { | |
| 			stop_sec	= gensub(/<\/?[^>]+>/, "", "g"); | |
| 		} | |
|  | |
| 		/<info1>.*<\/info1>/ { | |
| 			info1		= gensub(/(<info1><!\[CDATA\[|\]\]><\/info1>)/, "", "g"); | |
| 		} | |
|  | |
| 		/<info2>.*/ { | |
| 			info2_1		= gensub(/(<info2><!\[CDATA\[|\]\]><\/info2>)/, "", "g"); | |
| 			info2_open	= 1; | |
| 		} | |
|  | |
| 		/.*<\/info2>/ { | |
| 			info2_2		= gensub(/(<info2><!\[CDATA\[|\]\]><\/info2>)/, "", "g"); | |
| 			info2_open	= 0; | |
| 		} | |
|  | |
| 		# This 'captures' also lines inbetween newlines for info2 | |
| 		!/.*info2.*/ { | |
| 			if (info2_open) | |
| 			{ | |
| 				info2_1=info2_1 " " $0 | |
| 			} | |
| 		} | |
|  | |
| 		/<description><!\[CDATA\[.*\]\]><\/description>/ { | |
| 			description	= gensub(/(<description><!\[CDATA\[|\]\]><\/description>)/, "", "g"); | |
| 		} | |
|  | |
| 		/<\/prog>/ { | |
| 			if (info2_1 == info2_2) | |
| 			{ | |
| 				info2 = info2_1; | |
| 			} | |
| 			else | |
| 			{ | |
| 				info2 = info2_1 " " info2_2; | |
| 			} | |
|  | |
| 			if ((search_start_sec == start_sec) || (search_start_sec == start_sec-record_correction_secs_before) ) | |
| 			{ | |
| 				matches=1; | |
| 				printf("%s|%i|%i|%s|%s|%s|%s\n", channel_id, start_sec, stop_sec, channel_name, description, info1, info2); | |
| 			} | |
| 		} | |
| 	'  channel_name="$1" search_start_sec="$2" record_correction_secs_before="$record_correction_secs_before") | |
| 
 | |
| 	if [ $? -eq 0 ]; then | |
| 		log "\t\tfind_show_by_start_sec: Found match for start time '$2' on channel '$1'." | |
| 		echo -e "$result" | |
| 		return 0 | |
| 	fi | |
| } | |
| 
 | |
| #END SECTION "Retrieve & Search EPG" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Event & Timer Index" | |
| 
 | |
| read_history_index() { | |
| 	# Reads and upgrades the event index from the configured file $HISTORY_FILE. | |
| 	# No parameters | |
| 	# Returns: The read and upgraded index is available in the temporary file $HISTORY_IDX_TMP_FILE | |
| 
 | |
| 	local line title info timestamp new_timestamp filename | |
| 
 | |
| 	# First remove the temporary file, in case it exists - this shouldn't happen, but lets be safe. | |
| 	rm -f "$HISTORY_IDX_TMP_FILE" 2>/dev/null | |
| 
 | |
| 	# Now read the configured HISTORY FILE (if it exists) and upgrade it if necessary. | |
| 	if [ -e $HISTORY_FILE ]; then | |
| 
 | |
| 		# With the version property, future upgrades of the format are possible | |
| 		# It is currently (v0.34) not used, since with the first format change we can deal implicitly. | |
| 		if [ -f "${HISTORY_FILE}.properties" ]; then | |
| 			source "${HISTORY_FILE}.properties" 2>/dev/null | |
| 		else | |
| 			event_index_version=0 | |
| 		fi | |
| 
 | |
| 		log "\t\t+- Reading $HISTORY_FILE (v$event_index_version) with $(wc -l $HISTORY_FILE | cut -d ' ' -f1) events ..." | |
| 
 | |
| 		# for unavailable timestamps (upgrading) put the timestamp 2 days in the future | |
| 		# a subsequent run from pr-auto-timer, will either correct this or leave it; | |
| 		# if it remains, than the entry is considered old after two days.. | |
| 		new_timestamp=$(($(date +%s)+2*24*60*60)) | |
| 
 | |
| 		while read line; do | |
| 
 | |
| 			# ignore empty lines | |
| 			if [ -z "$line" ]; then | |
| 				log "\t\t+- Removing empty line from index." | |
| 				continue | |
| 			fi | |
| 
 | |
| 			# now split it in it's components | |
| 			begin_ifs_block | |
| 				IFS='|' | |
| 				set -- $line | |
| 				title=$1 | |
| 				info=$2 | |
| 				timestamp=$3 | |
| 				filename=$4 | |
| 			end_ifs_block | |
| 
 | |
| 			# now check, if some fields are empty and provide some initial values for them | |
| 			# this can happen with manual entries and when upgrading from an older file format | |
| 			# in case we anyhow generate a new index, we also reset the timestamp - this way we | |
| 			# can also get updated timestamps if the modification date changes | |
| 			if [ -z "$timestamp" ] || ( [ $AUTOGEN_SHOW_HISTORY -eq 1 ] && [ -f "$filename" ] ); then | |
| 				#log "\t\t+- Upgrading '$line' with new timestamp '$new_timestamp'." | |
| 				timestamp=$((new_timestamp++)) | |
| 			fi | |
| 
 | |
| 			# if there is no filename yet, we set some placeholder | |
| 			# we also remove/reset the filename, in case we are going to regenerate the index anyhow | |
| 			# the given placeholder ensures, that a new found path always 'wins' in sort against the placeholder | |
| 			if [ -z "$filename" ] || [ $AUTOGEN_SHOW_HISTORY -eq 1 ]; then | |
| 				#log "\t\t+- Adding placeholder filename to '$line'". | |
| 				filename="ZZZZ-not-available-ZZZZ" | |
| 			fi | |
| 
 | |
| 			#write the cleaned/upgraded line to the temporary index | |
| 			echo -e "$title|$info|$timestamp|$filename|unverified-magic-MYmtmIQMsoQ=" >> "$HISTORY_IDX_TMP_FILE" | |
| 
 | |
| 		done < "$HISTORY_FILE" | |
| 	else | |
| 		log "\t\t+- $HISTORY_FILE does not yet exist. Starting with empty index." | |
| 		touch "$HISTORY_IDX_TMP_FILE" | |
| 	fi | |
| } | |
| 
 | |
| generate_history_index() { | |
| 	# Generates/updates the $ME.show_history-file from your recordings | |
| 	# No parameters | |
| 	# Result: merged $ME.show_history with previous values and available records | |
| 
 | |
| 	local rec_path info info1 info2 title show history_index ts_file timestamp magic_token broken_records broken_records_count recorded_shows | |
| 
 | |
| 	log "\t\tgenerate_history_index: collecting information from previously recorded events ..." | |
| 
 | |
| 	read_history_index | |
| 	history_index="$(cat $HISTORY_IDX_TMP_FILE)\n" | |
| 
 | |
| 	# shall we auto-generate the history index? | |
| 	if [ $AUTOGEN_SHOW_HISTORY -eq 1 ]; then | |
| 		# now iterate over all provided paths | |
| 		begin_ifs_block | |
| 			IFS=';' | |
| 			for rec_path in ${MYRECORDPATHS//"std::neutrino"/$neutrino_rec_dir} | |
| 			do | |
| 				if [ $BROKEN_RECORD_HANDLING -gt 0 ]; then | |
| 					log "\t\t+- Searching path '$rec_path' recursively for broken *.ts-files (file size smaller than ${BROKEN_FILE_SIZE_LIMIT}kB)." | |
| 
 | |
| 					broken_records=$(find "$rec_path" -name '*.ts' -size -${BROKEN_FILE_SIZE_LIMIT}k) | |
| 					broken_records_count=$(echo -e "$broken_records" | sed '/^$/d' | wc -l) | |
| 
 | |
| 					if [ $broken_records_count -gt 0 ]; then | |
| 						if [ $BROKEN_RECORD_HANDLING -eq 2 ]; then | |
| 							if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 								log "\t\t| \\- Removing $broken_records_count broken record(s) (BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING)." | |
| 								echo -e "$broken_records" | xargs rm 2>/dev/null | |
| 							else | |
| 								log "\t\t| \\- Would remove $broken_records_count broken records now (BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING). Skipped due to DEBUG-DRY-RUN." | |
| 							fi | |
| 							# In this case, we will detect the a missing transport stream and handle the xml with the next part accordingly. | |
| 							# To avoid double work, we therefore reset BROKEN_RECORDS="" | |
| 							broken_records="" | |
| 						else | |
| 							log "\t\t| \\- Found $broken_records_count broken record(s), but leaving files untouched (BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING)." | |
| 						fi | |
| 					else | |
| 						log "\t\t| \\- No broken records found." | |
| 					fi | |
| 				fi | |
| 
 | |
| 				log "\t\t+- Searching path '$rec_path' recursively for *.xml-files" | |
| 				recorded_shows=$(find "$rec_path" -name '*.xml') | |
| 				IFS=$'\n' | |
| 				for show in $recorded_shows | |
| 				do | |
| 
 | |
| 					# Note: info2 is only extracted until the first newline. This is probably ok, since we need anyhow only the | |
| 					# first $INFO2_CHARACTERS (30) characters from it iff info1 is empty. | |
| 
 | |
| 					#echo "Processing '$show' .." | |
| 					title=$(grep epgtitle "$show" | sed 's/^[[:space:]]*//g;s/ *$//g;s/ \{1,\}/ /g;s/<epgtitle>//g;s/<\/epgtitle>//g' | html2text) | |
| 					info1=$(grep info1 "$show" | sed 's/^[[:space:]]*//g;s/ *$//g;s/ \{1,\}/ /g;s/<info1>//g;s/<\/info1>//g' | html2text) | |
| 					info2=$(grep "<info2>" "$show" | sed 's/^[[:space:]]*//g;s/ *$//g;s/ \{1,\}/ /g;s/<info2>//g;s/<\/info2>//g' | html2text) | |
| 
 | |
| 					# now check which information is available in the stored xml | |
| 					if [ -z "$info1" ] || [ "$info1" == "$title" ]; then | |
| 						# info1 was empty or exactly the same as the title | |
| 						# in this case, we use the $INFO2_CHARACTERS from info2; nothing else to do; if this is empty too, we can't do anything | |
| 						info=${info2:0:$INFO2_CHARACTERS} | |
| 					else | |
| 						info="$info1" | |
| 					fi | |
| 
 | |
| 					# create a magic token for good files. It is important, that this token is alphabetically BEFORE the bad-token | |
| 					# and also the unverified token (therefore it starts with 'a' - the random string is just to make it unique, so we can grep later for it | |
| 					# note that the random string used when reading the old HISTORY_FILE must be the same as this one here. | |
| 					magic_token="alright-magic-MYmtmIQMsoQ=" | |
| 
 | |
| 					# finally check if the transport stream exists and if it bigger than a given size | |
| 					ts_file=${show%".xml"}".ts" | |
| 					if ( [ -f "$ts_file" ] && ([ $BROKEN_RECORD_HANDLING -eq 0 ] || [ "$(echo -e "$broken_records" | grep "$ts_file")" == "" ]) ); then | |
| 						# all good, get the last modification time of the  .ts-file as a timestamp | |
| 						timestamp=$(date -r "$ts_file" +%s) | |
| 						#file_size_kb=`du -k "$filename" | cut -f1` | |
| 						#echo "$ts: $timestamp - $(date -r "$show" +%s)" | |
| 					else | |
| 						# No transport stream (recording) found. In this case we must handle a broken record if activated | |
| 						timestamp=$(date -r "$show" +%s) | |
| 						case $BROKEN_RECORD_HANDLING in | |
| 							0) | |
| 								log "\t\t| +- Warning: No or broken transport stream for $show found (Title='$title', Info='$info'). Still adding it to history index (BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING)." | |
| 							;; | |
| 							1) | |
| 								log "\t\t| +- Detected missing or broken transport stream for $show (Title='$title', Info='$info'). Removing from history index (BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING)." | |
| 								# make sure, this comes alphanumerically after the "good" token - with the later unique sort this should "survive" against the good one. | |
| 								magic_token="bad-magic-5NZm8PpjT8w=" | |
| 							;; | |
| 							2) | |
| 								if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 									log "\t\t| +- Detected missing or broken transport stream for $show (Title='$title', Info='$info'). Removing from history index and deleting. (BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING)." | |
| 									rm -f "$show" 2>/dev/null | |
| 								else | |
| 									log "\t\t| +- Detected missing or broken transport stream for $show (Title='$title', Info='$info'). Would now remove it from history index and delete it. (BROKEN_RECORD_HANDLING=$BROKEN_RECORD_HANDLING). Skipping the real operation due to DEBUG_DRY_RUN=$DEBUG_DRY_RUN." | |
| 								fi | |
| 								magic_token="bad-magic-5NZm8PpjT8w=" | |
| 							;; | |
| 							*) | |
| 								log "\t\t| +- ERROR: This should not happen - $BROKEN_RECORD_HANDLING is no valid value BROKEN_RECORD_HANDLING ($show: Title='$title', Info='$info')." | |
| 								continue | |
| 							;; | |
| 						esac | |
| 					fi | |
| 
 | |
| 					# add to the index iff there are meaningful values available. | |
| 					if  [ -n "$title" ] && [ "$title" != "not available" ] && [ -n "$info" ]; then | |
| 						history_index="$history_index$title|$info|$timestamp|$ts_file|$magic_token\n" | |
| 					else | |
| 						log "\t\t| +- Skipping file $show ($(date -D %s -d $timestamp +'%Y-%m-%d %H:%M:%S')) due to invalid info: Title='$title', Info='$info'" | |
| 					fi | |
| 				done | |
| 			done | |
| 		end_ifs_block | |
| 	fi | |
| 
 | |
| 	# at this point HISTORY_INDEX contains all values from the old files and | |
| 	# also a value for all collected files from the recorded directories (if active) | |
| 
 | |
| 	# Let's sort it and remove all duplicates, ignoring timestamps and ignoring bad records | |
| 	history_index=$(echo -ne "$history_index" | sort -t'|' -u -k1,2 | grep "MYmtmIQMsoQ=") | |
| 
 | |
| 	# write it to a temp file, so we can access it from different subprocesses | |
| 	echo -e "$history_index" > $HISTORY_IDX_TMP_FILE | |
| } | |
| 
 | |
| clean_history_file() { | |
| 	# Write (potentially updated) history index of recorded shows to the index file | |
| 	# Clean the file by removing n entries, so that the maximum entries given in MAX_HISTORY_ENTRIES is not violated | |
| 	# Only entries, to which no recorded file exists will be removed - otherwise they would be added over and over again | |
| 	# A setting of MAX_HISTORY_ENTRIES=0 deactivates the size check. If there are more physically available files, than | |
| 	# entries allowed, the MAX_HISTORY_ENTRIES will be ignored and a warning is issued in the logfile. | |
| 	# No parameter | |
| 	local entries line title info timestamp filename nr_removed_entries=0 new_timestamp | |
| 
 | |
| 	# proceed only if there is a tempfile, otherwise there is nothing to do here | |
| 	if [ -e "$HISTORY_IDX_TMP_FILE" ]; then | |
| 
 | |
| 		# check if we are in debug-dry-run. If yes, just skip any processing of the file. Otherwise proceed by writing | |
| 		# a new history index | |
| 		if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 			entries=$(wc -l $HISTORY_IDX_TMP_FILE | cut -d ' ' -f1) | |
| 
 | |
| 			# calculate the number of lines which need to be removed | |
| 			if [ $MAX_HISTORY_ENTRIES -gt 0 ]  && [ $entries -gt $MAX_HISTORY_ENTRIES ]; then | |
| 				over_limit=$((entries-MAX_HISTORY_ENTRIES)) | |
| 				log "\tTrying to minimize generated history index with $entries entries by $over_limit entries before writing to file $HISTORY_FILE .." | |
| 			else | |
| 				over_limit=0 | |
| 				log "\tCleaning generated history index with $entries entries and writing to file $HISTORY_FILE .." | |
| 			fi | |
| 
 | |
| 			# create also the temporary file for storing the number of removed elements | |
| 			echo 0 > "${HISTORY_IDX_TMP_FILE}_nr_removed" | |
| 
 | |
| 			# try to minimize the file by removing old entries | |
| 			sort -t '|' -k3 "$HISTORY_IDX_TMP_FILE" | while read line; do | |
| 				# ignore empty lines | |
| 				if [ -z "$line" ]; then | |
| 					echo $((++nr_removed_entries)) > "${HISTORY_IDX_TMP_FILE}_nr_removed" | |
| 					log "\t+- Removing empty line from index." | |
| 					continue | |
| 				fi | |
| 
 | |
| 				begin_ifs_block | |
| 					IFS='|' | |
| 					set -- $line | |
| 					title=$1 | |
| 					info=$2 | |
| 					timestamp=$3 | |
| 					filename=$4 | |
| 				end_ifs_block | |
| 
 | |
| 
 | |
| 				if [ $nr_removed_entries -ge $over_limit ] || [ -f "$filename" ]; then | |
| 					# not oversize or enough entries removed or filename exists | |
| 					# insert this line also into the new file, and use the current format | |
| 					echo -e "$title|$info|$timestamp|$filename" >> "${HISTORY_IDX_TMP_FILE}2" | |
| 				else | |
| 					# not enough events removed and the file does not exist anymore | |
| 					# hence this entry will be removed -- here we just skip it | |
| 					echo $((++nr_removed_entries)) > "${HISTORY_IDX_TMP_FILE}_nr_removed" | |
| 					log "\t+- Removing '$title' ('$info') with timestamp $timestamp ($(date -D %s -d $timestamp +'%Y-%m-%d %H:%M:%S')) from index." | |
| 				fi | |
| 			done | |
| 
 | |
| 			# grab the number from the file, since the subprocess from read does not allow variables to pass back | |
| 			nr_removed_entries=$(cat "${HISTORY_IDX_TMP_FILE}_nr_removed") | |
| 
 | |
| 			if [ $nr_removed_entries -lt $over_limit ]; then | |
| 				#we could not remove enough. print a warning in the log | |
| 				log "\tWARNING: Removed only $nr_removed_entries / $over_limit entries entries from index. Increase MAX_HISTORY_ENTRIES (=$MAX_HISTORY_ENTRIES) or delete some old files!" | |
| 			else | |
| 				# everything is fine. minimizing worked | |
| 				log "\tCleaned the generated history index by removing $nr_removed_entries entries. (required $over_limit entries)" | |
| 			fi | |
| 
 | |
| 			# just write the new index again sorted by column 1 and 2 | |
| 			sort -t '|' -u -k 1,2 "${HISTORY_IDX_TMP_FILE}2" > "$HISTORY_FILE" | |
| 			entries=$(cat $HISTORY_FILE | wc -l) | |
| 			log "\tWrote the updated history index with $entries entries to file $HISTORY_FILE." | |
| 
 | |
| 			# write also the meta-information, for now only the version of the file | |
| 			prop_file="${HISTORY_FILE}.properties" | |
| 			echo -e "# This file was automatically generated by pr-auto-timer v$VERSION" > $prop_file | |
| 			echo -e "# Do not delete or change this file manually. It contains properties" >> $prop_file | |
| 			echo -e "# about the .show_history file which are used by pr-auto-timer to" >> $prop_file | |
| 			echo -e "# determine the correct format of the file." >> $prop_file | |
| 			echo -e "event_index_version=1" >> $prop_file | |
| 		else | |
| 			log "\tDEBUG DRY RUN: Would update now $HISTORY_FILE now. Skipped due to dry run option." | |
| 		fi | |
| 	fi | |
| } | |
| 
 | |
| generate_timer_index() { | |
| 	# Retrieves all timers, collects EPG-information for each one and generates an index with this information | |
| 	# No parameters | |
| 	# Result: The temporary file TIMER_IDX_TMP_FILE contains info all currently set timers. | |
| 	# Currently there is a single line of the format "Title|Info" for each timer. | |
| 
 | |
| 	local timerlist timer_id timer_type timer_start channel_name epg_data title info1 info2 info timer_index | |
| 
 | |
| 	log "\t\tgenerate_timer_index: collecting information from already set timers ..." | |
| 
 | |
| 	# first retrieve all set timers | |
| 	timerlist=$(wget -qO - "$webserver_url/control/timer") | |
| 
 | |
| 	# process each timer, and try to find EPG-data for it | |
| 	timer_index=$(echo -e "$timerlist" | while read timer; | |
| 	do | |
| 		channel_name=$(echo "$timer" | cut -d ' ' -f 8-) | |
| 		timer_id=$(echo "$timer" | cut -d ' ' -f 1) | |
| 		timer_type=$(echo "$timer" | cut -d ' ' -f 2) | |
| 		timer_start=$(echo "$timer" | cut -d ' ' -f 6) | |
| 
 | |
| 		# We care only for record-type timers | |
| 		if [ $timer_type -eq 5 ]; then | |
| 			# get EPG information for each of them | |
| 			log "\t\t+- Retrieving EPG-data for timer id $timer_id starting at $(date -D %s -d $timer_start +'%Y-%m-%d %H:%M:%S') ($timer_start) on channel $channel_name ..." | |
| 			epg_data=$(find_show_by_start_sec "$channel_name" "$timer_start") | |
| 			if [ -n "$epg_data" ]; then | |
| 				begin_ifs_block | |
| 					IFS='|' | |
| 					set -- $epg_data | |
| 					title=$5 | |
| 					info1=$6 | |
| 					info2=$7 | |
| 				end_ifs_block | |
| 
 | |
| 				# now check which information is available in the stored xml | |
| 				if [ -z "$info1" ] || [ "$info1" == "$title" ]; then | |
| 					# info1 was empty or exactly the same as the title | |
| 					# in this case, we use the INFO2_CHARACTERS from info2 nothing else to do; if this is empty too, nothing can be done | |
| 					info=${info2:0:$INFO2_CHARACTERS} | |
| 				else | |
| 					info="$info1" | |
| 				fi | |
| 
 | |
| 				log "\t\tAdding '$title|$info' to the future timer index." | |
| 
 | |
| 				# update the index | |
| 				echo -e "$title|$info\n" | |
| 
 | |
| 			else | |
| 				log "\t\t| +- Warning: No EPG info found for timer id $timer_id starting at $timer_start on channel $channel_name. Skipping" | |
| 			fi | |
| 		fi | |
| 	done;) | |
| 
 | |
| 	# For the sake of completeness, just sort it - there should not be any duplicates here, but to be sure | |
| 	# (could only happen if a timer was set additionally) | |
| 	timer_index=$(echo -e "$timer_index" | sort -u) | |
| 
 | |
| 	# write it to a temporary file, in order to access from different subprocesses | |
| 	echo -e "$timer_index" > $TIMER_IDX_TMP_FILE | |
| } | |
| 
 | |
| add_historic_timer() { | |
| 	#Timer zur internen Liste hinzufügen, um nochmaligen Eintrag nach manuellem löschen zu verhindern. | |
| 	#$1: channel name | |
| 	#$2: alarm time | |
| 	#$3: stop time | |
| 	#$4: timerd event type | |
| 	local S | |
| 	S="$1;$2;$3;$4" | |
| 	if [ $DEL_TIMER_REFRESH -lt 2 ] && [ "$TMR2_FILE" != "" ]; then | |
| 		if  ! find_historic_timer "$1" $2 $3 $4 1; then | |
| 			echo -e "$S" >> $TMR2_FILE | |
| 		fi | |
| 	fi | |
| } | |
| 
 | |
| clean_historic_timer_file() { | |
| 	local jetzt=$(date +%s) | |
| 	local i=0 | |
| 	local a=0 | |
| 	if [ -e $TMR2_TEMPFILE ]; then | |
| 		rm $TMR2_TEMPFILE | |
| 		log "\tFound old tempfile for historic timers and deleted it." | |
| 	fi | |
| 
 | |
| 	if [ $DEL_TIMER_REFRESH -lt 2 ]; then | |
| 		log "\tProcessing historic timers index (DEL_TIMER_REFRESH=$DEL_TIMER_REFRESH).." | |
| 		if [ -e $TMR2_FILE ]; then | |
| 			while read line | |
| 			do | |
| 				# split line | |
| 				begin_ifs_block | |
| 					IFS=';' | |
| 					set -- $line | |
| 					channelname="$1" | |
| 					starttime=$2 | |
| 					endtime=$3 | |
| 					type=$4 | |
| 				end_ifs_block | |
| 				if [ $endtime -gt $jetzt ]; then | |
| 						if [ $MAX_TMR2_COUNT -gt $a ]; then | |
| 							echo -e "$line" >> $TMR2_TEMPFILE | |
| 						fi | |
| 					a=$(($a+1)) | |
| 				else | |
| 					i=1 | |
| 				fi | |
| 			done < $TMR2_FILE | |
| 			if [ $i -eq 1 ]; then | |
| 				log "\t$TMR2_FILE cleaned (removed $a entries)." | |
| 				rm $TMR2_FILE | |
| 				if [ -e $TMR2_TEMPFILE ]; then | |
| 					cp $TMR2_TEMPFILE $TMR2_FILE | |
| 				fi | |
| 			fi | |
| 		fi | |
| 		if [ -e $TMR2_TEMPFILE ]; then | |
| 			rm $TMR2_TEMPFILE | |
| 			log "\tDeleted tempfile for historic timers." | |
| 		fi | |
| 	else | |
| 		if [ -e $TMR2_FILE ]; then | |
| 			rm $TMR2_FILE | |
| 			log "\tFound historic timer file, but historic timers are deactivated in settings (DEL_TIMER_REFRESH=2). Deleted $TMR2_FILE." | |
| 		fi | |
| 	fi | |
| } | |
| 
 | |
| find_historic_timer() { | |
| 	#Find an (exact) existing timer using channel name, alarm time, stop time and event type | |
| 	#$1: channel name | |
| 	#$2: alarm time | |
| 	#$3: stop time | |
| 	#$4: event type (5: record, 3: zapto) | |
| 	#$5: Override = 1 | |
| 	local S | |
| 	local ret | |
| 	S="$1;$2;$3;$4" | |
| 	ret=1 | |
| 	if [ $DEL_TIMER_REFRESH -eq 0 ] || [ $5 -eq 1 ]; then | |
| 		if [ -e $TMR2_FILE ]; then | |
| 			while read line | |
| 			do | |
| 				if [ "$line" == "$S" ]; then | |
| 					ret=0 | |
| 					break | |
| 				fi | |
| 			done < $TMR2_FILE | |
| 		fi | |
| 	fi | |
| 	return $ret | |
| } | |
| 
 | |
| find_already_recorded() { | |
| 	# Find if a show with a certain description and info is either already recorded (i.e. in the history-file) or | |
| 	# a timer was already set (i.e. it is in the future timer index) | |
| 	#$1: description | |
| 	#$2: info | |
| 	local show ret title info timestamp | |
| 
 | |
| 	show="$1|$2" | |
| 	ret=1 | |
| 
 | |
| 	# determine if the the HISTORY_INDEX needs updating | |
| 	if [ ! -e "$HISTORY_IDX_TMP_FILE" ]; then | |
| 		log "\t\tfind_already_recorded: Updating/generating index file for previous records..." | |
| 		generate_history_index | |
| 	fi | |
| 
 | |
| 	# determine if the TIMER_INDEX needs updating | |
| 	if [ ! -e "$TIMER_IDX_TMP_FILE" ]; then | |
| 		log "\t\tfind_already_recorded: Updating future timer index ..." | |
| 		generate_timer_index | |
| 	fi | |
| 
 | |
| 	if [ -e $HISTORY_IDX_TMP_FILE ]; then | |
| 		while read line | |
| 		do | |
| 			#echo -e "processing line $line .." | |
| 			begin_ifs_block | |
| 				IFS='|' | |
| 				set -- $line | |
| 				title=$1 | |
| 				info=$2 | |
| 			end_ifs_block | |
| 
 | |
| 			if [ "$title|$info" == "$show" ]; then | |
| 				ret=0 | |
| 				break | |
| 			fi | |
| 		done < $HISTORY_IDX_TMP_FILE | |
| 	fi | |
| 
 | |
| 	if [ -e $TIMER_IDX_TMP_FILE ]; then | |
| 		while read line | |
| 		do | |
| 			#echo -e "processing line $line .." | |
| 			if [ "$line" == "$show" ]; then | |
| 				ret=0 | |
| 				break | |
| 			fi | |
| 		done < $TIMER_IDX_TMP_FILE | |
| 	fi | |
| 
 | |
| 	return $ret | |
| } | |
| 
 | |
| #END SECTION "Event & Timer Index" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Timer Handling" | |
| 
 | |
| add_timer() { | |
| 	#Add a timer using channel name, channel id, alarm time, stop time and event type if it does not already exist. Overlapping timers are removed before. | |
| 	#$1: channel name | |
| 	#$2: channel id | |
| 	#$3: alarm time | |
| 	#$4: stop time | |
| 	#$5: timerd event type | |
| 	#$6: alt rec dir | |
| 	#Note: Announce time is ignored. | |
| 	local S ret | |
| 	S="$1;$3;$4;$5" | |
| 	ret=1 | |
| 	if [ "$first_match" = "done" ]; then | |
| 		return $ret | |
| 	fi | |
| 	if find_neutrino_timer "$1" $3 $4 $5; then | |
| 		log "\t\tadd_timer: --- An identical timer '$1' $3 $4 $5 is currently already set. Skipping. ---" | |
| 	elif [ "$ignore_timer_refresh" == "false" ] && find_historic_timer "$1" $3 $4 $5 0; then | |
| 		log "\t\tadd_timer: --- An identical timer '$1' $3 $4 $5 was previously set. Skipping. ---" | |
| 	elif prevent_timer_by_timespan $3; then | |
| 		log "\t\tadd_timer: --- Preventing timer '$1' $3 ($(date -D %s -d ${3} +"%H:%M")) by timespan ($timespan). Skipping. ---" | |
| 	else | |
| 		remove_overlapping_timer "$1" $3 $5 | |
| 		if [ $DEBUG_DRY_RUN == 0 ]; then | |
| 			result=$(wget -qO - "$webserver_url/control/timer?action=new&type=$5&alarm=$3&stop=$4&channel_id=$2&rec_dir=$6") | |
| 			if [ "$ignore_timer_refresh" == "false" ]; then | |
| 				add_historic_timer "$1" $3 $4 $5 | |
| 				ret=0 | |
| 			fi | |
| 		else | |
| 			result="DISABLED (DEBUG_DRY_RUN)" | |
| 		fi | |
| 		log "\t\tadd_timer: Adding timer '$1' $3 $4 $5: $result." | |
| 		if [ "$first_match" = "true" ]; then | |
| 			log "\t\tadd_timer: Ignoring possible following shows on '$1'." | |
| 			first_match=done; | |
| 		fi | |
| 	fi | |
| 	return $ret | |
| } | |
| 
 | |
| remove_overlapping_timer() { | |
| 	#Find and remove overlapping/similar timers resulting from changed EPG data | |
| 	#$1: channel name (ignore case) | |
| 	#$2: alarm time | |
| 	#$3: timerd event type | |
| 	#returns: API result (hopefully: 'ok') | |
| 	#Note: remove_overlapping_timer ignores repeated timers | |
| 	alarm_time=$2 | |
| 	wget -qO - "$webserver_url/control/timer" | awk -v pattern="# $3 0 0 # # # $1" '{ | |
| 		timer_id=$1; | |
| 		tstart_sec=$6; | |
| 		$1=$5=$6=$7="#"; | |
| 		if(tolower($0)==tolower(pattern)) { | |
| 			print timer_id " " tstart_sec; | |
| 		} | |
| 	}' | while read timer; do | |
| 		begin_ifs_block | |
| 			IFS=' ' | |
| 			set -- $timer | |
| 			timer_id=$1 | |
| 			tstart_sec=$2 | |
| 		end_ifs_block | |
| 
 | |
| 		if [ $alarm_time -gt $tstart_sec ]; then | |
| 			diff_secs=$(( $alarm_time - $tstart_sec )) | |
| 		else | |
| 			diff_secs=$(( $tstart_sec - $alarm_time )) | |
| 		fi | |
| 		if [ $diff_secs -le $max_diff_secs ]; then | |
| 			if [ $DEBUG_DRY_RUN == 0 ]; then | |
| 				result=$(wget -qO - "$webserver_url/control/timer?action=remove&id=$timer_id") | |
| 			else | |
| 				result="DISABLED: DEBUG_DRY_RUN" | |
| 			fi | |
| 			log "\t\tremove_overlapping_timer: Timer id: $timer_id overlaps by $diff_secs seconds (max: $max_diff_secs). Removing: $result" | |
| 		fi | |
| 	done | |
| } | |
| 
 | |
| find_neutrino_timer() { | |
| 	#Find an (exact) existing timer using channel name, alarm time, stop time and event type | |
| 	#$1: channel name (ignore case) | |
| 	#$2: alarm time | |
| 	#$3: stop time | |
| 	#$4: event type (5: record, 3: zapto) | |
| 	#Note: find_neutrino_timer ignores repeated timers | |
| 	wget -qO - "$webserver_url/control/timer" | awk -v pattern="# $4 0 0 # $2 $3 $1" ' | |
| 		BEGIN { | |
| 			returncode=1; | |
| 		} | |
| 		{ | |
| 			$1=$5="#"; | |
| 			if(tolower($0)==tolower(pattern)) { | |
| 				returncode=0; | |
| 			} | |
| 		} | |
| 		END { | |
| 			exit returncode; | |
| 		} | |
| 	' | |
| } | |
| 
 | |
| get_overlapping_timers() { | |
| 	# Retrieve the maximum number of concurrent/overlapping timers for a given time span | |
| 	# $1: alarm time | |
| 	# $2: stop time | |
| 	# $3: channel_id for the new timer | |
| 	# Returns a value of the form "max_transponders|max_recordings", where max_transponders is the number of how many tuners | |
| 	# would be required, when setting the new timer and max_recordings is the number of how many recordings would happen | |
| 	# at most in parallel during the timespan of the new timer | |
| 	local result timerlist timerlist_extended maxts maxrec | |
| 
 | |
| 	# get the timerlist; Note: this cannot be cached, otherwise we would miss timers set during the same run | |
| 	# if we really need to cache it, we would need to update the cache as well, when adding a new timer | |
| 	timerlist=$(wget -qO - "$webserver_url/control/timer") | |
| 
 | |
| 	# we need the transponder_id inside awk, to determine how many tuners are really needed | |
| 	# therefore, the retrieved timerlist is extended to include this information before passing | |
| 	# it to awk. | |
| 	timerlist_extended=$(echo -e "$timerlist" | while read timer; do | |
| 		channel_name=$(echo "$timer" | cut -d ' ' -f 8-) | |
| 		timer_line=$(echo "$timer" | cut -d ' ' -f 1-7) | |
| 		sid=$(get_channel_id_by_channel_name "$channel_name") | |
| 
 | |
| 		echo "$timer_line $sid $channel_name" | |
| 	done;) | |
| 
 | |
| 	result=$(echo -e "$timerlist_extended" | awk -v span_start="$1" -v span_stop="$2" -v span_sid="$3" ' | |
| 		BEGIN { | |
| 			#printf ("Analysing timespan %i-%i (%is = %i Min) with respect to new timer on sid %s\n",span_start,span_stop,span_stop-span_start,(span_stop-span_start)/60,span_sid); | |
| 			span_tsid=substr(span_sid,5,4); | |
|  | |
| 			#initalize the array with the tsid of the potential new timer | |
| 			for (time_point=span_start; time_point<=span_stop; time_point++) { | |
| 				time_span[time_point,"ts"] = span_tsid; | |
| 				time_span[time_point,"rec"] = 1; | |
| 			} | |
| 		} | |
| 		{ | |
| 			timer_type=$2; | |
| 			# process timers only for record timers | |
| 			if (timer_type == 5) { | |
| 				# check for each existing timer, if it overlaps with the provided timespan | |
| 				timer_start=$6; | |
| 				timer_stop=$7; | |
| 				timer_sid=$8; | |
| 				timer_tsid=substr(timer_sid,5,4) | |
| 				timer_channel_name=substr($0, index($0,$9)); | |
|  | |
| 				# uncomment for debugging, keep commented in normal usage, since the output is the result, where a single integer ist expected | |
| 				#printf ("%s: alarmTime=%i, stopTime=%i, Type=%i, channel=%s (CID: %s, TSID: %s)\n", $0,timer_start,timer_stop,timer_type,timer_channel_name,timer_sid, timer_tsid); | |
|  | |
| 				# create an array with an entry for each second of the given time span with the number of concurrent timers | |
| 				# wondering, if there is a more efficient way to do this.. ? | |
| 				for (time_point=span_start; time_point<=span_stop; time_point++) { | |
| 					# verify if timepoint is within the currently processed timer | |
| 					if ((time_point >= timer_start) && (time_point <= timer_stop)) { | |
| 						# this timer clashes with the new timer. Check if the tsid is already used, and if not add the tsid | |
| 						if (! match(time_span[time_point,"ts"],timer_tsid)) { | |
| 							time_span[time_point,"ts"] = time_span[time_point,"ts"] "|" timer_tsid; | |
| 						} | |
| 						# increase the number of recordings for this second | |
| 						time_span[time_point,"rec"]++; | |
| 					} | |
| 				} | |
| 			} | |
| 		} | |
| 		END { | |
| 			#printf "done - calculating max tuners required.. \n"; | |
| 			max_transponders=0; | |
| 			max_recordings=0; | |
| 			for (i=span_start;i<=span_stop;i++) { | |
| 				different_transponders=split(time_span[i,"ts"],array,"|") | |
| 				#printf time_span[i,"ts"] ";"; | |
| 				#printf different_transponders; | |
| 				if (different_transponders > max_transponders) { | |
| 					max_transponders=different_transponders; | |
| 				} | |
| 				if (time_span[i,"rec"] > max_recordings) { | |
| 					max_recordings=time_span[i,"rec"]; | |
| 				} | |
| 			} | |
| 			printf ("%i|%i", max_transponders,max_recordings); | |
| 			exit 0 | |
| 		} | |
|  | |
| 	') | |
| 
 | |
| 	if [ $? -eq 0 ]; then | |
| 		#maxts=$(echo $result | cut -d '|' -f1) | |
| 		#maxrec=$(echo $result | cut -d '|' -f2) | |
| 		#log "\t\tget_overlapping_timers: Would require $maxts tuners (for $maxrec parallel recordings) to set new timer in timespan '$1-$2'. (Max available tuners: $MAX_TUNERS)" | |
| 		echo -e "$result" | |
| 		return 0 | |
| 	fi | |
| 
 | |
| } | |
| 
 | |
| prevent_timer_by_timespan() { | |
| 	#$1: alarm time | |
| 	local ret | |
| 
 | |
| 	if [ "$timespan" = "*" ]; then | |
| 		return 1 # don't prevent | |
| 	fi | |
| 
 | |
| 	timer_start_h=$(($(echo $(date -D %s -d ${1} +%H) | sed 's/^0//') * 60 * 60)) | |
| 	timer_start_m=$(($(echo $(date -D %s -d ${1} +%M) | sed 's/^0//') * 60)) | |
| 	timer_start=$((${timer_start_h} + ${timer_start_m})) | |
| 
 | |
| 	timespan_from_h=$(($(echo ${timespan_start_h} | sed 's/^0//') * 60 * 60)) | |
| 	timespan_from_m=$(($(echo ${timespan_start_m} | sed 's/^0//') * 60)) | |
| 	timespan_from=$((${timespan_from_h} + ${timespan_from_m})) | |
| 
 | |
| 	timespan_till_h=$(($(echo ${timespan_stop_h} | sed 's/^0//') * 60 * 60)) | |
| 	timespan_till_m=$(($(echo ${timespan_stop_m} | sed 's/^0//') * 60)) | |
| 	timespan_till=$((${timespan_till_h} + ${timespan_till_m})) | |
| 
 | |
| 	ret=1 | |
| 	if [ "$timespan_over_midnight" = "false" ]; then | |
| 		if ! [ $timer_start -ge $timespan_from -a $timer_start -le $timespan_till ]; then | |
| 			# timer doesn't start inside timespan | |
| 			ret=0 # prevent | |
| 		fi | |
| 	else | |
| 		if [ $timer_start -gt $timespan_till -a $timer_start -lt $timespan_from ]; then | |
| 			# timer starts outside timespan | |
| 			ret=0 # prevent | |
| 		fi | |
| 	fi | |
| 	return $ret | |
| } | |
| 
 | |
| #END SECTION "Timer Handling" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Sky Cinema" | |
| 
 | |
| get_sky_moviedata() { | |
| 	# we use a static tmp-dir | |
| 	test -d /tmp/$ME || mkdir -p /tmp/$ME | |
| 
 | |
| 	data_file=/tmp/$ME/sky_moviedata | |
| 	rm -f $data_file # force download; debug | |
| 	if [ -e $data_file ]; then | |
| 		if [ $(($(date +%s)\-$(date -r $data_file +%s))) -gt 86400 ]; then | |
| 			rm -f $data_file | |
| 		fi | |
| 	fi | |
| 	if [ ! -e $data_file ]; then | |
| 		log "\t\tget_sky_moviedata: get informations ..." | |
| 		wget -qO "$data_file" "http://www.moviepilot.de/liste/sky-neustarts-filmschauer" | |
| 	fi | |
| 
 | |
| 	test -e $data_file && cat $data_file | awk ' | |
| 		BEGIN { | |
| 			false=0; | |
| 			found=false; | |
| 		} | |
|  | |
| 		END { | |
| 		} | |
|  | |
| 		/board_item_description/ { | |
| 			found="date"; | |
| 			date=""; | |
| 			next; | |
| 		} | |
|  | |
| 		/clearfix/ { | |
| 			found=false; | |
| 			next; | |
| 		} | |
|  | |
| 		/<h3>/ { | |
| 			found="movie"; | |
| 			movie=""; | |
| 			next; | |
| 		} | |
|  | |
| 		/<\/h3>/ { | |
| 			found=false; | |
| 			next; | |
| 		} | |
|  | |
| 		/<p>|\/serie\// { | |
| 			next; | |
| 		} | |
|  | |
| 		// { | |
| 			if ( found == "date" ) { | |
| 				gsub (/(\,|\.|\:|\|)/, " "); # replace ",.:|" with " " | |
|  | |
| 				gsub ("Januar"   , "1"); | |
| 				gsub ("Februar"  , "2"); | |
| 				gsub ("März"     , "3"); | |
| 				gsub ("April"    , "4"); | |
| 				gsub ("Mai"      , "5"); | |
| 				gsub ("Juni"     , "6"); | |
| 				gsub ("Juli"     , "7"); | |
| 				gsub ("August"   , "8"); | |
| 				gsub ("September", "9"); | |
| 				gsub ("Oktober"  , "10"); | |
| 				gsub ("November" , "11"); | |
| 				gsub ("Dezember" , "12"); | |
|  | |
| 				#	  YYMMDDhhmm | |
| 				#date	= 9912312313 | |
| 				date	= $6$5$4$7$8; | |
| 			} | |
| 			if ( found == "movie" ) { | |
| 				gsub (/^[[:space:]]*/, ""); | |
| 				gsub (/'/, "'\''"); | |
|  | |
| 				movie	= gensub(/<\/?[^>]+>/, "", "g"); | |
| 				printf("%s %s\n", date, movie); | |
| 			} | |
| 			next; | |
| 		} | |
|  | |
| 		// { | |
| 			found=false; | |
| 			next; | |
| 		} | |
| 	' | |
| } | |
| 
 | |
| get_sky_cinema_channel() { | |
| 	#$1: include search words (ignore case) | |
| 	#$2: exclude search words (ignore case) | |
| 	#returns: name of the first(!) matching channel | |
| 
 | |
| 	# replace ' ' with '.*' and append '.*' | |
| 	include="${1// /.*}.*" | |
| 	# replace ' ' with '|' | |
| 	exclude="${2// /|}" | |
| 
 | |
| 	# replace '+' with '\+' | |
| 	include="${include//+/\\+}" | |
| 	exclude="${exclude//+/\\+}" | |
| 
 | |
| 	# tolower | |
| 	include=$(echo $include | awk '{print tolower($0)}') | |
| 	exclude=$(echo $exclude | awk '{print tolower($0)}') | |
| 
 | |
| 	temp_file="$temp_dir/channellist" | |
| 	if [ ! -e "$temp_file" ]; then | |
| 		wget -qO "$temp_file" "$webserver_url/control/channellist" | |
| 	fi | |
| 	sky_cinema_channels=$(grep -iE "Sky.*Cinema.*" $temp_file | cut -d" " -f2- | sort -u ) | |
| 	echo "$sky_cinema_channels" | \ | |
| 	while read channel; do | |
| 		echo "$channel" | awk '{print tolower($0)}' | grep -qE "($include)" | |
| 		if [ $? -ne 0 ]; then | |
| 			continue | |
| 		fi | |
| 		echo "$channel" | awk '{print tolower($0)}' | grep -qvE "($exclude)" | |
| 		if [ $? -eq 0  ]; then | |
| 			echo "$channel" | |
| 			break | |
| 		fi | |
| 	done | |
| } | |
| 
 | |
| parse_sky_cinema() { | |
| 	if [ "$SKY_CINEMA" = "" ]; then | |
| 		sky_cinema="" | |
| 		return | |
| 	fi | |
| 
 | |
| 	# split $SKY_CINEMA | |
| 	begin_ifs_block | |
| 		IFS=';' | |
| 		set -- $SKY_CINEMA | |
| 		sky_cinema=$1 | |
| 		dowfilter=${2:-*} | |
| 		recdir=$3 | |
| 	end_ifs_block | |
| 
 | |
| 	# split dowfilter | |
| 	begin_ifs_block | |
| 		IFS=',' | |
| 		set -- $dowfilter | |
| 		dowfilter=$1 | |
| 		timespan=$2 | |
| 	end_ifs_block | |
| 
 | |
| 	sky_start_sec_add=0 | |
| 	sky_exclude_channels="Active Action Comedy Emotion Family Hits Nostalgie" | |
| 
 | |
| 	case $(echo $sky_cinema | awk '{print toupper($0)}') in | |
| 		SD) | |
| 			sky_cinema=$(get_sky_cinema_channel "Sky Cinema" "+1 +24 HD $sky_exclude_channels") | |
| 		;; | |
| 		HD) | |
| 			sky_cinema=$(get_sky_cinema_channel "Sky Cinema HD" "+1 +24 $sky_exclude_channels") | |
| 		;; | |
| 		1) | |
| 			sky_cinema=$(get_sky_cinema_channel "Sky Cinema +1" "+24 HD $sky_exclude_channels") | |
| 			sky_start_sec_add=$((60 \* 60)) | |
| 		;; | |
| 		1HD) | |
| 			sky_cinema=$(get_sky_cinema_channel "Sky Cinema +1 HD" "+24 $sky_exclude_channels") | |
| 			sky_start_sec_add=$((60 \* 60)) | |
| 		;; | |
| 		24) | |
| 			sky_cinema=$(get_sky_cinema_channel "Sky Cinema +24" "+1 HD $sky_exclude_channels") | |
| 			sky_start_sec_add=$((60 \* 60 \* 24)) | |
| 		;; | |
| 		24HD) | |
| 			sky_cinema=$(get_sky_cinema_channel "Sky Cinema +24 HD" "+1 $sky_exclude_channels") | |
| 			sky_start_sec_add=$((60 \* 60 \* 24)) | |
| 		;; | |
| 		*) | |
| 			sky_cinema="" | |
| 		;; | |
| 	esac | |
| } | |
| 
 | |
| #END SECTION "Sky Cinema" | |
| ####################################################################################### | |
| 
 | |
| ####################################################################################### | |
| #BEGIN SECTION "Main" | |
| 
 | |
| # set the signal handler | |
| trap signal_handler INT TERM | |
| 
 | |
| # First initialize the values from the config, otherwise we cannot log anywhere | |
| init_config | |
| init_temp | |
| 
 | |
| HISTORY_IDX_TMP_FILE="$temp_dir/history_index" | |
| TIMER_IDX_TMP_FILE="$temp_dir/timer_index" | |
| 
 | |
| 
 | |
| # Now get command line option, as these might override some values from the config or default variables | |
| parse_options $@ | |
| 
 | |
| if [ -e $PID_FILE ]; then | |
| 	log "$ME ist already running. Exiting..." | |
| 	exit $EXIT_ALREADY_RUNNING | |
| else | |
| 	echo $$ > $PID_FILE | |
| fi | |
| 
 | |
| # We happily started.. | |
| log "" | |
| log "$ME V$VERSION started." | |
| 
 | |
| 
 | |
| if [ $DEBUG_TEMP == 1 ]; then | |
| 	log "\tmain: !!! WARNING: DEBUG_TEMP is enabled. Temporary files will not be removed !!!" | |
| fi | |
| if [ $DEBUG_DRY_RUN == 1 ]; then | |
| 	log "\tmain: !!! WARNING: DEBUG_DRY_RUN is enabled. Timers will not be modified !!!" | |
| fi | |
| 
 | |
| # prepare for main operation | |
| init_settings | |
| init_webserver | |
| init_displaylog | |
| 
 | |
| # Perform pre-actions, if activated. | |
| log "Executing configured pre-actions ... " | |
| 
 | |
| if [ -n "$PRE_ACTION" ]; then | |
| 	log "\tExecuting '$PRE_ACTION'" | |
| 	eval $PRE_ACTION | |
| 	log "\tDone." | |
| else | |
| 	log "\tNo pre-action configured." | |
| fi | |
| 
 | |
| # check for radio/tv mode of the box and switch to tv, in case radio was found | |
| # todo: we should also give some feedback on the screen, in case -m is used. | |
| box_mode=$(wget -q -O - $webserver_url/control/getmode?channelsmode=true | dos2unix) | |
| case $box_mode in | |
| 	tv) | |
| 		log "\tmain: Box is in ${box_mode}-mode. Proceeding without changing mode." | |
| 	;; | |
| 	radio) | |
| 		box_standby=$(wget -q -O - $webserver_url/control/standby | dos2unix) | |
| 		log "\tmain: Box is in ${box_mode}-mode (standby=$box_standby). Trying to switch to tv-mode ..." | |
| 		result=$(wget -q -O - $webserver_url/control/setmode?tv | dos2unix) | |
| 		if [ "$result" == "ok" ]; then | |
| 			log "\tmain: Successfully switched to tv-mode. Will switch back to $box_mode when finished." | |
| 		else | |
| 			log "\tmain: Could not switch to tv-mode. This is bad and adding timers will not work. Exiting now." | |
| 			cleanup | |
| 			exit 0 | |
| 		fi | |
| 	;; | |
| 	*) | |
| 		log "\tmain: Box is in ${box_mode}-mode! This should not happen and is considered a bad thing. Exiting now." | |
| 		cleanup | |
| 		exit 0 | |
| 	;; | |
| esac | |
| 
 | |
| if [ $opt_menu ]; then | |
| 	msgbox title="$NAME" size=20 timeout=5 refresh=0 cyclic=0 popup="Running..." >/dev/null | |
| fi | |
| 
 | |
| for rule_file in $RULE_FILES; do | |
| 	log "\tmain: processing '${rule_file}'" | |
| 	rule_line=0 | |
| 	cat $rule_file | while read line ;do | |
| 		rule_line=$((${rule_line}+1)) | |
| 		if echo $line | egrep -q '^[[:space:]]*([^#;]+);+([^;]+);+([^;]+);?([^;/]+)?;?([^;]+)?;?$'; then | |
| 
 | |
| 			log "" | |
| 			log "\tmain: Processing rule: $line" | |
| 
 | |
| 			# split config line | |
| 			begin_ifs_block | |
| 				IFS=';' | |
| 				set -- $line | |
| 				channelname=$1 | |
| 				dowfilter=$2 | |
| 				regex=$3 | |
| 				flags=$4 | |
| 				recdir=$5 | |
| 			end_ifs_block | |
| 
 | |
| 			# split dowfilter | |
| 			begin_ifs_block | |
| 				IFS=',' | |
| 				set -- $dowfilter | |
| 				dowfilter=$1 | |
| 				timespan=$2 | |
| 			end_ifs_block | |
| 
 | |
| 			# split regex | |
| 			begin_ifs_block | |
| 				IFS=',' | |
| 				set -- $regex | |
| 				regex=$1 | |
| 				xregex=$2 | |
| 			end_ifs_block | |
| 
 | |
| 			parse_regexes | |
| 			parse_timespan "$timespan" | |
| 			parse_flags "$flags" | |
| 			parse_recdir | |
| 
 | |
| 			log "\tmain: channel: '$channelname', dow: '$dowfilter', timespan: '$timespan', regex: '$regex', extended regex: '${xregex:-none}', extended regex option: '${xregex_option:-none}', event type: '$event_type', flags: '${flags:-none}', rec dir: '${recdir:-std::neutrino}'" | |
| 
 | |
| 			shows=$(find_show "$channelname" "$regex" "$xregex" "$xregex_option") | |
| 			shows_found=0 | |
| 			matches=$(echo "$shows"|wc -l) | |
| 
 | |
| 			echo -e "$shows" | { | |
| 				while read result; do | |
| 					if [ "$result" = "" ]; then | |
| 						break | |
| 					fi | |
| 					shows_found=$(( $shows_found + 1 )) | |
| 
 | |
| 					log "\tmain: Processing match $shows_found / $matches ..." | |
| 
 | |
| 					begin_ifs_block | |
| 						IFS='|' | |
| 						set -- $result | |
| 						channelid=$1 | |
| 						start_sec=$2 | |
| 						stop_sec=$3 | |
| 						channelname=$4 | |
| 						description=$5 | |
| 						info1=$6 | |
| 						info2=$7 | |
| 					end_ifs_block | |
| 
 | |
| 					set -- $(get_corrected_start_stop_times $start_sec $stop_sec $event_type) | |
| 					start_sec_corrected=$1 | |
| 					stop_sec_corrected=$2 | |
| 
 | |
| 					dow=$(date -D %s -d ${start_sec} +%a) | |
| 					parse_dowgroup | |
| 
 | |
| 					# check which information was send in the EPG | |
| 					if [ -z "$info1" ] || [ "$info1" == "$description" ]; then | |
| 						# info1 was empty or exactly the same as the title | |
| 						# in this case, we use the $INFO2_CHARACTERS from info2; nothing else to do; if this is empty too, we can't do anything | |
| 						info=${info2:0:$INFO2_CHARACTERS} | |
| 					else | |
| 						info="$info1" | |
| 					fi | |
| 
 | |
| 					log "\t\tEvent content: '$description' ('$info') on channel '$channelname' ($channelid)" | |
| 					log "\t\tEvent times: $(date -D %s -d ${start_sec} +'%a, %Y-%m-%d'), $(date -D %s -d ${start_sec} +'%H:%M:%S')-$(date -D %s -d ${stop_sec} +'%H:%M:%S') ($start_sec-$stop_sec), Corrected: $(date -D %s -d ${start_sec_corrected} +'%H:%M:%S')-$(date -D %s -d ${stop_sec_corrected} +'%H:%M:%S') ($start_sec_corrected-$stop_sec_corrected)" | |
| 
 | |
| 					if [ $start_sec_corrected -gt $(date +%s) ]; then | |
| 						if [ "$dow" = "$dowfilter" -o "$dowfilter" = "*" -o "$dowgroup" = "true" ]; then | |
| 							if  [ "$prevent_duplicates" == "false" ] || ! find_already_recorded "$description" "$info" "$info2"; then | |
| 								overlaps=$(get_overlapping_timers $start_sec_corrected $stop_sec_corrected $channelid) | |
| 								maxts=$(echo $overlaps | cut -d '|' -f1) | |
| 								maxrec=$(echo $overlaps | cut -d '|' -f2) | |
| 								log "\t\tmain: Would require $maxts / $MAX_TUNERS tuners (for $maxrec / $MAX_RECORDS parallel recordings) to set new timer in timespan '$(date -D %s -d ${start_sec_corrected} +'%H:%M:%S')-$(date -D %s -d ${stop_sec_corrected} +'%H:%M:%S')'." | |
| 								if [ $maxts -le $MAX_TUNERS ] && [ $maxrec -le $MAX_RECORDS ]; then | |
| 									if add_timer "$channelname" $channelid $start_sec_corrected $stop_sec_corrected $event_type $recdir; then | |
| 										log "\t\tmain: ########### Timer for '$description' ('$info') on channel '$channelname' successfully added ###########!" | |
| 										if [ "$prevent_duplicates" == "true" ] && ( [ -n "$info" ] || [ "$allow_empty_info" == "true" ] ); then | |
| 											log "\t\tmain: Duplicate prevention is active; adding '$description|$info' to the future timer index." | |
| 											echo "$description|$info" >> $TIMER_IDX_TMP_FILE | |
| 										fi | |
| 									#else | |
| 									#	log "\t\tmain: Timer NOT added." | |
| 									fi | |
| 									if [ "$first_match" = "done" ]; then | |
| 										if [ "$only_once" = "true" ]; then | |
| 											log "\t\tmain: Deactivating entry in rules-file as requested." | |
| 											sed -i "${rule_line} s/^/#/g" ${rule_file} | |
| 										fi | |
| 										break | |
| 									fi | |
| 								else | |
| 									log "\t\tmain: --- NOT adding timer because no more tuners available/to many parallel recordings for this time span. ---" | |
| 								fi | |
| 							else | |
| 								log "\t\tmain: --- NOT adding timer because show was already recorded/planed before. ---" | |
| 							fi | |
| 						else | |
| 							log "\t\tmain: --- NOT adding timer because of non-matching weekday. ---" | |
| 						fi | |
| 					else | |
| 						log "\t\tmain: --- NOT adding timer because start_secs lies in the past. ---" | |
| 					fi | |
| 				done | |
| 				displaylog "~T`scale2res 0150` $channelname ~T`scale2res 0300` $dowfilter ~T`scale2res 0425` $regex ~T`scale2res 0900` $shows_found Treffer" | |
| 			} | |
| 		fi | |
| 	done # while loop | |
| done # for loop | |
| 
 | |
| # After processing all rules from the rule-file, we go for the "sky-cinema" function, if activated. | |
| parse_timespan	# reset timespan | |
| parse_flags	# reset flags | |
| parse_sky_cinema | |
| 
 | |
| if [ ! "$sky_cinema" = "" ]; then | |
| 	log "" | |
| 	log "\tmain: processing new movies at \"$sky_cinema\"" | |
| 
 | |
| 	parse_timespan "$timespan" | |
| 	parse_recdir | |
| 
 | |
| 	sky_moviedata=$(get_sky_moviedata) | |
| 
 | |
| 	echo "$sky_moviedata" | while read sky_date sky_movie; do | |
| 		if sky_start_sec=$(date -d "$sky_date" +%s 2>/dev/null); then | |
| 			now=$(date +%s) | |
| 			if [ $sky_start_sec -lt $now ]; then | |
| 				continue | |
| 			fi | |
| 			sky_start_sec=$((sky_start_sec \+ sky_start_sec_add)) | |
| 			sky_show_data=$(find_show_by_start_sec "$sky_cinema" "$sky_start_sec") | |
| 			if [ ! "$sky_show_data" = "" ]; then | |
| 				# split sky_show_data | |
| 				begin_ifs_block | |
| 					IFS='|' | |
| 					set -- $sky_show_data | |
| 					channelid=$1 | |
| 					start_sec=$2 | |
| 					stop_sec=$3 | |
| 				end_ifs_block | |
| 
 | |
| 				set -- $(get_corrected_start_stop_times $start_sec $stop_sec $event_type) | |
| 				start_sec_corrected=$1 | |
| 				stop_sec_corrected=$2 | |
| 
 | |
| 				dow=$(date -D %s -d ${start_sec} +%a) | |
| 				parse_dowgroup | |
| 
 | |
| 				if [ $start_sec_corrected -gt $(date +%s) ]; then | |
| 					if [ "$dow" = "$dowfilter" -o "$dowfilter" = "*" -o "$dowgroup" = "true" ]; then | |
| 						add_timer "$sky_cinema" $channelid $start_sec_corrected $stop_sec_corrected $event_type $recdir | |
| 						displaylog "~T`scale2res 0150` $sky_cinema ~T`scale2res 0300` $(date -D %s -d ${start_sec} '+%d.%m.%Y %H:%M') ~T`scale2res 0425` $sky_movie" | |
| 					else | |
| 						log "\t\tmain: NOT adding timer because of non-matching weekday." | |
| 					fi | |
| 				fi | |
| 			fi | |
| 		fi | |
| 	done | |
| fi | |
| 
 | |
| # Display some results to the screen, if the option -m is given | |
| if [ $opt_menu ]; then | |
| 	msgbox title="$NAME" size=20 refresh=0 cyclic=0 timeout=15 popup=$displaylogfile >/dev/null | |
| fi | |
| 
 | |
| if [ $DEBUG_DISPLAYLOG == 1 ]; then | |
| 	cat $displaylogfile | |
| fi | |
| 
 | |
| log "$ME V$VERSION now performing cleanup .." | |
| 
 | |
| # Perform some cleanup before exiting | |
| # maybe bundle the calls to all cleanup actions in one function ? | |
| clean_historic_timer_file | |
| clean_history_file | |
| cleanup | |
| 
 | |
| # Perform post-actions, if activated. | |
| log "Executing configured post-actions ... " | |
| 
 | |
| if [ -n "$POST_ACTION" ]; then | |
| 	log "\tExecuting '$POST_ACTION'" | |
| 	eval $POST_ACTION | |
| 	log "\tDone." | |
| else | |
| 	log "\tNo post-action configured." | |
| fi | |
| 
 | |
| # Perform final operation, if activated. | |
| log "Performing final operation ... (END_SHUT_DOWN=$END_SHUT_DOWN)" | |
| if [ $END_SHUT_DOWN -gt 0 ]; then | |
| 	# wait while recording | |
| 	while [ $DEBUG_DRY_RUN -eq 0 ] && [ $(wget -q -O - $webserver_url/control/setmode?status | dos2unix) = "on" ] | |
| 	do | |
| 		log "\tRecording is active, waiting with final operation for 900 seconds." | |
| 		sleep 900 | |
| 	done | |
| 
 | |
| 	Stunde=$(date +%H) | |
| 	if [ x"$(pidof EPGscan.sh)" = "x" ]; then | |
| 		if [ $SHUT_DOWN_ALSO_DAY -eq 1 ] || [ $Stunde -lt 6 ]; then | |
| 			case $END_SHUT_DOWN in | |
| 				1) | |
| 					if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 						log "\tSetting the box to standby in 2 seconds." | |
| 						(sleep 2; wget -qO "/dev/null" "$webserver_url/control/standby?on") & | |
| 					else | |
| 						log "\tDEBUG DRY RUN: Would now set the box to standby in 2 seconds (using control/standby?on). Skipping." | |
| 					fi | |
| 				;; | |
| 				2) | |
| 					if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 						log "\tInitiating shutdown of the box in 2 seconds." | |
| 						(sleep 2; wget -qO "/dev/null" "$webserver_url/control/shutdown") & | |
| 					else | |
| 						log "\tDEBUG DRY RUN: Would now shutdown the box in 2 seconds (using /control/shutdown). Skipping." | |
| 					fi | |
| 				;; | |
| 				3) | |
| 					if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 						log "\tHalting the system in 2 seconds." | |
| 						(sleep 2; halt) & | |
| 					else | |
| 						log "\tDEBUG DRY RUN: Would now halt the box in 2 seconds (using command 'halt'). Skipping." | |
| 					fi | |
| 				;; | |
| 				4) | |
| 					if [ $DEBUG_DRY_RUN -eq 0 ]; then | |
| 						log "\tRebooting the system in 2 seconds." | |
| 						(sleep 2; reboot) & | |
| 					else | |
| 						log "\tDEBUG DRY RUN: Would now reboot the box in 2 seconds (using command 'reboot'). Skipping." | |
| 					fi | |
| 				;; | |
| 			esac | |
| 		else | |
| 			log "\tSkipping final operation during day (SHUT_DOWN_ALSO_DAY=$SHUT_DOWN_ALSO_DAY)." | |
| 		fi | |
| 	else | |
| 		log "\tIgnoring final operation since a running EPGscan was found!" | |
| 	fi | |
| else | |
| 	log "\tFinal operation is deactivated!" | |
| fi | |
| 
 | |
| log "$ME V$VERSION exiting." | |
| 
 | |
| #END SECTION "Main" | |
| #######################################################################################
 | |
| 
 |