Exemple #1
0
    def addTimer(self):
        if self.isRecording or self.event is None:
            return
        timer, isRecordEvent = self.doesTimerExist()
        if timer and isRecordEvent:
            menu = [(_("Delete Timer"), "delete"), (_("Edit Timer"), "edit")]
            buttons = ["red", "green"]

            def timerAction(choice):
                if choice is not None:
                    if choice[1] == "delete":
                        self.removeTimer(timer)
                    elif choice[1] == "edit":
                        self.session.openWithCallback(self.finishedEdit,
                                                      TimerEntry, timer)

            text = [_("Select action for timer '%s'.") % timer.name]
            if timer.repeated:
                text.insert(0, _("Attention, this is a repeated timer!"))
            self.session.openWithCallback(timerAction,
                                          ChoiceBox,
                                          text="\n".join(text),
                                          list=menu,
                                          keys=buttons)
        else:
            newEntry = RecordTimerEntry(self.serviceRef,
                                        checkOldTimers=True,
                                        dirname=preferredTimerPath(),
                                        *parseEvent(self.event))
            newEntry.justplay = config.recording.timer_default_type.value == "zap"
            newEntry.always_zap = config.recording.timer_default_type.value == "zap+record"
            self.session.openWithCallback(self.finishedAdd, TimerEntry,
                                          newEntry)
Exemple #2
0
	def timerAdd(self):
		cur = self["list"].getCurrent()
		event = cur[0]
		if event is None:
			return
		serviceref = cur[1]
		isRecordEvent = isRepeat = firstNextRepeatEvent = isRunning = False
		eventid = event.getEventId()
		begin = event.getBeginTime()
		end = begin + event.getDuration()
		refstr = ':'.join(serviceref.ref.toString().split(':')[:11])
		for timer in self.session.nav.RecordTimer.getAllTimersList():
			needed_ref = ':'.join(timer.service_ref.ref.toString().split(':')[:11]) == refstr
			if needed_ref and timer.eit == eventid and (begin < timer.begin <= end or timer.begin <= begin <= timer.end):
				isRecordEvent = True
				break
			elif needed_ref and timer.repeated and self.session.nav.RecordTimer.isInRepeatTimer(timer, event):
				isRecordEvent = True
				break
		if isRecordEvent:
			isRepeat = timer.repeated
			prev_state = timer.state
			isRunning = prev_state in (1, 2)
			title_text = isRepeat and _("Attention, this is repeated timer!\n") or ""
			firstNextRepeatEvent = isRepeat and (begin < timer.begin <= end or timer.begin <= begin <= timer.end) and not timer.justplay
			menu = [(_("Delete timer"), "delete"), (_("Edit timer"), "edit")]
			buttons = ["red", "green"]
			if not isRunning:
				if firstNextRepeatEvent and timer.isFindRunningEvent() and not timer.isFindNextEvent():
					menu.append((_("Options disable timer"), "disablerepeat"))
				else:
					menu.append((_("Disable timer"), "disable"))
				buttons.append("yellow")
			elif prev_state == 2 and firstNextRepeatEvent:
				menu.append((_("Options disable timer"), "disablerepeatrunning"))
				buttons.append("yellow")
			menu.append((_("Timer Overview"), "timereditlist"))

			def timerAction(choice):
				if choice is not None:
					if choice[1] == "delete":
						self.removeTimer(timer)
					elif choice[1] == "edit":
						self.session.openWithCallback(self.finishedEdit, TimerEntry, timer)
					elif choice[1] == "disable":
						self.disableTimer(timer, prev_state)
					elif choice[1] == "timereditlist":
						self.session.open(TimerEditList)
					elif choice[1] == "disablerepeatrunning":
						self.disableTimer(timer, prev_state, repeat=True, record=True)
					elif choice[1] == "disablerepeat":
						self.disableTimer(timer, prev_state, repeat=True)
			self.session.openWithCallback(timerAction, ChoiceBox, title=title_text + _("Select action for timer '%s'.") % timer.name, list=menu, keys=buttons)
		else:
			newEntry = RecordTimerEntry(serviceref, checkOldTimers=True, dirname=preferredTimerPath(), *parseEvent(event))
			newEntry.justplay = config.recording.timer_default_type.value == "zap"
			newEntry.always_zap = config.recording.timer_default_type.value == "zap+record"
			self.session.openWithCallback(self.finishedAdd, TimerEntry, newEntry)
def addTimer(session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, vpsinfo=None, logentries=None, eit=0, always_zap=-1):
	serviceref = unquote(serviceref)
	rt = session.nav.RecordTimer

	print "mao1", dirname

	if not dirname:
		dirname = preferredTimerPath()

	print "mao2", dirname

	try:
		timer = RecordTimerEntry(
			ServiceReference(serviceref),
			begin,
			end,
			name,
			description,
			eit,
			disabled,
			justplay,
			afterevent,
			dirname=dirname,
			tags=tags)

		timer.repeated = repeated

		if logentries:
			timer.log_entries = logentries

		conflicts = rt.record(timer)
		if conflicts:
			errors = []
			for conflict in conflicts:
				errors.append(conflict.name)

			return {
				"result": False,
				"message": _("Conflicting Timer(s) detected! %s") % " / ".join(errors)
			}
		#VPS
		if vpsinfo is not None:
			timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"]
			timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"]
			timer.vpsplugin_time = vpsinfo["vpsplugin_time"]

		if always_zap <> -1:
			if hasattr(timer,"always_zap"):
				timer.always_zap = always_zap == 1

	except Exception, e:
		print e
		return {
			"result": False,
			"message": _("Could not add timer '%s'!") % name
		}
Exemple #4
0
    def timerAdd(self):
        if self.isRecording:
            return
        event = self.event
        serviceref = self.currentService
        if event is None:
            return
        eventid = event.getEventId()
        begin = event.getBeginTime()
        end = begin + event.getDuration()
        refstr = ':'.join(serviceref.ref.toString().split(':')[:11])
        isRecordEvent = False
        for timer in self.session.nav.RecordTimer.getAllTimersList():
            needed_ref = ':'.join(
                timer.service_ref.ref.toString().split(':')[:11]) == refstr
            if needed_ref and timer.eit == eventid and (
                    begin < timer.begin <= end
                    or timer.begin <= begin <= timer.end):
                isRecordEvent = True
                break
            elif needed_ref and timer.repeated and self.session.nav.RecordTimer.isInRepeatTimer(
                    timer, event):
                isRecordEvent = True
                break
        if isRecordEvent:
            title_text = timer.repeated and _(
                "Attention, this is repeated timer!\n") or ""
            menu = [(_("Delete timer"), "delete"), (_("Edit timer"), "edit")]
            buttons = ["red", "green"]

            def timerAction(choice):
                if choice is not None:
                    if choice[1] == "delete":
                        self.removeTimer(timer)
                    elif choice[1] == "edit":
                        self.session.openWithCallback(self.finishedEdit,
                                                      TimerEntry, timer)

            self.session.openWithCallback(
                timerAction,
                ChoiceBox,
                title=title_text +
                _("Select action for timer '%s'.") % timer.name,
                list=menu,
                keys=buttons)
        else:
            newEntry = RecordTimerEntry(self.currentService,
                                        checkOldTimers=True,
                                        dirname=preferredTimerPath(),
                                        *parseEvent(self.event))
            newEntry.justplay = config.recording.timer_default_type.value == "zap"
            newEntry.always_zap = config.recording.timer_default_type.value == "zap+record"
            self.session.openWithCallback(self.finishedAdd, TimerEntry,
                                          newEntry)
Exemple #5
0
	def addCurrentTimer(self):
		event = None
		service = self.session.nav.getCurrentService()
		if service is not None:
			info = service.info()
			if info is not None:
				event = info.getEvent(0)

		# FIXME only works if already playing a service
		serviceref = ServiceReference(self.session.nav.getCurrentlyPlayingServiceOrGroup())

		if event is None:
			data = (int(time()), int(time() + 60), "", "", None)
		else:
			data = parseEvent(event, description=False)
		timer = RecordTimerEntry(serviceref, checkOldTimers=True, dirname=preferredTimerPath(), *data)
		timer.justplay = config.recording.timer_default_type.value == "zap"
		timer.always_zap = config.recording.timer_default_type.value == "zap+record"
		self.addTimer(timer)
