[Tease Program] Tease-AI Java (1.4)

Webteases are great, but what if you're in the mood for a slightly more immersive experience? Chat about Tease AI and other offline tease software.

Moderator: 1885

meaculpa_uk
Explorer
Explorer
Posts: 88
Joined: Mon Jan 11, 2016 9:46 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch

Re: [Tease Program] Tease-AI Java (1.3)

Post by meaculpa_uk »

GodDragon wrote: Sun Jan 03, 2021 8:37 pm
DatFace wrote: Sun Jan 03, 2021 3:42 pm Hi,
two little questions..

1. Is there any way to switch from part to full time slave and reverse, so I can unlock the Academy?
As long as my progress isn't going to be lost I would also try it manually by editing system files... just want to try it out without having to reinstall everything

2. Is there any way to list and delete toys without having to inspect and manually delete them in the system files with editor/notepad++?
1. Well there are a few things that need to done for fulltime transition to work. I can write something to check all marks.

2. I am planning on adding a delete button to my gui edit function for toys. For normal toys except plugs, dildos, chastity and heels I don't think I will add something soon if that's what you are thinking of.
I'm confused. Is this a TAIJ thread, or a Spicy thread, or have the two things become synonymous?

Mea
GodDragon
Explorer At Heart
Explorer At Heart
Posts: 795
Joined: Sun Jun 11, 2017 4:30 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch

Re: [Tease Program] Tease-AI Java (1.3)

Post by GodDragon »

meaculpa_uk wrote: Sun Jan 03, 2021 9:01 pm
GodDragon wrote: Sun Jan 03, 2021 8:37 pm
DatFace wrote: Sun Jan 03, 2021 3:42 pm Hi,
two little questions..

1. Is there any way to switch from part to full time slave and reverse, so I can unlock the Academy?
As long as my progress isn't going to be lost I would also try it manually by editing system files... just want to try it out without having to reinstall everything

2. Is there any way to list and delete toys without having to inspect and manually delete them in the system files with editor/notepad++?
1. Well there are a few things that need to done for fulltime transition to work. I can write something to check all marks.

2. I am planning on adding a delete button to my gui edit function for toys. For normal toys except plugs, dildos, chastity and heels I don't think I will add something soon if that's what you are thinking of.
I'm confused. Is this a TAIJ thread, or a Spicy thread, or have the two things become synonymous?

Mea
It's the TAJ thread. He just posted it in the wrong thread.
lotar232
Explorer
Explorer
Posts: 76
Joined: Sat Nov 01, 2008 6:34 pm

Re: [Tease Program] Tease-AI Java (1.3)

Post by lotar232 »

delivering "media modules" / organization:

so I've been playing around with a spicy fork, and mostly have been doing video modules... i.e. I've found some video where a mistress shocks a slave(Pishock now , or potentially e-STIM), or milks via controlling a vibrator or fleshlight (Lovense), and have been writing a script that actuates toys in sequence with the video( if I get the timing correct and the RF/BT/network gods are smiling. ;) )....


I was wondering how to share these modules since the scripts are tightly coupled with the media... and the media is heavy (500MB-1GB videos)... typically we put all the media in one directory and all the personality code in another...

I was wondering if it made sense to create some top level folder for "modules" (similar to video, audio, and Images) where code could sit next to the media... and if anyone had thought about this, or if it made sense to have a standard "module structure" (i.e. like mini-personalities that could be called)

any thoughts?
GodDragon
Explorer At Heart
Explorer At Heart
Posts: 795
Joined: Sun Jun 11, 2017 4:30 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch

Re: [Tease Program] Tease-AI Java (1.3)

Post by GodDragon »

lotar232 wrote: Fri Jan 08, 2021 10:59 pm delivering "media modules" / organization:

so I've been playing around with a spicy fork, and mostly have been doing video modules... i.e. I've found some video where a mistress shocks a slave(Pishock now , or potentially e-STIM), or milks via controlling a vibrator or fleshlight (Lovense), and have been writing a script that actuates toys in sequence with the video( if I get the timing correct and the RF/BT/network gods are smiling. ;) )....


I was wondering how to share these modules since the scripts are tightly coupled with the media... and the media is heavy (500MB-1GB videos)... typically we put all the media in one directory and all the personality code in another...

I was wondering if it made sense to create some top level folder for "modules" (similar to video, audio, and Images) where code could sit next to the media... and if anyone had thought about this, or if it made sense to have a standard "module structure" (i.e. like mini-personalities that could be called)

