diff --git a/docs/index.md b/docs/index.md index aaa190ca..8cf9159c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -92,8 +92,8 @@ highest plane on the network? The longest flight? The flight that has been conne [![Streamers Table](assets/images/showcase/table_streamers.png)](assets/images/showcase/table_streamers.png) -Want to find a VATSIM stream to watch? This table lists all users that have included a link to their twitch stream in -their remarks. [Want to make sure you show up here when you're streaming?](/streamers) +Want to find a VATSIM stream to watch? This table lists all users that have included a link to their Twitch or YouTube +stream in their remarks. [Want to make sure you show up here when you're streaming?](/streamers) #### Distance Measure diff --git a/docs/streamers.md b/docs/streamers.md index bf9873df..a6928cb3 100644 --- a/docs/streamers.md +++ b/docs/streamers.md @@ -2,15 +2,20 @@ ### [← back](/) -VATprism will show users which clients are streaming on Twitch. +VATprism will show users which clients are streaming on Twitch and YouTube. [![Stramer Detail View](assets/images/showcase/detail_streamer.png)](assets/images/showcase/detail_streamer.png) #### Want to make sure you show up? +**Twitch** Simply put `twitch.tv/` or `twitch.tv ` in your remarks and VATprism will automatically add a link to your Twitch stream to your pilot view and will add you to the streamers table. +**YouTube** +Simply put `youtube.com/` or `youtube.com ` in your remarks and VATprism will automatically +add a link to your YouTube stream to your pilot view and will add you to the streamers table. + [![Streamers Table](assets/images/showcase/table_streamers.png)](assets/images/showcase/table_streamers.png) ### [← back](/) diff --git a/src/main/java/net/marvk/fs/vatsim/map/data/Urls.java b/src/main/java/net/marvk/fs/vatsim/map/data/Urls.java index b19519d1..219a49d8 100644 --- a/src/main/java/net/marvk/fs/vatsim/map/data/Urls.java +++ b/src/main/java/net/marvk/fs/vatsim/map/data/Urls.java @@ -11,40 +11,54 @@ import java.util.stream.Collectors; public class Urls { - private static final Pattern URL = Pattern.compile("(?twitch\\.tv[ /][A-Z0-9_]+)|(?:(?:https?[: ]\\/\\/)?(?(www)?(?:[a-z0-9-]{1,256}\\.)+(?:[a-z]{2,})(?:\\/[a-z0-9-_]+)*\\/?))", Pattern.CASE_INSENSITIVE); + private static final Pattern TWITCH_URL = Pattern.compile("(?twitch\\.tv[ /][A-Z0-9_]+)|(?:(?:https?[: ]\\/\\/)?(?(www)?(?:[a-z0-9-]{1,256}\\.)+(?:[a-z]{2,})(?:\\/[a-z0-9-_]+)*\\/?))", Pattern.CASE_INSENSITIVE); + private static final Pattern YOUTUBE_URL = Pattern.compile("(?youtube\\.com[ /][A-Z0-9_]+)|(?:(?:https?[: ]\\/\\/)?(?(www)?(?:[a-z0-9-]{1,256}\\.)+(?:[a-z]{2,})(?:\\/[a-z0-9-_@]+)*\\/?))", Pattern.CASE_INSENSITIVE); private final ReadOnlyListWrapper urls = new ReadOnlyListWrapper<>(FXCollections.observableArrayList()); - private final StringProperty twitchUrl = new SimpleStringProperty(); - private final BooleanProperty twitch = new SimpleBooleanProperty(); + private final StringProperty url = new SimpleStringProperty(); + private final BooleanProperty livestream = new SimpleBooleanProperty(); + private final StringProperty platform = new SimpleStringProperty(); public ReadOnlyListProperty getUrls() { return urls.getReadOnlyProperty(); } - public String getTwitchUrl() { - return twitchUrl.get(); + public String getUrl() { + return url.get(); } - public ReadOnlyStringProperty twitchUrlProperty() { - return twitchUrl; + public ReadOnlyStringProperty urlProperty() { + return url; } - public boolean isTwitch() { - return twitch.get(); + public boolean getLivestream() { + return livestream.get(); } - public ReadOnlyBooleanProperty twitchProperty() { - return twitch; + public ReadOnlyBooleanProperty livestreamProperty() { + return livestream; + } + + public ReadOnlyStringProperty platformProperty() { + return platform; } void setUrlsFromString(final String s) { urls.setAll(parseStrings(s)); - final Optional maybeTwitchUrl = urls.stream().filter(e -> e.contains("twitch")).findFirst(); + final Optional maybeStreamUrl = urls.stream().filter(e -> e.contains("twitch") + || e.contains("youtube")).findFirst(); + + platform.set("Unknown"); + if (maybeStreamUrl.isPresent() && maybeStreamUrl.get().contains("youtube")) { + platform.set("YouTube"); + } else { + platform.set("Twitch.tv"); + } - twitchUrl.set(maybeTwitchUrl.orElse(null)); - twitch.set(maybeTwitchUrl.isPresent()); + url.set(maybeStreamUrl.orElse(null)); + livestream.set(maybeStreamUrl.isPresent()); } private static List parseStrings(final String s) { @@ -52,7 +66,15 @@ private static List parseStrings(final String s) { return Collections.emptyList(); } - return URL + if (s.toLowerCase(Locale.ROOT).contains("youtube.com/")) { + return YOUTUBE_URL + .matcher(s.replaceAll("/./\s+$", "")) + .results() + .map(Urls::getGroup) + .map(e -> e.toLowerCase(Locale.ROOT)) + .collect(Collectors.toList()); + } + return TWITCH_URL .matcher(s.replaceAll("/./\s+$", "")) .results() .map(Urls::getGroup) @@ -67,6 +89,9 @@ private static String getGroup(final java.util.regex.MatchResult e) { return e.group(2); } + if (g1.toLowerCase(Locale.ROOT).contains("youtube")) { + return g1.replaceAll("(?i)youtube\\.com\s+", "youtube\\.com/"); + } return g1.replaceAll("(?i)twitch\\.tv\s+", "twitch\\.tv/"); } } diff --git a/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.java b/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.java index 1ed16fdf..7bae7795 100644 --- a/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.java +++ b/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.java @@ -30,6 +30,8 @@ public class ClientDetailView extends DataDetailSubView labels() { @Override public void initialize() { super.initialize(); - viewModel.twitchStreamProperty().addListener((observable, oldValue, newValue) -> { + viewModel.livestreamProperty().addListener((observable, oldValue, newValue) -> { if (newValue) { - headerPane.getStyleClass().add("twitch-container"); + viewModel.livestreamPlatformProperty().addListener((observable1, oldValue1, newValue1) -> { + if (newValue1.equals("YouTube")) { + livestreamIcon.setIconLiteral("ion4-logo-youtube"); + headerPane.getStyleClass().remove("twitch-container"); + headerPane.getStyleClass().add("youtube-container"); + } else { + livestreamIcon.setIconLiteral("ion4-logo-twitch"); + headerPane.getStyleClass().remove("youtube-container"); + headerPane.getStyleClass().add("twitch-container"); + } + }); } else { + headerPane.getStyleClass().remove("youtube-container"); headerPane.getStyleClass().remove("twitch-container"); } }); diff --git a/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailViewModel.java b/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailViewModel.java index f7e9d060..e687c8e3 100644 --- a/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailViewModel.java +++ b/src/main/java/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailViewModel.java @@ -2,10 +2,7 @@ import com.google.inject.Inject; import javafx.application.HostServices; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.beans.property.ReadOnlyBooleanWrapper; -import javafx.beans.property.ReadOnlyObjectWrapper; +import javafx.beans.property.*; import net.marvk.fs.vatsim.api.VatsimApiUrlProvider; import net.marvk.fs.vatsim.map.data.Client; import net.marvk.fs.vatsim.map.data.Preferences; @@ -14,8 +11,9 @@ public class ClientDetailViewModel extends DataDetailSubViewModel { private final HostServices hostServices; private final VatsimApiUrlProvider urlProvider; - private final ReadOnlyBooleanWrapper twitchStream = new ReadOnlyBooleanWrapper(); - private final ReadOnlyObjectWrapper twitchStreamUrl = new ReadOnlyObjectWrapper<>(); + private final ReadOnlyBooleanWrapper livestream = new ReadOnlyBooleanWrapper(); + private final ReadOnlyObjectWrapper livestreamUrl = new ReadOnlyObjectWrapper<>(); + private final ReadOnlyStringWrapper livestreamPlatform = new ReadOnlyStringWrapper(); @Inject public ClientDetailViewModel(final HostServices hostServices, final VatsimApiUrlProvider urlProvider, final Preferences preferences) { @@ -25,17 +23,26 @@ public ClientDetailViewModel(final HostServices hostServices, final VatsimApiUrl final BooleanProperty social = preferences.booleanProperty("general.social"); data.addListener((observable, oldValue, newValue) -> { - twitchStream.bind(newValue.getUrls().twitchProperty().and(social)); - twitchStreamUrl.bind(newValue.getUrls().twitchUrlProperty()); + livestream.bind(newValue.getUrls().livestreamProperty().and(social)); + livestreamUrl.bind(newValue.getUrls().urlProperty()); + livestreamPlatform.bind(newValue.getUrls().platformProperty()); }); } - public boolean isTwitchStream() { - return twitchStream.get(); + public boolean isLivestream() { + return livestream.get(); } - public ReadOnlyBooleanProperty twitchStreamProperty() { - return twitchStream.getReadOnlyProperty(); + public ReadOnlyBooleanProperty livestreamProperty() { + return livestream.getReadOnlyProperty(); + } + + public String getLivestreamPlatform() { + return livestreamPlatform.get(); + } + + public ReadOnlyStringProperty livestreamPlatformProperty() { + return livestreamPlatform.getReadOnlyProperty(); } public void openStats() { @@ -45,8 +52,8 @@ public void openStats() { } public void openStream() { - if (twitchStreamUrl.get() != null) { - hostServices.showDocument("https://" + twitchStreamUrl.get()); + if (livestreamUrl.get() != null) { + hostServices.showDocument("https://" + livestreamUrl.get()); } } } diff --git a/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableView.java b/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableView.java index 53b6bad2..14ecc87f 100644 --- a/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableView.java +++ b/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableView.java @@ -26,9 +26,18 @@ protected void initializeColumns() { .build(); this.newColumnBuilder() - .title("Twitch Username") - .objectObservableValueFactory(e -> e.getUrls().twitchUrlProperty()) - .toStringMapper(e -> e.replaceAll("(?:www\\.)?twitch\\.tv/", "")) + .title("Platform") + .objectObservableValueFactory(e -> e.getUrls().platformProperty()) + .toStringMapper(e -> e) + .sortable() + .widthFactor(0.8) + .build(); + + this.newColumnBuilder() + .title("Username") + .objectObservableValueFactory(e -> e.getUrls().urlProperty()) + .toStringMapper(e -> e.replaceAll("(?:www\\.)?twitch\\.tv/", "") + .replaceAll("(?:www\\.)?youtube\\.com/", "")) .sortable() .widthFactor(2.0) .build(); diff --git a/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableViewModel.java b/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableViewModel.java index 0abad466..77c20c16 100644 --- a/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableViewModel.java +++ b/src/main/java/net/marvk/fs/vatsim/map/view/datatable/streamerstable/StreamersTableViewModel.java @@ -29,7 +29,7 @@ public StreamersTableViewModel(final HostServices hostServices, final Preference this.filteredList = new FilteredList<>(clientRepository.list()); this.filteredList.predicateProperty().bind(Bindings.createObjectBinding( - () -> e -> e.getUrls().isTwitch(), + () -> e -> e.getUrls().getLivestream(), currentTime )); Notifications.CLIENTS_RELOADED.subscribe(() -> currentTime.set(LocalDateTime.now())); @@ -42,9 +42,9 @@ public ObservableList items() { } public void openStream(final Client client) { - final String twitchUrl = client.getUrls().getTwitchUrl(); - if (twitchUrl != null) { - hostServices.showDocument("https://" + twitchUrl); + final String livestreamUrl = client.getUrls().getUrl(); + if (livestreamUrl != null) { + hostServices.showDocument("https://" + livestreamUrl); } } } diff --git a/src/main/resources/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.fxml b/src/main/resources/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.fxml index b277d8f6..d0c0d8a5 100644 --- a/src/main/resources/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.fxml +++ b/src/main/resources/net/marvk/fs/vatsim/map/view/datadetail/clientdetail/ClientDetailView.fxml @@ -25,7 +25,7 @@ - +