Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/medialibrary
  • luyikei/medialibrary
  • qiubit/medialibrary
  • lemourin/medialibrary
  • gsoc/GSoC2017/Bubu/medialibrary
  • Nerf/medialibrary
  • Dekans/medialibrary
  • Skantes/medialibrary
  • robUx4/medialibrary
  • bubu/medialibrary
  • craig_janeway/medialibrary
  • rom1v/medialibrary
  • tguillem/medialibrary
  • b1ue/medialibrary
  • Corbax/medialibrary
  • ilearninging/medialibrary
  • stevengivens542/medialibrary
  • fkuehne/medialibrary
  • gorbahaaa/medialibrary
  • 1480c1/medialibrary
  • ashleerenee2727/medialibrary
  • ashrafuddin5636/medialibrary
  • hkosacki/medialibrary
  • joykim/medialibrary
  • gsoc/gsoc2020/arnav-ishaan/medialibrary
  • elbrujo1987/medialibrary
  • asenat/medialibrary
  • Aza/medialibrary
  • gtskhadadze83/medialibrary
  • rm10161130/medialibrary
  • sandsmark/medialibrary
  • Aymeriic/medialibrary
  • louis/medialibrary
  • krsingh.chandan/medialibrary
  • ForteFrankie/medialibrary
  • mstorsjo/medialibrary
  • luc65r/medialibrary
  • abodbre1/medialibrary
  • Juniorzito8415/medialibrary
  • atas70835/medialibrary
  • Ninaquinin/medialibrary
  • litteh82/medialibrary
  • king7532/medialibrary
  • bharatraval7162/medialibrary
  • yenidunyabetul443/medialibrary
  • sergiomb2/medialibrary
  • boykaisaac758/medialibrary
  • Niram7777/medialibrary
  • tvermaashutosh/medialibrary
  • chub/medialibrary
  • bdazzle431/medialibrary
