@Peter , could you help with the following, please?
1.
playlist_manager_v4::create_playlist_ex /
playlist_manager_v4::create_playlist_ex: in which case can it return pfc_infinite? Is it more like paranoia check (i.e.
assert would suffice) or should I always validate it?
2. What is assigned to the return value of
playlist_manager::playlist_insert_items? Is it the new position of the first track (i.e. same as p_base argument)?
3. Is there any difference between using u* methods from shared.dll and using corresponding WinAPI methods? E.g.
uCreateFile vs
CreateFileThanks in advance =)
1.
Playlist creation will fail if called from an illegal context - wrong thread, called from a global callback, fb2k shutdown in progress.
If your code already ensures running in main thread, an assert check should be sufficient.
You cannot call any methods that alter the state of playback, playlist, etc from a global callback - such as playlist/playback/etc callback - because the method by itself dispatches callbacks, which cannot be done recursively.
2.
Returned value from playlist_insert_items is the index of the first inserted track - or pfc_infinite on failure (conditions above or an autoplaylist that refuses insertions).
3.
u* methods used to be relevant when Win9X was still supported - Win9X users got a different build of the DLL which called non-Unicode APIs. By now there's no difference. These methods are mainly retained for backwards compatibility. Though you can sometimes reduce the size of your component by calling them instead of converting UTF-8 to wchar_t by yourself.
Thanks!
1.
Playlist creation will fail if called from an illegal context - ... fb2k shutdown in progress ...
Am I right to assume that this condition won't be triggered until after
initquit::on_quit callback is executed?
And another small question:
Can
autoplaylist_manager::add_client_simple(p_query, p_sort, p_playlist, p_flags) fail on a newly created playlist?
If your component creates playlist in direct response to user events, it is safe to assume that shutdown is not yet in progress.
initquit::on_quit() is one way to know about shutdown in progress, but if some other component decides to do stuff that triggers your code in their on_quit(), your on_quit() might not yet have executed.
autoplaylist_manager::add_client_simple() on a newly created playlist should never fail. The current implementation throws an exception if the specified playlist index is invalid, or if the playlist is already an autoplaylist, neither of which can be true in your case.
Thanks for all the answers!
PS: It appears that add_client_simple may also fail when the query is incorrect (e.g. `PRESENT ALL`).
@Peter, a small fb2k API question: is
modal_dialog_scope::can_create() thread-safe? (shared.h)
Thanks in advance!
If anyone stumbles on the question above: judging by disassembly, it seems that modal_dialog_scope::can_create() always returns false when invoked on non-main thread.
Everything modal dialog related should only ever be called from main thread.
modal_dialog_scope::can_create() returns false when either called from a non main thread or there already is a known modal dialog running.
Figured as much, thanks!
@Peter, another small API question =)
What's the purpose of
modal_dialog_scope? Is it purely a FYI method (i.e. indication that it would be possible to create a modal dialog)? Is it safe to invoke a modal dialog without wrapping it's call in
modal_dialog_scope?
And another question: what is the correct way for a component to generate a dynamic main menu (i.e. so that it could be changed after component initialization)? There is such menu for playlists ('View>Switch to playlist'), but I'm not sure how to achieve that....
I've found 'mainmenu_commands_v2' which might be what I need: there is a `mainmenu_commands_v2::dynamic_execute` method which accepts subId GUID, but should this GUID be distinct from all the other menu GUIDs? If so, then should I run a check against already defined GUIDs every time I add a new menu item?
modal_dialog_scope exists to prevent accidental creation of another modal dialog while a modal dialog is already in progress. Execution of various fb2k menu commands is not blocked during a modal dialog, but you can at least know that there's a modal dialog in progress and refuse to create another one - which could lead to modal dialogs getting dismissed out of order or worse.
Dynamic mainmenu commands will be added to foo_sample.
Thanks for the answers!
modal_dialog_scope exists to prevent accidental creation of another modal dialog while a modal dialog is already in progress. Execution of various fb2k menu commands is not blocked during a modal dialog, but you can at least know that there's a modal dialog in progress and refuse to create another one - which could lead to modal dialogs getting dismissed out of order or worse.
Hm... So that means that it is actually *required* for the component to wrap every modal dialog creation call. I think it would be nice to add this to 'modal_dialog_scope' documentation =)
Dynamic mainmenu commands will be added to foo_sample.
Thanks!
Aaaand another one:
Is it possible to disable `space` key handling by fb2k in DUI so that it could be passed (and handled) by panel? E.g. user wants to enter some text.
@Peter: another question, if you don't mind =) What is the proper way to execute API action that requires initialized fb2k?
Example scenario #1: I want to show popup via `popup_message::g_show` during CUI panel creation (which happens before component's `on_init` is called). If invoked before fb2k is initialized, no popup will show up.
Example scenario #2: invocation timing is the same as above, but I need fb2k handle (via `core_api::get_main_window()`). This handle will be nullptr if requested before fb2k initialization.
Current workaround: queue all required action and execute them in the component's `on_init` callback.
Other workaround: queue actions via `fb2k::inMainThread` (this uses message queue probably, which activates only after fb2k initialization).
PS: The question above (https://hydrogenaud.io/index.php/topic,117208.msg973490.html#msg973490) is still relevant =)
@Peter, another small question (that's slightly similar to the one above):
Am I correct to assume that
init_stage_callback::on_init_stage(init_stages::after_ui_init) will be called after *all* components have been initialized?
Sounds like CUI panel creation occurs when there's no known main window yet. Apparently the popup_message code doesn't like that - does nothing instead of creating a parentless (toplevel) dialog.
initquit::on_init() gets called when everything's initialized, main window has been created and all other APIs are valid to call.
init_stage_callback::on_init_stage(init_stages::after_ui_init) gets called just before initquit::on_init().
In general, fb2k::inMainThread() should work for deferring problematic API calls.
Or, check if core_api::get_main_wiindow() returns null; if so, store the messages and show popups from initquit::on_init().
Not sure what you mean about space key handling, embedded Album List in Default UI can take space key in its editbox just fine?
Neat trick with the [font] for code bits, I'll have to remember that one
Thanks for all the answer! Really appreciated!
Not sure what you mean about space key handling, embedded Album List in Default UI can take space key in its editbox just fine?
You are absolutely right: this is a bug/feature in my code... Sorry for the confusion >_<
Neat trick with the [font] for code bits, I'll have to remember that one
I was just too used to being able to inline `code blocks` in markdown. Thankfully, Google knew the answer =)
@Peter, the problem is that
initquit::on_init in one component might be called before the
initquit::on_init in another component.
E.g. my component is already up (
initquit::on_init was called) and tries to use another component, which is not yet loaded completely (have yet to handle
initquit::on_init).
Is there a callback (or maybe a bool check, which would be harder to abuse by component devs) to verify that fb2k has loaded all components (invoked all
initquit::on_init callbacks) and thus it's safe to call any component?
@Peter, another day - another API question =)
Does API guarantee that input implemented with `input_factory_t` will not be called before `initquit::on_init` and after `innitquit::on_quit` (all implemented in the same component)?
No guarantees about inputs being called before on_init() or after on_quit().
Better just init on first use (std::call_once) and never deinit - only make sure all worker threads exit before process exits.
Thanks for the answer!
Another small question: is it possible to get the metadb of the next track? I mean not 'next track in playlist', but the actual track that will be played next. Scenario: pre-loading the next track (if needed) to avoid hiccups.
Next track info is not currently available. Decision on what to play next (random etc) is made at the point decoding of the previous track has EOF'd, so the info doesn't physically exist until the track needs to start decoding.
Darn... That's a shame... Thanks!
Aaaand another few small questions =)
1. Is it possible to create a custom auto-playlist, i.e. an auto-playlist that is managed by component?
Scenario: Spotify (a music streaming service) provides a playlist interface via an url. So ideally, a user could pass such link to the component, which would create an playlist that would be periodically synchronized with the Spotify service.
2. Is it possible to import multiple files via a single `File` > `Add location`? I.e. not as a single track with subsongs (`input_factory_t`), but as multiple separate tracks?
Scenario: same as above - an import of Spotify playlist.
Thanks in advance =)
1. Is it possible to create a custom auto-playlist, i.e. an auto-playlist that is managed by component?
Scenario: Spotify (a music streaming service) provides a playlist interface via an url. So ideally, a user could pass such link to the component, which would create an playlist that would be periodically synchronized with the Spotify service.
Considering that the component is responsible for periodically populating the playlist, you can easily reproduce such a scenario, if you set a playlist lock (playlist.h, playlist_lock) to the target playlist, which will be only deactivated during populating the playlist.
Autoplaylists are internally implemented via playlist locks. It's not that pretty but works.
Current autoplaylist API only filters Media Library content, rather than providing its own content.
Re: Add Location - it's meant for remote resources... subsongs are not supported for such in general. You'd have to parse the location as a playlist instead.
@fbuser ,
@Peter , thanks for the answers!
Considering that the component is responsible for periodically populating the playlist (...)
But is it possible to somehow `assign` a playlist to the component? I would not want to edit a random user-playlist if they decide to name it the same as component-managed-playlist =)
You'd have to parse the location as a playlist instead.
Could you, plz, point to the appropriate API for that?
But is it possible to somehow `assign` a playlist to the component? I would not want to edit a random user-playlist if they decide to name it the same as component-managed-playlist =)
Yes, you could mark that playlist with playlist_manager_v2::playlist_set_property() so you can distinguish this playlist from a user-playlist with the same name.
Could you, plz, point to the appropriate API for that?
You already have this implemented as plman.AddLocations in SMP using
process_locations_notify and
playlist_incoming_item_filter_v2 - >process_locations_async
@snotlicker , I'm not sure that these are applicable for my case: AFAIK these are used to handle callbacks *after* `FIle`>`Add location...` was invoked. And in my scenario I need to override the default behaviour of `Add location...` to generate the list of items first.
I've found `playlist_loader` API which looks exactly like the thing I need, but it doesn't work reliably for all cases and it can't handle arbitrary strings. E.g. `https://open.spotify.com/playlist/191UV79Bhgy1mBJOVfIpeS` fails with error `Object not found` for some reason. While `https://open.spotify.com/album/06bbY7KesJd8be5vQOMBxw` works fine :\
Oh and thank you
@fbuser for the tips =)
@Peter , would it be possible to make `playlist_loader` receive a path even if it's not recognized by fb2k? E.g. pass smth like `unrecognized` to `playlist_loader::is_our_content_type()`
The previous component was able to differentiate the different url types here??
https://github.com/stengerh/foo_input_spotify/blob/master/foo_input_spotify/input_spotify.cpp#L103L117
I guess libspotify did all the work but for whatever reason, the user playlist handling is borked while track/album urls load just fine. Isn't this where you need to step and in and make your web request to the spotify API and generate a list of track urls which libspotify can then open/play??
The previous component implemented everything via `input` interface: `album` and `playlist` objects would be handled as a multi-track file (like CUE files). Such behaviour has a lot of limitations, that's why I'm trying to use `playlist` interface instead and use `input` only for single tracks.
Isn't this where you need to step and in and make your web request to the spotify API and generate a list of track urls which libspotify can then open/play??
That's actually what I'm trying to do =)
It's all fine and dandy for `album` urls, but for some reason fb2k discards spotify `playlist` urls, making it impossible to generate a playable item set... There are no such limitations for `input` interface though.
Add your own main menu entry/input box. Now you have full control over how urls are handled.
Add your own main menu entry/input box.
That's my Plan B =)
But I would rather use (and integrate in) standard fb2k interface as much as possible.
Then you won't be able to take over arbitrary URLs. For the standard Open Location prompt, you can only control either whole schemas, or the MIME type that the server returns when the file is retrieved using foobar's built-in HTTP/S handler.
you can only control either whole schemas
Sorry, I don't understand what you mean by that (most likely due my lack of knowledge). `MIME type` is controlled via
playlist_loader::is_our_content_type. Is there a similar interface to handle the `schema` (w/e it means in this context)?
(...)when the file is retrieved using foobar's built-in HTTP/S handler.
Any tips why this handler would reject a valid url?
It accepts it, if it understands what file type it is. You have to register a playlist handler that accepts the exact MIME type your file returns. Otherwise, it will reject it.
Also, if it requires a login, there's no way to pass the login cookie or session to the URL handler, because every URL request handled this way explicitly passes zero cookies and only the exact information contained within the URL string.
As for the schema, you would have to register your own filesystem handler for a new protocol name, something like httpspotify or whatever, and you'd have to handle your own URL requests, or forward them to the existing https handler by modifying the URL and opening it directly yourself.
Thanks for the info!
I've tried fetching the Spotify playlist url via
curl with a plain
GET request and it does indeed return a 404 error for some reason (it is accessible in browser without logging in though)... Not sure what's the deal with that (and why it works in browser), but it's clearly not the fault of fb2k.
As for the schema, you would have to register your own filesystem handler for a new protocol name
Would it work properly if I implement a new handler that tries to handle `http(/s)`? I.e. would it conflict with the built-in one? Or would such approach be not recommened/too hacky/error-prone/unstable or w/e?
Darn... This problem affects drag-n-drop functionality as well: if the GET request for the link returns 404, fb2k prevents further item processing with "Object not found" message =(
I've figured it out why URL returns 404: it requires a proper user agent header (e.g. "User-Agent: Firefox/79.0").
Google says there are quite a few of websites that refuse to work (by returning 404) with empty or non-browser-specific user-agents...
@Peter ,
@kode54 , would it be possible to attach some user agent when resolving links? (perhaps configurable via Advanced Preferences)
@Peter,
another small question =)
Scenario:
- component has a custom `input_impl` implemented.
- user requests properties of multiple tracks (e.g. via selecting all those tracks and ALT-Enter).
- `input_impl::open` is called.
Questions:
- Is there any interface/callback/etc that would make it possible to process multiple tracks at once instead of one by one?
- If not, is it possible to force these to be executed parallely? I.e. several threads that would call `input_impl::open` simultaneously, so that it would be possible to batch requests manually inside the `input_impl::open` implementation.
Reason:
Operations performed in this custom `input_impl::open` are pretty slow, but they can be performed in batch for multiple tracks at the same cost as a single track.
@Peter, another small question: is it possible to add component in run-time? At least for the purpose of updates and stuff in the components settings section.
Scenario: a C++ component is used as a host/loader/proxy for .NET components (I doubt there will be more than one though xD). Everything SDK related (callback registration and etc) is handled by the host C++ component. But I want users to be able to interact just as usual even with said .NET components (check for updates/add/delete and etc).
Probably not, for the same reason it's currently not possible to remove a component at run-time. All callback services must finish executing and be removed from registration. All input services currently decoding files must be removed from registration and cease operation. Basically every service that could be registered must be released by every other still-running service so it isn't referenced again. And things like initquit services must be called to simulate app shutdown for that component, in case the component relies on it for things like saving configuration.
Component configuration variables must also be serialized for saving on shutdown and removal of the component as well.
In the current SDK design, the player literally has no idea what components a particular service belongs to. And it completely randomizes the order of that global service table on every startup, to provoke possible race conditions from components that expect a deterministic load order to work.
@kode54 , thanks for the answer!
I've found that it's possible to at least display such "external" components in Preferences components tab (like this >url (https://github.com/TheQwertiest/foo_title/blob/deeece65764ce21837cbafbce56ca35e9635dddc/foo_dotnet_component_host/fb2k/component_registration.cpp)<). Do you think that the version update check will work if I add those components to the fb2k component repo? I.e. without automatic download/update/removal, just the version check so that users can be notified when new version appears.
Since I'd rather avoid reimplementing fb2k component manager as much as possible :D
The app does report newer component releases from the repository, but I don't know if it can simply open a web page with updates for newer versions if only a link is supplied, or if it requires an explicit package to be uploaded to the site.
Guess I'll have to find out by experimenting =)
@Peter, a small API question: is >http client interface< (https://github.com/TheQwertiest/foobar2000-sdk/blob/master/SDK/http_client.h) thread-safe? I.e. can it be called from different threads without additional synchronization?
The http_client object is just an entrypoint for creating request objects so you can call http_client::get()->create_request(...) it from any thread you like.
Individual request and reply objects are not thread safe, that is you should synchronize if you want to call methods of the same object from different threads, which probably isn't what you want as I have no idea why anyone would do such thing.
I was thinking of off-loading web access to a worker thread, so this works out nicely for me =)
Thanks!
A similar question, but regarding album_art_extractor_instance::query and album_art_extractor_instance_v2::query_paths (>link< (https://github.com/TheQwertiest/foobar2000-sdk/blob/master/SDK/album_art.h#L202)). Can it be used safely off the main thread as well?
Your async album art methods in SMP inherited from JSP and originally WSH panel are already doing that.
If something takes an abort_callback&, it's designed for use in worker threads.
I'm cleaning the documentation up, I'll add info about all these.
I know it's not a pretty way of determining what is safe to call and what isn't, but I made most of main-thread-only-by-design functions crash/bugcheck if called from another thread.
If something takes an abort_callback&, it's designed for use in worker threads.
Nice! Now I don't have to bug you about each and every API point :D
Thanks =)
Your async album art methods in SMP inherited from JSP and originally WSH panel are already doing that.
Right! That's what I get for not servicing my code for too long...