pax_global_header00006660000000000000000000000064120217165470014517gustar00rootroot0000000000000052 comment=c43b97da7faa6f3645db554200611ad04bffc426 acoustid-fingerprinter-0.6/000077500000000000000000000000001202171654700160535ustar00rootroot00000000000000acoustid-fingerprinter-0.6/.bzrignore000066400000000000000000000001451202171654700200550ustar00rootroot00000000000000Makefile ./fpsubmit moc_*.cxx cmake_install.cmake CMakeFiles CMakeCache.txt ./acoustid-fingerprinter acoustid-fingerprinter-0.6/CHANGES.txt000066400000000000000000000020301202171654700176570ustar00rootroot00000000000000Version 0.6 (2012-09-05) ========================== - Compatibility with the latest FFmpeg API - Changed name in the .desktop file - Fixed a thread-related crash when using Chromaprint compiled with FFTW3 Version 0.5.1 (2012-01-08) ========================== - Compatibility with Qt 4.8 Version 0.5 (2011-12-22) ======================== - Unicode filename support on Windows - Support for 24-bit sample formats - Reading MBID tags from foobar2000-written MP3 files - Added .desktop file for Linux Version 0.4 (2011-08-06) ======================== - New icon - HTTP proxy support using the $http_proxy environment variable - When available, PUIDs are always submitted - Fixed submission of the file format Version 0.3 (2011-06-30) ======================== - Support for reading PUIDs from tags Version 0.2 (2011-06-26) ======================== - Support for submitting fingerprints without MBIDs - Make it possible to access external discs using the browser on OS X Version 0.1 (2010-10-30) ======================== - Initial release. acoustid-fingerprinter-0.6/CMakeLists.txt000066400000000000000000000071001202171654700206110ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(fpsubmit) set(fpsubmit_VERSION 0.6) set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)") set(EXEC_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE PATH "Installation prefix for executables and object code libraries" FORCE) set(BIN_INSTALL_DIR ${EXEC_INSTALL_PREFIX}/bin CACHE PATH "Installation prefix for user executables" FORCE) set(LIB_INSTALL_DIR ${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX} CACHE PATH "Installation prefix for object code libraries" FORCE) set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include CACHE PATH "Installation prefix for C header files" FORCE) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED) find_package(FFmpeg REQUIRED) find_package(Taglib REQUIRED) find_package(Chromaprint REQUIRED) include(${QT_USE_FILE}) if(CMAKE_BUILD_TYPE STREQUAL Release OR CMAKE_BUILD_TYPE STREQUAL MinSizeRel OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo) add_definitions(-DQT_NO_DEBUG_OUTPUT) endif(CMAKE_BUILD_TYPE STREQUAL Release OR CMAKE_BUILD_TYPE STREQUAL MinSizeRel OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo) set(fpsubmit_HEADERS checkabledirmodel.h progressdialog.h fingerprinter.h mainwindow.h loadfilelisttask.h analyzefiletask.h ) set(fpsubmit_SOURCES checkabledirmodel.cpp progressdialog.cpp fingerprinter.cpp fingerprintcalculator.cpp tagreader.cpp mainwindow.cpp decoder.cpp main.cpp loadfilelisttask.cpp analyzefiletask.cpp updatelogfiletask.cpp crc.c gzip.cpp ) #set(fpsubmit_UIS fpsubmit.ui) set(fpsubmit_RESOURCES fingerprinter.qrc) qt4_wrap_cpp(fpsubmit_MOC ${fpsubmit_HEADERS}) qt4_wrap_ui(fpsubmit_UIS_H ${fpsubmit_UIS}) qt4_add_resources(fpsubmit_RESOURCES_CPP ${fpsubmit_RESOURCES}) if(WIN32) set(fpsubmit_SOURCES ${fpsubmit_SOURCES} fingerprinter.rc) endif() add_definitions( -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -DVERSION="${fpsubmit_VERSION}" ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS} ${TAGLIB_INCLUDES} ${CHROMAPRINT_INCLUDE_DIR} ) set(CMAKE_REQUIRED_LIBRARIES ${FFMPEG_LIBAVFORMAT_LIBRARIES} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES} ${EXTRA_LIBS}) include(CheckFunctionExists) check_function_exists(av_audio_convert HAVE_AV_AUDIO_CONVERT) if(HAVE_AV_AUDIO_CONVERT) add_definitions(-DHAVE_AV_AUDIO_CONVERT) endif() if(WIN32) set(GUI_TYPE WIN32) endif(WIN32) if(APPLE) set(GUI_TYPE MACOSX_BUNDLE) set(fpsubmit_ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/acoustid-fp.icns) set_source_files_properties(${fpsubmit_ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set(fpsubmit_SOURCES ${fpsubmit_SOURCES} ${fpsubmit_ICON_FILE}) endif() add_executable(fpsubmit ${GUI_TYPE} ${fpsubmit_SOURCES} ${fpsubmit_MOC} ${fpsubmit_UIS_H} ${fpsubmit_RESOURCES_CPP} ) set_target_properties(fpsubmit PROPERTIES OUTPUT_NAME acoustid-fingerprinter MACOSX_BUNDLE_ICON_FILE acoustid-fp.icns MACOSX_BUNDLE_INFO_STRING "Acoustid Fingerprinter ${fpsubmit_VERSION}" MACOSX_BUNDLE_BUNDLE_NAME "Acoustid Fingerprinter" ) target_link_libraries(fpsubmit ${QT_LIBRARIES} ${FFMPEG_LIBAVFORMAT_LIBRARIES} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES} ${TAGLIB_LIBRARIES} ${CHROMAPRINT_LIBRARIES} ) if(APPLE) target_link_libraries(fpsubmit "-framework Accelerate -lz") endif() if(UNIX) install(FILES acoustid-fingerprinter.desktop DESTINATION share/applications) endif() install(TARGETS fpsubmit DESTINATION ${BIN_INSTALL_DIR}) acoustid-fingerprinter-0.6/COPYING.txt000066400000000000000000000431031202171654700177250ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 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. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. 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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. acoustid-fingerprinter-0.6/README.txt000066400000000000000000000005041202171654700175500ustar00rootroot00000000000000Acoustid Fingerprinter ====================== Dependencies ------------ * Qt * FFmpeg * Chromaprint Installation ------------ $ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local . $ make $ sudo make install acoustid-fingerprinter-0.6/acoustid-fingerprinter.desktop000066400000000000000000000005351202171654700241400ustar00rootroot00000000000000[Desktop Entry] Name=Acoustid Fingerprinter Exec=acoustid-fingerprinter Icon=acoustid-fingerprinter GenericName=Acoustid Music fingerprinting tool Terminal=false Type=Application Categories=AudioVideo;Audio;Qt;AudioVideoEditing; MimeType=audio/x-mp3;application/ogg; X-AppInstall-Package=acoustid-fingerprinter X-Desktop-File-Install-Version=0.18 acoustid-fingerprinter-0.6/analyzefiletask.cpp000066400000000000000000000047371202171654700217600ustar00rootroot00000000000000#include #include #include "decoder.h" #include "tagreader.h" #include "utils.h" #include "analyzefiletask.h" #include "constants.h" AnalyzeFileTask::AnalyzeFileTask(const QString &path) : m_path(path) { } void AnalyzeFileTask::run() { qDebug() << "Analyzing file" << m_path; AnalyzeResult *result = new AnalyzeResult(); result->fileName = m_path; TagReader tags(m_path); if (!tags.read()) { result->error = true; result->errorMessage = "Couldn't read metadata"; emit finished(result); return; } qDebug() << "Track:" << tags.track(); qDebug() << "Artist:" << tags.artist(); qDebug() << "Album:" << tags.album(); qDebug() << "AlbumArtist:" << tags.albumArtist(); qDebug() << "TrackNo:" << tags.trackNo(); qDebug() << "DiscNo:" << tags.discNo(); qDebug() << "Year:" << tags.year(); qDebug() << "PUID:" << tags.puid(); result->mbid = tags.mbid(); result->track = tags.track(); result->artist = tags.artist(); result->album = tags.album(); result->albumArtist = tags.albumArtist(); result->puid = tags.puid(); result->trackNo = tags.trackNo(); result->discNo = tags.discNo(); result->year = tags.year(); result->length = tags.length(); result->bitrate = tags.bitrate(); if (result->length < 10) { result->error = true; result->errorMessage = "Too short audio stream, should be at least 10 seconds"; emit finished(result); return; } if (result->mbid.isEmpty() && result->puid.isEmpty() && (result->track.isEmpty() || result->album.isEmpty() || result->artist.isEmpty())) { result->error = true; result->errorMessage = "Couldn't find any usable metadata"; emit finished(result); return; } #ifdef Q_OS_WIN32 QByteArray encodedPath = m_path.toUtf8(); #else QByteArray encodedPath = QFile::encodeName(m_path); #endif Decoder decoder(encodedPath.data()); if (!decoder.Open()) { result->error = true; result->errorMessage = QString("Couldn't open the file: ") + QString::fromStdString(decoder.LastError()); emit finished(result); return; } FingerprintCalculator fpcalculator; if (!fpcalculator.start(decoder.SampleRate(), decoder.Channels())) { result->error = true; result->errorMessage = "Error while fingerpriting the file"; emit finished(result); return; } decoder.Decode(&fpcalculator, AUDIO_LENGTH); result->fingerprint = fpcalculator.finish(); emit finished(result); } acoustid-fingerprinter-0.6/analyzefiletask.h000066400000000000000000000012651202171654700214160ustar00rootroot00000000000000#ifndef FPSUBMIT_ANALYZEFILETASK_H_ #define FPSUBMIT_ANALYZEFILETASK_H_ #include #include #include struct AnalyzeResult { AnalyzeResult() : error(false) { } QString fileName; QString mbid; QString fingerprint; QString track; QString artist; QString album; QString albumArtist; QString puid; int trackNo; int discNo; int year; int length; int bitrate; bool error; QString errorMessage; }; class AnalyzeFileTask : public QObject, public QRunnable { Q_OBJECT public: AnalyzeFileTask(const QString &path); void run(); signals: void finished(AnalyzeResult *result); private: QString m_path; }; #endif acoustid-fingerprinter-0.6/checkabledirmodel.cpp000066400000000000000000000046321202171654700222050ustar00rootroot00000000000000#include #include "checkabledirmodel.h" static const int CHECKABLE_COLUMN = 0; Qt::ItemFlags CheckableDirModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags = QFileSystemModel::flags(index); if (index.column() == CHECKABLE_COLUMN) { flags |= Qt::ItemIsUserCheckable; } return flags; } Qt::CheckState CheckableDirModel::findParentState(const QModelIndex &index) const { if (!index.isValid()) { return Qt::Unchecked; } if (hasIndexState(index)) { return getIndexState(index); } //qDebug() << "Looking up parent state for " << filePath(index); Qt::CheckState state = findParentState(index.parent()); setIndexState(index, state); return state; } QVariant CheckableDirModel::data(const QModelIndex& index, int role) const { if (index.isValid() && index.column() == CHECKABLE_COLUMN && role == Qt::CheckStateRole) { return findParentState(index); } return QFileSystemModel::data(index, role); } void CheckableDirModel::updateParents(const QModelIndex &parent) { if (!parent.isValid()) { return; } int childRowCount = rowCount(parent); QSet childStates; for (int i = 0; i < childRowCount; i++) { childStates.insert(getIndexState(index(i, CHECKABLE_COLUMN, parent))); } if (childStates.size() > 1) { setIndexState(parent, Qt::PartiallyChecked); emit dataChanged(parent, parent); } else if (childStates.size() == 1) { setIndexState(parent, *childStates.begin()); emit dataChanged(parent, parent); } updateParents(parent.parent()); } void CheckableDirModel::updateVisibleChildren(const QModelIndex &parent, Qt::CheckState state) { int childRowCount = rowCount(parent); for (int i = 0; i < childRowCount; i++) { const QModelIndex child = index(i, CHECKABLE_COLUMN, parent); if (hasIndexState(child)) { updateVisibleChildren(child, state); } setIndexState(child, state); emit dataChanged(child, child); } } bool CheckableDirModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (index.isValid() && index.column() == CHECKABLE_COLUMN && role == Qt::CheckStateRole) { Qt::CheckState state = Qt::CheckState(value.toInt()); setIndexState(index, state); updateVisibleChildren(index, state); updateParents(index.parent()); emit dataChanged(index, index); return true; } return QFileSystemModel::setData(index, value, role); } QList CheckableDirModel::selectedDirectories() { return m_state.keys(Qt::Checked); } acoustid-fingerprinter-0.6/checkabledirmodel.h000066400000000000000000000021771202171654700216540ustar00rootroot00000000000000#ifndef FPSUBMIT_CHECKABLEDIRMODEL_H_ #define FPSUBMIT_CHECKABLEDIRMODEL_H_ #include #include #include class CheckableDirModel : public QFileSystemModel { Q_OBJECT public: Qt::ItemFlags flags(const QModelIndex& index) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); QList selectedDirectories(); private: Qt::CheckState findParentState(const QModelIndex &index) const; void updateParents(const QModelIndex &index); void updateVisibleChildren(const QModelIndex &parent, Qt::CheckState); bool hasIndexState(const QModelIndex &index) const { return m_state.contains(filePath(index)); } void setIndexState(const QModelIndex &index, Qt::CheckState state) const { //qDebug() << "Setting state for " << filePath(index) << " to " << state; m_state.insert(filePath(index), state); } Qt::CheckState getIndexState(const QModelIndex &index) const { return m_state.value(filePath(index), Qt::Unchecked); } mutable QMap m_state; }; #endif acoustid-fingerprinter-0.6/cmake/000077500000000000000000000000001202171654700171335ustar00rootroot00000000000000acoustid-fingerprinter-0.6/cmake/modules/000077500000000000000000000000001202171654700206035ustar00rootroot00000000000000acoustid-fingerprinter-0.6/cmake/modules/FindChromaprint.cmake000066400000000000000000000010011202171654700246640ustar00rootroot00000000000000INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE(PkgConfig) PKG_CHECK_MODULES(PKG_LIBCHROMAPRINT libchromaprint) FIND_PATH(CHROMAPRINT_INCLUDE_DIR chromaprint.h ${PKG_LIBCHROMAPRINT_INCLUDE_DIRS} /usr/include /usr/local/include ) FIND_LIBRARY(CHROMAPRINT_LIBRARIES NAMES chromaprint chromaprint.dll PATHS ${PKG_LIBCHROMAPRINT_LIBRARY_DIRS} /usr/lib /usr/local/lib ) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Chromaprint DEFAULT_MSG CHROMAPRINT_LIBRARIES CHROMAPRINT_INCLUDE_DIR) acoustid-fingerprinter-0.6/cmake/modules/FindFFmpeg.cmake000066400000000000000000000077121202171654700235610ustar00rootroot00000000000000# Locate ffmpeg # This module defines # FFMPEG_LIBRARIES # FFMPEG_FOUND, if false, do not try to link to ffmpeg # FFMPEG_INCLUDE_DIR, where to find the headers # # $FFMPEG_DIR is an environment variable that would # correspond to the ./configure --prefix=$FFMPEG_DIR # # Created by Robert Osfield. # Modified by Lukas Lalinsky. #In ffmpeg code, old version use "#include " and newer use "#include " #In OSG ffmpeg plugin, we use "#include " for compatibility with old version of ffmpeg #We have to search the path which contain the header.h (usefull for old version) #and search the path which contain the libname/header.h (usefull for new version) #Then we need to include ${FFMPEG_libname_INCLUDE_DIRS} (in old version case, use by ffmpeg header and osg plugin code) # (in new version case, use by ffmpeg header) #and ${FFMPEG_libname_INCLUDE_DIRS/libname} (in new version case, use by osg plugin code) # Macro to find header and lib directories # example: FFMPEG_FIND(AVFORMAT avformat avformat.h) MACRO(FFMPEG_FIND varname shortname headername) FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername} PATHS ${FFMPEG_ROOT}/include $ENV{FFMPEG_DIR}/include ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include/ /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include /usr/freeware/include NO_DEFAULT_PATH PATH_SUFFIXES ffmpeg DOC "Location of FFMPEG Headers" ) FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername} PATH_SUFFIXES ffmpeg DOC "Location of FFMPEG Headers" ) FIND_LIBRARY(FFMPEG_${varname}_LIBRARIES NAMES ${shortname} PATHS ${FFMPEG_ROOT}/lib $ENV{FFMPEG_DIR}/lib ~/Library/Frameworks /Library/Frameworks /usr/local/lib /usr/local/lib64 /usr/lib /usr/lib64 /sw/lib /opt/local/lib /opt/csw/lib /opt/lib /usr/freeware/lib64 NO_DEFAULT_PATH DOC "Location of FFMPEG Libraries" ) FIND_LIBRARY(FFMPEG_${varname}_LIBRARIES NAMES ${shortname} DOC "Location of FFMPEG Libraries" ) IF (FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) SET(FFMPEG_${varname}_FOUND 1) ENDIF(FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) ENDMACRO(FFMPEG_FIND) SET(FFMPEG_ROOT "$ENV{FFMPEG_DIR}" CACHE PATH "Location of FFMPEG") # find stdint.h FIND_PATH(FFMPEG_STDINT_INCLUDE_DIR stdint.h PATHS ${FFMPEG_ROOT}/include $ENV{FFMPEG_DIR}/include ~/Library/Frameworks /Library/Frameworks /usr/local/include /usr/include /sw/include # Fink /opt/local/include # DarwinPorts /opt/csw/include # Blastwave /opt/include /usr/freeware/include PATH_SUFFIXES ffmpeg DOC "Location of FFMPEG stdint.h Header" ) FFMPEG_FIND(LIBAVFORMAT avformat avformat.h) FFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h) FFMPEG_FIND(LIBAVCODEC avcodec avcodec.h) FFMPEG_FIND(LIBAVCODEC_FFT avcodec avfft.h) FFMPEG_FIND(LIBAVUTIL avutil avutil.h) FFMPEG_FIND(LIBSWSCALE swscale swscale.h) # not sure about the header to look for here. SET(FFMPEG_FOUND "NO") # Note we don't check FFMPEG_LIBSWSCALE_FOUND here, it's optional. IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND AND FFMPEG_STDINT_INCLUDE_DIR) SET(FFMPEG_FOUND "YES") SET(FFMPEG_INCLUDE_DIRS ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}) SET(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS}) # Note we don't add FFMPEG_LIBSWSCALE_LIBRARIES here, it will be added if found later. SET(FFMPEG_LIBRARIES ${FFMPEG_LIBAVFORMAT_LIBRARIES} ${FFMPEG_LIBAVDEVICE_LIBRARIES} ${FFMPEG_LIBAVCODEC_LIBRARIES} ${FFMPEG_LIBAVUTIL_LIBRARIES}) ELSE () # MESSAGE(STATUS "Could not find FFMPEG") ENDIF() acoustid-fingerprinter-0.6/cmake/modules/FindTaglib.cmake000066400000000000000000000077361202171654700236250ustar00rootroot00000000000000# - Try to find the Taglib library # Once done this will define # # TAGLIB_FOUND - system has the taglib library # TAGLIB_CFLAGS - the taglib cflags # TAGLIB_LIBRARIES - The libraries needed to use taglib # Copyright (c) 2006, Laurent Montel, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. if(NOT TAGLIB_MIN_VERSION) set(TAGLIB_MIN_VERSION "1.6") endif(NOT TAGLIB_MIN_VERSION) if(NOT WIN32) find_program(TAGLIBCONFIG_EXECUTABLE NAMES taglib-config PATHS ${BIN_INSTALL_DIR} ) endif(NOT WIN32) #reset vars set(TAGLIB_LIBRARIES) set(TAGLIB_CFLAGS) # if taglib-config has been found if(TAGLIBCONFIG_EXECUTABLE) exec_program(${TAGLIBCONFIG_EXECUTABLE} ARGS --version RETURN_VALUE _return_VALUE OUTPUT_VARIABLE TAGLIB_VERSION) if(TAGLIB_VERSION STRLESS "${TAGLIB_MIN_VERSION}") message(STATUS "TagLib version too old: version searched :${TAGLIB_MIN_VERSION}, found ${TAGLIB_VERSION}") set(TAGLIB_FOUND FALSE) else(TAGLIB_VERSION STRLESS "${TAGLIB_MIN_VERSION}") exec_program(${TAGLIBCONFIG_EXECUTABLE} ARGS --libs RETURN_VALUE _return_VALUE OUTPUT_VARIABLE TAGLIB_LIBRARIES) exec_program(${TAGLIBCONFIG_EXECUTABLE} ARGS --cflags RETURN_VALUE _return_VALUE OUTPUT_VARIABLE TAGLIB_CFLAGS) if(TAGLIB_LIBRARIES AND TAGLIB_CFLAGS) set(TAGLIB_FOUND TRUE) endif(TAGLIB_LIBRARIES AND TAGLIB_CFLAGS) string(REGEX REPLACE " *-I" ";" TAGLIB_INCLUDES "${TAGLIB_CFLAGS}") endif(TAGLIB_VERSION STRLESS "${TAGLIB_MIN_VERSION}") mark_as_advanced(TAGLIB_CFLAGS TAGLIB_LIBRARIES TAGLIB_INCLUDES) else(TAGLIBCONFIG_EXECUTABLE) find_path(TAGLIB_INCLUDES NAMES tag.h PATH_SUFFIXES taglib PATHS ${KDE4_INCLUDE_DIR} ${INCLUDE_INSTALL_DIR} ) IF(NOT WIN32) # on non-win32 we don't need to take care about WIN32_DEBUG_POSTFIX FIND_LIBRARY(TAGLIB_LIBRARIES tag PATHS ${KDE4_LIB_DIR} ${LIB_INSTALL_DIR}) ELSE(NOT WIN32) # 1. get all possible libnames SET(args PATHS ${KDE4_LIB_DIR} ${LIB_INSTALL_DIR}) SET(newargs "") SET(libnames_release "") SET(libnames_debug "") LIST(LENGTH args listCount) # just one name LIST(APPEND libnames_release "tag") LIST(APPEND libnames_debug "tagd") SET(newargs ${args}) # search the release lib FIND_LIBRARY(TAGLIB_LIBRARIES_RELEASE NAMES ${libnames_release} ${newargs} ) # search the debug lib FIND_LIBRARY(TAGLIB_LIBRARIES_DEBUG NAMES ${libnames_debug} ${newargs} ) IF(TAGLIB_LIBRARIES_RELEASE AND TAGLIB_LIBRARIES_DEBUG) # both libs found SET(TAGLIB_LIBRARIES optimized ${TAGLIB_LIBRARIES_RELEASE} debug ${TAGLIB_LIBRARIES_DEBUG}) ELSE(TAGLIB_LIBRARIES_RELEASE AND TAGLIB_LIBRARIES_DEBUG) IF(TAGLIB_LIBRARIES_RELEASE) # only release found SET(TAGLIB_LIBRARIES ${TAGLIB_LIBRARIES_RELEASE}) ELSE(TAGLIB_LIBRARIES_RELEASE) # only debug (or nothing) found SET(TAGLIB_LIBRARIES ${TAGLIB_LIBRARIES_DEBUG}) ENDIF(TAGLIB_LIBRARIES_RELEASE) ENDIF(TAGLIB_LIBRARIES_RELEASE AND TAGLIB_LIBRARIES_DEBUG) MARK_AS_ADVANCED(TAGLIB_LIBRARIES_RELEASE) MARK_AS_ADVANCED(TAGLIB_LIBRARIES_DEBUG) ENDIF(NOT WIN32) INCLUDE(FindPackageMessage) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Taglib DEFAULT_MSG TAGLIB_INCLUDES TAGLIB_LIBRARIES) endif(TAGLIBCONFIG_EXECUTABLE) if(TAGLIB_FOUND) if(NOT Taglib_FIND_QUIETLY AND TAGLIBCONFIG_EXECUTABLE) message(STATUS "Taglib found: ${TAGLIB_LIBRARIES}") endif(NOT Taglib_FIND_QUIETLY AND TAGLIBCONFIG_EXECUTABLE) else(TAGLIB_FOUND) if(Taglib_FIND_REQUIRED) message(FATAL_ERROR "Could not find Taglib") endif(Taglib_FIND_REQUIRED) endif(TAGLIB_FOUND) acoustid-fingerprinter-0.6/constants.h000066400000000000000000000007321202171654700202420ustar00rootroot00000000000000#ifndef FPSUBMIT_CONSTANTS_H_ #define FPSUBMIT_CONSTANTS_H_ static const char *API_KEY_URL = "http://acoustid.org/api-key"; static const char *SUBMIT_URL = "http://api.acoustid.org/v2/submit"; //static const char *SUBMIT_URL = "http://127.0.0.1:8080/ws/submit"; static const char *CLIENT_API_KEY = "cvJ31mD0"; static const int AUDIO_LENGTH = 120; static const int MAX_ACTIVE_FILES = 3; static const int MAX_BATCH_SIZE = 100; static const int MIN_BATCH_SIZE = 50; #endif acoustid-fingerprinter-0.6/crc.c000066400000000000000000000121151202171654700167660ustar00rootroot00000000000000/** * \file crc.c * Functions and types for CRC checks. * * Generated on Sun Oct 24 10:58:31 2010, * by pycrc v0.7.6, http://www.tty1.net/pycrc/ * using the configuration: * Width = 32 * Poly = 0x04c11db7 * XorIn = 0xffffffff * ReflectIn = True * XorOut = 0xffffffff * ReflectOut = True * Algorithm = table-driven *****************************************************************************/ #include "crc.h" #include #include /** * Static table used for the table_driven implementation. *****************************************************************************/ static const crc_t crc_table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; /** * Reflect all bits of a \a data word of \a data_len bytes. * * \param data The data word to be reflected. * \param data_len The width of \a data expressed in number of bits. * \return The reflected data. *****************************************************************************/ crc_t crc_reflect(crc_t data, size_t data_len) { unsigned int i; crc_t ret; ret = data & 0x01; for (i = 1; i < data_len; i++) { data >>= 1; ret = (ret << 1) | (data & 0x01); } return ret; } /** * Update the crc value with new data. * * \param crc The current crc value. * \param data Pointer to a buffer of \a data_len bytes. * \param data_len Number of bytes in the \a data buffer. * \return The updated crc value. *****************************************************************************/ crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len) { unsigned int tbl_idx; while (data_len--) { tbl_idx = (crc ^ *data) & 0xff; crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff; data++; } return crc & 0xffffffff; } acoustid-fingerprinter-0.6/crc.h000066400000000000000000000043401202171654700167740ustar00rootroot00000000000000/** * \file crc.h * Functions and types for CRC checks. * * Generated on Sun Oct 24 10:58:41 2010, * by pycrc v0.7.6, http://www.tty1.net/pycrc/ * using the configuration: * Width = 32 * Poly = 0x04c11db7 * XorIn = 0xffffffff * ReflectIn = True * XorOut = 0xffffffff * ReflectOut = True * Algorithm = table-driven *****************************************************************************/ #ifndef __CRC_H__ #define __CRC_H__ #include #include #ifdef __cplusplus extern "C" { #endif /** * The definition of the used algorithm. *****************************************************************************/ #define CRC_ALGO_TABLE_DRIVEN 1 /** * The type of the CRC values. * * This type must be big enough to contain at least 32 bits. *****************************************************************************/ typedef unsigned long crc_t; /** * Reflect all bits of a \a data word of \a data_len bytes. * * \param data The data word to be reflected. * \param data_len The width of \a data expressed in number of bits. * \return The reflected data. *****************************************************************************/ crc_t crc_reflect(crc_t data, size_t data_len); /** * Calculate the initial crc value. * * \return The initial crc value. *****************************************************************************/ #define crc_init() (0xffffffff) /** * Update the crc value with new data. * * \param crc The current crc value. * \param data Pointer to a buffer of \a data_len bytes. * \param data_len Number of bytes in the \a data buffer. * \return The updated crc value. *****************************************************************************/ crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len); /** * Calculate the final crc value. * * \param crc The current crc value. * \return The final crc value. *****************************************************************************/ #define crc_finalize(crc) (crc ^ 0xffffffff) #ifdef __cplusplus } /* closing brace for extern "C" */ #endif #endif /* __CRC_H__ */ acoustid-fingerprinter-0.6/decoder.cpp000066400000000000000000000000571202171654700201660ustar00rootroot00000000000000#include "decoder.h" QMutex Decoder::m_mutex; acoustid-fingerprinter-0.6/decoder.h000066400000000000000000000172441202171654700176410ustar00rootroot00000000000000/* * Chromaprint -- Audio fingerprinting toolkit * Copyright (C) 2010 Lukas Lalinsky * * 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 */ #ifndef FPSUBMIT_DECODER_H_ #define FPSUBMIT_DECODER_H_ #include #include #include #include extern "C" { #include #include #ifdef HAVE_AV_AUDIO_CONVERT #include "ffmpeg/audioconvert.h" #include "ffmpeg/samplefmt.h" #endif } #include "fingerprintcalculator.h" #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 64, 0) #define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16 #endif class Decoder { public: Decoder(const std::string &fileName); ~Decoder(); bool Open(); void Decode(FingerprintCalculator *consumer, int maxLength = 0); int Channels() { return m_codec_ctx->channels; } int SampleRate() { return m_codec_ctx->sample_rate; } std::string LastError() { return m_error; } //static void lock_manager(); static void initialize(); private: static const int BUFFER_SIZE = AVCODEC_MAX_AUDIO_FRAME_SIZE * 2; uint8_t *m_buffer1; uint8_t *m_buffer2; std::string m_file_name; std::string m_error; AVFormatContext *m_format_ctx; AVCodecContext *m_codec_ctx; bool m_codec_open; AVStream *m_stream; static QMutex m_mutex; #ifdef HAVE_AV_AUDIO_CONVERT AVAudioConvert *m_convert_ctx; #endif }; /*inline static void Decoder::lock_manager(void **mutex, enum AVLockOp op) { switch (op) { case AV_LOCK_CREATE: *mutex = new QMutex(); return 1; case AV_LOCK_DESTROY: delete (QMutex *)(*mutex); return 1; case AV_LOCK_ACQUIRE: ((QMutex *)(*mutex))->lock(); return 1; case AV_LOCK_RELEASE: ((QMutex *)(*mutex))->unlock(); return 1; } return 0; }*/ inline void Decoder::initialize() { av_register_all(); av_log_set_level(AV_LOG_ERROR); //av_lockmgr_register(&Decoder::lock_manager) } inline Decoder::Decoder(const std::string &file_name) : m_file_name(file_name), m_format_ctx(0), m_codec_ctx(0), m_stream(0), m_codec_open(false) #ifdef HAVE_AV_AUDIO_CONVERT , m_convert_ctx(0) #endif { m_buffer1 = (uint8_t *)av_malloc(BUFFER_SIZE + 16); m_buffer2 = (uint8_t *)av_malloc(BUFFER_SIZE + 16); } inline Decoder::~Decoder() { if (m_codec_ctx && m_codec_open) { QMutexLocker locker(&m_mutex); avcodec_close(m_codec_ctx); } if (m_format_ctx) { #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 17, 0) av_close_input_file(m_format_ctx); #else avformat_close_input(&m_format_ctx); #endif } #ifdef HAVE_AV_AUDIO_CONVERT if (m_convert_ctx) { av_audio_convert_free(m_convert_ctx); } #endif av_free(m_buffer1); av_free(m_buffer2); } inline bool Decoder::Open() { QMutexLocker locker(&m_mutex); #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0) if (av_open_input_file(&m_format_ctx, m_file_name.c_str(), NULL, 0, NULL) != 0) { #else if (avformat_open_input(&m_format_ctx, m_file_name.c_str(), NULL, NULL) != 0) { #endif m_error = "Couldn't open the file." + m_file_name; return false; } #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 6, 0) if (av_find_stream_info(m_format_ctx) < 0) { #else if (avformat_find_stream_info(m_format_ctx, NULL) < 0) { #endif m_error = "Couldn't find stream information in the file."; return false; } //dump_format(m_format_ctx, 0, m_file_name.c_str(), 0); for (int i = 0; i < m_format_ctx->nb_streams; i++) { AVCodecContext *avctx = m_format_ctx->streams[i]->codec; #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 64, 0) if (avctx && avctx->codec_type == CODEC_TYPE_AUDIO) { #else if (avctx && avctx->codec_type == AVMEDIA_TYPE_AUDIO) { #endif m_stream = m_format_ctx->streams[i]; m_codec_ctx = avctx; break; } } if (!m_codec_ctx) { m_error = "Couldn't find any audio stream in the file."; return false; } AVCodec *codec = avcodec_find_decoder(m_codec_ctx->codec_id); if (!codec) { m_error = "Unknown codec."; return false; } #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 8, 0) if (avcodec_open(m_codec_ctx, codec) < 0) { #else if (avcodec_open2(m_codec_ctx, codec, NULL) < 0) { #endif m_error = "Couldn't open the codec."; return false; } m_codec_open = true; if (m_codec_ctx->sample_fmt != AV_SAMPLE_FMT_S16) { #ifdef HAVE_AV_AUDIO_CONVERT m_convert_ctx = av_audio_convert_alloc(AV_SAMPLE_FMT_S16, m_codec_ctx->channels, (AVSampleFormat)m_codec_ctx->sample_fmt, m_codec_ctx->channels, NULL, 0); if (!m_convert_ctx) { m_error = "Couldn't create sample format converter."; return false; } #else m_error = "Unsupported sample format."; return false; #endif } if (Channels() <= 0) { m_error = "Invalid audio stream (no channels)."; return false; } if (SampleRate() <= 0) { m_error = "Invalid sample rate."; return false; } return true; } #include inline void Decoder::Decode(FingerprintCalculator *consumer, int max_length) { AVPacket packet, packet_temp; int remaining = max_length * SampleRate() * Channels(); int stop = 0; av_init_packet(&packet); av_init_packet(&packet_temp); while (!stop) { if (av_read_frame(m_format_ctx, &packet) < 0) { // consumer->Flush(); break; } packet_temp.data = packet.data; packet_temp.size = packet.size; while (packet_temp.size > 0) { int buffer_size = BUFFER_SIZE; #if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(52, 25, 0) int consumed = avcodec_decode_audio2( m_codec_ctx, (int16_t *)m_buffer1, &buffer_size, packet_temp.data, packet_temp.size); #else int consumed = avcodec_decode_audio3( m_codec_ctx, (int16_t *)m_buffer1, &buffer_size, &packet_temp); #endif if (consumed < 0) { break; } packet_temp.data += consumed; packet_temp.size -= consumed; if (buffer_size <= 0) { continue; } int16_t *audio_buffer; #ifdef HAVE_AV_AUDIO_CONVERT if (m_convert_ctx) { const void *ibuf[6] = { m_buffer1 }; void *obuf[6] = { m_buffer2 }; #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 8, 0) int istride[6] = { av_get_bits_per_sample_format(m_codec_ctx->sample_fmt) / 8 }; #else int istride[6] = { av_get_bytes_per_sample(m_codec_ctx->sample_fmt) }; #endif int ostride[6] = { 2 }; int len = buffer_size / istride[0]; if (av_audio_convert(m_convert_ctx, obuf, ostride, ibuf, istride, len) < 0) { break; } audio_buffer = (int16_t *)m_buffer2; buffer_size = len * ostride[0]; } else { audio_buffer = (int16_t *)m_buffer1; } #else audio_buffer = (int16_t *)m_buffer1; #endif int length = buffer_size / 2; if (max_length) { length = std::min(remaining, length); } consumer->feed(audio_buffer, length); if (max_length) { remaining -= length; if (remaining <= 0) { stop = 1; break; } } } if (packet.data) { av_free_packet(&packet); } } } #endif acoustid-fingerprinter-0.6/ffmpeg/000077500000000000000000000000001202171654700173175ustar00rootroot00000000000000acoustid-fingerprinter-0.6/ffmpeg/audioconvert.h000066400000000000000000000074321202171654700222000ustar00rootroot00000000000000/* * audio conversion * Copyright (c) 2006 Michael Niedermayer * Copyright (c) 2008 Peter Ross * * This file is part of FFmpeg. * * FFmpeg 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. * * FFmpeg 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVCODEC_AUDIOCONVERT_H #define AVCODEC_AUDIOCONVERT_H /** * @file * Audio format conversion routines */ #include "libavcodec/avcodec.h" #include "samplefmt.h" #if FF_API_OLD_SAMPLE_FMT /** * @deprecated Use av_get_sample_fmt_string() instead. */ attribute_deprecated void avcodec_sample_fmt_string(char *buf, int buf_size, int sample_fmt); /** * @deprecated Use av_get_sample_fmt_name() instead. */ attribute_deprecated const char *avcodec_get_sample_fmt_name(int sample_fmt); /** * @deprecated Use av_get_sample_fmt() instead. */ attribute_deprecated enum AVSampleFormat avcodec_get_sample_fmt(const char* name); #endif #if FF_API_OLD_AUDIOCONVERT /** * @deprecated Use av_get_channel_layout() instead. */ attribute_deprecated int64_t avcodec_get_channel_layout(const char *name); /** * @deprecated Use av_get_channel_layout_string() instead. */ attribute_deprecated void avcodec_get_channel_layout_string(char *buf, int buf_size, int nb_channels, int64_t channel_layout); /** * @deprecated Use av_get_channel_layout_nb_channels() instead. */ attribute_deprecated int avcodec_channel_layout_num_channels(int64_t channel_layout); #endif /** * Guess the channel layout * @param nb_channels * @param codec_id Codec identifier, or CODEC_ID_NONE if unknown * @param fmt_name Format name, or NULL if unknown * @return Channel layout mask */ uint64_t avcodec_guess_channel_layout(int nb_channels, enum CodecID codec_id, const char *fmt_name); struct AVAudioConvert; typedef struct AVAudioConvert AVAudioConvert; /** * Create an audio sample format converter context * @param out_fmt Output sample format * @param out_channels Number of output channels * @param in_fmt Input sample format * @param in_channels Number of input channels * @param[in] matrix Channel mixing matrix (of dimension in_channel*out_channels). Set to NULL to ignore. * @param flags See AV_CPU_FLAG_xx * @return NULL on error */ AVAudioConvert *av_audio_convert_alloc(enum AVSampleFormat out_fmt, int out_channels, enum AVSampleFormat in_fmt, int in_channels, const float *matrix, int flags); /** * Free audio sample format converter context */ void av_audio_convert_free(AVAudioConvert *ctx); /** * Convert between audio sample formats * @param[in] out array of output buffers for each channel. set to NULL to ignore processing of the given channel. * @param[in] out_stride distance between consecutive output samples (measured in bytes) * @param[in] in array of input buffers for each channel * @param[in] in_stride distance between consecutive input samples (measured in bytes) * @param len length of audio frame size (measured in samples) */ int av_audio_convert(AVAudioConvert *ctx, void * const out[6], const int out_stride[6], const void * const in[6], const int in_stride[6], int len); #endif /* AVCODEC_AUDIOCONVERT_H */ acoustid-fingerprinter-0.6/ffmpeg/samplefmt.h000066400000000000000000000135411202171654700214640ustar00rootroot00000000000000/* * This file is part of FFmpeg. * * FFmpeg 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. * * FFmpeg 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef AVUTIL_SAMPLEFMT_H #define AVUTIL_SAMPLEFMT_H #include "libavutil/avutil.h" /** * all in native-endian format */ enum AVSampleFormat { AV_SAMPLE_FMT_NONE = -1, AV_SAMPLE_FMT_U8, ///< unsigned 8 bits AV_SAMPLE_FMT_S16, ///< signed 16 bits AV_SAMPLE_FMT_S32, ///< signed 32 bits AV_SAMPLE_FMT_FLT, ///< float AV_SAMPLE_FMT_DBL, ///< double AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar AV_SAMPLE_FMT_FLTP, ///< float, planar AV_SAMPLE_FMT_DBLP, ///< double, planar AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically }; /** * Return the name of sample_fmt, or NULL if sample_fmt is not * recognized. */ const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt); /** * Return a sample format corresponding to name, or AV_SAMPLE_FMT_NONE * on error. */ enum AVSampleFormat av_get_sample_fmt(const char *name); /** * Return the planar<->packed alternative form of the given sample format, or * AV_SAMPLE_FMT_NONE on error. If the passed sample_fmt is already in the * requested planar/packed format, the format returned is the same as the * input. */ enum AVSampleFormat av_get_alt_sample_fmt(enum AVSampleFormat sample_fmt, int planar); /** * Generate a string corresponding to the sample format with * sample_fmt, or a header if sample_fmt is negative. * * @param buf the buffer where to write the string * @param buf_size the size of buf * @param sample_fmt the number of the sample format to print the * corresponding info string, or a negative value to print the * corresponding header. * @return the pointer to the filled buffer or NULL if sample_fmt is * unknown or in case of other errors */ char *av_get_sample_fmt_string(char *buf, int buf_size, enum AVSampleFormat sample_fmt); #if FF_API_GET_BITS_PER_SAMPLE_FMT /** * @deprecated Use av_get_bytes_per_sample() instead. */ attribute_deprecated int av_get_bits_per_sample_fmt(enum AVSampleFormat sample_fmt); #endif /** * Return number of bytes per sample. * * @param sample_fmt the sample format * @return number of bytes per sample or zero if unknown for the given * sample format */ int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt); /** * Check if the sample format is planar. * * @param sample_fmt the sample format to inspect * @return 1 if the sample format is planar, 0 if it is interleaved */ int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt); /** * Get the required buffer size for the given audio parameters. * * @param[out] linesize calculated linesize, may be NULL * @param nb_channels the number of channels * @param nb_samples the number of samples in a single channel * @param sample_fmt the sample format * @return required buffer size, or negative error code on failure */ int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align); /** * Fill channel data pointers and linesize for samples with sample * format sample_fmt. * * The pointers array is filled with the pointers to the samples data: * for planar, set the start point of each channel's data within the buffer, * for packed, set the start point of the entire buffer only. * * The linesize array is filled with the aligned size of each channel's data * buffer for planar layout, or the aligned size of the buffer for all channels * for packed layout. * * @param[out] audio_data array to be filled with the pointer for each channel * @param[out] linesize calculated linesize * @param buf the pointer to a buffer containing the samples * @param nb_channels the number of channels * @param nb_samples the number of samples in a single channel * @param sample_fmt the sample format * @param align buffer size alignment (1 = no alignment required) * @return 0 on success or a negative error code on failure */ int av_samples_fill_arrays(uint8_t **audio_data, int *linesize, uint8_t *buf, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align); /** * Allocate a samples buffer for nb_samples samples, and fill data pointers and * linesize accordingly. * The allocated samples buffer can be freed by using av_freep(&audio_data[0]) * * @param[out] audio_data array to be filled with the pointer for each channel * @param[out] linesize aligned size for audio buffer(s) * @param nb_channels number of audio channels * @param nb_samples number of samples per channel * @param align buffer size alignment (1 = no alignment required) * @return 0 on success or a negative error code on failure * @see av_samples_fill_arrays() */ int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align); #endif /* AVUTIL_SAMPLEFMT_H */ acoustid-fingerprinter-0.6/fingerprintcalculator.cpp000066400000000000000000000015241202171654700231620ustar00rootroot00000000000000#include #include "fingerprintcalculator.h" QMutex FingerprintCalculator::m_mutex; FingerprintCalculator::FingerprintCalculator() { QMutexLocker locker(&m_mutex); m_context = chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT); } FingerprintCalculator::~FingerprintCalculator() { QMutexLocker locker(&m_mutex); chromaprint_free(m_context); } bool FingerprintCalculator::start(int sampleRate, int numChannels) { return chromaprint_start(m_context, sampleRate, numChannels); } void FingerprintCalculator::feed(qint16 *data, int size) { chromaprint_feed(m_context, data, size); } QString FingerprintCalculator::finish() { char *fingerprint; chromaprint_finish(m_context); chromaprint_get_fingerprint(m_context, &fingerprint); QString result(fingerprint); free(fingerprint); return result; } acoustid-fingerprinter-0.6/fingerprintcalculator.h000066400000000000000000000006741202171654700226340ustar00rootroot00000000000000#ifndef FPSUBMIT_FINGERPRINTCALCULATOR_H_ #define FPSUBMIT_FINGERPRINTCALCULATOR_H_ #include #include #include class FingerprintCalculator { public: FingerprintCalculator(); ~FingerprintCalculator(); bool start(int sampleRate, int numChannels); void feed(qint16 *data, int size); QString finish(); private: ChromaprintContext *m_context; static QMutex m_mutex; }; #endif acoustid-fingerprinter-0.6/fingerprinter.cpp000066400000000000000000000177741202171654700214550ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "loadfilelisttask.h" #include "analyzefiletask.h" #include "updatelogfiletask.h" #include "fingerprinter.h" #include "constants.h" #include "utils.h" #include "gzip.h" class NetworkProxyFactory : public QNetworkProxyFactory { public: NetworkProxyFactory() : m_httpProxy(QNetworkProxy::NoProxy) { char* httpProxyUrl = getenv("http_proxy"); if (httpProxyUrl) { QUrl url = QUrl::fromEncoded(QByteArray(httpProxyUrl)); if (url.isValid() && !url.host().isEmpty()) { m_httpProxy = QNetworkProxy(QNetworkProxy::HttpProxy, url.host(), url.port(80)); if (!url.userName().isEmpty()) { m_httpProxy.setUser(url.userName()); if (!url.password().isEmpty()) { m_httpProxy.setPassword(url.password()); } } } } } QList queryProxy(const QNetworkProxyQuery& query) { QList proxies = QNetworkProxyFactory::systemProxyForQuery(query); QString protocol = query.protocolTag().toLower(); if (m_httpProxy.type() != QNetworkProxy::NoProxy && protocol == QLatin1String("http")) { QMutableListIterator i(proxies); while (i.hasNext()) { QNetworkProxy& proxy = i.next(); if (proxy.type() == QNetworkProxy::NoProxy) { i.remove(); } } proxies.append(m_httpProxy); proxies.append(QNetworkProxy::NoProxy); } return proxies; } private: QNetworkProxy m_httpProxy; }; Fingerprinter::Fingerprinter(const QString &apiKey, const QStringList &directories) : m_apiKey(apiKey), m_directories(directories), m_paused(false), m_cancelled(false), m_finished(false), m_reply(0), m_activeFiles(0), m_fingerprintedFiles(0), m_submittedFiles(0) { m_networkAccessManager = new QNetworkAccessManager(this); m_networkAccessManager->setProxyFactory(new NetworkProxyFactory()); connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply *)), SLOT(onRequestFinished(QNetworkReply*))); } Fingerprinter::~Fingerprinter() { } void Fingerprinter::start() { m_time.start(); LoadFileListTask *task = new LoadFileListTask(m_directories); connect(task, SIGNAL(finished(const QStringList &)), SLOT(onFileListLoaded(const QStringList &)), Qt::QueuedConnection); connect(task, SIGNAL(currentPathChanged(const QString &)), SIGNAL(currentPathChanged(const QString &)), Qt::QueuedConnection); task->setAutoDelete(true); emit fileListLoadingStarted(); QThreadPool::globalInstance()->start(task); } void Fingerprinter::pause() { m_paused = true; } void Fingerprinter::resume() { m_paused = false; while (!m_files.isEmpty() && m_activeFiles < MAX_ACTIVE_FILES) { fingerprintNextFile(); } maybeSubmit(); } void Fingerprinter::cancel() { m_cancelled = true; m_files.clear(); m_submitQueue.clear(); if (m_reply) { m_reply->abort(); } } bool Fingerprinter::isPaused() { return m_paused; } bool Fingerprinter::isCancelled() { return m_cancelled; } bool Fingerprinter::isFinished() { return m_finished; } bool Fingerprinter::isRunning() { return !isPaused() && !isCancelled() && !isFinished(); } void Fingerprinter::onFileListLoaded(const QStringList &files) { m_files = files; if (m_files.isEmpty()) { m_finished = true; emit noFilesError(); emit finished(); return; } emit fingerprintingStarted(files.size()); while (!m_files.isEmpty() && m_activeFiles < MAX_ACTIVE_FILES) { fingerprintNextFile(); } } void Fingerprinter::fingerprintNextFile() { if (m_files.isEmpty()) { return; } m_activeFiles++; QString path = m_files.takeFirst(); emit currentPathChanged(path); AnalyzeFileTask *task = new AnalyzeFileTask(path); connect(task, SIGNAL(finished(AnalyzeResult *)), SLOT(onFileAnalyzed(AnalyzeResult *)), Qt::QueuedConnection); task->setAutoDelete(true); QThreadPool::globalInstance()->start(task); } void Fingerprinter::onFileAnalyzed(AnalyzeResult *result) { m_activeFiles--; emit progress(++m_fingerprintedFiles); if (!result->error) { if (!isCancelled()) { m_submitQueue.append(result); } if (isRunning()) { maybeSubmit(); } } else { qDebug() << "Error" << result->errorMessage << "while processing" << result->fileName; } if (isRunning()) { fingerprintNextFile(); } if (m_activeFiles == 0 && m_files.isEmpty()) { if (m_submitQueue.isEmpty()) { m_finished = true; emit finished(); return; } maybeSubmit(true); } } bool Fingerprinter::maybeSubmit(bool force) { int size = qMin(MAX_BATCH_SIZE, m_submitQueue.size()); if (!m_reply && (size >= MIN_BATCH_SIZE || (force && size > 0))) { qDebug() << "Submitting" << size << "fingerprints"; QUrl url; url.addQueryItem("user", m_apiKey); url.addQueryItem("client", CLIENT_API_KEY); for (int i = 0; i < size; i++) { AnalyzeResult *result = m_submitQueue.takeFirst(); qDebug() << " " << result->mbid; url.addQueryItem(QString("duration.%1").arg(i), QString::number(result->length)); if (!result->puid.isEmpty()) { url.addQueryItem(QString("puid.%1").arg(i), result->puid); } if (!result->mbid.isEmpty()) { url.addQueryItem(QString("mbid.%1").arg(i), result->mbid); } else { if (!result->track.isEmpty()) { url.addQueryItem(QString("track.%1").arg(i), result->track); } if (!result->artist.isEmpty()) { url.addQueryItem(QString("artist.%1").arg(i), result->artist); } if (!result->album.isEmpty()) { url.addQueryItem(QString("album.%1").arg(i), result->album); } if (!result->albumArtist.isEmpty()) { url.addQueryItem(QString("albumartist.%1").arg(i), result->albumArtist); } if (result->year) { url.addQueryItem(QString("year.%1").arg(i), QString::number(result->year)); } if (result->trackNo) { url.addQueryItem(QString("trackno.%1").arg(i), QString::number(result->trackNo)); } if (result->discNo) { url.addQueryItem(QString("discno.%1").arg(i), QString::number(result->discNo)); } } url.addQueryItem(QString("fingerprint.%1").arg(i), result->fingerprint); QString format = extractExtension(result->fileName); if (!format.isEmpty()) { url.addQueryItem(QString("fileformat.%1").arg(i), format); } if (result->bitrate) { url.addQueryItem(QString("bitrate.%1").arg(i), QString::number(result->bitrate)); } m_submitting.append(result->fileName); delete result; } qDebug() << url.encodedQuery(); QNetworkRequest request = QNetworkRequest(QUrl::fromEncoded(SUBMIT_URL)); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setRawHeader("Content-Encoding", "gzip"); request.setRawHeader("User-Agent", userAgentString().toAscii()); m_reply = m_networkAccessManager->post(request, gzipCompress(url.encodedQuery())); return true; } return false; } void Fingerprinter::onRequestFinished(QNetworkReply *reply) { bool stop = false; QNetworkReply::NetworkError error = reply->error(); if (m_cancelled) { stop = true; } else if (error == QNetworkReply::UnknownContentError) { QString errorMessage = reply->readAll(); if (errorMessage.contains("User with the API key")) { emit authenticationError(); stop = true; } else { qWarning() << "Submittion failed:" << errorMessage; } } else if (error != QNetworkReply::NoError) { qWarning() << "Submission failed with network error" << error; emit networkError(reply->errorString()); stop = true; } if (!stop && error == QNetworkReply::NoError) { m_submitted.append(m_submitting); m_submittedFiles += m_submitting.size(); maybeSubmit(); qDebug() << "Submission finished"; } if (m_submitted.size() > 0) { UpdateLogFileTask *task = new UpdateLogFileTask(m_submitted); task->setAutoDelete(true); QThreadPool::globalInstance()->start(task); m_submitted.clear(); } m_submitting.clear(); reply->deleteLater(); m_reply = 0; if (m_submitQueue.isEmpty() && m_files.isEmpty()) { m_finished = true; emit finished(); return; } if (isRunning()) { maybeSubmit(m_files.isEmpty()); } } acoustid-fingerprinter-0.6/fingerprinter.h000066400000000000000000000030161202171654700211020ustar00rootroot00000000000000#ifndef FPSUBMIT_FINGERPRINTER_H_ #define FPSUBMIT_FINGERPRINTER_H_ #include #include #include #include #include class AnalyzeResult; class QNetworkReply; class Fingerprinter : public QObject { Q_OBJECT public: Fingerprinter(const QString &apiKey, const QStringList &directories); ~Fingerprinter(); bool isPaused(); bool isCancelled(); bool isRunning(); bool isFinished(); int submitttedFingerprints() const { return m_submittedFiles; } signals: void statusChanged(const QString &message); void currentPathChanged(const QString &path); void fileListLoadingStarted(); void fingerprintingStarted(int fileCount); void progress(int i); void finished(); void networkError(const QString &message); void authenticationError(); void noFilesError(); public slots: void start(); void pause(); void resume(); void cancel(); private slots: void onFileListLoaded(const QStringList &files); void onFileAnalyzed(AnalyzeResult *); void onRequestFinished(QNetworkReply *reply); private: void fingerprintNextFile(); bool maybeSubmit(bool force=false); QString m_apiKey; QStringList m_files; QStringList m_directories; QNetworkAccessManager *m_networkAccessManager; QList m_submitQueue; QStringList m_submitting; QStringList m_submitted; QNetworkReply *m_reply; QTime m_time; int m_fingerprintedFiles; int m_submittedFiles; int m_activeFiles; bool m_cancelled; bool m_paused; bool m_finished; }; #endif acoustid-fingerprinter-0.6/fingerprinter.qrc000066400000000000000000000003561202171654700214440ustar00rootroot00000000000000 images/acoustid-fp-16.png images/acoustid-fp-24.png images/acoustid-fp-32.png images/acoustid-fp-48.png acoustid-fingerprinter-0.6/fingerprinter.rc000066400000000000000000000000661202171654700212610ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "images\\acoustid-fp.ico" acoustid-fingerprinter-0.6/gzip.cpp000066400000000000000000000021061202171654700175270ustar00rootroot00000000000000#include "crc.h" #include "gzip.h" inline QByteArray render32BitInt(unsigned long value) { unsigned char data[4]; data[0] = (value ) & 255; data[1] = (value >> 8) & 255; data[2] = (value >> 16) & 255; data[3] = (value >> 24) & 255; return QByteArray((char *)data, 4); } inline unsigned long calculateCrc32(const QByteArray &data) { crc_t crc; crc = crc_init(); crc = crc_update(crc, (unsigned char *)data.data(), data.size()); crc = crc_finalize(crc); return crc; } QByteArray gzipCompress(const QByteArray &data) { const char header[10] = { 0x1f, 0x8b, // ID1 + ID2 8, // Compression Method 0, // Flags 0, 0, 0, 0, // Modification Time 2, // Extra Flags 255, // Operating System }; QByteArray compressedData = qCompress(data); compressedData.remove(0, 6); // Qt size + zlib header compressedData.remove(compressedData.size() - 4, 4); // zlib footer QByteArray result; result.append(header, 10); result.append(compressedData); result.append(render32BitInt(calculateCrc32(data))); result.append(render32BitInt(data.size())); return result; } acoustid-fingerprinter-0.6/gzip.h000066400000000000000000000002031202171654700171700ustar00rootroot00000000000000#ifndef FPSUBMIT_GZIP_H_ #define FPSUBMIT_GZIP_H_ #include QByteArray gzipCompress(const QByteArray &data); #endif acoustid-fingerprinter-0.6/images/000077500000000000000000000000001202171654700173205ustar00rootroot00000000000000acoustid-fingerprinter-0.6/images/acoustid-fingerprinter.svg000066400000000000000000001160261202171654700245360ustar00rootroot00000000000000 image/svg+xml acoustid-fingerprinter-0.6/images/acoustid-fp-128.png000066400000000000000000000207061202171654700225610ustar00rootroot00000000000000‰PNG  IHDR€€У>aЫsBIT|dˆ pHYsnКnКжоБtEXtSoftwarewww.inkscape.org›ю< IDATxœэyеч?/ЋЊЋњО/ЉЅnH€„юЕ@X # —šcc/k№ВФx6fbbУыХ1уёыёc{=3і0OЛT у3У—И,Ф!@К­Ѓ[RЗњЈЊЬ§у—Џ+++3ыPK}щQ!ЉT•ѕђ§ОяwП—ЪВ,ЮbђТэœХшт,€ИR*ЎTѓhc4p–‚џ Œ+ѕПG{ gjВћqЅц9оКЖгВž­ёœiœе№M”rўћ/Fk ЃIM€ИR!рŽђ3Ј^ИPП=?Ўд @aћ5aчiТоXžИh­˜5‹ЦеЋбš@…B `Ч*BЃ6ЪгˆЩN€;*fЭ"‹Qоб!яZжF`.p№5р\„nО&м ˆU‘Њ*Jъъ0 *Я9Ы4gьyф‘Nрjр:рR`P‚˜† ƒIK€ИRm@KљЬ™˜‰V2IЌЕ#рр3Я\œœЌц•œ%Р„С2€ВщгБ’IyЅRФZZ8МeЫ\ њL„ L0_`2`90ЌўЭD+‘ жд@пюнe}Ÿ}CЂ€:`6аЬ3“™ЫPŠHUеА0“IJъы‡Ѓ#ЏМAцЈ‰І#ЮрYL,‹TWcYf29ь`Y”ддpфеWЕКˆ)Јc™II€ИR3Кh}НЌ~[јZ Djk8ВuЋДд3€&&˜”РЖџ‘šБџЖрЕˆTVpьwŒT?dš6$1t–учDЊЋг€-|3‘УРˆF1 ŽОўКжa hGЂƒ a&+:"UUУ€жšсВ2ŽМђŠžЃ"јЉіŸbю&ФMv€pEEZИД€N9ќ”!>@-ЂЦН˜ЌшWT |X и$PЁ(Х‘­[ѕ)$ЈCBeœ%РјC\Љ00%R]Ѕі/3‘@…B vuЉоO>б‚U@+Є84юo LBУ ‡кЯвщ„3Ќ@PЩ˜ПqE  T^ž%№Ќ—н.wєЕзєZ@Ѓчw shHџ3ŒЌўf$!4Ўчp\ОHt€`XшЮTАƒцр =лЖ9ЭР„qЧѕр‹DЛ ‹щvцџ3Д€CјЎК@а‚a\ЯсИ|‘ш•– мS 8дП†Уд ЁFЄ@4ЎB“ŠqЅ  -fм­№и,sє7м ЁZФ,х,Ц І#ЭИюВžПНЛvCGŽ8B•ˆ#8Ў§€q;№"‘ŽwjЫ4}П|єѕзнљ€Цy‡аЄ$€ ‡г ‡ѓч„Ћ. B•Œув№d#@€Ї?G*8hѕCVB(†…ъŸ`\jЩFбЁPІучJќјСхъ„P#уИChВ Ѕ23‰V*•ѓЫCGŽ8+ƒ:!дТ8ЮŽйAЧ•њOqЅіЦ•њя#xйv#ЩЮ§чБњ5ТNGpмVЧф уJ§ЗHMЭOЂmРїтJ­k* ]E"ž}љТU,G:„ЦmepЬ Ўдъpyљг:7вжЙ‘Віv€Ц•:Uл D3@WЮ?ИСв Ё(EњqЅZуJЭ{,cŽРŸ7_v*!епOэтEЈpxp=2С†§*tВ:@@wАєlпn8HЃ;„š(Тˆ++ѕSрАиWЊМ "ЦтJmˆ65]›вJЊ€Tџ–бЦFPъV$эZoПЪ(lќэFQЊ_УЃ2XI:!”їxтJ•Яї8оОјqСƒ:ŒиъяЯk—.Б…п?ќ*Љ­X›8vьрЋРjdГf!ЋЎ@FQЮŸЎЪ`9щЁBцѓ/ ѕ˜ЊцЯ'‹мWЊЉЈ1Cр2ЃДtQЌЅ•T?цРР№ŸЁX)@еОxќ[Р-РMШЁ mH&ˆP*Нњ‹йЌbš3їoкЄЕнЕРb„d#^sn0ЂбВHUU–№ѕ риіэ%Љўў"№DLCђђЙVGЛr8€nDjjЌѓx``С8knіз–Х—џіo^ ЁРЪ`\Љ№]hЄvљђ сЇњњР4Q†Сс—_Ој„W „ёоƒБB€;b­­ЄДњзЊ` C XІЩБwпе!` щЫ›N3WЊqж|‹>MkжЄТх…ЫЫiлИ1аC<єќѓЮ|@О•С[Б}‘ъ… Q†!А…Џ‰€Rt=ї\туЬFLо"ФЬŒЈuФ•šЌŽ64dЎњЌƒlйЖПBдтtrяж`Y–oшзђЕЏeІ^w]`qрацЭN? пЪр7BБUѓч >e“@ПЌTŠžэлУ§ћі• Ћ~Аih):сф…Q'p›‰ЈpY™ЏњзТД€t1fЙ'~:  OбЇe§њŒџh\Н:UR[ыkNьмiєяпЏHWЋ Ј Ц•ъ@BXj/ЦL$2V§А&8yRО`Yьќq}Jiт7Ь`„PЦЎдзgЏxс{я='bHЎ…`ћ(РЇо_1gNЊЌН=Cи*Жš/П|0hр]™~@ЎVёлЉЎІlњtR'Of ^ћьт‰щHЃ!Сˆ:ƒЃJ€ИRS€%‘Š oЕo;N8Вђj“;‚*rBŸепМn—]0Ї\uеб ёw=їœлœbЧK@_Ј>џ|R§§+_k7AoйJєє(ћzњˆšFАeД5РU€2"‘М„0иеЅ!˜sЏ^PVpzа ъVЌ№bFВѕъЋ?W†сл&thѓfЏH ЫХ•:˜ŠХ(ЉЏЯ№њЕ№Нђf"СпўVk}DЭTFАeД pЕa—gнЮ_zвŽ B&ЃS @еќљn[o‰HUесŠŠ=~пылН[ѕэо­§€тЅыЇ€ж”ЕЗЫъwy§n3чФL3а€h;ЂfдWЊXkФby­|'Že Б‹:ѓZЈœ7ЯM8ьWVОєнЎgŸе‚аQIйaщ:”"жвBъфЩ,ч/їЛН/Q_П!ТˆєŒІX”сpІ№ѓШЯ;ќmЋ™"йЩ—ПkE›šЬHuЕћm8ь WTl KW: +ƒSIявЯ$И$ки–•хѕчКпdoЏњђ_џUЛ!X+B|OљХ•ЊЖЋ91šИ$6/DјЧпп9n]‘г%bЗ˜JР}VЮ™“ђјN ші†++пЫЁ^№ђъIћ+€ЊXssІуwђdохш§›6щ:€>АВƒЊьў‚_=РѓіNЈ@Œ ьвяU(…•H$|€у;v(Чч5єЙ=ю{ TџsчК@ HGЎњ•+_ЗпѓDџъФЮ:;Љ§g8xЉŠХ2CОЛяЦоG %ŽзкЎ!@=3W*<m Ѕц ЙЎ=Z`)аЊ Ѓ`сƒЈEлљ‚Ь’Ќ—˜t­ЪsЮq{љ0€рФт‡ъv]ЃыйgйЩDышАєтpy9Щ“'гі?‡нw#йлЋ>џеЏtRЈ!u ™&яA`ЅыЋѓr]{Дp%Hb&зf ?И2‚ЅШЪ№zДЫyAзЉš7Я‹'pё ёЊА§€юЗоŠ+’’Дузлx_~иѕГŸ9Э@щИWъ*р^Џiђј<џhT PlCd@7gV#“яŽ P9О›&Ђz€~„ПКЦЁЭ›C6‘ЕЃж4Мyч‹€j”ЖџХўФ‡vцQ_?Ј{vщвiРУ>_KйŸЏ@H9toPтJнWъуJM-єЛії›Ажp*№ШV a’;%ьKeTЬšх•шGLРy`№а!uјХ5ёД€Z‡КЛ/Щ@&ћњp3Sv§ф'Юк@ћPww[Я;яќФџЩ‚‰є ОТ"ЄЄМ)* ‡Љ…vБЎP†ёHХЌYwOиБ|ЁИ‚Шbх и&ОN`ICCR…BюБ˜œ`GЎёь}єQg> ˜šьыЛ yќx^љ\8№Яџ>ЙgЎLyљškюЗ’Щчїљhcу АИшDњ .BLH з?Ў^А@5­]KЌЙy)№_‹И+‹јNNьмi8Т(эъЖ,}_ч@ЖXK‹щњ Q›}Р1`а~ятјbџу‡mѕ>œHіі.=ўЖЮЮфвŸџ<Аш4<ИTŠ—ЏН6жћЩ'%;ќу9‡_|ё’ Я7ЌZe љ–KM88Ч3 ic^‰RV/X@ЂЇ‡ђ3P†qП]ЯWŽWа5ТШcиNцр GВ›3нЙ€@ћ_:eŠE6†сї"dА:хРРРH`р‹/œf ќјŽ3ЬССРюоPY™ЕјЁ‡†fо}wВўТ soNzЖm3žž=Лbћ}їy^[3АЮ{р№$yTfПjH;ЫЙм]1{6(ХPOЉў~Bee1”њ!т„•#Ћ/Јyё"Ф ИЊqzЯ€3(†§Hа˜sЧ6 xђЩР№ uУ†dЌЅХ˜љ­oзЃю€… и"ѕѕ*чЭЛЩh­h QФАŒђ"@\ЉрЦЊsЯeЈЛ›Dw7CGъцЪ›ЗоrЫеˆcЗ€tžк‹9…Ре–Aœ›jвYИѓƒОябl!jџiP#'іoкЖлЭCŸ§ђ—гr}ойvжВ~}ŠSицОšVЏ>фмE2[ГAў&рыЅmmЅ*сл/sp@оВхмˆЁжчк9эП ‡Љ[БТ4JrЇВlнВЧВъЫэп.}ёŠ+Jѕч‹˜h'4NPTŸ=ќpј‹Їž*щнЕ+єйPiЉ5хъЋ‡е~ЌЅХЊYИАЈб)|­вІ^w.’…\DЬœ XљрЎЪЙs3„яLhє80sїУ_‰lжИ˜…+‹+еN• Афg?\ћкk§?ѕд@Ў‘ъячpцAЮУ)сУ/Нt!Ђ|Q:5+’е>@/’.ˆляЛЏфїїмЫѕЙ†UЋ’ЁВВŒlОќђ‚Э€[ј&`(EЫњѕхdeЋя­!САЧˆИRЋЂMMч‘ША№ннYŸ{яЛпmIіѕЕ!aЧ$5ы,YцTџ­6$Д-lZЛ65ылпЮЙuзЃ+ЇЈ0зчњn€ шC&Ћ`${{еЩ={rъђІЕkГœО–ѕы кЊь%|дœw^"карЎŒZШ}F"šМMР]ЅSЇfп+›еППБућпЏBкž—#!‡гўl њ YЫ~ё‹Œxiсƒїч#ѕr} вGЙзXЉдхЙnЬG$ A€NЫ:hП?"hОьВ,4ЌZ5*+Ы+№ўА§_ГЦ"[ЖУ=HЉ;Зˆ+UЎТсBБиА№ƒbкџњЏЃ'vюЌE6l,A„‰+5 1 ОhИјтЁXkkЦЪЩЇ?ПћЭ7 ћЙ>УMЂ‡_ziŽeš ƒОЇТссЇ„: 5РI„nфЅrЁЄЖжЌ]М8Kћ‘Ш`э’%s}пOј&b№›жЌёђ%’HrыB„МLРЕᆆВФёу$КЛI?јaspЗџшtsфbФ ­R†ёШ‘#hлИ1сѕ™i7н”3њбƒъ­aa fзпќЭеЙ~/ждdК| Bї# ž_'sљЂqѕъй‡A˜@OэВeYG-p‹lс+  бИzЕWk8|I:У™“З†ЫЫIєє0фaїНp№™gТћМ1ЫКžў\ЫВю њŽ2 kZgЇkЭ†UЋŽХš›‹чž|2lзфC'vюlиџ›пфŒ6|"€!dѕ;I.ŒˆhZЗЮKЭЇ€юЉз^ћ‚пїœТ7]/ ёјПњеTЄІЦЋХ­јЩq ;ИAлЉHф23™dшшбМNбвиvяНх=лЗOoПяОяcYAŸЏ_Йr0кдф^ТZЅід,YђVаї-гфѕлn‹кВ%ђцwN3‡†rn-ѕ'€W Q4єŠh^ЗЮГ 8вИzѕЛ*zзяћnсыїє†ЩwощйтŽнт†ф8†ЩдXxcЈМ<œАГ~… џРѕќХзGыызіэо3 oНђЪ$й*лДћ^ЭМw№щЇ/ КЦб7о0^XН:ятTi[›—€u˜фgv>ЪїњN8…_6uЊхг„вКŒ’’'S§§ мпїZѕњяe@Im­ху3iћŸсBА ИЫ"qьXОї™љ‹'NЈ|„Pї•Џ@6’ˆЭzoњ-ЗќoЇЌh”eР7pрУBЧ)$€ІK/ѕRЅкCпє„ЪЪžєЛ†—§бгoН5ЉŸwшњКгўg$ИќКJg+‹~!P†AнВЌі­ŽП>Љ^И№ №іHўЎ‡p&J< аiYнШ$цЗУf‡h~іП!Рёk~ЫўЛЇ№нš@Џ2Ÿz‚Vџћqй№з˜ч=ž2*fЯN…++Ндџq`B‚рЕ‘ќнi€r]лІ9“4ёџА§GюЕзўјА№ѓњЈ[ОмЌіN%'‘ц–нЩ2~пЗцКЩS…žнкхЫН­лВ?A2WIF˜eгІ’t"~Юš*чЮ5}ˆЇэџaв+єипѓМŽћCРМ?§S/ПEЗЗ@с‚тJЭE9Ї NЇЈnй2/‡(LЦвƒ>e8jх”ЖЕљ§ю Š$@­V†кCяAШNЇem^№КЎзъї9ЯРDVџ.ћњю—ЇXъwƒ#чф€ЇАВ_ƒHSЦЧШЭ§Л:ЛSR_oйGВ9Ё“@§ˆ "@P‚F‡i­6јЦџиіŸЬњW BђОn‚8џ?№Z§кЄ}‰8Џ]і}eР‹‹Мn№Tсžtlэ’%~!б—ШjtNкыХќЖIКўЉ№-Ѕ$PПы7нШ €ŸНwў ‹y9€ZH‡аG&ё~ |Ц{ўЊ€ц5kRЭоšE'>>Х‘ўuтŒ@>…ыцЯ7CЅYЁЛј—Є"Рš^0IUэ‰ћ„€:№KаiY:–Ю™œб _œ Из/'0уЗэVД{ 0uЋШnZ МЎЮZњѓŸћх,t§Ё}}OГцE€Х~7_(‚Bд.]ъ™ўEикE6 ŠУ5щєXt‰,  мKК4яыыЉ~§ “}cˆкўwуЁy:-ы_PъЯ"HЇi=ВђKJJИpгІСŠ9sќц№$Вђ?DШхЉе22іNжLR(Е/кијjлѕзG[жЏ?ЏrоМЦC/М§ќŸў)|јх—C~›;ќ&ЦЙы–/їR‰I{RŽр(ZиШ›юеЉ/b›/Г3„/М ЌєRљюїє„Ж^yЅч!ˆ_Г-ЃГPЁP‹ešпРВJJЇLМрG?J4Ў^эW№J &х]$’в;œВA€NЫJХ•Кщ!ј—Nгќ щѕћїіŸЊђœs’3яО;ЙяБЧB[oО9цмхъ5 nшіaАОіˆ‹&€S№zѕУ)х46џХынDх~щпDгФбЄсFЇe™Р|ыЎЛў2R[ЛqС~АX…BКСжЋюп‹д-оFLЉoI=ЋаiYOOлџдэжUЄwпГЎэ†RЫџспјЦ7Ђ–ifM„"€RГh‘{RLФw‘эвiY‡уJA4Ё/œaІѓпzІrЙB@—ьБ~dз/СЗеKЋџНdЦџ~0—ўнпэED`"gюWпЧ>d7г.d>Н5 ЙЫСŠДЯсѕ˜TЋ§іл‡цоџ О#ЇЃчї ечŸo‘ЌЂEкt{ФE™}3P\и‰NЫъЖЛУ3З№ukђДo џ>Ч#Cчƒ$В8Ж#бШq2нœ„§џoo!š%АЁ&шюRнф™ВhY1‡чоwпоP,f9BЮ‰ c7Ed#…  цц$€[њН `Гсњ-ї§Ць_Ž№o?Aі?кAој!!$ВЧ}!Чfћџ|mПF>чЬшv+=A $;зƒиъюXKЫЩІ5kжэ{ъЉКсмЯшЮ–hкдзu;€yРљwЇ ˆTUYсЪJїuužШЏЬ ›ќgƒt“Н›фa`ц]wy­@ !ј„ьљ’воV$0D:А†Лџ№&žПЙ њ9ЂRі“ЎZэГjЙтŠ-ž~z“eYЪЯаj1RSc5ЌZхEэУ?HчŠw›пN gШщ;a30†Ј–žѓ^c@ЈД”Žo~г‹)фї ї[H;ИžЇOьыьEЖ} 6џ„ љј2y xQ/ЅЄwЮG&- XГяНїУэїпџ|hhh–œ—ˆ 1Юњig(Ыt 'м‚wj€I ПVА,tZVw\ЉGУpЛћ^ ФqjЛс†dI]—’ˆр?';§›є\эBa9iRхLf9‘Дcq”tZZћzЗeš†5Zw{ІD)W]”Кд пЄ|ŠАнsѓ…—рN`@(пР‰Ÿ(И=Fкc5з`ж=їx™ЄйoПќœн\0‘ХЈmП~Џ 2хГ/@ЋШAЧ&ёP•V2љ›VŽ‘8_%@IM5ѕ†‚к–О@„{ЖfЉџ‘Š\cyxЃёmєЬ—wм‘Ќ_ЙвK)dѕяЄUэ-›$i™„="ІгВ†€_Gё&@90ыюЛ“њ\~Ді%ЂmВЪ–.xЖN{ ^ПяHљeѓMЛёПРNЯк–OŸžZєаC^{ўЕЖйј(љ…Ї ЇуŒ cp,LІ№#@,eЮwОуЇЕSЄ@Š&€ѓяZMхМ;-ырЛњЯRЅЌeПќхоHU•_ђGŸ:і99’4g#N€NЫњјcН+гДЄ˜ѕэo'b­­^ЌетnђKŠdеф§B?€40Є AEтгiYfD"wGjjўя’Ÿўє/šз­лw89„Ј§ЄеџЈтt=яіяCp} \žАЄuнК ~єЃйЄГ• iм@/di€ ѕЏ5€2 *ГЋgХ$ВpУаап луЇ ЇxEIпoЪўOћпУ(Џ~8MЧФйuь свв?.ЏЊzsкѕзПВђБЧ>UЁ_ія(21ЩOуZa^ЁŸ;4Ћ˜3Ч ••с‚Iц~РSqШtўФ—qЖ" nbъŠ&лHтД=ёкіжџ ј5pђФ їщ!zв “vˆ<œЂNЫJЦ•њ9*ЇњЗ/нЃkжJqjBбІь}ЄБІq RЙ$дQXђчДсtЉsз;ЛН^]љаўLО9qp˜Зњw;‚ 7ZЛxБŸњ?IКe№T Ћ™;Wь1BBлmР‹HЯ/Э}ЦqКŸyЏUп^р фРЂJћЅHw­lGBЃBдЂ'єПъ_ PуM€Aв•‘Jбh[U~§Џ!е|+gЇ›}Ж#Ч•V ЯТ3†ХW‘еQhLќфЇўѕMњ`яCЁŠ…E:WЙ7mtнЬрL@ЏВЯ‘>wXˆ,Ьї‘ЂЪg._р~/ ФZ[-гFДА‚ЊХ@Ї{ї!ъDœЊ1т8€ДmдM zЛї—Vwт#dBC~‚зя…š .№КОЎ?fф“2ВкЧдŠwуLžЎ›=>EьсkHFЬйе’7:-kаОрЏР—В*{ѓул“7‘qІ‹wЦмA'qф‹zя\`_ћ?„ј'^ Ј“ЃѕМЗМŠХ™TpFЖq;N‡њ7Э‡F†w EсŠ ЋbіlЏф“HœžOњyBbМ`+ШMИяlAЋ^Иа}"˜ўX/B€Iiџaœ гВv_ъ›№в€vJ5Юj€qŠ—i-рŒt;ГЧэі I ‘ЪŽ;L@кйs'€ъVЌ№Вџ§ˆд€:с1Ёр6 bіlгGє!ъПиІЬ ‰@€п§њБšN”гnОйЏйI€Iiџa гВРџИŽbШЭЕпv›_ožNOк&lќDшЌ  fётЄЯЖlž”D7IDATн‚оЭщЬ-L<TєЈ@PCРМ?љ/ћюДџA[а&&l3p+0ЈCТЦK.й5эІ›КЩЎ>•s/cЄ1s41!аiYЯ7ƒЁввЫ~ј1ВSМ:ўяB*‰cЊ;g40aаiYOœћНяU]sфШзЪ;:v‘НѓVЇї `R;€Ъ:…'xQш'xЏО \Lњ,AЄ!<Žt)‰юмбТ„в6Дš?€tчBКrє3?EК2Щэ?œЙ–А3 }–Ю6ф†хHšр3фєŒ™Ф '&*,Єеь]фpЋђФьїђŒё^Н3…‰шh($!иŠ<2Нqќvсs*чdФD& Jѕ&§HИ1зž=Z˜ш€єСЄ0rНˆџ§Іh,н§,яIENDЎB`‚acoustid-fingerprinter-0.6/images/acoustid-fp-16.png000066400000000000000000000012751202171654700224750ustar00rootroot00000000000000‰PNG  IHDRѓџasBIT|dˆ pHYs з зB(›xtEXtSoftwarewww.inkscape.org›ю<:IDAT8“OH“qЦŸпоїeГЕœšџЈ9ЩЬ–‡„РФQ‡t`ˆа!0ЛHъЁ“о:y<˜$ИЙ MмЁШ •d| Ё№uiЁВщІюпћ>’}О№==ŸгѓI}Шbёх&м„6Ћл=ръaC =ЬхNя,/пў/@^QQmz}]ЌЯЭ]ћgРtee„KЅЮeb1Ф"‘b' (Dž!РR\ќPэщЙ ™ЭЭ3ВЭІoЬЮJъ‚ВмluЛпpx%ФБ-Umн‹F(Ь%Ы;:Д­ХE‘ои№wЙюяЎ­]ŠЋъƒЇB(ћ€w ч мЪЦу'ЂSSж““~ъКУеоО#$)§mxИ9ЙВr’$Оєїп)" F†bЗЂbтcgg˜РYG]]|s~оО:8шŠ’Эїx>ЩVЋiЉЏяВйсH8љ}d$пюѕж№! (Е€cf3ƒBшqUнЗйЖƒ’Фpuu‚ф“ ЩдИрїgЂс№учŠЂЏ…B{$o˜D6kvњ|ŸЉiJJяёD„,ПЁІСRUe№Uгѕ—{ЪZ[5gSгаЉЖЖ^WKЫ,€Љп“М0]_џњ}WW†фН а9 №Cww†Є$FЫЫ#z.З№ЧŒ Hbгу€=ЙКњ‚dщP3 pi` EВ’$‹йI^џл… в3YNџœ™й!i6’ща`МДt:›L.eЋQh!yѓ(Р/{Й%tжЧIENDЎB`‚acoustid-fingerprinter-0.6/images/acoustid-fp-24.png000066400000000000000000000021711202171654700224700ustar00rootroot00000000000000‰PNG  IHDRрw=јsBIT|dˆ pHYsУУpMBtEXtSoftwarewww.inkscape.org›ю<іIDATH‰­–]hTWЧчо›dяюЦ5ЛiъjВ’ЭцЃБšЖ†š4#Ђ4”VЉ…Ђ”HAш‹PДіЉ/})ђБ”B[[в@кXR*ЂIГв‚Ц€6Б5љи|4ЩК›н{Ї&Ё ж]щР90џѓ›9g`"B:ЋЖЅЏ‘†QЪ‘‘—waђТ…кT5i\Pы.*rХ"‘ѓРSџ;Рp8^6\.&{{ГWRвЄШЬЭ-‰ONе@Чу4iU  #gўЦ ->5Е3Э:•rў{o'“љБёq!rљr!їФ€ѓ.з‹FYйрtW爓JiV4ъG€ШХ‹:PаЎд†ЏВВ>H `ƒ-ˆФы YЅА!17—сЋЊJnЗL]КЄеІпџЎЛЄфНлgЯЖІhWЪДwЬ 1ч‰eQммlћыыЧfњћ5+Џ>Ѓ”У,(h˜ЛvШ•+ял_ЎПК8<ь˜юыгJŠжlоlыЧ7V,Іf^№­[whсцM"Œvtv"q CЉэJ=їр‡-[о^Xжw&ж€J рmУэўрЏЮN‡цp\šžFw:‰MLЈСгЇ |jjЊЖjпљ§›ѓѓŸ§вииђ­Rй@Нnš g jЩhtЋ‚ч З{^7MЬPЈсжV#>6Ж-3''Zмд”И~ъ”Ч‚Ех--'Зсѕ]бюvwПяйЕыР,;~<ОщфЩI&ЙzфШ3@3? ˜+knCЉ?уSSЪŠЧГќѕѕvacc Ш$ЫчѓCРмѕы;юнЙЃMєєxzїn=ЏЎюc€‘ЖЖ @ѓTTdЗљРжялgИCЁ.eš§Q ЈЁ!LwЕvЅ _Із›XЙw•›+оЪJлYPаR *`xyNЖЫ%ў={, ЫКwя„ЗМ|Жќшб$а`М)2D6›99сžъъЕЃ}}™%ћї'•ЎЯ§ˆќ–€5Ё мZЮc їюЕt‡#tП%ВМ||КкENŸoTiZўd8\ŠNч€%W€ЇДд~PащёмzіиБ№=АА юj€‰хЋ|p§XWз," "ВMDј^ћЄ$61Б("лWb‡лк"ђ“ˆМєЈ‰іЈQїњŠќŸƒœЩЮЖ—СљХъO22ЯЏ8ћDЦаДqзЦФ€б‡b­GœСъ<жlЛo}MЭ ї[TRвЌXŠ?‰тХ‘‘vљ(н_ˆI9Ё"юwЪx:ќh[cѕC~ЏЇIENDЎB`‚acoustid-fingerprinter-0.6/images/acoustid-fp-256.png000066400000000000000000000505321202171654700225630ustar00rootroot00000000000000‰PNG  IHDR\rЈfsBIT|dˆ pHYsнuнuЌ‡УƒtEXtSoftwarewww.inkscape.org›ю< IDATxœэНy˜Wy/ќ;н=н==ћОhДoЖ$KЖ$K–mйВ-Џђ†ЭиŽ А1|мм/Й№%|qr!˜„'7$$Й@6ђБnh‚ЭnМ/ƒwƒŒeйк—бЬHЃYzЉяSoз[ЇЋКNѕ2г3]ПчЉЇЛЊ—:нUчwa @€Е‰аL @€3‡€Јa @ # €j‘™@€ъARˆ0€ЕїХ/ #;УC Pa@@Rˆ€џ№,€g|9)DА@Ьq€№ЯоЩіo№fh,І "ˆb3€Ї\^Оiа0˜Юё˜>@ј МіW*0w@#)ФZзxЫJwMгpL3№?Хb€NяЙзє˜c †‘" ркякК§з: ѓ\Сі…Й…ЬіЬ2PлИ@3ˆH-ыжЁeн:Ф{zœо{'ь“>Ђlœ ЬPлИ‘ž4ЏZ…p}= эмsо{Уwк„Фдh4ЗzѓX40Ћ@")„p=э7-_.ŸdГhZО\кьˆO §€8Єда ЯмКЭcqHi Y‚€j›!'/ оп0ВY BгЪ•yШІRяа РHС–ша; ЈrPЛИŠžDQзв†$€l-ЋWч}РHЇЯ}ѓўћWX `5€ГЌ3Ÿ/а) д!АЬ PЛШ)њѕДњg2@6 #›E][b]]ъgФўи`€3!'ўЋЌ0 @’U ЪDxе.,˜7/Зњ™LŽšVЎФфбЃЖОђЪVШАсХ: ‘ѓхгN0 mnЊPƒH Бвˆ@‰ўл-‚йo‘SПћнЊьффЄИпЙтwCЦ Ь‡Д+ДAJAАЪ@mТђѓ x_Ÿ\љЭ‰OD ТaФКЛmЬNNЦ=ќ№bH]?an-:ay:4РВЈRЇ6Б‘žФ:;!"9щ™ €Ё~оММњСz!­§uЋ| –kАЧ|l6*F@Е‰œыюЮ§sR@&ƒxooо‡?њ(Зє‡Эч$ t™[Ћy,ŒР#PЕ Ц`Y@GGžшŸ{4 „ыыЅ‹сдюнбёИЋ/ иЉДAЊQїYе"И0Е‡0уџ кою(њѓчЊŽэмYЧvЄЈŸ0ПЛУмš U Pe і` єЖЗл&Л“- ю@ЧўsїOIBQШIпI-А PЊдr B!)оgГ0 УеijЪЫ 8ўдSj}ЪЌ‡дџлЭGЎPe і#€КЖ6J“У@ЌЃУі%УПњU8;9IЛ$D WќFШЩЯ ((R… †`жі;›іcy†?'€‘Щ коnћЎьд†~ё >ЉЙа)ўЗСВѕЊдж@Šш,§ŠШяєXзв’xќч?чH CNј&Hh6ЯIЙЊд6ђКЖЖ| CHА‘• ‚Twрё'ŸTя"€Єаbn”HU†€j v@kЋ]єg"П“- ђ ЄP`PГЙ5Р^1(@• €к‚- ЎЅХ.њsO€j ШdinЖ}йФЁCblЯuBp5  AЩАЊD@5гИ†іi5w rŠ ЁPЎn сиЮЊ!Єђp5 @• ИЕƒE ђRm‘ЦFл—*†@ЯhB>@• €кС2Оmmu7ќ9Љ Ж/uБШЩN•ƒ›Ьч@• €къИPuˆ ‘ˆL61ђт‹Ёєи˜z.ЪЄ˜€FфG$P v`'€–[љ/?Жn0вi =§Д› K h@Tu v`€ˆ45х&Д/[@&ƒА’pь‰'мьQHбŸЄ*"иЊдril„…Мƒ€\AдейšˆђI7;Я h€фT Ј˜}г~ДЕеžьS„- ФьCO?эt‘ Лƒ=8Р " €кРHql@j)№Мˆ@ Ї††ФЩW_uЪ рЩADEP'Аj@m Яшdмs3ќ9‚štlчN;Љ›p@3Œ€j6  SъЏ“Zр”!Шm€Ж€: U‚р"д РЫкя™!ШH`ш™gмьD ие€ /  @m GсDBŠяn†?B0пУ1њЪ+NANv€ DX! €к€Э˜7щ§кˆŒL'ђ+ё’с\рv€3ˆрЬq$…XJћ‘††‚ж~›G@ЕАісДЯqќЉЇмдž@AAпР*@@sОw’ їTЕР5:PQG;Љd 5€'˜A0їa3† oуž—- “Щ[§ЈEB ф 5€мAbP €Й;дз{їе  AС=8ƒўќЙ<а-юš!шАњ†œэT DM ь3Œ€ц>r "„ъъД}tн*< …Њ‰A`†РмЧrzihаZс ‰ўnт?С% ˆї Pѓ;Р " €9 ГxЮЈ]#ўk?'/œјх/C I№Ф ЊHд˜a0Збж (\_ŸПТ{”WcМ>uJŒОє’[УЄ&˜ќм†ГаGљ/?Ћ?С# ˆд"jH3€€ц6ь{јJв„R#p *U˜лXЬwˆќ”џв§9†~ё 7C т†Р Bа ! €ЙyЙgB UўKMќёТшK/…2ЇOѓC|‚ѓˆ@r‚fЬm а“p<У0Š+юF&уLA\ ˆ!Ј4c`nУFNЦ=UєwЌX МcxfСŸ>З‘SBn@5*а#хW*Q@йH $€@@sI!š!o˜*€j?:Q…pТ=3Ч4 ? ( iD@s|'‹љKљ-aѕ€гoН%&tЪ ф-УШ@v€`ђO3˜ЛАѓш–џ*>‚‚ЬР@@sѓјN(Э3юyйJEЬ@5 ˆ"ƒ€ iF@s9 @D"BИЎєN‚хР‰ТAд2,Ш œA0waЙM§п5аЧсЕr`hзЎB™T*мЉDX€iB@s9@˜E@ Yћ‹IњёBњфI1њђЫn…ByЅ`n юЩiD№gЯ]X*@8ьЫ§WNИ” 2ЋјDRˆPRˆпO ёDRˆЧ“Bм1гcr\У|•„ rуИ~f``œDМп€ЂРlb‡/J Б}а0о3CУЪCRˆ8€ксАkГnлдˆР 3pš€>ўРEа}йeXјЮw yѕjўњЇ“B,vќфєУФ›,џUщДпЬ@ъР4 $…ирNшКј"ДžНummшКј"4,ЮЭљFїЮаUф€gвOс`T3е AС}9Mlzј$ДЎ[‹Ж‘MЅ`ЄгШІвhпМу ;9 З'…ј“AУ82ГУЕЋ0ƒ€м }Vœœэ<3Pэ8э@Rˆu.а рu_4Œ‘щУL `Z$…ИР–XwК.Н F:›ќF:…P$‚ЖsЮЁЗЧ|Pљ ЁlгWР1!Ј aП…0єЬ3U›˜Ђ.)Фпј€П№aŸА;)Ф•>џL# €0ыъпэ›6й ВЉTnђgSidR)ФчЭCД#gtџр“Зм†хюЂ•.Ьі+}c[ „ЌTР§Wiœ~ѓM1qшPеeš6›ў‡УљК|=)Фх•ЧL" €ТИ Ркh[—,Щ‰ў4љtШd`ЄгHЬЯЭЙžпўіЙАVЗ8лbА|н•$‚ќ  "њќ•е–˜т2П†ГW‡Pр?“B4Tj3€\"рcаЖa=ВLєЇЩ/їSШІRˆvtЪЩ&qфм r5щ2Ÿ7ТОЪUт&З*E"ЫM†Њ(30)Ф€џ Yуэmо[ЉБЬ4pЧ{,46Ђaй2Іїл'n?›EЌЛK~2›Н2ЇВ4ї Ш} !L­ЈЈkW/э ЅhЙS~uсQ*|к2Эџчk:но#ByC§яI!цф\™“?ЊT˜‘t-ыжСШЙЩoyR6 РHЇыїT6о4ќы_Џ€œјkЌp€5НњzДРъŽ[Ю›МьК†"зјџщФ‰]ЛB„Ѓкx@PЅTЄOИащ…p<ŽО;АшняFлњѕ€Ш~ е 8'zрŒwЁš–/вЉќпa?#‹†~ѓўћЏ…œ№gГm-€3!%NШЏм 2ѓЂЫбьЃTЄFGеЬРBMC+’˜тjqz­ОПѓoЛ ѕ§§ШІгh\О ђЗќ1ь†]u›•d€3ю€њˆP™”}ВgиЊЏ’A]K `јЙч.p,ё9€•цўHѕ Vє[ЙnЛ а .ЖЯ_9ЁilD2“Btј’гїе57ЃякkŠFЅD75…ьд—-ƒˆфBeЮ Бq#,•…v)„yжE0  )ФR˜"bbсBdRi€MєL*eлч*@6•’ѕїŒМ№Т Ш•О@ЗЙѕX`я‡Д4 М7ЛЭh šІШ?78” ŸNCрGр ї‹p};v@„УЙЩo˜АўN#›Не_ ЄAЗУ|l‚НЯсЌ!€ђёN@‚т§§­јlђgRљ*=‘D(„‰C‡Oўц7Н–ц„ЙЕ@zњ!Eѕ.Ш›‡lх€н Šў>-џч—i?їмВ0F€ Ъ фBЪж30)D€?pz­ћвKQзж–[ѕs›IБюn„ъъF:}€H_й|u$щ’JWn‰ЎЂ€С ќyФћњ …r“нHЇs“.FРœ- њсл`‰‡u7GфЭв)ДРђ}—уzиj–’ђЛўsŸ›МєЩ''.}ъЉё5їн7UъРF^|1”у‡М2Ы5‘ўѓ;mh^Е M+Wц&=­ќYіШеКl*Еь/~ёgAкtЮАвЎГђz6–yьE@v\ГЃnbў|лфЯІRжфW€f0—рШЃЦaн$ъжУ’(6 СоWъMS0H]_œYњЄ) љбІКЖm+)pРHЇqlчЮB™ѕ(sЯРЄ}ШЯF(EЧ–-љ_QВSS',ю8ќШ#7BNў цЖвИ{Єdаy=g…M ;о˜тoo.Ъ&vnђsН_нOЅr~фЃ?ћ™Њл“СЋ2РЄг|,Їшh€™шзђ/Тaœ§™Яф­јчќу?N1ЃXQ8њјуNA•Ь ќЈљН6ДmмˆP]]žШЯ'>ЁPŽд‡Ÿ}v€U~ЅљxІљ| Є‹З–+ГЊQѕœ.$…Јp Фzz!ђФћмdwлЇїfГ€H ‹г{ївLReР5ANўV”‰LІ?w@-Ўјгsљх™жuыђиЂeЭšьЂwП;]ьјриЯ~6m™fФ_^Ё–Hc#ZжЌЩ[ѕЩР|NjншЋЏvŒялЗв лЫАЛR ˜KЊЋz) 7Р ­яыsvѓбdwzT ƒ„счŸWџcюїn†$€fH(еŸм9i,‘є308ш:ЩпuWЊШБNьк6SЇ9*•јЇџЋ э›7УШdlпP‰@!AAA†§пњV#y/ЄєеќЊEUnšq' ХцXw—sдŸІMЅrЋэШsЯЉС/Дк‘AАv7R)зd~юD$ў“х_sѕEЃИщ&W]ПуМѓВЫ–эШLLЈ…B+’hJtяPЧ:;s‰]Ў†?"ро“пў6Е4'у.ЗыєТnм-—mЇ"9Cбхэь C~ѓ&Л* аqvГ8H€E Ћ3ЪгcAю™R@н—]–Ўkm-ШѓoЛ­$cрбЧŸŽЬРœDЧбОyГѓ$ŸœtžјL2`уЄFGyz7ItmАb>кЬпPеНЊv`гŒ;`њсу==ю!ПŠQаQ PќьЃ/ОXЈ‰-АзФ+vХА$ ђ™є3pѓЭž“{с;пYЊ`:2пЅH,X€XwwžОoџд€TЪFЂйЩIњўїiL\}i„•§йY€„МY„@ДЃн1ЪЯ)ъ/OpВ9Й{wHбyд€ѓ‘DЦbЏ‹%QTŸПюK.ё$€І+В-kз­ђЩУUЖЬРЄНЎА}y(„іsЯutљ9qi€лtћ|TNHh†] Ј:д<$…8 2АБŽ<мlN0вiŒМє’“рЄp;@IптМЗзhXВDы=лЗ-ЄO'~љKЗА`n$ЕШяКŠдаИbТ‰„ЇЫ/яXЪйцyшЁ‡Ть5~=Щ(иiп!{AUкjž uEІўя’ђ[Pp™ќ„‘|;U;yЁ%†яеПуМѓt'Ебsљх%y”xЇЬР&Ÿx'п ееЁuэZm—R##тшЌўreRЎЉvU›#p-@дЕДф[єН$Щ8`›OХЌ9 €ъњAЧљчg5Яit]tбd(-КЂˆƒА$5@лŸnVї]Ы5Џ^ ‰xЛќ”c^Ж“§< Њ\ЊkEc<*…š&3Id ƒCxFљЙљћ aф…мФ]Кa!I€тШ}[ŽЭЮEЙJ@ХЄќv^xЁŽuп 'ЉЖOћ>‰‰cO<ЁQУЅ‘Є›ё/‹хт§ ‡е?я˜љ>ЄЉпљ‹ф* e Ж!пPU$Pг`Ь Rздd3цЙFљљœќ€L‚qy‰nКсKq} №їћ]§CБ˜бЖqЃ.dLtmнzЬзIІ††„ 1ђ€ NŠžR‘YЖыv~ЌqЩР0ДB~s’‹оЏb|џ~СRœURo†$ЎT]T`­Рuє$марюуwќ`та!1yєЈzс ЙОŠёЯї~‹;šЯ<3ЊЋѓК9 sKяЙтЧЅœГ€€Фш&Xy:џЩzp)@У’%6ƒžŽЫЯі?№€šСе€фЛxЋ 5KI!b0ƒ ТБXсЈ?&ƒa{D Џdљ&пЗпеbї[мбДb…npO’NwnнКWD"EўєЇnAR]з„ќ(ЩBџЩvОяэЭЋђу4ёЙ4рЗPъo[%1/5 ЊЄ€š%—@^$D йЌЛŸ‡ј‰†@~УгЦC‚uo–’$€І•+utџ'œ ее Х:;п*іœЧžxЂPf “РkЕ@ƒSШЏЂылˆ ЛЩшЫ/‡NНіПVмРs=xnCе Њ3ЭАФџDТ;юП„ЩИКеTиfJ(‚ ЧКЛ[ь9'ЇvяvJ–Ђ€ R<эf%ч h?œHШИ№омБ"%;иџЭo:ЙЩИЫН3вїАj™ЎЅ'!UќwJ…‡!Њ% WЛbмFЅIgžщ%ћ’ўO0 `$10№r)ч=ђуЛU Vэ^Ж‘ Рђў.єдѕm.ПАџ[пr"U  bЇUх ЈI0}ХrХBжЮ+хWœ|ѕеBuёiХЋ‡ўъ7—Мx @] €єџqH `ЄyЭšч‹>/€УIE!›JсШ~ЄcШЫ–4UК€YЯ!›-˜о[ЩЖh‡y$=6vUQ_.Ф ШџЃ rђЏ„\ Ю‚Д ­2-…$€68t)ЊД№ПDЃmmXњСЂmуF@q}Яб IDAT’@2)ФВ Ÿ@ЮMvѕtœK.žР9"А'`9дvр>PпзWЌ T’ˆŠЎDHŸ<)Z}їМ@H3€цсчžлbћмш(2““3Ії;сєоНтѕљnФ Уде?ќ№Š§<№ЁbП[„УУЖ„ШћрLX~>Єў?@$I8К+FI!ЖИК.ЙйT mыз#огsрџVЉs+8В[kеРХ †“Р-И* ”ЄџЧћњtp зџUТыЧ|у­Џн)Е–‡д6япo‹‘Ї Ўb008˜оіјуЭЋV•н_јќ‡?;јнявЄ ˆџњзнOо–œl*і{УБифф^ IЫ '~7,##й\“*)ќ- sГ—.Ef|™‰ ДЎ_‰РХI!оVСѓЊТњЯ1uќИпПпЭш”р\дїїгЪэFДњs ЉS cРоRЦПћнpfb‚тОєF-“ЧŽ­uњЌ_D;:Œ ŸћмTзEeЮўЬgЪn9ЬŒуч7нпћх/зeSЉ№с‡nќщ•WЎNŽж—ђНБžž}–ўЅ$а9ёЉŸЂкj'RU–’B\ Г=SзХч&f|B$фмcvДЉ$ЊJџ'§ЩOœ>мB‚Й'€Oж’bт§§К6ешф ь.eL€lЊДпІе3 сиЮ‹В““-ЅžЮКяОЉhG‡=лЗgк7o.ЛMЅ№ЬЛоџЏXЌхЇW^й>yфHŸЯ ѓЕoкtР"HшƒдёIbŒ@ў_XAAD г"М—-CЌЋ+7љГцcЌЃЁX ьѕ™ŸёЦшГMtY+Ј” Gь% NžžЏЂ4 `о<B Њў?gЁdђМœ?њш’rœЃqйВь’{юБщ ЋюНЗrўУ"<ьЎ"›n‡$2№ЉНh №Ј@ R6гзДmи W6љ3ШLN"ж‘ ИїІІN6XЇKW—№йŠтшу{! €T›шfІ6—dH­Ÿ7Юыƒ=r€= (%ррїПяЄD$†ž~z ч˜Ы-i(љ4};vdt[ЄW†Ы&ТсTџѕзЧ €шуd,&р6$лЂZ рNБxo/Ђв=У'ПIЊЋ€жЬјјЧрѓ­СќЂъєТЉз^ ялч4љд*СnџС™(Бе” @ur§_§ŽВHщSЇФСя}/O ШІRёуO>Љ&С…љЗоъh5ьНтŠї#:M|@^№ФќљGCБиЄuП %єЈм-kзZŸO~vЬ0E"#“ЙgчuзЩf§Ьж{ЃЏЖJI!ъРŠT#ŽќшGnqд2сї#^ щоЂpЦe*A;ье`МP6ˆЖЗ‹пћоє™§h*Б`Aй‚ЬЧіьЇп|г)3ЧПчy ББ%z7т2@' $к  eБРя>ћй:ъКѓђ_ўeljhЈф{ЖiхЪLЫYgМŽНW^9­рЖъгF+E(ЫЖmи@n=7}ŒO~NмЙг”SИ@ЌqЩР0ђE~f p‹епППѓЗїwчBЊ‹ н„K!cI№,—b J(“Х‘XА {ХsЯoќїŸ\sп}SзМўњщО;Ъvsyь1ЗxŠ|ЃОx9щ'3>~9JМvѕ2 а $>’ ` zP6;@zl Oнv[lЯОyх“ŸŒ•у;{4Œ|гip[ѕm–ѓЕЮ /EЃ:YЂм~Cф]Qx }џNЋ>щ§^išЏ~ъSѓSЃЃ:  ЭmЌH'Џ_–рŸКжVcыCMд фў0cЫзП>бЖqcY\E.v"nЬU’5 ЃИ†Ишž”‚5љ]Х†ВI€ќvнuWЌ\Х=zЖoїj„‚жsЮЩЦ{z*žRXhечЏб`чнpCоЊАjМЅьЭ<уmY )ФEЮˆїѕ!‹хVњЌƒ Р SЧ‡_§ф'[ oz*p0`n$xеU/‹јПъо{'œjц‡ lњт'EИ$/G;€š@Ёœ1uућі…a%{7ъ _Œ(€ЇM(7D$‚ЎmлД’ z.ПМЂх„МV}кј$эПёFвtт7дŽyзЎ\€Œќ#нпХ§Ї‹нџ№ёё§ћiѕk…”(Г‰ЄЧжYI!Ж•њƒ–,Щ,ћа‡\-ХЭЋVeНћн%п Їп|S(Х0{ўxVkЌшу—_~6 з‹з‚ Ќкt$ŸВJхDл9чЄЭдtOє\qEХЂНV}'h]З.˜?_':–oЉ†#…ps‚(Шјih@][›]фчzПё-3>.^Мї^В|7@Њ}БнўO7)рА>ёХbЭ'>1a*ЙbѕЧ?>eКвJB;€Z?:q№р%ŸкA@ќFтF@З Т>Ш›Ўъа}щЅКК}ЖїЊЋ&DЈќЁ2:ЋОє]w]zБ0dЛЁЙ.NЈ€№NБФ‚ЮaПf,€_ь§в—ъF^xєр&HWрOU шD~0ФЅў&LA3фИgћі Ѕ“ #“сЕф *4яўЬg.0•„h{Лafcь–dW_В ЊЮаqоy™p<Ўs]‰&zЏМђ@ЙЮяwђ“ЕњЎЙ&Ѓ&.8Еq‡,Ю€ЖыVъdyФzzђу§}ъ§nјеўa<›JQŸuђ Ьƒ”Za†Cšљѕ%‹џuЭЭY3D‹D$bЬЛёЦ’C_пјтеИw’Ј^г‰]ЛЖ:}ж/тНН:„ЅъџГ^шОфmёМŠХŠcОдЯ&`е{ыПю:нЩФЅЖгА:9;•p+žЬZ{З‡уq„ыыѓу§‹аћ0њвKЁ—?і1ЪuІ Ј§$ ~т†.ь’SzЏЙf*ъfUВƒƒ%МNэоr(‰ГŒОђJпЩпќf[ЉчДJё•D5ъЦ?Tа-§џ^р+ш˜ˆD†—.}Ѕ”ѓ3љЋŒuИО^+xЩ„кЦ§ЄрXУЁ р‹ЂљFП"ѕ~7МњЉOEO<ћ,ХЦЗР’њ ‚‰Ьјј=х8зќСС)шYZs.ВžэлGЃээ%‹;Я§ёGY”$ї$^ќГ?лbd2%‹џ€g Aеџy#Y'D§j?Tm РHыйg?Sьy‹™јRвњЎЙ&ЎзЊ –Л'!W§SЈ МB кж–—ьSю^lF:_Мћн‰l*EnAnь~тњыЏЬІRo/ѕ<‘ЦFЃїъЋuOdR„У#н—]VВЎ8њђЫЁзўїџц“< 2іЦЭПѓВ„6ZA@n@Oaа0Ž-mЄХCP^˜1kQъ|, Й‚žpbЩ=їќE;-fђгѓ8Ќhё]wљџ'!Щk8JoE€YЧoАЎЙF&csџUЊ,ѓШ /„žћЃ?ЂˆИfШx€ўё~ф‘ћЪqŽо+Џœ ззыZ“у€#ѓnИЁфІ№вŸџyнБ;sЙџЉссШЮkЏ]œMЅJ;4язЊЇС €щJР INƒ3§џ:ЊЗ Ÿ0кuёХo…уq_]ŠYѕiŸЪa@bў|ЃG/1‰Ћl4іH˜€‹єVЌp5€іК––|Ћёк?§SєЩ[niЩŒ7ТЬјХ{оѓЮЬФDY*Ф М§эd§з-”9 љ'šы­O›НкJBjdD<О}{ќ•ћюЋћэЇ?{|ћіжб—^*K ,ACр+!'П 2гn0”GBЯіэКщЯє›Ч!'ЯIc‘ццјC1Ћ>m\и_xчiЭ`$>і1УїцмЙн.ы"B(EцєщŠш§nи—LFЧіьщИљцш‘Ч›wјбG;ЫёНЁК:ЃOZZНўКNiH=kРA‰Мяы{tlЯžСRЧ’œФ‹іgЋHQ„Р) ъьъ„"vt­gŸ]Œў?jnуЭЋV}чш‘#їъŒЁ˜UŸЏў aЩнwыŠдЊј?IcPЙrј&ГбцЕмђ_ НПNьк9БkWY6Џ^Ž44ш–#І=р8€CЧКК/TqЉРj Ўј%YЯuс4ЁыBv]|БЎ яNA^л“цуФЖџјЭd(Д†БМаЛъгЦYПћвK3‰… uЩš`’ДFЬё“аё{ŠQnEЃVŠoЕc*m6P˜ЅnЂ§бЧ0дЖaУd™рCр1T\ТМZд}€OЈЌВO№џOПyІўo>NШ !О]шƒХЎњ\bс+ВOуЗў8Kџ'bЫЛvХРђ“!ЄMё. §мsu›dаЪ8ЩВЧ!е€бѕŸ§ьqАЂ-‘І&CУЅФ'yŠ5VL4TWSzЎžаЇў?9‰NТZAЇdE$ђЗ–Въ;­ўбŽcоM7љq-sёпIџw„/H 1Р6єЩ“ШœЎHžФŒ }г&Ў6КAНIF 'џ0,QыЉ ГdдїѕљбџЩјG€n `а0ЦQ†VanЄGЇчt!ыћћІ•+§д?ЄkKњ.ŠЎcЫ–' %ОМ–:љUXxЧiЭТЄœЌI*VЧ—ыцWИ@†сYйg6!^5тˆiOAŠYУ+Х€ŒЂЊ ц]х†юGnа­ш„Вкм&Мњœа%УuП–_л“АVа4€ьЖŸќ$р{…О иЩOaŸ„Хwпэgѕ/$ў ођKwј|џЌ@ЫкЕь?š$Bє0ф #WЩ*'Э2WЊPŒP6;@!}_}N7vњЮџІџГЏvД”2љ и­џэ›6e[Ю:Ыw@ь‹Нрuг&€Єk KuЯ9Дmи ћgЋтџ ѓ‘r­ #“йe>ЏJФ Ћќž,: PAY$€Bњ~! G?ў_џOšЯI§!ќХNJ™ќ*,Оы.?Ў?'›_” Jm~$€wјxяЌ‚O Йˆ†! р˜Ј5hЇМPЙб–Ÿ1мшзH(YаљљршB6.[–е,уЮ ` rђТюB3`а0Ц|Хщ Šќ<ы/Б`сЃд§tОњs›”ЇкІE”љЇ9ЈЊ†гм& @чcdh9 ЫвъФДUЋhЙ  ”(@Bб€ЎОя&ўwmлІkќS'в(ьtѕ{ўžТe: '?‘жЊПј‹)Ц?ЎЖŒBЎўЧ‘ЏџЛBWИР|ЭїV-д?" FѓЊU:ЋГ&р4ПIЊ†дпыЁ№•4ŒуpАœы €§AЎ†оеWыЎЄ<ќЗў4Œ—!Uір?ŸоONйІ3Ю№[h–џN@N~’J=ѕ@ŸЪRˆrІPшДž}vV#жšЏˆмEФ­ФќЋŸ.зиK)†šрф(жј”МVRx˜=БЃQє\~Йпј'џПляў;О*0оBуР ўYѓ‰OLљ(BJ$= kѕ?KќзВйшž­Є>t3‰B“|4јPW n$RѕЌпB2ёŒ Т“ŽI№Ёp  X A› Mp.ЋЛM*šL\‰45љљ­T@г3… уa/вОЈ'5 Ћйђ>Иљf? "i2HTšWџЯ žЂ@Y QN7м.ŸРя;%6ћƒYwЦЄ’йеп*"ФККt&ЏфЗ Д ~Ф|7R _Кж^œШјwvкэ7џ== ;ŒЩmеч“ŸЌџg§е_љ ЌЩU-‚\lhѕƒ~џ- рB”ЁхtТ‹‰љЄhпДЉ˜0б‚uжLL;ШћфП5онmh$Хіb Кн€ Ё рWЬw{гџѕњ7rВуњ?UбѕR{ОрэD4~ m1Шb–€ŒUаtWђ1“†Ч#А$-Вж!€Y%ўUЈkn6š–/зЭŒуuжЉвj!?ы/‹џўa ?хЫ€§ћˆфey3bс*ИMbˆ€VџФТ…Fѓъе~гЙўЯWQзп=h“>KћTТЫkђ‡!+кЮњф'‹Y§ЙёяЄћO•\<ЁCљмŒРkеwЂУжѕыГ>J-ѓB‹Ў*ž G ЛСщЗњдџщыЪ-ь…K№ =z‰і:Є@њяUWщXвљ)xНZCЯ ŸL‡€ЕЊЛ§ž(€6Аr_wпnзЏUH_ЩC0ГQ!яK_d­C>7#аЛTёпg oД`‹‡ѓў:фЊ(Ht“їJ”hђ—т ›ШoдёъХЧKБ УЋў”Ињ0h?M б  И2еѕЗІ‘‹ъ6Ръbлљc,чјC[з­Ыnц™ё'оіЖиёŸџМ`г‹RV|7hзЯф@ieєњУ+.іпЫ9H:7нlхŠфx&xѕpzЎ>ж‰џХEџёђпž%Д4ПлиБwяOь‚l_w&€s ыiДBЮ•\ѓЭБђ*дP†еа, >hYШ 75ЫЭжПrхo2ŸGсadŒuw?ђШФc[Жд<џ|о{KљоЫYІmуF?@Ъ ^ы\Gt§гMН№0d/У’с&ъЛНпYu­­~‹ЊЫœ€д:iБЯЙјя#њ/ЁэлЁ њ~В/ЕўЄ$н9їМЄ.њSŠCpVф_бnкRлƒr^Qћ&sЃnОžbN8‘Р>8moЗ§Оќ.[Ёзœ6bЛXgЇбАh‘ЮЦ#Љгъ4*­0”] (4ёљq~qМыЫѓ‰сдИдеžќџrPŒр$ў‡ jџэ.е‘ћ*шxжаг'RŠе?ln'Ьc:џ+'Њ“>uѕ/щњ”B‚m$4˜[ У‚РhXМ8ЛщK_šА,ѓЦe-@^€Œ|•аљгЫZЯM p:Юџ| &дD r‰џ:ћ4`_Š%‚ћžОЋЏN‡bмг^NнШ^.iЧ@Оеў љШcі …r§‰|ўь‡$’Qј ћuBЉЉuСOd рENT €/рФ’™О;&ћvьHбJ№Pіmтq@rђ@”Ep›јNяЃу јё8…— 95€ЇавЩ§„ўђš%яПnј/нwМА Ѕv—SкЌћg –юО–ёЮ-уЎ•Ÿ2?ї–љXВюO(‡ ‚\эЃЄ…нјЧЏЅg> ku=}ж_џѕoсь4‘ MђB7vtoлц%*ђђ @Џ"N(Ћ!Pgт8ѓzЈ<ќњ”Гˆr@)Д|ќ9”њ705.]šѕ™MG6“А:шъЄџњ}щя' %€7!'ёQv^2(ѓE‘юЛ!Hтиkn‡Ь1—Мњх‘H #пВЉоTSАVSžШpР–5kоœЫ-Gi`^“к}€ вдdјЈЏFЈoКа{Х™PVyЇ`Ј‹7ЬяаVPU8Щ‡^R€J oЃ'ПVЊ  l4ŒБЄЛœGз…ТСt‰ жŠFБјНяѕSE—'џџПюПBч$[Р Ше?KR&ƒd#л'џAШЄВн@КП#tA”J|rѓ‚ ЃцыaиWQЪИтэ‹NСb4ЃћвK#ѕѓц­пП>}˜Nф‡јs›ЋH/RŒO„œŠџ@ F&)ФkVыМ_g`NЧду€Юxi‚”\ д?p Џ ЅLъ^OЎћїпpC:жн­ћлшї‘ў_nїŸлyy џ1ШŸM‘Q‹yX:ЃУ8]ЫьЂдв| ”КTBЋ\юПBчІ…pжхсЄаљгH]8)ђяƒ”(пПЌ’J)@(љ:C2–€ќcЉо!ЖНž>‰џмА^ђўїџћсGљВйxљ)N~Щ€ }ѓцŒfMЯѓkZБ"ыЃѕ`ў#§ŸїuЈ$И*0 Ы19С›!џ Ъѕ‚$^ъЋlЂ?Ё*Х(ANў1HкцnПIDATq%nОЮhLCљєМ~%]1яmo ХbпЪŒпншMzкчСšт?}›АqИ,ž€B+ОzЬTМј_JG W ЦDRˆ$€ЛyзSЙ ЗыJртПš|БЂ RAuЛJ}7‘I˜Дк' /-TŸ м.JхQш‡Œ@ђ$фuЂ‰tCёŒ„nYW"џ[f|ќV2оK\\ьПю:?•bx`Б@Тo‹јL~&>уq‰ ќ>U" PХ—aЙxr…г5MР’CБНч=ЅtахњЉ%ЯtСI€ўk*ьIkŸ3eЦRQЊ‹S?фЄHГR8щю –ХdLS3Эlл†ўчCˆ=€\!шDКсП48"€†E‹ ЭŽЋ\Wф s1bЗЯїчтwт|Ф№Џрњ9њТЯ §к,q‘F}ЮХџ›nJЧ:;§Њ6dp†НƒnЅХu<мзЯуh\М1‰я"~PŽ@ БMšSцFž\}ъЪяЈв fќ€рђмiЃ%шлБУЏИШ=мPфћf4Œ!H]ЎhИўQћy.@Н0`ОњsЛLE&‡Y'0зd3 {Ym(Я)Е”АєўРO1 ОкђКМŒVЅWœ˜x`_љЫЎ~Љ(іyЧуx:ЉзЄwјVу?d)ЪHgхч’B^АˆўярБтj›шb/FQj@Ёпpy_ž pётB“XНvt#VZ€Ћœ`'R‰ }hоM7Ѕ;/И@—˜8ЁŸ„ДU№LКщ&OнІeLх"‚ŽdЎAУx РЃ€\!œ&yЁ‘ШиsХ™жГЯіsУЈ"xЂHБЦ— +ъУсX1Њ˜L$Pж ƒ†qЌЎ~ф5SЏkXж_}=ЮўєЇuЋ-sщTmЂЁЖаЊI”›*/–+OWˆУЯјШGtХEuѕЇ2Q3&а м&О“Р/j8‘€f  W(4ЕA@*>љ_+=йш„ьЭ+?ђ‘IM•†РrШЕ6„ќюП5‰й@8AЕеu  Щќpл† йn§2бDф†!W‘ZИј&]ёЪОрз Њƒ†qРпђc-U3štУ2ф6ЏZ•>уOўDЗŽ>_§'`я KсПГqЬT=˜9ф,Qее- Kd\љсc,тЉЂjЭЕb'„o@нз‘ЛБLЃ} ­”4љЫе@№~ rхЇ4ог“књ§яŸЧу~ЦCжігАšhP`vнЙŒЊ'џрдpСЫ@ЋувЅйССbЌџдu…"yЪfБа&'Бощ=n$QBr AЊЂ0ыKо іš‡p}}ъќx5Бp!аш€л4NТZ§екšЦЌ Г&снRдRйMєчLVќбЅDHћ'ђ†gŠЉРbУdl„6ќˆџtŒrГ њ2W}И PіLРB4Œ#.№?оАxёЏЖ§јЧ_юиВхєSvUуЙўHќїеAw.Ѓ\щРЧ a<Ÿтo|Д ’ЦЉ†]ХzэцБіЭ›3KюЙЧЯъЏъ‹+Ю€JХnѓtуЖ_шx‘Y€<0…М•vцaа0оpѓƒ­­›Ђээ=[ЖˆЭїпŸ‚ЌЈл KlЇbР…ŠDC5љЈf`ќc˜5`т/М=Ќш„eЎІ€2EššŒЭїп?СЋ y€‹Рd§?+Tд-dй/~ `›з@ эг1ё<ъфsЁТЭ8н“ФИqxxфхlАrтвѕh†UШ‰H•ЫРЪЋ?jnУА—ўЊyЬ €0hоЙ:ЃRпЇ*Є€lНДљ+_i\КTзeчd§?{ЈhЙDamO€“ИO|‚э‚ –еЋ§иИ0“+$ПЄП“я^G|чЖœ!X%ЙЩј7“С?U…YE0hO‡ъъ.„{ез"Sч?јрk§з_ўž'Šœ€Мi*Q)ЦгXШХЇЃЈiР2Й ‡OK(j1ЉE1Щ‚OщЛNЄЬНt-ЉЗZJ+fŸ ИyjъХ‡зЎ]=іЦя22™ъššЂчŸtУч??ыю^ `>Ќ№qнЮ+д$‚VоtЁ\PP№уњszŸЛ АљЬ3uWU˜Љјx>&"%šШG I R№S‹Явч(‹sV)nЊЧ_TнЙŒYIpХѓЯŸ№oОйyg%€ѕцѓHЭ€ЧК‘_1ИБш(ђХХrрuѓћДZЁyї26q†ёŸ&OзžЩ‰Bу"+>MцnXэЕ"АB>И‡збЇz'1ѓФVu˜u*€.юq]O7Ю[Еќ5Пƒз]/›+lа0І ‹„КЦыЯщѓ6 @Џџ<'AJžŽ:Pkщб„VKjSх"Jј9Žќ:њд'X§f­ЛшJ%žBŠ{=АjЌЙ5aфb&7нZ\юf7њЧћџЄљ1@ў:ХЬфDQе€!H  ђк’`˜YШыHЅДп€Ќ7Р+щЮєoЊ:Ьfђѕw711„|ЗЗ4гъђЂ>ё•Иa^p•л‹~|ў^1ЭЋVщzAf< x*яaШсXљa“цѓ4$qя‡TЕі@СЪXGЎa.Љ§R'duтFИU‘ёф F7LЅ&Сsn/јџŽqё?озgдЕЖzЉ?|•Ѕb.3 ТI  šњ€œќD?iОNЅДїРjСL~Ьvь+95Sh‡$€fXѕ yZUШХtі–Ых6ўqф€ŽqЯэ}ќБ}мЉ@ЕLNђЃА~&эw@^у)Xз’JiS§šЮљ/„ЙBЊБhЄžи K ,"рzџШUƒк.•ЅщbМyГF^є+ўѓуќbњpЊРLЧ8ь€НЄіЌ†d§ч}'‚Œ?ЬьЋиЄ8OК"u‘NУЪš„еІщ s;hЋфъAУH%…xР:ѕИї Н/б@Zy!Ъj…<6мvC*]ъу0%Эf=цp—рqШƒ Qѓ’FH28 ЉО р5HkёT~ѕ'<FК.>Џу6 РПpљPM‡“ѕЇ` :ЦЗjћ U‡ЙB€ETž<kђŸ†t R ъНЖrђ@[.kРбшЧрЇW]Эz.@UќWKUјOЯТrяђ^ВnТSЬ%ь='Ь}jZВR%А:Qx)љ§ЇЫZl#€rˆџќBЦ:; h€НЪ8ьPЭˆ‡>8Н@s‘ЋD–]€\‚–€6^g:n mW ѓ#ў7y9ЧPV5Рlв›gУЋsЛ=€ћ‡a§^в{yэЛiЛщ уXRˆњљ н&ЙКяєо"“€x DъзШ‹ŸlŽc.o0тЕCшu2Э”ЮјLpŠQіPЂЋГIP"f{2P!pcЯuчэ—ЪUщЇфдžЫЊ#8Н—@Чyчщ–Aч§щЊе ‚˜ЋGЕŠГ6а™фnЂ O 'hлАAзР]€дŒrZеЁ3‹Й,T;l†@U а‰  у\џoлА!ЃY ‘Э&@€2" €™УoСcъJќ˜“ cЫнепЭ@ ! €‚й у%кw3zьаyСКњПJAвL " €™Х/щIХ`YEB уќѓ§И y"Ј0Гј9=q+YфЅXEёšVЌШЦ:;u]€xjЬ,vђ2цщЦџжф€ŽѓЮѓSиЩФд˜A ЦыЉЫ$јџmpўљКL@Tž 'j{ЏЏ*вyС~<)XЙѓDСъ_c`цёп)$8ЭL"€h[›QD"jЗU)Р*ˆ€fy@а‰џ'‰Ё§МѓВ^MиS€OCJ‡=2jЬ<~9X>}ПтЧ–-КўђŒCNў1Xе€  ЦР cа0RžсЧx`OЁиNНW]ЅkфжNеX,@…@uРІPЛ/)€ qйВlћЙчњ1RыьSвwЈ!PШ#Nт?]МљЗоъЧ§GеsOC–б3їgЊ.B€D@е'С&ž€%Ютл_pћэixCеџOСn,[дГT cР ќ‰їN“?YяZжЎЭjКџ€|§џ$ьР`ѕЏ1P=јпс">`—Эьј‚лnѓГњ“ўOт?€5Š€Њ_UФ‘Џ” 0BWќьёџdƒ=0 C@U‚AУј€ЇјБђЅ€zXС?›7g ъNZžџOт?/LўD@е…ћљŽ€ьnJе‚ЂšиыѓoЛm zW џU €ћЏF@uс?auС ~mцжkѕ46 юИcRѓ{ЙјOњџ)иs  @aа0ŽxX=F~/ёхџэПФ:;u&.ЯџŸ€œќЃˆџ5Ž€Њџ7d_CWФ:;'Юјг?= =пНКњBvIЂ  ‹n # €*У aќР{\п „БцŸx6ви8 oы=_§'!WџaШ^‰д1џkT! у[ўТщЕ…wмё%яџnи‹xўьŠ<{7ф5Š€Њƒ†ёq7x D$ВЛћВЫ>КщЫ_ўЌ Џ>.ўŸ‚\§‰ЦtЊyдBkАY SјігЗпоtж_џu8Б`A€3`‰№i”:Р+‚8џ† э $ўёџ5Ž€f6ѕЋcуАєјQXnМ:8Ksj{є!XР ў?@ Ь8­ф'`_Щ&Вjќ;Iœ8Јq0; NfхљdцоŠќЫРr§7З“АЛџд0˜= P^чA’%єpОa№їжџ˜-р“šмyЧЬmљ ЄTp rв17rџѕџŒ€Г м0 р(dWЁnШœЁ(Є 9СЧ 'џaЬї џ00Л@jР)ШеdžЙГD09љAЦь7п ј€! €й2ь‘[я8$4@–Єt3xР^H рœ †jЬ.№ШОQШžDџ HcХ АЧм›я|џl`іС€UиcrђЇ!­§ %€IH с€ƒЋ?oў €€f#xqOкŸ‚4№5AFІ`џPЌ@АњШƒ0Œр~˜Ѕ ‹R ˆCЎўaX1ді;РђРЬnШXŽфФчЭ…3АВ§‚•?€#˜§ьб)&6€й>ЩU  ўђ-y%eЖКŠIENDЎB`‚acoustid-fingerprinter-0.6/images/acoustid-fp-32.png000066400000000000000000000030151202171654700224650ustar00rootroot00000000000000‰PNG  IHDR szzєsBIT|dˆ pHYsЏЏ^‘tEXtSoftwarewww.inkscape.org›ю<ŠIDATX…Н—mlSeЧЯ}YiY;ЪЋКїё‡#Те`т хƒ !|˜†ˆoп0bBŒƒњY …I•Hд€АЁ!s!ЪІЈ nFКЕЖжЎЗэНЧЛХ9UNвЯyюџ<Пsžг{ŸЁ˜пnИЊНFБцѓѕt)ЕшrхF1kяTЊБВННAl{№А}В1ŠЊ€Ы=и™Ь-Р§—Ѓг\ъфѓdЮž-Я% Р0ƒСЦ\<ŽcYD6›Џ(€юѕжfуqrЩ$б#Gt`йшRЊD)U‘u+0ђщЇАфŠ8P-"Z6 озЇ;йь ь?јАЅхХ}С`Чa­cYищєeыэ5€Ч?ї–RuEt)Еи =d–—?Дќ jѓЉд_žющбЖТx—RјkkП{л4oЙl€Љuuцтq•O&ˆmяќF}>•Т[UхдЌY“љфXь‚OїVV>ыЋЉQ3кк^ю›4@—RК2Э;SЇOcEЃfтФ‰ЉРB”jhXЗ.?{уЦPhФ…€Ё тŸ=;`ЄЗЗ*?УEоМ—hЯO/4Zьи1 X Й\@АЕе™vнuIЅigЌhTЅОљІhё„BЋ$‹сX?эй3Л‰Uќ+MзWgуёѓуXoЏмрJыъшЧщˆvwkV,ЖшHžзўИsЇмнЅдРGt)х=0wю …ёЧJbлwŠїѕiРТJ€ _}Н ƒ;vПМѓЮ]™3gІјjjрмСƒњРцЭї/Ђ”Ж5чЪZZžАгщЧz"‘ €:ЫЅ @r`@s,ЋВ:ю0QныОˆ:ЄПuыMf0˜kнЖЭ|ž/Ÿ~к/`L_ЖLїF![{жЌGгCCищє–бЁ![A+РДыЏO.}џ}c]ЯБ,~кЛз={і!ouuа!Јr“ющё\ГbээЖцёdЫš"€4tvЮrРРЏeeЫRп~;Р6Ž­]ћМвД•зЌ\9eJ($ўІІУ}ыз—ЄN цЭѓКkхн L€Ћ#[†hЯ{…Х П_Њ#лнВ ›Hм>^эю6Хq|сА$’'OО K$”cY%ЅMMЩр‘ИЛ8€xН„nЛЭŽч’ЩWr.@cggо8n*И‹ &€ј|”ЗЕй@иіучsРдІ&H#ЎћИ d™Зоšз}>€§_СAЅыŸ—†BЮЕ?žђŒ5с€Рc‹ЖmлpѓЁC™’`Pl ”ЕЖкšiё@ПЫ…ИцfN 8žuчъW­В]їОЇDгЖ—мyєшnoU•ћ€ЯЌй[лй9<ГЃуЅњѕыпЭИ•K–‚єЌљ ј_sГ|Wасh0<ЊЦіљkр$Р*‘TiCУ:ррс?K=с˜МДWНоЁ— C~эяO‹ШЯ"Ђ‰Л kШvэ~П#")yrМўuMћ Ы–пнЙM“>–?%т”ŒŽЎ]Мiг‘ВљѓруБъ‚РgтVРW_/Ўфєx}Цqю˜ПqуK@ и91ўv КЙ"2 "Эп.П ВфƒH$чfyуEДЅ"2яп\L.u/ј ˜?Сзч€фAMkР *рZŠ я…ПГIЩV‰$N9@љœ9…Пр№dbр zuM“P8lsёьџ_оЈ]Ар€oь3|ЂX€ЫНеж‹HBDZŠН]+љgЪ‹лLр\БјrЅs)[ЎмнIENDЎB`‚acoustid-fingerprinter-0.6/images/acoustid-fp-48.png000066400000000000000000000050631202171654700225010ustar00rootroot00000000000000‰PNG  IHDR00Wљ‡sBIT|dˆ pHYs)†)†"ьпŒtEXtSoftwarewww.inkscape.org›ю< АIDAThеškl\ХЧsЏїnьЕ{эјэи ’Ž)БЈЁФISp* HёЁBjEAEˆJJAB§€*$>)Ђ$­у$ І*ДтбиA‰q^Ы„'1БЏ{§ин{яєУНГО^л!Иk‰iЕЛgfЮœџљŸ™3wv…”’џgбf{‚]Bќj—Ь–§Yр/,ќЙ„п5nоЌ"йіg@“7d.YRlƒСЪ;я|ј)I‘’Lc‰"ЁжШЪ"­Д”№—_> ЄлбdЭ1Ћ HЈеŒм\ккJqЌ"‰,Ь*_VVŒХ–ХхcЧRьHDпOцГ`ЛйF0˜e†У˜с0вВшkiIС№эgРeОЬLЬсaЌсaР{ящ@™Ќyf €€2ЭчC1`ŽrЉЕUtрv’ФТЌАЁ p #M“P[›.MSрHŠЬ ЪЅmcc†УX##„клu Šo;шzЙ‰`†Уибh\нпвЂKqЖдџYf  IˆUЛ…(œжАп_юО’Kh8єЛ$…и)DQюъšwЕЙ™лІы#tНЬ ‡Б#‘ њўд™ЈjЊqBTюЂшZ}™€рђЅѕчdЮ)(\{с­ЗЊ€boћŸ„СФшŒ]М(†OŸж˜Ђ"ятзОЬЬ}~џ›Г Iˆ›Œ`nuЄЇЄ-0ЭР^[s  )БF#OўКu–цѓХmєэпЏЗzЧ4 Б@žЭ[Г†@yљš– J“@KOп 0жгCЄя—-nK˜l‹Шъ}ћЦЪy$І/ЕЖъ@:p“вIјMVEХœиРцаўœœ?рЦЧUф0‚С†Ho/c/ tшЪщ[мw$\Їr?їŽ;,Э0(Оя>г@sћЎDЃ†?м˜’žЮHWЃнн\иЛзgG"UРИЪbџFš„ИAZжтБžЂЁЁC‡”3ЗxŒ^Ї>чЌZeф­^ЂCZ4Š/d ЋS‹ŠвGЯŸgЄЋ €и•+ЂЛЙй6% €їY##DћћуКh(ЄхT{ZY™Єfэ>@J.9ЛQ lDFККPЉаЕcG Л…(kbеŒhК^˜Єя?xP–3ў€4 М\нДI!>q Z P’’ž^щэ%688СюХЗпN1Р={23s,xЧ†-nØРn! З ‘ЈoЂ@Zж-‰z€P[›Є‹ЗЁ T[Рa  шPњоїпзNПўњ§Bз‹Чzz&йЕЦЦ8Зk—.ЃбЭf8м|'HyИ…p€єeЫоЮ(,l§@ˆ ›юfš\hoWы`хRПП№M“iЅЅЖ3œѓš‡P[›>rіЌ:x№GБ+WтЖђkk-ЏэŽ-[ќъыяFЪяŸ/ь^ž Qˆj#ЌB,œ7я—@Ўj№УЉœИ|фˆ&m`Ѕmš‹”~NA%RRшsІm2Щ™mл|—YЎTFV–ЌnnѓчфФ/ЋFККDwsГР>іXNє}Bў`№ёh?ЃннXcc[#ННџв…0€Zеoб“OF—Ојbќ„f†УbАЃCVbлї+}ZyЙ—Бsœтg‹Ю—_6Ўœ8‘ЎОчЏ[gi†Aо]wM`A9oѓx`л‘ š…HKЩЬМїЪ‰ŽSCCўЯ?Пј™аДq hš}г /Ф=ёDЬШЮŽGъТюнК98И)ЧЬŸoЛ‡)- SЕлб(.sее™љkзNИЕА€ 2xы­jЮƒ€ŒчyФ0jѕПwр™7о№Ѕ/\јКо‡;QіŠб”Œ YА~§сЎэл+NНіšoј‹/x*gкќљоЌфS` "|> яЙЧvэў‡qC9oзoмЈŽ$ho iZw1HЫтјгOЯ•БиѕJ—W[ЋЦШh_п‹ёєѕ‰/п|sBйO+)Q“ѓЈ&:oсTlпмЙ ЕЈh/BќгvсфЬO=ЅŠ„  Ю€Т†ѕ‰†EyЋW+ЮOENž|пUOк$ъь@ч]KOЫ}oи`yдяH),ЈSб/ЏЉБВ+*дŽі0.MP LћpЂЂ„ЯGюэЗ[Ў‘§kЮœЮ$іЕqЮ ЉХХЪзnА#ЪKг}Ёi”lкЄЮJЧ€ аЌAЏ hBАќ™gbу&xMDs5Ы€(B|8џС{3/Ж=bта˜Йb…(u‹gBМ§еLiЅЅ“4H’pV9oyUU– Vџфz)#>УЈž—нyїž=Ѓkз*†Žћ€€MRnkbgƒmG­бббw—/OWЮеG"'q|;PюšџНћNœ72c8L˜zjъсe/НДЯж4b8щ`љ55jЂг@€H`Рv_i__)ЇмIлnP|šFICƒJŸN<лЌ+Q јxhJА ь5% ѕшzSwж4rЋЋе"jѕљФk@uH+*В=Ж'0 `—ц(Кэ6oњьѕ:ч‘3Р]Р_№D?€ђсВлЩ6bБ­d XђшЃ1#TЦ?R:р ` ЦЃoщ%%Њя N!‹KН”ЉBќ-SгЬЪW_U•љj”љImW=NзKyѓŒЃѓпuWўЄbыжяeWV*LNŸЏ•Џ§ул~824є[_FЦG8Uv/pжлG@‡ +МВnОYEьЋЉь6Hк€Рg˜>њгЪ5=ај32>ў„€gIШCq4 ЛВRѕщКŠcјЮ ХWщ7­\ы™žРy 8?E{Œ№2}б"р“)њ{%œbЮУ7{Є”И‹5QD€œЅKmЁЉЩё™8v­’”ЫнzјLРE`оЪ•*њ6p‚FїZ$9ЗгRJ ўЅЙ ж­Sя$IќEr*IкѕК€w `^yyЄИЎNјŽ1‹б‡$ˆР[~hПёЁ‡>@хє‡ЩВ?ˆ$џйCрм^ьРйџof–S(йПдKœЈ ьaš]+™’l”h8`f§П<џCэС№okЯ[IENDЎB`‚acoustid-fingerprinter-0.6/images/acoustid-fp.ico000066400000000000000000000422061202171654700222360ustar00rootroot00000000000000 hF ˆ Ў  Ј600 Ј%о(  џџџџџџџџџџџџџџџџџџџџџџџџOџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЏЗєєћжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊЂљРРщѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЏооєЎџџџЌ.Ѓџ––жјџџџ&џџџяяџџџџLџџџџџџџџџџџџџџџџџџ-;;ЗМmmФњџџџqЋdЉџttЩњџџџHџџџ!!ЉЬќќўЪџџџџџџџџџџџџЋ:HHСгЋџЎўўўџОАЏЊџRRРћџџџjЈ&ЁџММцїџџџ'ccУtџџџМџџџЊ3ЊџЌџЋўвв№ђ ЏђЋџ**ИѓџџџЋvЊџhhЪњИИш†Ѕў­­пїџџџMџ##ЖнccЬ•ЊМ99НяЊџOOФтЋњћћўТАФЏўБёYYШ№Њџ>>Кэџџџvџџџ<<Р+џџџЋaЊџАўВВцzЊџЯЯяяЌїŽŽйиЊ§ЋџZZШ№ЊВџџџ.џџџџџџџџџГ Џё]]ШѓџџџЋьooЯњЋўыыј##ЖНЋўббя‘™џџџџџџџџџџџџџџџ>>ОuuбpџџџЏК Ў§22К§џџџOЊK""ЕШџџџџџџџџџџџџџџџџџџџџџ%%Жџџџџ""Е•ЊџmmЮњџџџџџџЊ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ**ЙlАџЁЁпсџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџА9&&ЗўллєœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџБ **ИпџџџAџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ­>џџџџџџџџџџџџџџџџџџџџџџџќџќџьџЬЯ€Э€A€ђђі7ў?џ?џ?џџџ(0 џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ%ррѕЋџџџ‹џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅВggХьџџџЭџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅш,,Ћ§џџџьџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџPџџџџџџ­Ѕџ  ўџџџєџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџCCОoџџџєџџџ7џџџЊNЇџ›џъъјѕџџџ>џџџџџџŸŸпџџџСџџџ:џџџџџџџџџџџџџџџџџџџџџџџџџџџЅ6—џЩЩъіџџџ‘џџџЋ‚ЊџžџФФъіџџџ`џџџЊЃмХХыіџџџІџџџџџџџџџџџџџџџџџџџџџььѕ§§ўщ66КљŸџggХњџџџфџџџЊГЊџЃџлјџџџ‚џџџЉMџTTПћџџџьџџџ џџџџџџ@џџџХџџџџџџЊ!ЊюАњЊџЊџЋўќќўєџџџ;ЊсЊџЉџyyЭњџџџЅџџџЉ€Ѕџ ЅўїїќєџџџSЈF**Џяээљѕџџџ~џџџџџџЊлЊџЊџЊџЊџШШьіттѕ‚ЊџЊџЊџWWЧљџџџЦџџџЊВЊџЇџЏЏуїџџџ ЋЩџЯљџџџпџџџџџџ**Й™ЊџГ§DDРЂЊџ‚‚еѓŸŸпђЊџaaЪњЊ§((ИьџџџхџџџЊсЊџЊџ]]ЩѓллѓчЊўЇџЌўџџџєџџџџџџД+ Е§ccЫБ Њі ­њДўЋџѕѕћбЊК ЎђџџџєзЊўБў­јАѕwwвњЊџЊџЇџььјсџџџ?џџџџџџ--КZџџџџџџЊЊЊџЊџ--Й§џџџžЉ‰ЊџііќєllЮYЊџyyвњ ДСЊџ ­џЊџ~~д№ЊяmmЮdџџџ3џџџџџџџџџџџџџџџЊTЊџЊџ__ЪћџџџjЉeЊџдд№і``Ъ–ЊџХХьђВeАџЊџВўњњ§”Ћ@Њ0џџџџџџџџџџџџџџџџџџЊ !!ЕѕЊџ••мјџџџ6Ћ@ЊџЇЇтјXXЧх­ўјјќРŸ66М№ЊџjjЭњџџџ>џџџџџџџџџџџџџџџџџџџџџџџџџџџ,,ИЕ''ЗџЧЧьмџџџЇЋџ88Нњ Ўў==Оќџџџ‡џџџГ,,ЙџЁЁр›џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ Џ6((ЗсЕЕэџџџ€АњЊџЊџwwвњџџџNџџџЊ­ƒџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџГ џџџџџџџџџГсЊџЊџГГхїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ЕХВџЊџыыјжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџДЇ//Кџ))З§џџџ”џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЎDDСџiiЭњџџџTџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊE99НџЉЉтэџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŸБєММъ=џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯџџџџџџџћё?С;3€€ијќ`‡ќ`ќ`ўспџсџџсџџсџџуџџѓџџїџџџџ( @ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ!џџџёџџџ/џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋI33ЙЖџџџєџџџ–џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџІЅšўппѕѕџџџУџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЄк—џнјџџџцџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊЅў™џuuЪњџџџєџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ)џџџеџџџ&џџџџџџЊBЇџџOOКћџџџєџџџ7џџџџџџџџџџџџџџџ џџџ*џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЄ>аСџџџєџџџІџџџџџџЉwЉџŸџ..­§џџџєџџџZџџџџџџџџџ™џџџ‹џџџшџџџ$џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ€ žт—џдд№іџџџцџџџџџџЊЋЊџЃџ Ѓўџџџєџџџ}џџџџџџџџџЁКˆˆдѕџџџєџџџ{џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ^џџџxxбБŸџ™џ{{Яљџџџєџџџ/џџџЊоЊџЅџŸџщщјѕџџџЁџџџџџџЋCџЁџёёћѕџџџЭџџџџџџџџџџџџџџџwџџџ џџџџџџџџџЉPŸŸрrППщїАўЇџЁџ??ИќџџџєџџџkБ ЊўЊџЉџ џУУыїџџџУџџџџџџЈ~ЁџџЂЂпјџџџєџџџ(џџџЊ ннѓЊџџџєџџџЁџџџџџџџџџЊмЊџЊџЊџЊџЉџ Љџњњ§єџџџЉЊ9ЊџЊџЊџЅџнјџџџцџџџџџџЊЏЇџЂџMMОћџџџєџџџw€ЅаЇ§ууіѕџџџ№џџџџџџџџџЉЊџЊџЊџЊџЊџЊџТТъїџџџхЋgЊџЊџЊџЊџyyвњџџџєџџџџџџЊрЊџЈџ ЈџѕѕќєџџџТЉDЄџ›џmmЩњџџџєџџџ€џџџџџџЏEЊџЊџЊџ==ПњЋѕЊџ€€ељџџџє++ЙРЊџЏўЌљЊџUUЦњџџџєџџџ2ЅЊџЊџЊџЉџЎЎфїџџџѓЎЋЉџЂџ ІўёёћѕџџџШџџџџџџП%%ЖэЊџБћссіpЊЅЊџ00КёччїѕЌџЊџЈЈтј++ИЗЊџ++ИёџџџєџџџPЉ;ЊџЊџЊџЊџ__ЪњћћўєЌїЊџЉџЂџЂЂојџџџѓџџџџџџџџџ//ЙДџNNУsџџџЉJЊџЊџ""Ж§ЊџЌџјј§єBBСNЊџЏіџџџєџџџnЊfЊџЊџ<<ОјЊџЏњННщїЊџЊџЊџЉџBBМгџџџєџџџ3џџџџџџЊ ((З‹џџџџџџПЊыЊџЊџЊџ00К§џџџнЄЊџЊџііќєџџџЊЊџAAПќŸŸпНЊџЊџДўЊџЋџдђЊћЊвџџџџџџџџџџџџџџџџџџџџџџџџџџџЋ—ЊџЊџЊџccЫћџџџЊџџџЊъЊџееђіџџџЋЊЛЊџœœојээјpВїЊџЊџЊџFFСќџџџšЊfЊбџџџџџџџџџџџџџџџџџџџџџџџџџџџЉAАџЊџЊџ˜˜нјџџџvџџџЊЧЊџЕЕцїџџџшЋчЊџййѓіџџџ3ВЗДџЊџЊџојџџџFџџџЊџџџџџџџџџџџџџџџџџџџџџџџџџџџЊ%%ЖэАџЊџЯЯяіџџџ@џџџЋЅЊџ€€ељббяіЊџБўџџџъџџџЋa>>ПџАџЌџююљфџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ЕЁ55МџБњќќўыџџџ џџџЋ‚ЊџГ§ZZШћЊџNNФќџџџЗџџџЊВё==Оџ<<ПуџџџaџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊ9FFСў##ЕЩџџџ>џџџџџџЋa ЎџЊџЊџЊџˆˆзљџџџ~џџџџџџЉkДўЋCџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ..К•А@џџџџџџџџџЊBВџЊџЊџЊџФФыіџџџFџџџџџџџџџЇ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЈ#Дџ ­џЊџЌџљљ§ђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊ Е§ГџЊџ::НќџџџЪџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџДш--ЙџЌџssањџџџŽџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџБЩ>>ПџБџББхїџџџMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌІOOФџГџ№№њђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊiSSЦџ33ЛьџџџПџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЈ#>>Пџ;;ОЉџџџ]џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ­нЊ6џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ­џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§џџџјџџџ№џџџ№џџџ№џџў№џџќpљџј`ёџр`pџр`pу€ `У€ `AР`Ф`Ю`юў џ џ џ ?џžџОџџўџџўџџўџџўџџўџџџџџџ?џџџџџџџџ(0` џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ џџџОџџџmџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџKџџџђџџџйџџџ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЄЊЬ||дИџџџѓџџџѓџџџ]џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋ[Ѓџ ЌјўўџєџџџѓџџџœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊЂџ—џААфїџџџєџџџЫџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЈч›џ•џiiЬњџџџєџџџ№џџџ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ(Їџџ—џ77Зќџџџєџџџђџџџ7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋRЇџŸџ™џ Єў§§ўєџџџѓџџџeџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџсџџџџџџџџџџџџџџџЋ|ЇџЁџ›џџззђіџџџѓџџџ’џџџџџџџџџџџџџџџџџџџџџџџџџџџ$џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџђђј'џџџюџџџѓџџџЁџџџџџџџџџџџџЋІЉџЃџџ›џЄЄсјџџџѓџџџАџџџџџџџџџџџџџџџџџџџџџџџџ–џџџюџџџAџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅЂЇєяяњєџџџєџџџёџџџ!џџџџџџџџџЊаЊџЅџŸџ›џttбњџџџєџџџЩџџџџџџџџџџџџџџџЊ„„д6џџџюџџџєџџџШџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌ.˜ў‘џeeШњџџџєџџџѓџџџ[џџџџџџ€ЊїЊџЇџЁџ›џTTЦћџџџєџџџфџџџџџџџџџџџџџџџЊЉЃћЬЬюѕџџџєџџџђџџџ*џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџІЌ™џ–џžџээљѕџџџѓџџџ”џџџџџџЊ$ЊџЊџЉџЃџџ88Кќџџџєџџџђџџџ џџџџџџџџџЌ(Ёџ•џ<<ЛќџџџєџџџѓџџџzџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџOџџџюџџџТЊЊуЏІџŸџ›џ›џББхїџџџєџџџЯџџџџџџЊQЊџЊџЊџЅџžџАўџџџєџџџђџџџ(џџџџџџџџџЋ|Ÿџ™џЁџффіѕџџџєџџџЪџџџџџџџџџџџџџџџџџџ!џџџПџџџ\џџџџџџџџџџџџџџџџџџЊ џџџ"џџџђііќє$$Ж§ЈџЄџЁџџqqЮњџџџєџџџђџџџџџџЉ€ЊџЊџЊџЇџ џІџўўџєџџџђџџџAџџџџџџџџџЉЎЁџџœџкљџџџєџџџђџџџ*џџџџџџџџџџџџџџџгџџџѓџџџёџџџUџџџџџџџџџџџџЋaЊљЊР[[Шщ))З§ЊџЊџЉџІџЂџ44З§џџџєџџџђџџџUџџџЊЎЊџЊџЊџЉџЃџЃџччїѕџџџѓџџџ\џџџџџџџџџЉрЅџЁџžџ::ЙќџџџєџџџѓџџџyџџџџџџЌ%ЎзААхіџџџєџџџєџџџУџџџџџџџџџџџџЌ4ЊџЊџЊџЊџЊџЊџЊџЊџЇџЉџєєћєџџџѓџџџ‘џџџЊнЊџЊџЊџЊџЈџЃџЪЪэіџџџѓџџџvџџџџџџЊЊџЉџЅџЂџЃџффіѕџџџєџџџУџџџЖЇщ™џ›џЙЙчїџџџєџџџђџџџ@џџџџџџџџџџЊцЊџЊџЊџЊџЊџЊџЊџЊџЉџЗЗчїџџџєџџџЮ ЛЊўЊџЊџЊџЊџЊџЉџЏЏфїџџџѓџџџџџџџџџЋCЊџЊџЉџІџЃџкљџџџєџџџђџџџЉSЅџџ™џ@@ЙќџџџєџџџѓџџџЌџџџџџџџџџџџџЌЊџЊџЊџЊџЊџЊџЊџЊџЊџvvбљџџџєџџџђEEТPЊџЊџЊџЊџЊџЊџЊџ’’лљџџџѓџџџЌџџџџџџЊuЊџЊџЊџЊџЇџCCПќџџџєџџџѓџџџdЊБЈџЂџџŸџЮЮюіџџџєџџџюџџџџџџџџџџџџЌOЏџЊџЊџЊџЋџ‰‰и№ЋлЊџЊџ11ЛїџџџєџџџѓZZШЬЊџЊџБў__ЪњЊџЊџЊџvvбљџџџєџџџЩџџџџџџЊЅЊџЊџЊџЊџЊџЋџ№№њѕџџџѓххіДЊљЊџЇџЂџžџZZЦћџџџєџџџђџџџIџџџџџџџџџГ ЕјЊџЊџЊџggЬњџџџsЋaЊџЊџЊ§ђђћєѕѕќєАўЊџЊџељёёћяЊѓЊџЊџXXЧњџџџєџџџчџџџџџџЊаЊџЊџЊџЊџЊџЊџЅЅсїџџџєœœођЊџЊџЊџЇџЂџ Іўњњ§єџџџѓџџџ„џџџџџџџџџџџџБЇ,,ЙџЊџ Ўјщщјаџџџ ЊЊѓЊџЊџssаљ„„жљЊџЊџЊџгг№іџџџХЊœЊџЊџ::НќџџџєџџџђџџџЊЊјЊџЊџЌџЋџЊџЊџWWЧћџџџєVVЧћЊџЊџЊџЊџЇџЃџФФыѕџџџѓџџџЛџџџџџџџџџџџџЊ #include #include #include #include #include "utils.h" #include "loadfilelisttask.h" LoadFileListTask::LoadFileListTask(const QStringList &directories) : m_directories(removeDuplicateDirectories(directories)) { } QStringList LoadFileListTask::removeDuplicateDirectories(const QStringList &directories) { int directoryCount = directories.size(); QList sortedDirectories; for (int i = 0; i < directoryCount; i++) { sortedDirectories.append(QDir(directories.at(i)).canonicalPath() + QDir::separator()); } qSort(sortedDirectories); QStringList result; result.append(sortedDirectories.first()); for (int j = 0, i = 1; i < directoryCount; i++) { QString path = sortedDirectories.at(i); if (!path.startsWith(result.at(j))) { result.append(path); j++; } } return result; } void LoadFileListTask::processFile(const QString &path) { static QSet allowedExtensions = QSet() << "MP3" << "MP4" << "M4A" << "FLAC" << "OGG" << "OGA" << "APE" << "OGGFLAC" << "TTA" << "WV" << "MPC" << "WMA"; if (allowedExtensions.contains(extractExtension(path))) { if (!m_cache.contains(path)) { m_cache.insert(path); m_files.append(path); } } } void LoadFileListTask::processDirectory(const QString &path) { emit currentPathChanged(path); QFileInfoList fileInfoList = QDir(path).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); for (int j = 0; j < fileInfoList.size(); j++) { QFileInfo fileInfo = fileInfoList.at(j); if (fileInfo.isDir()) { processDirectory(fileInfo.filePath()); } else { processFile(fileInfo.filePath()); } } } static QSet readCacheFile() { QString fileName = cacheFileName(); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Couldn't open cache file" << fileName << "for reading"; return QSet(); } QTextStream stream(&file); stream.setCodec("UTF-8"); QSet result; while (true) { QString fileName = stream.readLine(); if (fileName.isEmpty()) { break; } result.insert(fileName); } return result; } void LoadFileListTask::run() { m_cache = readCacheFile(); foreach (QString path, m_directories) { processDirectory(path); } emit finished(m_files); } acoustid-fingerprinter-0.6/loadfilelisttask.h000066400000000000000000000013021202171654700215560ustar00rootroot00000000000000#ifndef FPSUBMIT_LOADFILELISTTASK_H_ #define FPSUBMIT_LOADFILELISTTASK_H_ #include #include #include #include class LoadFileListTask : public QObject, public QRunnable { Q_OBJECT public: LoadFileListTask(const QStringList &directories); void run(); QStringList files() const { return m_files; } signals: void finished(const QStringList &files); void currentPathChanged(const QString &path); private: static QStringList removeDuplicateDirectories(const QStringList &directories); void processDirectory(const QString &path); void processFile(const QString &path); QSet m_cache; QStringList m_directories; QStringList m_files; }; #endif acoustid-fingerprinter-0.6/main.cpp000066400000000000000000000005721202171654700175070ustar00rootroot00000000000000#include #include "decoder.h" #include "mainwindow.h" int main(int argc, char **argv) { Decoder::initialize(); QApplication app(argc, argv); app.setOrganizationName("Acoustid"); app.setOrganizationDomain("acoustid.org"); app.setApplicationName("Fingerprinter"); app.setApplicationVersion(VERSION); MainWindow window; window.show(); return app.exec(); } acoustid-fingerprinter-0.6/mainwindow.cpp000066400000000000000000000102201202171654700207260ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "progressdialog.h" #include "checkabledirmodel.h" #include "fingerprinter.h" #include "mainwindow.h" #include "constants.h" MainWindow::MainWindow() { setupUi(); } void MainWindow::setupUi() { QTreeView *treeView = new QTreeView(); m_directoryModel = new CheckableDirModel(); m_directoryModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); #ifdef Q_WS_MAC m_directoryModel->setRootPath("/Volumes"); #else m_directoryModel->setRootPath(""); #endif treeView->setModel(m_directoryModel); treeView->setHeaderHidden(true); treeView->hideColumn(1); treeView->hideColumn(2); treeView->hideColumn(3); // QString homePath = QDir::homePath(); QString homePath = QDesktopServices::storageLocation(QDesktopServices::MusicLocation); const QModelIndex homePathIndex = m_directoryModel->index(homePath); treeView->expand(homePathIndex); treeView->selectionModel()->setCurrentIndex(homePathIndex, QItemSelectionModel::ClearAndSelect); treeView->scrollTo(homePathIndex); QLabel *treeViewLabel = new QLabel(tr("&Select which folders to fingerprint:")); treeViewLabel->setBuddy(treeView); m_apiKeyEdit = new QLineEdit(); QPushButton *apiKeyButton = new QPushButton(tr("&Get API key...")); connect(apiKeyButton, SIGNAL(clicked()), SLOT(openAcoustidWebsite())); QSettings settings; m_apiKeyEdit->setText(settings.value("apikey").toString()); QHBoxLayout *apiKeyLayout = new QHBoxLayout(); apiKeyLayout->addWidget(m_apiKeyEdit); apiKeyLayout->addWidget(apiKeyButton); QLabel *apiKeyLabel = new QLabel(tr("Your Acoustid &API key:")); apiKeyLabel->setBuddy(m_apiKeyEdit); QPushButton *fingerprintButton = new QPushButton(tr("&Fingerprint...")); connect(fingerprintButton, SIGNAL(clicked()), SLOT(fingerprint())); QPushButton *closeButton = new QPushButton(tr("&Close")); connect(closeButton, SIGNAL(clicked()), SLOT(close())); QDialogButtonBox *buttonBox = new QDialogButtonBox(); buttonBox->addButton(fingerprintButton, QDialogButtonBox::ActionRole); buttonBox->addButton(closeButton, QDialogButtonBox::RejectRole); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(apiKeyLabel); mainLayout->addLayout(apiKeyLayout); mainLayout->addWidget(treeViewLabel); mainLayout->addWidget(treeView); mainLayout->addWidget(buttonBox); QWidget *centralWidget = new QWidget(); centralWidget->setLayout(mainLayout); setCentralWidget(centralWidget); setWindowTitle(tr("Acoustid Fingerprinter")); QIcon icon; icon.addFile(":/images/acoustid-fp-16.png", QSize(16, 16)); icon.addFile(":/images/acoustid-fp-24.png", QSize(24, 24)); icon.addFile(":/images/acoustid-fp-32.png", QSize(32, 32)); icon.addFile(":/images/acoustid-fp-48.png", QSize(48, 48)); setWindowIcon(icon); resize(QSize(400, 500)); } void MainWindow::openAcoustidWebsite() { QDesktopServices::openUrl(QUrl::fromPercentEncoding(API_KEY_URL)); } void MainWindow::fingerprint() { QString apiKey; QList directories; if (!validateFields(apiKey, directories)) { return; } QSettings settings; settings.setValue("apikey", apiKey); Fingerprinter *fingerprinter = new Fingerprinter(apiKey, directories); ProgressDialog *progressDialog = new ProgressDialog(this, fingerprinter); fingerprinter->start(); progressDialog->setModal(true); progressDialog->show(); } bool MainWindow::validateFields(QString &apiKey, QList &directories) { apiKey = m_apiKeyEdit->text(); if (apiKey.isEmpty()) { QMessageBox::warning(this, tr("Error"), tr("Please enter your Acoustid API key. You can get an API key " "from the Acoustid website after signing in " "with your MusicBrainz account or any OpenID (Google, Yahoo, " "etc.)").arg(API_KEY_URL)); return false; } directories = m_directoryModel->selectedDirectories(); if (directories.isEmpty()) { QMessageBox::warning(this, tr("Error"), tr("Please select one or more folders with audio files to fingerprint.")); return false; } return true; } acoustid-fingerprinter-0.6/mainwindow.h000066400000000000000000000007021202171654700203770ustar00rootroot00000000000000#ifndef FPSUBMIT_MAINWINDOW_H_ #define FPSUBMIT_MAINWINDOW_H_ #include #include #include "checkabledirmodel.h" class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); private slots: void openAcoustidWebsite(); void fingerprint(); private: void setupUi(); bool validateFields(QString &apiKey, QList &directories); QLineEdit *m_apiKeyEdit; CheckableDirModel *m_directoryModel; }; #endif acoustid-fingerprinter-0.6/progressdialog.cpp000066400000000000000000000107251202171654700216100ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "progressdialog.h" #include "constants.h" ProgressDialog::ProgressDialog(QWidget *parent, Fingerprinter *fingerprinter) : QDialog(parent), m_fingerprinter(fingerprinter) { setupUi(); connect(fingerprinter, SIGNAL(fileListLoadingStarted()), SLOT(onFileListLoadingStarted())); connect(fingerprinter, SIGNAL(fingerprintingStarted(int)), SLOT(onFingerprintingStarted(int))); connect(fingerprinter, SIGNAL(currentPathChanged(const QString &)), SLOT(onCurrentPathChanged(const QString &))); connect(fingerprinter, SIGNAL(finished()), SLOT(onFinished())); connect(fingerprinter, SIGNAL(networkError(const QString &)), SLOT(onNetworkError(const QString &))); connect(fingerprinter, SIGNAL(authenticationError()), SLOT(onAuthenticationError())); connect(fingerprinter, SIGNAL(noFilesError()), SLOT(onNoFilesError())); } ProgressDialog::~ProgressDialog() { } void ProgressDialog::setupUi() { m_mainStatusLabel = new QLabel(tr("Starting...")); m_currentPathLabel = new QLabel(); m_closeButton = new QPushButton(tr("&Close")); connect(m_closeButton, SIGNAL(clicked()), SLOT(close())); m_stopButton = new QPushButton(tr("&Stop")); connect(m_stopButton, SIGNAL(clicked()), SLOT(stop())); m_pauseButton = new QPushButton(tr("&Pause")); m_pauseButton->setCheckable(true); connect(m_pauseButton, SIGNAL(clicked(bool)), SLOT(togglePause(bool))); QDialogButtonBox *buttonBox = new QDialogButtonBox(); buttonBox->addButton(m_pauseButton, QDialogButtonBox::ActionRole); buttonBox->addButton(m_stopButton, QDialogButtonBox::RejectRole); buttonBox->addButton(m_closeButton, QDialogButtonBox::RejectRole); m_closeButton->setVisible(false); m_progressBar = new QProgressBar(); m_progressBar->setMinimum(0); m_progressBar->setMaximum(0); m_progressBar->setFormat(tr("%v of %m")); m_progressBar->setTextVisible(false); connect(m_fingerprinter, SIGNAL(progress(int)), m_progressBar, SLOT(setValue(int))); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(m_mainStatusLabel); mainLayout->addWidget(m_progressBar); mainLayout->addWidget(m_currentPathLabel); mainLayout->addStretch(); mainLayout->addWidget(buttonBox); setLayout(mainLayout); setWindowTitle(tr("Acoustid Fingerprinter")); resize(QSize(450, 200)); } void ProgressDialog::onFileListLoadingStarted() { m_progressBar->setTextVisible(false); m_progressBar->setMaximum(0); m_progressBar->setValue(0); m_mainStatusLabel->setText(tr("Collecting files...")); } void ProgressDialog::onFingerprintingStarted(int count) { m_progressBar->setTextVisible(true); m_progressBar->setMaximum(count); m_progressBar->setValue(0); m_mainStatusLabel->setText(tr("Fingerprinting...")); } void ProgressDialog::onFinished() { m_mainStatusLabel->setText(tr("Submitted %n fingerprint(s), thank you!", "", m_fingerprinter->submitttedFingerprints())); m_closeButton->setVisible(true); m_pauseButton->setVisible(false); m_stopButton->setVisible(false); } void ProgressDialog::onCurrentPathChanged(const QString &path) { QString elidedPath = m_currentPathLabel->fontMetrics().elidedText( path, Qt::ElideMiddle, m_currentPathLabel->width()); m_currentPathLabel->setText(elidedPath); } void ProgressDialog::setProgress(int value) { m_progressBar->setValue(value); } void ProgressDialog::stop() { m_fingerprinter->cancel(); m_pauseButton->setEnabled(false); m_stopButton->setEnabled(false); } void ProgressDialog::onNetworkError(const QString &message) { stop(); QMessageBox::critical(this, tr("Network Error"), message); } void ProgressDialog::onAuthenticationError() { stop(); QMessageBox::critical(this, tr("Error"), tr("Invalid API key. Please check if the API key " "you entered matches your key on the " "Acoustid website.").arg(API_KEY_URL)); } void ProgressDialog::onNoFilesError() { QMessageBox::critical(this, tr("Error"), tr("There are no audio files in the selected folder(s).")); } void ProgressDialog::closeEvent(QCloseEvent *event) { if (!m_fingerprinter->isFinished()) { stop(); event->ignore(); } else { event->accept(); } } void ProgressDialog::togglePause(bool checked) { if (checked) { m_fingerprinter->pause(); } else { m_fingerprinter->resume(); } } acoustid-fingerprinter-0.6/progressdialog.h000066400000000000000000000017121202171654700212510ustar00rootroot00000000000000#ifndef FPSUBMIT_PROGRESSDIALOG_H_ #define FPSUBMIT_PROGRESSDIALOG_H_ #include #include #include #include "fingerprinter.h" class ProgressDialog : public QDialog { Q_OBJECT public: ProgressDialog(QWidget *parent, Fingerprinter *fingerprinter); ~ProgressDialog(); public slots: void setProgress(int value); void togglePause(bool); void stop(); void onFileListLoadingStarted(); void onFingerprintingStarted(int count); void onCurrentPathChanged(const QString &path); void onFinished(); void onNetworkError(const QString &message); void onAuthenticationError(); void onNoFilesError(); protected: void closeEvent(QCloseEvent *event); private: void setupUi(); Fingerprinter *m_fingerprinter; QPushButton *m_closeButton; QPushButton *m_pauseButton; QPushButton *m_stopButton; QProgressBar *m_progressBar; QLabel *m_mainStatusLabel; QLabel *m_currentPathLabel; }; #endif acoustid-fingerprinter-0.6/tagreader.cpp000066400000000000000000000171141202171654700205210ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #ifdef TAGLIB_WITH_ASF #include #endif #ifdef TAGLIB_WITH_MP4 #include #endif #include #include #include #include #include "tagreader.h" QMutex TagReader::m_mutex; TagReader::TagReader(const QString &fileName) : m_fileName(fileName), m_trackNo(0), m_discNo(0), m_year(0) { } TagReader::~TagReader() { } #define TAGLIB_STRING_TO_QSTRING(a) QString::fromStdWString((a).toWString()) #define DISPATCH_TAGLIB_FILE(tr, type, file) \ { \ type *tmp = dynamic_cast(file); \ if (tmp) { \ extractMetaFromFile(tr, tmp); \ return; \ } \ } void extractMetaFromXiphComment(TagReader *tr, TagLib::Ogg::XiphComment *tag) { const char *key = "MUSICBRAINZ_TRACKID"; if (tag->fieldListMap().contains(key)) { tr->m_mbid = TAGLIB_STRING_TO_QSTRING(tag->fieldListMap()[key].front()); } key = "ALBUMARTIST"; if (tag->fieldListMap().contains(key)) { tr->m_albumArtist = TAGLIB_STRING_TO_QSTRING(tag->fieldListMap()[key].front()); } key = "DISCNUMBER"; if (tag->fieldListMap().contains(key)) { tr->m_discNo = tag->fieldListMap()[key].front().toInt(); } key = "MUSICIP_PUID"; if (tag->fieldListMap().contains(key)) { tr->m_puid = TAGLIB_STRING_TO_QSTRING(tag->fieldListMap()[key].front()); } } void extractMetaFromAPETag(TagReader *tr, TagLib::APE::Tag *tag) { const char *key = "MUSICBRAINZ_TRACKID"; if (tag->itemListMap().contains(key)) { tr->m_mbid = TAGLIB_STRING_TO_QSTRING(tag->itemListMap()[key].toString()); } key = "ALBUM ARTIST"; // Foobar if (tag->itemListMap().contains(key)) { tr->m_albumArtist = TAGLIB_STRING_TO_QSTRING(tag->itemListMap()[key].toString()); } key = "ALBUMARTIST"; // Picard if (tag->itemListMap().contains(key)) { tr->m_albumArtist = TAGLIB_STRING_TO_QSTRING(tag->itemListMap()[key].toString()); } key = "DISC"; if (tag->itemListMap().contains(key)) { tr->m_discNo = tag->itemListMap()[key].toString().toInt(); } key = "MUSICIP_PUID"; if (tag->itemListMap().contains(key)) { tr->m_puid = TAGLIB_STRING_TO_QSTRING(tag->itemListMap()[key].toString()); } } void extractMetaFromFile(TagReader *tr, TagLib::Ogg::Vorbis::File *file) { extractMetaFromXiphComment(tr, file->tag()); } void extractMetaFromFile(TagReader *tr, TagLib::Ogg::FLAC::File *file) { extractMetaFromXiphComment(tr, file->tag()); } void extractMetaFromFile(TagReader *tr, TagLib::Ogg::Speex::File *file) { extractMetaFromXiphComment(tr, file->tag()); } void extractMetaFromFile(TagReader *tr, TagLib::FLAC::File *file) { extractMetaFromXiphComment(tr, file->xiphComment()); } void extractMetaFromFile(TagReader *tr, TagLib::MPC::File *file) { extractMetaFromAPETag(tr, file->APETag()); } void extractMetaFromFile(TagReader *tr, TagLib::WavPack::File *file) { extractMetaFromAPETag(tr, file->APETag()); } #ifdef TAGLIB_WITH_ASF void extractMetaFromFile(TagReader *tr, TagLib::ASF::File *file) { const char *key = "MusicBrainz/Track Id"; TagLib::ASF::Tag *tag = file->tag(); if (tag->attributeListMap().contains(key)) { tr->m_mbid = TAGLIB_STRING_TO_QSTRING(tag->attributeListMap()[key].front().toString()); } key = "WM/AlbumArtist"; if (tag->attributeListMap().contains(key)) { tr->m_albumArtist = TAGLIB_STRING_TO_QSTRING(tag->attributeListMap()[key].front().toString()); } key = "WM/PartOfSet"; if (tag->attributeListMap().contains(key)) { tr->m_discNo = tag->attributeListMap()[key].front().toString().toInt(); } key = "MusicIP/PUID"; if (tag->attributeListMap().contains(key)) { tr->m_puid = TAGLIB_STRING_TO_QSTRING(tag->attributeListMap()[key].front().toString()); } } #endif #ifdef TAGLIB_WITH_MP4 void extractMetaFromFile(TagReader *tr, TagLib::MP4::File *file) { const char *key = "----:com.apple.iTunes:MusicBrainz Track Id"; TagLib::MP4::Tag *tag = file->tag(); if (tag->itemListMap().contains(key)) { tr->m_mbid = TAGLIB_STRING_TO_QSTRING(tag->itemListMap()[key].toStringList().toString()); } key = "aART"; if (tag->itemListMap().contains(key)) { tr->m_albumArtist = TAGLIB_STRING_TO_QSTRING(tag->itemListMap()[key].toStringList().toString()); } key = "disk"; if (tag->itemListMap().contains(key)) { tr->m_discNo = tag->itemListMap()[key].toIntPair().first; } key = "----:com.apple.iTunes:MusicIP PUID"; if (tag->itemListMap().contains(key)) { tr->m_puid = TAGLIB_STRING_TO_QSTRING(tag->itemListMap()[key].toStringList().toString()); } } #endif void extractMetaFromFile(TagReader *tr, TagLib::MPEG::File *file) { TagLib::ID3v2::Tag *tag = file->ID3v2Tag(); TagLib::ID3v2::FrameList ufid = tag->frameListMap()["UFID"]; if (!ufid.isEmpty()) { for (TagLib::ID3v2::FrameList::Iterator i = ufid.begin(); i != ufid.end(); i++) { TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast(*i); if (frame && frame->owner() == "http://musicbrainz.org") { TagLib::ByteVector id = frame->identifier(); tr->m_mbid = QString::fromAscii(id.data(), id.size()); } } } if (tr->m_mbid.isEmpty()) { // foobar2k writes the tags like this TagLib::ID3v2::UserTextIdentificationFrame *trackidFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "MUSICBRAINZ_TRACKID"); if (trackidFrame) { TagLib::StringList texts = trackidFrame->fieldList(); if (texts.size() > 1) { tr->m_mbid = TAGLIB_STRING_TO_QSTRING(texts[1]); } } } TagLib::ID3v2::FrameList tpe2 = tag->frameListMap()["TPE2"]; if (!tpe2.isEmpty()) { tr->m_albumArtist = TAGLIB_STRING_TO_QSTRING(tpe2.front()->toString()); } TagLib::ID3v2::FrameList tpos = tag->frameListMap()["TPOS"]; if (!tpos.isEmpty()) { tr->m_discNo = tpos.front()->toString().toInt(); } TagLib::ID3v2::UserTextIdentificationFrame *puidFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "MusicIP PUID"); if (puidFrame) { TagLib::StringList texts = puidFrame->fieldList(); if (texts.size() > 1) { tr->m_puid = TAGLIB_STRING_TO_QSTRING(texts[1]); } } } void extractMeta(TagReader *tr, TagLib::File *file) { DISPATCH_TAGLIB_FILE(tr, TagLib::FLAC::File, file); DISPATCH_TAGLIB_FILE(tr, TagLib::Ogg::Vorbis::File, file); DISPATCH_TAGLIB_FILE(tr, TagLib::Ogg::FLAC::File, file); DISPATCH_TAGLIB_FILE(tr, TagLib::Ogg::Speex::File, file); DISPATCH_TAGLIB_FILE(tr, TagLib::MPC::File, file); DISPATCH_TAGLIB_FILE(tr, TagLib::WavPack::File, file); #ifdef TAGLIB_WITH_ASF DISPATCH_TAGLIB_FILE(tr, TagLib::ASF::File, file); #endif #ifdef TAGLIB_WITH_MP4 DISPATCH_TAGLIB_FILE(tr, TagLib::MP4::File, file); #endif DISPATCH_TAGLIB_FILE(tr, TagLib::MPEG::File, file); } bool TagReader::read() { // TagLib functions are not reentrant QMutexLocker locker(&m_mutex); #ifdef Q_OS_WIN32 TagLib::FileRef file(reinterpret_cast(m_fileName.utf16()), true); #else QByteArray encodedFileName = QFile::encodeName(m_fileName); TagLib::FileRef file(encodedFileName.constData(), true); #endif if (file.isNull()) { return false; } TagLib::Tag *tags = file.tag(); TagLib::AudioProperties *props = file.audioProperties(); if (!tags || !props) { return false; } m_artist = TAGLIB_STRING_TO_QSTRING(tags->artist()); m_album = TAGLIB_STRING_TO_QSTRING(tags->album()); m_track = TAGLIB_STRING_TO_QSTRING(tags->title()); m_trackNo = tags->track(); m_year = tags->year(); m_length = props->length(); m_bitrate = props->bitrate(); extractMeta(this, file.file()); if (!m_length) { return false; } return true; } acoustid-fingerprinter-0.6/tagreader.h000066400000000000000000000035251202171654700201670ustar00rootroot00000000000000/* * Chromaprint -- Audio fingerprinting toolkit * Copyright (C) 2010 Lukas Lalinsky * * 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 */ #ifndef FPSUBMIT_TAGREADER_H_ #define FPSUBMIT_TAGREADER_H_ #include #include class TagReader { public: TagReader(const QString &fileName); ~TagReader(); bool read(); QString mbid() const { return m_mbid; } int length() const { return m_length; } int bitrate() const { return m_bitrate; } QString track() const { return m_track; } QString artist() const { return m_artist; } QString album() const { return m_album; } QString albumArtist() const { return m_albumArtist; } QString puid() const { return m_puid; } int trackNo() const { return m_trackNo; } int discNo() const { return m_discNo; } int year() const { return m_year; } public: QString m_fileName; QString m_track; QString m_artist; QString m_album; QString m_albumArtist; QString m_mbid; QString m_puid; int m_trackNo; int m_discNo; int m_year; int m_bitrate; int m_length; static QMutex m_mutex; }; #endif acoustid-fingerprinter-0.6/updatelogfiletask.cpp000066400000000000000000000011571202171654700222720ustar00rootroot00000000000000#include #include #include #include "utils.h" #include "updatelogfiletask.h" UpdateLogFileTask::UpdateLogFileTask(const QStringList &files) : m_files(files) { } void UpdateLogFileTask::run() { QString fileName = cacheFileName(); qDebug() << "Updating" << fileName; QDir().mkpath(QDir::cleanPath(fileName + "/..")); QFile file(fileName); if (!file.open(QIODevice::Append)) { qCritical() << "Couldn't open cache file" << fileName << "for writing"; return; } QTextStream stream(&file); stream.setCodec("UTF-8"); foreach (QString fileName, m_files) { stream << fileName << "\n"; } } acoustid-fingerprinter-0.6/updatelogfiletask.h000066400000000000000000000004251202171654700217340ustar00rootroot00000000000000#ifndef FPSUBMIT_UPDATELOGFILETASK_H_ #define FPSUBMIT_UPDATELOGFILETASK_H_ #include #include class UpdateLogFileTask : public QRunnable { public: UpdateLogFileTask(const QStringList &files); void run(); private: QStringList m_files; }; #endif acoustid-fingerprinter-0.6/utils.h000066400000000000000000000010271202171654700173640ustar00rootroot00000000000000#ifndef FPSUBMIT_UTILS_H_ #define FPSUBMIT_UTILS_H_ #include #include inline QString userAgentString() { return QString("AcoustidFingerprinter/%1 Qt/%2").arg(VERSION).arg(qVersion()); } inline QString cacheFileName() { return QDesktopServices::storageLocation(QDesktopServices::CacheLocation) + "/submitted.log"; } inline QString extractExtension(const QString &fileName) { int pos = fileName.lastIndexOf('.'); if (pos == -1) { return ""; } return fileName.mid(pos + 1).toUpper(); } #endif