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 tewsbo (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 tewsbo (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 Uno"
|
|
|
|
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"
|
|
#######################################################################################
|
|
|