Exemple #6
0
	def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False):
		new = 0
		modified = 0

		# Workaround to allow search for umlauts if we know the encoding
		#match = timer.match
		match = timer.match.replace('\xc2\x86', '').replace('\xc2\x87', '')
		if timer.encoding != 'UTF-8':
			try:
				match = match.decode('UTF-8').encode(timer.encoding)
			except UnicodeDecodeError:
				pass

		if timer.searchType == "description":
			epgmatches = []
			mask = (eServiceReference.isMarker | eServiceReference.isDirectory)

			casesensitive = timer.searchCase == "sensitive"
			if not casesensitive:
				match = match.lower()

			# Service filter defined
			# Search only using the specified services
			test = [(service, 0, -1, -1) for service in timer.services]

			for bouquet in timer.bouquets:
				services = serviceHandler.list(eServiceReference(bouquet))
				if not services is None:
					while True:
						service = services.getNext()
						if not service.valid(): #check end of list
							break
						if not (service.flags & mask):
							test.append( (service.toString(), 0, -1, -1 ) )

			if not test:
				# No service filter defined
				# Search within all services - could be very slow

				# Get all bouquets
				bouquetlist = []
				if config.usage.multibouquet.value:
					refstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
					bouquetroot = eServiceReference(refstr)
					bouquets = serviceHandler.list(bouquetroot)
					if bouquets:
						while True:
							s = bouquets.getNext()
							if not s.valid():
								break
							if s.flags & eServiceReference.isDirectory and not s.flags & eServiceReference.isInvisible:
								info = serviceHandler.info(s)
								if info:
									bouquetlist.append((info.getName(s), s))
					mask = (eServiceReference.isMarker | eServiceReference.isDirectory)
					for name, bouquet in bouquetlist:
						if not bouquet.valid(): #check end of list
							break
						if bouquet.flags & eServiceReference.isDirectory:
							services = serviceHandler.list(bouquet)
							if not services is None:
								while True:
									service = services.getNext()
									if not service.valid(): #check end of list
										break
									if not (service.flags & mask):
										test.append( (service.toString(), 0, -1, -1 ) )
				else:
					service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 31) || (type == 134) || (type == 195)'
					refstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(service_types_tv)
					bouquetroot = eServiceReference(refstr)
					info = serviceHandler.info(bouquetroot)
					if info:
						bouquetlist.append((info.getName(bouquetroot), bouquetroot))
					mask = (eServiceReference.isMarker | eServiceReference.isDirectory)
					for name, bouquet in bouquetlist:
						if bouquet.flags & eServiceReference.isDirectory:
							services = serviceHandler.list(bouquet)
							if not services is None:
								while True:
									service = services.getNext()
									if not service.valid(): #check end of list
										break
									if not (service.flags & mask):
										test.append( (service.toString(), 0, -1, -1 ) )

			if test:
				# Get all events
				#  eEPGCache.lookupEvent( [ format of the returned tuples, ( service, 0 = event intersects given start_time, start_time -1 for now_time), ] )
				test.insert(0, 'RITBDSE')
				allevents = epgcache.lookupEvent(test) or []

				# Filter events
				for serviceref, eit, name, begin, duration, shortdesc, extdesc in allevents:
					if match in (shortdesc if casesensitive else shortdesc.lower()) \
						or match in (extdesc if casesensitive else extdesc.lower()):
						epgmatches.append( (serviceref, eit, name, begin, duration, shortdesc, extdesc) )

		else:
			# Search EPG, default to empty list
			epgmatches = epgcache.search( ('RITBDSE', 2500, typeMap[timer.searchType], match, caseMap[timer.searchCase]) ) or []

		# Sort list of tuples by begin time 'B'
		epgmatches.sort(key=itemgetter(3))

		# Contains the the marked similar eits and the conflicting strings
		similardict = defaultdict(list)

		# Loop over all EPG matches
		for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ):

			startLog()

			# timer destination dir
			dest = timer.destination

			evtBegin = begin
			evtEnd = end = begin + duration

			doLog("[AutoTimer] possible epgmatch %s" % (name))
			doLog("[AutoTimer] Serviceref %s" % (str(serviceref)))
			eserviceref = eServiceReference(serviceref)
			evt = epgcache.lookupEventId(eserviceref, eit)
			if not evt:
				doLog("[AutoTimer] Could not create Event!")
				skipped.append((name, begin, end, str(serviceref), timer.name, getLog()))
				continue
			# Try to determine real service (we always choose the last one)
			n = evt.getNumOfLinkageServices()
			if n > 0:
				i = evt.getLinkageService(eserviceref, n-1)
				serviceref = i.toString()
				doLog("[AutoTimer] Serviceref2 %s" % (str(serviceref)))


			# If event starts in less than 60 seconds skip it
			if begin < time() + 60:
				doLog("[AutoTimer] Skipping an event because it starts in less than 60 seconds")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue

			# Set short description to equal extended description if it is empty.
			if not shortdesc and timer.descShortEqualExt:
				shortdesc = extdesc

			# Convert begin time
			timestamp = localtime(begin)
			# Update timer
			timer.update(begin, timestamp)

			# Check if eit is in similar matches list
			# NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive
			similarTimer = False
			if eit in similardict:
				similarTimer = True
				dayofweek = None # NOTE: ignore day on similar timer
			else:
				# If maximum days in future is set then check time
				if checkEvtLimit:
					if begin > evtLimit:
						doLog("[AutoTimer] Skipping an event because of maximum days in future is reached")
						skipped.append((name, begin, end, serviceref, timer.name, getLog()))
						continue

				dayofweek = str(timestamp.tm_wday)

			# Check timer conditions
			# NOTE: similar matches do not care about the day/time they are on, so ignore them
			if timer.checkServices(serviceref):
				doLog("[AutoTimer] Skipping an event because of check services")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue
			if timer.checkDuration(duration):
				doLog("[AutoTimer] Skipping an event because of duration check")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue
			if not similarTimer:
				if timer.checkTimespan(timestamp):
					doLog("[AutoTimer] Skipping an event because of timestamp check")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue
				if timer.checkTimeframe(begin):
					doLog("[AutoTimer] Skipping an event because of timeframe check")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

			# Initialize
			newEntry = None
			oldEntry = None
			oldExists = False
			allow_modify = True
			newAT = None

			# Eventually change service to alternative
			if timer.overrideAlternatives:
				serviceref = timer.getAlternative(serviceref)

			if timer.series_labeling and sp_getSeasonEpisode is not None:
				allow_modify = False
				doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (name, shortdesc, dest))
				sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest)
				if sp and type(sp) in (tuple, list) and len(sp) == 4:
					name = sp[0] or name
					shortdesc = sp[1] or shortdesc
					dest = sp[2] or dest
					doLog(str(sp[3]))
					allow_modify = True
					doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name, shortdesc, dest))
				else:
					# Nothing found
					doLog(str(sp))
					# If AutoTimer name not equal match, do a second lookup with the name
					if timer.name.lower() != timer.match.lower():
						doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (timer.name, shortdesc, dest))
						sp = sp_getSeasonEpisode(serviceref, timer.name, evtBegin, evtEnd, shortdesc, dest)
						if sp and type(sp) in (tuple, list) and len(sp) == 4:
							name = sp[0] or name
							shortdesc = sp[1] or shortdesc
							dest = sp[2] or dest
							doLog(str(sp[3]))
							allow_modify = True
							doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name, shortdesc, dest))
						else:
							doLog(str(sp))

			if timer.checkFilter(name, shortdesc, extdesc, dayofweek):
				doLog("[AutoTimer] Skipping an event because of filter check")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue

			if timer.hasOffset():
				# Apply custom Offset
				begin, end = timer.applyOffset(begin, end)
			else:
				# Apply E2 Offset
				begin -= config.recording.margin_before.value * 60
				end += config.recording.margin_after.value * 60

			# Overwrite endtime if requested
			if timer.justplay and not timer.setEndtime:
				end = begin

			# Check for existing recordings in directory
			if timer.avoidDuplicateDescription == 3:
				# Reset movie Exists
				movieExists = False

				if dest and dest not in moviedict:
					self.addDirectoryToMovieDict(moviedict, dest, serviceHandler)
				for movieinfo in moviedict.get(dest, ()):
					if self.checkDuplicates(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc") ):
						doLog("[AutoTimer] We found a matching recorded movie, skipping event:", name)
						movieExists = True
						break
				if movieExists:
					doLog("[AutoTimer] Skipping an event because movie already exists")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

			# Check for double Timers
			# We first check eit and if user wants us to guess event based on time
			# we try this as backup. The allowed diff should be configurable though.
			for rtimer in timerdict.get(serviceref, ()):
				if rtimer.eit == eit:
					oldExists = True
					doLog("[AutoTimer] We found a timer based on eit")
					newEntry = rtimer
					oldEntry = rtimer
					break
				elif config.plugins.autotimer.try_guessing.value:
					if timer.hasOffset():
						# Remove custom Offset
						rbegin = rtimer.begin + timer.offset[0] * 60
						rend = rtimer.end - timer.offset[1] * 60
					else:
						# Remove E2 Offset
						rbegin = rtimer.begin + config.recording.margin_before.value * 60
						rend = rtimer.end - config.recording.margin_after.value * 60
					# As alternative we could also do a epg lookup
					#revent = epgcache.lookupEventId(rtimer.service_ref.ref, rtimer.eit)
					#rbegin = revent.getBeginTime() or 0
					#rduration = revent.getDuration() or 0
					#rend = rbegin + rduration or 0
					if getTimeDiff(rbegin, rend, evtBegin, evtEnd) > ((duration/10)*8) or timeSimilarityPercent(rtimer, evtBegin, evtEnd, timer) > 80:
						oldExists = True
						doLog("[AutoTimer] We found a timer based on time guessing")
						newEntry = rtimer
						oldEntry = rtimer
						break
				if timer.avoidDuplicateDescription >= 1 \
					and not rtimer.disabled:
						if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ):
						# if searchForDuplicateDescription > 1 then check short description
							oldExists = True
							doLog("[AutoTimer] We found a timer (similar service) with same description, skipping event")
							break

			# We found no timer we want to edit
			if newEntry is None:
				# But there is a match
				if oldExists:
					doLog("[AutoTimer] Skipping an event because a timer on same service exists")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

				# We want to search for possible doubles
				if timer.avoidDuplicateDescription >= 2:
					for rtimer in chain.from_iterable( itervalues(timerdict) ):
						if not rtimer.disabled:
							if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ):
								oldExists = True
								doLog("[AutoTimer] We found a timer (any service) with same description, skipping event")
								break
					if oldExists:
						doLog("[AutoTimer] Skipping an event because a timer on any service exists")
						skipped.append((name, begin, end, serviceref, timer.name, getLog()))
						continue

				if timer.checkCounter(timestamp):
					doLog("[AutoTimer] Not adding new timer because counter is depleted.")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue
			# Append to timerlist and abort if simulating
			timers.append((name, begin, end, serviceref, timer.name, getLog()))
			if simulateOnly:
				continue

			if newEntry is not None:
				# Abort if we don't want to modify timers or timer is repeated
				if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated:
					doLog("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer")
					continue

				if "autotimer" in newEntry.flags:
					msg = "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)
					doLog(msg)
					newEntry.log(501, msg)
				else:
					if config.plugins.autotimer.refresh.value != "all":
						doLog("[AutoTimer] Won't modify existing timer because it's no timer set by us")
						continue

					msg = "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, newEntry.name)
					doLog(msg)
					newEntry.log(501, msg)

				if allow_modify:
					if self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit, base_timer=timer):
						msg = "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name)
						doLog(msg)
						newEntry.log(501, msg)
						modified += 1
					else:
						msg = "[AutoTimer] AutoTimer modification not allowed for timer %s because conflicts or double timer." % (newEntry.name)
						doLog(msg)
						if oldEntry:
							self.setOldTimer(newEntry, oldEntry)
							doLog("[AutoTimer] conflict for modification timer %s detected return to old timer" % (newEntry.name))
						continue
				else:
					msg = "[AutoTimer] AutoTimer modification not allowed for timer: %s ." % (newEntry.name)
					doLog(msg)
					continue
			else:
				newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit)
				newAT = True

				msg = "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name)
				doLog(msg)
				newEntry.log(500, msg)

				# Mark this entry as AutoTimer
				newEntry.flags.add("autotimer")

			# Apply afterEvent
			if timer.hasAfterEvent():
				afterEvent = timer.getAfterEventTimespan(localtime(end))
				if afterEvent is None:
					afterEvent = timer.getAfterEvent()
				if afterEvent is not None:
					newEntry.afterEvent = afterEvent

			newEntry.dirname = dest
			newEntry.calculateFilename()
			newEntry.justplay = timer.justplay
			newEntry.vpsplugin_enabled = timer.vps_enabled
			newEntry.vpsplugin_overwrite = timer.vps_overwrite
			newEntry.conflict_detection = timer.conflict_detection
			newEntry.always_zap = timer.always_zap
			newEntry.zap_wakeup = timer.zap_wakeup

			tags = timer.tags[:]
			if config.plugins.autotimer.add_autotimer_to_tags.value:
				if 'AutoTimer' not in tags:
					tags.append('AutoTimer')
			if config.plugins.autotimer.add_name_to_tags.value:
				tagname = timer.name.strip()
				if tagname:
					tagname = tagname[0].upper() + tagname[1:].replace(" ", "_")
					if tagname not in tags:
						tags.append(tagname)
			newEntry.tags = tags

			if oldExists and newAT is None:
				if self.isResolvedConflict(newEntry):
					recordHandler.timeChanged(newEntry)
				else:
					if oldEntry:
						self.setOldTimer(newEntry, oldEntry)
						doLog("[AutoTimer] rechecking - conflict for timer %s detected return to old timer" % (newEntry.name))
					continue
			elif newAT:
				newAT = newEntry
				conflictString = ""
				if similarTimer:
					conflictString = similardict[eit].conflictString
					msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (conflictString)
					doLog(msg)
					newEntry.log(504, msg)

				# add new timer in AT timer list
				atDoubleTimer = False
				refstr = ':'.join(newEntry.service_ref.ref.toString().split(':')[:11])
				for at in addNewTimers:
					needed_ref = ':'.join(at.service_ref.ref.toString().split(':')[:11]) == refstr
					if needed_ref and at.eit == newEntry.eit and (newEntry.begin < at.begin <= newEntry.end or at.begin <= newEntry.begin <= at.end):
						atDoubleTimer = True
						break
				if atDoubleTimer:
					doLog("[AutoTimer] ignore double new auto timer %s." % newEntry.name)
					continue
				else:
					addNewTimers.append(newEntry)

				# Try to add timer
				conflicts = recordHandler.record(newEntry)

				if conflicts:
					# Maybe use newEntry.log
					conflictString += ' / '.join(["%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts])
					doLog("[AutoTimer] conflict with %s detected" % (conflictString))

					if config.plugins.autotimer.addsimilar_on_conflict.value:
						# We start our search right after our actual index
						# Attention we have to use a copy of the list, because we have to append the previous older matches
						lepgm = len(epgmatches)
						for i in xrange(lepgm):
							servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[ (i+idx+1)%lepgm ]
							if self.checkDuplicates(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True ):
								# Check if the similar is already known
								if eitS not in similardict:
									doLog("[AutoTimer] Found similar Timer: " + name)

									# Store the actual and similar eit and conflictString, so it can be handled later
									newEntry.conflictString = conflictString
									similardict[eit] = newEntry
									similardict[eitS] = newEntry
									similarTimer = True
									if beginS <= evtBegin:
										# Event is before our actual epgmatch so we have to append it to the epgmatches list
										epgmatches.append((servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS))
									# If we need a second similar it will be found the next time
								else:
									similarTimer = False
									newEntry = similardict[eitS]
								break

				if conflicts is None:
					timer.decrementCounter()
					if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]):
						new += 1
						newEntry.extdesc = extdesc
						timerdict[serviceref].append(newEntry)

						# Similar timers are in new timers list and additionally in similar timers list
						if similarTimer:
							similars.append((name, begin, end, serviceref, timer.name))
							similardict.clear()
					else:
						doLog("[AutoTimer] ignore double timer %s." % newEntry.name)

				# Don't care about similar timers
				elif not similarTimer:
					conflicting.append((name, begin, end, serviceref, timer.name))

					if config.plugins.autotimer.disabled_on_conflict.value:
						msg = "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString)
						doLog(msg)
						newEntry.log(503, msg)
						newEntry.disabled = True
						if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]):
							recordHandler.timeChanged(newEntry)
						else:
							# We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
							conflicts = recordHandler.record(newEntry)
					elif newAT != newEntry and newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]):
						if not self.isResolvedConflict(newEntry):
							newEntry.disabled = True
							recordHandler.timeChanged(newEntry)
							doLog("[AutoTimer] Unknown conflict, disable this timer %s." % newEntry.name)

		return (new, modified)
