maotoumao / MusicFreeDesktop

插件化、定制化、无广告的免费音乐播放器
http://musicfree.upup.fun/
GNU General Public License v3.0
2.35k stars 224 forks source link

请求支持从应用目录加载插件 #106

Closed ziqi-cn closed 2 months ago

ziqi-cn commented 2 months ago

我目前正在给loongarch架构打包此软件,有用户希望可以一起打包插件,因此希望可以从应用目录下加载插件,能否支持?

目前软件是从用户目录下加载插件的,如果要把插件打包并用包管理器管理,就需要从应用安装目录下读取插件,我目前已经实现了该逻辑 https://github.com/ziqi-cn/MusicFreeDesktop/commit/33944c9a0b514f1b30fc9a4bb0866b26cc5f2cb1 ,可以同时从用户目录和应用目录下读取插件。

但目前还有几个问题,一是目前加载只比较插件的哈希,相同哈希的插件不加载,但如果两个目录下的插件版本不同如何处理比较合适?然后是不是应该应用目录下的插件在插件页面显示该插件由系统管理禁止用户卸载更新?

或许还有更多细节需要考虑,希望能在此充分讨论。

maotoumao commented 2 months ago

谢谢你的工作~ 简单说下我的想法:

目前其实存在两种类型的插件:

按照之前的设计,我没有考虑到 【扩展内置插件】 这一行为,因此这部分的代码目前的实现其实不是很合理,在代码中应该有个专门的位置存放内置插件,并且它们的调用应该统一管理;

更具体些,在 src\main\core\plugin-manager\index.ts 路径下针对 localPluginHash 的判定应该修改成更加通用的写法:

/** 调用插件方法 */
function callPluginMethod({
  hash,
  platform,
  method,
  args,
}: ICallPluginMethodParams<keyof IPlugin.IPluginInstanceMethods>) {
  let plugin: Plugin;
  // TODO: 修改为更加通用的判定
  if (hash === localPluginHash || platform === localPluginName) { 
    plugin = localPlugin;
  } else if (hash) {
    plugin = plugins.find((item) => item.hash === hash);
  } else if (platform) {
    plugin = plugins.find((item) => item.name === platform);
  }
  if (!plugin) {
    return null;
  }
  const result = plugin.methods[method]?.apply?.({ plugin }, args);
  console.log(plugin, method, args);
  return result;
}

如果以上内容改好的话,应该可以实现一个更通用的【内置插件】。

我个人不太倾向于新增个【应用目录】的概念,因为这样子的话貌似和直接在启动时把内置的代码写入到用户目录下区别不大,以及这部分的插件作为软件内置的行为,不太想直接让用户接触到(防止修改什么导致软件崩掉)。

如果按这个设计思路的话,插件的更新规则、版本比较可以引入一个优先级的规则:

  1. 软件内置部分插件(如本地音乐),优先级最低。
  2. 内置插件可以更新,但无法卸载;更新后会把最新版本放在【用户目录】的插件
  3. 【用户目录】的插件支持正常的更新、卸载,规则和其他普通插件相同;其优先级高于【内置插件】。

似乎这样可以解决问题,有其他的想法也欢迎讨论 😄


我不太了解loongarch架构,如果可以的话可以帮忙科普一下,适配过程中如果有些改动也欢迎提PR 😆

最后,还是希望尽量不要内置一些有版权风险的插件,不然可能又要收函了… 😢

ziqi-cn commented 2 months ago

感谢您的回复。

我的考虑是这样的,“内置插件”作为模块引用必然要和本体打成一个包,那么用户就无法卸载,同时也可能存在版权问题,如果存在一个“应用目录”,那么就可以把本体和插件分别打包,安装本体时包管理器可以将插件作为推荐的依赖一起安装,做成接近开箱即用的形式,用户如果不需要也可以在包管理器中卸载,同时如果出现版权风险也可以单独下架插件包。可以参考LiteLoaderQQNT,在AUR上分成了本体包,和一些插件包:mspring-themechii-devtools,我们认为这可能更符合Linux用户的习惯。

如果直接将插件复制到用户目录,那么我们考虑了有两种方式:一是每次启动都复制一份进去;二是如果没有用户目录,在创建用户目录的时候复制一份插件进去。第一种方法普通用户将无法方便地卸载插件,第二种方法插件包更新或者引入其它插件的时候已经安装的用户就不会自动更新。另外,由于本体和插件是打包到一起的,版权风险也更高。


关于优先级我的思考类似,优先级可以是“用户目录”>“应用目录”>“内置插件”,内置插件和应用目录里的插件更新放到用户目录。由于可能存在不同版本的固件,现在用hash作为插件唯一的标识并不合适,因为同一插件的不同版本hash不会相同,那么就应该考虑其它的标识,现在考虑利用name作为唯一标识,相同name的插件不会重复加载。不知这样设计有没有问题。


loongarch架构基础工具已经比较完善,普通开发者大部分情况下不会遇到问题,唯有一个问题是sharp没有提供loongarch的预编译二进制包,但我看只在Windows下会用到这个包,索性将这个包和相关代码删掉了。

maotoumao commented 2 months ago

抱歉没有及时回复~

其实我还是没太理解【应用目录】存在的意义;因为感觉上如果存在这个应用目录,它的行为会和【用户目录】基本上一致,并且这样软件需要再多一个搜索插件的路径,额外增加了复杂度;

回到问题本身,诉求是“有用户希望可以一起打包插件”,这里的【打包】不知道是否可以理解成用户在安装时直接把插件随着软件本体一起安装,如果这样的话那貌似无论内置还是复制到用户目录,版权风险都是存在的 😢

另外如果在插件中定义了 srcUrl 字段的话,用户目录预置的插件也是可以自动更新的;


在现在的设计里面,platform 更像是插件的唯一标识的概念,插件安装和更新时也会以此字段为依据进行更新;所以如果通过正常操作安装/卸载插件的话,不会存在多个同名插件的问题。

所以如果用户目录下存在多个同名插件(也就是platform字段相同),那大概率是用户自己在目录下放置了多个相同 platform 但版本不同的插件,软件无法替用户决定哪个插件的版本是用户需求的,索性全部保留。但这样的话,比如在搜索结果中可能会出现两个相同的源,为了把它们区分开,所以这种情况下会用 文件hash 去确定用哪个插件进行播放等行为。


关于 sharp 库的问题,不知道在打包非window安装包时可否跳过相关文件~ (我有空了尝试一下~~)

ziqi-cn commented 2 months ago

谢谢,我思考了一下,确实引入【应用目录】这个概念不是必要的,版权问题我再咨询一下。