1
0
mirror of https://github.com/moparisthebest/rcrdit synced 2024-12-21 23:08:57 -05:00

Add List<StartStop> to Programs and generic scheduling algorithm, doesn't work perfectly yet and is not enabled

This commit is contained in:
Travis Burtrum 2017-03-23 23:10:46 -04:00
parent 24bde789b8
commit a79fc8b3c2
7 changed files with 527 additions and 25 deletions

4
.gitignore vendored
View File

@ -2,6 +2,8 @@
*.iml *.iml
target/ target/
out.xml out.xml
rcrdit.ics *.ics
xmltv.xml xmltv.xml
rcrdit.cfg.xml rcrdit.cfg.xml
*.txt
*.diff

View File

@ -24,6 +24,9 @@ import com.moparisthebest.jdbc.QueryMapper;
import com.moparisthebest.rcrdit.autorec.AutoRec; import com.moparisthebest.rcrdit.autorec.AutoRec;
import com.moparisthebest.rcrdit.autorec.Profile; import com.moparisthebest.rcrdit.autorec.Profile;
import com.moparisthebest.rcrdit.autorec.ProgramAutoRec; import com.moparisthebest.rcrdit.autorec.ProgramAutoRec;
import com.moparisthebest.rcrdit.scheduler.PartialScheduler;
import com.moparisthebest.rcrdit.scheduler.Scheduler;
import com.moparisthebest.rcrdit.scheduler.StartStop;
import com.moparisthebest.rcrdit.tuner.DummyTuner; import com.moparisthebest.rcrdit.tuner.DummyTuner;
import com.moparisthebest.rcrdit.tuner.HDHomerun; import com.moparisthebest.rcrdit.tuner.HDHomerun;
import com.moparisthebest.rcrdit.tuner.Tuner; import com.moparisthebest.rcrdit.tuner.Tuner;
@ -62,15 +65,12 @@ import java.math.BigInteger;
import java.net.URI; import java.net.URI;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.sql.Connection; import java.sql.*;
import java.sql.Date; import java.time.*;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.*;
import java.util.Date;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.moparisthebest.rcrdit.requestbeans.GetScheduleRequest; import com.moparisthebest.rcrdit.requestbeans.GetScheduleRequest;
import com.moparisthebest.rcrdit.requestbeans.NewRecordingRequest; import com.moparisthebest.rcrdit.requestbeans.NewRecordingRequest;
@ -84,6 +84,9 @@ import org.glassfish.jersey.jackson.JacksonFeature;
@Path("") @Path("")
public class RcrdIt extends ResourceConfig implements AutoCloseable { public class RcrdIt extends ResourceConfig implements AutoCloseable {
// for testing, should be null normally
private static final LocalDateTime fakeTime = null;//LocalDateTime.of(2017, 3, 11, 0, 0);
private static final Logger log = LoggerFactory.getLogger(RcrdIt.class); private static final Logger log = LoggerFactory.getLogger(RcrdIt.class);
// config items // config items
@ -99,6 +102,8 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
private final List<AutoRec> autoRecs = new ArrayList<>(); private final List<AutoRec> autoRecs = new ArrayList<>();
private Tv schedule; private Tv schedule;
private final Scheduler scheduler = new PartialScheduler();
private final String serverUri; private final String serverUri;
@ -147,6 +152,12 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
System.out.print("\"" + name + "\", "); System.out.print("\"" + name + "\", ");
*/ */
} }
private static void addMeeting(final Calendar calendar, final VTimeZone tz,
final Instant start, final Instant stop,
final Program prog, final MessageDigest md, final boolean skipped) {
addMeeting(calendar, tz, start, stop, new ProgramAutoRec(prog, prog.getAutoRec()), md, skipped);
}
private static void addMeeting(final Calendar calendar, final VTimeZone tz, private static void addMeeting(final Calendar calendar, final VTimeZone tz,
final Instant start, final Instant stop, final Instant start, final Instant stop,
@ -174,6 +185,8 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
description += "\nPriority: " + prog.getAutoRec().getPriority(); description += "\nPriority: " + prog.getAutoRec().getPriority();
meeting.getProperties().add(new Description(description)); meeting.getProperties().add(new Description(description));
if(fakeTime != null)
meeting.getProperties().add(new DtStamp(new DateTime(1489294362645L)));
// add timezone info.. // add timezone info..
meeting.getProperties().add(tz.getTimeZoneId()); meeting.getProperties().add(tz.getTimeZoneId());
@ -209,12 +222,13 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
calendar.getComponents().add(meeting); calendar.getComponents().add(meeting);
} }
private synchronized void reCalculateSchedule() { private synchronized void reCalculateScheduleOrig() {
log.debug("import starting."); log.debug("import starting.");
// cancel all pending timers, clear array, purge timer // cancel all pending timers, clear array, purge timer
startTimers.forEach(TimerTask::cancel); startTimers.forEach(TimerTask::cancel);
startTimers.clear(); startTimers.clear();
timer.purge(); timer.purge();
schedule.getPrograms().forEach(Program::clear);
if (schedule.getPrograms().isEmpty() || autoRecs.isEmpty()) { if (schedule.getPrograms().isEmpty() || autoRecs.isEmpty()) {
log.debug("nothing to import."); log.debug("nothing to import.");
@ -239,6 +253,8 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
// 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 // 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 = Instant.now().truncatedTo(ChronoUnit.MINUTES); Instant cursor = Instant.now().truncatedTo(ChronoUnit.MINUTES);
if(fakeTime != null)
cursor = fakeTime.toInstant(ZoneOffset.systemDefault().getRules().getOffset(fakeTime)).truncatedTo(ChronoUnit.MINUTES);
//final Instant end = cursor.plus(1, ChronoUnit.DAYS); //final Instant end = cursor.plus(1, ChronoUnit.DAYS);
final Instant end = schedule.getLastEndTime(); final Instant end = schedule.getLastEndTime();
final Map<ProgramAutoRec, Instant> currentRecs = new LinkedHashMap<>(tuners.numTuners()); // todo: needs to be sorted? final Map<ProgramAutoRec, Instant> currentRecs = new LinkedHashMap<>(tuners.numTuners()); // todo: needs to be sorted?
@ -251,7 +267,7 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
// this program is on // this program is on
for (final AutoRec autoRec : autoRecs) { for (final AutoRec autoRec : autoRecs) {
if (autoRec.matches(program)) { if (autoRec.matches(program)) {
programAutoRecs.add(new ProgramAutoRec(program, autoRec)); programAutoRecs.add(new ProgramAutoRec(program.setAutoRec(autoRec), autoRec));
break; // only match highest priority autorec, ie first since they are sorted break; // only match highest priority autorec, ie first since they are sorted
} }
} }
@ -276,15 +292,15 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
// free tuner, just add // free tuner, just add
if (currentRecs.size() < tuners.numTuners()) { if (currentRecs.size() < tuners.numTuners()) {
currentRecs.put(programAutoRec, cursor); currentRecs.put(programAutoRec, cursor);
programAutoRec.getProgram().addStartStop(new StartStop(cursor, true));
// check if we are starting one late, from skipped // check if we are starting one late, from skipped
final Instant start = skippedRecs.remove(programAutoRec); final Instant start = skippedRecs.remove(programAutoRec);
if (start != null) { if (start != null) {
addMeeting(calendar, tz, start, cursor, programAutoRec, md, true); addMeeting(calendar, tz, start, cursor, programAutoRec, md, true);
} }
//System.out.println("scheduling: "+programAutoRec); //System.out.println("scheduling: "+programAutoRec);
final RecordingTask rt = new RecordingTask(programAutoRec); final RecordingTask rt = new RecordingTask(programAutoRec, cursor);
startTimers.add(rt); startTimers.add(rt);
timer.schedule(rt, Date.from(cursor));
continue; continue;
} }
// from Tuners.recordNow // from Tuners.recordNow
@ -302,9 +318,11 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
) )
); );
//System.out.println("replacing: "+ recToReplace + " with scheduling: "+programAutoRec); //System.out.println("replacing: "+ recToReplace + " with scheduling: "+programAutoRec);
final RecordingTask rt = new RecordingTask(recToReplace, programAutoRec); final RecordingTask rt = new RecordingTask(recToReplace, programAutoRec, cursor);
startTimers.add(rt); startTimers.add(rt);
timer.schedule(rt, Date.from(cursor));
recToReplace.getProgram().addStartStop(new StartStop(cursor, false));
programAutoRec.getProgram().addStartStop(new StartStop(cursor, true, recToReplace.getProgram()));
// remove/replace it // remove/replace it
final Instant start = currentRecs.remove(recToReplace); final Instant start = currentRecs.remove(recToReplace);
@ -323,8 +341,163 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
cursor = cursor.plus(1, ChronoUnit.MINUTES); cursor = cursor.plus(1, ChronoUnit.MINUTES);
} }
// increment by minute up until end scheduling all timers until then, somehow store this info to show/email etc? // 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"); log.debug("import done.\n\n------\n\n");
if(fakeTime != null) {
try (FileOutputStream fout = new FileOutputStream("startTimers.orig.txt")) {
for (TimerTask tt : startTimers) {
fout.write(tt.toString().getBytes(UTF_8));
fout.write('\n');
}
} catch (Exception e) {
// ignore e.printStackTrace();
}
try (FileOutputStream fout = new FileOutputStream("programs.orig.txt")) {
for (Program prog : schedule.getPrograms()) {
fout.write(prog.fullString().getBytes(UTF_8));
fout.write('\n');
}
} catch (Exception e) {
// ignore e.printStackTrace();
}
}
if (!calendar.getComponents().isEmpty())
try (FileOutputStream fout = new FileOutputStream(fakeTime != null ? "rcrdit.orig.ics" : "rcrdit.ics")) {
final CalendarOutputter outputter = new CalendarOutputter();
outputter.output(calendar, fout);
} catch (Exception e) {
// ignore e.printStackTrace();
}
}
private synchronized void reCalculateSchedule() {
if(fakeTime != null)
for(String file : new String[]{"rcrdit.ics", "startTimers.txt", "programs.txt", "rcrdit.orig.ics", "startTimers.orig.txt", "programs.orig.txt"})
new File(file).delete();
reCalculateScheduleOrig();
if(fakeTime != null)
reCalculateScheduleNew();
}
private synchronized void reCalculateScheduleNew() {
log.debug("import starting.");
// cancel all pending timers, clear array, purge timer
startTimers.forEach(TimerTask::cancel);
startTimers.clear();
timer.purge();
schedule.getPrograms().forEach(Program::clear);
if (schedule.getPrograms().isEmpty() || autoRecs.isEmpty()) {
log.debug("nothing to import.");
return;
}
scheduler.schedulePrograms(autoRecs, schedule.getPrograms(), tuners.numTuners(), schedule.getLastEndTime(),
RcrdIt.fakeTime != null ?
RcrdIt.fakeTime.toInstant(ZoneOffset.systemDefault().getRules().getOffset(RcrdIt.fakeTime)).truncatedTo(ChronoUnit.MINUTES) :
Instant.now().truncatedTo(ChronoUnit.MINUTES)
);
/*
for(final Program prog : schedule.getPrograms())
if(prog.getAutoRec() != null && prog.getStartStops().size() > 2)
System.out.println(prog);
if(true)return;
*/
final MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("no sha-256?", e);
}
// Create a TimeZone
final VTimeZone tz = TimeZoneRegistryFactory.getInstance().createRegistry().getTimeZone(ZoneId.systemDefault().getId()).getVTimeZone();
final Calendar calendar = new Calendar();
calendar.getProperties().add(new ProdId("-//rcrdit//rcrdit//EN"));
calendar.getProperties().add(Version.VERSION_2_0);
calendar.getProperties().add(CalScale.GREGORIAN);
// set up startTimers and rcrdit.ics
//for(final Program program : schedule.getPrograms()) {
//for(int x = 0; x < schedule.getPrograms().size(); ++x) {
for(int x = schedule.getPrograms().size() - 1; x >= 0; --x) {
final Program program = schedule.getPrograms().get(x);
if(program.getAutoRec() != null) {
// wanted to record
final List<StartStop> startStops = program.getStartStops();
if(startStops.isEmpty()) {
// whole thing skipped...
addMeeting(calendar, tz, program.getStart(), program.getStop(), program, md, true);
continue;
}
if((startStops.size() % 2) != 0) { // todo: this is really bad, wtf
System.out.println(program.fullString());
continue;
}
int index = -1;
final StartStop first = startStops.get(++index);
final StartStop second = startStops.get(++index);
if(startStops.size() == 2 && first.getInstant().equals(program.getStart()) && second.getInstant().equals(program.getStop())) {
// whole thing recorded...
addMeeting(calendar, tz, program.getStart(), program.getStop(), program, md, false);
final RecordingTask rt = new RecordingTask(program, program.getStart());
startTimers.add(rt);
continue;
}
if(first.isStart()) {
if(first.getInstant().equals(program.getStart())) {
// not started at start time, skipped first part of program
addMeeting(calendar, tz, program.getStart(), first.getInstant(), program, md, true);
}
Program toStop = null;
Instant start = null, stop = null, lastStop = null;
for(final StartStop ss : program.getStartStops()) {
if(start == null && ss.isStart()) {
start = ss.getInstant();
toStop = ss.getToStop();
} else if(stop == null && !ss.isStart()) {
stop = ss.getInstant();
} else {
// both set
if(lastStop != null) { // todo: check if lastStop and start are the same, but shouldn't happen?
addMeeting(calendar, tz, lastStop, start, program, md, true);
}
addMeeting(calendar, tz, start, stop, program, md, false);
final RecordingTask rt = new RecordingTask(toStop, program, start);
startTimers.add(rt);
lastStop = stop;
start = stop = null;
}
}
} else {
log.error("holy shit should never happen, bad scheduler?");
throw new RuntimeException("holy shit should never happen, bad scheduler?");
}
}
}
log.debug("new import done.\n\n------\n\n");
try (FileOutputStream fout = new FileOutputStream("startTimers.txt")) {
for(TimerTask tt : startTimers) {
fout.write(tt.toString().getBytes(UTF_8));
fout.write('\n');
}
} catch (Exception e) {
// ignore e.printStackTrace();
}
try (FileOutputStream fout = new FileOutputStream("programs.txt")) {
for(Program prog : schedule.getPrograms()) {
fout.write(prog.fullString().getBytes(UTF_8));
fout.write('\n');
}
} catch (Exception e) {
// ignore e.printStackTrace();
}
if (!calendar.getComponents().isEmpty()) if (!calendar.getComponents().isEmpty())
try (FileOutputStream fout = new FileOutputStream("rcrdit.ics")) { try (FileOutputStream fout = new FileOutputStream("rcrdit.ics")) {
final CalendarOutputter outputter = new CalendarOutputter(); final CalendarOutputter outputter = new CalendarOutputter();
@ -345,6 +518,8 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
//if(prog.getStop().isBefore(finalCursor)) { //if(prog.getStop().isBefore(finalCursor)) {
final Instant start = entry.getValue(); final Instant start = entry.getValue();
addMeeting(calendar, tz, start, prog.getStop(), par, md, skipped); addMeeting(calendar, tz, start, prog.getStop(), par, md, skipped);
if(!skipped)
prog.addStartStop(new StartStop(prog.getStop(), false));
it.remove(); it.remove();
} }
} }
@ -395,7 +570,7 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
@Override @Override
public void run() { public void run() {
try { try {
schedule = Tv.readSchedule(xmltvPaths, allChannels); schedule = Tv.readSchedule(xmltvPaths, allChannels, RcrdIt.fakeTime != null ? RcrdIt.fakeTime.withMinute(0) : LocalDateTime.now().withMinute(0));
//System.out.println(schedule); //System.out.println(schedule);
// todo: only re-calcuate if schedule changed // todo: only re-calcuate if schedule changed
reCalculateSchedule(); reCalculateSchedule();
@ -407,14 +582,26 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
private class RecordingTask extends TimerTask { private class RecordingTask extends TimerTask {
private final ProgramAutoRec stop, start; private final ProgramAutoRec stop, start;
private final Instant runAt;
RecordingTask(final ProgramAutoRec stop, final ProgramAutoRec start) { RecordingTask(final ProgramAutoRec stop, final ProgramAutoRec start, final Instant runAt) {
this.stop = stop; this.stop = stop;
this.start = start; this.start = start;
this.runAt = runAt;
if(fakeTime != null)
timer.schedule(this, Date.from(runAt));
} }
RecordingTask(final ProgramAutoRec start) { RecordingTask(final ProgramAutoRec start, final Instant runAt) {
this(null, start); this(null, start, runAt);
}
RecordingTask(final Program stop, final Program start, final Instant runAt) {
this(stop == null ? null : new ProgramAutoRec(stop, stop.getAutoRec()), start == null ? null : new ProgramAutoRec(start, start.getAutoRec()), runAt);
}
RecordingTask(final Program start, final Instant runAt) {
this(null, start, runAt);
} }
@Override @Override
@ -429,6 +616,15 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override
public String toString() {
return "RecordingTask{" +
"stop=" + stop +
", start=" + start +
", runAt=" + runAt +
"} ";
}
} }
@Override @Override
@ -553,6 +749,7 @@ public class RcrdIt extends ResourceConfig implements AutoCloseable {
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
//System.out.println(System.currentTimeMillis()); if(true) return;
final File cfg; final File cfg;
if (args.length < 1 || !((cfg = new File(args[0])).exists())) { if (args.length < 1 || !((cfg = new File(args[0])).exists())) {
System.err.println("Usage: java -jar rcrdit.jar /path/to/rcrdit.cfg.xml"); System.err.println("Usage: java -jar rcrdit.jar /path/to/rcrdit.cfg.xml");

View File

@ -0,0 +1,135 @@
/*
* 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");
}
}

View File

@ -0,0 +1,32 @@
/*
* 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 java.time.Instant;
import java.util.List;
/**
* Created by mopar on 3/14/17.
*/
public interface Scheduler {
void schedulePrograms(final List<AutoRec> autoRecs, final List<Program> programs, final int numTuners, final Instant end, final Instant startAt);
}

View File

@ -0,0 +1,79 @@
/*
* 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.xmltv.Program;
import java.time.Instant;
import java.util.Objects;
/**
* Created by mopar on 3/14/17.
*/
public class StartStop {
private final Instant instant;
private final boolean start;
private final Program toStop;
public StartStop(final Instant instant, final boolean start, final Program toStop) {
this.instant = instant;
this.start = start;
this.toStop = toStop;
}
public StartStop(final Instant instant, final boolean start) {
this(instant, start, null);
}
public Instant getInstant() {
return instant;
}
public boolean isStart() {
return start;
}
public Program getToStop() {
return toStop;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof StartStop)) return false;
final StartStop startStop = (StartStop) o;
return start == startStop.start &&
Objects.equals(instant, startStop.instant) &&
Objects.equals(toStop, startStop.toStop);
}
@Override
public int hashCode() {
return Objects.hash(instant, start, toStop);
}
@Override
public String toString() {
return "StartStop{" +
"instant=" + instant +
", start=" + start +
", toStop=" + toStop +
'}';
}
}

View File

@ -18,7 +18,12 @@
package com.moparisthebest.rcrdit.xmltv; package com.moparisthebest.rcrdit.xmltv;
import com.moparisthebest.rcrdit.autorec.AutoRec;
import com.moparisthebest.rcrdit.scheduler.StartStop;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
@ -27,7 +32,7 @@ import java.util.Objects;
//@XmlRootElement(name="programme") //@XmlRootElement(name="programme")
//@JsonIgnoreProperties(value={"previously-shown", "lang", "", "system", "date"}) //@JsonIgnoreProperties(value={"previously-shown", "lang", "", "system", "date"})
//@JsonIgnoreProperties(ignoreUnknown = true) //@JsonIgnoreProperties(ignoreUnknown = true)
public class Program { public class Program implements Comparable<Program> {
/* /*
@ -60,6 +65,9 @@ public class Program {
private final String channelId, channelName, title, subTitle, desc, episodeNum, date, category; private final String channelId, channelName, title, subTitle, desc, episodeNum, date, category;
private final boolean previouslyShown; private final boolean previouslyShown;
private final List<StartStop> startStops = new ArrayList<>();
private AutoRec autoRec;
public Program(final Instant start, final Instant stop, final String channelId, final String channelName, final String title, final String subTitle, final String desc, final String episodeNum, final String date, final String category, final boolean previouslyShown) { public Program(final Instant start, final Instant stop, final String channelId, final String channelName, final String title, final String subTitle, final String desc, final String episodeNum, final String date, final String category, final boolean previouslyShown) {
this.start = start; this.start = start;
this.stop = stop; this.stop = stop;
@ -118,6 +126,39 @@ public class Program {
return previouslyShown; return previouslyShown;
} }
public AutoRec getAutoRec() {
return autoRec;
}
/**
* Set autoRec if this.autoRec is null, or autoRec.priority > this.autoRec.priority
* @param autoRec to set, must be non-null
* @return this
*/
public Program setAutoRec(final AutoRec autoRec) {
if(this.autoRec == null || this.autoRec.getPriority() < autoRec.getPriority())
this.autoRec = autoRec;
return this;
}
public void addStartStop(final StartStop startStop) {
startStops.add(startStop);
}
public List<StartStop> getStartStops() {
return startStops;
}
public void clear() {
startStops.clear();
this.autoRec = null;
}
@Override
public int compareTo(final Program o) {
return this.autoRec.compareTo(o.autoRec);
}
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (this == o) return true; if (this == o) return true;
@ -155,6 +196,26 @@ public class Program {
", date='" + date + '\'' + ", date='" + date + '\'' +
", category='" + category + '\'' + ", category='" + category + '\'' +
", previouslyShown=" + previouslyShown + ", previouslyShown=" + previouslyShown +
//", autoRec=" + autoRec +
//", startStops=" + startStops +
'}';
}
public String fullString() {
return "Program{" +
"start=" + start +
", stop=" + stop +
", channelId='" + channelId + '\'' +
", channelName='" + channelName + '\'' +
", title='" + title + '\'' +
", subTitle='" + subTitle + '\'' +
", desc='" + desc + '\'' +
", episodeNum='" + episodeNum + '\'' +
", date='" + date + '\'' +
", category='" + category + '\'' +
", previouslyShown=" + previouslyShown +
", autoRec=" + autoRec +
", startStops=" + startStops +
'}'; '}';
} }
} }

View File

@ -21,10 +21,7 @@ package com.moparisthebest.rcrdit.xmltv;
import com.moparisthebest.sxf4j.impl.AbstractXmlElement; import com.moparisthebest.sxf4j.impl.AbstractXmlElement;
import com.moparisthebest.sxf4j.impl.XmlElement; import com.moparisthebest.sxf4j.impl.XmlElement;
import java.time.Instant; import java.time.*;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.format.ResolverStyle; import java.time.format.ResolverStyle;
@ -78,7 +75,7 @@ public class Tv {
'}'; '}';
} }
public static Tv readSchedule(final List<String> resources, final Set<String> allChannels) throws Exception { public static Tv readSchedule(final List<String> resources, final Set<String> allChannels, final LocalDateTime topOfHour) throws Exception {
//try /*(InputStream is = new FileInputStream(resource))*/ { //try /*(InputStream is = new FileInputStream(resource))*/ {
/* /*
ObjectMapper mapper = new XmlMapper(new XmlFactory()); // create once, reuse ObjectMapper mapper = new XmlMapper(new XmlFactory()); // create once, reuse
@ -128,7 +125,6 @@ public class Tv {
break; break;
} }
} }
final LocalDateTime topOfHour = LocalDateTime.now().withMinute(0);
final Instant now = topOfHour.toInstant(ZoneOffset.systemDefault().getRules().getOffset(topOfHour)).truncatedTo(ChronoUnit.MINUTES); final Instant now = topOfHour.toInstant(ZoneOffset.systemDefault().getRules().getOffset(topOfHour)).truncatedTo(ChronoUnit.MINUTES);
//final Instant now = Instant.now()..truncatedTo(ChronoUnit.MINUTES); //final Instant now = Instant.now()..truncatedTo(ChronoUnit.MINUTES);
for (final XmlElement prog : tv.getChildren("programme")) { for (final XmlElement prog : tv.getChildren("programme")) {