def addTimer(session,
             serviceref,
             begin,
             end,
             name,
             description,
             disabled,
             justplay,
             afterevent,
             dirname,
             tags,
             repeated,
             recordingtype,
             vpsinfo=None,
             logentries=None,
             eit=0,
             always_zap=-1,
             pipzap=-1,
             allow_duplicate=1,
             autoadjust=-1):
    rt = session.nav.RecordTimer

    if not dirname:
        dirname = preferredTimerPath()

    #  IPTV Fix
    serviceref = serviceref.replace('%253a', '%3a')

    try:
        timer = RecordTimerEntry(ServiceReference(serviceref),
                                 begin,
                                 end,
                                 name,
                                 description,
                                 eit,
                                 disabled,
                                 justplay,
                                 afterevent,
                                 dirname=dirname,
                                 tags=tags)

        timer.repeated = repeated

        if logentries:
            timer.log_entries = logentries

        conflicts = rt.record(timer)
        if conflicts:
            errors = []
            conflictinfo = []
            for conflict in conflicts:
                errors.append(conflict.name)
                conflictinfo.append({
                    "serviceref":
                    str(conflict.service_ref),
                    "servicename":
                    conflict.service_ref.getServiceName().replace(
                        '\xc2\x86', '').replace('\xc2\x87', ''),
                    "name":
                    conflict.name,
                    "begin":
                    conflict.begin,
                    "end":
                    conflict.end,
                    "realbegin":
                    strftime(_("%d.%m.%Y %H:%M"),
                             (localtime(float(conflict.begin)))),
                    "realend":
                    strftime(_("%d.%m.%Y %H:%M"),
                             (localtime(float(conflict.end))))
                })

            return {
                "result":
                False,
                "message":
                _("Conflicting Timer(s) detected! %s") % " / ".join(errors),
                "conflicts":
                conflictinfo
            }
        # VPS
        if vpsinfo is not None:
            timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"]
            timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"]
            timer.vpsplugin_time = vpsinfo["vpsplugin_time"]

        if always_zap != -1:
            if hasattr(timer, "always_zap"):
                timer.always_zap = always_zap == 1
            if hasattr(timer, "zapbeforerecord"):
                timer.zapbeforerecord = always_zap == 1

        if hasattr(timer, "autoadjust"):
            if autoadjust == -1:
                autoadjust = config.recording.adjust_time_to_event.value and 1 or 0
            autoadjust = autoadjust

        if hasattr(timer, "allow_duplicate"):
            allow_duplicate = allow_duplicate

        if pipzap != -1:
            if hasattr(timer, "pipzap"):
                timer.pipzap = pipzap == 1

        if recordingtype:
            timer.descramble = {
                "normal": True,
                "descrambled": True,
                "scrambled": False,
            }[recordingtype]
            timer.record_ecm = {
                "normal": False,
                "descrambled": True,
                "scrambled": True,
            }[recordingtype]

    except Exception as e:
        print(str(e))
        return {
            "result": False,
            "message": _("Could not add timer '%s'!") % name
        }

    return {"result": True, "message": _("Timer '%s' added") % name}
Exemple #8
0
def addTimer(session,
             serviceref,
             begin,
             end,
             name,
             description,
             disabled,
             justplay,
             afterevent,
             dirname,
             tags,
             repeated,
             vpsinfo=None,
             logentries=None,
             eit=0,
             always_zap=-1):
    rt = session.nav.RecordTimer

    if not dirname:
        dirname = preferredTimerPath()

    try:
        timer = RecordTimerEntry(ServiceReference(serviceref),
                                 begin,
                                 end,
                                 name,
                                 description,
                                 eit,
                                 disabled,
                                 justplay,
                                 afterevent,
                                 dirname=dirname,
                                 tags=tags)

        timer.repeated = repeated

        if logentries:
            timer.log_entries = logentries

        conflicts = rt.record(timer)
        if conflicts:
            errors = []
            conflictinfo = []
            for conflict in conflicts:
                errors.append(conflict.name)
                conflictinfo.append({
                    "serviceref":
                    str(conflict.service_ref),
                    "servicename":
                    mangle_epg_text(conflict.service_ref.getServiceName()),
                    "name":
                    conflict.name,
                    "begin":
                    conflict.begin,
                    "end":
                    conflict.end,
                    "realbegin":
                    strftime("%d.%m.%Y %H:%M",
                             (localtime(float(conflict.begin)))),
                    "realend":
                    strftime("%d.%m.%Y %H:%M",
                             (localtime(float(conflict.end))))
                })

            return {
                "result":
                False,
                "message":
                _("Conflicting Timer(s) detected! %s") % " / ".join(errors),
                "conflicts":
                conflictinfo
            }
        # VPS
        if vpsinfo is not None:
            timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"]
            timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"]
            timer.vpsplugin_time = vpsinfo["vpsplugin_time"]

        if always_zap != -1:
            if hasattr(timer, "always_zap"):
                timer.always_zap = always_zap == 1

    except Exception as e:
        print e
        return {
            "result": False,
            "message": _("Could not add timer '%s'!") % name
        }

    return {"result": True, "message": _("Timer '%s' added") % name}
