无法使用 obstmusic Java MacOS 音乐应用程序的 applescript 包装程序将播放列表投射到 FolderPlaylist

Unable to cast Playlist to FolderPlaylist using obstmusic Java applescript wrapper for MacOS Music app

尝试在 Java 上使用 https://github.com/japlscript/obstmusic 与 macOS 上的 Apple Music 应用对话,我曾经编写原生 AppleScript,然后 java applescript 库,但已从 Java.

在这种方法中,它会查找一个名为 songkong 的现有文件夹播放列表,它会找到它,然后 returns 它。如果 none 存在,那么它会创建这样一个文件夹,然后 returns 它。

 private FolderPlaylist getPlayListFolder()
    {
        Application app = Application.getInstance();
        com.tagtraum.macos.music.Playlist[] songKongPlaylists = app.getPlaylists();
        for(com.tagtraum.macos.music.Playlist next:songKongPlaylists)
        {
            if(next.getName().equals("songkong"))
            {
                return (com.tagtraum.macos.music.FolderPlaylist)next;
            }
        }

        Object songkongPlaylist = app.make(FolderPlaylist.class);
        if(songkongPlaylist instanceof FolderPlaylist)
        {
            ((FolderPlaylist)songkongPlaylist).setName("songkong");
            return ((FolderPlaylist)songkongPlaylist);
        }
        return null;
    }

我第一次 运行 它时我必须创建一个文件夹播放列表,因为不存在它工作,但如果我再次 运行 所以它找到一个现有的文件夹播放列表然后失败抱怨关注

4/04/2022 14.53.25:BST:OSXUpdateItunesWithChanges:updateItunes:SEVERE: *** Unable to run itunes update:class jdk.proxy2.$Proxy62 cannot be cast to class com.tagtraum.macos.music.FolderPlaylist (jdk.proxy2.$Proxy62 is in module jdk.proxy2 of loader 'app'; com.tagtraum.macos.music.FolderPlaylist is in unnamed module of loader 'app') java.lang.ClassCastException: class jdk.proxy2.$Proxy62 cannot be cast to class com.tagtraum.macos.music.FolderPlaylist (jdk.proxy2.$Proxy62 is in module jdk.proxy2 of loader 'app'; com.tagtraum.macos.music.FolderPlaylist is in unnamed module of loader 'app') at com.jthink.songkong.ituneshelper.OSXUpdateMusicWithChanges.getPlayListFolder(OSXUpdateMusicWithChanges.java:41) at com.jthink.songkong.ituneshelper.OSXUpdateMusicWithChanges.createPlaylist(OSXUpdateMusicWithChanges.java:56) at com.jthink.songkong.ituneshelper.OSXUpdateItunesWithChanges.analyseFiles(OSXUpdateItunesWithChanges.java:246) at com.jthink.songkong.ituneshelper.OSXUpdateItunesWithChanges.updateItunes(OSXUpdateItunesWithChanges.java:126) at com.jthink.songkong.ituneshelper.UpdateItunesWithChanges.call(UpdateItunesWithChanges.java:184) at com.jthink.songkong.ituneshelper.UpdateItunesWithChanges.call(UpdateItunesWithChanges.java:33) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)

我没有使用模块,所以我认为对模块的引用可能具有误导性。更有可能的问题是我必须做的不仅仅是从 Playlist 转换为 FolderPlaylist 但我找不到替代方案。

好的我解决了这个问题你必须使用 Reference cast() 方法而不是做通常的 Java cast

例如

for(com.tagtraum.macos.music.Playlist next:songKongPlaylists)
{
    if(next.getName().equals("songkong"))
    {
       return next.cast(FolderPlaylist.class);
    }
}

但我不明白为什么会这样,我想这只是 java 做事方式和 Apple 做事方式之间的不完美映射。

ObstMusic uses JaplScript to talk to Apple's Music app via AppleScript (in an imperfect way). It does this by creating dynamic proxies for Java 已为 Music 的 AppleScript 类.

生成的界面

现在,您的代码中发生了什么?

com.tagtraum.macos.music.Playlist[] songKongPlaylists = app.getPlaylists();

这里,本质上,ObstMusic 生成一个 AppleScript 片段,要求 Music 所有播放列表。 getPlaylist()方法的签名as follows:

Playlist[] getPlaylists​();

现在,当 JaplScript 为返回的 AppleScript 引用生成动态代理时,它必须弄清楚它必须使用什么类型。理想情况下,它会查看 AppleScript 引用(它 可以 )以确定要使用的类型。但这将意味着另一个 AppleScript 往返(或不......见下面的更新)。所以对于大型集合,这可能需要一段时间。出于性能原因,JaplScript 仅使用您调用的方法中声明的类型。在本例中 Playlist,它是 FolderPlaylist 的超类。但是由于在动态代理生成期间未指定 FolderPlaylist,因此您不能简单地转换为它。这就是为什么您会看到 ClassCastException.

所描述的行为显然不是最方便的,因为它不符合通常的 Java 行为(或许多其他面向对象语言的行为)。

如果您想解决这个问题并且愿意承受性能损失,您可以通过调用 TypeClass typeClass = someJaplScriptProxy.getTypeClass() .您还可以通过调用例如获取每个音乐应用程序界面的 TypeClass TypeClass tc = Playlist.CLASS(注意大小写)。最后,您可以通过调用 Set<java.lang.Class<?>> classes = Application.APPLICATION_CLASSES 来获取所有音乐应用程序接口,其中 returns 为音乐应用程序声明的所有 Java 接口的集合。

将这些放在一起,您可以创建一个从真实 TypeClass 到最具体 Java 接口的映射,并在您的 cast() 调用中使用它,大致如下:

Set<java.lang.Class<?>> classes = Application.APPLICATION_CLASSES;
Map<TypeClass, java.lang.Class<?>> typeClassToJava = new HashMap<>();
for (final Class<?> c : classes) {
    typeClassToJava.put(TypeClass.fromClass(c), c);
}

使用此映射,您可以遍历返回的播放列表数组并将所有播放列表对象转换为它们的实际(最具体)类型,并解决您遇到的问题。

2022 年 4 月 21 日更新:

从版本 3.4.11 (Obstmusic 0.9.6) 开始,JaplScript 在创建具有最具体的 Java 适合 AppleScript 对象说明符的接口的动态代理方面要好得多。这意味着,您可能再也不需要手动投射了。