rcrdit/src/main/java/com/moparisthebest/rcrdit/scheduler/PartialScheduler.java

136 lines
7.0 KiB
Java

/*
* rcrdit records TV programs from TV tuners
* Copyright (C) 2017 Travis Burtrum
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.moparisthebest.rcrdit.scheduler;
import com.moparisthebest.rcrdit.autorec.AutoRec;
import com.moparisthebest.rcrdit.xmltv.Program;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.*;
/**
* Created by mopar on 3/14/17.
*/
public class PartialScheduler implements Scheduler {
private static final Logger log = LoggerFactory.getLogger(PartialScheduler.class);
@Override
public void schedulePrograms(final List<AutoRec> autoRecs, final List<Program> programs, final int numTuners, final Instant end, final Instant startAt) {
if (programs.isEmpty() || autoRecs.isEmpty()) {
log.debug("nothing to import.");
return;
}
// start now, get all matching programs scheduled for that minute, determine which to record, schedule timers, start any now? make sure current ones don't get reset
Instant cursor = startAt;
//final Instant end = cursor.plus(1, ChronoUnit.DAYS);
final Map<Program, Instant> currentRecs = new LinkedHashMap<>(numTuners); // todo: needs to be sorted?
final List<Program> programAutoRecs = new ArrayList<>();
log.debug("end: {}", end);
Program lastProgram = null, lastNonTimeProgram = null;
while (!cursor.isAfter(end)) {
programAutoRecs.clear();
for (final Program program : programs) {
lastNonTimeProgram = program;
if (!cursor.isBefore(program.getStart()) && cursor.isBefore(program.getStop())) {
lastProgram = program;
// this program is on
for (final AutoRec autoRec : autoRecs) {
if (autoRec.matches(program)) {
programAutoRecs.add(program.setAutoRec(autoRec));
break; // only match highest priority autorec, ie first since they are sorted
}
}
}
}
// schedule top 2 (numTuners), record stuff about rest, schedule only if not already recording ouch?
if (!programAutoRecs.isEmpty()) {
programAutoRecs.sort(Program::compareTo);
// start and stop are same minute and second
final Instant finalCursor = cursor;
// it already ended, and stopped itself, just remove it, for both currentRecs and skippedRecs
//recs.keySet().removeIf(c -> c.getProgram().getStop().isBefore(finalCursor));
for (final Iterator<Map.Entry<Program, Instant>> it = currentRecs.entrySet().iterator(); it.hasNext(); ) {
final Map.Entry<Program, Instant> entry = it.next();
final Program prog = entry.getKey();
if (!prog.getStop().isAfter(finalCursor)) {
//if(prog.getStop().isBefore(finalCursor)) {
final Instant start = entry.getValue();
prog.addStartStop(new StartStop(prog.getStop(), false));
it.remove();
}
}
// look at highest programAutoRecs up to the number of tuners
for (int x = 0; x < Math.min(programAutoRecs.size(), numTuners); ++x) {
final Program programAutoRec = programAutoRecs.get(x);
// if we are already recording it, move on
if (currentRecs.containsKey(programAutoRec))
continue;
// free tuner, just add
if (currentRecs.size() < numTuners) {
currentRecs.put(programAutoRec, cursor);
programAutoRec.addStartStop(new StartStop(cursor, true));
continue;
}
// from Tuners.recordNow
final Program recToReplace = currentRecs.keySet().stream().filter(r -> r != null && programAutoRec.getChannelName().equals(r.getChannelName())).findFirst().orElseGet(
// look for un-used tuners
() -> currentRecs.keySet().stream().filter(Objects::isNull).findFirst().orElseGet(
// look for current recordings ending within 70 seconds
() -> {
final Instant oneMinuteInFuture = finalCursor.plusSeconds(70);
return currentRecs.keySet().stream().filter(r -> r == null || r.getStop().isBefore(oneMinuteInFuture)).findFirst().orElseGet(
// find lowest priority
() -> currentRecs.keySet().stream().min(Comparator.comparingInt(cr -> cr.getAutoRec().getPriority())).orElse(null)
);
}
)
);
//System.out.println("replacing: "+ recToReplace + " with scheduling: "+programAutoRec);
if(programAutoRec.equals(recToReplace)) { // todo: remove this cause should never happen due to currentRecs.containsKey(programAutoRec) above, just testing though...
System.out.println("wtf...");
continue;
}
recToReplace.addStartStop(new StartStop(cursor, false));
programAutoRec.addStartStop(new StartStop(cursor, true, recToReplace));
// remove/replace it
final Instant start = currentRecs.remove(recToReplace);
currentRecs.put(programAutoRec, cursor);
}
//System.out.println(cursor);
//System.out.println(programAutoRecs);
}
cursor = cursor.plus(1, ChronoUnit.MINUTES);
}
log.debug("cursor: {}", cursor);
log.debug("lastProgram: {}", lastProgram);
log.debug("lastNonTimeProgram: {}", lastNonTimeProgram);
// increment by minute up until end scheduling all timers until then, somehow store this info to show/email etc?
log.debug("import done.\n\n------\n\n");
}
}