OpenSilk / Orpheus

Orpheus Music Player
https://play.google.com/store/apps/details?id=org.opensilk.music
GNU General Public License v3.0
52 stars 20 forks source link

Documentation required #22

Open X-Ryl669 opened 7 years ago

X-Ryl669 commented 7 years ago

Can you write a bit of documentation about how the plugin and library interface works ? I'd like to write a plugin for Koel / kutr like personal web player, but the code is too complex for me to grasp without some guidance. Thanks.

drewis commented 7 years ago

At this time the best I can offer is to point you to the LibraryProvider [1] base class for plugins. It is mostly documented. The plugin api is overly complex and convoluted. I'm (slowly) working on replacing it with the builtin document provider api [2] and using OPEN_DOCUMENT_TREE [3] to gain access to entire folder hierarchies. Thus turning Orpheus into a purely folder browsing music player.

[1] https://github.com/OpenSilk/Orpheus/blob/master/core-library/src/main/java/org/opensilk/music/library/provider/LibraryProvider.java [2] https://developer.android.com/guide/topics/providers/create-document-provider.html [3] https://developer.android.com/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT_TREE

X-Ryl669 commented 7 years ago

Right now, I've managed to build the code with Android Studio (needed modifications like those below) to support latest glide:

diff --git a/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtInfoRequestStreamLoaderFactory.java b/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtInfoRequestStreamLoaderFactory.java
index c433ab1..7f12398 100644
--- a/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtInfoRequestStreamLoaderFactory.java
+++ b/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtInfoRequestStreamLoaderFactory.java
@@ -29,13 +29,21 @@ import java.io.InputStream;
  * Created by drew on 12/25/15.
  */
 public class ArtInfoRequestStreamLoaderFactory implements ModelLoaderFactory<ArtInfoRequest, InputStream> {
+
+    private final Context context;
+
     @Override
-    public ModelLoader<ArtInfoRequest, InputStream> build(Context context, MultiModelLoaderFactory multiFactory) {
-        return new ArtInfoRequestStreamLoader(context);
+    public ModelLoader<ArtInfoRequest, InputStream> build(MultiModelLoaderFactory multiFactory) {
+        return new ArtInfoRequestStreamLoader(this.context);
     }

     @Override
     public void teardown() {
         //pass
     }
+
+    public ArtInfoRequestStreamLoaderFactory(Context context)
+    {
+        this.context = context;
+    }
 }

diff --git a/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtworkGlideModule.java b/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtworkGlideModule.java
index 7b72664..8bdf083 100644
--- a/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtworkGlideModule.java
+++ b/core-artwork/src/main/java/org/opensilk/music/artwork/glide/ArtworkGlideModule.java
@@ -61,6 +61,6 @@ public class ArtworkGlideModule implements com.bumptech.glide.module.GlideModule

     @Override
     public void registerComponents(Context context, Registry registry) {
-        registry.append(ArtInfoRequest.class, InputStream.class, new ArtInfoRequestStreamLoaderFactory());
+        registry.append(ArtInfoRequest.class, InputStream.class, new ArtInfoRequestStreamLoaderFactory(context));
     }
 }

I had to disable instant-run, and comment the ACRA stuff (because since I don't have keys, it failed to build because a static final String something = null is not a constant that can be used in @ReportCrash part.

Finally, it built but it's very unstable in the emulator (the app crashes a lot), and debugging is a pain with all these process (ui, prvdr, service, etc...). How do you debug the application ?

What I want to do is to write a plugin to support Kutr-like (or Koel) personal music provider. Such provider can be browsed by genres or artists > albums > songs or folders (Kutr only). If you change to "folder-only" music player, then Koel's users will not like it (or maybe I'm misreading you). There is also the Vanilla music player that's folder only, so I might not worth the effort, does it ?

Anyway, my initial idea was to duplicate the library-mediastore to make a "remote" version, but then I realized that it's frozen in the application to use that exact provider.foldersLibrary provider and unless modifying the application, it would not work to have another similar instance.

Then I looked into the plugin format (like plugin-upnp and library-upnp) but I failed to understand what's in there. I don't see where UPNP is queried and where it's building it's library model that the app is using. Also, I don't see where it's registering with the app, so that the app knows it's there and start instantiating it.

I'm not too familiar with many of the tools used in Orpheus (like Dagger, Mortar and so on), but I'm willing to understand how it works, and how hard it will be to implement what I want to implement.

So, can you describe a little bit how the app is working (what model it expect, how it finds its providers, how it's querying them, what it expects and so on), and/or the data flow ?

drewis commented 7 years ago

The libraries are just ContentProviders. Except we don't use the normal methods like query, everything is routed through the out-of-band call method. The design is largely based on how document providers work. The bulk of the logic is handled in the base LibraryProvider, and subclasses need to override the methods they want to support [3]. The library operates exclusively via RxJava Observables (well they wrap a custom cross process observable which is what bundleable and friends are for). The library-drive plugin is probably the best one to read through as it is the simplest. [4]

Orpheus finds the libraries by scanning for the LIBRARY_PROVIDER action [1]. Look in the AndroidManifest.xml for how it is declared [2].

You should be able to copy the library-mediastore as long as you change the authority declared in the manifest. You can also build your library in a separate apk like the drive and upnp plugins.

Mortar is just used to persist the singleton dagger component, dagger is used mostly for instantiation. Neither are required for library plugins. The main thing is RxJava which is used everywhere.

Flow as I recall. Its been a while so this may be wrong.

[1] https://github.com/OpenSilk/Orpheus/blob/master/core-library/src/main/java/org/opensilk/music/library/provider/LibraryProvider.java#L57 [2] https://github.com/OpenSilk/Orpheus/blob/master/library-mediastore/src/main/AndroidManifest.xml [3] https://github.com/OpenSilk/Orpheus/blob/master/core-library/src/main/java/org/opensilk/music/library/provider/LibraryProvider.java#L257 [4] https://github.com/OpenSilk/Orpheus/blob/master/library-googledrive/src/main/java/org/opensilk/music/library/drive/provider/DriveLibraryProvider.java [5] https://github.com/OpenSilk/Orpheus/blob/master/core-model/src/main/java/org/opensilk/music/model/Track.java

X-Ryl669 commented 7 years ago

Thank you very much. I'll have a look.