Exemple #9
0
	def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False):
		new = 0
		modified = 0

		# enable multiple timer if services or bouquets specified (eg. recording the same event on sd service and hd service)
		enable_multiple_timer = ((timer.services and 's' in config.plugins.autotimer.enable_multiple_timer.value or False) or (timer.bouquets and 'b' in config.plugins.autotimer.enable_multiple_timer.value or False))

		# Workaround to allow search for umlauts if we know the encoding
		match = timer.match.replace('\xc2\x86', '').replace('\xc2\x87', '')
		if timer.encoding != 'UTF-8':
			try:
				match = match.decode('UTF-8').encode(timer.encoding)
			except UnicodeDecodeError:
				pass

		if timer.searchType == "favoritedesc":
			epgmatches = []

			casesensitive = timer.searchCase == "sensitive"
			if not casesensitive:
				match = match.lower()

			test = []
			if timer.services or timer.bouquets:
				if timer.services:
					test = [(service, 0, -1, -1) for service in timer.services]
				if timer.bouquets:
					for bouquet in timer.bouquets:
						services = serviceHandler.list(eServiceReference(bouquet))
						if services:
							while True:
								service = services.getNext()
								if not service.valid():
									break
								playable = not (service.flags & (eServiceReference.isMarker | eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker)
								if playable:
									test.append((service.toString(), 0, -1, -1))
			else: # Get all bouquets
				bouquetlist = []
				if config.usage.multibouquet.value:
					refstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
					bouquetroot = eServiceReference(refstr)
					bouquets = serviceHandler.list(bouquetroot)
					if bouquets:
						while True:
							s = bouquets.getNext()
							if not s.valid():
								break
							if s.flags & eServiceReference.isDirectory and not s.flags & eServiceReference.isInvisible:
								info = serviceHandler.info(s)
								if info:
									bouquetlist.append(s)
				else:
					service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 31) || (type == 134) || (type == 195)'
					refstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet' % (service_types_tv)
					bouquetroot = eServiceReference(refstr)
					info = serviceHandler.info(bouquetroot)
					if info:
						bouquetlist.append(bouquetroot)
				if bouquetlist:
					for bouquet in bouquetlist:
						if not bouquet.valid():
							break
						if bouquet.flags & eServiceReference.isDirectory:
							services = serviceHandler.list(bouquet)
							if services:
								while True:
									service = services.getNext()
									if not service.valid():
										break
									playable = not (service.flags & (eServiceReference.isMarker | eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker)
									if playable:
										test.append((service.toString(), 0, -1, -1))

			if test:
				# Get all events
				#  eEPGCache.lookupEvent( [ format of the returned tuples, ( service, 0 = event intersects given start_time, start_time -1 for now_time), ] )
				test.insert(0, 'RITBDSE')
				allevents = epgcache.lookupEvent(test) or []

				# Filter events
				for serviceref, eit, name, begin, duration, shortdesc, extdesc in allevents:
					if match in (shortdesc if casesensitive else shortdesc.lower()) or match in (extdesc if casesensitive else extdesc.lower()) or match in (name if casesensitive else name.lower()):
						epgmatches.append((serviceref, eit, name, begin, duration, shortdesc, extdesc))

		else:
			# Search EPG, default to empty list
			epgmatches = epgcache.search(('RITBDSE', int(config.plugins.autotimer.max_search_events_match.value), typeMap[timer.searchType], match, caseMap[timer.searchCase])) or []

		# Sort list of tuples by begin time 'B'
		epgmatches.sort(key=itemgetter(3))

		# Contains the the marked similar eits and the conflicting strings
		similardict = defaultdict(list)

		dayofweek_exclude = timer.exclude[3]
		if dayofweek_exclude:
			dayofweek_exclude_values = dayofweek_exclude[:]
			if "weekend" in dayofweek_exclude_values:
				dayofweek_exclude_values.extend(("5", "6"))
			if "weekday" in dayofweek_exclude_values:
				dayofweek_exclude_values.extend(("0", "1", "2", "3", "4"))
		dayofweek_include = timer.include[3]
		if dayofweek_include:
			dayofweek_include_values = dayofweek_include[:]
			if "weekend" in dayofweek_include_values:
				dayofweek_include_values.extend(("5", "6"))
			if "weekday" in dayofweek_include_values:
				dayofweek_include_values.extend(("0", "1", "2", "3", "4"))

		# Loop over all EPG matches
		for idx, (serviceref, eit, name, begin, duration, shortdesc, extdesc) in enumerate(epgmatches):
			startLog()

			# timer destination dir
			dest = timer.destination or config.usage.default_path.value

			evtBegin = begin
			evtEnd = end = begin + duration

			doLog("[AutoTimer] possible epgmatch %s" % (name))
			doLog("[AutoTimer] Serviceref %s" % serviceref)
			eserviceref = eServiceReference(serviceref)
			evt = epgcache.lookupEventId(eserviceref, eit)
			if not evt:
				doLog("[AutoTimer] Could not create Event!")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue
			# Try to determine real service (we always choose the last one)
			#n = evt.getNumOfLinkageServices()
			#if n > 0:
			#	i = evt.getLinkageService(eserviceref, n-1)
			#	serviceref = i.toString()
			#	doLog("[AutoTimer] Serviceref2 %s" % serviceref)

			# If event starts in less than 60 seconds skip it
			if begin < time() + 60:
				doLog("[AutoTimer] Skipping an event because it starts in less than 60 seconds")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue

			# Set short description to equal extended description if it is empty.
			if not shortdesc and timer.descShortEqualExt and extdesc:
				shortdesc = extdesc

			# Convert begin time
			timestamp = localtime(begin)
			# Update timer
			timer.update(begin, timestamp)

			# Check if eit is in similar matches list
			# NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive
			similarTimer = False
			if eit in similardict:
				similarTimer = True
				dayofweek = None # NOTE: ignore day on similar timer
			else:
				# If maximum days in future is set then check time
				if checkEvtLimit:
					if begin > evtLimit:
						doLog("[AutoTimer] Skipping an event because of maximum days in future is reached")
						skipped.append((name, begin, end, serviceref, timer.name, getLog()))
						continue

				current_dayofweek = tdow = timestamp.tm_wday
				if (timer.timespan[0] != None) and timer.timespan[2]:
					begin_offset = 60 * timestamp.tm_hour + timestamp.tm_min
					timer_offset = 60 * timer.timespan[0][0] + timer.timespan[0][1]
					if begin_offset < timer_offset:
						tdow = (tdow - 1) % 7
				dayofweek = str(tdow)

				# Update dayofweek when programmes that cross midnight and have a dayofweek filter
				if str(current_dayofweek) == dayofweek and (dayofweek_exclude or dayofweek_include):
					end_timestamp = localtime(end)
					end_dayofweek = str(end_timestamp.tm_wday)
					if dayofweek != end_dayofweek:
						if dayofweek_exclude:
							if dayofweek in dayofweek_exclude_values:
								if not end_dayofweek in dayofweek_exclude_values:
									doLog("[AutoTimer] [AutoTimer] Update dayofweek by reason of exclude dayofweek filter")
									dayofweek = end_dayofweek
						if dayofweek_include and dayofweek != end_dayofweek:
							if not dayofweek in dayofweek_include_values:
								if end_dayofweek in dayofweek_include_values:
									doLog("[AutoTimer] [AutoTimer] Update dayofweek by reason of include dayofweek filter")
									dayofweek = end_dayofweek

			# Check timer conditions
			# NOTE: similar matches do not care about the day/time they are on, so ignore them
			if timer.checkServices(serviceref):
				doLog("[AutoTimer] Skipping an event because of check services")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue
			if timer.checkDuration(duration):
				doLog("[AutoTimer] Skipping an event because of duration check")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue
			if not similarTimer:
				if timer.checkTimespan(timestamp):
					doLog("[AutoTimer] Skipping an event because of timestamp check")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue
				if timer.checkTimeframe(begin):
					doLog("[AutoTimer] Skipping an event because of timeframe check")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

			# Initialize
			newEntry = None
			oldEntry = None
			oldExists = False
			allow_modify = True
			newAT = None

			# Eventually change service to alternative
			if timer.overrideAlternatives:
				serviceref = timer.getAlternative(serviceref)

			if timer.series_labeling and sp_getSeasonEpisode is not None:
				allow_modify = False
				doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (name, shortdesc, dest))
				sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest, True)
				if sp and type(sp) in (tuple, list) and len(sp) > 3:
					name = sp[0] or name
					shortdesc = sp[1] or shortdesc
					dest = sp[2] or dest
					doLog(str(sp[3]))
					allow_modify = True
					doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name, shortdesc, dest))
				else:
					# Nothing found
					doLog(str(sp))
					# If AutoTimer name not equal match, do a second lookup with the name
					if timer.name.lower() != timer.match.lower():
						doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (timer.name, shortdesc, dest))
						sp = sp_getSeasonEpisode(serviceref, timer.name, evtBegin, evtEnd, shortdesc, dest, True)
						if sp and type(sp) in (tuple, list) and len(sp) > 3:
							name = sp[0] or name
							shortdesc = sp[1] or shortdesc
							dest = sp[2] or dest
							doLog(str(sp[3]))
							allow_modify = True
							doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name, shortdesc, dest))
						else:
							doLog(str(sp))

			if timer.checkFilter(name, shortdesc, extdesc, dayofweek):
				doLog("[AutoTimer] Skipping an event because of filter check")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue

			if timer.hasOffset():
				# Apply custom Offset
				begin, end = timer.applyOffset(begin, end)
				offsetBegin = timer.offset[0]
				offsetEnd = timer.offset[1]
			else:
				# Apply E2 Offset
				begin -= config.recording.margin_before.value * 60
				end += config.recording.margin_after.value * 60
				offsetBegin = config.recording.margin_before.value * 60
				offsetEnd = config.recording.margin_after.value * 60

			# Overwrite endtime if requested
			if timer.justplay and not timer.setEndtime:
				end = begin
				evtEnd = evtBegin

			# Check for existing recordings in directory
			if timer.avoidDuplicateDescription == 3:
				# Reset movie Exists
				movieExists = False

				if dest and dest not in moviedict:
					self.addDirectoryToMovieDict(moviedict, dest, serviceHandler)
				for movieinfo in moviedict.get(dest, ()):
					if self.checkSimilarity(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc"), isMovie=True):
						doLog("[AutoTimer] We found a matching recorded movie, skipping event:", name)
						movieExists = True
						break
				if movieExists:
					doLog("[AutoTimer] Skipping an event because movie already exists")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

			# Check for double Timers
			# We first check eit and if user wants us to guess event based on time
			# we try this as backup. The allowed diff should be configurable though.
			for rtimer in timerdict.get(serviceref, ()):
				try: # protect against vps plugin not being present
					vps_changed = hasVps and (rtimer.vpsplugin_enabled != timer.vps_enabled or rtimer.vpsplugin_overwrite != timer.vps_overwrite)
				except:
					vps_changed = False
				time_changed = (evtBegin - offsetBegin != rtimer.begin) or (evtEnd + offsetEnd != rtimer.end)
				desc_changed = (timer.avoidDuplicateDescription >= 1 and shortdesc and rtimer.description and shortdesc != rtimer.description) or (timer.avoidDuplicateDescription >= 2 and extdesc and rtimer.extdesc and extdesc != rtimer.extdesc)
				if rtimer.eit == eit:
					oldExists = True
					doLog("[AutoTimer] We found a timer based on eit")
					if time_changed or desc_changed or vps_changed:
						newEntry = rtimer
						oldEntry = [rtimer.name, rtimer.description, rtimer.extdesc, rtimer.begin, rtimer.end, rtimer.service_ref, rtimer.eit, rtimer.disabled]
					break
				elif config.plugins.autotimer.try_guessing.value:
					if timeSimilarityPercent(rtimer, evtBegin, evtEnd, timer) > 80:
						oldExists = True
						doLog("[AutoTimer] We found a timer based on time guessing")
						if time_changed or desc_changed or vps_changed:
							newEntry = rtimer
							oldEntry = [rtimer.name, rtimer.description, rtimer.extdesc, rtimer.begin, rtimer.end, rtimer.service_ref, rtimer.eit, rtimer.disabled]
						break
				if oldExists is None and timer.avoidDuplicateDescription >= 1 and not rtimer.disabled:
					# searchForDuplicateDescription is 1 - check short description / searchForDuplicateDescription is 2 - check extended description
					if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc):
						oldExists = True
						doLog("[AutoTimer] We found a timer (similar service) with same description, skipping event")
						break

			# We found no timer we want to edit
			if newEntry is None:
				# But there is a match
				if oldExists:
					doLog("[AutoTimer] Skipping an event because a timer on same service exists")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

				# We want to search for possible doubles
				for rtimer in chain.from_iterable(itervalues(timerdict)):
					if not rtimer.disabled:
						if self.checkDoubleTimers(timer, name, rtimer.name, begin, rtimer.begin, end, rtimer.end, serviceref, rtimer.service_ref.ref.toString(), enable_multiple_timer):
							oldExists = True
							print("[AutoTimer] We found a timer with same start time, skipping event")
							break
						if timer.avoidDuplicateDescription >= 2:
							if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc):
								oldExists = True
								doLog("[AutoTimer] We found a timer (any service) with same description, skipping event")
								break
				if oldExists:
					doLog("[AutoTimer] Skipping an event because a timer on any service exists")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

				if timer.checkCounter(timestamp):
					doLog("[AutoTimer] Not adding new timer because counter is depleted.")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

			# if set option for check/save timer in filterlist and only if not found an existing timer
			isnewFilterEntry = False
			if (config.plugins.autotimer.series_save_filter.value or timer.series_save_filter) and not oldExists:
				if timer.series_labeling and sp_getSeasonEpisode is not None:
					if sp and type(sp) in (tuple, list) and len(sp) == 4:
						ret = self.addToFilterfile(str(sp[0]), begin, simulateOnly)
					if sp and type(sp) in (tuple, list) and len(sp) > 3:
						filter_title = str(sp[0])
						if len(sp) > 4:
							filter_title = "{series:s} - S{season:02d}E{rawepisode:s} - {title:s}".format(**sp[4])
						ret = self.addToFilterfile(filter_title, begin, simulateOnly, str(sp[0]))
						if ret:
							if simulateOnly:
								doLog("[AutoTimer SeriesPlugin] only simulate - new Timer would be saved in autotimer_filter")
							else:
								doLog("[AutoTimer SeriesPlugin] new Timer saved in autotimer_filter")
								isnewFilterEntry = True
						else:
							skipped.append((name, begin, end, serviceref, timer.name, getLog()))
							continue

			# Append to timerlist and abort if simulating
			timers.append((name, begin, end, serviceref, timer.name, getLog()))
			if simulateOnly:
				continue

			if newEntry is not None:
				# Abort if we don't want to modify timers or timer is repeated
				if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated:
					doLog("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer")
					continue
				if "autotimer" in newEntry.flags:
					msg = "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)
					doLog(msg)
					newEntry.log(501, msg)
				else:
					if config.plugins.autotimer.refresh.value != "all":
						doLog("[AutoTimer] Won't modify existing timer because it's no timer set by us")
						continue

					msg = "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, newEntry.name)
					doLog(msg)
					newEntry.log(501, msg)

				changed = newEntry.begin != begin or newEntry.end != end or newEntry.name != name
				if allow_modify:
					if oldExists and newEntry.service_ref.ref.toString() == serviceref and newEntry.eit == eit and newEntry.name == name and newEntry.begin < begin and newEntry.end < end and (0 < begin - newEntry.end <= 600):
						begin = newEntry.begin
						doLog("[AutoTimer] This same eit and different times end - update only end")
					if self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit, base_timer=timer):
						msg = "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name)
						doLog(msg)
						newEntry.log(501, msg)
						if changed:
							self.addToSearchLogfile(newEntry, "#", simulateOnly)
							modified += 1
					else:
						msg = "[AutoTimer] AutoTimer modification not allowed for timer %s because conflicts or double timer." % (newEntry.name)
						doLog(msg)
						if oldEntry:
							self.setOldTimer(newEntry, oldEntry)
							doLog("[AutoTimer] conflict for modification timer %s detected return to old timer" % (newEntry.name))
						continue
				else:
					msg = "[AutoTimer] AutoTimer modification not allowed for timer: %s ." % (newEntry.name)
					doLog(msg)
					continue
			else:
				newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit)
				newAT = True

				msg = "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name)
				doLog(msg)
				newEntry.log(500, msg)
				msg = "[AutoTimer] Timer start on: %s" % ctime(begin)
				doLog(msg)
				newEntry.log(509, msg)

				# Mark this entry as AutoTimer
				newEntry.flags.add("autotimer")
				# Mark this entry as timer name
				newEntry.flags.add(stringToXML(timer.name))

			# Apply afterEvent
			if timer.hasAfterEvent():
				afterEvent = timer.getAfterEventTimespan(localtime(end))
				if afterEvent is None:
					afterEvent = timer.getAfterEvent()
				if afterEvent is not None:
					newEntry.afterEvent = afterEvent

			newEntry.dirname = dest
			newEntry.calculateFilename()
			newEntry.justplay = timer.justplay
			newEntry.vpsplugin_enabled = timer.vps_enabled
			newEntry.vpsplugin_overwrite = timer.vps_overwrite
			newEntry.conflict_detection = timer.conflict_detection
			newEntry.always_zap = timer.always_zap
			newEntry.zap_wakeup = timer.zap_wakeup

			tags = timer.tags[:]
			if config.plugins.autotimer.add_autotimer_to_tags.value:
				if 'AutoTimer' not in tags:
					tags.append('AutoTimer')
			if config.plugins.autotimer.add_name_to_tags.value:
				tagname = timer.name.strip()
				if tagname:
					tagname = tagname[0].upper() + tagname[1:].replace(" ", "_")
					if tagname not in tags:
						tags.append(tagname)
			newEntry.tags = tags

			if oldExists and newAT is None:
				if self.isResolvedConflict(newEntry):
					recordHandler.timeChanged(newEntry)
				else:
					if oldEntry:
						self.setOldTimer(newEntry, oldEntry)
						doLog("[AutoTimer] rechecking - conflict for timer %s detected return to old timer" % (newEntry.name))
					continue
			elif newAT:
				newAT = newEntry
				conflictString = ""
				if similarTimer:
					conflictString = similardict[eit].conflictString
					msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (conflictString)
					doLog(msg)
					newEntry.log(504, msg)

				# add new timer in AT timer list
				atDoubleTimer = False
				refstr = ':'.join(newEntry.service_ref.ref.toString().split(':')[:11])
				for at in addNewTimers:
					needed_ref = ':'.join(at.service_ref.ref.toString().split(':')[:11]) == refstr
					if needed_ref and at.eit == newEntry.eit and (newEntry.begin < at.begin <= newEntry.end or at.begin <= newEntry.begin <= at.end):
						atDoubleTimer = True
						break
				if atDoubleTimer:
					doLog("[AutoTimer] ignore double new auto timer %s." % newEntry.name)
					continue
				else:
					addNewTimers.append(newEntry)

				# Try to add timer
				conflicts = recordHandler.record(newEntry)

				if conflicts and not timer.hasOffset() and not config.recording.margin_before.value and not config.recording.margin_after.value and len(conflicts) > 1:
					change_end = change_begin = False
					conflict_begin = conflicts[1].begin
					conflict_end = conflicts[1].end
					if conflict_begin == newEntry.end:
						newEntry.end -= 30
						change_end = True
					elif newEntry.begin == conflict_end:
						newEntry.begin += 30
						change_begin = True
					if change_end or change_begin:
						conflicts = recordHandler.record(newEntry)
						if conflicts:
							if change_end:
								newEntry.end += 30
							elif change_begin:
								newEntry.begin -= 30
						else:
							doLog("[AutoTimer] The conflict is resolved by offset time begin/end (30 sec) for %s." % newEntry.name)

				if conflicts:
					# Maybe use newEntry.log
					conflictString += ' / '.join(["%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts])
					doLog("[AutoTimer] conflict with %s detected" % (conflictString))

					if config.plugins.autotimer.addsimilar_on_conflict.value:
						# We start our search right after our actual index
						# Attention we have to use a copy of the list, because we have to append the previous older matches
						lepgm = len(epgmatches)
						for i in xrange(lepgm):
							servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[(i + idx + 1) % lepgm]
							if self.checkSimilarity(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True):
								# Check if the similar is already known
								if eitS not in similardict:
									doLog("[AutoTimer] Found similar Timer: " + name)

									# Store the actual and similar eit and conflictString, so it can be handled later
									newEntry.conflictString = conflictString
									similardict[eit] = newEntry
									similardict[eitS] = newEntry
									similarTimer = True
									if beginS <= evtBegin:
										# Event is before our actual epgmatch so we have to append it to the epgmatches list
										epgmatches.append((servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS))
									# If we need a second similar it will be found the next time
								else:
									similarTimer = False
									newEntry = similardict[eitS]
								break

				if conflicts is None:
					timer.decrementCounter()
					if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]):
						new += 1
						if isnewFilterEntry:
							self.addToSearchLogfile(newEntry, "++", simulateOnly)
						else:
							self.addToSearchLogfile(newEntry, "+", simulateOnly)
						newEntry.extdesc = extdesc
						timerdict[serviceref].append(newEntry)

						# Similar timers are in new timers list and additionally in similar timers list
						if similarTimer:
							similars.append((name, begin, end, serviceref, timer.name))
							similardict.clear()
					else:
						doLog("[AutoTimer] ignore double timer %s." % newEntry.name)

				# Don't care about similar timers
				elif not similarTimer:
					conflicting.append((name, begin, end, serviceref, timer.name))

					if config.plugins.autotimer.disabled_on_conflict.value:
						msg = "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString)
						doLog(msg)
						newEntry.log(503, msg)
						newEntry.disabled = True
						if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]):
							recordHandler.timeChanged(newEntry)
						else:
							# We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
							conflicts = recordHandler.record(newEntry)
					elif newAT != newEntry and newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]):
						if not self.isResolvedConflict(newEntry):
							newEntry.disabled = True
							recordHandler.timeChanged(newEntry)
							doLog("[AutoTimer] Unknown conflict, disable this timer %s." % newEntry.name)

		return (new, modified)
