forked from j62/ctbrec
1
0
Fork 0

Move HLS related classes ot own package

This commit is contained in:
0xboobface 2019-11-30 16:40:01 +01:00
parent 40c910bd5c
commit 00ea43c8b3
26 changed files with 357 additions and 250 deletions

View File

@ -44,6 +44,7 @@ import ctbrec.sites.chaturbate.Chaturbate;
import ctbrec.sites.fc2live.Fc2Live; import ctbrec.sites.fc2live.Fc2Live;
import ctbrec.sites.flirt4free.Flirt4Free; import ctbrec.sites.flirt4free.Flirt4Free;
import ctbrec.sites.jasmin.LiveJasmin; import ctbrec.sites.jasmin.LiveJasmin;
import ctbrec.sites.mfc.MyFreeCams;
import ctbrec.sites.streamate.Streamate; import ctbrec.sites.streamate.Streamate;
import ctbrec.ui.news.NewsTab; import ctbrec.ui.news.NewsTab;
import ctbrec.ui.settings.SettingsTab; import ctbrec.ui.settings.SettingsTab;
@ -88,7 +89,7 @@ public class CamrecApplication extends Application {
sites.add(new Fc2Live()); sites.add(new Fc2Live());
sites.add(new Flirt4Free()); sites.add(new Flirt4Free());
sites.add(new LiveJasmin()); sites.add(new LiveJasmin());
//sites.add(new MyFreeCams()); sites.add(new MyFreeCams());
sites.add(new Streamate()); sites.add(new Streamate());
loadConfig(); loadConfig();
registerAlertSystem(); registerAlertSystem();

View File

@ -38,7 +38,7 @@ import ctbrec.Recording;
import ctbrec.Recording.State; import ctbrec.Recording.State;
import ctbrec.StringUtil; import ctbrec.StringUtil;
import ctbrec.recorder.Recorder; import ctbrec.recorder.Recorder;
import ctbrec.recorder.download.MergedHlsDownload; import ctbrec.recorder.download.hls.MergedHlsDownload;
import ctbrec.sites.Site; import ctbrec.sites.Site;
import ctbrec.ui.controls.Toast; import ctbrec.ui.controls.Toast;
import javafx.application.Platform; import javafx.application.Platform;

View File

@ -536,9 +536,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
contextMenu.getItems().addAll(copyUrl, ignore); contextMenu.getItems().addAll(copyUrl, ignore);
if(cell.getModel() instanceof MyFreeCamsModel && Objects.equals(System.getenv("CTBREC_DEV"), "1")) { if(cell.getModel() instanceof MyFreeCamsModel && Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
MenuItem debug = new MenuItem("debug"); MenuItem debug = new MenuItem("debug");
debug.setOnAction((e) -> { debug.setOnAction(e -> MyFreeCamsClient.getInstance().getSessionState(cell.getModel()));
MyFreeCamsClient.getInstance().getSessionState(cell.getModel());
});
contextMenu.getItems().add(debug); contextMenu.getItems().add(debug);
} }
return contextMenu; return contextMenu;

View File

@ -26,7 +26,7 @@ import okhttp3.Response;
public class CamsodaUpdateService extends PaginatedScheduledService { public class CamsodaUpdateService extends PaginatedScheduledService {
private static final transient Logger LOG = LoggerFactory.getLogger(CamsodaUpdateService.class); private static final Logger LOG = LoggerFactory.getLogger(CamsodaUpdateService.class);
private String url; private String url;
private boolean loginRequired; private boolean loginRequired;
@ -66,7 +66,6 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
if(result.has("tpl")) { if(result.has("tpl")) {
JSONArray tpl = result.getJSONArray("tpl"); JSONArray tpl = result.getJSONArray("tpl");
String name = tpl.getString(getTemplateIndex(template, "username")); String name = tpl.getString(getTemplateIndex(template, "username"));
String displayName = tpl.getString(getTemplateIndex(template, "display_name"));
// int connections = tpl.getInt(2); // int connections = tpl.getInt(2);
String streamName = tpl.getString(getTemplateIndex(template, "stream_name")); String streamName = tpl.getString(getTemplateIndex(template, "stream_name"));
CamsodaModel model = (CamsodaModel) camsoda.createModel(name); CamsodaModel model = (CamsodaModel) camsoda.createModel(name);
@ -76,7 +75,11 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
model.setPreview(preview); model.setPreview(preview);
JSONArray edgeServers = tpl.getJSONArray(getTemplateIndex(template, "edge_servers")); JSONArray edgeServers = tpl.getJSONArray(getTemplateIndex(template, "edge_servers"));
model.setStreamUrl("https://" + edgeServers.getString(0) + "/cam/mp4:" + streamName + "_h264_aac_480p/playlist.m3u8"); model.setStreamUrl("https://" + edgeServers.getString(0) + "/cam/mp4:" + streamName + "_h264_aac_480p/playlist.m3u8");
model.setDisplayName(displayName); String displayName = tpl.getString(getTemplateIndex(template, "display_name"));
model.setDisplayName(displayName.replaceAll("[^a-zA-Z0-9]", ""));
if(model.getDisplayName().isBlank()) {
model.setDisplayName(name);
}
models.add(model); models.add(model);
} else { } else {
String name = result.getString("username"); String name = result.getString("username");
@ -89,7 +92,10 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
} }
if(result.has("display_name")) { if(result.has("display_name")) {
model.setDisplayName(result.getString("display_name")); model.setDisplayName(result.getString("display_name").replaceAll("[^a-zA-Z0-9]", ""));
if(model.getDisplayName().isBlank()) {
model.setDisplayName(name);
}
} }
if(result.has("edge_servers")) { if(result.has("edge_servers")) {
@ -103,7 +109,7 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
} }
} }
} catch (Exception e) { } catch (Exception e) {
LOG.warn("Couldn't parse one of the models: {}", result.toString(), e); LOG.warn("Couldn't parse one of the models: {}", result, e);
} }
} }
return models.stream() return models.stream()
@ -112,7 +118,7 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
.limit(modelsPerPage) .limit(modelsPerPage)
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
LOG.debug("Response was not successful: {}", json.toString()); LOG.debug("Response was not successful: {}", json);
return Collections.emptyList(); return Collections.emptyList();
} }
} else { } else {

View File

@ -1,5 +1,6 @@
package ctbrec.ui.sites.myfreecams; package ctbrec.ui.sites.myfreecams;
import static java.nio.charset.StandardCharsets.*;
import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardOpenOption.*;
import java.io.File; import java.io.File;
@ -28,17 +29,23 @@ import ctbrec.Config;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.StringUtil; import ctbrec.StringUtil;
import ctbrec.sites.mfc.MyFreeCams; import ctbrec.sites.mfc.MyFreeCams;
import ctbrec.sites.mfc.MyFreeCamsClient;
import ctbrec.sites.mfc.MyFreeCamsModel; import ctbrec.sites.mfc.MyFreeCamsModel;
import ctbrec.sites.mfc.SessionState; import ctbrec.sites.mfc.SessionState;
import ctbrec.sites.mfc.User;
import ctbrec.ui.DesktopIntegration; import ctbrec.ui.DesktopIntegration;
import ctbrec.ui.TabSelectionListener; import ctbrec.ui.TabSelectionListener;
import ctbrec.ui.action.FollowAction; import ctbrec.ui.action.FollowAction;
import ctbrec.ui.action.PlayAction; import ctbrec.ui.action.PlayAction;
import ctbrec.ui.action.StartRecordingAction; import ctbrec.ui.action.StartRecordingAction;
import ctbrec.ui.controls.SearchBox; import ctbrec.ui.controls.SearchBox;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -73,9 +80,9 @@ import javafx.stage.FileChooser;
import javafx.util.Duration; import javafx.util.Duration;
public class MyFreeCamsTableTab extends Tab implements TabSelectionListener { public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCamsTableTab.class); private static final Logger LOG = LoggerFactory.getLogger(MyFreeCamsTableTab.class);
private ScrollPane scrollPane = new ScrollPane(); private ScrollPane scrollPane = new ScrollPane();
private TableView<ModelTableRow> table = new TableView<ModelTableRow>(); private TableView<ModelTableRow> table = new TableView<>();
private ObservableList<ModelTableRow> filteredModels = FXCollections.observableArrayList(); private ObservableList<ModelTableRow> filteredModels = FXCollections.observableArrayList();
private ObservableList<ModelTableRow> observableModels = FXCollections.observableArrayList(); private ObservableList<ModelTableRow> observableModels = FXCollections.observableArrayList();
private TableUpdateService updateService; private TableUpdateService updateService;
@ -102,9 +109,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
updateService = new TableUpdateService(mfc); updateService = new TableUpdateService(mfc);
updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(1))); updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(1)));
updateService.setOnSucceeded(this::onSuccess); updateService.setOnSucceeded(this::onSuccess);
updateService.setOnFailed((event) -> { updateService.setOnFailed(event -> LOG.info("Couldn't update MyFreeCams model table", event.getSource().getException()));
LOG.info("Couldn't update MyFreeCams model table", event.getSource().getException());
});
} }
private void onSuccess(WorkerStateEvent evt) { private void onSuccess(WorkerStateEvent evt) {
@ -202,6 +207,10 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
}); });
int idx = 0; int idx = 0;
TableColumn<ModelTableRow, Number> uid = createTableColumn("UID", 65, idx++);
uid.setCellValueFactory(cdf -> cdf.getValue().uidProperty());
addTableColumnIfEnabled(uid);
TableColumn<ModelTableRow, String> name = createTableColumn("Name", 200, idx++); TableColumn<ModelTableRow, String> name = createTableColumn("Name", 200, idx++);
name.setCellValueFactory(cdf -> cdf.getValue().nameProperty()); name.setCellValueFactory(cdf -> cdf.getValue().nameProperty());
addTableColumnIfEnabled(name); addTableColumnIfEnabled(name);
@ -210,6 +219,14 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
state.setCellValueFactory(cdf -> cdf.getValue().stateProperty()); state.setCellValueFactory(cdf -> cdf.getValue().stateProperty());
addTableColumnIfEnabled(state); addTableColumnIfEnabled(state);
TableColumn<ModelTableRow, Boolean> hd = createTableColumn("HD", 130, idx++);
hd.setCellValueFactory(cdf -> cdf.getValue().hdProperty());
addTableColumnIfEnabled(hd);
TableColumn<ModelTableRow, Number> flags = createTableColumn("Flags", 75, idx++);
flags.setCellValueFactory(cdf -> cdf.getValue().flagsProperty());
addTableColumnIfEnabled(flags);
TableColumn<ModelTableRow, Number> camscore = createTableColumn("Score", 75, idx++); TableColumn<ModelTableRow, Number> camscore = createTableColumn("Score", 75, idx++);
camscore.setCellValueFactory(cdf -> cdf.getValue().camScoreProperty()); camscore.setCellValueFactory(cdf -> cdf.getValue().camScoreProperty());
addTableColumnIfEnabled(camscore); addTableColumnIfEnabled(camscore);
@ -250,7 +267,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
blurp.setCellValueFactory(cdf -> cdf.getValue().blurpProperty()); blurp.setCellValueFactory(cdf -> cdf.getValue().blurpProperty());
addTableColumnIfEnabled(blurp); addTableColumnIfEnabled(blurp);
TableColumn<ModelTableRow, String> topic = createTableColumn("Topic", 600, idx++); TableColumn<ModelTableRow, String> topic = createTableColumn("Topic", 600, idx);
topic.setCellValueFactory(cdf -> cdf.getValue().topicProperty()); topic.setCellValueFactory(cdf -> cdf.getValue().topicProperty());
addTableColumnIfEnabled(topic); addTableColumnIfEnabled(topic);
@ -278,7 +295,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
} }
MenuItem copyUrl = new MenuItem("Copy URL"); MenuItem copyUrl = new MenuItem("Copy URL");
copyUrl.setOnAction((e) -> { copyUrl.setOnAction(e -> {
Model selected = selectedModels.get(0); Model selected = selectedModels.get(0);
final Clipboard clipboard = Clipboard.getSystemClipboard(); final Clipboard clipboard = Clipboard.getSystemClipboard();
final ClipboardContent content = new ClipboardContent(); final ClipboardContent content = new ClipboardContent();
@ -287,13 +304,13 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
}); });
MenuItem startRecording = new MenuItem("Start Recording"); MenuItem startRecording = new MenuItem("Start Recording");
startRecording.setOnAction((e) -> startRecording(selectedModels)); startRecording.setOnAction(e -> startRecording(selectedModels));
MenuItem openInBrowser = new MenuItem("Open in Browser"); MenuItem openInBrowser = new MenuItem("Open in Browser");
openInBrowser.setOnAction((e) -> DesktopIntegration.open(selectedModels.get(0).getUrl())); openInBrowser.setOnAction(e -> DesktopIntegration.open(selectedModels.get(0).getUrl()));
MenuItem openInPlayer = new MenuItem("Open in Player"); MenuItem openInPlayer = new MenuItem("Open in Player");
openInPlayer.setOnAction((e) -> openInPlayer(selectedModels.get(0))); openInPlayer.setOnAction(e -> openInPlayer(selectedModels.get(0)));
MenuItem follow = new MenuItem("Follow"); MenuItem follow = new MenuItem("Follow");
follow.setOnAction((e) -> new FollowAction(getTabPane(), selectedModels).execute()); follow.setOnAction(e -> new FollowAction(getTabPane(), selectedModels).execute());
ContextMenu menu = new ContextMenu(); ContextMenu menu = new ContextMenu();
menu.getItems().addAll(startRecording, copyUrl, openInPlayer, openInBrowser, follow); menu.getItems().addAll(startRecording, copyUrl, openInPlayer, openInBrowser, follow);
@ -304,6 +321,12 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
openInBrowser.setDisable(true); openInBrowser.setDisable(true);
} }
if(Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
MenuItem debug = new MenuItem("debug");
debug.setOnAction(e -> MyFreeCamsClient.getInstance().getSessionState(selectedModels.get(0)));
menu.getItems().add(debug);
}
return menu; return menu;
} }
@ -392,8 +415,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
ps.println(); ps.println();
} }
} catch (Exception e) { } catch (Exception e) {
LOG.debug("Couldn't write mfc models table data: {}", e.getMessage()); LOG.debug("Couldn't write mfc models table data", e);
e.printStackTrace();
} }
} }
} }
@ -488,7 +510,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
data.put(model); data.put(model);
} }
File file = new File(Config.getInstance().getConfigDir(), "mfc-models.json"); File file = new File(Config.getInstance().getConfigDir(), "mfc-models.json");
Files.write(file.toPath(), data.toString(2).getBytes("utf-8"), CREATE, WRITE); Files.write(file.toPath(), data.toString(2).getBytes(UTF_8), CREATE, WRITE);
} catch (Exception e) { } catch (Exception e) {
LOG.debug("Couldn't write mfc models table data: {}", e.getMessage()); LOG.debug("Couldn't write mfc models table data: {}", e.getMessage());
} }
@ -519,7 +541,9 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
row.tags.set(model.optString("tags")); row.tags.set(model.optString("tags"));
row.topic.set(model.optString("topic")); row.topic.set(model.optString("topic"));
observableModels.add(row); observableModels.add(row);
} catch (Exception e) {} } catch (Exception e) {
// ignore this error
}
} }
} catch (Exception e) { } catch (Exception e) {
LOG.debug("Couldn't read mfc models table data: {}", e.getMessage()); LOG.debug("Couldn't read mfc models table data: {}", e.getMessage());
@ -537,7 +561,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
columnWidths[i] = table.getColumns().get(i).getWidth(); columnWidths[i] = table.getColumns().get(i).getWidth();
} }
Config.getInstance().getSettings().mfcModelsTableColumnWidths = columnWidths; Config.getInstance().getSettings().mfcModelsTableColumnWidths = columnWidths;
}; }
private void restoreState() { private void restoreState() {
String sortCol = Config.getInstance().getSettings().mfcModelsTableSortColumn; String sortCol = Config.getInstance().getSettings().mfcModelsTableSortColumn;
@ -563,12 +587,7 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
} }
private ListChangeListener<TableColumn<ModelTableRow, ?>> createSortOrderChangedListener() { private ListChangeListener<TableColumn<ModelTableRow, ?>> createSortOrderChangedListener() {
return new ListChangeListener<TableColumn<ModelTableRow, ?>>() { return c -> saveState();
@Override
public void onChanged(Change<? extends TableColumn<ModelTableRow, ?>> c) {
saveState();
}
};
} }
private static class ModelTableRow { private static class ModelTableRow {
@ -584,6 +603,9 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
private StringProperty tags = new SimpleStringProperty(); private StringProperty tags = new SimpleStringProperty();
private StringProperty blurp = new SimpleStringProperty(); private StringProperty blurp = new SimpleStringProperty();
private StringProperty topic = new SimpleStringProperty(); private StringProperty topic = new SimpleStringProperty();
private BooleanProperty isHd = new SimpleBooleanProperty();
private SimpleIntegerProperty uidProperty = new SimpleIntegerProperty();
private SimpleIntegerProperty flagsProperty = new SimpleIntegerProperty();
public ModelTableRow(SessionState st) { public ModelTableRow(SessionState st) {
update(st); update(st);
@ -594,18 +616,23 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
public void update(SessionState st) { public void update(SessionState st) {
uid = st.getUid(); uid = st.getUid();
uidProperty.set(uid);
setProperty(name, Optional.ofNullable(st.getNm())); setProperty(name, Optional.ofNullable(st.getNm()));
setProperty(state, Optional.ofNullable(st.getVs()).map(vs -> ctbrec.sites.mfc.State.of(vs).toString())); setProperty(state, Optional.ofNullable(st.getVs()).map(vs -> ctbrec.sites.mfc.State.of(vs).toString()));
setProperty(camScore, Optional.ofNullable(st.getM()).map(m -> m.getCamscore())); setProperty(camScore, Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getCamscore));
Optional<Integer> isNew = Optional.ofNullable(st.getM()).map(m -> m.getNewModel()); Optional<Integer> isNew = Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getNewModel);
if(isNew.isPresent()) { if(isNew.isPresent()) {
newModel.set(isNew.get() == 1 ? "new" : ""); newModel.set(isNew.get() == 1 ? "new" : "");
} }
setProperty(ethnic, Optional.ofNullable(st.getU()).map(u -> u.getEthnic())); setProperty(ethnic, Optional.ofNullable(st.getU()).map(User::getEthnic));
setProperty(country, Optional.ofNullable(st.getU()).map(u -> u.getCountry())); setProperty(country, Optional.ofNullable(st.getU()).map(User::getCountry));
setProperty(continent, Optional.ofNullable(st.getM()).map(m -> m.getContinent())); setProperty(continent, Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getContinent));
setProperty(occupation, Optional.ofNullable(st.getU()).map(u -> u.getOccupation())); setProperty(occupation, Optional.ofNullable(st.getU()).map(User::getOccupation));
Set<String> tagSet = Optional.ofNullable(st.getM()).map(m -> m.getTags()).orElse(Collections.emptySet()); int flags = Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getFlags).orElse(0);
//isHd.set((flags & 1024) == 1024);
isHd.set(Optional.ofNullable(st.getU()).map(User::getPhase).orElse("z").equalsIgnoreCase("a"));
flagsProperty.setValue(flags);
Set<String> tagSet = Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getTags).orElse(Collections.emptySet());
if(!tagSet.isEmpty()) { if(!tagSet.isEmpty()) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (String t : tagSet) { for (String t : tagSet) {
@ -613,8 +640,8 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
} }
tags.set(sb.substring(0, sb.length()-2)); tags.set(sb.substring(0, sb.length()-2));
} }
setProperty(blurp, Optional.ofNullable(st.getU()).map(u -> u.getBlurb())); setProperty(blurp, Optional.ofNullable(st.getU()).map(User::getBlurb));
String tpc = Optional.ofNullable(st.getM()).map(m -> m.getTopic()).orElse("n/a"); String tpc = Optional.ofNullable(st.getM()).map(ctbrec.sites.mfc.Model::getTopic).orElse("n/a");
try { try {
tpc = URLDecoder.decode(tpc, "utf-8"); tpc = URLDecoder.decode(tpc, "utf-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
@ -631,48 +658,60 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
public StringProperty nameProperty() { public StringProperty nameProperty() {
return name; return name;
}; }
public StringProperty stateProperty() { public StringProperty stateProperty() {
return state; return state;
}; }
public DoubleProperty camScoreProperty() { public DoubleProperty camScoreProperty() {
return camScore; return camScore;
}; }
public StringProperty newModelProperty() { public StringProperty newModelProperty() {
return newModel; return newModel;
}; }
public StringProperty ethnicityProperty() { public StringProperty ethnicityProperty() {
return ethnic; return ethnic;
}; }
public StringProperty countryProperty() { public StringProperty countryProperty() {
return country; return country;
}; }
public StringProperty continentProperty() { public StringProperty continentProperty() {
return continent; return continent;
}; }
public StringProperty occupationProperty() { public StringProperty occupationProperty() {
return occupation; return occupation;
}; }
public StringProperty tagsProperty() { public StringProperty tagsProperty() {
return tags; return tags;
}; }
public StringProperty blurpProperty() { public StringProperty blurpProperty() {
return blurp; return blurp;
}; }
public StringProperty topicProperty() { public StringProperty topicProperty() {
return topic; return topic;
} }
public BooleanProperty hdProperty() {
return isHd;
}
public IntegerProperty flagsProperty() {
return flagsProperty;
}
public IntegerProperty uidProperty() {
return uidProperty;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
@ -696,8 +735,6 @@ public class MyFreeCamsTableTab extends Tab implements TabSelectionListener {
} else if (!uid.equals(other.uid)) } else if (!uid.equals(other.uid))
return false; return false;
return true; return true;
}; }
} }
} }

