338 lines
8.8 KiB
Java
338 lines
8.8 KiB
Java
package ctbrec;
|
|
|
|
|
|
import ctbrec.recorder.download.HttpHeaderFactory;
|
|
import ctbrec.recorder.download.HttpHeaderFactoryImpl;
|
|
import ctbrec.recorder.download.RecordingProcess;
|
|
import ctbrec.recorder.download.hls.HlsDownload;
|
|
import ctbrec.recorder.download.hls.HlsdlDownload;
|
|
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
|
|
import ctbrec.sites.Site;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
|
|
import java.io.IOException;
|
|
import java.time.Instant;
|
|
import java.util.*;
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
import static ctbrec.io.HttpConstants.USER_AGENT;
|
|
|
|
public abstract class AbstractModel implements Model {
|
|
|
|
private String url;
|
|
private String name;
|
|
private String sanitizedName;
|
|
private String displayName;
|
|
private String preview;
|
|
private String description;
|
|
private List<String> tags = new ArrayList<>();
|
|
private int streamUrlIndex = -1;
|
|
private int priority = new Settings().defaultPriority;
|
|
private boolean suspended = false;
|
|
private boolean forcePriority = false;
|
|
private boolean markedForLaterRecording = false;
|
|
protected transient Site site;
|
|
protected State onlineState = State.UNCHECKED;
|
|
private Instant lastSeen;
|
|
private Instant lastRecorded;
|
|
private Instant recordUntil;
|
|
private Instant addedTimestamp = Instant.EPOCH;
|
|
private transient Instant delayUntil = Instant.EPOCH;
|
|
private SubsequentAction recordUntilSubsequentAction;
|
|
|
|
@Override
|
|
public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
|
|
return isOnline(false);
|
|
}
|
|
|
|
@Override
|
|
public String getUrl() {
|
|
return url;
|
|
}
|
|
|
|
@Override
|
|
public void setUrl(String url) {
|
|
this.url = url;
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
@Override
|
|
public void setName(String name) {
|
|
this.name = name;
|
|
this.sanitizedName = Optional.ofNullable(getName()).orElse("").replaceAll("[^a-zA-Z0-9.-]", "_");
|
|
}
|
|
|
|
@Override
|
|
public String getDisplayName() {
|
|
if (displayName != null) {
|
|
return displayName;
|
|
} else {
|
|
return getName();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setDisplayName(String name) {
|
|
this.displayName = name;
|
|
}
|
|
|
|
@Override
|
|
public String getSanitizedNamed() {
|
|
return sanitizedName;
|
|
}
|
|
|
|
@Override
|
|
public String getPreview() {
|
|
return preview;
|
|
}
|
|
|
|
@Override
|
|
public void setPreview(String preview) {
|
|
this.preview = preview;
|
|
}
|
|
|
|
@Override
|
|
public List<String> getTags() {
|
|
return tags;
|
|
}
|
|
|
|
@Override
|
|
public void setTags(List<String> tags) {
|
|
this.tags = tags;
|
|
}
|
|
|
|
@Override
|
|
public String getDescription() {
|
|
return description;
|
|
}
|
|
|
|
@Override
|
|
public void setDescription(String description) {
|
|
this.description = description;
|
|
}
|
|
|
|
@Override
|
|
public int getStreamUrlIndex() {
|
|
return streamUrlIndex;
|
|
}
|
|
|
|
@Override
|
|
public void setStreamUrlIndex(int streamUrlIndex) {
|
|
this.streamUrlIndex = streamUrlIndex;
|
|
}
|
|
|
|
@Override
|
|
public void readSiteSpecificData(Map<String, String> data) {
|
|
// noop default implementation, can be overriden by concrete models
|
|
}
|
|
|
|
@Override
|
|
public void writeSiteSpecificData(Map<String, String> data) {
|
|
// noop default implementation, can be overriden by concrete models
|
|
}
|
|
|
|
@Override
|
|
public boolean isSuspended() {
|
|
return suspended;
|
|
}
|
|
|
|
@Override
|
|
public void setSuspended(boolean suspended) {
|
|
this.suspended = suspended;
|
|
}
|
|
|
|
@Override
|
|
public boolean isForcePriority() { return forcePriority; }
|
|
|
|
@Override
|
|
public void setForcePriority(boolean forcePriority) { this.forcePriority = forcePriority; }
|
|
|
|
@Override
|
|
public void delay() {
|
|
this.delayUntil = Instant.now().plusSeconds(120);
|
|
}
|
|
|
|
@Override
|
|
public boolean isDelayed() {
|
|
return this.delayUntil.isAfter(Instant.now());
|
|
}
|
|
|
|
@Override
|
|
public boolean isMarkedForLaterRecording() {
|
|
return markedForLaterRecording;
|
|
}
|
|
|
|
@Override
|
|
public void setMarkedForLaterRecording(boolean marked) {
|
|
this.markedForLaterRecording = marked;
|
|
}
|
|
|
|
@Override
|
|
public State getOnlineState(boolean failFast) throws IOException, ExecutionException {
|
|
return onlineState;
|
|
}
|
|
|
|
public void setOnlineState(State status) {
|
|
this.onlineState = status;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
final int prime = 31;
|
|
int result = 1;
|
|
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
|
|
result = prime * result + ((getUrl() == null) ? 0 : getUrl().hashCode());
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj)
|
|
return true;
|
|
if (obj == null)
|
|
return false;
|
|
if (!(obj instanceof Model))
|
|
return false;
|
|
Model other = (Model) obj;
|
|
if (getName() == null) {
|
|
if (other.getName() != null)
|
|
return false;
|
|
} else if (!getSanitizedNamed().equals(other.getSanitizedNamed()))
|
|
return false;
|
|
if (getUrl() == null) {
|
|
if (other.getUrl() != null)
|
|
return false;
|
|
} else if (!getUrl().equals(other.getUrl()))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(Model o) {
|
|
String thisName = Optional.ofNullable(getDisplayName()).orElse("").toLowerCase();
|
|
String otherName = Optional.ofNullable(o).map(Model::getDisplayName).orElse("").toLowerCase();
|
|
return thisName.compareTo(otherName);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return getName();
|
|
}
|
|
|
|
@Override
|
|
public void setSite(Site site) {
|
|
this.site = site;
|
|
}
|
|
|
|
@Override
|
|
public Site getSite() {
|
|
return site;
|
|
}
|
|
|
|
@Override
|
|
public int getPriority() {
|
|
return priority;
|
|
}
|
|
|
|
@Override
|
|
public void setPriority(int priority) {
|
|
this.priority = priority;
|
|
}
|
|
|
|
@Override
|
|
public Instant getLastSeen() {
|
|
return Optional.ofNullable(lastSeen).orElse(Instant.EPOCH);
|
|
}
|
|
|
|
@Override
|
|
public void setLastSeen(Instant lastSeen) {
|
|
this.lastSeen = lastSeen;
|
|
}
|
|
|
|
@Override
|
|
public Instant getLastRecorded() {
|
|
return Optional.ofNullable(lastRecorded).orElse(Instant.EPOCH);
|
|
}
|
|
|
|
@Override
|
|
public void setLastRecorded(Instant lastRecorded) {
|
|
this.lastRecorded = lastRecorded;
|
|
}
|
|
|
|
@Override
|
|
public Instant getAddedTimestamp() {
|
|
return addedTimestamp;
|
|
}
|
|
|
|
@Override
|
|
public void setAddedTimestamp(Instant timestamp) {
|
|
this.addedTimestamp = timestamp;
|
|
}
|
|
|
|
@Override
|
|
public boolean isRecordingTimeLimited() {
|
|
return !getRecordUntil().equals(Instant.ofEpochMilli(RECORD_INDEFINITELY));
|
|
}
|
|
|
|
@Override
|
|
public Instant getRecordUntil() {
|
|
return Optional.ofNullable(recordUntil).orElse(Instant.ofEpochMilli(RECORD_INDEFINITELY));
|
|
}
|
|
|
|
@Override
|
|
public void setRecordUntil(Instant recordUntil) {
|
|
this.recordUntil = recordUntil;
|
|
}
|
|
|
|
@Override
|
|
public SubsequentAction getRecordUntilSubsequentAction() {
|
|
return Optional.ofNullable(recordUntilSubsequentAction).orElse(SubsequentAction.PAUSE);
|
|
}
|
|
|
|
@Override
|
|
public void setRecordUntilSubsequentAction(SubsequentAction recordUntilSubsequentAction) {
|
|
this.recordUntilSubsequentAction = recordUntilSubsequentAction;
|
|
}
|
|
|
|
@Override
|
|
public RecordingProcess createDownload() {
|
|
if (Config.getInstance().getSettings().useHlsdl) {
|
|
return new HlsdlDownload();
|
|
} else {
|
|
if (Config.isServerMode() && !Config.getInstance().getSettings().recordSingleFile) {
|
|
return new HlsDownload(getSite().getHttpClient());
|
|
} else {
|
|
return new MergedFfmpegHlsDownload(getSite().getHttpClient());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public HttpHeaderFactory getHttpHeaderFactory() {
|
|
HttpHeaderFactoryImpl fac = new HttpHeaderFactoryImpl();
|
|
fac.setMasterPlaylistHeaders(new HashMap<>());
|
|
fac.setSegmentPlaylistHeaders(new HashMap<>());
|
|
fac.setSegmentHeaders(new HashMap<>());
|
|
return fac;
|
|
}
|
|
|
|
@Override
|
|
public boolean exists() throws IOException {
|
|
Request req = new Request.Builder() // @formatter:off
|
|
.url(getUrl())
|
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
|
.build(); // @formatter:on
|
|
try (Response response = getSite().getHttpClient().execute(req)) {
|
|
if (!response.isSuccessful() && response.code() == 404) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|