WebAPI: Add metadata in /app/getDirectoryContent response

## Description

Send file/folder metadata instead of just the name of a filesystem entry.
Currently the endpoint only sends a list of string, containing the path of each entry, without specifying its type (file or folder).
The optional `withMetadata` flag has been added to provide metadata and to prevent breaking changes with older versions.
If `true`, JSON response will be an array of objects instead of an array of strings.

This object contains:
- `name`: the name of the file system entry (without path)
- `type`: Whether the file system entry is a "file" or a "dir"
- `creation_date`: file system entry's creation date
- `last_access_date`: file system entry's last access date
- `last_modification_date`: file system entry's last modification date

If the entry is a file, a `size` field is present with the file size in bytes.

## Objective

Build a server file browser inside WebUIs, feature is currently being developed for VueTorrent.
It will include file metadata, filtering and sorting on the different fields.

PR #22813.
This commit is contained in:
Rémi Marseault 2025-06-07 17:02:48 +02:00 committed by GitHub
parent 05bcc4e175
commit 8e2125ee72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 4 deletions

View File

@ -1,5 +1,26 @@
# 2.11.6
* https://github.com/qbittorrent/qBittorrent/pull/22460
# WebAPI Changelog
## 2.11.8
* [#21349](https://github.com/qbittorrent/qBittorrent/pull/21349)
* Handle sending `204 No Content` status code when response contains no data
* Some endpoints still return `200 OK` to ensure smooth transition
* [#22750](https://github.com/qbittorrent/qBittorrent/pull/22750)
* `torrents/info` allows an optional parameter `includeFiles` that defaults to `false`
* Each torrent will contain a new key `files` which will list all files similar to the `torrents/files` endpoint
* [#22813](https://github.com/qbittorrent/qBittorrent/pull/22813)
* `app/getDirectoryContent` allows an optional parameter `withMetadata` to send file metadata
* Fields are `name`, `type`, `size`, `creation_date`, `last_access_date`, `last_modification_date`
* See PR for TypeScript types
## 2.11.7
* [#22166](https://github.com/qbittorrent/qBittorrent/pull/22166)
* `sync/maindata` returns 3 new torrent fields: `has_tracker_warning`, `has_tracker_error`, `has_other_announce_error`
## 2.11.6
* [#22460](https://github.com/qbittorrent/qBittorrent/pull/22460)
* `app/setPreferences` allows only one of `max_ratio_enabled`, `max_ratio` to be present
* `app/setPreferences` allows only one of `max_seeding_time_enabled`, `max_seeding_time` to be present
* `app/setPreferences` allows only one of `max_inactive_seeding_time_enabled`, `max_inactive_seeding_time` to be present

View File

@ -77,6 +77,12 @@ const QString KEY_COOKIE_DOMAIN = u"domain"_s;
const QString KEY_COOKIE_PATH = u"path"_s;
const QString KEY_COOKIE_VALUE = u"value"_s;
const QString KEY_COOKIE_EXPIRATION_DATE = u"expirationDate"_s;
const QString KEY_FILE_METADATA_NAME = u"name"_s;
const QString KEY_FILE_METADATA_TYPE = u"type"_s;
const QString KEY_FILE_METADATA_SIZE = u"size"_s;
const QString KEY_FILE_METADATA_CREATION_DATE = u"creation_date"_s;
const QString KEY_FILE_METADATA_LAST_ACCESS_DATE = u"last_access_date"_s;
const QString KEY_FILE_METADATA_LAST_MODIFICATION_DATE = u"last_modification_date"_s;
void AppController::webapiVersionAction()
{
@ -1210,10 +1216,40 @@ void AppController::getDirectoryContentAction()
throw APIError(APIErrorType::BadParams, tr("Invalid mode, allowed values: %1").arg(u"all, dirs, files"_s));
};
const bool withMetadata {Utils::String::parseBool(params()[u"withMetadata"_s]).value_or(false)};
QJsonArray ret;
QDirIterator it {dirPath, (QDir::NoDotAndDotDot | parseDirectoryContentMode(visibility))};
while (it.hasNext())
ret.append(it.next());
{
if (withMetadata)
{
const QFileInfo fileInfo = it.nextFileInfo();
QJsonObject fileObject
{
{KEY_FILE_METADATA_NAME, fileInfo.fileName()},
{KEY_FILE_METADATA_CREATION_DATE, Utils::DateTime::toSecsSinceEpoch(fileInfo.birthTime())},
{KEY_FILE_METADATA_LAST_ACCESS_DATE, Utils::DateTime::toSecsSinceEpoch(fileInfo.lastRead())},
{KEY_FILE_METADATA_LAST_MODIFICATION_DATE, Utils::DateTime::toSecsSinceEpoch(fileInfo.lastModified())},
};
if (fileInfo.isDir())
{
fileObject.insert(KEY_FILE_METADATA_TYPE, u"dir"_s);
}
else if (fileInfo.isFile())
{
fileObject.insert(KEY_FILE_METADATA_TYPE, u"file"_s);
fileObject.insert(KEY_FILE_METADATA_SIZE, fileInfo.size());
}
ret.append(fileObject);
}
else
{
ret.append(it.next());
}
}
setResult(ret);
}

View File

@ -53,7 +53,7 @@
#include "base/utils/version.h"
#include "api/isessionmanager.h"
inline const Utils::Version<3, 2> API_VERSION {2, 11, 7};
inline const Utils::Version<3, 2> API_VERSION {2, 11, 8};
class APIController;
class AuthController;