forked from j62/ctbrec
1
0
Fork 0

Add range slider for the recording resolution

This commit is contained in:
0xboobface 2020-06-13 20:08:25 +02:00
parent 229fc1f432
commit 6e25f98b2b
12 changed files with 646 additions and 42 deletions

View File

@ -0,0 +1,46 @@
package ctbrec.ui.controls.range;
import java.util.List;
public class DiscreteRange<T> implements Range<T> {
private List<T> values;
private List<?> labels;
public DiscreteRange(List<T> values, List<?> labels) {
this.values = values;
this.labels = labels;
if (values == null) {
throw new IllegalArgumentException("Values cannot be null");
}
if (labels == null) {
throw new IllegalArgumentException("Labels cannot be null");
}
}
@Override
public T getMinimum() {
return values.get(0);
}
@Override
public T getMaximum() {
return values.get(values.size()-1);
}
@Override
public List<T> getTicks() {
return values;
}
public List<?> getLabels() {
return labels;
}
@Override
public String toString() {
return labels.toString();
}
}

View File

@ -0,0 +1,49 @@
package ctbrec.ui.controls.range;
import java.util.Collections;
import java.util.List;
import javafx.scene.chart.ValueAxis;
public class LabeledNumberAxis extends ValueAxis<Number> {
private DiscreteRange<Number> range;
public LabeledNumberAxis(DiscreteRange<Number> range) {
this.range = range;
}
@Override
protected List<Number> calculateMinorTickMarks() {
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
@Override
protected void setRange(Object range, boolean animate) {
if (!(range instanceof DiscreteRange)) {
throw new IllegalArgumentException("Range has to be of type DiscreteRange");
}
this.range = (DiscreteRange<Number>) range;
}
@Override
protected Object getRange() {
return range;
}
@Override
protected List<Number> calculateTickValues(double length, Object range) {
if (!(range instanceof Range)) {
throw new IllegalArgumentException("Range has to be of type ctbrec.ui.controls.range.Range<T>");
}
@SuppressWarnings("unchecked")
Range<Number> discreteRange = (Range<Number>) range;
return discreteRange.getTicks();
}
@Override
protected String getTickMarkLabel(Number value) {
return range.getLabels().get(value.intValue()).toString();
}
}

View File

@ -0,0 +1,10 @@
package ctbrec.ui.controls.range;
import java.util.List;
public interface Range<T> {
public T getMinimum();
public T getMaximum();
public List<T> getTicks();
}

View File

@ -0,0 +1,88 @@
package ctbrec.ui.controls.range;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Orientation;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
public class RangeSlider<T extends Number> extends Control {
private static final String DEFAULT_STYLE_CLASS = "rangeslider";
private Range<T> range;
private DoubleProperty low;
private DoubleProperty high;
private boolean showTickMarks = false;
private boolean showTickLabels = false;
private Orientation orientation = Orientation.HORIZONTAL;
public RangeSlider(Range<T> range) {
this.range = range;
low = new SimpleDoubleProperty(getMinimum().doubleValue());
high = new SimpleDoubleProperty(getMaximum().doubleValue());
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
}
@Override
protected Skin<?> createDefaultSkin() {
return new RangeSliderSkin(this, new RangeSliderBehavior<>(this));
}
@Override
public String getUserAgentStylesheet() {
return RangeSlider.class.getResource("rangeslider.css").toExternalForm();
}
public DoubleProperty getLow() {
return low;
}
public void setLow(T newPosition) {
low.set(newPosition.doubleValue());
}
public DoubleProperty getHigh() {
return high;
}
public void setHigh(T newPosition) {
this.high.set(newPosition.doubleValue());
}
public T getMinimum() {
return range.getMinimum();
}
public T getMaximum() {
return range.getMaximum();
}
public boolean isShowTickMarks() {
return showTickMarks;
}
public void setShowTickMarks(boolean showTickMarks) {
this.showTickMarks = showTickMarks;
}
public boolean isShowTickLabels() {
return showTickLabels;
}
public void setShowTickLabels(boolean showTickLabels) {
this.showTickLabels = showTickLabels;
}
public Orientation getOrientation() {
return orientation;
}
public void setOrientation(Orientation orientation) {
this.orientation = orientation;
}
public Range<T> getRange() {
return range;
}
}

View File

@ -0,0 +1,52 @@
package ctbrec.ui.controls.range;
import java.util.List;
import com.sun.javafx.scene.control.behavior.BehaviorBase; // NOSONAR
import com.sun.javafx.scene.control.inputmap.InputMap; // NOSONAR
public class RangeSliderBehavior<T extends Number> extends BehaviorBase<RangeSlider<T>> {
private RangeSlider<T> rangeSlider;
public RangeSliderBehavior(RangeSlider<T> rangeSlider) {
super(rangeSlider);
this.rangeSlider = rangeSlider;
}
@Override
public InputMap<RangeSlider<T>> getInputMap() {
InputMap<RangeSlider<T>> inputMap = new InputMap<>(rangeSlider);
return inputMap;
}
/**
* @param position
* The mouse position on track with 0.0 being beginning of track and 1.0 being the end
*/
public void lowThumbDragged(double position) {
rangeSlider.setLow(getNewPosition(position));
}
/**
* @param position
* The mouse position on track with 0.0 being beginning of track and 1.0 being the end
*/
public void highThumbDragged(double position) {
rangeSlider.setHigh(getNewPosition(position));
}
/**
* Calculate the new position of the thumb given the clicked/dragged position
*
* @param position
* clicked position
* @return new position
*/
private T getNewPosition(double position) {
List<T> ticks = rangeSlider.getRange().getTicks();
double percentPerTick = 1d / (ticks.size() - 1);
int index = (int) Math.round(position / percentPerTick);
return ticks.get(index);
}
}

View File

@ -0,0 +1,283 @@
package ctbrec.ui.controls.range;
import javafx.geometry.Orientation;
import javafx.geometry.Point2D;
import javafx.geometry.Side;
import javafx.scene.chart.Axis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ValueAxis;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.StackPane;
public class RangeSliderSkin extends SkinBase<RangeSlider<?>> {
private static final double TRACK_TO_TICK_GAP = 2;
private Axis<?> tickLine = null;
private StackPane track;
private ThumbRange thumbRange = new ThumbRange();
private RangeSliderBehavior<?> behavior;
private double thumbWidth;
private double thumbHeight;
private boolean showTickMarks;
private double trackStart;
private double trackLength;
private double lowThumbPos;
private double preDragPos; // used as a temp value for low and high thumbsRange
private Point2D preDragThumbPoint; // in skin coordinates
protected RangeSliderSkin(RangeSlider<?> control, RangeSliderBehavior<?> behavior) {
super(control);
this.behavior = behavior;
initTrack();
initThumbs(thumbRange);
;
registerChangeListener(control.getLow(), (obsVal) -> getSkinnable().requestLayout());
registerChangeListener(control.getHigh(), (obsVal) -> getSkinnable().requestLayout());
}
private void initThumbs(ThumbRange t) {
setShowTickMarks(getSkinnable().isShowTickMarks(), getSkinnable().isShowTickLabels());
getChildren().addAll(t.low, t.high, t.rangeBar);
t.low.setOnMousePressed(me -> {
preDragThumbPoint = t.low.localToParent(me.getX(), me.getY());
preDragPos = (getSkinnable().getLow().doubleValue() - getSkinnable().getMinimum().doubleValue()) / (getMaxMinusMinNoZero());
});
t.low.setOnMouseDragged(me -> {
Point2D cur = t.low.localToParent(me.getX(), me.getY());
double dragPos = (isHorizontal()) ? cur.getX() - preDragThumbPoint.getX() : -(cur.getY() - preDragThumbPoint.getY());
behavior.lowThumbDragged(preDragPos + dragPos / trackLength);
});
t.high.setOnMousePressed(me -> {
preDragThumbPoint = t.high.localToParent(me.getX(), me.getY());
preDragPos = (getSkinnable().getHigh().doubleValue() - getSkinnable().getMinimum().doubleValue()) / (getMaxMinusMinNoZero());
});
t.high.setOnMouseDragged(me -> {
boolean orientation = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
double trackLen = orientation ? track.getWidth() : track.getHeight();
Point2D cur = t.high.localToParent(me.getX(), me.getY());
double dragPos = getSkinnable().getOrientation() != Orientation.HORIZONTAL ? -(cur.getY() - preDragThumbPoint.getY()) : cur.getX() - preDragThumbPoint.getX();
behavior.highThumbDragged(preDragPos + dragPos / trackLen);
});
}
private boolean isHorizontal() {
return getSkinnable().getOrientation() == Orientation.HORIZONTAL;
}
private void initTrack() {
track = new StackPane();
track.getStyleClass().setAll("track");
getChildren().clear();
getChildren().add(track);
}
private void setShowTickMarks(boolean ticksVisible, boolean labelsVisible) {
showTickMarks = (ticksVisible || labelsVisible);
var rangeSlider = getSkinnable();
if (showTickMarks) {
if (tickLine == null) {
var range = rangeSlider.getRange();
tickLine = createAxis(range, ticksVisible, labelsVisible);
getChildren().addAll(tickLine);
} else {
tickLine.setTickLabelsVisible(labelsVisible);
tickLine.setTickMarkVisible(ticksVisible);
}
}
getSkinnable().requestLayout();
}
@SuppressWarnings("unchecked")
private Axis<?> createAxis(Range<? extends Number> range, boolean ticksVisible, boolean labelsVisible) {
ValueAxis<Number> axis;
if (range instanceof DiscreteRange) {
axis = new LabeledNumberAxis((DiscreteRange<Number>) range);
} else {
axis = new NumberAxis();
}
axis.setUpperBound(range.getMaximum().doubleValue());
axis.setLowerBound(range.getMinimum().doubleValue());
axis.setMinorTickVisible(false);
axis.setMinorTickCount(0);
axis.setAutoRanging(false);
axis.setAnimated(false);
axis.setSide(isHorizontal() ? Side.BOTTOM : Side.RIGHT);
axis.setTickMarkVisible(ticksVisible);
axis.setTickLabelsVisible(labelsVisible);
return axis;
}
@Override
protected void layoutChildren(final double x, final double y, final double w, final double h) {
if (thumbRange != null) {
thumbWidth = thumbRange.low.prefWidth(-1);
thumbHeight = thumbRange.low.prefHeight(-1);
thumbRange.low.resize(thumbWidth, thumbHeight);
thumbRange.high.resize(thumbWidth, thumbHeight);
}
// we are assuming the is common radius's for all corners on the track
double trackRadius = track.getBackground() == null ? 0
: !track.getBackground().getFills().isEmpty() ? track.getBackground().getFills().get(0).getRadii().getTopLeftHorizontalRadius() : 0;
double tickLineHeight = (showTickMarks) ? tickLine.prefHeight(-1) : 0;
double trackHeight = 5;// track.prefHeight(-1);
double trackAreaHeight = Math.max(trackHeight, thumbHeight);
double totalHeightNeeded = trackAreaHeight + ((showTickMarks) ? TRACK_TO_TICK_GAP + tickLineHeight : 0);
double startY = y + ((h - totalHeightNeeded) / 2); // center slider in available height vertically
trackLength = w - thumbWidth;
trackStart = x + (thumbWidth / 2);
double trackTop = (int) (startY + ((trackAreaHeight - trackHeight) / 2));
lowThumbPos = (int) (startY + ((trackAreaHeight - thumbHeight) / 2));
// layout track
track.resizeRelocate(trackStart - trackRadius, trackTop, trackLength + trackRadius + trackRadius, trackHeight);
positionThumbs();
if (showTickMarks) {
tickLine.setLayoutX(trackStart);
tickLine.setLayoutY(trackTop + trackHeight + TRACK_TO_TICK_GAP);
tickLine.resize(trackLength, tickLineHeight);
tickLine.requestAxisLayout();
} else {
if (tickLine != null) {
tickLine.resize(0, 0);
tickLine.requestAxisLayout();
}
tickLine = null;
}
}
private void positionThumbs() {
RangeSlider<?> s = getSkinnable();
double lxl = trackStart + (trackLength * ((s.getLow().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
double lxh = trackStart + (trackLength * ((s.getHigh().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
double ly = lowThumbPos;
if (thumbRange != null) {
thumbRange.low.setLayoutX(lxl);
thumbRange.low.setLayoutY(ly);
thumbRange.high.setLayoutX(lxh);
thumbRange.high.setLayoutY(ly);
thumbRange.rangeBar.resizeRelocate(thumbRange.low.getLayoutX() + thumbRange.low.getWidth(), track.getLayoutY(),
thumbRange.high.getLayoutX() - thumbRange.low.getLayoutX() - thumbRange.low.getWidth(), track.getHeight());
}
}
private double minTrackLength() {
return 2 * ((thumbRange != null) ? thumbRange.low.prefWidth(-1) : 1);
}
@Override
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
if (isHorizontal()) {
return (leftInset + minTrackLength() + ((thumbRange != null) ? thumbRange.low.prefWidth(-1) : 1) + rightInset);
} else {
return (leftInset + ((thumbRange != null) ? thumbRange.low.prefWidth(-1) : 1) + rightInset);
}
}
@Override
protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
if (isHorizontal()) {
return (topInset + ((thumbRange != null) ? thumbRange.low.prefHeight(-1) : 1) + bottomInset);
} else {
return (topInset + minTrackLength() + ((thumbRange != null) ? thumbRange.low.prefHeight(-1) : 1) + bottomInset);
}
}
@Override
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
if (isHorizontal()) {
if (showTickMarks) {
double w = Math.max(140, tickLine.prefWidth(-1));
System.err.println("computePrefWidth " + w);
return w;
} else {
return 140;
}
} else {
return leftInset + Math.max(((thumbRange != null) ? thumbRange.low.prefWidth(-1) : 1), track.prefWidth(-1))
+ ((showTickMarks) ? (TRACK_TO_TICK_GAP + tickLine.prefWidth(-1)) : 0) + rightInset;
}
}
@Override
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
if (isHorizontal()) {
return getSkinnable().getInsets().getTop() + Math.max(((thumbRange != null) ? thumbRange.low.prefHeight(-1) : 1), track.prefHeight(-1))
+ ((showTickMarks) ? (TRACK_TO_TICK_GAP + tickLine.prefHeight(-1)) : 0) + bottomInset;
} else {
if (showTickMarks) {
return Math.max(140, tickLine.prefHeight(-1));
} else {
return 140;
}
}
}
@Override
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
if (isHorizontal()) {
return Double.MAX_VALUE;
} else {
return getSkinnable().prefWidth(-1);
}
}
@Override
protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
if (isHorizontal()) {
return getSkinnable().prefHeight(width);
} else {
return Double.MAX_VALUE;
}
}
private double getMaxMinusMinNoZero() {
RangeSlider<?> s = getSkinnable();
return s.getMaximum().doubleValue() - s.getMinimum().doubleValue() == 0 ? 1 : s.getMaximum().doubleValue() - s.getMinimum().doubleValue();
}
private static class ThumbPane extends StackPane {
}
private static class ThumbRange {
ThumbPane low;
ThumbPane high;
StackPane rangeBar;
ThumbRange() {
low = new ThumbPane();
low.getStyleClass().setAll("low-thumb");
low.setFocusTraversable(false);
high = new ThumbPane();
high.getStyleClass().setAll("high-thumb");
high.setFocusTraversable(false);
rangeBar = new StackPane();
rangeBar.getStyleClass().setAll("range-bar");
rangeBar.setFocusTraversable(false);
}
}
}

View File

@ -0,0 +1,78 @@
.rangeslider .low-thumb,
.rangeslider .high-thumb {
-fx-background-color:
linear-gradient(to bottom, derive(-fx-text-box-border, -20%), derive(-fx-text-box-border, -30%)),
-fx-inner-border,
-fx-body-color;
-fx-background-insets: 0, 1, 2;
-fx-background-radius: 1.0em; /* makes sure this remains circular */
-fx-padding: 0.583333em; /* 7 */
-fx-effect: dropshadow(two-pass-box , rgba(0, 0, 0, 0.1), 5, 0.0 , 0, 2);
}
.rangeslider:focused .low-thumb,
.rangeslider:focused .high-thumb {
-fx-background-radius: 1.0em; /* makes sure this remains circular */
}
.rangeslider .low-thumb:focused,
.rangeslider .high-thumb:focused {
-fx-background-color:
-fx-focus-color,
derive(-fx-color,-36%),
derive(-fx-color,73%),
linear-gradient(to bottom, derive(-fx-color,-19%),derive(-fx-color,61%));
-fx-background-insets: -1.4, 0, 1, 2;
-fx-background-radius: 1.0em; /* makes sure this remains circular */
}
.rangeslider .low-thumb:hover,
.rangeslider .high-thumb:hover {
-fx-color: -fx-hover-base;
}
.rangeslider .range-bar {
-fx-background-color: -fx-accent;
}
.rangeslider .low-thumb:pressed,
.rangeslider .high-thumb:pressed {
-fx-color: -fx-pressed-base;
}
.rangeslider .track {
-fx-background-color:
-fx-shadow-highlight-color,
linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
linear-gradient(to bottom,
derive(-fx-control-inner-background, -9%),
derive(-fx-control-inner-background, 0%),
derive(-fx-control-inner-background, -5%),
derive(-fx-control-inner-background, -12%)
);
-fx-background-insets: 0 0 -1 0, 0, 1;
-fx-background-radius: 0.25em, 0.25em, 0.166667em; /* 3 3 2 */
-fx-padding: 0.25em; /* 3 */
}
.rangeslider:vertical .track {
-fx-background-color:
-fx-shadow-highlight-color,
-fx-text-box-border,
linear-gradient(to right,
derive(-fx-control-inner-background, -9%),
-fx-control-inner-background,
derive(-fx-control-inner-background, -9%)
);
}
.rangeslider .axis {
-fx-tick-label-fill: derive(-fx-text-background-color, 30%);
-fx-tick-length: 5px;
-fx-minor-tick-length: 3px;
-fx-border-color: null;
}
.rangeslider:disabled {
-fx-opacity: 0.4;
}

View File

@ -36,6 +36,8 @@ import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.controls.Dialogs;
import ctbrec.ui.controls.DirectorySelectionBox;
import ctbrec.ui.controls.ProgramSelectionBox;
import ctbrec.ui.controls.range.DiscreteRange;
import ctbrec.ui.controls.range.RangeSlider;
import ctbrec.ui.sites.ConfigUI;
import ctbrec.ui.tabs.TabSelectionListener;
import javafx.collections.FXCollections;
@ -95,7 +97,6 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private CheckBox removeRecordingAfterPp = new CheckBox();
private RadioButton recordLocal;
private ProxySettingsPane proxySettingsPane;
private TextField maxResolution;
private TextField concurrentRecordings;
private ComboBox<SplitAfterOption> splitAfter;
private ComboBox<DirectoryStructure> directoryStructure;
@ -346,29 +347,32 @@ public class SettingsTab extends Tab implements TabSelectionListener {
GridPane.setMargin(l, new Insets(0, 0, 0, 0));
GridPane.setMargin(splitAfter, new Insets(0, 0, 0, CHECKBOX_MARGIN));
l = new Label("Maximum resolution (0 = unlimited)");
Tooltip tt = new Tooltip("video height, e.g. 720 or 1080\n!Caution: If the resolution is unknown, ctbrec will not record the stream!");
l = new Label("Restrict Resolution");
Tooltip tt = new Tooltip("Only record streams with resolution within the given range");
l.setTooltip(tt);
layout.add(l, 0, row);
maxResolution = new TextField(Integer.toString(Config.getInstance().getSettings().maximumResolution));
maxResolution.setTooltip(tt);
maxResolution.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
maxResolution.setText(newValue.replaceAll(PATTERN_NOT_A_DIGIT, ""));
}
if (!maxResolution.getText().isEmpty()) {
int newRes = Integer.parseInt(maxResolution.getText());
if (newRes != Config.getInstance().getSettings().maximumResolution) {
Config.getInstance().getSettings().maximumResolution = newRes;
saveConfig();
}
}
});
maxResolution.prefWidthProperty().bind(directoryStructure.widthProperty());
layout.add(maxResolution, 1, row++);
List<Integer> labels = Arrays.asList(0, 240, 360, 480, 600, 720, 960, 1080, 1440, 2160, 4320, 8640);
List<Integer> values = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
DiscreteRange<Integer> rangeValues = new DiscreteRange<>(values, labels);
RangeSlider<Integer> resolutionRange = new RangeSlider<>(rangeValues);
resolutionRange.prefWidthProperty().bind(directoryStructure.widthProperty());
resolutionRange.setShowTickMarks(true);
resolutionRange.setShowTickLabels(true);
resolutionRange.setLow(labels.indexOf(Config.getInstance().getSettings().minimumResolution));
resolutionRange.setHigh(labels.indexOf(Config.getInstance().getSettings().maximumResolution));
layout.add(resolutionRange, 1, row++);
GridPane.setMargin(l, new Insets(0, 0, 0, 0));
GridPane.setColumnSpan(maxResolution, 3);
GridPane.setMargin(maxResolution, new Insets(0, 0, 0, CHECKBOX_MARGIN));
GridPane.setColumnSpan(resolutionRange, 3);
GridPane.setMargin(resolutionRange, new Insets(0, 0, 0, CHECKBOX_MARGIN));
resolutionRange.getLow().addListener((obs, oldV, newV) -> {
Config.getInstance().getSettings().minimumResolution = labels.get(newV.intValue());
saveConfig();
});
resolutionRange.getHigh().addListener((obs, oldV, newV) -> {
Config.getInstance().getSettings().maximumResolution = labels.get(newV.intValue());
saveConfig();
});
l = new Label("Concurrent Recordings (0 = unlimited)");
layout.add(l, 0, row);
@ -626,12 +630,6 @@ public class SettingsTab extends Tab implements TabSelectionListener {
layout.add(mediaPlayer, 1, row);
Button mediaPlayerParamsButton = new Button("");
mediaPlayerParamsButton.setOnAction(e -> {
// Optional<String> playerParams = Dialogs.showTextInput(mediaPlayerParamsButton.getScene(), "Media Player Parameters",
// "Media Player Start Parameters", settings.mediaPlayerParams);
// playerParams.ifPresent(p -> {
// settings.mediaPlayerParams = p;
// saveConfig();
// });
PlayerSettingsDialog dialog = new PlayerSettingsDialog(getTabPane().getScene(), Config.getInstance());
Optional<Exception> exception = dialog.showAndWait();
if (exception.isPresent()) {
@ -809,7 +807,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
useTLS.setDisable(local);
recordingsDirectory.setDisable(!local);
splitAfter.setDisable(!local);
maxResolution.setDisable(!local);
//maxResolution.setDisable(!local);
directoryStructure.setDisable(!local);
onlineCheckIntervalInSecs.setDisable(!local);
leaveSpaceOnDevice.setDisable(!local);

View File

@ -69,6 +69,7 @@ public class Settings {
public String livejasminUsername = "";
public boolean livePreviews = false;
public boolean localRecording = true;
public int minimumResolution = 0;
public int maximumResolution = 0;
public int maximumResolutionPlayer = 0;
public String mediaPlayer = "/usr/bin/mpv";

View File

@ -336,10 +336,11 @@ public class DashDownload extends AbstractDownload {
private AdaptationSetType chooseBestVideo(List<AdaptationSetType> videoStreams) {
AdaptationSetType best = null;
int minHeight = config.getSettings().minimumResolution;
int maxHeight = config.getSettings().maximumResolution;
long bestHeight = 0;
for (AdaptationSetType stream : videoStreams) {
if (stream.getHeight() > bestHeight && (maxHeight == 0 || stream.getHeight() <= maxHeight)) {
if (stream.getHeight() > bestHeight && (minHeight == 0 || stream.getHeight() >= minHeight) && (maxHeight == 0 || stream.getHeight() <= maxHeight)) {
bestHeight = stream.getHeight();
best = stream;
}

View File

@ -10,7 +10,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -22,6 +21,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBException;
@ -161,21 +161,18 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
url = streamSources.get(model.getStreamUrlIndex()).getMediaPlaylistUrl();
} else {
// filter out stream resolutions, which are too high
int minRes = Config.getInstance().getSettings().minimumResolution;
int maxRes = Config.getInstance().getSettings().maximumResolution;
if (maxRes > 0) {
for (Iterator<StreamSource> iterator = streamSources.iterator(); iterator.hasNext();) {
StreamSource streamSource = iterator.next();
if (streamSource.height > 0 && maxRes < streamSource.height) {
LOG.trace("Res too high {} > {}", streamSource.height, maxRes);
iterator.remove();
}
}
}
if (streamSources.isEmpty()) {
List<StreamSource> filteredStreamSources = streamSources.stream()
.filter(src -> src.height == 0 || minRes <= src.height)
.filter(src -> src.height == 0 || maxRes >= src.height)
.collect(Collectors.toList());
if (filteredStreamSources.isEmpty()) {
throw new ExecutionException(new RuntimeException("No stream left in playlist"));
} else {
LOG.debug("{} selected {}", model.getName(), streamSources.get(streamSources.size() - 1));
url = streamSources.get(streamSources.size() - 1).getMediaPlaylistUrl();
LOG.debug("{} selected {}", model.getName(), filteredStreamSources.get(filteredStreamSources.size() - 1));
url = filteredStreamSources.get(filteredStreamSources.size() - 1).getMediaPlaylistUrl();
}
}
LOG.debug("Segment playlist url {}", url);

View File

@ -55,6 +55,7 @@ public class ConfigServlet extends AbstractCtbrecServlet {
addParameter("httpUserAgent", "User-Agent", DataType.STRING, settings.httpUserAgent, json);
addParameter("httpUserAgentMobile", "Mobile User-Agent", DataType.STRING, settings.httpUserAgentMobile, json);
addParameter("generatePlaylist", "Generate Playlist", DataType.BOOLEAN, settings.generatePlaylist, json);
addParameter("minimumResolution", "Minimum Resolution", DataType.INTEGER, settings.minimumResolution, json);
addParameter("maximumResolution", "Maximum Resolution", DataType.INTEGER, settings.maximumResolution, json);
addParameter("minimumLengthInSeconds", "Minimum Length (secs)", DataType.INTEGER, settings.minimumLengthInSeconds, json);
addParameter("minimumSpaceLeftInBytes", "Leave Space On Device (GiB)", DataType.LONG, settings.minimumSpaceLeftInBytes, json);