View File

@ -71,6 +71,27 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mp4parser</groupId>
<artifactId>isoparser</artifactId>
<version>1.9.41</version>
</dependency>
<dependency>
<groupId>org.mp4parser</groupId>
<artifactId>muxer</artifactId>
<version>1.9.41</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
<scope>runtime</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -10,8 +10,8 @@ import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter; import com.squareup.moshi.JsonWriter;
import ctbrec.recorder.download.Download; import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.HlsDownload; import ctbrec.recorder.download.hls.HlsDownload;
import ctbrec.recorder.download.MergedHlsDownload; import ctbrec.recorder.download.hls.MergedHlsDownload;
import ctbrec.sites.Site; import ctbrec.sites.Site;
public abstract class AbstractModel implements Model { public abstract class AbstractModel implements Model {

View File

@ -20,7 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class MpegUtil { public class MpegUtil {
private static final transient Logger LOG = LoggerFactory.getLogger(MpegUtil.class); private static final Logger LOG = LoggerFactory.getLogger(MpegUtil.class);
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
readFile(new File("../../test-recs/ff.ts")); readFile(new File("../../test-recs/ff.ts"));
@ -49,7 +49,7 @@ public class MpegUtil {
public static _2<Integer, Demuxer> createM2TSDemuxer(FileChannelWrapper ch, TrackType targetTrack) throws IOException { public static _2<Integer, Demuxer> createM2TSDemuxer(FileChannelWrapper ch, TrackType targetTrack) throws IOException {
MTSDemuxer mts = new MTSDemuxer(ch); MTSDemuxer mts = new MTSDemuxer(ch);
Set<Integer> programs = mts.getPrograms(); Set<Integer> programs = mts.getPrograms();
if (programs.size() == 0) { if (programs.isEmpty()) {
LOG.error("The MPEG TS stream contains no programs"); LOG.error("The MPEG TS stream contains no programs");
return null; return null;
} }
@ -61,8 +61,8 @@ public class MpegUtil {
continue; continue;
} }
MPSDemuxer demuxer = new MPSDemuxer(program); MPSDemuxer demuxer = new MPSDemuxer(program);
if (targetTrack == TrackType.AUDIO && demuxer.getAudioTracks().size() > 0 if (targetTrack == TrackType.AUDIO && !demuxer.getAudioTracks().isEmpty()
|| targetTrack == TrackType.VIDEO && demuxer.getVideoTracks().size() > 0) { || targetTrack == TrackType.VIDEO && !demuxer.getVideoTracks().isEmpty()) {
found = org.jcodec.common.Tuple._2(pid, (Demuxer) demuxer); found = org.jcodec.common.Tuple._2(pid, (Demuxer) demuxer);
} else { } else {
program.close(); program.close();

View File

@ -1,16 +1,12 @@
package ctbrec; package ctbrec;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import ctbrec.event.EventBusHolder; import ctbrec.event.EventBusHolder;
import ctbrec.event.RecordingStateChangedEvent; import ctbrec.event.RecordingStateChangedEvent;
import ctbrec.recorder.download.Download; import ctbrec.recorder.download.Download;
@ -25,7 +21,7 @@ public class Recording {
private long sizeInByte = -1; private long sizeInByte = -1;
private String metaDataFile; private String metaDataFile;
public static enum State { public enum State {
RECORDING("recording"), RECORDING("recording"),
GENERATING_PLAYLIST("generating playlist"), GENERATING_PLAYLIST("generating playlist"),
POST_PROCESSING("post-processing"), POST_PROCESSING("post-processing"),
@ -49,8 +45,6 @@ public class Recording {
} }
} }
public Recording() {}
public Instant getStartDate() { public Instant getStartDate() {
return startDate; return startDate;
} }
@ -67,7 +61,7 @@ public class Recording {
this.status = status; this.status = status;
} }
public void setStatusWithEvent(State status, boolean fireEvent) { public void setStatusWithEvent(State status) {
setStatus(status); setStatus(status);
fireStatusEvent(status); fireStatusEvent(status);
} }
@ -135,7 +129,7 @@ public class Recording {
this.metaDataFile = metaDataFile; this.metaDataFile = metaDataFile;
} }
public Duration getLength() throws IOException, ParseException, PlaylistException { public Duration getLength() {
if (getDownload() != null) { if (getDownload() != null) {
return getDownload().getLength(); return getDownload().getLength();
} else { } else {
@ -197,15 +191,26 @@ public class Recording {
private long getSize() { private long getSize() {
File rec = new File(Config.getInstance().getSettings().recordingsDir, getPath()); File rec = new File(Config.getInstance().getSettings().recordingsDir, getPath());
if(rec.isDirectory()) { if(rec.isDirectory()) {
return getDirectorySize(rec);
} else {
if(!rec.exists()) {
return getDirectorySize(rec.getParentFile());
} else {
return rec.length();
}
}
}
private long getDirectorySize(File dir) {
long size = 0; long size = 0;
File[] files = rec.listFiles(); File[] files = dir.listFiles();
if (files == null) {
return 0;
}
for (File file : files) { for (File file : files) {
size += file.length(); size += file.length();
} }
return size; return size;
} else {
return rec.length();
}
} }
public void refresh() { public void refresh() {

View File

@ -7,7 +7,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class StreamRedirectThread implements Runnable { public class StreamRedirectThread implements Runnable {
private static final transient Logger LOG = LoggerFactory.getLogger(StreamRedirectThread.class); private static final Logger LOG = LoggerFactory.getLogger(StreamRedirectThread.class);
private InputStream in; private InputStream in;
private OutputStream out; private OutputStream out;

View File

@ -36,8 +36,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.Model; import ctbrec.Model;
@ -282,7 +280,7 @@ public class NextGenLocalRecorder implements Recorder {
} }
} }
private boolean deleteIfTooShort(Recording rec) throws IOException, ParseException, PlaylistException, InvalidKeyException, NoSuchAlgorithmException { private boolean deleteIfTooShort(Recording rec) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
Duration minimumLengthInSeconds = Duration.ofSeconds(Config.getInstance().getSettings().minimumLengthInSeconds); Duration minimumLengthInSeconds = Duration.ofSeconds(Config.getInstance().getSettings().minimumLengthInSeconds);
if (minimumLengthInSeconds.getSeconds() <= 0) { if (minimumLengthInSeconds.getSeconds() <= 0) {
return false; return false;

View File

@ -1,4 +1,4 @@
package ctbrec.recorder.download; package ctbrec.recorder.download.hls;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
@ -42,6 +42,8 @@ import ctbrec.UnknownModel;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.io.HttpException; import ctbrec.io.HttpException;
import ctbrec.io.StreamRedirectThread; import ctbrec.io.StreamRedirectThread;
import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.StreamSource;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;

View File

@ -1,4 +1,4 @@
package ctbrec.recorder.download; package ctbrec.recorder.download.hls;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -1,4 +1,4 @@
package ctbrec.recorder.download; package ctbrec.recorder.download.hls;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
@ -182,9 +182,9 @@ public class HlsDownload extends AbstractHlsDownload {
@Override @Override
public void postprocess(Recording recording) { public void postprocess(Recording recording) {
recording.setStatusWithEvent(State.GENERATING_PLAYLIST, true); recording.setStatusWithEvent(State.GENERATING_PLAYLIST);
generatePlaylist(recording); generatePlaylist(recording);
recording.setStatusWithEvent(State.POST_PROCESSING, true); recording.setStatusWithEvent(State.POST_PROCESSING);
super.postprocess(recording); super.postprocess(recording);
} }

View File

@ -1,4 +1,4 @@
package ctbrec.recorder.download; package ctbrec.recorder.download.hls;
import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardOpenOption.*;
@ -367,7 +367,7 @@ public class MergedHlsDownload extends AbstractHlsDownload {
Thread.sleep(1000); Thread.sleep(1000);
} }
if(!downloadFinished) { if(!downloadFinished) {
LOG.warn("Download didn't finishe properly for model {}", model); LOG.warn("Download didn't finish properly for model {}", model);
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();

View File

@ -1,4 +1,4 @@
package ctbrec.recorder.download; package ctbrec.recorder.download.hls;
import java.io.IOException; import java.io.IOException;

View File

@ -35,7 +35,7 @@ import okhttp3.Response;
public class CamsodaModel extends AbstractModel { public class CamsodaModel extends AbstractModel {
private static final transient Logger LOG = LoggerFactory.getLogger(CamsodaModel.class); private static final Logger LOG = LoggerFactory.getLogger(CamsodaModel.class);
private String streamUrl; private String streamUrl;
private List<StreamSource> streamSources = null; private List<StreamSource> streamSources = null;
private float sortOrder = 0; private float sortOrder = 0;

View File

@ -5,24 +5,17 @@ import java.io.IOException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.recorder.download.HlsDownload; import ctbrec.recorder.download.hls.HlsDownload;
public class Fc2HlsDownload extends HlsDownload { public class Fc2HlsDownload extends HlsDownload {
private static final transient Logger LOG = LoggerFactory.getLogger(Fc2HlsDownload.class); private static final Logger LOG = LoggerFactory.getLogger(Fc2HlsDownload.class);
public Fc2HlsDownload(HttpClient client) { public Fc2HlsDownload(HttpClient client) {
super(client); super(client);
} }
@Override
public void init(Config config, Model model) {
super.init(config, model);
}
@Override @Override
public void start() throws IOException { public void start() throws IOException {
Fc2Model fc2Model = (Fc2Model) model; Fc2Model fc2Model = (Fc2Model) model;
@ -30,14 +23,10 @@ public class Fc2HlsDownload extends HlsDownload {
fc2Model.openWebsocket(); fc2Model.openWebsocket();
super.start(); super.start();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Couldn't start download for {}", model, e); LOG.error("Couldn't start download for {}", model, e);
} finally { } finally {
fc2Model.closeWebsocket(); fc2Model.closeWebsocket();
} }
} }
@Override
public void stop() {
super.stop();
}
} }

View File

@ -5,24 +5,17 @@ import java.io.IOException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.recorder.download.MergedHlsDownload; import ctbrec.recorder.download.hls.MergedHlsDownload;
public class Fc2MergedHlsDownload extends MergedHlsDownload { public class Fc2MergedHlsDownload extends MergedHlsDownload {
private static final transient Logger LOG = LoggerFactory.getLogger(Fc2MergedHlsDownload.class); private static final Logger LOG = LoggerFactory.getLogger(Fc2MergedHlsDownload.class);
public Fc2MergedHlsDownload(HttpClient client) { public Fc2MergedHlsDownload(HttpClient client) {
super(client); super(client);
} }
@Override
public void init(Config config, Model model) {
super.init(config, model);
}
@Override @Override
public void start() throws IOException { public void start() throws IOException {
Fc2Model fc2Model = (Fc2Model) model; Fc2Model fc2Model = (Fc2Model) model;
@ -30,14 +23,10 @@ public class Fc2MergedHlsDownload extends MergedHlsDownload {
fc2Model.openWebsocket(); fc2Model.openWebsocket();
super.start(); super.start();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Couldn't start download for {}", model, e); LOG.error("Couldn't start download for {}", model, e);
} finally { } finally {
fc2Model.closeWebsocket(); fc2Model.closeWebsocket();
} }
} }
@Override
public void stop() {
super.stop();
}
} }

View File

@ -41,7 +41,7 @@ import okhttp3.WebSocketListener;
import okio.ByteString; import okio.ByteString;
public class Fc2Model extends AbstractModel { public class Fc2Model extends AbstractModel {
private static final transient Logger LOG = LoggerFactory.getLogger(Fc2Model.class); private static final Logger LOG = LoggerFactory.getLogger(Fc2Model.class);
private String id; private String id;
private int viewerCount; private int viewerCount;
private boolean online; private boolean online;
@ -95,7 +95,6 @@ public class Fc2Model extends AbstractModel {
setName(profileData.getString("name").replace('/', '_')); setName(profileData.getString("name").replace('/', '_'));
} }
} else { } else {
resp.close();
throw new IOException("HTTP status " + resp.code() + " " + resp.message()); throw new IOException("HTTP status " + resp.code() + " " + resp.message());
} }
} }
@ -120,6 +119,7 @@ public class Fc2Model extends AbstractModel {
sources.addAll(parseMasterPlaylist(playlistUrl)); sources.addAll(parseMasterPlaylist(playlistUrl));
return sources; return sources;
} catch (InterruptedException e1) { } catch (InterruptedException e1) {
Thread.currentThread().interrupt();
throw new ExecutionException(e1); throw new ExecutionException(e1);
} finally { } finally {
closeWebsocket(); closeWebsocket();
@ -205,10 +205,12 @@ public class Fc2Model extends AbstractModel {
@Override @Override
public void invalidateCacheEntries() { public void invalidateCacheEntries() {
// not needed
} }
@Override @Override
public void receiveTip(Double tokens) throws IOException { public void receiveTip(Double tokens) throws IOException {
// tipping is not implemented for FC2
} }
@Override @Override
@ -247,7 +249,6 @@ public class Fc2Model extends AbstractModel {
JSONObject json = new JSONObject(content); JSONObject json = new JSONObject(content);
return json.optInt("status") == 1; return json.optInt("status") == 1;
} else { } else {
resp.close();
LOG.error("Login failed {} {}", resp.code(), resp.message()); LOG.error("Login failed {} {}", resp.code(), resp.message());
return false; return false;
} }
@ -316,7 +317,7 @@ public class Fc2Model extends AbstractModel {
playlistUrl = playlist.getString("url"); playlistUrl = playlist.getString("url");
LOG.debug("Master Playlist: {}", playlistUrl); LOG.debug("Master Playlist: {}", playlistUrl);
synchronized (monitor) { synchronized (monitor) {
monitor.notify(); monitor.notifyAll();
} }
} else { } else {
LOG.trace(json.toString()); LOG.trace(json.toString());
@ -340,7 +341,7 @@ public class Fc2Model extends AbstractModel {
@Override @Override
public void onMessage(WebSocket webSocket, ByteString bytes) { public void onMessage(WebSocket webSocket, ByteString bytes) {
LOG.debug("ws btxt {}", bytes.toString()); LOG.debug("ws btxt {}", bytes);
} }
@Override @Override
@ -397,6 +398,6 @@ public class Fc2Model extends AbstractModel {
@Override @Override
public String getSanitizedNamed() { public String getSanitizedNamed() {
return id; return getId();
} }
} }

View File

@ -11,7 +11,7 @@ import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException; import com.iheartradio.m3u8.PlaylistException;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.recorder.download.HlsDownload; import ctbrec.recorder.download.hls.HlsDownload;
public class LiveJasminHlsDownload extends HlsDownload { public class LiveJasminHlsDownload extends HlsDownload {

View File

@ -11,7 +11,7 @@ import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException; import com.iheartradio.m3u8.PlaylistException;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.recorder.download.MergedHlsDownload; import ctbrec.recorder.download.hls.MergedHlsDownload;
public class LiveJasminMergedHlsDownload extends MergedHlsDownload { public class LiveJasminMergedHlsDownload extends MergedHlsDownload {

View File

@ -17,6 +17,7 @@ import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Queue; import java.util.Queue;
import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -43,7 +44,9 @@ import okio.ByteString;
public class MyFreeCamsClient { public class MyFreeCamsClient {
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCamsClient.class); private static final String HTTPS = "https://";
private static final Logger LOG = LoggerFactory.getLogger(MyFreeCamsClient.class);
private static MyFreeCamsClient instance; private static MyFreeCamsClient instance;
private MyFreeCams mfc; private MyFreeCams mfc;
@ -87,13 +90,14 @@ public class MyFreeCamsClient {
public void start() throws IOException { public void start() throws IOException {
running = true; running = true;
serverConfig = new ServerConfig(mfc); serverConfig = new ServerConfig(mfc);
List<String> websocketServers = new ArrayList<String>(serverConfig.wsServers.size()); List<String> websocketServers = new ArrayList<>(serverConfig.wsServers.size());
for (Entry<String, String> entry : serverConfig.wsServers.entrySet()) { for (Entry<String, String> entry : serverConfig.wsServers.entrySet()) {
if (entry.getValue().equals("rfc6455")) { if (entry.getValue().equals("rfc6455")) {
websocketServers.add(entry.getKey()); websocketServers.add(entry.getKey());
} }
} }
String server = websocketServers.get((int) (Math.random() * websocketServers.size() - 1));
String server = websocketServers.get(new Random().nextInt(websocketServers.size()-1));
String wsUrl = "ws://" + server + ".myfreecams.com:8080/fcsl"; String wsUrl = "ws://" + server + ".myfreecams.com:8080/fcsl";
LOG.debug("Connecting to random websocket server {}", wsUrl); LOG.debug("Connecting to random websocket server {}", wsUrl);
@ -101,16 +105,14 @@ public class MyFreeCamsClient {
while (running) { while (running) {
if (ws == null && !connecting) { if (ws == null && !connecting) {
LOG.info("Websocket is null. Starting a new connection"); LOG.info("Websocket is null. Starting a new connection");
Request req = new Request.Builder() Request req = new Request.Builder().url(wsUrl).addHeader("Origin", "http://m.myfreecams.com").build();
.url(wsUrl)
.addHeader("Origin", "http://m.myfreecams.com")
.build();
ws = createWebSocket(req); ws = createWebSocket(req);
} }
try { try {
Thread.sleep(10000); Thread.sleep(10000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("WatchDog couldn't sleep", e); LOG.error("WatchDog couldn't sleep", e);
stop(); stop();
running = false; running = false;
@ -140,7 +142,7 @@ public class MyFreeCamsClient {
private WebSocket createWebSocket(Request req) { private WebSocket createWebSocket(Request req) {
connecting = true; connecting = true;
WebSocket ws = mfc.getHttpClient().newWebSocket(req, new WebSocketListener() { WebSocket websocket = mfc.getHttpClient().newWebSocket(req, new WebSocketListener() {
@Override @Override
public void onOpen(WebSocket webSocket, Response response) { public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response); super.onOpen(webSocket, response);
@ -152,12 +154,14 @@ public class MyFreeCamsClient {
webSocket.send("hello fcserver\n"); webSocket.send("hello fcserver\n");
webSocket.send("fcsws_20180422\n"); webSocket.send("fcsws_20180422\n");
// TODO find out, what the values in the json message mean, at the moment we hust send 0s, which seems to work, too // TODO find out, what the values in the json message mean, at the moment we hust send 0s, which seems to work, too
// webSocket.send("1 0 0 81 0 %7B%22err%22%3A0%2C%22start%22%3A1540159843072%2C%22stop%22%3A1540159844121%2C%22a%22%3A6392%2C%22time%22%3A1540159844%2C%22key%22%3A%228da80f985c9db390809713dac71df297%22%2C%22cid%22%3A%22c504d684%22%2C%22pid%22%3A1%2C%22site%22%3A%22www%22%7D\n"); // webSocket.send("1 0 0 81 0
webSocket.send("1 0 0 81 0 %7B%22err%22%3A0%2C%22start%22%3A0%2C%22stop%22%3A0%2C%22a%22%3A0%2C%22time%22%3A0%2C%22key%22%3A%22%22%2C%22cid%22%3A%22%22%2C%22pid%22%3A1%2C%22site%22%3A%22www%22%7D\n"); // %7B%22err%22%3A0%2C%22start%22%3A1540159843072%2C%22stop%22%3A1540159844121%2C%22a%22%3A6392%2C%22time%22%3A1540159844%2C%22key%22%3A%228da80f985c9db390809713dac71df297%22%2C%22cid%22%3A%22c504d684%22%2C%22pid%22%3A1%2C%22site%22%3A%22www%22%7D\n");
webSocket.send(
"1 0 0 81 0 %7B%22err%22%3A0%2C%22start%22%3A0%2C%22stop%22%3A0%2C%22a%22%3A0%2C%22time%22%3A0%2C%22key%22%3A%22%22%2C%22cid%22%3A%22%22%2C%22pid%22%3A1%2C%22site%22%3A%22www%22%7D\n");
heartBeat = System.currentTimeMillis(); heartBeat = System.currentTimeMillis();
startKeepAlive(webSocket); startKeepAlive(webSocket);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOG.error("Error while processing onOpen event", e);
} }
} }
@ -280,6 +284,7 @@ public class MyFreeCamsClient {
break; break;
case ROOMDATA: case ROOMDATA:
LOG.debug("ROOMDATA: {}", message); LOG.debug("ROOMDATA: {}", message);
break;
case UEOPT: case UEOPT:
LOG.trace("UEOPT: {}", message); LOG.trace("UEOPT: {}", message);
break; break;
@ -304,9 +309,9 @@ public class MyFreeCamsClient {
} }
} }
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
e.printStackTrace(); LOG.error("Error while decoding ctxenc URL", e);
} catch (Throwable t) { } catch (Exception e) {
t.printStackTrace(); LOG.error("Exception occured while processing websocket message {}", msgBuffer, e);
} }
} }
@ -367,10 +372,10 @@ public class MyFreeCamsClient {
state.getM().setRank(inner.getInt(idx++)); state.getM().setRank(inner.getInt(idx++));
state.getM().setRc(inner.getInt(idx++)); state.getM().setRc(inner.getInt(idx++));
state.getM().setTopic(inner.getString(idx++)); state.getM().setTopic(inner.getString(idx++));
state.getM().setHidecs(inner.getInt(idx++) == 1); state.getM().setHidecs(inner.getInt(idx) == 1);
updateSessionState(state); updateSessionState(state);
} catch (Exception e) { } catch (Exception e) {
LOG.warn("Couldn't parse session state {}", inner.toString()); LOG.warn("Couldn't parse session state {}", inner);
} }
} }
} else if (object.has("type") && object.getInt("type") == 20) { } else if (object.has("type") && object.getInt("type") == 20) {
@ -382,9 +387,7 @@ public class MyFreeCamsClient {
if (model != null) { if (model != null) {
model.getTags().clear(); model.getTags().clear();
JSONArray jsonTags = outer.getJSONArray(uidString); JSONArray jsonTags = outer.getJSONArray(uidString);
jsonTags.forEach((tag) -> { jsonTags.forEach(tag -> model.getTags().add((String) tag));
model.getTags().add((String) tag);
});
} }
} catch (Exception e) { } catch (Exception e) {
// fail silently // fail silently
@ -415,7 +418,8 @@ public class MyFreeCamsClient {
private void updateModel(SessionState state) { private void updateModel(SessionState state) {
// essential data not yet available // essential data not yet available
if(state.getNm() == null || state.getM() == null || state.getU() == null || state.getU().getCamserv() == null || state.getU().getCamserv() == 0) { if (state.getNm() == null || state.getM() == null || state.getU() == null || state.getU().getCamserv() == null
|| state.getU().getCamserv() == 0) {
return; return;
} }
@ -468,10 +472,8 @@ public class MyFreeCamsClient {
fout.write(string.getBytes()); fout.write(string.getBytes());
fout.write(10); fout.write(10);
} }
//System.exit(1);
} catch (Exception e1) { } catch (Exception e1) {
LOG.error("Couldn't write mfc message history to " + logfile); LOG.error("Couldn't write mfc message history to {}", logfile, e1);
e1.printStackTrace();
} }
msgBuffer.setLength(0); msgBuffer.setLength(0);
return null; return null;
@ -492,7 +494,7 @@ public class MyFreeCamsClient {
LOG.debug("msgb: {}", bytes.hex()); LOG.debug("msgb: {}", bytes.hex());
} }
}); });
return ws; return websocket;
} }
protected boolean follow(int uid) { protected boolean follow(int uid) {
@ -526,8 +528,9 @@ public class MyFreeCamsClient {
} }
Thread.sleep(TimeUnit.SECONDS.toMillis(15)); Thread.sleep(TimeUnit.SECONDS.toMillis(15));
} catch (Exception e) { } catch (InterruptedException e) {
e.printStackTrace(); Thread.currentThread().interrupt();
LOG.warn("Websocket watchdog has been interrupted");
} }
} }
}); });
@ -553,29 +556,69 @@ public class MyFreeCamsClient {
} }
public String getStreamUrl(SessionState state) { public String getStreamUrl(SessionState state) {
Integer camserv = Optional.ofNullable(state.getU()).map(u -> u.getCamserv()).orElse(-1); Integer camserv = Optional.ofNullable(state.getU()).map(User::getCamserv).orElse(-1);
if(camserv != null && camserv != -1) { String camservString = camserv.toString();
int userChannel = 100000000 + state.getUid(); if (serverConfig.isOnWzObsVideoServer(state)) {
String streamUrl = ""; camservString = serverConfig.wzobsServers.get(camserv.toString());
String phase = state.getU().getPhase() != null ? state.getU().getPhase() : "z"; } else if (serverConfig.isOnObsServer(state)) {
if(serverConfig.isOnNgServer(state)) { camservString = serverConfig.ngVideoServers.get(camserv.toString());
String server = serverConfig.ngVideoServers.get(camserv.toString());
streamUrl = "https://" + server + ".myfreecams.com:8444/x-hls/" + cxid + '/' + userChannel + '/' + ctxenc + "/mfc_" + phase + '_' + userChannel + ".m3u8";
} else if(serverConfig.isOnWzObsVideoServer(state)) {
String server = serverConfig.wzobsServers.get(camserv.toString());
streamUrl = "https://"+ server + ".myfreecams.com/NxServer/ngrp:mfc_" + phase + '_' + userChannel + ".f4v_mobile/playlist.m3u8";
} else if (serverConfig.isOnHtml5VideoServer(state)) { } else if (serverConfig.isOnHtml5VideoServer(state)) {
String server = serverConfig.h5Servers.get(camserv.toString()); camservString = serverConfig.h5Servers.get(camserv.toString());
streamUrl = "https://"+ server + ".myfreecams.com/NxServer/ngrp:mfc_" + userChannel + ".f4v_mobile/playlist.m3u8"; } else if (camserv > 500) {
} else {
if(camserv > 500) {
camserv -= 500; camserv -= 500;
camservString = camserv.toString();
}
if (serverConfig.isOnObsServer(state)) {
// techorder -> hls
} else {
// techorder -> dash hls
}
int userChannel = 100000000 + state.getUid();
String phase = state.getU().getPhase() != null ? state.getU().getPhase() : "z";
String server = "video" + camservString.replaceAll("^\\D+", "");
boolean useHls = serverConfig.isOnObsServer(state);
String streamUrl;
if (serverConfig.isOnWzObsVideoServer(state) || !serverConfig.isOnObsServer(state)) {
// wowza server
// https://video350.myfreecams.com/NxServer/ngrp:mfc_108514276.f4v_desktop/manifest_w515822224_qbmM9MC40NTQ0NTYxNjUyOTYzNDA4.mpd
if (useHls) {
streamUrl = HTTPS + server + ".myfreecams.com/NxServer/ngrp:mfc_" + userChannel + ".f4v_mobile/playlist.m3u8";
} else {
streamUrl = HTTPS + server + ".myfreecams.com/NxServer/ngrp:mfc_" + userChannel + ".f4v_desktop/manifest.mpd";
}
} else {
// nginx server
if (useHls) {
streamUrl = HTTPS + server + ".myfreecams.com:8444/x-hls/" + cxid + '/' + userChannel + '/' + ctxenc + "/mfc_" + phase + '_' + userChannel
+ ".m3u8";
} else {
streamUrl = HTTPS + server + ".myfreecams.com:8444/x-dsh/" + cxid + '/' + userChannel + '/' + ctxenc + "/mfc_" + phase + '_' + userChannel
+ ".mpd";
} }
streamUrl = "https://video" + camserv + ".myfreecams.com/NxServer/ngrp:mfc_" + userChannel + ".f4v_mobile/playlist.m3u8";
} }
return streamUrl; return streamUrl;
}
return null; // https://video848.myfreecams.com/NxServer/ngrp:mfc_117940536.f4v_desktop/manifest_w514169512_qbmM9MC40NzQwNzU4NzQxNTA0NDUyNQ==.mpd
// if(camserv != null && ) {
// int userChannel = 100000000 + state.getUid();
// String streamUrl = "";
// String phase = state.getU().getPhase() != null ? state.getU().getPhase() : "z";
// } else if(serverConfig.isOnNgServer(state)) {
// String server = serverConfig.ngVideoServers.get(camserv.toString());
// streamUrl = "https://" + server + ".myfreecams.com:8444/x-hls/" + cxid + '/' + userChannel + '/' + ctxenc + "/mfc_" + phase + '_' + userChannel +
// ".m3u8";
// } else if(serverConfig.isOnWzObsVideoServer(state)) {
// String server = serverConfig.wzobsServers.get(camserv.toString());
// streamUrl = "https://"+ server + ".myfreecams.com/NxServer/ngrp:mfc_" + phase + '_' + userChannel + ".f4v_mobile/playlist.m3u8";
// } else if(serverConfig.isOnHtml5VideoServer(state)) {
// String server = serverConfig.h5Servers.get(camserv.toString());
// streamUrl = "https://"+ server + ".myfreecams.com/NxServer/ngrp:mfc_" + userChannel + ".f4v_mobile/playlist.m3u8";
// } else {
// streamUrl = "https://video" + camserv + ".myfreecams.com/NxServer/ngrp:mfc_" + userChannel + ".f4v_mobile/playlist.m3u8";
// }
} }
public MyFreeCamsModel getModel(int uid) { public MyFreeCamsModel getModel(int uid) {
@ -591,6 +634,8 @@ public class MyFreeCamsClient {
System.out.println("H5 " + serverConfig.isOnHtml5VideoServer(state)); System.out.println("H5 " + serverConfig.isOnHtml5VideoServer(state));
System.out.println("NG " + serverConfig.isOnNgServer(state)); System.out.println("NG " + serverConfig.isOnNgServer(state));
System.out.println("WZ " + serverConfig.isOnWzObsVideoServer(state)); System.out.println("WZ " + serverConfig.isOnWzObsVideoServer(state));
System.out.println("OBS " + serverConfig.isOnObsServer(state));
System.out.println("URL: " + getStreamUrl(state));
System.out.println("#####################"); System.out.println("#####################");
} }
} }
@ -606,7 +651,7 @@ public class MyFreeCamsClient {
Object monitor = new Object(); Object monitor = new Object();
List<ctbrec.Model> result = new ArrayList<>(); List<ctbrec.Model> result = new ArrayList<>();
responseHandlers.put(msgId, msg -> { responseHandlers.put(msgId, msg -> {
LOG.debug("Search result: " + msg); LOG.debug("Search result: {}", msg);
if (StringUtil.isNotBlank(msg.getMessage()) && !Objects.equals(msg.getMessage(), q)) { if (StringUtil.isNotBlank(msg.getMessage()) && !Objects.equals(msg.getMessage(), q)) {
JSONObject json = new JSONObject(msg.getMessage()); JSONObject json = new JSONObject(msg.getMessage());
String name = json.getString("nm"); String name = json.getString("nm");
@ -620,7 +665,7 @@ public class MyFreeCamsClient {
result.add(model); result.add(model);
} }
synchronized (monitor) { synchronized (monitor) {
monitor.notify(); monitor.notifyAll();
} }
}); });
ws.send("10 " + sessionId + " 0 " + msgId + " 0 " + q + "\n"); ws.send("10 " + sessionId + " 0 " + msgId + " 0 " + q + "\n");
@ -629,12 +674,10 @@ public class MyFreeCamsClient {
} }
for (MyFreeCamsModel model : models.asMap().values()) { for (MyFreeCamsModel model : models.asMap().values()) {
if(StringUtil.isNotBlank(model.getName())) { if (StringUtil.isNotBlank(model.getName()) && model.getName().toLowerCase().contains(q.toLowerCase())) {
if(model.getName().toLowerCase().contains(q.toLowerCase())) {
result.add(model); result.add(model);
} }
} }
}
return result; return result;
} }

View File

@ -32,7 +32,9 @@ import ctbrec.AbstractModel;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.io.HtmlParser; import ctbrec.io.HtmlParser;
import ctbrec.io.HttpException; import ctbrec.io.HttpException;
import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.StreamSource; import ctbrec.recorder.download.StreamSource;
import ctbrec.recorder.download.dash.DashDownload;
import okhttp3.FormBody; import okhttp3.FormBody;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
@ -40,14 +42,14 @@ import okhttp3.Response;
public class MyFreeCamsModel extends AbstractModel { public class MyFreeCamsModel extends AbstractModel {
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCamsModel.class); private static final Logger LOG = LoggerFactory.getLogger(MyFreeCamsModel.class);
private int uid = -1; // undefined private int uid = -1; // undefined
private String hlsUrl; private String streamUrl;
private double camScore; private double camScore;
private int viewerCount; private int viewerCount;
private ctbrec.sites.mfc.State state; private ctbrec.sites.mfc.State state;
private int resolution[] = new int[2]; private int[] resolution = new int[2];
/** /**
* This constructor exists only for deserialization. Please don't call it directly * This constructor exists only for deserialization. Please don't call it directly
@ -110,7 +112,7 @@ public class MyFreeCamsModel extends AbstractModel {
src.width = Integer.MAX_VALUE; src.width = Integer.MAX_VALUE;
src.height = Integer.MAX_VALUE; src.height = Integer.MAX_VALUE;
} }
String masterUrl = hlsUrl; String masterUrl = streamUrl;
String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1); String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1);
String segmentUri = baseUrl + playlist.getUri(); String segmentUri = baseUrl + playlist.getUri();
src.mediaPlaylistUrl = segmentUri; src.mediaPlaylistUrl = segmentUri;
@ -131,8 +133,8 @@ public class MyFreeCamsModel extends AbstractModel {
if(getHlsUrl() == null) { if(getHlsUrl() == null) {
throw new IllegalStateException("Stream url unknown"); throw new IllegalStateException("Stream url unknown");
} }
LOG.trace("Loading master playlist {}", hlsUrl); LOG.trace("Loading master playlist {}", streamUrl);
Request req = new Request.Builder().url(hlsUrl).build(); Request req = new Request.Builder().url(streamUrl).build();
try(Response response = site.getHttpClient().execute(req)) { try(Response response = site.getHttpClient().execute(req)) {
if(response.isSuccessful()) { if(response.isSuccessful()) {
InputStream inputStream = response.body().byteStream(); InputStream inputStream = response.body().byteStream();
@ -147,11 +149,11 @@ public class MyFreeCamsModel extends AbstractModel {
} }
private String getHlsUrl() { private String getHlsUrl() {
if(hlsUrl == null) { if(streamUrl == null) {
MyFreeCams mfc = (MyFreeCams) getSite(); MyFreeCams mfc = (MyFreeCams) getSite();
mfc.getClient().update(this); mfc.getClient().update(this);
} }
return hlsUrl; return streamUrl;
} }
@Override @Override
@ -203,7 +205,7 @@ public class MyFreeCamsModel extends AbstractModel {
@Override @Override
public int[] getStreamResolution(boolean failFast) throws ExecutionException { public int[] getStreamResolution(boolean failFast) throws ExecutionException {
if (!failFast && hlsUrl != null) { if (!failFast && streamUrl != null) {
try { try {
List<StreamSource> streamSources = getStreamSources(); List<StreamSource> streamSources = getStreamSources();
Collections.sort(streamSources); Collections.sort(streamSources);
@ -218,12 +220,12 @@ public class MyFreeCamsModel extends AbstractModel {
return resolution; return resolution;
} }
public void setStreamUrl(String hlsUrl) { public void setStreamUrl(String streamUrl) {
this.hlsUrl = hlsUrl; this.streamUrl = streamUrl;
} }
public String getStreamUrl() { public String getStreamUrl() {
return hlsUrl; return streamUrl;
} }
public double getCamScore() { public double getCamScore() {
@ -248,17 +250,16 @@ public class MyFreeCamsModel extends AbstractModel {
} }
public void update(SessionState state, String streamUrl) { public void update(SessionState state, String streamUrl) {
uid = Integer.parseInt(state.getUid().toString()); uid = state.getUid();
setName(state.getNm()); setName(state.getNm());
setMfcState(ctbrec.sites.mfc.State.of(state.getVs())); setMfcState(ctbrec.sites.mfc.State.of(state.getVs()));
setStreamUrl(streamUrl); setStreamUrl(streamUrl);
Optional<Double> camScore = Optional.ofNullable(state.getM()).map(m -> m.getCamscore()); setCamScore(Optional.ofNullable(state.getM()).map(Model::getCamscore).orElse(0.0));
setCamScore(camScore.orElse(0.0));
// preview // preview
String uid = state.getUid().toString(); String uidString = state.getUid().toString();
String uidStart = uid.substring(0, 3); String uidStart = uidString.substring(0, 3);
String previewUrl = "https://img.mfcimg.com/photos2/"+uidStart+'/'+uid+"/avatar.300x300.jpg"; String previewUrl = "https://img.mfcimg.com/photos2/"+uidStart+'/'+uidString+"/avatar.300x300.jpg";
if(MyFreeCamsModel.this.state == ctbrec.sites.mfc.State.ONLINE) { if(MyFreeCamsModel.this.state == ctbrec.sites.mfc.State.ONLINE) {
try { try {
previewUrl = getLivePreviewUrl(state); previewUrl = getLivePreviewUrl(state);
@ -269,14 +270,14 @@ public class MyFreeCamsModel extends AbstractModel {
setPreview(previewUrl); setPreview(previewUrl);
// tags // tags
Optional.ofNullable(state.getM()).map((m) -> m.getTags()).ifPresent((tags) -> { Optional.ofNullable(state.getM()).map(Model::getTags).ifPresent(tags -> {
ArrayList<String> t = new ArrayList<>(); ArrayList<String> t = new ArrayList<>();
t.addAll(tags); t.addAll(tags);
setTags(t); setTags(t);
}); });
// description // description
Optional.ofNullable(state.getM()).map((m) -> m.getTopic()).ifPresent((topic) -> { Optional.ofNullable(state.getM()).map(Model::getTopic).ifPresent(topic -> {
try { try {
setDescription(URLDecoder.decode(topic, "utf-8")); setDescription(URLDecoder.decode(topic, "utf-8"));
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
@ -284,14 +285,14 @@ public class MyFreeCamsModel extends AbstractModel {
} }
}); });
viewerCount = Optional.ofNullable(state.getM()).map((m) -> m.getRc()).orElseGet(() -> 0); viewerCount = Optional.ofNullable(state.getM()).map(Model::getRc).orElse(0);
} }
private String getLivePreviewUrl(SessionState state) { private String getLivePreviewUrl(SessionState state) {
String previewUrl; String previewUrl;
int userChannel = 100000000 + state.getUid(); int userChannel = 100000000 + state.getUid();
int camserv = state.getU().getCamserv(); int camserv = state.getU().getCamserv();
String server = Integer.toString(camserv); String server;
ServerConfig sc = ((MyFreeCams)site).getClient().getServerConfig(); ServerConfig sc = ((MyFreeCams)site).getClient().getServerConfig();
if(sc.isOnNgServer(state)) { if(sc.isOnNgServer(state)) {
server = sc.ngVideoServers.get(Integer.toString(camserv)); server = sc.ngVideoServers.get(Integer.toString(camserv));
@ -348,4 +349,13 @@ public class MyFreeCamsModel extends AbstractModel {
public void writeSiteSpecificData(JsonWriter writer) throws IOException { public void writeSiteSpecificData(JsonWriter writer) throws IOException {
writer.name("uid").value(uid); writer.name("uid").value(uid);
} }
@Override
public Download createDownload() {
if(streamUrl.endsWith("m3u8")) {
return super.createDownload();
} else {
return new DashDownload(getSite().getHttpClient(), streamUrl);
}
}
} }

View File

@ -65,9 +65,16 @@ public class ServerConfig {
return result; return result;
} }
public boolean isOnNgServer(SessionState state) { public boolean isOnHtml5VideoServer(SessionState state) {
int camserv = Objects.requireNonNull(Objects.requireNonNull(state.getU()).getCamserv()); int camserv = Objects.requireNonNull(Objects.requireNonNull(state.getU()).getCamserv());
return ngVideoServers.containsKey(Integer.toString(camserv)); return isOnObsServer(state)
|| h5Servers.containsKey(Integer.toString(camserv))
|| (camserv >= 904 && camserv <= 915
|| camserv >= 938 && camserv <= 960);
}
public boolean isOnObsServer(SessionState state) {
return isOnWzObsVideoServer(state) || isOnNgServer(state);
} }
public boolean isOnWzObsVideoServer(SessionState state) { public boolean isOnWzObsVideoServer(SessionState state) {
@ -75,8 +82,8 @@ public class ServerConfig {
return wzobsServers.containsKey(Integer.toString(camserv)); return wzobsServers.containsKey(Integer.toString(camserv));
} }
public boolean isOnHtml5VideoServer(SessionState state) { public boolean isOnNgServer(SessionState state) {
int camserv = Objects.requireNonNull(Objects.requireNonNull(state.getU()).getCamserv()); int camserv = Objects.requireNonNull(Objects.requireNonNull(state.getU()).getCamserv());
return h5Servers.containsKey(Integer.toString(camserv)) || (camserv >= 904 && camserv <= 915 || camserv >= 940 && camserv <= 960); return ngVideoServers.containsKey(Integer.toString(camserv));
} }
} }