any thoughts?
You can quite easily play a video from a file inside your personality code files even though I don't think that's optimal. I mean probably the best way to go about this would be to upload the videos to some video plattform and then stream them into TAJ with via a new feature.
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

I've discovered why GIF animations don't play under Linux.

This line changes the path to be all lowercase which means the file won't be found on a case-sensitive file system:
'80cd022:src/me/goddragon/teaseai/utils/media/GifDecoder.java:334'

Code: Select all

name = name.trim().toLowerCase();
From here:

Code: Select all

    public int read(String name) {
        status = STATUS_OK;
        try {
            name = name.trim().toLowerCase();
            if ((name.indexOf("file:") >= 0) ||
                    (name.indexOf(":/") > 0)) {
                URL url = new URL(name);
                in = new BufferedInputStream(url.openStream());
            } else {
                in = new BufferedInputStream(new FileInputStream(name));
            }
            status = read(in);
        } catch (IOException e) {
            status = STATUS_OPEN_ERROR;
        }

        return status;
    }
Moving the call to toLowerCase to the following line fixes it, i.e.

Code: Select all

            name = name.trim();
            if ((name.toLowerCase().indexOf("file:") >= 0) ||
                    (name.indexOf(":/") > 0)) {
I haven't verified this on Windows however because I no longer have a Windows installation.
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

Each call to ShowImage() ends up calling MediaHandler.getHandler().showPicture() twice, once from each of these locations:

'80cd022:src/me/goddragon/teaseai/api/scripts/nashorn/ShowImageFunction.java:48'

Code: Select all

                if (file == null) {
                    TeaseLogger.getLogger().log(Level.SEVERE, "Matching image file for path " + args[0] + " does not exist.");
                } else {
                    MediaHandler.getHandler().showPicture(file);
                }
'80cd022:src/me/goddragon/teaseai/api/scripts/nashorn/ShowImageFunction.java:62'

Code: Select all

            if (args.length == 2) {
                if (args[1] instanceof Number) {
                    MediaHandler.getHandler().showPicture(file, ((Number)args[1]).intValue());
                    return file;
                }
            } else {
                MediaHandler.getHandler().showPicture(file, 0);
                return file;
            }
Adding a return statement to the first block fixes it, i.e.

Code: Select all

                if (file == null) {
                    TeaseLogger.getLogger().log(Level.SEVERE, "Matching image file for path " + args[0] + " does not exist.");
                } else {
                    MediaHandler.getHandler().showPicture(file);
+                   return file;
                }
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

TeaseAI.application.getSession().end() ends up being called twice when a Personality finishes. I don't think this has any discernible side effects, and I appreciate that most Personalities keep running anyway.

The two places where the call is made are here:
'80cd022:src/me/goddragon/teaseai/api/scripts/ScriptHandler.java:128'

Code: Select all

    public void startPersonality(Personality personality) {
        File mainScript = new File(personality.getFolder().getAbsolutePath() + File.separator + "main.js");

        if (!mainScript.isFile() || !mainScript.exists()) {
            TeaseLogger.getLogger().log(Level.SEVERE, "Personality '" + currentPersonality.getName() + "' is missing the main.js script");
        } else {
            startPersonality(personality, mainScript);
        }

        TeaseAI.application.getSession().end();
    }
'80cd022:src/me/goddragon/teaseai/api/scripts/ScriptHandler.java:150'

Code: Select all

    public void startPersonality(Personality personality, File startScript) {
        //Reassign because we want to clear the catch
        this.engine = new ScriptEngineManager().getEngineByName("nashorn");
        ScriptHandler.getHandler().load();

        this.currentPersonality = personality;

        currentPersonality.getVariableHandler().setVariable("personalityVersion", currentPersonality.getVersion().getValue(), true);

        VocabularyHandler.getHandler().loadVocabulariesFromPersonality(personality);
        ResponseHandler.getHandler().loadResponsesFromPersonality(personality);


        try {
            runScript(startScript);
        } catch (FileNotFoundException e) {
            TeaseLogger.getLogger().log(Level.SEVERE, "Tried to run non-existent script '" + startScript.getName() + "'.");
        }

        TeaseAI.application.getSession().end();
    }
The change (if you think it's worth it!) is to move the call to getSession().end() earlier within the first block, i.e.

Code: Select all

    public void startPersonality(Personality personality) {
        File mainScript = new File(personality.getFolder().getAbsolutePath() + File.separator + "main.js");

        if (!mainScript.isFile() || !mainScript.exists()) {
            TeaseLogger.getLogger().log(Level.SEVERE, "Personality '" + currentPersonality.getName() + "' is missing the main.js script");
+           TeaseAI.application.getSession().end();
        } else {
            startPersonality(personality, mainScript);
        }
    }
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

Audio should also be stopped at the end of a session along with the video.
'80cd022:src/me/goddragon/teaseai/api/session/Session.java:145'

Code: Select all

                //Reset playing video
                if (MediaHandler.getHandler().getCurrentVideoPlayer() != null) {
                    MediaHandler.getHandler().getCurrentVideoPlayer().stop();
                    
+                MediaHandler.getHandler().stopAllAudio();
                }
I'm currently digging in to the MediaHandler code to try and determine why there are occasional MediaPlayer exceptions when playing audio. I think it's related to the MediaHandlers not being disposed and (at least under Linux) it ends up hogging resources. I've found a few other issues in here such as if the same audio is played twice, the entry in the map is replaced with the second entry, and therefore stopAllAudio() will only stop the second playing audio, not the first one. I think I've fixed that and few other issues, but that's a bigger change that'll be more suitable as a pull request.

One of the things I'm fighting against is that if MediaPlayer is trying to play a broken mp3 file, when calling stop() on it, it blocks and never returns and hence causes the TAJ session to freeze. While the obvious fix is to fix the mp3 file, I'd like to understand why MediaPlayer is misbehaving, and hopefully tame it to make it more robust.
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

Fix for tumblr images not being downloaded.

1. Import HttpURLConnection

80cd022:src/me/goddragon/teaseai/api/media/MediaHandler.java:16

Code: Select all

  import java.io.*;
+ import java.net.HttpURLConnection;
  import java.net.MalformedURLException;
  import java.net.URI;
2. Replace getImageFromURL() with this:

80cd022:src/me/goddragon/teaseai/api/media/MediaHandler.java:245
Spoiler: show

Code: Select all

    public File getImageFromURL(String url) throws IOException {
        currentImageURL = url;

        String[] split = url.split("/");
        String path = split[split.length - 1];

        path = MediaURL.IMAGE_DOWNLOAD_PATH + File.separator + path;
        File file = new File(path);

        if (file.exists()) {
            return file;
        }

        TeaseLogger.getLogger().log(
                Level.FINER, String.format("Fetching url '%s'", url));

        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.addRequestProperty("Referer", url);
            connection.addRequestProperty("Accept", "*/*");
            connection.addRequestProperty("User-Agent",
                    "Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/12.0");
            connection.connect();

            final int responseCode = connection.getResponseCode();
            final String responseMessage = connection.getResponseMessage();
            TeaseLogger.getLogger().log(
                    Level.FINER, String.format("Response code received %d '%s'", responseCode, responseMessage));

            if (responseCode == HttpURLConnection.HTTP_OK) {
                TeaseLogger.getLogger().log(
                    Level.FINER, String.format("Fetched %,d bytes of type '%s' encoded as '%s'",
                        connection.getContentLength(), connection.getContentType(),
                        connection.getContentEncoding()));

                InputStream in = new BufferedInputStream(connection.getInputStream());
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                byte[] buf = new byte[1024];

                int n;
                while (-1 != (n = in.read(buf))) {
                    out.write(buf, 0, n);
                }
                out.close();
                in.close();

                byte[] response = out.toByteArray();

                FileOutputStream fos = new FileOutputStream(path);
                fos.write(response);
                fos.close();
            } else {
                TeaseLogger.getLogger().log(
                    Level.WARNING, "Unsupported response code, ignoring conent");
            }
        } catch (IOException ex) {
            TeaseLogger.getLogger().log(
                    Level.WARNING, "Unable to find image on url " + url + ": " + ex.getMessage());
        } catch (ClassCastException ex) {
            TeaseLogger.getLogger().log(
                    Level.SEVERE, "Url " + url + " does not appear to be an http connection");
        }

        if (file.exists()) {
            return file;
        }

        return null;
    }
The magic bit is "connection.addRequestProperty("Referer", url);". Without that tumblr, returns the proxy image html page, but with the referer it thinks the image is being loaded from the proxy html page.

Tested with:

Code: Select all

showTeaseImage(5);
showTeaseImage(5);
showTeaseImage(5);
showTeaseImage(5);
I added some log messages to help me determine what was going on, but you don't have to keep those in:

Code: Select all

09:46:53 pm FINER: Fetching url 'https://78.media.tumblr.com/6385397b6d5a5bdd923ad6ffce23381a/tumblr_onka9xugA31upsojro8_1280.jpg'
09:46:54 pm FINER: Response code received 200 'OK'
09:46:54 pm FINER: Fetched 37,351 bytes of type 'image/jpeg' encoded as 'null'
09:46:59 pm FINER: Fetching url 'https://78.media.tumblr.com/706a8a467ea1e939ada92cec3a91a9af/tumblr_omjqnq3aKP1svgt29o1_1280.jpg'
09:47:00 pm FINER: Response code received 200 'OK'
09:47:00 pm FINER: Fetched 138,723 bytes of type 'image/jpeg' encoded as 'null'
09:47:05 pm FINER: Fetching url 'https://78.media.tumblr.com/79de450fb15e6baefdc023101daba137/tumblr_n239mzNA4e1szaue0o1_500.jpg'
09:47:06 pm FINER: Response code received 200 'OK'
09:47:06 pm FINER: Fetched 59,301 bytes of type 'image/jpeg' encoded as 'null'
09:47:11 pm FINER: Fetching url 'https://78.media.tumblr.com/78020f2b2596433b543426285994d5d0/tumblr_oi0h7yutdc1uceefco1_1280.jpg'
09:47:12 pm FINER: Response code received 200 'OK'
09:47:12 pm FINER: Fetched 139,898 bytes of type 'image/jpeg' encoded as 'null'
Probably should have done this one as a pull request, sorry about that. Hope it's useful anyway.
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

I've created a pull request for all five of the items that I mentioned above as five small commits:
https://github.com/GodDragoner/TeaseAIJava/pull/23
GodDragon
Explorer At Heart
Explorer At Heart
Posts: 795
Joined: Sun Jun 11, 2017 4:30 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch

Re: [Tease Program] Tease-AI Java (1.3)

Post by GodDragon »

FrozenWolf wrote: Sat Jan 16, 2021 4:01 pm I've created a pull request for all five of the items that I mentioned above as five small commits:
https://github.com/GodDragoner/TeaseAIJava/pull/23
Thanks! I changed the media show image function a bit so it won't be shown twice but keeps the cooldown in seconds (if given). Because in your case it wouldn't account for an integer as a second argument anymore if a string was given.
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

GodDragon wrote: Sat Jan 16, 2021 7:59 pm
FrozenWolf wrote: Sat Jan 16, 2021 4:01 pm I've created a pull request for all five of the items that I mentioned above as five small commits:
https://github.com/GodDragoner/TeaseAIJava/pull/23
Thanks! I changed the media show image function a bit so it won't be shown twice but keeps the cooldown in seconds (if given). Because in your case it wouldn't account for an integer as a second argument anymore if a string was given.
Ah, good catch, I didn't spot that!

I'm currently experimenting with refactoring the showImage handling to reduce the complexity by using reflection. That means that all of the parameter matching and error reporting relating to mismatched parameters is done in the CustomFunction base class, and the derived class just has to provide functions to match the desired parameters. I'll push it to a branch in my fork at some point and give you a link to the changes just to see what you think. I'm about to soak test it with an actual session with Michaela Isizzu , so either my code will break, or Michaela will break me. :-P
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

FrozenWolf wrote: Sat Jan 16, 2021 9:21 pm
GodDragon wrote: Sat Jan 16, 2021 7:59 pm
FrozenWolf wrote: Sat Jan 16, 2021 4:01 pm I've created a pull request for all five of the items that I mentioned above as five small commits:
https://github.com/GodDragoner/TeaseAIJava/pull/23
Thanks! I changed the media show image function a bit so it won't be shown twice but keeps the cooldown in seconds (if given). Because in your case it wouldn't account for an integer as a second argument anymore if a string was given.
Ah, good catch, I didn't spot that!

I'm currently experimenting with refactoring the showImage handling to reduce the complexity by using reflection. That means that all of the parameter matching and error reporting relating to mismatched parameters is done in the CustomFunction base class, and the derived class just has to provide functions to match the desired parameters. I'll push it to a branch in my fork at some point and give you a link to the changes just to see what you think. I'm about to soak test it with an actual session with Michaela Isizzu , so either my code will break, or Michaela will break me. :-P
Michaela broke me; anyway, have a look at this to see what you think:

This is what ShowImageFunction.java looks like now (it should work the same as before):
https://github.com/FrozenWolf4887/Tease ... ction.java
This is the supporting CustomFunctionExtended.java:
https://github.com/FrozenWolf4887/Tease ... ended.java

CustomFunctionExtended is derived from CustomFunction to incorporate the new feature without breaking all of the existing function implementations. That would mean that if you'd like to incorporate that technique into the other functions, it can be done one at a time, and when they're all done, we can merge CustomFunctionExtended and CustomFunction together.

All that the derived class has to do is have protected methods with the name "handleCall" and whatever parameters they need, and the base class will handle the gritty details. It still needs a bit of polishing, I've already spotted two bugs from just writing this post.

Did some basic testing with the following little script:

Code: Select all

let file1 = showImage("Images/Spicy/Deck/1/C1.jpg");
sleep(5);
let file2 = showImage("Images/Spicy/Deck/1/D1.jpg", 5);
showImage(file1);
sleep(5);
showImage(file2, 5);

// deliberately bad calls
showImage();
showImage(8);
showImage(9, file1);
showImage(file1, 6, 7);
The first four calls to showImage work fine, and the latter four produce the following log output:

Code: Select all

10:48:12 pm SEVERE: No match for function call to showImage()
10:48:12 pm INFO: Candidate functions are:
10:48:12 pm INFO:     showImage(MediaURL)
10:48:12 pm INFO:     showImage(File, Integer)
10:48:12 pm INFO:     showImage(MediaURL, Integer)
10:48:12 pm INFO:     showImage(File)
10:48:12 pm INFO:     showImage(String, Integer)
10:48:12 pm INFO:     showImage(String)
10:48:12 pm SEVERE: No match for function call to showImage(Integer)
10:48:12 pm INFO: Candidate functions are:
10:48:12 pm INFO:     showImage(MediaURL)
10:48:12 pm INFO:     showImage(File, Integer)
10:48:12 pm INFO:     showImage(MediaURL, Integer)
10:48:12 pm INFO:     showImage(File)
10:48:12 pm INFO:     showImage(String, Integer)
10:48:12 pm INFO:     showImage(String)
10:48:12 pm SEVERE: No match for function call to showImage(Integer, File)
10:48:12 pm INFO: Candidate functions are:
10:48:12 pm INFO:     showImage(MediaURL)
10:48:12 pm INFO:     showImage(File, Integer)
10:48:12 pm INFO:     showImage(MediaURL, Integer)
10:48:12 pm INFO:     showImage(File)
10:48:12 pm INFO:     showImage(String, Integer)
10:48:12 pm INFO:     showImage(String)
10:48:12 pm SEVERE: No match for function call to showImage(File, Integer, Integer)
10:48:12 pm INFO: Candidate functions are:
10:48:12 pm INFO:     showImage(MediaURL)
10:48:12 pm INFO:     showImage(File, Integer)
10:48:12 pm INFO:     showImage(MediaURL, Integer)
10:48:12 pm INFO:     showImage(File)
10:48:12 pm INFO:     showImage(String, Integer)
10:48:12 pm INFO:     showImage(String)
User avatar
FrozenWolf
Explorer At Heart
Explorer At Heart
Posts: 434
Joined: Tue Oct 30, 2018 7:50 pm
Gender: Male
Sexual Orientation: Straight
I am a: Switch
Dom/me(s): None
Sub/Slave(s): None
Location: UK

Re: [Tease Program] Tease-AI Java (1.3)

Post by FrozenWolf »

Added pull request relating to the previous post.
https://github.com/GodDragoner/TeaseAIJava/pull/24

(I won't be offended if you don't accept it since it is just a refactor and doesn't actually change any functionality!)
illit
Curious Newbie
Curious Newbie
Posts: 3
Joined: Mon Jan 18, 2021 5:57 am
Gender: Male
Sexual Orientation: Straight

Re: [Tease Program] Tease-AI Java (1.3)

Post by illit »

Hello, I have a small request. Would be great to have some brief instructions on how to build TAJ from source (for non-Java developers), or maybe a build script. I would like to have the latest changes, even if there is no new release yet, but I don't want to learn how to work with Java compiler.
Post Reply