51 results
Show changes
Commits on Source (116)
Showing
with 378 additions and 296 deletions
variables:
GIT_SUBMODULE_STRATEGY: normal
MEDIALIBRARY_30_IMAGE: registry.videolan.org/medialibrary-3.0:20220713114658
MEDIALIBRARY_40_IMAGE: registry.videolan.org/medialibrary-4.0:20220727133212
MEDIALIBRARY_WIN32_IMG: registry.videolan.org/medialibrary-win32:20221023212324
MEDIALIBRARY_WIN64_IMG: registry.videolan.org/medialibrary-win64:20221023210716
VLC_DEBIAN_UNSTABLE_IMG: registry.videolan.org/vlc-debian-unstable:20220127084320
MEDIALIBRARY_30_IMAGE: registry.videolan.org/medialibrary-3.0:20231017192545
MEDIALIBRARY_40_IMAGE: registry.videolan.org/medialibrary-4.0:20231017192545
MEDIALIBRARY_WIN32_IMG: registry.videolan.org/medialibrary-win32:20231013034500
MEDIALIBRARY_WIN64_IMG: registry.videolan.org/medialibrary-win64:20231013035411
VLC_DEBIAN_UNSTABLE_IMG: registry.videolan.org/vlc-debian-unstable:20221213103803
MEDIALIBRARY_ALPINE_IMG: registry.videolan.org/medialibrary-alpine:20220706115155
MEDIALIBRARY_ARCH_IMG: registry.videolan.org/medialibrary-archlinux:20220706120650
MEDIALIB_TEST_FOLDER: $CI_PROJECT_DIR/medialib_tests/
......@@ -55,12 +55,10 @@ build:arch:
- if: '$CI_PIPELINE_SOURCE == "schedule" && $MEDIALIB_MANUAL_JOB_NAME == null'
stage: test
script:
- cd /tmp/ && git clone --single-branch --branch=display_stack_on_timeout --depth=1 https://github.com/chouquette/meson
- export PATH=/tmp/meson:$PATH
- cd $CI_PROJECT_DIR
- meson.py -Db_coverage=true build
- meson -Db_coverage=true build
- cd build
- meson.py test --no-stdsplit
- meson test --no-stdsplit
- gcovr -r "$CI_PROJECT_DIR/" --json $CI_PROJECT_DIR/$CI_JOB_NAME.cov.json -j 4
artifacts:
reports:
......@@ -81,6 +79,7 @@ test:debian-4.0:
test:win32:
image: $MEDIALIBRARY_WIN32_IMG
allow_failure: true
variables:
MESON_TESTTHREADS: 8
stage: test
......@@ -112,6 +111,7 @@ test:win32:
test:win64:
image: $MEDIALIBRARY_WIN64_IMG
allow_failure: true
variables:
MESON_TESTTHREADS: 8
stage: test
......@@ -154,12 +154,10 @@ asan-ubsan:
- ./configure LDFLAGS="-lasan -lubsan" --prefix=$(pwd)/prefix --disable-qt --with-sanitizer=address,undefined --disable-medialibrary --disable-nls --enable-debug
- make install -j8
- export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$(pwd)/prefix/lib/pkgconfig"
- cd /tmp/ && git clone --single-branch --branch=display_stack_on_timeout --depth=1 https://github.com/chouquette/meson
- export PATH=/tmp/meson:$PATH
- cd $CI_PROJECT_DIR
- meson.py -Db_sanitize=address,undefined -Dlong_running_tests=true build
- cd build && meson.py test --no-stdsplit --no-suite long_running_tests
- meson.py test --suite long_running_tests --test-args $CI_PROJECT_DIR/test/samples/samples --no-stdsplit --logbase longtests
- meson -Db_sanitize=address,undefined -Dlong_running_tests=true build
- cd build && meson test --no-stdsplit --no-suite long_running_tests
- meson test --suite long_running_tests --test-args $CI_PROJECT_DIR/test/samples/samples --no-stdsplit --logbase longtests
artifacts:
when: on_failure
paths:
......@@ -175,19 +173,17 @@ asan-ubsan:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "$CI_DEFAULT_BRANCH@videolan/medialibrary"'
stage: test
script:
- cd /tmp/ && git clone --single-branch --branch=display_stack_on_timeout --depth=1 https://github.com/chouquette/meson
- export PATH=/tmp/meson:$PATH
- cd $CI_PROJECT_DIR
- >
CXX=clang++
meson.py
meson
-Dpkg_config_path=$PKG_CONFIG_PATH:$CI_PROJECT_DIR/vlc/prefix/lib/pkgconfig
-Db_sanitize=$SANITIZERS
-Db_lundef=false
-Dlong_running_tests=true
build
- cd build && meson.py test --no-stdsplit --no-suite long_running_tests
- meson.py test --suite long_running_tests --test-args $CI_PROJECT_DIR/test/samples/samples --no-stdsplit --logbase longtests
- cd build && meson test --no-stdsplit --no-suite long_running_tests
- meson test --suite long_running_tests --test-args $CI_PROJECT_DIR/test/samples/samples --no-stdsplit --logbase longtests
artifacts:
when: on_failure
paths:
......@@ -218,7 +214,7 @@ gen-test-db:
script:
- meson --buildtype=release build
- cd build && ninja
- ../ci/generate-samples.sh -o $CI_PROJECT_DIR/dummysamples -n 10
- ../ci/generate-samples.sh -o $CI_PROJECT_DIR/dummysamples -n 2
- test/discoverer/discoverer $CI_PROJECT_DIR/dummysamples -q
- echo "BEGIN;" > $CI_PROJECT_DIR/test_db.sql
- >
......
......@@ -23,6 +23,7 @@
#pragma once
#include <string>
#include <cstdint>
namespace medialibrary
{
......
......@@ -34,7 +34,7 @@ namespace medialibrary
*
* An external device lister shall only be used when the medialibrary can't list
* the devices itself.
* The device/folder/file management will still be the medialibrary's responsability
* The device/folder/file management will still be the medialibrary's responsibility
*/
class IDeviceListerCb
{
......
......@@ -54,7 +54,7 @@ public:
virtual bool isPresent() const = 0;
virtual bool isRemovable() const = 0;
/**
* @brief isBanned Will return true if the folder was explicitely banned
* @brief isBanned Will return true if the folder was explicitly banned
* from being discovered.
*/
virtual bool isBanned() const = 0;
......
......@@ -87,7 +87,7 @@ public:
* @param sizeType The thumbnail size type
* @param takeOwnership If true, the medialibrary will copy the thumbnail in
* its thumbnail directory and will manage its lifetime
* @return true if the thumbnail was successfully overriden, false otherwise.
* @return true if the thumbnail was successfully overridden, false otherwise.
*/
virtual bool setThumbnail( const std::string& mrl, ThumbnailSizeType sizeType,
bool takeOwnership ) = 0;
......
......@@ -111,10 +111,10 @@ public:
*/
enum class ProgressResult : uint8_t
{
/// An error occured and the progress wasn't changed
/// An error occurred and the progress wasn't changed
Error,
/// The provided position/time was interpreted as the beginning of the
/// media and has been reset to -1. This nedia playback is now not
/// media and has been reset to -1. This media playback is now not
/// considered started.
Begin,
/// The provided position/time was not interpreted as a special position
......@@ -170,11 +170,11 @@ public:
* @brief setLastPosition updates the last playback position
*
* @param lastPosition The current playback position expressed by a number in the range [0;1]
* @return a ProgressResult value indicating how the value was intepreted and
* @return a ProgressResult value indicating how the value was interpreted and
* if the operation succeeded
*
* The media library will interpret the value to determine if the playback
* is completed and the media should be marked as watched (therefor increasing
* is completed and the media should be marked as watched (therefore increasing
* the playcount). If the progress isn't large enough, the media library will
* ignore the new progress.
* The base value for the beginning/end of a media is 5%, meaning that the
......@@ -207,7 +207,7 @@ public:
/**
* @brief setLastTime Sets the last playback time.
* @param lastTime A time in millisecond
* @return a ProgressResult value indicating how the value was intepreted and
* @return a ProgressResult value indicating how the value was interpreted and
* if the operation succeeded
*
* This is similar to setLastPosition but works with a time in
......@@ -308,7 +308,7 @@ public:
/// Upon completion (successful or not) IMediaLibraryCb::onMediaThumbnailReady
/// will be called.
/// In case a thumbnail was already generated for the media, a new thumbnail
/// will be generated, and the previous one will be overriden.
/// will be generated, and the previous one will be overridden.
/// \param sizeType The size type of the thumbnail to generate
/// \param desiredWidth The desired thumbnail width
/// \param desiredHeight The desired thumbnail height
......@@ -348,7 +348,7 @@ public:
///
virtual std::unordered_map<MetadataType, std::string> metadata() const = 0;
///
/// \brief setMetadata Immediatly saves a metadata in database
/// \brief setMetadata Immediately saves a metadata in database
///
virtual bool setMetadata( MetadataType type, const std::string& value ) = 0;
virtual bool setMetadata( MetadataType type, int64_t value ) = 0;
......@@ -412,12 +412,12 @@ public:
///
/// \brief isDiscoveredMedia Returns true if this media was discovered
/// during a scan.
/// false means that the media has been explicitely added by the user
/// false means that the media has been explicitly added by the user
/// as a stream, or an external media
///
virtual bool isDiscoveredMedia() const = 0;
///
/// \brief isExternalMedia Returns true if the media was explicitely added
/// \brief isExternalMedia Returns true if the media was explicitly added
/// by the application.
/// This is the opposite counterpart of isDiscoveredMedia
///
......@@ -468,7 +468,7 @@ public:
/// The media is considered present if the device containing its main file
/// is present (ie. if a removable drive is mounted, or a network drive
/// connected)
/// This is only relevent when the media is not external
/// This is only relevant when the media is not external
///
virtual bool isPresent() const = 0;
......
......@@ -114,7 +114,7 @@ public:
/**
* @brief userInteracted Returns true if the group has had user interactions
*
* This includes being renamed, or being explicitely created with some specific
* This includes being renamed, or being explicitly created with some specific
* media or an explicit title.
* It doesn't include groups that were automatically created by the media library
* Removing a media from an automatically created group won't be interpreted
......@@ -178,7 +178,7 @@ public:
/**
* @brief rename Rename a group
* @param name The new name
* @return true if the rename was successfull, false otherwise
* @return true if the rename was successful, false otherwise
*
* This will not change the group content, however, it will prevent further
* media that matched the previous name to be automatically added to this
......
......@@ -112,6 +112,9 @@ struct QueryParameters
* public entities only.
*/
bool publicOnly = false;
/* If true, only favorite entities will be returned */
bool favoriteOnly = false;
};
enum class InitializeResult
......@@ -164,8 +167,10 @@ enum class ThumbnailStatus : uint8_t
enum class HistoryType : uint8_t
{
/// The history of both local and network media played
Global,
/// The history of media analyzed by the media library & external media
Media,
Local,
/// The network streams history
Network,
};
......@@ -217,7 +222,7 @@ struct SetupConfig
/**
* @brief logLevel The default log level to initialize the medialibrary with.
* This can be overwriten at a later point using IMediaLibrary::setVerbosity
* This can be overwritten at a later point using IMediaLibrary::setVerbosity
*/
LogLevel logLevel = LogLevel::Error;
......@@ -285,10 +290,10 @@ public:
/**
* @brief onDiscoveryStarted This callback will be invoked when the discoverer
* starts to crawl an entrypoint that was scheduled for discovery or reload.
* starts to crawl a root folder that was scheduled for discovery or reload.
*
* This callback will be invoked when the discoverer thread gets woken up
* regardless of how many entry points need to be discovered.
* regardless of how many roots need to be discovered.
*/
virtual void onDiscoveryStarted() = 0;
/**
......@@ -296,7 +301,7 @@ public:
* discoverer enters a new folder.
* @param currentFolder The folder being discovered
*
* This callback can be invoked multiple times even though a single entry point was asked to be
* This callback can be invoked multiple times even though a single root was asked to be
* discovered. ie. In the case of a file system discovery, discovering a folder would make this
* callback being invoked for all subfolders
*/
......@@ -305,58 +310,57 @@ public:
* @brief onDiscoveryCompleted Will be invoked when the discoverer finishes
* all its queued operations and goes back to idle.
*
* This callback will be invoked once for each invocation fo onDiscoveryStarted
* This callback will be invoked once for each invocation of onDiscoveryStarted
*/
virtual void onDiscoveryCompleted() = 0;
/**
* @brief onDiscoveryFailed Will be invoked when a discovery operation fails
* @param entryPoint The entry point for which the discovery failed.
* @param root The root folder for which the discovery failed.
*/
virtual void onDiscoveryFailed( const std::string& entryPoint ) = 0;
virtual void onDiscoveryFailed( const std::string& root ) = 0;
/**
* @brief onEntryPointAdded will be invoked when an entrypoint gets added
* @param entryPoint The entry point which was scheduled for discovery
* @brief onRootAdded will be invoked when an root folder is added
* @param root The root folder which was scheduled for discovery
* @param success A boolean to represent the operation's success
*
* This callback will only be emitted the first time the entry point gets
* This callback will only be emitted the first time the root folder is
* processed, after it has been inserted to the database.
* In case of failure, it might be emited every time the request is sent, since
* the provided entry point would most likely be invalid, and couldn't be inserted.
* Later processing of that entry point will still cause \sa{onDiscoveryStarted}
* In case of failure, it might be emitted every time the request is sent, since
* the provided folder would most likely be invalid, and couldn't be inserted.
* Later processing of the folder will still cause \sa{onDiscoveryStarted}
* \sa{onDiscoveryProgress} and \sa{onDiscoveryCompleted} events to be fired
* \warning This event will be fired after \sa{onDiscoveryStarted} since we
* don't know if an entry point is known before starting its processing
* don't know if a root folder is known before starting its processing
*/
virtual void onEntryPointAdded( const std::string& entryPoint, bool success ) = 0;
virtual void onRootAdded( const std::string& root, bool success ) = 0;
/**
* @brief onEntryPointRemoved will be invoked when an entrypoint removal
* request gets processsed
* by the appropriate worker thread.
* @param entryPoint The entry point which removal was required
* @brief onRootRemoved will be invoked when a root removal request is
* processsed by the appropriate worker thread.
* @param root The root folder which removal was required
* @param success A boolean representing the operation's success
*/
virtual void onEntryPointRemoved( const std::string& entryPoint, bool success ) = 0;
virtual void onRootRemoved( const std::string& root, bool success ) = 0;
/**
* @brief onEntryPointBanned will be called when an entrypoint ban request
* is done being processed.
* @param entryPoint The banned entrypoint
* @brief onRootBanned will be called when a root ban request
* has been processed.
* @param root The banned root folder
* @param success A boolean representing the operation's success
*/
virtual void onEntryPointBanned( const std::string& entryPoint, bool success ) = 0;
virtual void onRootBanned( const std::string& root, bool success ) = 0;
/**
* @brief onEntryPointUnbanned will be called when an entrypoint unban request
* is done being processed.
* @param entryPoint The unbanned entrypoint
* @brief onRootUnbanned will be called when a root unban request
* is done being processed.
* @param root The unbanned root folder
* @param success A boolean representing the operation's success
*/
virtual void onEntryPointUnbanned( const std::string& entryPoint, bool success ) = 0;
virtual void onRootUnbanned( const std::string& root, bool success ) = 0;
/**
* @brief onParsingStatsUpdated Called when the parser statistics are updated
*
* There is no waranty about how often this will be called.
* There is no warranty about how often this will be called.
* @param opsDone The number of operation the parser completed
* @param opsScheduled The number of operations currently scheduled by the parser
*
......@@ -400,7 +404,7 @@ public:
* IMediaLibrary::clearDatabase. After doing so, the medialibrary can still
* be used without any further calls (but will need to rescan the entire user
* collection). If clearDatabase isn't called, the database should be
* considered as corrupted, and therefor the medialibrary considered unusable.
* considered as corrupted, and therefore the medialibrary considered unusable.
*
* If clearSuggested is false, there are no certain way of knowing if the
* database is still usable or not.
......@@ -411,7 +415,7 @@ public:
/**
* @brief onRescanStarted will be invoked when a rescan is started.
*
* This won't be emited when the media library issues a rescan itself, due
* This won't be emitted when the media library issues a rescan itself, due
* to a migration.
*/
virtual void onRescanStarted() = 0;
......@@ -534,7 +538,7 @@ public:
virtual bool removeExternalMedia( MediaPtr media ) = 0;
/**
* @brief audioFiles Returns the media classified as Audio
* @brief mediaFiles Returns the media unclassified
* @param params Some query parameters.
* @return A query representing the results set
*
......@@ -549,13 +553,21 @@ public:
* - FileSize
* Default sorting parameter uses the media's title, in ascending order
*/
virtual Query<IMedia> mediaFiles( const QueryParameters* params = nullptr ) const = 0;
/**
* @brief audioFiles Returns the media classified as Audio
* @param params Some query parameters.
* @return A query representing the results set
*
* \see{IMediaLibrary::mediaFiles} for the supported sorting criteria
*/
virtual Query<IMedia> audioFiles( const QueryParameters* params = nullptr ) const = 0;
/**
* @brief videoFiles Returns the media classified as Video
* @param params Some query parameters.
* @return A query representing the results set
*
* \see{IMediaLibrary::audioFile} for the supported sorting criteria
* \see{IMediaLibrary::mediaFiles} for the supported sorting criteria
*/
virtual Query<IMedia> videoFiles( const QueryParameters* params = nullptr ) const = 0;
......@@ -634,7 +646,7 @@ public:
virtual Query<IMediaGroup> searchMediaGroups( const std::string& pattern,
const QueryParameters* params = nullptr ) const = 0;
/**
* @brief regroupAll Attemps to regroup all media that belong to a forced singleton group
* @brief regroupAll Attempts to regroup all media that belong to a forced singleton group
*
* This will try to regroup all media that were manually removed from their
* group, and now belong to a forced singleton group.
......@@ -655,6 +667,7 @@ public:
virtual ShowPtr show( int64_t id ) const = 0;
virtual MoviePtr movie( int64_t id ) const = 0;
virtual ArtistPtr artist( int64_t id ) const = 0;
virtual SubscriptionPtr subscription( int64_t id ) const = 0;
virtual Query<IShow> shows( const QueryParameters* params = nullptr ) const = 0;
virtual Query<IShow> searchShows( const std::string& pattern,
const QueryParameters* params = nullptr ) const = 0;
......@@ -706,22 +719,45 @@ public:
/**
* History
*/
virtual Query<IMedia> history() const = 0;
/**
* @brief history Fetch the media already played.
* @param type Filter the history.
* @params params Some query parameters, supports what is already supported for media listing.
* Default sort is descending last play date.
*/
virtual Query<IMedia> history( HistoryType, const QueryParameters* params = nullptr ) const = 0;
/**
* @brief history Returns the history for media of the provided type
* @param type Can be either Audio or Video. Other types are not supported
* @brief audioHistory Fetch the local audio history.
* @params params Some query parameters, supports what is already supported for media listing.
* Default sort is descending last play date.
*
* @note There's no way to filter network history with media types for now. Hence the lack of
* HistoryType parameters.
*/
virtual Query<IMedia> history( IMedia::Type type ) const = 0;
virtual Query<IMedia> streamHistory() const = 0;
virtual Query<IMedia> audioHistory( const QueryParameters* params = nullptr ) const = 0;
/**
* @brief videoHistory Fetch the local video history.
* @params params Some query parameters, supports what is already supported for media listing.
* Default sort is descending last play date.
*
* @note There's no way to filter network history with media types for now. Hence the lack of
* HistoryType parameters.
*/
virtual Query<IMedia> videoHistory( const QueryParameters* params = nullptr ) const = 0;
/**
* @brief clearHistory will clear both streams history & media history.
* @param type Filter the history to clear.
* @return true in case of success, false otherwise. The database will stay untouched in case
* of failure.
*
* This will clear all history, and also reset any potential playback progress
* for all media
*/
virtual bool clearHistory() = 0;
virtual bool clearHistory(HistoryType) = 0;
/**
* Search
......@@ -734,7 +770,7 @@ public:
* @param params Some query parameters.
*
* Only media that were discovered by the medialibrary will be included.
* For instance, media that are added explicitely, playlist items that
* For instance, media that are added explicitly, playlist items that
* point to remote content, will *not* be included
*
* \see{IMediaLibrary::audioFile} for the supported sorting criteria
......@@ -758,6 +794,41 @@ public:
searchSubscriptionMedia( const std::string& pattern,
const QueryParameters* params = nullptr ) const = 0;
/**
* @brief searchInHistory Search the media already played, based on a pattern.
* @param hisType Filter the history.
* @param pattern A 3 character or more pattern that will be matched against the media's title
* or filename if no title was set for this media.
* @param params Some query parameters, supports what is already supported for media listing.
* Default sort is descending last play date.
*/
virtual Query<IMedia> searchInHistory( HistoryType hisType, const std::string& pattern,
const QueryParameters* params = nullptr ) const = 0;
/**
* @brief searchInAudioHistory Search the local audio history, based on a pattern.
* @param pattern A 3 character or more pattern that will be matched against the audio's title
* or filename if no title was set for this audio.
* @param params Some query parameters, supports what is already supported for audio listing.
* Default sort is descending last play date.
*
* @note There's no way to filter network history with media types for now.
* Hence the lack of HistoryType parameter.
*/
virtual Query<IMedia> searchInAudioHistory( const std::string& pattern,
const QueryParameters* params = nullptr ) const = 0;
/**
* @brief searchInVideoHistory Search the local video history, based on a pattern.
* @param pattern A 3 character or more pattern that will be matched against the video's title
* or filename if no title was set for this video.
* @param params Some query parameters, supports what is already supported for video listing.
* Default sort is descending last play date.
*
* @note There's no way to filter network history with media types for now.
* Hence the lack of HistoryType parameter.
*/
virtual Query<IMedia> searchInVideoHistory( const std::string& pattern,
const QueryParameters* params = nullptr ) const = 0;
virtual Query<IPlaylist> searchPlaylists( const std::string& name, PlaylistType type,
const QueryParameters* params = nullptr ) const = 0;
virtual Query<IAlbum> searchAlbums( const std::string& pattern,
......@@ -770,15 +841,15 @@ public:
const QueryParameters* params = nullptr ) const = 0;
/**
* @brief discover Launch a discovery on the provided entry point.
* @brief discover Launch a discovery on the provided root folder.
* This will start the discoverer thread, device listers, and file system
* factories if needed
* The actuall discovery will run asynchronously, meaning this method will immediatly return.
* The actual discovery will run asynchronously, meaning this method will immediately return.
* Depending on which discoverer modules where provided, this might or might not work
* @note This must be called after initialize()
* @param entryPoint The MRL of the entrypoint to discover.
* @param root The MRL of the root folder to discover.
*/
virtual void discover( const std::string& entryPoint ) = 0;
virtual void discover( const std::string& root ) = 0;
/**
* @brief setDiscoverNetworkEnabled Enable discovery of network shares
* @return true In case of success, false otherwise.
......@@ -807,14 +878,15 @@ public:
*
* This is essentially a way of knowing what has been passed to discover()
* throughout the database life.
* The resulting list includes entry points on device that are currently unmounted.
* The resulting list includes root folders on device that are currently
* unmounted.
* If the passed params field publicOnly is true, this function will list
* top level public folders instead of the folders provided to discover()
*/
virtual Query<IFolder> roots( const QueryParameters* params ) const = 0;
/**
* @brief isIndexed Returns true if the mrl point to a file of folder in an
* indexed entrypoint
* @brief isIndexed Returns true if the mrl point to a file or folder in an
* indexed root.
* @param mrl The MRL to probe
* @return true if the mrl is indexed, false otherwise
*/
......@@ -847,7 +919,7 @@ public:
* would return a query containing 'z' and 'c' as the other folders are
* not containing any media.
* In case a non flattened list is desired, the
* entryPoints() & IFolder::subFolders() functions should be used.
* roots() & IFolder::subFolders() functions should be used.
*/
virtual Query<IFolder> folders( IMedia::Type type,
const QueryParameters* params = nullptr ) const = 0;
......@@ -857,10 +929,10 @@ public:
virtual FolderPtr folder( int64_t folderId ) const = 0;
virtual FolderPtr folder( const std::string& mrl ) const = 0;
/**
* @brief removeEntryPoint Removes an entry point
* @param entryPoint The MRL of the entry point to remove
* @brief removeRoot Removes a root folder
* @param root The MRL of the root folder point to remove
*
* This will remove the provided entry point from the list of know locations
* This will remove the provided root folder from the list of know locations
* to manage by the media library.
* The location will be ignored afterward, even if it is a sub folder of
* another managed location.
......@@ -870,9 +942,9 @@ public:
* task
* @note This must be called after initialize()
*/
virtual void removeEntryPoint( const std::string& entryPoint ) = 0;
virtual void removeRoot( const std::string& root ) = 0;
/**
* @brief banFolder will prevent an entry point folder from being discovered.
* @brief banFolder will prevent a root folder from being discovered.
* If the folder was already discovered, it will be removed prior to the ban, and all
* associated media will be discarded.
* @param mrl The MRL to ban
......@@ -883,9 +955,9 @@ public:
*/
virtual void banFolder( const std::string& mrl ) = 0;
/**
* @brief unbanFolder Unban an entrypoint.
* In case this entry point was indeed previously banned, this will issue a reload of
* that entry point
* @brief unbanFolder Unban a root folder.
* In case this root folder was indeed previously banned, this will issue a
* reload of that folder
* @param mrl The MRL to unban
* @note This method is asynchronous, but will interrupt any ongoing
* discovery, process the request, and resume the previously running
......@@ -894,12 +966,12 @@ public:
*/
virtual void unbanFolder( const std::string& mrl ) = 0;
/**
* @brief bannedEntryPoints Returns a query representing the banned entry points
* @brief bannedRoots Returns a query representing the banned root folders.
*
* The result set will include entry points on missing devices as well. Folder
* hierarchy isn't preserved, and the results are flattened.
* The result set will include root folders on missing devices as well.
* Folder hierarchy isn't preserved, and the results are flattened.
*/
virtual Query<IFolder> bannedEntryPoints() const = 0;
virtual Query<IFolder> bannedRoots() const = 0;
/**
* @brief pauseBackgroundOperations Will stop potentially CPU intensive background
* operations, until resumeBackgroundOperations() is called.
......@@ -912,7 +984,7 @@ public:
*/
virtual void resumeBackgroundOperations() = 0;
/**
* @brief reload Reload all known entry points
* @brief reload Reload all known roots
* @note This must be called after initialize()
*
* This will start the discoverer thread, appropriate device listers and
......@@ -920,14 +992,14 @@ public:
*/
virtual void reload() = 0;
/**
* @brief reload Reload a specific entry point
* @param entryPoint The entrypoint to reload
* @brief reload Reload a specific root folder.
* @param root The root folder to reload
* @note This must be called after initialize()
*
* This will start the discoverer thread, appropriate device listers and
* file system factories if needed.
*/
virtual void reload( const std::string& entryPoint ) = 0;
virtual void reload( const std::string& root ) = 0;
/**
* @brief forceParserRetry Forces a re-run of all metadata parsers and resets any
* unterminated file retry count to 0, granting them 3 new tries at being parsed
......@@ -961,7 +1033,7 @@ public:
/**
* @brief enableFailedThumbnailRegeneration Allow failed thumbnail attempt to be retried
*
* This will not attempt to regenerate the thumbnail immediatly, requestThumbnail
* This will not attempt to regenerate the thumbnail immediately, requestThumbnail
* still has to be called afterward.
*/
virtual void enableFailedThumbnailRegeneration() = 0;
......@@ -1019,7 +1091,7 @@ public:
* This will delete *ALL* removable devices from the database, causing *ALL*
* files & media stored on that device to be deleted as well.
* This is intended for applications with an external device lister to
* recover in case of an issue causing multiple devices or invalide entries
* recover in case of an issue causing multiple devices or invalid entries
* to be inserted in the database
*/
virtual bool deleteRemovableDevices() = 0;
......
......@@ -123,19 +123,48 @@ public:
/// If the position is greater than the playlist size, it will be interpreted
/// as a regular append operation, and the item position will be set to
/// <playlist size>
/// For instance, on the playlist [<B,0>, <A,1>, <C,2>], if add(D, 999)
/// gets called, the resulting playlist will be [<A,0>, <C,1>, <B,2>, <D,3>]
/// For instance, on the playlist [<A,0>, <B,1>, <C,2>], if add(D, 999)
/// gets called, the resulting playlist will be [<A,0>, <B,1>, <C,2>, <D,3>]
///
virtual bool add( const IMedia& media, uint32_t position ) = 0;
/// \brief append a list of medias to a playlist
/// The medias will be the last element of a subsequent call to media()
/// \param mediaList The medias to add
/// \return true on success, false on failure.
///
/// For instance on the playlist [<A,0>, <B,1>, <C,2>], if apend([D,E])
/// gets called, the resulting playlist will be [<A,0>, <B,1>, <C,2>, <D,3>, <E,4>]
///
virtual bool append( const std::vector<MediaPtr>& mediaList ) = 0;
///
/// \brief add Add a list of medias to the playlist at the given position.
/// \param mediaList The medias to add
/// \param position The position of this new medias, in the [0;size-1] range
/// \return true on success, false on failure
///
/// For instance on the playlist [<A,0>, <B,1>, <C,2>], if apend([D,E], 2)
/// gets called, the resulting playlist will be [<A,0>, <B,1>, <D,2>, <E,3>, <C,4>]
///
/// If the position is greater than the playlist size, it will be interpreted
/// as a regular append operation, and the item position will be set to
/// <playlist size
/// For instance, on the playlist [<A,0>, <B,1>, <C,2>], if add([D,E], 999)
/// gets called, the resulting playlist will be [<A,0>, <B,1>, <C,2>, <D,3>, <E,4>]
///
virtual bool add( const std::vector<MediaPtr>& mediaList, uint32_t position ) = 0;
/// Convenience wrappers
virtual bool append( int64_t mediaId ) = 0;
virtual bool add( int64_t mediaId, uint32_t position ) = 0;
virtual bool append( const std::vector<int64_t>& mediaList ) = 0;
virtual bool add( const std::vector<int64_t>& mediaList, uint32_t position ) = 0;
///
/// \brief move Change the position of a media
/// \param from The position of the item being moved
/// \param to The moved item target position
/// \param count The number of elements to move
///
/// \return true on success, false on failure
///
......@@ -145,21 +174,35 @@ public:
/// [<A,0>, <B,1>, <C,2>] on which move(0, 1) is called will result in the
/// playlist being changed to
/// [<B,0>, <A,1>, <C,2>]
///
/// Likewise, when moving multiple elements:
/// On the playlist [<A,0>, <B,1>, <C,2>, <D,3>, <E,4>], move(1, 3, 2) results in
/// [<A,0>, <D,1>, <B,2>, <C,3>, <E,4>]
///
/// Moving one or multiple elements at the same position or within its range has no effects.
/// On the playlist [<A,0>, <B,1>, <C,2>, <D,3>, <E,4>], move(1,1) or move(2,3,2)
/// doesn't alter the playlist. Modification won't be notified, the function returns true
///
/// If the target position is out of range (ie greater than the playlist size)
/// the target position will be interpreted as the playlist size (prior to insertion).
/// For instance, on the playlist [<B,0>, <A,1>, <C,2>], if move(0, 999)
/// gets called, the resulting playlist will be [<A,0>, <C,1>, <B,2>]
/// For instance, on the playlist [<A,0>, <B,1>, <C,2>], if move(0, 999)
/// gets called, the resulting playlist will be [<B,0>, <C,1>, <A,2>].
///
virtual bool move( uint32_t from, uint32_t to ) = 0;
virtual bool move( uint32_t from, uint32_t to, uint32_t count = 1 ) = 0;
///
/// \brief remove Removes an item from the playlist
/// \brief remove Removes a range of items from the playlist
/// \param position The position of the item to remove.
/// \param count The number of element to remove (one by default)
/// \return true on success, false on failure
///
virtual bool remove( uint32_t position ) = 0;
/// For instance, a playlist with <media,position> like
/// [<A,0>, <B,1>, <C,2>, <D,3>] on which remove(1, 2) is called will result in the
/// playlist being changed to[<A,0>, <D,1>]
///
virtual bool remove( uint32_t position, uint32_t count = 1 ) = 0;
///
/// \brief isReadOnly Return true if the playlist is backed by an actual file
/// and should therefor not modified directly.
/// and should therefore not modified directly.
/// \return true if the playlist should be considered read-only, false otherwise
///
/// If the application doesn't respect this, the medialibrary will, for
......
......@@ -45,7 +45,7 @@ public:
/**
* @brief items returns a subset of a query result
* @param nbItems The number of item requested
* @param offset The number of elements to omit from the begining of the result
* @param offset The number of elements to omit from the beginning of the result
* @return A vector of shared pointer for the requested type.
*
* If nbItems & offset are both 0, then this method returns all results.
......
......@@ -60,20 +60,20 @@ public:
virtual bool isNewMediaNotificationEnabled() const = 0;
virtual bool setNewMediaNotificationEnabled( bool enabled ) = 0;
/**
* @brief maxCachedSize Returns the maximum size of the cache for all of this service
* @brief maxCacheSize Returns the maximum size of the cache for all of this service
* @return The maximum cache size or -1 if the limit isn't set for this service
*
* If the limit isn't set, the global maximum cache size setting will be
* used instead.
*/
virtual int64_t maxCachedSize() const = 0;
virtual int64_t maxCacheSize() const = 0;
/**
* @brief setMaxCachedSize Sets the maximum cache size for this service
* @brief setMaxCacheSize Sets the maximum cache size for this service
* @param maxSize A size in bytes, or -1 to disable the setting for the service
* and inherit the default one.
* @return true if the change was successful, false otherwise.
*/
virtual bool setMaxCachedSize( int64_t maxSize ) = 0;
virtual bool setMaxCacheSize( int64_t maxSize ) = 0;
virtual bool addSubscription( std::string mrl ) = 0;
virtual Query<ISubscription> subscriptions( const QueryParameters* params ) const = 0;
virtual Query<ISubscription> searchSubscription( const std::string& pattern,
......
......@@ -66,17 +66,17 @@ public:
*/
virtual bool setMaxCachedMedia( int32_t nbCachedMedia ) = 0;
/**
* @brief maxCachedSize Returns the maximum size in bytes for this collection
* @brief maxCacheSize Returns the maximum size in bytes for this collection
*
* This will return the collection specific setting, regardless of the global
* setting value.
* In case the value is unset, -1 will be returned. In this case, the parent
* setting will be used when performing caching.
*/
virtual int64_t maxCachedSize() const = 0;
virtual int64_t maxCacheSize() const = 0;
/**
* @brief setMaxCachedSize Sets the maximum cached size for this collection
* @param maxCachedSize The size in bytes for this collection cache, or a
* @brief setMaxCacheSize Sets the maximum cache size for this collection
* @param maxCacheSize The size in bytes for this collection cache, or a
* negative value to use the parent setting.
* @return true if the change was successful, false otherwise.
*
......@@ -85,7 +85,7 @@ public:
* prevail when caching.
* If passing the current value, this will return true.
*/
virtual bool setMaxCachedSize( int64_t maxCachedSize ) = 0;
virtual bool setMaxCacheSize( int64_t maxCacheSize ) = 0;
/**
* @brief newMediaNotification Returns the new media notification setting
* @return A positive value if explicitly enabled, 0 if explicitly disabled, -1 is unset
......
......@@ -76,7 +76,7 @@ namespace fs
* @param ml A media library instance pointer
* @return true in case of success, false otherwise.
*
* If this returns false, the factory will be abandonned and the shared_ptr
* If this returns false, the factory will be abandoned and the shared_ptr
* for to that factory provided through SetupConfig will be destroyed
* If this factory handles a scheme that was already registered, it will
* not be used by the media library but this function will still be invoked
......
Subproject commit d75a31a0c4fba59c7a671d5862bfa5bd69095a73
Subproject commit 44c1f48e56a66c3f418175af1e1ef3fd1ab1b118
project('medialibrary', ['cpp','c'],
license: 'LGPLv2+',
version: '0.13.0',
version: '0.14.0',
meson_version: '>=0.56',
default_options: [
'cpp_std=c++14',
......
......@@ -232,7 +232,7 @@ std::string Album::addRequestJoin( const QueryParameters* params,
case SortingCriteria::ReleaseDate:
case SortingCriteria::Duration:
case SortingCriteria::TrackNumber:
/* No other tables required for this criterias */
/* No other tables required for this criteria */
break;
case SortingCriteria::PlayCount:
case SortingCriteria::InsertionDate:
......@@ -257,36 +257,30 @@ std::string Album::addRequestJoin( const QueryParameters* params,
return req;
}
std::string Album::orderTracksBy( const QueryParameters* params = nullptr )
std::string Album::addRequestConditions( const QueryParameters* params, bool forcePublic )
{
std::string req = " ORDER BY ";
auto sort = params != nullptr ? params->sort : SortingCriteria::TrackId;
auto desc = params != nullptr ? params->desc : false;
switch ( sort )
std::string req;
const bool includeMissing = params != nullptr ? params->includeMissing : false;
if ( includeMissing == false )
req += " alb.is_present != 0";
if ( params == nullptr )
return req;
if ( params->publicOnly || forcePublic )
{
case SortingCriteria::Alpha:
req += "med.title";
break;
case SortingCriteria::Duration:
req += "med.duration";
break;
case SortingCriteria::ReleaseDate:
req += "med.release_date";
break;
default:
LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Default" );
/* fall-through */
case SortingCriteria::TrackId:
case SortingCriteria::Default:
if ( desc == true )
req += "med.disc_number DESC, med.track_number DESC, med.filename";
else
req += "med.disc_number, med.track_number, med.filename";
break;
if ( req.empty() == false )
req += " AND";
req += " EXISTS(SELECT album_id FROM " + Media::Table::Name +
" WHERE is_public != 0 AND album_id = alb.id_album)";
}
if ( desc == true )
req += " DESC";
if ( params->favoriteOnly == true )
{
if ( req.empty() == false )
req += " AND";
req += " alb.is_favorite = TRUE";
}
return req;
}
......@@ -328,15 +322,15 @@ std::string Album::orderBy( const QueryParameters* params )
case SortingCriteria::PlayCount:
/* This voluntarily overrides the initial "ORDER BY" in req, since
* we need the GROUP BY first */
req = "GROUP BY alb.id_album "
"ORDER BY SUM(m.play_count) ";
req = " GROUP BY alb.id_album"
" ORDER BY SUM(m.play_count) ";
if ( desc == false )
req += "DESC "; // Most played first by default
req += ", alb.title";
break;
case SortingCriteria::InsertionDate:
req = "GROUP BY alb.id_album "
"ORDER BY MIN(m.insertion_date) ";
req = " GROUP BY alb.id_album"
" ORDER BY MIN(m.insertion_date) ";
if ( desc == true )
req += "DESC ";
break;
......@@ -358,32 +352,14 @@ std::string Album::orderBy( const QueryParameters* params )
Query<IMedia> Album::tracks( const QueryParameters* params ) const
{
// This doesn't return the cached version, because it would be fairly complicated, if not impossible or
// counter productive, to maintain a cache that respects all orderings.
std::string req = "FROM " + Media::Table::Name + " med "
" WHERE med.album_id = ?";
if ( params == nullptr || params->includeMissing == false )
req += " AND med.is_present != 0";
auto publicOnly = ( params != nullptr && params->publicOnly == true ) || m_publicOnlyListing;
if ( publicOnly == true )
req += " AND med.is_public != 0";
return make_query<Media, IMedia>( m_ml, "med.*", std::move( req ),
orderTracksBy( params ), m_id ).markPublic( publicOnly ).build();
return Media::fromAlbum( m_ml, m_id, params, m_publicOnlyListing, nullptr );
}
Query<IMedia> Album::tracks( GenrePtr genre, const QueryParameters* params ) const
{
if ( genre == nullptr )
return {};
std::string req = "FROM " + Media::Table::Name + " med "
" WHERE med.album_id = ?"
" AND med.genre_id = ?";
if ( params == nullptr || params->includeMissing == false )
req += " AND med.is_present != 0";
if ( ( params != nullptr && params->publicOnly == true ) || m_publicOnlyListing == true )
req += " AND med.is_public != 0";
return make_query<Media, IMedia>( m_ml, "med.*", std::move( req ),
orderTracksBy( params ), m_id, genre->id() ).build();
return Media::fromAlbum( m_ml, m_id, params, m_publicOnlyListing, genre );
}
std::vector<MediaPtr> Album::cachedTracks() const
......@@ -951,11 +927,10 @@ Query<IAlbum> Album::search( MediaLibraryPtr ml, const std::string& pattern,
req += "WHERE id_album IN "
"(SELECT rowid FROM " + FtsTable::Name + " WHERE " +
FtsTable::Name + " MATCH ?)";
if ( params == nullptr || params->includeMissing == false )
req += " AND alb.is_present != 0";
if ( params != nullptr && params->publicOnly == true )
req += " AND EXISTS(SELECT album_id FROM " + Media::Table::Name +
" WHERE is_public != 0 AND album_id = alb.id_album)";
const auto cond = addRequestConditions( params, false );
if ( cond.empty() == false )
req += " AND" + cond;
return make_query<Album, IAlbum>( ml, "alb.*", std::move( req ),
orderBy( params ),
sqlite::Tools::sanitizePattern( pattern ) ).build();
......@@ -970,11 +945,11 @@ Query<IAlbum> Album::searchFromArtist( MediaLibraryPtr ml, const std::string& pa
"(SELECT rowid FROM " + FtsTable::Name + " WHERE " +
FtsTable::Name + " MATCH ?)"
" AND artist_id = ?";
if ( params == nullptr || params->includeMissing == false )
req += " AND alb.is_present != 0";
if ( params != nullptr && params->publicOnly == true )
req += " AND EXISTS(SELECT album_id FROM " + Media::Table::Name +
" WHERE is_public != 0 AND album_id = alb.id_album)";
const auto cond = addRequestConditions( params, false );
if ( cond.empty() == false )
req += " AND" + cond;
return make_query<Album, IAlbum>( ml, "alb.*", std::move( req ),
orderBy( params ),
sqlite::Tools::sanitizePattern( pattern ),
......@@ -1028,11 +1003,11 @@ Query<IAlbum> Album::fromGenre( MediaLibraryPtr ml, int64_t genreId,
std::string req = "FROM " + Table::Name + " alb ";
req += addRequestJoin( params, true );
req += "WHERE m.genre_id = ?";
if ( ( params != nullptr && params->publicOnly == true ) ||
forcePublic == true )
{
req += " AND m.is_public != 0";
}
const auto cond = addRequestConditions( params, forcePublic );
if ( cond.empty() == false )
req += " AND" + cond;
std::string groupAndOrderBy = "GROUP BY m.album_id" + orderBy( params );
return make_query<Album, IAlbum>( ml, "alb.*", std::move( req ),
std::move( groupAndOrderBy ), genreId ).build();
......@@ -1048,11 +1023,11 @@ Query<IAlbum> Album::searchFromGenre( MediaLibraryPtr ml, const std::string& pat
"(SELECT rowid FROM " + FtsTable::Name + " WHERE " +
FtsTable::Name + " MATCH ?)"
"AND m.genre_id = ?";
if ( ( params != nullptr && params->publicOnly == true ) ||
forcePublic == true )
{
req += " AND m.is_public != 0";
}
const auto cond = addRequestConditions( params, forcePublic );
if ( cond.empty() == false )
req += " AND" + cond;
std::string groupAndOrderBy = "GROUP BY m.album_id" + orderBy( params );
return make_query<Album, IAlbum>( ml, "alb.*", std::move( req ),
std::move( groupAndOrderBy ),
......@@ -1062,35 +1037,18 @@ Query<IAlbum> Album::searchFromGenre( MediaLibraryPtr ml, const std::string& pat
Query<IAlbum> Album::listAll( MediaLibraryPtr ml, const QueryParameters* params )
{
std::string countReq = "SELECT COUNT(*) FROM " + Table::Name;
std::string req = "SELECT alb.*";
auto publicOnly = params != nullptr && params->publicOnly;
if ( publicOnly == true )
req += ", TRUE ";
req += " FROM " + Table::Name + " alb ";
std::string req = " FROM " + Table::Name + " alb ";
req += addRequestJoin( params, false );
if ( publicOnly )
{
auto clause = " WHERE EXISTS(SELECT album_id FROM " + Media::Table::Name +
" WHERE is_public != 0 AND album_id = id_album)";
req += clause;
countReq += clause;
if ( params == nullptr || params->includeMissing == false )
{
countReq += " AND is_present != 0";
req += "AND alb.is_present != 0";
}
}
else if ( params == nullptr || params->includeMissing == false )
const auto cond = addRequestConditions( params, false );
if ( cond.empty() == false )
{
countReq += " WHERE is_present != 0";
req += "WHERE alb.is_present != 0 ";
req += "WHERE" + cond;
}
req += orderBy( params );
return make_query_with_count<Album, IAlbum>( ml, std::move( countReq ),
std::move( req ) );
const auto publicOnly = params != nullptr && params->publicOnly;
return make_query<Album, IAlbum>( ml, "alb.*", std::move( req ), orderBy( params ) )
.markPublic( publicOnly ).build();
}
bool Album::checkDBConsistency( MediaLibraryPtr ml )
......
......@@ -165,6 +165,7 @@ class Album : public IAlbum, public DatabaseHelpers<Album>
private:
static std::string addRequestJoin( const QueryParameters* params,
bool albumTrack);
static std::string addRequestConditions( const QueryParameters* params, bool forcePublic );
static std::string orderTracksBy( const QueryParameters* params );
static std::string orderBy( const QueryParameters* params );
static bool shouldUpdateThumbnail( const Thumbnail& currentThumbnail );
......
......@@ -111,58 +111,7 @@ Query<IAlbum> Artist::searchAlbums( const std::string& pattern,
Query<IMedia> Artist::tracks( const QueryParameters* params ) const
{
SortingCriteria sort = params != nullptr ? params->sort : SortingCriteria::Default;
bool desc = params != nullptr ? params->desc : false;
std::string req = "FROM " + Media::Table::Name + " med "
"INNER JOIN " + MediaRelationTable::Name + " mar "
"ON mar.media_id = med.id_media ";
if ( sort != SortingCriteria::Duration &&
sort != SortingCriteria::InsertionDate &&
sort != SortingCriteria::ReleaseDate &&
sort != SortingCriteria::Alpha )
{
req += "INNER JOIN Album alb ON alb.id_album = med.album_id ";
}
req += "WHERE mar.artist_id = ?";
if ( params == nullptr || params->includeMissing == false )
req += " AND med.is_present != 0";
auto publicOnly = ( params != nullptr && params->publicOnly ) ||
m_publicOnlyListing == true;
if ( publicOnly == true )
req += " AND med.is_public != 0";
std::string orderBy = "ORDER BY ";
switch ( sort )
{
case SortingCriteria::Duration:
orderBy += "med.duration";
break;
case SortingCriteria::InsertionDate:
orderBy += "med.insertion_date";
break;
case SortingCriteria::ReleaseDate:
orderBy += "med.release_date";
break;
case SortingCriteria::Alpha:
orderBy += "med.title";
break;
default:
LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Default (Album)" );
/* fall-through */
case SortingCriteria::Album:
case SortingCriteria::Default:
if ( desc == true )
orderBy += "alb.title DESC, alb.id_album DESC, med.disc_number, med.track_number";
else
orderBy += "alb.title, alb.id_album, med.disc_number, med.track_number";
break;
}
if ( desc == true && sort != SortingCriteria::Album )
orderBy += " DESC";
return make_query<Media, IMedia>( m_ml, "med.*", std::move( req ),
std::move( orderBy ), m_id )
.markPublic( publicOnly ).build();
return Media::fromArtist( m_ml, m_id, params, m_publicOnlyListing );
}
Query<IMedia> Artist::searchTracks( const std::string& pattern, const QueryParameters* params ) const
......@@ -240,6 +189,21 @@ std::string Artist::addRequestJoin(const QueryParameters* params)
}
}
std::string Artist::addRequestConditions( const QueryParameters* params )
{
std::string req;
const bool includeMissing = params != nullptr ? params->includeMissing : false;
if ( includeMissing == false )
req = " AND art.is_present != 0";
const bool favoriteOnly = params != nullptr ? params->favoriteOnly : false;
if ( favoriteOnly == true )
req += " AND art.is_favorite = TRUE";
return req;
}
bool Artist::setThumbnail( std::shared_ptr<Thumbnail> newThumbnail )
{
assert( newThumbnail != nullptr );
......@@ -513,7 +477,7 @@ std::string Artist::trigger( Triggers trigger, uint32_t dbModelVersion )
// Automatically delete the artists that don't have any albums left,
// except the 2 special artists.
// Those are assumed to always exist, and deleting them would cause
// a constaint violation error when inserting an album with
// a constraint violation error when inserting an album with
// unknown/various artist(s).
// The alternative would be to always check the special artists for
// existence, which would be much slower when inserting an unknown
......@@ -774,8 +738,9 @@ Query<IArtist> Artist::search( MediaLibraryPtr ml, const std::string& name,
req += addRequestJoin( params );
req += " WHERE id_artist IN "
"(SELECT rowid FROM " + FtsTable::Name + " WHERE name MATCH ?)";
if ( params == nullptr || params->includeMissing == false )
req += " AND art.is_present != 0";
req += addRequestConditions( params );
// We are searching based on the name, so we're ignoring unknown/various artist
// This means all artist we find has at least one track associated with it, so
// we can simply filter out based on the number of associated albums
......@@ -809,27 +774,63 @@ Query<IArtist> Artist::listAll( MediaLibraryPtr ml, ArtistIncluded included,
else
req += "art.nb_tracks > 0";
}
if ( params == nullptr || params->includeMissing == false )
req += " AND art.is_present != 0";
req += addRequestConditions( params );
return make_query<Artist, IArtist>( ml, "art.*", std::move( req ),
sortRequest( params ) )
.markPublic( publicOnly ).build();
}
Query<IArtist> Artist::fromGenre( MediaLibraryPtr ml, int64_t genreId,
const QueryParameters* params, bool forcePublic )
{
std::string req = "FROM " + Table::Name +
" art"
" INNER JOIN " +
Media::Table::Name +
" m ON m.artist_id = art.id_artist"
" WHERE m.genre_id = ?";
auto publicOnly = ( params != nullptr && params->publicOnly == true ) || forcePublic == true;
if ( publicOnly == true )
req += " AND m.is_public != 0";
req += addRequestConditions( params );
std::string groupAndOrderBy = " GROUP BY m.artist_id ORDER BY art.name";
if ( params != nullptr )
{
if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" );
if ( params->desc == true )
groupAndOrderBy += " DESC";
}
return make_query<Artist, IArtist>( ml, "art.*", std::move( req ), std::move( groupAndOrderBy ),
genreId )
.markPublic( publicOnly )
.build();
}
Query<IArtist> Artist::searchByGenre( MediaLibraryPtr ml, const std::string& pattern,
const QueryParameters* params, int64_t genreId,
bool forcePublic )
{
std::string req = "FROM " + Table::Name + " a "
"INNER JOIN " + Media::Table::Name + " m ON m.artist_id = a.id_artist "
"WHERE id_artist IN "
"(SELECT rowid FROM " + FtsTable::Name + " WHERE name MATCH ?)"
"AND m.genre_id = ? ";
std::string req = "FROM " + Table::Name +
" art "
"INNER JOIN " +
Media::Table::Name +
" m ON m.artist_id = art.id_artist"
" WHERE id_artist IN "
"(SELECT rowid FROM " +
FtsTable::Name +
" WHERE name MATCH ?)"
" AND m.genre_id = ?";
if ( ( params != nullptr && params->publicOnly == true ) || forcePublic == true )
req += "AND m.is_public != 0 ";
req += " AND m.is_public != 0";
req += addRequestConditions( params );
std::string groupBy = "GROUP BY m.artist_id "
"ORDER BY a.name";
std::string groupBy = " GROUP BY m.artist_id ORDER BY art.name";
if ( params != nullptr )
{
if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
......@@ -837,10 +838,9 @@ Query<IArtist> Artist::searchByGenre( MediaLibraryPtr ml, const std::string& pat
if ( params->desc == true )
groupBy += " DESC";
}
return make_query<Artist, IArtist>( ml, "a.*", std::move( req ),
std::move( groupBy ),
sqlite::Tools::sanitizePattern( pattern ),
genreId ).build();
return make_query<Artist, IArtist>( ml, "art.*", std::move( req ), std::move( groupBy ),
sqlite::Tools::sanitizePattern( pattern ), genreId )
.build();
}
bool Artist::dropMediaArtistRelation( MediaLibraryPtr ml, int64_t mediaId )
......
......@@ -96,7 +96,7 @@ public:
* For instance, if an artist gets assigned the cover from an album, the
* thumbnail object is likely to have the Media origin if the album was just
* created.
* Specifying the origin explicitely allows for a finer control on the hierarchy
* Specifying the origin explicitly allows for a finer control on the hierarchy
*
* The implementation may chose to ignore a thumbnail update based on the
* current & new origin. In this case, `true` will still be returned.
......@@ -129,6 +129,8 @@ public:
ArtistIncluded included, const QueryParameters* params );
static Query<IArtist> listAll( MediaLibraryPtr ml, ArtistIncluded included,
const QueryParameters* params );
static Query<IArtist> fromGenre( MediaLibraryPtr ml, int64_t genreId,
const QueryParameters* params, bool forcePublic );
static Query<IArtist> searchByGenre( MediaLibraryPtr ml, const std::string& pattern,
const QueryParameters* params, int64_t genreId,
bool forcePublic );
......@@ -159,6 +161,7 @@ private:
static std::string sortRequest( const QueryParameters* params );
static bool shouldUpdateThumbnail( const Thumbnail& currentThumbnail );
static std::string addRequestJoin( const QueryParameters* params );
static std::string addRequestConditions( const QueryParameters* params );
private:
MediaLibraryPtr m_ml;
......
......@@ -58,14 +58,22 @@ void CacheWorker::setCacher( std::shared_ptr<ICacher> cacher )
m_cacher = std::move( cacher );
}
void CacheWorker::queueTask( std::shared_ptr<Media> m, bool cache )
bool CacheWorker::queueTask( std::shared_ptr<Media> m, bool cache )
{
std::lock_guard<compat::Mutex> lock{ m_mutex };
if ( m_cacher == nullptr )
{
LOG_WARN( "Cache implementation not provided" );
return false;
}
m_tasks.emplace( std::move( m ), cache );
if ( m_thread.joinable() == false )
m_thread = compat::Thread{ &CacheWorker::run, this };
else
m_cond.notify_all();
return true;
}
bool CacheWorker::cacheMedia( std::shared_ptr<Media> m )
......@@ -78,8 +86,7 @@ bool CacheWorker::cacheMedia( std::shared_ptr<Media> m )
LOG_DEBUG( "Media ", m->id(), " is already cached" );
return true;
}
queueTask( std::move( m ), true );
return true;
return queueTask( std::move( m ), true );
}
bool CacheWorker::removeCached( std::shared_ptr<Media> m )
......@@ -92,8 +99,7 @@ bool CacheWorker::removeCached( std::shared_ptr<Media> m )
LOG_DEBUG( "Media ", m->id(), " is not cached" );
return false;
}
queueTask( std::move( m ), false );
return true;
return queueTask( std::move( m ), false );
}
void CacheWorker::cacheSubscriptions()
......@@ -117,7 +123,9 @@ void CacheWorker::resume()
void CacheWorker::stop()
{
m_cacher->interrupt();
if ( m_cacher != nullptr )
m_cacher->interrupt();
{
std::lock_guard<compat::Mutex> lock{ m_mutex };
if ( m_thread.joinable() == false )
......@@ -289,7 +297,7 @@ bool CacheWorker::evictIfNeeded( const File& file, Subscription* s,
maxMedia = m_ml->settings().nbCachedMediaPerSubscription();
}
auto subCacheSize = s->cachedSize();
auto maxSubCacheSize = s->maxCachedSize();
auto maxSubCacheSize = s->maxCacheSize();
auto nbCachedMediaInSub = s->cachedMedia( false )->count();
LOG_DEBUG( "Subscription #", s->id(), " has ", nbCachedMediaInSub,
"/", maxMedia, " cached media" );
......