pax_global_header00006660000000000000000000000064150106554650014521gustar00rootroot0000000000000052 comment=23b262b133e1b3447d5dc1fee3d94f893f58c128 medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/000077500000000000000000000000001501065546500216315ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/.gitignore000066400000000000000000000001361501065546500236210ustar00rootroot00000000000000*~ *.user #*# .*.swp config.h *.cache *.lo .dirstamp stamp-h1 .deps build* .libs .DS_Store medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/.gitmodules000066400000000000000000000003471501065546500240120ustar00rootroot00000000000000[submodule "test/samples/samples"] path = test/samples/samples url = https://code.videolan.org/videolan/medialibrary-test-samples.git [submodule "libvlcpp"] path = libvlcpp url = https://code.videolan.org/videolan/libvlcpp.git medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/COPYING000066400000000000000000000636431501065546500227000ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/README.md000066400000000000000000000032211501065546500231060ustar00rootroot00000000000000## Dependencies There are 2 modes for building the medialibrary. With libvlc dependency, and without. Usually, unless you're building for integration with VLC desktop, you'll want to enable the libvlc integration. Please note that libvlc is required to build and run the tests. If you want to disable libvlc, you can do so by passing `-Dlibvlc=disabled` to meson when configuring the build. ### Libraries * libvlc * libvlcpp (available as a git submodule) * libsqlite3 (>= 3.33.0) * libjpeg (only when using libvlc 3) * rapidjson when building the functional tests ### Tools * meson * ninja ## Build instructions If some of your dependencies are not installed in a default location, you'll need to point meson to that folder by using `PKG_CONFIG_PATH` and point it to the folder that contains your .pc files The minimal command to setup the build is: ```bash cd /path/to/medialibrary PKG_CONFIG_PATH=/path/to/custom/lib/pkconfig meson build-folder ``` You can omit the `PKG_CONFIG_PATH` if all your dependencies are installed in the standard/default locations. Once configured, building is just a matter of invoking ninja ```bash cd build-folder ninja ``` ## Tests If the libvlc dependency was found, then the tests will be build automatically when required. You can launch them using either ```bash ninja test ``` or ```bash meson test ``` If you only want to run the unit tests, you can do so by running ```bash meson test --suite unittest ``` and similarly for functional tests: ```bash meson test --suite functional ``` You can find more options on how to tweak the test runs in the [meson tests documention](https://mesonbuild.com/Unit-tests.html#unit-tests) medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/000077500000000000000000000000001501065546500235635ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/hash/000077500000000000000000000000001501065546500245065ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/hash/BenchHash.cpp000066400000000000000000000037611501065546500270440ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "utils/XxHasher.h" template static void DoBench( benchmark::State& state ) { std::string::size_type stringLength = state.range( 0 ); std::string input( stringLength, 'x' ); while ( state.KeepRunning() ) { std::unordered_map um; um.emplace( input, 1234 ); auto res = um.find( input ); benchmark::DoNotOptimize( res ); } } static void BenchNormal( benchmark::State& state ) { DoBench>( state ); } static void BenchXXHash( benchmark::State& state ) { DoBench( state ); } BENCHMARK(BenchNormal)->RangeMultiplier( 2 )->Range( 16, 2048 ); BENCHMARK(BenchXXHash)->RangeMultiplier( 2 )->Range( 16, 2048 ); BENCHMARK_MAIN(); medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/hash/meson.build000066400000000000000000000002151501065546500266460ustar00rootroot00000000000000executable('bench_hash', files('BenchHash.cpp'), dependencies: benchmark_dep, link_with: medialib, include_directories: includes ) medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/meson.build000066400000000000000000000000771501065546500257310ustar00rootroot00000000000000subdir('url') subdir('task') subdir('hash') subdir('requests') medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/000077500000000000000000000000001501065546500254365ustar00rootroot00000000000000BenchAlbumRequests.cpp000066400000000000000000000053171501065546500316250ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "BenchRequestsCommon.h" #include #include "Album.h" static void ListAllAlbums( benchmark::State& state ) { auto bml = commonInit(); QueryParameters params{}; params.sort = static_cast( state.range( 0 ) ); for ( auto _ : state ) { auto albums = bml.ml->albums( ¶ms )->all(); benchmark::DoNotOptimize( albums ); } } static void ListAllPublicAlbums( benchmark::State& state ) { auto bml = commonInit(); QueryParameters params{}; params.publicOnly = true; for ( auto _ : state ) { auto albums = bml.ml->albums( ¶ms )->all(); assert(albums.empty() == true); benchmark::DoNotOptimize( albums ); } } static void SearchAlbumByName( benchmark::State& state ) { auto bml = commonInit(); const std::string req = "SELECT * FROM " + Album::Table::Name + " WHERE title = 'album_5'"; for ( auto _ : state ) { auto albums = Album::fetchAll( static_cast( bml.ml.get() ), req ); benchmark::DoNotOptimize( albums ); } } BENCHMARK( ListAllAlbums ) ->Arg( toInt( SortingCriteria::Artist ) ) ->Arg( toInt( SortingCriteria::ReleaseDate ) ) ->Arg( toInt( SortingCriteria::Duration ) ) ->Arg( toInt( SortingCriteria::TrackNumber ) ) ->Arg( toInt( SortingCriteria::PlayCount ) ) ->Arg( toInt( SortingCriteria::InsertionDate ) ) ->Arg( toInt( SortingCriteria::Default ) ); BENCHMARK( ListAllPublicAlbums ); BENCHMARK( SearchAlbumByName ); BenchArtistRequests.cpp000066400000000000000000000043361501065546500320330ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2022 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "BenchRequestsCommon.h" #include #include "Artist.h" static void ListAllArtists( benchmark::State& state ) { auto bml = commonInit(); QueryParameters params{}; params.sort = static_cast( state.range( 0 ) ); for ( auto _ : state ) { auto artists = bml.ml->artists( ArtistIncluded::All, ¶ms )->all(); benchmark::DoNotOptimize( artists ); } } static void ListAllPublicArtists( benchmark::State& state ) { auto bml = commonInit(); QueryParameters params{}; params.publicOnly = true; for ( auto _ : state ) { auto artists = bml.ml->artists( ArtistIncluded::All, ¶ms )->all(); // assert(artists.empty() == true); benchmark::DoNotOptimize( artists ); } } BENCHMARK( ListAllArtists ) ->Arg( toInt( SortingCriteria::Duration ) ) ->Arg( toInt( SortingCriteria::ReleaseDate ) ) ->Arg( toInt( SortingCriteria::InsertionDate ) ) ->Arg( toInt( SortingCriteria::Alpha ) ) ->Arg( toInt( SortingCriteria::Default ) ); BENCHMARK( ListAllPublicArtists ); BenchMediaRequests.cpp000066400000000000000000000041531501065546500316010ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "BenchRequestsCommon.h" #include #include "Album.h" static void ListAllAudioMedia( benchmark::State& state ) { auto bml = commonInit(); QueryParameters params{}; params.sort = static_cast( state.range( 0 ) ); for ( auto _ : state ) { auto albums = bml.ml->audioFiles( ¶ms )->all(); benchmark::DoNotOptimize( albums ); } } BENCHMARK( ListAllAudioMedia ) ->Arg( toInt( SortingCriteria::Duration ) ) ->Arg( toInt( SortingCriteria::InsertionDate ) ) ->Arg( toInt( SortingCriteria::ReleaseDate ) ) ->Arg( toInt( SortingCriteria::PlayCount ) ) ->Arg( toInt( SortingCriteria::Filename ) ) ->Arg( toInt( SortingCriteria::LastModificationDate ) ) ->Arg( toInt( SortingCriteria::FileSize ) ) ->Arg( toInt( SortingCriteria::Album ) ) ->Arg( toInt( SortingCriteria::Artist ) ) ->Arg( toInt( SortingCriteria::TrackId ) ) ->Arg( toInt( SortingCriteria::Alpha ) ); BenchRequestsCommon.cpp000066400000000000000000000030051501065546500320050ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "BenchRequestsCommon.h" #include "utils/File.h" #include "medialibrary/filesystem/Errors.h" BenchMedialib commonInit() { BenchMedialib bml; auto db = "bench.db"; if ( utils::fs::fileSize( db ) == 0 ) abort(); bml.ml.reset( NewMediaLibrary( db, "/tmp/bench_ml", false, nullptr ) ); bml.ml->initialize( &bml.cb ); return bml; } BenchRequestsCommon.h000066400000000000000000000026611501065546500314610ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "MediaLibrary.h" #include #include "test/common/NoopCallback.h" using namespace medialibrary; struct BenchMedialib { mock::NoopCallback cb; std::unique_ptr ml; }; constexpr int64_t toInt( SortingCriteria sc ) { return static_cast( sc ); } BenchMedialib commonInit(); medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/main.cpp000066400000000000000000000023011501065546500270620ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include BENCHMARK_MAIN(); medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/requests/meson.build000066400000000000000000000004471501065546500276050ustar00rootroot00000000000000bench_requests_srcs = [ 'BenchAlbumRequests.cpp', 'BenchMediaRequests.cpp', 'BenchArtistRequests.cpp', 'main.cpp', 'BenchRequestsCommon.cpp' ] executable('bench_requests', bench_requests_srcs, dependencies: benchmark_dep, link_with: medialib, include_directories: includes ) medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/task/000077500000000000000000000000001501065546500245255ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/task/BenchTask.cpp000066400000000000000000000110461501065546500270750ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include "parser/Task.h" static void BenchSetMeta( benchmark::State& state ) { while ( state.KeepRunning() ) { medialibrary::parser::Task t{ nullptr, "dummy://task", medialibrary::IFile::Type::Main }; t.setMeta( medialibrary::parser::Task::Metadata::Title, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::ArtworkUrl, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::ShowName, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Episode, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Album, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Genre, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Date, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::AlbumArtist, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Artist, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::TrackNumber, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::DiscNumber, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::DiscTotal, "value" ); benchmark::DoNotOptimize( t ); } } static void BenchSetAndReadMeta( benchmark::State& state ) { while ( state.KeepRunning() ) { medialibrary::parser::Task t{ nullptr, "dummy://task", medialibrary::IFile::Type::Main }; t.setMeta( medialibrary::parser::Task::Metadata::Title, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::ArtworkUrl, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::ShowName, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Episode, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Album, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Genre, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Date, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::AlbumArtist, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::Artist, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::TrackNumber, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::DiscNumber, "value" ); t.setMeta( medialibrary::parser::Task::Metadata::DiscTotal, "value" ); std::string v; v = t.meta( medialibrary::parser::Task::Metadata::Title ); v = t.meta( medialibrary::parser::Task::Metadata::ArtworkUrl ); v = t.meta( medialibrary::parser::Task::Metadata::ShowName ); v = t.meta( medialibrary::parser::Task::Metadata::Episode ); v = t.meta( medialibrary::parser::Task::Metadata::Album ); v = t.meta( medialibrary::parser::Task::Metadata::Genre ); v = t.meta( medialibrary::parser::Task::Metadata::Date ); v = t.meta( medialibrary::parser::Task::Metadata::AlbumArtist ); v = t.meta( medialibrary::parser::Task::Metadata::Artist ); v = t.meta( medialibrary::parser::Task::Metadata::TrackNumber ); v = t.meta( medialibrary::parser::Task::Metadata::DiscNumber ); v = t.meta( medialibrary::parser::Task::Metadata::DiscTotal ); benchmark::DoNotOptimize( t ); } } // Register the function as a benchmark BENCHMARK(BenchSetMeta); BENCHMARK(BenchSetAndReadMeta); // Run the benchmark BENCHMARK_MAIN(); medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/task/meson.build000066400000000000000000000002151501065546500266650ustar00rootroot00000000000000executable('bench_task', files('BenchTask.cpp'), dependencies: benchmark_dep, link_with: medialib, include_directories: includes ) medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/url/000077500000000000000000000000001501065546500243655ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/url/BenchUrl.cpp000066400000000000000000000035741501065546500266040ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include "utils/Url.h" static void BenchUrlSplit( benchmark::State& state ) { std::string input = "foo://example.com:8042/over/there?name=ferret#nose"; while ( state.KeepRunning() ) { benchmark::DoNotOptimize( medialibrary::utils::url::split( input ) ); } } static void BenchUrlDecode( benchmark::State& state ) { std::string input = "file://%22url%22%20%21benchmark%23%20with%20sp%E2%82%ACci%40l%20c%21%21%24%23%25aracters"; while ( state.KeepRunning() ) { benchmark::DoNotOptimize( medialibrary::utils::url::decode( input ) ); } } // Register the function as a benchmark BENCHMARK(BenchUrlSplit); BENCHMARK(BenchUrlDecode); // Run the benchmark BENCHMARK_MAIN(); medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/benchmark/url/meson.build000066400000000000000000000002131501065546500265230ustar00rootroot00000000000000executable('bench_url', files('BenchUrl.cpp'), dependencies: benchmark_dep, link_with: medialib, include_directories: includes ) medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/ci/000077500000000000000000000000001501065546500222245ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/ci/check-benchmark.py000077500000000000000000000031201501065546500256020ustar00rootroot00000000000000#!/usr/bin/env python3 import json import sys def run_compare(report): with open(report) as f: doc = json.load(f) decrease = 0 increase = 0 for testcase in doc: if len(testcase['aggregate_name']) == 0: if len(testcase['measurements']) > 1: # More than a single measurement, waiting for the aggregate results continue # fall through, measurements describes a single repetition test elif testcase['aggregate_name'] != "mean": # We are interested in the mean aggregate, ignore the others continue # Now we either have an aggregated mean result, or a single rep measurement m = testcase['measurements'][0] time = float(m["time"]) if time < -0.05: print(f"Performance increase detected for test {testcase['name']}", file=sys.stderr) increase += 1 elif time > 0.05: print(f"Performance decrease detected for test {testcase['name']}", file=sys.stderr) decrease += 1 else: print(f"No major performance change detected for test {testcase['name']}", file=sys.stderr) if decrease > 0 or increase > 0: print(f"{increase} tests with improved performance ; {decrease} with performance decrease") if decrease > 0: sys.exit(2) else: print("No performance change") def main(argv): if len(argv) != 2: print(f'Usage: {argv[0]} ') sys.exit(1) run_compare(argv[1]) if __name__ == '__main__': main(sys.argv) medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/ci/dummy.mp3000066400000000000000000000104431501065546500240020ustar00rootroot00000000000000ID3#TSSELavf58.45.100@Info(##)))//555;;AAAHHNNNTTZZZ``fffllrrryyLavc58.91$|) 4LAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU) 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUUS 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU| 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUUĦ 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUUσ 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU 4UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUmedialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/ci/generate-samples.sh000077500000000000000000000025531501065546500260240ustar00rootroot00000000000000#!/bin/sh NB_ARTISTS=100 NB_ALBUMS_PER_ARTIST=10 NB_TRACKS_PER_ALBUM=10 SCRIPT_DIRECTORY=$(dirname "$0") CORPUS_DIRECTORY=/tmp/medialib_samples/ usage() { echo "usage: $0 [-o dest_folder] [-n nb_artists]" exit 1 } while getopts ":o:n:" ARG; do case "$ARG" in o) CORPUS_DIRECTORY=$OPTARG ;; n) NB_ARTISTS=$OPTARG ;; *) usage ;; esac done echo "Generating samples to $CORPUS_DIRECTORY" echo "Generating $NB_ARTISTS artists with 10 albums of 10 tracks each" for i_art in `seq 1 $NB_ARTISTS`; do ARTIST_NAME="artist_$i_art" ARTIST_FOLDER=$CORPUS_DIRECTORY/$ARTIST_NAME for i_alb in `seq 1 $NB_ALBUMS_PER_ARTIST`; do ALBUM_NAME="album_$i_alb" ALBUM_FOLDER=$ARTIST_FOLDER/$ALBUM_NAME mkdir -p $ALBUM_FOLDER for i_track in `seq 1 $NB_TRACKS_PER_ALBUM`; do TRACK_NAME="track_$i_track" TRACK_PATH=$ALBUM_FOLDER/$TRACK_NAME.mp3 cp $SCRIPT_DIRECTORY/dummy.mp3 $TRACK_PATH id3tag --artist=$ARTIST_NAME --album=$ALBUM_NAME \ --song=$TRACK_NAME \ --track=$i_track \ --total=$NB_TRACKS_PER_ALBUM \ --year=$((2000 + $i_alb)) \ --genre=$(shuf -i 1-80 -n 1) \ $TRACK_PATH done done done medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/ci/gitlab-ci.yml000066400000000000000000000207201501065546500246030ustar00rootroot00000000000000variables: GIT_SUBMODULE_STRATEGY: normal 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/ stages: - build - test - generate default: tags: - docker - amd64 interruptible: true build:novlc: image: $MEDIALIBRARY_30_IMAGE stage: build rules: - if: $CI_MERGE_REQUEST_IID script: - meson -Dlibvlc=disabled --buildtype=release build - cd build && ninja build:alpine: image: $MEDIALIBRARY_ALPINE_IMG stage: build rules: - if: $CI_MERGE_REQUEST_IID script: - meson build - cd build && ninja build:arch: image: $MEDIALIBRARY_ARCH_IMG stage: build rules: - if: $CI_MERGE_REQUEST_IID script: - meson build - cd build && ninja .test:debian.base: image: $MEDIALIBRARY_30_IMAGE rules: - if: $CI_MERGE_REQUEST_IID - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_PIPELINE_SOURCE == "schedule" && $MEDIALIB_MANUAL_JOB_NAME == null' stage: test script: - cd $CI_PROJECT_DIR - meson -Db_coverage=true build - cd build - meson test --no-stdsplit - gcovr -r "$CI_PROJECT_DIR/" --json $CI_PROJECT_DIR/$CI_JOB_NAME.cov.json -j 4 artifacts: reports: junit: build/meson-logs/testlog.junit.xml paths: - $MEDIALIB_TEST_FOLDER/**/test.db - $CI_PROJECT_DIR/build/meson-logs/testlog.txt - $CI_PROJECT_DIR/$CI_JOB_NAME.cov.json when: always test:debian-3.0: extends: .test:debian.base image: $MEDIALIBRARY_30_IMAGE test:debian-4.0: extends: .test:debian.base image: $MEDIALIBRARY_40_IMAGE test:win32: image: $MEDIALIBRARY_WIN32_IMG allow_failure: true variables: MESON_TESTTHREADS: 8 stage: test rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "$CI_DEFAULT_BRANCH@videolan/medialibrary"' - if: '$CI_PIPELINE_SOURCE == "schedule"' when: never script: - > meson -Dpkg_config_path=/prefix/lib/pkgconfig --cross-file=/opt/crossfiles/i686-w64-mingw32.meson -Ddefault_library=static build - cd build && ninja - cp /prefix/dll/libvlc.dll . - cp /prefix/dll/libvlccore.dll . - ln -s /prefix/lib/vlc/plugins/ . - wineserver -p && wine wineboot - MEDIALIB_TEST_FOLDER=`winepath -w $MEDIALIB_TEST_FOLDER` meson test --no-stdsplit artifacts: when: always reports: junit: build/meson-logs/testlog.junit.xml paths: - $MEDIALIB_TEST_FOLDER/**/test.db - $CI_PROJECT_DIR/build/meson-logs/testlog.txt expire_in: 1 week test:win64: image: $MEDIALIBRARY_WIN64_IMG allow_failure: true variables: MESON_TESTTHREADS: 8 stage: test rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "$CI_DEFAULT_BRANCH@videolan/medialibrary"' - if: '$CI_PIPELINE_SOURCE == "schedule"' when: never script: - > PKG_CONFIG_PATH=/prefix/lib/pkgconfig meson --cross-file=/opt/crossfiles/x86_64-w64-mingw32.meson build - cd build && ninja - wineserver -p && wine wineboot - cp /prefix/dll/libvlc.dll . - cp /prefix/dll/libvlccore.dll . - ln -s /prefix/lib/vlc/plugins/ . - MEDIALIB_TEST_FOLDER=`winepath -w $MEDIALIB_TEST_FOLDER` meson test --no-stdsplit artifacts: when: always reports: junit: build/meson-logs/testlog.junit.xml paths: - $MEDIALIB_TEST_FOLDER/**/test.db - $CI_PROJECT_DIR/build/meson-logs/testlog.txt expire_in: 1 week asan-ubsan: image: $VLC_DEBIAN_UNSTABLE_IMG rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $MEDIALIB_MANUAL_JOB_NAME == null' stage: test variables: LSAN_OPTIONS: 'detect_leaks=0' script: - git clone https://code.videolan.org/videolan/vlc.git --depth=1 - cd vlc && ./bootstrap - ./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 $CI_PROJECT_DIR - 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: - $MEDIALIB_TEST_FOLDER/**/test.db - $CI_PROJECT_DIR/build/meson-logs/testlog.txt - $CI_PROJECT_DIR/build/meson-logs/longtests.txt expire_in: 1 week .base-sanitizer: image: $MEDIALIBRARY_40_IMAGE rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "$CI_DEFAULT_BRANCH@videolan/medialibrary"' stage: test script: - cd $CI_PROJECT_DIR - > CXX=clang++ 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 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: - $MEDIALIB_TEST_FOLDER/**/test.db - $CI_PROJECT_DIR/build/meson-logs/testlog.txt - $CI_PROJECT_DIR/build/meson-logs/longtests.txt expire_in: 1 week test:tsan: extends: .base-sanitizer image: $MEDIALIBRARY_30_IMAGE variables: TSAN_OPTIONS: 'suppressions=$CI_PROJECT_DIR/ci/tsan_suppress_file' SANITIZERS: thread test:asan-ubsan: extends: .base-sanitizer variables: LSAN_OPTIONS: 'detect_leaks=0' SANITIZERS: address,undefined gen-test-db: image: $MEDIALIBRARY_30_IMAGE stage: generate dependencies: [] rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $MEDIALIB_MANUAL_JOB_NAME == "gen-test-db"' script: - meson --buildtype=release build - cd build && ninja - ../ci/generate-samples.sh -o $CI_PROJECT_DIR/dummysamples -n 10 - test/discoverer/discoverer $CI_PROJECT_DIR/dummysamples -q - echo "BEGIN;" > $CI_PROJECT_DIR/test_db.sql - > sqlite3 $MEDIALIB_TEST_FOLDER/medialib/discoverer_test/test.db '.schema --nosys' | grep -vF '/*' >> $CI_PROJECT_DIR/test_db.sql - > sqlite3 $MEDIALIB_TEST_FOLDER/medialib/discoverer_test/test.db '.dump --data-only --nosys' | grep -v '^INSERT INTO [[:alpha:]]*Fts' >> $CI_PROJECT_DIR/test_db.sql - echo "COMMIT;" >> $CI_PROJECT_DIR/test_db.sql - mv $MEDIALIB_TEST_FOLDER/medialib/discoverer_test/test.db $CI_PROJECT_DIR/ artifacts: when: on_success expire_in: 1 day paths: - "$CI_PROJECT_DIR/test_db.sql" - "$CI_PROJECT_DIR/test.db" # Combine multiple coverage output into a single coverage artifact gen-coverage: image: $MEDIALIBRARY_30_IMAGE stage: generate needs: ["test:debian-3.0", "test:debian-4.0"] rules: - if: $CI_MERGE_REQUEST_IID - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_PIPELINE_SOURCE == "schedule" && $MEDIALIB_MANUAL_JOB_NAME == null' script: - mkdir html - > gcovr --add-tracefile '*.cov.json' -e "$CI_PROJECT_DIR/libvlcpp" -e "$CI_PROJECT_DIR/test" -e "$CI_PROJECT_DIR/src/database/SqliteErrors.h" -e "$CI_PROJECT_DIR/src/database/SqliteErrors.cpp" -e "$CI_PROJECT_DIR/include/medialibrary/filesystem/Errors.h" -e "$CI_PROJECT_DIR/include/medialibrary/IMediaLibrary.h" -e "$CI_PROJECT_DIR/src/utils/xxhash/" --xml cobertura.xml --html=html/medialibrary.html --html-details -s -j 4 coverage: /^\s*lines:\s*\d+.\d+\%/ artifacts: reports: coverage_report: coverage_format: cobertura path: cobertura.xml paths: - html/ name: "coverage-medialibrary-$CI_COMMIT_SHORT_SHA" medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/ci/tsan_suppress_file000066400000000000000000000001741501065546500260610ustar00rootroot00000000000000race:libavcodec.so race:libavutil.so race:std::__1::ios_base::width race:libvlccore.so race:EventManager.hpp race:libtag.so medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/config.h.in000066400000000000000000000007711501065546500236610ustar00rootroot00000000000000#define PROJECT_VERSION "@PROJECT_VERSION@" #mesondefine _FILE_OFFSET_BITS #ifndef _WIN32_WINNT #mesondefine _WIN32_WINNT #endif #mesondefine _UNICODE #mesondefine UNICODE #mesondefine _POSIX_C_SOURCE #mesondefine _BSD_SOURCE #mesondefine _GNU_SOURCE #mesondefine HAVE_LINK #mesondefine CXX11_THREADS #mesondefine CXX11_MUTEX #mesondefine CXX11_THREAD_LOCAL #mesondefine CXX11_CONDITION_VARIABLE #mesondefine HAVE_LIBVLC #mesondefine HAVE_JPEG #mesondefine FORCE_ATTACHMENTS_API #include "Fixup.h" medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/000077500000000000000000000000001501065546500232545ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/Common.h000066400000000000000000000040401501065546500246530ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #ifdef __GNUC__ # define ML_FORCE_USED __attribute__ ((warn_unused_result)) #else #define ML_FORCE_USED #endif #ifdef NDEBUG # define ML_UNHANDLED_EXCEPTION_INIT try # define ML_UNHANDLED_EXCEPTION_BODY(ctx) \ catch ( const sqlite::errors::Exception& ex ) \ { \ if ( m_ml->getCb()->onUnhandledException( ctx, \ ex.what(), \ ex.requiresDbReset() ) == true ) \ return; \ throw; \ } \ catch ( const std::exception& ex ) \ { \ if ( m_ml->getCb()->onUnhandledException( ctx, \ ex.what(), false ) == true ) \ return; \ throw; \ } #else # define ML_UNHANDLED_EXCEPTION_INIT # define ML_UNHANDLED_EXCEPTION_BODY(ctx) #endif #ifdef NDEBUG #define UNUSED_IN_RELEASE(x) (void)(x) #else #define UNUSED_IN_RELEASE(x) #endif medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/Fixup.h000066400000000000000000000023471501065546500245260ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #ifdef _MSC_VER # define strcasecmp _stricmp #endif #ifndef CXX11_THREAD_LOCAL # define thread_local __thread #endif medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/Types.h000066400000000000000000000023171501065546500245340ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2016-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once namespace medialibrary { class MediaLibrary; using MediaLibraryPtr = const MediaLibrary*; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/000077500000000000000000000000001501065546500257205ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IAlbum.h000066400000000000000000000100071501065546500272400ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "IQuery.h" #include "Types.h" namespace medialibrary { class IAlbum { public: virtual ~IAlbum() = default; virtual int64_t id() const = 0; virtual const std::string& title() const = 0; /** * @brief releaseYear returns the release year, or 0 if unknown. */ virtual unsigned int releaseYear() const = 0; virtual const std::string& shortSummary() const = 0; /** * @brief thumbnailStatus Returns this album current thumbnail status. */ virtual ThumbnailStatus thumbnailStatus( ThumbnailSizeType sizeType ) const = 0; virtual const std::string& thumbnailMrl( ThumbnailSizeType sizeType ) const = 0; /** * @brief tracks fetches album tracks from the database */ virtual Query tracks( const QueryParameters* params = nullptr ) const = 0; /** * @brief tracks fetches album tracks, filtered by genre * @param genre A musical genre. Only tracks of this genre will be returned * @return the requested tracks */ virtual Query tracks( GenrePtr genre, const QueryParameters* params = nullptr ) const = 0; /** * @brief albumArtist Returns the album main artist (generally tagged as album-artist) * This can be an artist that doesn't appear on the album, and is solely dependent * on the most present AlbumArtist tag for all of this album's tracks */ virtual ArtistPtr albumArtist() const = 0; /** * @brief artists Returns a Query object representing all artists appearing * on at least one track for this album. * Artists are sorted by name. * @param params the query parameters */ virtual Query artists( const QueryParameters* params = nullptr ) const = 0; /** * @brief nbTracks Returns the amount of track in this album. * The value is cached, and doesn't require fetching anything. */ virtual uint32_t nbTracks() const = 0; /** * @brief nbPresentTracks Returns the number of present tracks in this album */ virtual uint32_t nbPresentTracks() const = 0; /** * @brief nbDiscs Returns the total number of discs for this album. * Defaults to 1 */ virtual uint32_t nbDiscs() const = 0; /** * @brief duration Returns the total album duration in milliseconds */ virtual int64_t duration() const = 0; /** * @brief isUnknownAlbum returns true is this is an unknown album */ virtual bool isUnknownAlbum() const = 0; /** * @brief isFavorite returns true if the album is marked as favorite. */ virtual bool isFavorite() const = 0; /** * @brief setFavorite mark or unmark an album as favorite. */ virtual bool setFavorite( bool ) = 0; virtual Query searchTracks( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IArtist.h000066400000000000000000000072001501065546500274470ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "IQuery.h" #include "Types.h" namespace medialibrary { class IArtist { public: virtual ~IArtist() = default; virtual int64_t id() const = 0; virtual const std::string& name() const = 0; virtual const std::string& shortBio() const = 0; /** * @brief albums List the albums this artist appears on. * * This will return all albums by this artist, and all album the artist * appeared on, even if they are not the main artist (or AlbumArtist) */ virtual Query albums( const QueryParameters* params = nullptr ) const = 0; virtual Query searchAlbums( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual Query tracks( const QueryParameters* params = nullptr ) const = 0; virtual Query searchTracks( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /** * @brief thumbnailStatus returns this artist thumbnail status * * @param sizeType The targeted thumbnail size * @see{medialibrary::ThumbnailStatus} */ virtual ThumbnailStatus thumbnailStatus( ThumbnailSizeType sizeType ) const = 0; virtual const std::string& thumbnailMrl( ThumbnailSizeType sizeType ) const = 0; /** * @brief setThumbnail Assign a thumbnail to the artist * @param thumbnailMrl An mrl pointing to the thumbnail * @return true in case of success, false otherwise * * @note The medialibrary does not take ownership of the thumbnail. It is * application responsibility to ensure that it will always be available * or that a later call will invalidate the thumbnail if it gets (re)moved */ virtual bool setThumbnail( const std::string& thumbnailMrl, ThumbnailSizeType sizeType ) = 0; virtual const std::string& musicBrainzId() const = 0; /** * @brief nbAlbums * @return The number of albums *by* this artist. This doesn't include the * albums an artist appears on. */ virtual unsigned int nbAlbums() const = 0; virtual unsigned int nbTracks() const = 0; virtual unsigned int nbPresentTracks() const = 0; /** * @brief isFavorite returns true if the artist is marked as favorite. */ virtual bool isFavorite() const = 0; /** * @brief setFavorite mark or unmark an artist as favorite. */ virtual bool setFavorite( bool ) = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IAudioTrack.h000066400000000000000000000032711501065546500302330ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class IAudioTrack { public: virtual ~IAudioTrack() = default; virtual int64_t id() const = 0; virtual const std::string& codec() const = 0; /** * @return The bitrate in bits per second */ virtual unsigned int bitrate() const = 0; virtual unsigned int sampleRate() const = 0; virtual unsigned int nbChannels() const = 0; virtual const std::string& language() const = 0; virtual const std::string& description() const = 0; virtual bool isInAttachedFile() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IBookmark.h000066400000000000000000000061011501065546500277450ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { class IBookmark { public: enum class Type : uint8_t { Simple, }; virtual ~IBookmark() = default; /** * @brief id Returns the bookmark unique identifier */ virtual int64_t id() const = 0; /** * @brief mediaId Returns the associated media ID */ virtual int64_t mediaId() const = 0; /** * @brief time Returns the time of this bookmark, as it was * provided to \ref{IMedia::addBookmark} */ virtual int64_t time() const = 0; /** * @brief name Returns the bookmark name */ virtual const std::string& name() const = 0; /** * @brief setName Updates the bookmark name */ virtual bool setName( std::string name ) = 0; /** * @brief name Returns the bookmark description */ virtual const std::string& description() const = 0; /** * @brief creationDate Returns this bookmark creation date * * The date is expressed in seconds since Epoch (UTC) */ virtual time_t creationDate() const = 0; /** * @brief type Returns this bookmark type * * This is not returning valuable information for now and is here for * future use. */ virtual Type type() const = 0; /** * @brief setDescription Updates the bookmark description */ virtual bool setDescription( std::string description ) = 0; /** * @brief setNameAndDescription Convenience helper to update the name and * description in a single operation */ virtual bool setNameAndDescription( std::string name, std::string desc ) = 0; /** * @brief move Move a bookmark to a new time in the media * @param newTime The new time for this bookmark * @return true if successful, false otherwise. * * @note If a bookmark is already present at the given time, the move will fail */ virtual bool move( int64_t newTime ) = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/ICacher.h000066400000000000000000000025541501065546500273750ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright © 2022 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include namespace medialibrary { class ICacher { public: virtual ~ICacher() = default; virtual bool cache( const std::string& inputMrl, const std::string& outputPath ) = 0; virtual void interrupt() = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IChapter.h000066400000000000000000000031511501065546500275700ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2018-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class IChapter { public: virtual ~IChapter() = default; /** * @brief name Returns the chapter name */ virtual const std::string& name() const = 0; /** * @brief offset Returns the offset from the start of the media in seconds */ virtual int64_t offset() const = 0; /** * @brief duration Returns the duration of this chapter in seconds */ virtual int64_t duration() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IDeviceLister.h000066400000000000000000000057601501065546500305740ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include namespace medialibrary { /** * @brief IDeviceListerCb is intended for external device listers to signal device modifications * * 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 responsibility */ class IDeviceListerCb { public: virtual ~IDeviceListerCb() = default; /** * @brief onDeviceMounted Shall be invoked when a known device gets mounted * @param uuid The device UUID * @param mountpoint The device new mountpoint * @param removable The removable state of the mounted device */ virtual void onDeviceMounted( const std::string& uuid, const std::string& mountpoint, bool removable ) = 0; /** * @brief onDeviceUnmounted Shall be invoked when a known device gets unmounted * @param uuid The device UUID * @param mountpoint The mountpoint the device was mounted on */ virtual void onDeviceUnmounted( const std::string& uuid, const std::string& mountpoint ) = 0; }; class IDeviceLister { public: virtual ~IDeviceLister() = default; /** * @brief refresh Force a device refresh * * Implementation that solely rely on callback can implement this as a no-op * as long as they are guaranteed to invoke onDeviceMounted & * onDeviceUnmounted as soon as the information is available. */ virtual void refresh() = 0; /** * @brief start Starts watching for new device * @param cb An IDeviceListerCb implementation to invoke upon device changes * @return true in case of success, false otherwise */ virtual bool start( IDeviceListerCb* cb ) = 0; /** * @brief stop Stop watching for new device */ virtual void stop() = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IFile.h000066400000000000000000000073511501065546500270670ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { class IFile { public: /** * @brief Describes the type of a file * @warning These values are stored in DB. As such, any new value must be * appended, as modifying the existing values would invalidate any * existing db record. */ enum class Type { /// Unknown type, so far Unknown, /// The main file of a media. Main, /// A part of a media (for instance, the first half of a movie) Part, /// External soundtrack Soundtrack, /// External subtitles Subtitles, /// A playlist File Playlist, /// A disc file. Also considered to be a "main" file Disc, /// A resource describing a subscription. Subscription, /// A cached version of the main file Cache, }; enum class CacheType : uint8_t { Uncached, Manual, Automatic, }; virtual ~IFile() = default; virtual int64_t id() const = 0; /** * @brief mrl Returns the full mrl for this file. * Since we can't compute an mrl for a file or folder that is/was present on * a removable storage or network share that is not mounted, a * fs::DeviceRemovedException will be thrown when trying to get the mrl of * a non present file. * You should always account for this exception is isRemovable returns true. * If for some reasons we can't compute the MRL, an empty string will be returned * @return The folder's mrl */ virtual const std::string& mrl() const = 0; virtual Type type() const = 0; virtual time_t lastModificationDate() const = 0; virtual uint64_t size() const = 0; virtual bool isRemovable() const = 0; /// /// \brief isExternal returns true if this stream isn't managed by the medialibrary /// virtual bool isExternal() const = 0; /** * @brief isNetwork returns true if this file is on a network location * * If the file is external, this is a best guess effort. */ virtual bool isNetwork() const = 0; /** * @brief isMain Returns true if this file is the main file of a media * * This can be used to have a Disc file considered as the main file */ virtual bool isMain() const = 0; virtual time_t insertionDate() const = 0; /** * @brief cacheType Returns the cache type, if the file is a cached representation. * * If this is not a cache file, CacheType::Uncached will be returned. */ virtual CacheType cacheType() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IFolder.h000066400000000000000000000136511501065546500274230ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "Types.h" #include "IQuery.h" #include "IMedia.h" namespace medialibrary { class IFolder { public: virtual ~IFolder() = default; virtual int64_t id() const = 0; /** * @brief mrl Returns the full mrl for this folder. * Since we can't compute an mrl for a folder that is/was present on a * removable storage or network share that is not mounted, a * fs::DeviceRemovedException will be thrown when trying to get the mrl of * a non present folder. * Calling isPresent can prevent this to be called with a known missing * device, but there is always a window between a call to isPresent and mrl() * in which the device could be removed. * When calling this function on a removable device, you should check * for fs::DeviceRemovedException in any case. * If for some reasons we can't compute the MRL, an empty string will be returned * @return The folder's mrl */ virtual const std::string& mrl() const = 0; virtual const std::string& name() const = 0; virtual bool isPresent() const = 0; virtual bool isRemovable() const = 0; /** * @brief isBanned Will return true if the folder was explicitly banned * from being discovered. */ virtual bool isBanned() const = 0; virtual bool isPublic() const = 0; virtual bool setPublic( bool isPublic ) = 0; /** * @brief media Returns the media contained by this folder. * @param type The media type, or IMedia::Type::Unknown for all types * @param params A query parameter instance, or nullptr for the default * @return A Query object to be used to fetch the results * * This function will only return the media contained in the folder, not * the media contained in subfolders. * A media is considered to be in a directory when the main file representing * it is part of the directory. * For instance, in this file hierarchy: * . * ├── a * │ ├── c * │ │ └── NakedMoleRat.asf * │ └── seaotter_themovie.srt * └── b * └── seaotter_themovie.mkv * Media of 'a' would be empty (since the only file is a subtitle file and * not the actual media, and NakedMoleRat.asf * is in a subfolder) * Media of 'c' would contain NakedMoleRat.asf * Media of 'b' would contain seaotter_themovie.mkv */ virtual Query media( IMedia::Type type, const QueryParameters* params = nullptr ) const = 0; /** * @brief searchMedia Search the media of a given folder * @param pattern The pattern to search for * @param type The media type, or IMedia::Type::Unknown for all types * @param params A query parameter instance, or nullptr for the default * @return A Query object to be used to fetch the results * * This only search for the folder in a specific folder, not including the * media in its subfolders. */ virtual Query searchMedia( const std::string& pattern, IMedia::Type type, const QueryParameters* params = nullptr ) const = 0; /** * @brief subfolders Returns the subfolders contained folder * @return A query object to be used to fetch the results * * all of the folder subfolders, regardless of the folder content. * For instance, in this hierarchy: * ├── a * │ └── w * │ └── x * a->subfolders() would return w; w->subfolders would return x, even though * x is empty. * This is done for optimization purposes, as keeping track of the entire * folder hierarchy would be quite heavy. * As an alternative, it is possible to use IMediaLibrary::folders to return * a flattened list of all folders that contain media. */ virtual Query subfolders( const QueryParameters* params = nullptr ) const = 0; /** * @brief playlists Returns the playlists contained in this directory * @param params A query parameter instance, or nullptr for the default * @return A query object to be used to fetch the results */ virtual Query playlists( const QueryParameters* params = nullptr ) const = 0; virtual uint32_t nbVideo() const = 0; virtual uint32_t nbAudio() const = 0; virtual uint32_t nbMedia() const = 0; /** * @brief duration Returns this group duration * * Which is equal to the sum of all its member's durations * If some media duration is unknown, it is ignored in this total. */ virtual int64_t duration() const = 0; virtual bool isFavorite() const = 0; virtual bool setFavorite( bool favorite ) = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IGenre.h000066400000000000000000000104571501065546500272510ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "Types.h" #include "IQuery.h" namespace medialibrary { class IGenre { public: enum class TracksIncluded : uint8_t { /// Include all present tracks in the listing All, /// Only include tracks with a thumbnail WithThumbnailOnly, }; virtual ~IGenre() = default; virtual int64_t id() const = 0; virtual const std::string& name() const = 0; virtual uint32_t nbTracks() const = 0; virtual uint32_t nbPresentTracks() const = 0; virtual Query artists( const QueryParameters* params = nullptr ) const = 0; virtual Query searchArtists( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /** * @brief tracks Returns the tracks associated with this genre * @param included A TracksIncluded flag to specify which tracks to return * @param params Some query parameters, or nullptr for the default. * * This function supports sorting by: * - Duration * - InsertionDate * - ReleaseDate * - Alpha * * The default sort is to group tracks by their artist, album, disc number, * track number, and finally file name in case of ambiguous results. * Sort is ascending by default. */ virtual Query tracks( TracksIncluded included, const QueryParameters* params = nullptr ) const = 0; virtual Query searchTracks( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual Query albums( const QueryParameters* params = nullptr ) const = 0; virtual Query searchAlbums( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /** * @brief thumbnailMrl Returns this genre thumbnail mrl * @param sizeType The target thumbnail size type * @return An mrl to the thumbnail if any, or an empty string */ virtual const std::string& thumbnailMrl( ThumbnailSizeType sizeType ) const = 0; /** * @brief hasThumbnail Returns true if this genre has a thumbnail available * @param sizeType The probed thumbnail size type * @return true if a thumbnail is available, false otherwise */ virtual bool hasThumbnail( ThumbnailSizeType sizeType ) const = 0; /** * @brief setThumbnail Set a thumbnail for this genre * @param mrl The thumbnail mrl * @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 overridden, false otherwise. */ virtual bool setThumbnail( const std::string& mrl, ThumbnailSizeType sizeType, bool takeOwnership ) = 0; /** * @brief isFavorite returns true if the genre is marked as favorite. */ virtual bool isFavorite() const = 0; /** * @brief setFavorite mark or unmark a genre as favorite. */ virtual bool setFavorite( bool ) = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/ILabel.h000066400000000000000000000026441501065546500272270ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "IQuery.h" namespace medialibrary { class IMedia; class ILabel { public: virtual ~ILabel() = default; virtual int64_t id() const = 0; virtual const std::string& name() const = 0; virtual Query media() = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/ILogger.h000066400000000000000000000031031501065546500274160ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include namespace medialibrary { enum class LogLevel { Verbose, Debug, Info, Warning, Error, }; class ILogger { public: virtual ~ILogger() = default; virtual void Error( const std::string& msg ) = 0; virtual void Warning( const std::string& msg ) = 0; virtual void Info( const std::string& msg ) = 0; virtual void Debug( const std::string& msg ) = 0; virtual void Verbose( const std::string& msg ) = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IMedia.h000066400000000000000000000467521501065546500272370ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include #include #include #include "IFile.h" #include "IQuery.h" #include "Types.h" namespace medialibrary { class IMedia { public: enum class Type : uint8_t { /** * Unknown media type. Type is used to avoid 0 being a valid value * Media that are discovered by the medialibrary will not be * added to the collection when their type can't be determined */ Unknown, /** * Video media */ Video, /** * Audio media */ Audio, }; enum class SubType : uint8_t { Unknown, ShowEpisode, Movie, AlbumTrack, }; enum class MetadataType : uint32_t { Rating = 1, // Playback /* * Removed starting from model 27, this is now a full field in the * media table * Progress = 50, */ Speed = 51, Title, Chapter, Program, // Seen, // Replaced by the media playcount // Video: VideoTrack = 100, AspectRatio, Zoom, Crop, Deinterlace, VideoFilter, // Audio AudioTrack = 150, Gain, AudioDelay, // Spu SubtitleTrack = 200, SubtitleDelay, // Various ApplicationSpecific = 250, }; static constexpr size_t NbMeta = 17; /** * @brief The ProgressResult enum describes the result for setLastPosition * and setLastTime operations */ enum class ProgressResult : uint8_t { /// 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 media playback is now not /// considered started. Begin, /// The provided position/time was not interpreted as a special position /// and was updated as provided in the database. The playback will be /// considered in progress AsIs, /// The provided position/time was interpreted as the end of the media. /// The playback will not be considered in progress anymore and the /// play count has been incremented. End, }; virtual ~IMedia() = default; virtual int64_t id() const = 0; virtual Type type() const = 0; /** * @brief setType Updates this media's type * @param type The new type * @return true in case of success, false otherwise. * * If the media type was Unknown before, this will trigger a refresh for * this media. * If the refresh task fails to be created, false will be returned, and * the media will stay unmodified. */ virtual bool setType( Type type ) = 0; virtual SubType subType() const = 0; virtual const std::string& title() const = 0; /** * @brief setTitle Enforces a title for this media * @param title The new title * @return true if the title was successfully modified, false otherwise */ virtual bool setTitle( const std::string& title ) = 0; /** * @brief duration Returns the media duration in ms */ virtual int64_t duration() const = 0; virtual uint32_t playCount() const = 0; /** * @brief lastPosition Returns the last saved progress * * This is the same unit as VLC's playback position, ie. a float between * 0 and 1. * If the value is negative, it means the playback has either never been * played, or it was played to completion * If the duration is unknown, the media library will just return what the * application provided during its last call to setLastPosition() */ virtual float lastPosition() const = 0; /** * @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 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 (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 * first 5% will not increase the progress, and the last 5% will mark the * media as watched and reset the progress value (so that next playback * restarts from the beginning) * These 5% are decreased by 1% for every playback hour, so for instance, a * 3h movie will use 5% - (3h * 1%), so the first 2% will be ignored, the last * 2% will trigger the completion. * If the media duration is unknown, the progress will be stored as-is in * database but the playcount will not be updated, nor will the position be * clamped in the [0;1] range. * * Calling lastPosition() or playCount() afterward will fetch the curated values. * This will also bump the media last played date, causing it to appear at * the top of the history. * If the duration is known, this will also update lastTime(). * If the duration is unknown, lastTime will be set to -1 when this function * is called. */ virtual IMedia::ProgressResult setLastPosition( float lastPosition ) = 0; /** * @brief lastTime Returns the last playback time as provided by the application * * This is expected to be a time in millisecond, but is ultimately what the * application provided to the media library. * This defaults to -1 is the playback isn't in progress */ virtual int64_t lastTime() const = 0; /** * @brief setLastTime Sets the last playback time. * @param lastTime A time in millisecond * @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 * milliseconds rather than a percentage. * If the duration is unknown, calling this function will reset the lastProgress * to -1. */ virtual IMedia::ProgressResult setLastTime( int64_t lastTime ) = 0; /** * @brief setPlayCount Set a specific value to this media's play count * * This is mostly intended for migrations where single step increment * would not be the most efficient way. * This method will not bump the media in the history */ virtual bool setPlayCount( uint32_t playCount ) = 0; virtual time_t lastPlayedDate() const = 0; /** * @brief markAsPlayed Will mark the media as played and will bump it in the * history * @return true in case of success, false otherwise * * This is intended as an alternative to setLastPosition/setLastTime in case * where the user isn't interested in saving the progression in database, but * still cares about the media appearing in the history and using its play * count. */ virtual bool markAsPlayed() = 0; virtual ShowEpisodePtr showEpisode() const = 0; virtual const std::vector& files() const = 0; virtual FilePtr mainFile() const = 0; /** * @brief addFile Add a file to this media * @param mrl The new file mrl * @param fileType The new file type * @return The file pointer */ virtual FilePtr addFile( const std::string& mrl, IFile::Type fileType ) = 0; /** * @return The main file's filename */ virtual const std::string& fileName() const = 0; virtual FilePtr addExternalMrl( const std::string& mrl, IFile::Type type ) = 0; virtual bool isFavorite() const = 0; virtual bool setFavorite( bool favorite ) = 0; virtual bool addLabel( LabelPtr label ) = 0; virtual bool removeLabel( LabelPtr label ) = 0; virtual MoviePtr movie() const = 0; virtual Query labels() const = 0; virtual Query videoTracks() const = 0; virtual Query audioTracks() const = 0; virtual Query subtitleTracks() const = 0; /// /// \brief chapters Returns the chapters for this media, if any /// /// For this query, the default sorting parameter is by chapter offset. /// Supported criteria are: Alpha, Duration, Default. /// Any other criteria will fallback to default. /// Default order for duration is from longer to shorter. /// Passing desc = true will invert this default. /// virtual Query chapters( const QueryParameters* params = nullptr ) const = 0; /// /// \brief thumbnail Returns the mrl of a thumbnail of the given size for this media /// \param sizeType The targeted thumbnail size /// \return An mrl, representing the absolute path to the media thumbnail /// or an empty string, if the thumbnail generation failed or /// was never requested /// /// \sa{thumbnailStatus} /// virtual const std::string& thumbnailMrl( ThumbnailSizeType sizeType ) const = 0; /// /// \brief thumbnailStatus Returns this media thumbnail status /// \param sizeType The targeted thumbnail size /// /// This will return Missing if no thumbnail generation has been requested /// for this media, or Success/Failure/Crash, depending on the generation /// results. /// virtual ThumbnailStatus thumbnailStatus( ThumbnailSizeType sizeType ) const = 0; /// /// \brief setThumbnail Sets a thumbnail for the current media /// \param mrl A mrl pointing the the thumbnail file. /// \param sizeType The targeted thumbnail size type /// \return true in case the thumbnail was successfully stored to database /// false otherwise /// This is intended to be used by applications that have their own way /// of computing thumbnails. /// virtual bool setThumbnail( const std::string& mrl, ThumbnailSizeType sizeType ) = 0; /// /// \brief requestThumbnail Queues a thumbnail generation request for /// this media, to be run asynchronously. /// 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 overridden. /// \param sizeType The size type of the thumbnail to generate /// \param desiredWidth The desired thumbnail width /// \param desiredHeight The desired thumbnail height /// \param position The position at which to generate the thumbnail, in [0;1] range /// /// The generated thumbnail will try to oblige by the requested size, while /// respecting the source aspect ratio. If the aspect ratios differ, the /// source image will be cropped. /// If one of the dimension is 0, the other one will be deduced from the /// source aspect ratio. If both are 0, the source dimensions will be used. /// /// This function is thread-safe /// virtual bool requestThumbnail( ThumbnailSizeType sizeType, uint32_t desiredWidth, uint32_t desiredHeight, float position ) = 0; /// /// \brief removeThumbnail Clear this media's thumbnail /// \param sizeType The thumbnail size type /// \return true if the thumbnail was successfully cleared, false otherwise /// /// If this is successful, later calls to thumbnailStatus will return Missing /// virtual bool removeThumbnail( ThumbnailSizeType sizeType ) = 0; virtual unsigned int insertionDate() const = 0; virtual time_t releaseDate() const = 0; /// Metadata /// /// \brief metadata Fetch (or return a cached) metadata value for this media /// \param type The metadata type /// \return A reference to a wrapper object representing the metadata. /// virtual const IMetadata& metadata( MetadataType type ) const = 0; /// /// \brief metadata Returns all the meta set for this device. /// virtual std::unordered_map metadata() const = 0; /// /// \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; virtual bool unsetMetadata( MetadataType type ) = 0; /// /// \brief setMetadata Sets multiple metadata at once /// \param meta An unordered_map, indexed by the provided meta, containing a string as value /// \return true if all meta were successfully set, false otherwise. /// /// If this function returns false, no meta will have been updated. /// virtual bool setMetadata( const std::unordered_map& meta ) = 0; /// /// \brief removeFromHistory Removes a media from the history. /// \return true in case of success, false otherwise /// /// This can be used for all type of media, including streams & network. /// If this call succeeds, the media will have a play count of 0, and /// won't appear in the history anymore. Any potential progress will /// also be lost. /// After calling this method, the observable state is as if the media /// was never played. /// /// This will return false in case of a database failure /// virtual bool removeFromHistory() = 0; /// /// \brief bookmarks Returns a query representing this media bookmarks /// \param params Some query parameters, or nullptr for the default /// /// The sorting criteria supported for this requests are Alpha & Default /// (default being by ascending time) /// Any other criteria will fallback to default. /// virtual Query bookmarks( const QueryParameters* params = nullptr ) const = 0; /// /// \brief bookmark Returns the bookmark at the provided time /// \return A bookmark if found, nullptr otherwise /// virtual BookmarkPtr bookmark( int64_t time ) const = 0; /// /// /// \brief addBookmark Add a bookmark to this media /// \param time The bookmark time /// \return A pointer to the new bookmark in case of successful /// addition, or nullptr otherwise /// virtual BookmarkPtr addBookmark( int64_t time ) = 0; /// /// \brief removeBookmark Removes a bookmark by its time /// \param time The time at which the bookmark must be removed. /// \return false in case of a database error /// virtual bool removeBookmark( int64_t time ) = 0; /// /// \brief removeAllBookmarks Remove all bookmarks attached to this media /// virtual bool removeAllBookmarks() = 0; /// /// \brief isDiscoveredMedia Returns true if this media was discovered /// during a scan. /// 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 explicitly added /// by the application. /// This is the opposite counterpart of isDiscoveredMedia /// virtual bool isExternalMedia() const = 0; /// /// \brief isStream Returns true if this media is an external media, and /// of type stream. /// virtual bool isStream() const = 0; /// /// \brief addToGroup Adds this media to the given group /// \param group The target media group /// \return true if the media was successfully added, false otherwise /// virtual bool addToGroup( IMediaGroup& group ) = 0; /// /// \brief addToGroup Adds this media to the given group /// \param groupId The target group ID /// \return true if the media was successfully added, false otherwise /// virtual bool addToGroup( int64_t groupId ) = 0; /// /// \brief removeFromGroup Remove this media from its group /// \return true if the media was successfully removed, false otherwise. /// virtual bool removeFromGroup() = 0; /// /// \brief groupId Returns this media's group ID /// virtual int64_t groupId() const = 0; /// /// \brief group Return this media's group /// virtual MediaGroupPtr group() const = 0; /// /// \brief regroup Attempts to group this media with other ungrouped media /// \return true in case of success, false otherwise /// /// This will attempt to find other ungrouped media which start with the /// same prefix (currently, 6 characters) as the current media. /// This can only be used on ungroupped media, as we don't want to tinkle /// with groups that may have been organized manually by the user. /// virtual bool regroup() = 0; /// /// \brief isPresent Returns true if the media is present /// /// 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 relevant when the media is not external /// virtual bool isPresent() const = 0; virtual ArtistPtr artist() const = 0; virtual int64_t artistId() const = 0; virtual GenrePtr genre() const = 0; virtual int64_t genreId() const = 0; virtual unsigned int trackNumber() const = 0; virtual AlbumPtr album() const = 0; virtual int64_t albumId() const = 0; /** * @return Which disc this tracks appears on (or 0 if unspecified) */ virtual uint32_t discNumber() const = 0; /** * @brief lyrics returns the lyrics associated with this media, if any */ virtual const std::string& lyrics() const = 0; virtual bool isPublic() const = 0; virtual uint32_t nbSubscriptions() const = 0; /// \brief linkedSubscriptions Returns a query representing subscriptions /// containing this media /// \param params Some query parameters, or nullptr for the default /// /// The only query parameter supported for this query for now is to choose /// a descending order of the name sorting. virtual Query linkedSubscriptions( const QueryParameters* ) const = 0; /** * @brief description Returns this media description, if available. * @return A description or a reference to an empty string when none is available. */ virtual const std::string& description() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IMediaGroup.h000066400000000000000000000164021501065546500302410ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "medialibrary/IQuery.h" #include "medialibrary/IMedia.h" namespace medialibrary { struct QueryParameters; class IMediaGroup { public: virtual ~IMediaGroup() = default; /** * @brief id Returns this group id */ virtual int64_t id() const = 0; /** * @brief name Returns this group name */ virtual const std::string& name() const = 0; /** * @brief nbTotalMedia Returns the number of media in this group, not accounting * for their presence. * * Even if all this group's media are missing, this will still return a non * 0 count. */ virtual uint32_t nbTotalMedia() const = 0; /** * @brief nbMedia Returns the number of present media in this group */ virtual uint32_t nbPresentMedia() const = 0; /** * @brief nbVideo returns the number of present video media in this group */ virtual uint32_t nbPresentVideo() const = 0; /** * @brief nbAudio Returns the number of present audio media in this group */ virtual uint32_t nbPresentAudio() const = 0; /** * @brief nbUnknown Returns the number of present media of unknown type in this group */ virtual uint32_t nbPresentUnknown() const = 0; /** * @brief nbPresentSeen Returns the number of present seen media in this group */ virtual uint32_t nbPresentSeen() const = 0; /** * @brief nbVideo Returns the number of video (present or not ) media in this group */ virtual uint32_t nbVideo() const = 0; /** * @brief nbAudio Returns the number of audio (present or not ) media in this group */ virtual uint32_t nbAudio() const = 0; /** * @brief nbUnknown Returns the number of media of unknown type (present or not) * in this group */ virtual uint32_t nbUnknown() const = 0; /** * @brief nbSeen Returns the number of seen media (present or not) in this group */ virtual uint32_t nbSeen() const = 0; /** * @brief duration Returns this group duration * * Which is equal to the sum of all its member's durations */ virtual int64_t duration() const = 0; /** * @brief creationDate Returns the group creation date * * The date is expressed as per time(2), ie. a number of seconds since * Epoch (UTC) */ virtual time_t creationDate() const = 0; /** * @brief lastModificationDate Returns the group last modification date * * Modification date include last media addition/removal, and renaming * The date is expressed as per time(2), ie. a number of seconds since * Epoch (UTC) */ virtual time_t lastModificationDate() const = 0; /** * @brief userInteracted Returns true if the group has had user interactions * * 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 * as a user interaction. */ virtual bool userInteracted() const = 0; /** * @brief add Adds a media to this group. * @param media A reference to the media to add * @return true if the media was successfully added to the group, false otherwise * * The media will be automatically removed its previous group if it belonged * to one */ virtual bool add( IMedia& media ) = 0; /** * @brief add Adds a media to this group * @param mediaId The media to add's ID * @return true if the media was successfully added to the group, false otherwise * * The media will be automatically removed its previous group if it belonged * to one */ virtual bool add( int64_t mediaId ) = 0; /** * @brief add Removes a media from this group. * @param media A reference to the media to remove * @return true if the media was successfully removed from the group, false otherwise */ virtual bool remove( IMedia& media ) = 0; /** * @brief add Removes a media from this group * @param mediaId The media to remove's ID * @return true if the media was successfully removed from the group, false otherwise */ virtual bool remove( int64_t mediaId ) = 0; /** * @brief media List the media that belong to this group * @param mediaType The type of media to return, or Unknown to return them all * @param params Some query parameters * @return A query object representing the results * * \see{IMediaLibrary::audioFile} for the supported sorting criteria */ virtual Query media( IMedia::Type mediaType, const QueryParameters* params = nullptr ) = 0; /** * @brief searchMedia Search amongst the media belonging to this group * @param pattern The search pattern (3 characters minimum) * @param mediaType The type of media to return, or Unknown to return them all * @param params Some query parameters * @return A query object representing the results * * \see{IMediaLibrary::audioFile} for the supported sorting criteria */ virtual Query searchMedia( const std::string& pattern, IMedia::Type mediaType, const QueryParameters* params = nullptr ) = 0; /** * @brief rename Rename a group * @param name The new name * @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 * group when they are added to the media library. */ virtual bool rename( std::string name ) = 0; /** * @brief destroy Destroys a media group. * @return true in case of success, false otherwise * * This will ungroup all media that are part of this group. */ virtual bool destroy() = 0; virtual bool isFavorite() const = 0; virtual bool setFavorite( bool favorite ) = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IMediaLibrary.h000066400000000000000000001557541501065546500305670ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include #include #include #include #include "medialibrary/ILogger.h" #include "Types.h" #include "IQuery.h" #include "IMedia.h" #include "IService.h" struct libvlc_instance_t; namespace medialibrary { class PriorityAccessImpl; namespace fs { class IFileSystemFactory; } namespace parser { class IParserService; } class PriorityAccess { public: PriorityAccess( std::unique_ptr p ); PriorityAccess( PriorityAccess&& ); ~PriorityAccess(); private: std::unique_ptr p; }; static constexpr auto UnknownArtistID = 1u; static constexpr auto VariousArtistID = 2u; static constexpr auto UnknownShowID = 1u; struct SearchAggregate { Query albums; Query artists; Query genres; Query media; Query shows; Query playlists; }; enum class SortingCriteria { /* * Default depends on the entity type: * - By track number (and disc number) for album tracks * - Alphabetical order for others */ Default, Alpha, Duration, InsertionDate, LastModificationDate, ReleaseDate, FileSize, Artist, PlayCount, Album, Filename, // Sort by number of tracks in the containing entity (album, genre, artist, ...) TrackNumber, // Sort by track ID (Track #1, track #2, ...) TrackId, // Valid for folders only. Default order is descending NbVideo, NbAudio, // Valid for folders & media groups NbMedia, // Only valid for artists for now NbAlbum, LastPlaybackDate, }; struct QueryParameters { /* This query sorting parameter. Its actual meaning is query dependent */ SortingCriteria sort = SortingCriteria::Default; /* Descending order */ bool desc = false; /* If true, media that are stored on missing devices will still be returned */ bool includeMissing = false; /* * If true, only public entities will be returned. * When fetching public entities only, some features related to counters * will be disabled, for instance, it will not be possible to sort an artist * by its number of tracks since the triggers don't maintain a count for * public entities only. */ bool publicOnly = false; /* If true, only favorite entities will be returned */ bool favoriteOnly = false; }; enum class InitializeResult { ///< Everything worked out fine Success, ///< Should be considered the same as Success, but is an indication of /// unrequired subsequent calls to initialize. AlreadyInitialized, ///< A fatal error occurred, the IMediaLibrary instance should be destroyed Failed, ///< The database was reset, the caller needs to re-configure folders to /// discover at the bare minimum. DbReset, ///< Something is wrong with the database. It is up to the application to ///< chose what to do, the DB needs to be recovered or dropped in any case. DbCorrupted, }; enum class ThumbnailSizeType : uint8_t { /// A small sized thumbnail. Considered to be the default value before model 17 Thumbnail, /// A banner type thumbnail. The exact size is application dependent. Banner, /// The number of different size type Count, }; enum class ThumbnailStatus : uint8_t { /// No thumbnail for this entity Missing, /// This thumbnail was successfully generated or was provided by the user /// and is available to use Available, /// The thumbnail generation failed, without specific reason, usually /// because of a timeout. /// It is fine to ask for a new generation in this case Failure, /// The thumbnail generation failed at least 3 times. A new generation might /// be required, but is likely to fail again. PersistentFailure, /// The thumbnail generation failed because of a crash. Asking for a new /// generation is not recommended, unless you know the underlying issue was /// fixed. Crash, }; 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 Local, /// The network streams history Network, }; enum class ArtistIncluded : uint8_t { /// Include all artists, as long as they have at least one track present All, /// Do not return the artist that are only doing featurings on some albums. /// In other word, this would only return artists which have at least 1 album AlbumArtistOnly, }; enum class PlaylistType : uint8_t { /// Include all kind of playlist, regarding of the media types All, /// Include playlists containing at least one audio track Audio, /// Include playlists containing at least one video or one unknown track Video, /// Include playlists containing audio tracks only AudioOnly, /// Include playlists containing video tracks only VideoOnly, }; struct SetupConfig { /** * @brief parserServices A vector of external parser services provided by the application * @note This currently only supports MetadataExtraction services. */ std::vector> parserServices; /** * @brief deviceListers Provides some external device listers. * * The key is the scheme (including the '://') and the value is a IDeviceLister * implementation. * This is meant for OSes with complicated/impossible to achieve device listing (due to * missing APIs, permissions problems...), or for non-local devices, such as * network shares. */ std::unordered_map deviceListers; /** * @brief fsFactories Provides an external filesystem factory implementation */ std::vector> fsFactories; /** * @brief logLevel The default log level to initialize the medialibrary with. * This can be overwritten at a later point using IMediaLibrary::setVerbosity */ LogLevel logLevel = LogLevel::Error; /** * @brief logger An ILogger instance if the application wishes to use a * custom one. * If nullptr is provided, the default IOstream logger will be used. */ std::shared_ptr logger; std::shared_ptr cacher; }; class IMediaLibraryCb { public: virtual ~IMediaLibraryCb() = default; /** * @brief onMediaAdded Will be called when some media get added. * Depending if the media is being restored or was just discovered, * the media type might be a best effort guess. If the media was freshly * discovered, it is extremely likely that no metadata will be * available yet. * The number of media is undefined, but is guaranteed to be at least 1. */ virtual void onMediaAdded( std::vector media ) = 0; /** * @brief onMediaUpdated Will be called when a file metadata gets updated. */ virtual void onMediaModified( std::set mediaIds ) = 0; virtual void onMediaDeleted( std::set mediaIds ) = 0; virtual void onArtistsAdded( std::vector artists ) = 0; virtual void onArtistsModified( std::set artistsIds ) = 0; virtual void onArtistsDeleted( std::set artistsIds ) = 0; virtual void onAlbumsAdded( std::vector albums ) = 0; virtual void onAlbumsModified( std::set albumsIds ) = 0; virtual void onAlbumsDeleted( std::set albumsIds ) = 0; virtual void onPlaylistsAdded( std::vector playlists ) = 0; virtual void onPlaylistsModified( std::set playlistsIds ) = 0; virtual void onPlaylistsDeleted( std::set playlistIds ) = 0; virtual void onGenresAdded( std::vector genres ) = 0; virtual void onGenresModified( std::set genresIds ) = 0; virtual void onGenresDeleted( std::set genreIds ) = 0; virtual void onMediaGroupsAdded( std::vector mediaGroups ) = 0; virtual void onMediaGroupsModified( std::set mediaGroupsIds ) = 0; virtual void onMediaGroupsDeleted( std::set mediaGroupsIds ) = 0; virtual void onBookmarksAdded( std::vector bookmarks ) = 0; virtual void onBookmarksModified( std::set bookmarkIds ) = 0; virtual void onBookmarksDeleted( std::set bookmarkIds ) = 0; virtual void onFoldersAdded( std::vector folders ) = 0; virtual void onFoldersModified( std::set folderIds ) = 0; virtual void onFoldersDeleted( std::set folderIds ) = 0; virtual void onSubscriptionsAdded( std::vector subscriptions ) = 0; virtual void onSubscriptionsModified( std::set subscriptionIds ) = 0; virtual void onSubscriptionsDeleted( std::set subscriptionsIds ) = 0; /** * @brief onDiscoveryStarted This callback will be invoked when the discoverer * 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 roots need to be discovered. */ virtual void onDiscoveryStarted() = 0; /** * @brief onDiscoveryProgress This callback will be invoked each time the * discoverer enters a new folder. * @param currentFolder The folder being discovered * * 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 */ virtual void onDiscoveryProgress( const std::string& currentFolder ) = 0; /** * @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 of onDiscoveryStarted */ virtual void onDiscoveryCompleted() = 0; /** * @brief onDiscoveryFailed Will be invoked when a discovery operation fails * @param root The root folder for which the discovery failed. */ virtual void onDiscoveryFailed( const std::string& root ) = 0; /** * @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 root folder is * processed, after it has been inserted to the database. * 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 a root folder is known before starting its processing */ virtual void onRootAdded( const std::string& root, bool success ) = 0; /** * @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 onRootRemoved( const std::string& root, bool success ) = 0; /** * @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 onRootBanned( const std::string& root, bool success ) = 0; /** * @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 onRootUnbanned( const std::string& root, bool success ) = 0; /** * @brief onParsingStatsUpdated Called when the parser statistics are updated * * 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 * */ virtual void onParsingStatsUpdated( uint32_t opsDone, uint32_t opsScheduled ) = 0; /** * @brief onBackgroundTasksIdleChanged Called when background tasks idle state change * * When all parser tasks are idle, it is guaranteed that no entity modification * callbacks will be invoked. * @param isIdle true when all background tasks are idle, false otherwise */ virtual void onBackgroundTasksIdleChanged( bool isIdle ) = 0; /** * @brief onMediaThumbnailReady Called when a thumbnail generation completed. * @param media The media for which a thumbnail was generated * @param sizeType The size type that was requerested * @param success true if the thumbnail was generated, false if the generation failed */ virtual void onMediaThumbnailReady( MediaPtr media, ThumbnailSizeType sizeType, bool success ) = 0; /** * @brief onHistoryChanged Called when a media history gets modified (including when cleared) * @param type The history type */ virtual void onHistoryChanged( HistoryType type ) = 0; /** * @brief onUnhandledException will be invoked in case of an unhandled exception * * @param context A minimal context hint * @param errMsg The exception string, as returned by std::exception::what() * @param clearSuggested A boolean to inform the application that a database * clearing is suggested. * * If the application chooses to handle the error to present it to the user * or report it somehow, it should return true. * If the implementation returns false, then the exception will be rethrown * If clearSuggested is true, the application is advised to call * 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 therefore the medialibrary considered unusable. * * If clearSuggested is false, there are no certain way of knowing if the * database is still usable or not. */ virtual bool onUnhandledException( const char* /* context */, const char* /* errMsg */, bool /* clearSuggested */ ) { return false; } /** * @brief onRescanStarted will be invoked when a rescan is started. * * This won't be emitted when the media library issues a rescan itself, due * to a migration. */ virtual void onRescanStarted() = 0; /** * @brief onSubscriptionNewMedia will be invoked when some media are added to * one or more subscription(s) * @param subscriptionId The subscription ID(s) */ virtual void onSubscriptionNewMedia( std::set subscriptionId ) = 0; /** * @brief onSubscriptionCacheUpdated Invoked after at least a media changed cached status * for a subscription. * @param subscriptionId The subscription for which the cache was updated * * If the subscription by the cache worker didn't change, this will not * be invoked. */ virtual void onSubscriptionCacheUpdated( int64_t subscriptionId ) = 0; /** * @brief onCacheIdleChanged Will be invoked when the background cache worker * changes its idle state * @param idle true if the worker went back to idle, false if it resumed, meaning * some media and/or subscriptions are being cached. */ virtual void onCacheIdleChanged( bool idle ) = 0; }; class IMediaLibrary { public: virtual ~IMediaLibrary() = default; /** * \brief initialize Initializes the media library. * * \param mlCallback A pointer to an IMediaLibraryCb that will be invoked * with various events during the medialibrary lifetime. * \return An \see{InitializeResult} code. * * If initialize returns Failed, this medialibrary must not be used * anymore, and should be disposed off. * If it returns Ok the first time, calling this method again is a no-op and * AlreadyInitialized will be returned * In case DbReset is returned, it is up to application to decide what * to do to repopulate the database. * * This method is thread safe. If multiple initialization start simultaneously * only the first one will return Success, the later ones will return * AlreadyInitialized */ virtual InitializeResult initialize( IMediaLibraryCb* mlCallback ) = 0; /** * @brief setVerbosity Sets the log level * @param v The required log level * * This defaults to Error. */ virtual void setVerbosity( LogLevel v ) = 0; /** * @brief createLabel Create a label that can be assigned to various entities * @param label The label name * @return A label instance * * Creating 2 labels with the same name is considered an error and will * throw a ConstraintUnique exception */ virtual LabelPtr createLabel( const std::string& label ) = 0; /** * @brief deleteLabel Delete a label from the database * @param label An instance of the label to be deleted * @return true if the label was deleted, false otherwise */ virtual bool deleteLabel( LabelPtr label ) = 0; /** * @brief media Fetch a media by its ID * @param mediaId The ID of the media to fetch * @return A media instance, or nullptr in case of error or if no media matched */ virtual MediaPtr media( int64_t mediaId ) const = 0; /** * @brief media Attempts to fetch a media by its MRL * @param mrl The MRL of the media to fetch * @return A media instance or nullptr in case of error or if no media matched * * This will attempt to fetch an external media with the given MRL first, and * will then attempt to fetch an analyzed media. * Even if the media is removable, the MRL must represent the absolute path * to the media */ virtual MediaPtr media( const std::string& mrl ) const = 0; /** * @brief addExternalMedia Adds an external media to the list of known media * @param mrl This media MRL * @param duration A duration for this media (values <= 0 are ignored) * * Once created, this media can be used just like any other media, except * it won't have a subType, and won't be analyzed to extract tracks and * won't be inserted to any collection (ie. album/show/...) * If the mrl is already known to the medialibrary, this function will * return nullptr. * * The media can be fetched using media( std::string ) afterward. */ virtual MediaPtr addExternalMedia( const std::string& mrl, int64_t duration ) = 0; /** * @brief addStream Create an external media of type IMedia::Type::Stream * * This is equivalent to addExternalMedia, except for the resulting * new media's type */ virtual MediaPtr addStream( const std::string& mrl ) = 0; /** * @brief removeExternalMedia Remove an external media or a stream * @return false if the media was not external/stream, or in case of DB failure */ virtual bool removeExternalMedia( MediaPtr media ) = 0; /** * @brief mediaFiles Returns the media unclassified * @param params Some query parameters. * @return A query representing the results set * * All media accessors throughout the media library supports the same sorting * criteria, which are: * - Duration * - InsertionDate * - ReleaseDate * - PlayCount * - Filename * - LastModificationDate * - FileSize * Default sorting parameter uses the media's title, in ascending order */ virtual Query 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 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::mediaFiles} for the supported sorting criteria */ virtual Query videoFiles( const QueryParameters* params = nullptr ) const = 0; /** * @brief movies Returns the media classified as Movie * @param params Some query parameters. * @return A query representing the results set * * \see{IMediaLibrary::audioFiles} for the supported sorting criteria */ virtual Query movies( const QueryParameters* params = nullptr ) const = 0; /** * @brief subscriptionMedia Returns the media discovered in subscriptions * @param params Some query parameters. * @return A query representing the results set * * \see{IMediaLibrary::audioFiles} for the supported sorting criteria */ virtual Query subscriptionMedia( const QueryParameters* params = nullptr ) const = 0; /** * @brief inProgressMedia Returns media for which playback wasn't completed * @param type The type of media to fetch, or 'Unknown' for all * @param params Some query parameters * @return A query representing the results set * * @see{IMedia::setProgress} */ virtual Query inProgressMedia( IMedia::Type type, const QueryParameters* params = nullptr ) const = 0; /** * @brief createMediaGroup Creates a media group * @param name The group name * @return The new group instance, or nullptr in case of error */ virtual MediaGroupPtr createMediaGroup( std::string name ) = 0; /** * @brief createMediaGroup Creates a media group with the provided media * @param mediaIds A list of media to be included in the group * @return The new group instance, or nullptr in case of error * * If the provided media are already part of a group, they will be moved to * the newly created one. * The group will have no name and will return an empty string. */ virtual MediaGroupPtr createMediaGroup( const std::vector& mediaIds ) = 0; /** * @brief deleteMediaGroup Deletes a media group * @param id The group ID * @return true in case of success, false otherwise * * This will ungroup all media that were part of the group. */ virtual bool deleteMediaGroup( int64_t id ) = 0; /** * @brief mediaGroup Returns a media group with the given id * @return A media group, or nullptr if the group doesn't exist, or in case * of sporadic failure. */ virtual MediaGroupPtr mediaGroup( int64_t id ) const = 0; /** * @brief mediaGroups Returns a query representing the root media groups * @param mediaType The type of media contained in this group, or Unknown if all * types should be returned * @param params A query parameter * * The supported sorting criteria are: * - Alpha (default) * - NbVideo * - NbAudio * - NbMedia */ virtual Query mediaGroups( IMedia::Type mediaType, const QueryParameters* params = nullptr ) const = 0; virtual Query searchMediaGroups( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /** * @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. * Media that belong to a group of only 1 element will not be affected by this. * Usual regrouping rules apply, meaning that a minimum of 6 characters match * is required for 2 media to be grouped together, and if applicable, the longest * match will be used to name the created group * In case of error, false will be returned, but some media might have been * regrouped already. * * @warning This might be a relatively long operation as it must fetch the * first media being part of a singleton group and regroup it with * its matching media, in a loop, until all media are regrouped */ virtual bool regroupAll() = 0; virtual AlbumPtr album( int64_t id ) const = 0; virtual Query albums( const QueryParameters* params = nullptr ) const = 0; 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 shows( const QueryParameters* params = nullptr ) const = 0; virtual Query searchShows( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /** * @brief artists List all artists that have at least an album. * Artists that only appear on albums as guests won't be listed from here, but will be * returned when querying an album for all its appearing artists * @param included If true, all artists including those without album * will be returned. If false, only artists which have * an album will be returned. * @param params Some query parameters * * This function only handles lexical sort */ virtual Query artists( ArtistIncluded included, const QueryParameters* params = nullptr ) const = 0; /** * @brief genres Return the list of music genres * @param params Some query parameters */ virtual Query genres( const QueryParameters* params = nullptr ) const = 0; virtual GenrePtr genre( int64_t id ) const = 0; /*** * Playlists */ virtual PlaylistPtr createPlaylist( std::string name ) = 0; /** * @brief playlists List all playlists known to the media library * @param type The type of playlist to return * @param params Some query parameters * @return A Query object * * The provided playlist type allows the application to fetch the playlist containing * only Video/Audio media. Depending on QueryParameters::includeMissing * missing media will or will not be included. * This means that at a given time, a playlist might be considered an audio * playlist if all the video it contains are on a remove device. When the * device comes back, the playlist will turn back to a non-audio playlist. * * If a playlist contains a media of unknown type, it is assumed to be a video. * * If a playlist is empty, it will only be returned for PlaylistType::All */ virtual Query playlists( PlaylistType type, const QueryParameters* params = nullptr ) = 0; virtual PlaylistPtr playlist( int64_t id ) const = 0; virtual bool deletePlaylist( int64_t playlistId ) = 0; /** * History */ /** * @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 history( HistoryType, const QueryParameters* params = nullptr ) const = 0; /** * @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 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 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(HistoryType) = 0; /** * Search */ /** * @brief searchMedia, searchAudio, searchMovie, and searchVideo search for some media, based on a pattern. * @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. * * Only media that were discovered by the medialibrary will be included. * 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 */ virtual Query searchMedia( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual Query searchAudio( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual Query searchVideo( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual Query searchMovie( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /** * @brief searchSubscriptionMedia search a subscription media. * @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. */ virtual Query 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 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 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 searchInVideoHistory( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual Query searchPlaylists( const std::string& name, PlaylistType type, const QueryParameters* params = nullptr ) const = 0; virtual Query searchAlbums( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual Query searchGenre( const std::string& genre, const QueryParameters* params = nullptr ) const = 0; virtual Query searchArtists( const std::string& name, ArtistIncluded included, const QueryParameters* params = nullptr ) const = 0; virtual SearchAggregate search( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /** * @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 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 root The MRL of the root folder to discover. */ virtual void discover( const std::string& root ) = 0; /** * @brief setDiscoverNetworkEnabled Enable discovery of network shares * @return true In case of success, false otherwise. * * This can be called at any time. * If enabling before the discoverer thread gets started, the intent will * be stored, but the device listers nor the file system factories won't be * started. * When disabling network discovery, all content that was discovered on * the network will be marked as non-present, meaning they won't be * returned until network discovery gets enabled again. * As far as the user is concerned, this is equivalent to (un)plugging * a USB drive, in the sense that the medialibrary will still store * information about network content and won't have to discover/parse it * again. * When enabling, this method will consider the lack of network enabled * IFsFactory implementation a failure. * Setting the same state twice is considered as a success, regardless of * the previous call status. */ virtual bool setDiscoverNetworkEnabled( bool enabled ) = 0; virtual bool isDiscoverNetworkEnabled() const = 0; /** * @brief roots List the main folders that are managed by the medialibrary * @param params A pointer to a QueryParameters structure or nullptr for the default * * This is essentially a way of knowing what has been passed to discover() * throughout the database life. * 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 roots( const QueryParameters* params ) const = 0; /** * @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 */ virtual bool isIndexed( const std::string& mrl ) const = 0; /** * @brief isBanned Returns true if the folder represented by the MRL is banned * @param mrl The folder MRL to probe * @return true is banned, false otherwise (including if the MRL is not matching * any known folder) */ virtual bool isBanned( const std::string& mrl ) const = 0; /** * @brief folders Returns a flattened list of all folders containing at * least a media of a given type * @param type A required type of media, or IMedia::Type::Unknown if any * media type is fine. * @param params A query parameters object * @return A query object to be used to fetch the results * * This is flattened, ie. * ├── a * │ └── w * │ └── x * │ └── y * │ └── z * │ └── DogMeme.avi * ├── c * │ └── NakedMoleRat.asf * * 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 * roots() & IFolder::subFolders() functions should be used. */ virtual Query folders( IMedia::Type type, const QueryParameters* params = nullptr ) const = 0; virtual Query searchFolders( const std::string& pattern, IMedia::Type type, const QueryParameters* params = nullptr ) const = 0; virtual FolderPtr folder( int64_t folderId ) const = 0; virtual FolderPtr folder( const std::string& mrl ) const = 0; /** * @brief removeRoot Removes a root folder * @param root The MRL of the root folder point to remove * * 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. * This can be reverted by calling unbanFolder * @note This method is asynchronous, but will interrupt any ongoing * discovery, process the request, and resume the previously running * task * @note This must be called after initialize() */ virtual void removeRoot( const std::string& root ) = 0; /** * @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 * @note This method is asynchronous, but will interrupt any ongoing * discovery, process the request, and resume the previously running * task * @note This must be called after initialize() */ virtual void banFolder( const std::string& mrl ) = 0; /** * @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 * task * @note This must be called after initialize() */ virtual void unbanFolder( const std::string& mrl ) = 0; /** * @brief bannedRoots Returns a query representing the banned root folders. * * The result set will include root folders on missing devices as well. * Folder hierarchy isn't preserved, and the results are flattened. */ virtual Query bannedRoots() const = 0; /** * @brief pauseBackgroundOperations Will stop potentially CPU intensive background * operations, until resumeBackgroundOperations() is called. * If an operation is currently running, it will finish before pausing. */ virtual void pauseBackgroundOperations() = 0; /** * @brief resumeBackgroundOperations Resumes background tasks, previously * interrupted by pauseBackgroundOperations(). */ virtual void resumeBackgroundOperations() = 0; /** * @brief reload Reload all known roots * @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() = 0; /** * @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& 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 */ virtual bool forceParserRetry() = 0; /** * @brief deviceLister Get a device lister for the provided scheme * @param scheme The scheme for which a device lister is required * @return A device lister instance, if any. * * This will return the device lister provided to the constructor through * SetupConfig, or a media library provided one. * If no device lister is available for this scheme, nullptr will be returned */ virtual DeviceListerPtr deviceLister( const std::string& scheme ) const = 0; /** * @brief forceRescan Deletes all entities except Media and Playlist, and * forces all media to be rescanned. * * This can be called anytime after the medialibrary has been initialized. * It will make all held instances outdated. Those should be considered * as invalid the moment this method returns. * * This will return false in case of a database error. If this happens, * nothing will be updated. */ virtual bool forceRescan() = 0; /** * @brief enableFailedThumbnailRegeneration Allow failed thumbnail attempt to be retried * * This will not attempt to regenerate the thumbnail immediately, requestThumbnail * still has to be called afterward. */ virtual void enableFailedThumbnailRegeneration() = 0; virtual void addThumbnailer( std::shared_ptr thumbnailer ) = 0; /** * @brief clearDatabase Will drop & recreate the database * @param restorePlaylists If true, the media library will attempt to keep * the user created playlists */ virtual bool clearDatabase( bool restorePlaylists ) = 0; /** * @brief supportedMediaExtensions Returns the supported media extensions * * The list is guaranteed to be ordered alphabetically */ virtual const std::vector& supportedMediaExtensions() const = 0; /** * @brief isMediaExtensionSupported Checks if the provided media extension * is supported. */ virtual bool isMediaExtensionSupported( const char* ext ) const = 0; /** * @brief supportedPlaylistExtensions Returns the supported playlist extensions * * The list is guaranteed to be ordered alphabetically */ virtual const std::vector& supportedPlaylistExtensions() const = 0; /** * @brief isPlaylistExtensionSupported Checks if the provided playlist extension * is supported. */ virtual bool isPlaylistExtensionSupported( const char* ext ) const = 0; /** * @brief isDeviceKnown Probes a device to know if it has been seen by the medialibrary * @param uuid The device UUID * @param mountpoint The device mountpoint, as an MRL * @param isRemovable The device's removable state * @return true if the device has been seen by the media library * * If this function returns false, a representation for this device will be * inserted in database, and any later call will return true. * This must be called *after* calling initialize() */ virtual bool isDeviceKnown( const std::string& uuid, const std::string& mountpoint, bool isRemovable ) = 0; /** * @brief deleteRemovableDevices Deletes all removable devices * @return true if devices were deleted, false otherwise * * 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 invalid entries * to be inserted in the database */ virtual bool deleteRemovableDevices() = 0; /** * @brief supportedSubtitlesExtensions Returns the supported subtitle extensions * * The list is guaranteed to be ordered alphabetically */ virtual const std::vector& supportedSubtitleExtensions() const = 0; /** * @brief isSubtitleExtensionSupported Checks if the provided subtitle extension * is supported. */ virtual bool isSubtitleExtensionSupported( const char* ext ) const = 0; /** * @brief requestThumbnail Request an asynchronous thumbnail generation * @param mediaId The media for which to generate a thumbnail * @param sizeType A value representing the type of thumbnail size * @param desiredWidth The desired width * @param desiredHeight The desired height * @param position A position in the range [0;1] * @return true if the request has been scheduled * * When the thumbnail is generated, IMediaLibraryCb::onMediaThumbnailReady * will be invoked from the thumbnailer thread. * If this is invoked multiple time before the original request is processed, * the later requests will be ignored, and no callback will be invoked before * the first one has completed. * The desired width or height might be 0 to automatically infer one from the * other by respecting the source aspect ratio. * If both sizes are provided, the resulting thumbnail will be cropped to * abide by the source aspect ratio. */ virtual bool requestThumbnail( int64_t mediaId, ThumbnailSizeType sizeType, uint32_t desiredWidth, uint32_t desiredHeight, float position ) = 0; /** * @brief bookmark Returns the bookmark with the given ID * @param bookmarkId The bookmark ID * @return A bookmark instance, or nullptr if no bookmark has this ID */ virtual BookmarkPtr bookmark( int64_t bookmarkId ) const = 0; /** * @brief setExternalLibvlcInstance Provides an existing libvlc instance * @param inst A libvlc instance which can be released as soon as this function returns * @return true if the instance was correctly set * * If the media library is build with libvlc support, this can't fail. * This must be called before any discovery or parsing is started, so ideally * the user should call this right after creating an IMediaLibrary instance * through NewMediaLibrary */ virtual bool setExternalLibvlcInstance( libvlc_instance_t* inst ) = 0; /** * @brief acquirePriorityAccess Acquires a priority context for the calling thread * @return A PriorityAccess wrapper object. * * The returned object will release its priority context when it gets destroyed. */ virtual PriorityAccess acquirePriorityAccess() = 0; /** * @brief flushUserProvidedThumbnails Removes all user provided thumbnail from the database * @return true in case of success, false otherwise. * * The media library will *not* attempt to remove the files, as it doesn't * own them. * This will only make sure that any media that the application provided a * thumbnail for will not have a thumbnail associated with it anymore. */ virtual bool flushUserProvidedThumbnails() = 0; virtual bool removeSubscription( int64_t subscriptionId ) = 0; /** * @brief cacheNewSubscriptionMedia Asynchronously starts a caching of new subscription media */ virtual void cacheNewSubscriptionMedia() = 0; /** * @brief fitsInSubscriptionCache Checks if the provided media will fit in * the subscription cache * @param m The media to probe * @return true if the media fits *or if its size is unknown* false otherwise * * This will use the associated files to figure out the size on disk. If the * size is unknown, true will be returned, and the size will be updated in * database when caching is attempted. * The media will fit in cache if: * - The global maximum cache size allows for it to fit * - Its associated subscription maximum cache size allows it to fit as well * The maximum number of media, be it global or for the associated * subscription, it *not* taken into account for this. */ virtual bool fitsInSubscriptionCache( const IMedia& m ) const = 0; /** * @brief service Returns an object representing a service * @param type The service type * @return A service instance, or nullptr if the service isn't available */ virtual ServicePtr service( IService::Type type ) const = 0; /** * @brief subscription Returns an object representing a subscription * @param id The subscription's id * @return A Subscription instance, or a nullptr if the id matches no * subscription * Subscriptions usually should be instantiated through their service, but * this becomes useful in a JNI context where we can only instantiate * through an ID. */ virtual SubscriptionPtr subscription( uint64_t id ) const = 0; /** * @brief setSubscriptionMaxCachedMedia Sets the maximum number of cached media * for each subscription * @param nbCachedMedia The number of media to automatically cache * @return true if the change was successful, false otherwise * * This setting will be used when a subscription is set to inherit the global * setting, but each subscription can individually override this setting. */ virtual bool setSubscriptionMaxCachedMedia( uint32_t nbCachedMedia ) = 0; /** * @brief setSubscriptionMaxCacheSize Sets the maximum cache size for each subscriptions * @param maxCacheSize The size of each subscription cache in bytes * @return true if the change was successful, false otherwise * * This setting will be used when a subscription is set to inherit the global * setting, but each subscription can individually override this setting. */ virtual bool setSubscriptionMaxCacheSize( uint64_t maxCacheSize ) = 0; /** * @brief setMaxCacheSize Sets the maximum overall cache size * @param nbCacheMedia The size the global cache in bytes * @return true if the change was successful, false otherwise */ virtual bool setMaxCacheSize( uint64_t maxCacheSize ) = 0; /** * @brief getSubscriptionMaxCachedMedia Gets the maximum number of cached media * for each subscription * @return maximum number of cached media for each subscription * * This setting will be used when a subscription is set to inherit the global * setting, but each subscription can individually override this setting. */ virtual uint32_t getSubscriptionMaxCachedMedia() const = 0; /** * @brief getSubscriptionMaxCacheSize Gets the maximum cache size for each subscriptions * @return maximum cache size for each subscriptions * * This setting will be used when a subscription is set to inherit the global * setting, but each subscription can individually override this setting. */ virtual uint64_t getSubscriptionMaxCacheSize() const = 0; /** * @brief getMaxCacheSize Gets the maximum overall cache size * @return maximum overall cache size */ virtual uint64_t getMaxCacheSize() const = 0; /** * @brief refreshAllSubscriptions Queue refresh tasks for each subscriptions. * @return true if all the subscriptions were refreshed successfully. */ virtual bool refreshAllSubscriptions() = 0; }; } extern "C" { /** * @brief NewMediaLibrary Creates a media library instance * @param dbPath The path to the database. * @param mlFolderPath The path to the dedicated media library folder * @param lockFile A boolean that represents the need for locking the media library * @param cfg A pointer to additional configurations. Can be nullptr * @return A media library instance * * If lockFile is true, nullptr will be returned if another instance holds * the same media library directory. * \see{SetupConfig} */ medialibrary::IMediaLibrary* NewMediaLibrary( const char* dbPath, const char* mlFolderPath, bool lockFile, const medialibrary::SetupConfig* cfg ); } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IMetadata.h000066400000000000000000000027371501065546500277330ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class IMetadata { public: enum class EntityType : uint8_t { Media = 1, }; virtual ~IMetadata() = default; virtual bool isSet() const = 0; virtual int64_t asInt() const = 0; virtual double asDouble() const = 0; virtual const std::string& asStr() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IMovie.h000066400000000000000000000025771501065546500272740ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class IMovie { public: virtual ~IMovie() = default; virtual int64_t id() const = 0; virtual const std::string& shortSummary() const = 0; virtual const std::string& imdbId() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IPlaylist.h000066400000000000000000000234031501065546500300050ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include #include "Types.h" #include "IQuery.h" namespace medialibrary { struct QueryParameters; class IPlaylist { public: virtual ~IPlaylist() = default; /// /// \brief id Returns the playlist id /// virtual int64_t id() const = 0; /// /// \brief name Returns the playlist name /// virtual const std::string& name() const = 0; /// /// \brief setName Updates the playlist name /// virtual bool setName( const std::string& name ) = 0; /// /// \brief creationDate Returns the playlist creation date. /// /// For playlist that were analyzed based on a playlist file (as opposed to /// created by the application) this will be the date when the playlist was /// first discovered, not the playlist *file* creation/last modification date /// virtual unsigned int creationDate() const = 0; /// /// \brief artworkMrl An artwork for this playlist, if any. /// \return An artwork, or an empty string if none is available. /// virtual const std::string& artworkMrl() const = 0; /// /// \brief nbMedia Return the number of media in this playlist /// /// This number doesn't reflect media presence. For the count of present /// media, use nbPresentMedia /// virtual uint32_t nbMedia() const = 0; virtual uint32_t nbVideo() const = 0; virtual uint32_t nbAudio() const = 0; virtual uint32_t nbUnknown() const = 0; /// /// \brief nbPresentMedia Returns the number of present media in this playlist /// virtual uint32_t nbPresentMedia() const = 0; virtual uint32_t nbPresentVideo() const = 0; virtual uint32_t nbPresentAudio() const = 0; virtual uint32_t nbPresentUnknown() const = 0; /// /// \brief duration Returns the duration of the playlist /// /// This is equal to the sum of the duration for all media belonging to /// that playlist. /// Some media duration may not be known by the media library, in which case /// \sa{nbDurationUnknown} will return a non-zero value. /// virtual int64_t duration() const = 0; /// /// \brief nbDurationUnknown Returns the number of media for which the duration /// is unknown /// /// virtual uint32_t nbDurationUnknown() const = 0; /// /// \brief media Returns the media contained in this playlist /// \param params A QueryParameters object or nullptr for the default params /// \return A query object representing the media in this playlist /// /// \note The media will be sorted by their ascending position in the playlist by default. /// virtual Query media( const QueryParameters* params ) const = 0; /// /// \brief searchMedia Search some media in a playlist /// \param pattern The search pattern. Minimal length is 3 characters /// \param params Some query parameters. \see{IMediaLibrary::searchMedia} /// \return A query object, or nullptr in case of error or if the pattern is too short /// virtual Query searchMedia( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; /// /// \brief append Appends a media to a playlist /// The media will be the last element of a subsequent call to media() /// \param media The media to add /// \return true on success, false on failure. /// virtual bool append( const IMedia& media ) = 0; /// /// \brief add Add a media to the playlist at the given position. /// \param media The media to add /// \param position The position of this new media, in the [0;size-1] range /// \return true on success, false on failure /// /// 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 /// /// For instance, on the playlist [, , ], if add(D, 999) /// gets called, the resulting playlist will be [, , , ] /// 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 [, , ], if apend([D,E]) /// gets called, the resulting playlist will be [, , , , ] /// virtual bool append( const std::vector& 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 [, , ], if apend([D,E], 2) /// gets called, the resulting playlist will be [, , , , ] /// /// 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 /// , , ], if add([D,E], 999) /// gets called, the resulting playlist will be [, , , , ] /// virtual bool add( const std::vector& 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& mediaList ) = 0; virtual bool add( const std::vector& 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 /// /// In case there is already an item at the given position, it will be placed before /// the media being moved. This will cascade to any media placed afterward. /// For instance, a playlist with like /// [, , ] on which move(0, 1) is called will result in the /// playlist being changed to /// [, , ] /// /// Likewise, when moving multiple elements: /// On the playlist [, , , , ], move(1, 3, 2) results in /// [, , , , ] /// /// Moving one or multiple elements at the same position or within its range has no effects. /// On the playlist [, , , , ], 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 [, , ], if move(0, 999) /// gets called, the resulting playlist will be [, , ]. /// virtual bool move( uint32_t from, uint32_t to, uint32_t count = 1 ) = 0; /// /// \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 /// /// For instance, a playlist with like /// [, , , ] on which remove(1, 2) is called will result in the /// playlist being changed to[, ] /// 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 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 /// now, accept the changes, but if the playlist file changes, any user /// provided changes will be discarded without warning. /// virtual bool isReadOnly() const = 0; /// /// \brief mrl Return the file backing this playlist. /// /// This must be called only when isReadOnly() returns true, as a modifiable /// playlist has no file associated with it. /// In case of errors, an empty string will be returned. /// virtual std::string mrl() const = 0; virtual bool isFavorite() const = 0; virtual bool setFavorite( bool favorite ) = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IQuery.h000066400000000000000000000044541501065546500273160ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include #include namespace medialibrary { template class IQuery { public: using Result = std::vector>; virtual ~IQuery() = default; /** * @brief count returns the total number of items that would be returned by all() * * There is no temporal guarantee, meaning that if an item gets added between * a call to count() and all(), the call to all() will return count() + 1 items. */ virtual size_t count() = 0; /** * @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 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. * nbItems & offset are directly mapped to the LIMIT OFFSET parameters of * the SQL query that will be generated to compute the result. */ virtual Result items( uint32_t nbItems, uint32_t offset ) = 0; virtual Result all() = 0; }; template using Query = std::unique_ptr>; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IService.h000066400000000000000000000076371501065546500276170ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2022 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "Types.h" #include "IQuery.h" namespace medialibrary { /** * @brief The IService interface represents a service that can allow the user * to import subscriptions */ class IService { public: enum class Type : uint8_t { /* * The type is used as the service's primary key, so any valid value * must be != 0 */ Podcast = 1, }; virtual ~IService() = default; virtual Type type() const = 0; virtual bool isAutoDownloadEnabled() const = 0; virtual bool setAutoDownloadEnabled( bool enabled ) = 0; /** * @brief newMediaNotification Returns true if new content for this service * should issue a notification * * Each subscription can override this setting, or inherit it. */ virtual bool isNewMediaNotificationEnabled() const = 0; virtual bool setNewMediaNotificationEnabled( bool enabled ) = 0; /** * @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 maxCacheSize() const = 0; /** * @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 setMaxCacheSize( int64_t maxSize ) = 0; virtual bool addSubscription( std::string mrl ) = 0; virtual Query subscriptions( const QueryParameters* params ) const = 0; virtual Query searchSubscription( const std::string& pattern, const QueryParameters* params ) const = 0; virtual Query media( const QueryParameters* params ) const = 0; virtual Query searchMedia( const std::string& pattern, const QueryParameters* params ) const = 0; /** * @brief nbSubscriptions Returns the number of subscriptions associated with this service * * This is equivalent to invoking count() on the query returned by subscriptions(), but * this version will not issue a request and will return an already computed counter. */ virtual uint32_t nbSubscriptions() const = 0; virtual uint32_t nbUnplayedMedia() const = 0; virtual uint32_t nbMedia() const = 0; /** * @brief refresh Queue refresh tasks for each subscriptions of this service. * @return true if all the subscriptions were refreshed successfully. */ virtual bool refresh() = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IShow.h000066400000000000000000000036431501065546500271300ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include #include "IQuery.h" #include "Types.h" namespace medialibrary { class IShow { public: virtual ~IShow() = default; virtual int64_t id() const = 0; virtual const std::string& title() const = 0; virtual time_t releaseDate() const = 0; virtual const std::string& shortSummary() const = 0; virtual const std::string& artworkMrl() const = 0; virtual const std::string& tvdbId() const = 0; virtual Query episodes( const QueryParameters* params = nullptr ) const = 0; virtual Query searchEpisodes( const std::string& pattern, const QueryParameters* params = nullptr ) const = 0; virtual uint32_t nbSeasons() const = 0; virtual uint32_t nbEpisodes() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IShowEpisode.h000066400000000000000000000031541501065546500304360ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { class IShow; class IShowEpisode { public: virtual ~IShowEpisode() = default; virtual int64_t id() const = 0; virtual unsigned int episodeId() const = 0; virtual unsigned int seasonId() const = 0; virtual const std::string& title() const = 0; virtual const std::string& shortSummary() const = 0; virtual const std::string& tvdbId() const = 0; virtual std::shared_ptr show() = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/ISubscription.h000066400000000000000000000121001501065546500306600ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2022 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include "medialibrary/Types.h" #include "medialibrary/IQuery.h" #include "medialibrary/IService.h" namespace medialibrary { struct QueryParameters; class ISubscription { public: virtual ~ISubscription() = default; virtual int64_t id() const = 0; virtual IService::Type service() const = 0; virtual const std::string& name() const = 0; virtual Query childSubscriptions( const QueryParameters* params ) const = 0; virtual SubscriptionPtr parent() = 0; virtual Query media( const QueryParameters* params ) const = 0; virtual Query search( const std::string&, const QueryParameters* ) const = 0; virtual bool refresh() = 0; /** * @brief cachedSize Returns the sum of all cached files for this collection */ virtual uint64_t cachedSize() const = 0; /** * @brief maxCachedMedia Returns the maximum number of cached media for this * collection, or -1 is unset */ virtual int32_t maxCachedMedia() const = 0; /** * @brief setMaxCachedMedia Sets the maximum number of automatically cached media * @param nbCachedMedia The number of cached media, or a negative value to unset * @return true if the setting was changed, false otherwise * * If this isn't set for the collection, the global setting is used instead. * If this is set to a negative value, this collection's setting will be * erased and the parent setting will be used again when caching, however * the maxCachedMedia will return -1 to denote that this collection inherits * its setting from its parent. */ virtual bool setMaxCachedMedia( int32_t nbCachedMedia ) = 0; /** * @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 maxCacheSize() const = 0; /** * @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. * * This will not perform any checks against the global setting, but if this * is greater than the global maximum cache size, the global setting will * prevail when caching. * If passing the current value, this will return true. */ 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 * * If this value is unset, the parent service setting will be used. */ virtual int8_t newMediaNotification() const = 0; /** * @brief setNewMediaNotification Sets the new media notification setting * @param value A negative value to default to the parent service setting, 0 * to disable, and a positive value to enable. * @return true if the change was successful, false otherwise. */ virtual bool setNewMediaNotification( int8_t value ) = 0; /** * @brief nbUnplayedMedia Returns the number of media that belong to this * subscription and haven't been played */ virtual uint32_t nbUnplayedMedia() const = 0; /** * @brief nbMedia Returns the number of media that belong to this * subscription. */ virtual uint32_t nbMedia() const = 0; /** * @brief artworkMRL Returns the subscription's artwork MRL. * * @note An empty string is returned in case of no artwork MRL is available. */ virtual const std::string& artworkMRL() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/ISubtitleTrack.h000066400000000000000000000032551501065546500307670ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class ISubtitleTrack { public: virtual ~ISubtitleTrack() = default; virtual int64_t id() const = 0; virtual const std::string& codec() const = 0; virtual const std::string& language() const = 0; virtual const std::string& description() const = 0; virtual const std::string& encoding() const = 0; /** * @brief isInAttachedFile Returns true if the track is in an attached * (ie. not the main file) */ virtual bool isInAttachedFile() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IThumbnailer.h000066400000000000000000000044231501065546500304570ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class IMedia; class IThumbnailer { public: virtual ~IThumbnailer() = default; /** * @brief request Generate a thumbnail for the provided media * @param media A reference to the media a thumbnail is being generated for * @param mrl The mrl to the main file for this media * @param desiredWidth The desired thumbnail width * @param desiredHeight The desired thumbnail height * @param position The position at which to generate the thumbnail, in the [0;1] range * @param destination The path in which to save the thumbnail * * This method is expected to be blocking until the generation is * successful or stopped. * Upon successful return, the path must contain the generated * thumbnail */ virtual bool generate( const IMedia& media, const std::string& mrl, uint32_t desiredWidth, uint32_t desiredHeight, float position, const std::string& destination ) = 0; /** * @brief stop Stop any ongoing processing as soon as possible */ virtual void stop() = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/IVideoTrack.h000066400000000000000000000034061501065546500302400ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class IVideoTrack { public: virtual ~IVideoTrack() = default; virtual int64_t id() const = 0; virtual const std::string& codec() const = 0; virtual unsigned int width() const = 0; virtual unsigned int height() const = 0; virtual float fps() const = 0; virtual uint32_t fpsNum() const = 0; virtual uint32_t fpsDen() const = 0; virtual uint32_t bitrate() const = 0; virtual uint32_t sarNum() const = 0; virtual uint32_t sarDen() const = 0; virtual const std::string& description() const = 0; virtual const std::string& language() const = 0; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/Types.h000066400000000000000000000054531501065546500272040ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include namespace medialibrary { class IAlbum; class IAudioTrack; class IBookmark; class IChapter; class IFile; class IGenre; class IMedia; class ILabel; class IMetadataService; class IMovie; class IShow; class IShowEpisode; class IVideoTrack; class ILogger; class IArtist; class IPlaylist; class IMediaLibraryCb; class IDeviceLister; class IDeviceListerCb; class IFolder; class ISubtitleTrack; class IThumbnailer; class IMediaGroup; class ISubscription; class ICacher; class IService; class IShowEpisode; class ITrackInformation; class IMetadata; struct QueryParameters; enum class ThumbnailSizeType : uint8_t; enum class ThumbnailStatus : uint8_t; namespace parser { class IParserService; } namespace fs { class IFileSystemFactory; } using AlbumPtr = std::shared_ptr; using ArtistPtr = std::shared_ptr; using AudioTrackPtr = std::shared_ptr; using BookmarkPtr = std::shared_ptr; using ChapterPtr = std::shared_ptr; using FilePtr = std::shared_ptr; using GenrePtr = std::shared_ptr; using LabelPtr = std::shared_ptr; using MediaPtr = std::shared_ptr; using MoviePtr = std::shared_ptr; using PlaylistPtr = std::shared_ptr; using ShowEpisodePtr = std::shared_ptr; using ShowPtr = std::shared_ptr; using VideoTrackPtr = std::shared_ptr; using DeviceListerPtr = std::shared_ptr; using FolderPtr = std::shared_ptr; using SubtitleTrackPtr = std::shared_ptr; using MediaGroupPtr = std::shared_ptr; using SubscriptionPtr = std::shared_ptr; using ServicePtr = std::shared_ptr; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/filesystem/000077500000000000000000000000001501065546500301045ustar00rootroot00000000000000Errors.h000066400000000000000000000065271501065546500314640ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/filesystem/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { namespace fs { namespace errors { class Exception : public std::runtime_error { public: Exception( const std::string& str ) : std::runtime_error( str ) { } }; class UnknownScheme : public Exception { public: UnknownScheme( const std::string& scheme ) : Exception( "No filesystem factory found for scheme " + scheme ) , m_scheme( scheme ) { } const std::string& scheme() const { return m_scheme; } private: std::string m_scheme; }; class UnhandledScheme : public Exception { public: UnhandledScheme( const std::string& scheme ) : Exception( "Unhandled MRL scheme: " + scheme ) , m_scheme( scheme ) { } const std::string& scheme() const noexcept { return m_scheme; } private: std::string m_scheme; }; class DeviceRemoved : public Exception { public: DeviceRemoved() noexcept : Exception( "The device containing this file/folder was removed" ) { } }; class DeviceMapper : public Exception { public: DeviceMapper( const std::string& str ) : Exception( "Failed to resolve using device mapper: " + str ) { } }; class DeviceListing : public Exception { public: DeviceListing( const std::string& str ) : Exception( "Failed to list devices: " + str ) { } }; class NotFound : public Exception { public: NotFound( const std::string& mrl, const std::string& container ) : Exception( mrl + " was not found in " + container ) { } }; class System : public Exception { public: #ifdef _WIN32 System( unsigned long err, const std::string& msg ) : Exception( msg + ": " + std::error_code( err, std::generic_category() ).message() ) , m_errc( err, std::generic_category() ) { } #endif System( int err, const std::string& msg ) : Exception( msg + ": " + std::error_code( err, std::generic_category() ).message() ) , m_errc( err, std::generic_category() ) { } const std::error_code& code() const noexcept { return m_errc; } private: std::error_code m_errc; }; } } } IDevice.h000066400000000000000000000065611501065546500315160ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/filesystem/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { namespace fs { class IDevice { public: virtual ~IDevice() = default; virtual const std::string& uuid() const = 0; /** * @brief scheme Returns this device scheme * * Since if the device has no remaining mountpoint, this getter can be used * to know what scheme this device is using at any time. */ virtual const std::string& scheme() const = 0; virtual bool isRemovable() const = 0; virtual bool isPresent() const = 0; virtual bool isNetwork() const = 0; /** * @brief mountpoint Returns a mountpoint of this device. * * If the device has multiple mountpoints, the result is undetermined */ virtual std::vector mountpoints() const = 0; virtual void addMountpoint( std::string mountpoint ) = 0; virtual void removeMountpoint( const std::string& mountpoint ) = 0; /** * @brief matchesMountpoint checks if the provided mrl matches this device * @param mrl The mrl to probe * @return A tuple containing: * - a bool, that reflects the match status (ie. true if the device matches the mrl) * - a string, containing the mountpoint that matched, or an empty string * if the device did not match */ virtual std::tuple matchesMountpoint( const std::string& mrl ) const = 0; /** * @brief relativeMrl Returns an mrl relative to the device mountpoint * @param absoluteMrl The absolute MRL pointing to a file or folder, including * the scheme * @return An scheme-less MRL */ virtual std::string relativeMrl( const std::string& absoluteMrl ) const = 0; /** * @brief absoluteMrl Returns an absolute mrl to the provided file or folder * @param relativeMrl A relative MRL pointing to a file or folder, *not* * including the scheme * @return An absolute MRL, including the scheme * * If the device has multiple mountpoints, the resulting mrl is undetermined * but guaranteed to yield the same device back when using * IFileSystemFactory::createDeviceFromMrl */ virtual std::string absoluteMrl( const std::string& relativeMrl ) const = 0; }; } } IDirectory.h000066400000000000000000000051211501065546500322520ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/filesystem/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { namespace fs { class IFile; class IDevice; class IDirectory { public: virtual ~IDirectory() = default; // Returns the absolute path to this directory virtual const std::string& mrl() const = 0; /// Returns a list of absolute files path virtual const std::vector>& files() const = 0; /// Returns a list of absolute path to this folder subdirectories virtual const std::vector>& dirs() const = 0; virtual std::shared_ptr device() const = 0; /// /// \brief file Returns a file matching the provided mrl from this directory /// \param mrl A file mrl. It can be an absolute file mrl, or just the filename (url encoded) /// \return An IFile instance if a file matched, nullptr otherwise. /// virtual std::shared_ptr file( const std::string& mrl ) const = 0; /// /// \brief contains Returns true if this directory contains the given file /// \param file The file to search for /// \return true if the file is contained in this directory, false otherwise /// /// The comparison is done in a case insensitive way. /// \warning The comparison is done against the URL encoded file name /// virtual bool contains( const std::string& file ) const = 0; }; } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/filesystem/IFile.h000066400000000000000000000046631501065546500312560ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { namespace fs { class IFile { public: enum class LinkedFileType : uint8_t { ///< This file is not a linked file None, ///< This is a linked subtitle file Subtitles, ///< This is a linked soundtrack file SoundTrack, }; virtual ~IFile() = default; /// Returns the URL encoded filename, including the extension virtual const std::string& name() const = 0; /// Returns the mrl of this file virtual const std::string& mrl() const = 0; virtual const std::string& extension() const = 0; virtual time_t lastModificationDate() const = 0; virtual uint64_t size() const = 0; virtual bool isNetwork() const = 0; /** * @brief type Returns the file type, or None if not linked with another file */ virtual LinkedFileType linkedType() const = 0; /** * @brief linkedWith Return the MRL this file is linked to, or an empty * string if it's not linked with anything * * If type() is None, it's invalid to call this function */ virtual const std::string& linkedWith() const = 0; }; } } IFileSystemFactory.h000066400000000000000000000164011501065546500337250ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/filesystem/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include namespace medialibrary { class IMediaLibrary; namespace fs { class IDirectory; class IFile; class IDevice; /** * @brief IFileSystemFactoryCb is for external file system factories to signal device changes */ class IFileSystemFactoryCb { public: virtual ~IFileSystemFactoryCb() = default; /** * @brief onDevicePlugged Shall be invoked when a device gets plugged * @param device The mounted device * @param newMountpoint the mountpoint that was just added to this device * * When this callback is invoked, the FS device presence must already be * updated. */ virtual void onDeviceMounted( const fs::IDevice& device, const std::string& newMountpoint ) = 0; /** * @brief onDeviceUnplugged Shall be invoked when a device gets unplugged * @param device The unmounted device * @param removedMountpoint the mountpoint that was just unmounted * * When this callback is invoked, the FS device presence must already be * updated. If no more mountpoints are available, device->isPresent must * return false by the time this is called */ virtual void onDeviceUnmounted( const fs::IDevice& device, const std::string& removedMountpoint ) = 0; }; class IFileSystemFactory { public: virtual ~IFileSystemFactory() = default; /** * @brief initialize Will be invoked when the factory gets added to the medialib * * @param ml A media library instance pointer * @return true in case of success, false otherwise. * * 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 */ virtual bool initialize( const IMediaLibrary* ml ) = 0; /// /// \brief createDirectory creates a representation of a directory /// \note This method can fail by throwing an exception if the /// directory doesn't exist, or any other I/O issue occurs. /// Once created, the path of this IDirectory will be sanitized. /// \param mrl An MRL describing the desired directory /// virtual std::shared_ptr createDirectory( const std::string& mrl ) = 0; /// /// \brief createFile Creates a representation of a file /// \note This method can fail by throwing an exception if the file /// doesn't exist, or if any other I/O issue occurs. /// \note The resulting file will have its mrl sanitized. /// \param mrl An MRL describing the desired file /// virtual std::shared_ptr createFile( const std::string& mrl ) = 0; /// /// \brief createDevice creates a representation of a device /// \param uuid The device UUID /// \return A representation of the device, or nullptr if the device is currently unavailable. /// virtual std::shared_ptr createDevice( const std::string& uuid ) = 0; /// /// \brief createDeviceFromPath creates a representation of a device /// \param mrl An MRL. /// \return A representation of the device, or nullptr if none match. /// /// The provided path can and most often will refer to a file in that /// device, and will not simply be the path to the device mountpoint /// This function guarantees that the returned device, if any, is the /// one that matches the mrl best. /// For instance, if a device contains another device mountpoint, if /// that mountpoint or once of its subfolder is passed, the innermost /// device will be returned, even though more than one device matched /// virtual std::shared_ptr createDeviceFromMrl( const std::string& mrl ) = 0; /// /// \brief refresh Will cause any FS cache to be refreshed. /// virtual void refreshDevices() = 0; /// /// \brief isPathSupported Checks for support of a path by this FS factory /// \param path The path to probe for support /// \return True if supported, false otherwise /// virtual bool isMrlSupported( const std::string& path ) const = 0; /// /// \brief isNetworkFileSystem Returns true if this FS factory handles network file systems /// virtual bool isNetworkFileSystem() const = 0; /// /// \brief scheme Returns the mrl scheme handled by this file system factory /// virtual const std::string& scheme() const = 0; /// /// \brief start Starts any potential background operation that would be /// required by this factory. /// \return False in case of an error. /// \param cb An instance of IFileSystemFactoryCb to be used to signal device changes /// This instance is owned by the medialibrary, and must not /// be released /// virtual bool start( IFileSystemFactoryCb* cb ) = 0; /// /// \brief stop stops any potential background operation that would be /// required by this factory. /// virtual void stop() = 0; /// /// \brief isStarted Returns true if the factory is started /// virtual bool isStarted() const = 0; /// /// \brief waitForDevice Will wait for a device that matches the given url /// \param mrl The mrl to match, as it would be passed to createDeviceFromMrl /// \param timeout A timeout, in milliseconds /// \return true if the device was already available or appeared before the timeout, false otherwise /// virtual bool waitForDevice( const std::string& mrl, uint32_t timeout ) const = 0; }; } } meson.build000066400000000000000000000002641501065546500321710ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/filesystemmedialib_fs_headers = [ 'IDevice.h', 'IDirectory.h', 'IFile.h', 'IFileSystemFactory.h', 'Errors.h', ] install_headers(medialib_fs_headers, subdir: 'medialibrary/filesystem') medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/meson.build000066400000000000000000000013171501065546500300640ustar00rootroot00000000000000medialib_headers = [ 'IAlbum.h', 'IArtist.h', 'IAudioTrack.h', 'IBookmark.h', 'IChapter.h', 'IFile.h', 'IGenre.h', 'ILabel.h', 'ILogger.h', 'IMedia.h', 'IMediaLibrary.h', 'IMovie.h', 'IPlaylist.h', 'IShowEpisode.h', 'IShow.h', 'IVideoTrack.h', 'Types.h', 'IDeviceLister.h', 'IFolder.h', 'IQuery.h', 'IMetadata.h', 'ISubtitleTrack.h', 'IThumbnailer.h', 'IMediaGroup.h', 'ISubscription.h', 'ICacher.h', 'IService.h', ] subdir('filesystem') subdir('parser') install_headers(medialib_headers, subdir: 'medialibrary') medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/parser/000077500000000000000000000000001501065546500272145ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/parser/IItem.h000066400000000000000000000164041501065546500304010ustar00rootroot00000000000000 /***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include #include #include #include #include "medialibrary/Types.h" #include "medialibrary/IFile.h" namespace medialibrary { namespace fs { class IDirectory; } namespace fs { class IFile; } namespace parser { class IEmbeddedThumbnail { public: virtual ~IEmbeddedThumbnail() = default; virtual bool save( const std::string& path ) = 0; virtual size_t size() const = 0; virtual std::string hash() const = 0; virtual std::string extension() const = 0; }; class IItem { public: enum class LinkType : uint8_t { NoLink, Playlist, Media, Subscription, }; enum class Metadata : uint8_t { Title, ArtworkUrl, ShowName, Episode, Album, Genre, Date, AlbumArtist, Artist, TrackNumber, DiscNumber, DiscTotal, Description, /* for convenience, please keep this last */ NbValues, }; struct Track { Track() = default; enum class Type : uint8_t { Video, Audio, Subtitle, }; std::string codec; Type type; uint32_t bitrate; std::string language; std::string description; // Audio union { struct { uint32_t nbChannels; uint32_t rate; } a; struct { // Video uint32_t height; uint32_t width; uint32_t sarNum; uint32_t sarDen; uint32_t fpsNum; uint32_t fpsDen; } v; struct { char encoding[sizeof(v)]; } s; } u; }; public: virtual ~IItem() = default; /** * @brief meta Returns a stored meta for this item. * @param type The type of meta * @return A copy of the meta. It can be safely moved to another std::string * if required. * A default constructed string if the meta isn't known for this item */ virtual std::string meta( Metadata type ) const = 0; /** * @brief setMeta Store a meta for the item * @param type The metadata type * @param value The metadata value */ virtual void setMeta( Metadata type, std::string value ) = 0; /** * @return The MRL representing this item. */ virtual const std::string& mrl() const = 0; /** * @brief fileType Returns the analyzed file type */ virtual IFile::Type fileType() const = 0; /** * @return The number of linked items for this item. */ virtual size_t nbLinkedItems() const = 0; /** * @return The linked item at the given index. * The linked items are owned by their parent item. No bound checking is * performed. */ virtual const IItem& linkedItem( unsigned int index ) const = 0; /** * @brief createLinkedItem Create a linked item in place. * @param mrl The item's MRL * @param itemType The linked item type * @param linkExtra Some additional information about the item being linked * @return A *reference* to the created item, so the item can be populated * after its creation. */ virtual IItem& createLinkedItem( std::string mrl, IFile::Type itemType, int64_t linkExtra ) = 0; /** * @brief duration Returns the item duration in milliseconds */ virtual int64_t duration() const = 0; /** * @brief setDuration Sets the item duration * @param duration A duration in milliseconds */ virtual void setDuration( int64_t duration ) = 0; /** * @brief tracks List all the item tracks * * This only contains the Audio & Video tracks */ virtual const std::vector& tracks() const = 0; /** * @brief addTrack Add a track to this item. * @param t The track to add. */ virtual void addTrack( Track&& t ) = 0; /** * @brief media Returns the media associated with this item, if any. * @return nullptr if the item isn't associated with a Media yet. */ virtual MediaPtr media() = 0; /** * @brief setMedia Assigns a Media to this item */ virtual void setMedia( MediaPtr media ) = 0; /** * @brief file Returns the File (as in, the database entity) associated with * this item, if any. It returns nullptr otherwise */ virtual FilePtr file() = 0; /** * @brief fileId Returns the ID of the file associated with this item, if any. * * If no file was created for this item yet, 0 is returned. */ virtual int64_t fileId() const = 0; /** * @brief setFile Assigns a File to the item */ virtual bool setFile( FilePtr file ) = 0; /** * @brief parentFolder Returns the Folder (as in the database entity) * containing this item. * * @return This can be nullptr if the item refers to an "external" media, ie. * if it was added through its complete MRL, instead of being * discovered through its parent folder. */ virtual FolderPtr parentFolder() = 0; /** * @brief fileFs returns an fs::IFile representing the item * * This will return nullptr for external media. */ virtual std::shared_ptr fileFs() const = 0; /** * @brief parentFolderFs Returns an fs::IDirectory representing the parent * folder * * This will return nullptr for external media */ virtual std::shared_ptr parentFolderFs() = 0; virtual bool isRefresh() const = 0; virtual bool isRestore() const = 0; virtual LinkType linkType() const = 0; virtual int64_t linkToId() const = 0; virtual int64_t linkExtra() const = 0; virtual const std::string& linkToMrl() const = 0; virtual const std::vector>& embeddedThumbnails() const = 0; virtual void addEmbeddedThumbnail( std::shared_ptr t ) = 0; }; } } IParserService.h000066400000000000000000000062301501065546500321750ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/parser/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include namespace medialibrary { class IMediaLibrary; namespace parser { class IItem; } namespace parser { enum class Status; } namespace parser { enum class Step : uint8_t; } class IMediaLibrary; namespace parser { class IParserService { public: virtual ~IParserService() = default; /** * @brief run Process a specific task * @param task The task to be processed * @return A status code */ virtual parser::Status run( parser::IItem& item ) = 0; /** * @brief name returns the name of this service. * * This is for logging purposes only. */ virtual const char* name() const = 0; /** * @brief targetedStep Returns the ParserStep targeted by this service. */ virtual parser::Step targetedStep() const = 0; /** * @brief initialize Run service specific initialization. * * By the time this function is called, the database is fully initialized and * can be used. * A pointer the the medialibrary is provided to allow the service to * create/fetch entities. * * If false is returned, the service will be released and won't be used. */ virtual bool initialize( IMediaLibrary* ml ) = 0; /** * @brief onFlushing will be invoked prior to restarting/flushing the service * * A service must release any database entity it might hold. * The service will be paused or unstarted when this method gets called. */ virtual void onFlushing() = 0; /** * @brief onRestarted will be invoked prior to a service restart. * * A restart will always occur after a flush. Once this function gets called, * the service is free to cache some database entities, or interact with the * medialibrary again. * The thread(s) running the service itself won't have been restarted when this * function gets called. */ virtual void onRestarted() = 0; /** * @brief stop Require the service to interrupt its processing as soon as possible */ virtual void stop() = 0; }; } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/parser/Parser.h000066400000000000000000000045711501065546500306300ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2018 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include namespace medialibrary { namespace parser { enum class Status { /// Default value. /// Also, having success = 0 is not the best idea ever. Unknown, /// All good Success, /// We can't compute this file for now (for instance the file was on a network drive which /// isn't connected anymore) /// The task will be run again next time the parser gets started. /// This will still increment the task's retry count TemporaryUnavailable, /// Something failed and we won't continue Fatal, /// The task must now be considered completed, regardless of the /// current step. Completed, /// The task should be discarded, ie removed from the database, regardless /// of its status. /// This is likely to be used when trying to parse playlist items, /// as they already could have been queued before. Discarded, /// The task can't be run right now, but should be rescheduled at the back /// of the queue. /// The task's retry count will still be incremented to avoid looping forever /// if this gets returned continuously Requeue, }; enum class Step : uint8_t { None = 0, MetadataExtraction = 1, MetadataAnalysis = 2, Completed = 1 | 2, Linking = 4, }; } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/medialibrary/parser/meson.build000066400000000000000000000002361501065546500313570ustar00rootroot00000000000000medialib_parser_headers = [ 'IItem.h', 'Parser.h', 'IParserService.h', ] install_headers(medialib_parser_headers, subdir: 'medialibrary/parser') medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/include/meson.build000066400000000000000000000000271501065546500254150ustar00rootroot00000000000000subdir('medialibrary') medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/libvlcpp/000077500000000000000000000000001501065546500234445ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/medialibrary.pc.in000066400000000000000000000004661501065546500252340ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=${prefix}/lib includedir=${prefix}/include Name: medialibrary Description: cross platform medialibrary Version: @PACKAGE_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lmedialibrary @ML_EXTRA_LIBS@ Libs.private: @SQLITE_LIBS@ @LIBJPEG_LIBS@ @THREAD_LIBS@ medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/meson.build000066400000000000000000000130021501065546500237670ustar00rootroot00000000000000project('medialibrary', ['cpp','c'], license: 'LGPLv2+', version: '0.13.0', meson_version: '>=0.56', default_options: [ 'cpp_std=c++14', 'buildtype=debug', 'warning_level=3', 'b_ndebug=if-release' ] ) macosx_dep = [] if host_machine.system() == 'darwin' add_languages('objcpp', required: true) # https://github.com/mesonbuild/meson/issues/5495 add_global_arguments('-std=c++14', language: 'objcpp') macosx_dep = dependency('appleframeworks', modules: 'foundation') endif medialib_soname_version = '0.0.0' ver_arr = medialib_soname_version.split('.') medialib_major_version = ver_arr[0] medialib_minor_version = ver_arr[1] medialib_micro_version = ver_arr[2] cxx = meson.get_compiler('cpp') extra_warnings = [ ] foreach w : extra_warnings if cxx.has_argument(w) add_project_arguments(w, language: 'cpp') endif endforeach if get_option('b_sanitize') == 'thread' if cxx.get_id() != 'clang' error('thread sanitizer requires clang and libc++') endif add_project_arguments('-stdlib=libc++', language: 'cpp') add_project_link_arguments('-stdlib=libc++', language: 'cpp') endif cdata = configuration_data() cdata.set('PROJECT_VERSION', meson.project_version()) cdata.set('_FILE_OFFSET_BITS', 64) if host_machine.system() == 'windows' cdata.set('_WIN32_WINNT', '0x601') cdata.set('_UNICODE', 1) cdata.set('UNICODE', 1) cdata.set('_POSIX_C_SOURCE', '200809L') cdata.set('_BSD_SOURCE', 1) cdata.set('_GNU_SOURCE', 1) endif if cxx.has_function('link') cdata.set('HAVE_LINK', 1) endif message('Checking for C++11 thread support') if cxx.compiles('''#include struct ThreadRunner { void run(); }; int main() { std::thread t; ThreadRunner tr; std::thread t2{ &ThreadRunner::run, tr }; }''') cdata.set('CXX11_THREADS', 1) endif message('Checking for C++11 mutex support') if cxx.compiles('''#include int main() { std::mutex m; }''') cdata.set('CXX11_MUTEX', 1) endif message('Checking for C++11 thread_local support') if cxx.compiles('thread_local int seaOtter = 0; int main() {}') cdata.set('CXX11_THREAD_LOCAL', 1) endif message('Checking for C++11 condition_variable support') if cxx.compiles('''#include int main() { std::condition_variable cv; }''') cdata.set('CXX11_CONDITION_VARIABLE', 1) endif if host_machine.system() == 'windows' threads_dep = [] else pthreads = dependency('threads', required: true) if get_option('libtool_workaround') libtool_forced_ldflags = ['-lpthread'] if cxx.find_library('atomic', required: false).found() libtool_forced_ldflags += '-latomic' endif threads_dep = declare_dependency(link_args: libtool_forced_ldflags, dependencies: pthreads) else threads_dep = pthreads endif endif sqlite_dep = dependency('sqlite3', version: '>= 3.33.0', required: true) libvlc_dep = dependency('libvlc', version: '>= 3.0', required: get_option('libvlc')) if libvlc_dep.found() cdata.set('HAVE_LIBVLC', 1) endif # We only require libjpeg when using the 3.0 thumbnailer, using vmem if libvlc_dep.found() and libvlc_dep.version().version_compare('< 4.0') libjpeg_dep = dependency('libjpeg', required: false) if not libjpeg_dep.found() jpegprefix = get_option('libjpeg_prefix') if not cxx.has_header('jpeglib.h', args: '-I' + jpegprefix / 'include') error('libjpeg was not found') endif libjpeg_dep = declare_dependency( include_directories: include_directories(jpegprefix / 'include'), link_args: ['-L' + jpegprefix / 'lib', '-ljpeg'] ) endif cdata.set('HAVE_JPEG', 1) else libjpeg_dep = [] endif if libvlc_dep.found() libvlcpp_dep = dependency('libvlcpp', required: false) if not libvlcpp_dep.found() vlcpp_inc_dir = include_directories('libvlcpp/') if cxx.has_header('vlcpp/vlc.hpp', include_directories: vlcpp_inc_dir) libvlcpp_dep = declare_dependency( include_directories: vlcpp_inc_dir ) else error('libvlcpp not found. Please install it or initialize the submodule') endif endif else libvlcpp_dep = [] endif libxxhash_dep = dependency('libxxhash', required: false) if get_option('force_attachment_api') if not libvlc_dep.found() warning('Invalid option \'force_attachment_api\' provided without libvlc enabled') elif libvlc_dep.version().version_compare('>=4.0') warning('Invalid option \'force_attachment_api\' provided with libvlc >=4.x') else cdata.set('FORCE_ATTACHMENTS_API', 1) endif endif if get_option('fuzz') if not cxx.has_argument('-fsanitize=fuzzer-no-link') error('Selected compiler doesn\'t support libfuzzer') endif add_project_arguments('-fsanitize=fuzzer-no-link,address,undefined', language: 'cpp') endif config_h = configure_file(input: 'config.h.in', output: 'config.h', configuration: cdata) add_project_arguments('-DHAVE_CONFIG_H=1', language: ['cpp']) includes = include_directories('.', 'include', 'src') subdir('include') subdir('src') # Ideally we shouldn't need an option to build tests, but we need # https://github.com/mesonbuild/meson/issues/2518 # to be fixed first if libvlc_dep.found() and not get_option('tests').disabled() subdir('test') endif benchmark_dep = dependency('benchmark', required: false, static: true) if benchmark_dep.found() and get_option('b_sanitize') != 'thread' subdir('benchmark') endif pkg = import('pkgconfig') pkg.generate(libraries: medialib, version: meson.project_version(), name: 'medialibrary', description: 'cross platform medialibrary' ) medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/meson_options.txt000066400000000000000000000012321501065546500252640ustar00rootroot00000000000000option('libvlc', type: 'feature', value: 'auto') option('libjpeg_prefix', type: 'string') option('tests', type: 'feature', value: 'auto') option('force_attachment_api', type: 'boolean', value: false) option('fuzz', type: 'boolean', value: false) option('libtool_workaround', type: 'boolean', value: false, description: 'Force explicit mention of some libraries in the .pc ' + 'file that would be overwritten by libtool\'s use of -nostdlib') option('long_running_tests', type: 'boolean', value: false, description: 'Enable the long running tests. The binaries will be built ' + 'regardless and can still be run manually. This is intended for CI invocations') medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/000077500000000000000000000000001501065546500224205ustar00rootroot00000000000000medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Album.cpp000066400000000000000000001153651501065546500241770ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include "Album.h" #include "Artist.h" #include "Genre.h" #include "Media.h" #include "Thumbnail.h" #include "utils/Enums.h" #include "utils/ModificationsNotifier.h" #include "Deprecated.h" #include "database/SqliteTools.h" #include "database/SqliteQuery.h" namespace medialibrary { const std::string Album::Table::Name = "Album"; const std::string Album::Table::PrimaryKeyColumn = "id_album"; int64_t Album::* const Album::Table::PrimaryKey = &Album::m_id; const std::string Album::FtsTable::Name = "AlbumFts"; Album::Album(MediaLibraryPtr ml, sqlite::Row& row) : m_ml( ml ) , m_id( row.extract() ) , m_title( row.extract() ) , m_artistId( row.extract() ) , m_releaseYear( row.extract() ) , m_shortSummary( row.extract() ) , m_nbTracks( row.extract() ) , m_duration( row.extract() ) , m_nbDiscs( row.extract() ) , m_nbPresentTracks( row.extract() ) , m_isFavorite( row.extract() ) { if ( row.hasRemainingColumns() == true ) m_publicOnlyListing = row.extract(); else m_publicOnlyListing = false; assert( row.hasRemainingColumns() == false ); } Album::Album( MediaLibraryPtr ml, std::string title ) : m_ml( ml ) , m_id( 0 ) , m_title( std::move( title ) ) , m_artistId( 0 ) , m_releaseYear( ~0u ) , m_nbTracks( 0 ) , m_duration( 0 ) , m_nbDiscs( 1 ) , m_nbPresentTracks( 0 ) , m_publicOnlyListing( false ) , m_isFavorite( false ) { } Album::Album( MediaLibraryPtr ml, const Artist* artist ) : m_ml( ml ) , m_id( 0 ) , m_artistId( artist->id() ) , m_releaseYear( ~0u ) , m_nbTracks( 0 ) , m_duration( 0 ) , m_nbDiscs( 1 ) , m_nbPresentTracks( 0 ) , m_publicOnlyListing( false ) , m_isFavorite( false ) { } int64_t Album::id() const { return m_id; } const std::string& Album::title() const { return m_title; } unsigned int Album::releaseYear() const { if ( m_releaseYear == ~0U ) return 0; return m_releaseYear; } bool Album::setReleaseYear( unsigned int date, bool force ) { if ( date == m_releaseYear ) return true; if ( force == false ) { if ( m_releaseYear != ~0u ) { // If we already have set the date back to 0, don't do it again. if ( m_releaseYear == 0 ) return true; date = 0; } } static const std::string req = "UPDATE " + Album::Table::Name + " SET release_year = ? WHERE id_album = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, date, m_id ) == false ) return false; m_releaseYear = date; return true; } const std::string& Album::shortSummary() const { return m_shortSummary; } bool Album::setShortSummary( const std::string& summary ) { static const std::string req = "UPDATE " + Album::Table::Name + " SET short_summary = ? WHERE id_album = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, summary, m_id ) == false ) return false; m_shortSummary = summary; return true; } ThumbnailStatus Album::thumbnailStatus( ThumbnailSizeType sizeType ) const { auto t = thumbnail( sizeType ); if ( t == nullptr ) return ThumbnailStatus::Missing; return t->status(); } const std::string& Album::thumbnailMrl( ThumbnailSizeType sizeType ) const { const auto t = thumbnail( sizeType ); if ( t == nullptr ) return Thumbnail::EmptyMrl; return t->mrl(); } std::shared_ptr Album::thumbnail( ThumbnailSizeType sizeType ) const { auto idx = Thumbnail::SizeToInt( sizeType ); if ( m_thumbnails[idx] == nullptr ) { auto thumbnail = Thumbnail::fetch( m_ml, Thumbnail::EntityType::Album, m_id, sizeType ); if ( thumbnail == nullptr ) return nullptr; m_thumbnails[idx] = std::move( thumbnail ); } return m_thumbnails[idx]; } bool Album::shouldUpdateThumbnail( const Thumbnail& currentThumbnail ) { /* * We want to update an album thumbnail. * * If we inherited the thumbnail from a media, we need to insert a new record * as we won't want to update the source media's thumbnail. * * If the cover comes from an album file, we can update it, and let the * thumbnails that were based on this album be updated as well * * In other cases, let's just insert a new thumbnail. */ switch ( currentThumbnail.origin() ) { case Thumbnail::Origin::Media: return false; case Thumbnail::Origin::CoverFile: return true; default: return false; } } bool Album::setThumbnail( std::shared_ptr newThumbnail ) { assert( newThumbnail != nullptr ); auto currentThumbnail = thumbnail( newThumbnail->sizeType() ); auto thumbnailIdx = Thumbnail::SizeToInt( newThumbnail->sizeType() ); currentThumbnail = Thumbnail::updateOrReplace( m_ml, currentThumbnail, std::move( newThumbnail ), Album::shouldUpdateThumbnail, m_id, Thumbnail::EntityType::Album ); if ( currentThumbnail == nullptr ) return false; m_thumbnails[thumbnailIdx] = std::move( currentThumbnail ); auto notifier = m_ml->getNotifier(); if ( notifier != nullptr ) notifier->notifyAlbumModification( m_id ); return true; } std::string Album::addRequestJoin( const QueryParameters* params, bool media ) { auto sort = params != nullptr ? params->sort : SortingCriteria::Alpha; bool artist = false; switch( sort ) { case SortingCriteria::ReleaseDate: case SortingCriteria::Duration: case SortingCriteria::TrackNumber: /* No other tables required for this criteria */ break; case SortingCriteria::PlayCount: case SortingCriteria::InsertionDate: media = true; break; case SortingCriteria::Artist: /* Different case than default, but same tables in the end */ /* fall-through */ case SortingCriteria::Alpha: case SortingCriteria::Default: default: // In case of identical album name // sort should continue with artist name artist = true; break; } std::string req; if ( artist == true ) req += "LEFT JOIN " + Artist::Table::Name + " art ON alb.artist_id = art.id_artist "; if ( media == true ) req += "INNER JOIN " + Media::Table::Name + " m ON m.album_id = alb.id_album "; return req; } std::string Album::addRequestConditions( const QueryParameters* params, bool forcePublic ) { 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 ) { 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 ( params->favoriteOnly == true ) { if ( req.empty() == false ) req += " AND"; req += " alb.is_favorite = TRUE"; } return req; } std::string Album::orderBy( const QueryParameters* params ) { std::string req = " ORDER BY "; auto sort = params != nullptr ? params->sort : SortingCriteria::Default; auto desc = params != nullptr ? params->desc : false; if ( params != nullptr && params->publicOnly == true && ( sort == SortingCriteria::TrackNumber ) ) { LOG_WARN( "Unsupported sort for public entities. Falling back to Default" ); sort = SortingCriteria::Default; } switch ( sort ) { case SortingCriteria::Artist: req += "art.name"; if ( desc == true ) req += " DESC"; req += ", alb.title "; break; case SortingCriteria::ReleaseDate: if ( desc == true ) req += "release_year DESC, title"; else req += "release_year, title"; break; case SortingCriteria::Duration: req += "duration"; if ( desc == true ) req += " DESC"; break; case SortingCriteria::TrackNumber: req += "nb_tracks"; if ( desc == false ) req += " DESC"; break; 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) "; 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) "; if ( desc == true ) req += "DESC "; break; default: LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Default (Alpha)" ); /* fall-through */ case SortingCriteria::Default: case SortingCriteria::Alpha: req += "title"; if ( desc == true ) req += " DESC"; req += ", art.name"; if ( desc == true ) req += " DESC"; break; } return req; } Query Album::tracks( const QueryParameters* params ) const { return Media::fromAlbum( m_ml, m_id, params, m_publicOnlyListing, nullptr ); } Query Album::tracks( GenrePtr genre, const QueryParameters* params ) const { if ( genre == nullptr ) return {}; return Media::fromAlbum( m_ml, m_id, params, m_publicOnlyListing, genre ); } std::vector Album::cachedTracks() const { if ( m_tracks.empty() == true ) m_tracks = tracks( nullptr )->all(); return m_tracks; } Query Album::searchTracks( const std::string& pattern, const QueryParameters* params ) const { return Media::searchAlbumTracks( m_ml, pattern, m_id, params, m_publicOnlyListing ); } bool Album::addTrack( std::shared_ptr media, unsigned int trackNb, unsigned int discNumber, int64_t artistId, Genre* genre ) { auto duration = media->duration() >= 0 ? media->duration() : 0; if ( media->markAsAlbumTrack( m_id, trackNb, discNumber, artistId, genre ) == false ) return false; if ( genre != nullptr && genre->updateNbTracks( 1 ) == false ) return false; m_nbTracks++; assert( media->isPresent() ); ++m_nbPresentTracks; m_duration += duration; // Don't assume we have always have a valid value in m_tracks. // While it's ok to assume that if we are currently parsing the album, we // have a valid cache tracks, this isn't true when restarting an interrupted parsing. // The nbTracks value will be correct however. If it's equal to one, it means we're inserting // the first track in this album if ( ( m_tracks.empty() == true && m_nbTracks == 1 ) || ( m_tracks.empty() == false && m_nbTracks > 1 ) ) m_tracks.push_back( std::move( media ) ); return true; } bool Album::removeTrack( Media& media ) { /* * Remove references to genre/album/artist from the media table before we * start updating album & genre. * If we don't do this first, if we're removing the last album track we'd * end up with a genre/album being deleted while there's still a foreign key * pointing to it */ if ( media.setSubTypeUnknown() == false ) return false; static const std::string req = "UPDATE " + Table::Name + " SET " "duration = duration - ? " "WHERE id_album = ?"; auto duration = media.duration() >= 0 ? media.duration() : 0; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, duration, m_id ) == false ) return false; auto genre = std::static_pointer_cast( media.genre() ); if ( genre != nullptr && genre->updateNbTracks( -1 ) ) return false; m_duration -= duration; m_nbTracks--; auto it = std::find_if( begin( m_tracks ), end( m_tracks ), [&media]( MediaPtr& m ) { return m->id() == media.id(); }); if ( it != end( m_tracks ) ) m_tracks.erase( it ); return true; } unsigned int Album::nbTracks() const { if ( m_publicOnlyListing == true ) return 0; return m_nbTracks; } uint32_t Album::nbPresentTracks() const { if ( m_publicOnlyListing == true ) return 0; return m_nbPresentTracks; } uint32_t Album::nbDiscs() const { return m_nbDiscs; } bool Album::setNbDiscs( uint32_t nbDiscs ) { static const std::string req = "UPDATE " + Album::Table::Name + " SET nb_discs = ? WHERE id_album = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, nbDiscs, m_id ) == false ) return false; m_nbDiscs = nbDiscs; return true; } int64_t Album::duration() const { return m_duration; } bool Album::isUnknownAlbum() const { return m_title.empty(); } bool Album::isFavorite() const { return m_isFavorite; } bool Album::setFavorite( bool favorite ) { static const std::string req = "UPDATE " + Table::Name + " SET is_favorite = ? WHERE id_album = ?"; if ( m_isFavorite == favorite ) return true; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, favorite, m_id ) == false ) return false; m_isFavorite = favorite; return true; } ArtistPtr Album::albumArtist() const { if ( m_artistId == 0 ) return nullptr; if ( m_albumArtist == nullptr ) m_albumArtist = Artist::fetch( m_ml, m_artistId ); return m_albumArtist; } bool Album::setAlbumArtist( std::shared_ptr artist ) { if ( m_artistId == artist->id() ) return true; if ( artist->id() == 0 ) return false; static const std::string req = "UPDATE " + Table::Name + " SET " "artist_id = ? WHERE id_album = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, artist->id(), m_id ) == false ) return false; m_artistId = artist->id(); m_albumArtist = std::move( artist ); static const std::string ftsReq = "UPDATE " + FtsTable::Name + " SET " " artist = ? WHERE rowid = ?"; return sqlite::Tools::executeUpdate( m_ml->getConn(), ftsReq, m_albumArtist->name(), m_id ); } Query Album::artists( const QueryParameters* params ) const { std::string req = "FROM " + Artist::Table::Name + " art " "INNER JOIN " + Media::Table::Name + " m " "ON m.artist_id = art.id_artist " "WHERE m.album_id = ?"; if ( params != nullptr && ( params->sort != SortingCriteria::Alpha && params->sort != SortingCriteria::Default ) ) LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" ); std::string orderBy = "GROUP BY art.id_artist ORDER BY art.name"; if ( params != nullptr && params->desc == true ) orderBy += " DESC"; if ( ( params != nullptr && params->publicOnly == true ) || m_publicOnlyListing == true ) { req += " AND m.is_public = 1"; } return make_query( m_ml, "art.*", std::move( req ), std::move( orderBy ), m_id ).build(); } void Album::createTable( sqlite::Connection* dbConnection ) { const std::string reqs[] = { schema( Table::Name, Settings::DbModelVersion ), schema( FtsTable::Name, Settings::DbModelVersion ), }; for ( const auto& req : reqs ) sqlite::Tools::executeRequest( dbConnection, req ); } void Album::createTriggers( sqlite::Connection* dbConnection ) { sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::IsPresent, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::DeleteTrack, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::InsertFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::DeleteFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::DeleteEmpty, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::UpdateOnMediaAlbumIdChange, Settings::DbModelVersion ) ); } void Album::createIndexes( sqlite::Connection* dbConnection ) { sqlite::Tools::executeRequest( dbConnection, index( Indexes::ArtistId, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, index( Indexes::NbTracks, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, index( Indexes::Title, Settings::DbModelVersion ) ); } std::string Album::schema( const std::string& tableName, uint32_t dbModel ) { if ( tableName == Table::Name ) { if ( dbModel <= 16 ) { return "CREATE TABLE " + Table::Name + "(" "id_album INTEGER PRIMARY KEY AUTOINCREMENT," "title TEXT COLLATE NOCASE," "artist_id UNSIGNED INTEGER," "release_year UNSIGNED INTEGER," "short_summary TEXT," "thumbnail_id UNSIGNED INT," "nb_tracks UNSIGNED INTEGER DEFAULT 0," "duration UNSIGNED INTEGER NOT NULL DEFAULT 0," "nb_discs UNSIGNED INTEGER NOT NULL DEFAULT 1," "is_present UNSIGNED INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks)," "FOREIGN KEY(artist_id) REFERENCES " + Artist::Table::Name + "(id_artist) ON DELETE CASCADE," "FOREIGN KEY(thumbnail_id) REFERENCES " + Thumbnail::Table::Name + "(id_thumbnail)" ")"; } if ( dbModel < 37 ) { return "CREATE TABLE " + Table::Name + "(" "id_album INTEGER PRIMARY KEY AUTOINCREMENT," "title TEXT COLLATE NOCASE," "artist_id UNSIGNED INTEGER," "release_year UNSIGNED INTEGER," "short_summary TEXT," "nb_tracks UNSIGNED INTEGER DEFAULT 0," "duration UNSIGNED INTEGER NOT NULL DEFAULT 0," "nb_discs UNSIGNED INTEGER NOT NULL DEFAULT 1," "is_present UNSIGNED INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks)," "FOREIGN KEY(artist_id) REFERENCES " + Artist::Table::Name + "(id_artist) ON DELETE CASCADE" ")"; } return "CREATE TABLE " + Table::Name + "(" "id_album INTEGER PRIMARY KEY AUTOINCREMENT," "title TEXT COLLATE NOCASE," "artist_id UNSIGNED INTEGER," "release_year UNSIGNED INTEGER," "short_summary TEXT," // Number of tracks in this album, regardless of their presence // state. "nb_tracks UNSIGNED INTEGER DEFAULT 0," "duration UNSIGNED INTEGER NOT NULL DEFAULT 0," "nb_discs UNSIGNED INTEGER NOT NULL DEFAULT 1," // The album presence state, which is the number of present tracks // in this album "is_present UNSIGNED INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks), " "is_favorite BOOLEAN NOT NULL DEFAULT FALSE," "FOREIGN KEY(artist_id) REFERENCES " + Artist::Table::Name + "(id_artist) ON DELETE CASCADE" ")"; } else if ( tableName == FtsTable::Name ) { return "CREATE VIRTUAL TABLE " + Album::FtsTable::Name + " USING FTS3(title,artist)"; } assert( !"Invalid table name provided" ); return ""; } std::string Album::trigger( Triggers trigger, uint32_t dbModel ) { switch ( trigger ) { case Triggers::IsPresent: { if ( dbModel < 23 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " BEGIN " " UPDATE " + Table::Name + " SET is_present=is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END)" "WHERE id_album = (SELECT album_id FROM " + AlbumTrack::Table::Name + " " "WHERE media_id = new.id_media" ");" " END"; } else if ( dbModel < 34 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " AND old.is_present != new.is_present" " BEGIN " " UPDATE " + Table::Name + " SET is_present=is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END)" "WHERE id_album = (SELECT album_id FROM " + AlbumTrack::Table::Name + " " "WHERE media_id = new.id_media" ");" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " AND old.is_present != new.is_present" " BEGIN " " UPDATE " + Table::Name + " SET is_present=is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END)" "WHERE id_album = new.album_id;" " END"; } case Triggers::AddTrack: { assert( dbModel < 34 ); return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER INSERT ON " + AlbumTrack::Table::Name + " BEGIN" " UPDATE " + Table::Name + " SET duration = duration + new.duration," " nb_tracks = nb_tracks + 1," " is_present = is_present + 1" " WHERE id_album = new.album_id;" " END"; } case Triggers::DeleteTrack: { if ( dbModel < 34 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER DELETE ON " + AlbumTrack::Table::Name + " BEGIN " " UPDATE " + Table::Name + " SET" " nb_tracks = nb_tracks - 1," " is_present = is_present - 1," " duration = duration - old.duration" " WHERE id_album = old.album_id;" " DELETE FROM " + Table::Name + " WHERE id_album=old.album_id AND nb_tracks = 0;" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER DELETE ON " + Media::Table::Name + " WHEN old.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " BEGIN " " UPDATE " + Table::Name + " SET" " nb_tracks = nb_tracks - 1," " is_present = is_present - IIF(old.is_present != 0, 1, 0)," " duration = duration - MAX(old.duration, 0)" " WHERE id_album = old.album_id;" " END"; } case Triggers::InsertFts: { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER INSERT ON " + Table::Name + // Skip unknown albums " WHEN new.title IS NOT NULL" " BEGIN" " INSERT INTO " + FtsTable::Name + "(rowid, title)" " VALUES(new.id_album, new.title);" " END"; } case Triggers::DeleteFts: { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " BEFORE DELETE ON " + Table::Name + // Unknown album probably won't be deleted, but better safe than sorry " WHEN old.title IS NOT NULL" " BEGIN" " DELETE FROM " + FtsTable::Name + " WHERE rowid = old.id_album;" " END"; } case Triggers::DeleteEmpty: { assert( dbModel >= 34 ); return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF nb_tracks ON " + Table::Name + " WHEN new.nb_tracks = 0" " BEGIN " " DELETE FROM " + Table::Name + " WHERE id_album=new.id_album;" " END"; } case Triggers::UpdateOnMediaAlbumIdChange: { assert( dbModel >= 36 ); return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF album_id ON " + Media::Table::Name + " WHEN IFNULL(old.album_id, 0) != IFNULL(new.album_id, 0)" " BEGIN" " UPDATE " + Table::Name + " SET " " is_present = is_present - IIF(old.is_present != 0, 1, 0)," " nb_tracks = nb_tracks - 1," " duration = duration - IIF(old.duration >= 0, old.duration, 0)" " WHERE old.album_id IS NOT NULL AND id_album = old.album_id;" " UPDATE " + Table::Name + " SET " " is_present = is_present + IIF(old.is_present != 0, 1, 0)," " nb_tracks = nb_tracks + 1," " duration = duration + IIF(new.duration >= 0, new.duration, 0)" " WHERE new.album_id IS NOT NULL AND id_album = new.album_id;" " END"; } default: assert( !"Invalid trigger provided" ); } return ""; } std::string Album::triggerName( Album::Triggers trigger, uint32_t dbModel ) { switch ( trigger ) { case Triggers::IsPresent: { if ( dbModel < 23 ) return "is_album_present"; return "album_is_present"; } case Triggers::AddTrack: assert( dbModel < 34 ); return "add_album_track"; case Triggers::DeleteTrack: if ( dbModel < 34 ) return "delete_album_track"; return "album_delete_track"; case Triggers::InsertFts: return "insert_album_fts"; case Triggers::DeleteFts: return "delete_album_fts"; case Triggers::DeleteEmpty: assert( dbModel >= 34 ); return "album_delete_empty"; case Triggers::UpdateOnMediaAlbumIdChange: assert( dbModel >= 36 ); return "album_update_on_media_album_id"; default: assert( !"Invalid trigger provided" ); } return ""; } std::string Album::index( Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::ArtistId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(artist_id)"; case Indexes::NbTracks: assert( dbModel >= 34 ); return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(nb_tracks, is_present)"; case Indexes::Title: assert( dbModel >= 37 ); return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(title)"; } return ""; } std::string Album::indexName( Album::Indexes index, uint32_t dbModel ) { UNUSED_IN_RELEASE( index ); UNUSED_IN_RELEASE( dbModel ); switch ( index ) { case Indexes::ArtistId: return "album_artist_id_idx"; case Indexes::NbTracks: assert( dbModel >= 34 ); return "album_nb_tracks_idx"; case Indexes::Title: assert( dbModel >= 37 ); return "album_title_idx"; } return ""; } bool Album::checkDbModel( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); if ( sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ) == false || sqlite::Tools::checkTableSchema( schema( FtsTable::Name, Settings::DbModelVersion ), FtsTable::Name ) == false ) return false; auto checkIndex = []( Indexes i ) { return sqlite::Tools::checkIndexStatement( index( i, Settings::DbModelVersion ), indexName( i, Settings::DbModelVersion ) ); }; auto check = []( Triggers t ) { return sqlite::Tools::checkTriggerStatement( trigger( t, Settings::DbModelVersion ), triggerName( t, Settings::DbModelVersion ) ); }; return check( Triggers::IsPresent ) && check( Triggers::DeleteTrack ) && check( Triggers::InsertFts ) && check( Triggers::DeleteFts ) && check( Triggers::DeleteEmpty ) && check( Triggers::UpdateOnMediaAlbumIdChange ) && checkIndex( Indexes::ArtistId ) && checkIndex( Indexes::NbTracks ); } std::shared_ptr Album::create( MediaLibraryPtr ml, std::string title ) { auto album = std::make_shared( ml, std::move( title ) ); static const std::string req = "INSERT INTO " + Table::Name + "(id_album, title) VALUES(NULL, ?)"; if ( insert( ml, album, req, album->m_title ) == false ) return nullptr; return album; } std::shared_ptr Album::createUnknownAlbum( MediaLibraryPtr ml, const Artist* artist ) { auto album = std::make_shared( ml, artist ); static const std::string req = "INSERT INTO " + Table::Name + "(id_album, artist_id) VALUES(NULL, ?)"; if ( insert( ml, album, req, artist->id() ) == false ) return nullptr; return album; } Query Album::search( MediaLibraryPtr ml, const std::string& pattern, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " alb "; req += addRequestJoin( params, false ); req += "WHERE id_album IN " "(SELECT rowid FROM " + FtsTable::Name + " WHERE " + FtsTable::Name + " MATCH ?)"; const auto cond = addRequestConditions( params, false ); if ( cond.empty() == false ) req += " AND" + cond; return make_query( ml, "alb.*", std::move( req ), orderBy( params ), sqlite::Tools::sanitizePattern( pattern ) ).build(); } Query Album::searchFromArtist( MediaLibraryPtr ml, const std::string& pattern, int64_t artistId, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " alb "; req += addRequestJoin( params, false ); req += "WHERE id_album IN " "(SELECT rowid FROM " + FtsTable::Name + " WHERE " + FtsTable::Name + " MATCH ?)" " AND artist_id = ?"; const auto cond = addRequestConditions( params, false ); if ( cond.empty() == false ) req += " AND" + cond; return make_query( ml, "alb.*", std::move( req ), orderBy( params ), sqlite::Tools::sanitizePattern( pattern ), artistId ).build(); } Query Album::fromArtist( MediaLibraryPtr ml, int64_t artistId, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " alb " "INNER JOIN " + Media::Table::Name + " m " "ON m.album_id = alb.id_album " "WHERE (m.artist_id = ? OR alb.artist_id = ?)"; if ( params == nullptr || params->includeMissing == false ) req += " AND m.is_present != 0"; if ( params != nullptr && params->publicOnly == true ) req += " AND m.is_public != 0"; std::string groupAndOrder = " GROUP BY m.album_id ORDER BY "; auto sort = params != nullptr ? params->sort : SortingCriteria::Default; auto desc = params != nullptr ? params->desc : false; switch ( sort ) { case SortingCriteria::Alpha: groupAndOrder += "title"; if ( desc == true ) groupAndOrder += " DESC"; break; default: LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Default (ReleaseDate)" ); /* fall-through */ case SortingCriteria::Default: case SortingCriteria::ReleaseDate: // When listing albums of an artist, default order is by descending year (with album title // discrimination in case 2+ albums went out the same year) // This leads to DESC being used for "non-desc" case if ( desc == true ) groupAndOrder += "release_year, title"; else groupAndOrder += "release_year DESC, title"; break; } return make_query( ml, "alb.*", std::move( req ), std::move( groupAndOrder ), artistId, artistId ).build(); } Query Album::fromGenre( MediaLibraryPtr ml, int64_t genreId, const QueryParameters* params, bool forcePublic ) { std::string req = "FROM " + Table::Name + " alb "; req += addRequestJoin( params, true ); req += "WHERE m.genre_id = ?"; 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( ml, "alb.*", std::move( req ), std::move( groupAndOrderBy ), genreId ).build(); } Query Album::searchFromGenre( MediaLibraryPtr ml, const std::string& pattern, int64_t genreId, const QueryParameters* params, bool forcePublic ) { std::string req = "FROM " + Table::Name + " alb "; req += addRequestJoin( params, true ); req += "WHERE id_album IN " "(SELECT rowid FROM " + FtsTable::Name + " WHERE " + FtsTable::Name + " MATCH ?)" "AND m.genre_id = ?"; 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( ml, "alb.*", std::move( req ), std::move( groupAndOrderBy ), sqlite::Tools::sanitizePattern( pattern ), genreId ).build(); } Query Album::listAll( MediaLibraryPtr ml, const QueryParameters* params ) { std::string req = " FROM " + Table::Name + " alb "; req += addRequestJoin( params, false ); const auto cond = addRequestConditions( params, false ); if ( cond.empty() == false ) { req += "WHERE" + cond; } const auto publicOnly = params != nullptr && params->publicOnly; return make_query( ml, "alb.*", std::move( req ), orderBy( params ) ) .markPublic( publicOnly ).build(); } bool Album::checkDBConsistency( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); sqlite::Statement stmt{ "SELECT nb_tracks, is_present FROM " + Album::Table::Name }; stmt.execute(); sqlite::Row row; while ( ( row = stmt.row() ) != nullptr ) { auto nbTracks = row.extract(); auto isPresent = row.extract(); if ( nbTracks != isPresent ) return false; } return true; } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Album.h000066400000000000000000000207251501065546500236370ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifndef ALBUM_H #define ALBUM_H #include #include "medialibrary/IMediaLibrary.h" #include "database/DatabaseHelpers.h" #include "medialibrary/IAlbum.h" #include "Thumbnail.h" namespace medialibrary { class AlbumTrack; class Artist; class Media; class Album : public IAlbum, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t Album::*const PrimaryKey; }; struct FtsTable { static const std::string Name; }; enum class Triggers : uint8_t { IsPresent, AddTrack, // Deprecated in model 34 DeleteTrack, InsertFts, DeleteFts, DeleteEmpty, // Introduced in model 34 UpdateOnMediaAlbumIdChange, // Introduced in model 36 }; enum class Indexes : uint8_t { ArtistId, NbTracks, Title, }; Album( MediaLibraryPtr ml, sqlite::Row& row ); Album( MediaLibraryPtr ml, std::string title ); /** * @brief Album Constructs an unknown album for the provided artist */ Album( MediaLibraryPtr ml, const Artist* artist ); virtual int64_t id() const override; virtual const std::string& title() const override; virtual unsigned int releaseYear() const override; /** * @brief setReleaseYear Updates the release year * @param date The desired date. * @param force If force is true, the date will be applied no matter what. * If force is false, the date will be applied if it never was * applied before. Otherwise, setReleaseYear() will restore the release * date to 0. * @return */ bool setReleaseYear( unsigned int date, bool force ); virtual const std::string& shortSummary() const override; bool setShortSummary( const std::string& summary ); virtual ThumbnailStatus thumbnailStatus( ThumbnailSizeType sizeType ) const override; virtual const std::string& thumbnailMrl( ThumbnailSizeType sizeType ) const override; std::shared_ptr thumbnail( ThumbnailSizeType sizeType ) const; bool setThumbnail(std::shared_ptr thumbnail ); virtual Query tracks( const QueryParameters* params ) const override; virtual Query tracks( GenrePtr genre, const QueryParameters* params ) const override; /// /// \brief cachedTracks Returns a cached list of tracks /// This has no warranty of ordering, validity, or anything else. /// \return An unordered-list of this album's tracks /// std::vector cachedTracks() const; /// /// \brief addTrack Add a track to the album. /// This will modify the media, but *not* save it. /// The media will be added to the tracks cache. /// bool addTrack( std::shared_ptr media, unsigned int trackNb, unsigned int discNumber, int64_t artistId, Genre* genre ); bool removeTrack( Media& media ); unsigned int nbTracks() const override; uint32_t nbPresentTracks() const override; virtual uint32_t nbDiscs() const override; bool setNbDiscs( uint32_t nbDiscs ); virtual int64_t duration() const override; virtual bool isUnknownAlbum() const override; virtual bool isFavorite() const override; virtual bool setFavorite( bool ) override; virtual ArtistPtr albumArtist() const override; bool setAlbumArtist( std::shared_ptr artist ); virtual Query artists( const QueryParameters* params ) const override; virtual Query searchTracks( const std::string& pattern, const QueryParameters* params = nullptr ) const override; static void createTable( sqlite::Connection* dbConnection ); static void createTriggers( sqlite::Connection* dbConnection ); static void createIndexes( sqlite::Connection* dbConnection ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static std::string trigger( Triggers trigger, uint32_t dbModel ); static std::string triggerName(Triggers trigger , uint32_t dbModel); static std::string index( Indexes index, uint32_t dbModel ); static std::string indexName( Indexes index, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr create( MediaLibraryPtr ml, std::string title ); static std::shared_ptr createUnknownAlbum( MediaLibraryPtr ml, const Artist* artist ); /// /// \brief search search for an album, through its albumartist or title /// \param pattern A pattern representing the title, or the name of the main artist /// \return /// static Query search( MediaLibraryPtr ml, const std::string& pattern, const QueryParameters* params ); static Query searchFromArtist( MediaLibraryPtr ml, const std::string& pattern, int64_t artistId, const QueryParameters* params ); static Query fromArtist( MediaLibraryPtr ml, int64_t artistId, const QueryParameters* params ); static Query fromGenre( MediaLibraryPtr ml, int64_t genreId, const QueryParameters* params, bool forcePublic ); static Query searchFromGenre(MediaLibraryPtr ml, const std::string& pattern, int64_t genreId, const QueryParameters* params , bool forcePublic); static Query listAll( MediaLibraryPtr ml, const QueryParameters* params ); /** * @brief checkDBConsistency Checks the consistency of all artists records * @return false in case of an inconsistency * * This is meant for testing only, and expected all devices to be back to * present once this is called */ static bool checkDBConsistency( MediaLibraryPtr ml ); 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 ); protected: MediaLibraryPtr m_ml; int64_t m_id; const std::string m_title; int64_t m_artistId; unsigned int m_releaseYear; std::string m_shortSummary; unsigned int m_nbTracks; int64_t m_duration; uint32_t m_nbDiscs; uint32_t m_nbPresentTracks; bool m_publicOnlyListing; bool m_isFavorite; mutable std::vector m_tracks; mutable std::shared_ptr m_albumArtist; mutable std::shared_ptr m_thumbnails[Thumbnail::SizeToInt( ThumbnailSizeType::Count )]; }; } #endif // ALBUM_H medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Artist.cpp000066400000000000000000001053251501065546500244000ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "Artist.h" #include "Album.h" #include "Media.h" #include "utils/Enums.h" #include "utils/ModificationsNotifier.h" #include "Deprecated.h" #include "database/SqliteTools.h" #include "database/SqliteQuery.h" namespace medialibrary { const std::string Artist::Table::Name = "Artist"; const std::string Artist::Table::PrimaryKeyColumn = "id_artist"; int64_t Artist::*const Artist::Table::PrimaryKey = &Artist::m_id; const std::string Artist::FtsTable::Name = "ArtistFts"; const std::string Artist::MediaRelationTable::Name = "MediaArtistRelation"; Artist::Artist( MediaLibraryPtr ml, sqlite::Row& row ) : m_ml( ml ) , m_id( row.extract() ) , m_name( row.extract() ) , m_shortBio( row.extract() ) , m_nbAlbums( row.extract() ) , m_nbTracks( row.extract() ) , m_mbId( row.extract() ) , m_nbPresentTracks( row.extract() ) , m_isFavorite( row.extract() ) { if ( row.hasRemainingColumns() == true ) m_publicOnlyListing = row.extract(); else m_publicOnlyListing = false; assert( row.hasRemainingColumns() == false ); } Artist::Artist( MediaLibraryPtr ml, std::string name ) : m_ml( ml ) , m_id( 0 ) , m_name( std::move( name ) ) , m_nbAlbums( 0 ) , m_nbTracks( 0 ) , m_nbPresentTracks( 0 ) , m_publicOnlyListing( false ) , m_isFavorite( false ) { } int64_t Artist::id() const { return m_id; } const std::string& Artist::name() const { return m_name; } const std::string& Artist::shortBio() const { return m_shortBio; } bool Artist::setShortBio(const std::string& shortBio) { static const std::string req = "UPDATE " + Table::Name + " SET shortbio = ? WHERE id_artist = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, shortBio, m_id ) == false ) return false; m_shortBio = shortBio; return true; } Query Artist::albums( const QueryParameters* params ) const { return Album::fromArtist( m_ml, m_id, params ); } Query Artist::searchAlbums( const std::string& pattern, const QueryParameters* params ) const { return Album::searchFromArtist( m_ml, pattern, m_id, params ); } Query Artist::tracks( const QueryParameters* params ) const { return Media::fromArtist( m_ml, m_id, params, m_publicOnlyListing ); } Query Artist::searchTracks( const std::string& pattern, const QueryParameters* params ) const { return Media::searchArtistTracks( m_ml, pattern, m_id, params, m_publicOnlyListing ); } bool Artist::addMedia( const Media& media ) { static const std::string req = "INSERT INTO " + MediaRelationTable::Name + " VALUES(?, ?)"; if ( sqlite::Tools::executeInsert( m_ml->getConn(), req, media.id(), m_id ) == 0 ) return false; ++m_nbTracks; return true; } ThumbnailStatus Artist::thumbnailStatus( ThumbnailSizeType sizeType ) const { auto t = thumbnail( sizeType ); if ( t == nullptr ) return ThumbnailStatus::Missing; return t->status(); } const std::string& Artist::thumbnailMrl( ThumbnailSizeType sizeType ) const { const auto t = thumbnail( sizeType ); if ( t == nullptr ) return Thumbnail::EmptyMrl; return t->mrl(); } std::shared_ptr Artist::thumbnail( ThumbnailSizeType sizeType ) const { auto idx = Thumbnail::SizeToInt( sizeType ); if ( m_thumbnails[idx] == nullptr ) { auto thumbnail = Thumbnail::fetch( m_ml, Thumbnail::EntityType::Artist, m_id, sizeType ); if ( thumbnail == nullptr ) return nullptr; m_thumbnails[idx] = std::move( thumbnail ); } return m_thumbnails[idx]; } bool Artist::shouldUpdateThumbnail( const Thumbnail& currentThumbnail ) { /* * Regardless of the origin, we only want to update an artist thumbnail if * it isn't shared. * Artists don't have thumbnail of their own (yet), and will only rely on * other entities providing thumbnail for them (albums or media), so they * are extremely likely to be shared. * If we are updating this specific artist thumbnail, we do not want to * update the album or media we originally got the thumbnail from */ return currentThumbnail.isShared() == false; } std::string Artist::addRequestJoin(const QueryParameters* params) { auto sort = params != nullptr ? params->sort : SortingCriteria::Default; switch ( sort ) { default: return std::string{}; case SortingCriteria::LastPlaybackDate: return std::string{ " INNER JOIN " } + MediaRelationTable::Name + " mrt ON art.id_artist = mrt.artist_id" " INNER JOIN " + Media::Table::Name + " m ON mrt.media_id = m.id_media"; } } 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 newThumbnail ) { assert( newThumbnail != nullptr ); auto thumbnailIdx = Thumbnail::SizeToInt( newThumbnail->sizeType() ); auto currentThumbnail = thumbnail( newThumbnail->sizeType() ); currentThumbnail = Thumbnail::updateOrReplace( m_ml, currentThumbnail, std::move( newThumbnail ), Artist::shouldUpdateThumbnail, m_id, Thumbnail::EntityType::Artist ); if ( currentThumbnail == nullptr ) return false; m_thumbnails[thumbnailIdx] = std::move( currentThumbnail ); auto notifier = m_ml->getNotifier(); if ( notifier != nullptr ) notifier->notifyArtistModification( m_id ); return true; } bool Artist::setThumbnail( const std::string& thumbnailMrl, ThumbnailSizeType sizeType ) { return setThumbnail( std::make_shared( m_ml, thumbnailMrl, Thumbnail::Origin::UserProvided, sizeType, false ) ); } std::shared_ptr Artist::unknownAlbum() { static const std::string req = "SELECT * FROM " + Album::Table::Name + " WHERE artist_id = ? AND title IS NULL"; return Album::fetch( m_ml, req, m_id ); } std::shared_ptr Artist::createUnknownAlbum() { auto alb = Album::createUnknownAlbum( m_ml, this ); if ( alb == nullptr ) return nullptr; m_nbAlbums++; return alb; } const std::string& Artist::musicBrainzId() const { return m_mbId; } bool Artist::setMusicBrainzId( const std::string& mbId ) { static const std::string req = "UPDATE " + Table::Name + " SET mb_id = ? WHERE id_artist = ?"; if ( mbId == m_mbId ) return true; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, mbId, m_id ) == false ) return false; m_mbId = mbId; return true; } unsigned int Artist::nbAlbums() const { if ( m_publicOnlyListing == true ) return 0; return m_nbAlbums; } unsigned int Artist::nbTracks() const { if ( m_publicOnlyListing == true ) return 0; return m_nbTracks; } unsigned int Artist::nbPresentTracks() const { if ( m_publicOnlyListing == true ) return 0; return m_nbPresentTracks; } bool Artist::isFavorite() const { return m_isFavorite; } bool Artist::setFavorite( bool favorite ) { static const std::string req = "UPDATE " + Table::Name + " SET is_favorite = ? WHERE id_artist = ?"; if ( m_isFavorite == favorite ) return true; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, favorite, m_id ) == false ) return false; m_isFavorite = favorite; return true; } void Artist::createTable( sqlite::Connection* dbConnection ) { const std::string reqs[] = { schema( Table::Name, Settings::DbModelVersion ), schema( FtsTable::Name, Settings::DbModelVersion ), schema( MediaRelationTable::Name, Settings::DbModelVersion ), }; for ( const auto& req : reqs ) sqlite::Tools::executeRequest( dbConnection, req ); } void Artist::createTriggers( sqlite::Connection* dbConnection ) { sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::HasTrackPresent, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::InsertFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::DeleteFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::DeleteArtistsWithoutTracks, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::IncrementNbTracks, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::DecrementNbTracks, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::UpdateNbAlbums, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::DecrementNbAlbums, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, trigger( Triggers::IncrementNbAlbums, Settings::DbModelVersion ) ); } void Artist::createIndexes(sqlite::Connection* dbConnection) { sqlite::Tools::executeRequest( dbConnection, index( Indexes::MediaRelArtistId, Settings::DbModelVersion ) ); } std::string Artist::schema( const std::string& tableName, uint32_t dbModelVersion ) { if ( tableName == FtsTable::Name ) { return "CREATE VIRTUAL TABLE " + FtsTable::Name + " USING FTS3(name)"; } else if ( tableName == MediaRelationTable::Name ) { return "CREATE TABLE " + MediaRelationTable::Name + "(" "media_id INTEGER NOT NULL," "artist_id INTEGER," "PRIMARY KEY(media_id,artist_id)," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media) ON DELETE CASCADE," "FOREIGN KEY(artist_id) REFERENCES " + Table::Name + "(" + Table::PrimaryKeyColumn + ") ON DELETE CASCADE" ")"; } assert( tableName == Table::Name ); if ( dbModelVersion <= 16 ) { return "CREATE TABLE " + Table::Name + "(" "id_artist INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL," "shortbio TEXT," "thumbnail_id TEXT," "nb_albums UNSIGNED INT DEFAULT 0," "nb_tracks UNSIGNED INT DEFAULT 0," "mb_id TEXT," "is_present UNSIGNED INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks)," "FOREIGN KEY(thumbnail_id) REFERENCES " + Thumbnail::Table::Name + "(id_thumbnail)" ")"; } if ( dbModelVersion < 37 ) { return "CREATE TABLE " + Table::Name + "(" "id_artist INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL," "shortbio TEXT," "nb_albums UNSIGNED INT DEFAULT 0," "nb_tracks UNSIGNED INT DEFAULT 0," "mb_id TEXT," "is_present UNSIGNED INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks)" ")"; } return "CREATE TABLE " + Table::Name + "(" "id_artist INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL," "shortbio TEXT," // Contains the number of albums where this artist is the album // artist. Some or all albums might not be present. "nb_albums UNSIGNED INT DEFAULT 0," // Contains the number of tracks by this artist, regardless of // the album they appear on. Some or all tracks might not be present "nb_tracks UNSIGNED INT DEFAULT 0," "mb_id TEXT," // A presence flag, that represents the number of present tracks. // This is not linked to the album presence at all. // An artist of which all tracks are not present is considered // not present, even if one of its album contains a present // track from another artist (the album will be present, however) "is_present UNSIGNED INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks), " "is_favorite BOOLEAN NOT NULL DEFAULT FALSE" ")"; } std::string Artist::trigger( Triggers trigger, uint32_t dbModelVersion ) { switch ( trigger ) { case Triggers::HasTrackPresent: { if ( dbModelVersion < 23 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " BEGIN " " UPDATE " + Table::Name + " SET is_present=is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END)" "WHERE id_artist = (SELECT artist_id FROM " + AlbumTrack::Table::Name + " " " WHERE media_id = new.id_media " ");" " END"; } if ( dbModelVersion < 34 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " AND old.is_present != new.is_present" " BEGIN " " UPDATE " + Table::Name + " SET is_present=is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END)" "WHERE id_artist = (SELECT artist_id FROM " + AlbumTrack::Table::Name + " " " WHERE media_id = new.id_media " ");" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " AND old.is_present != new.is_present" " BEGIN " " UPDATE " + Table::Name + " SET is_present=is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END)" "WHERE id_artist = new.artist_id;" " END"; } case Triggers::HasAlbumRemaining: { assert( dbModelVersion < 23 ); // 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 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 // artist album return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER DELETE ON " + Album::Table::Name + " WHEN old.artist_id != " + std::to_string( UnknownArtistID ) + " AND old.artist_id != " + std::to_string( VariousArtistID ) + " BEGIN" " UPDATE " + Table::Name + " SET nb_albums = nb_albums - 1" " WHERE id_artist = old.artist_id;" " DELETE FROM " + Table::Name + " WHERE id_artist = old.artist_id AND nb_albums = 0" " AND nb_tracks = 0;" " END"; } case Triggers::DeleteArtistsWithoutTracks: { if ( dbModelVersion >= 8 && dbModelVersion < 23 ) { // Don't create this trigger if the database is about to be migrated. // This could make earlier migration fail, and needs to be done when // migrating to v7 to v8. // While the has_album_remaining trigger now also references the nb_tracks // field, it was present from before version 3, so it wouldn't be recreated. // As we don't support any model before 3 (or rather we just recreate // everything), we don't have to bother here. return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER DELETE ON " + AlbumTrack::Table::Name + " WHEN old.artist_id != " + std::to_string( UnknownArtistID ) + " AND old.artist_id != " + std::to_string( VariousArtistID ) + " BEGIN" " UPDATE " + Table::Name + " SET" " nb_tracks = nb_tracks - 1," " is_present = is_present - 1" " WHERE id_artist = old.artist_id;" " DELETE FROM " + Table::Name + " WHERE id_artist = old.artist_id " " AND nb_albums = 0 " " AND nb_tracks = 0;" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER UPDATE OF nb_tracks, nb_albums ON " + Table::Name + " WHEN new.nb_tracks = 0 AND new.nb_albums = 0" " AND new.id_artist != " + std::to_string( UnknownArtistID ) + " AND new.id_artist != " + std::to_string( VariousArtistID ) + " BEGIN" " DELETE FROM " + Table::Name + " WHERE id_artist = old.id_artist;" " END"; } case Triggers::IncrementNbTracks: { assert( dbModelVersion >= 23 ); return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER INSERT ON " + MediaRelationTable::Name + " BEGIN" " UPDATE " + Table::Name + " SET nb_tracks = nb_tracks + 1, is_present = is_present + 1" " WHERE id_artist = new.artist_id;" " END"; } case Triggers::DecrementNbTracks: { assert( dbModelVersion >= 23 ); return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER DELETE ON " + MediaRelationTable::Name + " BEGIN" " UPDATE " + Table::Name + " SET nb_tracks = nb_tracks - 1, is_present = is_present - 1" " WHERE id_artist = old.artist_id;" " END"; } case Triggers::UpdateNbAlbums: { assert( dbModelVersion >= 23 ); return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER UPDATE OF artist_id ON " + Album::Table::Name + " BEGIN" " UPDATE " + Table::Name + " SET nb_albums = nb_albums + 1" " WHERE id_artist = new.artist_id;" // Even if this is the first update, the old value will be NULL // and won't update anything " UPDATE " + Table::Name + " SET nb_albums = nb_albums - 1" " WHERE id_artist = old.artist_id;" " END"; } case Triggers::DecrementNbAlbums: { assert( dbModelVersion >= 23 ); return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER DELETE ON " + Album::Table::Name + " BEGIN" " UPDATE " + Table::Name + " SET nb_albums = nb_albums - 1" " WHERE id_artist = old.artist_id;" " END"; } case Triggers::IncrementNbAlbums: { assert( dbModelVersion >= 23 ); return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER INSERT ON " + Album::Table::Name + " WHEN new.artist_id IS NOT NULL" " BEGIN" " UPDATE " + Table::Name + " SET nb_albums = nb_albums + 1" " WHERE id_artist = new.artist_id;" " END"; } case Triggers::InsertFts: { return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " AFTER INSERT ON " + Table::Name + " WHEN new.name IS NOT NULL" " BEGIN" " INSERT INTO " + FtsTable::Name + "(rowid,name)" " VALUES(new.id_artist, new.name);" " END"; } case Triggers::DeleteFts: { return "CREATE TRIGGER " + triggerName( trigger, dbModelVersion ) + " BEFORE DELETE ON " + Table::Name + " WHEN old.name IS NOT NULL" " BEGIN" " DELETE FROM " + FtsTable::Name + " WHERE rowid=old.id_artist;" " END"; } default: assert( !"Invalid trigger provided" ); } return ""; } std::string Artist::triggerName( Triggers trigger, uint32_t dbModelVersion ) { switch ( trigger ) { case Triggers::HasTrackPresent: { if ( dbModelVersion < 23 ) return "has_tracks_present"; return "artist_has_tracks_present"; } case Triggers::HasAlbumRemaining: return "has_album_remaining"; case Triggers::DeleteArtistsWithoutTracks: { if ( dbModelVersion >= 8 && dbModelVersion < 23 ) return "has_track_remaining"; return "delete_artist_without_tracks"; } case Triggers::IncrementNbTracks: return "artist_increment_nb_tracks"; case Triggers::DecrementNbTracks: return "artist_decrement_nb_tracks"; case Triggers::UpdateNbAlbums: return "artist_update_nb_albums"; case Triggers::DecrementNbAlbums: return "artist_decrement_nb_albums"; case Triggers::IncrementNbAlbums: return "artist_increment_nb_albums_unknown_album"; case Triggers::InsertFts: return "insert_artist_fts"; case Triggers::DeleteFts: return "delete_artist_fts"; default: assert( !"Invalid trigger provided" ); } return ""; } std::string Artist::index( Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::MediaRelArtistId: assert( dbModel >= 34 ); return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + MediaRelationTable::Name + "(artist_id)"; } return ""; } std::string Artist::indexName( Indexes index, uint32_t dbModel ) { UNUSED_IN_RELEASE( dbModel ); switch ( index ) { case Indexes::MediaRelArtistId: assert( dbModel >= 34 ); return "artist_media_rel_artist_id_idx"; } return ""; } bool Artist::checkDbModel(MediaLibraryPtr ml) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); if ( sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ) == false || sqlite::Tools::checkTableSchema( schema( FtsTable::Name, Settings::DbModelVersion ), FtsTable::Name ) == false || sqlite::Tools::checkTableSchema( schema( MediaRelationTable::Name, Settings::DbModelVersion ), MediaRelationTable::Name ) == false ) return false; auto check = []( Triggers t ) { return sqlite::Tools::checkTriggerStatement( trigger( t, Settings::DbModelVersion ), triggerName( t, Settings::DbModelVersion ) ); }; auto checkIndex = []( Indexes i ) { return sqlite::Tools::checkIndexStatement( index( i, Settings::DbModelVersion ), indexName( i, Settings::DbModelVersion ) ); }; return check( Triggers::HasTrackPresent ) && check( Triggers::DeleteArtistsWithoutTracks ) && check( Triggers::IncrementNbTracks ) && check( Triggers::DecrementNbTracks ) && check( Triggers::UpdateNbAlbums ) && check( Triggers::DecrementNbAlbums ) && check( Triggers::IncrementNbAlbums ) && check( Triggers::InsertFts ) && check( Triggers::DeleteFts ) && checkIndex( Indexes::MediaRelArtistId ); } bool Artist::createDefaultArtists( sqlite::Connection* dbConnection ) { static const std::string req = "INSERT INTO " + Table::Name + "(id_artist) VALUES(?),(?)"; return sqlite::Tools::executeInsert( dbConnection, req, UnknownArtistID, VariousArtistID ) != 0; } std::shared_ptr Artist::create( MediaLibraryPtr ml, std::string name ) { auto artist = std::make_shared( ml, std::move( name ) ); static const std::string req = "INSERT INTO " + Table::Name + "(id_artist, name) VALUES(NULL, ?)"; if ( insert( ml, artist, req, artist->m_name ) == false ) return nullptr; return artist; } Query Artist::search( MediaLibraryPtr ml, const std::string& name, ArtistIncluded included, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " art"; req += addRequestJoin( params ); req += " WHERE id_artist IN " "(SELECT rowid FROM " + FtsTable::Name + " WHERE name MATCH ?)"; 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 if ( included == ArtistIncluded::AlbumArtistOnly ) req += " AND art.nb_albums > 0"; else req += " AND art.nb_tracks > 0"; return make_query( ml, "art.*", std::move( req ), sortRequest( params ), sqlite::Tools::sanitizePattern( name ) ).build(); } Query Artist::listAll( MediaLibraryPtr ml, ArtistIncluded included, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " art"; req += addRequestJoin( params ); req += " WHERE "; auto publicOnly = params != nullptr && params->publicOnly == true; if ( publicOnly == true ) { if ( included != ArtistIncluded::All ) LOG_WARN( "Filtering by album artist is unsupported for public listing" ); req += " EXISTS(SELECT artist_id FROM " + Media::Table::Name + " WHERE artist_id = art.id_artist AND is_public != 0)"; } else { if ( included == ArtistIncluded::AlbumArtistOnly ) req += "art.nb_albums > 0"; else req += "art.nb_tracks > 0"; } req += addRequestConditions( params ); return make_query( ml, "art.*", std::move( req ), sortRequest( params ) ) .markPublic( publicOnly ).build(); } Query 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( ml, "art.*", std::move( req ), std::move( groupAndOrderBy ), genreId ) .markPublic( publicOnly ) .build(); } Query Artist::searchByGenre( MediaLibraryPtr ml, const std::string& pattern, const QueryParameters* params, int64_t genreId, bool forcePublic ) { 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 += addRequestConditions( params ); std::string groupBy = " 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 ) groupBy += " DESC"; } return make_query( ml, "art.*", std::move( req ), std::move( groupBy ), sqlite::Tools::sanitizePattern( pattern ), genreId ) .build(); } bool Artist::dropMediaArtistRelation( MediaLibraryPtr ml, int64_t mediaId ) { const std::string req = "DELETE FROM " + MediaRelationTable::Name + " WHERE media_id = ?"; return sqlite::Tools::executeDelete( ml->getConn(), req, mediaId ); } std::string Artist::sortRequest( const QueryParameters* params ) { auto sort = params != nullptr ? params->sort : SortingCriteria::Default; auto desc = params != nullptr ? params->desc : false; std::string req = " ORDER BY "; if ( params != nullptr && params->publicOnly == true && ( sort == SortingCriteria::TrackNumber || sort == SortingCriteria::NbAlbum ) ) { LOG_WARN( "Unsupported sort for public entities. Falling back to Default" ); sort = SortingCriteria::Default; } switch ( sort ) { default: LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" ); /* fall-through */ case SortingCriteria::Default: case SortingCriteria::Alpha: req += "art.name"; break; case SortingCriteria::NbAlbum: req += "art.nb_albums"; break; case SortingCriteria::TrackNumber: req += "art.nb_tracks"; break; case SortingCriteria::LastPlaybackDate: req = " GROUP BY art.id_artist" " ORDER BY MAX(IFNULL(m.last_played_date, 0))"; break; } if ( desc == true ) req += " DESC"; return req; } bool Artist::checkDBConsistency( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); sqlite::Statement stmt{ "SELECT nb_tracks, is_present FROM " + Table::Name }; stmt.execute(); sqlite::Row row; while ( ( row = stmt.row() ) != nullptr ) { auto nbTracks = row.extract(); auto isPresent = row.extract(); if ( nbTracks != isPresent ) return false; } return true; } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Artist.h000066400000000000000000000175131501065546500240460ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "database/DatabaseHelpers.h" #include "medialibrary/IArtist.h" #include "medialibrary/IMediaLibrary.h" #include "Thumbnail.h" namespace medialibrary { class Album; class Media; class Artist : public IArtist, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t Artist::*const PrimaryKey; }; struct FtsTable { static const std::string Name; }; struct MediaRelationTable { static const std::string Name; }; enum class Triggers : uint8_t { HasTrackPresent, HasAlbumRemaining, DeleteArtistsWithoutTracks, IncrementNbTracks, DecrementNbTracks, UpdateNbAlbums, DecrementNbAlbums, IncrementNbAlbums, InsertFts, DeleteFts, }; enum class Indexes : uint8_t { MediaRelArtistId, }; Artist( MediaLibraryPtr ml, sqlite::Row& row ); Artist( MediaLibraryPtr ml, std::string name ); virtual int64_t id() const override; virtual const std::string& name() const override; virtual const std::string& shortBio() const override; bool setShortBio( const std::string& shortBio ); virtual Query albums( const QueryParameters* params ) const override; virtual Query searchAlbums( const std::string& pattern, const QueryParameters* params = nullptr ) const override; virtual Query tracks( const QueryParameters* params ) const override; virtual Query searchTracks( const std::string& pattern, const QueryParameters* params = nullptr ) const override; bool addMedia( const Media& tracks ); virtual ThumbnailStatus thumbnailStatus( ThumbnailSizeType sizeType ) const override; virtual const std::string& thumbnailMrl( ThumbnailSizeType sizeType ) const override; std::shared_ptr thumbnail( ThumbnailSizeType sizeType ) const; /** * @brief setThumbnail Set a new thumbnail for this artist. * @param newThumbnail A thumbnail object. It can be already inserted in DB or not * @param origin The origin for this thumbnail * @return true if the thumbnail was updated (or wilfully ignored), false in case of failure * * The origin parameter is there in case the thumbnail was create for an entity * but shared through another one. * 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 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. */ bool setThumbnail( std::shared_ptr newThumbnail ); virtual bool setThumbnail( const std::string& thumbnailMrl, ThumbnailSizeType sizeType ) override; std::shared_ptr unknownAlbum(); std::shared_ptr createUnknownAlbum(); virtual const std::string& musicBrainzId() const override; bool setMusicBrainzId( const std::string& musicBrainzId ); virtual unsigned int nbAlbums() const override; virtual unsigned int nbTracks() const override; virtual unsigned int nbPresentTracks() const override; virtual bool isFavorite() const override; virtual bool setFavorite( bool ) override; static void createTable( sqlite::Connection* dbConnection ); static void createTriggers( sqlite::Connection* dbConnection ); static void createIndexes( sqlite::Connection* dbConnection ); static std::string schema( const std::string& tableName, uint32_t dbModelVersion ); static std::string trigger( Triggers trigger, uint32_t dbModelVersion ); static std::string triggerName( Triggers trigger, uint32_t dbModelVersion ); static std::string index( Indexes index, uint32_t dbModel ); static std::string indexName( Indexes index, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static bool createDefaultArtists( sqlite::Connection* dbConnection ); static std::shared_ptr create( MediaLibraryPtr ml, std::string name ); static Query search( MediaLibraryPtr ml, const std::string& name, ArtistIncluded included, const QueryParameters* params ); static Query listAll( MediaLibraryPtr ml, ArtistIncluded included, const QueryParameters* params ); static Query fromGenre( MediaLibraryPtr ml, int64_t genreId, const QueryParameters* params, bool forcePublic ); static Query searchByGenre( MediaLibraryPtr ml, const std::string& pattern, const QueryParameters* params, int64_t genreId, bool forcePublic ); /** * @brief dropMediaArtistRelation Drops any relation between a media and N artists * @param ml A media library instance * @param mediaId The media to drop the relation with * * This is intended to remove any link between media & artist(s) when none * of them is deleted. When any of those 2 entities is deleted, any relation * will automatically get dropped. * Effectively, this is meant to be used when refreshing a media, since we * can't delete it, at the risk of dropping it from any playlist, and we * won't delete an artist when a media gets updated. */ static bool dropMediaArtistRelation( MediaLibraryPtr ml, int64_t mediaId ); /** * @brief checkDBConsistency Checks the consistency of all artists records * @return false in case of an inconsistency * * This is meant for testing only, and expected all devices to be back to * present once this is called */ static bool checkDBConsistency( MediaLibraryPtr ml ); 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; int64_t m_id; const std::string m_name; std::string m_shortBio; unsigned int m_nbAlbums; unsigned int m_nbTracks; std::string m_mbId; unsigned int m_nbPresentTracks; bool m_publicOnlyListing; bool m_isFavorite; mutable std::shared_ptr m_thumbnails[Thumbnail::SizeToInt( ThumbnailSizeType::Count )]; friend struct Artist::Table; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/AudioTrack.cpp000066400000000000000000000223601501065546500251550ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "AudioTrack.h" #include "Media.h" #include "database/SqliteQuery.h" #include "File.h" namespace medialibrary { const std::string AudioTrack::Table::Name = "AudioTrack"; const std::string AudioTrack::Table::PrimaryKeyColumn = "id_track"; int64_t AudioTrack::* const AudioTrack::Table::PrimaryKey = &AudioTrack::m_id; AudioTrack::AudioTrack( MediaLibraryPtr, sqlite::Row& row ) : m_id( row.extract() ) , m_codec( row.extract() ) , m_bitrate( row.extract() ) , m_sampleRate( row.extract() ) , m_nbChannels( row.extract() ) , m_language( row.extract() ) , m_description( row.extract() ) , m_mediaId( row.extract() ) , m_attachedFileId( row.extract() ) { assert( row.hasRemainingColumns() == false ); } AudioTrack::AudioTrack( MediaLibraryPtr, std::string codec, unsigned int bitrate, unsigned int sampleRate, unsigned int nbChannels, std::string language, std::string desc, int64_t mediaId, int64_t attachedFileId ) : m_id( 0 ) , m_codec( std::move( codec ) ) , m_bitrate( bitrate ) , m_sampleRate( sampleRate ) , m_nbChannels( nbChannels ) , m_language( std::move( language ) ) , m_description( std::move( desc ) ) , m_mediaId( mediaId ) , m_attachedFileId( attachedFileId ) { } int64_t AudioTrack::id() const { return m_id; } const std::string&AudioTrack::codec() const { return m_codec; } unsigned int AudioTrack::bitrate() const { return m_bitrate; } unsigned int AudioTrack::sampleRate() const { return m_sampleRate; } unsigned int AudioTrack::nbChannels() const { return m_nbChannels; } const std::string& AudioTrack::language() const { return m_language; } const std::string& AudioTrack::description() const { return m_description; } bool AudioTrack::isInAttachedFile() const { return m_attachedFileId != 0; } void AudioTrack::createTable( sqlite::Connection* dbConnection ) { sqlite::Tools::executeRequest( dbConnection, schema( Table::Name, Settings::DbModelVersion ) ); } void AudioTrack::createIndexes(sqlite::Connection* dbConnection) { sqlite::Tools::executeRequest( dbConnection, index( Indexes::MediaId, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, index( Indexes::AttachedFileId, Settings::DbModelVersion ) ); } std::string AudioTrack::schema( const std::string& tableName, uint32_t dbModel ) { UNUSED_IN_RELEASE( tableName ); assert( tableName == Table::Name ); if ( dbModel < 27 ) { return "CREATE TABLE " + Table::Name + "(" + Table::PrimaryKeyColumn + " INTEGER PRIMARY KEY AUTOINCREMENT," "codec TEXT," "bitrate UNSIGNED INTEGER," "samplerate UNSIGNED INTEGER," "nb_channels UNSIGNED INTEGER," "language TEXT," "description TEXT," "media_id UNSIGNED INT," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media) ON DELETE CASCADE" ")"; } if ( dbModel < 34 ) { return "CREATE TABLE " + Table::Name + "(" + Table::PrimaryKeyColumn + " INTEGER PRIMARY KEY AUTOINCREMENT," "codec TEXT," "bitrate UNSIGNED INTEGER," "samplerate UNSIGNED INTEGER," "nb_channels UNSIGNED INTEGER," "language TEXT," "description TEXT," "media_id UNSIGNED INT," "attached_file_id UNSIGNED INT," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media) ON DELETE CASCADE," "FOREIGN KEY(attached_file_id) REFERENCES " + File::Table::Name + "(id_file) ON DELETE CASCADE," "UNIQUE(media_id, attached_file_id) ON CONFLICT FAIL" ")"; } return "CREATE TABLE " + Table::Name + "(" + Table::PrimaryKeyColumn + " INTEGER PRIMARY KEY AUTOINCREMENT," "codec TEXT," "bitrate UNSIGNED INTEGER," "samplerate UNSIGNED INTEGER," "nb_channels UNSIGNED INTEGER," "language TEXT," "description TEXT," "media_id UNSIGNED INT," "attached_file_id UNSIGNED INT," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media) ON DELETE CASCADE," "FOREIGN KEY(attached_file_id) REFERENCES " + File::Table::Name + "(id_file) ON DELETE CASCADE" ")"; } std::string AudioTrack::index( Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::MediaId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(media_id)"; case Indexes::AttachedFileId: assert( dbModel >= 34 ); return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(attached_file_id)"; } return ""; } std::string AudioTrack::indexName( Indexes index, uint32_t dbModel ) { UNUSED_IN_RELEASE( index ); UNUSED_IN_RELEASE( dbModel ); switch ( index ) { case Indexes::MediaId: return "audio_track_media_idx"; case Indexes::AttachedFileId: assert( dbModel >= 34 ); return "audio_track_attached_file_idx"; } return ""; } bool AudioTrack::checkDbModel( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); auto checkIndex = []( Indexes idx ) { return sqlite::Tools::checkIndexStatement( index( idx, Settings::DbModelVersion ), indexName( idx, Settings::DbModelVersion ) ); }; return sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ) && checkIndex( Indexes::MediaId ) && checkIndex( Indexes::AttachedFileId ); } std::shared_ptr AudioTrack::create( MediaLibraryPtr ml, std::string codec, unsigned int bitrate, unsigned int sampleRate, unsigned int nbChannels, std::string language, std::string desc, int64_t mediaId, int64_t attachedFileId ) { static const std::string req = "INSERT INTO " + Table::Name + "(codec, bitrate, samplerate, nb_channels, language, description, " "media_id, attached_file_id) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; auto track = std::make_shared( ml, std::move( codec ), bitrate, sampleRate, nbChannels, std::move( language ), std::move( desc ), mediaId, attachedFileId ); if ( insert( ml, track, req, track->m_codec, bitrate, sampleRate, nbChannels, track->m_language, track->m_description, mediaId, sqlite::ForeignKey{ attachedFileId } ) == false ) return nullptr; return track; } bool AudioTrack::removeFromMedia( MediaLibraryPtr ml, int64_t mediaId, bool internalTracksOnly ) { std::string req = "DELETE FROM " + Table::Name + " WHERE media_id = ?"; if ( internalTracksOnly == true ) req += " AND attached_file_id IS NULL"; return sqlite::Tools::executeDelete( ml->getConn(), req, mediaId ); } Query AudioTrack::fromMedia( MediaLibraryPtr ml, int64_t mediaId, bool internalTracksOnly ) { std::string req = "FROM " + Table::Name + " WHERE media_id = ?"; if ( internalTracksOnly == true ) req += " AND attached_file_id IS NULL"; return make_query( ml, "*", req, "", mediaId ).build(); } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/AudioTrack.h000066400000000000000000000076111501065546500246240ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifndef AUDIOTRACK_H #define AUDIOTRACK_H #include "medialibrary/IAudioTrack.h" #include "database/DatabaseHelpers.h" namespace medialibrary { class AudioTrack : public IAudioTrack, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t AudioTrack::* const PrimaryKey; }; enum class Indexes : uint8_t { MediaId, AttachedFileId, }; AudioTrack(MediaLibraryPtr ml, sqlite::Row& row ); AudioTrack( MediaLibraryPtr ml, std::string codec, unsigned int bitrate, unsigned int sampleRate, unsigned int nbChannels, std::string language, std::string desc, int64_t mediaId, int64_t attachedFileId ); virtual int64_t id() const override; virtual const std::string&codec() const override; virtual unsigned int bitrate() const override; virtual unsigned int sampleRate() const override; virtual unsigned int nbChannels() const override; virtual const std::string& language() const override; virtual const std::string& description() const override; virtual bool isInAttachedFile() const override; static void createTable( sqlite::Connection* dbConnection ); static void createIndexes( sqlite::Connection* dbConnection ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static std::string index( Indexes index, uint32_t dbModel ); static std::string indexName( Indexes index, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr create( MediaLibraryPtr ml, std::string codec, unsigned int bitrate, unsigned int sampleRate, unsigned int nbChannels, std::string language, std::string desc, int64_t mediaId, int64_t attachedFileId ); static bool removeFromMedia( MediaLibraryPtr ml, int64_t mediaId, bool internalTracksOnly ); static Query fromMedia( MediaLibraryPtr ml, int64_t mediaId, bool internalTracksOnly ); private: int64_t m_id; const std::string m_codec; const unsigned int m_bitrate; const unsigned int m_sampleRate; const unsigned int m_nbChannels; const std::string m_language; const std::string m_description; const int64_t m_mediaId; const int64_t m_attachedFileId; private: friend struct AudioTrack::Table; }; } #endif // AUDIOTRACK_H medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Bookmark.cpp000066400000000000000000000226761501065546500247060ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "Bookmark.h" #include "Media.h" #include "database/SqliteQuery.h" #include "utils/ModificationsNotifier.h" namespace medialibrary { const std::string Bookmark::Table::Name = "Bookmark"; const std::string Bookmark::Table::PrimaryKeyColumn = "id_bookmark"; int64_t Bookmark::* const Bookmark::Table::PrimaryKey = &Bookmark::m_id; Bookmark::Bookmark( MediaLibraryPtr ml, int64_t time, int64_t mediaId ) : m_ml( ml ) , m_id( 0 ) , m_time( time ) , m_mediaId( mediaId ) , m_creationDate( ::time( nullptr ) ) , m_type( Type::Simple ) { } Bookmark::Bookmark( MediaLibraryPtr ml, sqlite::Row& row ) : m_ml( ml ) , m_id( row.extract() ) , m_time( row.extract() ) , m_name( row.extract() ) , m_description( row.extract() ) , m_mediaId( row.extract() ) , m_creationDate( row.extract() ) , m_type( row.extract() ) { assert( row.hasRemainingColumns() == false ); } int64_t Bookmark::id() const { return m_id; } int64_t Bookmark::mediaId() const { return m_mediaId; } int64_t Bookmark::time() const { return m_time; } const std::string&Bookmark::name() const { return m_name; } bool Bookmark::setName( std::string name ) { return setNameAndDescription( std::move( name ), m_description ); } const std::string&Bookmark::description() const { return m_description; } time_t Bookmark::creationDate() const { return m_creationDate; } IBookmark::Type Bookmark::type() const { return m_type; } bool Bookmark::setDescription( std::string description ) { return setNameAndDescription( m_name, std::move( description ) ); } bool Bookmark::setNameAndDescription( std::string name, std::string desc ) { if ( m_name == name && m_description == desc ) return true; const std::string req = "UPDATE " + Table::Name + " SET name = ?, description = ? WHERE id_bookmark = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, name, desc, m_id ) == false ) return false; m_name = std::move( name ); m_description = std::move( desc ); return true; } bool Bookmark::move( int64_t newTime ) { const std::string req = "UPDATE " + Table::Name + " SET time = ? WHERE id_bookmark = ?"; try { if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, newTime, m_id ) == false ) return false; } catch ( const sqlite::errors::ConstraintViolation& ) { return false; } m_time = newTime; return true; } void Bookmark::createTable(sqlite::Connection* dbConn) { const std::string reqs[] = { schema( Table::Name, Settings::DbModelVersion ), }; for ( const auto& req : reqs ) sqlite::Tools::executeRequest( dbConn, req ); } void Bookmark::createIndexes(sqlite::Connection* dbConnection) { sqlite::Tools::executeRequest( dbConnection, index( Indexes::MediaId, Settings::DbModelVersion ) ); } std::string Bookmark::schema( const std::string& tableName, uint32_t dbModel ) { UNUSED_IN_RELEASE( tableName ); assert( dbModel >= 17 ); assert( tableName == Table::Name ); if ( dbModel < 25 ) { return "CREATE TABLE " + Table::Name + "(" "id_bookmark INTEGER PRIMARY KEY AUTOINCREMENT," "time UNSIGNED INTEGER NOT NULL," "name TEXT," "description TEXT," "media_id UNSIGNED INTEGER NOT NULL," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media)," "UNIQUE(time,media_id) ON CONFLICT FAIL" ")"; } if ( dbModel < 35 ) { return "CREATE TABLE " + Table::Name + "(" "id_bookmark INTEGER PRIMARY KEY AUTOINCREMENT," "time UNSIGNED INTEGER NOT NULL," "name TEXT," "description TEXT," "media_id UNSIGNED INTEGER NOT NULL," "creation_date UNSIGNED INTEGER NOT NULL," "type UNSIGNED INTEGER NOT NULL," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media)," "UNIQUE(time,media_id) ON CONFLICT FAIL" ")"; } return "CREATE TABLE " + Table::Name + "(" "id_bookmark INTEGER PRIMARY KEY AUTOINCREMENT," "time UNSIGNED INTEGER NOT NULL," "name TEXT," "description TEXT," "media_id UNSIGNED INTEGER NOT NULL," "creation_date UNSIGNED INTEGER NOT NULL," "type UNSIGNED INTEGER NOT NULL," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media) ON DELETE CASCADE," "UNIQUE(time,media_id) ON CONFLICT FAIL" ")"; } std::string Bookmark::index( Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::MediaId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(media_id)"; } return ""; } std::string Bookmark::indexName( Indexes index, uint32_t dbModel ) { UNUSED_IN_RELEASE( dbModel ); switch ( index ) { case Indexes::MediaId: assert( dbModel >= 34 ); return "bookmark_media_id_idx"; } return ""; } bool Bookmark::checkDbModel( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); return sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ) && sqlite::Tools::checkIndexStatement( index( Indexes::MediaId, Settings::DbModelVersion ), indexName( Indexes::MediaId, Settings::DbModelVersion ) ); } std::shared_ptr Bookmark::create( MediaLibraryPtr ml, int64_t time, int64_t mediaId ) { auto self = std::make_shared( ml, time, mediaId ); const std::string req = "INSERT INTO " + Table::Name + "(time, media_id, creation_date, type) VALUES(?, ?, ?, ?)"; try { if ( insert( ml, self, req, time, mediaId, self->creationDate(), self->type() ) == false ) return nullptr; } catch ( const sqlite::errors::ConstraintUnique& ) { return nullptr; } auto notifier = ml->getNotifier(); if ( notifier != nullptr ) notifier->notifyBookmarkCreation( self ); return self; } bool Bookmark::remove( MediaLibraryPtr ml, int64_t time, int64_t mediaId ) { const std::string req = "DELETE FROM " + Table::Name + " WHERE time = ? AND media_id = ?"; return sqlite::Tools::executeDelete( ml->getConn(), req, time, mediaId ); } Query Bookmark::fromMedia( MediaLibraryPtr ml, int64_t mediaId, const QueryParameters* params ) { const std::string req = "FROM " + Table::Name + " WHERE media_id = ?"; auto sort = params != nullptr ? params->sort : SortingCriteria::Default; std::string orderBy = " ORDER BY "; switch ( sort ) { case SortingCriteria::Alpha: orderBy += "name"; break; case SortingCriteria::InsertionDate: orderBy += "creation_date"; break; default: LOG_INFO( "Unsupported sorting criteria, falling back to default" ); /* fall-through */ case SortingCriteria::Default: orderBy += "time"; break; } if ( params != nullptr && params->desc == true ) orderBy += " DESC"; return make_query( ml, "*", req, orderBy, mediaId ).build(); } std::shared_ptr Bookmark::fromMedia( MediaLibraryPtr ml, int64_t mediaId, int64_t time ) { const std::string req = "SELECT * FROM " + Table::Name + " WHERE time = ? AND media_id = ?"; return fetch( ml, req, time, mediaId ); } bool Bookmark::removeAll( MediaLibraryPtr ml, int64_t mediaId ) { const std::string req = "DELETE FROM " + Table::Name + " WHERE media_id = ?"; return sqlite::Tools::executeDelete( ml->getConn(), req, mediaId ); } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Bookmark.h000066400000000000000000000065471501065546500243520ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "medialibrary/IBookmark.h" #include "database/DatabaseHelpers.h" #include "medialibrary/IQuery.h" namespace medialibrary { class Bookmark : public IBookmark, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t Bookmark::*const PrimaryKey; }; enum class Indexes : uint8_t { MediaId, }; Bookmark( MediaLibraryPtr ml, int64_t time, int64_t mediaId ); Bookmark( MediaLibraryPtr ml, sqlite::Row& row ); virtual int64_t id() const override; virtual int64_t mediaId() const override; virtual int64_t time() const override; virtual const std::string& name() const override; virtual bool setName( std::string name ) override; virtual const std::string& description() const override; virtual time_t creationDate() const override; virtual Type type() const override; virtual bool setDescription( std::string description ) override; virtual bool setNameAndDescription( std::string name, std::string desc ) override; virtual bool move( int64_t newTime ) override; static void createTable( sqlite::Connection* dbConn ); static void createIndexes( sqlite::Connection* dbConnection ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static std::string index( Indexes index, uint32_t dbModel ); static std::string indexName( Indexes index, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr create(MediaLibraryPtr ml, int64_t time, int64_t mediaId ); static bool remove( MediaLibraryPtr ml, int64_t time, int64_t mediaId ); static Query fromMedia( MediaLibraryPtr ml, int64_t mediaId, const QueryParameters* params ); static std::shared_ptr fromMedia( MediaLibraryPtr ml, int64_t mediaId, int64_t time ); static bool removeAll( MediaLibraryPtr ml, int64_t mediaId ); private: MediaLibraryPtr m_ml; int64_t m_id; int64_t m_time; std::string m_name; std::string m_description; int64_t m_mediaId; time_t m_creationDate; Type m_type; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/CacheWorker.cpp000066400000000000000000000315441501065546500253300ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright © 2022 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "CacheWorker.h" #include "Media.h" #include "Subscription.h" #include "utils/Url.h" #include "utils/File.h" #include "utils/Filename.h" #include "medialibrary/filesystem/IFileSystemFactory.h" #include "medialibrary/filesystem/IDirectory.h" #include "medialibrary/filesystem/IFile.h" #include "medialibrary/ICacher.h" #include namespace medialibrary { CacheWorker::CacheWorker( MediaLibrary* ml ) : m_ml( ml ) , m_paused( false ) , m_run( true ) { } CacheWorker::~CacheWorker() { stop(); } void CacheWorker::setCacher( std::shared_ptr cacher ) { assert( m_cacher == nullptr ); m_cacher = std::move( cacher ); } bool CacheWorker::queueTask( std::shared_ptr m, bool cache ) { std::lock_guard 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 m ) { auto f = m->mainFile(); if ( f == nullptr ) return false; if ( f->type() == IFile::Type::Cache ) { LOG_DEBUG( "Media ", m->id(), " is already cached" ); return true; } return queueTask( std::move( m ), true ); } bool CacheWorker::removeCached( std::shared_ptr m ) { auto f = m->mainFile(); if ( f == nullptr ) return false; if ( f->type() != IFile::Type::Cache ) { LOG_DEBUG( "Media ", m->id(), " is not cached" ); return false; } return queueTask( std::move( m ), false ); } void CacheWorker::cacheSubscriptions() { queueTask( nullptr, true ); } void CacheWorker::pause() { std::lock_guard lock{ m_mutex }; m_paused = true; m_cond.notify_one(); } void CacheWorker::resume() { std::lock_guard lock{ m_mutex }; m_paused = false; m_cond.notify_one(); } void CacheWorker::stop() { if ( m_cacher != nullptr ) m_cacher->interrupt(); { std::lock_guard lock{ m_mutex }; if ( m_thread.joinable() == false ) return; m_run = false; m_cond.notify_one(); } m_thread.join(); } uint64_t CacheWorker::cacheSize() const { return m_cacheSize.load( std::memory_order_acquire ); } uint64_t CacheWorker::availableSubscriptionCacheSize() const { auto usedSize = cacheSize(); auto totalSize = m_ml->settings().maxSubscriptionCacheSize(); if ( usedSize > totalSize ) { LOG_WARN( "Subscription cache is overused: ", usedSize, " / ", totalSize ); return 0; } return totalSize - usedSize; } uint64_t CacheWorker::availableCacheSize() const { auto usedSize = cacheSize(); auto totalSize = m_ml->settings().maxCacheSize(); if ( usedSize > totalSize ) { LOG_WARN( "Cache is overused: ", usedSize, " / ", totalSize ); return 0; } return totalSize - usedSize; } void CacheWorker::run() { auto cb = m_ml->getCb(); LOG_DEBUG( "Starting cache worker" ); checkCache(); while ( true ) { Task t; { std::unique_lock lock{ m_mutex }; if ( m_run == false ) break; if ( m_paused == true || m_tasks.empty() == true ) { if ( cb != nullptr ) cb->onCacheIdleChanged( true ); m_cond.wait( lock, [this]() { return m_run == false || ( m_paused == false && m_tasks.empty() == false ); }); if ( m_run == false ) break; assert( m_paused == false ); if ( cb != nullptr ) cb->onCacheIdleChanged( false ); } t = std::move( m_tasks.front() ); m_tasks.pop(); } if ( t.m == nullptr ) doSubscriptionCache(); else if ( t.cache == true ) doCache( std::move( t.m ), nullptr, File::CacheType::Manual ); else doUncache( std::move( t.m ) ); } LOG_DEBUG( "Terminating cache worker" ); } uint64_t CacheWorker::doCache( std::shared_ptr m, Subscription* s, IFile::CacheType cacheType ) { auto f = std::static_pointer_cast( m->mainFile() ); if ( f == nullptr ) { assert( !"No main file for the media" ); return 0; } if ( f->type() == IFile::Type::Cache ) { assert( !"Media was already cached" ); return 0; } LOG_DEBUG( "Attempting to ", ( cacheType == IFile::CacheType::Automatic ? "automatically" : "manually" ), " cache file at ", f->mrl() ); if ( evictIfNeeded( *f, s, cacheType ) == false ) { LOG_DEBUG( "Failed to evict media from cache, can't cache ", f->mrl() ); return 0; } auto cachedPath = m_ml->cachePath() + f->cachedFileName(); if ( m_cacher->cache( f->mrl(), cachedPath ) == false ) return 0; auto fileSize = f->size(); if ( fileSize == 0 ) fileSize = utils::fs::fileSize( cachedPath ); m->cache( utils::file::toMrl( cachedPath ), cacheType, fileSize ); return f->size(); } bool CacheWorker::removeFromCache( const std::string& mrl ) { assert( utils::url::schemeIs( "file://", mrl ) ); auto path = utils::url::toLocalPath( mrl ); return utils::fs::remove( path ); } void CacheWorker::doUncache( std::shared_ptr m ) { auto f = std::static_pointer_cast( m->mainFile() ); if ( f == nullptr || f->type() != IFile::Type::Cache ) { assert( !"The media has no cached file" ); return; } const auto& mrl = f->mrl(); if ( removeFromCache( mrl ) == false ) return; m->removeCached(); } void CacheWorker::doSubscriptionCache() { auto cb = m_ml->getCb(); auto subscriptions = Subscription::fetchAll( m_ml ); for ( auto& s : subscriptions ) { auto uncachedMedia = s->uncachedMedia( true ); if ( uncachedMedia.empty() == false ) { for ( auto& m : uncachedMedia ) { doCache( m, s.get(), File::CacheType::Automatic ); } if ( cb != nullptr ) cb->onSubscriptionCacheUpdated( s->id() ); } s->markCacheAsHandled(); } } bool CacheWorker::evictIfNeeded( const File& file, Subscription* s, IFile::CacheType cacheType ) { if ( cacheType == File::CacheType::Automatic ) { /* Automatic caching is only requested for subscription */ assert( s != nullptr ); /* * We only want to check the number of cached media for a subscription * when doing automatic caching. */ auto maxMedia = s->maxCachedMedia(); if ( maxMedia < 0 ) { LOG_DEBUG( "No subscription settings, falling back to global settings" ); maxMedia = m_ml->settings().nbCachedMediaPerSubscription(); } auto subCacheSize = s->cachedSize(); auto maxSubCacheSize = s->maxCacheSize(); auto nbCachedMediaInSub = s->cachedMedia( false )->count(); LOG_DEBUG( "Subscription #", s->id(), " has ", nbCachedMediaInSub, "/", maxMedia, " cached media" ); while ( nbCachedMediaInSub >= static_cast( maxMedia ) || ( maxSubCacheSize > 0 && static_cast( maxSubCacheSize ) >= subCacheSize ) ) { auto toEvict = s->cachedMedia( true )->items( 1, 0 ); if ( toEvict.size() != 1 ) return false; auto f = toEvict[0]->mainFile(); if ( f->type() != File::Type::Cache ) { assert( !"Invalid file type" ); return false; } if ( removeFromCache( f->mrl() ) == false ) return false; if ( toEvict[0]->removeCached() == false ) return false; m_cacheSize.fetch_sub( f->size(), std::memory_order_acq_rel ); subCacheSize -= f->size(); } } auto fileSize = file.size(); auto evictionNeeded = fileSize > availableCacheSize(); if ( evictionNeeded == false ) return true; auto cachedFiles = File::cachedFiles( m_ml ); auto cachedFilesIdx = 0; assert( cachedFiles.empty() == false ); while ( evictionNeeded == true ) { auto f = std::move( cachedFiles[cachedFilesIdx] ); const auto& mrl = f->mrl(); if ( removeFromCache( mrl ) == false ) return false; if ( cachedFiles[cachedFilesIdx]->destroy() == false ) return false; m_cacheSize.fetch_sub( cachedFiles[cachedFilesIdx]->size(), std::memory_order_acq_rel ); evictionNeeded = fileSize > availableCacheSize(); } return true; } void CacheWorker::checkCache() { auto cachePath = m_ml->cachePath(); auto cacheMrl = utils::file::toMrl( cachePath ); auto fsFactory = m_ml->fsFactoryForMrl( cacheMrl ); auto fsDir = fsFactory->createDirectory( cacheMrl ); auto files = fsDir->files(); struct Comp { bool operator()( const std::shared_ptr& f, const std::string& fileName ) const { return f->name() < fileName; } bool operator()( const std::string& fileName, const std::shared_ptr& f ) const { return fileName < f->name(); } bool operator()( const std::shared_ptr& l, const std::shared_ptr& r ) const { return l->name() < r->name(); } }; // Sort the files from the cache folder to fasten up following lookups std::sort( begin( files ), end( files ), Comp{} ); // Fetch the known cached files from the DB auto cachedFiles = File::cachedFiles( m_ml ); m_cacheSize.store( 0, std::memory_order_release ); for ( auto cachedIt = begin( cachedFiles ); cachedIt != end( cachedFiles ); ) { auto wantedFileName = utils::url::encode( (*cachedIt)->cachedFileName() ); auto it = std::lower_bound( begin( files ), end( files ), wantedFileName, Comp{} ); if ( it != end( files ) ) { if ( (*it)->name() == wantedFileName ) { /* The cached file was indeed found it cache, all is well */ auto fileSize = (*it)->size(); assert( fileSize != 0 ); m_cacheSize.fetch_add( fileSize, std::memory_order_acq_rel ); files.erase( it ); cachedIt = cachedFiles.erase( cachedIt ); continue; } } /* A file flagged as cached in DB was not found on disk, unflag it */ LOG_DEBUG( "File ", (*cachedIt)->rawMrl(), " was flagged as cached but " "wasn't found on disk. Unflagging it." ); (*cachedIt)->destroy(); cachedIt = cachedFiles.erase( cachedIt ); } /* * Whatever remains in the files vector are files that were not flagged as cached * in DB. * For now, take the easy way out and remove the cached version without touching * the database, but ideally we should flag the file as cached */ for ( const auto& f : files ) { auto path = utils::url::toLocalPath( f->mrl() ); LOG_DEBUG( "Removing stale file from cache: ", path ); utils::fs::remove( path ); } } CacheWorker::Task::Task( std::shared_ptr m, bool c ) : m( std::move( m ) ) , cache( c ) { } } // namespace medialibrary medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/CacheWorker.h000066400000000000000000000054531501065546500247750ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright © 2022 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include #include "compat/Thread.h" #include "compat/Mutex.h" #include "compat/ConditionVariable.h" #include "medialibrary/IFile.h" #include #include namespace medialibrary { class Media; class File; class MediaLibrary; class Subscription; class ICacher; class CacheWorker { private: struct Task { Task() = default; Task( std::shared_ptr m, bool c ); std::shared_ptr m; // If nullptr, we're caching all subscriptions bool cache; // true if we're caching, false if we're uncaching }; public: CacheWorker( MediaLibrary* ml ); ~CacheWorker(); void setCacher( std::shared_ptr cacher ); bool cacheMedia( std::shared_ptr m ); bool removeCached( std::shared_ptr m ); void cacheSubscriptions(); void pause(); void resume(); void stop(); uint64_t cacheSize() const; private: void run(); uint64_t doCache( std::shared_ptr m, Subscription* s, IFile::CacheType cacheType ); void doUncache( std::shared_ptr m ); void doSubscriptionCache(); void checkCache(); bool removeFromCache( const std::string& mrl ); bool evictIfNeeded( const File& file, Subscription* s, IFile::CacheType cacheType ); bool queueTask( std::shared_ptr m, bool cache ); uint64_t availableSubscriptionCacheSize() const; uint64_t availableCacheSize() const; private: MediaLibrary* m_ml; compat::Mutex m_mutex; compat::Thread m_thread; compat::ConditionVariable m_cond; std::queue m_tasks; bool m_paused; bool m_run; std::shared_ptr m_cacher; std::atomic_uint64_t m_cacheSize; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Chapter.cpp000066400000000000000000000134061501065546500245160ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2018-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "Chapter.h" #include "database/SqliteTools.h" #include "database/SqliteQuery.h" #include "Media.h" #include "utils/Enums.h" namespace medialibrary { const std::string Chapter::Table::Name = "Chapter"; const std::string Chapter::Table::PrimaryKeyColumn = "id_chapter"; int64_t Chapter::* const Chapter::Table::PrimaryKey = &Chapter::m_id; void Chapter::createTable( sqlite::Connection* dbConn ) { sqlite::Tools::executeRequest( dbConn, schema( Table::Name, Settings::DbModelVersion ) ); } void Chapter::createIndexes( sqlite::Connection* dbConnection ) { sqlite::Tools::executeRequest( dbConnection, index( Indexes::MediaId, Settings::DbModelVersion ) ); } std::string Chapter::schema( const std::string& tableName, uint32_t ) { UNUSED_IN_RELEASE( tableName ); assert( tableName == Table::Name ); return "CREATE TABLE " + Table::Name + "(" + Table::PrimaryKeyColumn + " INTEGER PRIMARY KEY AUTOINCREMENT," "offset INTEGER NOT NULL," "duration INTEGER NOT NULL," "name TEXT," "media_id INTEGER," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(" + Media::Table::PrimaryKeyColumn + ")" " ON DELETE CASCADE" ")"; } std::string Chapter::index( Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::MediaId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(media_id)"; } return ""; } std::string Chapter::indexName( Indexes index, uint32_t dbModel ) { UNUSED_IN_RELEASE( dbModel ); switch ( index ) { case Indexes::MediaId: assert( dbModel >= 34 ); return "chapter_media_id_idx"; } return ""; } bool Chapter::checkDbModel( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); return sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ); } std::shared_ptr Chapter::create( MediaLibraryPtr ml, int64_t offset, int64_t duration, std::string name, int64_t mediaId ) { static const std::string req = "INSERT INTO " + Table::Name + "(offset, duration, name, media_id) VALUES(?, ?, ?, ?)"; auto self = std::make_shared( ml, offset, duration, std::move( name ) ); if ( insert( ml, self, req, offset, duration, self->m_name, mediaId ) == false ) return nullptr; return self; } Query Chapter::fromMedia( MediaLibraryPtr ml, int64_t mediaId, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " WHERE media_id = ?"; std::string orderBy = "ORDER BY "; auto desc = params != nullptr ? params->desc : false; auto sort = params != nullptr ? params->sort : SortingCriteria::Default; switch ( sort ) { case SortingCriteria::Alpha: orderBy += "name"; break; case SortingCriteria::Duration: orderBy += "duration"; desc = !desc; break; default: LOG_WARN( "Unsupported sorting criteria ", utils::enum_to_string( sort ), " falling back to default (by offset)" ); /* fall-through */ case SortingCriteria::Default: orderBy += "offset"; break; } if ( desc == true ) orderBy += " DESC"; return make_query( ml, "*", req, orderBy, mediaId ).build(); } Chapter::Chapter( MediaLibraryPtr, sqlite::Row& row ) : m_id( row.extract() ) , m_offset( row.extract() ) , m_duration( row.extract() ) , m_name( row.extract() ) { // Simply check that the media_id row is present, until we eventually store it assert( row.extract() ); assert( row.hasRemainingColumns() == false ); } Chapter::Chapter( MediaLibraryPtr, int64_t offset, int64_t duration, std::string name ) : m_id( 0 ) , m_offset( offset ) , m_duration( duration ) , m_name( std::move( name ) ) { } const std::string& Chapter::name() const { return m_name; } int64_t Chapter::offset() const { return m_offset; } int64_t Chapter::duration() const { return m_duration; } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Chapter.h000066400000000000000000000053121501065546500241600ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2018-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "medialibrary/IChapter.h" #include "database/DatabaseHelpers.h" #include "Types.h" namespace medialibrary { class Chapter : public IChapter, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t Chapter::*const PrimaryKey; }; enum class Indexes : uint8_t { MediaId, }; Chapter( MediaLibraryPtr ml, sqlite::Row& row ); Chapter( MediaLibraryPtr ml, int64_t offset, int64_t duration, std::string name ); virtual const std::string& name() const override; virtual int64_t offset() const override; virtual int64_t duration() const override; static void createTable( sqlite::Connection* dbConn ); static void createIndexes( sqlite::Connection* dbConnection ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static std::string index( Indexes index, uint32_t dbModel ); static std::string indexName( Indexes index, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr create( MediaLibraryPtr ml, int64_t offset, int64_t duration, std::string name, int64_t mediaId ); static Query fromMedia( MediaLibraryPtr ml, int64_t mediaId, const QueryParameters* params ); private: int64_t m_id; int64_t m_offset; int64_t m_duration; std::string m_name; friend Chapter::Table; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Deprecated.cpp000066400000000000000000000064021501065546500251660ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "Deprecated.h" #include "Common.h" #include "Media.h" #include "Artist.h" #include "Genre.h" #include namespace medialibrary { const std::string AlbumTrack::Table::Name = "AlbumTrack"; std::string AlbumTrack::schema( const std::string& tableName, uint32_t ) { UNUSED_IN_RELEASE( tableName ); assert( tableName == Table::Name ); return "CREATE TABLE " + AlbumTrack::Table::Name + "(" "id_track INTEGER PRIMARY KEY AUTOINCREMENT," "media_id INTEGER UNIQUE," "duration INTEGER NOT NULL," "artist_id UNSIGNED INTEGER," "genre_id INTEGER," "track_number UNSIGNED INTEGER," "album_id UNSIGNED INTEGER NOT NULL," "disc_number UNSIGNED INTEGER," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media)" " ON DELETE CASCADE," "FOREIGN KEY(artist_id) REFERENCES " + Artist::Table::Name + "(id_artist)" " ON DELETE CASCADE," "FOREIGN KEY(genre_id) REFERENCES " + Genre::Table::Name + "(id_genre)," "FOREIGN KEY(album_id) REFERENCES Album(id_album) " " ON DELETE CASCADE" ")"; } std::string AlbumTrack::index( AlbumTrack::Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::AlbumGenreArtist: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(album_id, genre_id, artist_id)"; case Indexes::MediaArtistGenreAlbum: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(media_id, artist_id, genre_id, album_id)"; } return ""; } std::string AlbumTrack::indexName(AlbumTrack::Indexes index, uint32_t ) { switch ( index ) { case Indexes::AlbumGenreArtist: return "album_track_album_genre_artist_ids"; case Indexes::MediaArtistGenreAlbum: return "album_media_artist_genre_album_idx"; default: assert( !"Invalid index provided" ); }; return ""; } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Deprecated.h000066400000000000000000000034261501065546500246360ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2021 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once /* This file is meant to contain old tables or similar references to deprecated * code. It shouldn't be referenced outside of old schema/trigger/index functions * or old migration code */ #include #include namespace medialibrary { class AlbumTrack { public: struct Table { static const std::string Name; }; enum class Indexes : uint8_t { MediaArtistGenreAlbum, AlbumGenreArtist, }; static std::string schema( const std::string& tableName, uint32_t ); static std::string index( AlbumTrack::Indexes index, uint32_t dbModel ); static std::string indexName( AlbumTrack::Indexes index, uint32_t ); }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Device.cpp000066400000000000000000000266021501065546500243310ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "Device.h" #include "Folder.h" #include "utils/Filename.h" #include "utils/Url.h" #include namespace medialibrary { const std::string Device::Table::Name = "Device"; const std::string Device::Table::PrimaryKeyColumn = "id_device"; int64_t Device::* const Device::Table::PrimaryKey = &Device::m_id; const std::string Device::MountpointTable::Name = "DeviceMountpoint"; Device::Device( MediaLibraryPtr ml, sqlite::Row& row ) : m_ml( ml ) , m_id( row.extract() ) , m_uuid( row.extract() ) , m_scheme( row.extract() ) , m_isRemovable( row.extract() ) , m_isPresent( row.extract() ) , m_isNetwork( row.extract() ) { #ifndef NDEBUG auto lastSeen = row.extract(); assert( row.hasRemainingColumns() == false ); assert( lastSeen != 0 || m_isRemovable == false ); #endif } Device::Device( MediaLibraryPtr ml, std::string uuid, std::string scheme, bool isRemovable, bool isNetwork ) : m_ml( ml ) , m_id( 0 ) , m_uuid( std::move( uuid ) ) , m_scheme( std::move( scheme ) ) , m_isRemovable( isRemovable ) // Assume we can't add an absent device , m_isPresent( true ) , m_isNetwork( isNetwork ) { } int64_t Device::id() const { return m_id; } const std::string&Device::uuid() const { return m_uuid; } bool Device::isRemovable() const { return m_isRemovable; } bool Device::isPresent() const { return m_isPresent; } void Device::setPresent(bool value) { if ( m_isRemovable == false ) { assert( !"Can't change the presence of a non-removable device" ); return; } assert( m_isPresent != value ); static const std::string req = "UPDATE " + Device::Table::Name + " SET is_present = ? WHERE id_device = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, value, m_id ) == false ) return; m_isPresent = value; } bool Device::isNetwork() const { return m_isNetwork; } const std::string& Device::scheme() const { return m_scheme; } void Device::updateLastSeen() { assert( m_isRemovable == true ); const std::string req = "UPDATE " + Device::Table::Name + " SET " "last_seen = ? WHERE id_device = ?"; auto lastSeen = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch() ).count(); if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, lastSeen, m_id ) == false ) LOG_WARN( "Failed to update last seen date for device ", m_uuid ); } bool Device::addMountpoint( const std::string& mrl, int64_t seenDate ) { /* * We don't need to bother handling mountpoints in database for non * removable, non network devices. * Local devices can be quickly refreshed and can avoid using a potentially * stalled cache from database */ assert( m_isRemovable == true ); assert( m_isNetwork == true ); assert( utils::url::schemeIs( m_scheme, mrl ) == true ); static const std::string req = "INSERT INTO " + MountpointTable::Name + " VALUES(?, ?, ?)"; return sqlite::Tools::executeInsert( m_ml->getConn(), req, m_id, utils::file::toFolderPath( mrl ), seenDate ) != 0; } std::shared_ptr Device::create( MediaLibraryPtr ml, std::string uuid, std::string scheme, bool isRemovable, bool isNetwork ) { static const std::string req = "INSERT INTO " + Device::Table::Name + "(uuid, scheme, is_removable, is_present, is_network, last_seen) " "VALUES(?, ?, ?, ?, ?, ?)"; auto lastSeen = isRemovable ? std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch() ).count() : 0; auto self = std::make_shared( ml, std::move( uuid ), std::move( scheme ), isRemovable, isNetwork ); if ( insert( ml, self, req, self->m_uuid, self->m_scheme, isRemovable, self->isPresent(), self->isNetwork(), lastSeen ) == false ) return nullptr; return self; } void Device::createTable( sqlite::Connection* connection ) { sqlite::Tools::executeRequest( connection, schema( Table::Name, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( connection, schema( MountpointTable::Name, Settings::DbModelVersion ) ); } std::string Device::schema( const std::string& tableName, uint32_t dbModel ) { if ( tableName == MountpointTable::Name ) { assert( dbModel >= 27 ); return "CREATE TABLE " + MountpointTable::Name + "(" "device_id INTEGER," "mrl TEXT COLLATE NOCASE," "last_seen INTEGER," "PRIMARY KEY(device_id, mrl) ON CONFLICT REPLACE," "FOREIGN KEY(device_id) REFERENCES " + Table::Name + "(id_device) ON DELETE CASCADE" ")"; } assert( tableName == Table::Name ); if ( dbModel < 24 ) { return "CREATE TABLE " + Device::Table::Name + "(" "id_device INTEGER PRIMARY KEY AUTOINCREMENT," "uuid TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL," "scheme TEXT," "is_removable BOOLEAN," "is_present BOOLEAN," "last_seen UNSIGNED INTEGER" ")"; } else if ( dbModel == 24 ) { return "CREATE TABLE " + Device::Table::Name + "(" "id_device INTEGER PRIMARY KEY AUTOINCREMENT," "uuid TEXT COLLATE NOCASE," "scheme TEXT," "is_removable BOOLEAN," "is_present BOOLEAN," "last_seen UNSIGNED INTEGER," "UNIQUE(uuid,scheme) ON CONFLICT FAIL" ")"; } return "CREATE TABLE " + Device::Table::Name + "(" "id_device INTEGER PRIMARY KEY AUTOINCREMENT," "uuid TEXT COLLATE NOCASE," "scheme TEXT," "is_removable BOOLEAN," "is_present BOOLEAN," "is_network BOOLEAN," "last_seen UNSIGNED INTEGER," "UNIQUE(uuid,scheme) ON CONFLICT FAIL" ")"; } bool Device::checkDbModel( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); return sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ); } std::shared_ptr Device::fromUuid( MediaLibraryPtr ml, const std::string& uuid, const std::string& scheme ) { static const std::string req = "SELECT * FROM " + Device::Table::Name + " WHERE uuid = ? AND scheme = ?"; return fetch( ml, req, uuid, scheme ); } void Device::removeOldDevices( MediaLibraryPtr ml, std::chrono::seconds maxLifeTime ) { static const std::string req = "DELETE FROM " + Device::Table::Name + " " "WHERE last_seen < ? AND is_removable != 0"; auto deadline = std::chrono::duration_cast( (std::chrono::system_clock::now() - maxLifeTime).time_since_epoch() ); if ( sqlite::Tools::executeDelete( ml->getConn(), req, deadline.count() ) == false ) LOG_WARN( "Failed to remove old devices" ); } std::vector> Device::fetchByScheme( MediaLibraryPtr ml, const std::string& scheme ) { static const std::string req = "SELECT * FROM " + Table::Name + " WHERE scheme = ?"; return fetchAll( ml, req, scheme ); } bool Device::deleteRemovable( MediaLibraryPtr ml ) { const std::string req = "DELETE FROM " + Device::Table::Name + " WHERE is_removable = 1"; return sqlite::Tools::executeDelete( ml->getConn(), req ); } bool Device::markNetworkAsDeviceMissing( MediaLibraryPtr ml ) { const std::string req = "UPDATE " + Table::Name + " SET is_present = 0 " "WHERE is_present != 0 AND is_network != 0 AND is_removable != 0"; return sqlite::Tools::executeUpdate( ml->getConn(), req ); } std::tuple Device::fromMountpoint( MediaLibraryPtr ml, const std::string& mrl ) { LOG_DEBUG( "Trying to match ", mrl, " with a cached mountpoint" ); static const std::string req = "SELECT device_id, mrl FROM " + MountpointTable::Name + " mt " "INNER JOIN " + Device::Table::Name + " d ON mt.device_id = d.id_device " "WHERE d.scheme = ? ORDER BY mt.last_seen DESC"; auto dbConn = ml->getConn(); sqlite::Connection::ReadContext ctx; if ( sqlite::Transaction::isInProgress() == false ) ctx = dbConn->acquireReadContext(); sqlite::Statement stmt{ req }; stmt.execute( utils::url::scheme( mrl ) ); for ( auto row = stmt.row(); row != nullptr; row = stmt.row() ) { assert( row.nbColumns() == 2 ); int64_t deviceId; std::string mountpoint; row >> deviceId >> mountpoint; if ( strncasecmp( mrl.c_str(), mountpoint.c_str(), mountpoint.length() ) == 0 ) { LOG_DEBUG( "Matched device #", deviceId ); /* * Since we match the stored mountpoint in a case insensitive way, * be sure to return the match as it was provided, not stored, otherwise * the caller may not manage to remove the path since it won't be * found in the processed mrl */ return std::make_tuple( deviceId, mrl.substr( 0, mountpoint.length() ) ); } } LOG_DEBUG( "No match" ); return std::make_tuple( 0, "" ); } std::string Device::cachedMountpoint() const { static const std::string req = "SELECT mrl FROM " + MountpointTable::Name + " WHERE device_id = ? ORDER BY last_seen DESC"; auto dbConn = m_ml->getConn(); auto ctx = dbConn->acquireReadContext(); sqlite::Statement stmt{ ctx.handle(), req }; stmt.execute( m_id ); auto row = stmt.row(); if ( row == nullptr ) return {}; return row.extract(); } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Device.h000066400000000000000000000121411501065546500237670ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "Types.h" #include "database/DatabaseHelpers.h" #include namespace medialibrary { class Device : public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t Device::*const PrimaryKey; }; struct MountpointTable { static const std::string Name; }; Device(MediaLibraryPtr ml, std::string uuid, std::string scheme, bool isRemovable, bool isNetwork ); Device( MediaLibraryPtr ml, sqlite::Row& row ); int64_t id() const; const std::string& uuid() const; bool isRemovable() const; bool isPresent() const; void setPresent( bool value ); bool isNetwork() const; /// /// \brief scheme returns the scheme that was used for this device when it was /// originally created. This allows to use the appropriate IFileSystemFactory to find the /// recreate a IDevice based on its id or UUID /// \return /// const std::string& scheme() const; void updateLastSeen(); /** * @brief addMountpoint Stores a device mountpoint in database * @param mrl The mrl to that mountpoint * @return true if the insertion succeeded, false otherwise * * If the mountpoint was already known, the request is ignored */ bool addMountpoint( const std::string& mrl, int64_t seenDate ); /** * @brief cachedMountpoint Fetches a cached mountpoint from the database * @param ml A media library instance * @return A previously seen mountpoint for this device * * This is only valid for network devices. */ std::string cachedMountpoint() const; static std::shared_ptr create( MediaLibraryPtr ml, std::string uuid, std::string scheme, bool isRemovable, bool isNetwork ); static void createTable( sqlite::Connection* connection ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr fromUuid( MediaLibraryPtr ml, const std::string& uuid, const std::string& scheme ); static void removeOldDevices( MediaLibraryPtr ml, std::chrono::seconds maxLifeTime ); static std::vector> fetchByScheme( MediaLibraryPtr ml, const std::string& scheme ); static bool deleteRemovable( MediaLibraryPtr ml ); /** * @brief markNetworkDeviceMissing Will mark all network devices as missing * @param ml A media library instance * @return true if the request successfully executed. False otherwise * * This is used to have a coherent state before the device lister are started. * We don't know what devices are present, so we mark them all as missing and * wait for the device lister to signal their presence */ static bool markNetworkAsDeviceMissing( MediaLibraryPtr ml ); /** * @brief fromMountpoint Returns a device matching the given mountpoint * @param ml A media library instance * @param mrl An mrl * @return A device instance if a match was found, nullptr otherwise. * * This will look at the mountpoints that were stored in database */ static std::tuple fromMountpoint( MediaLibraryPtr ml, const std::string& mrl ); private: MediaLibraryPtr m_ml; // This is a database ID int64_t m_id; // This is a unique ID on the system side, in the /dev/disk/by-uuid sense. // It can be a name or what not, depending on the OS. const std::string m_uuid; const std::string m_scheme; // Can't be const anymore, but should be if we ever get to remove the // removable->non removable device fixup (introduced after vlc-android 3.1.0 rc3) bool m_isRemovable; bool m_isPresent; bool m_isNetwork; friend struct Device::Table; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Factory.cpp000066400000000000000000000033431501065546500245360ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "MediaLibrary.h" #include "logging/Logger.h" extern "C" medialibrary::IMediaLibrary* NewMediaLibrary( const char* dbPath, const char* mlFolderPath, bool lockFile, const medialibrary::SetupConfig* cfg ) { try { auto ml = medialibrary::MediaLibrary::create( dbPath, mlFolderPath, lockFile, cfg ); if ( ml ) return ml.release(); return nullptr; } catch ( const std::exception& ex ) { LOG_ERROR( "Failed to instantiate medialibrary: ", ex.what() ); } return nullptr; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/File.cpp000066400000000000000000000561171501065546500240150ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "File.h" #include "Media.h" #include "Folder.h" #include "Playlist.h" #include "Subscription.h" #include "utils/Filename.h" #include "utils/Url.h" #include "utils/Enums.h" #include "medialibrary/filesystem/IFile.h" #include "medialibrary/filesystem/IDevice.h" #include "medialibrary/filesystem/Errors.h" namespace medialibrary { const std::string File::Table::Name = "File"; const std::string File::Table::PrimaryKeyColumn = "id_file"; int64_t File::* const File::Table::PrimaryKey = &File::m_id; File::File( MediaLibraryPtr ml, sqlite::Row& row ) : m_ml( ml ) , m_id( row.extract() ) , m_mediaId( row.extract() ) , m_playlistId( row.extract() ) , m_mrl( row.extract() ) , m_type( row.extract() ) , m_lastModificationDate( row.extract() ) , m_size( row.extract() ) , m_folderId( row.extract() ) , m_isRemovable( row.extract() ) , m_isExternal( row.extract() ) , m_isNetwork( row.extract() ) , m_subscriptionId( row.hasRemainingColumns() ? row.extract() : 0 ) , m_insertionDate( row.hasRemainingColumns() ? row.extract() : 0 ) , m_cacheType( row.hasRemainingColumns() ? row.extract() : CacheType::Uncached ) { assert( row.hasRemainingColumns() == false ); } File::File( MediaLibraryPtr ml, int64_t mediaId, int64_t playlistId, Type type, const fs::IFile& file, int64_t folderId, bool isRemovable, time_t insertionDate ) : m_ml( ml ) , m_id( 0 ) , m_mediaId( mediaId ) , m_playlistId( playlistId ) /* * We don't expect a subscription with an actual file on the file system * at least for now. */ , m_mrl( isRemovable == true ? file.name() : file.mrl() ) , m_type( type ) , m_lastModificationDate( file.lastModificationDate() ) , m_size( file.size() ) , m_folderId( folderId ) , m_isRemovable( isRemovable ) , m_isExternal( false ) , m_isNetwork( file.isNetwork() ) , m_subscriptionId( 0 ) , m_insertionDate( insertionDate ) , m_cacheType( CacheType::Uncached ) { assert( ( mediaId == 0 && playlistId != 0 ) || ( mediaId != 0 && playlistId == 0 ) ); } File::File( MediaLibraryPtr ml, int64_t mediaId, int64_t playlistId, int64_t subscriptionId, IFile::Type type, const std::string& mrl, int64_t fileSize, time_t insertionDate, CacheType cacheType ) : m_ml( ml ) , m_id( 0 ) , m_mediaId( mediaId ) , m_playlistId( playlistId ) , m_mrl( mrl ) , m_type( type ) , m_lastModificationDate( 0 ) , m_size( fileSize ) , m_folderId( 0 ) , m_isRemovable( false ) , m_isExternal( true ) , m_isNetwork( utils::url::schemeIs( "file://", mrl ) == false ) , m_subscriptionId( subscriptionId ) , m_insertionDate( insertionDate ) , m_cacheType( cacheType ) , m_fullPath( mrl ) { assert( ( mediaId == 0 && playlistId != 0 && subscriptionId == 0 ) || ( mediaId != 0 && playlistId == 0 && subscriptionId == 0 ) || ( mediaId == 0 && playlistId == 0 && subscriptionId != 0 ) ); } int64_t File::id() const { return m_id; } const std::string& File::mrl() const { if ( m_isRemovable == false ) return m_mrl; // If the file is removable, then it needs to have a parent folder assert( m_folderId != 0 ); if ( m_fullPath.empty() == false ) return m_fullPath; auto folder = Folder::fetch( m_ml, m_folderId ); if ( folder == nullptr ) { assert( !"Can't find the folder for an existing file" ); return m_mrl; } m_fullPath = folder->mrl() + m_mrl; return m_fullPath; } const std::string& File::rawMrl() const { return m_mrl; } void File::setMrl( std::string mrl ) { if ( m_mrl == mrl ) return; if ( setMrl( m_ml, mrl, m_id ) == false ) return; m_mrl = std::move( mrl ); } bool File::setMrl( MediaLibraryPtr ml, const std::string& mrl, int64_t fileId ) { const static std::string req = "UPDATE " + File::Table::Name + " SET " "mrl = ? WHERE id_file = ?"; return sqlite::Tools::executeUpdate( ml->getConn(), req, mrl, fileId ); } IFile::Type File::type() const { return m_type; } time_t File::lastModificationDate() const { return m_lastModificationDate; } uint64_t File::size() const { return m_size; } bool File::isExternal() const { return m_isExternal; } bool File::updateFsInfo( time_t newLastModificationDate, uint64_t newSize ) { if ( m_lastModificationDate == newLastModificationDate && m_size == newSize ) return true; const std::string req = "UPDATE " + File::Table::Name + " SET last_modification_date = ?, size = ? WHERE id_file = ?"; auto res = sqlite::Tools::executeUpdate( m_ml->getConn(), req, newLastModificationDate, newSize, m_id ); if ( res == true ) { m_lastModificationDate = newLastModificationDate; m_size = newSize; } return res; } bool File::isRemovable() const { return m_isRemovable; } bool File::isNetwork() const { return m_isNetwork; } bool File::isMain() const { return m_type == Type::Main || m_type == Type::Cache; } time_t File::insertionDate() const { return m_insertionDate; } IFile::CacheType File::cacheType() const { return m_cacheType; } std::shared_ptr File::media() const { if ( m_mediaId == 0 ) return nullptr; auto media = m_media.lock(); if ( media == nullptr ) { media = Media::fetch( m_ml, m_mediaId ); m_media = media; } return media; } int64_t File::mediaId() const { return m_mediaId; } bool File::setMediaId( int64_t mediaId ) { if ( mediaId == m_mediaId ) return true; const std::string req = "UPDATE " + Table::Name + " SET media_id = ? " "WHERE id_file = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, mediaId, m_id ) == false ) return false; m_mediaId = mediaId; return true; } bool File::setPlaylistId( int64_t playlistId ) { if ( playlistId == m_playlistId ) return true; const std::string req = "UPDATE " + Table::Name + " SET media_id = NULL " ", playlist_id = ? WHERE id_file = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, playlistId, m_id ) == false ) return false; m_playlistId = playlistId; return true; } bool File::destroy() { return DatabaseHelpers::destroy( m_ml, m_id ); } int64_t File::folderId() const { return m_folderId; } bool File::update( const fs::IFile& fileFs, int64_t folderId, bool isRemovable ) { const std::string req = "UPDATE " + Table::Name + " SET " "mrl = ?, last_modification_date = ?, size = ?, folder_id = ?, " "is_removable = ?, is_external = ?, is_network = ? WHERE id_file = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, isRemovable == true ? fileFs.name() : fileFs.mrl(), fileFs.lastModificationDate(), fileFs.size(), folderId, isRemovable, false, fileFs.isNetwork(), m_id ) == false ) return false; m_mrl = isRemovable == true ? fileFs.name() : fileFs.mrl(); m_fullPath = fileFs.mrl(); m_lastModificationDate = fileFs.lastModificationDate(); m_size = fileFs.size(); m_folderId = folderId; m_isRemovable = isRemovable; m_isExternal = false; m_isNetwork = fileFs.isNetwork(); return true; } bool File::convertToExternal() { const std::string req = "UPDATE " + Table::Name + " SET " "mrl = ?, folder_id = NULL, is_removable = 0, is_external = 1 WHERE id_file = ?"; return sqlite::Tools::executeUpdate( m_ml->getConn(), req, mrl(), m_id ); } FilePtr File::cache( const std::string& mrl, CacheType cacheType, uint64_t fileSize ) { if ( utils::url::schemeIs( "file://", mrl ) == false ) return nullptr; LOG_DEBUG( "Marking ", mrl, " as a cached MRL for file #", m_id ); return File::createForCache( m_ml, m_mediaId, mrl, fileSize > 0 ? fileSize : m_size, time(nullptr), cacheType ); } void File::createTable( sqlite::Connection* dbConnection ) { sqlite::Tools::executeRequest( dbConnection, schema( Table::Name, Settings::DbModelVersion ) ); } void File::createIndexes( sqlite::Connection* dbConnection ) { sqlite::Tools::executeRequest( dbConnection, index( Indexes::MediaId, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, index( Indexes::FolderId, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConnection, index( Indexes::PlaylistId, Settings::DbModelVersion ) ); } std::string File::schema( const std::string& tableName, uint32_t dbModel ) { UNUSED_IN_RELEASE( tableName ); assert( tableName == Table::Name ); if ( dbModel < 37 ) { return "CREATE TABLE " + Table::Name + "(" "id_file INTEGER PRIMARY KEY AUTOINCREMENT," "media_id UNSIGNED INT DEFAULT NULL," "playlist_id UNSIGNED INT DEFAULT NULL," "mrl TEXT," "type UNSIGNED INTEGER," "last_modification_date UNSIGNED INT," "size UNSIGNED INT," "folder_id UNSIGNED INTEGER," "is_removable BOOLEAN NOT NULL," "is_external BOOLEAN NOT NULL," "is_network BOOLEAN NOT NULL," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media) ON DELETE CASCADE," "FOREIGN KEY(playlist_id) REFERENCES " + Playlist::Table::Name + "(id_playlist) ON DELETE CASCADE," "FOREIGN KEY(folder_id) REFERENCES " + Folder::Table::Name + "(id_folder) ON DELETE CASCADE," "UNIQUE(mrl,folder_id) ON CONFLICT FAIL" ")"; } return "CREATE TABLE " + Table::Name + "(" "id_file INTEGER PRIMARY KEY AUTOINCREMENT," "media_id UNSIGNED INT DEFAULT NULL," "playlist_id UNSIGNED INT DEFAULT NULL," "mrl TEXT," "type UNSIGNED INTEGER," "last_modification_date UNSIGNED INT," "size UNSIGNED INT," "folder_id UNSIGNED INTEGER," "is_removable BOOLEAN NOT NULL," "is_external BOOLEAN NOT NULL," "is_network BOOLEAN NOT NULL," "subscription_id UNSIGNED INTEGER UNIQUE," "insertion_date UNSIGNED INTEGER," "cache_type UNSIGNED INTEGER NOT NULL DEFAULT " + utils::enum_to_string( CacheType::Uncached ) + "," "FOREIGN KEY(media_id) REFERENCES " + Media::Table::Name + "(id_media) ON DELETE CASCADE," "FOREIGN KEY(playlist_id) REFERENCES " + Playlist::Table::Name + "(id_playlist) ON DELETE CASCADE," "FOREIGN KEY(folder_id) REFERENCES " + Folder::Table::Name + "(id_folder) ON DELETE CASCADE," "FOREIGN KEY(subscription_id) REFERENCES " + Subscription::Table::Name + "(id_subscription) ON DELETE CASCADE," "UNIQUE(mrl,folder_id) ON CONFLICT FAIL" ")"; } std::string File::index( Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::MediaId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(media_id)"; case Indexes::FolderId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(folder_id)"; case Indexes::PlaylistId: assert( dbModel >= 34 ); return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(playlist_id)"; case Indexes::InsertionDate: assert( dbModel >= 37 ); return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + "(insertion_date)"; default: assert( !"Invalid index provided" ); } return ""; } std::string File::indexName( File::Indexes index, uint32_t dbModel ) { UNUSED_IN_RELEASE( dbModel ); switch ( index ) { case Indexes::MediaId: return "file_media_id_index"; case Indexes::FolderId: return "file_folder_id_index"; case Indexes::PlaylistId: assert( dbModel >= 34 ); return "file_playlist_id_idx"; case Indexes::InsertionDate: assert( dbModel >= 37 ); return "file_insertion_date_idx"; } return ""; } bool File::checkDbModel( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); return sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ) && sqlite::Tools::checkIndexStatement( index( Indexes::MediaId, Settings::DbModelVersion ), indexName( Indexes::MediaId, Settings::DbModelVersion ) ) && sqlite::Tools::checkIndexStatement( index( Indexes::FolderId, Settings::DbModelVersion ), indexName( Indexes::FolderId, Settings::DbModelVersion ) ) && sqlite::Tools::checkIndexStatement( index( Indexes::PlaylistId, Settings::DbModelVersion ), indexName( Indexes::PlaylistId, Settings::DbModelVersion ) ); } std::shared_ptr File::createFromMedia( MediaLibraryPtr ml, int64_t mediaId, Type type, const fs::IFile& fileFs, int64_t folderId, bool isRemovable, time_t insertionDate ) { assert( mediaId > 0 ); auto self = std::make_shared( ml, mediaId, 0, type, fileFs, folderId, isRemovable, insertionDate ); static const std::string req = "INSERT INTO " + File::Table::Name + "(media_id, mrl, type, folder_id, last_modification_date, size, " "is_removable, is_external, is_network, insertion_date) " "VALUES(?, ?, ?, ?, ?, ?, ?, 0, ?, ?)"; if ( insert( ml, self, req, mediaId, self->m_mrl, type, sqlite::ForeignKey( folderId ), self->m_lastModificationDate, self->m_size, isRemovable, self->m_isNetwork, insertionDate ) == false ) return nullptr; self->m_fullPath = fileFs.mrl(); return self; } std::shared_ptr File::createFromExternalMedia( MediaLibraryPtr ml, int64_t mediaId, IFile::Type type, const std::string& mrl, int64_t fileSize, time_t insertionDate ) { assert( mediaId > 0 ); // Sqlite won't ensure uniqueness for (folder_id, mrl) when folder_id is null, so we have to ensure // of it ourselves static const std::string existingReq = "SELECT * FROM " + File::Table::Name + " WHERE folder_id IS NULL AND mrl = ?"; auto existing = fetch( ml, existingReq, mrl ); if ( existing != nullptr ) return nullptr; auto self = std::make_shared( ml, mediaId, 0, 0, type, mrl, fileSize, insertionDate, CacheType::Uncached ); static const std::string req = "INSERT INTO " + File::Table::Name + "(media_id, mrl, type, size, folder_id, is_removable, is_external," "is_network, subscription_id, insertion_date) " "VALUES(?, ?, ?, ?, NULL, 0, 1, ?, ?, ?)"; if ( insert( ml, self, req, mediaId, mrl, type, fileSize, self->m_isNetwork, nullptr, insertionDate ) == false ) return nullptr; return self; } std::shared_ptr File::createForCache( MediaLibraryPtr ml, int64_t mediaId, const std::string& mrl, int64_t fileSize, time_t insertionDate, CacheType cacheType ) { /* We *need* a file size to maintain a cache that abides by the user's settings */ if ( fileSize == 0 ) return nullptr; assert( mediaId > 0 ); auto self = std::make_shared( ml, mediaId, 0, 0, Type::Cache, mrl, fileSize, insertionDate, cacheType ); static const std::string req = "INSERT INTO " + File::Table::Name + "(media_id, mrl, type, size, folder_id, is_removable, is_external," "is_network, insertion_date, cache_type) " "VALUES(?, ?, ?, ?, NULL, 0, 1, 0, ?, ?)"; if ( insert( ml, self, req, mediaId, mrl, self->m_type, fileSize, insertionDate, cacheType ) == false ) return nullptr; return self; } std::shared_ptr File::createFromPlaylist( MediaLibraryPtr ml, int64_t playlistId, const fs::IFile& fileFs, int64_t folderId, bool isRemovable, time_t insertionDate ) { assert( playlistId > 0 ); const auto type = IFile::Type::Playlist; auto self = std::make_shared( ml, 0, playlistId, type , fileFs, folderId, isRemovable, insertionDate ); static const std::string req = "INSERT INTO " + File::Table::Name + "(playlist_id, mrl, type, folder_id, last_modification_date, size, " "is_removable, is_external, is_network, insertion_date) " "VALUES(?, ?, ?, ?, ?, ?, ?, 0, ?, ?)"; if ( insert( ml, self, req, playlistId, self->m_mrl, type, sqlite::ForeignKey( folderId ), self->m_lastModificationDate, self->m_size, isRemovable, self->m_isNetwork, insertionDate ) == false ) return nullptr; self->m_fullPath = fileFs.mrl(); return self; } std::shared_ptr File::createFromSubscription( MediaLibraryPtr ml, std::string mrl, int64_t subscriptionId ) { assert( subscriptionId > 0 ); auto self = std::make_shared( ml, 0, 0, subscriptionId, IFile::Type::Subscription, mrl, 0, time(nullptr), CacheType::Uncached ); const std::string req = "INSERT INTO " + Table::Name + "(mrl, type, is_removable, is_external, is_network, subscription_id) " "VALUES(?, ?, ?, ?, ?, ?)"; if ( insert( ml, self, req, self->mrl(), IFile::Type::Subscription, false, true, false, subscriptionId ) == false ) return nullptr; self->m_fullPath = self->mrl(); return self; } bool File::exists( MediaLibraryPtr ml, const std::string& mrl ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); sqlite::Statement stmt{ "SELECT EXISTS(" "SELECT id_file FROM " + Table::Name + " WHERE mrl = ?)" }; stmt.execute( mrl ); auto row = stmt.row(); auto res = row.extract(); assert( stmt.row() == nullptr ); return res; } std::shared_ptr File::fromMrl( MediaLibraryPtr ml, const std::string& mrl ) { /* Be optimistic and attempt to fetch a non-removable file first */ static const std::string req = "SELECT * FROM " + File::Table::Name + " WHERE mrl = ? AND folder_id IS NOT NULL"; auto file = fetch( ml, req, mrl ); if ( file != nullptr ) { // safety checks: since this only works for files on non removable devices // isRemovable must be false assert( file->m_isRemovable == false ); return file; } /* * Otherwise, fallback to constructing the mrl based on the device that * stores it */ auto folder = Folder::fromMrl( ml, utils::file::directory( mrl ) ); if ( folder == nullptr ) { LOG_DEBUG( "Failed to find folder containing ", mrl ); return nullptr; } file = fromFileName( ml, utils::file::fileName( mrl ), folder->id() ); if ( file == nullptr ) { LOG_DEBUG( "Failed to fetch file for ", mrl ); } return file; } std::shared_ptr File::fromFileName( MediaLibraryPtr ml, const std::string& fileName, int64_t folderId ) { static const std::string req = "SELECT * FROM " + File::Table::Name + " WHERE mrl = ? AND folder_id = ?"; auto file = fetch( ml, req, fileName, folderId ); if ( file == nullptr ) return nullptr; assert( file->m_isRemovable == true ); return file; } std::shared_ptr File::fromExternalMrl( MediaLibraryPtr ml, const std::string& mrl ) { std::string scheme; try { scheme = utils::url::scheme( mrl ); } catch ( const fs::errors::UnhandledScheme& ) { return nullptr; } static const std::string req = "SELECT * FROM " + File::Table::Name + " WHERE mrl = ? AND folder_id IS NULL"; auto file = fetch( ml, req, mrl ); if ( file == nullptr ) return nullptr; assert( file->m_isExternal == true ); return file; } std::vector> File::fromParentFolder( MediaLibraryPtr ml, int64_t parentFolderId ) { static const std::string req = "SELECT * FROM " + File::Table::Name + " WHERE folder_id = ?"; return File::fetchAll( ml, req, parentFolderId ); } std::vector> File::cachedFiles( MediaLibraryPtr ml ) { const std::string req = "SELECT * FROM " + Table::Name + " WHERE type = ? ORDER BY insertion_date ASC"; return fetchAll( ml, req, Type::Cache ); } std::string File::cachedFileName() const { return std::to_string( m_id ) + "_" + utils::url::decode( utils::file::fileName( m_mrl ) ); } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/File.h000066400000000000000000000167111501065546500234560ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "medialibrary/IFile.h" #include "database/DatabaseHelpers.h" namespace medialibrary { class Media; class File : public IFile, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t File::*const PrimaryKey; }; enum class Indexes : uint8_t { MediaId, FolderId, PlaylistId, InsertionDate, }; File( MediaLibraryPtr ml, sqlite::Row& row ); File( MediaLibraryPtr ml, int64_t mediaId, int64_t playlistId, Type type, const fs::IFile& file, int64_t folderId, bool isRemovable, time_t insertionDate ); File( MediaLibraryPtr ml, int64_t mediaId, int64_t playlistId, int64_t subscriptionId, Type type, const std::string& mrl, int64_t fileSize, time_t insertionDate, CacheType cacheType ); virtual int64_t id() const override; virtual const std::string& mrl() const override; /** * @brief rawMrl returns the raw mrl, ie. the mrl from the mountpoint. * This is the same as MRL for files on non removable devices. * This is meant to be used when fiddling with the value stored in database * during a migration, but shouldn't be used otherwise, as it would be unusable */ const std::string& rawMrl() const; void setMrl( std::string mrl ); static bool setMrl(MediaLibraryPtr ml, const std::string &mrl, int64_t fileId ); virtual Type type() const override; virtual time_t lastModificationDate() const override; virtual uint64_t size() const override; virtual bool isExternal() const override; bool updateFsInfo( time_t newLastModificationDate, uint64_t newSize ); virtual bool isRemovable() const override; virtual bool isNetwork() const override; virtual bool isMain() const override; virtual time_t insertionDate() const override; virtual CacheType cacheType() const override; std::shared_ptr media() const; int64_t mediaId() const; /** * @brief setMediaId Associates this file with a different media * * If this was the previous media's main file, it will be deleted. */ bool setMediaId( int64_t mediaId ); bool setPlaylistId( int64_t playlistId ); bool destroy(); int64_t folderId() const; bool update( const fs::IFile& fileFs, int64_t folderId, bool isRemovable ); bool convertToExternal(); FilePtr cache( const std::string& mrl, CacheType cacheType, uint64_t fileSize ); std::string cachedFileName() const; static void createTable( sqlite::Connection* dbConnection ); static void createIndexes( sqlite::Connection* dbConnection ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static std::string index( Indexes index, uint32_t dbModel ); static std::string indexName( Indexes index, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr createFromMedia( MediaLibraryPtr ml, int64_t mediaId, Type type, const fs::IFile& file, int64_t folderId, bool isRemovable, time_t insertionDate ); static std::shared_ptr createFromExternalMedia( MediaLibraryPtr ml, int64_t mediaId, Type type, const std::string& mrl, int64_t fileSize, time_t insertionDate ); static std::shared_ptr createFromPlaylist( MediaLibraryPtr ml, int64_t playlistId, const fs::IFile& file, int64_t folderId, bool isRemovable, time_t insertionDate ); static std::shared_ptr createFromSubscription( MediaLibraryPtr ml, std::string mrl, int64_t subscriptionId ); std::shared_ptr createForCache( MediaLibraryPtr ml, int64_t mediaId, const std::string& mrl, int64_t fileSize, time_t insertionDate, CacheType cacheType ); static bool exists( MediaLibraryPtr ml, const std::string& mrl ); /** * @brief fromPath Attempts to fetch a file using its mrl * This will only work if the file was stored on a non removable device * @param path The wanted file mrl * @return A pointer to the wanted file, or nullptr if it wasn't found */ static std::shared_ptr fromMrl( MediaLibraryPtr ml, const std::string& mrl ); /** * @brief fromFileName Attempts to fetch a file based on its filename and folder id * @param ml * @param fileName * @param folderId * @return */ static std::shared_ptr fromFileName( MediaLibraryPtr ml, const std::string& fileName, int64_t folderId ); /** * @brief fromMrl Attempts to find an external stream (ie. it was added with MediaLibrary::addMedia, * and not discovered through any IDiscoverer) * This implies the folder_id is null * @return */ static std::shared_ptr fromExternalMrl( MediaLibraryPtr ml, const std::string& mrl ); /** * @brief fromParentFolder Returns a vector of the known file in a given folder * @param ml The medialibrary instance pointer * @param parentFolderId The parent folder ID */ static std::vector> fromParentFolder( MediaLibraryPtr ml, int64_t parentFolderId ); static std::vector> cachedFiles( MediaLibraryPtr ml ); private: MediaLibraryPtr m_ml; int64_t m_id; int64_t m_mediaId; int64_t m_playlistId; // Contains the path relative to the containing folder for files contained in a removable folder // or the full file MRL for non removable ones std::string m_mrl; const Type m_type; time_t m_lastModificationDate; uint64_t m_size; int64_t m_folderId; bool m_isRemovable; bool m_isExternal; bool m_isNetwork; int64_t m_subscriptionId; time_t m_insertionDate; CacheType m_cacheType; // Contains the full path as a MRL mutable std::string m_fullPath; mutable std::weak_ptr m_media; friend File::Table; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Folder.cpp000066400000000000000000001303001501065546500243340ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "File.h" #include "Folder.h" #include "Device.h" #include "Media.h" #include "Playlist.h" #include "database/SqliteTools.h" #include "database/SqliteQuery.h" #include "medialibrary/filesystem/IDirectory.h" #include "medialibrary/filesystem/IDevice.h" #include "medialibrary/filesystem/IFileSystemFactory.h" #include "medialibrary/filesystem/Errors.h" #include "utils/Filename.h" #include "utils/Url.h" #include "utils/Enums.h" #include "utils/ModificationsNotifier.h" #include namespace medialibrary { const std::string Folder::Table::Name = "Folder"; const std::string Folder::Table::PrimaryKeyColumn = "id_folder"; int64_t Folder::* const Folder::Table::PrimaryKey = &Folder::m_id; const std::string Folder::FtsTable::Name = "FolderFts"; const std::string Folder::ExcludedFolderTable::Name = "ExcludedEntryFolder"; Folder::Folder( MediaLibraryPtr ml, sqlite::Row& row ) : m_ml( ml ) , m_id( row.extract() ) , m_path( row.extract() ) , m_name( row.extract() ) , m_parent( row.extract() ) , m_isBanned( row.extract() ) , m_deviceId( row.extract() ) , m_isRemovable( row.extract() ) , m_nbAudio( row.extract() ) , m_nbVideo( row.extract() ) /* * 15 to 16 migration uses fetchAll, meaning that the code need to adapt to * the missing column, or we need to reimplement the migration without using * Folder instances. */ , m_duration( row.hasRemainingColumns() == true ? row.extract() : 0 ) , m_isPublic( row.hasRemainingColumns() == true ? row.extract() : false ) , m_isFavorite( row.hasRemainingColumns() == true ? row.extract() : false ) { if ( row.hasRemainingColumns() == true ) m_publicOnlyListing = row.extract(); else m_publicOnlyListing = false; assert( row.hasRemainingColumns() == false ); } Folder::Folder(MediaLibraryPtr ml, std::string path, std::string name, int64_t parent, int64_t deviceId, bool isRemovable ) : m_ml( ml ) , m_id( 0 ) , m_path( std::move( path ) ) , m_name( std::move( name ) ) , m_parent( parent ) , m_isBanned( false ) , m_deviceId( deviceId ) , m_isRemovable( isRemovable ) , m_nbAudio( 0 ) , m_nbVideo( 0 ) , m_duration( 0 ) , m_isPublic( false ) , m_isFavorite( false ) , m_publicOnlyListing( false ) { } void Folder::createTable( sqlite::Connection* connection) { const std::string reqs[] = { schema( Table::Name, Settings::DbModelVersion ), schema( FtsTable::Name, Settings::DbModelVersion ), }; for ( const auto& req : reqs ) sqlite::Tools::executeRequest( connection, req ); } void Folder::createTriggers( sqlite::Connection* connection ) { sqlite::Tools::executeRequest( connection, trigger( Triggers::InsertFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( connection, trigger( Triggers::DeleteFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( connection, trigger( Triggers::UpdateNbMediaOnIndex, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( connection, trigger( Triggers::UpdateNbMediaOnDelete, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( connection, trigger( Triggers::UpdateNbMediaOnUpdate, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( connection, trigger( Triggers::UpdateIsPublic, Settings::DbModelVersion ) ); } void Folder::createIndexes( sqlite::Connection* connection ) { sqlite::Tools::executeRequest( connection, index( Indexes::DeviceId, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( connection, index( Indexes::ParentId, Settings::DbModelVersion ) ); } std::string Folder::schema( const std::string& tableName, uint32_t dbModel ) { if ( tableName == FtsTable::Name ) { return "CREATE VIRTUAL TABLE " + FtsTable::Name + " USING FTS3(name)"; } else if ( tableName == ExcludedFolderTable::Name ) { assert( dbModel < 30 ); return "CREATE TABLE " + ExcludedFolderTable::Name + "(" "folder_id UNSIGNED INTEGER NOT NULL," "FOREIGN KEY(folder_id) REFERENCES " + Table::Name + "(id_folder) ON DELETE CASCADE," "UNIQUE(folder_id) ON CONFLICT FAIL" ")"; } assert( tableName == Table::Name ); if ( dbModel < 37 ) { return "CREATE TABLE " + Table::Name + "(" "id_folder INTEGER PRIMARY KEY AUTOINCREMENT," "path TEXT," "name TEXT" + (dbModel >= 15 ? " COLLATE NOCASE" : "") + "," "parent_id UNSIGNED INTEGER," "is_banned BOOLEAN NOT NULL DEFAULT 0," "device_id UNSIGNED INTEGER," "is_removable BOOLEAN NOT NULL," "nb_audio UNSIGNED INTEGER NOT NULL DEFAULT 0," "nb_video UNSIGNED INTEGER NOT NULL DEFAULT 0," "FOREIGN KEY(parent_id) REFERENCES " + Table::Name + "(id_folder) ON DELETE CASCADE," "FOREIGN KEY(device_id) REFERENCES " + Device::Table::Name + "(id_device) ON DELETE CASCADE," "UNIQUE(path,device_id) ON CONFLICT FAIL" ")"; } if ( dbModel < 38 ) { return "CREATE TABLE " + Table::Name + "(" "id_folder INTEGER PRIMARY KEY AUTOINCREMENT," "path TEXT," "name TEXT COLLATE NOCASE," "parent_id UNSIGNED INTEGER," "is_banned BOOLEAN NOT NULL DEFAULT 0," "device_id UNSIGNED INTEGER," "is_removable BOOLEAN NOT NULL," "nb_audio UNSIGNED INTEGER NOT NULL DEFAULT 0," "nb_video UNSIGNED INTEGER NOT NULL DEFAULT 0," "duration UNSIGNED INTEGER NOT NULL DEFAULT 0," "is_public BOOLEAN NOT NULL," "FOREIGN KEY(parent_id) REFERENCES " + Table::Name + "(id_folder) ON DELETE CASCADE," "FOREIGN KEY(device_id) REFERENCES " + Device::Table::Name + "(id_device) ON DELETE CASCADE," "UNIQUE(path,device_id) ON CONFLICT FAIL" ")"; } return "CREATE TABLE " + Table::Name + "(" "id_folder INTEGER PRIMARY KEY AUTOINCREMENT," "path TEXT," "name TEXT COLLATE NOCASE," "parent_id UNSIGNED INTEGER," "is_banned BOOLEAN NOT NULL DEFAULT 0," "device_id UNSIGNED INTEGER," "is_removable BOOLEAN NOT NULL," "nb_audio UNSIGNED INTEGER NOT NULL DEFAULT 0," "nb_video UNSIGNED INTEGER NOT NULL DEFAULT 0," "duration UNSIGNED INTEGER NOT NULL DEFAULT 0," "is_public BOOLEAN NOT NULL," " is_favorite BOOLEAN NOT NULL DEFAULT FALSE," "FOREIGN KEY(parent_id) REFERENCES " + Table::Name + "(id_folder) ON DELETE CASCADE," "FOREIGN KEY(device_id) REFERENCES " + Device::Table::Name + "(id_device) ON DELETE CASCADE," "UNIQUE(path,device_id) ON CONFLICT FAIL" ")"; } std::string Folder::trigger( Triggers trigger, uint32_t dbModel ) { switch ( trigger ) { case Triggers::InsertFts: return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER INSERT ON " + Table::Name + " " "BEGIN " "INSERT INTO " + FtsTable::Name + "(rowid,name) " "VALUES(new.id_folder,new.name);" "END"; case Triggers::DeleteFts: return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " BEFORE DELETE ON " + Table::Name + " " "BEGIN " "DELETE FROM " + FtsTable::Name + " WHERE rowid = old.id_folder;" "END"; case Triggers::UpdateNbMediaOnIndex: assert( dbModel >= 14 ); return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER INSERT ON " + Media::Table::Name + " WHEN new.folder_id IS NOT NULL " "BEGIN " "UPDATE " + Table::Name + " SET " "nb_audio = nb_audio + " "(CASE new.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN 1 " "ELSE 0 " "END)," "nb_video = nb_video + " "(CASE new.type WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN 1 " "ELSE 0 " "END) " "WHERE id_folder = new.folder_id;" "END"; case Triggers::UpdateNbMediaOnUpdate: assert( dbModel >= 14 ); if ( dbModel <= 30 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE ON " + Media::Table::Name + " WHEN new.folder_id IS NOT NULL AND old.type != new.type " "BEGIN " "UPDATE " + Table::Name + " SET " "nb_audio = nb_audio + " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN -1 " "ELSE 0 " "END)" "+" "(CASE new.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN 1 " "ELSE 0 " "END)" "," "nb_video = nb_video + " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN -1 " "ELSE 0 " "END)" "+" "(CASE new.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN 1 " "ELSE 0 " "END)" "WHERE id_folder = new.folder_id;" "END"; } if ( dbModel < 37 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF folder_id, type ON " + Media::Table::Name + " WHEN IFNULL(old.folder_id, 0) != IFNULL(new.folder_id, 0)" " OR old.type != new.type" " BEGIN" /* Increment the count for the new type/folder_id */ " UPDATE " + Table::Name + " SET" " nb_audio = nb_audio + " "(CASE new.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN 1 " "ELSE 0 " "END)" "," "nb_video = nb_video + " "(CASE new.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN 1 " "ELSE 0 " "END)" "WHERE new.folder_id IS NOT NULL AND id_folder = new.folder_id;" /* And decrement the count for the old type/folder_id */ "UPDATE " + Table::Name + " SET" " nb_audio = nb_audio - " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN -1 " "ELSE 0 " "END)" "," "nb_video = nb_video - " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN 1 " "ELSE 0 " "END)" "WHERE old.folder_id IS NOT NULL AND id_folder = old.folder_id;" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF folder_id, type, duration ON " + Media::Table::Name + " WHEN IFNULL(old.folder_id, 0) != IFNULL(new.folder_id, 0)" " OR old.type != new.type OR old.duration != new.duration" " BEGIN" /* Increment the count for the new type/folder_id */ " UPDATE " + Table::Name + " SET" " nb_audio = nb_audio + " "(CASE new.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN 1 " "ELSE 0 " "END)" "," "nb_video = nb_video + " "(CASE new.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN 1 " "ELSE 0 " "END)" "," "duration = duration + IIF(new.duration > 0, new.duration, 0) " "WHERE new.folder_id IS NOT NULL AND id_folder = new.folder_id;" /* And decrement the count for the old type/folder_id */ "UPDATE " + Table::Name + " SET" " nb_audio = nb_audio - " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN -1 " "ELSE 0 " "END)" "," "nb_video = nb_video - " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN 1 " "ELSE 0 " "END)" "," "duration = duration - IIF(old.duration > 0, old.duration, 0) " "WHERE old.folder_id IS NOT NULL AND id_folder = old.folder_id;" " END"; case Triggers::UpdateNbMediaOnDelete: assert( dbModel >= 14 ); if ( dbModel < 37 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER DELETE ON " + Media::Table::Name + " WHEN old.folder_id IS NOT NULL " "BEGIN " "UPDATE " + Table::Name + " SET " "nb_audio = nb_audio + " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN -1 " "ELSE 0 " "END)," "nb_video = nb_video + " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN -1 " "ELSE 0 " "END) " "WHERE id_folder = old.folder_id;" "END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER DELETE ON " + Media::Table::Name + " WHEN old.folder_id IS NOT NULL " "BEGIN " "UPDATE " + Table::Name + " SET " "nb_audio = nb_audio + " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Audio ) + " THEN -1 " "ELSE 0 " "END)," "nb_video = nb_video + " "(CASE old.type " "WHEN " + utils::enum_to_string( IMedia::Type::Video ) + " THEN -1 " "ELSE 0 " "END)," "duration = duration - IIF(old.duration > 0, old.duration, 0) " "WHERE id_folder = old.folder_id;" "END"; case Triggers::UpdateIsPublic: assert( dbModel >= 37 ); return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF is_public ON " + Table::Name + " WHEN new.is_public != old.is_public" " BEGIN" " UPDATE " + Table::Name + " SET is_public = new.is_public" " WHERE parent_id = new.id_folder;" " END"; default: assert( !"Invalid trigger provided" ); } return ""; } std::string Folder::triggerName( Triggers trigger, uint32_t dbModel ) { switch ( trigger ) { case Triggers::InsertFts: return "insert_folder_fts"; case Triggers::DeleteFts: return "delete_folder_fts"; case Triggers::UpdateNbMediaOnIndex: assert( dbModel >= 14 ); return "update_folder_nb_media_on_insert"; case Triggers::UpdateNbMediaOnUpdate: assert( dbModel >= 14 ); if ( dbModel <= 30 ) return "update_folder_nb_media_on_update"; return "folder_update_nb_media_on_media_update"; case Triggers::UpdateNbMediaOnDelete: assert( dbModel >= 14 ); return "update_folder_nb_media_on_delete"; case Triggers::UpdateIsPublic: assert( dbModel >= 37 ); return "folder_update_is_public"; default: assert( !"Invalid trigger provided" ); } return ""; } std::string Folder::index( Indexes index, uint32_t dbModel ) { switch ( index ) { case Indexes::DeviceId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + " (device_id)"; case Indexes::ParentId: return "CREATE INDEX " + indexName( index, dbModel ) + " ON " + Table::Name + " (parent_id)"; default: assert( !"Invalid index provided" ); } return ""; } std::string Folder::indexName( Indexes index, uint32_t ) { switch ( index ) { case Indexes::DeviceId: return "folder_device_id_idx"; case Indexes::ParentId: return "parent_folder_id_idx"; default: assert( !"Invalid index provided" ); } return ""; } bool Folder::checkDbModel( MediaLibraryPtr ml ) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); if ( sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ) == false || sqlite::Tools::checkTableSchema( schema( FtsTable::Name, Settings::DbModelVersion ), FtsTable::Name ) == false ) return false; auto check = []( Triggers t ) { return sqlite::Tools::checkTriggerStatement( trigger( t, Settings::DbModelVersion ), triggerName( t, Settings::DbModelVersion ) ); }; if ( check( Triggers::InsertFts ) == false || check( Triggers::DeleteFts ) == false || check( Triggers::UpdateNbMediaOnIndex ) == false || check( Triggers::UpdateNbMediaOnUpdate ) == false || check( Triggers::UpdateNbMediaOnDelete ) == false || check( Triggers::UpdateIsPublic ) == false ) return false; return sqlite::Tools::checkIndexStatement( index( Indexes::DeviceId, Settings::DbModelVersion ), indexName( Indexes::DeviceId, Settings::DbModelVersion ) ) && sqlite::Tools::checkIndexStatement( index( Indexes::ParentId, Settings::DbModelVersion ), indexName( Indexes::ParentId, Settings::DbModelVersion ) ); } std::shared_ptr Folder::create( MediaLibraryPtr ml, const std::string& mrl, int64_t parentId, const Device& device, fs::IDevice& deviceFs ) { std::string path; std::string name = utils::url::decode( utils::file::directoryName( mrl ) ); if ( device.isRemovable() == true ) path = deviceFs.relativeMrl( mrl ); else path = mrl; auto self = std::make_shared( ml, std::move( path ), std::move( name ), parentId, device.id(), deviceFs.isRemovable() ); static const std::string req = "INSERT INTO " + Folder::Table::Name + "(path, name, parent_id, device_id, is_removable, is_public) VALUES(?, ?, ?, ?, ?, ?)"; if ( insert( ml, self, req, self->m_path, self->m_name, sqlite::ForeignKey( parentId ), device.id(), deviceFs.isRemovable(), self->m_isPublic ) == false ) return nullptr; if ( device.isRemovable() == true ) self->m_fullPath = deviceFs.absoluteMrl( self->m_path ); auto notifier = ml->getNotifier(); if ( notifier != nullptr ) notifier->notifyFolderCreation( self ); return self; } bool Folder::ban( MediaLibraryPtr ml, const std::string& mrl ) { auto t = ml->getConn()->newTransaction(); auto f = fromMrl( ml, mrl, BannedType::Any ); if ( f != nullptr ) { // No need to ban a folder twice if ( f->m_isBanned == true ) return true; if ( remove( ml, std::move( f ), RemovalBehavior::RemovedFromDisk ) == false ) return false; } auto fsFactory = ml->fsFactoryForMrl( mrl ); if ( fsFactory == nullptr ) return false; std::shared_ptr folderFs; try { folderFs = fsFactory->createDirectory( mrl ); } catch ( const fs::errors::System& ex ) { LOG_ERROR( "Failed to instantiate a directory to ban folder: ", ex.what() ); return false; } auto deviceFs = folderFs->device(); if ( deviceFs == nullptr ) { LOG_ERROR( "Can't find device associated with mrl ", mrl ); return false; } auto device = Device::fromUuid( ml, deviceFs->uuid(), fsFactory->scheme() ); if ( device == nullptr ) device = Device::create( ml, deviceFs->uuid(), utils::url::scheme( mrl ), deviceFs->isRemovable(), deviceFs->isNetwork() ); std::string path; if ( deviceFs->isRemovable() == true ) path = deviceFs->relativeMrl( mrl ); else path = mrl; static const std::string req = "INSERT INTO " + Folder::Table::Name + "(path, parent_id, is_banned, device_id, is_removable, is_public) " "VALUES(?, ?, ?, ?, ?, FALSE)"; auto res = sqlite::Tools::executeInsert( ml->getConn(), req, path, nullptr, true, device->id(), deviceFs->isRemovable() ) != 0; if ( res == true ) t->commit(); return res; } std::shared_ptr Folder::fromMrl( MediaLibraryPtr ml, const std::string& mrl ) { return fromMrl( ml, mrl, BannedType::No ); } std::shared_ptr Folder::bannedFolder( MediaLibraryPtr ml, const std::string& mrl ) { return fromMrl( ml, mrl, BannedType::Yes ); } std::shared_ptr Folder::fromMrl( MediaLibraryPtr ml, const std::string& mrl, BannedType bannedType ) { if ( mrl.empty() == true ) return nullptr; auto fsFactory = ml->fsFactoryForMrl( mrl ); if ( fsFactory == nullptr ) return nullptr; std::shared_ptr deviceFs; std::shared_ptr folderFs; try { /* * It's ok to instantiate a fs::IFolder even though the fs factories are * not started, since no actual FS interaction will happen by doing so. * This allows us to use the sanitized mrl that's returned * by fs::IFolder::mrl() (decoded & reencoded to ensure it matches our * encoding scheme) */ folderFs = fsFactory->createDirectory( mrl ); } catch ( const fs::errors::System& ex ) { LOG_ERROR( "Failed to instantiate a folder for mrl: ", mrl, ": ", ex.what() ); return nullptr; } if ( fsFactory->isStarted() == true ) { /* If the fs factory is started, we can probe the devices it knows */ deviceFs = folderFs->device(); if ( deviceFs == nullptr ) { LOG_WARN( "Failed to get device containing an existing folder: ", folderFs->mrl() ); return nullptr; } } int64_t deviceId; std::string path; if ( deviceFs != nullptr ) { if ( deviceFs->isRemovable() == false ) { std::string req = "SELECT * FROM " + Folder::Table::Name + " WHERE path = ? AND is_removable = 0"; if ( bannedType == BannedType::Any ) return fetch( ml, req, folderFs->mrl() ); req += " AND is_banned = ?"; return fetch( ml, req, folderFs->mrl(), bannedType == BannedType::Yes ? true : false ); } auto device = Device::fromUuid( ml, deviceFs->uuid(), fsFactory->scheme() ); // We are trying to find a folder. If we don't know the device it's on, we don't know the folder. if ( device == nullptr ) return nullptr; path = deviceFs->relativeMrl( folderFs->mrl() ); deviceId = device->id(); } else { /* * If it's not started, or if the device was unknown, we can try to * probe the previously known mountpoints that were stored in database */ auto deviceTuple = Device::fromMountpoint( ml, mrl ); deviceId = std::get<0>( deviceTuple ); if ( deviceId == 0 ) return nullptr; path = utils::file::removePath( mrl, std::get<1>( deviceTuple ) ); } std::string req = "SELECT * FROM " + Folder::Table::Name + " WHERE path = ? AND device_id = ?"; std::shared_ptr folder; if ( bannedType == BannedType::Any ) { folder = fetch( ml, req, path, deviceId ); } else { req += " AND is_banned = ?"; folder = fetch( ml, req, path, deviceId, bannedType == BannedType::Yes ? true : false ); } if ( folder == nullptr ) return nullptr; folder->m_fullPath = deviceFs != nullptr ? deviceFs->absoluteMrl( path ) : mrl; return folder; } std::string Folder::sortRequest( const QueryParameters* params ) { auto sort = params != nullptr ? params->sort : SortingCriteria::Default; auto desc = params != nullptr ? params->desc : false; std::string req = "ORDER BY "; switch ( sort ) { case SortingCriteria::NbVideo: req += "nb_video"; desc = !desc; break; case SortingCriteria::NbAudio: req += "nb_audio"; desc = !desc; break; case SortingCriteria::NbMedia: req += "(nb_audio + nb_video)"; desc = !desc; break; default: LOG_WARN( "Unsupported sorting criteria, falling back to Default (alpha)" ); /* fall-through */ case SortingCriteria::Default: case SortingCriteria::Alpha: req += "name"; } if ( desc == true ) req += " DESC"; return req; } std::string Folder::filterByMediaType( IMedia::Type type ) { switch ( type ) { case IMedia::Type::Audio: return " f.nb_audio > 0"; case IMedia::Type::Video: return " f.nb_video > 0"; default: assert( !"Only Audio/Video/Unknown types are supported when listing folders" ); /* Fall-through */ case IMedia::Type::Unknown: return " (f.nb_audio > 0 OR f.nb_video > 0)"; } } std::shared_ptr Folder::device() const { if ( m_device == nullptr ) { m_device = Device::fetch( m_ml, m_deviceId ); // There must be a device containing the folder, since we never create a folder // without a device assert( m_device != nullptr ); } return m_device; } bool Folder::ban() { const std::string req = "UPDATE " + Table::Name + " SET is_banned = 1 WHERE id_folder = ?"; return sqlite::Tools::executeUpdate( m_ml->getConn(), req, m_id ); } Query Folder::withMedia( MediaLibraryPtr ml, IMedia::Type type, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " f "; auto includeMissing = params != nullptr && params->includeMissing; if ( includeMissing == false ) { req += " LEFT JOIN " + Device::Table::Name + " d ON d.id_device = f.device_id "; } req += " WHERE " + filterByMediaType( type ); if ( includeMissing == false ) req += " AND d.is_present != 0"; auto publicOnly = params != nullptr && params->publicOnly == true; if ( publicOnly ) req += " AND d.is_public != 0"; const bool favoriteOnly = params != nullptr && params->favoriteOnly == true; if ( favoriteOnly == true ) req += " AND f.is_favorite = TRUE"; return make_query( ml, "f.*", req, sortRequest( params ) ) .markPublic( publicOnly ).build(); } Query Folder::searchWithMedia( MediaLibraryPtr ml, const std::string& pattern, IMedia::Type type, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " f "; auto includeMissing = params != nullptr && params->includeMissing; if ( includeMissing == false ) { req += " LEFT JOIN " + Device::Table::Name + " d ON d.id_device = f.device_id "; } req += "WHERE f.id_folder IN (SELECT rowid FROM " + FtsTable::Name + " WHERE " + FtsTable::Name + " MATCH ?) "; if ( includeMissing == false ) req += "AND d.is_present != 0 "; auto publicOnly = params != nullptr && params->publicOnly == true; if ( publicOnly == true ) req += " AND d.is_public != 0"; req += "AND " + filterByMediaType( type ); const bool favoriteOnly = params != nullptr && params->favoriteOnly == true; if ( favoriteOnly == true ) req += " AND f.is_favorite = TRUE"; return make_query( ml, "f.*", req, sortRequest( params ), sqlite::Tools::sanitizePattern( pattern ) ) .markPublic( publicOnly ).build(); } Query Folder::roots( MediaLibraryPtr ml, bool banned, int64_t deviceId, const QueryParameters* params ) { std::string req = "FROM " + Folder::Table::Name + " f WHERE" " f.is_banned = ?"; auto publicOnly = params != nullptr && params->publicOnly == true; if ( publicOnly == true ) { req += " AND f.is_public != 0 AND" " (parent_id IS NULL OR" " NOT EXISTS (SELECT TRUE FROM " + Table::Name + " WHERE is_public != 0 AND id_folder = f.parent_id))"; } else req += " AND parent_id IS NULL"; const bool favoriteOnly = params != nullptr && params->favoriteOnly == true; if ( favoriteOnly == true ) req += " AND f.is_favorite = TRUE"; if ( deviceId == 0 ) return make_query( ml, "f.*", req, "", banned ) .markPublic( publicOnly ).build(); req += " AND device_id = ?"; return make_query( ml, "f.*", req, "", banned, deviceId ) .markPublic( publicOnly ).build(); } bool Folder::remove( MediaLibraryPtr ml, std::shared_ptr folder, RemovalBehavior behavior ) { if ( behavior == RemovalBehavior::RemovedFromDisk ) { /* We expect a banned folder to no have any media linked with it */ assert( folder->isBanned() == false || folder->nbMedia() == 0 ); /* * If we want to delete the media as well, we can just let the foreign * keys delete everything */ return DatabaseHelpers::destroy( ml, folder->id() ); } std::queue> queue; queue.push( folder ); /** * Crawl through all the sub folders & convert the media to external * Afterward, delete the folder, which will propagate to all subfolders * through foreign keys */ while ( queue.empty() == false ) { auto f = std::move( queue.front() ); queue.pop(); auto subFolders = f->subfolders( nullptr )->all(); while ( subFolders.empty() == false ) { /* * subfolders() will return the folders with a parent, which is not * the case for banned folders. * If we were to remove a ban folder here, we would actually unban it */ assert( subFolders.back()->isBanned() == false ); queue.push( std::move( subFolders.back() ) ); subFolders.pop_back(); } auto t = ml->getConn()->newTransaction(); auto subMedia = f->media( IMedia::Type::Unknown, nullptr )->all(); for ( const auto& media : subMedia ) { auto m = static_cast( media.get() ); if ( m->convertToExternal() == false ) return false; } auto playlists = f->playlists( nullptr )->all(); for ( const auto& pl : playlists ) { if ( Playlist::destroy( ml, pl->id() ) == false ) return false; } t->commit(); } /* If we're banning a root folder, we just need to delete if from the database */ if ( folder->isRootFolder() == true ) return DatabaseHelpers::destroy( ml, folder->id() ); /* Otherwise we need to flag that it was banned so that it's not discovered again */ return folder->ban(); } int64_t Folder::id() const { return m_id; } const std::string& Folder::mrl() const { if ( m_isRemovable == false ) return m_path; if ( m_fullPath.empty() == false ) return m_fullPath; /* * We need the device entity to know its scheme, in order to fetch the * fs factory associated with it */ auto d = device(); if ( d == nullptr ) throw fs::errors::DeviceRemoved{}; /* Fetch and start the fs factory if required */ auto fsFactory = m_ml->fsFactoryForMrl( m_device->scheme() ); if ( fsFactory == nullptr ) throw fs::errors::UnknownScheme{ m_device->scheme() }; if ( fsFactory->isStarted() == false ) { /* * Starting the factory will refresh its device list. This is synchronous * for local devices, and asynchronous for network devices. * For network devices, we'll try to rely on a previously seen mountpoint * in the likely case that the factory doesn't refresh the devices we're * about to probe */ m_ml->startFsFactory( *fsFactory ); } // We can't compute the full path of a folder if it's removable and the // device isn't present. When there's no device, we don't know the // mountpoint, therefore we don't know the full path. Calling isPresent will // ensure we have the device representation cached locally if ( isPresent() == false ) { if ( d->isNetwork() == true ) { auto mountpoint = d->cachedMountpoint(); if ( mountpoint.empty() == false ) { m_fullPath = mountpoint + m_path; return m_fullPath; } } throw fs::errors::DeviceRemoved{}; } auto deviceFs = fsFactory->createDevice( m_device->uuid() ); // In case the device lister hasn't been updated accordingly, we might think // a device still is present while it's not. if( deviceFs == nullptr ) { // We only checked for the database representation so far. If the device // representation in DB was not updated but we can't find the device, we // should still assume that the device was removed throw fs::errors::DeviceRemoved{}; } m_fullPath = deviceFs->absoluteMrl( m_path ); return m_fullPath; } const std::string&Folder::name() const { if ( m_isRemovable == true && m_name.empty() == true ) { // In case this is the root folder of an external device, we don't have // any information before knowing the actual mountpoint, so we have to do // this at runtime auto fullPath = mrl(); m_name = utils::url::decode( utils::file::directoryName( fullPath ) ); } return m_name; } const std::string& Folder::rawMrl() const { return m_path; } void Folder::setMrl( std::string mrl ) { if ( m_path == mrl ) return; static const std::string req = "UPDATE " + Folder::Table::Name + " SET " "path = ? WHERE id_folder = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, mrl, m_id ) == false ) return; // We shouldn't use this if any full path/mrl has been cached. // This is meant for migration only, so there is no need to have cached this // information so far. assert( m_isRemovable == false || m_fullPath.empty() == true ); m_path = std::move( mrl ); } std::vector> Folder::files() { static const std::string req = "SELECT * FROM " + File::Table::Name + " WHERE folder_id = ?"; return File::fetchAll( m_ml, req, m_id ); } std::vector> Folder::folders() { static const std::string req = "SELECT f.* FROM " + Folder::Table::Name + " f " " LEFT JOIN " + Device::Table::Name + " d ON d.id_device = f.device_id" " WHERE parent_id = ? AND is_banned = 0 AND d.is_present != 0"; return DatabaseHelpers::fetchAll( m_ml, req, m_id ); } int64_t Folder::deviceId() const { return m_deviceId; } bool Folder::isRemovable() const { return m_isRemovable; } bool Folder::isPresent() const { auto d = device(); if( d == nullptr ) return false; return d->isPresent(); } bool Folder::isBanned() const { return m_isBanned; } bool Folder::isPublic() const { return m_isPublic; } bool Folder::setPublic( bool isPublic ) { const std::string req = "UPDATE " + Table::Name + " SET is_public = ? WHERE id_folder = ?"; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, isPublic, m_id ) == false ) return false; m_isPublic = isPublic; return true; } bool Folder::isRootFolder() const { return m_parent == 0; } Query Folder::media( IMedia::Type type, const QueryParameters* params ) const { return Media::fromFolderId( m_ml, type, m_id, params, m_publicOnlyListing ); } Query Folder::searchMedia( const std::string& pattern, IMedia::Type type, const QueryParameters* params ) const { return Media::searchFromFolderId( m_ml, pattern, type, m_id, params, m_publicOnlyListing ); } Query Folder::subfolders( const QueryParameters* params ) const { std::string req = "FROM " + Table::Name + " WHERE parent_id = ?"; if ( params != nullptr && params->favoriteOnly == true ) req += " AND is_favorite = TRUE"; return make_query( m_ml, "*", req, sortRequest( params ), m_id ) .markPublic( m_publicOnlyListing ).build(); } Query Folder::playlists(const QueryParameters* params) const { return Playlist::fromFolder( m_ml, m_id, params, m_publicOnlyListing ); } uint32_t Folder::nbVideo() const { return m_nbVideo; } uint32_t Folder::nbAudio() const { return m_nbAudio; } uint32_t Folder::nbMedia() const { return m_nbAudio + m_nbVideo; } int64_t Folder::duration() const { return m_duration; } bool Folder::isFavorite() const { return m_isFavorite; } bool Folder::setFavorite( bool favorite ) { static const std::string req = "UPDATE " + Table::Name + " SET is_favorite = ? WHERE id_folder = ?"; if ( m_isFavorite == favorite ) return true; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, favorite, m_id ) == false ) return false; m_isFavorite = favorite; return true; } std::vector> Folder::fetchRootFolders( MediaLibraryPtr ml ) { static const std::string req = "SELECT f.* FROM " + Folder::Table::Name + " f " " LEFT JOIN " + Device::Table::Name + " d ON d.id_device = f.device_id" " WHERE f.parent_id IS NULL AND f.is_banned = 0 AND d.is_present != 0"; return DatabaseHelpers::fetchAll( ml, req ); } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Folder.h000066400000000000000000000206411501065546500240070ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "medialibrary/IFolder.h" #include "medialibrary/IMedia.h" #include "database/DatabaseHelpers.h" namespace medialibrary { class File; class Device; // This doesn't publicly expose the DatabaseHelper inheritance in order to force // the user to go through Folder's overloads, as they take care of the device mountpoint // fetching & path composition class Folder : public IFolder, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t Folder::*const PrimaryKey; }; struct FtsTable { static const std::string Name; }; /* Deprecated since model 30 */ struct ExcludedFolderTable { static const std::string Name; }; enum class Triggers : uint8_t { InsertFts, DeleteFts, UpdateNbMediaOnIndex, UpdateNbMediaOnUpdate, UpdateNbMediaOnDelete, UpdateIsPublic, }; enum class Indexes : uint8_t { DeviceId, ParentId, }; enum class BannedType { Yes, //< Only select banned folders No, //< Only select unbanned folders Any, //< Well... any of the above. }; enum class RemovalBehavior { /* * The folder was removed from disk: remove it from the db and delete * the media it contained */ RemovedFromDisk, /* * The root folder was explicitly removed. Flag it as such in database * but keep its media on disk */ RootRemoved, }; Folder( MediaLibraryPtr ml, sqlite::Row& row ); Folder(MediaLibraryPtr ml, std::string path, std::string name, int64_t parent, int64_t deviceId , bool isRemovable ); static void createTable( sqlite::Connection* connection ); static void createTriggers( sqlite::Connection* connection ); static void createIndexes( sqlite::Connection* connection ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static std::string trigger( Triggers trigger, uint32_t dbModel ); static std::string triggerName( Triggers trigger, uint32_t dbModel ); static std::string index( Indexes index, uint32_t dbModel ); static std::string indexName( Indexes index, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr create( MediaLibraryPtr ml, const std::string& mrl, int64_t parentId, const Device& device, fs::IDevice& deviceFs ); static bool ban( MediaLibraryPtr ml, const std::string& mrl ); static std::vector> fetchRootFolders( MediaLibraryPtr ml ); static std::shared_ptr fromMrl( MediaLibraryPtr ml, const std::string& mrl ); static std::shared_ptr bannedFolder( MediaLibraryPtr ml, const std::string& mrl ); static Query withMedia( MediaLibraryPtr ml, IMedia::Type type, const QueryParameters* params ); static Query searchWithMedia( MediaLibraryPtr ml, const std::string& pattern, IMedia::Type type, const QueryParameters* params ); static Query roots( MediaLibraryPtr ml, bool banned, int64_t deviceId, const QueryParameters* params ); /** * @brief deleteFolder Mark a folder in database as removed * @param ml The media library instance * @param folder The folder to remove * @param behavior An enum member to drive the removal behavior * @return true in case of success, false otherwise. * * This will mark the folder as removed. If the folder was a root folder, * it will simply be removed from the database. If it was a sub folder of an * root folder, the folder will be marked as banned in order to not be * discovered again. * If the behavior passed is RemovedFromDisk, all the media that belonged to * that folder will be removed from the database. This also means that the * media will be removed from any media group or playlist they belonged to. * Otherwise, when behavior is RootRemoved, the media will be converted * to external media, and will be kept in database. */ static bool remove( MediaLibraryPtr ml, std::shared_ptr folder, RemovalBehavior behavior ); virtual int64_t id() const override; virtual const std::string& mrl() const override; virtual const std::string& name() const override; const std::string& rawMrl() const; void setMrl( std::string mrl ); std::vector> files(); std::vector> folders(); int64_t deviceId() const; virtual bool isRemovable() const override; virtual bool isPresent() const override; virtual bool isBanned() const override; virtual bool isPublic() const override; virtual bool setPublic( bool isPublic ) override; bool isRootFolder() const; virtual Query media( IMedia::Type type, const QueryParameters* params ) const override; virtual Query searchMedia( const std::string& pattern, IMedia::Type type, const QueryParameters* params = nullptr ) const override; virtual Query subfolders( const QueryParameters* params ) const override; virtual Query playlists(const QueryParameters* params) const override; virtual uint32_t nbVideo() const override; virtual uint32_t nbAudio() const override; virtual uint32_t nbMedia() const override; virtual int64_t duration() const override; virtual bool isFavorite() const override; virtual bool setFavorite( bool favorite ) override; static std::shared_ptr fromMrl( MediaLibraryPtr ml, const std::string& mrl, BannedType bannedType ); private: static std::string sortRequest( const QueryParameters* params ); static std::string filterByMediaType( IMedia::Type type ); std::shared_ptr device() const; bool ban(); private: MediaLibraryPtr m_ml; int64_t m_id; // This contains the path relative to the device mountpoint (ie. excluding it) // or the full path (including mrl scheme) for folders on non removable devices std::string m_path; mutable std::string m_name; const int64_t m_parent; const bool m_isBanned; const int64_t m_deviceId; // Can't be const anymore, but should be if we ever get to remove the // removable->non removable device fixup (introduced after vlc-android 3.1.0 rc3) bool m_isRemovable; uint32_t m_nbAudio; uint32_t m_nbVideo; int64_t m_duration; bool m_isPublic; bool m_isFavorite; bool m_publicOnlyListing; mutable std::shared_ptr m_device; // This contains the full path, including device mountpoint (and mrl scheme, // as its part of the mountpoint mutable std::string m_fullPath; friend struct Folder::Table; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Genre.cpp000066400000000000000000000511441501065546500241710ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "Genre.h" #include "Album.h" #include "Artist.h" #include "Media.h" #include "database/SqliteQuery.h" #include "utils/Enums.h" #include "utils/ModificationsNotifier.h" #include "Deprecated.h" namespace medialibrary { const std::string Genre::Table::Name = "Genre"; const std::string Genre::Table::PrimaryKeyColumn = "id_genre"; int64_t Genre::* const Genre::Table::PrimaryKey = &Genre::m_id; const std::string Genre::FtsTable::Name = "GenreFts"; Genre::Genre( MediaLibraryPtr ml, sqlite::Row& row ) : m_ml( ml ) , m_id( row.extract() ) , m_name( row.extract() ) , m_nbTracks( row.extract() ) , m_nbPresentTracks( row.extract() ) , m_isFavorite( row.extract() ) { if ( row.hasRemainingColumns() == true ) m_publicOnlyListing = row.extract(); else m_publicOnlyListing = false; assert( row.hasRemainingColumns() == false ); } Genre::Genre( MediaLibraryPtr ml, std::string name ) : m_ml( ml ) , m_id( 0 ) , m_name( std::move( name ) ) , m_nbTracks( 0 ) , m_nbPresentTracks( 0 ) , m_publicOnlyListing( false ) , m_isFavorite( false ) { } int64_t Genre::id() const { return m_id; } const std::string& Genre::name() const { return m_name; } uint32_t Genre::nbTracks() const { if ( m_publicOnlyListing == true ) return 0; return m_nbTracks; } uint32_t Genre::nbPresentTracks() const { if ( m_publicOnlyListing == true ) return 0; return m_nbPresentTracks; } bool Genre::updateNbTracks( int increment ) { m_nbTracks += increment; m_nbPresentTracks += increment; return true; } bool Genre::isFavorite() const { return m_isFavorite; } bool Genre::setFavorite( bool favorite ) { static const std::string req = "UPDATE " + Table::Name + " SET is_favorite = ? WHERE id_genre = ?"; if ( m_isFavorite == favorite ) return true; if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, favorite, m_id ) == false ) return false; m_isFavorite = favorite; return true; } Query Genre::artists( const QueryParameters* params ) const { return Artist::fromGenre( m_ml, m_id, params, m_publicOnlyListing ); } Query Genre::searchArtists( const std::string& pattern, const QueryParameters* params ) const { return Artist::searchByGenre( m_ml, pattern, params, m_id, m_publicOnlyListing ); } Query Genre::tracks( TracksIncluded included, const QueryParameters* params ) const { return Media::tracksFromGenre( m_ml, m_id, included, params, m_publicOnlyListing ); } Query Genre::searchTracks( const std::string& pattern, const QueryParameters* params ) const { return Media::searchGenreTracks( m_ml, pattern, m_id, params, m_publicOnlyListing ); } Query Genre::albums( const QueryParameters* params ) const { return Album::fromGenre( m_ml, m_id, params, m_publicOnlyListing ); } Query Genre::searchAlbums( const std::string& pattern, const QueryParameters* params ) const { return Album::searchFromGenre( m_ml, pattern, m_id, params, m_publicOnlyListing ); } const std::string&Genre::thumbnailMrl( ThumbnailSizeType sizeType ) const { const auto t = thumbnail( sizeType ); if ( t == nullptr ) return Thumbnail::EmptyMrl; return t->mrl(); } bool Genre::hasThumbnail( ThumbnailSizeType sizeType ) const { if ( m_thumbnails[Thumbnail::SizeToInt( sizeType )] != nullptr ) return true; return thumbnail( sizeType ) != nullptr; } bool Genre::shouldUpdateThumbnail( const Thumbnail& oldThumbnail ) { return oldThumbnail.isShared() == false; } bool Genre::setThumbnail( const std::string& mrl, ThumbnailSizeType sizeType, bool takeOwnership ) { auto thumbnailIdx = Thumbnail::SizeToInt( sizeType ); auto currentThumbnail = thumbnail( sizeType ); auto newThumbnail = std::make_shared( m_ml, mrl, Thumbnail::Origin::UserProvided, sizeType, false ); currentThumbnail = Thumbnail::updateOrReplace( m_ml, currentThumbnail, newThumbnail, Genre::shouldUpdateThumbnail, m_id, Thumbnail::EntityType::Genre ); if ( currentThumbnail == nullptr ) return false; m_thumbnails[thumbnailIdx] = std::move( currentThumbnail ); if ( takeOwnership == true ) m_thumbnails[thumbnailIdx]->relocate(); auto notifier = m_ml->getNotifier(); if ( notifier != nullptr ) notifier->notifyGenreModification( m_id ); return true; } std::shared_ptr Genre::thumbnail( ThumbnailSizeType sizeType ) const { auto idx = Thumbnail::SizeToInt( sizeType ); if ( m_thumbnails[idx] == nullptr ) { auto thumbnail = Thumbnail::fetch( m_ml, Thumbnail::EntityType::Genre, m_id, sizeType ); if ( thumbnail == nullptr ) return nullptr; m_thumbnails[idx] = std::move( thumbnail ); } return m_thumbnails[idx]; } void Genre::createTable( sqlite::Connection* dbConn ) { const std::string reqs[] = { schema( Table::Name, Settings::DbModelVersion ), schema( FtsTable::Name, Settings::DbModelVersion ), }; for ( const auto& req : reqs ) sqlite::Tools::executeRequest( dbConn, req ); } void Genre::createTriggers( sqlite::Connection* dbConn ) { sqlite::Tools::executeRequest( dbConn, trigger( Triggers::InsertFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConn, trigger( Triggers::DeleteFts, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConn, trigger( Triggers::UpdateOnTrackDelete, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConn, trigger( Triggers::UpdateIsPresent, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConn, trigger( Triggers::DeleteEmpty, Settings::DbModelVersion ) ); sqlite::Tools::executeRequest( dbConn, trigger( Triggers::UpdateOnMediaGenreIdChange, Settings::DbModelVersion ) ); } std::string Genre::schema( const std::string& tableName, uint32_t dbModel ) { if ( tableName == FtsTable::Name ) { return "CREATE VIRTUAL TABLE " + FtsTable::Name + " USING FTS3(name)"; } assert( tableName == Table::Name ); if ( dbModel < 30 ) { return "CREATE TABLE " + Table::Name + "(" "id_genre INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL," "nb_tracks INTEGER NOT NULL DEFAULT 0" ")"; } if (dbModel < 37 ) { return "CREATE TABLE " + Table::Name + "(" "id_genre INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL," "nb_tracks INTEGER NOT NULL DEFAULT 0," "is_present INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks)" ")"; } return "CREATE TABLE " + Table::Name + "(" "id_genre INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL," "nb_tracks INTEGER NOT NULL DEFAULT 0," "is_present INTEGER NOT NULL DEFAULT 0 " "CHECK(is_present <= nb_tracks), " "is_favorite BOOLEAN NOT NULL DEFAULT FALSE" ")"; } std::string Genre::trigger( Triggers trigger, uint32_t dbModel ) { switch ( trigger ) { case Triggers::InsertFts: return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER INSERT ON " + Table::Name + " BEGIN" " INSERT INTO " + FtsTable::Name + "(rowid,name)" " VALUES(new.id_genre, new.name);" " END"; case Triggers::DeleteFts: return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " BEFORE DELETE ON " + Table::Name + " BEGIN" " DELETE FROM " + FtsTable::Name + " WHERE rowid = old.id_genre;" " END"; case Triggers::UpdateOnNewTrack: assert( dbModel < 34 ); return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER INSERT ON " + AlbumTrack::Table::Name + " WHEN new.genre_id IS NOT NULL" " BEGIN" " UPDATE " + Table::Name + " SET nb_tracks = nb_tracks + 1," " is_present = is_present + 1" " WHERE id_genre = new.genre_id;" " END"; case Triggers::UpdateOnTrackDelete: if ( dbModel < 34 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER DELETE ON " + AlbumTrack::Table::Name + " WHEN old.genre_id IS NOT NULL" " BEGIN" " UPDATE " + Table::Name + " SET nb_tracks = nb_tracks - 1," " is_present = is_present - 1" " WHERE id_genre = old.genre_id;" " DELETE FROM " + Table::Name + " WHERE nb_tracks = 0;" " END"; } if ( dbModel == 34 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER DELETE ON " + Media::Table::Name + " WHEN old.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " BEGIN" " UPDATE " + Table::Name + " SET nb_tracks = nb_tracks - 1," " is_present = is_present - IIF(old.is_present != 0, 1, 0)" " WHERE id_genre = old.genre_id;" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER DELETE ON " + Media::Table::Name + " WHEN old.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " BEGIN" " UPDATE " + Table::Name + " SET is_present = is_present - IIF(old.is_present != 0, 1, 0)," " nb_tracks = nb_tracks - 1" " WHERE id_genre = old.genre_id;" " END"; case Triggers::UpdateIsPresent: assert( dbModel >= 30 ); if ( dbModel < 34 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " AND old.is_present != new.is_present" " BEGIN" " UPDATE " + Table::Name + " SET is_present = is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END) " "WHERE id_genre = " "(SELECT genre_id FROM " + AlbumTrack::Table::Name + " WHERE media_id = new.id_media);" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF is_present ON " + Media::Table::Name + " WHEN new.subtype = " + utils::enum_to_string( IMedia::SubType::AlbumTrack ) + " AND old.is_present != new.is_present" " BEGIN" " UPDATE " + Table::Name + " SET is_present = is_present + " "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END) " "WHERE id_genre = new.genre_id;" " END"; case Triggers::DeleteEmpty: { assert( dbModel >= 34 ); if ( dbModel == 34 ) { return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF nb_tracks ON " + Table::Name + " WHEN new.nb_tracks = 0" " BEGIN" " DELETE FROM " + Table::Name + ";" " END"; } return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF nb_tracks ON " + Table::Name + " WHEN new.nb_tracks = 0" " BEGIN" " DELETE FROM " + Table::Name + " WHERE id_genre = old.id_genre;" " END"; } case Triggers::UpdateOnMediaGenreIdChange: { assert( dbModel >= 36 ); return "CREATE TRIGGER " + triggerName( trigger, dbModel ) + " AFTER UPDATE OF genre_id ON " + Media::Table::Name + " WHEN IFNULL(new.genre_id, 0) != IFNULL(old.genre_id, 0)" " BEGIN" /* Decrement the old genre first */ " UPDATE " + Table::Name + " SET is_present = is_present -" " IIF(old.is_present != 0, 1, 0)," " nb_tracks = nb_tracks - 1" " WHERE old.genre_id IS NOT NULL AND id_genre = old.genre_id;" /* And increment the new one afterward */ " UPDATE " + Table::Name + " SET is_present = is_present +" " IIF(old.is_present != 0, 1, 0)," " nb_tracks = nb_tracks + 1" " WHERE new.genre_id IS NOT NULL AND id_genre = new.genre_id;" " END"; } default: assert( !"Invalid trigger provided" ); } return ""; } std::string Genre::triggerName( Triggers trigger, uint32_t dbModel ) { switch ( trigger ) { case Triggers::InsertFts: return "insert_genre_fts"; case Triggers::DeleteFts: return "delete_genre_fts"; case Triggers::UpdateOnNewTrack: assert( dbModel < 34 ); return "update_genre_on_new_track"; case Triggers::UpdateOnTrackDelete: if ( dbModel < 34 ) return "update_genre_on_track_deleted"; return "genre_update_on_track_deleted"; case Triggers::UpdateIsPresent: assert( dbModel >= 30 ); return "genre_update_is_present"; case Triggers::DeleteEmpty: assert( dbModel >= 34 ); return "genre_delete_empty"; case Triggers::UpdateOnMediaGenreIdChange: assert( dbModel >= 36 ); return "genre_update_on_media_genre_id_change"; default: assert( !"Invalid trigger provided" ); } return ""; } bool Genre::checkDbModel(MediaLibraryPtr ml) { OPEN_READ_CONTEXT( ctx, ml->getConn() ); if ( sqlite::Tools::checkTableSchema( schema( Table::Name, Settings::DbModelVersion ), Table::Name ) == false || sqlite::Tools::checkTableSchema( schema( FtsTable::Name, Settings::DbModelVersion ), FtsTable::Name ) == false ) return false; auto check = []( Triggers t ) { return sqlite::Tools::checkTriggerStatement( trigger( t, Settings::DbModelVersion ), triggerName( t, Settings::DbModelVersion ) ); }; return check( Triggers::InsertFts ) && check( Triggers::DeleteFts ) && check( Triggers::UpdateOnTrackDelete ) && check( Triggers::UpdateIsPresent ) && check( Triggers::DeleteEmpty ) && check( Triggers::UpdateOnMediaGenreIdChange ); } std::shared_ptr Genre::create( MediaLibraryPtr ml, std::string name ) { static const std::string req = "INSERT INTO " + Table::Name + "(name)" "VALUES(?)"; auto self = std::make_shared( ml, std::move( name ) ); if ( insert( ml, self, req, self->m_name ) == false ) return nullptr; return self; } std::shared_ptr Genre::fromName( MediaLibraryPtr ml, const std::string& name ) { static const std::string req = "SELECT * FROM " + Table::Name + " WHERE name = ?"; return fetch( ml, req, name ); } std::string Genre::addRequestConditions( const QueryParameters* params ) { if ( params == nullptr ) return ""; std::string req; if ( params->publicOnly == true ) req = " EXISTS(SELECT genre_id FROM " + Media::Table::Name + " WHERE genre_id = id_genre AND is_public != 0)"; if ( params->favoriteOnly == true ) { if ( req.empty() == false ) req += " AND"; req += " is_favorite = TRUE"; } return req; } Query Genre::search( MediaLibraryPtr ml, const std::string& name, const QueryParameters* params ) { std::string req = "FROM " + Table::Name + " WHERE id_genre IN " "(SELECT rowid FROM " + FtsTable::Name + " " "WHERE name MATCH ?)"; const auto cond = addRequestConditions( params ); if ( cond.empty() == false ) req += " AND" + cond; std::string orderBy = "ORDER BY 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 ) orderBy += " DESC"; } return make_query( ml, "*", std::move( req ), std::move( orderBy ), sqlite::Tools::sanitizePattern( name ) ).build(); } Query Genre::listAll( MediaLibraryPtr ml, const QueryParameters* params ) { std::string req = "FROM " + Table::Name; const auto cond = addRequestConditions( params ); if ( cond.empty() == false ) req += " WHERE" + cond; std::string orderBy = " ORDER BY 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 ) orderBy += " DESC"; } const bool publicOnly = params != nullptr && params->publicOnly == true; return make_query( ml, "*", std::move( req ), std::move( orderBy ) ) .markPublic( publicOnly ).build(); } } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Genre.h000066400000000000000000000111741501065546500236350ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #pragma once #include "medialibrary/IGenre.h" #include "database/DatabaseHelpers.h" #include "Thumbnail.h" namespace medialibrary { class Genre : public IGenre, public DatabaseHelpers { public: struct Table { static const std::string Name; static const std::string PrimaryKeyColumn; static int64_t Genre::*const PrimaryKey; }; struct FtsTable { static const std::string Name; }; enum class Triggers : uint8_t { InsertFts, DeleteFts, UpdateOnNewTrack, UpdateOnTrackDelete, UpdateIsPresent, DeleteEmpty, // Introduced in model 34 UpdateOnMediaGenreIdChange, // Introduced in model 36 }; Genre( MediaLibraryPtr ml, sqlite::Row& row ); Genre( MediaLibraryPtr ml, std::string name ); virtual int64_t id() const override; virtual const std::string& name() const override; virtual uint32_t nbTracks() const override; virtual uint32_t nbPresentTracks() const override; virtual bool isFavorite() const override; virtual bool setFavorite( bool ) override; bool updateNbTracks(int increment ); virtual Query artists( const QueryParameters* params ) const override; virtual Query searchArtists( const std::string& pattern, const QueryParameters* params = nullptr ) const override; virtual Query tracks( TracksIncluded included, const QueryParameters* params ) const override; virtual Query searchTracks( const std::string& pattern, const QueryParameters* params = nullptr ) const override; virtual Query albums( const QueryParameters* params ) const override; virtual Query searchAlbums( const std::string& pattern, const QueryParameters* params = nullptr ) const override; virtual const std::string& thumbnailMrl( ThumbnailSizeType sizeType ) const override; virtual bool hasThumbnail( ThumbnailSizeType sizeType ) const override; virtual bool setThumbnail( const std::string& mrl, ThumbnailSizeType sizeType, bool takeOwnership ) override; std::shared_ptr thumbnail( ThumbnailSizeType sizeType ) const; static void createTable( sqlite::Connection* dbConn ); static void createTriggers( sqlite::Connection* dbConn ); static std::string schema( const std::string& tableName, uint32_t dbModel ); static std::string trigger( Triggers trigger, uint32_t dbModel ); static std::string triggerName( Triggers trigger, uint32_t dbModel ); static bool checkDbModel( MediaLibraryPtr ml ); static std::shared_ptr create( MediaLibraryPtr ml, std::string name ); static std::shared_ptr fromName( MediaLibraryPtr ml, const std::string& name ); static Query search( MediaLibraryPtr ml, const std::string& name, const QueryParameters* params ); static Query listAll( MediaLibraryPtr ml, const QueryParameters* params ); private: static bool shouldUpdateThumbnail( const Thumbnail& oldThumbnail ); static std::string addRequestConditions( const QueryParameters* params ); private: MediaLibraryPtr m_ml; int64_t m_id; const std::string m_name; uint32_t m_nbTracks; uint32_t m_nbPresentTracks; bool m_publicOnlyListing; bool m_isFavorite; mutable std::shared_ptr m_thumbnails[Thumbnail::SizeToInt( ThumbnailSizeType::Count )]; friend Genre::Table; }; } medialibrary-0.13.2-23b262b133e1b3447d5dc1fee3d94f893f58c128/src/Label.cpp000066400000000000000000000165331501065546500241530ustar00rootroot00000000000000/***************************************************************************** * Media Library ***************************************************************************** * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN * * Authors: Hugo Beauzée-Luyssen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include #include "Label.h" #include "Media.h" #include "database/SqliteTools.h" #include "database/SqliteQuery.h" #include "utils/Enums.h" namespace medialibrary { const std::string Label::Table::Name = "Label"; const std::string Label::Table::PrimaryKeyColumn = "id_label"; int64_t Label::* const Label::Table::PrimaryKey = &Label::m_id; const std::string Label::FileRelationTable::Name = "LabelFileRelation"; Label::Label(MediaLibraryPtr ml, sqlite::Row& row ) : m_ml( ml ) , m_id( row.extract() ) , m_name( row.extract() ) { assert( row.hasRemainingColumns() == false ); } Label::Label( MediaLibraryPtr ml, const std::string& name ) : m_ml( ml ) , m_id( 0 ) , m_name( name ) { } int64_t Label::id() const { return m_id; } const std::string& Label::name() const { return m_name; } Query Label::media() { static const std::string req = "FROM " + Media::Table::Name + " m " "INNER JOIN " + FileRelationTable::Name + " lfr ON lfr.entity_id = m.id_media " "WHERE lfr.label_id = ? " "AND lfr.entity_type = " + utils::enum_to_string( EntityType::Media ); return make_query( m_ml, "m.*", req, "", m_id ).build(); } LabelPtr Label::create( MediaLibraryPtr ml, const std::string& name ) { auto self = std::make_shared