Exemple #10
0
    def parseTimer(self,
                   timer,
                   epgcache,
                   serviceHandler,
                   recordHandler,
                   checkEvtLimit,
                   evtLimit,
                   timers,
                   conflicting,
                   similars,
                   skipped,
                   existing,
                   timerdict,
                   moviedict,
                   taskname,
                   simulateOnly=False):
        new = 0
        modified = 0

        # enable multiple timer if services or bouquets specified (eg. recording the same event on sd service and hd service)
        enable_multiple_timer = (
            (timer.services
             and 's' in config.plugins.autotimer.enable_multiple_timer.value
             or False)
            or (timer.bouquets
                and 'b' in config.plugins.autotimer.enable_multiple_timer.value
                or False))

        # Precompute timer destination dir
        dest = timer.destination or config.usage.default_path.value

        # Workaround to allow search for umlauts if we know the encoding
        match = timer.match.replace('\xc2\x86', '').replace('\xc2\x87', '')
        if timer.encoding != 'UTF-8':
            try:
                match = match.decode('UTF-8').encode(timer.encoding)
            except UnicodeDecodeError:
                pass

        if timer.searchType == "description":
            epgmatches = []

            casesensitive = timer.searchCase == "sensitive"
            if not casesensitive:
                match = match.lower()

            test = []
            if timer.services:
                test = [(service, 0, -1, -1) for service in timer.services]
            elif timer.bouquets:
                for bouquet in timer.bouquets:
                    services = serviceHandler.list(eServiceReference(bouquet))
                    if services:
                        while True:
                            service = services.getNext()
                            if not service.valid():
                                break
                            playable = not (
                                service.flags &
                                (eServiceReference.isMarker
                                 | eServiceReference.isDirectory)) or (
                                     service.flags
                                     & eServiceReference.isNumberedMarker)
                            if playable:
                                test.append((service.toString(), 0, -1, -1))
            else:  # Get all bouquets
                bouquetlist = []
                refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet'
                bouquetroot = eServiceReference(refstr)
                mask = eServiceReference.isDirectory
                if config.usage.multibouquet.value:
                    bouquets = serviceHandler.list(bouquetroot)
                    if bouquets:
                        while True:
                            s = bouquets.getNext()
                            if not s.valid():
                                break
                            if s.flags & mask:
                                info = serviceHandler.info(s)
                                if info:
                                    bouquetlist.append(s)
                else:
                    info = serviceHandler.info(bouquetroot)
                    if info:
                        bouquetlist.append(bouquetroot)
                if bouquetlist:
                    for bouquet in bouquetlist:
                        if not bouquet.valid():
                            break
                    if bouquet.flags & eServiceReference.isDirectory:
                        services = serviceHandler.list(bouquet)
                        if services:
                            while True:
                                service = services.getNext()
                                if not service.valid():
                                    break
                                playable = not (
                                    service.flags &
                                    (eServiceReference.isMarker
                                     | eServiceReference.isDirectory)) or (
                                         service.flags
                                         & eServiceReference.isNumberedMarker)
                                if playable:
                                    test.append(
                                        (service.toString(), 0, -1, -1))

            if test:
                # Get all events
                #  eEPGCache.lookupEvent( [ format of the returned tuples, ( service, 0 = event intersects given start_time, start_time -1 for now_time), ] )
                test.insert(0, 'RITBDSE')
                allevents = epgcache.lookupEvent(test) or []

                # Filter events
                for serviceref, eit, name, begin, duration, shortdesc, extdesc in allevents:
                    if match in (shortdesc if casesensitive else
                                 shortdesc.lower()) or match in (
                                     extdesc
                                     if casesensitive else extdesc.lower()):
                        epgmatches.append((serviceref, eit, name, begin,
                                           duration, shortdesc, extdesc))

        else:
            # Search EPG, default to empty list
            if timer.searchType in typeMap:
                EPG_searchType = typeMap[timer.searchType]
            else:
                EPG_searchType = typeMap["partial"]
            epgmatches = epgcache.search(
                ('RITBDSE', 3000, EPG_searchType, match,
                 caseMap[timer.searchCase])) or []

        # Sort list of tuples by begin time 'B'
        epgmatches.sort(key=itemgetter(3))

        # Contains the the marked similar eits and the conflicting strings
        similardict = defaultdict(list)

        # Loop over all EPG matches
        preveit = False
        for idx, (serviceref, eit, name, begin, duration, shortdesc,
                  extdesc) in enumerate(epgmatches):

            eserviceref = eServiceReference(serviceref)
            evt = epgcache.lookupEventId(eserviceref, eit)
            evtBegin = begin
            evtEnd = end = begin + duration

            if not evt:
                msg = "[AutoTimer] Could not create Event!"
                print(msg)
                skipped.append(
                    (name, begin, end, str(serviceref), timer.name, msg))
                continue
            # Try to determine real service (we always choose the last one)
            n = evt.getNumOfLinkageServices()
            if n > 0:
                i = evt.getLinkageService(eserviceref, n - 1)
                serviceref = i.toString()

            # If event starts in less than 60 seconds skip it
            # if begin < time() + 60:
            # 	print ("[AutoTimer] Skipping " + name + " because it starts in less than 60 seconds")
            # 	skipped += 1
            # 	continue

            # Set short description to equal extended description if it is empty.
            if not shortdesc:
                shortdesc = extdesc

            # Convert begin time
            timestamp = localtime(begin)
            # Update timer
            timer.update(begin, timestamp)

            # Check if eit is in similar matches list
            # NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive
            similarTimer = False
            if eit in similardict:
                similarTimer = True
                dayofweek = None  # NOTE: ignore day on similar timer
            else:
                # If maximum days in future is set then check time
                if checkEvtLimit:
                    if begin > evtLimit:
                        msg = "[AutoTimer] Skipping an event because of maximum days in future is reached"
                        #						print(msg)
                        skipped.append(
                            (name, begin, end, serviceref, timer.name, msg))
                        continue

                dayofweek = str(timestamp.tm_wday)

            # Check timer conditions
            # NOTE: similar matches do not care about the day/time they are on, so ignore them
            if timer.checkServices(serviceref) \
             or timer.checkDuration(duration) \
             or (not similarTimer and (
              timer.checkTimespan(timestamp)
              or timer.checkTimeframe(begin)
             )) or timer.checkFilter(name, shortdesc, extdesc, dayofweek):
                msg = "[AutoTimer] Skipping an event because of filter check"
                #				print(msg)
                skipped.append((name, begin, end, serviceref, timer.name, msg))
                continue

            if timer.hasOffset():
                # Apply custom Offset
                begin, end = timer.applyOffset(begin, end)
                offsetBegin = timer.offset[0]
                offsetEnd = timer.offset[1]
            else:
                # Apply E2 Offset
                begin -= config.recording.margin_before.value * 60
                end += config.recording.margin_after.value * 60
                offsetBegin = config.recording.margin_before.value * 60
                offsetEnd = config.recording.margin_after.value * 60

            # Overwrite endtime if requested
            if timer.justplay and not timer.setEndtime:
                end = begin

            # Eventually change service to alternative
            if timer.overrideAlternatives:
                serviceref = timer.getAlternative(serviceref)

            # Append to timerlist and abort if simulating
            timers.append((name, begin, end, serviceref, timer.name))
            if simulateOnly:
                continue

            # Check for existing recordings in directory
            if timer.avoidDuplicateDescription == 3:
                # Reset movie Exists
                movieExists = False

                if dest and dest not in moviedict:
                    self.addDirectoryToMovieDict(moviedict, dest,
                                                 serviceHandler)
                for movieinfo in moviedict.get(dest, ()):
                    if self.checkSimilarity(timer, name, movieinfo.get("name"),
                                            shortdesc,
                                            movieinfo.get("shortdesc"),
                                            extdesc, movieinfo.get("extdesc")):
                        # print("[AutoTimer] We found a matching recorded movie, skipping event:", name)
                        movieExists = True
                        break
                if movieExists:
                    msg = "[AutoTimer] Skipping an event because movie already exists"
                    #					print(msg)
                    skipped.append(
                        (name, begin, end, serviceref, timer.name, msg))
                    continue

            # Initialize
            newEntry = None
            oldExists = False

            # Check for double Timers
            # We first check eit and if user wants us to guess event based on time
            # we try this as backup. The allowed diff should be configurable though.
            for rtimer in timerdict.get(serviceref, ()):
                if rtimer.eit == eit or (
                        config.plugins.autotimer.try_guessing.getValue()
                        and timeSimilarityPercent(rtimer, evtBegin, evtEnd,
                                                  timer) > 80):
                    oldExists = True

                    # Abort if we don't want to modify timers or timer is repeated
                    if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
                        #						print("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer")
                        break

                    if eit == preveit:
                        break
                    try:  # protect against vps plugin not being present
                        vps_changed = rtimer.vpsplugin_enabled != timer.vps_enabled or rtimer.vpsplugin_overwrite != timer.vps_overwrite
                    except AttributeError:
                        vps_changed = False
                    if (evtBegin - offsetBegin != rtimer.begin
                        ) or (evtEnd + offsetEnd != rtimer.end) or (
                            shortdesc != rtimer.description) or vps_changed:
                        if rtimer.isAutoTimer and eit == rtimer.eit:
                            # print ("[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name))
                            # rtimer.log(501, "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name))
                            preveit = eit
                        else:
                            if config.plugins.autotimer.refresh.getValue(
                            ) != "all":
                                # print("[AutoTimer] Won't modify existing timer because it's no timer set by us")
                                break
                            rtimer.log(
                                501,
                                "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ."
                                % (timer.name, rtimer.name))
                        newEntry = rtimer
                        modified += 1
                        self.modifyTimer(rtimer, name, shortdesc, begin, end,
                                         serviceref, eit)
                        # rtimer.log(501, "[AutoTimer] AutoTimer modified timer: %s ." % (rtimer.name))
                        break
                    else:
                        # print ("[AutoTimer] Skipping timer because it has not changed.")
                        existing.append(
                            (name, begin, end, serviceref, timer.name))
                        break
                elif timer.avoidDuplicateDescription >= 1 and not rtimer.disabled:
                    if self.checkSimilarity(timer, name, rtimer.name,
                                            shortdesc, rtimer.description,
                                            extdesc, rtimer.extdesc):
                        # print("[AutoTimer] We found a timer with similar description, skipping event")
                        oldExists = True
                        break

            # We found no timer we want to edit
            if newEntry is None:
                # But there is a match
                if oldExists:
                    continue

                # We want to search for possible doubles
                for rtimer in chain.from_iterable(itervalues(timerdict)):
                    if not rtimer.disabled:
                        if self.checkDoubleTimers(timer, name, rtimer.name,
                                                  begin, rtimer.begin, end,
                                                  rtimer.end, serviceref,
                                                  str(rtimer.service_ref),
                                                  enable_multiple_timer):
                            oldExists = True
                            print(
                                "[AutoTimer] We found a timer with same StartTime, skipping event"
                            )
                            break
                        if timer.avoidDuplicateDescription >= 2:
                            if self.checkSimilarity(timer, name, rtimer.name,
                                                    shortdesc,
                                                    rtimer.description,
                                                    extdesc, rtimer.extdesc):
                                oldExists = True
                                # print("[AutoTimer] We found a timer (any service) with same description, skipping event")
                                break
                if oldExists:
                    continue

                if timer.checkCounter(timestamp):
                    # print("[AutoTimer] Not adding new timer because counter is depleted.")
                    continue

                newEntry = RecordTimerEntry(ServiceReference(serviceref),
                                            begin, end, name, shortdesc, eit)
                newEntry.log(
                    500,
                    "[AutoTimer] Try to add new timer based on AutoTimer %s." %
                    (timer.name))
                newEntry.log(509,
                             "[AutoTimer] Timer start on: %s" % ctime(begin))

                # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
                newEntry.isAutoTimer = True
                newEntry.autoTimerId = timer.id

            # Apply afterEvent
            if timer.hasAfterEvent():
                afterEvent = timer.getAfterEventTimespan(localtime(end))
                if afterEvent is None:
                    afterEvent = timer.getAfterEvent()
                if afterEvent is not None:
                    newEntry.afterEvent = afterEvent

            newEntry.dirname = timer.destination
            newEntry.justplay = timer.justplay
            newEntry.vpsplugin_enabled = timer.vps_enabled
            newEntry.vpsplugin_overwrite = timer.vps_overwrite

            if hasattr(timer, 'always_zap') and hasattr(
                    newEntry, 'always_zap'):
                newEntry.always_zap = timer.always_zap
            tags = timer.tags[:]
            if config.plugins.autotimer.add_autotimer_to_tags.value:
                if TAG not in tags:
                    tags.append(TAG)
            if config.plugins.autotimer.add_name_to_tags.value:
                tagname = timer.name.strip()
                if tagname:
                    tagname = tagname[0].upper() + tagname[1:].replace(
                        " ", "_")
                    if tagname not in tags:
                        tags.append(tagname)
            newEntry.tags = tags

            if oldExists:
                # XXX: this won't perform a sanity check, but do we actually want to do so?
                recordHandler.timeChanged(newEntry)

                if renameTimer is not None and timer.series_labeling:
                    renameTimer(newEntry, name, evtBegin, evtEnd)

            else:
                conflictString = ""
                if similarTimer:
                    conflictString = similardict[eit].conflictString
                    msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (
                        conflictString)
                    print(msg)
                    newEntry.log(504, msg)

                # Try to add timer
                conflicts = recordHandler.record(newEntry)

                if conflicts and not timer.hasOffset(
                ) and not config.recording.margin_before.value and not config.recording.margin_after.value and len(
                        conflicts) > 1:
                    change_end = change_begin = False
                    conflict_begin = conflicts[1].begin
                    conflict_end = conflicts[1].end
                    if conflict_begin == newEntry.end:
                        newEntry.end -= 30
                        change_end = True
                    elif newEntry.begin == conflict_end:
                        newEntry.begin += 30
                        change_begin = True
                    if change_end or change_begin:
                        conflicts = recordHandler.record(newEntry)
                        if conflicts:
                            if change_end:
                                newEntry.end += 30
                            elif change_begin:
                                newEntry.begin -= 30
                        else:
                            print(
                                "[AutoTimer] The conflict is resolved by offset time begin/end (30 sec) for %s."
                                % newEntry.name)

                if conflicts:
                    # Maybe use newEntry.log
                    conflictString += ' / '.join([
                        "%s (%s)" %
                        (x.name, strftime("%Y%m%d %H%M", localtime(x.begin)))
                        for x in conflicts
                    ])
                    print("[AutoTimer] conflict with %s detected" %
                          (conflictString))

                    if config.plugins.autotimer.addsimilar_on_conflict.value:
                        # We start our search right after our actual index
                        # Attention we have to use a copy of the list, because we have to append the previous older matches
                        lepgm = len(epgmatches)
                        for i in xrange(lepgm):
                            servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[
                                (i + idx + 1) % lepgm]
                            if self.checkSimilarity(timer,
                                                    name,
                                                    nameS,
                                                    shortdesc,
                                                    shortdescS,
                                                    extdesc,
                                                    extdescS,
                                                    force=True):
                                # Check if the similar is already known
                                if eitS not in similardict:
                                    # print("[AutoTimer] Found similar Timer: " + name)

                                    # Store the actual and similar eit and conflictString, so it can be handled later
                                    newEntry.conflictString = conflictString
                                    similardict[eit] = newEntry
                                    similardict[eitS] = newEntry
                                    similarTimer = True
                                    if beginS <= evtBegin:
                                        # Event is before our actual epgmatch so we have to append it to the epgmatches list
                                        epgmatches.append(
                                            (servicerefS, eitS, nameS, beginS,
                                             durationS, shortdescS, extdescS))
                                    # If we need a second similar it will be found the next time
                                else:
                                    similarTimer = False
                                    newEntry = similardict[eitS]
                                break

                if conflicts is None:
                    timer.decrementCounter()
                    new += 1
                    newEntry.extdesc = extdesc
                    timerdict[serviceref].append(newEntry)

                    if renameTimer is not None and timer.series_labeling:
                        renameTimer(newEntry, name, evtBegin, evtEnd)

                    # Similar timers are in new timers list and additionally in similar timers list
                    if similarTimer:
                        similars.append(
                            (name, begin, end, serviceref, timer.name))
                        similardict.clear()

                # Don't care about similar timers
                elif not similarTimer:
                    conflicting.append(
                        (name, begin, end, serviceref, timer.name))

                    if config.plugins.autotimer.disabled_on_conflict.value:
                        msg = "[AutoTimer] Timer disabled because of conflicts with %s." % (
                            conflictString)
                        print(msg)
                        newEntry.log(503, msg)
                        newEntry.disabled = True
                        # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
                        conflicts = recordHandler.record(newEntry)
        self.result = (new, modified)
        self.completed.append(taskname)
        sleep(0.5)
