Mediaplayer in JAR - javafx

This code compile in Intellij, but it does not work in jar.
Working with getResourceAsStream() or getResource doest not solve the problem.
(I have tried out with Image like Image image = newImage(getClass().getResourceAsStream("image.png");and it does work)
package Sound;
import javafx.application.Application;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.io.File;
/**
*/
public class TestSound extends Application{
public TestSound() {
play();
}
#Override
public void start(Stage primaryStage) throws Exception {
new TestSound().play();
}
private void play(){
String mainsound = "src/res/sound/main.mp3";
Media i = null;
i = new Media( new File(mainsound).toURI().toString());
MediaPlayer mediaPlayer = new MediaPlayer(i);
mediaPlayer.play();
mediaPlayer.setStopTime(new Duration(120000));
mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
}
public static void main (String[] args){
launch(args);
}
}
EDIT:
Media i = new Media(getClass().getClassLoader().getResource(mainsound));
wont work, because the constructor need a String.
but also
Media i = new Media(getClass().getClassLoader().getResource(mainsound).toString());
new MediaPlayer(i).play();
does not work.
ofcourse mainsound="res/sound/main.mp3";
After extraction of jar with winrar
I got dictionary of sound with include the
testsound.class and main.mp3
And another dictionary of res with include main.mp3
Both dictionary are in the same root.

getClass().getResource("res/sound/main.mp3") is going to look for a resource named res/sound/main.mp3 relative to the current class. Since your class is in the sound package, it's effectively going to look for /sound/res/sound/main.mp3, which is not where the resource is located in your jar.
If the list of entries in the jar file is showing
/sound/main.mp3
then the following should work:
String mediaURL = getClass().getResource("/sound/main.mp3").toExternalForm();
// for debugging:
System.out.println(mediaURL);
Media i = new Media(mediaURL);

Instead of using getClass ().getResource (...) it would be ideal to use it like
Media m = new Media ("/myMedia.mp3");//if the media exists in default package
or
Media m = new Media ("/mypackage/myMedia.mp3");//if the media exists in mypackage

Related

return type of getStyleableProperty

When you look docs about what returns a given CSSMetadata (getCSSmetadata) the function getStyleableProperty tells something about <? capture of extends styleable
what is the type and how does it work.
I try to cast -fx-max-width to ( easyno subproperties) Styleable but does not work
hbox.getCSSMetadata().stream().filter(prop -> prop.toString().constanis("-fx-max-width")).forEach(prop2->{
prop2.getProperty(); //ok returns the string of the name
prop2.getStyleableProperty(<????? what goes here and what is the type of the returned value>);
});
Your prop2 variable (which is not really well named) is of type CSSMetaData<? extends Styleable, ?>.
The parameter you need to pass to the getStyleableProperty(...) method is the Styleable for which it's a property; since this CSSMetaData came from hbox, and I assume that's an HBox, then the parameter should be hbox.
However, the compiler will insist the parameter is the same type as the first type parameter to the CSSMetaData; since this is a wildcard type (? extends Styleable) there's no way for it to check this. So you need a downcast:
((CSSMetaData<HBox, ?>)prop2).getStyleableProperty(hbox)
The cast does not need to be this specific;
((CSSMetaData<Styleable, ?>)prop2).getStyleableProperty(hbox)
will also work (since hbox is an HBox, which is an implementation of Styleable).
Note that this is actually going to give you the hbox's maxWidthProperty(), so really you could just do hbox.maxWidthProperty() instead. (But maybe your -fx-max-width is just a contrived example, and you are trying to get this dynamically for some reason.)
Note that it's almost always bad practice to check an object's toString() method to determine data about it. So you should replace
prop.toString().contains("-fx-max-width")
with
prop.getProperty().equals("-fx-max-width)
The return type of getStyleableProperty(), with this downcast, will be
StyleableProperty<?>
(since there is a wildcard for the second type parameter in the downcast). If you knew more information, as in this case, you can make that more specific if needed. For example, if you wanted to set the value, you would need to use the fact that the -fx-max-width CSS property is numeric, and use the downcast
StyleableProperty<Number> maxWidth = ((CSSMetaData<Styleable, Number>)prop2).getStyleableProperty(hbox);
and then the return type would be
StyleableProperty<Number>
and you'd be able to do, for example
maxWidth.setValue(400.0);
Here's an example:
import javafx.application.Application;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.css.StyleableProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class SPTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
HBox root = new HBox();
root.setPadding(new Insets(20));
root.setAlignment(Pos.CENTER);
Button show = new Button("Show");
show.setOnAction(e -> {
root.getCssMetaData().stream().filter(cssMD -> cssMD.getProperty().equals("-fx-max-width")).forEach(maxWidthMD -> {
StyleableProperty<?> maxWidth = ((CssMetaData<Styleable, ?>)maxWidthMD).getStyleableProperty(root);
System.out.println(maxWidth.getValue());
System.out.println(maxWidth == root.maxWidthProperty());
});
System.out.println(root.getMaxWidth());
});
root.getChildren().add(show);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
I used a simple style.css file to test this:
.root {
-fx-max-width: 400 ;
}
Note the compiler has no way of checking the cast will work, so you get a compiler warning with this code. Since you're assured the cast will work (because the CSSMetaData was retrieved from the hbox), you can suppress this warning:
#SuppressWarnings("unchecked")
StyleableProperty<?> maxWidth = ((CssMetaData<HBox, ?>)prop2).getStyleableProperty(root);

Problem with adding Blocks to Minecraft Mod (1.15.2)

I have spent the last few hours adding a block to my Minecraft Mod. I have looked at several tutorials and none of them work. The blocks are not added to the Creative Inventory and I can't set them by command either. Unfortunately I didn't have any bugs in the console that I could show here. At some point I gave up and tried to do armor, here the same problem. On the other hand: normal items work (You can see the Item "ruby" which woked finde).
Here the code of my main class:
package de.thom.clashOfClasses;
import de.thom.clashOfClasses.init.ArmorMaterialList;
import de.thom.clashOfClasses.init.BlockList;
import de.thom.clashOfClasses.init.ItemList;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
#Mod("clashofclasses")
public class ClashOfClasses {
public static ClashOfClasses instance;
public static final String modid = "clashofclasses";
public static final Logger logger = LogManager.getLogger(modid);
public ClashOfClasses() {
instance = this;
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientRegistries);
MinecraftForge.EVENT_BUS.register(this);
}
public void setup(final FMLCommonSetupEvent event) {
logger.info("Setup method complete");
}
public void clientRegistries(final FMLClientSetupEvent event) {
logger.info("ClientRegistries method complete");
}
#Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public static class RegistryEvents {
#SubscribeEvent
public static void registerItems(final RegistryEvent.Register<Item> event) {
logger.info("Item Registry started");
event.getRegistry().registerAll(
ItemList.RUBY,
ItemList.ruby_block = new BlockItem(BlockList.ruby_block,new Item.Properties().group(ItemGroup.MISC)).setRegistryName(BlockList.ruby_block.getRegistryName())
);
logger.info("Items registerd");
}
#SubscribeEvent
public static void registerBlocks(final RegistryEvent.Register<Block> event) {
logger.info("Block Registry started");
event.getRegistry().registerAll
(
BlockList.ruby_block = new Block(Block.Properties.create(Material.IRON).hardnessAndResistance(2.0f,3.0f).lightValue(5).sound(SoundType.METAL)).setRegistryName(location("ruby_block"))
);
logger.info("Blocks registerd");
}
private static ResourceLocation location(String name){
return new ResourceLocation(ClashOfClasses.modid, name);
}
}
}
Here is the code of BlockList
package de.thom.clashOfClasses.init;
import net.minecraft.block.Block;
public class BlockList {
public static Block ruby_block;
}
Here is the code of ItemList:
package de.thom.clashOfClasses.init;
import de.thom.clashOfClasses.ClashOfClasses;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.ResourceLocation;
public class ItemList
{
//Test Items
public static Item RUBY = new Item(new Item.Properties().group(ItemGroup.MATERIALS)).setRegistryName(location("ruby"));
public static Item ruby_block;
private static ResourceLocation location(String name){
return new ResourceLocation(ClashOfClasses.modid, name);
}
}
A block in the world and a “block” in an inventory are very different things. A block in the world is represented by an IBlockState, and its behavior defined by an instance of Block. Meanwhile, an item in an inventory is an ItemStack, controlled by an Item. As a bridge between the different worlds of Block and Item, there exists the class ItemBlock. ItemBlock is a subclass of Item that has a field block that holds a reference to the Block it represents. ItemBlock defines some of the behavior of a “block” as an item, like how a right click places the block. It’s possible to have a Block without an ItemBlock. (E.g. minecraft:water exists a block, but not an item. It is therefore impossible to hold it in an inventory as one.)
When a block is registered, only a block is registered. The block does not automatically have an ItemBlock. To create a basic ItemBlock for a block, one should use new ItemBlock(block).setRegistryName(block.getRegistryName()). The unlocalized name is the same as the block’s. Custom subclasses of ItemBlock may be used as well. Once an ItemBlock has been registered for a block, Item.getItemFromBlock can be used to retrieve it. Item.getItemFromBlock will return null if there is no ItemBlock for the Block, so if you are not certain that there is an ItemBlock for the Block you are using, check for null.
from https://mcforge.readthedocs.io/en/latest/blocks/blocks/.
I short, if everything works, your blocks shoudnt appear in your
#ObjectHolder(modid)
#Mod.EventBusSunscriber(modid = modid, bus = Bus.Mod)
public class BlockInit {
public static final Block example_block = null;
#SubscribeEvent
public static void registerBlocks(final RegistryEvent.Register<Block> event) {
event.getRegistry().register(new Block(Block.Properties.create(Material)).setRegistry("example_block"));
}
#SubscribeEvent
public static void registerBlockItems(final RegistryEvent.Register<Item> event){
event.getRegistry().register(new BlockItem(example_item, new Item.Properties().group(ItemGroup)).setRegistry("example_block"));
}
That works for me just replace example_block with the name of your block and add more properties if you want
for another block just repeat the event.getRegistry stuff and use the name of your new block instead of example_block.
and don't forget to do the json files

javafx cannot load image resource

I am very new to javafx, and was getting java.lang.reflect.InvocationTargetException when testing with the code tutorial:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public static final String IMAGE_NAME = "groceries.jpg";
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(setupScene(), 300, 300);
primaryStage.setScene(scene);
primaryStage.setTitle("Image Screen");
primaryStage.show();
}
StackPane setupScene() {
StackPane root = new StackPane();
ImageView imageView = new ImageView();
imageView.setPreserveRatio(true);
imageView.setSmooth(true);
Image image = new Image(getClass().getClassLoader().getResource(IMAGE_NAME).toString());
imageView.setImage(image);
root.setPrefSize(250, 250);
imageView.setFitHeight(root.getPrefHeight());
imageView.setFitWidth(root.getPrefWidth());
root.getChildren().add(imageView);
return root;
}
}
The exception was caused by java.lang.NullPointerException in line Image image = new Image(getClass().getClassLoader().getResource(IMAGE_NAME).toString());
The image file is in my project folder, but it doesn't seem to be loaded. I was able to get the image using Image image = new Image(new File(IMAGE_NAME).toURI().toURL().toString()), but when I switched back to Image image = new Image(getClass().getClassLoader().getResource(IMAGE_NAME).toString()), it just never worked.
Does anyone know why my program is behaving like this? Any ideas would be highly appreciated...
Edit: My image file is on the same level of the src folder:
- projectfolder
- groceries.jpg
- src
- Main.java
I'm using IntelliJ JavaFX Application to create the project, everything is in default state.
Image image = new Image(getClass().getClassLoader().getResource(IMAGE_NAME).toString()) and Image image = new Image(new File(IMAGE_NAME).toURI().toURL().toString()) do two different things.
The first will get a resource based off the class loader, which is most commonly used when extracting a resource directly from the jar file. The second is used to load the image from the file system, which is why that works in your case (that's where the file is!)
The getClass().getClassLoader().getResource() line, searchs for the file in the same location where your Main class is.
If you have this image in the src, you just need to add a '/' :
private String theme1Url = getClass().getResource("/groceries.jpg").toExternalForm();

Jsoup to parse multiple websites for links published today

I am currently using jsoup (below) to output a .csv of links which include a string date format in the url from just one website.
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.io.FileOutputStream;
import java.io.PrintStream;
import org.jsoup.nodes.Element;
public class readAllLinks {
public static Set<String> uniqueURL = new HashSet<String>();
public static String my_site;
public static String published = ("20180731");
public static void main(String[] args) {
readAllLinks obj = new readAllLinks();
my_site = ("news24.com/SouthAfrica/News");
obj.get_links("https://www.news24.com/SouthAfrica/News/");
}
private void get_links(String url) {
try {
Document doc = Jsoup.connect(url).get();
Elements links = doc.select("a");
FileOutputStream fout=new FileOutputStream("links.csv");
PrintStream csv=new PrintStream(fout);
links.stream().map((link) -> link.attr("abs:href")).forEachOrdered((this_url) -> {
boolean add = uniqueURL.add(this_url);
if (add && this_url.contains(my_site) && this_url.contains(published)) {
System.out.println(this_url);
get_links(this_url);
}
if (this_url.contains(published))
csv.println(this_url);
} );
} catch (IOException ex) {
}
}
}
Instead I would like to make a csv of links published today (i.e using today's date) from multiple websites.
How do you specify the .select for the newly published links to get the date contained in a span?
And how do you parse multiple websites from a list?
Many thanks for your help.
This will select all links that contains value of variable 'published'.
Elements links = doc.select("a[href*="+published+"]");

What's a good observable appendable base for a TextArea?

I have a StringBuffer that is occasionally appended with new information.
In a separate module, I have a JavaFX TextArea that displays that StringBuffer.
Right now, I have to manually update the TextArea every time the underlying data is modified.
Is there something like an ObservableList (which I use for TableViews) that I can use as the back-end data for the TextArea instead, so I don't have to manually manage pushing the changes to the display?
I am not attached to using a StringBuffer. I'm glad to use any appendable data structure to hold text.
You can consider something simple like this:
import javafx.beans.binding.StringBinding;
public class ObservableStringBuffer extends StringBinding {
private final StringBuffer buffer = new StringBuffer() ;
#Override
protected String computeValue() {
return buffer.toString();
}
public void set(String content) {
buffer.replace(0, buffer.length(), content);
invalidate();
}
public void append(String text) {
buffer.append(text);
invalidate();
}
// wrap other StringBuffer methods as needed...
}
This enables easy coding for binding to a text area. You can simply do
TextArea textArea = new TextArea();
ObservableStringBuffer buffer = new ObservableStringBuffer();
textArea.textProperty().bind(buffer);
// ...
buffer.append("Hello world");
However, it's important to note here that you don't transfer the efficiency of the buffer API to the text area: the text area simply has a textProperty() representing its text, which can still only really be modified by set(...) and setValue(...). In other words, when you append to the buffer, you essentially end up with textArea.setText(textArea.getText() + "Hello world") (not textArea.appendText("Hello world"). If you're just looking for a clean API, then this should work for you; if you're looking for something efficient, you would have to "wire" the calls to appendText yourself, since that is simply not supported by the text area's textProperty().
Here's a SSCCE using the above class:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ObservableStringBufferTest extends Application {
private int counter ;
#Override
public void start(Stage primaryStage) {
ObservableStringBuffer buffer = new ObservableStringBuffer();
TextArea textArea = new TextArea();
textArea.setEditable(false);
textArea.textProperty().bind(buffer);
buffer.set("Item 0");
Timeline timeline = new Timeline(new KeyFrame(
Duration.seconds(1),
e -> buffer.append("\nItem "+(++counter))));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
primaryStage.setScene(new Scene(new StackPane(textArea)));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Resources