package ctbrec; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import com.iheartradio.m3u8.Encoding; import com.iheartradio.m3u8.Format; import com.iheartradio.m3u8.ParseException; import com.iheartradio.m3u8.ParsingMode; import com.iheartradio.m3u8.PlaylistException; import com.iheartradio.m3u8.PlaylistParser; import com.iheartradio.m3u8.data.MediaPlaylist; import com.iheartradio.m3u8.data.Playlist; import com.iheartradio.m3u8.data.TrackData; import ctbrec.event.EventBusHolder; import ctbrec.event.RecordingStateChangedEvent; import ctbrec.recorder.download.Download; public class Recording { private Model model; private transient Download download; private Instant startDate; private String path; private State status = State.UNKNOWN; private int progress = -1; private long sizeInByte = -1; private String metaDataFile; public static enum State { RECORDING("recording"), STOPPED("stopped"), GENERATING_PLAYLIST("generating playlist"), POST_PROCESSING("post-processing"), FINISHED("finished"), DOWNLOADING("downloading"), DELETING("deleting"), DELETED("deleted"), UNKNOWN("unknown"), WAITING("waiting"), FAILED("failed"); private String desc; State(String desc) { this.desc = desc; } @Override public String toString() { return desc; } } public Recording() {} // public Recording(String path) throws ParseException { // this.path = path; // this.modelName = path.substring(0, path.indexOf("/")); // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm"); // Date date = sdf.parse(path.substring(path.indexOf('/')+1)); // startDate = Instant.ofEpochMilli(date.getTime()); // } public Instant getStartDate() { return startDate; } public void setStartDate(Instant startDate) { this.startDate = startDate; } public State getStatus() { return status; } public void setStatus(State status) { this.status = status; } public void setStatusWithEvent(State status, boolean fireEvent) { setStatus(status); fireStatusEvent(status); } public int getProgress() { return this.progress; } public void setProgress(int progress) { this.progress = progress; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public File getAbsoluteFile() { String recordingsDir = Config.getInstance().getSettings().recordingsDir; File recordingsFile = new File(recordingsDir, getPath()); return recordingsFile; } public long getSizeInByte() { if (sizeInByte == -1 || getStatus() == State.RECORDING) { this.sizeInByte = getSize(); } return sizeInByte; } public void setSizeInByte(long sizeInByte) { this.sizeInByte = sizeInByte; } public void postprocess() { getDownload().postprocess(this); } private void fireStatusEvent(State status) { RecordingStateChangedEvent evt = new RecordingStateChangedEvent(getDownload().getTarget(), status, getModel(), getStartDate()); EventBusHolder.BUS.post(evt); } public Model getModel() { return model; } public void setModel(Model model) { this.model = model; } public Download getDownload() { return download; } public void setDownload(Download download) { this.download = download; } public String getMetaDataFile() { return metaDataFile; } public void setMetaDataFile(String metaDataFile) { this.metaDataFile = metaDataFile; } public Duration getLength() throws IOException, ParseException, PlaylistException { // check, if the recording exists File rec = new File(Config.getInstance().getSettings().recordingsDir, getPath()); if (!rec.exists()) { return Duration.ofSeconds(0); } // check, if the recording has data at all long size = getSizeInByte(); if (size == 0) { return Duration.ofSeconds(0); } // determine the length if (getPath().endsWith(".ts")) { return Duration.ofSeconds((long) MpegUtil.getFileDuration(rec)); } else if (rec.isDirectory()) { File playlist = new File(rec, "playlist.m3u8"); if (playlist.exists()) { return Duration.ofSeconds((long) getPlaylistLength(playlist)); } } return Duration.ofSeconds(0); } private double getPlaylistLength(File playlist) throws IOException, ParseException, PlaylistException { if (playlist.exists()) { PlaylistParser playlistParser = new PlaylistParser(new FileInputStream(playlist), Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT); Playlist m3u = playlistParser.parse(); MediaPlaylist mediaPlaylist = m3u.getMediaPlaylist(); double length = 0; for (TrackData trackData : mediaPlaylist.getTracks()) { length += trackData.getTrackInfo().duration; } return length; } else { throw new FileNotFoundException(playlist.getAbsolutePath() + " does not exist"); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getModel() == null) ? 0 : getModel().hashCode()); result = prime * result + ((getPath() == null) ? 0 : getPath().hashCode()); result = prime * result + ((getStartDate() == null) ? 0 : getStartDate().hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if(!(obj instanceof Recording)) { return false; } Recording other = (Recording) obj; if (getModel() == null) { if (other.getModel() != null) return false; } else if (!getModel().equals(other.getModel())) return false; if (getPath() == null) { if (other.getPath() != null) return false; } else if (!getPath().equals(other.getPath())) return false; if (getStartDate() == null) { if (other.getStartDate() != null) return false; } else if (!getStartDate().equals(other.getStartDate())) return false; return true; } @Override public String toString() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Config.RECORDING_DATE_FORMAT); LocalDateTime localStartDate = LocalDateTime.ofInstant(getStartDate(), ZoneId.systemDefault()); return getModel().getSanitizedNamed() + '_' + formatter.format(localStartDate); } private long getSize() { File rec = new File(Config.getInstance().getSettings().recordingsDir, getPath()); if(rec.isDirectory()) { long size = 0; File[] files = rec.listFiles(); for (File file : files) { size += file.length(); } return size; } else { return rec.length(); } } }