Exemple #11
0
	def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False):
		new = 0
		modified = 0

		# Workaround to allow search for umlauts if we know the encoding
		#match = timer.match
		match = timer.match.replace('\xc2\x86', '').replace('\xc2\x87', '')
		if timer.encoding != 'UTF-8':
			try:
				match = match.decode('UTF-8').encode(timer.encoding)
			except UnicodeDecodeError:
				pass

		if timer.searchType == "description":
			epgmatches = []
			mask = (eServiceReference.isMarker | eServiceReference.isDirectory)

			casesensitive = timer.searchCase == "sensitive"
			if not casesensitive:
				match = match.lower()

			# Service filter defined
			# Search only using the specified services
			test = [(service, 0, -1, -1) for service in timer.services]

			for bouquet in timer.bouquets:
				services = serviceHandler.list(eServiceReference(bouquet))
				if not services is None:
					while True:
						service = services.getNext()
						if not service.valid(): #check end of list
							break
						if not (service.flags & mask):
							test.append( (service.toString(), 0, -1, -1 ) )

			if not test:
				# No service filter defined
				# Search within all services - could be very slow

				# Get all bouquets
				bouquetlist = []
				if config.usage.multibouquet.value:
					refstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet'
					bouquetroot = eServiceReference(refstr)
					bouquets = serviceHandler.list(bouquetroot)
					if bouquets:
						while True:
							s = bouquets.getNext()
							if not s.valid():
								break
							if s.flags & eServiceReference.isDirectory:
								info = serviceHandler.info(s)
								if info:
									bouquetlist.append((info.getName(s), s))
					mask = (eServiceReference.isMarker | eServiceReference.isDirectory)
					for name, bouquet in bouquetlist:
						if not bouquet.valid(): #check end of list
							break
						if bouquet.flags & eServiceReference.isDirectory:
							services = serviceHandler.list(bouquet)
							if not services is None:
								while True:
									service = services.getNext()
									if not service.valid(): #check end of list
										break
									if not (service.flags & mask):
										test.append( (service.toString(), 0, -1, -1 ) )
				else:
					service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 134) || (type == 195)'
					refstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(service_types_tv)
					bouquetroot = eServiceReference(refstr)
					info = serviceHandler.info(bouquetroot)
					if info:
						bouquetlist.append((info.getName(bouquetroot), bouquetroot))
					mask = (eServiceReference.isMarker | eServiceReference.isDirectory)
					for name, bouquet in bouquetlist:
						if bouquet.flags & eServiceReference.isDirectory:
							services = serviceHandler.list(bouquet)
							if not services is None:
								while True:
									service = services.getNext()
									if not service.valid(): #check end of list
										break
									if not (service.flags & mask):
										test.append( (service.toString(), 0, -1, -1 ) )

			if test:
				# Get all events
				#  eEPGCache.lookupEvent( [ format of the returned tuples, ( service, 0 = event intersects given start_time, start_time -1 for now_time), ] )
				test.insert(0, 'RITBDSE')
				allevents = epgcache.lookupEvent(test) or []

				# Filter events
				for serviceref, eit, name, begin, duration, shortdesc, extdesc in allevents:
					if match in (shortdesc if casesensitive else shortdesc.lower()) \
						or match in (extdesc if casesensitive else extdesc.lower()):
						epgmatches.append( (serviceref, eit, name, begin, duration, shortdesc, extdesc) )

		else:
			# Search EPG, default to empty list
			epgmatches = epgcache.search( ('RITBDSE', 2000, typeMap[timer.searchType], match, caseMap[timer.searchCase]) ) or []

		# Sort list of tuples by begin time 'B'
		epgmatches.sort(key=itemgetter(3))

		# Contains the the marked similar eits and the conflicting strings
		similardict = defaultdict(list)

		# Loop over all EPG matches
		for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ):

			startLog()

			# timer destination dir
			dest = timer.destination

			evtBegin = begin
			evtEnd = end = begin + duration

			doLog("[AutoTimer] possible epgmatch %s" % (name))
			doLog("[AutoTimer] Serviceref %s" % (str(serviceref)))
			eserviceref = eServiceReference(serviceref)
			evt = epgcache.lookupEventId(eserviceref, eit)
			if not evt:
				doLog("[AutoTimer] Could not create Event!")
				skipped.append((name, begin, end, str(serviceref), timer.name, getLog()))
				continue
			# Try to determine real service (we always choose the last one)
			n = evt.getNumOfLinkageServices()
			if n > 0:
				i = evt.getLinkageService(eserviceref, n-1)
				doLog("[AutoTimer] Serviceref2 %s" % (str(serviceref)))
				serviceref = i.toString()

			evtBegin = begin
			evtEnd = end = begin + duration

			# If event starts in less than 60 seconds skip it
			if begin < time() + 60:
				doLog("[AutoTimer] Skipping an event because it starts in less than 60 seconds")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue

			# Set short description to equal extended description if it is empty.
			if not shortdesc:
				shortdesc = extdesc

			# Convert begin time
			timestamp = localtime(begin)
			# Update timer
			timer.update(begin, timestamp)

			# Check if eit is in similar matches list
			# NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive
			similarTimer = False
			if eit in similardict:
				similarTimer = True
				dayofweek = None # NOTE: ignore day on similar timer
			else:
				# If maximum days in future is set then check time
				if checkEvtLimit:
					if begin > evtLimit:
						doLog("[AutoTimer] Skipping an event because of maximum days in future is reached")
						skipped.append((name, begin, end, serviceref, timer.name, getLog()))
						continue

				dayofweek = str(timestamp.tm_wday)

			# Check timer conditions
			# NOTE: similar matches do not care about the day/time they are on, so ignore them
			if timer.checkServices(serviceref):
				doLog("[AutoTimer] Skipping an event because of check services")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue
			if timer.checkDuration(duration):
				doLog("[AutoTimer] Skipping an event because of duration check")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue
			if not similarTimer:
				if timer.checkTimespan(timestamp):
					doLog("[AutoTimer] Skipping an event because of timestamp check")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue
				if timer.checkTimeframe(begin):
					doLog("[AutoTimer] Skipping an event because of timeframe check")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

			# Initialize
			newEntry = None
			oldExists = False
			allow_modify = True

			# Eventually change service to alternative
			if timer.overrideAlternatives:
				serviceref = timer.getAlternative(serviceref)

			if timer.series_labeling and sp_getSeasonEpisode is not None:
				allow_modify = False
				#doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (name,shortdesc,dest))
				sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest)
				if sp and type(sp) in (tuple, list) and len(sp) == 4:
					name = sp[0] or name
					shortdesc = sp[1] or shortdesc
					dest = sp[2] or dest
					doLog(str(sp[3]))
					allow_modify = True
					#doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name,shortdesc,dest))
				else:
					# Nothing found
					doLog(str(sp))
					# If AutoTimer name not equal match, do a second lookup with the name
					if timer.name.lower() != timer.match.lower():
						#doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (timer.name,shortdesc,dest))
						sp = sp_getSeasonEpisode(serviceref, timer.name, evtBegin, evtEnd, shortdesc, dest)
						if sp and type(sp) in (tuple, list) and len(sp) == 4:
							name = sp[0] or name
							shortdesc = sp[1] or shortdesc
							dest = sp[2] or dest
							doLog(str(sp[3]))
							allow_modify = True
							#doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name,shortdesc,dest))
						else:
							doLog(str(sp))

			if timer.checkFilter(name, shortdesc, extdesc, dayofweek):
				doLog("[AutoTimer] Skipping an event because of filter check")
				skipped.append((name, begin, end, serviceref, timer.name, getLog()))
				continue

			if timer.hasOffset():
				# Apply custom Offset
				begin, end = timer.applyOffset(begin, end)
			else:
				# Apply E2 Offset
				begin -= config.recording.margin_before.value * 60
				end += config.recording.margin_after.value * 60

			# Overwrite endtime if requested
			if timer.justplay and not timer.setEndtime:
				end = begin

			# Check for existing recordings in directory
			if timer.avoidDuplicateDescription == 3:
				# Reset movie Exists
				movieExists = False

				if dest and dest not in moviedict:
					self.addDirectoryToMovieDict(moviedict, dest, serviceHandler)
				for movieinfo in moviedict.get(dest, ()):
					if self.checkDuplicates(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc") ):
						doLog("[AutoTimer] We found a matching recorded movie, skipping event:", name)
						movieExists = True
						break
				if movieExists:
					doLog("[AutoTimer] Skipping an event because movie already exists")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

			# Check for double Timers
			# We first check eit and if user wants us to guess event based on time
			# we try this as backup. The allowed diff should be configurable though.
			for rtimer in timerdict.get(serviceref, ()):
				if rtimer.eit == eit:
					oldExists = True
					doLog("[AutoTimer] We found a timer based on eit")
					newEntry = rtimer
					break
				elif config.plugins.autotimer.try_guessing.value:
					if timer.hasOffset():
						# Remove custom Offset
						rbegin = rtimer.begin + timer.offset[0] * 60
						rend = rtimer.end - timer.offset[1] * 60
					else:
						# Remove E2 Offset
						rbegin = rtimer.begin + config.recording.margin_before.value * 60
						rend = rtimer.end - config.recording.margin_after.value * 60
					# As alternative we could also do a epg lookup
					#revent = epgcache.lookupEventId(rtimer.service_ref.ref, rtimer.eit)
					#rbegin = revent.getBeginTime() or 0
					#rduration = revent.getDuration() or 0
					#rend = rbegin + rduration or 0
					if getTimeDiff(rbegin, rend, evtBegin, evtEnd) > ((duration/10)*8):
						oldExists = True
						doLog("[AutoTimer] We found a timer based on time guessing")
						newEntry = rtimer
						break
				elif timer.avoidDuplicateDescription >= 1 \
					and not rtimer.disabled:
						if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ):
						# if searchForDuplicateDescription > 1 then check short description
							oldExists = True
							doLog("[AutoTimer] We found a timer (similar service) with same description, skipping event")
							break

			# We found no timer we want to edit
			if newEntry is None:
				# But there is a match
				if oldExists:
					doLog("[AutoTimer] Skipping an event because a timer on same service exists")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue

				# We want to search for possible doubles
				if timer.avoidDuplicateDescription >= 2:
					for rtimer in chain.from_iterable( itervalues(timerdict) ):
						if not rtimer.disabled:
							if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ):
								oldExists = True
								doLog("[AutoTimer] We found a timer (any service) with same description, skipping event")
								break
					if oldExists:
						doLog("[AutoTimer] Skipping an event because a timer on any service exists")
						skipped.append((name, begin, end, serviceref, timer.name, getLog()))
						continue

				if timer.checkCounter(timestamp):
					doLog("[AutoTimer] Not adding new timer because counter is depleted.")
					skipped.append((name, begin, end, serviceref, timer.name, getLog()))
					continue
			# Append to timerlist and abort if simulating
			timers.append((name, begin, end, serviceref, timer.name, getLog()))
			if simulateOnly:
				continue

			if newEntry is not None:
				# Abort if we don't want to modify timers or timer is repeated
				if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated:
					doLog("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer")
					continue

				if "autotimer" in newEntry.flags:
					msg = "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)
					doLog(msg)
					newEntry.log(501, msg)
				else:
					if config.plugins.autotimer.refresh.value != "all":
						doLog("[AutoTimer] Won't modify existing timer because it's no timer set by us")
						continue

					msg = "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, newEntry.name)
					doLog(msg)
					newEntry.log(501, msg)

				modified += 1

				if allow_modify:
					self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit)
					msg = "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name)
					doLog(msg)
					newEntry.log(501, msg)
				else:
					msg = "[AutoTimer] AutoTimer modification not allowed for timer: %s ." % (newEntry.name)
					doLog(msg)
			else:
				newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit)

				msg = "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name)
				doLog(msg)
				newEntry.log(500, msg)

				# Mark this entry as AutoTimer
				newEntry.flags.add("autotimer")

			# Apply afterEvent
			if timer.hasAfterEvent():
				afterEvent = timer.getAfterEventTimespan(localtime(end))
				if afterEvent is None:
					afterEvent = timer.getAfterEvent()
				if afterEvent is not None:
					newEntry.afterEvent = afterEvent

			newEntry.dirname = dest
			newEntry.calculateFilename()
			newEntry.justplay = timer.justplay
			newEntry.vpsplugin_enabled = timer.vps_enabled
			newEntry.vpsplugin_overwrite = timer.vps_overwrite
			newEntry.conflict_detection = timer.conflict_detection
			newEntry.always_zap = timer.always_zap
			newEntry.zap_wakeup = timer.zap_wakeup

			tags = timer.tags[:]
			if config.plugins.autotimer.add_autotimer_to_tags.value:
				tags.append('AutoTimer')
			if config.plugins.autotimer.add_name_to_tags.value:
				tagname = timer.name.strip()
				if tagname:
					tagname = tagname[0].upper() + tagname[1:].replace(" ", "_")
					tags.append(tagname)
			newEntry.tags = tags

			if oldExists:
				# XXX: this won't perform a sanity check, but do we actually want to do so?
				recordHandler.timeChanged(newEntry)

			else:
				conflictString = ""
				if similarTimer:
					conflictString = similardict[eit].conflictString
					msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (conflictString)
					doLog(msg)
					newEntry.log(504, msg)

				# Try to add timer
				conflicts = recordHandler.record(newEntry)

				if conflicts:
					# Maybe use newEntry.log
					conflictString += ' / '.join(["%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts])
					doLog("[AutoTimer] conflict with %s detected" % (conflictString))

					if config.plugins.autotimer.addsimilar_on_conflict.value:
						# We start our search right after our actual index
						# Attention we have to use a copy of the list, because we have to append the previous older matches
						lepgm = len(epgmatches)
						for i in xrange(lepgm):
							servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[ (i+idx+1)%lepgm ]
							if self.checkDuplicates(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True ):
								# Check if the similar is already known
								if eitS not in similardict:
									doLog("[AutoTimer] Found similar Timer: " + name)

									# Store the actual and similar eit and conflictString, so it can be handled later
									newEntry.conflictString = conflictString
									similardict[eit] = newEntry
									similardict[eitS] = newEntry
									similarTimer = True
									if beginS <= evtBegin:
										# Event is before our actual epgmatch so we have to append it to the epgmatches list
										epgmatches.append((servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS))
									# If we need a second similar it will be found the next time
								else:
									similarTimer = False
									newEntry = similardict[eitS]
								break

				if conflicts is None:
					timer.decrementCounter()
					new += 1
					newEntry.extdesc = extdesc
					timerdict[serviceref].append(newEntry)

					#if renameTimer is not None and timer.series_labeling:
					#	renameTimer(newEntry, name, evtBegin, evtEnd)

					# Similar timers are in new timers list and additionally in similar timers list
					if similarTimer:
						similars.append((name, begin, end, serviceref, timer.name))
						similardict.clear()

				# Don't care about similar timers
				elif not similarTimer:
					conflicting.append((name, begin, end, serviceref, timer.name))

					if config.plugins.autotimer.disabled_on_conflict.value:
						msg = "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString)
						doLog(msg)
						newEntry.log(503, msg)
						newEntry.disabled = True
						# We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
						conflicts = recordHandler.record(newEntry)

		return (new, modified)
Exemple #12
0
def addTimer(session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, vpsinfo=None, logentries=None, eit=0, always_zap=-1):
	rt = session.nav.RecordTimer

	if not dirname:
		dirname = preferredTimerPath()

	try:
		timer = RecordTimerEntry(
			ServiceReference(serviceref),
			begin,
			end,
			name,
			description,
			eit,
			disabled,
			justplay,
			afterevent,
			dirname=dirname,
			tags=tags)

		timer.repeated = repeated

		if logentries:
			timer.log_entries = logentries

		conflicts = rt.record(timer)
		if conflicts:
			errors = []
			conflictinfo = []
			for conflict in conflicts:
				errors.append(conflict.name)
				conflictinfo.append({
					"serviceref": str(conflict.service_ref),
					"servicename": conflict.service_ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''),
					"name": conflict.name,
					"begin" : conflict.begin,
					"end" : conflict.end,
					"realbegin":strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.begin)))),
					"realend":strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.end))))
				})

			return {
				"result": False,
				"message": _("Conflicting Timer(s) detected! %s") % " / ".join(errors),
				"conflicts" : conflictinfo
			}
		#VPS
		if vpsinfo is not None:
			timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"]
			timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"]
			timer.vpsplugin_time = vpsinfo["vpsplugin_time"]

		if always_zap <> -1:
			if hasattr(timer,"always_zap"):
				timer.always_zap = always_zap == 1

	except Exception, e:
		print e
		return {
			"result": False,
			"message": _("Could not add timer '%s'!") % name
		}
	def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, existing, timerdict, moviedict, taskname, simulateOnly=False):
		new = 0
		modified = 0

		# enable multiple timer if services or bouquets specified (eg. recording the same event on sd service and hd service)
		enable_multiple_timer = ((timer.services and 's' in config.plugins.autotimer.enable_multiple_timer.value or False) or (timer.bouquets and 'b' in config.plugins.autotimer.enable_multiple_timer.value or False))

		# Precompute timer destination dir
		dest = timer.destination or config.usage.default_path.value

		# Workaround to allow search for umlauts if we know the encoding
		match = timer.match.replace('\xc2\x86', '').replace('\xc2\x87', '')
		if timer.encoding != 'UTF-8':
			try:
				match = match.decode('UTF-8').encode(timer.encoding)
			except UnicodeDecodeError:
				pass

		if timer.searchType == "description":
			epgmatches = []

			casesensitive = timer.searchCase == "sensitive"
			if not casesensitive:
				match = match.lower()

			test = []
			if timer.services:
				test = [(service, 0, -1, -1) for service in timer.services]
			elif timer.bouquets:
				for bouquet in timer.bouquets:
					services = serviceHandler.list(eServiceReference(bouquet))
					if services:
						while True:
							service = services.getNext()
							if not service.valid():
								break
							playable = not (service.flags & (eServiceReference.isMarker|eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker)
							if playable:
								test.append((service.toString(), 0, -1, -1))
			else: # Get all bouquets
				bouquetlist = []
				refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet'
				bouquetroot = eServiceReference(refstr)
				mask = eServiceReference.isDirectory
				if config.usage.multibouquet.value:
					bouquets = serviceHandler.list(bouquetroot)
					if bouquets:
						while True:
							s = bouquets.getNext()
							if not s.valid():
								break
							if s.flags & mask:
								info = serviceHandler.info(s)
								if info:
									bouquetlist.append(s)
				else:
					info = serviceHandler.info(bouquetroot)
					if info:
						bouquetlist.append(bouquetroot)
				if bouquetlist:
					for bouquet in bouquetlist:
						if not bouquet.valid():
							break
					if bouquet.flags & eServiceReference.isDirectory:
						services = serviceHandler.list(bouquet)
						if services:
							while True:
								service = services.getNext()
								if not service.valid():
									break
								playable = not (service.flags & (eServiceReference.isMarker|eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker)
								if playable:
									test.append((service.toString(), 0, -1, -1))

			if test:
				# Get all events
				#  eEPGCache.lookupEvent( [ format of the returned tuples, ( service, 0 = event intersects given start_time, start_time -1 for now_time), ] )
				test.insert(0, 'RITBDSE')
				allevents = epgcache.lookupEvent(test) or []

				# Filter events
				for serviceref, eit, name, begin, duration, shortdesc, extdesc in allevents:
					if match in (shortdesc if casesensitive else shortdesc.lower()) or match in (extdesc if casesensitive else extdesc.lower()):
						epgmatches.append((serviceref, eit, name, begin, duration, shortdesc, extdesc))

		else:
			# Search EPG, default to empty list
			epgmatches = epgcache.search( ('RITBDSE', 3000, typeMap[timer.searchType], match, caseMap[timer.searchCase]) ) or []

		# Sort list of tuples by begin time 'B'
		epgmatches.sort(key=itemgetter(3))

		# Contains the the marked similar eits and the conflicting strings
		similardict = defaultdict(list)		

		# Loop over all EPG matches
		preveit = False
		for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ):

			eserviceref = eServiceReference(serviceref)
			evt = epgcache.lookupEventId(eserviceref, eit)
			evtBegin = begin
			evtEnd = end = begin + duration

			if not evt:
				msg="[AutoTimer] Could not create Event!"
				print(msg)
				skipped.append((name, begin, end, str(serviceref), timer.name, msg))
				continue
			# Try to determine real service (we always choose the last one)
			n = evt.getNumOfLinkageServices()
			if n > 0:
				i = evt.getLinkageService(eserviceref, n-1)
				serviceref = i.toString()

			# If event starts in less than 60 seconds skip it
			# if begin < time() + 60:
			# 	print ("[AutoTimer] Skipping " + name + " because it starts in less than 60 seconds")
			# 	skipped += 1
			# 	continue

			# Set short description to equal extended description if it is empty.
			if not shortdesc:
				shortdesc = extdesc

			# Convert begin time
			timestamp = localtime(begin)
			# Update timer
			timer.update(begin, timestamp)

			# Check if eit is in similar matches list
			# NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive
			similarTimer = False
			if eit in similardict:
				similarTimer = True
				dayofweek = None # NOTE: ignore day on similar timer
			else:
				# If maximum days in future is set then check time
				if checkEvtLimit:
					if begin > evtLimit:
						msg="[AutoTimer] Skipping an event because of maximum days in future is reached"
#						print(msg)
						skipped.append((name, begin, end, serviceref, timer.name, msg))
						continue

				dayofweek = str(timestamp.tm_wday)

			# Check timer conditions
			# NOTE: similar matches do not care about the day/time they are on, so ignore them
			if timer.checkServices(serviceref) \
				or timer.checkDuration(duration) \
				or (not similarTimer and (\
					timer.checkTimespan(timestamp) \
					or timer.checkTimeframe(begin) \
				)) or timer.checkFilter(name, shortdesc, extdesc, dayofweek):
				msg="[AutoTimer] Skipping an event because of filter check"
#				print(msg)
				skipped.append((name, begin, end, serviceref, timer.name, msg))
				continue

			if timer.hasOffset():
				# Apply custom Offset
				begin, end = timer.applyOffset(begin, end)
				offsetBegin = timer.offset[0]
				offsetEnd   = timer.offset[1]
			else:
				# Apply E2 Offset
				begin -= config.recording.margin_before.value * 60
				end += config.recording.margin_after.value * 60
				offsetBegin = config.recording.margin_before.value * 60
				offsetEnd   = config.recording.margin_after.value * 60

			# Overwrite endtime if requested
			if timer.justplay and not timer.setEndtime:
				end = begin

			# Eventually change service to alternative
			if timer.overrideAlternatives:
				serviceref = timer.getAlternative(serviceref)

			# Append to timerlist and abort if simulating
			timers.append((name, begin, end, serviceref, timer.name))
			if simulateOnly:
				continue

			# Check for existing recordings in directory
			if timer.avoidDuplicateDescription == 3:
				# Reset movie Exists
				movieExists = False

				if dest and dest not in moviedict:
					self.addDirectoryToMovieDict(moviedict, dest, serviceHandler)
				for movieinfo in moviedict.get(dest, ()):
					if self.checkSimilarity(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc")):
						print("[AutoTimer] We found a matching recorded movie, skipping event:", name)
						movieExists = True
						break
				if movieExists:
					msg="[AutoTimer] Skipping an event because movie already exists"
#					print(msg)
					skipped.append((name, begin, end, serviceref, timer.name, msg))
					continue

			# Initialize
			newEntry = None
			oldExists = False

			# Check for double Timers
			# We first check eit and if user wants us to guess event based on time
			# we try this as backup. The allowed diff should be configurable though.
			for rtimer in timerdict.get(serviceref, ()):
				if rtimer.eit == eit or (config.plugins.autotimer.try_guessing.getValue() and timeSimilarityPercent(rtimer, evtBegin, evtEnd, timer) > 80):
					oldExists = True

					# Abort if we don't want to modify timers or timer is repeated
					if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
#						print("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer")
						break

					if eit == preveit:
						break
					
					if (evtBegin - offsetBegin != rtimer.begin) or (evtEnd + offsetEnd != rtimer.end) or (shortdesc != rtimer.description):
						if rtimer.isAutoTimer and eit == rtimer.eit:
							print ("[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name))
							# rtimer.log(501, "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name))
							preveit = eit
						else:
							if config.plugins.autotimer.refresh.getValue() != "all":
								print("[AutoTimer] Won't modify existing timer because it's no timer set by us")
								break
							rtimer.log(501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, rtimer.name))
						newEntry = rtimer
						modified += 1
						self.modifyTimer(rtimer, name, shortdesc, begin, end, serviceref, eit)
						# rtimer.log(501, "[AutoTimer] AutoTimer modified timer: %s ." % (rtimer.name))
						break
					else:
#						print("[AutoTimer] Skipping timer because it has not changed.")
						existing.append((name, begin, end, serviceref, timer.name))
						break
				elif timer.avoidDuplicateDescription >= 1 and not rtimer.disabled:
					if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ):
						print("[AutoTimer] We found a timer with similar description, skipping event")
						oldExists = True
						break

			# We found no timer we want to edit
			if newEntry is None:
				# But there is a match
				if oldExists:
					continue

				# We want to search for possible doubles
				for rtimer in chain.from_iterable( itervalues(timerdict) ):
					if not rtimer.disabled:
						if self.checkDoubleTimers(timer, name, rtimer.name, begin, rtimer.begin, end, rtimer.end, serviceref, str(rtimer.service_ref), enable_multiple_timer ):
							oldExists = True
							print("[AutoTimer] We found a timer with same StartTime, skipping event")
							break
						if timer.avoidDuplicateDescription >= 2:
							if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ):
								oldExists = True
