xbmcvfs.exists() problem and lack of xbmcvfs.isdir()
#1
I am trying to port the Advanced Launchers filesystem operations from using the Pyhton standard library to use Kodi VFS API. The reason for this is to allow users to have both ROMs and artwork in Kodi remotes. With the current version of the Advanced Launchers, both ROMs and artwork must be available as a local filesystem (in Linux parlance, the remote filesystem must be mounted using autofs, sshfs, etc.).

The problem is as follows. xbmcvfs.exists(path) fails if path is a directory not ending in / or \. xbmcvfs does not include an isdir() method, so currently there is no way to solve this situation.

Can you please add a new function xbmcvfs.isdir(path) that does the same as Python os.path.isdir(path)? By the way, a new method xbmcvfs.isfile(path) will be also very convenient.
Reply
#2
Now that Leia has been branched away from master, I would like to bump this thread.

xbmcvfs.isdir(path) and xbmcvfs.isfile(path) would be great additions for addons that have to deal with many files!
Reply
#3
A hacky workaround you can use xbmc.vfs.listdir() to create a test.
Image Lunatixz - Kodi / Beta repository
Image PseudoTV - Forum | Website | Youtube | Help?
Reply
#4
(2019-04-17, 15:56)Lunatixz Wrote: A hacky workaround you can use xbmc.vfs.listdir() to create a test.

Thanks Lunatixz. Yes, I can use that to test if something is a directory. The problem is, if something is really a directory and have a lot of files inside the performance penalty is huge.

The main problem is that in Unix (Linux) by default paths do not end in a /, even if path_str is a directory, because there is the isdir() and isfile() function to determine the nature of path_str if necessary. For example:

Code:
$ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.path.join('/home/kodi', 'ROM_name')
'/home/kodi/ROM_name'
>>> os.path.join('/home/kodi/file', 'ROM_name')
'/home/kodi/file/ROM_name'
>>> os.path.join('/home/kodi/file.zip', 'ROM_name')
'/home/kodi/file.zip/ROM_name'

Kodi VFS API, however, is inconsistent with this behaviour, because functions like exist() require the trailing / character. In other words, you must know in advance if something is a directory or not, which currently is impossible with the Kodi VFS API in an efficient way.
Reply
#5
(2019-04-17, 19:25)Wintermute0110 Wrote:
(2019-04-17, 15:56)Lunatixz Wrote: A hacky workaround you can use xbmc.vfs.listdir() to create a test.

Thanks Lunatixz. Yes, I can use that to test if something is a directory. The problem is, if something is really a directory and have a lot of files inside the performance penalty is huge.

The main problem is that in Unix (Linux) by default paths do not end in a /, even if path_str is a directory, because there is the isdir() and isfile() function to determine the nature of path_str if necessary. For example:
Code:
$ python3
Python 3.7.3 (default, Apr 3 2019, 05:39:12)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.path.join('/home/kodi', 'ROM_name')
'/home/kodi/ROM_name'
>>> os.path.join('/home/kodi/file', 'ROM_name')
'/home/kodi/file/ROM_name'
>>> os.path.join('/home/kodi/file.zip', 'ROM_name')
'/home/kodi/file.zip/ROM_name'

Kodi VFS API, however, is inconsistent with this behaviour, because functions like exist() require the trailing / character. In other words, you must know in advance if something is a directory or not, which currently is impossible with the Kodi VFS API in an efficient way. 

xbmcvfs.exist() has i/o penaltes too, BTW.
Why do you u need to stay in Kodi's VFS? you can use xbmc.translatePath() get the 'real path' then use os.isDir()
You can also test for file extension... I agree it would be nice to have isDir, isFile for Kodis VFS but for now there are workarounds.
Image Lunatixz - Kodi / Beta repository
Image PseudoTV - Forum | Website | Youtube | Help?
Reply
#6
(2019-04-17, 19:40)Lunatixz Wrote: xbmcvfs.exist() has i/o penaltes too, BTW.
Why do you u need to stay in Kodi's VFS? you can use xbmc.translatePath() get the 'real path' then use os.isDir()
You can also test for file extension... I agree it would be nice to have isDir, isFile for Kodis VFS but for now there are workarounds.

Before Retroplayer, ROMs ZIP files or ISO images must be in a local filesystem because the external emulators (MAME, Retroarch) do not understand Kodi VFS al all. The situation has totally changed with Retroplayer, where some cores already support VFS. A feature long requested by many users is the ability to have ROMs and assets in Kodi VFS remotes, because most users have an HTPC computer with a small disk where Kodi installed and then a NAS to store media. When the user has both ROMs and assets in a remote I cannot use Python FS functions at all, only Kodi VFS.

In the Advanced Launchers I use isdir() to check for errors. If a user uses the Kodi GUI wizard to create the launchers, I can force xbmc.Dialog.browse() to return a directory when required. However, users with big collections may have as much as 50 launchers (SNES, NES, Game Boy, etc.) and using the wizard is not practical. For those, I created the XML configurations:

Code:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<advanced_emulator_launcher_configuration>
<launcher>
  <name>Atari 2600 (Retroarch)</name>
  <category>Atari</category>
  <Launcher_NFO>../Launcher assets Atari/Atari 2600.nfo</Launcher_NFO>
  <platform>Atari 2600</platform>
  <application>/home/kodi/bin/retroarch</application>
  <args>-L /home/kodi/bin/libretro/stella_libretro.so -v -f &quot;$rom$&quot;</args>
  <ROM_path>/home/kodi/AEL-ROMs/atari-2600/</ROM_path>
  <ROM_ext>zip</ROM_ext>
  <ROM_asset_path>/home/kodi/AEL-assets/atari-2600/</ROM_asset_path>
  <Asset_Prefix>../Launcher assets Atari/Atari 2600</Asset_Prefix>
</launcher>
</advanced_emulator_launcher_configuration>

However, when using XML for importing launchers I have to check, for example, whether <ROM_path> is actually a directory or not and exists, and report "In Launcher XXXX ROM path YYYY is not a directory" or "In Launcher XXXX, directory YYYY not found", and more.

I agree with you, I can avoid using isdir()/isfile() at all, but that will make error reporting in the Advanced Launchers fuzzy and imprecise. Also, Kodi M**** master was just created and now it is time to propose new features and/or improvements Smile

By the way ... xbmc.Dialog.browse() has the ability to distinguish between files and directories, and obviously works in local as well as remote filesystems. Hence, isdir()/isfile() just need to expose some functionality already known by the core.
Reply
#7
Correct me if I'm wrong, but in my tests the method xbmcvfs.listdir always returns a vector, I passed file paths, empty directories and with files and the return is always the same as a vector varying only to have files or not, at least I couldn't find a way of using this as a hack to test if it's a directory.

In my opinion, if confirmed that the inconsistent behavior exists, a possible way would be:

1. If it is an empty folder, returns an empty vector.
2. If it is a folder with files, it returns a vector with the list of files.
3. If it is a file returns False or returns an error, exception.

Perhaps this method could return a regular list, it would reduce the need for loops to extract the vector directories within python.

And the xbmcvfs.exists method doesn't really work correctly with directories that don't have the / or \ if the os.path.join method is used, it ends up causing confusion.
Reply

Logout Mark Read Team Forum Stats Members Help
xbmcvfs.exists() problem and lack of xbmcvfs.isdir()0