#								print("[AutoTimer] We found a timer (any service) with same description, skipping event")
								break
				if oldExists:
					continue

				if timer.checkCounter(timestamp):
					print("[AutoTimer] Not adding new timer because counter is depleted.")
					continue

				newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit)
				newEntry.log(500, "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name))
				newEntry.log(509, "[AutoTimer] Timer start on: %s" % ctime(begin))
				
				# Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
				# It is only temporarily, after a restart it will be lost,
				# because it won't be stored in the timer xml file
				newEntry.isAutoTimer = True

			# Apply afterEvent
			if timer.hasAfterEvent():
				afterEvent = timer.getAfterEventTimespan(localtime(end))
				if afterEvent is None:
					afterEvent = timer.getAfterEvent()
				if afterEvent is not None:
					newEntry.afterEvent = afterEvent

			newEntry.dirname = timer.destination
			newEntry.justplay = timer.justplay
			newEntry.vpsplugin_enabled = timer.vps_enabled
			newEntry.vpsplugin_overwrite = timer.vps_overwrite

			if hasattr(timer, 'always_zap') and hasattr(newEntry, 'always_zap'):
				newEntry.always_zap = timer.always_zap
			tags = timer.tags[:]
			if config.plugins.autotimer.add_autotimer_to_tags.value:
				if TAG not in tags:
					tags.append(TAG)
			if config.plugins.autotimer.add_name_to_tags.value:
				tagname = timer.name.strip()
				if tagname:
					tagname = tagname[0].upper() + tagname[1:].replace(" ", "_")
					if tagname not in tags:
						tags.append(tagname)
			newEntry.tags = tags

			if oldExists:
				# XXX: this won't perform a sanity check, but do we actually want to do so?
				recordHandler.timeChanged(newEntry)

				if renameTimer is not None and timer.series_labeling:
					renameTimer(newEntry, name, evtBegin, evtEnd)

			else:
				conflictString = ""
				if similarTimer:
					conflictString = similardict[eit].conflictString
					msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (conflictString)
					print(msg)
					newEntry.log(504, msg)

				# Try to add timer
				conflicts = recordHandler.record(newEntry)

				if conflicts and not timer.hasOffset() and not config.recording.margin_before.value and not config.recording.margin_after.value and len(conflicts) > 1:
					change_end = change_begin = False
					conflict_begin = conflicts[1].begin
					conflict_end = conflicts[1].end
					if conflict_begin == newEntry.end:
						newEntry.end -= 30
						change_end = True
					elif newEntry.begin == conflict_end:
						newEntry.begin += 30
						change_begin = True
					if change_end or change_begin:
						conflicts = recordHandler.record(newEntry)
						if conflicts:
							if change_end:
								newEntry.end += 30
							elif change_begin:
								newEntry.begin -= 30
						else:
							print ("[AutoTimer] The conflict is resolved by offset time begin/end (30 sec) for %s." % newEntry.name)

				if conflicts:
					# Maybe use newEntry.log
					conflictString += ' / '.join(["%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts])
					print("[AutoTimer] conflict with %s detected" % (conflictString))

					if config.plugins.autotimer.addsimilar_on_conflict.value:
						# We start our search right after our actual index
						# Attention we have to use a copy of the list, because we have to append the previous older matches
						lepgm = len(epgmatches)
						for i in xrange(lepgm):
							servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[ (i+idx+1)%lepgm ]
							if self.checkSimilarity(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True ):
								# Check if the similar is already known
								if eitS not in similardict:
									print("[AutoTimer] Found similar Timer: " + name)

									# Store the actual and similar eit and conflictString, so it can be handled later
									newEntry.conflictString = conflictString
									similardict[eit] = newEntry
									similardict[eitS] = newEntry
									similarTimer = True
									if beginS <= evtBegin:
										# Event is before our actual epgmatch so we have to append it to the epgmatches list
										epgmatches.append((servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS))
									# If we need a second similar it will be found the next time
								else:
									similarTimer = False
									newEntry = similardict[eitS]
								break

				if conflicts is None:
					timer.decrementCounter()
					new += 1
					newEntry.extdesc = extdesc
					timerdict[serviceref].append(newEntry)

					if renameTimer is not None and timer.series_labeling:
						renameTimer(newEntry, name, evtBegin, evtEnd)

					# Similar timers are in new timers list and additionally in similar timers list
					if similarTimer:
						similars.append((name, begin, end, serviceref, timer.name))
						similardict.clear()

				# Don't care about similar timers
				elif not similarTimer:
					conflicting.append((name, begin, end, serviceref, timer.name))

					if config.plugins.autotimer.disabled_on_conflict.value:
						msg = "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString)
						print(msg)
						newEntry.log(503, msg)
						newEntry.disabled = True
						# We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
						conflicts = recordHandler.record(newEntry)
		self.result=(new, modified)
		self.completed.append(taskname)
		sleep(0.5)