From b963b558d86f3d4f65e5224cce6170f63bec3522 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 1 Feb 2004 18:22:52 +0000 Subject: Initial revision git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@3 e10066b5-e1e2-0310-b819-94efdf66514b --- COPYING | 340 ++++++++++++++++ HISTORY | 6 + Makefile | 91 +++++ README | 11 + gd_content_interface.c | 943 +++++++++++++++++++++++++++++++++++++++++++++ gd_content_interface.h | 214 ++++++++++ mg_content_interface.c | 397 +++++++++++++++++++ mg_content_interface.h | 261 +++++++++++++ mg_database.c | 35 ++ mg_database.h | 34 ++ mg_media.c | 272 +++++++++++++ mg_media.h | 137 +++++++ mg_tools.c | 140 +++++++ mg_tools.h | 31 ++ muggle.c | 98 +++++ muggle.h | 51 +++ sh_console_osd.c | 212 ++++++++++ sh_console_osd.h | 191 +++++++++ sh_console_osd_menuitems.c | 530 +++++++++++++++++++++++++ sh_console_osd_menuitems.h | 130 +++++++ sh_dummy_content.c | 567 +++++++++++++++++++++++++++ sh_dummy_content.h | 178 +++++++++ sh_muggle.c | 365 ++++++++++++++++++ sh_muggle2.c | 66 ++++ sh_plugin.c | 77 ++++ sh_plugin.h | 46 +++ vdr_menu.c | 523 +++++++++++++++++++++++++ vdr_menu.h | 87 +++++ 28 files changed, 6033 insertions(+) create mode 100644 COPYING create mode 100644 HISTORY create mode 100644 Makefile create mode 100644 README create mode 100644 gd_content_interface.c create mode 100644 gd_content_interface.h create mode 100755 mg_content_interface.c create mode 100755 mg_content_interface.h create mode 100644 mg_database.c create mode 100644 mg_database.h create mode 100644 mg_media.c create mode 100644 mg_media.h create mode 100644 mg_tools.c create mode 100644 mg_tools.h create mode 100644 muggle.c create mode 100644 muggle.h create mode 100644 sh_console_osd.c create mode 100644 sh_console_osd.h create mode 100644 sh_console_osd_menuitems.c create mode 100644 sh_console_osd_menuitems.h create mode 100644 sh_dummy_content.c create mode 100644 sh_dummy_content.h create mode 100644 sh_muggle.c create mode 100644 sh_muggle2.c create mode 100644 sh_plugin.c create mode 100644 sh_plugin.h create mode 100644 vdr_menu.c create mode 100644 vdr_menu.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..0f92024 --- /dev/null +++ b/HISTORY @@ -0,0 +1,6 @@ +VDR Plugin 'muggle' Revision History +------------------------------------ + +2004-01-15: Version 0.0.1 + +- Initial revision. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8728cc3 --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id: Makefile,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# +PLUGIN = muggle + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + +CXX ?= g++-3.3 +CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g + +### The directory environment: + +DVBDIR = ../../../../DVB +VDRDIR = ../../.. +LIBDIR = ../../lib +TMPDIR = /tmp + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR (taken from VDR's "config.h"): + +VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/mysql/ + +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +### The object files (add further files here): + +OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o sh_dummy_content.o gd_content_interface.o mg_tools.o mg_media.o + +BINOBJS = sh_shell_osd_plugin.o sh_shell_osd.o sh_shell_osd_menuitems.o muggle.o vdr_menu.o content_interface.o dummy_content.o gd_contentinterface.o muggle_tools.o mgmedia.o + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +# Dependencies: + +MAKEDEP = g++ -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Targets: + +all: libvdr-$(PLUGIN).so + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) -shared $(OBJS) -lmysqlclient -o $@ + @cp $@ $(LIBDIR)/$@.$(VDRVERSION) + +sh_muggle : sh_muggle.c $(BINOBJS) + $(CXX) $(CXXFLAGS) $(BINOBJS) sh_muggle.c -lmysqlclient -o $@ + +sh_muggle2 : sh_muggle2.c $(BINOBJS) + $(CXX) $(CXXFLAGS) sh_muggle2.c $(BINOBJS) -lmysqlclient -o $@ + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + @-rm -f $(OBJS) $(BINOBJS) $(DEPFILE) *.so *.tgz core* *~ + @-rm -f sh_muggle sh_muggle2 diff --git a/README b/README new file mode 100644 index 0000000..4e04b83 --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: Your Name + +Project's homepage: URL + +Latest version available at: URL + +See the file COPYING for license information. + +Description: diff --git a/gd_content_interface.c b/gd_content_interface.c new file mode 100644 index 0000000..5707f78 --- /dev/null +++ b/gd_content_interface.c @@ -0,0 +1,943 @@ +/*******************************************************************/ +/*! \file content_interface.cpp + * \brief Data Objects for content (e.g. mp3 files, movies) + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * DUMMY + * Implements main classes of for content items and interfaces to SQL databases + * + * This file implements the following classes + * - GdPlaylist a playlist + * - mgGdTrack a single track (content item). e.g. an mp3 file + * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * + */ +/*******************************************************************/ +#define DEBUG + +#include "gd_content_interface.h" +#include "mg_tools.h" + +using namespace std; + +#define GD_PLAYLIST_TYPE 0 // listtype for giant disc db + +// some dummies to keep the compiler happy +#define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax +#define DUMMY + +// non-member function +int GdInitDatabase(MYSQL *db) +{ + if(mysql_init(db) == NULL) + { + return -1; + } + + if(mysql_real_connect(db,"localhost","root","", + "GiantDisc2",0,NULL,0) == NULL) + { + return -2; + } + return 0; +} + +vector GdGetStoredPlaylists(MYSQL db) +{ + vector list; + MYSQL_RES *result; + MYSQL_ROW row; + + result=mgSqlReadQuery(&db, "SELECT title FROM playlist"); + + while((row = mysql_fetch_row(result)) != NULL) + { + list.push_back(row[0]); + } + return list; +} + +/*******************************************************************/ +/* class mgTack */ +/********************************************************************/ +mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack(); + +/*! + ***************************************************************************** + * \brief Constructor: creates a mgGdTrack obect + * + * \param sqlIdentifier identifies a unique row in the track database + * \param dbase database which stores the track table + * + * On creation, the object contains only the idea. The actual data fields + * are filled when readData() is called for the first time. + ****************************************************************************/ +mgGdTrack::mgGdTrack(int sqlIdentifier, MYSQL dbase) +{ + m_uniqID = sqlIdentifier; + m_db = dbase; + m_retrieved = false; + +} + +/*! + ***************************************************************************** + * \brief copy constructor + * + ****************************************************************************/ +mgGdTrack::mgGdTrack(const mgGdTrack& org) +{ + m_uniqID = org.m_uniqID; + m_db = org.m_db; + m_retrieved = org.m_retrieved; + if(m_retrieved) + { + m_artist = org.m_artist; + m_title = org.m_title; + m_mp3file = org.m_mp3file; + m_album = org.m_album; + m_genre = org.m_genre; + m_year = org.m_year; + m_rating = org.m_rating; + } + +} + + +/*! + ***************************************************************************** + * \brief destructor + * + ****************************************************************************/ +mgGdTrack::~mgGdTrack() +{ + // nothing to be done +} + +/*! + ***************************************************************************** + * \brief accesses the database to fill the actual data fields + * + * In order to avoid innecessary queries to the database, the content fields + * of the mgGdTrack object may not be filled upon creation. + * As soon as the first content field is needed, this private function + * is called to fill all content fields at once + ****************************************************************************/ +bool mgGdTrack::readData() +{ + MYSQL_RES *result; + int nrows; + int nfields; + + // note: this does not work with empty album or genre fields + result = mgSqlReadQuery(&m_db, + "SELECT tracks.artist, album.title, tracks.title, " + " tracks.mp3file, genre.genre, tracks.year, " + " tracks.rating, tracks.length " + "FROM tracks, album, genre " + "WHERE tracks.id=%d " + "AND album.cddbid=tracks.sourceid AND " + " genre.id=tracks.genre1", + m_uniqID); + + nrows = mysql_num_rows(result); + nfields = mysql_num_fields(result); + if(nrows == 0) + { + mgWarning("No entries found \n"); + return false; + } + else + { + if (nrows >1 ) + { + mgWarning("More than one entry found"); + } + MYSQL_ROW row; + + row = mysql_fetch_row(result); + m_artist = row[0]; + m_album = row[1]; + m_title = row [2]; + m_mp3file = row [3]; + m_genre = row [4]; + if(sscanf(row [5], "%d", &m_year) !=1) + { + mgError("Invalid year '%s' in database", row [5]); + } + if(sscanf(row [6], "%d", &m_rating) !=1) + { + mgError("Invalid rating '%s' in database", row [6]); + } + if(sscanf(row [7], "%d", &m_length) !=1) + { + mgError("Invalid duration '%s' in database", row [7]); + } + } + m_retrieved = true; + return true; +} + +/*! + ***************************************************************************** + * \brief returns value for _mp3file + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +string mgGdTrack::getSourceFile() +{ + if(!m_retrieved) + { + readData(); + } + return m_mp3file; +} + +/*! + ***************************************************************************** + * \brief returns value for m_title + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +string mgGdTrack::getTitle() +{ + if(!m_retrieved) + { + readData(); + } + return m_title; +} + +/*! + ***************************************************************************** + * \brief returns value for m_artist + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +string mgGdTrack::getArtist() +{ + if(!m_retrieved) + { + readData(); + } + return m_artist; +} + +/*! + ***************************************************************************** + * \brief returns a string for one field of the item + * + * This is a generic function that shozld work for all content items + ****************************************************************************/ +string mgGdTrack::getLabel(int col) +{ + if(!m_retrieved) + { + readData(); + } + switch(col) + { + case 0: + return m_title; + case 1: + return m_artist; + case 2: + return m_album; + case 3: + return m_genre; + default: + return ""; + } +} + +string mgGdTrack::getDescription() +{ + if(!m_retrieved) + { + readData(); + } + return m_artist + " - " + m_title; + +} + +/*! + ***************************************************************************** + * \brief returns value for m_album + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +string mgGdTrack::getAlbum() +{ + if(!m_retrieved) + { + readData(); + } + return m_album; +} + +/*! + ***************************************************************************** + * \brief returns value for m_genre + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +string mgGdTrack::getGenre() +{ + if(!m_retrieved) + { + readData(); + } + return m_genre; +} + +/*! + ***************************************************************************** + * \brief returns value for m_year + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +int mgGdTrack::getYear() +{ + if(!m_retrieved) + { + readData(); + } + return m_year; +} + +/*! + ***************************************************************************** + * \brief returns value for m_rating + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +int mgGdTrack::getRating() +{ + if(!m_retrieved) + { + readData(); + } + return m_rating; +} + +int mgGdTrack::getDuration() +{ + if(!m_retrieved) + { + readData(); + } + return m_rating; +} +string mgGdTrack::getImageFile() +{ + return "dummyImg.jpg"; +} + +/*! + ***************************************************************************** + * \brief sets the field for m_title to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void mgGdTrack::setTitle(string new_title) +{ + m_title = new_title; +} + +/*! + ***************************************************************************** + * \brief sets the field for m_artist to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void mgGdTrack::setArtist(string new_artist) +{ + m_artist = new_artist; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_album to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void mgGdTrack::setAlbum(string new_album) +{ + m_album = new_album; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_genre to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void mgGdTrack::setGenre(string new_genre) +{ + m_genre = new_genre; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_year to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void mgGdTrack::setYear(int new_year) +{ + m_year = new_year; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_rating to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void mgGdTrack::setRating(int new_rating) +{ + m_rating = new_rating; +} + + +/*! + ***************************************************************************** + * \brief stores current values in the sql database + * + * Note: At the moment, only the values stored directly in the 'tracks' + * database are updated + ****************************************************************************/ +bool mgGdTrack::writeData() +{ + mgSqlWriteQuery(&m_db, "UPDATE tracks " + "SET artist=\"%s\", title=\"%s\", year=%d, rating=%d " + "WHERE id=%d", + m_artist.c_str(), m_title.c_str(), + m_year, m_rating, m_uniqID); + return true; +} + +GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) +{ + MYSQL_RES *result; + MYSQL_ROW row; + int trackid; + + result = mgSqlReadQuery(&db_handle, + "SELECT tracks.id " + " FROM tracks, album, genre WHERE %s" + " AND album.cddbid=tracks.sourceid " + " AND genre.id=tracks.genre1", + restrictions.c_str()); + while((row = mysql_fetch_row(result)) != NULL) + { + // row[0] is the trackid + if(sscanf(row[0], "%d", &trackid) != 1) + { + mgError("Can not extract integer track id from '%s'", + row[0]); + } + m_list.push_back(new mgGdTrack(trackid, db_handle)); + } + +} + + +/*******************************************************************/ +/* class GdPlaylist */ +/********************************************************************/ + +/*! + ***************************************************************************** + * \brief Constructor: opens playlist by name + * + * \param listname user-readable identifier of the paylist + * \param db_handl database which stores the playlist + * + * If the playlist does not yet exist, an empty playlist is created + ****************************************************************************/ +GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) +{ + MYSQL_RES *result; + MYSQL_ROW row; + int nrows; + + m_db = db_handle; + + // + // check, if the database really exists + // + result=mgSqlReadQuery(&m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + listname.c_str()); + nrows = mysql_num_rows(result); + if(nrows == 0) + { + mgDebug(3, "No playlist with name %s found. Creating new playlist\n", + listname.c_str()); + + // create new database entry + mgSqlWriteQuery(&m_db, "INSERT into playlist " + "SET title=\"%s\", author=\"%s\"", + listname.c_str(), + "VDR", // default author + ""); // creates current time as timestamp + m_author = "VDR"; + m_listname = listname; + } + else // playlist exists, read data + { + row = mysql_fetch_row(result); + + if(sscanf(row [0], "%d", & m_sqlId) !=1) + { + mgError("Invalid id '%s' in database", row [5]); + } + m_author = row[1]; + m_listname = listname; + // now read allentries of the playlist and + // write them into the tracklist + insertDataFromSQL(); + }// end 'else (playlist exists) + + m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists +} + +/*! + ***************************************************************************** + * \brief Constructor: construct playlist object from existing sql playlist + * + * \param sql_identifier: sql internal identifier for the playlist + * \param db_handl database which stores the playlist + * + * This constructor is typically used when a playlist is selected from an + * internal list of playlists + ****************************************************************************/ +GdPlaylist::GdPlaylist(unsigned int sql_identifier, MYSQL db_handle) +{ + MYSQL_RES *result; + int nrows; + + m_db = db_handle; + + // check, if the database really exists + result = mgSqlReadQuery(&m_db, + "SELECT title,author FROM playlist where id=%d", + sql_identifier); + nrows = mysql_num_rows(result); + if(nrows == 0) + { + mgDebug(3, "No playlist with id %d found. Creating new playlist\n", + sql_identifier); + + // create new database entry + // DUMMY + } + else // playlist exists, read data + { + MYSQL_ROW row; + row = mysql_fetch_row(result); + + m_listname = row[0]; + m_author = row[1]; + m_sqlId = sql_identifier; + // now read allentries of the playlist and + // write them into the tracklist + insertDataFromSQL(); + } + m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists +} + +/*! + ***************************************************************************** + * \brief empty destructor + * + * Nothing to be done. Constructor of parent class takes care + ****************************************************************************/ +GdPlaylist::~GdPlaylist() +{ + +} + +/*! + ***************************************************************************** + * \brief reads the track list from the sql database into a locallist + ****************************************************************************/ +int GdPlaylist::insertDataFromSQL() +{ + MYSQL_RES *result; + MYSQL_ROW row; + mgGdTrack* trackptr; + int id; + int nrows; + + result = mgSqlReadQuery(&m_db, + "SELECT tracknumber, trackid FROM playlistitem " + "WHERE playlist = %d ORDER BY tracknumber", + m_sqlId); + nrows = mysql_num_rows(result); + while((row = mysql_fetch_row(result)) != NULL) + { + // add antry to tracklist + if(sscanf(row[1], "%d", &id) !=1) + { + mgWarning("Track id '%s' is not an integer. Ignoring \n", row[1]); + } + else + { + trackptr = new mgGdTrack(id, m_db); + m_list.push_back(trackptr); + } + } + return nrows; +} +bool GdPlaylist::storePlaylist() +{ + vector::iterator iter; + int num; + + + if(m_listname =="") + { + mgWarning("Can not store Tracklist without name"); + return false; + } + // remove old playlist items from db + mgSqlWriteQuery(&m_db, + "DELETE FROM playlistitem WHERE playlist = %d", + m_sqlId); + + // add new playlist items to db + + for(iter= m_list.begin(), num=0; iter != m_list.end(); iter++, num++) + { + + mgSqlWriteQuery(&m_db, + "INSERT into playlistitem " + "SET tracknumber=\"%s\", trackid=\"%s\", playlist=%d", + num, (*iter)->getId(), m_sqlId); + } + return true; +} +/*! + ***************************************************************************** + * \brief returns the total duration of all songs in the list in seconds + * + ****************************************************************************/ +int GdPlaylist::getPlayTime() +{ + //DUMMY + // go over all entries in the playlist and accumulate their playtime + + return 0; +} + +/*! + ***************************************************************************** + * \brief returns the duration of all remaining songs in the list in seconds + * + ****************************************************************************/ +int GdPlaylist::getPlayTimeRemaining() +{ + //DUMMY + // go over all remaining entries in the playlist and accumulate their + // playtime + // The remaining playtime of the current song is only known by the mplayer + return 0; // dummy +} + +/*******************************************************************/ +/* class GdTreeNode */ +/*******************************************************************/ + +/*! + ***************************************************************************** + * \brief constructor + * + ****************************************************************************/ +GdTreeNode::GdTreeNode(MYSQL db, int view, string filters) + : mgSelectionTreeNode(db, view) +{ + // create a root node + // everything is done in the parent class + m_restriction = filters; + m_view = view; +} +GdTreeNode::GdTreeNode(mgSelectionTreeNode* parent, + string id, string label, string restriction) + : mgSelectionTreeNode(parent, id, label) +{ + m_restriction = restriction; + // everything else is done in the parent class +} + +/*! + ***************************************************************************** + * \brief destructor + * + ****************************************************************************/ +GdTreeNode::~GdTreeNode() +{ + // _children.clear(); +} + + + +/*! + ***************************************************************************** + * \brief compute children on the fly + * + * \return: true, if the node could be expanded (or was already), false,of + * node can not be expanded any further + * + * retrieves all entries for the next level that satisfy the restriction of + * the current level and create a child-arc for each distinct entry + * + ****************************************************************************/ +bool GdTreeNode::expand() +{ + + MYSQL_ROW row; + MYSQL_RES *result; + int nrows; + int nfields; + char sqlbuff[1024]; /* hope it's big enough ! */ + char idbuf[255]; + int numchild; + + string labelfield; // human readable db field for the column to be expanded + string idfield; // unique id field for the column to be expanded + string tables; // stores the db tables used + string new_restriction_field; // field to be restricted by the new level + string new_restriction; // complete restriction str for the current child + string new_label; + GdTreeNode* new_child; + + if (m_expanded) + { + mgWarning("Node already expanded\n"); + return true; + } + if (m_level == 1) + { + m_view = atoi(m_id.c_str()); + } + mgDebug(5, "Expanding level %d view %d\n", m_level,m_view); + if (m_level > 0) + { + switch(m_view) + { + case 1: // artist -> album -> title + if(m_level == 1) { + sprintf(sqlbuff, + "SELECT DISTINCT album.artist,album.artist" + " FROM album" + " WHERE %s" + " ORDER BY album.artist" + , m_restriction.c_str() ); + idfield = "album.artist"; + } else if(m_level == 2) { // artist -> album + sprintf(sqlbuff, + "SELECT DISTINCT album.title,album.cddbid" + " FROM album" + " WHERE %s" + " ORDER BY album.title" + , m_restriction.c_str() ); + idfield = "album.cddbid"; + } else if(m_level == 3) { // album -> title + sprintf(sqlbuff, + "SELECT tracks.title,tracks.id" + " FROM tracks,album" + " WHERE %s AND tracks.sourceid=album.cddbid" + " ORDER BY tracks.tracknb" + , m_restriction.c_str() ); + idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; + case 2: // genre -> artist -> album -> track + if(m_level == 1) { // genre + sprintf(sqlbuff, + "SELECT DISTINCT genre.genre,tracks.genre1" + " FROM tracks,genre" + " WHERE (genre.id=tracks.genre1) AND" + " %s" + " ORDER BY genre.id" + , m_restriction.c_str()); + idfield = "tracks.genre1"; + } else if(m_level == 2) { // genre -> artist + sprintf(sqlbuff, + "SELECT DISTINCT album.artist,album.artist" + " FROM tracks,genre,album" + " WHERE (genre.id=tracks.genre1) AND" + " (album.cddbid=tracks.sourceid) AND" + " %s" + " ORDER BY album.artist", + m_restriction.c_str()); + idfield = "album.artist"; + } else if(m_level == 3) { // genre -> artist -> album + sprintf(sqlbuff, + "SELECT DISTINCT album.title,tracks.sourceid" + " FROM tracks,genre,album" + " WHERE (genre.id=tracks.genre1) AND" + " (album.cddbid=tracks.sourceid) AND" + " %s" + " ORDER BY album.title" + , m_restriction.c_str()); + idfield = "tracks.sourceid"; + } else if(m_level == 4) { // genre -> artist -> album -> track + sprintf(sqlbuff, + "SELECT DISTINCT tracks.title, tracks.id" + " FROM tracks,genre,album" + " WHERE (genre.id=tracks.genre1) AND" + " (album.cddbid=tracks.sourceid) AND" + " %s" + " ORDER BY tracks.tracknb" + , m_restriction.c_str()); + idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; + case 3: // Artist -> Track + if(m_level ==1) + { + sprintf(sqlbuff, + "SELECT DISTINCT tracks.artist,tracks.artist" + " FROM tracks" + " WHERE " + " %s" + " ORDER BY tracks.artist" + , m_restriction.c_str()); + idfield = "tracks.artist"; + } else if (m_level == 2) { // Track + sprintf(sqlbuff, + "SELECT DISTINCT tracks.title,tracks.id" + " FROM tracks, album" + " WHERE" + " %s AND tracks.sourceid=album.cddbid" + " ORDER BY tracks.title" + , m_restriction.c_str()); + idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; + default: + mgError("View '%d' not yet implemented", m_view); + } + + // now get all childrean ofthe current node fromthe database + result = mgSqlReadQuery(&m_db, sqlbuff); + nrows = mysql_num_rows(result); + nfields = mysql_num_fields(result); + + numchild=1; + while((row = mysql_fetch_row(result)) != NULL) + { + // row[0] is the printable label for the new child + // row[1] is the unique id for the new child + sprintf(idbuf, "%s_%03d", m_id.c_str(), numchild); + + new_restriction = m_restriction + " AND " + + idfield + "= '" + row[1] + "'"; + + new_child = new GdTreeNode(this, // parent + (string) idbuf, // id + row[0], // label, + new_restriction); + m_children.push_back(new_child); + numchild++; + } + } else { + new_child = new GdTreeNode(this, // parent + "1" , // id + "Artist -> Album -> Title", // label, + "1"); + m_children.push_back(new_child); + new_child = new GdTreeNode(this, // parent + "2" , // id + "Genre -> Artist -> Album -> Track" , // label, + "1"); + m_children.push_back(new_child); + new_child = new GdTreeNode(this, // parent + "3" , // id + "Artist -> Track" , // label, + "1"); + m_children.push_back(new_child); + } + + m_expanded = true; + mgDebug(5, "%d children expanded\n", m_children.size()); + return true; +} + +/*! + ***************************************************************************** + * \brief go over all children recursively to find the tracks + * + ****************************************************************************/ +vector* GdTreeNode::getTracks() +{ + MYSQL_ROW row; + MYSQL_RES *result; + int nrows; + int nfields; + vector* retlist; + int trackid; + + retlist = new vector(); + + // get all tracks satisying the restrictions of this node + mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str()); + + result = mgSqlReadQuery(&m_db, + "SELECT tracks.id FROM tracks, album, genre WHERE %s" + " AND album.cddbid=tracks.sourceid AND genre.id=tracks.genre1", + m_restriction.c_str()); + nrows = mysql_num_rows(result); + nfields = mysql_num_fields(result); + + while((row = mysql_fetch_row(result)) != NULL) + { + // row[0] is the trackid + if(sscanf(row[0], "%d", &trackid) != 1) + { + mgError("Can not extract integer track id from '%s'", + row[0]); + } + retlist->push_back(new mgGdTrack(trackid, m_db)); + } + return retlist; +} + + + + + + + + + diff --git a/gd_content_interface.h b/gd_content_interface.h new file mode 100644 index 0000000..8e63956 --- /dev/null +++ b/gd_content_interface.h @@ -0,0 +1,214 @@ + +/*******************************************************************/ +/*! \file gd_content_interface.h + * \brief Data Objects for content (e.g. mp3 files, movies) + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * Declares main classes of for content items and interfaces to SQL databases + * + * This file defines the following classes + * - mgPlaylist a playlist + * - mgGdTrack a single track (content item). e.g. an mp3 file + * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * + */ +/*******************************************************************/ + +/* makes sure we dont use parse the same declarations twice */ +#ifndef _GD_CONTENT_INTERFACE_H +#define _GD_CONTENT_INTERFACE_H + +#include +#include + +#include + +#include "mg_content_interface.h" + +// non-member function +int GdInitDatabase(MYSQL *db); +std::vector GdGetStoredPlaylists(MYSQL db); + +/*! + ******************************************************************* + * \class mgGdTrack + * + * \brief represents a a single track + * This may be any content item. e.g. a mp3 fileselection + * + * The object is initially created with a database identifier. + * The actual data is only read when a content field is accessed for + * The first time + ********************************************************************/ +class mgGdTrack : public mgContentItem +{ + +private: + MYSQL m_db; + bool m_retrieved; // false if content field values have not yet been + // retrieved from database. This is done on demand + + // content fields + std::string m_artist; + std::string m_title; + std::string m_mp3file; + std::string m_album; + std::string m_genre; + int m_year; + int m_rating; + int m_length; + + bool readData(); + + public: + + + /* constructor */ + mgGdTrack(){ m_uniqID = -1;} // creates invalid item + mgGdTrack(int sqlIdentifier, MYSQL dbase); + mgGdTrack(const mgGdTrack&); + + /* destructor */ + virtual ~mgGdTrack(); + + virtual mgContentItem::contentType getContentType(){return mgContentItem::GD_AUDIO;} + virtual mgMediaPlayer getPlayer() + { + return mgMediaPlayer(); + } + + /* data acess */ + //virtual functions of the base class + virtual std::string getSourceFile(); + virtual std::string getTitle(); + virtual std::string getLabel(int col); + virtual std::string getDescription(); + virtual std::string getGenre(); + virtual int getRating(); + + // additional class-specific functions + std::string getArtist(); + std::string getAlbum(); + int getYear(); + int getDuration(); + std::string getImageFile(); + + /* data manipulation */ + void setTitle(std::string new_title); + void setArtist(std::string new_artist); + void setAlbum(std::string new_album); + void setGenre(std::string new_genre); + void setYear(int new_rating); + void setRating(int new_rating); + + bool writeData(); + static mgGdTrack UNDEFINED; + +}; + +class GdTracklist : public mgTracklist +{ + public: + GdTracklist(MYSQL db_handle, std::string restrictions); +}; + +/*! + ******************************************************************* + * \class GdPlaylist + * + * \brief represents a playlist, i.e. an ordered collection of tracks + ********************************************************************/ +class GdPlaylist : public mgPlaylist +{ + private: + int m_sqlId; /* -1 means: not valid */ + int m_listtype; // used in GiantDisc db queries + std::string m_author; + MYSQL m_db; + + private: + int insertDataFromSQL(); + + public: + + + /*==== constructors ====*/ + GdPlaylist(std::string listname, MYSQL db_handle); + /* opens existing or creates empty playlist */ + + GdPlaylist(unsigned int sql_identifier, MYSQL db_handle); + /* construct from the db by internal id*/ + + + /*==== destructor ====*/ + virtual ~GdPlaylist(); + + + int getPlayTime(); + /* returns the total duration of all songs in the list in seconds */ + + int getPlayTimeRemaining(); + /* returns the duration of all remaining songs in the list in seconds */ + + + bool storePlaylist(); +}; +/*! + ******************************************************************* + * \class mgSelectionTreeNode + * + * \brief hierarchical representation of a set of tracks + * The selection can be based on the whole database or a subset of it. + * Within this selection, the data is organized in a tree hierarchy + * The levels hof the hierarchy can be expanded dynamically by specifying + * the database field for the next expansion step + * In this way, the expnasion scheme (order of the fields) is not static. + * When a node is expanded, a list of children is created. + * Each child inherits the restrictions of its father and an additional + * restriction on the recently expanded db field + ********************************************************************/ +class GdTreeNode : public mgSelectionTreeNode{ + +private: + // everything is in the base class +public: + + /*==== constructors ====*/ + GdTreeNode(MYSQL db, int view, std::string filters); // top level + GdTreeNode(mgSelectionTreeNode* parent, + std::string id, std::string label, std::string restriction); + + /*==== destructor ====*/ + virtual ~GdTreeNode(); + + // compute children on the fly + virtual bool expand(); + + // access data in current node + virtual std::vector* getTracks(); +}; + +#endif /* END _GD_CONTENT_INTERFACE_H */ + + + + + + + + + + + + + + + + + + diff --git a/mg_content_interface.c b/mg_content_interface.c new file mode 100755 index 0000000..5015cf5 --- /dev/null +++ b/mg_content_interface.c @@ -0,0 +1,397 @@ +/*******************************************************************/ +/*! \file content_interface.c + * \brief Data Objects for content (e.g. mp3 files, movies) + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * DUMMY + * Implements main classes of for content items and interfaces to SQL databases + * + * This file implements the following classes + * - GdTracklist a playlist + * - mgGdTrack a single track (content item). e.g. an mp3 file + * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * + */ +/*******************************************************************/ +#define DEBUG + +#include "mg_content_interface.h" +#include "mg_tools.h" + +#define DUMMY + +/* constructor */ +mgContentItem mgContentItem::UNDEFINED = mgContentItem(); + +using namespace std; + +/*! + ***************************************************************************** + * \brief construcor + * + * creates empty tracklist + ****************************************************************************/ +mgTracklist::mgTracklist() +{ + +} + +/*! + ***************************************************************************** + * \brief destrucor + * + * Deletes all items in the tracklist and removes the list itself + ****************************************************************************/ +mgTracklist::~mgTracklist() +{ + mgContentItem* ptr; + vector::iterator iter; + + for(iter = m_list.begin(); iter != m_list.end();iter++) + { + ptr = *iter; + delete ptr; + } + m_list.clear(); +} + +/*! + ***************************************************************************** + * \brief returns a pointer to the list of elements + * + ****************************************************************************/ +vector *mgTracklist::getAll() +{ + return &m_list; +} + +/*! + ***************************************************************************** + * \brief returns the number of elements in the list + * + ****************************************************************************/ +unsigned int mgTracklist::getNumItems() +{ + return m_list.size(); +} + +/*! + ***************************************************************************** + * \brief randomizes the order of the elements in the list + * + ****************************************************************************/ +void mgTracklist::shuffle() +{ + random_shuffle(m_list.begin(),m_list.end ()); +} + +/*! + ***************************************************************************** + * \brief sorts the elements in the list by the nth column + * + ****************************************************************************/ +void mgTracklist::sortBy(int col, bool direction) +{ +} + +/*! + ***************************************************************************** + * \brief stores the ids of columns to be used in label creation + * + * The list can create a label with different fields (columns) using the + * function getLabel() + * This function defines the fields of the contentItems to be used + * in the label and their order + ****************************************************************************/ +void mgTracklist::setDisplayColumns(vector cols) +{ + + m_columns = cols; +} + +/*! + ***************************************************************************** + * \brief returns the number of dsplay coulmns + * + ****************************************************************************/ +unsigned int mgTracklist::getNumColumns() +{ + return m_columns.size(); +} + + +/*! + ***************************************************************************** + * \brief creates the label string for an item + * + * The list can create a label with different fields (columns). + * The fields used in the list and their order is set by the function + using the function setDisplayColumns + * function getLabel(). + * This function creates a string from these columns, separated by the string + * 'separator' + * in the label and their order + ****************************************************************************/ +string mgTracklist::getLabel(unsigned int position, const string separator) +{ + string label =""; + mgContentItem* item; + + if(position >= m_list.size()) + return ""; + + else + { + item = *(m_list.begin()+position); + } + + + for( vector::iterator iter = m_columns.begin(); + iter != m_columns.end(); iter++ ) + { + if( iter != m_columns.begin() ) + { + label += separator; + } + label += item->getLabel(*iter); + } + return label; +} + + +/*! + ***************************************************************************** + * \brief returns an item from the list at the specified position + * + ****************************************************************************/ +mgContentItem* mgTracklist::getItem(unsigned int position) +{ + if(position >= m_list.size()) + return &(mgContentItem::UNDEFINED); //invalid + return *(m_list.begin()+position); +} + +/*! + ***************************************************************************** + * \brief remove item at position + * + ****************************************************************************/ +bool mgTracklist::remove(unsigned int position) +{ + if(position >= m_list.size()) return false; + vector::iterator iter; + + iter = m_list.begin()+ position; + m_list.erase(iter); + return true; +} + +/*! + ***************************************************************************** + * \brief remove all occurences of item + * + ****************************************************************************/ +int mgTracklist::remove(mgContentItem* item) +{ + int retval = 0; + vector::iterator iter; + for(iter=m_list.begin(); iter != m_list.end (); iter++) + { + if(*iter == item) + { + m_list.erase(iter); + retval++; + break; + } + } + return retval; +} + + +/*=================================================================*/ +/* */ +/* class mgPlaylist */ +/* */ +/*=================================================================*/ +mgPlaylist::mgPlaylist() +{ +} +mgPlaylist::mgPlaylist(string listname) +{ + m_listname = listname; +} + + /*==== destructor ====*/ +mgPlaylist::~mgPlaylist() +{ + +} +/*==== add/ remove tracks ====*/ + +/* adds a song at the end of the playlist */ +void mgPlaylist::append(mgContentItem* item) +{ + m_list.push_back(item); +} + +void mgPlaylist::appendList(vector *tracks) +{ + vector::iterator iter; + mgDebug(3, "Adding %d tracks to the playlist",tracks->size()); + for(iter = tracks->begin(); iter != tracks->end(); iter++) + { + m_list.push_back(*iter); + } + tracks->clear(); +} + + +/* adds a song after 'position' */ +void mgPlaylist::insert(mgContentItem* item, unsigned int position) +{ + if(position >= m_list.size()) + m_list.push_back(item); + else + m_list.insert(m_list.begin()+position, item); +} + + + +/*==== access tracks ====*/ +string mgPlaylist::getListname() { return m_listname; } +void mgPlaylist::setListname(string name){ m_listname = name;} + + + +// returns the first item of the list +mgContentItem* mgPlaylist::getFirst() +{ + m_current = m_list.begin(); + return *m_current; +} + +// returns the nth track from the playlist +mgContentItem* mgPlaylist::getPosition(unsigned int position) +{ + if(position >= m_list.size()) + return &(mgContentItem::UNDEFINED); //invalid + m_current = m_list.begin()+position; + return *m_current; +} + +// proceeds to the next item +mgContentItem* mgPlaylist::skipFwd() +{ + if(m_current+1 == m_list.end()) + return &(mgContentItem::UNDEFINED); //invalid + else + return * (++m_current); +} + +// goes back to the previous item +mgContentItem* mgPlaylist::skipBack() +{ + if(m_current == m_list.begin()) + return &(mgContentItem::UNDEFINED); //invalid + else + return * (--m_current); +} + +// get next track, do not update data structures +mgContentItem* mgPlaylist::sneakNext() +{ + if(m_current+1 == m_list.end()) + return &(mgContentItem::UNDEFINED); //invalid + else + return * (m_current+1); +} + + +/*=================================================================*/ +/* */ +/* class mgSelectionTreeNode */ +/* */ +/*=================================================================*/ +mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view) +{ + m_db = db; + m_parent=NULL; + m_level = 0; + m_view = view; + m_id = ""; + m_label = "ROOT"; + m_expanded = false; +} +mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, string id, string label) +{ + m_parent = parent; + m_level = m_parent->m_level+1; + m_view = m_parent->m_view; + m_db = m_parent->m_db; + m_id = id; + m_label = label; + m_expanded = false; +} + + /*==== destructor ====*/ +mgSelectionTreeNode::~mgSelectionTreeNode() +{ + collapse(); +// _children.clear(); +} + +mgSelectionTreeNode* mgSelectionTreeNode::getParent() +{ + return m_parent; +} + +void mgSelectionTreeNode::collapse() // removes all children (recursively) +{ + vector ::iterator iter; + mgSelectionTreeNode* ptr; + + for(iter = m_children.begin(); iter != m_children.end();iter++) + { + ptr = *iter; + delete ptr; + } + m_expanded = false; + m_children.clear(); +} +// access children +vector &mgSelectionTreeNode::getChildren() +{ + mgDebug(5," returning %d children", m_children.size()); + return m_children; +} + +// access data in current node +string mgSelectionTreeNode::getID() +{ + return m_id; +} +string mgSelectionTreeNode::getLabel() +{ + return m_label; +} +string mgSelectionTreeNode::getLabel(int n) +{ + mgSelectionTreeNode* node = this; + int d = m_level; + while(n < d) + { + node = node->m_parent; + d--; + } + return node->m_label; +} + +string mgSelectionTreeNode::getRestrictions() +{ + return m_restriction; +} diff --git a/mg_content_interface.h b/mg_content_interface.h new file mode 100755 index 0000000..d39e5d0 --- /dev/null +++ b/mg_content_interface.h @@ -0,0 +1,261 @@ +/*******************************************************************/ +/*! \file content_interface.h + * \brief Data Objects for content (e.g. mp3 files, movies) + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * Declares main classes of for content items and interfaces to SQL databases + * + * This file defines the following classes + * - mgPlaylist a playlist + * - mgTrack a single track (content item). e.g. an mp3 file + * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * + */ +/*******************************************************************/ + +/* makes sure we dont parse the same declarations twice */ +#ifndef _CONTENT_INTERFACE_H +#define _CONTENT_INTERFACE_H + +#include +#include + +#include + +#define ILLEGAL_ID -1 + +/*! + ******************************************************************* + * \class mgMediaPlayer + * + * \brief dummy player class + ********************************************************************/ +class mgMediaPlayer +{ + public: + mgMediaPlayer(){;} + ~mgMediaPlayer(){;} + +}; + +/*! + ******************************************************************* + * \class mgContentItem + * + * \brief Generic class that represents a single content item. + * This is the parent class from which classes like mgGdTrack are derived + ********************************************************************/ +class mgContentItem +{ + + public: + typedef enum contentType{ + ABSTRACT =0, + GD_AUDIO, + EPG + }contentType; + + protected: + int m_uniqID; // internal identifier to uniquely identify a content item; + + public: + /* constructor */ + mgContentItem(){ m_uniqID = -1;} + mgContentItem(int id){m_uniqID = id;} + mgContentItem(const mgContentItem& org){m_uniqID = org.m_uniqID;} + /* destructor */ + virtual ~mgContentItem(){}; + + /* data acess */ + int getId(){ return m_uniqID;} + + // what type of content are we looking at (e.g. audio, video, epg) + virtual contentType getContentType(){return ABSTRACT;} + + // return (global?) object that is used to play the content items + virtual mgMediaPlayer getPlayer(){return mgMediaPlayer();} + + + // get the "file" (or URL) that is passed to the player + virtual std::string getSourceFile(){return "";} + + // ============ data access ================= + virtual std::string getTitle(){return "";} // return the title + virtual std::string getLabel(int col){return "";} // return the title + + virtual std::string getDescription()// return a short textual description + {return "";} + virtual std::string getGenre(){return "";} + virtual int getRating() + { + return 0; + } + + virtual bool operator == (mgContentItem o){return m_uniqID == o.m_uniqID;} + + // check, if usable + virtual bool isValid() {return (m_uniqID >=0);} + static mgContentItem UNDEFINED; +}; + + +class mgTracklist +{ + protected: + std::vector m_list; + std::vector m_columns; + int sorting; + + public: + mgTracklist(); // creates empty tracklist; + + virtual ~mgTracklist(); + + std::vector *getAll(); + unsigned int getNumItems(); + + virtual void shuffle(); + virtual void sortBy(int col, bool direction); + + void setDisplayColumns(std::vector cols); + unsigned int getNumColumns(); + virtual std::string getLabel(unsigned int position, const std::string separator); + + virtual mgContentItem* mgTracklist::getItem(unsigned int position); + + virtual int remove(mgContentItem* item); // remove all occurences of item + virtual bool remove(unsigned int position); // remove item at position +}; + +/*! + ******************************************************************* + * \class mgPlaylist + * + * Represents a generic playlist, i.e. an ordered collection of tracks + * The derived classes take care of specifics of certain media types + ********************************************************************/ +class mgPlaylist : public mgTracklist +{ + protected: + std::string m_listname; + std::vector::iterator m_current; + public: + + /*==== constructors ====*/ + mgPlaylist(); + mgPlaylist(std::string listname); + + /*==== destructor ====*/ + virtual ~mgPlaylist(); + + /*==== add/ remove tracks ====*/ + + /* adds a song at the end of the playlist */ + virtual void append(mgContentItem* item); + virtual void appendList(std::vector *tracks); + /* adds a song after 'position' */ + virtual void insert(mgContentItem* item, unsigned int position); + + /*==== access tracks ====*/ + std::string getListname() ; + void setListname(std::string name); + + // returns the first item of the list + virtual mgContentItem* getFirst(); + + // returns the nth track from the playlist + virtual mgContentItem* getPosition(unsigned int position); + + // proceeds to the next item + virtual mgContentItem* skipFwd(); + + // goes back to the previous item + virtual mgContentItem* skipBack(); + + virtual mgContentItem* sneakNext(); + +}; + +class mgSelectionTreeNode +{ + +protected: + MYSQL m_db; + bool m_expanded; // already expanded ? + std::string m_restriction; // list of active restrictions at this level + std::string m_id; // ID of the node, used for further expand + int m_level; // depth of tree (0 = root) + int m_view; + std::string m_label; + +// std::vector _labels; // Labels used for interaction with the user + // about this node + +// vector _children; // if expanded the links to the + // children are stopred here + mgSelectionTreeNode* m_parent; + std::vector m_children; + +public: + + /*==== constructors ====*/ + mgSelectionTreeNode(MYSQL db, int view); + + mgSelectionTreeNode(mgSelectionTreeNode* parent, std::string id, std::string label); + + /*==== destructor ====*/ + virtual ~mgSelectionTreeNode(); + + // compute children on the fly + virtual bool expand()=0; + virtual void collapse(); // removes all children (recursively) + + mgSelectionTreeNode* getParent(); + + // access children + virtual std::vector &getChildren(); + + // access data in current node + bool isExpanded(){return m_expanded;} + int getLevel(){return m_level;} // for debugging + std::string getID(); + virtual std::string getRestrictions(); + + std::string getLabel(); + virtual std::string getLabel(int n); + #if 0 + virtual std::string viewTitle(int level)=0; + virtual std::vector viewChoices(int level, int choice); +#endif + + // returns all tracks below this node + // Note: This function allocates memory for the vector and for all elements of the vector + // The calling function is in charge of releasing this memory + virtual std::vector* getTracks()=0; + +}; + +#endif /* END _CONTENT_INTERFACE_H */ + + + + + + + + + + + + + + + + + + diff --git a/mg_database.c b/mg_database.c new file mode 100644 index 0000000..9a85cb8 --- /dev/null +++ b/mg_database.c @@ -0,0 +1,35 @@ +/*******************************************************************/ +/*! \file mg_database.c + * \brief A capsule around MySql database access + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + +#include "mg_database.h" + +using namespace std; + +mgDB::mgDB() +{ +} + +mgDB::mgDB(string user, string pass) +{ +} + +mgDB::~mgDB() +{ +} + +MYSQL mgDB::getDBHandle() +{ + MYSQL m; + + return m; +} + + diff --git a/mg_database.h b/mg_database.h new file mode 100644 index 0000000..b979df4 --- /dev/null +++ b/mg_database.h @@ -0,0 +1,34 @@ +/*******************************************************************/ +/*! \file mg_database.h + * \brief A capsule around MySql database access + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + + +#ifndef __MG_DATABASE_H +#define __MG_DATABASE_H + +#include + +#include + +class mgDB +{ + public: + + mgDB( ); + mgDB( std::string user, std::string pass ); + ~mgDB(); + + MYSQL getDBHandle(); + + private: + // MYSQL m_dbase; +}; + +#endif diff --git a/mg_media.c b/mg_media.c new file mode 100644 index 0000000..26d8b8f --- /dev/null +++ b/mg_media.c @@ -0,0 +1,272 @@ +/*******************************************************************/ +/*! \file mg_media.c + * \brief Top level access to media in vdr plugin muggle + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * + */ +/*******************************************************************/ + +/* makes sure we dont parse the same declarations twice */ +#include "mg_media.h" +#include "mg_tools.h" +#include "mg_content_interface.h" +#include "gd_content_interface.h" + +#include "sh_dummy_content.h" + +using namespace std; + +mgFilters::mgFilters() +{ +} + +mgFilters::~mgFilters() +{ +} + +int mgFilters::getNumFilters() +{ + return 0; +} + +string mgFilters::getName(int filter) +{ + return 0; +} + +int mgFilters::getValue(int filter) +{ + return 0; +} + +mgFilters::filterType mgFilters::getType(int filter) +{ + return NUMBER; +} + +// for NUMBER filters + +int mgFilters::getMin(int filter) +{ + return 0; +} + +int mgFilters::getMax(int filter) +{ + return 0; +} + +// for CHOICE + +vector mgFilters::getChoices() +{ + return vector(); +} + +int mgFilters::getCurrent(int filter) +{ + return 0; +} + + // check, if a value is correct +bool mgFilters::checkValue(int filter, string value) +{ + return false; +} + +bool mgFilters::checkValue(int filter, int value) +{ + return false; +} + +// finally set the values +bool mgFilters::setValue(int filter, string value) +{ + return false; +} + +bool mgFilters::setValue(int filter, int value) +{ + return false; +} + +/*! + ******************************************************************* + * \class mgMedia + * + * \brief mein class to access content in the vdr plugin muggle + ********************************************************************/ +mgMedia::mgMedia(contentType mediatype) +{ + int errval = 0; + + m_mediatype = mediatype; + m_filter = "1"; + m_defaultView = 1; + + // now initialize the database + mgDebug(1, "Media Type %sselected", getMediaTypeName().c_str()); + switch(m_mediatype) + { + case DUMMY: + { + errval = DummyInitDatabase(&m_db); + break; + } + case GD_MP3: + { + errval = GdInitDatabase(&m_db); + mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); + } + } + if(errval < 0) + { + mgError("Error connecting to database\n"); + } + +} + +mgMedia::~mgMedia() +{ +} + +string mgMedia::getMediaTypeName() +{ + switch(m_mediatype) + { + case DUMMY: + return "DUMMY"; + case GD_MP3: + return "GiantDisc-mp3"; + } + mgError("implementation Error"); // we should never get here + return ""; +} + + +mgSelectionTreeNode* mgMedia::getSelectionRoot() +{ + switch(m_mediatype) + { + case DUMMY: + return new DummyTreeNode(m_db, m_defaultView); + case GD_MP3: + return new GdTreeNode(m_db, m_defaultView, m_filter); + } + mgError("implementation Error"); // we should never get here + return NULL; +} + +mgFilters mgMedia::getActiveFilters() +{ + switch(m_mediatype) + { + case DUMMY: + return mgFilters(); + case GD_MP3: + return mgFilters(); + } + mgError("implementation Error"); // we should never get here + return mgFilters(); +} + +void mgMedia::setFilters(mgFilters filters) +{ +} + +mgPlaylist* mgMedia::createTemporaryPlaylist() +{ + string tmpname = "current"; + return loadPlaylist(tmpname); +} + +mgPlaylist* mgMedia::loadPlaylist(string name) +{ + mgPlaylist *list; + switch(m_mediatype) + { + case DUMMY: + list = new DummyPlaylist(name, m_db); + list->setDisplayColumns(getDefaultCols()); + return list; + case GD_MP3: + list = new GdPlaylist(name, m_db); + list->setDisplayColumns(getDefaultCols()); + return list; + } + mgError("implementation Error"); // we should never get here + return NULL; +} + +vector mgMedia::getStoredPlaylists() +{ + switch(m_mediatype) + { + case DUMMY: + return DummyGetStoredPlaylists(m_db); + case GD_MP3: + return GdGetStoredPlaylists(m_db); + } + mgError("implementation Error"); // we should never get here + return vector(); +} + +vector mgMedia::getDefaultCols() +{ + vector cols; + switch(m_mediatype) + { + case DUMMY: + cols.push_back(1); // artist + cols.push_back(0); // track + return cols; + case GD_MP3: + cols.push_back(1); // artist + cols.push_back(0); // track + return cols; + } + mgError("implementation Error"); // we should never get here + + return cols; +} + +mgTracklist* mgMedia::getTracks() +{ + mgTracklist *tracks; + switch(m_mediatype) + { + case DUMMY: + + tracks = new DummyTracklist(m_db, m_filter); + tracks->setDisplayColumns(getDefaultCols()); + return tracks; + case GD_MP3: + tracks = new GdTracklist(m_db, m_filter); + tracks->setDisplayColumns(getDefaultCols()); + return tracks; + } + mgError("implementation Error"); // we should never get here + + return NULL; +} + + + + + + + + + + + + + + + diff --git a/mg_media.h b/mg_media.h new file mode 100644 index 0000000..f4cd64f --- /dev/null +++ b/mg_media.h @@ -0,0 +1,137 @@ +/*******************************************************************/ +/*! \file mgmedia.h + * \brief Top level access to media in vdr plugin muggle + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * + */ +/*******************************************************************/ +/* makes sur we dont use parse the same declarations twice */ +#ifndef _MG_MEDIA_H +#define _MG_MEDIA_H + +#include +#include + +#include + +class mgPlaylist; +class mgTracklist; +class mgSelectionTreeNode; + +/*! + ******************************************************************* + * \class mgFilters + * + * \brief stores a set of search constraints to the media database + ********************************************************************/ +class mgFilters +{ + typedef enum filterType + { + NUMBER, // integer number (with upper and lower limits) + STRING, // any string + REGEXP_STRING, // string containing wildcard symbol '*' + CHOICE // value fro ma list of choices + }filterType; + + public: + std::vector fields; + std::vector values; + + mgFilters(); + ~mgFilters(); + + int getNumFilters(); + std::string getName(int filter); + int getValue(int filter); + filterType getType(int filter); + + // for NUMBER filters + int getMin(int filter); + int getMax(int filter); + + // for CHOICE + std::vector getChoices(); + int getCurrent(int filter); + + // check, if a value is correct + bool checkValue(int filter, std::string value); + bool checkValue(int filter, int value); + + // finally set the values + + bool setValue(int filter, std::string value); + bool setValue(int filter, int value); +}; + + +/*! + ******************************************************************* + * \class mgMedia + * + * \brief main class to access content in the vdr plugin muggle + ********************************************************************/ +class mgMedia +{ + + public: + typedef enum contentType{ + DUMMY =0, + GD_MP3 + } contentType; + + private: + MYSQL m_db; + contentType m_mediatype; + std::string m_filter; + int m_defaultView; + + public: + mgMedia(contentType mediatype); + ~mgMedia(); + + std::string getMediaTypeName(); + + mgSelectionTreeNode* getSelectionRoot(); + + mgFilters getActiveFilters(); + + void setFilters(mgFilters filters); + void setFilters(std::string sql) + { + m_filter=sql; + } + + // playlist management + mgPlaylist* createTemporaryPlaylist(); + mgPlaylist* loadPlaylist( std::string name ); + std::vector getStoredPlaylists(); + + std::vector getDefaultCols(); + mgTracklist* getTracks(); +}; +#endif /* END _CONTENT_INTERFACE_H */ + + + + + + + + + + + + + + + + + + diff --git a/mg_tools.c b/mg_tools.c new file mode 100644 index 0000000..b540a4e --- /dev/null +++ b/mg_tools.c @@ -0,0 +1,140 @@ +/*******************************************************************/ +/*! \file muggle_tools.cpp + * \brief A few util functions for standalone and plugin messaging + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + */ +/*******************************************************************/ + +#include "mg_tools.h" + +extern "C" +{ + #include + #include +} +#include + + +#define MAX_BUFLEN 1024 +#define MAX_QUERY_BUFLEN 1024 +static char buffer[MAX_BUFLEN]; +static char querybuf[MAX_QUERY_BUFLEN]; + +static int DEBUG_LEVEL=3; + +void mgSetDebugLevel(int new_level) +{ + DEBUG_LEVEL = new_level; +} + +void mgDebug(int level, const char *fmt, ...) +{ + + va_list ap; + if(level <= DEBUG_LEVEL) + { + va_start(ap, fmt); + + vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); + if(STANDALONE) + { + fprintf(stderr, "dbg %d: %s\n", level, buffer); + } + else + { +#if !STANDALONE + isyslog( "%s\n", buffer); +#endif + } + } + va_end(ap); +} + +void mgDebug( const char *fmt, ... ) +{ + va_list ap; + va_start( ap, fmt ); + mgDebug( 1, fmt, ap ); +} + + +void mgWarning(const char *fmt, ...) +{ + + va_list ap; + + va_start(ap, fmt); + + vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); + if(STANDALONE) + { + fprintf(stderr, "warning: %s\n", buffer); + } + else + { +#if !STANDALONE + isyslog( "Warning: %s\n", buffer); +#endif + + } + + va_end(ap); +} + +void mgError(const char *fmt, ...) +{ + + va_list ap; + + va_start(ap, fmt); + + vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); + if(STANDALONE) + { + fprintf(stderr, "Error: %s\n", buffer); + exit(1); + } + else + { +#if !STANDALONE + isyslog( "Error in Muggle: %s\n", buffer); +#endif + } + + va_end(ap); +} + +MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap); + mgDebug(9, "SQL-Query: '%s'",querybuf); + if(mysql_query(db,querybuf)) + { + mgError("SQL error in MUGGLE\n%s\n", querybuf); + } + return mysql_store_result(db); +} + +void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap); + if(mysql_query(db,querybuf)) + { + mgError("SQL error in MUGGLE\n%s\n", querybuf); + } +} + diff --git a/mg_tools.h b/mg_tools.h new file mode 100644 index 0000000..68c8104 --- /dev/null +++ b/mg_tools.h @@ -0,0 +1,31 @@ +/*******************************************************************/ +/*! \file muggle_tools.h + * \brief A few util functions for standalone and plugin messaging + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + */ +/*******************************************************************/ +/* makes sur we dont use parse the same declarations twice */ +#ifndef _MUGGLE_TOOLS_H +#define _MUGGLE_TOOLS_H + +#include "mysql/mysql.h" + +#define STANDALONE 1 + +void mgSetDebugLevel(int new_level); +void mgDebug(int level, const char *fmt, ...); +void mgDebug( const char *fmt, ... ); +void mgWarning(const char *fmt, ...); +void mgError(const char *fmt, ...); + + +MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...); +void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...); + +#endif diff --git a/muggle.c b/muggle.c new file mode 100644 index 0000000..481c699 --- /dev/null +++ b/muggle.c @@ -0,0 +1,98 @@ +/*******************************************************************/ +/*! \file muggle.c + * \brief Implements a plugin for browsing media libraries within VDR + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + +static const char *VERSION = "0.0.1"; +static const char *DESCRIPTION = "Access GiantDisc database contents"; +static const char *MAINMENUENTRY = "Muggle"; + +#include "muggle.h" +#include "vdr_menu.h" +#include "mg_tools.h" + +const char* mgMuggle::Version(void) +{ + return VERSION; +} + +const char* mgMuggle::Description(void) +{ + return DESCRIPTION; +} + +const char* mgMuggle::MainMenuEntry(void) +{ + return MAINMENUENTRY; +} + +mgMuggle::mgMuggle(void) +{ + // Initialize any member variables here. + // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL + // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! +} + +mgMuggle::~mgMuggle() +{ + // Clean up after yourself! +} + +const char *mgMuggle::CommandLineHelp(void) +{ + // Return a string that describes all known command line options. + return NULL; +} + +bool mgMuggle::ProcessArgs(int argc, char *argv[]) +{ + // Implement command line argument processing here if applicable. + return true; +} + +bool mgMuggle::Initialize(void) +{ + // Initialize any background activities the plugin shall perform. + return true; +} + +bool mgMuggle::Start(void) +{ + // Start any background activities the plugin shall perform. + mgSetDebugLevel( 99 ); + + return true; +} + +void mgMuggle::Housekeeping(void) +{ + // Perform any cleanup or other regular tasks. +} + +cOsdObject *mgMuggle::MainMenuAction(void) +{ + // Perform the action when selected from the main VDR menu. + cOsdObject* osd = new mgMainMenu(); + + return osd; +} + +cMenuSetupPage *mgMuggle::SetupMenu(void) +{ + // Return a setup menu in case the plugin supports one. + return NULL; +} + +bool mgMuggle::SetupParse(const char *Name, const char *Value) +{ + // Parse your own setup parameters and store their values. + return false; +} + +VDRPLUGINCREATOR(mgMuggle); // Don't touch this! diff --git a/muggle.h b/muggle.h new file mode 100644 index 0000000..e6ed1c2 --- /dev/null +++ b/muggle.h @@ -0,0 +1,51 @@ +/*******************************************************************/ +/*! \file muggle.h + * \brief Implements a plugin for browsing media libraries within VDR + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + +#ifndef _MUGGLE_H +#define _MUGGLE_H + +#include + +class mgMuggle : public cPlugin +{ +public: + + mgMuggle(void); + + virtual ~mgMuggle(); + + virtual const char *Version(void); + + virtual const char *Description(void); + + virtual const char *CommandLineHelp(void); + + virtual bool ProcessArgs(int argc, char *argv[]); + + virtual bool Initialize(void); + + virtual bool Start(void); + + virtual void Housekeeping(void); + + virtual const char *MainMenuEntry(void); + + virtual cOsdObject *MainMenuAction(void); + + virtual cMenuSetupPage *SetupMenu(void); + + virtual bool SetupParse(const char *Name, const char *Value); + +private: + +}; + +#endif diff --git a/sh_console_osd.c b/sh_console_osd.c new file mode 100644 index 0000000..ebb4702 --- /dev/null +++ b/sh_console_osd.c @@ -0,0 +1,212 @@ +/* + * osd.c: Abstract On Screen Display layer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sh_console_osd.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ + */ + +#include "myosd.h" +#include +//#include "device.h" +// #include "i18n.h" +//#include "status.h" + +// --- cOsdItem -------------------------------------------------------------- + +cOsdItem::cOsdItem(eOSState State) +{ + text = NULL; +} + +cOsdItem::cOsdItem(const char *Text, eOSState State) +{ + text = NULL; + SetText(Text,true); +} + +cOsdItem::~cOsdItem() +{ + free(text); +} + +void cOsdItem::SetText(const char *Text, bool Copy) +{ + free(text); + text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership! +} + +const char* cOsdItem::Get() +{ + return text; +} +void cOsdItem::Display() +{ + printf("%s\n", text); +} + +// --- cOsdMenu -------------------------------------------------------------- + +cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) +{ + cols[0] = c0; + cols[1] = c1; + cols[2] = c2; + cols[3] = c3; + cols[4] = c4; + m_first = 0; + m_current = m_marked = -1; + subMenu = NULL; + helpRed = helpGreen = helpYellow = helpBlue = NULL; + status = NULL; +} + +cOsdMenu::~cOsdMenu() +{ + free(title); + delete subMenu; + free(status); +} + + +void cOsdMenu::SetTitle(const char *Title, bool ShowDate) +{ + title = strdup(Title); +} + +void cOsdMenu::Add(cOsdItem *Item) +{ + m_display.push_back(Item); +} + +void cOsdMenu::Display(void) +{ + //Interface->Clear(); + printf("\n\n\n\n"); + printf("----------(start %d, current %d)--------------------\n", + m_first, m_current); + //Interface->SetCols(cols); + //Interface->Title(title); + printf(" Title: %s\n", title); + //Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); + for(int i= m_first; i < (int) m_display.size() && i < m_first+DISPLAY_SIZE; i++) + { + if(i == m_current) + { + printf("==>"); + + } + else + { + printf(" "); + + } + printf("%s\n", m_display[i]->Get()); + } + printf("----------------------------------------------\n"); + if(hasHotkeys) + { + printf("%15s (r) %15s (g) %15s (y) %15s (b)\n", + helpRed? helpRed :"" , + helpGreen? helpGreen :"" , + helpYellow? helpYellow :"" , + helpBlue? helpBlue :"" ); + + } +} + +int cOsdMenu::Index(cOsdItem *Item) +{ + int pos=0; + for(vector::iterator iter = m_display.begin(); + iter != m_display.end(); iter++) + { + if((*iter) == Item) + { + break; + } + pos++; + } + return pos; +} +void cOsdMenu::SetCurrent(cOsdItem *Item) +{ + m_current = Item ? Index(Item) : -1; +} +void cOsdMenu::CursorUp(void) +{ + if(m_current >0) m_current--; + if(m_current < m_first) m_first = m_first - PAGE_JUMP; + Display(); +} +void cOsdMenu::CursorDown(void) +{ + if(m_current < (int)m_display.size()-1) + m_current++; + if(m_current >= m_first+DISPLAY_SIZE) m_first =m_first + PAGE_JUMP; + Display(); +} + +void cOsdMenu::PageUp(void) +{ + if(m_first >= PAGE_JUMP) + { + m_first -= PAGE_JUMP; + m_current -= PAGE_JUMP; + } + Display(); + +} +void cOsdMenu::PageDown(void) +{ + if(m_first+ PAGE_JUMP< (int)m_display.size()) + { + m_first += PAGE_JUMP; + m_current += PAGE_JUMP; + if(m_current < m_first) m_current = m_first; + if(m_current >= (int) m_display.size()) + m_current = (int) m_display.size(); + } + Display(); +} + +void cOsdMenu::Clear(void) +{ + m_first = 0; + m_current = m_marked = -1; + hasHotkeys = false; + for(vector::iterator iter = m_display.begin(); + iter != m_display.end(); iter++) + { + delete *iter; + } + m_display.clear(); +} + +eOSState cOsdMenu::ProcessKey(eKeys Key) +{ + + switch (Key) { + case kUp|k_Repeat: + case kUp: CursorUp(); break; + case kDown|k_Repeat: + case kDown: CursorDown(); break; + case kLeft|k_Repeat: + case kLeft: PageUp(); break; + case kRight|k_Repeat: + case kRight: PageDown(); break; + case kBack: return osBack; + default: if (m_marked < 0) + return osUnknown; + } + return osContinue; +} + +void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + helpRed = Red; + helpGreen = Green; + helpYellow = Yellow; + helpBlue = Blue; +} diff --git a/sh_console_osd.h b/sh_console_osd.h new file mode 100644 index 0000000..4a4e413 --- /dev/null +++ b/sh_console_osd.h @@ -0,0 +1,191 @@ +/* + * osd.h: Abstract On Screen Display layer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sh_console_osd.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ + */ +#ifndef __OSD_H +#define __OSD_H +#define DEBUG_OSD +#if defined(DEBUG_OSD) +#include +#include +#include +#endif +//#include "config.h" +// #include "interface.h" +//#include "osdbase.h" +//#include "tools.h" +#define MaxCols 5 +#define Setup_OSDheight 12 +#define Setup_OSDwidth 40 +#define Setup_Symbol false +#define Setup_MenuScrollPage true +#define MAXOSDITEMS (Setup.OSDheight - 4) +enum eDvbColor { + clrBackground, + clrTransparent = clrBackground, + clrBlack = clrBackground, + clrRed, + clrGreen, + clrYellow, + clrBlue, + clrMagenta, + clrCyan, + clrWhite, + }; + +extern int bgbackground, bgchannelname, bgepgtime, bgtitleline, bgscrolline, + bghelpred, bghelpgreen, bghelpyellow, bghelpblue; + +#define clrBackground ((eDvbColor)bgbackground) +#define clrChannelName ((eDvbColor)bgchannelname) +#define clrEpgTime ((eDvbColor)bgepgtime) +#define clrTitleLine ((eDvbColor)bgtitleline) +#define clrScrolLine ((eDvbColor)bgscrolline) +#define clrHelpRed ((eDvbColor)bghelpred) +#define clrHelpGreen ((eDvbColor)bghelpgreen) +#define clrHelpYellow ((eDvbColor)bghelpyellow) +#define clrHelpBlue ((eDvbColor)bghelpblue) + +#define DISPLAY_SIZE 12 +#define PAGE_JUMP 11 + +extern int fgchannelname, fgdatetime, fgepgtime, fgtitle, fgsubtitle, fgtitleline, fgscrolline, + fgmenufont, volumebar, timebar1, timebar2, fgsymbolon, fgsymboloff; + +enum eOSState { osUnknown, + osContinue, + osSchedule, + osChannels, + osDirector, + osTimers, + osRecordings, + osPlugin, + osSetup, + osCommands, + osPause, + osRecord, + osReplay, + osStopRecord, + osStopReplay, + osCancelEdit, + osSwitchDvb, + osBack, + osEnd, + os_User, // the following values can be used locally + osUser1, + osUser2, + osUser3, + osUser4, + osUser5, + osUser6, + osUser7, + osUser8, + osUser9, + osUser10, + osUser11, + }; +enum eDvbFont { + fontOsd, + fontOsd2, + fontFix, + fontSym, +/* TODO as soon as we have the font files... + fontTtxSmall, + fontTtxLarge, +*/ + }; +enum eKeys { // "Up" and "Down" must be the first two keys! + kUp, + kDown, + kMenu, + kOk, + kBack, + kLeft, + kRight, + kRed, + kGreen, + kYellow, + kBlue, + k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, + kPlay, + kPause, + kStop, + kRecord, + kFastFwd, + kFastRew, + kPower, + kChanUp, + kChanDn, + kVolUp, + kVolDn, + kMute, + kSchedule, + kChannels, + kTimers, + kRecordings, + kDirector, + kSetup, + kCommands, + kUser1, kUser2, kUser3, kUser4, kUser5, kUser6, kUser7, kUser8, kUser9, + kNone, + kKbd, + // The following codes are used internally: + k_Plugin, + k_Setup, + // The following flags are OR'd with the above codes: + k_Repeat = 0x8000, + k_Release = 0x4000, + k_Flags = k_Repeat | k_Release, + }; + +class cOsdItem { +private: + char *text; + public: + cOsdItem(eOSState State = osUnknown); + cOsdItem(const char *Text, eOSState State = osUnknown); + void SetText(const char *Text, bool Copy); + virtual ~cOsdItem(); + int Index(); + const char* Get(); + void Display(); +}; + +class cOsdMenu { +private: + char *title; + int cols[MaxCols]; + int m_first, m_current, m_marked; + cOsdMenu *subMenu; + const char *helpRed, *helpGreen, *helpYellow, *helpBlue; + char *status; + int digit; + bool hasHotkeys; + vector m_display; +protected: + bool visible; + void CursorUp(void); + void CursorDown(void); + void PageUp(void); + void PageDown(void); + int Index(cOsdItem *Item); +public: + cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); + virtual ~cOsdMenu(); + void SetTitle(const char *Title, bool ShowDate=false); + int Current(void) { return m_current; } + void Add(cOsdItem *Item); +// void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL); + virtual void Display(void); + virtual void SetCurrent(cOsdItem *Item); + virtual eOSState ProcessKey(eKeys Key); + virtual void Clear(void); + virtual void SetHasHotkeys(){ hasHotkeys = true; } + virtual void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); +}; + +#endif //__OSD_H diff --git a/sh_console_osd_menuitems.c b/sh_console_osd_menuitems.c new file mode 100644 index 0000000..48b5d3a --- /dev/null +++ b/sh_console_osd_menuitems.c @@ -0,0 +1,530 @@ +/* + * menuitems.c: General purpose menu items + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sh_console_osd_menuitems.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ + */ + +#include "mymenuitems.h" + +const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~"; + +// --- cMenuEditItem --------------------------------------------------------- + +cMenuEditItem::cMenuEditItem(const char *Name) +{ + name = strdup(Name); + value = NULL; +} + +cMenuEditItem::~cMenuEditItem() +{ + free(name); + free(value); +} + +void cMenuEditItem::SetValue(const char *Value) +{ + free(value); + value = strdup(Value); + char *buffer = NULL; + asprintf(&buffer, "%s:\t%s", name, value); + SetText(buffer, false); + Display(); +} + +// --- cMenuEditIntItem ------------------------------------------------------ + +cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max) +:cMenuEditItem(Name) +{ + value = Value; + min = Min; + max = Max; + Set(); +} + +void cMenuEditIntItem::Set(void) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%d", *value); + SetValue(buf); +} + +eOSState cMenuEditIntItem::ProcessKey(eKeys Key) +{ + return osContinue; +} + +// --- cMenuEditBoolItem ----------------------------------------------------- + +cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString) +:cMenuEditIntItem(Name, Value, 0, 1) +{ + falseString = FalseString ? FalseString : "no"; + trueString = TrueString ? TrueString : "yes"; + Set(); +} + +void cMenuEditBoolItem::Set(void) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString); + SetValue(buf); +} + +// --- cMenuEditNumItem ------------------------------------------------------ + +cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind) +:cMenuEditItem(Name) +{ + value = Value; + length = Length; + blind = Blind; + Set(); +} + +void cMenuEditNumItem::Set(void) +{ + if (blind) { + char buf[length + 1]; + int i; + for (i = 0; i < length && value[i]; i++) + buf[i] = '*'; + buf[i] = 0; + SetValue(buf); + } + else + SetValue(value); +} + +eOSState cMenuEditNumItem::ProcessKey(eKeys Key) +{ + return osContinue; +} + +// --- cMenuEditChrItem ------------------------------------------------------ + +cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed) +:cMenuEditItem(Name) +{ + value = Value; + allowed = strdup(Allowed); + current = strchr(allowed, *Value); + if (!current) + current = allowed; + Set(); +} + +cMenuEditChrItem::~cMenuEditChrItem() +{ + free(allowed); +} + +void cMenuEditChrItem::Set(void) +{ + char buf[2]; + snprintf(buf, sizeof(buf), "%c", *value); + SetValue(buf); +} + +eOSState cMenuEditChrItem::ProcessKey(eKeys Key) +{ + return osContinue; +} + +// --- cMenuEditStrItem ------------------------------------------------------ + +cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed) +:cMenuEditItem(Name) +{ + value = Value; + length = Length; + allowed = strdup(Allowed); + pos = -1; + insert = uppercase = false; + newchar = true; + ieCurChr = 0; + lastKey = kNone; + Set(); +} + +cMenuEditStrItem::~cMenuEditStrItem() +{ + free(allowed); +} + +void cMenuEditStrItem::SetHelpKeys(void) +{ +} + +void cMenuEditStrItem::Set(void) +{ +} + +eOSState cMenuEditStrItem::ProcessKey(eKeys Key) +{ +#if 0 + const char c1[] = "-.#~,/_@1"; + const char c2[] = "abcäåá2"; + const char c3[] = "defé3"; + const char c4[] = "ghi4"; + const char c5[] = "jkl5"; + const char c6[] = "mnoöñó6"; + const char c7[] = "pqrs7"; + const char c8[] = "tuvüú8"; + const char c9[] = "wxyz9"; + const char c0[] = " 0"; + + switch (Key) { + case kRed: // Switch between upper- and lowercase characters + if (pos >= 0 && (!insert || !newchar)) { + uppercase = !uppercase; + value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]); + } + break; + case kGreen: // Toggle insert/overwrite modes + if (pos >= 0) { + insert = !insert; + newchar = true; + } + SetHelpKeys(); + lastKey = Key; + break; + case kYellow|k_Repeat: + case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor + if (pos >= 0) { + if (strlen(value) > 1) { + if (!insert || pos < int(strlen(value)) - 1) + memmove(value + pos, value + pos + 1, strlen(value) - pos); + // reduce position, if we removed the last character + if (pos == int(strlen(value))) + pos--; + } + else if (strlen(value) == 1) + value[0] = ' '; // This is the last character in the string, replace it with a blank + if (isalpha(value[pos])) + uppercase = isupper(value[pos]); + newchar = true; + } + lastKey = Key; + break; + case kLeft|k_Repeat: + case kLeft: if (pos > 0) { + if (!insert || newchar) + pos--; + newchar = true; + } + if (!insert && isalpha(value[pos])) + uppercase = isupper(value[pos]); + lastKey = Key; + break; + case kRight|k_Repeat: + case kRight: if (pos < length - 2 && pos < int(strlen(value)) ) { + if (++pos >= int(strlen(value))) { + if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ') + pos--; // allow only two blanks at the end + else { + value[pos] = ' '; + value[pos + 1] = 0; + } + } + } + newchar = true; + if (!insert && isalpha(value[pos])) + uppercase = isupper(value[pos]); + if (pos == 0) + SetHelpKeys(); + lastKey = Key; + break; + case kUp|k_Repeat: + case kUp: + case kDown|k_Repeat: + case kDown: if (pos >= 0) { + if (insert && newchar) { + // create a new character in insert mode + if (int(strlen(value)) < length - 1) { + memmove(value + pos + 1, value + pos, strlen(value) - pos + 1); + value[pos] = ' '; + } + } + if (uppercase) + value[pos] = toupper(Inc(tolower(value[pos]), NORMALKEY(Key) == kUp)); + else + value[pos] = Inc( value[pos], NORMALKEY(Key) == kUp); + newchar = false; + } + else + return cMenuEditItem::ProcessKey(Key); + lastKey = Key; + break; + case k0|k_Repeat ... k9|k_Repeat: + case k0 ... k9: if (Key != lastKey) { + ieCurChr = 0; + if (!newchar) { + // kRight + if (pos < length - 2 && pos < int(strlen(value)) ) { + if (++pos >= int(strlen(value))) { + if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ') + pos--; // allow only two blanks at the end + else { + value[pos] = ' '; + value[pos + 1] = 0; + } + } + } + newchar = true; + if (!insert && isalpha(value[pos])) + uppercase = isupper(value[pos]); + } + } + // kUp/kDown + if (pos >= 0) { + if (insert && newchar) { + // create a new character in insert mode + if (int(strlen(value)) < length - 1) { + memmove(value + pos + 1, value + pos, strlen(value) - pos + 1); + value[pos] = ' '; + } + } + } + else + return cMenuEditItem::ProcessKey(Key); + switch (Key) { + case k1: + if (uppercase) + value[pos] = toupper(c1[ieCurChr]); + else + value[pos] = c1[ieCurChr]; + if (c1[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k2: + if (uppercase) + value[pos] = toupper(c2[ieCurChr]); + else + value[pos] = c2[ieCurChr]; + if (c2[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k3: + if (uppercase) + value[pos] = toupper(c3[ieCurChr]); + else + value[pos] = c3[ieCurChr]; + if (c3[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k4: + if (uppercase) + value[pos] = toupper(c4[ieCurChr]); + else + value[pos] = c4[ieCurChr]; + if (c4[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k5: + if (uppercase) + value[pos] = toupper(c5[ieCurChr]); + else + value[pos] = c5[ieCurChr]; + if (c5[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k6: + if (uppercase) + value[pos] = toupper(c6[ieCurChr]); + else + value[pos] = c6[ieCurChr]; + if (c6[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k7: + if (uppercase) + value[pos] = toupper(c7[ieCurChr]); + else + value[pos] = c7[ieCurChr]; + if (c7[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k8: + if (uppercase) + value[pos] = toupper(c8[ieCurChr]); + else + value[pos] = c8[ieCurChr]; + if (c8[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k9: + if (uppercase) + value[pos] = toupper(c9[ieCurChr]); + else + value[pos] = c9[ieCurChr]; + if (c9[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + case k0: + if (uppercase) + value[pos] = toupper(c0[ieCurChr]); + else + value[pos] = c0[ieCurChr]; + if (c0[ieCurChr+1] == 0) + ieCurChr = 0; + else + ieCurChr++; + break; + default: + break; + } + lastKey = Key; + newchar = false; + break; + case kOk: if (pos >= 0) { + pos = -1; + lastKey = Key; + newchar = true; + stripspace(value); + SetHelpKeys(); + break; + } + // run into default + default: if (pos >= 0 && BASICKEY(Key) == kKbd) { + int c = KEYKBD(Key); + if (c <= 0xFF) { + const char *p = strchr(allowed, tolower(c)); + if (p) { + int l = strlen(value); + if (insert && l < length - 1) + memmove(value + pos + 1, value + pos, l - pos + 1); + value[pos] = c; + if (pos < length - 2) + pos++; + if (pos >= l) { + value[pos] = ' '; + value[pos + 1] = 0; + } + } + else { + switch (c) { + case 0x7F: // backspace + if (pos > 0) { + pos--; + return ProcessKey(kYellow); + } + break; + } + } + } + else { + switch (c) { + case kfHome: pos = 0; break; + case kfEnd: pos = strlen(value) - 1; break; + case kfIns: return ProcessKey(kGreen); + case kfDel: return ProcessKey(kYellow); + } + } + } + else + return cMenuEditItem::ProcessKey(Key); + } + Set(); +#endif + return osContinue; +} + +// --- cMenuEditStraItem ----------------------------------------------------- + +cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings) +:cMenuEditIntItem(Name, Value, 0, NumStrings - 1) +{ + strings = Strings; + Set(); +} + +void cMenuEditStraItem::Set(void) +{ + SetValue(strings[*value]); +} + +// --- cMenuTextItem --------------------------------------------------------- + +cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font) +{ + + text = strdup(Text); +} + +cMenuTextItem::~cMenuTextItem() +{ + free(text); +} + +void cMenuTextItem::Clear(void) +{ +} + +void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor) +{ +} + +void cMenuTextItem::ScrollUp(bool Page) +{ +} + +void cMenuTextItem::ScrollDown(bool Page) +{ +} + +eOSState cMenuTextItem::ProcessKey(eKeys Key) +{ + return osContinue; +} + +// --- cMenuSetupPage -------------------------------------------------------- + +cMenuSetupPage::cMenuSetupPage(void) +:cOsdMenu("", 33) +{ + plugin = NULL; +} + +void cMenuSetupPage::SetSection(const char *Section) +{ +} + +eOSState cMenuSetupPage::ProcessKey(eKeys Key) +{ + return osContinue; +} + +void cMenuSetupPage::SetPlugin(cPlugin *Plugin) +{ +} + +void cMenuSetupPage::SetupStore(const char *Name, const char *Value) +{ +} + +void cMenuSetupPage::SetupStore(const char *Name, int Value) +{ +} diff --git a/sh_console_osd_menuitems.h b/sh_console_osd_menuitems.h new file mode 100644 index 0000000..8812862 --- /dev/null +++ b/sh_console_osd_menuitems.h @@ -0,0 +1,130 @@ +/* + * menuitems.h: General purpose menu items + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sh_console_osd_menuitems.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ + */ + +#ifndef __MENUITEMS_H +#define __MENUITEMS_H + +#include "myosd.h" + +extern const char *FileNameChars; + +class cMenuEditItem : public cOsdItem { +private: + char *name; + char *value; +public: + cMenuEditItem(const char *Name); + ~cMenuEditItem(); + void SetValue(const char *Value); + }; + +class cMenuEditIntItem : public cMenuEditItem { +protected: + int *value; + int min, max; + virtual void Set(void); +public: + cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX); + virtual eOSState ProcessKey(eKeys Key); + }; + +class cMenuEditBoolItem : public cMenuEditIntItem { +protected: + const char *falseString, *trueString; + virtual void Set(void); +public: + cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL); + }; + +class cMenuEditNumItem : public cMenuEditItem { +protected: + char *value; + int length; + bool blind; + virtual void Set(void); +public: + cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind = false); + virtual eOSState ProcessKey(eKeys Key); + }; + +class cMenuEditChrItem : public cMenuEditItem { +private: + char *value; + char *allowed; + const char *current; + virtual void Set(void); +public: + cMenuEditChrItem(const char *Name, char *Value, const char *Allowed); + ~cMenuEditChrItem(); + virtual eOSState ProcessKey(eKeys Key); + }; + +class cMenuEditStrItem : public cMenuEditItem { +private: + char *value; + int length; + char *allowed; + int pos; + bool insert, newchar, uppercase; + void SetHelpKeys(void); + virtual void Set(void); + char Inc(char c, bool Up); + int ieCurChr; + eKeys lastKey; +public: + cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed); + ~cMenuEditStrItem(); + virtual eOSState ProcessKey(eKeys Key); + }; + +class cMenuEditStraItem : public cMenuEditIntItem { +private: + const char * const *strings; +protected: + virtual void Set(void); +public: + cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings); + }; + +class cMenuTextItem : public cOsdItem { +private: + char *text; + int x, y, w, h, lines, offset; + eDvbColor fgColor, bgColor; + eDvbFont font; +public: + cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground, eDvbFont Font = fontOsd); + ~cMenuTextItem(); + int Height(void) { return h; } + void Clear(void); + virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); + bool CanScrollUp(void) { return offset > 0; } + bool CanScrollDown(void) { return h + offset < lines; } + void ScrollUp(bool Page); + void ScrollDown(bool Page); + virtual eOSState ProcessKey(eKeys Key); + }; + +class cPlugin; + +class cMenuSetupPage : public cOsdMenu { +private: + cPlugin *plugin; +protected: + void SetSection(const char *Section); + virtual void Store(void) = 0; + void SetupStore(const char *Name, const char *Value = NULL); + void SetupStore(const char *Name, int Value); +public: + cMenuSetupPage(void); + virtual eOSState ProcessKey(eKeys Key); + void SetPlugin(cPlugin *Plugin); + }; + +#endif //__MENUITEMS_H diff --git a/sh_dummy_content.c b/sh_dummy_content.c new file mode 100644 index 0000000..a6afcd9 --- /dev/null +++ b/sh_dummy_content.c @@ -0,0 +1,567 @@ +/*******************************************************************/ +/*! \file content_interface.cpp + * \brief Data Objects for content (e.g. mp3 files, movies) + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * DUMMY + * + * + * Dummy artists: Artist_1, Artist_2, Artist_3 + * Dummy albums by ArtistX: Album_X1, .... AlbumX3 + * Dummy Tracks On Album_XY: Track_XY1,... Track_XY5 + * + */ +/*******************************************************************/ +#define DEBUG + +#include "sh_dummy_content.h" +#include "mg_tools.h" + +#include +#include +#define GD_PLAYLIST_TYPE 0 // listtype for giant disc db + +// some dummies to keep the compiler happy +#define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax +#define DUMMY +#define NUM_ARTISTS 5 +#define NUM_ALBUMS_PER_ARTIST 3 +#define NUM_TRACKS_PER_ALBUM 9 + +int DummyInitDatabase(MYSQL *db){return 0;} +vector DummyGetStoredPlaylists(MYSQL db){ return vector();} + +/*******************************************************************/ +/* class mgTack */ +/********************************************************************/ +DummyTrack DummyTrack::UNDEFINED = DummyTrack(); + +/*! + ***************************************************************************** + * \brief Constructor: creates a DummyTrack obect + * + * \param sqlIdentifier identifies a unique row in the track database + * \param dbase database which stores the track table + * + * On creation, the object contains only the idea. The actual data fields + * are filled when readData() is called for the first time. + ****************************************************************************/ +DummyTrack::DummyTrack(int sqlIdentifier, MYSQL dbase) +{ + m_uniqID = sqlIdentifier; + char buf[255]; + + mgDebug(9, "Creating dumy track %d", sqlIdentifier); + // create dummy value based on the id + sprintf(buf, "ArtistName_%d", (int) m_uniqID / 100); + m_artist = buf; + sprintf(buf,"Album_%d", (int) m_uniqID / 10); + m_album = buf; + sprintf(buf,"Track_%d",m_uniqID); + m_title = buf; + sprintf(buf,"sourcefile_%d", m_uniqID); + m_mp3file = buf; + m_genre = "unknown_genre"; + m_year = 1970 + (m_uniqID%35); + m_rating = 2; + m_length = 180; +} + +/*! + ***************************************************************************** + * \brief copy constructor + * + ****************************************************************************/ +DummyTrack::DummyTrack(const DummyTrack& org) +{ + m_uniqID = org.m_uniqID; + m_db = org.m_db; + + mgDebug(9, + "Creating a TrackCopy for track '%s' (is this really necessary?", + org.m_title.c_str()); + + m_artist = org.m_artist; + m_title = org.m_title; + m_mp3file = org.m_mp3file; + m_album = org.m_album; + m_genre = org.m_genre; + m_year = org.m_year; + m_rating = org.m_rating; + +} + + +/*! + ***************************************************************************** + * \brief destructor + * + ****************************************************************************/ +DummyTrack::~DummyTrack() +{ + // nothing to be done +} + +/*! + ***************************************************************************** + * \brief returns value for _mp3file + ****************************************************************************/ +string DummyTrack::getSourceFile() +{ + return m_mp3file; +} + +/*! + ***************************************************************************** + * \brief returns value for m_title + ****************************************************************************/ +string DummyTrack::getTitle() +{ + return m_title; +} + +/*! + ***************************************************************************** + * \brief returns value for m_artist + ****************************************************************************/ +string DummyTrack::getArtist() +{ + return m_artist; +} +string DummyTrack::getLabel(int col) +{ + switch(col) + { + case 0: + return m_artist; + break; + case 1: + return m_title; + break; + default: + return ""; + } +} +string DummyTrack::getDescription() +{ + return m_artist + " - " + m_title; +} + +/*! + ***************************************************************************** + * \brief returns value for m_album + ****************************************************************************/ +string DummyTrack::getAlbum() +{ + return m_album; +} + +/*! + ***************************************************************************** + * \brief returns value for m_genre + ****************************************************************************/ +string DummyTrack::getGenre() +{ + return m_genre; +} + +/*! + ***************************************************************************** + * \brief returns value for m_year + ****************************************************************************/ +int DummyTrack::getYear() +{ + return m_year; +} + +/*! + ***************************************************************************** + * \brief returns value for m_rating + * + * If value has not been retrieved from the database, radData() is called first + ****************************************************************************/ +int DummyTrack::getRating() +{ + return m_rating; +} + +int DummyTrack::getDuration() +{ + return m_rating; +} + +/*! + ***************************************************************************** + * \brief sets the field for m_title to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void DummyTrack::setTitle(string new_title) +{ + m_title = new_title; +} + +/*! + ***************************************************************************** + * \brief sets the field for m_artist to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void DummyTrack::setArtist(string new_artist) +{ + m_artist = new_artist; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_album to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void DummyTrack::setAlbum(string new_album) +{ + m_album = new_album; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_genre to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void DummyTrack::setGenre(string new_genre) +{ + m_genre = new_genre; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_year to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void DummyTrack::setYear(int new_year) +{ + m_year = new_year; +} + + +/*! + ***************************************************************************** + * \brief sets the field for m_rating to the specified value + * + * Note: The new value is not stored in the database. + * This is only done, when writeData() is called + ****************************************************************************/ +void DummyTrack::setRating(int new_rating) +{ + m_rating = new_rating; +} + + +/*! + ***************************************************************************** + * \brief stores current values in the sql database + * + * Note: At the moment, only the values stored directly in the 'tracks' + * database are updated + ****************************************************************************/ +bool DummyTrack::writeData() +{ + return true; +} + +DummyTracklist::DummyTracklist(MYSQL db_handle, string restrictions) +{ +} + +/*******************************************************************/ +/* class DummyPlaylist */ +/********************************************************************/ + +/*! + ***************************************************************************** + * \brief Constructor: opens playlist by name + * + * \param listname user-readable identifier of the paylist + * \param db_handl database which stores the playlist + * + * If the playlist does not yet exist, an empty playlist is created + ****************************************************************************/ +DummyPlaylist::DummyPlaylist(string listname, MYSQL db_handle) +{ + m_db = db_handle; + + // + // check, if the database really exists + // + if(listname =="EXISTS") + { + + m_author = "DUMMY_author"; + m_listname = listname; + createDummyPlaylist(1); + } + else + { + // new Playlist + m_author = "VDR"; + m_listname = listname; + + } + +} + +/*! + ***************************************************************************** + * \brief Constructor: construct playlist object from existing sql playlist + * + * \param sql_identifier: sql internal identifier for the playlist + * \param db_handl database which stores the playlist + * + * This constructor is typically used when a playlist is selected from an + * internal list of playlists + ****************************************************************************/ +DummyPlaylist::DummyPlaylist(unsigned int sql_identifier, MYSQL db_handle) +{ + m_db = db_handle; + char buf[256]; + + m_author = "DUMMY_author"; + sprintf(buf, "Dummylist_%d", sql_identifier); + m_listname = buf; + createDummyPlaylist(sql_identifier); +} + +void DummyPlaylist::createDummyPlaylist(int start) +{ + DummyTrack* trackptr; + for(int id=start; id < start+20; id++) + { + trackptr = new DummyTrack(id, m_db); + m_list.push_back(trackptr); + } +} + +/*! + ***************************************************************************** + * \brief empty destructor + * + * Nothing to be done. The actual data is stored in the sql db + ****************************************************************************/ +DummyPlaylist::~DummyPlaylist() +{ + // nothing to be done +} + +bool DummyPlaylist::storePlaylist() +{ + mgDebug(1, "Storing Playlist #%s'", m_listname.c_str()); + return true; +} +/*! + ***************************************************************************** + * \brief returns the total duration of all songs in the list in seconds + * + ****************************************************************************/ +int DummyPlaylist::getPlayTime() +{ + return m_list.size()* 180; +} + +/*! + ***************************************************************************** + * \brief returns the duration of all remaining songs in the list in seconds + * + ****************************************************************************/ +int DummyPlaylist::getPlayTimeRemaining() +{ + return 0; // dummy +} + +/*******************************************************************/ +/* class DummyTreeNode */ +/*******************************************************************/ + +DummyTreeNode::DummyTreeNode(MYSQL db, int view) + : mgSelectionTreeNode(db, view) +{ + // create a root node + // everything is done in the parent class + m_restriction ="1"; +} + +DummyTreeNode::DummyTreeNode(mgSelectionTreeNode* parent, + string id, string label, string restriction) + : mgSelectionTreeNode(parent, id, label) +{ + m_restriction = restriction; + // everything else is done in the parent class +} + + +/*! + ***************************************************************************** + * \brief destructor + * + ****************************************************************************/ +DummyTreeNode::~DummyTreeNode() +{ + m_children.clear(); +} + + + +/*! + ***************************************************************************** + * \brief compute children on the fly + * + ****************************************************************************/ +bool DummyTreeNode::expand() +{ + char buf[20]; + if (m_expanded) + { + mgWarning("Node already expanded\n"); + return true; + } + m_expanded = true; + + mgDebug(5, "Expanding level %d\n", m_level); + switch(m_view) + { + case 1: // artist -> album -> title + if(m_level == 0) // root, expand artist + { + for(int artnum=1; artnum <= NUM_ARTISTS; artnum++) + { + sprintf(buf, "%d", artnum); + m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf, + "Artist "+ (string)buf, + m_restriction + " AND album.artist='Artist "+(string) buf+"'")); + } + + } + else if(m_level == 1) // artist, expand album + { + // create album names in the form Album_x1, ... Album_x3 + // where x is the artist ID + // for normal usage: _restrictions should now hold: + // album.artist = XYZ + for(int albnum=1; albnum <= NUM_ALBUMS_PER_ARTIST; albnum++) + { + sprintf(buf, "%d", albnum); + + m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf, + "Album_"+ m_id + (string) buf, + m_restriction + " AND track.sourceid='0x00"+ m_id + (string) buf +"'")); + } + break; + } + else if(m_level == 2) // artist -> album, expand title + { + // create track names in the form Track_xy1, ... Track_xy5 + // where x is the artist ID and y is the album id + // for normal usage: _restrictions should now hold: + // album.artist = ... AND track.sourceid='0x00XY + for(int tracknum=1; tracknum <= NUM_TRACKS_PER_ALBUM; tracknum++) + { + sprintf(buf, "%d", tracknum); + m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf, + "Track_"+m_id+ (string) buf, + m_restriction + " AND track.id=tr"+ m_id + (string) buf + "'" )); + } + break; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; + default: + mgError("View '%d' not yet implemented", m_view); + } + mgDebug(5, "%d children expanded\n", m_children.size()); + return true; +} + +/*! + ***************************************************************************** + * \brief go over all children recursively to find the tracks + * + ****************************************************************************/ +vector* DummyTreeNode::getTracks() +{ + vector* dummy; + string sql; + + int artistnum; + int albumnum; + int tracknum; + dummy = new vector(); + + sql = m_restriction; + mgDebug(5, "QUERY:\n%s\n", sql.c_str()); + + artistnum = 0; + do // artist_loop + { + if(m_level >=1) + artistnum = m_id[0]-'0'; // we have a unique artist + else + artistnum++; // we are in a loop + + albumnum = 0; + do // album_loop + { + if(m_level >=2) + albumnum = m_id[1]-'0'; // we have a unique album + else + albumnum++; // we are in a loop + + tracknum =0; + do // track_loop + { + if(m_level ==3) + tracknum = m_id[2]-'0'; // we have a unique track + else + tracknum++; // we are in a loop + dummy->push_back(new DummyTrack(artistnum*100+albumnum*10+tracknum, m_db)); + }while ((m_level < 3) && (tracknum< NUM_TRACKS_PER_ALBUM)); + }while ((m_level < 2) && (albumnum < NUM_ALBUMS_PER_ARTIST)); + }while ((m_level < 1) && (artistnum < NUM_ARTISTS )); + + return dummy; +} + + + + + + + + + + + + + + + + + diff --git a/sh_dummy_content.h b/sh_dummy_content.h new file mode 100644 index 0000000..71d3ce7 --- /dev/null +++ b/sh_dummy_content.h @@ -0,0 +1,178 @@ +/*******************************************************************/ +/*! \file dummy_content.h + * \brief Dummy Data Objects for testing Muggle + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + * + * Declares main classes of for content items and interfaces to SQL databases + * + *******************************************************************/ +/* makes sur we dont use parse the same declarations twice */ +#ifndef _DUMMY_CONTENT_H +#define _DUMMY_CONTENT_H +using namespace std; +#include +#include +#include + +#include "mg_content_interface.h" + +// non-member function +int DummyInitDatabase(MYSQL *db); +vector DummyGetStoredPlaylists(MYSQL db); + +/*! + ******************************************************************* + * \class DummyTrack + * + * \brief represents a a single track + * DUMMY + ********************************************************************/ +class DummyTrack : public mgContentItem +{ + +private: + MYSQL m_db; + + // content fields + string m_artist; + string m_title; + string m_mp3file; + string m_album; + string m_genre; + int m_year; + int m_rating; + int m_length; + + + public: + + /* constructor */ + DummyTrack(){ m_uniqID = -1;} // creates invalid item + DummyTrack(int sqlIdentifier, MYSQL dbase); + + DummyTrack(const DummyTrack&); + + /* destructor */ + ~DummyTrack(); + + virtual mgContentItem::contentType getContentType() + {return mgContentItem::GD_AUDIO;} + virtual mgMediaPlayer getPlayer(){return mgMediaPlayer();} + + /* data acess */ + //virtual functions of the base class + virtual string getSourceFile(); + virtual string getTitle(); + virtual string getLabel(int col); + virtual string getDescription(); + virtual string getGenre(); + virtual int getRating(); + + // additional class-specific functions + string getArtist(); + string getAlbum(); + int getYear(); + int getDuration(); + + /* data manipulation */ + void setTitle(string new_title); + void setArtist(string new_artist); + void setAlbum(string new_album); + void setGenre(string new_genre); + void setYear(int new_rating); + void setRating(int new_rating); + + bool writeData(); + static DummyTrack UNDEFINED; + +}; + +class DummyTracklist : public mgTracklist +{ + public: + DummyTracklist(MYSQL db_handle, string restrictions); +}; + +/*! + ******************************************************************* + * \class GdTracklist + * + * \brief represents a playlist, i.e. an ordered collection of tracks + ********************************************************************/ +class DummyPlaylist : public mgPlaylist +{ + private: + int m_sqlId; /* -1 means: not valid */ + string m_author; + MYSQL m_db; + private: + void createDummyPlaylist(int strt); + public: + + + /*==== constructors ====*/ + DummyPlaylist(string listname, MYSQL db_handle); + /* opens existing or creates empty playlist */ + + DummyPlaylist(unsigned int sql_identifier, MYSQL db_handle); + /* construct from the db by internal id*/ + + /*==== destructor ====*/ + ~DummyPlaylist(); + + int getPlayTime(); + /* returns the total duration of all songs in the list in seconds */ + + int getPlayTimeRemaining(); + /* returns the duration of all remaining songs in the list in seconds */ + + bool storePlaylist(); +}; +/*! + ******************************************************************* + * \class mgSelectionTreeNode + ********************************************************************/ +class DummyTreeNode : public mgSelectionTreeNode { + +private: + MYSQL m_db; // underlying db + +public: + + /*==== constructors ====*/ + DummyTreeNode(MYSQL db, int view); + DummyTreeNode(mgSelectionTreeNode* parent, + string id, string label, string restriction); + + /*==== destructor ====*/ + ~DummyTreeNode(); + + // compute children o^xn the fly + virtual bool expand(); + + virtual vector* getTracks(); +}; + +#endif /* END _CONTENT_INTERFACE_H */ + + + + + + + + + + + + + + + + + + diff --git a/sh_muggle.c b/sh_muggle.c new file mode 100644 index 0000000..ef26f57 --- /dev/null +++ b/sh_muggle.c @@ -0,0 +1,365 @@ +#include +#include +#include "mysql/mysql.h" + +#include "content_interface.h" +#include "mgmedia.h" +#include "muggle_tools.h" +#include + +#define DISPLAY_SIZE 20 +#define PAGE_JUMP 18 + + +#define EXIT 0 +#define TREE_VIEW 1 +#define LIST_VIEW 2 +#define PLAYLIST_VIEW 3 +#define FILTER_VIEW 4 + + +const char playlist_command_str[] = +" s : shuffle\n" +" 8 : up\n" +" 2 : down\n" +" 9 : page-up\n" +" 3 : page-down\n" +" p : go to top\n" +" t : switch to tree view\n" +" q : quit"; + +const char tree_command_str[] = +" 6 : expand\n" +" 4 : collapse\n" +" 8 : up\n" +" 2 : down\n" +" 9 : page-up\n" +" 3 : page-down\n" +" 5 : add selection to playlist\n" +" p : switch to playlist view\n" +" t : go to root node\n" +" q : quit"; + +mgSelectionTreeNode* GV_currentNode; +vector GV_treeDisplay; + +mgPlaylist *GV_currentPlaylist; + +void print_tree(mgSelectionTreeNode* node, string tab) +{ + vector children; + vector::iterator iter; + + tab = tab + " "; + printf("%2d %c %s-- Id: '%s', level %d '%s' \n", + GV_treeDisplay.size(), (node->isExpanded()?'x':'-'), tab.c_str(), + node->getID().c_str(), node->getLevel(), + node->getLabel().c_str()); +// printf(" %sRestrictions: '%s'\n", tab.c_str(), node->getRestrictions().c_str()); + GV_treeDisplay.push_back(node); + if(node->isExpanded()) + { + children = node->getChildren(); + for(iter = children.begin(); iter != children.end(); iter++) + { + print_tree(*iter, tab); + } + } +} +void print_node(mgSelectionTreeNode* node, unsigned int selected, unsigned int start) +{ + vector children; + vector::iterator iter; + mgSelectionTreeNode* child; + unsigned int i; + + GV_treeDisplay.clear(); + printf("--%s---\n", node->getLabel().c_str()); + children = node->getChildren(); + // first, add al nodes to the storage + for(iter = children.begin(); iter != children.end(); iter++) + { + GV_treeDisplay.push_back(*iter); + } + + // now display the ones in question + printf("Displaying %d-%d of %d (%s)\n", start+1, start+DISPLAY_SIZE+1, + GV_treeDisplay.size(), node->getRestrictions().c_str()); + if(start >0) + printf(".......\n"); + for (i = start; i < start+DISPLAY_SIZE && i< GV_treeDisplay.size(); i++) + { + child = GV_treeDisplay[i]; + if(i == selected) + printf ("==>"); + else + printf(" "); + printf ("%s\n",child->getLabel().c_str()); + } + if(i==start+DISPLAY_SIZE) + printf(".......\n"); +} + +void print_tracks(vector* tracks, string restriction) +{ + vector::iterator iter; + + printf("======BEGIN_TRACKS=====(restriction:'%s'======\n", restriction.c_str()); + for(iter = tracks->begin(); iter != tracks->end(); iter++) + { + printf(" Track: (%d): '%s' \n",(*iter)->getId(), (*iter)->getTitle().c_str()); + } + printf("======END_TRACKS===========\n"); + printf("press key t continue\n"); + getchar(); +} + +void PrintPlaylist(mgPlaylist *pl, unsigned int selected, unsigned int start) +{ + vector::iterator iter; + unsigned int i; + + // now display the ones in question + printf("Playlist '%s' (%d entries)\n", pl->getListname().c_str(), + pl->getNumItems()); + if(start >0) + printf(".......\n"); + for (i = start; igetNumItems(); i++) + { + if(i == selected) + printf ("==>"); + else + printf(" "); + printf ("%s\n",pl->getLabel(i, "\t").c_str()); + } + if(i==start+DISPLAY_SIZE) + printf(".......\n"); +} +int PlaylistView() +{ + char cmd; + bool loop = true; + unsigned int selected = 0; + unsigned int start=0; + + while(loop) + { + printf("\n\n\n\n\n\n"); + PrintPlaylist(GV_currentPlaylist, selected, start); + printf("------- enter command ('h' for help) -----------\n"); + cmd = getchar(); + switch (cmd) + { + case 'h': // shuffle + printf("%s\n", playlist_command_str); + break; + case 's': // shuffle + GV_currentPlaylist->shuffle(); + break; + case '8': // up + if(selected >0) selected--; + if(selected < start) start = start - PAGE_JUMP; + break; + case '2': // down + if(selected < (GV_currentPlaylist->getNumItems()-1)) + selected++; + if(selected >= start+DISPLAY_SIZE) start = start + PAGE_JUMP; + break; + case '9': // pgup + if(start >= PAGE_JUMP) + { + start -= PAGE_JUMP; + selected -= PAGE_JUMP; + } + else + mgDebug(6,"Can not go up %d %d", start, selected); + break; + case '3': // pgdown + mgDebug(6,"pgdown %d %d", start, selected); + if(start+ PAGE_JUMP< (GV_currentNode->getChildren()).size()) + { + start += PAGE_JUMP; + selected += PAGE_JUMP; + if(selected >= ((GV_currentNode->getChildren()).size())) + selected = (GV_currentNode->getChildren()).size(); + } + + break; + case 'p': // go to top of playlist view + selected=0; + start=0; + break; + case 't': // tree view (on root node) + cmd = getchar(); + return TREE_VIEW; + break; + case 'q': // tree view (on root node) + cmd = getchar(); + return EXIT; + break; + default: + mgWarning("Invalid Command '%c'",cmd); + + } + cmd = getchar(); + + }// end nodeview loop + return EXIT; +} +// displays one node (and all its children) +int NodeView() +{ + vector* tracks; + char cmd; + bool loop = true; + unsigned int selected = 0; + unsigned int start=0; + while(loop) + { + printf("\n\n\n\n\n\n"); + if(!GV_currentNode->isExpanded()) + { + GV_currentNode->expand(); + } + print_node(GV_currentNode,selected, start); + printf("------- enter command ('h' for help) -----------\n"); + + cmd = getchar(); + switch (cmd) + { + case 'h': // shuffle + printf("%s\n", tree_command_str); + break; + case 'e': + case '6':// expand + GV_currentNode = GV_treeDisplay[selected]; + if(GV_currentNode->expand()) + { + selected = 0; + start=0; + } + else + { + GV_currentNode = GV_currentNode ->getParent(); + } + break; + case 'c' : + case '4': + if(GV_currentNode->getParent() != NULL) + { + GV_currentNode->collapse(); + GV_currentNode = GV_currentNode->getParent(); + selected = 0; + start=0; + } + else + { + mgWarning("Already at top level"); + } + break; + case '8': // up + if(selected >0) selected--; + if(selected < start) start = start - PAGE_JUMP; + break; + case '2': // down + if(selected < ((GV_currentNode->getChildren()).size()-1)) + selected++; + if(selected >= start+DISPLAY_SIZE) start = start + PAGE_JUMP; + break; + case '9': // pgup + if(start >= PAGE_JUMP) + { + start -= PAGE_JUMP; + selected -= PAGE_JUMP; + } + else + mgDebug(6,"Can not go up %d %d", start, selected); + break; + case '3': // pgdown + mgDebug(6,"pgdown %d %d", start, selected); + if(start+ PAGE_JUMP< (GV_currentNode->getChildren()).size()) + { + start += PAGE_JUMP; + selected += PAGE_JUMP; + if(selected >= ((GV_currentNode->getChildren()).size())) + selected = (GV_currentNode->getChildren()).size(); + } + + break; + case '5': // OK ,add to playlist + tracks = GV_treeDisplay[selected]->getTracks(); + GV_currentPlaylist->appendList(tracks); + tracks = NULL; + break; + case 'p': // go to playlist view + cmd = getchar(); + return PLAYLIST_VIEW; + break; + case 't': // tree view (on root node) + while(GV_currentNode->getParent() != NULL) + { + GV_currentNode->collapse(); + GV_currentNode = GV_currentNode->getParent(); + } + selected = 0; + start=0; + break; + case 'q': // go to playlist view + cmd = getchar(); + return EXIT; + break; + default: + mgWarning("Invalid Command '%c'",cmd); + + } + cmd = getchar(); + + }// end nodeview loop + return EXIT; +} +int main (int argc, char **argv) +{ + mgMedia* media; + string tab; + bool loop = true; + /* now to connect to the database */ + mgSelectionTreeNode *root; + int activeScreen; + + vector listcols; + mgSetDebugLevel(8); + media = new mgMedia(mgMedia::GD_MP3); + + mgDebug(3," Staring sh_muggle"); + // create initial tree node in view '1' + activeScreen = TREE_VIEW; + root = media->getSelectionRoot(); + root->expand(); + GV_currentNode = root->getChildren()[0]; + GV_currentPlaylist = media->createTemporaryPlaylist(); + listcols.push_back(1); + listcols.push_back(0); + GV_currentPlaylist->setDisplayColumns(listcols); + mgDebug(3," Entering sh_muggle main loop"); + + // now switch to the initial view; + while(loop) + { + switch(activeScreen) + { + case TREE_VIEW: + activeScreen = NodeView(); + break; + case PLAYLIST_VIEW: + activeScreen = PlaylistView(); + break; + case EXIT: + exit(0); + break; + default: + mgError("Invalid screen %d"); + } + } + mgError("leavingNodeView"); + +} diff --git a/sh_muggle2.c b/sh_muggle2.c new file mode 100644 index 0000000..e3748a4 --- /dev/null +++ b/sh_muggle2.c @@ -0,0 +1,66 @@ +#include +#include +#include "mysql/mysql.h" + +#include "myosd.h" +#include "muggle.h" +#include "mgmedia.h" +#include "muggle_tools.h" +#include + +eKeys waitForKey() +{ + char buf[2]; + read(0, buf, 2); + mgDebug(9, "Key '%c' read\n",buf[0]); + switch(buf[0]) + { + case '8': + return kUp; + case '2': + return kDown; + case '1': + return kMenu; + case '5': + return kOk; + case '3': + return kBack; + case '4': + return kLeft; + case '6': + return kRight; + case 'r': + return kRed; + case 'g': + return kGreen; + case 'y': + return kYellow; + case 'b': + return kBlue; + default: + return kNone; + + } +} +int main (int argc, char **argv) +{ + + cOsdObject *mainMenu; + eKeys key; + eOSState state; + bool loop=true; + mgMuggle muggle; + muggle.Initialize(); + muggle.Start(); + mgSetDebugLevel(8); + + mainMenu = muggle.MainMenuAction(); + + while(loop) + { + key = waitForKey(); + state = mainMenu->ProcessKey(key); + if(state == osEnd) + loop = false; + } +} diff --git a/sh_plugin.c b/sh_plugin.c new file mode 100644 index 0000000..eff5cff --- /dev/null +++ b/sh_plugin.c @@ -0,0 +1,77 @@ +/* + * plugin.c: The VDR plugin interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sh_plugin.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ + */ + +#include "shell_plugin.h" + +#define LIBVDR_PREFIX "libvdr-" +#define SO_INDICATOR ".so." + +#define MAXPLUGINARGS 1024 +#define HOUSEKEEPINGDELTA 10 // seconds + +// --- cPlugin --------------------------------------------------------------- + +char *cPlugin::configDirectory = NULL; + +cPlugin::cPlugin(void) +{ + name = NULL; +} + +cPlugin::~cPlugin() +{ + // I18nRegister(NULL, Name()); +} + +void cPlugin::SetName(const char *s) +{ + name = s; +} + +const char *cPlugin::CommandLineHelp(void) +{ + return NULL; +} + +bool cPlugin::ProcessArgs(int argc, char *argv[]) +{ + return true; +} + +bool cPlugin::Initialize(void) +{ + return true; +} + +bool cPlugin::Start(void) +{ + return true; +} + +void cPlugin::Housekeeping(void) +{ +} + +const char *cPlugin::MainMenuEntry(void) +{ + return NULL; +} + +cOsdObject *cPlugin::MainMenuAction(void) +{ + return NULL; +} +bool cPlugin::SetupParse(const char *Name, const char *Value) +{ + return false; +} +cMenuSetupPage *cPlugin::SetupMenu(void) +{ + return NULL; +} diff --git a/sh_plugin.h b/sh_plugin.h new file mode 100644 index 0000000..2bf7bcb --- /dev/null +++ b/sh_plugin.h @@ -0,0 +1,46 @@ +/* + * plugin.h: The VDR plugin interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sh_plugin.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ + */ + +#ifndef __PLUGIN_H +#define __PLUGIN_H + +#include "myosd.h" +#include "mymenuitems.h" +typedef cOsdMenu cOsdObject; +#define VDRPLUGINCREATOR(PluginClass) extern "C" void *VDRPluginCreator(void) { return new PluginClass; } + +class cPlugin { +private: + static char *configDirectory; + const char *name; + void SetName(const char *s); +public: + cPlugin(void); + virtual ~cPlugin(); + + const char *Name(void) { return name; } + virtual const char *Version(void) = 0; + virtual const char *Description(void) = 0; + virtual const char *CommandLineHelp(void); + + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Initialize(void); + virtual bool Start(void); + virtual void Housekeeping(void); + + virtual const char *MainMenuEntry(void); + virtual cOsdObject *MainMenuAction(void); + + virtual cMenuSetupPage *SetupMenu(void); + + virtual bool SetupParse(const char *Name, const char *Value); + + }; + +#endif //__PLUGIN_H diff --git a/vdr_menu.c b/vdr_menu.c new file mode 100644 index 0000000..d706e94 --- /dev/null +++ b/vdr_menu.c @@ -0,0 +1,523 @@ +/*******************************************************************/ +/*! \file vdr_menu.c + * \brief Implements menu handling for broswing media libraries within VDR + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + +#include +#include +#include + +#include "vdr_menu.h" + +#include "mg_content_interface.h" +#include "mg_tools.h" +#include "mg_media.h" + +#include "gd_content_interface.h" + +#include +#include + +using namespace std; + +static const char* alpha_num_keys = "abcdefghijklmnopqrstuvwxyz0123456789-"; + +// ----------------------- mgMenuTreeItem ------------------ + +mgMenuTreeItem::mgMenuTreeItem( mgSelectionTreeNode *node ) + : m_node( node ) +{ + Set(); +} + +mgSelectionTreeNode* mgMenuTreeItem::Node() +{ + return m_node; +} + +void mgMenuTreeItem::Set() +{ + char *buffer = 0; + asprintf( &buffer, m_node->getLabel().c_str() ); + SetText( buffer, false ); +} + +// ----------------------- mgMenuTrackItem ------------------ + + +// ----------------------- mgMainMenu ---------------------- + +mgMainMenu::mgMainMenu() + : cOsdMenu( "" ) +{ + mgDebug( 1, "Creating Muggle Main Menu" ); + + SetTitle( "Muggle Media Database" ); + SetButtons(); + + m_media = new mgMedia( mgMedia::GD_MP3 ); + m_root = m_media->getSelectionRoot(); + + m_filtername = new char[32]; + strcpy( m_filtername, "none" ); + + m_title = new char[32]; + strcpy( m_title, "none" ); + + m_interpret = new char[32]; + strcpy( m_interpret, "none" ); + + m_album = new char[32]; + strcpy( m_album, "none" ); + + m_playlist = new char[32]; + strcpy( m_playlist, "none" ); + + m_year_min = 1900; + m_year_max = 2100; + + m_filter = 0; + + m_current_playlist = m_media->createTemporaryPlaylist(); + m_tracklist = NULL; + + DisplayTree( m_root ); +} + +mgSelectionTreeNode *mgMainMenu::CurrentNode() +{ + mgMenuTreeItem *item = (mgMenuTreeItem *)Get( Current() ); + return item? item->Node(): 0; +} + +void mgMainMenu::SetButtons( ) +{ + SetHasHotkeys(); + + if( m_state == TREE ) + { + SetHelp( "Add", "Filter", "Playlist", "Change View" ); + } + else if( m_state == PLAYLIST ) + { + SetHelp( "Tracks", "Filter", "Edit PL", "Tree" ); + } + else if( m_state == PLAYLIST_TRACKINFO ) + { + SetHelp( "Play/Pause", "Album Info", "Filter", "Tree" ); + } + else if( m_state == PLAYLIST_ALBUMINFO ) + { + SetHelp( "Play/Pause", "Filter", "Playlist", "Tree" ); + } + else if( m_state == FILTER ) + { + SetHelp( "Add", "Playlist", "Filter", "Tree" ); + } + else if( m_state == TRACKS ) + { + SetHelp( "Add", "Filter", "Playlist", "Tree" ); + } +} + +eOSState mgMainMenu::ProcessKey(eKeys key) +{ + mgDebug( 1, "mgMainMenu::ProcessKey" ); + eOSState state = cOsdMenu::ProcessKey(key); + + if( m_state == PLAYLIST ) + { + mgDebug( 1, "mgMainMenu: in state PLAYLIST" ); + if( state == osUnknown ) + { + switch( key ) + { + case kOk: + { + mgDebug( 1, "mgMainMenu: Mark" ); + Mark(); // Mark (to move), moving done by VDR, calls Move + state = osContinue; + } break; + case kRed: + { + mgDebug( 1, "mgMainMenu: " ); + // Shuffle? + } + case kYellow: + { + if( m_state == PLAYLIST ) + { + mgDebug( 1, "mgMainMenu: switch to TrackInfo" ); + DisplayTrackInfo(); + } + else if( m_state == PLAYLIST_TRACKINFO ) + { + mgDebug( 1, "mgMainMenu: switch to AlbumInfo" ); + DisplayAlbumInfo(); + } + else if( m_state == PLAYLIST_ALBUMINFO ) + { + mgDebug( 1, "mgMainMenu: switch to Playlist" ); + DisplayPlaylist(); + } + state = osContinue; + } break; + case kBlue: + { + mgDebug( 1, "mgMainMenu: switch to TreeView" ); + DisplayTree( m_root ); + state = osContinue; + } break; + case kGreen: + { + mgDebug( 1, "mgMainMenu: switch to Filter" ); + DisplayFilter(); + state = osContinue; + } break; + default: + { + mgDebug( 1, "mgMainMenu: default" ); + state = osContinue; + }; + } + } + } + else if( m_state == FILTER ) + { + mgDebug( 1, "mgMainMenu: in state FILTER" ); + + if( state == osUnknown ) + { + switch( key ) + { + case kOk: + { + // OK: Create filter and selection tree and display + mgDebug( 1, "mgMainMenu: create and apply filter" ); + } break; + case kRed: // ??? +case kYellow: + { + // Yellow always goes to playlist view + mgDebug( 1, "mgMainMenu: switch to playlist" ); + DisplayPlaylist(); + state = osContinue; + } break; + case kGreen: + { + mgDebug( 1, "mgMainMenu: switch to filter" ); + // Green: select other filters + DisplayFilterSelector(); + } break; + case kBlue: + { + // Blue: treeview + mgDebug( 1, "mgMainMenu: switch to treeview" ); + DisplayTree( m_root ); + state = osContinue; + } + default: + { + state = osContinue; + } + } + } + } + else if( m_state == TREE ) + { + mgDebug( 1, "mgMainMenu: in state TREE" ); + // Navigate with up/dn, left/right (pgup, pgdn) + // Expand with OK, Collapse with Back + if( state == osUnknown ) + { + switch( key ) + { + case kOk: + { + mgDebug( 1, "mgMainMenu: switch to filter" ); + mgSelectionTreeNode *child = CurrentNode(); // m_node->getChildren()[ Current() ]; + DisplayTree( child ); + + state = osContinue; + } break; + case kRed: + { + mgSelectionTreeNode *current = CurrentNode(); + if( current ) + { + mgDebug( 1, "mgMainMenu: add selection %s to playlist", current->getLabel().c_str() ); + // Add selection to Play + vector *tracks = current->getTracks(); + + if( tracks ) + { + m_current_playlist->appendList(tracks); + + char buffer[256]; + sprintf( buffer, "%d tracks sent to current playlist", (int) tracks->size() ); + Interface->Status( buffer ); + Interface->Flush(); + } + else + { + mgDebug(1, "No tracks for current selection" ); + } + } + else + { + mgDebug(1, "Cannot find currently selected node!" ); + } + state = osContinue; + } break; + case kYellow: + { + mgDebug( 1, "mgMainMenu: display playlist" ); + // Yellow always goes to playlist view + DisplayPlaylist(); + state = osContinue; + } break; + case kGreen: + { + mgDebug( 1, "mgMainMenu: display filter" ); + DisplayFilter(); + state = osContinue; + } break; + case kBlue: + { + mgDebug( 1, "mgMainMenu: select other tree view" ); + // Select other views -> Select other Media + DisplayTreeViewSelector(); + state = osContinue; + } break; + default: + { + state = osContinue; + } break; + } + } + else if( state == osBack ) + { + mgSelectionTreeNode *parent = m_node->getParent(); + + if( parent ) + { + mgDebug( 1, "mgMainMenu: collapse current node" ); + m_node->collapse(); + DisplayTree( parent ); + } + state = osContinue; + } + } + else if( m_state == TRACKS ) + { + // Navigate with up/dn, left/right (pgup, pgdn) + if( state == osUnknown ) + { + switch( key ) + { + case kOk: + { + // Show Song Info + state = osContinue; + } break; + case kRed: + { + // Add item to Playlist + if(Current() >= 0 && Current() < (int) m_tracklist->getNumItems()) + { + mgContentItem* item = m_tracklist->getItem( Current() ); + mgDebug(3, "Ading item %s to playlist", + item->getTitle().c_str() ); + + m_current_playlist->append( item ); + } + state = osContinue; + } break; + case kGreen: + { + // Green always goes to the filter view + DisplayFilter(); + state = osContinue; + } break; + case kYellow: + { + // Yellow always goes to playlist view + DisplayPlaylist(); + state = osContinue; + } break; + case kBlue: + { + DisplayTree( m_root ); + state = osContinue; + } break; + default: + { + state = osContinue; + } break; + } + } + else if( state == osBack ) + { + // where to go on back? + state = osContinue; + } + } + else + { + mgDebug(1, "Process key: else"); + } + + return state; +} + +void mgMainMenu::Move( int from, int to ) +{ + // check current view, perform move in the content view + if( m_state == PLAYLIST ) + { + // resort + } +} + +void mgMainMenu::DisplayTracklist() +{ + m_state = TRACKS; + mgDebug( 1, "mgBrowseMenu::DisplayTracklist"); + + Clear(); + SetButtons(); + + mgDebug( 1, "mgBrowseMenu::DisplayTracklist: %d elements received", + m_tracklist->getNumItems() ); + + static char titlestr[80]; + sprintf( titlestr, "Muggle Tracklist (%d items)", m_tracklist->getNumItems() ); + SetTitle( titlestr ); + + // create tracklist with the current filters + if( m_tracklist ) + { + delete m_tracklist; + } + + m_tracklist = m_media->getTracks(); + + for( unsigned int i = 0; i < m_tracklist->getNumItems(); i++) + { + string label = m_tracklist->getLabel( i, " " ); + Add( new cOsdItem( label.c_str() ) ); + } + + Display(); +} + +void mgMainMenu::DisplayPlaylist() +{ + m_state = PLAYLIST; + mgDebug( 1, "mgBrowseMenu::DisplayPlaylist"); + + // make sure we have a current playlist + Clear(); + + SetButtons(); + + vector* list = m_current_playlist-> getAll(); + static char titlestr[80]; + sprintf( titlestr, "Muggle Playlist (%d items)", list->size() ); + SetTitle( titlestr ); + + mgDebug( 1, "mgBrowseMenu::DisplayPlaylist: %d elements received", + list->size() ); + + for( unsigned int i = 0; i < m_current_playlist->getNumItems(); i++) + { + string label = m_current_playlist->getLabel( i, " " ); + Add( new cOsdItem( label.c_str() ) ); + } + + Display(); +} + +void mgMainMenu::DisplayTrackInfo() +{ + m_state = PLAYLIST_TRACKINFO; + // show info of the currently playing track +} + +void mgMainMenu::DisplayAlbumInfo() +{ + m_state = PLAYLIST_ALBUMINFO; + // show info of the currently playing track +} + +void mgMainMenu::DisplayTree( mgSelectionTreeNode* node ) +{ + m_state = TREE; + + if( node->expand( ) ) + { + Clear(); + + char buffer[256]; + sprintf( buffer, "Muggle - %s", node->getLabel().c_str() ); + + SetTitle( buffer ); + SetButtons(); + + m_node = node; + mgDebug( 1, "mgBrowseMenu::DisplaySelection: node %s received", node->getLabel().c_str() ); + vector children = node->getChildren(); + + mgDebug( 1, "mgBrowseMenu::DisplaySelection: %d elements received", children.size() ); + + for( vector::iterator iter = children.begin(); + iter != children.end(); + iter ++ ) + { + Add( new mgMenuTreeItem( *iter ) ); + } + + mgDebug( 1, "mgBrowseMenu::DisplayNode: Children added to OSD" ); + } + Display(); +} + +void mgMainMenu::DisplayTreeViewSelector() +{ +} + +void mgMainMenu::DisplayFilter() +{ + m_state = FILTER; + Clear(); + + mgDebug( 1, "Creating Muggle filter view" ); + SetButtons(); + + SetTitle( "Muggle Filter View" ); + + Add( new cMenuEditStrItem( "Name ", m_filtername, 32, alpha_num_keys ) ); + Add( new cMenuEditBoolItem( "Filter ", &m_filter, "Off", "On" ) ); + + // Separator??? + + Add( new cMenuEditStrItem( "Title ", m_title, 32, alpha_num_keys ) ); + Add( new cMenuEditStrItem( "Interpret", m_interpret, 32, alpha_num_keys ) ); + Add( new cMenuEditStrItem( "Album ", m_album, 32, alpha_num_keys ) ); + Add( new cMenuEditStrItem( "Playlist ", m_playlist, 32, alpha_num_keys ) ); + Add( new cMenuEditIntItem( "Year from", &m_year_min, 1600, 9999 ) ); + Add( new cMenuEditIntItem( "Year to ", &m_year_max, 1600, 9999 ) ); + + Display(); +} + +void mgMainMenu::DisplayFilterSelector() +{ + +} diff --git a/vdr_menu.h b/vdr_menu.h new file mode 100644 index 0000000..480c687 --- /dev/null +++ b/vdr_menu.h @@ -0,0 +1,87 @@ +/*******************************************************************/ +/*! \file vdr_menu.h + * \brief Implements menu handling for broswing media libraries within VDR + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/01 18:22:53 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + +#ifndef _VDR_MENU_H +#define _VDR_MENU_H + +#include + +class mgMedia; +class mgSelectionTreeNode; +class mgPlaylist; +class mgTracklist; + +class mgMenuTreeItem : public cOsdItem +{ + public: + + mgMenuTreeItem( mgSelectionTreeNode *node ); + + mgSelectionTreeNode *Node(); + + void Set(); + + private: + + mgSelectionTreeNode *m_node; + +}; + +class mgMainMenu : public cOsdMenu +{ + public: + + mgMainMenu(); + + mgSelectionTreeNode *CurrentNode(); + + eOSState ProcessKey(eKeys Key); + void Move( int from, int to); + + protected: + + enum MuggleStatus + { + TREE, FILTER, TRACKS, + PLAYLIST, PLAYLIST_TRACKINFO, PLAYLIST_ALBUMINFO, + }; + + // To be rewritten mode dependent + void SetButtons(); + + void DisplayTree( mgSelectionTreeNode *node ); + void DisplayTreeViewSelector(); + + void DisplayFilter(); + void DisplayFilterSelector(); + + void DisplayPlaylist(); + void DisplayTracklist(); + void DisplayTrackInfo(); + void DisplayAlbumInfo(); + + private: + + // content stuff + mgMedia *m_media; + mgSelectionTreeNode *m_root; + mgSelectionTreeNode *m_node; + mgPlaylist *m_current_playlist; + mgTracklist *m_tracklist; + + // filter items + char *m_title, *m_interpret, *m_album, *m_playlist, *m_filtername; + int m_year_min, m_year_max, m_filter; + + MuggleStatus m_state; +}; + +#endif -- cgit v1.2.3 From d7633e8526680c06efe373b00d9789371f5dd2b8 Mon Sep 17 00:00:00 2001 From: RaK Date: Sun, 1 Feb 2004 19:01:47 +0000 Subject: Added documentation of menu handling original created by Andi git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@7 e10066b5-e1e2-0310-b819-94efdf66514b --- menu.txt | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 menu.txt diff --git a/menu.txt b/menu.txt new file mode 100644 index 0000000..cdea3f8 --- /dev/null +++ b/menu.txt @@ -0,0 +1,73 @@ +PLAYLIST +--------- +OK --// should start playing in the future +BACK -- + +UP select previous item (VDR-mechanism) +DOWN select next item (VDR-mechanism) +LEFT selection pg-up (VDR-mechanism) +RIGHT selection pg-down (VDR-mechanism) + +RED ==>display Tracklist(current filter setting) +GREEN ==>DisplayFilter +Yellow ==>DisplayTrackInfo(current) +Blue ==>DisplayTree( m_root ) + +FILTER +--------- + + + +TREE +--------- +OK Leaf: ==>DisplayTrackInfo( leaf ) + Node: ==> DisplayTree( child ); // expand +BACK ==> DisplayTree( parent ); // collapse + +UP select previous item (VDR-mechanism) +DOWN select next item (VDR-mechanism) +LEFT selection pg-up (VDR-mechanism) +RIGHT selection pg-down (VDR-mechanism) + +RED add all tracks under this node to playlist +GREEN ==>DisplayFilter +YELLOW ==>DisplayPlaylist(); +BLUE -- + + +TRACKS (Tracklist) +------------------ +OK DisplayTrackInfo(current) +BACK ==> DisplayTree( parent ); // collapse + +UP select previous item (VDR-mechanism) +DOWN select next item (VDR-mechanism) +LEFT selection pg-up (VDR-mechanism) +RIGHT selection pg-down (VDR-mechanism) + +RED add currend item to playlist +GREEN ==>DisplayFilter +YELLOW ==>DisplayPlaylist(); +BLUE ==>DisplayTree(root) + +TRACKINFO (previous == TREE || previous == TRACKS ) +------------------ +OK PlayItem +BACK go to previous view (==> DisplayTracklist || ==>DisplayTree ) + +RED add currend item to playlist +GREEN ==>DisplayFilter +YELLOW ==>DisplayPlaylist(); +BLUE -- + +TRACKINFO (previous == playlist ) +------------------ +OK PlayItem +BACK ==>DisplayPlaylist(); + +RED -- +GREEN ==>DisplayFilter +YELLOW ==>DisplayPlaylist(); // also on back +BLUE -- + + -- cgit v1.2.3 From 9759e698af717c16757bba005c77386fafe338f9 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 1 Feb 2004 19:06:11 +0000 Subject: Added author list git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@8 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 4e04b83..7f57c51 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ This is a "plugin" for the Video Disk Recorder (VDR). -Written by: Your Name +Written by: Ralf Klüber, Andi Kellner, Lars von Wedel Project's homepage: URL -- cgit v1.2.3 From c0b0e6c64d29eb1b279b58fcaec185599e248495 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 1 Feb 2004 19:08:44 +0000 Subject: Added Lars' email git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@9 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 7f57c51..7f1e222 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ This is a "plugin" for the Video Disk Recorder (VDR). -Written by: Ralf Klüber, Andi Kellner, Lars von Wedel +Written by: Ralf Klüber, Andi Kellner, Lars von Wedel Project's homepage: URL -- cgit v1.2.3 From 53ffef33f9e32ef4368fb55d8c87666ee7d667f5 Mon Sep 17 00:00:00 2001 From: RaK Date: Sun, 1 Feb 2004 19:12:19 +0000 Subject: *** empty log message *** git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@10 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 7f1e222..692baf1 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ This is a "plugin" for the Video Disk Recorder (VDR). -Written by: Ralf Klüber, Andi Kellner, Lars von Wedel +Written by: Ralf Klüber , Andi Kellner, Lars von Wedel Project's homepage: URL -- cgit v1.2.3 From 89b813bb617c34c24a22bc05442331cbbe71f061 Mon Sep 17 00:00:00 2001 From: RaK Date: Sun, 1 Feb 2004 22:12:56 +0000 Subject: Added new View Genre -> Year -> Track git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@11 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 6 +++-- README | 4 +++- gd_content_interface.c | 61 ++++++++++++++++++++++++++++++++++++++++++++------ menu.txt | 5 ++++- muggle.h | 14 ++++++++---- sh_muggle2.c | 4 ++-- vdr_menu.h | 15 +++++++++---- 7 files changed, 88 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 8728cc3..609c3dc 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ +# $Id: Makefile,v 1.2 2004/02/01 22:12:56 RaK Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -48,7 +48,8 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o sh_dummy_content.o gd_content_interface.o mg_tools.o mg_media.o -BINOBJS = sh_shell_osd_plugin.o sh_shell_osd.o sh_shell_osd_menuitems.o muggle.o vdr_menu.o content_interface.o dummy_content.o gd_contentinterface.o muggle_tools.o mgmedia.o +#BINOBJS = sh_shell_osd_plugin.o sh_shell_osd.o sh_shell_osd_menuitems.o muggle.o vdr_menu.o content_interface.o dummy_content.o gd_contentinterface.o muggle_tools.o mgmedia.o +BINOBJS = muggle.o vdr_menu.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o ### Implicit rules: @@ -89,3 +90,4 @@ dist: clean clean: @-rm -f $(OBJS) $(BINOBJS) $(DEPFILE) *.so *.tgz core* *~ @-rm -f sh_muggle sh_muggle2 + diff --git a/README b/README index 692baf1..4f6cbbf 100644 --- a/README +++ b/README @@ -1,6 +1,8 @@ This is a "plugin" for the Video Disk Recorder (VDR). -Written by: Ralf Klüber , Andi Kellner, Lars von Wedel +Written by: Andi Kellner, + Lars von Wedel + Ralf Klüber , Project's homepage: URL diff --git a/gd_content_interface.c b/gd_content_interface.c index 5707f78..becae1c 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/01 22:12:56 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -40,7 +40,7 @@ int GdInitDatabase(MYSQL *db) } if(mysql_real_connect(db,"localhost","root","", - "GiantDisc2",0,NULL,0) == NULL) + "GiantDisc",0,NULL,0) == NULL) { return -2; } @@ -733,7 +733,7 @@ bool GdTreeNode::expand() mgWarning("Node already expanded\n"); return true; } - if (m_level == 1) + if (m_level == 1 && m_view < 100) { m_view = atoi(m_id.c_str()); } @@ -845,7 +845,43 @@ bool GdTreeNode::expand() return false; } break; - default: + case 4: // Genre -> Year -> Track + if(m_level == 1) { // Genre + sprintf(sqlbuff, + "SELECT DISTINCT genre.genre,tracks.genre1" + " FROM genre,tracks" + " WHERE (genre.id=tracks.genre1) AND" + " %s" + " ORDER BY genre.genre" + , m_restriction.c_str()); + idfield = "tracks.genre1"; + } else if (m_level == 2) { // Year + sprintf(sqlbuff, + "SELECT DISTINCT tracks.year,tracks.year" + " FROM genre,tracks" + " WHERE (genre.id=tracks.genre1) AND" + " %s" + " ORDER BY tracks.year" + , m_restriction.c_str()); + idfield = "tracks.year"; + } else if (m_level == 3) { // Track + sprintf(sqlbuff, + "SELECT DISTINCT" + " CONCAT(tracks.artist,' - ',tracks.title) AS title" + " ,tracks.id" + " FROM tracks,genre" + " WHERE (genre.id=tracks.genre1) AND" + " %s" + " ORDER BY tracks.title" + , m_restriction.c_str()); + idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; + default: mgError("View '%d' not yet implemented", m_view); } @@ -871,7 +907,7 @@ bool GdTreeNode::expand() m_children.push_back(new_child); numchild++; } - } else { + } else if (m_view <100) { new_child = new GdTreeNode(this, // parent "1" , // id "Artist -> Album -> Title", // label, @@ -887,6 +923,17 @@ bool GdTreeNode::expand() "Artist -> Track" , // label, "1"); m_children.push_back(new_child); + new_child = new GdTreeNode(this, // parent + "4" , // id + "Genre -> Year -> Track" , // label, + "1"); + m_children.push_back(new_child); + } else { + new_child = new GdTreeNode(this, // parent + "100" , // id + "Search Result", // label, + m_restriction); + m_children.push_back(new_child); } m_expanded = true; diff --git a/menu.txt b/menu.txt index cdea3f8..08c7b2e 100644 --- a/menu.txt +++ b/menu.txt @@ -29,14 +29,17 @@ DOWN select next item (VDR-mechanism) LEFT selection pg-up (VDR-mechanism) RIGHT selection pg-down (VDR-mechanism) -RED add all tracks under this node to playlist +RED add all tracks under currently highlighted node to playlist GREEN ==>DisplayFilter YELLOW ==>DisplayPlaylist(); BLUE -- + // Should be in the future: Collapse all and goto default View TRACKS (Tracklist) ------------------ + // Andi, ich kapier es nicht. Was unterscheidet Tracks from Tree? + // IMHO ist Tracks eine Sonderform von Tree, der nur Leaves hat, oder? OK DisplayTrackInfo(current) BACK ==> DisplayTree( parent ); // collapse diff --git a/muggle.h b/muggle.h index e6ed1c2..6fbe44d 100644 --- a/muggle.h +++ b/muggle.h @@ -2,17 +2,23 @@ /*! \file muggle.h * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/01 22:12:56 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ */ /*******************************************************************/ #ifndef _MUGGLE_H #define _MUGGLE_H -#include +#undef SHELL_TEST + +#ifdef SHELL_TEST + #include "shell_plugin.h" +#else + #include +#endif class mgMuggle : public cPlugin { diff --git a/sh_muggle2.c b/sh_muggle2.c index e3748a4..4c98da6 100644 --- a/sh_muggle2.c +++ b/sh_muggle2.c @@ -4,8 +4,8 @@ #include "myosd.h" #include "muggle.h" -#include "mgmedia.h" -#include "muggle_tools.h" +#include "mg_media.h" +#include "mg_tools.h" #include eKeys waitForKey() diff --git a/vdr_menu.h b/vdr_menu.h index 480c687..a0807a4 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,17 +2,24 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/01 22:12:56 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ */ /*******************************************************************/ #ifndef _VDR_MENU_H #define _VDR_MENU_H -#include +#undef SHELL_TEST + +#ifdef SHELL_TEST + #include "myosd.h" + #include "mymenuitems.h" +#else + #include +#endif class mgMedia; class mgSelectionTreeNode; -- cgit v1.2.3 From 71c1f01d3921445579feea0015f550be2f9e98e0 Mon Sep 17 00:00:00 2001 From: RaK Date: Sun, 1 Feb 2004 22:35:23 +0000 Subject: Added View Artist -> Tracks git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@12 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 5 ++-- gd_content_interface.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 609c3dc..ad5a8df 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.2 2004/02/01 22:12:56 RaK Exp $ +# $Id: Makefile,v 1.3 2004/02/01 22:35:22 RaK Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -48,8 +48,7 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o sh_dummy_content.o gd_content_interface.o mg_tools.o mg_media.o -#BINOBJS = sh_shell_osd_plugin.o sh_shell_osd.o sh_shell_osd_menuitems.o muggle.o vdr_menu.o content_interface.o dummy_content.o gd_contentinterface.o muggle_tools.o mgmedia.o -BINOBJS = muggle.o vdr_menu.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o +BINOBJS = sh_shell_osd_plugin.o sh_shell_osd.o sh_shell_osd_menuitems.o muggle.o vdr_menu.o content_interface.o dummy_content.o gd_content_interface.o muggle_tools.o mgmedia.o ### Implicit rules: diff --git a/gd_content_interface.c b/gd_content_interface.c index becae1c..f3f1b47 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/01 22:12:56 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/01 22:35:23 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -40,7 +40,7 @@ int GdInitDatabase(MYSQL *db) } if(mysql_real_connect(db,"localhost","root","", - "GiantDisc",0,NULL,0) == NULL) + "GiantDisc2",0,NULL,0) == NULL) { return -2; } @@ -795,7 +795,9 @@ bool GdTreeNode::expand() idfield = "album.artist"; } else if(m_level == 3) { // genre -> artist -> album sprintf(sqlbuff, - "SELECT DISTINCT album.title,tracks.sourceid" + "SELECT DISTINCT" + " album.title," + " tracks.sourceid" " FROM tracks,genre,album" " WHERE (genre.id=tracks.genre1) AND" " (album.cddbid=tracks.sourceid) AND" @@ -881,7 +883,62 @@ bool GdTreeNode::expand() return false; } break; - default: + case 5: // Album -> Tracks + if(m_level == 1) { // Album + sprintf(sqlbuff, + "SELECT DISTINCT" + " CONCAT(album.artist,' - ',album.title) AS title," + " album.cddbid" + " FROM album" + " WHERE 1 AND" + " %s" + " ORDER BY title" + , m_restriction.c_str()); + idfield = "tracks.genre1"; + } else if (m_level == 2) { // + sprintf(sqlbuff, + "SELECT DISTINCT tracks.title, tracks.id" + " FROM tracks,genre,album" + " WHERE (genre.id=tracks.genre1) AND" + " (album.cddbid=tracks.sourceid) AND" + " %s" + " ORDER BY tracks.tracknb" + , m_restriction.c_str()); + idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + case 100: + if (m_level == 1) { + sprintf(sqlbuff, + "SELECT DISTINCT" + " CONCAT(album.artist,' - ',album.title) as title," + " album.cddbid" + " FROM album,genre" + " WHERE " + " %s" + " ORDER BY title" + , m_restriction.c_str()); + idfield = "tracks.sourceid"; + } else if (m_level == 2) { + sprintf(sqlbuff, + "SELECT tracks.title,tracks.id" + " FROM tracks,genre,album" + " WHERE (genre.id=tracks.genre1) AND" + " (album.cddbid=tracks.sourceid) AND" + " %s" + " ORDER BY tracks.tracknb" + , m_restriction.c_str()); + idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; +default: mgError("View '%d' not yet implemented", m_view); } @@ -928,6 +985,11 @@ bool GdTreeNode::expand() "Genre -> Year -> Track" , // label, "1"); m_children.push_back(new_child); + new_child = new GdTreeNode(this, // parent + "5" , // id + "Album -> Track" , // label, + "1"); + m_children.push_back(new_child); } else { new_child = new GdTreeNode(this, // parent "100" , // id -- cgit v1.2.3 From f93735c67796b4d300737aff8cff39061dae465e Mon Sep 17 00:00:00 2001 From: RaK Date: Sun, 1 Feb 2004 23:13:33 +0000 Subject: *** empty log message *** git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@13 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index f3f1b47..cf8e89b 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/01 22:35:23 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/01 23:13:33 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -40,7 +40,7 @@ int GdInitDatabase(MYSQL *db) } if(mysql_real_connect(db,"localhost","root","", - "GiantDisc2",0,NULL,0) == NULL) + "GiantDisc",0,NULL,0) == NULL) { return -2; } @@ -910,7 +910,8 @@ bool GdTreeNode::expand() m_expanded = false; return false; } - case 100: + break; + case 100: // Albumsearch result if (m_level == 1) { sprintf(sqlbuff, "SELECT DISTINCT" @@ -938,7 +939,7 @@ bool GdTreeNode::expand() return false; } break; -default: + default: mgError("View '%d' not yet implemented", m_view); } @@ -992,7 +993,7 @@ default: m_children.push_back(new_child); } else { new_child = new GdTreeNode(this, // parent - "100" , // id + "" , // id "Search Result", // label, m_restriction); m_children.push_back(new_child); -- cgit v1.2.3 From d79d2d24fcf5a3b4594face6a8c7e7bcb36d1dbc Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 2 Feb 2004 02:01:11 +0000 Subject: data structures for filters prepared and partially implemented git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@14 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 169 +++++++++++++++++++++++++++++++++++++++++++++---- gd_content_interface.h | 23 +++++-- mg_media.c | 165 +++++++++++++++++++++++++++++++++-------------- mg_media.h | 133 ++++++++++++++++++++++++-------------- sh_dummy_content.c | 56 ++++++++++++++-- sh_dummy_content.h | 11 ++-- 6 files changed, 436 insertions(+), 121 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index cf8e89b..ed74d9d 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/01 23:13:33 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/02/02 02:01:11 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author file owner: $Author: MountainMan $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -47,9 +47,9 @@ int GdInitDatabase(MYSQL *db) return 0; } -vector GdGetStoredPlaylists(MYSQL db) +vector *GdGetStoredPlaylists(MYSQL db) { - vector list; + vector* list = new vector(); MYSQL_RES *result; MYSQL_ROW row; @@ -57,11 +57,84 @@ vector GdGetStoredPlaylists(MYSQL db) while((row = mysql_fetch_row(result)) != NULL) { - list.push_back(row[0]); + list->push_back(row[0]); } return list; } +/*******************************************************************/ +/* class class gdTrackFilters */ +/********************************************************************/ +gdTrackFilters::gdTrackFilters() +{ + clear(); +} +gdTrackFilters::~gdTrackFilters() +{ +} +void gdTrackFilters::clear() +{ + mgFilter* filter ; + // remove old filters + for(vector::iterator iter = m_filters.begin(); + iter != m_filters.end(); iter++) + { + delete (*iter); + } + m_filters.clear(); + + // create an initial set of filters with empty values + // title + filter = new mgFilterString("title", ""); + m_filters.push_back(filter); + // artist + filter = new mgFilterString("artist", ""); + m_filters.push_back(filter); + // genre + filter = new mgFilterString("genre", ""); + m_filters.push_back(filter); + // year + filter = new mgFilterInt("year", -1); + m_filters.push_back(filter); + // rating + filter = new mgFilterInt("rating", -1); + m_filters.push_back(filter); +} + +string gdTrackFilters::CreateSQL() +{ + string sql_str = "1"; + + for(vector::iterator iter = m_filters.begin(); + iter != m_filters.end(); iter++) + { + if(strcmp((*iter)->getName(), "title") == 0 ) + { + sql_str = "AND tracks.title like '" + + (*iter)->getStrVal() + "%'"; + } + else if(strcmp((*iter)->getName(), "artist") == 0 ) + { + sql_str = "AND tracks.artist like '" + + (*iter)->getStrVal() + "%'"; + } + else if(strcmp((*iter)->getName(), "genre") == 0 ) + { + sql_str = "AND genre.name like '" + + (*iter)->getStrVal() + "%'"; + } + else if(strcmp((*iter)->getName(), "year") == 0 ) + { + sql_str = "AND tracks.year = " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "rating") == 0 ) + { + sql_str = "AND tracks.rating >= " + (*iter)->getStrVal(); + } + } + return sql_str; +} + /*******************************************************************/ /* class mgTack */ /********************************************************************/ @@ -698,6 +771,42 @@ GdTreeNode::~GdTreeNode() +/*! + ***************************************************************************** + * \brief checks if this node can be further expandded or not + * \true, if node ia leaf node, false if node can be expanded + * + ****************************************************************************/ +bool GdTreeNode::isLeafNode() +{ + if( m_level == 0) + return false; + switch(m_view) + { + case 1: // artist -> album -> title + if( m_level <= 3 ) + { + return false; + } + break; + case 2: // genre -> artist -> album -> track + if( m_level <= 3 ) + { + return false; + } + break; + case 3: // Artist -> Track + if( m_level <= 2 ) + { + return false; + } + break; + default: + mgError("View '%d' not yet implemented", m_view); + } + return true; +} + /*! ***************************************************************************** * \brief compute children on the fly @@ -1044,10 +1153,46 @@ vector* GdTreeNode::getTracks() } +/*! + ***************************************************************************** + * \brief returns the first track matchin the restrictions of this node + * assuming we are in a leaf node, this returns the track represented by the + * the leaf + ****************************************************************************/ +mgContentItem* GdTreeNode::getSingleTrack() +{ + MYSQL_ROW row; + MYSQL_RES *result; + int nrows; + int nfields; + mgContentItem* track = NULL; + int trackid; - - - - - - + // get all tracks satisying the restrictions of this node + mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str()); + + result = mgSqlReadQuery(&m_db, + "SELECT tracks.id FROM tracks, album, genre WHERE %s" + " AND album.cddbid=tracks.sourceid AND genre.id=tracks.genre1", + m_restriction.c_str()); + nrows = mysql_num_rows(result); + nfields = mysql_num_fields(result); + + if( nrows != 1 ) + { + mgWarning( "GdTreeNode::getSingleTrack() :SQL call returned %d tracks, using only the first", + nrows ); + } + // get the first row + if( ( row = mysql_fetch_row(result)) != NULL ) + { + // row[0] is the trackid + if(sscanf(row[0], "%d", &trackid) != 1) + { + mgError("Can not extract integer track id from '%s'", + row[0]); + } + track = new mgGdTrack(trackid, m_db); + } + return track; +} diff --git a/gd_content_interface.h b/gd_content_interface.h index 8e63956..2c2cbef 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -4,10 +4,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 02:01:11 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * Declares main classes of for content items and interfaces to SQL databases * @@ -29,10 +29,23 @@ #include #include "mg_content_interface.h" +#include "mg_media.h" // non-member function int GdInitDatabase(MYSQL *db); -std::vector GdGetStoredPlaylists(MYSQL db); +std::vector *GdGetStoredPlaylists(MYSQL db); + + +class gdTrackFilters: public mgTrackFilters +{ + public: + gdTrackFilters(); + ~gdTrackFilters(); + + virtual std::string CreateSQL(); + virtual void clear(); + +}; /*! ******************************************************************* @@ -187,10 +200,12 @@ public: virtual ~GdTreeNode(); // compute children on the fly + virtual bool isLeafNode(); virtual bool expand(); // access data in current node virtual std::vector* getTracks(); + virtual mgContentItem* getSingleTrack(); }; #endif /* END _GD_CONTENT_INTERFACE_H */ diff --git a/mg_media.c b/mg_media.c index 26d8b8f..be86621 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 02:01:11 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * */ @@ -22,78 +22,133 @@ using namespace std; -mgFilters::mgFilters() +mgFilter::mgFilter(const char* name) { + m_name = strdup(name); } - -mgFilters::~mgFilters() +mgFilter::~mgFilter() { + free(m_name); } -int mgFilters::getNumFilters() +const char* mgFilter::getName() { - return 0; + return m_name; } -string mgFilters::getName(int filter) +mgFilterInt::mgFilterInt(const char *name, int value, int min, int max) + : mgFilter(name) +{ + m_type = INT; + m_intval = value; + m_min = min; + m_max = max; +} +mgFilterInt::~mgFilterInt() { - return 0; } -int mgFilters::getValue(int filter) +int mgFilterInt::getVal() { - return 0; + return m_intval; } +string mgFilterInt::getStrVal() +{ + char buffer[20]; + sprintf(buffer, "%d", m_intval); -mgFilters::filterType mgFilters::getType(int filter) + return (string)buffer; +} +int mgFilterInt::getMin() { - return NUMBER; + return m_min; +} +int mgFilterInt::getMax() +{ + return m_max; } -// for NUMBER filters +void mgFilterInt::setVal(int value) +{ + m_intval = value; +} -int mgFilters::getMin(int filter) +mgFilterString::mgFilterString(const char *name, const char* value) + : mgFilter(name) { - return 0; + m_type = STRING; + m_strval = strdup(value); +} +mgFilterString::~mgFilterString() +{ + if(m_strval) + { + free(m_strval); + } } -int mgFilters::getMax(int filter) +const char* mgFilterString::getVal() { - return 0; + return m_strval; +} +string mgFilterString::getStrVal() +{ + + return (string) m_strval; } -// for CHOICE +void mgFilterString::setVal(const char* value) +{ + if(m_strval) + { + free(m_strval); + } + m_strval = strdup(value); +} +mgFilterBool::mgFilterBool(const char *name, bool value) + : mgFilter(name) +{ + m_type = BOOL; + m_bval = value; +} +mgFilterBool::~mgFilterBool() +{ +} -vector mgFilters::getChoices() +bool mgFilterBool::getVal() { - return vector(); + return m_bval; } -int mgFilters::getCurrent(int filter) +string mgFilterBool::getStrVal() { - return 0; + if(m_bval) + return "true"; + else + return "false"; } - // check, if a value is correct -bool mgFilters::checkValue(int filter, string value) +void mgFilterBool::setVal(bool value) { - return false; + m_bval = value; } -bool mgFilters::checkValue(int filter, int value) +mgTrackFilters::mgTrackFilters() { - return false; } - -// finally set the values -bool mgFilters::setValue(int filter, string value) +mgTrackFilters::~mgTrackFilters() { - return false; + for(vector::iterator iter = m_filters.begin(); + iter != m_filters.end(); iter++) + { + delete (*iter); + } + m_filters.clear(); } -bool mgFilters::setValue(int filter, int value) +vector *mgTrackFilters::getFilters() { - return false; + return &m_filters; } /*! @@ -105,9 +160,9 @@ bool mgFilters::setValue(int filter, int value) mgMedia::mgMedia(contentType mediatype) { int errval = 0; - + mgTrackFilters *m_trackfilter; m_mediatype = mediatype; - m_filter = "1"; + m_sql_trackfilter = "1"; m_defaultView = 1; // now initialize the database @@ -130,6 +185,20 @@ mgMedia::mgMedia(contentType mediatype) mgError("Error connecting to database\n"); } + mgDebug(3, "Initializing track filters"); + switch(m_mediatype) + { + case DUMMY: + { + errval = DummyInitDatabase(&m_db); + break; + } + case GD_MP3: + { + errval = GdInitDatabase(&m_db); + mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); + } + } } mgMedia::~mgMedia() @@ -157,26 +226,28 @@ mgSelectionTreeNode* mgMedia::getSelectionRoot() case DUMMY: return new DummyTreeNode(m_db, m_defaultView); case GD_MP3: - return new GdTreeNode(m_db, m_defaultView, m_filter); + return new GdTreeNode(m_db, m_defaultView, m_sql_trackfilter); } mgError("implementation Error"); // we should never get here return NULL; } -mgFilters mgMedia::getActiveFilters() +vector *mgMedia::getTrackFilters() { switch(m_mediatype) { case DUMMY: - return mgFilters(); + //return mgFilters(); case GD_MP3: - return mgFilters(); + // return m_trackfilters; + default: + break; } mgError("implementation Error"); // we should never get here - return mgFilters(); + return NULL; } -void mgMedia::setFilters(mgFilters filters) +void mgMedia::setTrackFilters(vector *filters) { } @@ -204,7 +275,7 @@ mgPlaylist* mgMedia::loadPlaylist(string name) return NULL; } -vector mgMedia::getStoredPlaylists() +vector *mgMedia::getStoredPlaylists() { switch(m_mediatype) { @@ -214,7 +285,7 @@ vector mgMedia::getStoredPlaylists() return GdGetStoredPlaylists(m_db); } mgError("implementation Error"); // we should never get here - return vector(); + return new vector(); } vector mgMedia::getDefaultCols() @@ -243,11 +314,11 @@ mgTracklist* mgMedia::getTracks() { case DUMMY: - tracks = new DummyTracklist(m_db, m_filter); + tracks = new DummyTracklist(m_db, m_sql_trackfilter); tracks->setDisplayColumns(getDefaultCols()); return tracks; case GD_MP3: - tracks = new GdTracklist(m_db, m_filter); + tracks = new GdTracklist(m_db, m_sql_trackfilter); tracks->setDisplayColumns(getDefaultCols()); return tracks; } diff --git a/mg_media.h b/mg_media.h index f4cd64f..9e8c3e7 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 02:01:11 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * */ @@ -26,50 +26,89 @@ class mgSelectionTreeNode; /*! ******************************************************************* - * \class mgFilters + * \class mgFilter * - * \brief stores a set of search constraints to the media database ********************************************************************/ -class mgFilters +class mgFilter { - typedef enum filterType - { - NUMBER, // integer number (with upper and lower limits) - STRING, // any string - REGEXP_STRING, // string containing wildcard symbol '*' - CHOICE // value fro ma list of choices - }filterType; - public: - std::vector fields; - std::vector values; - - mgFilters(); - ~mgFilters(); - - int getNumFilters(); - std::string getName(int filter); - int getValue(int filter); - filterType getType(int filter); - - // for NUMBER filters - int getMin(int filter); - int getMax(int filter); - - // for CHOICE - std::vector getChoices(); - int getCurrent(int filter); - - // check, if a value is correct - bool checkValue(int filter, std::string value); - bool checkValue(int filter, int value); - - // finally set the values - - bool setValue(int filter, std::string value); - bool setValue(int filter, int value); + typedef enum filterType { UNDEF=0, INT, STRING, BOOL }filterType; + protected: + filterType m_type; + char* m_name; + + public: + + mgFilter(const char* name); + virtual ~mgFilter(); + filterType getType(); + const char* getName(); + virtual std::string getStrVal()=0; }; +class mgFilterInt : public mgFilter +{ + private: + int m_min; + int m_max; + int m_intval; + + public: + mgFilterInt(const char *name, int value, int min = 0, int max = INT_MAX); + virtual ~mgFilterInt(); + + int getVal(); + int getMin(); + int getMax(); + virtual std::string getStrVal(); + + void setVal(int value); +}; + +class mgFilterString : public mgFilter +{ + private: + char* m_strval; + + public: + mgFilterString(const char *name, const char* value); + virtual ~mgFilterString(); + + const char* getVal(); + virtual std::string getStrVal(); + + void setVal(const char* val); +}; + +class mgFilterBool : public mgFilter +{ + private: + bool m_bval; + + public: + mgFilterBool(const char *name, bool value); + virtual ~mgFilterBool(); + + bool getVal(); + virtual std::string getStrVal(); + + void setVal(bool val); +}; + +class mgTrackFilters +{ + protected: + std::vector m_filters; + public: + mgTrackFilters(); + virtual ~mgTrackFilters(); + + std::vector *getFilters(); + + virtual std::string CreateSQL()=0; + virtual void clear()=0; + +}; /*! ******************************************************************* @@ -89,7 +128,7 @@ class mgMedia private: MYSQL m_db; contentType m_mediatype; - std::string m_filter; + std::string m_sql_trackfilter; int m_defaultView; public: @@ -100,18 +139,14 @@ class mgMedia mgSelectionTreeNode* getSelectionRoot(); - mgFilters getActiveFilters(); + std::vector *getTrackFilters(); - void setFilters(mgFilters filters); - void setFilters(std::string sql) - { - m_filter=sql; - } + void setTrackFilters(std::vector *filters); // playlist management mgPlaylist* createTemporaryPlaylist(); mgPlaylist* loadPlaylist( std::string name ); - std::vector getStoredPlaylists(); + std::vector *getStoredPlaylists(); std::vector getDefaultCols(); mgTracklist* getTracks(); diff --git a/sh_dummy_content.c b/sh_dummy_content.c index a6afcd9..27c23bd 100644 --- a/sh_dummy_content.c +++ b/sh_dummy_content.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 02:01:11 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * DUMMY * @@ -34,7 +34,7 @@ #define NUM_TRACKS_PER_ALBUM 9 int DummyInitDatabase(MYSQL *db){return 0;} -vector DummyGetStoredPlaylists(MYSQL db){ return vector();} +vector *DummyGetStoredPlaylists(MYSQL db){ return new vector();} /*******************************************************************/ /* class mgTack */ @@ -423,6 +423,27 @@ DummyTreeNode::~DummyTreeNode() m_children.clear(); } +/*! + ***************************************************************************** + * \brief checks if this node can be further expandded or not + * \true, if node ia leaf node, false if node can be expanded + * + ****************************************************************************/ +bool DummyTreeNode::isLeafNode() +{ + switch(m_view) + { + case 1: // artist -> album -> title + if( m_level <= 3 ) + { + return true; + } + break; + default: + mgError("View '%d' not yet implemented", m_view); + } + return false; +} /*! @@ -549,6 +570,33 @@ vector* DummyTreeNode::getTracks() return dummy; } +/*! + ***************************************************************************** + * \brief returns the first track matchin the restrictions of this node + * assuming we are in a leaf node, this returns the track represented by the + * the leaf + ****************************************************************************/ +mgContentItem* DummyTreeNode::getSingleTrack() +{ + // get all tracks satisying the restrictions of this node + mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str()); + + mgContentItem* track = new DummyTrack(atoi(m_id.c_str()), m_db); + + return track; +} + + + + + + + + + + + + diff --git a/sh_dummy_content.h b/sh_dummy_content.h index 71d3ce7..41e526f 100644 --- a/sh_dummy_content.h +++ b/sh_dummy_content.h @@ -2,10 +2,10 @@ /*! \file dummy_content.h * \brief Dummy Data Objects for testing Muggle ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 02:01:11 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * Declares main classes of for content items and interfaces to SQL databases * @@ -22,8 +22,7 @@ using namespace std; // non-member function int DummyInitDatabase(MYSQL *db); -vector DummyGetStoredPlaylists(MYSQL db); - +vector *DummyGetStoredPlaylists(MYSQL db); /*! ******************************************************************* * \class DummyTrack @@ -152,9 +151,11 @@ public: ~DummyTreeNode(); // compute children o^xn the fly + virtual bool isLeafNode(); virtual bool expand(); virtual vector* getTracks(); + virtual mgContentItem* getSingleTrack(); }; #endif /* END _CONTENT_INTERFACE_H */ -- cgit v1.2.3 From 41b962e0042a8e3cf66caeea7fd6610648175941 Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 2 Feb 2004 17:57:53 +0000 Subject: values m_ival, m_bbal, m_strval are now public git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@15 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_media.c | 35 ++--------------------------------- mg_media.h | 20 ++++++++------------ 2 files changed, 10 insertions(+), 45 deletions(-) diff --git a/mg_media.c b/mg_media.c index be86621..4027ab9 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 02:01:11 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/02 17:57:53 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -48,10 +48,6 @@ mgFilterInt::~mgFilterInt() { } -int mgFilterInt::getVal() -{ - return m_intval; -} string mgFilterInt::getStrVal() { char buffer[20]; @@ -68,11 +64,6 @@ int mgFilterInt::getMax() return m_max; } -void mgFilterInt::setVal(int value) -{ - m_intval = value; -} - mgFilterString::mgFilterString(const char *name, const char* value) : mgFilter(name) { @@ -87,24 +78,12 @@ mgFilterString::~mgFilterString() } } -const char* mgFilterString::getVal() -{ - return m_strval; -} string mgFilterString::getStrVal() { return (string) m_strval; } -void mgFilterString::setVal(const char* value) -{ - if(m_strval) - { - free(m_strval); - } - m_strval = strdup(value); -} mgFilterBool::mgFilterBool(const char *name, bool value) : mgFilter(name) { @@ -115,11 +94,6 @@ mgFilterBool::~mgFilterBool() { } -bool mgFilterBool::getVal() -{ - return m_bval; -} - string mgFilterBool::getStrVal() { if(m_bval) @@ -128,11 +102,6 @@ string mgFilterBool::getStrVal() return "false"; } -void mgFilterBool::setVal(bool value) -{ - m_bval = value; -} - mgTrackFilters::mgTrackFilters() { } diff --git a/mg_media.h b/mg_media.h index 9e8c3e7..a77500a 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 02:01:11 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/02 17:57:53 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -51,9 +51,10 @@ class mgFilterInt : public mgFilter private: int m_min; int m_max; - int m_intval; public: + int m_intval; + mgFilterInt(const char *name, int value, int min = 0, int max = INT_MAX); virtual ~mgFilterInt(); @@ -61,38 +62,33 @@ class mgFilterInt : public mgFilter int getMin(); int getMax(); virtual std::string getStrVal(); - - void setVal(int value); }; class mgFilterString : public mgFilter { private: - char* m_strval; public: + char* m_strval; + mgFilterString(const char *name, const char* value); virtual ~mgFilterString(); - const char* getVal(); virtual std::string getStrVal(); - - void setVal(const char* val); }; class mgFilterBool : public mgFilter { private: - bool m_bval; public: + bool m_bval; + mgFilterBool(const char *name, bool value); virtual ~mgFilterBool(); - bool getVal(); virtual std::string getStrVal(); - void setVal(bool val); }; class mgTrackFilters -- cgit v1.2.3 From f47b07b342a6e37e76b41b1ab40101190918dbe5 Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 2 Feb 2004 18:34:34 +0000 Subject: class mgFilterChoices implemented git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@16 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_media.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- mg_media.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/mg_media.c b/mg_media.c index 4027ab9..de0f91d 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/02 17:57:53 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/02 18:34:34 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -22,6 +22,9 @@ using namespace std; +//------------------------------------------------------------------- +// mgFilter +//------------------------------------------------------------------- mgFilter::mgFilter(const char* name) { m_name = strdup(name); @@ -36,6 +39,9 @@ const char* mgFilter::getName() return m_name; } +//------------------------------------------------------------------- +// mgFilterInt +//------------------------------------------------------------------- mgFilterInt::mgFilterInt(const char *name, int value, int min, int max) : mgFilter(name) { @@ -64,6 +70,9 @@ int mgFilterInt::getMax() return m_max; } +//------------------------------------------------------------------- +// mgFilterString +//------------------------------------------------------------------- mgFilterString::mgFilterString(const char *name, const char* value) : mgFilter(name) { @@ -84,6 +93,9 @@ string mgFilterString::getStrVal() return (string) m_strval; } +//------------------------------------------------------------------- +// mgFilterBool +//------------------------------------------------------------------- mgFilterBool::mgFilterBool(const char *name, bool value) : mgFilter(name) { @@ -102,6 +114,39 @@ string mgFilterBool::getStrVal() return "false"; } +//------------------------------------------------------------------- +// mgFilterChoice +//------------------------------------------------------------------- +mgFilterChoice::mgFilterChoice(const char *name, int val, vector *choices) + : mgFilter(name) +{ + m_choices = *choices; + m_selval = val; + if( m_selval < 0 || m_selval >= (int) m_choices.size() ) + { + mgError("mgFilterChoice::mgFilterChoice(..): Illegal index %d", m_selval); + } +} +mgFilterChoice::~mgFilterChoice() +{ + m_choices.clear(); +} + +string mgFilterChoice::getStrVal() +{ + if( m_selval < 0 || m_selval >= (int) m_choices.size() ) + { + mgError("mgFilterChoice::getStrVal(): Illegal index %d", m_selval); + } + return m_choices[m_selval]; +} +vector &mgFilterChoice::getChoices() +{ + return m_choices; +} +//------------------------------------------------------------------- +// mgTrackFilters +//------------------------------------------------------------------- mgTrackFilters::mgTrackFilters() { } @@ -216,7 +261,7 @@ vector *mgMedia::getTrackFilters() return NULL; } -void mgMedia::setTrackFilters(vector *filters) +void mgMedia::applyTrackFilters(vector *filters) { } diff --git a/mg_media.h b/mg_media.h index a77500a..0df4bd9 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,15 +3,15 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/02 17:57:53 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/02 18:34:34 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * * */ /*******************************************************************/ -/* makes sur we dont use parse the same declarations twice */ +/* makes sure we dont use parse the same declarations twice */ #ifndef _MG_MEDIA_H #define _MG_MEDIA_H @@ -28,6 +28,7 @@ class mgSelectionTreeNode; ******************************************************************* * \class mgFilter * + * represents a filter value with boundaries or choices ********************************************************************/ class mgFilter { @@ -38,14 +39,17 @@ class mgFilter char* m_name; public: - mgFilter(const char* name); virtual ~mgFilter(); filterType getType(); const char* getName(); virtual std::string getStrVal()=0; }; - + +/*! + ******************************************************************* + * \class mgFilterInt + ********************************************************************/ class mgFilterInt : public mgFilter { private: @@ -64,6 +68,10 @@ class mgFilterInt : public mgFilter virtual std::string getStrVal(); }; +/*! + ******************************************************************* + * \class mgFilterString + ********************************************************************/ class mgFilterString : public mgFilter { private: @@ -77,6 +85,10 @@ class mgFilterString : public mgFilter virtual std::string getStrVal(); }; +/*! + ******************************************************************* + * \class mgFilterBool + ********************************************************************/ class mgFilterBool : public mgFilter { private: @@ -91,6 +103,33 @@ class mgFilterBool : public mgFilter }; +/*! + ******************************************************************* + * \class mgFilterChoices + ********************************************************************/ +class mgFilterChoice : public mgFilter +{ + private: + std::vector m_choices; + + public: + int m_selval; // index of the currently selected item + + mgFilterChoice(const char *name, int val, std::vector *choices); + virtual ~mgFilterChoice(); + + virtual std::string getStrVal(); + virtual std::vector &getChoices(); + +}; + +/*! + ******************************************************************* + * \class mgrackFilters + * + * Represents a set of filters to set and memorize the search + * constraint for a specific track + ********************************************************************/ class mgTrackFilters { protected: @@ -105,12 +144,16 @@ class mgTrackFilters virtual void clear()=0; }; - /*! ******************************************************************* * \class mgMedia * * \brief main class to access content in the vdr plugin muggle + * + * The constructor of this class should be the only point in the plugin, + * where the data type is explicitelymentioned. + * The class provides a set of objects that abstract from the data + * type and source ********************************************************************/ class mgMedia { @@ -135,9 +178,9 @@ class mgMedia mgSelectionTreeNode* getSelectionRoot(); + // filter management std::vector *getTrackFilters(); - - void setTrackFilters(std::vector *filters); + void applyTrackFilters(std::vector *filters); // playlist management mgPlaylist* createTemporaryPlaylist(); -- cgit v1.2.3 From 0ca80f90d20394d3d4679f73e95986f4d86af40a Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 2 Feb 2004 19:17:44 +0000 Subject: Added generic filter handling to OSD git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@17 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++----------- vdr_menu.h | 20 +++++++++++--- 2 files changed, 91 insertions(+), 18 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index d706e94..39d2afe 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,10 +2,15 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 19:17:44 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ + * + * $Log: vdr_menu.c,v $ + * Revision 1.2 2004/02/02 19:17:44 LarsAC + * Added generic filter handling to OSD + * */ /*******************************************************************/ @@ -48,6 +53,16 @@ void mgMenuTreeItem::Set() SetText( buffer, false ); } +void mgMenuTreeItem::setChildIndex( int index ) +{ + m_child_index = index; +} + +int mgMenuTreeItem::getChildIndex( ) +{ + return m_child_index; +} + // ----------------------- mgMenuTrackItem ------------------ @@ -96,6 +111,13 @@ mgSelectionTreeNode *mgMainMenu::CurrentNode() return item? item->Node(): 0; } +mgMenuTreeItem *mgMainMenu::CurrentItem() +{ + mgMenuTreeItem *item = (mgMenuTreeItem *)Get( Current() ); + + return item; +} + void mgMainMenu::SetButtons( ) { SetHasHotkeys(); @@ -202,7 +224,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) mgDebug( 1, "mgMainMenu: create and apply filter" ); } break; case kRed: // ??? -case kYellow: + case kYellow: { // Yellow always goes to playlist view mgDebug( 1, "mgMainMenu: switch to playlist" ); @@ -241,9 +263,12 @@ case kYellow: case kOk: { mgDebug( 1, "mgMainMenu: switch to filter" ); - mgSelectionTreeNode *child = CurrentNode(); // m_node->getChildren()[ Current() ]; + + m_indices.push_back( Current() ); + + mgSelectionTreeNode *child = CurrentNode(); DisplayTree( child ); - + state = osContinue; } break; case kRed: @@ -308,8 +333,15 @@ case kYellow: if( parent ) { mgDebug( 1, "mgMainMenu: collapse current node" ); + m_node->collapse(); + DisplayTree( parent ); + + // restore last selected entry + cOsdItem *item = Get( m_indices.back() ); + m_indices.pop_back(); + SetCurrent( item ); } state = osContinue; } @@ -502,17 +534,44 @@ void mgMainMenu::DisplayFilter() SetTitle( "Muggle Filter View" ); - Add( new cMenuEditStrItem( "Name ", m_filtername, 32, alpha_num_keys ) ); - Add( new cMenuEditBoolItem( "Filter ", &m_filter, "Off", "On" ) ); - - // Separator??? + vector *filter_list = m_media->getTrackFilters(); - Add( new cMenuEditStrItem( "Title ", m_title, 32, alpha_num_keys ) ); - Add( new cMenuEditStrItem( "Interpret", m_interpret, 32, alpha_num_keys ) ); - Add( new cMenuEditStrItem( "Album ", m_album, 32, alpha_num_keys ) ); - Add( new cMenuEditStrItem( "Playlist ", m_playlist, 32, alpha_num_keys ) ); - Add( new cMenuEditIntItem( "Year from", &m_year_min, 1600, 9999 ) ); - Add( new cMenuEditIntItem( "Year to ", &m_year_max, 1600, 9999 ) ); + for( vector::iterator iter = filter_list->begin(); + iter != filter_list->end(); + iter ++ ) + { + switch( (*iter)->getType() ) + { + case mgFilter::INT: + { + mgFilterInt *fi = (mgFilterInt *) (*iter); + Add( new cMenuEditIntItem( fi->getName(), + &(fi->m_intval), + fi->getMin(), fi->getMax() ) ); + } break; + case mgFilter::STRING: + { + mgFilterString *fs = (mgFilterString *) (*iter); + /* + Add( new cMenuEditStrItem( fs->getName(), &( fs->m_strval), + fs->getMaxLength(), fs->getAllowedChars() ); + + */ + } break; + case mgFilter::BOOL: + { + mgFilterBool *fb = (mgFilterBool *) (*iter); + /* + Add( new cMenuEditIntItem( fb->getName(), &( fb->m_bval), + fb->getTrueString(), fb->getFalseString() ); + */ + } break; + default: + case mgFilter::UNDEF: + { + } break; + } + } Display(); } diff --git a/vdr_menu.h b/vdr_menu.h index a0807a4..a4bfed4 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,10 +2,15 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/01 22:12:56 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/02 19:17:44 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author file owner: $Author: LarsAC $ + * + * $Log: vdr_menu.h,v $ + * Revision 1.3 2004/02/02 19:17:44 LarsAC + * Added generic filter handling to OSD + * */ /*******************************************************************/ @@ -21,6 +26,8 @@ #include #endif +#include + class mgMedia; class mgSelectionTreeNode; class mgPlaylist; @@ -35,10 +42,14 @@ class mgMenuTreeItem : public cOsdItem mgSelectionTreeNode *Node(); void Set(); + + void setChildIndex( int index ); + int getChildIndex( ); private: mgSelectionTreeNode *m_node; + int m_child_index; }; @@ -49,6 +60,7 @@ class mgMainMenu : public cOsdMenu mgMainMenu(); mgSelectionTreeNode *CurrentNode(); + mgMenuTreeItem *CurrentItem(); eOSState ProcessKey(eKeys Key); void Move( int from, int to); @@ -89,6 +101,8 @@ class mgMainMenu : public cOsdMenu int m_year_min, m_year_max, m_filter; MuggleStatus m_state; + + std::list m_indices; }; #endif -- cgit v1.2.3 From 531f6fef17eb573f8cfc64b1da32c9a98b4ccac1 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 2 Feb 2004 19:42:18 +0000 Subject: Added getType function git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@18 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_media.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mg_media.c b/mg_media.c index de0f91d..2f9c1d2 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/02 18:34:34 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/02/02 19:42:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: LarsAC $ * * */ @@ -39,6 +39,11 @@ const char* mgFilter::getName() return m_name; } +mgFilter::filterType mgFilter::getType() +{ + return m_type; +} + //------------------------------------------------------------------- // mgFilterInt //------------------------------------------------------------------- -- cgit v1.2.3 From 9655ee972793a013efd5af1cf9000e7109533a95 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 2 Feb 2004 19:42:37 +0000 Subject: Added positioning of menubar when collapsing nodes. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@19 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 28 +++++++++++++--------------- vdr_menu.h | 13 ++++++------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 39d2afe..699af2c 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,15 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 19:17:44 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/02 19:42:37 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * * $Log: vdr_menu.c,v $ + * Revision 1.3 2004/02/02 19:42:37 LarsAC + * Added positioning of menubar when collapsing nodes. + * * Revision 1.2 2004/02/02 19:17:44 LarsAC * Added generic filter handling to OSD * @@ -53,16 +56,6 @@ void mgMenuTreeItem::Set() SetText( buffer, false ); } -void mgMenuTreeItem::setChildIndex( int index ) -{ - m_child_index = index; -} - -int mgMenuTreeItem::getChildIndex( ) -{ - return m_child_index; -} - // ----------------------- mgMenuTrackItem ------------------ @@ -264,7 +257,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { mgDebug( 1, "mgMainMenu: switch to filter" ); - m_indices.push_back( Current() ); + m_history.push_back( Current() ); mgSelectionTreeNode *child = CurrentNode(); DisplayTree( child ); @@ -339,8 +332,8 @@ eOSState mgMainMenu::ProcessKey(eKeys key) DisplayTree( parent ); // restore last selected entry - cOsdItem *item = Get( m_indices.back() ); - m_indices.pop_back(); + cOsdItem *item = Get( m_history.back() ); + m_history.pop_back(); SetCurrent( item ); } state = osContinue; @@ -491,6 +484,11 @@ void mgMainMenu::DisplayAlbumInfo() void mgMainMenu::DisplayTree( mgSelectionTreeNode* node ) { m_state = TREE; + + if( node == m_root ) + { + m_history.clear(); + } if( node->expand( ) ) { diff --git a/vdr_menu.h b/vdr_menu.h index a4bfed4..36e3bb3 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,12 +2,15 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/02 19:17:44 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/02 19:42:37 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * * $Log: vdr_menu.h,v $ + * Revision 1.4 2004/02/02 19:42:37 LarsAC + * Added positioning of menubar when collapsing nodes. + * * Revision 1.3 2004/02/02 19:17:44 LarsAC * Added generic filter handling to OSD * @@ -43,13 +46,9 @@ class mgMenuTreeItem : public cOsdItem void Set(); - void setChildIndex( int index ); - int getChildIndex( ); - private: mgSelectionTreeNode *m_node; - int m_child_index; }; @@ -102,7 +101,7 @@ class mgMainMenu : public cOsdMenu MuggleStatus m_state; - std::list m_indices; + std::list m_history; }; #endif -- cgit v1.2.3 From a1e64b757a9363827797732a3785034e216f0678 Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 2 Feb 2004 22:33:24 +0000 Subject: changes in mgFilter classes (and ttheir use in the osd) git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@20 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_media.c | 44 ++++++++++++++++++++++++++++++++++++-------- mg_media.h | 42 ++++++++++++++++++++++++++---------------- vdr_menu.c | 23 ++++++++++++----------- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/mg_media.c b/mg_media.c index 2f9c1d2..6f00ea1 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.5 $ - * \date $Date: 2004/02/02 19:42:18 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/02/02 22:33:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * */ @@ -78,11 +78,14 @@ int mgFilterInt::getMax() //------------------------------------------------------------------- // mgFilterString //------------------------------------------------------------------- -mgFilterString::mgFilterString(const char *name, const char* value) +mgFilterString::mgFilterString(const char *name, const char* value, + int maxlen, string allowedchar) : mgFilter(name) { m_type = STRING; m_strval = strdup(value); + m_allowedchar = allowedchar; + m_maxlen = maxlen; } mgFilterString::~mgFilterString() { @@ -91,7 +94,16 @@ mgFilterString::~mgFilterString() free(m_strval); } } + +int mgFilterString::getMaxLength() +{ + return m_maxlen; +} +string mgFilterString::getAllowedChars() +{ + return m_allowedchar; +} string mgFilterString::getStrVal() { @@ -101,11 +113,15 @@ string mgFilterString::getStrVal() //------------------------------------------------------------------- // mgFilterBool //------------------------------------------------------------------- -mgFilterBool::mgFilterBool(const char *name, bool value) +mgFilterBool::mgFilterBool(const char *name, bool value, + string truestr, string falsestr) : mgFilter(name) { m_type = BOOL; - m_bval = value; + m_bval = (bool) value; + m_truestr = truestr; + m_falsestr = falsestr; + } mgFilterBool::~mgFilterBool() { @@ -118,11 +134,23 @@ string mgFilterBool::getStrVal() else return "false"; } +string mgFilterBool::getTrueString() +{ + return m_truestr; +} +string mgFilterBool::getFalseString() +{ + return m_falsestr; +} +bool mgFilterBool::getVal() +{ + return (bool) m_bval; +} //------------------------------------------------------------------- // mgFilterChoice //------------------------------------------------------------------- -mgFilterChoice::mgFilterChoice(const char *name, int val, vector *choices) +mgFilterChoice::mgFilterChoice(const char *name, int val, vector *choices) : mgFilter(name) { m_choices = *choices; @@ -145,7 +173,7 @@ string mgFilterChoice::getStrVal() } return m_choices[m_selval]; } -vector &mgFilterChoice::getChoices() +vector &mgFilterChoice::getChoices() { return m_choices; } diff --git a/mg_media.h b/mg_media.h index 0df4bd9..e073cbf 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/02 18:34:34 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/02/02 22:33:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -39,11 +39,11 @@ class mgFilter char* m_name; public: - mgFilter(const char* name); - virtual ~mgFilter(); - filterType getType(); - const char* getName(); - virtual std::string getStrVal()=0; + mgFilter(const char* name); + virtual ~mgFilter(); + filterType getType(); + const char* getName(); + virtual std::string getStrVal()=0; }; /*! @@ -75,13 +75,18 @@ class mgFilterInt : public mgFilter class mgFilterString : public mgFilter { private: - + std::string m_allowedchar; + int m_maxlen; public: char* m_strval; - mgFilterString(const char *name, const char* value); + mgFilterString(const char *name, const char* value, int maxlen=255, + std::string allowedchar="abcdefghijklmnopqrstuvwxyz0123456789-"); + virtual ~mgFilterString(); + int getMaxLength(); + std::string getAllowedChars(); virtual std::string getStrVal(); }; @@ -92,15 +97,20 @@ class mgFilterString : public mgFilter class mgFilterBool : public mgFilter { private: - + std::string m_truestr; + std::string m_falsestr; + public: - bool m_bval; + int m_bval; - mgFilterBool(const char *name, bool value); + mgFilterBool(const char *name, bool value, + std::string truestr="yes", std::string falsestr="no"); virtual ~mgFilterBool(); virtual std::string getStrVal(); - + std::string getTrueString(); + std::string getFalseString(); + bool getVal(); }; /*! @@ -110,16 +120,16 @@ class mgFilterBool : public mgFilter class mgFilterChoice : public mgFilter { private: - std::vector m_choices; + std::vector m_choices; public: int m_selval; // index of the currently selected item - mgFilterChoice(const char *name, int val, std::vector *choices); + mgFilterChoice(const char *name, int val, std::vector *choices); virtual ~mgFilterChoice(); virtual std::string getStrVal(); - virtual std::vector &getChoices(); + virtual std::vector &getChoices(); }; diff --git a/vdr_menu.c b/vdr_menu.c index 699af2c..e57e997 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,15 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/02 19:42:37 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/02 22:33:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * $Log: vdr_menu.c,v $ + * Revision 1.4 2004/02/02 22:33:24 MountainMan + * changes in mgFilter classes (and ttheir use in the osd) + * * Revision 1.3 2004/02/02 19:42:37 LarsAC * Added positioning of menubar when collapsing nodes. * @@ -550,19 +553,17 @@ void mgMainMenu::DisplayFilter() case mgFilter::STRING: { mgFilterString *fs = (mgFilterString *) (*iter); - /* - Add( new cMenuEditStrItem( fs->getName(), &( fs->m_strval), - fs->getMaxLength(), fs->getAllowedChars() ); + Add( new cMenuEditStrItem( fs->getName(), fs->m_strval, + fs->getMaxLength(), + fs->getAllowedChars().c_str() ) ); - */ } break; case mgFilter::BOOL: { mgFilterBool *fb = (mgFilterBool *) (*iter); - /* - Add( new cMenuEditIntItem( fb->getName(), &( fb->m_bval), - fb->getTrueString(), fb->getFalseString() ); - */ + Add( new cMenuEditBoolItem( fb->getName(), &( fb->m_bval), + fb->getTrueString().c_str(), + fb->getFalseString().c_str() ) ); } break; default: case mgFilter::UNDEF: -- cgit v1.2.3 From def2b0b7241040df3051be97a8780c073788d9f0 Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 2 Feb 2004 22:48:04 +0000 Subject: added CVS $Log git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@21 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 13 +++++++++++-- gd_content_interface.h | 28 ++++++++++------------------ mg_content_interface.c | 15 ++++++++++++--- mg_content_interface.h | 15 ++++++++++++--- mg_media.c | 25 ++++++++++--------------- mg_media.h | 30 +++++++++++------------------- mg_tools.c | 14 +++++++++++--- mg_tools.h | 16 ++++++++++++---- sh_dummy_content.c | 39 ++++++++++----------------------------- sh_dummy_content.h | 13 +++++++++++-- 10 files changed, 110 insertions(+), 98 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index ed74d9d..7b7d15e 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.5 $ - * \date $Date: 2004/02/02 02:01:11 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -1196,3 +1196,12 @@ mgContentItem* GdTreeNode::getSingleTrack() } return track; } + +/* -------------------- begin CVS log --------------------------------- + * $Log: gd_content_interface.c,v $ + * Revision 1.6 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ diff --git a/gd_content_interface.h b/gd_content_interface.h index 2c2cbef..476dc5d 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -1,11 +1,10 @@ - /*******************************************************************/ /*! \file gd_content_interface.h * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 02:01:11 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -208,22 +207,15 @@ public: virtual mgContentItem* getSingleTrack(); }; +/* -------------------- begin CVS log --------------------------------- + * $Log: gd_content_interface.h,v $ + * Revision 1.3 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ #endif /* END _GD_CONTENT_INTERFACE_H */ - - - - - - - - - - - - - - - diff --git a/mg_content_interface.c b/mg_content_interface.c index 5015cf5..0d1f270 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -395,3 +395,12 @@ string mgSelectionTreeNode::getRestrictions() { return m_restriction; } + +/* -------------------- begin CVS log --------------------------------- + * $Log: mg_content_interface.c,v $ + * Revision 1.2 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ diff --git a/mg_content_interface.h b/mg_content_interface.h index d39e5d0..1c210d1 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * Declares main classes of for content items and interfaces to SQL databases * @@ -240,6 +240,15 @@ public: }; +/* -------------------- begin CVS log --------------------------------- + * $Log: mg_content_interface.h,v $ + * Revision 1.2 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ + #endif /* END _CONTENT_INTERFACE_H */ diff --git a/mg_media.c b/mg_media.c index 6f00ea1..8243a3f 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.6 $ - * \date $Date: 2004/02/02 22:33:24 $ + * \version $Revision: 1.7 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -373,18 +373,13 @@ mgTracklist* mgMedia::getTracks() return NULL; } - - - - - - - - - - - - - +/* -------------------- begin CVS log --------------------------------- + * $Log: mg_media.c,v $ + * Revision 1.7 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ diff --git a/mg_media.h b/mg_media.h index e073cbf..dce48db 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.5 $ - * \date $Date: 2004/02/02 22:33:24 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -200,22 +200,14 @@ class mgMedia std::vector getDefaultCols(); mgTracklist* getTracks(); }; -#endif /* END _CONTENT_INTERFACE_H */ - - - - - - - - - - - - - - - - +/* -------------------- begin CVS log --------------------------------- + * $Log: mg_media.h,v $ + * Revision 1.6 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ +#endif /* END _CONTENT_INTERFACE_H */ diff --git a/mg_tools.c b/mg_tools.c index b540a4e..9b8fe91 100644 --- a/mg_tools.c +++ b/mg_tools.c @@ -3,10 +3,10 @@ * \brief A few util functions for standalone and plugin messaging * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * */ /*******************************************************************/ @@ -138,3 +138,11 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) } } +/* -------------------- begin CVS log --------------------------------- + * $Log: mg_tools.c,v $ + * Revision 1.2 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ diff --git a/mg_tools.h b/mg_tools.h index 68c8104..f913667 100644 --- a/mg_tools.h +++ b/mg_tools.h @@ -3,10 +3,10 @@ * \brief A few util functions for standalone and plugin messaging * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * */ /*******************************************************************/ @@ -28,4 +28,12 @@ void mgError(const char *fmt, ...); MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...); void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...); -#endif +/* -------------------- begin CVS log --------------------------------- + * $Log: mg_tools.h,v $ + * Revision 1.2 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ +#endif /* _MUGGLE_TOOLS_H */ diff --git a/sh_dummy_content.c b/sh_dummy_content.c index 27c23bd..1ccfe9e 100644 --- a/sh_dummy_content.c +++ b/sh_dummy_content.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 02:01:11 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -586,30 +586,11 @@ mgContentItem* DummyTreeNode::getSingleTrack() return track; } - - - - - - - - - - - - - - - - - - - - - - - - - - - +/* -------------------- begin CVS log --------------------------------- + * $Log: sh_dummy_content.c,v $ + * Revision 1.3 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ diff --git a/sh_dummy_content.h b/sh_dummy_content.h index 41e526f..7f79498 100644 --- a/sh_dummy_content.h +++ b/sh_dummy_content.h @@ -2,8 +2,8 @@ /*! \file dummy_content.h * \brief Dummy Data Objects for testing Muggle ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 02:01:11 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/02 22:48:04 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -158,6 +158,15 @@ public: virtual mgContentItem* getSingleTrack(); }; +/* -------------------- begin CVS log --------------------------------- + * $Log: sh_dummy_content.h,v $ + * Revision 1.3 2004/02/02 22:48:04 MountainMan + * added CVS $Log + * + * + * --------------------- end CVS log ---------------------------------- + */ + #endif /* END _CONTENT_INTERFACE_H */ -- cgit v1.2.3 From 27a129c26ad2520d3747f9e05af9dbe6cae0c64a Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 2 Feb 2004 23:33:41 +0000 Subject: impementation of gdTrackFilters git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@22 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_media.c | 21 ++++++++++++++++----- mg_media.h | 8 ++++++-- vdr_menu.c | 9 ++++++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/mg_media.c b/mg_media.c index 8243a3f..088ba65 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.7 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.8 $ + * \date $Date: 2004/02/02 23:33:41 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -207,7 +207,7 @@ vector *mgTrackFilters::getFilters() mgMedia::mgMedia(contentType mediatype) { int errval = 0; - mgTrackFilters *m_trackfilter; + m_trackfilter = NULL; m_mediatype = mediatype; m_sql_trackfilter = "1"; m_defaultView = 1; @@ -250,6 +250,10 @@ mgMedia::mgMedia(contentType mediatype) mgMedia::~mgMedia() { + if( m_trackfilter ) + { + delete m_trackfilter; + } } string mgMedia::getMediaTypeName() @@ -284,9 +288,13 @@ vector *mgMedia::getTrackFilters() switch(m_mediatype) { case DUMMY: - //return mgFilters(); + // return mgFilters(); case GD_MP3: - // return m_trackfilters; + if( m_trackfilter == NULL ) + { + m_trackfilter = new gdTrackFilters(); + } + return m_trackfilter->getFilters(); default: break; } @@ -375,6 +383,9 @@ mgTracklist* mgMedia::getTracks() } /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.c,v $ + * Revision 1.8 2004/02/02 23:33:41 MountainMan + * impementation of gdTrackFilters + * * Revision 1.7 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/mg_media.h b/mg_media.h index dce48db..d39ce93 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.6 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.7 $ + * \date $Date: 2004/02/02 23:33:41 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -179,6 +179,7 @@ class mgMedia contentType m_mediatype; std::string m_sql_trackfilter; int m_defaultView; + mgTrackFilters *m_trackfilter; public: mgMedia(contentType mediatype); @@ -203,6 +204,9 @@ class mgMedia /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.h,v $ + * Revision 1.7 2004/02/02 23:33:41 MountainMan + * impementation of gdTrackFilters + * * Revision 1.6 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/vdr_menu.c b/vdr_menu.c index e57e997..1aff6a7 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,15 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/02 22:33:24 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/02/02 23:33:41 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * * $Log: vdr_menu.c,v $ + * Revision 1.5 2004/02/02 23:33:41 MountainMan + * impementation of gdTrackFilters + * * Revision 1.4 2004/02/02 22:33:24 MountainMan * changes in mgFilter classes (and ttheir use in the osd) * @@ -37,7 +40,7 @@ using namespace std; -static const char* alpha_num_keys = "abcdefghijklmnopqrstuvwxyz0123456789-"; +// static const char* alpha_num_keys = "abcdefghijklmnopqrstuvwxyz0123456789-"; // ----------------------- mgMenuTreeItem ------------------ -- cgit v1.2.3 From 45eec7862d12b533c7fe8c828802adaf770ae312 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 3 Feb 2004 00:13:24 +0000 Subject: Improved OSD handling of collapse/back git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@23 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 4 ++-- gd_content_interface.c | 11 +++++---- muggle.h | 8 +++---- vdr_menu.c | 61 +++++++++++++++++++++++++++----------------------- vdr_menu.h | 23 +++++++++++-------- 5 files changed, 60 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index ad5a8df..fcad314 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.3 2004/02/01 22:35:22 RaK Exp $ +# $Id: Makefile,v 1.4 2004/02/03 00:13:24 LarsAC Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -40,7 +40,7 @@ PACKAGE = vdr-$(ARCHIVE) ### Includes and Defines (add further entries here): -INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/mysql/ +INCLUDES += -I$(VDRDIR) -I$(DVBDIR)/include -I/usr/include/mysql/ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' diff --git a/gd_content_interface.c b/gd_content_interface.c index 7b7d15e..2727f4d 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.6 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.7 $ + * \date $Date: 2004/02/03 00:13:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: LarsAC $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -40,7 +40,7 @@ int GdInitDatabase(MYSQL *db) } if(mysql_real_connect(db,"localhost","root","", - "GiantDisc",0,NULL,0) == NULL) + "GiantDisc2",0,NULL,0) == NULL) { return -2; } @@ -1199,6 +1199,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.7 2004/02/03 00:13:24 LarsAC + * Improved OSD handling of collapse/back + * * Revision 1.6 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/muggle.h b/muggle.h index 6fbe44d..e2e9bba 100644 --- a/muggle.h +++ b/muggle.h @@ -2,10 +2,10 @@ /*! \file muggle.h * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/01 22:12:56 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/03 00:13:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author file owner: $Author: LarsAC $ */ /*******************************************************************/ @@ -17,7 +17,7 @@ #ifdef SHELL_TEST #include "shell_plugin.h" #else - #include + #include #endif class mgMuggle : public cPlugin diff --git a/vdr_menu.c b/vdr_menu.c index 1aff6a7..cdce65b 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,29 +2,17 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.5 $ - * \date $Date: 2004/02/02 23:33:41 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/02/03 00:13:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ - * - * $Log: vdr_menu.c,v $ - * Revision 1.5 2004/02/02 23:33:41 MountainMan - * impementation of gdTrackFilters - * - * Revision 1.4 2004/02/02 22:33:24 MountainMan - * changes in mgFilter classes (and ttheir use in the osd) - * - * Revision 1.3 2004/02/02 19:42:37 LarsAC - * Added positioning of menubar when collapsing nodes. - * - * Revision 1.2 2004/02/02 19:17:44 LarsAC - * Added generic filter handling to OSD + * \author file owner: $Author: LarsAC $ * + * $Id: vdr_menu.c,v 1.6 2004/02/03 00:13:24 LarsAC Exp $ */ /*******************************************************************/ -#include -#include +#include +#include #include #include "vdr_menu.h" @@ -40,8 +28,6 @@ using namespace std; -// static const char* alpha_num_keys = "abcdefghijklmnopqrstuvwxyz0123456789-"; - // ----------------------- mgMenuTreeItem ------------------ mgMenuTreeItem::mgMenuTreeItem( mgSelectionTreeNode *node ) @@ -221,6 +207,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { // OK: Create filter and selection tree and display mgDebug( 1, "mgMainMenu: create and apply filter" ); + // m_media->applyFilters(); } break; case kRed: // ??? case kYellow: @@ -249,6 +236,10 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } } } + else if( state == osBack ) + { + // m_media->resetFilters(); + } } else if( m_state == TREE ) { @@ -264,6 +255,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) mgDebug( 1, "mgMainMenu: switch to filter" ); m_history.push_back( Current() ); + mgDebug( 1, "Remember current node #%i", Current() ); mgSelectionTreeNode *child = CurrentNode(); DisplayTree( child ); @@ -338,9 +330,17 @@ eOSState mgMainMenu::ProcessKey(eKeys key) DisplayTree( parent ); // restore last selected entry - cOsdItem *item = Get( m_history.back() ); + int last = m_history.back(); + mgDebug( 1, "Setting current to #%d", last ); + + cOsdItem *item = Get( last ); m_history.pop_back(); SetCurrent( item ); + + RefreshCurrent(); + DisplayCurrent(true); + + Interface->Flush(); } state = osContinue; } @@ -491,11 +491,6 @@ void mgMainMenu::DisplayTree( mgSelectionTreeNode* node ) { m_state = TREE; - if( node == m_root ) - { - m_history.clear(); - } - if( node->expand( ) ) { Clear(); @@ -565,8 +560,8 @@ void mgMainMenu::DisplayFilter() { mgFilterBool *fb = (mgFilterBool *) (*iter); Add( new cMenuEditBoolItem( fb->getName(), &( fb->m_bval), - fb->getTrueString().c_str(), - fb->getFalseString().c_str() ) ); + fb->getTrueString().c_str(), + fb->getFalseString().c_str() ) ); } break; default: case mgFilter::UNDEF: @@ -582,3 +577,13 @@ void mgMainMenu::DisplayFilterSelector() { } + +/************************************************************ + * + * $Log: vdr_menu.c,v $ + * Revision 1.6 2004/02/03 00:13:24 LarsAC + * Improved OSD handling of collapse/back + * + * + ************************************************************ + */ diff --git a/vdr_menu.h b/vdr_menu.h index 36e3bb3..4c84631 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,17 +2,12 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/02 19:42:37 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/02/03 00:13:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Log: vdr_menu.h,v $ - * Revision 1.4 2004/02/02 19:42:37 LarsAC - * Added positioning of menubar when collapsing nodes. - * - * Revision 1.3 2004/02/02 19:17:44 LarsAC - * Added generic filter handling to OSD + * $Id: vdr_menu.h,v 1.5 2004/02/03 00:13:24 LarsAC Exp $ * */ /*******************************************************************/ @@ -26,7 +21,7 @@ #include "myosd.h" #include "mymenuitems.h" #else - #include + #include #endif #include @@ -105,3 +100,13 @@ class mgMainMenu : public cOsdMenu }; #endif + +/************************************************************ + * + * $Log: vdr_menu.h,v $ + * Revision 1.5 2004/02/03 00:13:24 LarsAC + * Improved OSD handling of collapse/back + * + * + ************************************************************ + */ -- cgit v1.2.3 From 7949447fe14d839329e4ff80fd9353928df5cabd Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 3 Feb 2004 19:15:08 +0000 Subject: OSD selection now jumps back to parent when collapsing. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@24 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 34 +++++++++++++++++++--------------- vdr_menu.h | 11 +++++++---- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index cdce65b..a21d664 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.6 $ - * \date $Date: 2004/02/03 00:13:24 $ + * \version $Revision: 1.7 $ + * \date $Date: 2004/02/03 19:15:08 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.6 2004/02/03 00:13:24 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.7 2004/02/03 19:15:08 LarsAC Exp $ */ /*******************************************************************/ @@ -327,20 +327,11 @@ eOSState mgMainMenu::ProcessKey(eKeys key) m_node->collapse(); - DisplayTree( parent ); - // restore last selected entry int last = m_history.back(); - mgDebug( 1, "Setting current to #%d", last ); - - cOsdItem *item = Get( last ); m_history.pop_back(); - SetCurrent( item ); - RefreshCurrent(); - DisplayCurrent(true); - - Interface->Flush(); + DisplayTree( parent, last ); } state = osContinue; } @@ -419,7 +410,7 @@ void mgMainMenu::Move( int from, int to ) void mgMainMenu::DisplayTracklist() { m_state = TRACKS; - mgDebug( 1, "mgBrowseMenu::DisplayTracklist"); + mgDebug( 1, "mgBrowseMenu::DisplayTracklist"); Clear(); SetButtons(); @@ -487,7 +478,7 @@ void mgMainMenu::DisplayAlbumInfo() // show info of the currently playing track } -void mgMainMenu::DisplayTree( mgSelectionTreeNode* node ) +void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) { m_state = TREE; @@ -513,6 +504,16 @@ void mgMainMenu::DisplayTree( mgSelectionTreeNode* node ) { Add( new mgMenuTreeItem( *iter ) ); } + + mgDebug( 1, "Setting current to #%d", select ); + + cOsdItem *item = Get( select ); + SetCurrent( item ); + + RefreshCurrent(); + DisplayCurrent(true); + + // Interface->Flush(); mgDebug( 1, "mgBrowseMenu::DisplayNode: Children added to OSD" ); } @@ -581,6 +582,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.7 2004/02/03 19:15:08 LarsAC + * OSD selection now jumps back to parent when collapsing. + * * Revision 1.6 2004/02/03 00:13:24 LarsAC * Improved OSD handling of collapse/back * diff --git a/vdr_menu.h b/vdr_menu.h index 4c84631..a9cfae2 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,12 +2,12 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.5 $ - * \date $Date: 2004/02/03 00:13:24 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/02/03 19:15:08 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.h,v 1.5 2004/02/03 00:13:24 LarsAC Exp $ + * $Id: vdr_menu.h,v 1.6 2004/02/03 19:15:08 LarsAC Exp $ * */ /*******************************************************************/ @@ -70,7 +70,7 @@ class mgMainMenu : public cOsdMenu // To be rewritten mode dependent void SetButtons(); - void DisplayTree( mgSelectionTreeNode *node ); + void DisplayTree( mgSelectionTreeNode *node, int select = 0 ); void DisplayTreeViewSelector(); void DisplayFilter(); @@ -104,6 +104,9 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.6 2004/02/03 19:15:08 LarsAC + * OSD selection now jumps back to parent when collapsing. + * * Revision 1.5 2004/02/03 00:13:24 LarsAC * Improved OSD handling of collapse/back * -- cgit v1.2.3 From 275ff28581ff55ba212cfab4edc5f96af5edc0da Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 3 Feb 2004 19:28:46 +0000 Subject: Playlist now created in plugin instead of in menu. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@25 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 15 ++++++++++++--- muggle.h | 12 ++++++++++-- vdr_menu.c | 17 ++++++++--------- vdr_menu.h | 11 +++++++---- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/muggle.c b/muggle.c index 481c699..845eab3 100644 --- a/muggle.c +++ b/muggle.c @@ -2,8 +2,8 @@ /*! \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/03 19:28:46 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ */ @@ -14,8 +14,13 @@ static const char *DESCRIPTION = "Access GiantDisc database contents"; static const char *MAINMENUENTRY = "Muggle"; #include "muggle.h" + #include "vdr_menu.h" + #include "mg_tools.h" +#include "mg_content_interface.h" +#include "mg_media.h" + const char* mgMuggle::Version(void) { @@ -67,6 +72,10 @@ bool mgMuggle::Start(void) // Start any background activities the plugin shall perform. mgSetDebugLevel( 99 ); + m_media = new mgMedia( mgMedia::GD_MP3 ); + m_root = m_media->getSelectionRoot(); + m_playlist = m_media->createTemporaryPlaylist(); + return true; } @@ -78,7 +87,7 @@ void mgMuggle::Housekeeping(void) cOsdObject *mgMuggle::MainMenuAction(void) { // Perform the action when selected from the main VDR menu. - cOsdObject* osd = new mgMainMenu(); + cOsdObject* osd = new mgMainMenu( m_media, m_root, m_playlist ); return osd; } diff --git a/muggle.h b/muggle.h index e2e9bba..c5c0d04 100644 --- a/muggle.h +++ b/muggle.h @@ -2,8 +2,8 @@ /*! \file muggle.h * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/03 00:13:24 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/03 19:28:46 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ */ @@ -20,6 +20,10 @@ #include #endif +class mgMedia; +class mgSelectionTreeNode; +class mgPlaylist; + class mgMuggle : public cPlugin { public: @@ -51,6 +55,10 @@ public: virtual bool SetupParse(const char *Name, const char *Value); private: + + mgMedia *m_media; + mgSelectionTreeNode *m_root; + mgPlaylist *m_playlist; }; diff --git a/vdr_menu.c b/vdr_menu.c index a21d664..0bcf5e6 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.7 $ - * \date $Date: 2004/02/03 19:15:08 $ + * \version $Revision: 1.8 $ + * \date $Date: 2004/02/03 19:28:46 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.7 2004/02/03 19:15:08 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.8 2004/02/03 19:28:46 LarsAC Exp $ */ /*******************************************************************/ @@ -53,17 +53,14 @@ void mgMenuTreeItem::Set() // ----------------------- mgMainMenu ---------------------- -mgMainMenu::mgMainMenu() - : cOsdMenu( "" ) +mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *playlist) + : cOsdMenu( "" ), m_media(media), m_root(root), m_current_playlist(playlist) { mgDebug( 1, "Creating Muggle Main Menu" ); SetTitle( "Muggle Media Database" ); SetButtons(); - m_media = new mgMedia( mgMedia::GD_MP3 ); - m_root = m_media->getSelectionRoot(); - m_filtername = new char[32]; strcpy( m_filtername, "none" ); @@ -84,7 +81,6 @@ mgMainMenu::mgMainMenu() m_filter = 0; - m_current_playlist = m_media->createTemporaryPlaylist(); m_tracklist = NULL; DisplayTree( m_root ); @@ -582,6 +578,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.8 2004/02/03 19:28:46 LarsAC + * Playlist now created in plugin instead of in menu. + * * Revision 1.7 2004/02/03 19:15:08 LarsAC * OSD selection now jumps back to parent when collapsing. * diff --git a/vdr_menu.h b/vdr_menu.h index a9cfae2..1b318c0 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,12 +2,12 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.6 $ - * \date $Date: 2004/02/03 19:15:08 $ + * \version $Revision: 1.7 $ + * \date $Date: 2004/02/03 19:28:46 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.h,v 1.6 2004/02/03 19:15:08 LarsAC Exp $ + * $Id: vdr_menu.h,v 1.7 2004/02/03 19:28:46 LarsAC Exp $ * */ /*******************************************************************/ @@ -51,7 +51,7 @@ class mgMainMenu : public cOsdMenu { public: - mgMainMenu(); + mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *playlist); mgSelectionTreeNode *CurrentNode(); mgMenuTreeItem *CurrentItem(); @@ -104,6 +104,9 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.7 2004/02/03 19:28:46 LarsAC + * Playlist now created in plugin instead of in menu. + * * Revision 1.6 2004/02/03 19:15:08 LarsAC * OSD selection now jumps back to parent when collapsing. * -- cgit v1.2.3 From 6389d7300612507519b9144c11b564ac9eb9871e Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 3 Feb 2004 19:34:51 +0000 Subject: Back on root level now jumps back to VDR main menu. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@26 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 0bcf5e6..34fb9bb 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.8 $ - * \date $Date: 2004/02/03 19:28:46 $ + * \version $Revision: 1.9 $ + * \date $Date: 2004/02/03 19:34:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.8 2004/02/03 19:28:46 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.9 2004/02/03 19:34:51 LarsAC Exp $ */ /*******************************************************************/ @@ -328,8 +328,15 @@ eOSState mgMainMenu::ProcessKey(eKeys key) m_history.pop_back(); DisplayTree( parent, last ); + + state = osContinue; + } + else + { + // Back pressed on root level... Go back to Main VDR menu + + state = osBack; } - state = osContinue; } } else if( m_state == TRACKS ) @@ -509,8 +516,6 @@ void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) RefreshCurrent(); DisplayCurrent(true); - // Interface->Flush(); - mgDebug( 1, "mgBrowseMenu::DisplayNode: Children added to OSD" ); } Display(); @@ -578,6 +583,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.9 2004/02/03 19:34:51 LarsAC + * Back on root level now jumps back to VDR main menu. + * * Revision 1.8 2004/02/03 19:28:46 LarsAC * Playlist now created in plugin instead of in menu. * -- cgit v1.2.3 From c103d31964c381468fcd2ef081efc91cf173f360 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 3 Feb 2004 20:23:33 +0000 Subject: Added an initial TODO file git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@27 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000..3489dbd --- /dev/null +++ b/TODO @@ -0,0 +1,42 @@ +TODO File for Muggle +==================== + +Code polishing +============== +- check for memory leaks +- check for (reasonably) consistent usage of char*/string + +Short term items +================ + +OSD +--- +- DisplayTrackInfo +- DisplayAlbumInfo +- Edit playlist (means moving tracks around?) +- handle filters: + - create tracklist from filter + - create tree from filter + +Content +------- + +Medium term items +================= + +- Add a player for various formats + - from mp3 plugin + - use mplayer in slave mode + - check mpg123 (used in GD) + +Long term items +=============== + +- Display covers as still pictures + +Visions +======= + +- handle films +- handle off-line media (CDs, DVDs) +- handle EPG -- cgit v1.2.3 From ec0fff23b07830aea416d28e313faf6453315fca Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 3 Feb 2004 20:24:29 +0000 Subject: Clear index history when jumping to root node in order to avoid overflow git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@28 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 34fb9bb..b8d92e3 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.9 $ - * \date $Date: 2004/02/03 19:34:51 $ + * \version $Revision: 1.10 $ + * \date $Date: 2004/02/03 20:24:29 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.9 2004/02/03 19:34:51 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.10 2004/02/03 20:24:29 LarsAC Exp $ */ /*******************************************************************/ @@ -174,6 +174,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) case kBlue: { mgDebug( 1, "mgMainMenu: switch to TreeView" ); + m_history.clear(); DisplayTree( m_root ); state = osContinue; } break; @@ -206,6 +207,9 @@ eOSState mgMainMenu::ProcessKey(eKeys key) // m_media->applyFilters(); } break; case kRed: // ??? + { + state = osContinue; + } beak; case kYellow: { // Yellow always goes to playlist view @@ -215,7 +219,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } break; case kGreen: { - mgDebug( 1, "mgMainMenu: switch to filter" ); + mgDebug( 1, "mgMainMenu: switch filters" ); // Green: select other filters DisplayFilterSelector(); } break; @@ -223,6 +227,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { // Blue: treeview mgDebug( 1, "mgMainMenu: switch to treeview" ); + m_history.clear(); DisplayTree( m_root ); state = osContinue; } @@ -378,6 +383,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } break; case kBlue: { + m_history.clear(); DisplayTree( m_root ); state = osContinue; } break; @@ -508,21 +514,19 @@ void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) Add( new mgMenuTreeItem( *iter ) ); } - mgDebug( 1, "Setting current to #%d", select ); - cOsdItem *item = Get( select ); SetCurrent( item ); RefreshCurrent(); DisplayCurrent(true); - - mgDebug( 1, "mgBrowseMenu::DisplayNode: Children added to OSD" ); } Display(); } void mgMainMenu::DisplayTreeViewSelector() { + m_history.clear(); + DisplayTree( m_root ); } void mgMainMenu::DisplayFilter() @@ -583,6 +587,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.10 2004/02/03 20:24:29 LarsAC + * Clear index history when jumping to root node in order to avoid overflow + * * Revision 1.9 2004/02/03 19:34:51 LarsAC * Back on root level now jumps back to VDR main menu. * -- cgit v1.2.3 From a65e42dbbbc02909a07f17b8ae049fa63dd3e7ee Mon Sep 17 00:00:00 2001 From: RaK Date: Tue, 3 Feb 2004 21:53:32 +0000 Subject: beak = break in l 212 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@29 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index b8d92e3..4699bb8 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.10 $ - * \date $Date: 2004/02/03 20:24:29 $ + * \version $Revision: 1.11 $ + * \date $Date: 2004/02/03 21:53:32 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ * - * $Id: vdr_menu.c,v 1.10 2004/02/03 20:24:29 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.11 2004/02/03 21:53:32 RaK Exp $ */ /*******************************************************************/ @@ -209,7 +209,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) case kRed: // ??? { state = osContinue; - } beak; + } break; case kYellow: { // Yellow always goes to playlist view @@ -587,6 +587,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.11 2004/02/03 21:53:32 RaK + * beak = break in l 212 + * * Revision 1.10 2004/02/03 20:24:29 LarsAC * Clear index history when jumping to root node in order to avoid overflow * -- cgit v1.2.3 From 948ba08fb8fea82e6614af14d3038e6bc98fcc0f Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 8 Feb 2004 10:48:44 +0000 Subject: Made major revisions in OSD behavior git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@30 e10066b5-e1e2-0310-b819-94efdf66514b --- menu.txt | 117 ++++++++-------- vdr_menu.c | 446 +++++++++++++++++++++++++++++++------------------------------ vdr_menu.h | 21 ++- 3 files changed, 295 insertions(+), 289 deletions(-) diff --git a/menu.txt b/menu.txt index 08c7b2e..57f0049 100644 --- a/menu.txt +++ b/menu.txt @@ -1,76 +1,73 @@ -PLAYLIST ---------- -OK --// should start playing in the future -BACK -- - -UP select previous item (VDR-mechanism) -DOWN select next item (VDR-mechanism) -LEFT selection pg-up (VDR-mechanism) -RIGHT selection pg-down (VDR-mechanism) - -RED ==>display Tracklist(current filter setting) -GREEN ==>DisplayFilter -Yellow ==>DisplayTrackInfo(current) -Blue ==>DisplayTree( m_root ) - -FILTER ---------- - - - TREE --------- -OK Leaf: ==>DisplayTrackInfo( leaf ) +OK Leaf: ==>DisplayTrackInfo( leaf ) // TODO Node: ==> DisplayTree( child ); // expand BACK ==> DisplayTree( parent ); // collapse + ==> on root: return to VDR main menu UP select previous item (VDR-mechanism) -DOWN select next item (VDR-mechanism) -LEFT selection pg-up (VDR-mechanism) -RIGHT selection pg-down (VDR-mechanism) +DOWN select next item (VDR-mechanism) +LEFT selection pg-up (VDR-mechanism) +RIGHT selection pg-down (VDR-mechanism) RED add all tracks under currently highlighted node to playlist -GREEN ==>DisplayFilter -YELLOW ==>DisplayPlaylist(); -BLUE -- - // Should be in the future: Collapse all and goto default View - - -TRACKS (Tracklist) ------------------- - // Andi, ich kapier es nicht. Was unterscheidet Tracks from Tree? - // IMHO ist Tracks eine Sonderform von Tree, der nur Leaves hat, oder? -OK DisplayTrackInfo(current) -BACK ==> DisplayTree( parent ); // collapse +GREEN ==>Switch to next tree view(TODO?) +YELLOW ==>Cycle to playlist view +BLUE ==>Enter tree view submenu -UP select previous item (VDR-mechanism) -DOWN select next item (VDR-mechanism) -LEFT selection pg-up (VDR-mechanism) -RIGHT selection pg-down (VDR-mechanism) +PLAYLIST +--------- +OK -- // should start playing in the future +BACK -- -RED add currend item to playlist -GREEN ==>DisplayFilter -YELLOW ==>DisplayPlaylist(); -BLUE ==>DisplayTree(root) +UP select previous item (VDR-mechanism) +DOWN select next item (VDR-mechanism) +LEFT selection pg-up (VDR-mechanism) +RIGHT selection pg-down (VDR-mechanism) -TRACKINFO (previous == TREE || previous == TRACKS ) ------------------- -OK PlayItem -BACK go to previous view (==> DisplayTracklist || ==>DisplayTree ) +RED ==>Edit playlist (mark/move, VDR mechanism) TODO +GREEN ==>Playlist -> Track info -> Album info -> Playlist +YELLOW ==>Cycle to filter view +BLUE ==>Playlist submenu -RED add currend item to playlist -GREEN ==>DisplayFilter -YELLOW ==>DisplayPlaylist(); -BLUE -- +TRACKINFO (TODO) +--------- +OK PlayItem (jump/skip to item? useful choice?) +BACK go to previous view (?) -TRACKINFO (previous == playlist ) ------------------- -OK PlayItem -BACK ==>DisplayPlaylist(); +RED ?? +GREEN ==>Album info (TODO) +YELLOW ==>Cycle to filter view +BLUE ==>Playlist submenu -RED -- -GREEN ==>DisplayFilter -YELLOW ==>DisplayPlaylist(); // also on back -BLUE -- +ALBUMINFO +--------- +OK PlayItem (track? album?) +BACK ==>DisplayPlaylist(); (?) +RED ?? +GREEN ==>Cycle to playlist view +YELLOW ==>Cycle to filter view +BLUE ==>Playlist submenu +FILTER +------ +OK Confirm entry (really? VDR mechanism) +BACK ? + +UP Previous filter entry (VDR mechanism) +DOWN Next filter entry (VDR mechanism) + +RED Perform query, display according to view type entry (TODO) +GREEN ==>Load other filter +YELLOW ==>Cycle to tree view +BLUE ==>Filter submenu + +/************************************************************ + * + * $Log: menu.txt,v $ + * Revision 1.3 2004/02/08 10:48:44 LarsAC + * Made major revisions in OSD behavior + * + * + ************************************************************/ diff --git a/vdr_menu.c b/vdr_menu.c index 4699bb8..317a647 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.11 $ - * \date $Date: 2004/02/03 21:53:32 $ + * \version $Revision: 1.12 $ + * \date $Date: 2004/02/08 10:48:44 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.11 2004/02/03 21:53:32 RaK Exp $ + * $Id: vdr_menu.c,v 1.12 2004/02/08 10:48:44 LarsAC Exp $ */ /*******************************************************************/ @@ -105,144 +105,36 @@ void mgMainMenu::SetButtons( ) if( m_state == TREE ) { - SetHelp( "Add", "Filter", "Playlist", "Change View" ); + SetHelp( "Add", "Cycle tree", "Playlist", "Submenu" ); } else if( m_state == PLAYLIST ) { - SetHelp( "Tracks", "Filter", "Edit PL", "Tree" ); + SetHelp( "Edit PL", "Track info", "Filter", "Submenu" ); } else if( m_state == PLAYLIST_TRACKINFO ) { - SetHelp( "Play/Pause", "Album Info", "Filter", "Tree" ); + SetHelp( "Edit PL?", "Album info", "Filter", "Submenu" ); } else if( m_state == PLAYLIST_ALBUMINFO ) { - SetHelp( "Play/Pause", "Filter", "Playlist", "Tree" ); + SetHelp( "Edit PL?", "Playlist", "Filter", "Submenu" ); } else if( m_state == FILTER ) { - SetHelp( "Add", "Playlist", "Filter", "Tree" ); + SetHelp( "Query", "Load", "Tree", "Submenu" ); } - else if( m_state == TRACKS ) + else { - SetHelp( "Add", "Filter", "Playlist", "Tree" ); - } + SetHelp( "t", "o", "d", "o" ); + } } eOSState mgMainMenu::ProcessKey(eKeys key) { mgDebug( 1, "mgMainMenu::ProcessKey" ); eOSState state = cOsdMenu::ProcessKey(key); - - if( m_state == PLAYLIST ) - { - mgDebug( 1, "mgMainMenu: in state PLAYLIST" ); - if( state == osUnknown ) - { - switch( key ) - { - case kOk: - { - mgDebug( 1, "mgMainMenu: Mark" ); - Mark(); // Mark (to move), moving done by VDR, calls Move - state = osContinue; - } break; - case kRed: - { - mgDebug( 1, "mgMainMenu: " ); - // Shuffle? - } - case kYellow: - { - if( m_state == PLAYLIST ) - { - mgDebug( 1, "mgMainMenu: switch to TrackInfo" ); - DisplayTrackInfo(); - } - else if( m_state == PLAYLIST_TRACKINFO ) - { - mgDebug( 1, "mgMainMenu: switch to AlbumInfo" ); - DisplayAlbumInfo(); - } - else if( m_state == PLAYLIST_ALBUMINFO ) - { - mgDebug( 1, "mgMainMenu: switch to Playlist" ); - DisplayPlaylist(); - } - state = osContinue; - } break; - case kBlue: - { - mgDebug( 1, "mgMainMenu: switch to TreeView" ); - m_history.clear(); - DisplayTree( m_root ); - state = osContinue; - } break; - case kGreen: - { - mgDebug( 1, "mgMainMenu: switch to Filter" ); - DisplayFilter(); - state = osContinue; - } break; - default: - { - mgDebug( 1, "mgMainMenu: default" ); - state = osContinue; - }; - } - } - } - else if( m_state == FILTER ) - { - mgDebug( 1, "mgMainMenu: in state FILTER" ); - - if( state == osUnknown ) - { - switch( key ) - { - case kOk: - { - // OK: Create filter and selection tree and display - mgDebug( 1, "mgMainMenu: create and apply filter" ); - // m_media->applyFilters(); - } break; - case kRed: // ??? - { - state = osContinue; - } break; - case kYellow: - { - // Yellow always goes to playlist view - mgDebug( 1, "mgMainMenu: switch to playlist" ); - DisplayPlaylist(); - state = osContinue; - } break; - case kGreen: - { - mgDebug( 1, "mgMainMenu: switch filters" ); - // Green: select other filters - DisplayFilterSelector(); - } break; - case kBlue: - { - // Blue: treeview - mgDebug( 1, "mgMainMenu: switch to treeview" ); - m_history.clear(); - DisplayTree( m_root ); - state = osContinue; - } - default: - { - state = osContinue; - } - } - } - else if( state == osBack ) - { - // m_media->resetFilters(); - } - } - else if( m_state == TREE ) + + if( m_state == TREE ) { mgDebug( 1, "mgMainMenu: in state TREE" ); // Navigate with up/dn, left/right (pgup, pgdn) @@ -253,7 +145,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { case kOk: { - mgDebug( 1, "mgMainMenu: switch to filter" ); + mgDebug( 1, "mgMainMenu: expand and descend" ); m_history.push_back( Current() ); mgDebug( 1, "Remember current node #%i", Current() ); @@ -292,24 +184,24 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } state = osContinue; } break; - case kYellow: + case kGreen: { - mgDebug( 1, "mgMainMenu: display playlist" ); - // Yellow always goes to playlist view - DisplayPlaylist(); + mgDebug( 1, "mgMainMenu: cycle treeviews (todo)" ); + state = osContinue; } break; - case kGreen: + case kYellow: { - mgDebug( 1, "mgMainMenu: display filter" ); - DisplayFilter(); - state = osContinue; + mgDebug( 1, "mgMainMenu: cycle to playlist view" ); + + DisplayPlaylist(); } break; case kBlue: { - mgDebug( 1, "mgMainMenu: select other tree view" ); - // Select other views -> Select other Media - DisplayTreeViewSelector(); + mgDebug( 1, "mgMainMenu: submenu (todo)" ); + + DisplayTreeSubmenu(); + state = osContinue; } break; default: @@ -342,110 +234,244 @@ eOSState mgMainMenu::ProcessKey(eKeys key) state = osBack; } + } + } + else if( m_state == TREE_SUBMENU ) + { + if( state == osUnknown ) + { + switch( key ) + { + case k0 ... k9: + { + int n = key - k0; + + TreeSubmenuAction( n ); + + state = osContinue; + } + case kOk: + { + TreeSubmenuAction( Current() ); + + state = osContinue; + } break; + default: + { + state = osContinue; + } break; + } + } + else if( state == osBack ) + { + mgDebug( 1, "mgMainMenu: return from tree view submenu" ); + + // restore last selected entry + int last = m_history.back(); + + DisplayTree( m_node, last ); + + state = osContinue; } } - else if( m_state == TRACKS ) + else if( m_state == PLAYLIST ) { - // Navigate with up/dn, left/right (pgup, pgdn) + mgDebug( 1, "mgMainMenu: in state PLAYLIST" ); if( state == osUnknown ) { switch( key ) { case kOk: { - // Show Song Info + mgDebug( 1, "mgMainMenu: playlist ok" ); state = osContinue; } break; case kRed: { - // Add item to Playlist - if(Current() >= 0 && Current() < (int) m_tracklist->getNumItems()) + mgDebug( 1, "mgMainMenu: edit playlist" ); + Mark(); // Mark (to move), moving done by VDR, calls Move + } + case kGreen: + { + if( m_state == PLAYLIST ) + { + mgDebug( 1, "mgMainMenu: switch to TrackInfo" ); + DisplayTrackInfo(); + } + else if( m_state == PLAYLIST_TRACKINFO ) { - mgContentItem* item = m_tracklist->getItem( Current() ); - mgDebug(3, "Ading item %s to playlist", - item->getTitle().c_str() ); - - m_current_playlist->append( item ); + mgDebug( 1, "mgMainMenu: switch to AlbumInfo" ); + DisplayAlbumInfo(); + } + else if( m_state == PLAYLIST_ALBUMINFO ) + { + mgDebug( 1, "mgMainMenu: switch to Playlist" ); + DisplayPlaylist(); } state = osContinue; } break; - case kGreen: + case kYellow: { - // Green always goes to the filter view + mgDebug( 1, "mgMainMenu: cycle playlist to filter" ); DisplayFilter(); - state = osContinue; + state = osContinue; } break; - case kYellow: + case kBlue: + { + // Submenu + mgDebug( 1, "mgMainMenu: playlist submenu (todo)" ); + + state = osContinue; + } break; + default: + { + mgDebug( 1, "mgMainMenu: default" ); + state = osContinue; + }; + } + } + } + else if( m_state == FILTER ) + { + mgDebug( 1, "mgMainMenu: in state FILTER" ); + + if( state == osUnknown ) + { + switch( key ) + { + case kOk: + { + // OK: Create filter and selection tree and display + mgDebug( 1, "mgMainMenu: create and apply filter" ); + // m_media->applyFilters(); + } break; + case kRed: // ??? { - // Yellow always goes to playlist view - DisplayPlaylist(); state = osContinue; + mgDebug( 1, "mgMainMenu: query and display results" ); } break; - case kBlue: + case kGreen: { - m_history.clear(); - DisplayTree( m_root ); + // cycle FILTER -> TREE + mgDebug( 1, "mgMainMenu: clear filters (todo)" ); + DisplayFilterSelector(); state = osContinue; } break; + case kYellow: + { + // Green: treeview + mgDebug( 1, "mgMainMenu: switch to treeview" ); + + DisplayTreeViewSelector(); + + state = osContinue; + } break; + case kBlue: + { + mgDebug( 1, "mgMainMenu: submenu" ); + } default: { state = osContinue; - } break; + } } } else if( state == osBack ) { - // where to go on back? - state = osContinue; + // m_media->resetFilters();? } } else { mgDebug(1, "Process key: else"); + mgDebug(1, "Process key: %d", (int) state); + } return state; } -void mgMainMenu::Move( int from, int to ) +void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) { - // check current view, perform move in the content view - if( m_state == PLAYLIST ) + m_state = TREE; + + if( node->expand( ) ) { - // resort + Clear(); + + char buffer[256]; + sprintf( buffer, "Muggle - %s", node->getLabel().c_str() ); + + SetTitle( buffer ); + SetButtons(); + + m_node = node; + mgDebug( 1, "mgBrowseMenu::DisplaySelection: node %s received", node->getLabel().c_str() ); + vector children = node->getChildren(); + + mgDebug( 1, "mgBrowseMenu::DisplaySelection: %d elements received", children.size() ); + + for( vector::iterator iter = children.begin(); + iter != children.end(); + iter ++ ) + { + Add( new mgMenuTreeItem( *iter ) ); + } + + cOsdItem *item = Get( select ); + SetCurrent( item ); + + RefreshCurrent(); + DisplayCurrent(true); } + Display(); } -void mgMainMenu::DisplayTracklist() +void mgMainMenu::DisplayTreeViewSelector() { - m_state = TRACKS; - mgDebug( 1, "mgBrowseMenu::DisplayTracklist"); + m_history.clear(); + // collapse all! + DisplayTree( m_root ); +} - Clear(); - SetButtons(); +void mgMainMenu::DisplayTreeSubmenu() +{ + m_state = TREE_SUBMENU; - mgDebug( 1, "mgBrowseMenu::DisplayTracklist: %d elements received", - m_tracklist->getNumItems() ); - - static char titlestr[80]; - sprintf( titlestr, "Muggle Tracklist (%d items)", m_tracklist->getNumItems() ); - SetTitle( titlestr ); + Clear(); + + mgDebug( 1, "Creating Muggle tree view submenu" ); + SetButtons(); - // create tracklist with the current filters - if( m_tracklist ) - { - delete m_tracklist; - } - - m_tracklist = m_media->getTracks(); + SetTitle( "Muggle Tree View Commands" ); + + // Add items + Add( new cOsdItem( "1 - Test1" ) ); + Add( new cOsdItem( "9 - Test9" ) ); + Add( new cOsdItem( "0 - Test0" ) ); + + Display(); +} - for( unsigned int i = 0; i < m_tracklist->getNumItems(); i++) +void mgMainMenu::TreeSubmenuAction( int n ) +{ + mgDebug( "mgMainMenu: TreeSubmenuAction( %d )", n ); + + switch( n ) + { + case 0: { - string label = m_tracklist->getLabel( i, " " ); - Add( new cOsdItem( label.c_str() ) ); - } - - Display(); + // action 0 + } break; + case 1: + { + // action 0 + } break; + default: + { + // undefined action + } break; + } } void mgMainMenu::DisplayPlaylist() @@ -478,55 +504,28 @@ void mgMainMenu::DisplayPlaylist() void mgMainMenu::DisplayTrackInfo() { m_state = PLAYLIST_TRACKINFO; + SetButtons(); + // show info of the currently playing track } void mgMainMenu::DisplayAlbumInfo() { m_state = PLAYLIST_ALBUMINFO; - // show info of the currently playing track + SetButtons(); + + // show info of the currently playing album } -void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) +void mgMainMenu::Move( int from, int to ) { - m_state = TREE; - - if( node->expand( ) ) + // check current view, perform move in the content view + if( m_state == PLAYLIST ) { - Clear(); - - char buffer[256]; - sprintf( buffer, "Muggle - %s", node->getLabel().c_str() ); - - SetTitle( buffer ); - SetButtons(); - - m_node = node; - mgDebug( 1, "mgBrowseMenu::DisplaySelection: node %s received", node->getLabel().c_str() ); - vector children = node->getChildren(); - - mgDebug( 1, "mgBrowseMenu::DisplaySelection: %d elements received", children.size() ); - - for( vector::iterator iter = children.begin(); - iter != children.end(); - iter ++ ) - { - Add( new mgMenuTreeItem( *iter ) ); - } - - cOsdItem *item = Get( select ); - SetCurrent( item ); - - RefreshCurrent(); - DisplayCurrent(true); + // resort } - Display(); -} -void mgMainMenu::DisplayTreeViewSelector() -{ - m_history.clear(); - DisplayTree( m_root ); + // what now? } void mgMainMenu::DisplayFilter() @@ -581,12 +580,15 @@ void mgMainMenu::DisplayFilter() void mgMainMenu::DisplayFilterSelector() { - + // show available filters, load on OK? } /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.12 2004/02/08 10:48:44 LarsAC + * Made major revisions in OSD behavior + * * Revision 1.11 2004/02/03 21:53:32 RaK * beak = break in l 212 * diff --git a/vdr_menu.h b/vdr_menu.h index 1b318c0..76317bc 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,12 +2,12 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.7 $ - * \date $Date: 2004/02/03 19:28:46 $ + * \version $Revision: 1.8 $ + * \date $Date: 2004/02/08 10:48:44 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.h,v 1.7 2004/02/03 19:28:46 LarsAC Exp $ + * $Id: vdr_menu.h,v 1.8 2004/02/08 10:48:44 LarsAC Exp $ * */ /*******************************************************************/ @@ -63,21 +63,25 @@ class mgMainMenu : public cOsdMenu enum MuggleStatus { - TREE, FILTER, TRACKS, + TREE, TREE_SUBMENU, PLAYLIST, PLAYLIST_TRACKINFO, PLAYLIST_ALBUMINFO, + FILTER }; - // To be rewritten mode dependent void SetButtons(); + // Tree view handling void DisplayTree( mgSelectionTreeNode *node, int select = 0 ); void DisplayTreeViewSelector(); + void DisplayTreeSubmenu(); + void TreeSubmenuAction( int n ); + // Filter view handling void DisplayFilter(); void DisplayFilterSelector(); + // Playlist view handling void DisplayPlaylist(); - void DisplayTracklist(); void DisplayTrackInfo(); void DisplayAlbumInfo(); @@ -89,7 +93,7 @@ class mgMainMenu : public cOsdMenu mgSelectionTreeNode *m_node; mgPlaylist *m_current_playlist; mgTracklist *m_tracklist; - + // filter items char *m_title, *m_interpret, *m_album, *m_playlist, *m_filtername; int m_year_min, m_year_max, m_filter; @@ -104,6 +108,9 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.8 2004/02/08 10:48:44 LarsAC + * Made major revisions in OSD behavior + * * Revision 1.7 2004/02/03 19:28:46 LarsAC * Playlist now created in plugin instead of in menu. * -- cgit v1.2.3 From 5ad0ef849611a4df07d001fe633fa7207a66fc5d Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 9 Feb 2004 19:27:52 +0000 Subject: filter set implemented git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@31 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 7 +- gd_content_interface.c | 201 +++++++++++++-------- gd_content_interface.h | 31 +++- mg_content_interface.h | 14 +- mg_media.c | 475 +++++++++++++++++++++++++++++++++++++++++-------- mg_media.h | 145 ++++++++++++--- muggle.c | 8 +- sh_muggle.c | 7 +- vdr_menu.c | 37 ++-- 9 files changed, 720 insertions(+), 205 deletions(-) diff --git a/Makefile b/Makefile index fcad314..d913ff7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.4 2004/02/03 00:13:24 LarsAC Exp $ +# $Id: Makefile,v 1.5 2004/02/09 19:27:52 MountainMan Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -46,9 +46,10 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o sh_dummy_content.o gd_content_interface.o mg_tools.o mg_media.o +OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o -BINOBJS = sh_shell_osd_plugin.o sh_shell_osd.o sh_shell_osd_menuitems.o muggle.o vdr_menu.o content_interface.o dummy_content.o gd_content_interface.o muggle_tools.o mgmedia.o +BINOBJS = mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o +#BINOBJS = sh_console_osd.o muggle.o vdr_menu.o content_interface.o gd_content_interface.o muggle_tools.o mgmedia.o ### Implicit rules: diff --git a/gd_content_interface.c b/gd_content_interface.c index 2727f4d..806dff6 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.7 $ - * \date $Date: 2004/02/03 00:13:24 $ + * \version $Revision: 1.8 $ + * \date $Date: 2004/02/09 19:27:52 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -62,82 +62,122 @@ vector *GdGetStoredPlaylists(MYSQL db) return list; } -/*******************************************************************/ -/* class class gdTrackFilters */ -/********************************************************************/ -gdTrackFilters::gdTrackFilters() -{ - clear(); -} -gdTrackFilters::~gdTrackFilters() -{ -} -void gdTrackFilters::clear() +//------------------------------------------------------------------ +//------------------------------------------------------------------ +// +// class gdTrackFilters +// +//------------------------------------------------------------------ +//------------------------------------------------------------------ + +/*! + ******************************************************************* + * \brief constructor, constracts a number >=1 of filter sets + * + * the first set (index 0 ) is active by default + ********************************************************************/ +gdFilterSets::gdFilterSets() { - mgFilter* filter ; - // remove old filters - for(vector::iterator iter = m_filters.begin(); - iter != m_filters.end(); iter++) - { - delete (*iter); - } - m_filters.clear(); + mgFilter* filter; + m_titles.push_back("Trackfilter"); + // create an initial set of filters with empty values - // title + vector* set = new vector(); + + // title filter = new mgFilterString("title", ""); - m_filters.push_back(filter); + set->push_back(filter); // artist filter = new mgFilterString("artist", ""); - m_filters.push_back(filter); + set->push_back(filter); // genre filter = new mgFilterString("genre", ""); - m_filters.push_back(filter); - // year - filter = new mgFilterInt("year", -1); - m_filters.push_back(filter); + set->push_back(filter); + // year-from + filter = new mgFilterInt("year (from)", 0); + set->push_back(filter); + // year-to + filter = new mgFilterInt("year (to)", 9999); + set->push_back(filter); // rating - filter = new mgFilterInt("rating", -1); - m_filters.push_back(filter); + filter = new mgFilterInt("rating", 0); + set->push_back(filter); + + m_sets.push_back(set); + m_activeSetId = 0; + m_activeSet = set; +} + +/*! + ******************************************************************* + * \briefdestructor + ********************************************************************/ +gdFilterSets::~gdFilterSets() +{ + // everything is done in the destructor of the base class } -string gdTrackFilters::CreateSQL() +/*! + ******************************************************************* + * \brief computes the restrictions specified by the active filter set + * \param viewPrt index of the appropriate defualt view + * \return sql string representing the restrictions + ********************************************************************/ +string gdFilterSets::computeRestriction(int *viewPrt) { string sql_str = "1"; - for(vector::iterator iter = m_filters.begin(); - iter != m_filters.end(); iter++) + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) { - if(strcmp((*iter)->getName(), "title") == 0 ) + if((*iter)->isSet()) + { + if(strcmp((*iter)->getName(), "title") == 0 ) { - sql_str = "AND tracks.title like '" - + (*iter)->getStrVal() + "%'"; + sql_str = sql_str + " AND tracks.title like ' " + + (*iter)->getStrVal() + "'"; } - else if(strcmp((*iter)->getName(), "artist") == 0 ) - { - sql_str = "AND tracks.artist like '" - + (*iter)->getStrVal() + "%'"; + else if(strcmp((*iter)->getName(), "artist") == 0 ) + { + sql_str = sql_str + " AND tracks.artist like ''" + + (*iter)->getStrVal() + "'"; } - else if(strcmp((*iter)->getName(), "genre") == 0 ) - { - sql_str = "AND genre.name like '" - + (*iter)->getStrVal() + "%'"; + else if(strcmp((*iter)->getName(), "genre") == 0 ) + { + sql_str = sql_str + " AND genre.name like ''" + + (*iter)->getStrVal() + "'"; } - else if(strcmp((*iter)->getName(), "year") == 0 ) - { - sql_str = "AND tracks.year = " + (*iter)->getStrVal(); + else if(strcmp((*iter)->getName(), "year (from)") == 0 ) + { + sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); } - else if(strcmp((*iter)->getName(), "rating") == 0 ) - { - sql_str = "AND tracks.rating >= " + (*iter)->getStrVal(); + else if(strcmp((*iter)->getName(), "year (to)") == 0 ) + { + sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "rating") == 0 ) + { + sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); } + else + { + mgWarning("Ignoring unknown filter %s", (*iter)->getName()); + } + } } + *viewPrt = 1; // artist -> album -> title + mgDebug(1, "Applying sql string %s (view=%d)",sql_str.c_str(), *viewPrt); return sql_str; } -/*******************************************************************/ -/* class mgTack */ -/********************************************************************/ +//------------------------------------------------------------------ +//------------------------------------------------------------------ +// +// class mgTack +// +//------------------------------------------------------------------ +//------------------------------------------------------------------ mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack(); /*! @@ -328,14 +368,13 @@ string mgGdTrack::getLabel(int col) } } -string mgGdTrack::getDescription() +vector *mgGdTrack::getTrackInfo() { - if(!m_retrieved) - { - readData(); - } - return m_artist + " - " + m_title; - + return new vector(); +} +bool mgGdTrack::setTrackInfo(vector *info) +{ + return false; } /*! @@ -505,6 +544,13 @@ bool mgGdTrack::writeData() return true; } +//------------------------------------------------------------------ +//------------------------------------------------------------------ +// +// class GdTracklist +// +//------------------------------------------------------------------ +//------------------------------------------------------------------ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) { MYSQL_RES *result; @@ -531,9 +577,13 @@ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) } -/*******************************************************************/ -/* class GdPlaylist */ -/********************************************************************/ +//------------------------------------------------------------------ +//------------------------------------------------------------------ +// +// class GdPlaylist +// +//------------------------------------------------------------------ +//------------------------------------------------------------------ /*! ***************************************************************************** @@ -734,9 +784,13 @@ int GdPlaylist::getPlayTimeRemaining() return 0; // dummy } -/*******************************************************************/ -/* class GdTreeNode */ -/*******************************************************************/ +//------------------------------------------------------------------ +//------------------------------------------------------------------ +// +// class GdTreeNode +// +//------------------------------------------------------------------ +//------------------------------------------------------------------ /*! ***************************************************************************** @@ -1078,27 +1132,27 @@ bool GdTreeNode::expand() new_child = new GdTreeNode(this, // parent "1" , // id "Artist -> Album -> Title", // label, - "1"); + m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "2" , // id "Genre -> Artist -> Album -> Track" , // label, - "1"); + m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "3" , // id "Artist -> Track" , // label, - "1"); + m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "4" , // id "Genre -> Year -> Track" , // label, - "1"); + m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "5" , // id "Album -> Track" , // label, - "1"); + m_restriction); m_children.push_back(new_child); } else { new_child = new GdTreeNode(this, // parent @@ -1199,6 +1253,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.8 2004/02/09 19:27:52 MountainMan + * filter set implemented + * * Revision 1.7 2004/02/03 00:13:24 LarsAC * Improved OSD handling of collapse/back * diff --git a/gd_content_interface.h b/gd_content_interface.h index 476dc5d..fecca7c 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/09 19:27:52 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -35,17 +35,24 @@ int GdInitDatabase(MYSQL *db); std::vector *GdGetStoredPlaylists(MYSQL db); -class gdTrackFilters: public mgTrackFilters +class gdFilterSets : public mgFilterSets { + public: - gdTrackFilters(); - ~gdTrackFilters(); + gdFilterSets(); + // constructor, constracts a number >=1 of filter sets + // the first set (index 0 ) is active by default + + virtual ~gdFilterSets(); + // destructor + + virtual std::string computeRestriction(int *viewPrt); + // computes the (e.g. sql-) restrictions specified by the active filter set + // and returns the index of the appropriate defualt view in viewPrt - virtual std::string CreateSQL(); - virtual void clear(); - }; + /*! ******************************************************************* * \class mgGdTrack @@ -99,7 +106,10 @@ private: virtual std::string getSourceFile(); virtual std::string getTitle(); virtual std::string getLabel(int col); - virtual std::string getDescription(); + + virtual std::vector *getTrackInfo(); + virtual bool setTrackInfo(std::vector*); + virtual std::string getGenre(); virtual int getRating(); @@ -209,6 +219,9 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.4 2004/02/09 19:27:52 MountainMan + * filter set implemented + * * Revision 1.3 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/mg_content_interface.h b/mg_content_interface.h index 1c210d1..b7bc7a3 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/09 19:27:52 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -28,6 +28,7 @@ #include #define ILLEGAL_ID -1 +class mgFilter; /*! ******************************************************************* @@ -90,6 +91,9 @@ class mgContentItem virtual std::string getDescription()// return a short textual description {return "";} + virtual std::vector *getTrackInfo(){return NULL;} + virtual bool updateTrackInfo(std::vector*){return false;} + virtual std::string getGenre(){return "";} virtual int getRating() { @@ -212,6 +216,7 @@ public: virtual ~mgSelectionTreeNode(); // compute children on the fly + virtual bool isLeafNode()=0; virtual bool expand()=0; virtual void collapse(); // removes all children (recursively) @@ -237,11 +242,14 @@ public: // Note: This function allocates memory for the vector and for all elements of the vector // The calling function is in charge of releasing this memory virtual std::vector* getTracks()=0; - + virtual mgContentItem* getSingleTrack()=0; }; /* -------------------- begin CVS log --------------------------------- * $Log: mg_content_interface.h,v $ + * Revision 1.3 2004/02/09 19:27:52 MountainMan + * filter set implemented + * * Revision 1.2 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/mg_media.c b/mg_media.c index 088ba65..54fd32c 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.8 $ - * \date $Date: 2004/02/02 23:33:41 $ + * \version $Revision: 1.9 $ + * \date $Date: 2004/02/09 19:27:52 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -18,7 +18,6 @@ #include "mg_content_interface.h" #include "gd_content_interface.h" -#include "sh_dummy_content.h" using namespace std; @@ -52,8 +51,10 @@ mgFilterInt::mgFilterInt(const char *name, int value, int min, int max) { m_type = INT; m_intval = value; - m_min = min; + m_default_val = value; + m_stored_val = value; m_max = max; + m_min = min; } mgFilterInt::~mgFilterInt() { @@ -66,15 +67,50 @@ string mgFilterInt::getStrVal() return (string)buffer; } + +int mgFilterInt::getIntVal() +{ + return (int) m_intval; +} + +int mgFilterInt::getVal() +{ + return m_intval; +} + int mgFilterInt::getMin() { return m_min; } + int mgFilterInt::getMax() { return m_max; } +void mgFilterInt::store() +{ + m_stored_val = m_intval; +} +void mgFilterInt::restore() +{ + m_intval = m_stored_val; +} +void mgFilterInt::clear() +{ + m_stored_val = m_default_val; + m_intval = m_default_val; +} + +bool mgFilterInt::isSet() +{ + if(m_stored_val == m_default_val) + { + return false; + } + return true; +} + //------------------------------------------------------------------- // mgFilterString //------------------------------------------------------------------- @@ -84,6 +120,8 @@ mgFilterString::mgFilterString(const char *name, const char* value, { m_type = STRING; m_strval = strdup(value); + m_default_val = strdup(value); + m_stored_val = strdup(value); m_allowedchar = allowedchar; m_maxlen = maxlen; } @@ -109,7 +147,33 @@ string mgFilterString::getStrVal() return (string) m_strval; } +void mgFilterString::store() +{ + if(m_stored_val) free(m_stored_val); + m_stored_val = strdup(m_strval); +} +void mgFilterString::restore() +{ + if(m_strval) free(m_strval); + m_strval = strdup(m_stored_val); +} +void mgFilterString::clear() +{ + if(m_stored_val) free(m_stored_val); + if(m_strval) free(m_strval); + + m_stored_val = strdup(m_default_val); + m_strval = strdup(m_default_val); +} +bool mgFilterString::isSet() +{ + if(strlen(m_stored_val) == 0) + { + return false; + } + return true; +} //------------------------------------------------------------------- // mgFilterBool //------------------------------------------------------------------- @@ -118,11 +182,14 @@ mgFilterBool::mgFilterBool(const char *name, bool value, : mgFilter(name) { m_type = BOOL; - m_bval = (bool) value; + m_bval = (int) value; + m_default_val = value; + m_stored_val = value; m_truestr = truestr; m_falsestr = falsestr; } + mgFilterBool::~mgFilterBool() { } @@ -134,27 +201,60 @@ string mgFilterBool::getStrVal() else return "false"; } + +int mgFilterBool::getIntVal() +{ + return (int) m_bval; +} + string mgFilterBool::getTrueString() { return m_truestr; } + string mgFilterBool::getFalseString() { return m_falsestr; } + bool mgFilterBool::getVal() { return (bool) m_bval; } +void mgFilterBool::store() +{ + m_stored_val = (bool) m_bval; +} + +void mgFilterBool::restore() +{ + m_bval = (int) m_stored_val; +} + +void mgFilterBool::clear() +{ + m_stored_val = (int) m_default_val; + m_bval = (int) m_default_val; +} + +bool mgFilterBool::isSet() +{ + if(m_stored_val == m_default_val ) + { + return false; + } + return true; +} //------------------------------------------------------------------- // mgFilterChoice //------------------------------------------------------------------- -mgFilterChoice::mgFilterChoice(const char *name, int val, vector *choices) +mgFilterChoice::mgFilterChoice(const char *name, int value, vector *choices) : mgFilter(name) { m_choices = *choices; - m_selval = val; + m_selval = value; + m_default_val = value; if( m_selval < 0 || m_selval >= (int) m_choices.size() ) { mgError("mgFilterChoice::mgFilterChoice(..): Illegal index %d", m_selval); @@ -177,27 +277,171 @@ vector &mgFilterChoice::getChoices() { return m_choices; } +void mgFilterChoice::store() +{ + m_stored_val = m_selval; + +} +void mgFilterChoice::restore() +{ + m_selval = m_stored_val; +} +void mgFilterChoice::clear() +{ + m_stored_val = m_default_val; + m_selval = m_default_val; +} + +bool mgFilterChoice::isSet() +{ + if(m_stored_val == m_default_val) + { + return false; + } + return true; +} + //------------------------------------------------------------------- -// mgTrackFilters +// mgFilterSets //------------------------------------------------------------------- -mgTrackFilters::mgTrackFilters() +/*! + ******************************************************************* + * \brief constructor + * + * constructor of the base class + ********************************************************************/ +mgFilterSets::mgFilterSets() { + // nothing to be done in the base class } -mgTrackFilters::~mgTrackFilters() + /*! + ******************************************************************* + * \brief destructor + ********************************************************************/ +mgFilterSets::~mgFilterSets() { - for(vector::iterator iter = m_filters.begin(); - iter != m_filters.end(); iter++) + vector *set; + for(vector*>::iterator iter1 = m_sets.begin(); + iter1 != m_sets.end(); iter1++) + { + set = *iter1; + for(vector::iterator iter2 = set->begin(); + iter2 != set->end(); iter2++) { - delete (*iter); + delete (*iter2); } - m_filters.clear(); + set->clear(); + delete set; + } + m_sets.clear(); +} + /*! + ******************************************************************* + * \brief returns the number of available sets + ********************************************************************/ +int mgFilterSets::numSets() +{ + return m_sets.size(); +} + + /*! + ******************************************************************* + * \brief proceeds to the next one in a circular fashion + ********************************************************************/ +void mgFilterSets::nextSet() +{ + m_activeSetId++; + if(m_activeSetId >= (int) m_sets.size()) + { + m_activeSetId = 0; + } + m_activeSet = m_sets[m_activeSetId]; +} + // + +/*! + ******************************************************************* + * \brief activates a specific set by index + * + * \par n : index of the set to be activated + * + * If n is not a valid filter set, the first set (index 0 ) is activated + ********************************************************************/ +void mgFilterSets::select(int n) +{ + m_activeSetId = n ; + if(m_activeSetId >= (int) m_sets.size()) + { + m_activeSetId = 0; + } + m_activeSet = m_sets[m_activeSetId]; + +} + + /*! + ******************************************************************* + * \brief restores the default values for all filter values in the active set + ********************************************************************/ + void mgFilterSets::clear() +{ + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) + { + (*iter)->clear(); + } +} + /*! + ******************************************************************* + * \brief stores the current filter values + ********************************************************************/ + void mgFilterSets::accept() +{ + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) + { + (*iter)->store(); + } +} + +/*! + ******************************************************************* + * \brief returns the active filter set to the application + * + * the application may temporarily modify the filter values + * accept() needs to be called to memorize the changed values + ********************************************************************/ + vector *mgFilterSets::getFilters() +{ + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) + { + (*iter)->restore(); + } + return m_activeSet; } -vector *mgTrackFilters::getFilters() + +/*! + ******************************************************************* + * \brief return title of the active filter set + ********************************************************************/ +string mgFilterSets::getTitle() { - return &m_filters; + if(m_activeSetId < (int) m_titles.size()) + { + return m_titles[m_activeSetId]; + } + else + { + mgWarning("Implementation error: No title string for filter set %d", + m_activeSetId); + return "NO-TITLE"; + } } +//------------------------------------------------------------------- +// mgFilterSets +//------------------------------------------------------------------- /*! ******************************************************************* * \class mgMedia @@ -207,20 +451,15 @@ vector *mgTrackFilters::getFilters() mgMedia::mgMedia(contentType mediatype) { int errval = 0; - m_trackfilter = NULL; + m_filters = NULL; m_mediatype = mediatype; - m_sql_trackfilter = "1"; + m_sql_filter = "1"; m_defaultView = 1; // now initialize the database mgDebug(1, "Media Type %sselected", getMediaTypeName().c_str()); switch(m_mediatype) { - case DUMMY: - { - errval = DummyInitDatabase(&m_db); - break; - } case GD_MP3: { errval = GdInitDatabase(&m_db); @@ -235,11 +474,6 @@ mgMedia::mgMedia(contentType mediatype) mgDebug(3, "Initializing track filters"); switch(m_mediatype) { - case DUMMY: - { - errval = DummyInitDatabase(&m_db); - break; - } case GD_MP3: { errval = GdInitDatabase(&m_db); @@ -248,20 +482,26 @@ mgMedia::mgMedia(contentType mediatype) } } +/*! + ******************************************************************* + * \brief + ********************************************************************/ mgMedia::~mgMedia() { - if( m_trackfilter ) + if( m_filters ) { - delete m_trackfilter; + delete m_filters; } } +/*! + ******************************************************************* + * \brief + ********************************************************************/ string mgMedia::getMediaTypeName() { switch(m_mediatype) { - case DUMMY: - return "DUMMY"; case GD_MP3: return "GiantDisc-mp3"; } @@ -270,57 +510,41 @@ string mgMedia::getMediaTypeName() } +/*! + ******************************************************************* + * \brief + ********************************************************************/ mgSelectionTreeNode* mgMedia::getSelectionRoot() { switch(m_mediatype) { - case DUMMY: - return new DummyTreeNode(m_db, m_defaultView); case GD_MP3: - return new GdTreeNode(m_db, m_defaultView, m_sql_trackfilter); + return new GdTreeNode(m_db, m_defaultView, m_sql_filter); } mgError("implementation Error"); // we should never get here return NULL; } -vector *mgMedia::getTrackFilters() -{ - switch(m_mediatype) - { - case DUMMY: - // return mgFilters(); - case GD_MP3: - if( m_trackfilter == NULL ) - { - m_trackfilter = new gdTrackFilters(); - } - return m_trackfilter->getFilters(); - default: - break; - } - mgError("implementation Error"); // we should never get here - return NULL; -} - -void mgMedia::applyTrackFilters(vector *filters) -{ -} +/*! + ******************************************************************* + * \brief + ********************************************************************/ mgPlaylist* mgMedia::createTemporaryPlaylist() { string tmpname = "current"; return loadPlaylist(tmpname); } +/*! + ******************************************************************* + * \brief + ********************************************************************/ mgPlaylist* mgMedia::loadPlaylist(string name) { mgPlaylist *list; switch(m_mediatype) { - case DUMMY: - list = new DummyPlaylist(name, m_db); - list->setDisplayColumns(getDefaultCols()); - return list; case GD_MP3: list = new GdPlaylist(name, m_db); list->setDisplayColumns(getDefaultCols()); @@ -330,12 +554,14 @@ mgPlaylist* mgMedia::loadPlaylist(string name) return NULL; } +/*! + ******************************************************************* + * \brief + ********************************************************************/ vector *mgMedia::getStoredPlaylists() { switch(m_mediatype) { - case DUMMY: - return DummyGetStoredPlaylists(m_db); case GD_MP3: return GdGetStoredPlaylists(m_db); } @@ -343,15 +569,15 @@ vector *mgMedia::getStoredPlaylists() return new vector(); } +/*! + ******************************************************************* + * \brief + ********************************************************************/ vector mgMedia::getDefaultCols() { vector cols; switch(m_mediatype) { - case DUMMY: - cols.push_back(1); // artist - cols.push_back(0); // track - return cols; case GD_MP3: cols.push_back(1); // artist cols.push_back(0); // track @@ -362,18 +588,17 @@ vector mgMedia::getDefaultCols() return cols; } +/*! + ******************************************************************* + * \brief + ********************************************************************/ mgTracklist* mgMedia::getTracks() { mgTracklist *tracks; switch(m_mediatype) { - case DUMMY: - - tracks = new DummyTracklist(m_db, m_sql_trackfilter); - tracks->setDisplayColumns(getDefaultCols()); - return tracks; case GD_MP3: - tracks = new GdTracklist(m_db, m_sql_trackfilter); + tracks = new GdTracklist(m_db, m_sql_filter); tracks->setDisplayColumns(getDefaultCols()); return tracks; } @@ -381,8 +606,112 @@ mgTracklist* mgMedia::getTracks() return NULL; } + +/*! + ******************************************************************* + * \brief creates FiliterSetObject for the selected media type + * and activates set n (if available) + ********************************************************************/ +void mgMedia::initFilterSet(int num) +{ + switch(m_mediatype) + { + case GD_MP3: + m_filters = new gdFilterSets(); + m_filters->select(num); + break; + } +} + +/*! + ******************************************************************* + * \brief returns pointer to the activen filter set to be modified by the osd + * + * Note: Modifications become only effective by calling applyActiveFilter() + ********************************************************************/ +vector *mgMedia::getActiveFilters() +{ + if(!m_filters) + { + mgError("ImplementationError: getActiveFilters m_filters == NULL"); + } + return m_filters->getFilters(); +} + +/*! + ******************************************************************* + * \brief returns title of the active filter set + ********************************************************************/ +string mgMedia::getActiveFilterTitle() +{ + + switch(m_mediatype) + { + case GD_MP3: + if(!m_filters) + { + mgError("ImplementationError:getActiveFilterTitle m_filters == NULL"); + } + return m_filters->getTitle(); + } + return ""; +} + +/*! + ******************************************************************* + * \brief proceeds to the next filter set in a cirlucar fashion + ********************************************************************/ +void mgMedia::nextFilterSet() +{ + if(!m_filters) + { + mgError("ImplementationError: nextFilterSet m_filters == NULL"); + } + m_filters->nextSet(); +} + +/*! + ******************************************************************* + * \brief clears the current filter values and restores defaults + ********************************************************************/ +void mgMedia::clearActiveFilter() +{ + if(!m_filters) + { + mgError("ImplementationError: clearActiveFilter m_filters == NULL"); + } + m_filters->clear(); +} + +/*! + ******************************************************************* + * \brief Applies the active filter set and returns a root node for the + * selection in the default view for this filter set + ********************************************************************/ +mgSelectionTreeNode *mgMedia::applyActiveFilter() +{ + int view; + + switch(m_mediatype) + { + case GD_MP3: + if(!m_filters) + { + mgError("ImplementationError: applyActiveFilter() m_filters == NULL"); + } + m_filters->accept(); + m_sql_filter = m_filters->computeRestriction(&view); + return new GdTreeNode(m_db, view, m_sql_filter); + } + return NULL; +} + + /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.c,v $ + * Revision 1.9 2004/02/09 19:27:52 MountainMan + * filter set implemented + * * Revision 1.8 2004/02/02 23:33:41 MountainMan * impementation of gdTrackFilters * diff --git a/mg_media.h b/mg_media.h index d39ce93..619c2e7 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,8 +3,8 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.7 $ - * \date $Date: 2004/02/02 23:33:41 $ + * \version $Revision: 1.8 $ + * \date $Date: 2004/02/09 19:27:52 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: MountainMan $ * @@ -28,7 +28,7 @@ class mgSelectionTreeNode; ******************************************************************* * \class mgFilter * - * represents a filter value with boundaries or choices + * Abstract base class for representation of filter values with boundaries ********************************************************************/ class mgFilter { @@ -44,6 +44,11 @@ class mgFilter filterType getType(); const char* getName(); virtual std::string getStrVal()=0; + virtual int getIntVal(){return 0;} + virtual void store()=0; + virtual void restore()=0; + virtual void clear()=0; + virtual bool isSet()=0; }; /*! @@ -55,17 +60,24 @@ class mgFilterInt : public mgFilter private: int m_min; int m_max; + int m_stored_val; + int m_default_val; public: int m_intval; - mgFilterInt(const char *name, int value, int min = 0, int max = INT_MAX); + mgFilterInt(const char *name, int value, int min = 0, int max = 9999); virtual ~mgFilterInt(); int getVal(); int getMin(); int getMax(); virtual std::string getStrVal(); + virtual int getIntVal(); + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); }; /*! @@ -77,6 +89,9 @@ class mgFilterString : public mgFilter private: std::string m_allowedchar; int m_maxlen; + char* m_stored_val; + char* m_default_val; + public: char* m_strval; @@ -88,6 +103,10 @@ class mgFilterString : public mgFilter int getMaxLength(); std::string getAllowedChars(); virtual std::string getStrVal(); + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); }; /*! @@ -99,6 +118,8 @@ class mgFilterBool : public mgFilter private: std::string m_truestr; std::string m_falsestr; + bool m_stored_val; + bool m_default_val; public: int m_bval; @@ -108,9 +129,14 @@ class mgFilterBool : public mgFilter virtual ~mgFilterBool(); virtual std::string getStrVal(); + virtual int getIntVal(); std::string getTrueString(); std::string getFalseString(); bool getVal(); + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); }; /*! @@ -121,7 +147,9 @@ class mgFilterChoice : public mgFilter { private: std::vector m_choices; - + int m_stored_val; + int m_default_val; + public: int m_selval; // index of the currently selected item @@ -130,30 +158,68 @@ class mgFilterChoice : public mgFilter virtual std::string getStrVal(); virtual std::vector &getChoices(); - + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); }; + /*! ******************************************************************* - * \class mgrackFilters + * \class mgFilterSets * - * Represents a set of filters to set and memorize the search - * constraint for a specific track + * Represents one or several sets of filters to set and memorize + * search constraint ********************************************************************/ -class mgTrackFilters -{ +class mgFilterSets { protected: - std::vector m_filters; + int m_activeSetId; + std::vector *m_activeSet; // pointer to the active filter set + + std::vector< std::vector*> m_sets; + // stores name-value pairs, even if a different set is active + + std::vector m_titles; + // stores the titles for all filters + public: - mgTrackFilters(); - virtual ~mgTrackFilters(); + mgFilterSets(); + // constructor, constracts a number >=1 of filter sets + // the first set (index 0 ) is active by default - std::vector *getFilters(); - - virtual std::string CreateSQL()=0; - virtual void clear()=0; - + virtual ~mgFilterSets(); + // destructor + + int numSets(); + // returns the number of available sets + + void nextSet(); + // proceeds to the next one in a circular fashion + + void select(int n); + // activates a specific set by index + + virtual void clear(); + // restores the default values for all filter values in the active set + // normally, the default values represent 'no restrictions' + + void accept(); + // stores the current filter values + + std::vector *getFilters(); + // returns the active set to the application + // the application may temporarily modify the filter values + // accept() needs to be called to memorize the changed values + + virtual std::string computeRestriction(int *viewPrt)=0; + // computes the (e.g. sql-) restrictions specified by the active filter set + // and returns the index of the appropriate defualt view in viewPrt + + std::string getTitle(); + // returns title of active filter set }; + /*! ******************************************************************* * \class mgMedia @@ -170,16 +236,15 @@ class mgMedia public: typedef enum contentType{ - DUMMY =0, - GD_MP3 + GD_MP3 } contentType; private: MYSQL m_db; contentType m_mediatype; - std::string m_sql_trackfilter; + std::string m_sql_filter; int m_defaultView; - mgTrackFilters *m_trackfilter; + mgFilterSets *m_filters; public: mgMedia(contentType mediatype); @@ -189,10 +254,6 @@ class mgMedia mgSelectionTreeNode* getSelectionRoot(); - // filter management - std::vector *getTrackFilters(); - void applyTrackFilters(std::vector *filters); - // playlist management mgPlaylist* createTemporaryPlaylist(); mgPlaylist* loadPlaylist( std::string name ); @@ -200,10 +261,38 @@ class mgMedia std::vector getDefaultCols(); mgTracklist* getTracks(); + + // filter management + + void initFilterSet(int num=0); + // creates FiliterSetObject for the selected media type + // and activates set n (if available) + + + std::vector *getActiveFilters(); + // returns pointer to the activen filter set to be modified by the osd + // Note: Modifications become only active by calling applyActiveFilter() + + std::string getActiveFilterTitle(); + + void nextFilterSet(); + // proceeds to the next filter set in a cirlucar fashion + + void clearActiveFilter(); + // clears the current filter values and restores defaults + + + mgSelectionTreeNode *applyActiveFilter(); + // Applies the active filter set and returns a root node for the + // selection in the default view for this filter set + }; /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.h,v $ + * Revision 1.8 2004/02/09 19:27:52 MountainMan + * filter set implemented + * * Revision 1.7 2004/02/02 23:33:41 MountainMan * impementation of gdTrackFilters * @@ -213,5 +302,5 @@ class mgMedia * * --------------------- end CVS log ---------------------------------- */ -#endif /* END _CONTENT_INTERFACE_H */ +#endif /* END _MG_MEDIA_H */ diff --git a/muggle.c b/muggle.c index 845eab3..e03e56d 100644 --- a/muggle.c +++ b/muggle.c @@ -2,10 +2,10 @@ /*! \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/03 19:28:46 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/09 19:27:52 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ */ /*******************************************************************/ @@ -75,7 +75,7 @@ bool mgMuggle::Start(void) m_media = new mgMedia( mgMedia::GD_MP3 ); m_root = m_media->getSelectionRoot(); m_playlist = m_media->createTemporaryPlaylist(); - + m_media->initFilterSet(); return true; } diff --git a/sh_muggle.c b/sh_muggle.c index ef26f57..485905e 100644 --- a/sh_muggle.c +++ b/sh_muggle.c @@ -2,9 +2,9 @@ #include #include "mysql/mysql.h" -#include "content_interface.h" -#include "mgmedia.h" -#include "muggle_tools.h" +#include "mg_content_interface.h" +#include "mg_media.h" +#include "mg_tools.h" #include #define DISPLAY_SIZE 20 @@ -17,6 +17,7 @@ #define PLAYLIST_VIEW 3 #define FILTER_VIEW 4 +using namespace std; const char playlist_command_str[] = " s : shuffle\n" diff --git a/vdr_menu.c b/vdr_menu.c index 317a647..30d254a 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.12 $ - * \date $Date: 2004/02/08 10:48:44 $ + * \version $Revision: 1.13 $ + * \date $Date: 2004/02/09 19:27:52 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: MountainMan $ * - * $Id: vdr_menu.c,v 1.12 2004/02/08 10:48:44 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.13 2004/02/09 19:27:52 MountainMan Exp $ */ /*******************************************************************/ @@ -344,17 +344,22 @@ eOSState mgMainMenu::ProcessKey(eKeys key) // OK: Create filter and selection tree and display mgDebug( 1, "mgMainMenu: create and apply filter" ); // m_media->applyFilters(); + state = osContinue; } break; - case kRed: // ??? + case kRed: // { - state = osContinue; mgDebug( 1, "mgMainMenu: query and display results" ); + if(m_root) delete m_root; + m_root = m_media->applyActiveFilter(); + DisplayTree( m_root ); + state = osContinue; } break; case kGreen: { // cycle FILTER -> TREE - mgDebug( 1, "mgMainMenu: clear filters (todo)" ); - DisplayFilterSelector(); + mgDebug( 1, "mgMainMenu: next filters " ); + m_media->nextFilterSet(); + DisplayFilter(); state = osContinue; } break; case kYellow: @@ -536,19 +541,27 @@ void mgMainMenu::DisplayFilter() mgDebug( 1, "Creating Muggle filter view" ); SetButtons(); - SetTitle( "Muggle Filter View" ); + SetTitle( m_media->getActiveFilterTitle().c_str() ); + mgDebug( 1, "filter view2" ); - vector *filter_list = m_media->getTrackFilters(); + vector *filter_list = m_media->getActiveFilters(); + mgDebug( 1, "filter view3" ); + int i=0; for( vector::iterator iter = filter_list->begin(); iter != filter_list->end(); iter ++ ) { + mgDebug( 1, "Filter %d/%dint filter %s='%s'", + i, filter_list->size(), + (*iter)->getName(), + (*iter)->getStrVal().c_str()); switch( (*iter)->getType() ) { case mgFilter::INT: { mgFilterInt *fi = (mgFilterInt *) (*iter); + Add( new cMenuEditIntItem( fi->getName(), &(fi->m_intval), fi->getMin(), fi->getMax() ) ); @@ -573,6 +586,7 @@ void mgMainMenu::DisplayFilter() { } break; } + i++; } Display(); @@ -586,6 +600,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.13 2004/02/09 19:27:52 MountainMan + * filter set implemented + * * Revision 1.12 2004/02/08 10:48:44 LarsAC * Made major revisions in OSD behavior * -- cgit v1.2.3 From d64986da204cb06511f362fed1c3fdc5d1d6949c Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 9 Feb 2004 22:07:44 +0000 Subject: secound filter set (album search incl. special view #101 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@32 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 12 +-- gd_content_interface.c | 201 ++++++++++++++++++++++++++++++++++--------------- mg_media.h | 11 ++- 3 files changed, 155 insertions(+), 69 deletions(-) diff --git a/Makefile b/Makefile index d913ff7..af19f23 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.5 2004/02/09 19:27:52 MountainMan Exp $ +# $Id: Makefile,v 1.6 2004/02/09 22:07:44 RaK Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -51,10 +51,9 @@ OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_in BINOBJS = mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o #BINOBJS = sh_console_osd.o muggle.o vdr_menu.o content_interface.o gd_content_interface.o muggle_tools.o mgmedia.o -### Implicit rules: +### Targets: -%.o: %.c - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< +all: libvdr-$(PLUGIN).so # Dependencies: @@ -65,9 +64,10 @@ $(DEPFILE): Makefile -include $(DEPFILE) -### Targets: +### Implicit rules: -all: libvdr-$(PLUGIN).so +%.o: %.c %.h + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< libvdr-$(PLUGIN).so: $(OBJS) $(CXX) $(CXXFLAGS) -shared $(OBJS) -lmysqlclient -o $@ diff --git a/gd_content_interface.c b/gd_content_interface.c index 806dff6..fa7734a 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.8 $ - * \date $Date: 2004/02/09 19:27:52 $ + * \version $Revision: 1.9 $ + * \date $Date: 2004/02/09 22:07:44 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: RaK $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -80,31 +80,44 @@ gdFilterSets::gdFilterSets() { mgFilter* filter; - m_titles.push_back("Trackfilter"); + m_titles.push_back("Track Search"); // create an initial set of filters with empty values vector* set = new vector(); // title - filter = new mgFilterString("title", ""); - set->push_back(filter); + filter = new mgFilterString("title", ""); set->push_back(filter); // artist - filter = new mgFilterString("artist", ""); - set->push_back(filter); + filter = new mgFilterString("artist", ""); set->push_back(filter); // genre - filter = new mgFilterString("genre", ""); - set->push_back(filter); + filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from - filter = new mgFilterInt("year (from)", 0); - set->push_back(filter); + filter = new mgFilterInt("year (from)", 0); set->push_back(filter); // year-to - filter = new mgFilterInt("year (to)", 9999); - set->push_back(filter); + filter = new mgFilterInt("year (to)", 9999); set->push_back(filter); // rating - filter = new mgFilterInt("rating", 0); - set->push_back(filter); + filter = new mgFilterInt("rating", 0); set->push_back(filter); m_sets.push_back(set); + + set->clear(); + m_titles.push_back("Album Search"); + + // title + filter = new mgFilterString("album title", ""); set->push_back(filter); + // artist + filter = new mgFilterString("album artist", ""); set->push_back(filter); + // genre + filter = new mgFilterString("genre", ""); set->push_back(filter); + // year-from + filter = new mgFilterInt("year (from)", 0); set->push_back(filter); + // year-to + filter = new mgFilterInt("year (to)", 9999); set->push_back(filter); + // rating + filter = new mgFilterInt("rating", 0); set->push_back(filter); + + m_sets.push_back(set); + m_activeSetId = 0; m_activeSet = set; } @@ -127,46 +140,97 @@ gdFilterSets::~gdFilterSets() string gdFilterSets::computeRestriction(int *viewPrt) { string sql_str = "1"; - - for(vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - if((*iter)->isSet()) - { - if(strcmp((*iter)->getName(), "title") == 0 ) + switch (m_activeSetId) { + case 0: + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) { - sql_str = sql_str + " AND tracks.title like ' " - + (*iter)->getStrVal() + "'"; + if((*iter)->isSet()) + { + if(strcmp((*iter)->getName(), "title") == 0 ) + { + sql_str = sql_str + " AND tracks.title like ' " + + (*iter)->getStrVal() + "'"; + } + else if(strcmp((*iter)->getName(), "artist") == 0 ) + { + sql_str = sql_str + " AND tracks.artist like ''" + + (*iter)->getStrVal() + "'"; + } + else if(strcmp((*iter)->getName(), "genre") == 0 ) + { + sql_str = sql_str + " AND (genre1.genre like ''" + + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " OR genre2.genre like ''" + + (*iter)->getStrVal() + "')"; + } + else if(strcmp((*iter)->getName(), "year (from)") == 0 ) + { + sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "year (to)") == 0 ) + { + sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "rating") == 0 ) + { + sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); + } + else + { + mgWarning("Ignoring unknown filter %s", (*iter)->getName()); + } + } } - else if(strcmp((*iter)->getName(), "artist") == 0 ) - { - sql_str = sql_str + " AND tracks.artist like ''" - + (*iter)->getStrVal() + "'"; - } - else if(strcmp((*iter)->getName(), "genre") == 0 ) - { - sql_str = sql_str + " AND genre.name like ''" - + (*iter)->getStrVal() + "'"; - } - else if(strcmp((*iter)->getName(), "year (from)") == 0 ) - { - sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "year (to)") == 0 ) - { - sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "rating") == 0 ) - { - sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); - } - else + *viewPrt = 100; // tracks (flatlist for mountain man ;-)) + break; + case 1: + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) { - mgWarning("Ignoring unknown filter %s", (*iter)->getName()); - } - } + if((*iter)->isSet()) + { + if(strcmp((*iter)->getName(), "album title") == 0 ) + { + sql_str = sql_str + " AND album.title like ' " + + (*iter)->getStrVal() + "'"; + } + else if(strcmp((*iter)->getName(), "album artist") == 0 ) + { + sql_str = sql_str + " AND album.artist like ''" + + (*iter)->getStrVal() + "'"; + } + else if(strcmp((*iter)->getName(), "genre") == 0 ) + { + sql_str = sql_str + " AND (genre1.genre like ''" + + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " OR genre2.genre like ''" + + (*iter)->getStrVal() + "')"; + } + else if(strcmp((*iter)->getName(), "year (from)") == 0 ) + { + sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "year (to)") == 0 ) + { + sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "rating") == 0 ) + { + sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); + } + else + { + mgWarning("Ignoring unknown filter %s", (*iter)->getName()); + } + } + } + *viewPrt = 101; // album -> tracks + break; + default: + mgWarning("Ignoring Filter Set %i", m_activeSetId); + break; } - *viewPrt = 1; // artist -> album -> title mgDebug(1, "Applying sql string %s (view=%d)",sql_str.c_str(), *viewPrt); return sql_str; } @@ -1074,28 +1138,44 @@ bool GdTreeNode::expand() return false; } break; - case 100: // Albumsearch result + case 100: + if (m_level == 1) { + sprintf(sqlbuff, + "SELECT CONCAT(tracks.artist,' - ',tracks.title),tracks.id" + " FROM tracks,genre as genre1,genre as genre2,album" + " WHERE (genre.id=tracks.genre1) AND" + " (genre.id=tracks.genre2) AND" + " (album.cddbid=tracks.sourceid) AND" + " %s" + " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" + , m_restriction.c_str()); + idfield = "tracks.id"; + } + break; + case 101: // Albumsearch result if (m_level == 1) { sprintf(sqlbuff, "SELECT DISTINCT" " CONCAT(album.artist,' - ',album.title) as title," " album.cddbid" - " FROM album,genre" - " WHERE " + " FROM album,genre as genre1, genre as genre2" + " WHERE (genre.id=tracks.genre1) AND" + " (genre.id=tracks.genre2) AND" " %s" - " ORDER BY title" + " ORDER BY CONCAT(album.artist,' - ',album.title)" , m_restriction.c_str()); idfield = "tracks.sourceid"; } else if (m_level == 2) { - sprintf(sqlbuff, + sprintf(sqlbuff, "SELECT tracks.title,tracks.id" - " FROM tracks,genre,album" + " FROM tracks,genre as genre1,genre as genre2,album" " WHERE (genre.id=tracks.genre1) AND" + " (genre.id=tracks.genre2) AND" " (album.cddbid=tracks.sourceid) AND" " %s" " ORDER BY tracks.tracknb" , m_restriction.c_str()); - idfield = "tracks.id"; + idfield = "tracks.id"; } else { mgWarning("View #%d level %d' not yet implemented", m_view, m_level); m_expanded = false; @@ -1253,6 +1333,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.9 2004/02/09 22:07:44 RaK + * secound filter set (album search incl. special view #101 + * * Revision 1.8 2004/02/09 19:27:52 MountainMan * filter set implemented * diff --git a/mg_media.h b/mg_media.h index 619c2e7..f599ce4 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.8 $ - * \date $Date: 2004/02/09 19:27:52 $ + * \version $Revision: 1.9 $ + * \date $Date: 2004/02/09 22:07:44 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: RaK $ * * */ @@ -253,7 +253,7 @@ class mgMedia std::string getMediaTypeName(); mgSelectionTreeNode* getSelectionRoot(); - + // playlist management mgPlaylist* createTemporaryPlaylist(); mgPlaylist* loadPlaylist( std::string name ); @@ -290,6 +290,9 @@ class mgMedia /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.h,v $ + * Revision 1.9 2004/02/09 22:07:44 RaK + * secound filter set (album search incl. special view #101 + * * Revision 1.8 2004/02/09 19:27:52 MountainMan * filter set implemented * -- cgit v1.2.3 From 4be249122f5dab1ddd8296f206dfd290ef0aa3a5 Mon Sep 17 00:00:00 2001 From: MountainMan Date: Mon, 9 Feb 2004 23:21:33 +0000 Subject: partial bug fixes git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@33 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index fa7734a..e3c43f4 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.9 $ - * \date $Date: 2004/02/09 22:07:44 $ + * \version $Revision: 1.10 $ + * \date $Date: 2004/02/09 23:21:33 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author file owner: $Author: MountainMan $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -79,11 +79,11 @@ vector *GdGetStoredPlaylists(MYSQL db) gdFilterSets::gdFilterSets() { mgFilter* filter; - + vector* set; m_titles.push_back("Track Search"); // create an initial set of filters with empty values - vector* set = new vector(); + set = new vector(); // title filter = new mgFilterString("title", ""); set->push_back(filter); @@ -100,9 +100,9 @@ gdFilterSets::gdFilterSets() m_sets.push_back(set); - set->clear(); m_titles.push_back("Album Search"); + set = new vector(); // title filter = new mgFilterString("album title", ""); set->push_back(filter); // artist @@ -149,19 +149,19 @@ string gdFilterSets::computeRestriction(int *viewPrt) { if(strcmp((*iter)->getName(), "title") == 0 ) { - sql_str = sql_str + " AND tracks.title like ' " + sql_str = sql_str + " AND tracks.title like '" + (*iter)->getStrVal() + "'"; } else if(strcmp((*iter)->getName(), "artist") == 0 ) { - sql_str = sql_str + " AND tracks.artist like ''" + sql_str = sql_str + " AND tracks.artist like '" + (*iter)->getStrVal() + "'"; } else if(strcmp((*iter)->getName(), "genre") == 0 ) { - sql_str = sql_str + " AND (genre1.genre like ''" + sql_str = sql_str + " AND (genre1.genre like '" + (*iter)->getStrVal() + "'"; - sql_str = sql_str + " OR genre2.genre like ''" + sql_str = sql_str + " OR genre2.genre like '" + (*iter)->getStrVal() + "')"; } else if(strcmp((*iter)->getName(), "year (from)") == 0 ) @@ -197,14 +197,14 @@ string gdFilterSets::computeRestriction(int *viewPrt) } else if(strcmp((*iter)->getName(), "album artist") == 0 ) { - sql_str = sql_str + " AND album.artist like ''" + sql_str = sql_str + " AND album.artist like '" + (*iter)->getStrVal() + "'"; } else if(strcmp((*iter)->getName(), "genre") == 0 ) { - sql_str = sql_str + " AND (genre1.genre like ''" + sql_str = sql_str + " AND (genre1.genre like '" + (*iter)->getStrVal() + "'"; - sql_str = sql_str + " OR genre2.genre like ''" + sql_str = sql_str + " OR genre2.genre like '" + (*iter)->getStrVal() + "')"; } else if(strcmp((*iter)->getName(), "year (from)") == 0 ) @@ -1143,8 +1143,8 @@ bool GdTreeNode::expand() sprintf(sqlbuff, "SELECT CONCAT(tracks.artist,' - ',tracks.title),tracks.id" " FROM tracks,genre as genre1,genre as genre2,album" - " WHERE (genre.id=tracks.genre1) AND" - " (genre.id=tracks.genre2) AND" + " WHERE (genre1.id=tracks.genre1 OR" + " genre2.id=tracks.genre2) AND" " (album.cddbid=tracks.sourceid) AND" " %s" " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" @@ -1158,9 +1158,9 @@ bool GdTreeNode::expand() "SELECT DISTINCT" " CONCAT(album.artist,' - ',album.title) as title," " album.cddbid" - " FROM album,genre as genre1, genre as genre2" - " WHERE (genre.id=tracks.genre1) AND" - " (genre.id=tracks.genre2) AND" + " FROM tracks,album,genre as genre1, genre as genre2" + " WHERE (genre1.id=tracks.genre1 OR" + " genre2.id=tracks.genre2) AND" " %s" " ORDER BY CONCAT(album.artist,' - ',album.title)" , m_restriction.c_str()); @@ -1169,8 +1169,8 @@ bool GdTreeNode::expand() sprintf(sqlbuff, "SELECT tracks.title,tracks.id" " FROM tracks,genre as genre1,genre as genre2,album" - " WHERE (genre.id=tracks.genre1) AND" - " (genre.id=tracks.genre2) AND" + " WHERE (genre1.id=tracks.genre1 OR" + " genre2.id=tracks.genre2) AND" " (album.cddbid=tracks.sourceid) AND" " %s" " ORDER BY tracks.tracknb" @@ -1333,6 +1333,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.10 2004/02/09 23:21:33 MountainMan + * partial bug fixes + * * Revision 1.9 2004/02/09 22:07:44 RaK * secound filter set (album search incl. special view #101 * -- cgit v1.2.3 From 8fd87850751a84a7a2d4acee17f535975d634033 Mon Sep 17 00:00:00 2001 From: RaK Date: Tue, 10 Feb 2004 01:23:06 +0000 Subject: Ein fehler beim Tracksearch behoben. geht jetzt, aber nur einmal?!?! git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@34 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index e3c43f4..03b3929 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.10 $ - * \date $Date: 2004/02/09 23:21:33 $ + * \version $Revision: 1.11 $ + * \date $Date: 2004/02/10 01:23:06 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: RaK $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -40,7 +40,7 @@ int GdInitDatabase(MYSQL *db) } if(mysql_real_connect(db,"localhost","root","", - "GiantDisc2",0,NULL,0) == NULL) + "GiantDisc",0,NULL,0) == NULL) { return -2; } @@ -1142,10 +1142,9 @@ bool GdTreeNode::expand() if (m_level == 1) { sprintf(sqlbuff, "SELECT CONCAT(tracks.artist,' - ',tracks.title),tracks.id" - " FROM tracks,genre as genre1,genre as genre2,album" - " WHERE (genre1.id=tracks.genre1 OR" + " FROM tracks,genre as genre1,genre as genre2" + " WHERE (genre1.id=tracks.genre1 AND" " genre2.id=tracks.genre2) AND" - " (album.cddbid=tracks.sourceid) AND" " %s" " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" , m_restriction.c_str()); @@ -1159,7 +1158,7 @@ bool GdTreeNode::expand() " CONCAT(album.artist,' - ',album.title) as title," " album.cddbid" " FROM tracks,album,genre as genre1, genre as genre2" - " WHERE (genre1.id=tracks.genre1 OR" + " WHERE (genre1.id=tracks.genre1 AND" " genre2.id=tracks.genre2) AND" " %s" " ORDER BY CONCAT(album.artist,' - ',album.title)" @@ -1333,6 +1332,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.11 2004/02/10 01:23:06 RaK + * Ein fehler beim Tracksearch behoben. geht jetzt, aber nur einmal?!?! + * * Revision 1.10 2004/02/09 23:21:33 MountainMan * partial bug fixes * -- cgit v1.2.3 From a526903d95e186b6520b6ef58d051b7e3c0e0f4e Mon Sep 17 00:00:00 2001 From: RaK Date: Tue, 10 Feb 2004 23:47:23 +0000 Subject: - views konsitent gemacht. siehe FROMJOIN - isLeafNode angepasst fuer neue views 4,5,100,101 - like '%abba%' eingebaut - filter ist default mit abba gefuellt, zum leichteren testen. - search results werden jetzt gleich im ROOT expanded git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@35 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 168 ++++++++++++++++++++++++------------------------- mg_content_interface.c | 15 +++-- mg_media.c | 18 ++++-- 3 files changed, 106 insertions(+), 95 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 03b3929..fe2d946 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.11 $ - * \date $Date: 2004/02/10 01:23:06 $ + * \version $Revision: 1.12 $ + * \date $Date: 2004/02/10 23:47:23 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -88,13 +88,13 @@ gdFilterSets::gdFilterSets() // title filter = new mgFilterString("title", ""); set->push_back(filter); // artist - filter = new mgFilterString("artist", ""); set->push_back(filter); + filter = new mgFilterString("artist", "abba"); set->push_back(filter); // genre filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from - filter = new mgFilterInt("year (from)", 0); set->push_back(filter); + filter = new mgFilterInt("year (from)", 1900); set->push_back(filter); // year-to - filter = new mgFilterInt("year (to)", 9999); set->push_back(filter); + filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); // rating filter = new mgFilterInt("rating", 0); set->push_back(filter); @@ -106,20 +106,20 @@ gdFilterSets::gdFilterSets() // title filter = new mgFilterString("album title", ""); set->push_back(filter); // artist - filter = new mgFilterString("album artist", ""); set->push_back(filter); + filter = new mgFilterString("album artist", "abba"); set->push_back(filter); // genre filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from - filter = new mgFilterInt("year (from)", 0); set->push_back(filter); + filter = new mgFilterInt("year (from)", 1900); set->push_back(filter); // year-to - filter = new mgFilterInt("year (to)", 9999); set->push_back(filter); + filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); // rating filter = new mgFilterInt("rating", 0); set->push_back(filter); m_sets.push_back(set); m_activeSetId = 0; - m_activeSet = set; + m_activeSet = m_sets[m_activeSetId]; } /*! @@ -149,13 +149,13 @@ string gdFilterSets::computeRestriction(int *viewPrt) { if(strcmp((*iter)->getName(), "title") == 0 ) { - sql_str = sql_str + " AND tracks.title like '" - + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " AND tracks.title like '%%" + + (*iter)->getStrVal() + "%%'"; } else if(strcmp((*iter)->getName(), "artist") == 0 ) { - sql_str = sql_str + " AND tracks.artist like '" - + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " AND tracks.artist like '%%" + + (*iter)->getStrVal() + "%%'"; } else if(strcmp((*iter)->getName(), "genre") == 0 ) { @@ -192,13 +192,13 @@ string gdFilterSets::computeRestriction(int *viewPrt) { if(strcmp((*iter)->getName(), "album title") == 0 ) { - sql_str = sql_str + " AND album.title like ' " - + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " AND album.title like '%%" + + (*iter)->getStrVal() + "%%'"; } else if(strcmp((*iter)->getName(), "album artist") == 0 ) { - sql_str = sql_str + " AND album.artist like '" - + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " AND album.artist like '%%" + + (*iter)->getStrVal() + "%%'"; } else if(strcmp((*iter)->getName(), "genre") == 0 ) { @@ -913,12 +913,36 @@ bool GdTreeNode::isLeafNode() return false; } break; - case 3: // Artist -> Track + case 3: // Artist -> Track + if( m_level <= 2 ) + { + return false; + } + break; + case 4: if( m_level <= 2 ) { return false; } break; + case 5: + if( m_level <= 1 ) + { + return false; + } + break; + case 100: + if( m_level <= 0 ) + { + return false; + } + break; + case 101: + if( m_level <= 1 ) + { + return false; + } + break; default: mgError("View '%d' not yet implemented", m_view); } @@ -949,12 +973,15 @@ bool GdTreeNode::expand() string labelfield; // human readable db field for the column to be expanded string idfield; // unique id field for the column to be expanded - string tables; // stores the db tables used string new_restriction_field; // field to be restricted by the new level string new_restriction; // complete restriction str for the current child string new_label; GdTreeNode* new_child; + string tables; // stores the db tables used + + #define FROMJOIN " FROM tracks, genre as genre1, genre as genre2, album WHERE tracks.sourceid=album.cddbid AND genre1.id=tracks.genre1 AND genre2.id=tracks.genre2 AND %s " + if (m_expanded) { mgWarning("Node already expanded\n"); @@ -972,25 +999,22 @@ bool GdTreeNode::expand() case 1: // artist -> album -> title if(m_level == 1) { sprintf(sqlbuff, - "SELECT DISTINCT album.artist,album.artist" - " FROM album" - " WHERE %s" + "SELECT DISTINCT album.artist,album.artist" + FROMJOIN " ORDER BY album.artist" , m_restriction.c_str() ); idfield = "album.artist"; } else if(m_level == 2) { // artist -> album sprintf(sqlbuff, "SELECT DISTINCT album.title,album.cddbid" - " FROM album" - " WHERE %s" + FROMJOIN " ORDER BY album.title" , m_restriction.c_str() ); idfield = "album.cddbid"; } else if(m_level == 3) { // album -> title sprintf(sqlbuff, "SELECT tracks.title,tracks.id" - " FROM tracks,album" - " WHERE %s AND tracks.sourceid=album.cddbid" + FROMJOIN " ORDER BY tracks.tracknb" , m_restriction.c_str() ); idfield = "tracks.id"; @@ -1003,42 +1027,29 @@ bool GdTreeNode::expand() case 2: // genre -> artist -> album -> track if(m_level == 1) { // genre sprintf(sqlbuff, - "SELECT DISTINCT genre.genre,tracks.genre1" - " FROM tracks,genre" - " WHERE (genre.id=tracks.genre1) AND" - " %s" - " ORDER BY genre.id" + "SELECT DISTINCT genre1.genre,tracks.genre1" + FROMJOIN + " ORDER BY genre1.id" , m_restriction.c_str()); idfield = "tracks.genre1"; } else if(m_level == 2) { // genre -> artist sprintf(sqlbuff, "SELECT DISTINCT album.artist,album.artist" - " FROM tracks,genre,album" - " WHERE (genre.id=tracks.genre1) AND" - " (album.cddbid=tracks.sourceid) AND" - " %s" + FROMJOIN " ORDER BY album.artist", m_restriction.c_str()); idfield = "album.artist"; } else if(m_level == 3) { // genre -> artist -> album sprintf(sqlbuff, - "SELECT DISTINCT" - " album.title," - " tracks.sourceid" - " FROM tracks,genre,album" - " WHERE (genre.id=tracks.genre1) AND" - " (album.cddbid=tracks.sourceid) AND" - " %s" + "SELECT DISTINCT album.title,tracks.sourceid" + FROMJOIN " ORDER BY album.title" , m_restriction.c_str()); idfield = "tracks.sourceid"; } else if(m_level == 4) { // genre -> artist -> album -> track sprintf(sqlbuff, "SELECT DISTINCT tracks.title, tracks.id" - " FROM tracks,genre,album" - " WHERE (genre.id=tracks.genre1) AND" - " (album.cddbid=tracks.sourceid) AND" - " %s" + FROMJOIN " ORDER BY tracks.tracknb" , m_restriction.c_str()); idfield = "tracks.id"; @@ -1053,18 +1064,14 @@ bool GdTreeNode::expand() { sprintf(sqlbuff, "SELECT DISTINCT tracks.artist,tracks.artist" - " FROM tracks" - " WHERE " - " %s" + FROMJOIN " ORDER BY tracks.artist" , m_restriction.c_str()); idfield = "tracks.artist"; } else if (m_level == 2) { // Track sprintf(sqlbuff, "SELECT DISTINCT tracks.title,tracks.id" - " FROM tracks, album" - " WHERE" - " %s AND tracks.sourceid=album.cddbid" + FROMJOIN " ORDER BY tracks.title" , m_restriction.c_str()); idfield = "tracks.id"; @@ -1077,30 +1084,24 @@ bool GdTreeNode::expand() case 4: // Genre -> Year -> Track if(m_level == 1) { // Genre sprintf(sqlbuff, - "SELECT DISTINCT genre.genre,tracks.genre1" - " FROM genre,tracks" - " WHERE (genre.id=tracks.genre1) AND" - " %s" - " ORDER BY genre.genre" + "SELECT DISTINCT genre1.genre,tracks.genre1" + FROMJOIN + " ORDER BY genre1.genre" , m_restriction.c_str()); idfield = "tracks.genre1"; } else if (m_level == 2) { // Year sprintf(sqlbuff, "SELECT DISTINCT tracks.year,tracks.year" - " FROM genre,tracks" - " WHERE (genre.id=tracks.genre1) AND" - " %s" + FROMJOIN " ORDER BY tracks.year" , m_restriction.c_str()); idfield = "tracks.year"; } else if (m_level == 3) { // Track sprintf(sqlbuff, "SELECT DISTINCT" - " CONCAT(tracks.artist,' - ',tracks.title) AS title" - " ,tracks.id" - " FROM tracks,genre" - " WHERE (genre.id=tracks.genre1) AND" - " %s" + " CONCAT(tracks.artist,' - ',tracks.title) AS title" + " ,tracks.id" + FROMJOIN " ORDER BY tracks.title" , m_restriction.c_str()); idfield = "tracks.id"; @@ -1116,19 +1117,14 @@ bool GdTreeNode::expand() "SELECT DISTINCT" " CONCAT(album.artist,' - ',album.title) AS title," " album.cddbid" - " FROM album" - " WHERE 1 AND" - " %s" + FROMJOIN " ORDER BY title" , m_restriction.c_str()); - idfield = "tracks.genre1"; + idfield = "tracks.sourceid"; } else if (m_level == 2) { // sprintf(sqlbuff, "SELECT DISTINCT tracks.title, tracks.id" - " FROM tracks,genre,album" - " WHERE (genre.id=tracks.genre1) AND" - " (album.cddbid=tracks.sourceid) AND" - " %s" + FROMJOIN " ORDER BY tracks.tracknb" , m_restriction.c_str()); idfield = "tracks.id"; @@ -1141,11 +1137,9 @@ bool GdTreeNode::expand() case 100: if (m_level == 1) { sprintf(sqlbuff, - "SELECT CONCAT(tracks.artist,' - ',tracks.title),tracks.id" - " FROM tracks,genre as genre1,genre as genre2" - " WHERE (genre1.id=tracks.genre1 AND" - " genre2.id=tracks.genre2) AND" - " %s" + "SELECT CONCAT(tracks.artist,' - ',tracks.title)," + " tracks.id" + FROMJOIN " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" , m_restriction.c_str()); idfield = "tracks.id"; @@ -1157,21 +1151,14 @@ bool GdTreeNode::expand() "SELECT DISTINCT" " CONCAT(album.artist,' - ',album.title) as title," " album.cddbid" - " FROM tracks,album,genre as genre1, genre as genre2" - " WHERE (genre1.id=tracks.genre1 AND" - " genre2.id=tracks.genre2) AND" - " %s" + FROMJOIN " ORDER BY CONCAT(album.artist,' - ',album.title)" , m_restriction.c_str()); idfield = "tracks.sourceid"; } else if (m_level == 2) { sprintf(sqlbuff, "SELECT tracks.title,tracks.id" - " FROM tracks,genre as genre1,genre as genre2,album" - " WHERE (genre1.id=tracks.genre1 OR" - " genre2.id=tracks.genre2) AND" - " (album.cddbid=tracks.sourceid) AND" - " %s" + FROMJOIN " ORDER BY tracks.tracknb" , m_restriction.c_str()); idfield = "tracks.id"; @@ -1332,6 +1319,13 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.12 2004/02/10 23:47:23 RaK + * - views konsitent gemacht. siehe FROMJOIN + * - isLeafNode angepasst fuer neue views 4,5,100,101 + * - like '%abba%' eingebaut + * - filter ist default mit abba gefuellt, zum leichteren testen. + * - search results werden jetzt gleich im ROOT expanded + * * Revision 1.11 2004/02/10 01:23:06 RaK * Ein fehler beim Tracksearch behoben. geht jetzt, aber nur einmal?!?! * diff --git a/mg_content_interface.c b/mg_content_interface.c index 0d1f270..c6c2e63 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/02/10 23:47:23 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: RaK $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -354,7 +354,7 @@ void mgSelectionTreeNode::collapse() // removes all children (recursively) { vector ::iterator iter; mgSelectionTreeNode* ptr; - + for(iter = m_children.begin(); iter != m_children.end();iter++) { ptr = *iter; @@ -398,6 +398,13 @@ string mgSelectionTreeNode::getRestrictions() /* -------------------- begin CVS log --------------------------------- * $Log: mg_content_interface.c,v $ + * Revision 1.3 2004/02/10 23:47:23 RaK + * - views konsitent gemacht. siehe FROMJOIN + * - isLeafNode angepasst fuer neue views 4,5,100,101 + * - like '%abba%' eingebaut + * - filter ist default mit abba gefuellt, zum leichteren testen. + * - search results werden jetzt gleich im ROOT expanded + * * Revision 1.2 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/mg_media.c b/mg_media.c index 54fd32c..3a2a874 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.9 $ - * \date $Date: 2004/02/09 19:27:52 $ + * \version $Revision: 1.10 $ + * \date $Date: 2004/02/10 23:47:23 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: RaK $ * * */ @@ -691,6 +691,7 @@ void mgMedia::clearActiveFilter() mgSelectionTreeNode *mgMedia::applyActiveFilter() { int view; + GdTreeNode* node; switch(m_mediatype) { @@ -701,7 +702,9 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() } m_filters->accept(); m_sql_filter = m_filters->computeRestriction(&view); - return new GdTreeNode(m_db, view, m_sql_filter); + node = new GdTreeNode(m_db, view, m_sql_filter); + node->expand(); + return node->getChildren()[0]; } return NULL; } @@ -709,6 +712,13 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.c,v $ + * Revision 1.10 2004/02/10 23:47:23 RaK + * - views konsitent gemacht. siehe FROMJOIN + * - isLeafNode angepasst fuer neue views 4,5,100,101 + * - like '%abba%' eingebaut + * - filter ist default mit abba gefuellt, zum leichteren testen. + * - search results werden jetzt gleich im ROOT expanded + * * Revision 1.9 2004/02/09 19:27:52 MountainMan * filter set implemented * -- cgit v1.2.3 From cb80e862553b828f5967c4da2e561725db4995de Mon Sep 17 00:00:00 2001 From: RaK Date: Wed, 11 Feb 2004 21:55:16 +0000 Subject: - playlistsearch eingebaut - filter search liefert nun in der zweiten ebene alle tracks des albums/playlist git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@36 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 4 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index fe2d946..0a71e23 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.12 $ - * \date $Date: 2004/02/10 23:47:23 $ + * \version $Revision: 1.13 $ + * \date $Date: 2004/02/11 21:55:16 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -118,6 +118,28 @@ gdFilterSets::gdFilterSets() m_sets.push_back(set); + m_titles.push_back("Playlist Search"); + + set = new vector(); + // title + filter = new mgFilterString("playlist title", ""); set->push_back(filter); + // artist + filter = new mgFilterString("playlist author", ""); set->push_back(filter); + // title + filter = new mgFilterString("title", ""); set->push_back(filter); + // artist + filter = new mgFilterString("artist", "abba"); set->push_back(filter); + // genre + filter = new mgFilterString("genre", ""); set->push_back(filter); + // year-from + filter = new mgFilterInt("year (from)", 1900); set->push_back(filter); + // year-to + filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); + // rating + filter = new mgFilterInt("rating", 0); set->push_back(filter); + + m_sets.push_back(set); + m_activeSetId = 0; m_activeSet = m_sets[m_activeSetId]; } @@ -227,6 +249,59 @@ string gdFilterSets::computeRestriction(int *viewPrt) } *viewPrt = 101; // album -> tracks break; + case 2: // playlist -> tracks + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) + { + if((*iter)->isSet()) + { + if(strcmp((*iter)->getName(), "playlist title") == 0 ) + { + sql_str = sql_str + " AND playlist.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "playlist author") == 0 ) + { + sql_str = sql_str + " AND playlist.author like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "title") == 0 ) + { + sql_str = sql_str + " AND tracks.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "artist") == 0 ) + { + sql_str = sql_str + " AND tracks.artist like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "genre") == 0 ) + { + sql_str = sql_str + " AND (genre1.genre like '" + + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " OR genre2.genre like '" + + (*iter)->getStrVal() + "')"; + } + else if(strcmp((*iter)->getName(), "year (from)") == 0 ) + { + sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "year (to)") == 0 ) + { + sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "rating") == 0 ) + { + sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); + } + else + { + mgWarning("Ignoring unknown filter %s", (*iter)->getName()); + } + } + } + *viewPrt = 102; // playlist -> tracks + break; default: mgWarning("Ignoring Filter Set %i", m_activeSetId); break; @@ -943,6 +1018,12 @@ bool GdTreeNode::isLeafNode() return false; } break; + case 102: + if( m_level <= 1 ) + { + return false; + } + break; default: mgError("View '%d' not yet implemented", m_view); } @@ -1168,6 +1249,37 @@ bool GdTreeNode::expand() return false; } break; + case 102: + if (m_level == 1) { + sprintf(sqlbuff, + "SELECT CONCAT(playlist.title,' (',playlist.author,')')," + " playlist.id" + " FROM playlist,playlistitem,tracks,genre as genre1,genre as genre2" + " WHERE playlist.id=playlistitem.playlist AND" + " playlistitem.trackid=tracks.id AND" + " genre1.id=tracks.genre1 AND" + " genre2.id=tracks.genre2 AND" + " %s" + " ORDER CONCAT(playlist.title,' (',playlist.author,')')," + , m_restriction.c_str()); + idfield = "playlist.id"; + } else if (m_level == 2) { + sprintf(sqlbuff, + "SELECT DISTINCT CONCAT(tracks.artist,' - ',tracks.title)," + " tracks.id" + " FROM playlist,playlistitem,tracks" + " WHERE playlist.id=playlistitem.playlist AND" + " playlistitem.trackid=tracks.id AND" + " %s" + " ORDER playlistitem.tracknumber" + , m_restriction.c_str()); + idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; default: mgError("View '%d' not yet implemented", m_view); } @@ -1184,8 +1296,15 @@ bool GdTreeNode::expand() // row[1] is the unique id for the new child sprintf(idbuf, "%s_%03d", m_id.c_str(), numchild); - new_restriction = m_restriction + " AND " - + idfield + "= '" + row[1] + "'"; + // Zweite ebene zeigt alle Tracks des Albums und nicht nur + // diese die den Filterkriterien entsprechen. + // das betrifft nur die Search Views! + if(m_view <100) { + new_restriction = m_restriction + " AND " + + idfield + "='" + row[1] + "'"; + } else { + new_restriction = idfield + "='" + row[1] + "'"; + } new_child = new GdTreeNode(this, // parent (string) idbuf, // id @@ -1319,6 +1438,11 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.13 2004/02/11 21:55:16 RaK + * - playlistsearch eingebaut + * - filter search liefert nun in der zweiten + * ebene alle tracks des albums/playlist + * * Revision 1.12 2004/02/10 23:47:23 RaK * - views konsitent gemacht. siehe FROMJOIN * - isLeafNode angepasst fuer neue views 4,5,100,101 -- cgit v1.2.3 From 0ea51bb1b4a280d523bec9ecc8ffb8adaa3d4548 Mon Sep 17 00:00:00 2001 From: RaK Date: Thu, 12 Feb 2004 07:56:46 +0000 Subject: - SQL Fehler bei der Playlist Search korrigiert git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@37 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 0a71e23..c916e6a 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.13 $ - * \date $Date: 2004/02/11 21:55:16 $ + * \version $Revision: 1.14 $ + * \date $Date: 2004/02/12 07:56:46 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -1252,7 +1252,7 @@ bool GdTreeNode::expand() case 102: if (m_level == 1) { sprintf(sqlbuff, - "SELECT CONCAT(playlist.title,' (',playlist.author,')')," + "SELECT DISTINCT playlist.title," " playlist.id" " FROM playlist,playlistitem,tracks,genre as genre1,genre as genre2" " WHERE playlist.id=playlistitem.playlist AND" @@ -1260,18 +1260,18 @@ bool GdTreeNode::expand() " genre1.id=tracks.genre1 AND" " genre2.id=tracks.genre2 AND" " %s" - " ORDER CONCAT(playlist.title,' (',playlist.author,')')," + " ORDER BY playlist.title," , m_restriction.c_str()); idfield = "playlist.id"; } else if (m_level == 2) { sprintf(sqlbuff, - "SELECT DISTINCT CONCAT(tracks.artist,' - ',tracks.title)," + "SELECT CONCAT(tracks.artist,' - ',tracks.title)," " tracks.id" " FROM playlist,playlistitem,tracks" " WHERE playlist.id=playlistitem.playlist AND" " playlistitem.trackid=tracks.id AND" " %s" - " ORDER playlistitem.tracknumber" + " ORDER BY playlistitem.tracknumber" , m_restriction.c_str()); idfield = "tracks.id"; } else { @@ -1438,6 +1438,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.14 2004/02/12 07:56:46 RaK + * - SQL Fehler bei der Playlist Search korrigiert + * * Revision 1.13 2004/02/11 21:55:16 RaK * - playlistsearch eingebaut * - filter search liefert nun in der zweiten -- cgit v1.2.3 From 13414776db3ca8457acef6b3466720507c191946 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 12 Feb 2004 09:08:48 +0000 Subject: Added handling of filter choices (untested) git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@39 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 51 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 30d254a..03064e4 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -1,13 +1,13 @@ /*******************************************************************/ /*! \file vdr_menu.c - * \brief Implements menu handling for broswing media libraries within VDR + * \brief Implements menu handling for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.13 $ - * \date $Date: 2004/02/09 19:27:52 $ + * \version $Revision: 1.14 $ + * \date $Date: 2004/02/12 09:08:48 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.13 2004/02/09 19:27:52 MountainMan Exp $ + * $Id: vdr_menu.c,v 1.14 2004/02/12 09:08:48 LarsAC Exp $ */ /*******************************************************************/ @@ -20,6 +20,7 @@ #include "mg_content_interface.h" #include "mg_tools.h" #include "mg_media.h" +#include "mg_filters.h" #include "gd_content_interface.h" @@ -349,17 +350,26 @@ eOSState mgMainMenu::ProcessKey(eKeys key) case kRed: // { mgDebug( 1, "mgMainMenu: query and display results" ); - if(m_root) delete m_root; + + if( m_root ) + { + delete m_root; + } + m_root = m_media->applyActiveFilter(); + // collapse all? DisplayTree( m_root ); + state = osContinue; } break; case kGreen: { // cycle FILTER -> TREE mgDebug( 1, "mgMainMenu: next filters " ); + m_media->nextFilterSet(); DisplayFilter(); + state = osContinue; } break; case kYellow: @@ -569,6 +579,9 @@ void mgMainMenu::DisplayFilter() case mgFilter::STRING: { mgFilterString *fs = (mgFilterString *) (*iter); + + // BUG: This might be buggy as fs->getAllowedChars() may become + // invalid while VDR is still trying to access it Add( new cMenuEditStrItem( fs->getName(), fs->m_strval, fs->getMaxLength(), fs->getAllowedChars().c_str() ) ); @@ -581,6 +594,29 @@ void mgMainMenu::DisplayFilter() fb->getTrueString().c_str(), fb->getFalseString().c_str() ) ); } break; + case mgFilter::CHOICE: + { + mgFilterChoice *fc = (mgFilterChoice *) (*iter); + vector choices = fc->getChoices(); + + char **choices_str = new (char *)[ choices.size() ]; + + int j = 0; + for( vector::iterator iter = choices.begin(); + iter != choices.end(); + iter ++, j ++ ) + { + // BUG: Is this a big memory leak!? When to delete and who? + choices_str[j] = strndup( choices[i].c_str(), 128 ); + } + + Add( new cMenuEditStraItem( fc->getName(), &( fc->m_selval ), + choices.size(), choices_str ) ); + + // delete all choices_str elements! + // delete[] choices_str; // ??? + + } break; default: case mgFilter::UNDEF: { @@ -600,6 +636,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.14 2004/02/12 09:08:48 LarsAC + * Added handling of filter choices (untested) + * * Revision 1.13 2004/02/09 19:27:52 MountainMan * filter set implemented * -- cgit v1.2.3 From 368bacb21c6d0c57c0066ae67c88810b6a79b58d Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 12 Feb 2004 09:15:07 +0000 Subject: Moved filter classes into separate files git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@40 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 14 +- TODO | 1 + gd_content_interface.c | 11 +- gd_content_interface.h | 11 +- mg_filters.c | 296 ++++++++++++++++++++++ mg_filters.h | 164 +++++++++++++ mg_media.c | 298 +---------------------- mg_media.h | 240 ++++-------------- sh_console_osd.c | 212 ---------------- sh_console_osd.h | 191 --------------- sh_console_osd_menuitems.c | 530 ---------------------------------------- sh_console_osd_menuitems.h | 130 ---------- sh_dummy_content.c | 596 --------------------------------------------- sh_dummy_content.h | 188 -------------- sh_muggle.c | 366 ---------------------------- sh_muggle2.c | 66 ----- sh_plugin.c | 77 ------ sh_plugin.h | 46 ---- 18 files changed, 538 insertions(+), 2899 deletions(-) create mode 100644 mg_filters.c create mode 100644 mg_filters.h delete mode 100644 sh_console_osd.c delete mode 100644 sh_console_osd.h delete mode 100644 sh_console_osd_menuitems.c delete mode 100644 sh_console_osd_menuitems.h delete mode 100644 sh_dummy_content.c delete mode 100644 sh_dummy_content.h delete mode 100644 sh_muggle.c delete mode 100644 sh_muggle2.c delete mode 100644 sh_plugin.c delete mode 100644 sh_plugin.h diff --git a/Makefile b/Makefile index af19f23..68d0ac8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.6 2004/02/09 22:07:44 RaK Exp $ +# $Id: Makefile,v 1.7 2004/02/12 09:15:07 LarsAC Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -46,10 +46,8 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o - -BINOBJS = mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o -#BINOBJS = sh_console_osd.o muggle.o vdr_menu.o content_interface.o gd_content_interface.o muggle_tools.o mgmedia.o +OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o +BINOBJS = mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o ### Targets: @@ -73,12 +71,6 @@ libvdr-$(PLUGIN).so: $(OBJS) $(CXX) $(CXXFLAGS) -shared $(OBJS) -lmysqlclient -o $@ @cp $@ $(LIBDIR)/$@.$(VDRVERSION) -sh_muggle : sh_muggle.c $(BINOBJS) - $(CXX) $(CXXFLAGS) $(BINOBJS) sh_muggle.c -lmysqlclient -o $@ - -sh_muggle2 : sh_muggle2.c $(BINOBJS) - $(CXX) $(CXXFLAGS) sh_muggle2.c $(BINOBJS) -lmysqlclient -o $@ - dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) diff --git a/TODO b/TODO index 3489dbd..885341e 100644 --- a/TODO +++ b/TODO @@ -17,6 +17,7 @@ OSD - handle filters: - create tracklist from filter - create tree from filter +- i18n Content ------- diff --git a/gd_content_interface.c b/gd_content_interface.c index c916e6a..bcaf500 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.14 $ - * \date $Date: 2004/02/12 07:56:46 $ + * \version $Revision: 1.15 $ + * \date $Date: 2004/02/12 09:15:07 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author file owner: $Author: LarsAC $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -40,7 +40,7 @@ int GdInitDatabase(MYSQL *db) } if(mysql_real_connect(db,"localhost","root","", - "GiantDisc",0,NULL,0) == NULL) + "GiantDisc2",0,NULL,0) == NULL) { return -2; } @@ -1438,6 +1438,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.15 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * * Revision 1.14 2004/02/12 07:56:46 RaK * - SQL Fehler bei der Playlist Search korrigiert * diff --git a/gd_content_interface.h b/gd_content_interface.h index fecca7c..ce86163 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/09 19:27:52 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/02/12 09:15:07 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: LarsAC $ * * Declares main classes of for content items and interfaces to SQL databases * @@ -29,12 +29,12 @@ #include "mg_content_interface.h" #include "mg_media.h" +#include "mg_filters.h" // non-member function int GdInitDatabase(MYSQL *db); std::vector *GdGetStoredPlaylists(MYSQL db); - class gdFilterSets : public mgFilterSets { @@ -219,6 +219,9 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.5 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * * Revision 1.4 2004/02/09 19:27:52 MountainMan * filter set implemented * diff --git a/mg_filters.c b/mg_filters.c new file mode 100644 index 0000000..bd19178 --- /dev/null +++ b/mg_filters.c @@ -0,0 +1,296 @@ +/*******************************************************************/ +/*! \file mg_filters.c + * \brief + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/12 09:15:07 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + +/* makes sure we dont parse the same declarations twice */ +#include "mg_filters.h" +#include "mg_tools.h" + +using namespace std; + +//------------------------------------------------------------------- +// mgFilter +//------------------------------------------------------------------- +mgFilter::mgFilter(const char* name) +{ + m_name = strdup(name); +} +mgFilter::~mgFilter() +{ + free(m_name); +} + +const char* mgFilter::getName() +{ + return m_name; +} + +mgFilter::filterType mgFilter::getType() +{ + return m_type; +} + +//------------------------------------------------------------------- +// mgFilterInt +//------------------------------------------------------------------- +mgFilterInt::mgFilterInt(const char *name, int value, int min, int max) + : mgFilter(name) +{ + m_type = INT; + m_intval = value; + m_default_val = value; + m_stored_val = value; + m_max = max; + m_min = min; +} +mgFilterInt::~mgFilterInt() +{ +} + +string mgFilterInt::getStrVal() +{ + char buffer[20]; + sprintf(buffer, "%d", m_intval); + + return (string)buffer; +} + +int mgFilterInt::getIntVal() +{ + return (int) m_intval; +} + +int mgFilterInt::getVal() +{ + return m_intval; +} + +int mgFilterInt::getMin() +{ + return m_min; +} + +int mgFilterInt::getMax() +{ + return m_max; +} + +void mgFilterInt::store() +{ + m_stored_val = m_intval; +} +void mgFilterInt::restore() +{ + m_intval = m_stored_val; +} +void mgFilterInt::clear() +{ + m_stored_val = m_default_val; + m_intval = m_default_val; +} + +bool mgFilterInt::isSet() +{ + if(m_stored_val == m_default_val) + { + return false; + } + return true; +} + +//------------------------------------------------------------------- +// mgFilterString +//------------------------------------------------------------------- +mgFilterString::mgFilterString(const char *name, const char* value, + int maxlen, string allowedchar) + : mgFilter(name) +{ + m_type = STRING; + m_strval = strdup(value); + m_default_val = strdup(value); + m_stored_val = strdup(value); + m_allowedchar = allowedchar; + m_maxlen = maxlen; +} +mgFilterString::~mgFilterString() +{ + if(m_strval) + { + free(m_strval); + } +} + +int mgFilterString::getMaxLength() +{ + return m_maxlen; +} + +string mgFilterString::getAllowedChars() +{ + return m_allowedchar; +} +string mgFilterString::getStrVal() +{ + + return (string) m_strval; +} +void mgFilterString::store() +{ + if(m_stored_val) free(m_stored_val); + m_stored_val = strdup(m_strval); +} +void mgFilterString::restore() +{ + if(m_strval) free(m_strval); + m_strval = strdup(m_stored_val); +} +void mgFilterString::clear() +{ + if(m_stored_val) free(m_stored_val); + if(m_strval) free(m_strval); + + m_stored_val = strdup(m_default_val); + m_strval = strdup(m_default_val); +} + +bool mgFilterString::isSet() +{ + if(strlen(m_stored_val) == 0) + { + return false; + } + return true; +} +//------------------------------------------------------------------- +// mgFilterBool +//------------------------------------------------------------------- +mgFilterBool::mgFilterBool(const char *name, bool value, + string truestr, string falsestr) + : mgFilter(name) +{ + m_type = BOOL; + m_bval = (int) value; + m_default_val = value; + m_stored_val = value; + m_truestr = truestr; + m_falsestr = falsestr; + +} + +mgFilterBool::~mgFilterBool() +{ +} + +string mgFilterBool::getStrVal() +{ + if(m_bval) + return "true"; + else + return "false"; +} + +int mgFilterBool::getIntVal() +{ + return (int) m_bval; +} + +string mgFilterBool::getTrueString() +{ + return m_truestr; +} + +string mgFilterBool::getFalseString() +{ + return m_falsestr; +} + +bool mgFilterBool::getVal() +{ + return (bool) m_bval; +} + +void mgFilterBool::store() +{ + m_stored_val = (bool) m_bval; +} + +void mgFilterBool::restore() +{ + m_bval = (int) m_stored_val; +} + +void mgFilterBool::clear() +{ + m_stored_val = (int) m_default_val; + m_bval = (int) m_default_val; +} + +bool mgFilterBool::isSet() +{ + if(m_stored_val == m_default_val ) + { + return false; + } + return true; +} +//------------------------------------------------------------------- +// mgFilterChoice +//------------------------------------------------------------------- +mgFilterChoice::mgFilterChoice(const char *name, int value, vector *choices) + : mgFilter(name) +{ + m_choices = *choices; + m_selval = value; + m_default_val = value; + if( m_selval < 0 || m_selval >= (int) m_choices.size() ) + { + mgError("mgFilterChoice::mgFilterChoice(..): Illegal index %d", m_selval); + } +} +mgFilterChoice::~mgFilterChoice() +{ + m_choices.clear(); +} + +string mgFilterChoice::getStrVal() +{ + if( m_selval < 0 || m_selval >= (int) m_choices.size() ) + { + mgError("mgFilterChoice::getStrVal(): Illegal index %d", m_selval); + } + return m_choices[m_selval]; +} +vector &mgFilterChoice::getChoices() +{ + return m_choices; +} +void mgFilterChoice::store() +{ + m_stored_val = m_selval; + +} +void mgFilterChoice::restore() +{ + m_selval = m_stored_val; +} +void mgFilterChoice::clear() +{ + m_stored_val = m_default_val; + m_selval = m_default_val; +} + +bool mgFilterChoice::isSet() +{ + if(m_stored_val == m_default_val) + { + return false; + } + return true; +} diff --git a/mg_filters.h b/mg_filters.h new file mode 100644 index 0000000..8a983f7 --- /dev/null +++ b/mg_filters.h @@ -0,0 +1,164 @@ +/*******************************************************************/ +/*! \file mg_filters.h + * \brief Top level access to media in vdr plugin muggle + * for the vdr muggle plugindatabase + ******************************************************************** + * \version $Revision: 1.1 $ + * \date $Date: 2004/02/12 09:15:07 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: LarsAC $ + */ +/*******************************************************************/ + +#ifndef _MG_FILTERS_H +#define _MG_FILTERS_H + +#include +#include + +/*! + ******************************************************************* + * \class mgFilter + * + * Abstract base class for representation of filter values with boundaries + ********************************************************************/ +class mgFilter +{ + public: + typedef enum filterType + { + UNDEF=0, INT, STRING, BOOL, CHOICE + } filterType; + + protected: + filterType m_type; + char* m_name; + + public: + mgFilter(const char* name); + virtual ~mgFilter(); + filterType getType(); + const char* getName(); + virtual std::string getStrVal()=0; + virtual int getIntVal(){return 0;} + virtual void store()=0; + virtual void restore()=0; + virtual void clear()=0; + virtual bool isSet()=0; +}; + +/*! + ******************************************************************* + * \class mgFilterInt + ********************************************************************/ +class mgFilterInt : public mgFilter +{ + private: + int m_min; + int m_max; + int m_stored_val; + int m_default_val; + + public: + int m_intval; + + mgFilterInt(const char *name, int value, int min = 0, int max = 9999); + virtual ~mgFilterInt(); + + int getVal(); + int getMin(); + int getMax(); + virtual std::string getStrVal(); + virtual int getIntVal(); + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); +}; + +/*! + ******************************************************************* + * \class mgFilterString + ********************************************************************/ +class mgFilterString : public mgFilter +{ + private: + std::string m_allowedchar; + int m_maxlen; + char* m_stored_val; + char* m_default_val; + + public: + char* m_strval; + + mgFilterString(const char *name, const char* value, int maxlen=255, + std::string allowedchar="abcdefghijklmnopqrstuvwxyz0123456789-"); + + virtual ~mgFilterString(); + + int getMaxLength(); + std::string getAllowedChars(); + virtual std::string getStrVal(); + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); +}; + +/*! + ******************************************************************* + * \class mgFilterBool + ********************************************************************/ +class mgFilterBool : public mgFilter +{ + private: + std::string m_truestr; + std::string m_falsestr; + bool m_stored_val; + bool m_default_val; + + public: + int m_bval; + + mgFilterBool(const char *name, bool value, + std::string truestr="yes", std::string falsestr="no"); + virtual ~mgFilterBool(); + + virtual std::string getStrVal(); + virtual int getIntVal(); + std::string getTrueString(); + std::string getFalseString(); + bool getVal(); + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); +}; + +/*! + ******************************************************************* + * \class mgFilterChoice + ********************************************************************/ +class mgFilterChoice : public mgFilter +{ + private: + std::vector m_choices; + int m_stored_val; + int m_default_val; + + public: + int m_selval; // index of the currently selected item + + mgFilterChoice(const char *name, int val, std::vector *choices); + virtual ~mgFilterChoice(); + + virtual std::string getStrVal(); + virtual std::vector &getChoices(); + virtual void store(); + virtual void restore(); + virtual void clear(); + virtual bool isSet(); +}; + + +#endif diff --git a/mg_media.c b/mg_media.c index 3a2a874..4a2e83b 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,12 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.10 $ - * \date $Date: 2004/02/10 23:47:23 $ + * \version $Revision: 1.11 $ + * \date $Date: 2004/02/12 09:15:07 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ - * - * + * \author file owner: $Author: LarsAC $ */ /*******************************************************************/ @@ -18,289 +16,8 @@ #include "mg_content_interface.h" #include "gd_content_interface.h" - using namespace std; -//------------------------------------------------------------------- -// mgFilter -//------------------------------------------------------------------- -mgFilter::mgFilter(const char* name) -{ - m_name = strdup(name); -} -mgFilter::~mgFilter() -{ - free(m_name); -} - -const char* mgFilter::getName() -{ - return m_name; -} - -mgFilter::filterType mgFilter::getType() -{ - return m_type; -} - -//------------------------------------------------------------------- -// mgFilterInt -//------------------------------------------------------------------- -mgFilterInt::mgFilterInt(const char *name, int value, int min, int max) - : mgFilter(name) -{ - m_type = INT; - m_intval = value; - m_default_val = value; - m_stored_val = value; - m_max = max; - m_min = min; -} -mgFilterInt::~mgFilterInt() -{ -} - -string mgFilterInt::getStrVal() -{ - char buffer[20]; - sprintf(buffer, "%d", m_intval); - - return (string)buffer; -} - -int mgFilterInt::getIntVal() -{ - return (int) m_intval; -} - -int mgFilterInt::getVal() -{ - return m_intval; -} - -int mgFilterInt::getMin() -{ - return m_min; -} - -int mgFilterInt::getMax() -{ - return m_max; -} - -void mgFilterInt::store() -{ - m_stored_val = m_intval; -} -void mgFilterInt::restore() -{ - m_intval = m_stored_val; -} -void mgFilterInt::clear() -{ - m_stored_val = m_default_val; - m_intval = m_default_val; -} - -bool mgFilterInt::isSet() -{ - if(m_stored_val == m_default_val) - { - return false; - } - return true; -} - -//------------------------------------------------------------------- -// mgFilterString -//------------------------------------------------------------------- -mgFilterString::mgFilterString(const char *name, const char* value, - int maxlen, string allowedchar) - : mgFilter(name) -{ - m_type = STRING; - m_strval = strdup(value); - m_default_val = strdup(value); - m_stored_val = strdup(value); - m_allowedchar = allowedchar; - m_maxlen = maxlen; -} -mgFilterString::~mgFilterString() -{ - if(m_strval) - { - free(m_strval); - } -} - -int mgFilterString::getMaxLength() -{ - return m_maxlen; -} - -string mgFilterString::getAllowedChars() -{ - return m_allowedchar; -} -string mgFilterString::getStrVal() -{ - - return (string) m_strval; -} -void mgFilterString::store() -{ - if(m_stored_val) free(m_stored_val); - m_stored_val = strdup(m_strval); -} -void mgFilterString::restore() -{ - if(m_strval) free(m_strval); - m_strval = strdup(m_stored_val); -} -void mgFilterString::clear() -{ - if(m_stored_val) free(m_stored_val); - if(m_strval) free(m_strval); - - m_stored_val = strdup(m_default_val); - m_strval = strdup(m_default_val); -} - -bool mgFilterString::isSet() -{ - if(strlen(m_stored_val) == 0) - { - return false; - } - return true; -} -//------------------------------------------------------------------- -// mgFilterBool -//------------------------------------------------------------------- -mgFilterBool::mgFilterBool(const char *name, bool value, - string truestr, string falsestr) - : mgFilter(name) -{ - m_type = BOOL; - m_bval = (int) value; - m_default_val = value; - m_stored_val = value; - m_truestr = truestr; - m_falsestr = falsestr; - -} - -mgFilterBool::~mgFilterBool() -{ -} - -string mgFilterBool::getStrVal() -{ - if(m_bval) - return "true"; - else - return "false"; -} - -int mgFilterBool::getIntVal() -{ - return (int) m_bval; -} - -string mgFilterBool::getTrueString() -{ - return m_truestr; -} - -string mgFilterBool::getFalseString() -{ - return m_falsestr; -} - -bool mgFilterBool::getVal() -{ - return (bool) m_bval; -} - -void mgFilterBool::store() -{ - m_stored_val = (bool) m_bval; -} - -void mgFilterBool::restore() -{ - m_bval = (int) m_stored_val; -} - -void mgFilterBool::clear() -{ - m_stored_val = (int) m_default_val; - m_bval = (int) m_default_val; -} - -bool mgFilterBool::isSet() -{ - if(m_stored_val == m_default_val ) - { - return false; - } - return true; -} -//------------------------------------------------------------------- -// mgFilterChoice -//------------------------------------------------------------------- -mgFilterChoice::mgFilterChoice(const char *name, int value, vector *choices) - : mgFilter(name) -{ - m_choices = *choices; - m_selval = value; - m_default_val = value; - if( m_selval < 0 || m_selval >= (int) m_choices.size() ) - { - mgError("mgFilterChoice::mgFilterChoice(..): Illegal index %d", m_selval); - } -} -mgFilterChoice::~mgFilterChoice() -{ - m_choices.clear(); -} - -string mgFilterChoice::getStrVal() -{ - if( m_selval < 0 || m_selval >= (int) m_choices.size() ) - { - mgError("mgFilterChoice::getStrVal(): Illegal index %d", m_selval); - } - return m_choices[m_selval]; -} -vector &mgFilterChoice::getChoices() -{ - return m_choices; -} -void mgFilterChoice::store() -{ - m_stored_val = m_selval; - -} -void mgFilterChoice::restore() -{ - m_selval = m_stored_val; -} -void mgFilterChoice::clear() -{ - m_stored_val = m_default_val; - m_selval = m_default_val; -} - -bool mgFilterChoice::isSet() -{ - if(m_stored_val == m_default_val) - { - return false; - } - return true; -} - //------------------------------------------------------------------- // mgFilterSets //------------------------------------------------------------------- @@ -463,7 +180,7 @@ mgMedia::mgMedia(contentType mediatype) case GD_MP3: { errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); + mgDebug(3, "Successfully conntected to sql database 'GiantDisc2'"); } } if(errval < 0) @@ -477,7 +194,7 @@ mgMedia::mgMedia(contentType mediatype) case GD_MP3: { errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); + mgDebug(3, "Successfully conntected to sql database 'GiantDisc2'"); } } } @@ -503,7 +220,7 @@ string mgMedia::getMediaTypeName() switch(m_mediatype) { case GD_MP3: - return "GiantDisc-mp3"; + return "GiantDisc2"; } mgError("implementation Error"); // we should never get here return ""; @@ -712,6 +429,9 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.c,v $ + * Revision 1.11 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * * Revision 1.10 2004/02/10 23:47:23 RaK * - views konsitent gemacht. siehe FROMJOIN * - isLeafNode angepasst fuer neue views 4,5,100,101 diff --git a/mg_media.h b/mg_media.h index f599ce4..cb685c3 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,12 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.9 $ - * \date $Date: 2004/02/09 22:07:44 $ + * \version $Revision: 1.10 $ + * \date $Date: 2004/02/12 09:15:07 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ - * - * + * \author file owner: $Author: LarsAC $ */ /*******************************************************************/ /* makes sure we dont use parse the same declarations twice */ @@ -23,147 +21,8 @@ class mgPlaylist; class mgTracklist; class mgSelectionTreeNode; - -/*! - ******************************************************************* - * \class mgFilter - * - * Abstract base class for representation of filter values with boundaries - ********************************************************************/ -class mgFilter -{ - public: - typedef enum filterType { UNDEF=0, INT, STRING, BOOL }filterType; - protected: - filterType m_type; - char* m_name; - - public: - mgFilter(const char* name); - virtual ~mgFilter(); - filterType getType(); - const char* getName(); - virtual std::string getStrVal()=0; - virtual int getIntVal(){return 0;} - virtual void store()=0; - virtual void restore()=0; - virtual void clear()=0; - virtual bool isSet()=0; -}; - -/*! - ******************************************************************* - * \class mgFilterInt - ********************************************************************/ -class mgFilterInt : public mgFilter -{ - private: - int m_min; - int m_max; - int m_stored_val; - int m_default_val; - - public: - int m_intval; - - mgFilterInt(const char *name, int value, int min = 0, int max = 9999); - virtual ~mgFilterInt(); - - int getVal(); - int getMin(); - int getMax(); - virtual std::string getStrVal(); - virtual int getIntVal(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - -/*! - ******************************************************************* - * \class mgFilterString - ********************************************************************/ -class mgFilterString : public mgFilter -{ - private: - std::string m_allowedchar; - int m_maxlen; - char* m_stored_val; - char* m_default_val; - - public: - char* m_strval; - - mgFilterString(const char *name, const char* value, int maxlen=255, - std::string allowedchar="abcdefghijklmnopqrstuvwxyz0123456789-"); - - virtual ~mgFilterString(); - - int getMaxLength(); - std::string getAllowedChars(); - virtual std::string getStrVal(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - -/*! - ******************************************************************* - * \class mgFilterBool - ********************************************************************/ -class mgFilterBool : public mgFilter -{ - private: - std::string m_truestr; - std::string m_falsestr; - bool m_stored_val; - bool m_default_val; - - public: - int m_bval; - - mgFilterBool(const char *name, bool value, - std::string truestr="yes", std::string falsestr="no"); - virtual ~mgFilterBool(); - - virtual std::string getStrVal(); - virtual int getIntVal(); - std::string getTrueString(); - std::string getFalseString(); - bool getVal(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - -/*! - ******************************************************************* - * \class mgFilterChoices - ********************************************************************/ -class mgFilterChoice : public mgFilter -{ - private: - std::vector m_choices; - int m_stored_val; - int m_default_val; - - public: - int m_selval; // index of the currently selected item - - mgFilterChoice(const char *name, int val, std::vector *choices); - virtual ~mgFilterChoice(); - - virtual std::string getStrVal(); - virtual std::vector &getChoices(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - +class mgFilter; +class mgFilterSets; /*! ******************************************************************* @@ -220,6 +79,7 @@ class mgFilterSets { // returns title of active filter set }; + /*! ******************************************************************* * \class mgMedia @@ -235,61 +95,63 @@ class mgMedia { public: - typedef enum contentType{ - GD_MP3 - } contentType; - + typedef enum contentType + { + GD_MP3 + } contentType; + private: MYSQL m_db; contentType m_mediatype; std::string m_sql_filter; int m_defaultView; - mgFilterSets *m_filters; + mgFilterSets *m_filters; public: - mgMedia(contentType mediatype); - ~mgMedia(); - - std::string getMediaTypeName(); - - mgSelectionTreeNode* getSelectionRoot(); - - // playlist management - mgPlaylist* createTemporaryPlaylist(); - mgPlaylist* loadPlaylist( std::string name ); - std::vector *getStoredPlaylists(); - - std::vector getDefaultCols(); - mgTracklist* getTracks(); - - // filter management - - void initFilterSet(int num=0); - // creates FiliterSetObject for the selected media type - // and activates set n (if available) - - - std::vector *getActiveFilters(); - // returns pointer to the activen filter set to be modified by the osd - // Note: Modifications become only active by calling applyActiveFilter() - - std::string getActiveFilterTitle(); - - void nextFilterSet(); - // proceeds to the next filter set in a cirlucar fashion - - void clearActiveFilter(); - // clears the current filter values and restores defaults - - - mgSelectionTreeNode *applyActiveFilter(); - // Applies the active filter set and returns a root node for the - // selection in the default view for this filter set + mgMedia(contentType mediatype); + ~mgMedia(); + + std::string getMediaTypeName(); + + mgSelectionTreeNode* getSelectionRoot(); + + // playlist management + mgPlaylist* createTemporaryPlaylist(); + mgPlaylist* loadPlaylist( std::string name ); + std::vector *getStoredPlaylists(); + + std::vector getDefaultCols(); + mgTracklist* getTracks(); + + // filter management + + void initFilterSet(int num=0); + // creates FiliterSetObject for the selected media type + // and activates set n (if available) + + std::vector *getActiveFilters(); + // returns pointer to the activen filter set to be modified by the osd + // Note: Modifications become only active by calling applyActiveFilter() + + std::string getActiveFilterTitle(); + + void nextFilterSet(); + // proceeds to the next filter set in a cirlucar fashion + + void clearActiveFilter(); + // clears the current filter values and restores defaults + + mgSelectionTreeNode *applyActiveFilter(); + // Applies the active filter set and returns a root node for the + // selection in the default view for this filter set }; /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.h,v $ + * Revision 1.10 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * * Revision 1.9 2004/02/09 22:07:44 RaK * secound filter set (album search incl. special view #101 * diff --git a/sh_console_osd.c b/sh_console_osd.c deleted file mode 100644 index ebb4702..0000000 --- a/sh_console_osd.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * osd.c: Abstract On Screen Display layer - * - * See the main source file 'vdr.c' for copyright information and - * how to reach the author. - * - * $Id: sh_console_osd.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ - */ - -#include "myosd.h" -#include -//#include "device.h" -// #include "i18n.h" -//#include "status.h" - -// --- cOsdItem -------------------------------------------------------------- - -cOsdItem::cOsdItem(eOSState State) -{ - text = NULL; -} - -cOsdItem::cOsdItem(const char *Text, eOSState State) -{ - text = NULL; - SetText(Text,true); -} - -cOsdItem::~cOsdItem() -{ - free(text); -} - -void cOsdItem::SetText(const char *Text, bool Copy) -{ - free(text); - text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership! -} - -const char* cOsdItem::Get() -{ - return text; -} -void cOsdItem::Display() -{ - printf("%s\n", text); -} - -// --- cOsdMenu -------------------------------------------------------------- - -cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) -{ - cols[0] = c0; - cols[1] = c1; - cols[2] = c2; - cols[3] = c3; - cols[4] = c4; - m_first = 0; - m_current = m_marked = -1; - subMenu = NULL; - helpRed = helpGreen = helpYellow = helpBlue = NULL; - status = NULL; -} - -cOsdMenu::~cOsdMenu() -{ - free(title); - delete subMenu; - free(status); -} - - -void cOsdMenu::SetTitle(const char *Title, bool ShowDate) -{ - title = strdup(Title); -} - -void cOsdMenu::Add(cOsdItem *Item) -{ - m_display.push_back(Item); -} - -void cOsdMenu::Display(void) -{ - //Interface->Clear(); - printf("\n\n\n\n"); - printf("----------(start %d, current %d)--------------------\n", - m_first, m_current); - //Interface->SetCols(cols); - //Interface->Title(title); - printf(" Title: %s\n", title); - //Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); - for(int i= m_first; i < (int) m_display.size() && i < m_first+DISPLAY_SIZE; i++) - { - if(i == m_current) - { - printf("==>"); - - } - else - { - printf(" "); - - } - printf("%s\n", m_display[i]->Get()); - } - printf("----------------------------------------------\n"); - if(hasHotkeys) - { - printf("%15s (r) %15s (g) %15s (y) %15s (b)\n", - helpRed? helpRed :"" , - helpGreen? helpGreen :"" , - helpYellow? helpYellow :"" , - helpBlue? helpBlue :"" ); - - } -} - -int cOsdMenu::Index(cOsdItem *Item) -{ - int pos=0; - for(vector::iterator iter = m_display.begin(); - iter != m_display.end(); iter++) - { - if((*iter) == Item) - { - break; - } - pos++; - } - return pos; -} -void cOsdMenu::SetCurrent(cOsdItem *Item) -{ - m_current = Item ? Index(Item) : -1; -} -void cOsdMenu::CursorUp(void) -{ - if(m_current >0) m_current--; - if(m_current < m_first) m_first = m_first - PAGE_JUMP; - Display(); -} -void cOsdMenu::CursorDown(void) -{ - if(m_current < (int)m_display.size()-1) - m_current++; - if(m_current >= m_first+DISPLAY_SIZE) m_first =m_first + PAGE_JUMP; - Display(); -} - -void cOsdMenu::PageUp(void) -{ - if(m_first >= PAGE_JUMP) - { - m_first -= PAGE_JUMP; - m_current -= PAGE_JUMP; - } - Display(); - -} -void cOsdMenu::PageDown(void) -{ - if(m_first+ PAGE_JUMP< (int)m_display.size()) - { - m_first += PAGE_JUMP; - m_current += PAGE_JUMP; - if(m_current < m_first) m_current = m_first; - if(m_current >= (int) m_display.size()) - m_current = (int) m_display.size(); - } - Display(); -} - -void cOsdMenu::Clear(void) -{ - m_first = 0; - m_current = m_marked = -1; - hasHotkeys = false; - for(vector::iterator iter = m_display.begin(); - iter != m_display.end(); iter++) - { - delete *iter; - } - m_display.clear(); -} - -eOSState cOsdMenu::ProcessKey(eKeys Key) -{ - - switch (Key) { - case kUp|k_Repeat: - case kUp: CursorUp(); break; - case kDown|k_Repeat: - case kDown: CursorDown(); break; - case kLeft|k_Repeat: - case kLeft: PageUp(); break; - case kRight|k_Repeat: - case kRight: PageDown(); break; - case kBack: return osBack; - default: if (m_marked < 0) - return osUnknown; - } - return osContinue; -} - -void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) -{ - helpRed = Red; - helpGreen = Green; - helpYellow = Yellow; - helpBlue = Blue; -} diff --git a/sh_console_osd.h b/sh_console_osd.h deleted file mode 100644 index 4a4e413..0000000 --- a/sh_console_osd.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * osd.h: Abstract On Screen Display layer - * - * See the main source file 'vdr.c' for copyright information and - * how to reach the author. - * - * $Id: sh_console_osd.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ - */ -#ifndef __OSD_H -#define __OSD_H -#define DEBUG_OSD -#if defined(DEBUG_OSD) -#include -#include -#include -#endif -//#include "config.h" -// #include "interface.h" -//#include "osdbase.h" -//#include "tools.h" -#define MaxCols 5 -#define Setup_OSDheight 12 -#define Setup_OSDwidth 40 -#define Setup_Symbol false -#define Setup_MenuScrollPage true -#define MAXOSDITEMS (Setup.OSDheight - 4) -enum eDvbColor { - clrBackground, - clrTransparent = clrBackground, - clrBlack = clrBackground, - clrRed, - clrGreen, - clrYellow, - clrBlue, - clrMagenta, - clrCyan, - clrWhite, - }; - -extern int bgbackground, bgchannelname, bgepgtime, bgtitleline, bgscrolline, - bghelpred, bghelpgreen, bghelpyellow, bghelpblue; - -#define clrBackground ((eDvbColor)bgbackground) -#define clrChannelName ((eDvbColor)bgchannelname) -#define clrEpgTime ((eDvbColor)bgepgtime) -#define clrTitleLine ((eDvbColor)bgtitleline) -#define clrScrolLine ((eDvbColor)bgscrolline) -#define clrHelpRed ((eDvbColor)bghelpred) -#define clrHelpGreen ((eDvbColor)bghelpgreen) -#define clrHelpYellow ((eDvbColor)bghelpyellow) -#define clrHelpBlue ((eDvbColor)bghelpblue) - -#define DISPLAY_SIZE 12 -#define PAGE_JUMP 11 - -extern int fgchannelname, fgdatetime, fgepgtime, fgtitle, fgsubtitle, fgtitleline, fgscrolline, - fgmenufont, volumebar, timebar1, timebar2, fgsymbolon, fgsymboloff; - -enum eOSState { osUnknown, - osContinue, - osSchedule, - osChannels, - osDirector, - osTimers, - osRecordings, - osPlugin, - osSetup, - osCommands, - osPause, - osRecord, - osReplay, - osStopRecord, - osStopReplay, - osCancelEdit, - osSwitchDvb, - osBack, - osEnd, - os_User, // the following values can be used locally - osUser1, - osUser2, - osUser3, - osUser4, - osUser5, - osUser6, - osUser7, - osUser8, - osUser9, - osUser10, - osUser11, - }; -enum eDvbFont { - fontOsd, - fontOsd2, - fontFix, - fontSym, -/* TODO as soon as we have the font files... - fontTtxSmall, - fontTtxLarge, -*/ - }; -enum eKeys { // "Up" and "Down" must be the first two keys! - kUp, - kDown, - kMenu, - kOk, - kBack, - kLeft, - kRight, - kRed, - kGreen, - kYellow, - kBlue, - k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, - kPlay, - kPause, - kStop, - kRecord, - kFastFwd, - kFastRew, - kPower, - kChanUp, - kChanDn, - kVolUp, - kVolDn, - kMute, - kSchedule, - kChannels, - kTimers, - kRecordings, - kDirector, - kSetup, - kCommands, - kUser1, kUser2, kUser3, kUser4, kUser5, kUser6, kUser7, kUser8, kUser9, - kNone, - kKbd, - // The following codes are used internally: - k_Plugin, - k_Setup, - // The following flags are OR'd with the above codes: - k_Repeat = 0x8000, - k_Release = 0x4000, - k_Flags = k_Repeat | k_Release, - }; - -class cOsdItem { -private: - char *text; - public: - cOsdItem(eOSState State = osUnknown); - cOsdItem(const char *Text, eOSState State = osUnknown); - void SetText(const char *Text, bool Copy); - virtual ~cOsdItem(); - int Index(); - const char* Get(); - void Display(); -}; - -class cOsdMenu { -private: - char *title; - int cols[MaxCols]; - int m_first, m_current, m_marked; - cOsdMenu *subMenu; - const char *helpRed, *helpGreen, *helpYellow, *helpBlue; - char *status; - int digit; - bool hasHotkeys; - vector m_display; -protected: - bool visible; - void CursorUp(void); - void CursorDown(void); - void PageUp(void); - void PageDown(void); - int Index(cOsdItem *Item); -public: - cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); - virtual ~cOsdMenu(); - void SetTitle(const char *Title, bool ShowDate=false); - int Current(void) { return m_current; } - void Add(cOsdItem *Item); -// void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL); - virtual void Display(void); - virtual void SetCurrent(cOsdItem *Item); - virtual eOSState ProcessKey(eKeys Key); - virtual void Clear(void); - virtual void SetHasHotkeys(){ hasHotkeys = true; } - virtual void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); -}; - -#endif //__OSD_H diff --git a/sh_console_osd_menuitems.c b/sh_console_osd_menuitems.c deleted file mode 100644 index 48b5d3a..0000000 --- a/sh_console_osd_menuitems.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * menuitems.c: General purpose menu items - * - * See the main source file 'vdr.c' for copyright information and - * how to reach the author. - * - * $Id: sh_console_osd_menuitems.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ - */ - -#include "mymenuitems.h" - -const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~"; - -// --- cMenuEditItem --------------------------------------------------------- - -cMenuEditItem::cMenuEditItem(const char *Name) -{ - name = strdup(Name); - value = NULL; -} - -cMenuEditItem::~cMenuEditItem() -{ - free(name); - free(value); -} - -void cMenuEditItem::SetValue(const char *Value) -{ - free(value); - value = strdup(Value); - char *buffer = NULL; - asprintf(&buffer, "%s:\t%s", name, value); - SetText(buffer, false); - Display(); -} - -// --- cMenuEditIntItem ------------------------------------------------------ - -cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max) -:cMenuEditItem(Name) -{ - value = Value; - min = Min; - max = Max; - Set(); -} - -void cMenuEditIntItem::Set(void) -{ - char buf[16]; - snprintf(buf, sizeof(buf), "%d", *value); - SetValue(buf); -} - -eOSState cMenuEditIntItem::ProcessKey(eKeys Key) -{ - return osContinue; -} - -// --- cMenuEditBoolItem ----------------------------------------------------- - -cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString) -:cMenuEditIntItem(Name, Value, 0, 1) -{ - falseString = FalseString ? FalseString : "no"; - trueString = TrueString ? TrueString : "yes"; - Set(); -} - -void cMenuEditBoolItem::Set(void) -{ - char buf[16]; - snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString); - SetValue(buf); -} - -// --- cMenuEditNumItem ------------------------------------------------------ - -cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind) -:cMenuEditItem(Name) -{ - value = Value; - length = Length; - blind = Blind; - Set(); -} - -void cMenuEditNumItem::Set(void) -{ - if (blind) { - char buf[length + 1]; - int i; - for (i = 0; i < length && value[i]; i++) - buf[i] = '*'; - buf[i] = 0; - SetValue(buf); - } - else - SetValue(value); -} - -eOSState cMenuEditNumItem::ProcessKey(eKeys Key) -{ - return osContinue; -} - -// --- cMenuEditChrItem ------------------------------------------------------ - -cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed) -:cMenuEditItem(Name) -{ - value = Value; - allowed = strdup(Allowed); - current = strchr(allowed, *Value); - if (!current) - current = allowed; - Set(); -} - -cMenuEditChrItem::~cMenuEditChrItem() -{ - free(allowed); -} - -void cMenuEditChrItem::Set(void) -{ - char buf[2]; - snprintf(buf, sizeof(buf), "%c", *value); - SetValue(buf); -} - -eOSState cMenuEditChrItem::ProcessKey(eKeys Key) -{ - return osContinue; -} - -// --- cMenuEditStrItem ------------------------------------------------------ - -cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed) -:cMenuEditItem(Name) -{ - value = Value; - length = Length; - allowed = strdup(Allowed); - pos = -1; - insert = uppercase = false; - newchar = true; - ieCurChr = 0; - lastKey = kNone; - Set(); -} - -cMenuEditStrItem::~cMenuEditStrItem() -{ - free(allowed); -} - -void cMenuEditStrItem::SetHelpKeys(void) -{ -} - -void cMenuEditStrItem::Set(void) -{ -} - -eOSState cMenuEditStrItem::ProcessKey(eKeys Key) -{ -#if 0 - const char c1[] = "-.#~,/_@1"; - const char c2[] = "abcäåá2"; - const char c3[] = "defé3"; - const char c4[] = "ghi4"; - const char c5[] = "jkl5"; - const char c6[] = "mnoöñó6"; - const char c7[] = "pqrs7"; - const char c8[] = "tuvüú8"; - const char c9[] = "wxyz9"; - const char c0[] = " 0"; - - switch (Key) { - case kRed: // Switch between upper- and lowercase characters - if (pos >= 0 && (!insert || !newchar)) { - uppercase = !uppercase; - value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]); - } - break; - case kGreen: // Toggle insert/overwrite modes - if (pos >= 0) { - insert = !insert; - newchar = true; - } - SetHelpKeys(); - lastKey = Key; - break; - case kYellow|k_Repeat: - case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor - if (pos >= 0) { - if (strlen(value) > 1) { - if (!insert || pos < int(strlen(value)) - 1) - memmove(value + pos, value + pos + 1, strlen(value) - pos); - // reduce position, if we removed the last character - if (pos == int(strlen(value))) - pos--; - } - else if (strlen(value) == 1) - value[0] = ' '; // This is the last character in the string, replace it with a blank - if (isalpha(value[pos])) - uppercase = isupper(value[pos]); - newchar = true; - } - lastKey = Key; - break; - case kLeft|k_Repeat: - case kLeft: if (pos > 0) { - if (!insert || newchar) - pos--; - newchar = true; - } - if (!insert && isalpha(value[pos])) - uppercase = isupper(value[pos]); - lastKey = Key; - break; - case kRight|k_Repeat: - case kRight: if (pos < length - 2 && pos < int(strlen(value)) ) { - if (++pos >= int(strlen(value))) { - if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ') - pos--; // allow only two blanks at the end - else { - value[pos] = ' '; - value[pos + 1] = 0; - } - } - } - newchar = true; - if (!insert && isalpha(value[pos])) - uppercase = isupper(value[pos]); - if (pos == 0) - SetHelpKeys(); - lastKey = Key; - break; - case kUp|k_Repeat: - case kUp: - case kDown|k_Repeat: - case kDown: if (pos >= 0) { - if (insert && newchar) { - // create a new character in insert mode - if (int(strlen(value)) < length - 1) { - memmove(value + pos + 1, value + pos, strlen(value) - pos + 1); - value[pos] = ' '; - } - } - if (uppercase) - value[pos] = toupper(Inc(tolower(value[pos]), NORMALKEY(Key) == kUp)); - else - value[pos] = Inc( value[pos], NORMALKEY(Key) == kUp); - newchar = false; - } - else - return cMenuEditItem::ProcessKey(Key); - lastKey = Key; - break; - case k0|k_Repeat ... k9|k_Repeat: - case k0 ... k9: if (Key != lastKey) { - ieCurChr = 0; - if (!newchar) { - // kRight - if (pos < length - 2 && pos < int(strlen(value)) ) { - if (++pos >= int(strlen(value))) { - if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ') - pos--; // allow only two blanks at the end - else { - value[pos] = ' '; - value[pos + 1] = 0; - } - } - } - newchar = true; - if (!insert && isalpha(value[pos])) - uppercase = isupper(value[pos]); - } - } - // kUp/kDown - if (pos >= 0) { - if (insert && newchar) { - // create a new character in insert mode - if (int(strlen(value)) < length - 1) { - memmove(value + pos + 1, value + pos, strlen(value) - pos + 1); - value[pos] = ' '; - } - } - } - else - return cMenuEditItem::ProcessKey(Key); - switch (Key) { - case k1: - if (uppercase) - value[pos] = toupper(c1[ieCurChr]); - else - value[pos] = c1[ieCurChr]; - if (c1[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k2: - if (uppercase) - value[pos] = toupper(c2[ieCurChr]); - else - value[pos] = c2[ieCurChr]; - if (c2[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k3: - if (uppercase) - value[pos] = toupper(c3[ieCurChr]); - else - value[pos] = c3[ieCurChr]; - if (c3[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k4: - if (uppercase) - value[pos] = toupper(c4[ieCurChr]); - else - value[pos] = c4[ieCurChr]; - if (c4[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k5: - if (uppercase) - value[pos] = toupper(c5[ieCurChr]); - else - value[pos] = c5[ieCurChr]; - if (c5[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k6: - if (uppercase) - value[pos] = toupper(c6[ieCurChr]); - else - value[pos] = c6[ieCurChr]; - if (c6[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k7: - if (uppercase) - value[pos] = toupper(c7[ieCurChr]); - else - value[pos] = c7[ieCurChr]; - if (c7[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k8: - if (uppercase) - value[pos] = toupper(c8[ieCurChr]); - else - value[pos] = c8[ieCurChr]; - if (c8[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k9: - if (uppercase) - value[pos] = toupper(c9[ieCurChr]); - else - value[pos] = c9[ieCurChr]; - if (c9[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - case k0: - if (uppercase) - value[pos] = toupper(c0[ieCurChr]); - else - value[pos] = c0[ieCurChr]; - if (c0[ieCurChr+1] == 0) - ieCurChr = 0; - else - ieCurChr++; - break; - default: - break; - } - lastKey = Key; - newchar = false; - break; - case kOk: if (pos >= 0) { - pos = -1; - lastKey = Key; - newchar = true; - stripspace(value); - SetHelpKeys(); - break; - } - // run into default - default: if (pos >= 0 && BASICKEY(Key) == kKbd) { - int c = KEYKBD(Key); - if (c <= 0xFF) { - const char *p = strchr(allowed, tolower(c)); - if (p) { - int l = strlen(value); - if (insert && l < length - 1) - memmove(value + pos + 1, value + pos, l - pos + 1); - value[pos] = c; - if (pos < length - 2) - pos++; - if (pos >= l) { - value[pos] = ' '; - value[pos + 1] = 0; - } - } - else { - switch (c) { - case 0x7F: // backspace - if (pos > 0) { - pos--; - return ProcessKey(kYellow); - } - break; - } - } - } - else { - switch (c) { - case kfHome: pos = 0; break; - case kfEnd: pos = strlen(value) - 1; break; - case kfIns: return ProcessKey(kGreen); - case kfDel: return ProcessKey(kYellow); - } - } - } - else - return cMenuEditItem::ProcessKey(Key); - } - Set(); -#endif - return osContinue; -} - -// --- cMenuEditStraItem ----------------------------------------------------- - -cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings) -:cMenuEditIntItem(Name, Value, 0, NumStrings - 1) -{ - strings = Strings; - Set(); -} - -void cMenuEditStraItem::Set(void) -{ - SetValue(strings[*value]); -} - -// --- cMenuTextItem --------------------------------------------------------- - -cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font) -{ - - text = strdup(Text); -} - -cMenuTextItem::~cMenuTextItem() -{ - free(text); -} - -void cMenuTextItem::Clear(void) -{ -} - -void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor) -{ -} - -void cMenuTextItem::ScrollUp(bool Page) -{ -} - -void cMenuTextItem::ScrollDown(bool Page) -{ -} - -eOSState cMenuTextItem::ProcessKey(eKeys Key) -{ - return osContinue; -} - -// --- cMenuSetupPage -------------------------------------------------------- - -cMenuSetupPage::cMenuSetupPage(void) -:cOsdMenu("", 33) -{ - plugin = NULL; -} - -void cMenuSetupPage::SetSection(const char *Section) -{ -} - -eOSState cMenuSetupPage::ProcessKey(eKeys Key) -{ - return osContinue; -} - -void cMenuSetupPage::SetPlugin(cPlugin *Plugin) -{ -} - -void cMenuSetupPage::SetupStore(const char *Name, const char *Value) -{ -} - -void cMenuSetupPage::SetupStore(const char *Name, int Value) -{ -} diff --git a/sh_console_osd_menuitems.h b/sh_console_osd_menuitems.h deleted file mode 100644 index 8812862..0000000 --- a/sh_console_osd_menuitems.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * menuitems.h: General purpose menu items - * - * See the main source file 'vdr.c' for copyright information and - * how to reach the author. - * - * $Id: sh_console_osd_menuitems.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ - */ - -#ifndef __MENUITEMS_H -#define __MENUITEMS_H - -#include "myosd.h" - -extern const char *FileNameChars; - -class cMenuEditItem : public cOsdItem { -private: - char *name; - char *value; -public: - cMenuEditItem(const char *Name); - ~cMenuEditItem(); - void SetValue(const char *Value); - }; - -class cMenuEditIntItem : public cMenuEditItem { -protected: - int *value; - int min, max; - virtual void Set(void); -public: - cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX); - virtual eOSState ProcessKey(eKeys Key); - }; - -class cMenuEditBoolItem : public cMenuEditIntItem { -protected: - const char *falseString, *trueString; - virtual void Set(void); -public: - cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL); - }; - -class cMenuEditNumItem : public cMenuEditItem { -protected: - char *value; - int length; - bool blind; - virtual void Set(void); -public: - cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind = false); - virtual eOSState ProcessKey(eKeys Key); - }; - -class cMenuEditChrItem : public cMenuEditItem { -private: - char *value; - char *allowed; - const char *current; - virtual void Set(void); -public: - cMenuEditChrItem(const char *Name, char *Value, const char *Allowed); - ~cMenuEditChrItem(); - virtual eOSState ProcessKey(eKeys Key); - }; - -class cMenuEditStrItem : public cMenuEditItem { -private: - char *value; - int length; - char *allowed; - int pos; - bool insert, newchar, uppercase; - void SetHelpKeys(void); - virtual void Set(void); - char Inc(char c, bool Up); - int ieCurChr; - eKeys lastKey; -public: - cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed); - ~cMenuEditStrItem(); - virtual eOSState ProcessKey(eKeys Key); - }; - -class cMenuEditStraItem : public cMenuEditIntItem { -private: - const char * const *strings; -protected: - virtual void Set(void); -public: - cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings); - }; - -class cMenuTextItem : public cOsdItem { -private: - char *text; - int x, y, w, h, lines, offset; - eDvbColor fgColor, bgColor; - eDvbFont font; -public: - cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground, eDvbFont Font = fontOsd); - ~cMenuTextItem(); - int Height(void) { return h; } - void Clear(void); - virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); - bool CanScrollUp(void) { return offset > 0; } - bool CanScrollDown(void) { return h + offset < lines; } - void ScrollUp(bool Page); - void ScrollDown(bool Page); - virtual eOSState ProcessKey(eKeys Key); - }; - -class cPlugin; - -class cMenuSetupPage : public cOsdMenu { -private: - cPlugin *plugin; -protected: - void SetSection(const char *Section); - virtual void Store(void) = 0; - void SetupStore(const char *Name, const char *Value = NULL); - void SetupStore(const char *Name, int Value); -public: - cMenuSetupPage(void); - virtual eOSState ProcessKey(eKeys Key); - void SetPlugin(cPlugin *Plugin); - }; - -#endif //__MENUITEMS_H diff --git a/sh_dummy_content.c b/sh_dummy_content.c deleted file mode 100644 index 1ccfe9e..0000000 --- a/sh_dummy_content.c +++ /dev/null @@ -1,596 +0,0 @@ -/*******************************************************************/ -/*! \file content_interface.cpp - * \brief Data Objects for content (e.g. mp3 files, movies) - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/02 22:48:04 $ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ - * - * DUMMY - * - * - * Dummy artists: Artist_1, Artist_2, Artist_3 - * Dummy albums by ArtistX: Album_X1, .... AlbumX3 - * Dummy Tracks On Album_XY: Track_XY1,... Track_XY5 - * - */ -/*******************************************************************/ -#define DEBUG - -#include "sh_dummy_content.h" -#include "mg_tools.h" - -#include -#include -#define GD_PLAYLIST_TYPE 0 // listtype for giant disc db - -// some dummies to keep the compiler happy -#define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax -#define DUMMY -#define NUM_ARTISTS 5 -#define NUM_ALBUMS_PER_ARTIST 3 -#define NUM_TRACKS_PER_ALBUM 9 - -int DummyInitDatabase(MYSQL *db){return 0;} -vector *DummyGetStoredPlaylists(MYSQL db){ return new vector();} - -/*******************************************************************/ -/* class mgTack */ -/********************************************************************/ -DummyTrack DummyTrack::UNDEFINED = DummyTrack(); - -/*! - ***************************************************************************** - * \brief Constructor: creates a DummyTrack obect - * - * \param sqlIdentifier identifies a unique row in the track database - * \param dbase database which stores the track table - * - * On creation, the object contains only the idea. The actual data fields - * are filled when readData() is called for the first time. - ****************************************************************************/ -DummyTrack::DummyTrack(int sqlIdentifier, MYSQL dbase) -{ - m_uniqID = sqlIdentifier; - char buf[255]; - - mgDebug(9, "Creating dumy track %d", sqlIdentifier); - // create dummy value based on the id - sprintf(buf, "ArtistName_%d", (int) m_uniqID / 100); - m_artist = buf; - sprintf(buf,"Album_%d", (int) m_uniqID / 10); - m_album = buf; - sprintf(buf,"Track_%d",m_uniqID); - m_title = buf; - sprintf(buf,"sourcefile_%d", m_uniqID); - m_mp3file = buf; - m_genre = "unknown_genre"; - m_year = 1970 + (m_uniqID%35); - m_rating = 2; - m_length = 180; -} - -/*! - ***************************************************************************** - * \brief copy constructor - * - ****************************************************************************/ -DummyTrack::DummyTrack(const DummyTrack& org) -{ - m_uniqID = org.m_uniqID; - m_db = org.m_db; - - mgDebug(9, - "Creating a TrackCopy for track '%s' (is this really necessary?", - org.m_title.c_str()); - - m_artist = org.m_artist; - m_title = org.m_title; - m_mp3file = org.m_mp3file; - m_album = org.m_album; - m_genre = org.m_genre; - m_year = org.m_year; - m_rating = org.m_rating; - -} - - -/*! - ***************************************************************************** - * \brief destructor - * - ****************************************************************************/ -DummyTrack::~DummyTrack() -{ - // nothing to be done -} - -/*! - ***************************************************************************** - * \brief returns value for _mp3file - ****************************************************************************/ -string DummyTrack::getSourceFile() -{ - return m_mp3file; -} - -/*! - ***************************************************************************** - * \brief returns value for m_title - ****************************************************************************/ -string DummyTrack::getTitle() -{ - return m_title; -} - -/*! - ***************************************************************************** - * \brief returns value for m_artist - ****************************************************************************/ -string DummyTrack::getArtist() -{ - return m_artist; -} -string DummyTrack::getLabel(int col) -{ - switch(col) - { - case 0: - return m_artist; - break; - case 1: - return m_title; - break; - default: - return ""; - } -} -string DummyTrack::getDescription() -{ - return m_artist + " - " + m_title; -} - -/*! - ***************************************************************************** - * \brief returns value for m_album - ****************************************************************************/ -string DummyTrack::getAlbum() -{ - return m_album; -} - -/*! - ***************************************************************************** - * \brief returns value for m_genre - ****************************************************************************/ -string DummyTrack::getGenre() -{ - return m_genre; -} - -/*! - ***************************************************************************** - * \brief returns value for m_year - ****************************************************************************/ -int DummyTrack::getYear() -{ - return m_year; -} - -/*! - ***************************************************************************** - * \brief returns value for m_rating - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ -int DummyTrack::getRating() -{ - return m_rating; -} - -int DummyTrack::getDuration() -{ - return m_rating; -} - -/*! - ***************************************************************************** - * \brief sets the field for m_title to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ -void DummyTrack::setTitle(string new_title) -{ - m_title = new_title; -} - -/*! - ***************************************************************************** - * \brief sets the field for m_artist to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ -void DummyTrack::setArtist(string new_artist) -{ - m_artist = new_artist; -} - - -/*! - ***************************************************************************** - * \brief sets the field for m_album to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ -void DummyTrack::setAlbum(string new_album) -{ - m_album = new_album; -} - - -/*! - ***************************************************************************** - * \brief sets the field for m_genre to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ -void DummyTrack::setGenre(string new_genre) -{ - m_genre = new_genre; -} - - -/*! - ***************************************************************************** - * \brief sets the field for m_year to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ -void DummyTrack::setYear(int new_year) -{ - m_year = new_year; -} - - -/*! - ***************************************************************************** - * \brief sets the field for m_rating to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ -void DummyTrack::setRating(int new_rating) -{ - m_rating = new_rating; -} - - -/*! - ***************************************************************************** - * \brief stores current values in the sql database - * - * Note: At the moment, only the values stored directly in the 'tracks' - * database are updated - ****************************************************************************/ -bool DummyTrack::writeData() -{ - return true; -} - -DummyTracklist::DummyTracklist(MYSQL db_handle, string restrictions) -{ -} - -/*******************************************************************/ -/* class DummyPlaylist */ -/********************************************************************/ - -/*! - ***************************************************************************** - * \brief Constructor: opens playlist by name - * - * \param listname user-readable identifier of the paylist - * \param db_handl database which stores the playlist - * - * If the playlist does not yet exist, an empty playlist is created - ****************************************************************************/ -DummyPlaylist::DummyPlaylist(string listname, MYSQL db_handle) -{ - m_db = db_handle; - - // - // check, if the database really exists - // - if(listname =="EXISTS") - { - - m_author = "DUMMY_author"; - m_listname = listname; - createDummyPlaylist(1); - } - else - { - // new Playlist - m_author = "VDR"; - m_listname = listname; - - } - -} - -/*! - ***************************************************************************** - * \brief Constructor: construct playlist object from existing sql playlist - * - * \param sql_identifier: sql internal identifier for the playlist - * \param db_handl database which stores the playlist - * - * This constructor is typically used when a playlist is selected from an - * internal list of playlists - ****************************************************************************/ -DummyPlaylist::DummyPlaylist(unsigned int sql_identifier, MYSQL db_handle) -{ - m_db = db_handle; - char buf[256]; - - m_author = "DUMMY_author"; - sprintf(buf, "Dummylist_%d", sql_identifier); - m_listname = buf; - createDummyPlaylist(sql_identifier); -} - -void DummyPlaylist::createDummyPlaylist(int start) -{ - DummyTrack* trackptr; - for(int id=start; id < start+20; id++) - { - trackptr = new DummyTrack(id, m_db); - m_list.push_back(trackptr); - } -} - -/*! - ***************************************************************************** - * \brief empty destructor - * - * Nothing to be done. The actual data is stored in the sql db - ****************************************************************************/ -DummyPlaylist::~DummyPlaylist() -{ - // nothing to be done -} - -bool DummyPlaylist::storePlaylist() -{ - mgDebug(1, "Storing Playlist #%s'", m_listname.c_str()); - return true; -} -/*! - ***************************************************************************** - * \brief returns the total duration of all songs in the list in seconds - * - ****************************************************************************/ -int DummyPlaylist::getPlayTime() -{ - return m_list.size()* 180; -} - -/*! - ***************************************************************************** - * \brief returns the duration of all remaining songs in the list in seconds - * - ****************************************************************************/ -int DummyPlaylist::getPlayTimeRemaining() -{ - return 0; // dummy -} - -/*******************************************************************/ -/* class DummyTreeNode */ -/*******************************************************************/ - -DummyTreeNode::DummyTreeNode(MYSQL db, int view) - : mgSelectionTreeNode(db, view) -{ - // create a root node - // everything is done in the parent class - m_restriction ="1"; -} - -DummyTreeNode::DummyTreeNode(mgSelectionTreeNode* parent, - string id, string label, string restriction) - : mgSelectionTreeNode(parent, id, label) -{ - m_restriction = restriction; - // everything else is done in the parent class -} - - -/*! - ***************************************************************************** - * \brief destructor - * - ****************************************************************************/ -DummyTreeNode::~DummyTreeNode() -{ - m_children.clear(); -} - -/*! - ***************************************************************************** - * \brief checks if this node can be further expandded or not - * \true, if node ia leaf node, false if node can be expanded - * - ****************************************************************************/ -bool DummyTreeNode::isLeafNode() -{ - switch(m_view) - { - case 1: // artist -> album -> title - if( m_level <= 3 ) - { - return true; - } - break; - default: - mgError("View '%d' not yet implemented", m_view); - } - return false; -} - - -/*! - ***************************************************************************** - * \brief compute children on the fly - * - ****************************************************************************/ -bool DummyTreeNode::expand() -{ - char buf[20]; - if (m_expanded) - { - mgWarning("Node already expanded\n"); - return true; - } - m_expanded = true; - - mgDebug(5, "Expanding level %d\n", m_level); - switch(m_view) - { - case 1: // artist -> album -> title - if(m_level == 0) // root, expand artist - { - for(int artnum=1; artnum <= NUM_ARTISTS; artnum++) - { - sprintf(buf, "%d", artnum); - m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf, - "Artist "+ (string)buf, - m_restriction + " AND album.artist='Artist "+(string) buf+"'")); - } - - } - else if(m_level == 1) // artist, expand album - { - // create album names in the form Album_x1, ... Album_x3 - // where x is the artist ID - // for normal usage: _restrictions should now hold: - // album.artist = XYZ - for(int albnum=1; albnum <= NUM_ALBUMS_PER_ARTIST; albnum++) - { - sprintf(buf, "%d", albnum); - - m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf, - "Album_"+ m_id + (string) buf, - m_restriction + " AND track.sourceid='0x00"+ m_id + (string) buf +"'")); - } - break; - } - else if(m_level == 2) // artist -> album, expand title - { - // create track names in the form Track_xy1, ... Track_xy5 - // where x is the artist ID and y is the album id - // for normal usage: _restrictions should now hold: - // album.artist = ... AND track.sourceid='0x00XY - for(int tracknum=1; tracknum <= NUM_TRACKS_PER_ALBUM; tracknum++) - { - sprintf(buf, "%d", tracknum); - m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf, - "Track_"+m_id+ (string) buf, - m_restriction + " AND track.id=tr"+ m_id + (string) buf + "'" )); - } - break; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; - default: - mgError("View '%d' not yet implemented", m_view); - } - mgDebug(5, "%d children expanded\n", m_children.size()); - return true; -} - -/*! - ***************************************************************************** - * \brief go over all children recursively to find the tracks - * - ****************************************************************************/ -vector* DummyTreeNode::getTracks() -{ - vector* dummy; - string sql; - - int artistnum; - int albumnum; - int tracknum; - dummy = new vector(); - - sql = m_restriction; - mgDebug(5, "QUERY:\n%s\n", sql.c_str()); - - artistnum = 0; - do // artist_loop - { - if(m_level >=1) - artistnum = m_id[0]-'0'; // we have a unique artist - else - artistnum++; // we are in a loop - - albumnum = 0; - do // album_loop - { - if(m_level >=2) - albumnum = m_id[1]-'0'; // we have a unique album - else - albumnum++; // we are in a loop - - tracknum =0; - do // track_loop - { - if(m_level ==3) - tracknum = m_id[2]-'0'; // we have a unique track - else - tracknum++; // we are in a loop - dummy->push_back(new DummyTrack(artistnum*100+albumnum*10+tracknum, m_db)); - }while ((m_level < 3) && (tracknum< NUM_TRACKS_PER_ALBUM)); - }while ((m_level < 2) && (albumnum < NUM_ALBUMS_PER_ARTIST)); - }while ((m_level < 1) && (artistnum < NUM_ARTISTS )); - - return dummy; -} - -/*! - ***************************************************************************** - * \brief returns the first track matchin the restrictions of this node - * assuming we are in a leaf node, this returns the track represented by the - * the leaf - ****************************************************************************/ -mgContentItem* DummyTreeNode::getSingleTrack() -{ - // get all tracks satisying the restrictions of this node - mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str()); - - mgContentItem* track = new DummyTrack(atoi(m_id.c_str()), m_db); - - return track; -} - -/* -------------------- begin CVS log --------------------------------- - * $Log: sh_dummy_content.c,v $ - * Revision 1.3 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ diff --git a/sh_dummy_content.h b/sh_dummy_content.h deleted file mode 100644 index 7f79498..0000000 --- a/sh_dummy_content.h +++ /dev/null @@ -1,188 +0,0 @@ -/*******************************************************************/ -/*! \file dummy_content.h - * \brief Dummy Data Objects for testing Muggle - ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/02 22:48:04 $ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ - * - * Declares main classes of for content items and interfaces to SQL databases - * - *******************************************************************/ -/* makes sur we dont use parse the same declarations twice */ -#ifndef _DUMMY_CONTENT_H -#define _DUMMY_CONTENT_H -using namespace std; -#include -#include -#include - -#include "mg_content_interface.h" - -// non-member function -int DummyInitDatabase(MYSQL *db); -vector *DummyGetStoredPlaylists(MYSQL db); -/*! - ******************************************************************* - * \class DummyTrack - * - * \brief represents a a single track - * DUMMY - ********************************************************************/ -class DummyTrack : public mgContentItem -{ - -private: - MYSQL m_db; - - // content fields - string m_artist; - string m_title; - string m_mp3file; - string m_album; - string m_genre; - int m_year; - int m_rating; - int m_length; - - - public: - - /* constructor */ - DummyTrack(){ m_uniqID = -1;} // creates invalid item - DummyTrack(int sqlIdentifier, MYSQL dbase); - - DummyTrack(const DummyTrack&); - - /* destructor */ - ~DummyTrack(); - - virtual mgContentItem::contentType getContentType() - {return mgContentItem::GD_AUDIO;} - virtual mgMediaPlayer getPlayer(){return mgMediaPlayer();} - - /* data acess */ - //virtual functions of the base class - virtual string getSourceFile(); - virtual string getTitle(); - virtual string getLabel(int col); - virtual string getDescription(); - virtual string getGenre(); - virtual int getRating(); - - // additional class-specific functions - string getArtist(); - string getAlbum(); - int getYear(); - int getDuration(); - - /* data manipulation */ - void setTitle(string new_title); - void setArtist(string new_artist); - void setAlbum(string new_album); - void setGenre(string new_genre); - void setYear(int new_rating); - void setRating(int new_rating); - - bool writeData(); - static DummyTrack UNDEFINED; - -}; - -class DummyTracklist : public mgTracklist -{ - public: - DummyTracklist(MYSQL db_handle, string restrictions); -}; - -/*! - ******************************************************************* - * \class GdTracklist - * - * \brief represents a playlist, i.e. an ordered collection of tracks - ********************************************************************/ -class DummyPlaylist : public mgPlaylist -{ - private: - int m_sqlId; /* -1 means: not valid */ - string m_author; - MYSQL m_db; - private: - void createDummyPlaylist(int strt); - public: - - - /*==== constructors ====*/ - DummyPlaylist(string listname, MYSQL db_handle); - /* opens existing or creates empty playlist */ - - DummyPlaylist(unsigned int sql_identifier, MYSQL db_handle); - /* construct from the db by internal id*/ - - /*==== destructor ====*/ - ~DummyPlaylist(); - - int getPlayTime(); - /* returns the total duration of all songs in the list in seconds */ - - int getPlayTimeRemaining(); - /* returns the duration of all remaining songs in the list in seconds */ - - bool storePlaylist(); -}; -/*! - ******************************************************************* - * \class mgSelectionTreeNode - ********************************************************************/ -class DummyTreeNode : public mgSelectionTreeNode { - -private: - MYSQL m_db; // underlying db - -public: - - /*==== constructors ====*/ - DummyTreeNode(MYSQL db, int view); - DummyTreeNode(mgSelectionTreeNode* parent, - string id, string label, string restriction); - - /*==== destructor ====*/ - ~DummyTreeNode(); - - // compute children o^xn the fly - virtual bool isLeafNode(); - virtual bool expand(); - - virtual vector* getTracks(); - virtual mgContentItem* getSingleTrack(); -}; - -/* -------------------- begin CVS log --------------------------------- - * $Log: sh_dummy_content.h,v $ - * Revision 1.3 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ - -#endif /* END _CONTENT_INTERFACE_H */ - - - - - - - - - - - - - - - - - - diff --git a/sh_muggle.c b/sh_muggle.c deleted file mode 100644 index 485905e..0000000 --- a/sh_muggle.c +++ /dev/null @@ -1,366 +0,0 @@ -#include -#include -#include "mysql/mysql.h" - -#include "mg_content_interface.h" -#include "mg_media.h" -#include "mg_tools.h" -#include - -#define DISPLAY_SIZE 20 -#define PAGE_JUMP 18 - - -#define EXIT 0 -#define TREE_VIEW 1 -#define LIST_VIEW 2 -#define PLAYLIST_VIEW 3 -#define FILTER_VIEW 4 - -using namespace std; - -const char playlist_command_str[] = -" s : shuffle\n" -" 8 : up\n" -" 2 : down\n" -" 9 : page-up\n" -" 3 : page-down\n" -" p : go to top\n" -" t : switch to tree view\n" -" q : quit"; - -const char tree_command_str[] = -" 6 : expand\n" -" 4 : collapse\n" -" 8 : up\n" -" 2 : down\n" -" 9 : page-up\n" -" 3 : page-down\n" -" 5 : add selection to playlist\n" -" p : switch to playlist view\n" -" t : go to root node\n" -" q : quit"; - -mgSelectionTreeNode* GV_currentNode; -vector GV_treeDisplay; - -mgPlaylist *GV_currentPlaylist; - -void print_tree(mgSelectionTreeNode* node, string tab) -{ - vector children; - vector::iterator iter; - - tab = tab + " "; - printf("%2d %c %s-- Id: '%s', level %d '%s' \n", - GV_treeDisplay.size(), (node->isExpanded()?'x':'-'), tab.c_str(), - node->getID().c_str(), node->getLevel(), - node->getLabel().c_str()); -// printf(" %sRestrictions: '%s'\n", tab.c_str(), node->getRestrictions().c_str()); - GV_treeDisplay.push_back(node); - if(node->isExpanded()) - { - children = node->getChildren(); - for(iter = children.begin(); iter != children.end(); iter++) - { - print_tree(*iter, tab); - } - } -} -void print_node(mgSelectionTreeNode* node, unsigned int selected, unsigned int start) -{ - vector children; - vector::iterator iter; - mgSelectionTreeNode* child; - unsigned int i; - - GV_treeDisplay.clear(); - printf("--%s---\n", node->getLabel().c_str()); - children = node->getChildren(); - // first, add al nodes to the storage - for(iter = children.begin(); iter != children.end(); iter++) - { - GV_treeDisplay.push_back(*iter); - } - - // now display the ones in question - printf("Displaying %d-%d of %d (%s)\n", start+1, start+DISPLAY_SIZE+1, - GV_treeDisplay.size(), node->getRestrictions().c_str()); - if(start >0) - printf(".......\n"); - for (i = start; i < start+DISPLAY_SIZE && i< GV_treeDisplay.size(); i++) - { - child = GV_treeDisplay[i]; - if(i == selected) - printf ("==>"); - else - printf(" "); - printf ("%s\n",child->getLabel().c_str()); - } - if(i==start+DISPLAY_SIZE) - printf(".......\n"); -} - -void print_tracks(vector* tracks, string restriction) -{ - vector::iterator iter; - - printf("======BEGIN_TRACKS=====(restriction:'%s'======\n", restriction.c_str()); - for(iter = tracks->begin(); iter != tracks->end(); iter++) - { - printf(" Track: (%d): '%s' \n",(*iter)->getId(), (*iter)->getTitle().c_str()); - } - printf("======END_TRACKS===========\n"); - printf("press key t continue\n"); - getchar(); -} - -void PrintPlaylist(mgPlaylist *pl, unsigned int selected, unsigned int start) -{ - vector::iterator iter; - unsigned int i; - - // now display the ones in question - printf("Playlist '%s' (%d entries)\n", pl->getListname().c_str(), - pl->getNumItems()); - if(start >0) - printf(".......\n"); - for (i = start; igetNumItems(); i++) - { - if(i == selected) - printf ("==>"); - else - printf(" "); - printf ("%s\n",pl->getLabel(i, "\t").c_str()); - } - if(i==start+DISPLAY_SIZE) - printf(".......\n"); -} -int PlaylistView() -{ - char cmd; - bool loop = true; - unsigned int selected = 0; - unsigned int start=0; - - while(loop) - { - printf("\n\n\n\n\n\n"); - PrintPlaylist(GV_currentPlaylist, selected, start); - printf("------- enter command ('h' for help) -----------\n"); - cmd = getchar(); - switch (cmd) - { - case 'h': // shuffle - printf("%s\n", playlist_command_str); - break; - case 's': // shuffle - GV_currentPlaylist->shuffle(); - break; - case '8': // up - if(selected >0) selected--; - if(selected < start) start = start - PAGE_JUMP; - break; - case '2': // down - if(selected < (GV_currentPlaylist->getNumItems()-1)) - selected++; - if(selected >= start+DISPLAY_SIZE) start = start + PAGE_JUMP; - break; - case '9': // pgup - if(start >= PAGE_JUMP) - { - start -= PAGE_JUMP; - selected -= PAGE_JUMP; - } - else - mgDebug(6,"Can not go up %d %d", start, selected); - break; - case '3': // pgdown - mgDebug(6,"pgdown %d %d", start, selected); - if(start+ PAGE_JUMP< (GV_currentNode->getChildren()).size()) - { - start += PAGE_JUMP; - selected += PAGE_JUMP; - if(selected >= ((GV_currentNode->getChildren()).size())) - selected = (GV_currentNode->getChildren()).size(); - } - - break; - case 'p': // go to top of playlist view - selected=0; - start=0; - break; - case 't': // tree view (on root node) - cmd = getchar(); - return TREE_VIEW; - break; - case 'q': // tree view (on root node) - cmd = getchar(); - return EXIT; - break; - default: - mgWarning("Invalid Command '%c'",cmd); - - } - cmd = getchar(); - - }// end nodeview loop - return EXIT; -} -// displays one node (and all its children) -int NodeView() -{ - vector* tracks; - char cmd; - bool loop = true; - unsigned int selected = 0; - unsigned int start=0; - while(loop) - { - printf("\n\n\n\n\n\n"); - if(!GV_currentNode->isExpanded()) - { - GV_currentNode->expand(); - } - print_node(GV_currentNode,selected, start); - printf("------- enter command ('h' for help) -----------\n"); - - cmd = getchar(); - switch (cmd) - { - case 'h': // shuffle - printf("%s\n", tree_command_str); - break; - case 'e': - case '6':// expand - GV_currentNode = GV_treeDisplay[selected]; - if(GV_currentNode->expand()) - { - selected = 0; - start=0; - } - else - { - GV_currentNode = GV_currentNode ->getParent(); - } - break; - case 'c' : - case '4': - if(GV_currentNode->getParent() != NULL) - { - GV_currentNode->collapse(); - GV_currentNode = GV_currentNode->getParent(); - selected = 0; - start=0; - } - else - { - mgWarning("Already at top level"); - } - break; - case '8': // up - if(selected >0) selected--; - if(selected < start) start = start - PAGE_JUMP; - break; - case '2': // down - if(selected < ((GV_currentNode->getChildren()).size()-1)) - selected++; - if(selected >= start+DISPLAY_SIZE) start = start + PAGE_JUMP; - break; - case '9': // pgup - if(start >= PAGE_JUMP) - { - start -= PAGE_JUMP; - selected -= PAGE_JUMP; - } - else - mgDebug(6,"Can not go up %d %d", start, selected); - break; - case '3': // pgdown - mgDebug(6,"pgdown %d %d", start, selected); - if(start+ PAGE_JUMP< (GV_currentNode->getChildren()).size()) - { - start += PAGE_JUMP; - selected += PAGE_JUMP; - if(selected >= ((GV_currentNode->getChildren()).size())) - selected = (GV_currentNode->getChildren()).size(); - } - - break; - case '5': // OK ,add to playlist - tracks = GV_treeDisplay[selected]->getTracks(); - GV_currentPlaylist->appendList(tracks); - tracks = NULL; - break; - case 'p': // go to playlist view - cmd = getchar(); - return PLAYLIST_VIEW; - break; - case 't': // tree view (on root node) - while(GV_currentNode->getParent() != NULL) - { - GV_currentNode->collapse(); - GV_currentNode = GV_currentNode->getParent(); - } - selected = 0; - start=0; - break; - case 'q': // go to playlist view - cmd = getchar(); - return EXIT; - break; - default: - mgWarning("Invalid Command '%c'",cmd); - - } - cmd = getchar(); - - }// end nodeview loop - return EXIT; -} -int main (int argc, char **argv) -{ - mgMedia* media; - string tab; - bool loop = true; - /* now to connect to the database */ - mgSelectionTreeNode *root; - int activeScreen; - - vector listcols; - mgSetDebugLevel(8); - media = new mgMedia(mgMedia::GD_MP3); - - mgDebug(3," Staring sh_muggle"); - // create initial tree node in view '1' - activeScreen = TREE_VIEW; - root = media->getSelectionRoot(); - root->expand(); - GV_currentNode = root->getChildren()[0]; - GV_currentPlaylist = media->createTemporaryPlaylist(); - listcols.push_back(1); - listcols.push_back(0); - GV_currentPlaylist->setDisplayColumns(listcols); - mgDebug(3," Entering sh_muggle main loop"); - - // now switch to the initial view; - while(loop) - { - switch(activeScreen) - { - case TREE_VIEW: - activeScreen = NodeView(); - break; - case PLAYLIST_VIEW: - activeScreen = PlaylistView(); - break; - case EXIT: - exit(0); - break; - default: - mgError("Invalid screen %d"); - } - } - mgError("leavingNodeView"); - -} diff --git a/sh_muggle2.c b/sh_muggle2.c deleted file mode 100644 index 4c98da6..0000000 --- a/sh_muggle2.c +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include "mysql/mysql.h" - -#include "myosd.h" -#include "muggle.h" -#include "mg_media.h" -#include "mg_tools.h" -#include - -eKeys waitForKey() -{ - char buf[2]; - read(0, buf, 2); - mgDebug(9, "Key '%c' read\n",buf[0]); - switch(buf[0]) - { - case '8': - return kUp; - case '2': - return kDown; - case '1': - return kMenu; - case '5': - return kOk; - case '3': - return kBack; - case '4': - return kLeft; - case '6': - return kRight; - case 'r': - return kRed; - case 'g': - return kGreen; - case 'y': - return kYellow; - case 'b': - return kBlue; - default: - return kNone; - - } -} -int main (int argc, char **argv) -{ - - cOsdObject *mainMenu; - eKeys key; - eOSState state; - bool loop=true; - mgMuggle muggle; - muggle.Initialize(); - muggle.Start(); - mgSetDebugLevel(8); - - mainMenu = muggle.MainMenuAction(); - - while(loop) - { - key = waitForKey(); - state = mainMenu->ProcessKey(key); - if(state == osEnd) - loop = false; - } -} diff --git a/sh_plugin.c b/sh_plugin.c deleted file mode 100644 index eff5cff..0000000 --- a/sh_plugin.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * plugin.c: The VDR plugin interface - * - * See the main source file 'vdr.c' for copyright information and - * how to reach the author. - * - * $Id: sh_plugin.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ - */ - -#include "shell_plugin.h" - -#define LIBVDR_PREFIX "libvdr-" -#define SO_INDICATOR ".so." - -#define MAXPLUGINARGS 1024 -#define HOUSEKEEPINGDELTA 10 // seconds - -// --- cPlugin --------------------------------------------------------------- - -char *cPlugin::configDirectory = NULL; - -cPlugin::cPlugin(void) -{ - name = NULL; -} - -cPlugin::~cPlugin() -{ - // I18nRegister(NULL, Name()); -} - -void cPlugin::SetName(const char *s) -{ - name = s; -} - -const char *cPlugin::CommandLineHelp(void) -{ - return NULL; -} - -bool cPlugin::ProcessArgs(int argc, char *argv[]) -{ - return true; -} - -bool cPlugin::Initialize(void) -{ - return true; -} - -bool cPlugin::Start(void) -{ - return true; -} - -void cPlugin::Housekeeping(void) -{ -} - -const char *cPlugin::MainMenuEntry(void) -{ - return NULL; -} - -cOsdObject *cPlugin::MainMenuAction(void) -{ - return NULL; -} -bool cPlugin::SetupParse(const char *Name, const char *Value) -{ - return false; -} -cMenuSetupPage *cPlugin::SetupMenu(void) -{ - return NULL; -} diff --git a/sh_plugin.h b/sh_plugin.h deleted file mode 100644 index 2bf7bcb..0000000 --- a/sh_plugin.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * plugin.h: The VDR plugin interface - * - * See the main source file 'vdr.c' for copyright information and - * how to reach the author. - * - * $Id: sh_plugin.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $ - */ - -#ifndef __PLUGIN_H -#define __PLUGIN_H - -#include "myosd.h" -#include "mymenuitems.h" -typedef cOsdMenu cOsdObject; -#define VDRPLUGINCREATOR(PluginClass) extern "C" void *VDRPluginCreator(void) { return new PluginClass; } - -class cPlugin { -private: - static char *configDirectory; - const char *name; - void SetName(const char *s); -public: - cPlugin(void); - virtual ~cPlugin(); - - const char *Name(void) { return name; } - virtual const char *Version(void) = 0; - virtual const char *Description(void) = 0; - virtual const char *CommandLineHelp(void); - - virtual bool ProcessArgs(int argc, char *argv[]); - virtual bool Initialize(void); - virtual bool Start(void); - virtual void Housekeeping(void); - - virtual const char *MainMenuEntry(void); - virtual cOsdObject *MainMenuAction(void); - - virtual cMenuSetupPage *SetupMenu(void); - - virtual bool SetupParse(const char *Name, const char *Value); - - }; - -#endif //__PLUGIN_H -- cgit v1.2.3 From 53fbd7857faa6572e90b7ddaac17034ba7f0c486 Mon Sep 17 00:00:00 2001 From: RaK Date: Sat, 14 Feb 2004 22:02:45 +0000 Subject: - mgFilterChoice Debuged fehlendes m_type = CHOICE in mg_filters.c falscher iterator in vdr_menu.c git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@41 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 238 ++++++++++++++++++++----------------------------- mg_filters.c | 7 +- vdr_menu.c | 17 ++-- 3 files changed, 113 insertions(+), 149 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index bcaf500..7913744 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.15 $ - * \date $Date: 2004/02/12 09:15:07 $ + * \version $Revision: 1.16 $ + * \date $Date: 2004/02/14 22:02:45 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ * * DUMMY * Implements main classes of for content items and interfaces to SQL databases @@ -40,7 +40,7 @@ int GdInitDatabase(MYSQL *db) } if(mysql_real_connect(db,"localhost","root","", - "GiantDisc2",0,NULL,0) == NULL) + "GiantDisc",0,NULL,0) == NULL) { return -2; } @@ -80,23 +80,29 @@ gdFilterSets::gdFilterSets() { mgFilter* filter; vector* set; + vector* rating; m_titles.push_back("Track Search"); // create an initial set of filters with empty values set = new vector(); + rating = new vector(); + rating->push_back("-"); + rating->push_back("O"); + rating->push_back("+"); + rating->push_back("++"); // title filter = new mgFilterString("title", ""); set->push_back(filter); // artist - filter = new mgFilterString("artist", "abba"); set->push_back(filter); + filter = new mgFilterString("artist", ""); set->push_back(filter); // genre filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from filter = new mgFilterInt("year (from)", 1900); set->push_back(filter); // year-to filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); - // rating - filter = new mgFilterInt("rating", 0); set->push_back(filter); + // rating + filter = new mgFilterChoice("rating", 1, rating); set->push_back(filter); m_sets.push_back(set); @@ -106,7 +112,7 @@ gdFilterSets::gdFilterSets() // title filter = new mgFilterString("album title", ""); set->push_back(filter); // artist - filter = new mgFilterString("album artist", "abba"); set->push_back(filter); + filter = new mgFilterString("album artist", ""); set->push_back(filter); // genre filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from @@ -114,7 +120,7 @@ gdFilterSets::gdFilterSets() // year-to filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); // rating - filter = new mgFilterInt("rating", 0); set->push_back(filter); + filter = new mgFilterChoice("rating", 1, rating); set->push_back(filter); m_sets.push_back(set); @@ -128,7 +134,7 @@ gdFilterSets::gdFilterSets() // title filter = new mgFilterString("title", ""); set->push_back(filter); // artist - filter = new mgFilterString("artist", "abba"); set->push_back(filter); + filter = new mgFilterString("artist", ""); set->push_back(filter); // genre filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from @@ -136,7 +142,7 @@ gdFilterSets::gdFilterSets() // year-to filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); // rating - filter = new mgFilterInt("rating", 0); set->push_back(filter); + filter = new mgFilterChoice("rating", 1, rating); set->push_back(filter); m_sets.push_back(set); @@ -162,150 +168,91 @@ gdFilterSets::~gdFilterSets() string gdFilterSets::computeRestriction(int *viewPrt) { string sql_str = "1"; + switch (m_activeSetId) { case 0: - for(vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - if((*iter)->isSet()) - { - if(strcmp((*iter)->getName(), "title") == 0 ) - { - sql_str = sql_str + " AND tracks.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "artist") == 0 ) - { - sql_str = sql_str + " AND tracks.artist like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "genre") == 0 ) - { - sql_str = sql_str + " AND (genre1.genre like '" - + (*iter)->getStrVal() + "'"; - sql_str = sql_str + " OR genre2.genre like '" - + (*iter)->getStrVal() + "')"; - } - else if(strcmp((*iter)->getName(), "year (from)") == 0 ) - { - sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "year (to)") == 0 ) - { - sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "rating") == 0 ) - { - sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); - } - else - { - mgWarning("Ignoring unknown filter %s", (*iter)->getName()); - } - } - } *viewPrt = 100; // tracks (flatlist for mountain man ;-)) break; case 1: - for(vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - if((*iter)->isSet()) - { - if(strcmp((*iter)->getName(), "album title") == 0 ) - { - sql_str = sql_str + " AND album.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "album artist") == 0 ) - { - sql_str = sql_str + " AND album.artist like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "genre") == 0 ) - { - sql_str = sql_str + " AND (genre1.genre like '" - + (*iter)->getStrVal() + "'"; - sql_str = sql_str + " OR genre2.genre like '" - + (*iter)->getStrVal() + "')"; - } - else if(strcmp((*iter)->getName(), "year (from)") == 0 ) - { - sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "year (to)") == 0 ) - { - sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "rating") == 0 ) - { - sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); - } - else - { - mgWarning("Ignoring unknown filter %s", (*iter)->getName()); - } - } - } *viewPrt = 101; // album -> tracks break; case 2: // playlist -> tracks - for(vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - if((*iter)->isSet()) - { - if(strcmp((*iter)->getName(), "playlist title") == 0 ) - { - sql_str = sql_str + " AND playlist.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "playlist author") == 0 ) - { - sql_str = sql_str + " AND playlist.author like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "title") == 0 ) - { - sql_str = sql_str + " AND tracks.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "artist") == 0 ) - { - sql_str = sql_str + " AND tracks.artist like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), "genre") == 0 ) - { - sql_str = sql_str + " AND (genre1.genre like '" - + (*iter)->getStrVal() + "'"; - sql_str = sql_str + " OR genre2.genre like '" - + (*iter)->getStrVal() + "')"; - } - else if(strcmp((*iter)->getName(), "year (from)") == 0 ) - { - sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "year (to)") == 0 ) - { - sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), "rating") == 0 ) - { - sql_str = sql_str + " AND tracks.rating >= " + (*iter)->getStrVal(); - } - else - { - mgWarning("Ignoring unknown filter %s", (*iter)->getName()); - } - } - } *viewPrt = 102; // playlist -> tracks break; default: mgWarning("Ignoring Filter Set %i", m_activeSetId); break; } + + for(vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); iter++) + { + if((*iter)->isSet()) + { + if(strcmp((*iter)->getName(), "playlist title") == 0 ) + { + sql_str = sql_str + " AND playlist.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "playlist author") == 0 ) + { + sql_str = sql_str + " AND playlist.author like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "album title") == 0 ) + { + sql_str = sql_str + " AND album.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "album artist") == 0 ) + { + sql_str = sql_str + " AND album.artist like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "title") == 0 ) + { + sql_str = sql_str + " AND tracks.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "artist") == 0 ) + { + sql_str = sql_str + " AND tracks.artist like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), "genre") == 0 ) + { + sql_str = sql_str + " AND (genre1.genre like '" + + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " OR genre2.genre like '" + + (*iter)->getStrVal() + "')"; + } + else if(strcmp((*iter)->getName(), "year (from)") == 0 ) + { + sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "year (to)") == 0 ) + { + sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), "rating") == 0 ) + { + if ((*iter)->getStrVal() == "-") { + sql_str = sql_str + " AND tracks.rating >= 0 "; + } else if ((*iter)->getStrVal() == "O") { + sql_str = sql_str + " AND tracks.rating >= 1 "; + } else if ((*iter)->getStrVal() == "+") { + sql_str = sql_str + " AND tracks.rating >= 2 "; + } else if ((*iter)->getStrVal() == "++") { + sql_str = sql_str + " AND tracks.rating >= 3 "; + } + } + else + { + mgWarning("Ignoring unknown filter %s", (*iter)->getName()); + } + } + } + mgDebug(1, "Applying sql string %s (view=%d)",sql_str.c_str(), *viewPrt); return sql_str; } @@ -1224,6 +1171,10 @@ bool GdTreeNode::expand() " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" , m_restriction.c_str()); idfield = "tracks.id"; + } else { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; } break; case 101: // Albumsearch result @@ -1438,6 +1389,11 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.16 2004/02/14 22:02:45 RaK + * - mgFilterChoice Debuged + * fehlendes m_type = CHOICE in mg_filters.c + * falscher iterator in vdr_menu.c + * * Revision 1.15 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * diff --git a/mg_filters.c b/mg_filters.c index bd19178..c5859e0 100644 --- a/mg_filters.c +++ b/mg_filters.c @@ -2,10 +2,10 @@ /*! \file mg_filters.c * \brief ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/12 09:15:07 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/02/14 22:02:45 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ */ /*******************************************************************/ @@ -246,6 +246,7 @@ bool mgFilterBool::isSet() mgFilterChoice::mgFilterChoice(const char *name, int value, vector *choices) : mgFilter(name) { + m_type = CHOICE; m_choices = *choices; m_selval = value; m_default_val = value; diff --git a/vdr_menu.c b/vdr_menu.c index 03064e4..4dcf0c7 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.14 $ - * \date $Date: 2004/02/12 09:08:48 $ + * \version $Revision: 1.15 $ + * \date $Date: 2004/02/14 22:02:45 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ * - * $Id: vdr_menu.c,v 1.14 2004/02/12 09:08:48 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.15 2004/02/14 22:02:45 RaK Exp $ */ /*******************************************************************/ @@ -607,7 +607,9 @@ void mgMainMenu::DisplayFilter() iter ++, j ++ ) { // BUG: Is this a big memory leak!? When to delete and who? - choices_str[j] = strndup( choices[i].c_str(), 128 ); + // RaK: zweiter Iterator war "i" richtig: "j" + // choices_str[j] = strndup( choices[i].c_str(), 128 ); + choices_str[j] = strndup( choices[j].c_str(), 128 ); } Add( new cMenuEditStraItem( fc->getName(), &( fc->m_selval ), @@ -636,6 +638,11 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.15 2004/02/14 22:02:45 RaK + * - mgFilterChoice Debuged + * fehlendes m_type = CHOICE in mg_filters.c + * falscher iterator in vdr_menu.c + * * Revision 1.14 2004/02/12 09:08:48 LarsAC * Added handling of filter choices (untested) * -- cgit v1.2.3 From da41cc77e5600aad1f37c8409cb112dbe071de67 Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 15:17:50 +0000 Subject: - first i18n attempt git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@42 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 6 +-- TODO | 10 +++-- gd_content_interface.c | 22 ++++++---- gd_content_interface.h | 11 +++-- i18n.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ i18n.h | 16 ++++++++ mg_content_interface.c | 13 ++++-- muggle.c | 7 ++-- vdr_menu.c | 16 +++++--- vdr_menu.h | 13 ++++-- 10 files changed, 190 insertions(+), 33 deletions(-) create mode 100644 i18n.c create mode 100644 i18n.h diff --git a/Makefile b/Makefile index 68d0ac8..de4a3f2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.7 2004/02/12 09:15:07 LarsAC Exp $ +# $Id: Makefile,v 1.8 2004/02/23 15:41:21 RaK Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -40,13 +40,13 @@ PACKAGE = vdr-$(ARCHIVE) ### Includes and Defines (add further entries here): -INCLUDES += -I$(VDRDIR) -I$(DVBDIR)/include -I/usr/include/mysql/ +INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/mysql/ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o +OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o i18n.o BINOBJS = mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o ### Targets: diff --git a/TODO b/TODO index 885341e..656c0a1 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,7 @@ Code polishing ============== - check for memory leaks - check for (reasonably) consistent usage of char*/string +- mg_database is not used Short term items ================ @@ -14,9 +15,6 @@ OSD - DisplayTrackInfo - DisplayAlbumInfo - Edit playlist (means moving tracks around?) -- handle filters: - - create tracklist from filter - - create tree from filter - i18n Content @@ -41,3 +39,9 @@ Visions - handle films - handle off-line media (CDs, DVDs) - handle EPG + +Already Done +============ +- handle filters: + - create tracklist from filter + - create tree from filter diff --git a/gd_content_interface.c b/gd_content_interface.c index 7913744..0f741c0 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.16 $ - * \date $Date: 2004/02/14 22:02:45 $ + * \version $Revision: 1.17 $ + * \date $Date: 2004/02/23 15:41:21 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -22,6 +22,7 @@ #include "gd_content_interface.h" #include "mg_tools.h" +#include "i18n.h" using namespace std; @@ -92,9 +93,9 @@ gdFilterSets::gdFilterSets() rating->push_back("++"); // title - filter = new mgFilterString("title", ""); set->push_back(filter); + filter = new mgFilterString(tr("title"), ""); set->push_back(filter); // artist - filter = new mgFilterString("artist", ""); set->push_back(filter); + filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); // genre filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from @@ -132,9 +133,9 @@ gdFilterSets::gdFilterSets() // artist filter = new mgFilterString("playlist author", ""); set->push_back(filter); // title - filter = new mgFilterString("title", ""); set->push_back(filter); + filter = new mgFilterString(tr("title"), ""); set->push_back(filter); // artist - filter = new mgFilterString("artist", ""); set->push_back(filter); + filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); // genre filter = new mgFilterString("genre", ""); set->push_back(filter); // year-from @@ -209,12 +210,12 @@ string gdFilterSets::computeRestriction(int *viewPrt) sql_str = sql_str + " AND album.artist like '%%" + (*iter)->getStrVal() + "%%'"; } - else if(strcmp((*iter)->getName(), "title") == 0 ) + else if(strcmp((*iter)->getName(), tr("title")) == 0 ) { sql_str = sql_str + " AND tracks.title like '%%" + (*iter)->getStrVal() + "%%'"; } - else if(strcmp((*iter)->getName(), "artist") == 0 ) + else if(strcmp((*iter)->getName(), tr("artist")) == 0 ) { sql_str = sql_str + " AND tracks.artist like '%%" + (*iter)->getStrVal() + "%%'"; @@ -1290,7 +1291,7 @@ bool GdTreeNode::expand() "Album -> Track" , // label, m_restriction); m_children.push_back(new_child); - } else { + } else { new_child = new GdTreeNode(this, // parent "" , // id "Search Result", // label, @@ -1389,6 +1390,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.17 2004/02/23 15:41:21 RaK + * - first i18n attempt + * * Revision 1.16 2004/02/14 22:02:45 RaK * - mgFilterChoice Debuged * fehlendes m_type = CHOICE in mg_filters.c diff --git a/gd_content_interface.h b/gd_content_interface.h index ce86163..c572c75 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -3,10 +3,10 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.5 $ - * \date $Date: 2004/02/12 09:15:07 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/02/23 15:41:21 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ * * Declares main classes of for content items and interfaces to SQL databases * @@ -31,6 +31,8 @@ #include "mg_media.h" #include "mg_filters.h" +#include "i18n.h" + // non-member function int GdInitDatabase(MYSQL *db); std::vector *GdGetStoredPlaylists(MYSQL db); @@ -219,6 +221,9 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.6 2004/02/23 15:41:21 RaK + * - first i18n attempt + * * Revision 1.5 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * diff --git a/i18n.c b/i18n.c new file mode 100644 index 0000000..4782002 --- /dev/null +++ b/i18n.c @@ -0,0 +1,109 @@ +/* + * i18n.c: Internationalization + * + * See the README file for copyright information and how to reach the author. + * + * $Id: i18n.c,v 1.1 2004/02/23 15:41:21 RaK Exp $ + */ + +#include "i18n.h" + +const tI18nPhrase Phrases[] = { + { "artist", + "Interpret", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "title", + "Titel", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Add", + "Hinzufügen", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Cycle tree", + "Ansicht wechseln", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Playlist", + "Playlist", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Submenu", + "Untermenü", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { NULL } + }; diff --git a/i18n.h b/i18n.h new file mode 100644 index 0000000..e4e6993 --- /dev/null +++ b/i18n.h @@ -0,0 +1,16 @@ +/* + * i18n.h: Internationalization + * + * See the README file for copyright information and how to reach the author. + * + * $Id: i18n.h,v 1.1 2004/02/23 15:41:21 RaK Exp $ + */ + +#ifndef _I18N__H +#define _I18N__H + +#include + +extern const tI18nPhrase Phrases[]; + +#endif //_I18N__H diff --git a/mg_content_interface.c b/mg_content_interface.c index c6c2e63..5d60358 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/10 23:47:23 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/23 15:41:21 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -320,7 +320,7 @@ mgContentItem* mgPlaylist::sneakNext() mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view) { m_db = db; - m_parent=NULL; + m_parent = NULL; m_level = 0; m_view = view; m_id = ""; @@ -347,7 +347,11 @@ mgSelectionTreeNode::~mgSelectionTreeNode() mgSelectionTreeNode* mgSelectionTreeNode::getParent() { + if (m_view < 100 || m_level > 1) { return m_parent; + } else { + return NULL; + } } void mgSelectionTreeNode::collapse() // removes all children (recursively) @@ -398,6 +402,9 @@ string mgSelectionTreeNode::getRestrictions() /* -------------------- begin CVS log --------------------------------- * $Log: mg_content_interface.c,v $ + * Revision 1.4 2004/02/23 15:41:21 RaK + * - first i18n attempt + * * Revision 1.3 2004/02/10 23:47:23 RaK * - views konsitent gemacht. siehe FROMJOIN * - isLeafNode angepasst fuer neue views 4,5,100,101 diff --git a/muggle.c b/muggle.c index e03e56d..06817fc 100644 --- a/muggle.c +++ b/muggle.c @@ -2,10 +2,10 @@ /*! \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/09 19:27:52 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/02/23 15:41:21 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: RaK $ */ /*******************************************************************/ @@ -21,6 +21,7 @@ static const char *MAINMENUENTRY = "Muggle"; #include "mg_content_interface.h" #include "mg_media.h" +#include "i18n.h" const char* mgMuggle::Version(void) { diff --git a/vdr_menu.c b/vdr_menu.c index 4dcf0c7..a5fbbde 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.15 $ - * \date $Date: 2004/02/14 22:02:45 $ + * \version $Revision: 1.16 $ + * \date $Date: 2004/02/23 15:41:21 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * - * $Id: vdr_menu.c,v 1.15 2004/02/14 22:02:45 RaK Exp $ + * $Id: vdr_menu.c,v 1.16 2004/02/23 15:41:21 RaK Exp $ */ /*******************************************************************/ @@ -24,6 +24,8 @@ #include "gd_content_interface.h" +#include "i18n.h" + #include #include @@ -106,7 +108,7 @@ void mgMainMenu::SetButtons( ) if( m_state == TREE ) { - SetHelp( "Add", "Cycle tree", "Playlist", "Submenu" ); + SetHelp( tr("Add"), tr("Cycle tree"), tr("Playlist"), tr("Submenu") ); } else if( m_state == PLAYLIST ) { @@ -233,7 +235,8 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { // Back pressed on root level... Go back to Main VDR menu - state = osBack; + // state = osBack; + state = osContinue; } } } @@ -638,6 +641,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.16 2004/02/23 15:41:21 RaK + * - first i18n attempt + * * Revision 1.15 2004/02/14 22:02:45 RaK * - mgFilterChoice Debuged * fehlendes m_type = CHOICE in mg_filters.c diff --git a/vdr_menu.h b/vdr_menu.h index 76317bc..e0ef30b 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,12 +2,12 @@ /*! \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR ******************************************************************** - * \version $Revision: 1.8 $ - * \date $Date: 2004/02/08 10:48:44 $ + * \version $Revision: 1.9 $ + * \date $Date: 2004/02/23 15:41:21 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ * - * $Id: vdr_menu.h,v 1.8 2004/02/08 10:48:44 LarsAC Exp $ + * $Id: vdr_menu.h,v 1.9 2004/02/23 15:41:21 RaK Exp $ * */ /*******************************************************************/ @@ -26,6 +26,8 @@ #include +#include "i18n.h" + class mgMedia; class mgSelectionTreeNode; class mgPlaylist; @@ -108,6 +110,9 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.9 2004/02/23 15:41:21 RaK + * - first i18n attempt + * * Revision 1.8 2004/02/08 10:48:44 LarsAC * Made major revisions in OSD behavior * -- cgit v1.2.3 From ebcdb191a5db63acd338047b4bbdf201232f2188 Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 15:17:51 +0000 Subject: - i18n git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@43 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 45 ++++++------ i18n.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++- muggle.c | 5 +- 3 files changed, 206 insertions(+), 26 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 0f741c0..7406320 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.17 $ - * \date $Date: 2004/02/23 15:41:21 $ + * \version $Revision: 1.18 $ + * \date $Date: 2004/02/23 15:17:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -82,7 +82,7 @@ gdFilterSets::gdFilterSets() mgFilter* filter; vector* set; vector* rating; - m_titles.push_back("Track Search"); + m_titles.push_back(tr("Track Search")); // create an initial set of filters with empty values set = new vector(); @@ -97,53 +97,53 @@ gdFilterSets::gdFilterSets() // artist filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); // genre - filter = new mgFilterString("genre", ""); set->push_back(filter); + filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); // year-from - filter = new mgFilterInt("year (from)", 1900); set->push_back(filter); + filter = new mgFilterInt(tr("year (from)"), 1900); set->push_back(filter); // year-to - filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); + filter = new mgFilterInt(tr("year (to)"), 2100); set->push_back(filter); // rating - filter = new mgFilterChoice("rating", 1, rating); set->push_back(filter); + filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); m_sets.push_back(set); - m_titles.push_back("Album Search"); + m_titles.push_back(tr("Album Search")); set = new vector(); // title - filter = new mgFilterString("album title", ""); set->push_back(filter); + filter = new mgFilterString(tr("album title"), ""); set->push_back(filter); // artist - filter = new mgFilterString("album artist", ""); set->push_back(filter); + filter = new mgFilterString(tr("album artist"), ""); set->push_back(filter); // genre - filter = new mgFilterString("genre", ""); set->push_back(filter); + filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); // year-from - filter = new mgFilterInt("year (from)", 1900); set->push_back(filter); + filter = new mgFilterInt(tr("year (from)"), 1900); set->push_back(filter); // year-to - filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); + filter = new mgFilterInt(tr("year (to)"), 2100); set->push_back(filter); // rating - filter = new mgFilterChoice("rating", 1, rating); set->push_back(filter); + filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); m_sets.push_back(set); - m_titles.push_back("Playlist Search"); + m_titles.push_back(tr("Playlist Search")); set = new vector(); // title - filter = new mgFilterString("playlist title", ""); set->push_back(filter); + filter = new mgFilterString(tr("playlist title"), ""); set->push_back(filter); // artist - filter = new mgFilterString("playlist author", ""); set->push_back(filter); + filter = new mgFilterString(tr("playlist author"), ""); set->push_back(filter); // title filter = new mgFilterString(tr("title"), ""); set->push_back(filter); // artist filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); // genre - filter = new mgFilterString("genre", ""); set->push_back(filter); + filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); // year-from - filter = new mgFilterInt("year (from)", 1900); set->push_back(filter); + filter = new mgFilterInt(tr("year (from)"), 1900); set->push_back(filter); // year-to - filter = new mgFilterInt("year (to)", 2100); set->push_back(filter); + filter = new mgFilterInt(tr("year (to)"), 2100); set->push_back(filter); // rating - filter = new mgFilterChoice("rating", 1, rating); set->push_back(filter); + filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); m_sets.push_back(set); @@ -1390,6 +1390,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.18 2004/02/23 15:17:51 RaK + * - i18n + * * Revision 1.17 2004/02/23 15:41:21 RaK * - first i18n attempt * diff --git a/i18n.c b/i18n.c index 4782002..e298121 100644 --- a/i18n.c +++ b/i18n.c @@ -3,12 +3,188 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.c,v 1.1 2004/02/23 15:41:21 RaK Exp $ + * $Id: i18n.c,v 1.2 2004/02/23 15:17:51 RaK Exp $ */ #include "i18n.h" const tI18nPhrase Phrases[] = { + { "Track Search", + "Titel Suche", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Album Search", + "Album Suche", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Playlist Search", + "Playlist Suche", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "playlist title", + "Playlist Titel", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "playlist author", + "Playlist Author", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "album artist", + "Albuminterpret", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "album title", + "Albumtitel", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "rating", + "Bewertung", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "year (to)", + "Jahr (bis)", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "year (from)", + "Jahr (von)", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "genre", + "Genre", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { "artist", "Interpret", "",// TODO @@ -42,7 +218,7 @@ const tI18nPhrase Phrases[] = { "",// TODO }, { "Add", - "Hinzufügen", + "Hinzu", "",// TODO "",// TODO "",// TODO @@ -74,7 +250,7 @@ const tI18nPhrase Phrases[] = { "",// TODO }, { "Playlist", - "Playlist", + "Playliste", "",// TODO "",// TODO "",// TODO diff --git a/muggle.c b/muggle.c index 06817fc..d03fc1b 100644 --- a/muggle.c +++ b/muggle.c @@ -2,8 +2,8 @@ /*! \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/23 15:41:21 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/02/23 15:17:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ */ @@ -77,6 +77,7 @@ bool mgMuggle::Start(void) m_root = m_media->getSelectionRoot(); m_playlist = m_media->createTemporaryPlaylist(); m_media->initFilterSet(); + RegisterI18n(Phrases); return true; } -- cgit v1.2.3 From 7325afceedb2ae852e4dfc47020024426939226c Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 15:56:19 +0000 Subject: - i18n git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@44 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 20 +++-- i18n.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++- vdr_menu.c | 17 ++-- 3 files changed, 232 insertions(+), 16 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 7406320..d01e97d 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.18 $ - * \date $Date: 2004/02/23 15:17:51 $ + * \version $Revision: 1.19 $ + * \date $Date: 2004/02/23 15:56:19 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -891,6 +891,7 @@ GdTreeNode::GdTreeNode(MYSQL db, int view, string filters) // everything is done in the parent class m_restriction = filters; m_view = view; + m_label = tr("Browser"); } GdTreeNode::GdTreeNode(mgSelectionTreeNode* parent, string id, string label, string restriction) @@ -1268,33 +1269,33 @@ bool GdTreeNode::expand() } else if (m_view <100) { new_child = new GdTreeNode(this, // parent "1" , // id - "Artist -> Album -> Title", // label, + tr("Artist -> Album -> Track"), // label, m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "2" , // id - "Genre -> Artist -> Album -> Track" , // label, + tr("Genre -> Artist -> Album -> Track") , // label, m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "3" , // id - "Artist -> Track" , // label, + tr("Artist -> Track") , // label, m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "4" , // id - "Genre -> Year -> Track" , // label, + tr("Genre -> Year -> Track") , // label, m_restriction); m_children.push_back(new_child); new_child = new GdTreeNode(this, // parent "5" , // id - "Album -> Track" , // label, + tr("Album -> Track") , // label, m_restriction); m_children.push_back(new_child); } else { new_child = new GdTreeNode(this, // parent "" , // id - "Search Result", // label, + tr("Search Result"), // label, m_restriction); m_children.push_back(new_child); } @@ -1390,6 +1391,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.19 2004/02/23 15:56:19 RaK + * - i18n + * * Revision 1.18 2004/02/23 15:17:51 RaK * - i18n * diff --git a/i18n.c b/i18n.c index e298121..f37c53a 100644 --- a/i18n.c +++ b/i18n.c @@ -3,12 +3,221 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.c,v 1.2 2004/02/23 15:17:51 RaK Exp $ + * $Id: i18n.c,v 1.3 2004/02/23 15:56:19 RaK Exp $ */ #include "i18n.h" const tI18nPhrase Phrases[] = { + + { "Album -> Track", + "Titel nach Album", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Genre -> Year -> Track", + "Titel nach Genre und Jahr", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Artist -> Track", + "Titel nach Interpret", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Genre -> Artist -> Album -> Track", + "Album nach Genre und Interpret", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Artist -> Album -> Track", + "Album nach Interpret", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Browser", + "Browser", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Load", + "Laden", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Query", + "Suche", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Album info", + "Album Details", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Edit PL?", + "Edit PL?", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Filter", + "Filter", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Track info", + "Titel Details", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Edit PL", + "Edit PL", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { "Track Search", "Titel Suche", "",// TODO diff --git a/vdr_menu.c b/vdr_menu.c index a5fbbde..5e5d3fa 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.16 $ - * \date $Date: 2004/02/23 15:41:21 $ + * \version $Revision: 1.17 $ + * \date $Date: 2004/02/23 15:56:19 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * - * $Id: vdr_menu.c,v 1.16 2004/02/23 15:41:21 RaK Exp $ + * $Id: vdr_menu.c,v 1.17 2004/02/23 15:56:19 RaK Exp $ */ /*******************************************************************/ @@ -112,19 +112,19 @@ void mgMainMenu::SetButtons( ) } else if( m_state == PLAYLIST ) { - SetHelp( "Edit PL", "Track info", "Filter", "Submenu" ); + SetHelp( tr("Edit PL"), tr("Track info"), tr("Filter"), tr("Submenu") ); } else if( m_state == PLAYLIST_TRACKINFO ) { - SetHelp( "Edit PL?", "Album info", "Filter", "Submenu" ); + SetHelp( tr("Edit PL?"), tr("Album info"), tr("Filter"), tr("Submenu") ); } else if( m_state == PLAYLIST_ALBUMINFO ) { - SetHelp( "Edit PL?", "Playlist", "Filter", "Submenu" ); + SetHelp( tr("Edit PL?"), tr("Playlist"), tr("Filter"), tr("Submenu") ); } else if( m_state == FILTER ) { - SetHelp( "Query", "Load", "Tree", "Submenu" ); + SetHelp( tr("Query"), tr("Load"), tr("Browser"), tr("Submenu") ); } else { @@ -641,6 +641,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.17 2004/02/23 15:56:19 RaK + * - i18n + * * Revision 1.16 2004/02/23 15:41:21 RaK * - first i18n attempt * -- cgit v1.2.3 From 4963f70aca32729f576035686f16415434118d62 Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 16:18:15 +0000 Subject: - i18n git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@45 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 1 + i18n.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- muggle.c | 6 +++--- vdr_menu.c | 19 ++++++++++------- 4 files changed, 83 insertions(+), 13 deletions(-) diff --git a/TODO b/TODO index 656c0a1..0920abb 100644 --- a/TODO +++ b/TODO @@ -6,6 +6,7 @@ Code polishing - check for memory leaks - check for (reasonably) consistent usage of char*/string - mg_database is not used +- album filter works not properly Short term items ================ diff --git a/i18n.c b/i18n.c index f37c53a..8024731 100644 --- a/i18n.c +++ b/i18n.c @@ -3,13 +3,77 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.c,v 1.3 2004/02/23 15:56:19 RaK Exp $ + * $Id: i18n.c,v 1.4 2004/02/23 16:18:15 RaK Exp $ */ #include "i18n.h" const tI18nPhrase Phrases[] = { + { "items", + "Einträge", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Muggle Media Database", + "Muggle Media Database", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Tree View Commands", + "Browser Befehle", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Search Result", + "Suchergebnis", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { "Album -> Track", "Titel nach Album", "",// TODO @@ -106,8 +170,8 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO }, - { "Load", - "Laden", + { "Other Search", + "Suchmodus", "",// TODO "",// TODO "",// TODO diff --git a/muggle.c b/muggle.c index d03fc1b..c8d1c76 100644 --- a/muggle.c +++ b/muggle.c @@ -2,8 +2,8 @@ /*! \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.5 $ - * \date $Date: 2004/02/23 15:17:51 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/02/23 16:18:15 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ */ @@ -73,11 +73,11 @@ bool mgMuggle::Start(void) // Start any background activities the plugin shall perform. mgSetDebugLevel( 99 ); + RegisterI18n(Phrases); m_media = new mgMedia( mgMedia::GD_MP3 ); m_root = m_media->getSelectionRoot(); m_playlist = m_media->createTemporaryPlaylist(); m_media->initFilterSet(); - RegisterI18n(Phrases); return true; } diff --git a/vdr_menu.c b/vdr_menu.c index 5e5d3fa..815e533 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.17 $ - * \date $Date: 2004/02/23 15:56:19 $ + * \version $Revision: 1.18 $ + * \date $Date: 2004/02/23 16:18:15 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * - * $Id: vdr_menu.c,v 1.17 2004/02/23 15:56:19 RaK Exp $ + * $Id: vdr_menu.c,v 1.18 2004/02/23 16:18:15 RaK Exp $ */ /*******************************************************************/ @@ -61,7 +61,7 @@ mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *pl { mgDebug( 1, "Creating Muggle Main Menu" ); - SetTitle( "Muggle Media Database" ); + SetTitle( tr("Muggle Media Database") ); SetButtons(); m_filtername = new char[32]; @@ -124,7 +124,7 @@ void mgMainMenu::SetButtons( ) } else if( m_state == FILTER ) { - SetHelp( tr("Query"), tr("Load"), tr("Browser"), tr("Submenu") ); + SetHelp( tr("Query"), tr("Other Search"), tr("Browser"), tr("Submenu") ); } else { @@ -461,7 +461,7 @@ void mgMainMenu::DisplayTreeSubmenu() mgDebug( 1, "Creating Muggle tree view submenu" ); SetButtons(); - SetTitle( "Muggle Tree View Commands" ); + SetTitle( strcat("Muggle - ",tr("Tree View Commands") ) ); // Add items Add( new cOsdItem( "1 - Test1" ) ); @@ -504,7 +504,9 @@ void mgMainMenu::DisplayPlaylist() vector* list = m_current_playlist-> getAll(); static char titlestr[80]; - sprintf( titlestr, "Muggle Playlist (%d items)", list->size() ); + sprintf( titlestr, "Muggle - %s (%d %s)",tr("Playlist"), + list->size() , + tr("items") ); SetTitle( titlestr ); mgDebug( 1, "mgBrowseMenu::DisplayPlaylist: %d elements received", @@ -641,6 +643,9 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.18 2004/02/23 16:18:15 RaK + * - i18n + * * Revision 1.17 2004/02/23 15:56:19 RaK * - i18n * -- cgit v1.2.3 From 4d31599a91400694e0661cc8b5ee60ca13c880e3 Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 16:19:48 +0000 Subject: *** empty log message *** git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@46 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 0920abb..7ae39f5 100644 --- a/TODO +++ b/TODO @@ -46,3 +46,4 @@ Already Done - handle filters: - create tracklist from filter - create tree from filter +- i18n (english and german) -- cgit v1.2.3 From 8f1fe24d9415d7377c0fd6a4256b16b2bede15f4 Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 16:30:58 +0000 Subject: - album search error because of i18n corrected git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@47 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 2 -- gd_content_interface.c | 23 +++++++++++++---------- mg_media.c | 15 +++++++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/TODO b/TODO index 7ae39f5..2ce638a 100644 --- a/TODO +++ b/TODO @@ -6,7 +6,6 @@ Code polishing - check for memory leaks - check for (reasonably) consistent usage of char*/string - mg_database is not used -- album filter works not properly Short term items ================ @@ -16,7 +15,6 @@ OSD - DisplayTrackInfo - DisplayAlbumInfo - Edit playlist (means moving tracks around?) -- i18n Content ------- diff --git a/gd_content_interface.c b/gd_content_interface.c index d01e97d..2ceedb5 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.19 $ - * \date $Date: 2004/02/23 15:56:19 $ + * \version $Revision: 1.20 $ + * \date $Date: 2004/02/23 16:30:58 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -190,22 +190,22 @@ string gdFilterSets::computeRestriction(int *viewPrt) { if((*iter)->isSet()) { - if(strcmp((*iter)->getName(), "playlist title") == 0 ) + if(strcmp((*iter)->getName(), tr("playlist title")) == 0 ) { sql_str = sql_str + " AND playlist.title like '%%" + (*iter)->getStrVal() + "%%'"; } - else if(strcmp((*iter)->getName(), "playlist author") == 0 ) + else if(strcmp((*iter)->getName(), tr("playlist author")) == 0 ) { sql_str = sql_str + " AND playlist.author like '%%" + (*iter)->getStrVal() + "%%'"; } - else if(strcmp((*iter)->getName(), "album title") == 0 ) + else if(strcmp((*iter)->getName(), tr("album title")) == 0 ) { sql_str = sql_str + " AND album.title like '%%" + (*iter)->getStrVal() + "%%'"; } - else if(strcmp((*iter)->getName(), "album artist") == 0 ) + else if(strcmp((*iter)->getName(), tr("album artist")) == 0 ) { sql_str = sql_str + " AND album.artist like '%%" + (*iter)->getStrVal() + "%%'"; @@ -220,22 +220,22 @@ string gdFilterSets::computeRestriction(int *viewPrt) sql_str = sql_str + " AND tracks.artist like '%%" + (*iter)->getStrVal() + "%%'"; } - else if(strcmp((*iter)->getName(), "genre") == 0 ) + else if(strcmp((*iter)->getName(), tr("genre")) == 0 ) { sql_str = sql_str + " AND (genre1.genre like '" + (*iter)->getStrVal() + "'"; sql_str = sql_str + " OR genre2.genre like '" + (*iter)->getStrVal() + "')"; } - else if(strcmp((*iter)->getName(), "year (from)") == 0 ) + else if(strcmp((*iter)->getName(), tr("year (from)")) == 0 ) { sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); } - else if(strcmp((*iter)->getName(), "year (to)") == 0 ) + else if(strcmp((*iter)->getName(), tr("year (to)")) == 0 ) { sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); } - else if(strcmp((*iter)->getName(), "rating") == 0 ) + else if(strcmp((*iter)->getName(), tr("rating")) == 0 ) { if ((*iter)->getStrVal() == "-") { sql_str = sql_str + " AND tracks.rating >= 0 "; @@ -1391,6 +1391,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.20 2004/02/23 16:30:58 RaK + * - album search error because of i18n corrected + * * Revision 1.19 2004/02/23 15:56:19 RaK * - i18n * diff --git a/mg_media.c b/mg_media.c index 4a2e83b..bbb682b 100644 --- a/mg_media.c +++ b/mg_media.c @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.11 $ - * \date $Date: 2004/02/12 09:15:07 $ + * \version $Revision: 1.12 $ + * \date $Date: 2004/02/23 16:30:58 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: RaK $ */ /*******************************************************************/ @@ -180,7 +180,7 @@ mgMedia::mgMedia(contentType mediatype) case GD_MP3: { errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully conntected to sql database 'GiantDisc2'"); + mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); } } if(errval < 0) @@ -194,7 +194,7 @@ mgMedia::mgMedia(contentType mediatype) case GD_MP3: { errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully conntected to sql database 'GiantDisc2'"); + mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); } } } @@ -220,7 +220,7 @@ string mgMedia::getMediaTypeName() switch(m_mediatype) { case GD_MP3: - return "GiantDisc2"; + return "GiantDisc"; } mgError("implementation Error"); // we should never get here return ""; @@ -429,6 +429,9 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.c,v $ + * Revision 1.12 2004/02/23 16:30:58 RaK + * - album search error because of i18n corrected + * * Revision 1.11 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * -- cgit v1.2.3 From 6ae8ca945bd2b040337707fd91cc28c01c145753 Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 17:03:24 +0000 Subject: - error in filter view while trying to switch or using the colour keys workaround: first filter criteria is inttype. than it works, dont ask why ;-( git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@48 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 32 ++++++++++++++++++-------------- vdr_menu.c | 23 ++++++++++++----------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 2ceedb5..ba5429a 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -3,8 +3,8 @@ * \brief Data Objects for content (e.g. mp3 files, movies) * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.20 $ - * \date $Date: 2004/02/23 16:30:58 $ + * \version $Revision: 1.21 $ + * \date $Date: 2004/02/23 17:03:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * @@ -92,16 +92,16 @@ gdFilterSets::gdFilterSets() rating->push_back("+"); rating->push_back("++"); + // year-from + filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); + // year-to + filter = new mgFilterInt(tr("year (to)"), 2099, 1900, 2100); set->push_back(filter); // title filter = new mgFilterString(tr("title"), ""); set->push_back(filter); // artist filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); // genre filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); - // year-from - filter = new mgFilterInt(tr("year (from)"), 1900); set->push_back(filter); - // year-to - filter = new mgFilterInt(tr("year (to)"), 2100); set->push_back(filter); // rating filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); @@ -110,16 +110,16 @@ gdFilterSets::gdFilterSets() m_titles.push_back(tr("Album Search")); set = new vector(); + // year-from + filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); + // year-to + filter = new mgFilterInt(tr("year (to)"), 2099, 1900, 2100); set->push_back(filter); // title filter = new mgFilterString(tr("album title"), ""); set->push_back(filter); // artist filter = new mgFilterString(tr("album artist"), ""); set->push_back(filter); // genre filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); - // year-from - filter = new mgFilterInt(tr("year (from)"), 1900); set->push_back(filter); - // year-to - filter = new mgFilterInt(tr("year (to)"), 2100); set->push_back(filter); // rating filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); @@ -128,6 +128,10 @@ gdFilterSets::gdFilterSets() m_titles.push_back(tr("Playlist Search")); set = new vector(); + // year-from + filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); + // year-to + filter = new mgFilterInt(tr("year (to)"), 2099, 1900, 2100); set->push_back(filter); // title filter = new mgFilterString(tr("playlist title"), ""); set->push_back(filter); // artist @@ -138,10 +142,6 @@ gdFilterSets::gdFilterSets() filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); // genre filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); - // year-from - filter = new mgFilterInt(tr("year (from)"), 1900); set->push_back(filter); - // year-to - filter = new mgFilterInt(tr("year (to)"), 2100); set->push_back(filter); // rating filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); @@ -1391,6 +1391,10 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.21 2004/02/23 17:03:24 RaK + * - error in filter view while trying to switch or using the colour keys + * workaround: first filter criteria is inttype. than it works, dont ask why ;-( + * * Revision 1.20 2004/02/23 16:30:58 RaK * - album search error because of i18n corrected * diff --git a/vdr_menu.c b/vdr_menu.c index 815e533..8d4cf06 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ /*! \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR ******************************************************************** - * \version $Revision: 1.18 $ - * \date $Date: 2004/02/23 16:18:15 $ + * \version $Revision: 1.19 $ + * \date $Date: 2004/02/23 17:03:24 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: RaK $ * - * $Id: vdr_menu.c,v 1.18 2004/02/23 16:18:15 RaK Exp $ + * $Id: vdr_menu.c,v 1.19 2004/02/23 17:03:24 RaK Exp $ */ /*******************************************************************/ @@ -343,13 +343,6 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { switch( key ) { - case kOk: - { - // OK: Create filter and selection tree and display - mgDebug( 1, "mgMainMenu: create and apply filter" ); - // m_media->applyFilters(); - state = osContinue; - } break; case kRed: // { mgDebug( 1, "mgMainMenu: query and display results" ); @@ -387,7 +380,8 @@ eOSState mgMainMenu::ProcessKey(eKeys key) case kBlue: { mgDebug( 1, "mgMainMenu: submenu" ); - } + state = osContinue; + } break; default: { state = osContinue; @@ -398,6 +392,9 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { // m_media->resetFilters();? } + // RaK: Verhindert, dass die Help Buttons verschwinden, + // ist aber keine schöne Lösung + //SetHelp( tr("Query"), tr("Other Search"), tr("Browser"), tr("Submenu") ); } else { @@ -643,6 +640,10 @@ void mgMainMenu::DisplayFilterSelector() /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.19 2004/02/23 17:03:24 RaK + * - error in filter view while trying to switch or using the colour keys + * workaround: first filter criteria is inttype. than it works, dont ask why ;-( + * * Revision 1.18 2004/02/23 16:18:15 RaK * - i18n * -- cgit v1.2.3 From 1a95137eb0f4d533bc38377558de8ccc442e5de6 Mon Sep 17 00:00:00 2001 From: RaK Date: Mon, 23 Feb 2004 17:07:08 +0000 Subject: *** empty log message *** git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@49 e10066b5-e1e2-0310-b819-94efdf66514b --- i18n.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n.c b/i18n.c index 8024731..3a0cbd8 100644 --- a/i18n.c +++ b/i18n.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.c,v 1.4 2004/02/23 16:18:15 RaK Exp $ + * $Id: i18n.c,v 1.5 2004/02/23 17:07:08 RaK Exp $ */ #include "i18n.h" @@ -507,7 +507,7 @@ const tI18nPhrase Phrases[] = { "",// TODO }, { "Cycle tree", - "Ansicht wechseln", + "Browser Modus", "",// TODO "",// TODO "",// TODO -- cgit v1.2.3 From 616adfc77dc1d08f3bfcd79991a78c6350e4e2f6 Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 28 May 2004 15:29:19 +0000 Subject: Merged player branch back on HEAD branch. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@98 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 9 +- TODO | 113 ++++- gd_content_interface.c | 121 ++++-- gd_content_interface.h | 187 +++++++-- i18n.c | 66 ++- i18n.h | 2 +- mg_content_interface.c | 155 ++----- mg_content_interface.h | 247 ++++++----- mg_database.c | 13 +- mg_database.h | 37 +- mg_filters.c | 14 +- mg_filters.h | 6 +- mg_media.c | 70 ++-- mg_media.h | 67 +-- mg_playlist.c | 209 ++++++++++ mg_playlist.h | 143 +++++++ mg_tools.h | 61 ++- muggle.c | 122 ++++-- muggle.h | 25 +- vdr_config.h | 119 ++++++ vdr_decoder.c | 137 +++++++ vdr_decoder.h | 158 +++++++ vdr_decoder_mp3.c | 450 ++++++++++++++++++++ vdr_decoder_mp3.h | 114 +++++ vdr_menu.c | 373 +++++++++++------ vdr_menu.h | 92 +++-- vdr_network.h | 45 ++ vdr_player.c | 1073 ++++++++++++++++++++++++++++++++++++++++++++++++ vdr_player.h | 137 +++++++ vdr_setup.c | 70 ++++ vdr_setup.h | 76 ++++ vdr_sound.c | 609 +++++++++++++++++++++++++++ vdr_stream.c | 332 +++++++++++++++ vdr_stream.h | 57 +++ 34 files changed, 4907 insertions(+), 602 deletions(-) create mode 100644 mg_playlist.c create mode 100644 mg_playlist.h create mode 100644 vdr_config.h create mode 100644 vdr_decoder.c create mode 100644 vdr_decoder.h create mode 100644 vdr_decoder_mp3.c create mode 100644 vdr_decoder_mp3.h create mode 100644 vdr_network.h create mode 100644 vdr_player.c create mode 100644 vdr_player.h create mode 100644 vdr_setup.c create mode 100644 vdr_setup.h create mode 100644 vdr_sound.c create mode 100644 vdr_stream.c create mode 100644 vdr_stream.h diff --git a/Makefile b/Makefile index de4a3f2..46bac1e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.8 2004/02/23 15:41:21 RaK Exp $ +# $Id: Makefile,v 1.9 2004/05/28 15:29:18 lvw Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -46,8 +46,9 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o i18n.o -BINOBJS = mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o +OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o + +LIBS = -lmad -lmysqlclient ### Targets: @@ -68,7 +69,7 @@ $(DEPFILE): Makefile $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< libvdr-$(PLUGIN).so: $(OBJS) - $(CXX) $(CXXFLAGS) -shared $(OBJS) -lmysqlclient -o $@ + $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@ @cp $@ $(LIBDIR)/$@.$(VDRVERSION) dist: clean diff --git a/TODO b/TODO index 2ce638a..93a5255 100644 --- a/TODO +++ b/TODO @@ -3,45 +3,126 @@ TODO File for Muggle Code polishing ============== -- check for memory leaks -- check for (reasonably) consistent usage of char*/string -- mg_database is not used +- Check for memory leaks +- Check for (reasonably) consistent usage of char*/string +- mgDatabase is not used? mgPlayer used what for? +- Check for unnecessary log commands +- Add logs, documentation (generate in HTML) +- Generate HTML documentation using doxygen, dotty/gv for state machines Short term items ================ +- Adapt scripts + - use working directory by default or argument + - how to install into correct path OSD --- -- DisplayTrackInfo -- DisplayAlbumInfo -- Edit playlist (means moving tracks around?) Content ------- +- Handle loop mode in mgPlaylist +- Handle shuffle mode in mgPlaylist +- Save/load playlists to database + +Player +------ +- Determine max. framecount (needed for rewinding) ? +- Init scale/level/normalize? +- Check play speed +- Add ogg decoder + +- Add a simple progress display (song title, artist, ...) +- DisplayTrackInfo (part of the player!) +- DisplayAlbumInfo (part of the player!) Medium term items ================= -- Add a player for various formats - - from mp3 plugin - - use mplayer in slave mode - - check mpg123 (used in GD) +- really abstract from specific queries etc. +- mgDatabase should abstract database stuff!? -Long term items -=============== +OSD +--- +- Type numbers to enter characters and jump to first title accordingly +- Add separators for first letter (A, B, C, ...) that can be skipped using left/right +Content +------- +- Save/load filter sets +- Apply filter set as dynamic playlist + +Player +------ +- Shuffle: toggle and loop keys. Shuffle only songs not already played, not easy though - Display covers as still pictures +- Add flac decoder + +Web interface +------------- +- Look at PHP stuff from c't 11/04 and adapt to schema + +Long term ideas +=============== +- daapd integration? +- Display arbitrary images while playing music Visions ======= - -- handle films -- handle off-line media (CDs, DVDs) -- handle EPG +- handle off-line films (CDs, DVDs, recordings) +- handle streams (live TV with channel list, MP3 radio,..., EPG) +- handle images (possibly in sync with music/radio) +- muggle content syndication (e.g. via DAAPD) +- provide a stream (e.g. icecast) of the currently played music? Already Done ============ +- Delete selected item +- Add command line option for top level directory + - prepended to filename in non-GD-mode + - searched in GD-mode +- Edit playlist (move tracks like channels in VDR channel list) + (OK in playlist view) +- Instant play = empty current playlist, append tracks of current node and play + (easy, in submenu of browser) +- Clear playlist (submenu action) +- Find files from database entry based on GD compatibility flag +- Handle Next/PrevFile in mgPlaylist (vdr_player.c) +- Add plugin parameters for database name/host/user/pass +- Add plugin parameter for GD filename compatibility - handle filters: - create tracklist from filter - create tree from filter - i18n (english and german) + +************************************************************ +* +* $Log: TODO,v $ +* Revision 1.7 2004/05/28 15:29:18 lvw +* Merged player branch back on HEAD branch. +* +* Revision 1.1.2.12 2004/05/27 07:58:38 lvw +* Removed bugs in moving and removing tracks from playlists +* +* Revision 1.1.2.11 2004/05/25 21:57:58 lvw +* Updated TODO list +* +* Revision 1.1.2.10 2004/05/25 00:10:45 lvw +* Code cleanup and added use of real database source files +* +* Revision 1.1.2.9 2004/05/24 11:48:35 lvw +* Extended TODO list +* +* Revision 1.1.2.8 2004/05/13 06:48:00 lvw +* Updated TODO list +* +* Revision 1.1.2.7 2004/05/12 22:38:37 lvw +* Some cleanup +* +* Revision 1.1.2.6 2004/05/11 06:35:16 lvw +* Added debugging while hunting stop bug. +* +* Revision 1.1.2.5 2004/05/07 06:46:41 lvw +* Removed a bug in playlist deallocation. Added infrastructure to display information while playing. +* +***********************************************************/ diff --git a/gd_content_interface.c b/gd_content_interface.c index ba5429a..9c339d6 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,19 +1,16 @@ -/*******************************************************************/ /*! \file content_interface.cpp - * \brief Data Objects for content (e.g. mp3 files, movies) - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.21 $ - * \date $Date: 2004/02/23 17:03:24 $ + * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugindatabase + * + * \version $Revision: 1.22 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author Responsible author: $Author: lvw $ * - * DUMMY * Implements main classes of for content items and interfaces to SQL databases * * This file implements the following classes * - GdPlaylist a playlist - * - mgGdTrack a single track (content item). e.g. an mp3 file + * - mgGdTrack a single track (content item). e.g. an mp3 file * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) * */ @@ -21,8 +18,10 @@ #define DEBUG #include "gd_content_interface.h" + #include "mg_tools.h" #include "i18n.h" +#include "vdr_setup.h" using namespace std; @@ -35,16 +34,23 @@ using namespace std; // non-member function int GdInitDatabase(MYSQL *db) { - if(mysql_init(db) == NULL) + if( mysql_init(db) == NULL ) { return -1; } - if(mysql_real_connect(db,"localhost","root","", - "GiantDisc",0,NULL,0) == NULL) - { + // if(mysql_real_connect( db, "localhost", "root", "", + // "GiantDisc2", 0, NULL, 0) == NULL) + if( mysql_real_connect( db, + the_setup.DbHost, + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + the_setup.DbPort, + NULL, 0 ) == NULL ) + { return -2; - } + } return 0; } @@ -63,20 +69,12 @@ vector *GdGetStoredPlaylists(MYSQL db) return list; } -//------------------------------------------------------------------ -//------------------------------------------------------------------ -// -// class gdTrackFilters -// -//------------------------------------------------------------------ -//------------------------------------------------------------------ - /*! - ******************************************************************* + * * \brief constructor, constracts a number >=1 of filter sets * * the first set (index 0 ) is active by default - ********************************************************************/ + */ gdFilterSets::gdFilterSets() { mgFilter* filter; @@ -102,8 +100,9 @@ gdFilterSets::gdFilterSets() filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); // genre filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); - // rating - filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); + + // rating. TODO: Currently buggy. LVW + // filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); m_sets.push_back(set); @@ -170,7 +169,8 @@ string gdFilterSets::computeRestriction(int *viewPrt) { string sql_str = "1"; - switch (m_activeSetId) { + switch (m_activeSetId) + { case 0: *viewPrt = 100; // tracks (flatlist for mountain man ;-)) break; @@ -261,7 +261,7 @@ string gdFilterSets::computeRestriction(int *viewPrt) //------------------------------------------------------------------ //------------------------------------------------------------------ // -// class mgTack +// class mgTrack // //------------------------------------------------------------------ //------------------------------------------------------------------ @@ -269,7 +269,7 @@ mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack(); /*! ***************************************************************************** - * \brief Constructor: creates a mgGdTrack obect + * \brief Constructor: creates a mgGdTrack object * * \param sqlIdentifier identifies a unique row in the track database * \param dbase database which stores the track table @@ -296,16 +296,15 @@ mgGdTrack::mgGdTrack(const mgGdTrack& org) m_db = org.m_db; m_retrieved = org.m_retrieved; if(m_retrieved) - { - m_artist = org.m_artist; - m_title = org.m_title; - m_mp3file = org.m_mp3file; - m_album = org.m_album; - m_genre = org.m_genre; - m_year = org.m_year; - m_rating = org.m_rating; - } - + { + m_artist = org.m_artist; + m_title = org.m_title; + m_mp3file = org.m_mp3file; + m_album = org.m_album; + m_genre = org.m_genre; + m_year = org.m_year; + m_rating = org.m_rating; + } } @@ -459,6 +458,7 @@ vector *mgGdTrack::getTrackInfo() { return new vector(); } + bool mgGdTrack::setTrackInfo(vector *info) { return false; @@ -766,7 +766,7 @@ GdPlaylist::GdPlaylist(unsigned int sql_identifier, MYSQL db_handle) m_listname = row[0]; m_author = row[1]; m_sqlId = sql_identifier; - // now read allentries of the playlist and + // now read all entries of the playlist and // write them into the tracklist insertDataFromSQL(); } @@ -1391,6 +1391,46 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.22 2004/05/28 15:29:18 lvw + * Merged player branch back on HEAD branch. + * + * + * Revision 1.21 2004/02/23 17:03:24 RaK + * - error in filter view while trying to switch or using the colour keys + * workaround: first filter criteria is inttype. than it works, dont ask why ;-( + * + * Revision 1.20 2004/02/23 16:30:58 RaK + * - album search error because of i18n corrected + * + * Revision 1.19 2004/02/23 15:56:19 RaK + * - i18n + * + * Revision 1.18 2004/02/23 15:17:51 RaK + * - i18n + * + * Revision 1.17 2004/02/23 15:41:21 RaK + * - first i18n attempt + * + * Revision 1.16 2004/02/14 22:02:45 RaK + * - mgFilterChoice Debuged + * fehlendes m_type = CHOICE in mg_filters.c + * falscher iterator in vdr_menu.c + * + * Revision 1.15 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * + * Revision 1.14.2.4 2004/05/26 14:34:58 lvw + * Formatting changed + * + * Revision 1.14.2.3 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.14.2.2 2004/03/14 17:57:30 lvw + * Linked against libmad. Introduced config options into code. + * + * Revision 1.14.2.1 2004/03/02 07:05:50 lvw + * Initial adaptations from MP3 plugin added (untested) + * * Revision 1.21 2004/02/23 17:03:24 RaK * - error in filter view while trying to switch or using the colour keys * workaround: first filter criteria is inttype. than it works, dont ask why ;-( @@ -1415,6 +1455,7 @@ mgContentItem* GdTreeNode::getSingleTrack() * Revision 1.15 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * +>>>>>>> 1.14.2.4 * Revision 1.14 2004/02/12 07:56:46 RaK * - SQL Fehler bei der Playlist Search korrigiert * diff --git a/gd_content_interface.h b/gd_content_interface.h index c572c75..8e95267 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -1,24 +1,23 @@ -/*******************************************************************/ -/*! \file gd_content_interface.h - * \brief Data Objects for content (e.g. mp3 files, movies) - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.6 $ - * \date $Date: 2004/02/23 15:41:21 $ +/*! + * \file gd_content_interface.h + * \brief Data objects for content (e.g. mp3 files, movies) + * for the vdr muggle plugin database + * + * \version $Revision: 1.7 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author Responsible author: $Author: lvw $ * - * Declares main classes of for content items and interfaces to SQL databases + * Declares main classes for content items and interfaces to SQL databases * * This file defines the following classes - * - mgPlaylist a playlist - * - mgGdTrack a single track (content item). e.g. an mp3 file - * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * - gdFilterSets + * - mgGdTrack a single track (content item). e.g. an mp3 file + * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) * */ -/*******************************************************************/ -/* makes sure we dont use parse the same declarations twice */ +/* makes sure we dont use the same declarations twice */ #ifndef _GD_CONTENT_INTERFACE_H #define _GD_CONTENT_INTERFACE_H @@ -29,75 +28,158 @@ #include "mg_content_interface.h" #include "mg_media.h" -#include "mg_filters.h" +#include "mg_playlist.h" +#include "mg_filters.h" #include "i18n.h" -// non-member function +// non-member functions int GdInitDatabase(MYSQL *db); std::vector *GdGetStoredPlaylists(MYSQL db); +/*! + * \brief A set of filters to search for content + */ class gdFilterSets : public mgFilterSets { public: + + /*! + * \brief the default constructor + * + * Constructs a number ( >= 1 ) of filter sets where + * the first (index 0) is active by default. + */ gdFilterSets(); - // constructor, constracts a number >=1 of filter sets - // the first set (index 0 ) is active by default + /*! + * \brief the destructor + */ virtual ~gdFilterSets(); - // destructor + /*! + * \brief compute restriction w.r.t active filters + * + * Computes the (e.g. sql) restrictions specified by + * the active filter sets. + * + * \param viewPort - after call, contains the index of the appropriate default view in + */ virtual std::string computeRestriction(int *viewPrt); - // computes the (e.g. sql-) restrictions specified by the active filter set - // and returns the index of the appropriate defualt view in viewPrt - }; /*! - ******************************************************************* - * \class mgGdTrack - * * \brief represents a a single track + * This may be any content item. e.g. a mp3 fileselection - * * The object is initially created with a database identifier. * The actual data is only read when a content field is accessed for - * The first time - ********************************************************************/ + * the first time. For subsequent access, cached values are used. + */ class mgGdTrack : public mgContentItem { private: + + /*! + * \brief the database in which the track resides + * */ MYSQL m_db; - bool m_retrieved; // false if content field values have not yet been - // retrieved from database. This is done on demand + + /*! + * \brief a dirty flag + * + * false, if content field values have not yet been retrieved + * from the database. Set to true when contents are retrieved + * (on demand only). + */ + bool m_retrieved; // content fields + /*! + * \brief the artist name + */ std::string m_artist; + + /*! + * \brief the track title + */ std::string m_title; + + /*! + * \brief the filename + */ std::string m_mp3file; + + /*! + * \brief The album to which the file belongs + */ std::string m_album; + + /*! + * \brief The genre of the music + */ std::string m_genre; + + /*! + * \brief The year in which the track appeared + */ int m_year; + + /*! + * \brief The rating by the user + */ int m_rating; + + /*! + * \brief The length of the file in bytes. + * \todo TODO: is this true? Or what length are we talking about? + */ int m_length; + /*! + * \brief Access the data of the item from the database + */ bool readData(); public: - - /* constructor */ - mgGdTrack(){ m_uniqID = -1;} // creates invalid item - mgGdTrack(int sqlIdentifier, MYSQL dbase); + /*! + * \brief a constructor + * + * Creates an invalid item. + * + * \todo does this make sense? + */ + mgGdTrack() + { + m_uniqID = -1; + } + + /*! + * \brief a constructor for a specific item + * + * The constructor creates a specific item in a given database + * + * \param sqlIdentifier - a unique ID of the item which will be represented + * \param dbase - the database in which the item exists + */ + mgGdTrack( int sqlIdentifier, MYSQL dbase ); + + /*! + * \brief a copy constructor + */ mgGdTrack(const mgGdTrack&); - /* destructor */ + /*! + * \brief the destructor + */ virtual ~mgGdTrack(); virtual mgContentItem::contentType getContentType(){return mgContentItem::GD_AUDIO;} + virtual mgMediaPlayer getPlayer() { return mgMediaPlayer(); @@ -106,13 +188,17 @@ private: /* data acess */ //virtual functions of the base class virtual std::string getSourceFile(); + virtual std::string getTitle(); - virtual std::string getLabel(int col); + + virtual std::string getLabel(int col = 0); virtual std::vector *getTrackInfo(); + virtual bool setTrackInfo(std::vector*); virtual std::string getGenre(); + virtual int getRating(); // additional class-specific functions @@ -221,12 +307,41 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.7 2004/05/28 15:29:18 lvw + * Merged player branch back on HEAD branch. + * + * + * Revision 1.6 2004/02/23 15:41:21 RaK + * - first i18n attempt + * + * Revision 1.5 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * + * Revision 1.4.2.6 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.4.2.5 2004/04/01 21:35:32 lvw + * Minor corrections, some debugging aid. + * + * Revision 1.4.2.4 2004/03/14 17:57:30 lvw + * Linked against libmad. Introduced config options into code. + * + * Revision 1.4.2.3 2004/03/10 13:11:24 lvw + * Added documentation + * + * Revision 1.4.2.2 2004/03/08 07:14:27 lvw + * Preliminary changes to muggle player + * + * Revision 1.4.2.1 2004/03/02 07:05:50 lvw + * Initial adaptations from MP3 plugin added (untested) + * * Revision 1.6 2004/02/23 15:41:21 RaK * - first i18n attempt * * Revision 1.5 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * +>>>>>>> 1.4.2.6 * Revision 1.4 2004/02/09 19:27:52 MountainMan * filter set implemented * diff --git a/i18n.c b/i18n.c index 3a0cbd8..cb36790 100644 --- a/i18n.c +++ b/i18n.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.c,v 1.5 2004/02/23 17:07:08 RaK Exp $ + * $Id: i18n.c,v 1.6 2004/05/28 15:29:18 lvw Exp $ */ #include "i18n.h" @@ -554,5 +554,69 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO }, + { "Load", + "Laden", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Save", + "Speichern", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Clear", + "Loeschen", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Mainmenu", + "Hauptmenue", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { NULL } }; diff --git a/i18n.h b/i18n.h index e4e6993..ef5a31f 100644 --- a/i18n.h +++ b/i18n.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.h,v 1.1 2004/02/23 15:41:21 RaK Exp $ + * $Id: i18n.h,v 1.2 2004/05/28 15:29:18 lvw Exp $ */ #ifndef _I18N__H diff --git a/mg_content_interface.c b/mg_content_interface.c index 5d60358..437d8b5 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -1,14 +1,11 @@ -/*******************************************************************/ -/*! \file content_interface.c - * \brief Data Objects for content (e.g. mp3 files, movies) - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/23 15:41:21 $ +/*! \file mg_content_interface.c + * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin + * + * \version $Revision: 1.5 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author Responsible author: $Author: lvw $ * - * DUMMY * Implements main classes of for content items and interfaces to SQL databases * * This file implements the following classes @@ -17,7 +14,6 @@ * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) * */ -/*******************************************************************/ #define DEBUG #include "mg_content_interface.h" @@ -31,22 +27,20 @@ mgContentItem mgContentItem::UNDEFINED = mgContentItem(); using namespace std; /*! - ***************************************************************************** - * \brief construcor + * \brief constructor * - * creates empty tracklist - ****************************************************************************/ + * create an empty tracklist + */ mgTracklist::mgTracklist() { } /*! - ***************************************************************************** * \brief destrucor * * Deletes all items in the tracklist and removes the list itself - ****************************************************************************/ + */ mgTracklist::~mgTracklist() { mgContentItem* ptr; @@ -183,11 +177,15 @@ mgContentItem* mgTracklist::getItem(unsigned int position) ****************************************************************************/ bool mgTracklist::remove(unsigned int position) { - if(position >= m_list.size()) return false; + if( position >= m_list.size() ) + { + return false; + } vector::iterator iter; iter = m_list.begin()+ position; m_list.erase(iter); + return true; } @@ -213,104 +211,6 @@ int mgTracklist::remove(mgContentItem* item) } -/*=================================================================*/ -/* */ -/* class mgPlaylist */ -/* */ -/*=================================================================*/ -mgPlaylist::mgPlaylist() -{ -} -mgPlaylist::mgPlaylist(string listname) -{ - m_listname = listname; -} - - /*==== destructor ====*/ -mgPlaylist::~mgPlaylist() -{ - -} -/*==== add/ remove tracks ====*/ - -/* adds a song at the end of the playlist */ -void mgPlaylist::append(mgContentItem* item) -{ - m_list.push_back(item); -} - -void mgPlaylist::appendList(vector *tracks) -{ - vector::iterator iter; - mgDebug(3, "Adding %d tracks to the playlist",tracks->size()); - for(iter = tracks->begin(); iter != tracks->end(); iter++) - { - m_list.push_back(*iter); - } - tracks->clear(); -} - - -/* adds a song after 'position' */ -void mgPlaylist::insert(mgContentItem* item, unsigned int position) -{ - if(position >= m_list.size()) - m_list.push_back(item); - else - m_list.insert(m_list.begin()+position, item); -} - - - -/*==== access tracks ====*/ -string mgPlaylist::getListname() { return m_listname; } -void mgPlaylist::setListname(string name){ m_listname = name;} - - - -// returns the first item of the list -mgContentItem* mgPlaylist::getFirst() -{ - m_current = m_list.begin(); - return *m_current; -} - -// returns the nth track from the playlist -mgContentItem* mgPlaylist::getPosition(unsigned int position) -{ - if(position >= m_list.size()) - return &(mgContentItem::UNDEFINED); //invalid - m_current = m_list.begin()+position; - return *m_current; -} - -// proceeds to the next item -mgContentItem* mgPlaylist::skipFwd() -{ - if(m_current+1 == m_list.end()) - return &(mgContentItem::UNDEFINED); //invalid - else - return * (++m_current); -} - -// goes back to the previous item -mgContentItem* mgPlaylist::skipBack() -{ - if(m_current == m_list.begin()) - return &(mgContentItem::UNDEFINED); //invalid - else - return * (--m_current); -} - -// get next track, do not update data structures -mgContentItem* mgPlaylist::sneakNext() -{ - if(m_current+1 == m_list.end()) - return &(mgContentItem::UNDEFINED); //invalid - else - return * (m_current+1); -} - /*=================================================================*/ /* */ @@ -402,6 +302,31 @@ string mgSelectionTreeNode::getRestrictions() /* -------------------- begin CVS log --------------------------------- * $Log: mg_content_interface.c,v $ + * Revision 1.5 2004/05/28 15:29:18 lvw + * Merged player branch back on HEAD branch. + * + * + * Revision 1.4 2004/02/23 15:41:21 RaK + * - first i18n attempt + * + * Revision 1.3.2.6 2004/05/27 07:58:38 lvw + * Removed bugs in moving and removing tracks from playlists + * + * Revision 1.3.2.5 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.3.2.4 2004/05/04 16:51:53 lvw + * Debugging aids added. + * + * Revision 1.3.2.3 2004/03/08 21:42:22 lvw + * Added count method. Some comments for further todos added. + * + * Revision 1.3.2.2 2004/03/08 07:14:27 lvw + * Preliminary changes to muggle player + * + * Revision 1.3.2.1 2004/03/02 07:08:12 lvw + * 118 additions + * * Revision 1.4 2004/02/23 15:41:21 RaK * - first i18n attempt * diff --git a/mg_content_interface.h b/mg_content_interface.h index b7bc7a3..bb99999 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -1,19 +1,21 @@ /*******************************************************************/ -/*! \file content_interface.h - * \brief Data Objects for content (e.g. mp3 files, movies) - * for the vdr muggle plugindatabase +/*! \file mg_content_interface.h + * \brief data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin + * ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/02/09 19:27:52 $ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * + * \version $Revision: 1.4 $ + * \date $Date: 2004/05/28 15:29:18 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author file owner: $Author: lvw $ * - * Declares main classes of for content items and interfaces to SQL databases + * Declares generic classes of for content items and interfaces to SQL databases * - * This file defines the following classes - * - mgPlaylist a playlist - * - mgTrack a single track (content item). e.g. an mp3 file - * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * This file defines the following classes + * - mgMediaMplayer + * - mgContentItem + * - mgTracklist + * - mgSelectionTreeNode * */ /*******************************************************************/ @@ -28,82 +30,150 @@ #include #define ILLEGAL_ID -1 + class mgFilter; +class mgPlaylist; /*! - ******************************************************************* * \class mgMediaPlayer - * * \brief dummy player class - ********************************************************************/ + */ class mgMediaPlayer { - public: - mgMediaPlayer(){;} - ~mgMediaPlayer(){;} - + public: + + mgMediaPlayer() + { } + + ~mgMediaPlayer() + { } }; /*! - ******************************************************************* - * \class mgContentItem - * * \brief Generic class that represents a single content item. + * * This is the parent class from which classes like mgGdTrack are derived - ********************************************************************/ + * + */ class mgContentItem { + public: + typedef enum contentType + { + ABSTRACT =0, + GD_AUDIO, + EPG + }; - public: - typedef enum contentType{ - ABSTRACT =0, - GD_AUDIO, - EPG - }contentType; - - protected: + protected: int m_uniqID; // internal identifier to uniquely identify a content item; - public: - /* constructor */ - mgContentItem(){ m_uniqID = -1;} - mgContentItem(int id){m_uniqID = id;} - mgContentItem(const mgContentItem& org){m_uniqID = org.m_uniqID;} - /* destructor */ - virtual ~mgContentItem(){}; - - /* data acess */ - int getId(){ return m_uniqID;} + public: - // what type of content are we looking at (e.g. audio, video, epg) - virtual contentType getContentType(){return ABSTRACT;} + /*! \brief default constructor + */ + mgContentItem() + : m_uniqID( -1 ) + { } + + /*! \brief constructor with explicit id + */ + mgContentItem( int id ) + : m_uniqID( id ) + { } + + /*! \brief copy constructor + */ + mgContentItem( const mgContentItem& org ) + : m_uniqID( org.m_uniqID ) + { } + + /*! \brief destructor + */ + virtual ~mgContentItem() + { }; + + /*! \brief acess unique id + */ + int getId() + { + return m_uniqID; + } - // return (global?) object that is used to play the content items - virtual mgMediaPlayer getPlayer(){return mgMediaPlayer();} + /*! \brief determine what type of content are we looking at (e.g. audio, video, epg) + */ + virtual contentType getContentType() + { + return ABSTRACT; + } + /*! \brief return a (global?) object that is used to play content items + * \todo What for? Interesting properties? Last state, play info, ...? + */ + virtual mgMediaPlayer getPlayer() + { + return mgMediaPlayer(); + } - // get the "file" (or URL) that is passed to the player - virtual std::string getSourceFile(){return "";} + /*! \brief get the "file" (or URL) that is passed to the player + */ + virtual std::string getSourceFile() + { + return ""; + } // ============ data access ================= - virtual std::string getTitle(){return "";} // return the title - virtual std::string getLabel(int col){return "";} // return the title - - virtual std::string getDescription()// return a short textual description - {return "";} - virtual std::vector *getTrackInfo(){return NULL;} - virtual bool updateTrackInfo(std::vector*){return false;} + /*! \brief return the title + */ + virtual std::string getTitle() + { + return ""; + } + + /*! \brief return a specific label + */ + virtual std::string getLabel(int col = 0) + { + return ""; + } + + /*! \brief return a short textual description + */ + virtual std::string getDescription() + { + return ""; + } + + virtual std::vector *getTrackInfo() + { + return NULL; + } + + virtual bool updateTrackInfo(std::vector*) + { + return false; + } + + virtual std::string getGenre() + { + return ""; + } - virtual std::string getGenre(){return "";} virtual int getRating() { return 0; } - virtual bool operator == (mgContentItem o){return m_uniqID == o.m_uniqID;} + virtual bool operator == (mgContentItem o) + { + return m_uniqID == o.m_uniqID; + } // check, if usable - virtual bool isValid() {return (m_uniqID >=0);} + virtual bool isValid() + { + return ( m_uniqID >= 0 ); + } static mgContentItem UNDEFINED; }; @@ -136,54 +206,6 @@ class mgTracklist virtual bool remove(unsigned int position); // remove item at position }; -/*! - ******************************************************************* - * \class mgPlaylist - * - * Represents a generic playlist, i.e. an ordered collection of tracks - * The derived classes take care of specifics of certain media types - ********************************************************************/ -class mgPlaylist : public mgTracklist -{ - protected: - std::string m_listname; - std::vector::iterator m_current; - public: - - /*==== constructors ====*/ - mgPlaylist(); - mgPlaylist(std::string listname); - - /*==== destructor ====*/ - virtual ~mgPlaylist(); - - /*==== add/ remove tracks ====*/ - - /* adds a song at the end of the playlist */ - virtual void append(mgContentItem* item); - virtual void appendList(std::vector *tracks); - /* adds a song after 'position' */ - virtual void insert(mgContentItem* item, unsigned int position); - - /*==== access tracks ====*/ - std::string getListname() ; - void setListname(std::string name); - - // returns the first item of the list - virtual mgContentItem* getFirst(); - - // returns the nth track from the playlist - virtual mgContentItem* getPosition(unsigned int position); - - // proceeds to the next item - virtual mgContentItem* skipFwd(); - - // goes back to the previous item - virtual mgContentItem* skipBack(); - - virtual mgContentItem* sneakNext(); - -}; class mgSelectionTreeNode { @@ -247,6 +269,21 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: mg_content_interface.h,v $ + * Revision 1.4 2004/05/28 15:29:18 lvw + * Merged player branch back on HEAD branch. + * + * Revision 1.3.2.4 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.3.2.3 2004/04/01 21:35:32 lvw + * Minor corrections, some debugging aid. + * + * Revision 1.3.2.2 2004/03/08 21:42:22 lvw + * Added count method. Some comments for further todos added. + * + * Revision 1.3.2.1 2004/03/08 07:14:28 lvw + * Preliminary changes to muggle player + * * Revision 1.3 2004/02/09 19:27:52 MountainMan * filter set implemented * diff --git a/mg_database.c b/mg_database.c index 9a85cb8..eabff6d 100644 --- a/mg_database.c +++ b/mg_database.c @@ -2,10 +2,10 @@ /*! \file mg_database.c * \brief A capsule around MySql database access ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: lvw $ */ /*******************************************************************/ @@ -17,7 +17,9 @@ mgDB::mgDB() { } -mgDB::mgDB(string user, string pass) +mgDB::mgDB(string host, string name, + string user, string pass, + int port) { } @@ -27,9 +29,8 @@ mgDB::~mgDB() MYSQL mgDB::getDBHandle() { - MYSQL m; - return m; + return m_dbase; } diff --git a/mg_database.h b/mg_database.h index b979df4..4e00826 100644 --- a/mg_database.h +++ b/mg_database.h @@ -1,34 +1,47 @@ -/*******************************************************************/ -/*! \file mg_database.h - * \brief A capsule around MySql database access - ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/01 18:22:53 $ +/*! + * \file mg_database.h + * \brief A capsule around MySql database access + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ */ -/*******************************************************************/ - #ifndef __MG_DATABASE_H #define __MG_DATABASE_H #include - #include class mgDB { public: + /*! \brief default constructor + */ mgDB( ); - mgDB( std::string user, std::string pass ); + + /*! \brief constructor + * + * \param host + * \param name + * \param user + * \param pass + * \param port + */ + mgDB( std::string host, std::string name, + std::string user, std::string pass, + int port ); + + /*! \brief constructor */ ~mgDB(); + /*! \brief obtain database handle*/ MYSQL getDBHandle(); private: - // MYSQL m_dbase; + MYSQL m_dbase; }; #endif diff --git a/mg_filters.c b/mg_filters.c index c5859e0..f8c164d 100644 --- a/mg_filters.c +++ b/mg_filters.c @@ -2,10 +2,10 @@ /*! \file mg_filters.c * \brief ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/14 22:02:45 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author file owner: $Author: lvw $ */ /*******************************************************************/ @@ -284,14 +284,14 @@ void mgFilterChoice::restore() void mgFilterChoice::clear() { m_stored_val = m_default_val; - m_selval = m_default_val; + m_selval = m_default_val; } bool mgFilterChoice::isSet() { if(m_stored_val == m_default_val) - { - return false; - } + { + return false; + } return true; } diff --git a/mg_filters.h b/mg_filters.h index 8a983f7..5501d63 100644 --- a/mg_filters.h +++ b/mg_filters.h @@ -3,10 +3,10 @@ * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.1 $ - * \date $Date: 2004/02/12 09:15:07 $ + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: lvw $ */ /*******************************************************************/ diff --git a/mg_media.c b/mg_media.c index bbb682b..18e44f3 100644 --- a/mg_media.c +++ b/mg_media.c @@ -1,40 +1,38 @@ -/*******************************************************************/ /*! \file mg_media.c * \brief Top level access to media in vdr plugin muggle - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.12 $ - * \date $Date: 2004/02/23 16:30:58 $ + * + * \version $Revision: 1.13 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author Responsible author: $Author: lvw $ */ -/*******************************************************************/ /* makes sure we dont parse the same declarations twice */ #include "mg_media.h" + #include "mg_tools.h" #include "mg_content_interface.h" + #include "gd_content_interface.h" +#include "vdr_setup.h" + using namespace std; //------------------------------------------------------------------- // mgFilterSets //------------------------------------------------------------------- /*! - ******************************************************************* - * \brief constructor - * - * constructor of the base class - ********************************************************************/ + * \brief constructor + * + * constructor of the base class + */ + mgFilterSets::mgFilterSets() { // nothing to be done in the base class } - /*! - ******************************************************************* - * \brief destructor - ********************************************************************/ + mgFilterSets::~mgFilterSets() { vector *set; @@ -52,7 +50,7 @@ mgFilterSets::~mgFilterSets() } m_sets.clear(); } - /*! +/*! ******************************************************************* * \brief returns the number of available sets ********************************************************************/ @@ -139,9 +137,8 @@ void mgFilterSets::select(int n) /*! - ******************************************************************* * \brief return title of the active filter set - ********************************************************************/ + */ string mgFilterSets::getTitle() { if(m_activeSetId < (int) m_titles.size()) @@ -156,15 +153,11 @@ string mgFilterSets::getTitle() } } -//------------------------------------------------------------------- -// mgFilterSets -//------------------------------------------------------------------- /*! - ******************************************************************* * \class mgMedia * * \brief mein class to access content in the vdr plugin muggle - ********************************************************************/ + */ mgMedia::mgMedia(contentType mediatype) { int errval = 0; @@ -180,7 +173,7 @@ mgMedia::mgMedia(contentType mediatype) case GD_MP3: { errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); + mgDebug(3, "Successfully conntected to sql database %s", the_setup.DbName ); } } if(errval < 0) @@ -193,8 +186,8 @@ mgMedia::mgMedia(contentType mediatype) { case GD_MP3: { - errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully conntected to sql database 'GiantDisc'"); + errval = GdInitDatabase( &m_db ); // TODO: why duplicate this? LVW + mgDebug(3, "Successfully conntected to sql database %s", the_setup.DbName ); } } } @@ -220,7 +213,9 @@ string mgMedia::getMediaTypeName() switch(m_mediatype) { case GD_MP3: + { return "GiantDisc"; + } break; } mgError("implementation Error"); // we should never get here return ""; @@ -429,6 +424,24 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.c,v $ + * Revision 1.13 2004/05/28 15:29:18 lvw + * Merged player branch back on HEAD branch. + * + * Revision 1.12 2004/02/23 16:30:58 RaK + * - album search error because of i18n corrected + * + * Revision 1.11 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * + * Revision 1.10.2.3 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.10.2.2 2004/03/14 17:57:30 lvw + * Linked against libmad. Introduced config options into code. + * + * Revision 1.10.2.1 2004/03/02 07:05:50 lvw + * Initial adaptations from MP3 plugin added (untested) + * * Revision 1.12 2004/02/23 16:30:58 RaK * - album search error because of i18n corrected * @@ -451,8 +464,5 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() * Revision 1.7 2004/02/02 22:48:04 MountainMan * added CVS $Log * - * * --------------------- end CVS log ---------------------------------- */ - - diff --git a/mg_media.h b/mg_media.h index cb685c3..394db13 100644 --- a/mg_media.h +++ b/mg_media.h @@ -1,15 +1,14 @@ -/*******************************************************************/ -/*! \file mgmedia.h +/*! + * \file mgmedia.h * \brief Top level access to media in vdr plugin muggle - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.10 $ - * \date $Date: 2004/02/12 09:15:07 $ + * + * \version $Revision: 1.11 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ */ -/*******************************************************************/ -/* makes sure we dont use parse the same declarations twice */ + +// makes sure we dont use parse the same declarations twice #ifndef _MG_MEDIA_H #define _MG_MEDIA_H @@ -25,63 +24,61 @@ class mgFilter; class mgFilterSets; /*! - ******************************************************************* * \class mgFilterSets * - * Represents one or several sets of filters to set and memorize - * search constraint - ********************************************************************/ -class mgFilterSets { + * Represents one or several sets of filters to set and memorize search constraint + */ +class mgFilterSets +{ protected: int m_activeSetId; - std::vector *m_activeSet; // pointer to the active filter set + std::vector *m_activeSet; // pointer to the active filter set - std::vector< std::vector*> m_sets; // stores name-value pairs, even if a different set is active + std::vector< std::vector*> m_sets; - std::vector m_titles; // stores the titles for all filters + std::vector m_titles; public: - mgFilterSets(); // constructor, constracts a number >=1 of filter sets // the first set (index 0 ) is active by default + mgFilterSets(); - virtual ~mgFilterSets(); // destructor + virtual ~mgFilterSets(); - int numSets(); // returns the number of available sets + int numSets(); - void nextSet(); // proceeds to the next one in a circular fashion + void nextSet(); - void select(int n); // activates a specific set by index + void select(int n); - virtual void clear(); // restores the default values for all filter values in the active set // normally, the default values represent 'no restrictions' + virtual void clear(); - void accept(); // stores the current filter values + void accept(); - std::vector *getFilters(); // returns the active set to the application // the application may temporarily modify the filter values // accept() needs to be called to memorize the changed values + std::vector *getFilters(); - virtual std::string computeRestriction(int *viewPrt)=0; // computes the (e.g. sql-) restrictions specified by the active filter set // and returns the index of the appropriate defualt view in viewPrt + virtual std::string computeRestriction(int *viewPrt)=0; - std::string getTitle(); // returns title of active filter set + std::string getTitle(); }; /*! - ******************************************************************* * \class mgMedia * * \brief main class to access content in the vdr plugin muggle @@ -90,7 +87,7 @@ class mgFilterSets { * where the data type is explicitelymentioned. * The class provides a set of objects that abstract from the data * type and source - ********************************************************************/ + */ class mgMedia { @@ -149,6 +146,18 @@ class mgMedia /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.h,v $ + * Revision 1.11 2004/05/28 15:29:18 lvw + * Merged player branch back on HEAD branch. + * + * Revision 1.10 2004/02/12 09:15:07 LarsAC + * Moved filter classes into separate files + * + * Revision 1.9.2.2 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.9.2.1 2004/03/02 07:05:50 lvw + * Initial adaptations from MP3 plugin added (untested) + * * Revision 1.10 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * diff --git a/mg_playlist.c b/mg_playlist.c new file mode 100644 index 0000000..f6387b0 --- /dev/null +++ b/mg_playlist.c @@ -0,0 +1,209 @@ +/*! + * \file mg_playlist.c + * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * This file implements the class mgPlaylist which maintains a playlist + * and supports editing (e.g. adding or moving tracks), navigating it + * (e.g. obtaining arbitrary items or accessing them sequentially) or + * making it persistent in some database. + */ + +#include "mg_playlist.h" +#include "mg_tools.h" + +#include +#include + +using namespace std; + +/* ==== constructors ==== */ + +mgPlaylist::mgPlaylist() +{ + m_current_idx = 0; + + char *buffer; + asprintf( &buffer, "Playlist-%ld", random() ); + + m_listname = buffer; +} + +mgPlaylist::mgPlaylist(string listname) +{ + m_current_idx = 0; + m_listname = listname; +} + +/* ==== destructor ==== */ + +mgPlaylist::~mgPlaylist() +{ +} + +void mgPlaylist::toggleShuffle() +{ +} + +void mgPlaylist::toggleLoop() +{ +} + +void mgPlaylist::initialize() +{ + m_current = m_list.begin(); +} + +/* ==== add/remove tracks ==== */ + +/* adds a song at the end of the playlist */ +void mgPlaylist::append(mgContentItem* item) +{ + m_list.push_back(item); +} + +/* append a list of tracks at the end of the playlist */ +void mgPlaylist::appendList(vector *tracks) +{ + vector::iterator iter; + + mgDebug( 3, "Adding %d tracks to the playlist", tracks->size() ); + + for( iter = tracks->begin(); iter != tracks->end(); iter++ ) + { + m_list.push_back(*iter); + } + + // TODO: why is this vector cleared? shouldn't the caller take care of this? LVW + tracks->clear(); +} + +/* add a song after 'position' */ +void mgPlaylist::insert(mgContentItem* item, unsigned int position) +{ + if( position >= m_list.size() ) + { + m_list.push_back(item); + } + else + { + m_list.insert( m_list.begin() + position, item ); + } +} + +void mgPlaylist::clear() +{ + // TODO: who takes care of memory allocation/deallocation of mgItems? + + vector::iterator iter; + + for( iter = m_list.begin(); iter != m_list.end(); iter++ ) + { // delete each item in the list + delete *iter; + } + + // finally clear the list itself + m_list.clear(); +} + +void mgPlaylist::move( int from, int to ) +{ + vector::iterator from_iter = m_list.begin() + from; + vector::iterator to_iter = m_list.begin() + to; + + m_list.insert( to_iter, *from_iter); + m_list.erase( from_iter ); +} + +/*==== access tracks ====*/ +string mgPlaylist::getListname() +{ + return m_listname; +} + +void mgPlaylist::setListname(string name) +{ + m_listname = name; +} + +// returns the count of items in the list +int mgPlaylist::count() +{ + return m_list.size(); +} + +// returns the first item of the list +mgContentItem* mgPlaylist::getFirst() +{ + m_current = m_list.begin(); + + return *m_current; +} + +// returns the nth track from the playlist +mgContentItem* mgPlaylist::getPosition(unsigned int position) +{ + if( position >= m_list.size() ) + { + // TODO: why not return a NULL pointer? LVW + return &(mgContentItem::UNDEFINED); //invalid + } + m_current = m_list.begin() + position; + + return *m_current; +} + +// proceeds to the next item +mgContentItem* mgPlaylist::skipFwd() +{ + mgContentItem* next; + + if( m_current + 1 == m_list.end() ) // unless loop mode + { + // TODO: why not return a NULL pointer? LVW + next = &(mgContentItem::UNDEFINED); //invalid item + } + else + { + return * (++ m_current); + } + + return next; +} + +// goes back to the previous item +mgContentItem* mgPlaylist::skipBack() +{ + mgContentItem* prev; + + if( m_current == m_list.begin() ) + { + // TODO: why not return a NULL pointer? LVW + prev = &(mgContentItem::UNDEFINED); //invalid + } + else + { + prev = * (--m_current); + } + + return prev; +} + +// get next track, do not update data structures +mgContentItem* mgPlaylist::sneakNext() +{ + if( m_current+1 == m_list.end() ) + { + // TODO: why not return a NULL pointer? LVW + return &(mgContentItem::UNDEFINED); //invalid + } + else + { + return * (m_current+1); + } +} + diff --git a/mg_playlist.h b/mg_playlist.h new file mode 100644 index 0000000..dcec71c --- /dev/null +++ b/mg_playlist.h @@ -0,0 +1,143 @@ +/*! + * \file mg_playlist.c + * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * This file implements the class mgPlaylist which maintains a playlist + * and supports editing (e.g. adding or moving tracks), navigating it + * (e.g. obtaining arbitrary items or accessing them sequentially) or + * making it persistent in some database. + */ +#ifndef __MG_PLAYLIST +#define __MG_PLAYLIST + +#include "mg_content_interface.h" + +/*! + * \class mgPlaylist + * + * \brief Represents a generic playlist, i.e. an ordered collection of tracks + * Derived classes take care of specifics of certain media types + */ +class mgPlaylist : public mgTracklist +{ + +private: + //! \brief current index in the playlist + // TODO: should be a property of the player? + int m_current_idx; + +protected: + + // TODO: Why not make these private? Subclasses should use access functions. LVW + + //! \brief the name of the playlist + std::string m_listname; + + //! \brief the current item as an iterator + std::vector::iterator m_current; + +public: + + /* ==== constructors and initialization ==== */ + + //! \brief the default constructor (random listname) + mgPlaylist(); + + /*! \brief constructor with a specified listname + * + * \param listname - initial name of the list + */ + mgPlaylist( std::string listname ); + + void initialize(); + + /* ==== destructor ==== */ + //! \brief the destructor + virtual ~mgPlaylist(); + + /* === control behavior */ + + //! \brief toggle the loop mode. TODO. + void toggleLoop(); + + //! \brief toggle the shuffle mode. TODO. + void toggleShuffle(); + + /* ==== add/ remove tracks ==== */ + + /*! \brief adds a song at the end of the playlist + * + * \param item - the item to be appended + */ + virtual void append(mgContentItem* item); + + /*! \brief add a vector of songs at the end of the playlist + * + * \param tracks - the vector of tracks to be added + */ + virtual void appendList(std::vector *tracks); + + /*! \brief add a song after a specified position + * + * Might be merged with append by using a default argument for the position + * + * \param item - the item to be added + * \param position - the position where the item should be added + */ + virtual void insert(mgContentItem* item, unsigned int position); + + //! \brief clear all tracks + virtual void clear(); + + /*! \brief move tracks within playlist + * + * \param from - the item of the index to be moved + * \param to - the target index of the item + */ + void move( int from, int to ); + + /*! \brief remove a track from the playlist + * + * \param pos - the index of the track to be removed + */ + // bool remove( int pos ); + + /* ==== access tracks ==== */ + + //! \brief obtain the listname + std::string getListname() ; + + /*! \brief set the listname + * + * \param name - the new name of this list + */ + void setListname(std::string name); + + //! \brief returns the count of items in the list + int count(); + + //! \brief returns the first item of the list + virtual mgContentItem* getFirst(); + + /*! \brief returns the nth track from the playlist + * + * \param position - the position to skip to + */ + virtual mgContentItem* getPosition(unsigned int position); + + //! \brief proceeds to the next item + virtual mgContentItem* skipFwd(); + + //! \brief goes back to the previous item + virtual mgContentItem* skipBack(); + + //! \brief obtain the next item without skipping the current position + virtual mgContentItem* sneakNext(); +}; + +#endif diff --git a/mg_tools.h b/mg_tools.h index f913667..d56f7d8 100644 --- a/mg_tools.h +++ b/mg_tools.h @@ -3,10 +3,10 @@ * \brief A few util functions for standalone and plugin messaging * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: lvw $ * */ /*******************************************************************/ @@ -14,6 +14,9 @@ #ifndef _MUGGLE_TOOLS_H #define _MUGGLE_TOOLS_H +#include +#include + #include "mysql/mysql.h" #define STANDALONE 1 @@ -24,12 +27,62 @@ void mgDebug( const char *fmt, ... ); void mgWarning(const char *fmt, ...); void mgError(const char *fmt, ...); - MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...); void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...); +#ifdef DEBUG +#define MGLOG(x) mgLog __thelog(x) +#else +#define MGLOG(x) {} +#endif + +#ifdef DEBUG +#define MGLOGSTREAM __thelog.getStream() +#else +#define MGLOGSTREAM __thelog.getStream() +#endif + + +class mgLog +{ + public: + enum + { + LOG, WARNING, ERROR, FATAL + } mgLogLevel; + + std::ostream& getStream() + { + return std::cout; + } + + mgLog( std::string methodname ) : m_methodname( methodname ) + { + getStream() << m_methodname << " entered" << std::endl; + }; + + ~mgLog() + { + getStream() << m_methodname << " terminated" << std::endl; + } + + private: + + std::string m_methodname; + +}; + /* -------------------- begin CVS log --------------------------------- * $Log: mg_tools.h,v $ + * Revision 1.3 2004/05/28 15:29:18 lvw + * Merged player branch back on HEAD branch. + * + * Revision 1.2.2.2 2004/04/18 14:08:41 lvw + * Added some more logging/debugging code + * + * Revision 1.2.2.1 2004/04/09 16:14:48 lvw + * Added further code for logging/debugging. + * * Revision 1.2 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/muggle.c b/muggle.c index c8d1c76..86e5c9d 100644 --- a/muggle.c +++ b/muggle.c @@ -1,28 +1,33 @@ -/*******************************************************************/ -/*! \file muggle.c - * \brief Implements a plugin for browsing media libraries within VDR - ******************************************************************** - * \version $Revision: 1.6 $ - * \date $Date: 2004/02/23 16:18:15 $ +/*! + * \file muggle.c + * \brief Implements a plugin for browsing media libraries within VDR + * + * \version $Revision: 1.7 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author Responsible author: $Author: lvw $ + * + * $Id: muggle.c,v 1.7 2004/05/28 15:29:18 lvw Exp $ */ -/*******************************************************************/ -static const char *VERSION = "0.0.1"; -static const char *DESCRIPTION = "Access GiantDisc database contents"; -static const char *MAINMENUENTRY = "Muggle"; +#include #include "muggle.h" #include "vdr_menu.h" - +#include "vdr_setup.h" #include "mg_tools.h" #include "mg_content_interface.h" #include "mg_media.h" #include "i18n.h" +static const char *VERSION = "0.0.1"; +static const char *DESCRIPTION = "Media juggle plugin for VDR"; +static const char *MAINMENUENTRY = "Muggle"; + +using namespace std; + const char* mgMuggle::Version(void) { return VERSION; @@ -40,9 +45,14 @@ const char* mgMuggle::MainMenuEntry(void) mgMuggle::mgMuggle(void) { - // Initialize any member variables here. - // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL - // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! + // defaults for database arguments + the_setup.DbHost = "localhost"; + the_setup.DbPort = 0; + the_setup.DbName = "GiantDisc2"; + the_setup.DbUser = ""; + the_setup.DbPass = "" ; + the_setup.GdCompatibility = false; + the_setup.ToplevelDir = ""; } mgMuggle::~mgMuggle() @@ -53,12 +63,70 @@ mgMuggle::~mgMuggle() const char *mgMuggle::CommandLineHelp(void) { // Return a string that describes all known command line options. - return NULL; + return + " -h HHHH, --host=HHHH specify database host (default is localhost)\n" + " -n NNNN, --name=NNNN specify database name (overridden by -g)\n" + " -p PPPP, --port=PPPP specify port of database server (default is )\n" + " -u UUUU, --user=UUUU specify database user (default is )\n" + " -w WWWW, --password=WWWW specify database password (default is empty)\n" + " -t TTTT, --toplevel=TTTT specify toplevel directory for music\n" + " -g, --giantdisc enable full Giantdisc compatibility mode\n"; } bool mgMuggle::ProcessArgs(int argc, char *argv[]) { + cout << "mgMuggle::ProcessArgs" << endl << flush; + // Implement command line argument processing here if applicable. + static struct option long_options[] = + { + { "host", required_argument, NULL, 'h' }, + { "name", required_argument, NULL, 'n' }, + { "port", required_argument, NULL, 'p' }, + { "user", required_argument, NULL, 'u' }, + { "password", required_argument, NULL, 'w' }, + { "toplevel", required_argument, NULL, 't' }, + { "giantdisc", no_argument, NULL, 'g' }, + { NULL } + }; + + int c, option_index = 0; + while( ( c = getopt_long( argc, argv, "h:p:u:w:g:", long_options, &option_index ) ) != -1 ) + { + switch (c) + { + case 'h': + { + the_setup.DbHost = optarg; + } break; + case 'n': + { + the_setup.DbName = optarg; + } + case 'p': + { + the_setup.DbPort = atoi( optarg ); + } break; + case 'u': + { + the_setup.DbUser = optarg; + } break; + case 'w': + { + the_setup.DbPass = optarg; + } break; + case 't': + { + the_setup.ToplevelDir = optarg; + } break; + case 'g': + { + the_setup.GdCompatibility = true; + } break; + default: return false; + } + } + return true; } @@ -73,9 +141,9 @@ bool mgMuggle::Start(void) // Start any background activities the plugin shall perform. mgSetDebugLevel( 99 ); - RegisterI18n(Phrases); - m_media = new mgMedia( mgMedia::GD_MP3 ); - m_root = m_media->getSelectionRoot(); + RegisterI18n( Phrases ); + m_media = new mgMedia( mgMedia::GD_MP3 ); + m_root = m_media->getSelectionRoot(); m_playlist = m_media->createTemporaryPlaylist(); m_media->initFilterSet(); return true; @@ -96,14 +164,22 @@ cOsdObject *mgMuggle::MainMenuAction(void) cMenuSetupPage *mgMuggle::SetupMenu(void) { - // Return a setup menu in case the plugin supports one. - return NULL; + return new mgMenuSetup(); } bool mgMuggle::SetupParse(const char *Name, const char *Value) { - // Parse your own setup parameters and store their values. - return false; + if (!strcasecmp(Name, "InitLoopMode")) the_setup.InitLoopMode = atoi(Value); + else if (!strcasecmp(Name, "InitShuffleMode")) the_setup.InitShuffleMode = atoi(Value); + else if (!strcasecmp(Name, "AudioMode")) the_setup.AudioMode = atoi(Value); + else if (!strcasecmp(Name, "DisplayMode")) the_setup.DisplayMode = atoi(Value); + else if (!strcasecmp(Name, "BackgrMode")) the_setup.BackgrMode = atoi(Value); + else if (!strcasecmp(Name, "TargetLevel")) the_setup.TargetLevel = atoi(Value); + else if (!strcasecmp(Name, "LimiterLevel")) the_setup.LimiterLevel = atoi(Value); + else if (!strcasecmp(Name, "Only48kHz")) the_setup.Only48kHz = atoi(Value); + else return false; + + return true; } VDRPLUGINCREATOR(mgMuggle); // Don't touch this! diff --git a/muggle.h b/muggle.h index c5c0d04..957ce5e 100644 --- a/muggle.h +++ b/muggle.h @@ -1,24 +1,19 @@ -/*******************************************************************/ -/*! \file muggle.h - * \brief Implements a plugin for browsing media libraries within VDR - ******************************************************************** - * \version $Revision: 1.4 $ - * \date $Date: 2004/02/03 19:28:46 $ +/*! + * \file muggle.h + * \brief Implements a plugin for browsing media libraries within VDR + * + * \version $Revision: 1.5 $ + * \date $Date: 2004/05/28 15:29:18 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author: lvw $ + * + * $Id: muggle.h,v 1.5 2004/05/28 15:29:18 lvw Exp $ */ -/*******************************************************************/ #ifndef _MUGGLE_H #define _MUGGLE_H -#undef SHELL_TEST - -#ifdef SHELL_TEST - #include "shell_plugin.h" -#else - #include -#endif +#include class mgMedia; class mgSelectionTreeNode; diff --git a/vdr_config.h b/vdr_config.h new file mode 100644 index 0000000..6178a34 --- /dev/null +++ b/vdr_config.h @@ -0,0 +1,119 @@ +/*! + * \file vdr_menu.c + * \brief Implements menu handling for browsing media libraries within VDR + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_config.h,v 1.2 2004/05/28 15:29:18 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +// This files contains some option switches/values for compile time +// configuration of the MP3/MPlayer plugin. You should only alter +// this file, if you have understand the source code parts which deal +// with the changed value. + +// After changing this file you should do a "make plugins-clean ; make plugins" +// to recompile vdr. + +#ifndef ___CONFIG_H +#define ___CONFIG_H + +// The buffer size in bytes for the decoded audio data which is about to +// be send to the dvb driver. Should not be made to big, as this delays the +// pause function too much. +#define MP3BUFSIZE (100*1024) + +// The number of consecutive frame decoding error that may occure during +// decoding before aborting the file. +#define MAX_FRAME_ERR 10 + +// Defines the min. gain required to launch the normalizer. Don't bother normalizing +// songs which wouldn't change much in volumen. +#define MIN_GAIN 0.03 + +// Comment this out to disable the fast limiter function in the normalizer. The fast function +// uses a lookup table and is 12-14 times faster than the normal function, but less +// accurate (rel. error about 1e-7). +#define USE_FAST_LIMITER + +// Defines the filename extention to use for playlist files. +#define PLAYLISTEXT ".m3u" + +// Defines the text to identify WinAmp-Style playlists. +#define WINAMPEXT "#EXTM3U" + +// Comment this out, if you don't want to use mmap() to access the data files. +// Normaly there is no need to disable mmap(), as the code switches to normal +// file i/o if mmap() is not available for a specific file/filesystem. +#define USE_MMAP + +// Defines the max. memory size used for mmapping. This should not exceed the +// available free memory on your machine. +#define MAX_MMAP_SIZE (32*1024*1024) + +// The buffer size in bytes to use for normal file i/o if a file cannot be +// mmap()ed. If set to large on slow media, may cause audio drop outs as the +// audio buffer may underrun while filling this buffer. +#define MP3FILE_BUFSIZE (32*1024) + +// The filename to save the cached id3 information. The file is located in the +// video directory and this definition must not contain any path (no "/" ) +#define CACHEFILENAME "id3info.cache" + +// The interval in seconds in which the id3 cache is saved to disk (only +// if any changes occured). +#define CACHESAVETIMEOUT 120 + +// How many days to keep unused entries in the id3 cache. Unused entries from +// undefined mp3sources are purged after timeout/10 days. +#define CACHEPURGETIMEOUT 120 + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +// Defines the timeout in seconds for functions which use a single key +// (e.g. openning the playlist window). If the key is repressed during +// the timeout, the secondary function is activated. +#define MULTI_TIMEOUT 3 + +// Defines the timeout in ms for entering the single digits in direct song +// selection. +#define SELECT_TIMEOUT 1000 + +// If the progress display is closed on direct song selection, the display +// is opend temporarily. This defines the time in seconds after the display +// is closed again. +#define SELECTHIDE_TIMEOUT 3 + +// Defines the time in seconds to jump inside a song with left/right. +#define JUMPSIZE 3 + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +// DEBUGGING OPTIONS +// (not needed for normal operation) + +// Uncomment to enable generic debugging messages to the console. This may slow +// down operation in some cases. +#define DEBUG + +// Uncomment to disable audio output to the dvb driver. The audio data is +// simply discarded. +//#define DUMMY_OUTPUT + +// Uncomment to enable dumping the normalize limiter lookup table to the file +// "/tmp/limiter". The generated file will be about 3MB in size. This option shouldn't +// be enabled for day-by-day operation. +//#define ACC_DUMP + +#endif //___CONFIG_H diff --git a/vdr_decoder.c b/vdr_decoder.c new file mode 100644 index 0000000..8b4cf70 --- /dev/null +++ b/vdr_decoder.c @@ -0,0 +1,137 @@ +/*! + * \file vdr_decoder.h + * \brief A generic decoder for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_decoder.c,v 1.2 2004/05/28 15:29:18 lvw Exp $ + * + * Adapted from: + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001-2003 Stefan Huelswitt + */ + +#include +#include +#include +#include +#include + +#include +#include + +// #include "common.h" +// #include "data-mp3.h" +// #include "decoder-core.h" +// #include "decoder-mp3-stream.h" +// #include "decoder-snd.h" +// #include "decoder-ogg.h" + +#include "vdr_decoder.h" +#include "vdr_decoder_mp3.h" + +using namespace std; + +// --- mgDecoders --------------------------------------------------------------- + +mgMediaType mgDecoders::getMediaType( string s ) +{ + // TODO: currently handles only mp3. LVW + return MT_MP3; +} + +mgDecoder *mgDecoders::findDecoder( string filename ) +{ + mgDecoder *decoder = 0; + + switch( getMediaType( filename ) ) + { + case MT_MP3: + { + // prepend filename with path to music library?? TODO + printf( "mp3 file detected, launching mgMP3Decoder\n" ); + decoder = new mgMP3Decoder(filename); + } break; + + /* + case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; + #ifdef HAVE_SNDFILE + case MT_SND: decoder = new cSndDecoder(full); break; + #endif + #ifdef HAVE_VORBISFILE + case MT_OGG: decoder = new mgOggDecoder(full); break; + #endif + */ + default: + { + esyslog("ERROR: unknown media type" ); + } break; + } + + if( decoder && !decoder->valid() ) + { + // no decoder found or decoder doesn't match + + delete decoder; // might be carried out on NULL pointer! + decoder = 0; + + esyslog("ERROR: no valid decoder found for %s", filename.c_str() ); + } + return decoder; +} + +// --- mgDecoder ---------------------------------------------------------------- + +mgDecoder::mgDecoder(string filename) +{ + m_filename = filename; + m_locked = 0; + m_urgentLock = false; + m_playing = false; +} + +mgDecoder::~mgDecoder() +{ +} + +void mgDecoder::lock(bool urgent) +{ + m_locklock.Lock(); + + if( urgent && m_locked ) + { + m_urgentLock = true; // signal other locks to release quickly + } + m_locked ++; + + m_locklock.Unlock(); // don't hold the "locklock" when locking "lock", may cause a deadlock + m_lock.Lock(); + m_urgentLock = false; +} + +void mgDecoder::unlock(void) +{ + m_locklock.Lock(); + + m_locked--; + + m_lock.Unlock(); + m_locklock.Unlock(); +} + +bool mgDecoder::tryLock(void) +{ + bool res = false; + m_locklock.Lock(); + + if( !m_locked && !m_playing ) + { + lock(); + res = true; + } + m_locklock.Unlock(); + return res; +} diff --git a/vdr_decoder.h b/vdr_decoder.h new file mode 100644 index 0000000..74b200e --- /dev/null +++ b/vdr_decoder.h @@ -0,0 +1,158 @@ +/*! + * \file vdr_decoder.h + * \brief A generic decoder for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:18 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_decoder.h,v 1.2 2004/05/28 15:29:18 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#ifndef ___DECODER_H +#define ___DECODER_H + +#include + +#include +#include + +#define DEC_ID(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d)) + +// --------From decoder_core.h ------------------------------------ + +/*! + * \brief The current status of the decoder + */ +enum eDecodeStatus + { + dsOK=0, dsPlay, dsSkip, dsEof, dsError, dsSoftError + }; + +// ---------------------------------------------------------------- + +/*! + * \brief A data structure to put decoded PCM data + */ +struct mgDecode +{ + eDecodeStatus status; + int index; + struct mad_pcm *pcm; +}; + +// ---------------------------------------------------------------- + +/*! + * \brief Information about ??? + */ +class mgPlayInfo +{ +public: + int m_index, m_total; +}; + +// ---------------------------------------------------------------- + +/*! + * \brief Media types + */ +enum mgMediaType +{ + MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC +}; + +/*! + * \brief A generic decoder class to handle conversion into PCM format + */ +class mgDecoder +{ +protected: + + /*! \brief The currently playing file */ + std::string m_filename; + + /*! \brief Mutexes to coordinate threads */ + cMutex m_lock, m_locklock; + int m_locked; + bool m_urgentLock; + + /*! \brief Whether the decoder is currently active */ + bool m_playing; + + /*! \brief ??? */ + mgPlayInfo m_playinfo; + + /*! \brief Place a lock */ + virtual void lock( bool urgent = false ); + + /*! \brief Release a lock */ + virtual void unlock(void); + + /*! \brief Try to obtain a lock */ + virtual bool tryLock(void); + +public: + + /*! \brief The constructor */ + mgDecoder( std::string filename ); + + /*! \brief The destructor */ + virtual ~mgDecoder(); + + /*! \brief Whether a decoder instance is able to play the given file */ + virtual bool valid() = 0; + + /*! \brief Whether a stream (i.e. from the network is being decoded */ + virtual bool isStream() + { + return false; + } + + /*! \brief Start decoding */ + virtual bool start() = 0; + + /*! \brief Stop decoding */ + virtual bool stop() = 0; + + /*! \brief Skip an amount of time. Impossible by default */ + virtual bool skip( int seconds, int avail, int rate) + { + return false; + } + + /*! \brief Return decoded data */ + virtual struct mgDecode *decode() = 0; + + /*! \brief Information about the current playback status */ + virtual mgPlayInfo *playInfo() + { + return 0; + } +}; + +// ---------------------------------------------------------------- + +/*! + * \brief A generic decoder manager class to handle different decoders + */ +class mgDecoders +{ +public: + + /*! \brief Try to find a valid decoder for a file + */ + static mgDecoder *findDecoder( std::string filename ); + + /*! \brief determine the media type for a given source + */ + static mgMediaType getMediaType( std::string filename ); + +}; + +#endif //___DECODER_H diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c new file mode 100644 index 0000000..436d7b6 --- /dev/null +++ b/vdr_decoder_mp3.c @@ -0,0 +1,450 @@ +/*! + * \file vdr_decoder_mp3.h + * \brief An mp3 decoder for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_decoder_mp3.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vdr_config.h" +#include "vdr_decoder_mp3.h" +#include "vdr_stream.h" + +#include "mg_tools.h" + +#define d(x) x + +using namespace std; + +// ---------------------------------------------------------------- + +int mgMadStream(struct mad_stream *stream, mgStream *str) +{ + unsigned char *data; + unsigned long len; + if( str->stream( data, len, stream->next_frame ) ) + { + if( len > 0 ) + { + mad_stream_buffer(stream, data, len); + } + return len; + } + return -1; +} + +// --- mgMP3Decoder ------------------------------------------------------------- + +mgMP3Decoder::mgMP3Decoder(string filename, bool preinit) + : mgDecoder(filename) +{ + MGLOG( "mgMP3Decoder::mgMP3Decoder" ); + + m_stream = 0; + m_isStream = false; + + if( preinit ) + { + m_stream = new mgStream( filename ); + } + m_madframe = 0; + m_madsynth = 0; + + memset( &m_madstream, 0, sizeof(m_madstream) ); + + init(); +} + +mgMP3Decoder::~mgMP3Decoder() +{ + MGLOG( "mgMP3Decoder::~mgMP3Decoder" ); + + clean(); + delete m_stream; +} + +void mgMP3Decoder::init() +{ + MGLOG( "mgMP3Decoder::init" ); + + clean(); + mad_stream_init( &m_madstream ); + + m_madframe = new mad_frame; + mad_frame_init( m_madframe ); + + m_madsynth = new mad_synth; + mad_synth_init( m_madsynth ); + + mad_stream_options( &m_madstream, MAD_OPTION_IGNORECRC); + + m_playtime = mad_timer_zero; + m_skiptime = mad_timer_zero; + m_framenum = m_framemax = 0; + m_mute = m_errcount = 0; +} + +void mgMP3Decoder::clean() +{ + MGLOG( "mgMP3Decoder::clean" ); + + m_playing = false; + if( m_madsynth ) + { + mad_synth_finish( m_madsynth ); + delete m_madsynth; + m_madsynth = 0; + } + + if( m_madframe ) + { + mad_frame_finish( m_madframe ); + delete m_madframe; + m_madframe = 0; + } + mad_stream_finish( &m_madstream ); + + /* + if( m_frameinfo ) + { + cout << "mgMP3Decoder::clean: m_frameinfo deleted." << endl << flush; + delete m_frameinfo; + cout << "mgMP3Decoder::clean: m_frameinfo deleted." << endl << flush; + m_frameinfo = 0; + } + */ +} + +bool mgMP3Decoder::valid(void) +{ + MGLOG( "mgMP3Decoder::valid" ); + + bool res = false; + if( tryLock() ) + { + if( start() ) + { + struct mgDecode *dd; + int count = 10; + do + { + dd = decode(); + if( dd->status == dsEof ) + { + count=0; + } + if( dd->status != dsPlay) + { + break; + } + } while( --count ); + if( !count ) + { + res=true; + } + stop(); + } + unlock(); + } + return res; +} + +mgPlayInfo *mgMP3Decoder::playInfo() +{ + MGLOG( "mgMP3Decoder::playInfo" ); + if( m_playing ) + { + m_playinfo.m_index = mad_timer_count( m_playtime, MAD_UNITS_SECONDS ); + // m_playinfo.Total = m_scan->Total; // TODO + + return &m_playinfo; + } + return 0; +} + +bool mgMP3Decoder::start() +{ + MGLOG( "mgMP3Decoder::start" ); + + lock(true); + init(); + m_playing = true; + + if( m_stream->open(true) ) + { + if(!m_isStream) + { + m_stream->seek(); + + printf( "mgMP3Decoder::start: m_framemax not determined, rewinding disabled!!!\n" ); + + /* m_framemax = scan->Frames+20; // TODO + + m_frameinfo = new struct FrameInfo[m_framemax]; + if(!m_frameinfo) + { + printf( "mgMP3Decoder::start: no memory for frame index, rewinding disabled" ); + } + */ + } + unlock(); + + printf( "mgMP3Decoder::start: true\n" ); + return true; + } + m_stream->close(); + clean(); + unlock(); + + printf( "mgMP3Decoder::start: false" ); + return false; +} + +bool mgMP3Decoder::stop(void) +{ + MGLOG( "mgMP3Decoder::stop" ); + + lock(); + + if( m_playing ) + { + m_stream->close(); + clean(); + } + unlock(); + return true; +} + +struct mgDecode *mgMP3Decoder::done(eDecodeStatus status) +{ + // MGLOG( "mgMP3Decoder::done" ); + + m_ds.status = status; + m_ds.index = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS ); + m_ds.pcm = &m_madsynth->pcm; + unlock(); // release the lock from Decode() + + return &m_ds; +} + +eDecodeStatus mgMP3Decoder::decodeError(bool hdr) +{ + MGLOG( "mgMP3Decoder::decodeError" ); + + if( m_madstream.error == MAD_ERROR_BUFLEN || m_madstream.error == MAD_ERROR_BUFPTR ) + { + int s = mgMadStream( &m_madstream, m_stream ); + if( s < 0 ) + { + printf( "mgMP3Decoder::decodeError: dsError returned\n" ); + return dsError; + } + if( s == 0 ) + { + printf( "mgMP3Decoder::decodeError: dsEof returned\n" ); + return dsEof; + } + } + else if( !MAD_RECOVERABLE( m_madstream.error ) ) + { + printf( "mgMP3Decoder::decodeError: mad decode %sfailed, frame=%d: %s. Returning dsError\n", + hdr? "hdr " : "", m_framenum, + mad_stream_errorstr( &m_madstream ) ); + return dsError; + } + else + { + m_errcount += hdr? 1: 100; + printf( "mgMP3Decoder::decodeError: mad decode %s error, frame=%d count=%d: %s. Returning dsOK\n", + hdr? "hdr ": "", m_framenum, m_errcount, + mad_stream_errorstr( &m_madstream ) ); + } + return dsOK; +} + +struct mgDecode *mgMP3Decoder::decode() +{ + // MGLOG( "mgMP3Decoder::decode" ); + + lock(); // this is released in Done() + eDecodeStatus r; + + while( m_playing ) + { + if( m_errcount >= MAX_FRAME_ERR*100 ) + { + printf( "mgMP3Decoder::decode: excessive decoding errors," + " aborting file %s\n", m_filename.c_str() ); + return done(dsError); + } + + if( mad_header_decode( &m_madframe->header, &m_madstream) == -1) + { + if( (r = decodeError(true) ) ) + { + return done(r); + } + } + else + { + if(!m_isStream) + { +#ifdef DEBUG2 + if( m_framenum >= m_framemax ) + { + printf( "mgMP3Decoder::start: framenum >= framemax!!!!\n" ); + } +#endif + if( m_frameinfo && m_framenum < m_framemax ) + { + m_frameinfo[m_framenum].Pos = + m_stream->bufferPos() + + ( m_madstream.this_frame - m_madstream.buffer ); + m_frameinfo[m_framenum].Time = m_playtime; + } + } + + mad_timer_add( &m_playtime, m_madframe->header.duration); + m_framenum ++; + + if( mad_timer_compare(m_playtime, m_skiptime) >= 0 ) + { + m_skiptime = mad_timer_zero; + } + else + { + return done(dsSkip); // skipping, decode next header + } + + if( mad_frame_decode( m_madframe, &m_madstream ) == -1 ) + { + if( ( r = decodeError(false) ) ) + { + return done(r); + } + } + else + { + m_errcount = 0; + + // TODO: // m_scan->InfoHook( &frame->header ); + + mad_synth_frame( m_madsynth, m_madframe); + + if( m_mute ) + { + m_mute--; + return done( dsSkip ); + } + return done( dsPlay ); + } + } + } + return done( dsError ); +} + +void mgMP3Decoder::makeSkipTime( mad_timer_t *skiptime, + mad_timer_t playtime, + int secs, int avail, int dvbrate) +{ + mad_timer_t time; + + *skiptime = playtime; + + mad_timer_set( &time, abs(secs), 0, 0 ); + + if( secs < 0 ) + { + mad_timer_negate(&time); + } + + mad_timer_add( skiptime, time ); + + float bufsecs = (float)avail / (float)(dvbrate * (16/8 * 2)); // Byte/s = samplerate * 16 bit * 2 chan + + printf( "mgMP3Decoder::makeSkipTime: skip: avail=%d bufsecs=%f\n", avail, bufsecs ); + + int full = (int)bufsecs; + bufsecs -= (float)full; + + mad_timer_set( &time, full, (int)(bufsecs*1000.0), 1000); + + mad_timer_negate(&time); + + mad_timer_add( skiptime, time ); + + printf( "mgMP3Decoder::makeSkipTime: skip: playtime=%ld secs=%d full=%d bufsecs=%f skiptime=%ld\n", + mad_timer_count(playtime,MAD_UNITS_MILLISECONDS), + secs, full, bufsecs, + mad_timer_count(*skiptime,MAD_UNITS_MILLISECONDS ) ); +} + +bool mgMP3Decoder::skip( int seconds, int avail, int rate ) +{ + lock(); + + bool res = false; + if( m_playing && !m_isStream ) + { + if( !mad_timer_compare( m_skiptime, mad_timer_zero ) ) + { // allow only one skip at any time + mad_timer_t time; + makeSkipTime( &time, m_playtime, seconds, avail, rate); + + if( mad_timer_compare( m_playtime, time ) <= 0 ) + { // forward skip +#ifdef DEBUG + int i = mad_timer_count( time, MAD_UNITS_SECONDS ); + printf( "mgMP3Decoder::skip: forward skipping to %02d:%02d\n", i/60, i%60 ); +#endif + m_skiptime = time; + m_mute=1; + res = true; + } + else + { // backward skip + if( m_frameinfo ) + { +#ifdef DEBUG + int i = mad_timer_count( time, MAD_UNITS_SECONDS ); + printf( "mgMP3Decoder::skip: rewinding to %02d:%02d\n", i/60, i%60 ); +#endif + while( m_framenum && mad_timer_compare( time, m_frameinfo[--m_framenum].Time) < 0) ; + m_mute = 2; + if( m_framenum >= 2) + { + m_framenum-=2; + } + m_playtime = m_frameinfo[m_framenum].Time; + m_stream->seek( m_frameinfo[m_framenum].Pos ); + mad_stream_finish( &m_madstream ); // reset stream buffer + mad_stream_init( &m_madstream ); +#ifdef DEBUG + i = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS ); + printf( "mgMP3Decoder::skip: new playtime=%d framenum=%d filepos=%lld\n", i, m_framenum, m_frameinfo[m_framenum].Pos); +#endif + res = true; + } + } + } + } + unlock(); + return res; +} diff --git a/vdr_decoder_mp3.h b/vdr_decoder_mp3.h new file mode 100644 index 0000000..abc8eed --- /dev/null +++ b/vdr_decoder_mp3.h @@ -0,0 +1,114 @@ +/*! + * \file vdr_decoder_mp3.h + * \brief An mp3 decoder for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_decoder_mp3.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#ifndef ___DECODER_MP3_H +#define ___DECODER_MP3_H + +#define DEC_MP3 DEC_ID('M','P','3',' ') +#define DEC_MP3_STR "MP3" + +#include +#include + +#include "vdr_decoder.h" + +#if MAD_F_FRACBITS != 28 +#warning libmad with MAD_F_FRACBITS != 28 not tested +#endif + +class mgStream; + +// ---------------------------------------------------------------- + +/*! + * \brief A class to decode mp3 songs into PCM using libmad + */ +class mgMP3Decoder : public mgDecoder +{ +private: + struct mgDecode m_ds; + + // + struct mad_stream m_madstream; + struct mad_frame *m_madframe; + struct mad_synth *m_madsynth; + mad_timer_t m_playtime, m_skiptime; + + // + struct FrameInfo + { + unsigned long long Pos; + mad_timer_t Time; + } *m_frameinfo; + + int m_framenum, m_framemax, m_errcount, m_mute; + // + void init(); + + void clean(); + + struct mgDecode *done( eDecodeStatus status ); + + virtual mgPlayInfo *playInfo(); + + eDecodeStatus decodeError(bool hdr); + + void makeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, + int secs, int avail, int dvbrate); + +protected: + mgStream *m_stream; + bool m_isStream; + +public: + + /*! + * \brief construct a decoder from a filename + */ + mgMP3Decoder( std::string filename, bool preinit = true ); + + /*! + * \brief the destructor + */ + virtual ~mgMP3Decoder(); + + /*! + * \brief check, whether the file contains useable MP3 content + */ + virtual bool valid(); + + /*! + * \brief start the decoding process + */ + virtual bool start(); + + /*! + * \brief stop the decoding process + */ + virtual bool stop(); + + /*! + * \brief skip an amount of seconds + */ + virtual bool skip( int seconds, int avail, int rate ); + + /*! + * \brief the actual decoding function (uses libmad) + */ + virtual struct mgDecode *decode(); +}; + +#endif //___DECODER_MP3_H diff --git a/vdr_menu.c b/vdr_menu.c index 8d4cf06..dcc442f 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -1,34 +1,36 @@ -/*******************************************************************/ -/*! \file vdr_menu.c - * \brief Implements menu handling for browsing media libraries within VDR - ******************************************************************** - * \version $Revision: 1.19 $ - * \date $Date: 2004/02/23 17:03:24 $ +/*! + * \file vdr_menu.c + * \brief Implements menu handling for browsing media libraries within VDR + * + * \version $Revision: 1.20 $ + * \date $Date: 2004/05/28 15:29:19 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ + * \author Responsible author: $Author: lvw $ * - * $Id: vdr_menu.c,v 1.19 2004/02/23 17:03:24 RaK Exp $ + * $Id: vdr_menu.c,v 1.20 2004/05/28 15:29:19 lvw Exp $ */ -/*******************************************************************/ + +#include +#include + +#include #include #include -#include #include "vdr_menu.h" +#include "vdr_player.h" +#include "i18n.h" #include "mg_content_interface.h" +#include "mg_playlist.h" +#define DEBUG #include "mg_tools.h" #include "mg_media.h" #include "mg_filters.h" #include "gd_content_interface.h" -#include "i18n.h" - -#include -#include - using namespace std; // ----------------------- mgMenuTreeItem ------------------ @@ -51,9 +53,6 @@ void mgMenuTreeItem::Set() SetText( buffer, false ); } -// ----------------------- mgMenuTrackItem ------------------ - - // ----------------------- mgMainMenu ---------------------- mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *playlist) @@ -64,28 +63,6 @@ mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *pl SetTitle( tr("Muggle Media Database") ); SetButtons(); - m_filtername = new char[32]; - strcpy( m_filtername, "none" ); - - m_title = new char[32]; - strcpy( m_title, "none" ); - - m_interpret = new char[32]; - strcpy( m_interpret, "none" ); - - m_album = new char[32]; - strcpy( m_album, "none" ); - - m_playlist = new char[32]; - strcpy( m_playlist, "none" ); - - m_year_min = 1900; - m_year_max = 2100; - - m_filter = 0; - - m_tracklist = NULL; - DisplayTree( m_root ); } @@ -110,17 +87,17 @@ void mgMainMenu::SetButtons( ) { SetHelp( tr("Add"), tr("Cycle tree"), tr("Playlist"), tr("Submenu") ); } - else if( m_state == PLAYLIST ) + else if( m_state == TREE_SUBMENU ) { - SetHelp( tr("Edit PL"), tr("Track info"), tr("Filter"), tr("Submenu") ); + SetHelp( tr("Instant Play"), tr("2"), tr("3"), tr("Mainmenu") ); } - else if( m_state == PLAYLIST_TRACKINFO ) + else if( m_state == PLAYLIST ) { - SetHelp( tr("Edit PL?"), tr("Album info"), tr("Filter"), tr("Submenu") ); + SetHelp( tr("Play"), tr("Move"), tr("Filter"), tr("Submenu") ); } - else if( m_state == PLAYLIST_ALBUMINFO ) + else if( m_state == PLAYLIST_SUBMENU ) { - SetHelp( tr("Edit PL?"), tr("Playlist"), tr("Filter"), tr("Submenu") ); + SetHelp( tr("Load"), tr("Save"), tr("Delete"), tr("Mainmenu") ); } else if( m_state == FILTER ) { @@ -132,9 +109,17 @@ void mgMainMenu::SetButtons( ) } } +void mgMainMenu::Move( int from, int to ) +{ + m_current_playlist->move( from, to ); + + cOsdMenu::Move( from, to ); + Display(); +} + eOSState mgMainMenu::ProcessKey(eKeys key) { - mgDebug( 1, "mgMainMenu::ProcessKey" ); + MGLOG( "mgMainMenu::ProcessKey" ); eOSState state = cOsdMenu::ProcessKey(key); if( m_state == TREE ) @@ -169,10 +154,10 @@ eOSState mgMainMenu::ProcessKey(eKeys key) if( tracks ) { - m_current_playlist->appendList(tracks); char buffer[256]; sprintf( buffer, "%d tracks sent to current playlist", (int) tracks->size() ); + m_current_playlist->appendList(tracks); Interface->Status( buffer ); Interface->Flush(); } @@ -201,8 +186,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } break; case kBlue: { - mgDebug( 1, "mgMainMenu: submenu (todo)" ); - + m_last_osd_index = Current(); DisplayTreeSubmenu(); state = osContinue; @@ -248,18 +232,25 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { case k0 ... k9: { - int n = key - k0; - + int n = key - k0; TreeSubmenuAction( n ); state = osContinue; - } - case kOk: + } break; + case kBlue: { - TreeSubmenuAction( Current() ); + m_state = TREE; + // restore last selected entry + int last = m_history.back(); + DisplayTree( m_node, last ); + state = osContinue; } break; + case kOk: + { + state = TreeSubmenuAction( Current() ); + } break; default: { state = osContinue; @@ -268,11 +259,10 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } else if( state == osBack ) { - mgDebug( 1, "mgMainMenu: return from tree view submenu" ); - + m_state = TREE; + // restore last selected entry int last = m_history.back(); - DisplayTree( m_node, last ); state = osContinue; @@ -280,65 +270,91 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } else if( m_state == PLAYLIST ) { - mgDebug( 1, "mgMainMenu: in state PLAYLIST" ); if( state == osUnknown ) { switch( key ) { case kOk: { - mgDebug( 1, "mgMainMenu: playlist ok" ); + // show some more information? state = osContinue; } break; case kRed: { - mgDebug( 1, "mgMainMenu: edit playlist" ); - Mark(); // Mark (to move), moving done by VDR, calls Move - } + // TODO: what happens if the user presses play and the player is already active? + Play( m_current_playlist ); + state = osEnd; + } break; case kGreen: { - if( m_state == PLAYLIST ) - { - mgDebug( 1, "mgMainMenu: switch to TrackInfo" ); - DisplayTrackInfo(); - } - else if( m_state == PLAYLIST_TRACKINFO ) - { - mgDebug( 1, "mgMainMenu: switch to AlbumInfo" ); - DisplayAlbumInfo(); - } - else if( m_state == PLAYLIST_ALBUMINFO ) - { - mgDebug( 1, "mgMainMenu: switch to Playlist" ); - DisplayPlaylist(); - } + Mark(); + state = osContinue; } break; case kYellow: { - mgDebug( 1, "mgMainMenu: cycle playlist to filter" ); DisplayFilter(); state = osContinue; } break; case kBlue: { // Submenu - mgDebug( 1, "mgMainMenu: playlist submenu (todo)" ); - + m_last_osd_index = Current(); + DisplayPlaylistSubmenu(); + state = osContinue; } break; default: { - mgDebug( 1, "mgMainMenu: default" ); state = osContinue; }; } } } + else if( m_state == PLAYLIST_SUBMENU ) + { + if( state == osUnknown ) + { + switch( key ) + { + case k0 ... k9: + { + int n = key - k0; + PlaylistSubmenuAction( n ); + + state = osContinue; + } break; + case kYellow: + { + PlaylistSubmenuAction( 3 ); + } break; + case kBlue: + { + m_state = PLAYLIST; + DisplayPlaylist( m_last_osd_index ); + + state = osContinue; + } break; + case kOk: + { + state = PlaylistSubmenuAction( Current() ); + } break; + default: + { + state = osContinue; + } break; + } + } + else if( state == osBack ) + { + m_state = PLAYLIST; + DisplayPlaylist( m_last_osd_index ); + + state = osContinue; + } + } else if( m_state == FILTER ) { - mgDebug( 1, "mgMainMenu: in state FILTER" ); - if( state == osUnknown ) { switch( key ) @@ -399,8 +415,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) else { mgDebug(1, "Process key: else"); - mgDebug(1, "Process key: %d", (int) state); - + mgDebug(1, "Process key: %d", (int) state); } return state; @@ -461,22 +476,43 @@ void mgMainMenu::DisplayTreeSubmenu() SetTitle( strcat("Muggle - ",tr("Tree View Commands") ) ); // Add items - Add( new cOsdItem( "1 - Test1" ) ); + Add( new cOsdItem( "1 - Instant play" ) ); Add( new cOsdItem( "9 - Test9" ) ); Add( new cOsdItem( "0 - Test0" ) ); Display(); } -void mgMainMenu::TreeSubmenuAction( int n ) +eOSState mgMainMenu::TreeSubmenuAction( int n ) { mgDebug( "mgMainMenu: TreeSubmenuAction( %d )", n ); + eOSState state = osContinue; switch( n ) { case 0: { - // action 0 + // action 0: instant play of current node, might need a security question + + mgSelectionTreeNode *current = CurrentNode(); + if( current ) + { + // clear playlist + m_current_playlist->clear(); + + // append current node + vector *tracks = current->getTracks(); + + if( tracks ) + { + m_current_playlist->appendList( tracks ); + + // play + Play( m_current_playlist ); + + state = osEnd; + } + } } break; case 1: { @@ -487,16 +523,16 @@ void mgMainMenu::TreeSubmenuAction( int n ) // undefined action } break; } + + return state; } -void mgMainMenu::DisplayPlaylist() +void mgMainMenu::DisplayPlaylist( int index_current ) { m_state = PLAYLIST; - mgDebug( 1, "mgBrowseMenu::DisplayPlaylist"); // make sure we have a current playlist Clear(); - SetButtons(); vector* list = m_current_playlist-> getAll(); @@ -506,68 +542,109 @@ void mgMainMenu::DisplayPlaylist() tr("items") ); SetTitle( titlestr ); - mgDebug( 1, "mgBrowseMenu::DisplayPlaylist: %d elements received", - list->size() ); - for( unsigned int i = 0; i < m_current_playlist->getNumItems(); i++) { string label = m_current_playlist->getLabel( i, " " ); Add( new cOsdItem( label.c_str() ) ); } + + if( index_current >= 0 ) + { + cOsdItem *item = Get( m_last_osd_index ); + SetCurrent( item ); + RefreshCurrent(); + DisplayCurrent( true ); + } Display(); } -void mgMainMenu::DisplayTrackInfo() +void mgMainMenu::DisplayPlaylistSubmenu() { - m_state = PLAYLIST_TRACKINFO; - SetButtons(); - - // show info of the currently playing track -} + m_state = PLAYLIST_SUBMENU; -void mgMainMenu::DisplayAlbumInfo() -{ - m_state = PLAYLIST_ALBUMINFO; + Clear(); SetButtons(); + SetTitle( "Muggle - Playlist View Commands" ); - // show info of the currently playing album + // Add items + Add( new cOsdItem( "1 - Load playlist" ) ); + Add( new cOsdItem( "2 - Save playlist" ) ); + Add( new cOsdItem( "3 - Clear playlist" ) ); + Add( new cOsdItem( "4 - Remove entry from list" ) ); + + Display(); } -void mgMainMenu::Move( int from, int to ) +eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { - // check current view, perform move in the content view - if( m_state == PLAYLIST ) + cout << "mgMainMenu::PlaylistSubmenuAction: " << n << endl << flush; + eOSState state = osContinue; + + switch( n ) { - // resort + case 0: + { + Interface->Status( "Load not yet implemented" ); + Interface->Flush(); + } break; + case 1: + { + Interface->Status( "Save not yet implemented" ); + Interface->Flush(); + } break; + case 2: + { // clear playlist + m_current_playlist->clear(); + + // confirmation + Interface->Status( "Playlist cleared" ); + Interface->Flush(); + + state = osContinue; + } break; + case 3: + { // remove selected title + m_current_playlist->remove( m_last_osd_index ); + if( m_last_osd_index > 0 ) + { + m_last_osd_index --; + } + DisplayPlaylist( m_last_osd_index ); + + // confirmation + Interface->Status( "Entry removed" ); + Interface->Flush(); + } + default: + { + // undefined action + } break; } - // what now? + return state; } void mgMainMenu::DisplayFilter() { m_state = FILTER; - Clear(); - - mgDebug( 1, "Creating Muggle filter view" ); + + Clear(); SetButtons(); SetTitle( m_media->getActiveFilterTitle().c_str() ); - mgDebug( 1, "filter view2" ); - vector *filter_list = m_media->getActiveFilters(); - mgDebug( 1, "filter view3" ); int i=0; for( vector::iterator iter = filter_list->begin(); iter != filter_list->end(); iter ++ ) { - mgDebug( 1, "Filter %d/%dint filter %s='%s'", - i, filter_list->size(), - (*iter)->getName(), - (*iter)->getStrVal().c_str()); + mgDebug( 1, "Filter %d/%dint filter %s='%s'", + i, filter_list->size(), + (*iter)->getName(), + (*iter)->getStrVal().c_str()); + switch( (*iter)->getType() ) { case mgFilter::INT: @@ -637,9 +714,29 @@ void mgMainMenu::DisplayFilterSelector() // show available filters, load on OK? } +void mgMainMenu::Play(mgPlaylist *plist) +{ + MGLOG( "mgMainMenu::Play" ); + cControl *control = cControl::Control(); + + if( control && typeid(*control) == typeid(mgPlayerControl) ) + { // is there a running MP3 player? + cout << "mgMainMenu::Play: signal new playlist to existing control" << endl; + static_cast(control)->NewPlaylist(plist); // signal the running player to load the new playlist + } + else + { + cout << "mgMainMenu::Play: starting new control" << endl; + cControl::Launch( new mgPlayerControl(plist) ); + } +} + /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.20 2004/05/28 15:29:19 lvw + * Merged player branch back on HEAD branch. + * * Revision 1.19 2004/02/23 17:03:24 RaK * - error in filter view while trying to switch or using the colour keys * workaround: first filter criteria is inttype. than it works, dont ask why ;-( @@ -661,6 +758,42 @@ void mgMainMenu::DisplayFilterSelector() * Revision 1.14 2004/02/12 09:08:48 LarsAC * Added handling of filter choices (untested) * + * Revision 1.13.2.12 2004/05/27 07:58:38 lvw + * Removed bugs in moving and removing tracks from playlists + * + * Revision 1.13.2.11 2004/05/26 14:31:04 lvw + * Added submenu for playlist view + * + * Revision 1.13.2.10 2004/05/25 21:58:45 lvw + * Handle submenus for views + * + * Revision 1.13.2.9 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.13.2.8 2004/05/11 06:35:16 lvw + * Added debugging while hunting stop bug. + * + * Revision 1.13.2.7 2004/05/04 16:51:53 lvw + * Debugging aids added. + * + * Revision 1.13.2.6 2004/04/29 06:48:21 lvw + * Output statements to aid debugging added + * + * Revision 1.13.2.5 2004/04/25 18:44:07 lvw + * Removed bugs in menu handling + * + * Revision 1.13.2.4 2004/03/14 12:30:56 lvw + * Menu now calls player + * + * Revision 1.13.2.3 2004/03/11 07:22:32 lvw + * Added setup menu + * + * Revision 1.13.2.2 2004/03/08 22:28:40 lvw + * Added documentation headers. + * + * Revision 1.13.2.1 2004/03/02 07:07:27 lvw + * Initial adaptations from MP3 plugin added (untested) + * * Revision 1.13 2004/02/09 19:27:52 MountainMan * filter set implemented * diff --git a/vdr_menu.h b/vdr_menu.h index e0ef30b..a4a1f7c 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -1,30 +1,23 @@ -/*******************************************************************/ -/*! \file vdr_menu.h - * \brief Implements menu handling for broswing media libraries within VDR - ******************************************************************** - * \version $Revision: 1.9 $ - * \date $Date: 2004/02/23 15:41:21 $ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: RaK $ +/*! + * \file vdr_menu.h + * \brief Implements menu handling for broswing media libraries within VDR * - * $Id: vdr_menu.h,v 1.9 2004/02/23 15:41:21 RaK Exp $ + * \version $Revision: 1.10 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ * + * $Id: vdr_menu.h,v 1.10 2004/05/28 15:29:19 lvw Exp $ */ -/*******************************************************************/ #ifndef _VDR_MENU_H #define _VDR_MENU_H -#undef SHELL_TEST +#include -#ifdef SHELL_TEST - #include "myosd.h" - #include "mymenuitems.h" -#else - #include -#endif +#include -#include +#include "i18n.h" #include "i18n.h" @@ -33,6 +26,9 @@ class mgSelectionTreeNode; class mgPlaylist; class mgTracklist; +/*! + * \brief a special menu item + */ class mgMenuTreeItem : public cOsdItem { public: @@ -49,6 +45,9 @@ class mgMenuTreeItem : public cOsdItem }; +/*! + * \brief the muggle main OSD + */ class mgMainMenu : public cOsdMenu { public: @@ -59,50 +58,51 @@ class mgMainMenu : public cOsdMenu mgMenuTreeItem *CurrentItem(); eOSState ProcessKey(eKeys Key); - void Move( int from, int to); protected: enum MuggleStatus { TREE, TREE_SUBMENU, - PLAYLIST, PLAYLIST_TRACKINFO, PLAYLIST_ALBUMINFO, - FILTER + PLAYLIST, PLAYLIST_SUBMENU, + FILTER, FILTER_SUBMENU }; void SetButtons(); + void Move( int from, int to ); // Tree view handling void DisplayTree( mgSelectionTreeNode *node, int select = 0 ); void DisplayTreeViewSelector(); - void DisplayTreeSubmenu(); - void TreeSubmenuAction( int n ); - // Filter view handling - void DisplayFilter(); - void DisplayFilterSelector(); + void DisplayTreeSubmenu(); + eOSState TreeSubmenuAction( int n ); // Playlist view handling - void DisplayPlaylist(); + void DisplayPlaylist( int index_current = -1 ); void DisplayTrackInfo(); void DisplayAlbumInfo(); + void DisplayPlaylistSubmenu(); + eOSState PlaylistSubmenuAction( int n ); + + // Filter view handling + void DisplayFilter(); + void DisplayFilterSelector(); + private: + void Play(mgPlaylist *plist); // content stuff mgMedia *m_media; mgSelectionTreeNode *m_root; mgSelectionTreeNode *m_node; mgPlaylist *m_current_playlist; - mgTracklist *m_tracklist; - - // filter items - char *m_title, *m_interpret, *m_album, *m_playlist, *m_filtername; - int m_year_min, m_year_max, m_filter; MuggleStatus m_state; - std::list m_history; + + int m_last_osd_index; }; #endif @@ -110,9 +110,33 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.10 2004/05/28 15:29:19 lvw + * Merged player branch back on HEAD branch. + * * Revision 1.9 2004/02/23 15:41:21 RaK * - first i18n attempt * + * Revision 1.8.2.7 2004/05/27 07:58:38 lvw + * Removed bugs in moving and removing tracks from playlists + * + * Revision 1.8.2.6 2004/05/26 14:31:04 lvw + * Added submenu for playlist view + * + * Revision 1.8.2.5 2004/05/25 21:58:54 lvw + * Handle submenus for views + * + * Revision 1.8.2.4 2004/03/14 12:30:56 lvw + * Menu now calls player + * + * Revision 1.8.2.3 2004/03/11 07:22:32 lvw + * Added setup menu + * + * Revision 1.8.2.2 2004/03/08 22:28:40 lvw + * Added documentation headers. + * + * Revision 1.8.2.1 2004/03/02 07:07:27 lvw + * Initial adaptations from MP3 plugin added (untested) + * * Revision 1.8 2004/02/08 10:48:44 LarsAC * Made major revisions in OSD behavior * @@ -125,6 +149,4 @@ class mgMainMenu : public cOsdMenu * Revision 1.5 2004/02/03 00:13:24 LarsAC * Improved OSD handling of collapse/back * - * - ************************************************************ */ diff --git a/vdr_network.h b/vdr_network.h new file mode 100644 index 0000000..182fa23 --- /dev/null +++ b/vdr_network.h @@ -0,0 +1,45 @@ +/* + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#ifndef ___NETWORK_H +#define ___NETWORK_H + +#include +#include +#include + +class cRingBufferLinear; + +// ---------------------------------------------------------------- + +class mgNet : public cRingBufferLinear, cThread +{ +private: + int m_fd; + bool m_connected, m_netup; + int m_deferedErrno; + int m_rwTimeout, m_conTimeout; + unsigned char m_lineBuff[4096]; + int m_count; + // + void close(void); + int ringRead(unsigned char *dest, int len); + void copyFromBuff(unsigned char *dest, int n); +protected: + virtual void action(void); +public: + mgNet(int size, int ConTimeoutMs, int RwTimeoutMs); + ~mgNet(); + bool connect(const char *hostname, const int port); + void disconnect(void); + bool connected(void) { return m_connected; } + int gets(char *dest, int len); + int puts(char *dest); + int read(unsigned char *dest, int len); + int write(unsigned char *dest, int len); +}; + +#endif //___NETWORK_H diff --git a/vdr_player.c b/vdr_player.c new file mode 100644 index 0000000..090bbc6 --- /dev/null +++ b/vdr_player.c @@ -0,0 +1,1073 @@ +/*! + * \file vdr_player.c + * \brief A generic PCM player for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_player.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "vdr_player.h" +#include "vdr_decoder.h" +#include "vdr_config.h" +#include "vdr_setup.h" + +#include "mg_tools.h" +#include "mg_playlist.h" +#include "mg_content_interface.h" + +using namespace std; + +// ---------------------------------------------------------------- + +// TODO: check for use of constants +#define OUT_BITS 16 // output 16 bit samples to DVB driver +#define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes + +// cResample +#define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame + +// cNormalize +#define MAX_GAIN 3.0 // max. allowed gain +#define LIM_ACC 12 // bit, accuracy for lookup table +#define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1) // max. value covered by lookup table +#define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC) // shift value for table lookup +#define F_LIM_JMP (mad_fixed_t)(1<initialize(); + if( NextFile() ) + { + Play(); + } + Unlock(); + } + } + else if( m_started && m_active ) + { + Lock(); + StopPlay(); + Unlock(); + + m_active = false; + SetPlayMode( pmStartup ); + + Cancel(2); + } +} + +void mgPCMPlayer::NewPlaylist( mgPlaylist *plist ) +{ + MGLOG( "mgPCMPlayer::NewPlaylist" ); + + Lock(); + StopPlay(); + Unlock(); + + // memory management of playlists should happen elsewhere (menu, content) + m_playlist = plist; + m_current = 0; + + if( NextFile() ) + { + Play(); + } +} + +void mgPCMPlayer::SetPlayMode(ePlayMode mode) +{ + m_playmode_mutex.Lock(); + if( mode != m_playmode ) + { + m_playmode = mode; + m_playmode_cond.Broadcast(); + } + m_playmode_mutex.Unlock(); +} + +void mgPCMPlayer::WaitPlayMode(ePlayMode mode, bool inv) +{ + // must be called with m_playmode_mutex LOCKED !!! + + while( m_active && ( (!inv && mode != m_playmode) || (inv && mode == m_playmode) ) ) + { + m_playmode_cond.Wait(m_playmode_mutex); + } +} + +void mgPCMPlayer::Action(void) +{ + MGLOG( "mgPCMPlayer::Action" ); + + struct mgDecode *ds=0; + struct mad_pcm *pcm=0; + cResample resample[2]; + unsigned int nsamples[2]; + const mad_fixed_t *data[2]; + cScale scale; + cLevel level; + cNormalize norm; + bool haslevel=false; + struct LPCMFrame lpcmFrame; + const unsigned char *p=0; + int pc = 0, only48khz = the_setup.Only48kHz; + cPoller poll; +#ifdef DEBUG + int beat=0; +#endif + + dsyslog( "muggle: player thread started (pid=%d)", getpid() ); + + memset( &lpcmFrame, 0, sizeof(lpcmFrame) ); + lpcmFrame.PES[2]=0x01; + lpcmFrame.PES[3]=0xbd; + lpcmFrame.PES[6]=0x87; + lpcmFrame.LPCM[0]=0xa0; // substream ID + lpcmFrame.LPCM[1]=0xff; + lpcmFrame.LPCM[5]=0x01; + lpcmFrame.LPCM[6]=0x80; + + dvbSampleRate = 48000; + m_state = msStop; + SetPlayMode( pmStopped ); + cout << "Playmode set." << endl << flush; + + while( m_active ) + { +#ifdef DEBUG + if(time(0)>=beat+30) + { + cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer->Available() << endl << flush; + scale.Stats(); if(haslevel) norm.Stats(); + beat=time(0); + } +#endif + + Lock(); + + if( !m_rframe && m_playmode == pmPlay ) + { + switch( m_state ) + { + case msStart: + { + m_index = 0; + m_playing = m_current; + + string filename = getSourceFile(); + + if( ( m_decoder = mgDecoders::findDecoder( filename ) ) && m_decoder->start() ) + { + levelgood = true; + haslevel = false; + + scale.Init(); + level.Init(); + + m_state = msDecode; + + break; + } + + m_state = msEof; + } break; + case msDecode: + { + ds = m_decoder->decode(); + switch( ds->status ) + { + case dsPlay: + { + pcm = ds->pcm; + m_index = ds->index/1000; + m_state = msNormalize; + } break; + case dsSkip: + case dsSoftError: + { + // skipping, state unchanged, next decode + } break; + case dsEof: + { + m_state = msEof; + } break; + case dsOK: + case dsError: + { + m_state = msError; + } break; + } + } break; + case msNormalize: + { + if(!haslevel) + { + if( levelgood ) + { + level.GetPower( pcm ); + } + } + else + { + norm.AddGain( pcm ); + } + m_state = msResample; + } break; + case msResample: + { +#ifdef DEBUG + { + static unsigned int oldrate=0; + if(oldrate!=pcm->samplerate) + { + cout << "mgPCMPlayer::Action: new input sample rate " << pcm->samplerate << endl << flush; + oldrate = pcm->samplerate; + } + } +#endif + nsamples[0] = nsamples[1] = pcm->length; + data[0] = pcm->samples[0]; + data[1] = pcm->channels > 1 ? pcm->samples[1]: 0; + + lpcmFrame.LPCM[5]&=0xcf; + dvbSampleRate=48000; + if(!only48khz) + { + switch(pcm->samplerate) + { // If one of the supported frequencies, do it without resampling. + case 96000: + { // Select a "even" upsampling frequency if possible, too. + lpcmFrame.LPCM[5] |= 1 << 4; + dvbSampleRate = 96000; + } break; + + //case 48000: // this is already the default ... + // lpcmFrame.LPCM[5]|=0<<4; + // dvbSampleRate=48000; + // break; + case 11025: + case 22050: + case 44100: + { + lpcmFrame.LPCM[5]|=2<<4; + dvbSampleRate = 44100; + } break; + case 8000: + case 16000: + case 32000: + { + lpcmFrame.LPCM[5]|=3<<4; + dvbSampleRate = 32000; + } break; + } + } + + if( dvbSampleRate != pcm->samplerate ) + { + if( resample[0].SetInputRate( pcm->samplerate, dvbSampleRate ) ) + { + nsamples[0] = resample[0].ResampleBlock( nsamples[0], data[0] ); + data[0] = resample[0].Resampled(); + } + if(data[1] && resample[1].SetInputRate( pcm->samplerate, dvbSampleRate ) ) + { + nsamples[1] = resample[1].ResampleBlock( nsamples[1], data[1] ); + data[1] = resample[1].Resampled(); + } + } + m_state=msOutput; + } break; + case msOutput: + { + if( nsamples[0] > 0 ) + { + unsigned int outlen = scale.ScaleBlock( lpcmFrame.Data, + sizeof(lpcmFrame.Data), + nsamples[0], data[0], + data[1], + the_setup.AudioMode? amDither: amRound ); + if( outlen ) + { + outlen += sizeof(lpcmFrame.LPCM)+LEN_CORR; + lpcmFrame.PES[4] = outlen >> 8; + lpcmFrame.PES[5] = outlen; + m_rframe = new cFrame( (unsigned char *)&lpcmFrame, + outlen + sizeof( lpcmFrame.PES ) - LEN_CORR ); + } + } + else + { + m_state=msDecode; + } + } break; + case msError: + case msEof: + { + if( NextFile() ) + { + m_state = msStart; + } + else + { + m_state = msWait; + } + } // fall through + case msStop: + { + m_playing = 0; + if( m_decoder ) + { // who deletes decoder? + m_decoder->stop(); + m_decoder = 0; + } + + levelgood = false; + + scale.Stats(); + if( haslevel ) + { + norm.Stats(); + } + if( m_state == msStop ) + { // might be unequal in case of fall through from eof/error + SetPlayMode( pmStopped ); + } + } break; + case msWait: + { + if( m_ringbuffer->Available() == 0 ) + { + m_active = false; + SetPlayMode(pmStopped); + } + } break; + } + } + + if( m_rframe && m_ringbuffer->Put( m_rframe ) ) + { + m_rframe = 0; + } + + if( !m_pframe && m_playmode == pmPlay ) + { + m_pframe = m_ringbuffer->Get(); + if( m_pframe ) + { + p = m_pframe->Data(); + pc = m_pframe->Count(); + } + } + + if( m_pframe ) + { + int w = PlayVideo( p, pc ); + if( w > 0 ) + { + p += w; + pc -= w; + + if( pc <= 0 ) + { + m_ringbuffer->Drop(m_pframe); + m_pframe=0; + } + } + else if( w < 0 && FATALERRNO ) + { + LOG_ERROR; + break; + } + } + + Unlock(); + + if( (m_rframe || m_state == msWait) && m_pframe ) + { + // Wait for output to become ready + DevicePoll( poll, 500 ); + } + else + { + if( m_playmode != pmPlay ) + { + m_playmode_mutex.Lock(); + + if( m_playmode != pmPlay ) + { + WaitPlayMode( m_playmode, true ); // Wait on playMode change + } + m_playmode_mutex.Unlock(); + } + } + } + + Lock(); + + if( m_rframe ) + { + delete m_rframe; + m_rframe=0; + } + + if( m_decoder ) + { // who deletes decoder? + m_decoder->stop(); + m_decoder = 0; + } + + m_playing = 0; + + SetPlayMode(pmStopped); + + Unlock(); + + m_active = false; + + dsyslog( "muggle: player thread ended (pid=%d)", getpid() ); +} + +void mgPCMPlayer::Empty(void) +{ + MGLOG( "mgPCMPlayer::Empty" ); + + Lock(); + + m_ringbuffer->Clear(); + DeviceClear(); + + delete m_rframe; + m_rframe = 0; + m_pframe = 0; + + Unlock(); +} + +void mgPCMPlayer::StopPlay() +{ // StopPlay() must be called in locked state!!! + MGLOG( "mgPCMPlayer::StopPlay" ); + if( m_playmode != pmStopped ) + { + Empty(); + m_state = msStop; + SetPlayMode( pmPlay ); + Unlock(); // let the decode thread process the stop signal + + m_playmode_mutex.Lock(); + WaitPlayMode( pmStopped, false ); + m_playmode_mutex.Unlock(); + + Lock(); + } +} + +string mgPCMPlayer::getSourceFile() +{ + string filename; + + if( !the_setup.GdCompatibility ) + { // use filename itself + filename = string( the_setup.ToplevelDir ) + m_playing->getSourceFile(); + } + else + { // find the unique name within any directory, but what is top? video? + char *cmd = NULL; + asprintf( &cmd, "find %s -follow -type f -name '%s'|sort ", + the_setup.ToplevelDir, m_playing->getSourceFile().c_str() ); + + FILE *p = popen(cmd, "r"); + if (p) + { + char *s; + if( (s = readline(p) ) != NULL ) + { + filename = string( s ); + } + } + pclose( p ); + delete cmd; + } + + cout << "mgPCMPlayer::getSourceFile: found filename " << filename << endl << flush; + return "/test.mp3"; +} + +bool mgPCMPlayer::NextFile() +{ + bool res = false; + + mgContentItem *newcurr = m_playlist->skipFwd(); + + if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) + { + m_current = newcurr; + res = true; + } + + return res; +} + +bool mgPCMPlayer::PrevFile(void) +{ + bool res = false; + + mgContentItem *newcurr = m_playlist->skipBack(); + + if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) + { + m_current = newcurr; + res = true; + } + + return res; +} + +void mgPCMPlayer::ToggleShuffle() +{ + m_playlist->toggleShuffle(); +} + +void mgPCMPlayer::ToggleLoop(void) +{ + m_playlist->toggleLoop(); +} + +void mgPCMPlayer::Pause(void) +{ + if( m_playmode == pmPaused ) + { + Play(); + } + else + { + if( m_playmode == pmPlay ) + { + // DeviceFreeze(); + SetPlayMode( pmPaused ); + } + } +} + +void mgPCMPlayer::Play(void) +{ + MGLOG( "mgPCMPlayer::Play" ); + + Lock(); + if( m_playmode != pmPlay && m_current ) + { + if( m_playmode == pmStopped ) + { + m_state = msStart; + } + // DevicePlay(); // TODO? Commented out in original code, too + SetPlayMode( pmPlay ); + } + Unlock(); +} + +void mgPCMPlayer::Forward(void) +{ + MGLOG( "mgPCMPlayer::Forward" ); + + Lock(); + if( NextFile() ) + { + StopPlay(); + Play(); + } + Unlock(); +} + +void mgPCMPlayer::Backward(void) +{ + Lock(); + if( PrevFile() ) + { + StopPlay(); + Play(); + } + Unlock(); +} + +void mgPCMPlayer::Goto( int index, bool still ) +{ + mgContentItem *next = m_playlist->getPosition( index-1 ); + + if( next != &(mgContentItem::UNDEFINED) ) //invalid + { + Lock(); + StopPlay(); + m_current = next; + Play(); + Unlock(); + } +} + +void mgPCMPlayer::SkipSeconds(int secs) +{ + if( m_playmode != pmStopped ) + { + Lock(); + if( m_playmode == pmPaused ) + { + SetPlayMode( pmPlay ); + } + if( m_decoder && m_decoder->skip( secs, m_ringbuffer->Available(), dvbSampleRate ) ) + { + levelgood=false; + } + Empty(); + Unlock(); + } +} + +bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) +{ + bool res = false; + current = SecondsToFrames(m_index); + total = -1; + + return res; +} + +// --- mgPlayerControl ------------------------------------------------------- + +mgPlayerControl::mgPlayerControl( mgPlaylist *plist ) + : cControl( m_player = new mgPCMPlayer(plist) ) +{ + MGLOG( "mgPlayerControl::mgPlayerControl" ); + + m_visible = false; + m_has_osd = false; +} + +mgPlayerControl::~mgPlayerControl() +{ + Hide(); + Stop(); +} + +bool mgPlayerControl::Active(void) +{ + MGLOG( "mgPlayerControl::Active" ); + + return m_player && m_player->Active(); +} + +void mgPlayerControl::Stop(void) +{ + if( m_player ) + { + delete m_player; + m_player = 0; + } +} + +void mgPlayerControl::Pause(void) +{ + if( m_player ) + { + m_player->Pause(); + } +} + +void mgPlayerControl::Play(void) +{ + if( m_player ) + { + m_player->Play(); + } +} + +void mgPlayerControl::Forward(void) +{ + if( m_player ) + { + m_player->Forward(); + } +} + +void mgPlayerControl::Backward(void) +{ + if( m_player ) + { + m_player->Backward(); + } +} + +void mgPlayerControl::SkipSeconds(int Seconds) +{ + if( m_player ) + { + m_player->SkipSeconds(Seconds); + } +} + +void mgPlayerControl::Goto(int Position, bool Still) +{ + if( m_player ) + { + m_player->Goto(Position, Still); + } +} + +void mgPlayerControl::ToggleShuffle(void) +{ + if( m_player ) + { + m_player->ToggleShuffle(); + } +} + +void mgPlayerControl::ToggleLoop(void) +{ + if( m_player ) + { + m_player->ToggleLoop(); + } +} + +void mgPlayerControl::NewPlaylist(mgPlaylist *plist) +{ + if( m_player ) + { + m_player->NewPlaylist(plist); + } +} + +void mgPlayerControl::ShowProgress() +{ + if( m_visible ) + { + if( !m_has_osd ) + { + // open the osd if its not already there... + Interface->Open(); + m_has_osd = true; + } + + // now an osd is open, go on + + int w = Interface->Width(); + int h = Interface->Height(); + + Interface->WriteText( w/2, h/2, "Muggle is active!" ); + + // Add: song info (name, artist, pos in playlist, time, ...) + // Add: progress bar + + Interface->Flush(); + } + else + { + Hide(); + } +} + +void mgPlayerControl::Hide() +{ + if( m_has_osd ) + { + Interface->Close(); + m_has_osd = false; + } +} + +eOSState mgPlayerControl::ProcessKey(eKeys key) +{ + if( !Active() ) + { + return osEnd; + } + + ShowProgress(); + + eOSState state = cControl::ProcessKey(key); + + if( state == osUnknown ) + { + switch( key ) + { + case kUp: + { + Forward(); + } break; + case kDown: + { + Backward(); + } break; + case kPause: + case kYellow: + { + Pause(); + } break; + case kStop: + case kBlue: + { + Hide(); + Stop(); + + return osEnd; + } break; + case kOk: + { + m_visible = !m_visible; + ShowProgress(); + + return osContinue; + } break; + case kBack: + { + Hide(); + Stop(); + + return osEnd; + } break; + default: + { + return osUnknown; + } + } + } + return osContinue; +} + +/************************************************************ + * + * $Log: vdr_player.c,v $ + * Revision 1.2 2004/05/28 15:29:19 lvw + * Merged player branch back on HEAD branch. + * + * Revision 1.1.2.19 2004/05/26 14:30:27 lvw + * Removed bug in finding correct mp3 file in GD mode + * + * Revision 1.1.2.18 2004/05/25 06:48:24 lvw + * Documentation and code polishing. + * + * Revision 1.1.2.17 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.1.2.16 2004/05/24 11:48:52 lvw + * Debugging info added to find deadlock + * + * Revision 1.1.2.15 2004/05/12 22:38:37 lvw + * Some cleanup + * + * Revision 1.1.2.14 2004/05/11 06:35:16 lvw + * Added debugging while hunting stop bug. + * + * Revision 1.1.2.13 2004/05/07 06:46:41 lvw + * Removed a bug in playlist deallocation. Added infrastructure to display information while playing. + * + * + ***********************************************************/ diff --git a/vdr_player.h b/vdr_player.h new file mode 100644 index 0000000..6ce2f1b --- /dev/null +++ b/vdr_player.h @@ -0,0 +1,137 @@ +/*! + * \file vdr_player.h + * \brief A player/control combination to let VDR play music + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_player.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + + +#ifndef ___DVB_MP3_H +#define ___DVB_MP3_H + +#include + +// ------------------------------------------------------------------- + +class mgPCMPlayer; +class mgPlaylist; + +// ------------------------------------------------------------------- + +/*! + * \brief exerts control over the player itself + * + * This control is launched from the main menu and manages a link + * to the player. Key events are caught and signaled to the player. + */ +class mgPlayerControl : public cControl +{ +private: + + //! \brief the reference to the player + mgPCMPlayer *m_player; + + //! \brief indicates, whether the osd should be visible + bool m_visible; + + //! \brief indicates, whether an osd is currently displayed + bool m_has_osd; + +public: + + /*! \brief construct a control with a playlist + * + * \param plist - the playlist to be played + */ + mgPlayerControl(mgPlaylist *plist); + + /*! \brief destructor + */ + virtual ~mgPlayerControl(); + + //! \brief indicate, whether the corresponding player is active + bool Active(); + + //! \brief stop the corresponding player + void Stop(); + + //! \brief toggle the pause mode of the corresponding player + void Pause(); + + //! \brief start playing + void Play(); + + //! \brief skip to the next song + void Forward(); + + //! \brief skip to the previous song + void Backward(); + + /*! \brief skip a specified number of seconds + * + * \param seconds - the number of seconds to skip + */ + void SkipSeconds(int seconds); + + /*! \brief goto a certain position in the playlist + * + * \param index - the position in the playlist to skip to + * \param still - currently unused + */ + void Goto(int index, bool still = false); + + //! \brief toggle the shuffle mode of the corresponding player + void ToggleShuffle(); + + //! \brief toggle the loop mode of the corresponding player + void ToggleLoop(); + + /*! \brief signal a new playlist + * + * The caller has to take care of deallocating the previous list + * + * \param plist - the new playlist to be played + */ + void NewPlaylist( mgPlaylist *plist ); + + //! \brief a progress display + void ShowProgress(); + + //! \brief hide the osd, if present + void Hide(); + + //! \brief process key events + eOSState ProcessKey(eKeys key); +}; + +#endif //___DVB_MP3_H + +/************************************************************ + * + * $Log: vdr_player.h,v $ + * Revision 1.2 2004/05/28 15:29:19 lvw + * Merged player branch back on HEAD branch. + * + * Revision 1.1.2.9 2004/05/25 06:48:24 lvw + * Documentation and code polishing. + * + * Revision 1.1.2.8 2004/05/25 00:10:45 lvw + * Code cleanup and added use of real database source files + * + * Revision 1.1.2.7 2004/05/11 06:35:16 lvw + * Added debugging while hunting stop bug. + * + * Revision 1.1.2.6 2004/05/07 06:46:41 lvw + * Removed a bug in playlist deallocation. Added infrastructure to display information while playing. + * + * + ***********************************************************/ diff --git a/vdr_setup.c b/vdr_setup.c new file mode 100644 index 0000000..04ba06b --- /dev/null +++ b/vdr_setup.c @@ -0,0 +1,70 @@ +/*! + * \file vdr_setup.c + * \brief A setup class for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_setup.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Partially adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#include + +#include "vdr_setup.h" +#include "i18n.h" + +mgSetup the_setup; + +// --- mgMenuSetup ----------------------------------------------------------- + +mgMenuSetup::mgMenuSetup() +{ + static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" }; + + m_data = the_setup; + + SetSection( tr("Muggle") ); + + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Initial loop mode"), &m_data.InitLoopMode)); + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Initial shuffle mode"), &m_data.InitShuffleMode)); + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Audio mode"), &m_data.AudioMode, tr("Round"), tr("Dither"))); + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Use 48kHz mode only"), &m_data.Only48kHz)); + Add(new cMenuEditIntItem( tr("Setup.Muggle$Display mode"), &m_data.DisplayMode, 1, 3)); + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Background mode"), &m_data.BackgrMode, tr("Black"), tr("Live"))); + Add(new cMenuEditIntItem( tr("Setup.Muggle$Normalizer level"), &m_data.TargetLevel, 0, MAX_TARGET_LEVEL)); + Add(new cMenuEditIntItem( tr("Setup.Muggle$Limiter level"), &m_data.LimiterLevel, MIN_LIMITER_LEVEL, 100)); +} + +void mgMenuSetup::Store(void) +{ + the_setup = m_data; + + SetupStore("InitLoopMode", the_setup.InitLoopMode ); + SetupStore("InitShuffleMode", the_setup.InitShuffleMode); + SetupStore("AudioMode", the_setup.AudioMode ); + SetupStore("DisplayMode", the_setup.DisplayMode ); + SetupStore("BackgrMode", the_setup.BackgrMode ); + SetupStore("TargetLevel", the_setup.TargetLevel ); + SetupStore("LimiterLevel", the_setup.LimiterLevel ); + SetupStore("Only48kHz", the_setup.Only48kHz ); +} + +// --- mgSetup --------------------------------------------------------------- + +mgSetup::mgSetup() +{ + InitLoopMode = 0; + InitShuffleMode = 0; + AudioMode = 1; + DisplayMode = 3; + BackgrMode = 1; + TargetLevel = DEFAULT_TARGET_LEVEL; + LimiterLevel = DEFAULT_LIMITER_LEVEL; + Only48kHz = 0; +} diff --git a/vdr_setup.h b/vdr_setup.h new file mode 100644 index 0000000..a77cf75 --- /dev/null +++ b/vdr_setup.h @@ -0,0 +1,76 @@ +/*! + * \file vdr_setup.h + * \brief A setup class for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_setup.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#ifndef ___SETUP_MG_H +#define ___SETUP_MG_H + +// #include +#include + +#define MAX_STRING_LEN 128 + +#define DEFAULT_TARGET_LEVEL 25 +#define MAX_TARGET_LEVEL 50 +#define DEFAULT_LIMITER_LEVEL 70 +#define MIN_LIMITER_LEVEL 25 + +/*! + * \brief storage for setup data + */ +class mgSetup +{ +public: + int InitLoopMode; + int InitShuffleMode; + int AudioMode; + int DisplayMode; + int BackgrMode; + int MenuMode; + int TargetLevel; + int LimiterLevel; + int Only48kHz; + + char *DbHost; + char *DbName; + char *DbUser; + char *DbPass; + int DbPort; + bool GdCompatibility; + char *ToplevelDir; + + char PathPrefix[MAX_STRING_LEN]; + + public: + mgSetup(void); +}; + +extern mgSetup the_setup; + +/*! + * \brief allow user to modify setup on OSD + */ +class mgMenuSetup : public cMenuSetupPage +{ +private: + mgSetup m_data; +protected: + virtual void Store(); +public: + mgMenuSetup(); +}; + + +#endif //___SETUP_MP3_H diff --git a/vdr_sound.c b/vdr_sound.c new file mode 100644 index 0000000..b06061c --- /dev/null +++ b/vdr_sound.c @@ -0,0 +1,609 @@ +/*! + * \file vdr_setup.c + * \brief Sound manipulation classes for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_sound.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +// --- cResample ------------------------------------------------------------ + +// The resample code has been adapted from the madplay project +// (resample.c) found in the libmad distribution + +class cResample +{ +private: + mad_fixed_t ratio; + mad_fixed_t step; + mad_fixed_t last; + mad_fixed_t resampled[MAX_NSAMPLES]; +public: + bool SetInputRate(unsigned int oldrate, unsigned int newrate); + unsigned int ResampleBlock(unsigned int nsamples, const mad_fixed_t *old); + const mad_fixed_t *Resampled(void) { return resampled; } + }; + +bool cResample::SetInputRate(unsigned int oldrate, unsigned int newrate) +{ + if(oldrate<8000 || oldrate>newrate*6) { // out of range + esyslog("WARNING: samplerate %d out of range 8000-%d\n",oldrate,newrate*6); + return 0; + } + ratio=mad_f_tofixed((double)oldrate/(double)newrate); + step=0; last=0; +#ifdef DEBUG + static mad_fixed_t oldratio=0; + if(oldratio!=ratio) { + printf("mad: new resample ratio %f (from %d kHz to %d kHz)\n",mad_f_todouble(ratio),oldrate,newrate); + oldratio=ratio; + } +#endif + return ratio!=MAD_F_ONE; +} + +unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t *old) +{ + // This resampling algorithm is based on a linear interpolation, which is + // not at all the best sounding but is relatively fast and efficient. + // + // A better algorithm would be one that implements a bandlimited + // interpolation. + + mad_fixed_t *nsam=resampled; + const mad_fixed_t *end=old+nsamples; + const mad_fixed_t *begin=nsam; + + if(step < 0) { + step = mad_f_fracpart(-step); + + while (step < MAD_F_ONE) { + *nsam++ = step ? last+mad_f_mul(*old-last,step) : last; + step += ratio; + if(((step + 0x00000080L) & 0x0fffff00L) == 0) + step = (step + 0x00000080L) & ~0x0fffffffL; + } + step -= MAD_F_ONE; + } + + while (end - old > 1 + mad_f_intpart(step)) { + old += mad_f_intpart(step); + step = mad_f_fracpart(step); + *nsam++ = step ? *old + mad_f_mul(old[1] - old[0], step) : *old; + step += ratio; + if (((step + 0x00000080L) & 0x0fffff00L) == 0) + step = (step + 0x00000080L) & ~0x0fffffffL; + } + + if (end - old == 1 + mad_f_intpart(step)) { + last = end[-1]; + step = -step; + } + else step -= mad_f_fromint(end - old); + + return nsam-begin; +} + +// --- cLevel ---------------------------------------------------------------- + +// The normalize algorithm and parts of the code has been adapted from the +// Normalize 0.7 project. (C) 1999-2002, Chris Vaill + +// A little background on how normalize computes the volume +// of a wav file, in case you want to know just how your +// files are being munged: +// +// The volumes calculated are RMS amplitudes, which corre­ +// spond (roughly) to perceived volume. Taking the RMS ampli­ +// tude of an entire file would not give us quite the measure +// we want, though, because a quiet song punctuated by short +// loud parts would average out to a quiet song, and the +// adjustment we would compute would make the loud parts +// excessively loud. +// +// What we want is to consider the maximum volume of the +// file, and normalize according to that. We break up the +// signal into 100 chunks per second, and get the signal +// power of each chunk, in order to get an estimation of +// "instantaneous power" over time. This "instantaneous +// power" signal varies too much to get a good measure of the +// original signal's maximum sustained power, so we run a +// smoothing algorithm over the power signal (specifically, a +// mean filter with a window width of 100 elements). The max­ +// imum point of the smoothed power signal turns out to be a +// good measure of the maximum sustained power of the file. +// We can then take the square root of the power to get maxi­ +// mum sustained RMS amplitude. + +class cLevel { +private: + double maxpow; + mad_fixed_t peak; + struct Power { + // smooth + int npow, wpow; + double powsum, pows[POW_WIN]; + // sum + unsigned int nsum; + double sum; + } power[2]; + // + inline void AddPower(struct Power *p, double pow); +public: + void Init(void); + void GetPower(struct mad_pcm *pcm); + double GetLevel(void); + double GetPeak(void); + }; + +void cLevel::Init(void) +{ + for(int l=0 ; l<2 ; l++) { + struct Power *p=&power[l]; + p->sum=p->powsum=0.0; p->wpow=p->npow=p->nsum=0; + for(int i=POW_WIN-1 ; i>=0 ; i--) p->pows[i]=0.0; + } + maxpow=0.0; peak=0; +} + +void cLevel::GetPower(struct mad_pcm *pcm) +{ + for(int i=0 ; ichannels ; i++) { + struct Power *p=&power[i]; + mad_fixed_t *data=pcm->samples[i]; + for(int n=pcm->length ; n>0 ; n--) { + if(*data < -peak) peak = -*data; + if(*data > peak) peak = *data; + double s=mad_f_todouble(*data++); + p->sum+=(s*s); + if(++(p->nsum)>=pcm->samplerate/100) { + AddPower(p,p->sum/(double)p->nsum); + p->sum=0.0; p->nsum=0; + } + } + } +} + +void cLevel::AddPower(struct Power *p, double pow) +{ + p->powsum+=pow; + if(p->npow>=POW_WIN) { + if(p->powsum>maxpow) maxpow=p->powsum; + p->powsum-=p->pows[p->wpow]; + } + else p->npow++; + p->pows[p->wpow]=pow; + p->wpow=(p->wpow+1) % POW_WIN; +} + +double cLevel::GetLevel(void) +{ + if(maxpowmaxpow) maxpow=power[0].powsum; + if(power[1].powsum>maxpow) maxpow=power[1].powsum; + } + double level=sqrt(maxpow/(double)POW_WIN); // adjust for the smoothing window size and root + printf("norm: new volumen level=%f peak=%f\n",level,mad_f_todouble(peak)); + return level; +} + +double cLevel::GetPeak(void) +{ + return mad_f_todouble(peak); +} + +// --- cNormalize ------------------------------------------------------------ + +class cNormalize { +private: + mad_fixed_t gain; + double d_limlvl, one_limlvl; + mad_fixed_t limlvl; + bool dogain, dolimit; +#ifdef DEBUG + // stats + unsigned long limited, clipped, total; + mad_fixed_t peak; +#endif + // limiter +#ifdef USE_FAST_LIMITER + mad_fixed_t *table, tablestart; + int tablesize; + inline mad_fixed_t FastLimiter(mad_fixed_t x); +#endif + inline mad_fixed_t Limiter(mad_fixed_t x); +public: + cNormalize(void); + ~cNormalize(); + void Init(double Level, double Peak); + void Stats(void); + void AddGain(struct mad_pcm *pcm); + }; + +cNormalize::cNormalize(void) +{ + d_limlvl = (double)the_setup.LimiterLevel / 100.0; + one_limlvl = 1 - d_limlvl; + limlvl = mad_f_tofixed(d_limlvl); + printf( "norm: lim_lev=%f lim_acc=%d\n", d_limlvl, LIM_ACC ); + +#ifdef USE_FAST_LIMITER + mad_fixed_t start=limlvl & ~(F_LIM_JMP-1); + tablestart=start; + tablesize=(unsigned int)(F_LIM_MAX-start)/F_LIM_JMP + 2; + table=new mad_fixed_t[tablesize]; + if(table) { + printf("norm: table size=%d start=%08x jump=%08x\n",tablesize,start,F_LIM_JMP); + for(int i=0 ; i=limlvl ; x-=mad_f_tofixed(1e-4)) { + mad_fixed_t diff=mad_f_abs(Limiter(x)-FastLimiter(x)); + if(diff>maxdiff) maxdiff=diff; +#ifdef ACC_DUMP + fprintf(out,"%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n", + mad_f_todouble(x),mad_f_todouble(Limiter(x)),mad_f_todouble(FastLimiter(x)),mad_f_todouble(diff),mad_f_todouble(maxdiff)); + if(ferror(out)) break; +#endif + } +#ifdef ACC_DUMP + fclose(out); +#endif + printf("norm: accuracy %.12f\n",mad_f_todouble(maxdiff)); + if(mad_f_todouble(maxdiff)>1e-6) + { + esyslog("ERROR: accuracy check failed, normalizer disabled"); + delete table; table=0; + } + } + else esyslog("ERROR: no memory for lookup table, normalizer disabled"); +#endif // USE_FAST_LIMITER +} + +cNormalize::~cNormalize() +{ +#ifdef USE_FAST_LIMITER + delete table; +#endif +} + +void cNormalize::Init(double Level, double Peak) +{ + double Target=(double)the_setup.TargetLevel/100.0; + double dgain=Target/Level; + if(dgain>MAX_GAIN) dgain=MAX_GAIN; + gain=mad_f_tofixed(dgain); + // Check if we actually need to apply a gain + dogain=(Target>0.0 && fabs(1-dgain)>MIN_GAIN); +#ifdef USE_FAST_LIMITER + if(!table) dogain=false; +#endif + // Check if we actually need to do limiting: + // we have to if limiter is enabled, if gain>1 and if the peaks will clip. + dolimit=(d_limlvl<1.0 && dgain>1.0 && Peak*dgain>1.0); +#ifdef DEBUG + printf("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",dgain,dogain,dolimit,Target,Level,Peak); + limited=clipped=total=0; peak=0; +#endif +} + +void cNormalize::Stats(void) +{ +#ifdef DEBUG + if(total) + printf("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n", + total,limited,(double)limited/total*100.0,clipped,(double)clipped/total*100.0,mad_f_todouble(peak)); +#endif +} + +mad_fixed_t cNormalize::Limiter(mad_fixed_t x) +{ +// Limiter function: +// +// / x (for x <= lev) +// x' = | +// \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev) +// +// call only with x>=0. For negative samples, preserve sign outside this function +// +// With limiter level = 0, this is equivalent to a tanh() function; +// with limiter level = 1, this is equivalent to clipping. + + if(x>limlvl) { +#ifdef DEBUG + if(x>MAD_F_ONE) clipped++; + limited++; +#endif + x=mad_f_tofixed(tanh((mad_f_todouble(x)-d_limlvl) / one_limlvl) * one_limlvl + d_limlvl); + } + return x; +} + +#ifdef USE_FAST_LIMITER +mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x) +{ +// The fast algorithm is based on a linear interpolation between the +// the values in the lookup table. Relays heavly on libmads fixed point format. + + if(x>limlvl) { + int i=(unsigned int)(x-tablestart)/F_LIM_JMP; +#ifdef DEBUG + if(x>MAD_F_ONE) clipped++; + limited++; + if(i>=tablesize) printf("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n", + mad_f_todouble(x),mad_f_todouble(x-tablestart),i,tablesize); +#endif + mad_fixed_t r=x & (F_LIM_JMP-1); + x=MAD_F_ONE; + if(i>MAD_F_FRACBITS + // which is senseless in the case of following <>LIM_SHIFT; // better, don't know if works on all machines + } + } + return x; +} +#endif + +#ifdef USE_FAST_LIMITER +#define LIMITER_FUNC FastLimiter +#else +#define LIMITER_FUNC Limiter +#endif + +void cNormalize::AddGain(struct mad_pcm *pcm) +{ + if(dogain) { + for(int i=0 ; ichannels ; i++) { + mad_fixed_t *data=pcm->samples[i]; +#ifdef DEBUG + total+=pcm->length; +#endif + if(dolimit) { + for(int n=pcm->length ; n>0 ; n--) { + mad_fixed_t s=mad_f_mul(*data,gain); + if(s<0) { + s=-s; +#ifdef DEBUG + if(s>peak) peak=s; +#endif + s=LIMITER_FUNC(s); + s=-s; + } + else { +#ifdef DEBUG + if(s>peak) peak=s; +#endif + s=LIMITER_FUNC(s); + } + *data++=s; + } + } + else { + for(int n=pcm->length ; n>0 ; n--) { + mad_fixed_t s=mad_f_mul(*data,gain); +#ifdef DEBUG + if(s>peak) peak=s; + else if(-s>peak) peak=-s; +#endif + if(s>MAD_F_ONE) s=MAD_F_ONE; // do clipping + if(s<-MAD_F_ONE) s=-MAD_F_ONE; + *data++=s; + } + } + } + } +} + +// --- cScale ---------------------------------------------------------------- + +// The dither code has been adapted from the madplay project +// (audio.c) found in the libmad distribution + +enum eAudioMode { amRound, amDither }; + +class cScale { +private: + enum { MIN=-MAD_F_ONE, MAX=MAD_F_ONE - 1 }; +#ifdef DEBUG + // audio stats + unsigned long clipped_samples; + mad_fixed_t peak_clipping; + mad_fixed_t peak_sample; +#endif + // dither + struct dither { + mad_fixed_t error[3]; + mad_fixed_t random; + } leftD, rightD; + // + inline mad_fixed_t Clip(mad_fixed_t sample, bool stats=true); + inline signed long LinearRound(mad_fixed_t sample); + inline unsigned long Prng(unsigned long state); + inline signed long LinearDither(mad_fixed_t sample, struct dither *dither); +public: + void Init(void); + void Stats(void); + unsigned int ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode); + }; + +void cScale::Init(void) +{ +#ifdef DEBUG + clipped_samples=0; peak_clipping=peak_sample=0; +#endif + memset(&leftD,0,sizeof(leftD)); + memset(&rightD,0,sizeof(rightD)); +} + +void cScale::Stats(void) +{ +#ifdef DEBUG + printf("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n", + clipped_samples,mad_f_todouble(peak_clipping),mad_f_todouble(peak_sample)); +#endif +} + +// gather signal statistics while clipping +mad_fixed_t cScale::Clip(mad_fixed_t sample, bool stats) +{ +#ifndef DEBUG + if (sample > MAX) sample = MAX; + if (sample < MIN) sample = MIN; +#else + if(!stats) { + if (sample > MAX) sample = MAX; + if (sample < MIN) sample = MIN; + } + else { + if (sample >= peak_sample) { + if (sample > MAX) { + ++clipped_samples; + if (sample - MAX > peak_clipping) + peak_clipping = sample - MAX; + sample = MAX; + } + peak_sample = sample; + } + else if (sample < -peak_sample) { + if (sample < MIN) { + ++clipped_samples; + if (MIN - sample > peak_clipping) + peak_clipping = MIN - sample; + sample = MIN; + } + peak_sample = -sample; + } + } +#endif + return sample; +} + +// generic linear sample quantize routine +signed long cScale::LinearRound(mad_fixed_t sample) +{ + // round + sample += (1L << (MAD_F_FRACBITS - OUT_BITS)); + // clip + sample=Clip(sample); + // quantize and scale + return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS); +} + +// 32-bit pseudo-random number generator +unsigned long cScale::Prng(unsigned long state) +{ + return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; +} + +// generic linear sample quantize and dither routine +signed long cScale::LinearDither(mad_fixed_t sample, struct dither *dither) +{ + unsigned int scalebits; + mad_fixed_t output, mask, random; + + // noise shape + sample += dither->error[0] - dither->error[1] + dither->error[2]; + dither->error[2] = dither->error[1]; + dither->error[1] = dither->error[0] / 2; + // bias + output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1)); + scalebits = MAD_F_FRACBITS + 1 - OUT_BITS; + mask = (1L << scalebits) - 1; + // dither + random = Prng(dither->random); + output += (random & mask) - (dither->random & mask); + dither->random = random; + // clip + output=Clip(output); + sample=Clip(sample,false); + // quantize + output &= ~mask; + // error feedback + dither->error[0] = sample - output; + // scale + return output >> scalebits; +} + +// write a block of signed 16-bit big-endian PCM samples +unsigned int cScale::ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode) +{ + signed int sample; + unsigned int len, res; + + len=size/OUT_FACT; res=size; + if(len>nsamples) { len=nsamples; res=len*OUT_FACT; } + nsamples-=len; + + if(right) { // stereo + switch (mode) { + case amRound: + while (len--) { + sample = LinearRound(*left++); + *data++ = sample >> 8; + *data++ = sample >> 0; + sample = LinearRound(*right++); + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + case amDither: + while (len--) { + sample = LinearDither(*left++,&leftD); + *data++ = sample >> 8; + *data++ = sample >> 0; + sample = LinearDither(*right++,&rightD); + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + } + } + else { // mono, duplicate left channel + switch (mode) { + case amRound: + while (len--) { + sample = LinearRound(*left++); + *data++ = sample >> 8; + *data++ = sample >> 0; + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + case amDither: + while (len--) { + sample = LinearDither(*left++,&leftD); + *data++ = sample >> 8; + *data++ = sample >> 0; + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + } + } + return res; +} diff --git a/vdr_stream.c b/vdr_stream.c new file mode 100644 index 0000000..d82877e --- /dev/null +++ b/vdr_stream.c @@ -0,0 +1,332 @@ +/*! + * \file vdr_stream.c + * \brief Implementation of media stream classes + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_stream.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001-2003 Stefan Huelswitt + */ + +#include +#include +#include +#include +#include + +#include + +#include "mg_tools.h" + +// #include "setup-mp3.h" +#include "vdr_stream.h" +#include "vdr_network.h" +#include "vdr_config.h" +// #include "i18n.h" +// #include "version.h" + +#define tr(x) x + +#ifdef USE_MMAP +#include +#endif + +#define DEFAULT_PORT 80 // default port for streaming (HTTP) + +using namespace std; + +// --- mgStream ----------------------------------------------------------------- + +mgStream::mgStream( string filename ) + : m_filename( filename ) +{ + m_fd = -1; + m_ismmap = false; + m_buffer = 0; +} + +mgStream::~mgStream() +{ + close(); +} + +bool mgStream::open(bool log) +{ + if( m_fd >= 0 ) + { + return seek(); + } + + // just check, whether file exists? + if( fileinfo( log ) ) + { + printf( "mgStream::open: fileinfo == true\n"); + + if( ( m_fd = ::open( m_filename.c_str(), O_RDONLY ) ) >=0 ) + { + printf( "mgStream::open: file opened\n" ); + + m_buffpos = m_readpos = 0; + m_fill = 0; + + printf( "mgStream::open: buffpos, readpos, fill set\n" ); + + /* +#ifdef USE_MMAP + if( m_filesize <= MAX_MMAP_SIZE ) + { + m_buffer = (unsigned char*)mmap( 0, m_filesize, PROT_READ, + MAP_SHARED, m_fd, 0 ); + if( m_buffer != MAP_FAILED ) + { + m_ismmap = true; + return true; + } + else + { + dsyslog("mmap() failed for %s: %s", m_filename.c_str(), strerror(errno) ); + } + } +#endif + */ + printf( "mgStream::open: allocating buffer: %d\n", MP3FILE_BUFSIZE ); + m_buffer = new unsigned char[MP3FILE_BUFSIZE]; + printf( "mgStream::open: buffer allocated\n" ); + + if( m_buffer ) + { + printf( "mgStream::open: buffer allocated, returning true\n" ); + + return true; + } + else + { + esyslog("ERROR: not enough memory for buffer: %s", m_filename.c_str() ); + } + } + else + { + if( log ) + { + esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) ); + } + } + } + + close(); + printf( "mgStream::open: returning false\n" ); + return false; +} + +void mgStream::close(void) +{ +#ifdef USE_MMAP + if( m_ismmap ) + { + munmap( m_buffer, m_filesize ); + m_buffer = 0; + m_ismmap = false; + } + else + { +#endif + delete m_buffer; + m_buffer = 0; +#ifdef USE_MMAP + } +#endif + if( m_fd >= 0 ) + { + ::close( m_fd ); + m_fd = -1; + } +} + +bool mgStream::seek(unsigned long long pos) +{ + printf( "mgStream::seek\n" ); + if( m_fd >= 0 && pos >= 0 && pos <= m_filesize ) + { + printf( "mgStream::seek valid file and position detected\n" ); + + m_buffpos = 0; + m_fill = 0; + + if( m_ismmap ) + { + m_readpos = pos; + + printf( "mgStream::seek: returning true\n" ); + return true; + } + else + { + if( ( m_readpos = lseek64( m_fd, pos, SEEK_SET ) ) >=0 ) + { + if( m_readpos != pos ) + { + dsyslog( "seek mismatch in %s, wanted %lld, got %lld", m_filename.c_str(), pos, m_readpos ); + } + printf( "mgStream::seek: returning true\n" ); + return true; + } + else + { + esyslog( "ERROR: seeking failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) ); + } + } + } + else + { + printf( "mp3: bad seek call fd=%d pos=%lld name=%s\n", m_fd, pos, m_filename.c_str() ); + } + + printf( "mgStream::seek: returning false\n" ); + return false; +} + +bool mgStream::stream(unsigned char * &data, + unsigned long &len, + const unsigned char *rest) +{ + if( m_fd >= 0 ) + { + if( m_readpos < m_filesize ) + { + if( m_ismmap ) + { + if( rest && m_fill ) + { + m_readpos = (rest - m_buffer); // take care of remaining data + } + m_fill = m_filesize - m_readpos; + data = m_buffer + m_readpos; + len = m_fill; + m_buffpos = m_readpos; + m_readpos += m_fill; + + return true; + } + else + { + if( rest && m_fill ) + { // copy remaining data to start of buffer + m_fill -= ( rest - m_buffer); // remaing bytes + memmove( m_buffer, rest, m_fill ); + } + else + { + m_fill = 0; + } + + int r; + do + { + r = read( m_fd, m_buffer + m_fill, + MP3FILE_BUFSIZE - m_fill ); + } while( r == -1 && errno == EINTR ); + + if( r >= 0 ) + { + m_buffpos = m_readpos - m_fill; + m_readpos += r; + m_fill += r; + data = m_buffer; + len = m_fill; + + return true; + } + else + { + esyslog("ERROR: read failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) ); + } + } + } + else + { + len = 0; + return true; + } + } + return false; +} + +bool mgStream::removable() +{ + // we do not handle removable media at this time + return false; +} + +bool mgStream::fileinfo( bool log ) +{ + struct stat64 ds; + + if( !stat64( m_filename.c_str(), &ds ) ) + { + printf( "mgStream::fileinfo: stat64 == 0\n" ); + + if( S_ISREG( ds.st_mode ) ) + { + m_fsID = ""; + m_fsType = 0; + + struct statfs64 sfs; + + if( !statfs64( m_filename.c_str(), &sfs) ) + { + if( removable() ) + { + char *tmpbuf; + asprintf( &tmpbuf, "%llx:%llx", sfs.f_blocks, sfs.f_files ); + m_fsID = tmpbuf; + free( tmpbuf ); + } + m_fsType = sfs.f_type; + } + else + { + if( errno != ENOSYS && log ) + { + esyslog("ERROR: can't statfs %s: %s", m_filename.c_str(), strerror(errno) ); + } + } + + m_filesize = ds.st_size; + m_ctime = ds.st_ctime; + +#ifdef CDFS_MAGIC + if( m_fsType == CDFS_MAGIC ) + { + m_ctime=0; // CDFS returns mount time as ctime + } +#endif + // infodone tells that info has been read, like a cache flag + // InfoDone(); + return true; + } + else + { + if(log) + { + esyslog("ERROR: %s is not a regular file", m_filename.c_str() ); + } + } + } + else + { + if(log) + { + esyslog("ERROR: can't stat %s: %s", m_filename.c_str(), strerror(errno) ); + } + + printf( "mgStream::fileinfo: stat64 != 0 for %s\n", m_filename.c_str() ); + } + + return false; +} diff --git a/vdr_stream.h b/vdr_stream.h new file mode 100644 index 0000000..68f7ea7 --- /dev/null +++ b/vdr_stream.h @@ -0,0 +1,57 @@ +/*! + * \file vdr_stream.h + * \brief Definitions of media streams + * + * \version $Revision: 1.2 $ + * \date $Date: 2004/05/28 15:29:19 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: lvw $ + * + * $Id: vdr_stream.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001-2003 Stefan Huelswitt + */ + +#ifndef ___STREAM_H +#define ___STREAM_H + +#include + +#include "vdr_decoder.h" + +class cNet; + +// ---------------------------------------------------------------- + +class mgStream // : public mgFileInfo +{ +private: + int m_fd; + bool m_ismmap; + + // from cFileInfo + std::string m_filename, m_fsID; + unsigned long long m_filesize; + time_t m_ctime; + long m_fsType; + + bool fileinfo( bool log ); + bool removable(); + +protected: + unsigned char *m_buffer; + unsigned long long m_readpos, m_buffpos; + unsigned long m_fill; +public: + mgStream( std::string filename ); + virtual ~mgStream(); + virtual bool open(bool log = true); + virtual void close(); + virtual bool stream( unsigned char *&data, unsigned long &len, const unsigned char *rest=NULL ); + virtual bool seek( unsigned long long pos = 0 ); + virtual unsigned long long bufferPos() { return m_buffpos; } +}; + +#endif //___STREAM_H -- cgit v1.2.3 From 2370a13b7f6512147550e5eec8a773e69d49119b Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 28 May 2004 15:30:48 +0000 Subject: Merged and added import scripts git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@99 e10066b5-e1e2-0310-b819-94efdf66514b --- scripts/createdb.mysql | 10 + scripts/createtables.mysql | 235 ++++ scripts/gddb.pm | 344 +++++ scripts/gdgentools.pm | 1606 +++++++++++++++++++++ scripts/gdimport.pl | 254 ++++ scripts/gdio.pm | 349 +++++ scripts/gdparams.pm | 336 +++++ scripts/gdserv.pm | 3305 ++++++++++++++++++++++++++++++++++++++++++++ scripts/gdsoundcard.pm | 149 ++ scripts/gdupdate.pm | 509 +++++++ scripts/genres.txt | 224 +++ scripts/languages.txt | 45 + scripts/make-db | 8 + scripts/make-empty-db | 24 + scripts/make-tables | 26 + scripts/musictypes.txt | 4 + scripts/myhash.pm | 29 + scripts/sources.txt | 6 + 18 files changed, 7463 insertions(+) create mode 100755 scripts/createdb.mysql create mode 100755 scripts/createtables.mysql create mode 100755 scripts/gddb.pm create mode 100755 scripts/gdgentools.pm create mode 100755 scripts/gdimport.pl create mode 100755 scripts/gdio.pm create mode 100755 scripts/gdparams.pm create mode 100755 scripts/gdserv.pm create mode 100755 scripts/gdsoundcard.pm create mode 100755 scripts/gdupdate.pm create mode 100755 scripts/genres.txt create mode 100755 scripts/languages.txt create mode 100755 scripts/make-db create mode 100755 scripts/make-empty-db create mode 100755 scripts/make-tables create mode 100755 scripts/musictypes.txt create mode 100755 scripts/myhash.pm create mode 100755 scripts/sources.txt diff --git a/scripts/createdb.mysql b/scripts/createdb.mysql new file mode 100755 index 0000000..8b9ea42 --- /dev/null +++ b/scripts/createdb.mysql @@ -0,0 +1,10 @@ +/* Creates DB and opens it to any user */ +/* Run this mysql macro as root! */ + +DROP DATABASE IF EXISTS GiantDisc; +CREATE DATABASE GiantDisc; +use GiantDisc; +grant all privileges on GiantDisc.* to music@'%'; +grant all privileges on GiantDisc.* to music@localhost; +grant all privileges on GiantDisc.* to apache@localhost with grant option; + diff --git a/scripts/createtables.mysql b/scripts/createtables.mysql new file mode 100755 index 0000000..5969ce2 --- /dev/null +++ b/scripts/createtables.mysql @@ -0,0 +1,235 @@ +-- MySQL dump 8.21 +-- +-- Host: localhost Database: GiantDisc +--------------------------------------------------------- +-- Server version 3.23.49 + +-- +-- Current Database: GiantDisc +-- + +--CREATE DATABASE /*!32312 IF NOT EXISTS*/ GiantDisc; + +USE GiantDisc; + +-- +-- Table structure for table 'album' +-- + +drop table if exists album; +CREATE TABLE album ( + artist varchar(255) default NULL, + title varchar(255) default NULL, + cddbid varchar(20) NOT NULL default '', + coverimg varchar(255) default NULL, + covertxt mediumtext, + modified date default NULL, + genre varchar(10) default NULL, + PRIMARY KEY (cddbid), + KEY artist (artist(10)), + KEY title (title(10)), + KEY genre (genre), + KEY modified (modified) +) TYPE=MyISAM; + +-- +-- Table structure for table 'genre' +-- + +drop table if exists genre; +CREATE TABLE genre ( + id varchar(10) NOT NULL default '', + id3genre smallint(6) default NULL, + genre varchar(255) default NULL, + freq int(11) default NULL, + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'language' +-- + +drop table if exists language; +CREATE TABLE language ( + id varchar(4) NOT NULL default '', + language varchar(40) default NULL, + freq int(11) default NULL, + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'musictype' +-- + +drop table if exists musictype; +CREATE TABLE musictype ( + musictype varchar(40) default NULL, + id tinyint(3) unsigned NOT NULL auto_increment, + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'player' +-- + +drop table if exists player; +CREATE TABLE player ( + ipaddr varchar(255) NOT NULL default '', + uichannel varchar(255) NOT NULL default '', + logtarget int(11) default NULL, + cdripper varchar(255) default NULL, + mp3encoder varchar(255) default NULL, + cdromdev varchar(255) default NULL, + cdrwdev varchar(255) default NULL, + id int(11) NOT NULL default '0', + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'playerstate' +-- + +drop table if exists playerstate; +CREATE TABLE playerstate ( + playerid int(11) NOT NULL default '0', + playertype int(11) NOT NULL default '0', + snddevice varchar(255) default NULL, + playerapp varchar(255) default NULL, + playerparams varchar(255) default NULL, + ptlogger varchar(255) default NULL, + currtracknb int(11) default NULL, + state varchar(4) default NULL, + shufflepar varchar(255) default NULL, + shufflestat varchar(255) default NULL, + pauseframe int(11) default NULL, + framesplayed int(11) default NULL, + framestotal int(11) default NULL, + anchortime bigint(20) default NULL, + PRIMARY KEY (playerid,playertype) +) TYPE=HEAP; + +-- +-- Table structure for table 'playlist' +-- + +drop table if exists playlist; +CREATE TABLE playlist ( + title varchar(255) default NULL, + author varchar(255) default NULL, + note varchar(255) default NULL, + created timestamp(8) NOT NULL, + id int(10) unsigned NOT NULL auto_increment, + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'playlistitem' +-- + +drop table if exists playlistitem; +CREATE TABLE playlistitem ( + playlist int(11) NOT NULL default '0', + tracknumber mediumint(9) NOT NULL default '0', + trackid int(11) default NULL, + PRIMARY KEY (playlist,tracknumber) +) TYPE=MyISAM; + +-- +-- Table structure for table 'playlog' +-- + +drop table if exists playlog; +CREATE TABLE playlog ( + trackid int(11) default NULL, + played date default NULL, + id tinyint(3) unsigned NOT NULL auto_increment, + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'recordingitem' +-- + +drop table if exists recordingitem; +CREATE TABLE recordingitem ( + trackid int(11) default NULL, + recdate date default NULL, + rectime time default NULL, + reclength int(11) default NULL, + enddate date default NULL, + endtime time default NULL, + repeat varchar(10) default NULL, + initcmd varchar(255) default NULL, + parameters varchar(255) default NULL, + atqjob int(11) default NULL, + id int(11) NOT NULL default '0', + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'source' +-- + +drop table if exists source; +CREATE TABLE source ( + source varchar(40) default NULL, + id tinyint(3) unsigned NOT NULL auto_increment, + PRIMARY KEY (id) +) TYPE=MyISAM; + +-- +-- Table structure for table 'tracklistitem' +-- + +drop table if exists tracklistitem; +CREATE TABLE tracklistitem ( + playerid int(11) NOT NULL default '0', + listtype smallint(6) NOT NULL default '0', + tracknb int(11) NOT NULL default '0', + trackid int(11) NOT NULL default '0', + PRIMARY KEY (playerid,listtype,tracknb) +) TYPE=MyISAM; + +-- +-- Table structure for table 'tracks' +-- + +drop table if exists tracks; +CREATE TABLE tracks ( + artist varchar(255) default NULL, + title varchar(255) default NULL, + genre1 varchar(10) default NULL, + genre2 varchar(10) default NULL, + year smallint(5) unsigned default NULL, + lang varchar(4) default NULL, + type tinyint(3) unsigned default NULL, + rating tinyint(3) unsigned default NULL, + length smallint(5) unsigned default NULL, + source tinyint(3) unsigned default NULL, + sourceid varchar(20) default NULL, + tracknb tinyint(3) unsigned default NULL, + mp3file varchar(255) default NULL, + condition tinyint(3) unsigned default NULL, + voladjust smallint(6) default '0', + lengthfrm mediumint(9) default '0', + startfrm mediumint(9) default '0', + bpm smallint(6) default '0', + lyrics mediumtext, + bitrate varchar(10) default NULL, + created date default NULL, + modified date default NULL, + backup tinyint(3) unsigned default NULL, + id int(11) NOT NULL auto_increment, + PRIMARY KEY (id), + KEY title (title(10)), + KEY mp3file (mp3file(10)), + KEY genre1 (genre1), + KEY genre2 (genre2), + KEY year (year), + KEY lang (lang), + KEY type (type), + KEY rating (rating), + KEY sourceid (sourceid), + KEY artist (artist(10)) +) TYPE=MyISAM; + diff --git a/scripts/gddb.pm b/scripts/gddb.pm new file mode 100755 index 0000000..88f5bea --- /dev/null +++ b/scripts/gddb.pm @@ -0,0 +1,344 @@ +################################################## +# +# GiantDisc mp3 Jukebok +# +# © 2000, Rolf Brugger +# +################################################## + + +### +### GiantDisc: Common database routines (mainly db modification) +### + +package gddb; + +use strict; + +### Init constants + + +#BEGIN{ +#} + +############################################################ +### TOOL ROUTINES ### +############################################################ + +############################################################ + +sub sql_escape_squotes{ + ### escapes single quotes of the string passed as argument + # + # Parameter: string to be quoted + # Returns: quoted string + + # Usually you would use $dbh->quote instead, except if you don't want + # to add single quotes around the string + + my $sqlstring = $_[0]; + $sqlstring =~ s/'/\\'/g; + return $sqlstring; +} + + +############################################################ +### +sub field_where_clause{ # returns a string with a SQL-where clause + # for a text field like artist or title. If + # keyword is empty, an empty string is returned + # + # - Multiple keywords are space separated + # - Field begins are matched using the * wildcard + + # Examples: + # field="artist" keyword="abc" + # -> "AND artist LIKE + # + # field="artist" keyword="abc xyz" + # -> "AND artist LIKE "%abc%" AND artist LIKE "%xyz%" " + # + # field="artist" keyword="abc*" (everything after * is ignored) + # -> "AND artist LIKE "abc%" " + # + + my ($fieldname,$keywords) = @_; + $keywords = sql_escape_squotes($keywords); + my $cmd = ""; + + if ($keywords ne ""){ + if($keywords =~ m/\*/ ){ + ### wildcard expression + my @words = split (/\*/, $keywords ); + $cmd.=" AND $fieldname LIKE '$words[0]%' "; + } + else{ ### non wildcard expression + my @words = split (/ /, $keywords ); + my $current; + while($current = shift(@words)){ + $cmd.=" AND $fieldname LIKE '%$current%' "; + } + } + } + return $cmd; +} + +############################################################ +### +my $get_all=0; +my $get_tracks=1; +my $get_streams=2; + +sub attrib_where_clause{ # returns a string with a SQL-where clause + # for genre, year, language, type and rating + # field names are prefixed by 'tracks.' that the clauses can also be + # used in JOIN queries. + + my ($get_what, $genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating) = @_; + + my $tmpcmd; + my $cmd=" "; + + ### Genre + $tmpcmd="0 "; # false OR ... + if ($genre1 ne ""){ + $tmpcmd.="OR tracks.genre1 LIKE '$genre1%' OR tracks.genre2 LIKE '$genre1%' ";}; + if ($genre2 ne ""){ + $tmpcmd.="OR tracks.genre1 LIKE '$genre2%' OR tracks.genre2 LIKE '$genre2%' ";}; + if (length($tmpcmd)>3){ # genre query not empty? + $cmd .= " AND ($tmpcmd) "; + } + + ### Year + if(length($yearfrom)==4){ $cmd.=" AND tracks.year >= ".$yearfrom;} + if(length($yearto)==4){ $cmd.=" AND tracks.year <= ".$yearto;} + + ### Language + if ($lang ne ""){ $cmd.=" AND tracks.lang = '$lang' ";}; + + ### type + if ($type ne ""){ $cmd.=" AND tracks.type = '$type' ";}; + + ### rating + if ($rating ne ""){ + if ($rating == 0) { + $cmd.=" AND tracks.rating = $rating "; + } + else{ + $cmd.=" AND tracks.rating >= $rating "; + } + } + + ### track/stream/all + if($get_what==$get_tracks){ + $cmd.=" AND mp3file NOT LIKE 'http://%' "; + } + if($get_what==$get_streams){ + $cmd.=" AND mp3file LIKE 'http://%' "; + } + + return $cmd; +} + +############################################################ +### +sub track_where_clause{ # returns the where clause without keyword "WHERE" + my ($get_what, $artist,$title,$genre1,$genre2,$yearfrom,$yearto, + $lang,$type,$rating, + $ordercmd) = @_; + + my $where=" 1 "; # true AND ... + + ### Artist + $where .= field_where_clause("artist",$artist); + ### Title + $where .= field_where_clause("title",$title); + ### genre, etc ... + $where.= attrib_where_clause($get_what, $genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating); + + return $where; +} + +############################################################ + +sub track_order_clause{ # returns the order clause with keyword "ORDER BY" + my ($get_what, $artist,$title,$genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating, + $ordercmd) = @_; + + my $order = ""; + + if(length($ordercmd)>1){ + $order = "ORDER BY "; + if ($ordercmd =~ m/random/ ){ + $order .= "RAND() "; + } + elsif($ordercmd =~ m/year/ ){ + $order .= "year "; + } + elsif($ordercmd =~ m/recd/ ){ + $order .= "created "; + } + elsif($ordercmd =~ m/modd/ ){ + $order .= "modified "; + } + elsif($ordercmd =~ m/recmod/ ){ + $order .= "GREATEST(created,modified) "; + } + + if($ordercmd =~ m/-inv/ ){ + $order .= " DESC "; + } + } + + return $order; +} + +############################################################ +### +sub album_where_clause{ + my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, + $lang,$type,$rating, + $ordercmd) = @_; + + my $where=" 1 "; # true AND ... + ### Album: Artist + $where .= gddb::field_where_clause("album.artist",$artist); + ### Album: Title + $where .= gddb::field_where_clause("album.title",$title); + ### Track: genre, etc ... + $where.= gddb::attrib_where_clause(1, $genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating); + +#print "ALBUM WHERE: $where\n"; + return $where; +} + +############################################################ + +sub album_order_clause{ # returns the order clause with keyword "ORDER BY" + my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating, + $ordercmd) = @_; + + my $order = ""; + + if(length($ordercmd)>1){ + $order = "ORDER BY "; + if ($ordercmd =~ m/random/ ){ + $order .= "RAND() "; + } + elsif($ordercmd =~ m/year/ ){ + $order .= "tracks.year "; + } + elsif($ordercmd =~ m/recd/ ){ + $order .= "album.modified "; + } + elsif($ordercmd =~ m/modd/ ){ + $order .= "album.modified "; + } + elsif($ordercmd =~ m/recmod/ ){ + $order .= "album.modified "; + } + + if($ordercmd =~ m/-inv/ ){ + $order .= " DESC "; + } + } + + return $order; +} + +############################################################ +### DB CREATION & UPDATE ### +############################################################ + +############################################################ +### Creates/updates a new track record + # If $id is empty, a new record is created. Otherwise, the record + # is updated. + # Returns the (new) id + +sub insert_track_record{ + my ($dbh,$artist,$title,$genre1,$genre2,$year, + $lang,$type,$rating,$length,$source,$sourceid, + $tracknb,$mp3file,$condition,$voladjust, + $lengthfrm,$startfrm,$bpm,$bitrate, + $created,$id, ### these two fields are only defined on update! + ) = @_; + + if(length($artist)==0){$artist="-";}; + if(length($title)==0) {$title="-";}; + if(length($year)<4) {$year="0";}; + if(length($type)==0) {$type="NULL";}; + if(length($rating)==0){$rating="NULL";}; + if(length($length)==0){$length="0";}; + if(length($source)==0){$source="0";}; + if(length($tracknb)==0){$tracknb="0";}; + if(length($condition)==0){$condition="0";}; + if(length($voladjust)==0){$voladjust="0";}; + if(length($lengthfrm)==0){$lengthfrm="0";}; + if(length($startfrm)==0) {$startfrm="0";}; + if(length($bpm)==0) {$bpm="0";}; + if(length($bitrate)==0) {$bitrate="128";}; + if(length($created)==0) {$created="CURDATE()";}; + + + my $sqlcmd; + $sqlcmd = + "artist=".$dbh->quote($artist).", " # quote adds single quotes around the string! + ."title=".$dbh->quote($title).", " + ."genre1='$genre1', " + ."genre2='$genre2', " + ."year = $year, " + ."lang ='$lang', " + ."type = $type, " + ."rating=$rating, " + ."length=$length, " + ."source=$source, " + ."sourceid='$sourceid', " + ."tracknb=$tracknb, " + ."mp3file='$mp3file', " + ."condition=$condition, " + ."voladjust=$voladjust, " + ."lengthfrm=$lengthfrm, " + ."startfrm=$startfrm, " + ."bpm=$bpm, " + ."bitrate='$bitrate', "; + + if(length($id)==0){ + ### INSERT a new record + $sqlcmd = "INSERT tracks SET ".$sqlcmd + ."created=CURDATE() "; + } + else{ + ### REPLACE an existing record + $sqlcmd = "UPDATE tracks SET ".$sqlcmd + ."created='$created', " + ."modified=CURDATE() " + ."WHERE id=$id "; + } + + #print("SQL: $sqlcmd \n"); + my $sth = $dbh->prepare($sqlcmd); + $sth->execute; + + if(length($id)==0){ ### if new record created + $id = $sth->{mysql_insertid}; + } + $sth->finish; + return $id; +} + + + + + +############################################################ +### QUERIES ### +############################################################ +# + + + + + +#### end +1; diff --git a/scripts/gdgentools.pm b/scripts/gdgentools.pm new file mode 100755 index 0000000..3522302 --- /dev/null +++ b/scripts/gdgentools.pm @@ -0,0 +1,1606 @@ +package gdgentools; + +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2000, Rolf Brugger +# +################################################## + + + +### General tool routines + +use lib '/home/andi/muggle/import'; +use gdparams; +use IO::Socket; + + +### Constants +my $pl_list = 0; # play list +my $rp_list = 1; # rip list +my $co_list = 2; # compression list +my $cr_list = 3; # cd recording list + + + + + +#use lib '/usr/local/bin'; +#BEGIN{;} +#END{;} + + +############################################################ +### Shuffle routine +sub fisher_yates_shuffle {# generate a random permutation of @array in place + my $array = shift; + my $i; + for ($i = @$array; --$i; ) { + my $j = int rand ($i+1); + next if $i == $j; + @$array[$i,$j] = @$array[$j,$i]; + } +} +# USAGE: +# fisher_yates_shuffle( \@array ); # permutes @array in placesub query_random_artists{ + + + + + +############################################################ +### barix exstreamer routines + +sub exstreamer_command{ + # sends a command string to the exstreamer at 'playerhost', without + # waiting for a result that is sent back by the exstreamer + + my ($playerhost, $command) = @_; + my $port = 12302; # default exstreamer tcp command port + my $sock = new IO::Socket::INET( PeerAddr => $playerhost, + PeerPort => $port, + Proto => 'tcp'); + die "Error: could not open socket $opt_h:$port. $!\n" unless $sock; + + ### send the command + print $sock $command."\n"; + close($sock); +} + + +sub exstreamer_command_res{ + # sends a command string to the exstreamer at 'playerhost'. + # It returns the first line that is sent back by the exstreamer (last \n chopped off) + + my ($playerhost, $command) = @_; + my $port = 12302; # default exstreamer tcp command port + my $sock = new IO::Socket::INET( PeerAddr => $playerhost, + PeerPort => $port, + Proto => 'tcp'); + die "Error: could not open socket $opt_h:$port. $!\n" unless $sock; + + ### send a command + print $sock $command."\n"; + my $res = <$sock>; + close($sock); + chop $res; + return $res; +} + + +############################################################ +### Playlist routines + + +# creates new playstate record. If one with the same playerid/audiochannel +# exists, it is overwritten. +sub pll_new_playstate{ + my ($dbh, $playerid, $audiochannel, $playertype, $snddevice, $playerapp, $playerparams, $ptlogger) = @_; + $dbh->do("REPLACE INTO playerstate SET " + ."playerid=$playerid, " + ."audiochannel=$audiochannel, " + ."playertype=$playertype, " + ."snddevice='$snddevice', " + ."playerapp='$playerapp', " + ."playerparams='$playerparams', " + ."ptlogger='$ptlogger', " + ."currtracknb=0, " + ."state='st', " # stopped + ."framesplayed=0, " + ."framestotal=0 " + ); +} + +# deletes playstate record +sub pll_del_playstate{ + my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it + $dbh->do("REPLACE FROM playerstate " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel "); +} + + +# Returns th specified player-type +sub pll_get_playertype{ + my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it + + my $sth = $dbh->prepare( + "SELECT playertype,ipaddr,snddevice FROM player,playerstate " + ."WHERE player.id=playerstate.playerid " + ." AND player.id=$playerid " + ." AND playerstate.audiochannel=$audiochannel"); + + my $nbrec = $sth->execute; + #print("$nbrec playstates found (should be exactly 1)\n"); + + my ($playertype,$playerhost,$snddevice); + + if($row = $sth->fetchrow_hashref){ + $playertype = $row->{playertype}; + $playerhost = $row->{ipaddr}; + $snddevice = $row->{snddevice}; + } + else{ + ### This case should not happen! make the best out of it + print ("ERROR: can't get playertype for player $playerid channel $audiochannel\n"); + print (" no player/playerstate record found\n"); + } + $sth->finish; + + return ($playertype,$playerhost,$snddevice); +} + + +# Returns the current playstate +sub pll_get_playstate{ + my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it + + my $sth = $dbh->prepare( + "SELECT * FROM playerstate " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); + + my $nbrec = $sth->execute; + #print("$nbrec playstates found (should be exactly 1)\n"); + + my $currtracknb = 0; + my $state = "st"; + my $shufflestat = ""; + + if($row = $sth->fetchrow_hashref){ + $currtracknb = $row->{currtracknb}; + $state = $row->{state}; + $framesplayed= $row->{framesplayed}; + $shufflestat = $row->{shufflestat}; + } + else{ + ### This case should not happen! make the best out of it + #pll_new_playstate($dbh, $playerid, $audiochannel, "", "", ""); + print ("ERROR: can't get playstate for player $playerid channel $audiochannel\n"); + print (" no playerstate record found\n"); + } + $sth->finish; + + return ($currtracknb, $state, $framesplayed, $shufflestat); +} + + +# Returns +sub playerdefinition{ + my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it + + my $sth = $dbh->prepare( + "SELECT playertype, ipaddr, snddevice FROM player,playerstate " + ."WHERE player.id=$playerid AND playerstate.playerid=$playerid AND audiochannel=$audiochannel"); + + my $nbrec = $sth->execute; + #print("$nbrec playstates found (should be exactly 1)\n"); + + my ($playertype, $playerhost, $sounddevice); + my $playertype = 0; + my $playerhost = "localhost"; + my $sounddevice = "/dev/dsp"; + + if($row = $sth->fetchrow_hashref){ + $playertype = $row->{playertype}; + $playerhost = $row->{ipaddr}; + $sounddevice = $row->{snddevice}; + } + else{ + ### This case should not happen! + print ("ERROR: can't get player definition for player $playerid channel $audiochannel\n"); + print (" no playerstate record found\n"); + } + $sth->finish; + + return ($playertype, $playerhost, $sounddevice); +} + + +# Returns the process id of the player (that was previously stored in the DB) +sub pll_get_player_processid{ + my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it + my $sth = $dbh->prepare( + "SELECT processid FROM playerstate " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); + my $nbrec = $sth->execute; + my $playerpid = -1; + if($row = $sth->fetchrow_hashref){ + $playerpid = $row->{processid}; + } + else{ + ### This case should not happen! make the best out of it + print ("ERROR: can't get playstate for player $playerid channel $audiochannel\n no playerstate record found\n"); + } + $sth->finish; + return ($playerpid); +} + + + +# Writes the playstate to the playstate record +# Parameters: +# currtracknb +# state (one of: pl, st, in, ff, Ff, rw, Rw) [in=pause(interrupted)] +sub pll_write_playstate{ + my ($dbh, $playerid, $audiochannel, $currtracknb, $state) = @_; + my $retval = $dbh->do( + "UPDATE playerstate " + ."SET currtracknb=$currtracknb, state='$state' " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); +} + + +# Writes the players process id to the playstate record +# The programs 'gdplayd.pl' and 'gdplaytmsim.pl' write their process id into +# the DB. This is necessary to efficiently stop playing or rewinding. +sub pll_write_player_processid{ + my ($dbh, $playerid, $audiochannel, $playerpid) = @_; + my $retval = $dbh->do( + "UPDATE playerstate " + ."SET processid=$playerpid " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); +} + +#sub pll_clear_player_processid{ +# my ($dbh, $playerid, $audiochannel, $playerpid) = @_; +# if ($gdparms::multiclients){ +##print "clearing pid \n"; +# my $retval = $dbh->do( +# "UPDATE playerstate " +# ."SET processid=0 " +# ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); +# } +#} + + +### Shuffle Play Parameters +sub pll_write_shuffleparameters{ + my ($dbh, $playerid, $audiochannel, $parameterstring, $statisticsstring) = @_; + print("writing shuffleparamstring: '$parameterstring'\n"); + my $retval = $dbh->do( + "UPDATE playerstate " + ."SET shufflepar =".$dbh->quote($parameterstring).", " + ." shufflestat=".$dbh->quote($statisticsstring)." " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); +} + + + +####################################################################### +### Stop playing +# +# Stopping to play means killing the specifig playing-deamon 'gdplayd.pl' +# and all the sub-processes it has spawned. +# +# As long as only one instance of a GiantDisc server is running on a +# machine, we can just blindly kill all potentially spawned processes by +# name (see routine 'kill_all_play_processes'). +# +# If we have more than one server instance on the same machine, this blindly +# killing method doesn't work anymore. Stopping to play on one instance would +# terminate the play processes of all other server instances. It is therefore +# necessary to specifically kill the involved processes. I have tried many +# (really a lot of) approaches during 2 years - nothing really worked well. +# There were 2 main problems: +# - killing the processes was far too slow +# - multiple instances of gdplayd.pl and it sub-processes appeared leading +# to locked soundcards etc. ( This especially happened, when playing was +# quickly stopped and restarted, or after fast sequences of 'play next' +# commands. +# +# The currently adopted however method seems to work fine, it is robust and +# efficient enough. It is explained in detail below, see routines plmsg_... +# + + +### tested and not well working: Killall and Killfam! +### Comment: Proc::Killall is terribly slow (much slower than system("killall...")) +### using Proc::Killfam is unstable because of overlapping playing +### commands, that inevitably lead to multiple instances of gdplayd.pl +### (mainly because pid of gdplayd.pl is known too late?) +### Finally - Proc::Killfam is too slow too! + + +sub stop_play_processes{ + + my ($dbh, $playerid, $audiochannel) = @_; + my $new_kill_player = 1; +# if ($gdparms::multiclients){ + if ($new_kill_player){ + my $playerpid = pll_get_player_processid($dbh, $playerid, $audiochannel); + + if ($playerpid >0){ + ### get all child processes of $playerpid + my $chprocs = `pstree $playerpid -p |tr -d '\012'`; # get process id's, on one text line + my @chpids = split /\(/, $chprocs; + shift @chpids; # pop off first element + foreach $elem (@chpids){ + $elem =~ s/\).*//; + } + + if (scalar(@chpids)>0){ + #print " specifically killing ".join (",",@chpids)."\n"; + system "kill ".join (" ",@chpids); + } + } + } + else{ + kill_all_play_processes(); + } +} + + +sub kill_all_play_processes{ + #print "killing blindly\n"; + system("killall -q gdplayd.pl; " + ."killall -q mpg123; " + ."killall -q ogg123; " + ."killall -q flac; " + ."killall -q rawplay; " + ."killall -q gdplaytmsim.pl; " + ."killall -q gdstream.pl"); + + #killall and killfam are terribly slow! +} + + +####################################################################### +### IPC Message routines to make sure, that a stop operation really kills +# all player processes. +# Method: +# 1) the server starts the player 'gdplayd.pl' in background +# 2) right after starting gdplayd.pl, the server waits for a message of +# gdplayd.pl +# 3) in the init phase of gdplayd.pl, it writes it's own process-id to the +# database and sends then a message to the message queue +# 4) the server receives the message and can continue to process requests +# from the palm +# 5) when the server should now stop playing, it is 100% sure, that the +# correct process-id o the playing process is in the db. +# Killing it (and it's subprocesses) stops playing. + +use IPC::SysV qw(IPC_CREAT S_IRWXU ftok); +use IPC::Msg; + +# Die folgenden Konstanten kennzeichnen die Message-Queue im System. +# RENDEZVOUS muss der Name einer _existierenden_ Datei sein! +# Nur die unteren 8 Bits von RVID sind wichtig und muessen !=0 sein! +# ftok(RENDEZVOUS, RVID) erzeugt einen immer identischen Schluessel, +# so lange die Datei RENDEZVOUS nicht neu angelegt wurde. + +use constant RENDEZVOUS => "/etc"; +#use constant RVID => 121; + +sub rendevous_id{ + my ($playerid, $audiochannel) = @_; +#print "p=$playerid, chn=$audiochannel -> rdvid=".($playerid*16 + $audiochannel + 1)."\n"; + if ($playerid>15 || $audiochannel>15){ + print "\n WARNING:\n "; + print "too many palyers/audiochannels\n"; + print " p=$playerid, chn=$audiochannel -> rdvid=".($playerid*16 + $audiochannel + 1)."\n\n"; + } + return $playerid*16 + $audiochannel + 1; +} +sub plmsg_newqueue{ + ### creates messae queue RENDEVOUS, if it doesn't already exist + my ($playerid, $audiochannel) = @_; + my $rdvid = rendevous_id($playerid, $audiochannel); + use vars qw($msg); + $msg = new IPC::Msg(ftok(RENDEZVOUS, $rdvid), S_IRWXU | IPC_CREAT); + +} + +sub plmsg_send{ + ### appends a message to the queue + my ($playerid, $audiochannel) = @_; + my $rdvid = rendevous_id($playerid, $audiochannel); + my $msg = new IPC::Msg(ftok(RENDEZVOUS, $rdvid), 0); + my ($prio, $text)=(1,"player started");# = @MESSAGES[$i,$i+1]; + $msg->snd($prio, $text, 0); +} + +sub plmsg_waitrcv{ + my ($playerid, $audiochannel) = @_; + ### pulls one message from the message queue, waits until one message is there + my $rdvid = rendevous_id($playerid, $audiochannel); + my $msg = new IPC::Msg(ftok(RENDEZVOUS, $rdvid), 0); + my $buflen = 256; + $prio = $msg->rcv($buf, $buflen, 0, 0); +# print "Found: ($buf, $prio)\n"; +} + + +sub pl_start_playd_and_wait{ +# starts playing-deamon and waits until it has written it's pid in the db + my ($dbhost, $playerid, $audiochannel) = @_; +#print "--> start gdplayd.pl\n"; + system("gdplayd.pl $dbhost $playerid $audiochannel & "); +#print "--> wait for gdplayd.pl to be started\n"; + plmsg_waitrcv($playerid, $audiochannel); +#print "--> message received\n"; +} + + +####################################################################### +####################################################################### +# +# Basic Play Controls (also used as API) + + +sub pl_play{ +# starts playing + my ($dbh, $playerid, $audiochannel) = @_; + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + if(tracklist_get_item($dbh, $playerid, 0, $trackind) < 1){$trackind=1;} # playstate error + stop_play_processes($dbh, $playerid, $audiochannel); + pll_write_playstate($dbh, $playerid, $audiochannel, $trackind, "pl"); + pl_start_playd_and_wait($gdparms::dbhost, $playerid, $audiochannel); +# system("gdplayd.pl $gdparms::dbhost $playerid $audiochannel & "); +} + +sub pl_play_at{ +# starts playing at specified position (seconds) + my ($dbh, $playerid, $audiochannel, $songpos_sec) = @_; +# my ($songpos_sec) = @_; + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + if(tracklist_get_item($dbh, $playerid, 0, $trackind) < 1){$trackind=1;} # playstate error + stop_play_processes($dbh, $playerid, $audiochannel); + my $startframe = $songpos_sec * frames_per_second(); + pll_write_playtime_only($dbh, $playerid, $audiochannel, $startframe); + pll_write_playstate($dbh, $playerid, $audiochannel, $trackind, "pl"); + pl_start_playd_and_wait($gdparms::dbhost, $playerid, $audiochannel); +# system("gdplayd.pl $gdparms::dbhost $playerid $audiochannel & "); +} + +sub pl_stop{ +# stops playing and reset playtime-state + my ($dbh, $playerid, $audiochannel) = @_; + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + my($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); + stop_play_processes($dbh, $playerid, $audiochannel); + pll_write_playstate($dbh, $playerid, $audiochannel, $trackind, "st"); + pll_write_playtime($dbh, $playerid, $audiochannel, 0, $total); +} + +sub pl_pause{ +# stops playing and preserve playtime-state + my ($dbh, $playerid, $audiochannel) = @_; + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + my($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); + stop_play_processes($dbh, $playerid, $audiochannel); + pll_write_playstate($dbh, $playerid, $audiochannel, + $trackind, "in"); # state: interrupted +} + +sub pl_rw{ +# similar as pause + my ($dbh, $playerid, $audiochannel) = @_; + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + my ($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); + stop_play_processes($dbh, $playerid, $audiochannel); + pll_write_playstate($dbh, $playerid, $audiochannel, + $trackind, "rw"); # state: rw + + ### continuously write current playtime to db (every second) + system("gdplaytmsim.pl $gdparms::dbhost $playerid $audiochannel & "); +} + +sub pl_ff{ +# similar as pause + my ($dbh, $playerid, $audiochannel) = @_; + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + my ($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); + stop_play_processes($dbh, $playerid, $audiochannel); + pll_write_playstate($dbh, $playerid, $audiochannel, + $trackind, "ff"); # state: ff + + ### continuously write current playtime to db (every second) + system("gdplaytmsim.pl $gdparms::dbhost $playerid $audiochannel & "); +} + +sub pl_goto{ + ### makes a new track the current track + # sets the current playtime-posititon to zero + # the rest of the playstate is preserved + + use integer; + my ($dbh, $playerid, $audiochannel, $newind) = @_; +# my ($newind) = @_; # the new index (must be valid) + + stop_play_processes($dbh, $playerid, $audiochannel); + + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + pll_write_playstate($dbh, $playerid, $audiochannel, $newind, $state); + pll_write_playtime($dbh, $playerid, $audiochannel, 0, 0); + if($state eq "pl"){ # restart player + pl_start_playd_and_wait($gdparms::dbhost, $playerid, $audiochannel); +# system("gdplayd.pl $gdparms::dbhost $playerid $audiochannel & "); + } + else{ # new 'current' track: load it's total length + my $trackid = tracklist_get_item($dbh, $playerid, 0, $newind); + if (length($trackid)>0) { + my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id = $trackid"); + my $cnt = $sth->execute; + if ($cnt > 0){ + my $row = $sth->fetchrow_hashref; + pll_write_playtime($dbh, $playerid, $audiochannel, 0, $row->{length}*frames_per_second()); + } + else{ + pll_write_playtime($dbh, $playerid, $audiochannel, 0, 0); + } + $sth->finish; + } + else{ + pll_write_playtime($dbh, $playerid, $audiochannel, 0, 0); + } + } +} + +sub pl_next{ + my ($dbh, $playerid, $audiochannel) = @_; + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + my $listlen = tracklist_get_nb_items($dbh, $playerid, $pl_list); + if ($trackind < $listlen) {$trackind++;} + pl_goto($dbh, $playerid, $audiochannel, $trackind); +} + +sub pl_prev{ + my ($dbh, $playerid, $audiochannel) = @_; + my $frames5sec = 5*frames_per_second(); # nb frames in 5 sec + my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); + my ($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); + if ($trackind>0 && $played<$frames5sec){$trackind--;} + pl_goto($dbh, $playerid, $audiochannel, $trackind); +} + +####################################################################### +####################################################################### + + + + + +####################################################################### +# +# Playtime routines: +# The current position is always saved in the field 'framesplayed' +# If the player app can't continuously write this field, another realtime +# app has to write the current playtime (and ff, rew position) + +# mp3 constants +my $samplesPerFrame = 1152; +my $samplesPerSecond = 44100; + +sub frames_per_second{ + use integer; + return $samplesPerSecond/$samplesPerFrame; # = 38.281 +} +sub samples_per_frame{ + return $samplesPerFrame; +} +sub samples_per_second{ + return $samplesPerSecond; +} + + +# Writes the playtime to the playtime record. +# Parameters: played, total (units: frames) +sub pll_write_playtime{ + my ($dbh, $playerid, $audiochannel, $played, $total) = @_; + my $retval = $dbh->do( + "UPDATE playerstate " + ."SET framesplayed=$played, framestotal=$total " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); +} + +sub pll_write_playtime_only{ # like pll_write_playtime without changing 'framestotal' + my ($dbh, $playerid, $audiochannel, $played) = @_; + my $retval = $dbh->do( + "UPDATE playerstate " + ."SET framesplayed=$played " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); +} + + +# Returns the current playtime (frames played and total frames) +sub pll_get_playtime{ + my ($dbh, $playerid, $audiochannel) = @_; + my $played = 0; + my $total = 0; + + use integer; + + my $sth = $dbh->prepare( + "SELECT * FROM playerstate " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); + + my $nbrec = $sth->execute; + #print("$nbrec playtime found (should be exactly 1)\n"); + + if($row = $sth->fetchrow_hashref){ + $total = $row->{framestotal}; + $played = $row->{framesplayed}; + } + $sth->finish; + + return ($played, $total); +} + + +sub seconds_to_hm{ + my ($seconds) = @_; + my ($hours, $minutes); + use integer; # switch to int math + $hours = $seconds / 3600; + $minutes = ($seconds % 3600)/60; + return sprintf("%ih%02im", $hours, $minutes); +} + +sub seconds_to_sm{ + my ($seconds) = @_; + my ($minutes, $sec); + use integer; # switch to int math + $minutes = $seconds / 60; + $sec = $seconds % 60; + return sprintf("%i:%02i", $minutes, $sec); +} + + +### is the string a mp3-file or a mp3-stream? +sub is_mp3stream{ + my ($mp3filename) = @_; + return ($mp3filename =~ /^http:\/\/.*/); # matches "http://" at the beginning? +} + + +####################################################################### + +# Returns the player parameter ($snddevice, $playerapp, $playerparams, $ptlogger, $shufflepar) +sub pll_get_playparams{ + my ($dbh, $playerid, $audiochannel) = @_; + my ($snddevice, $playerapp, $playerparams, $ptlogger); + + my $sth = $dbh->prepare( + "SELECT * FROM playerstate " + ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); + my $nbrec = $sth->execute; + + if($row = $sth->fetchrow_hashref){ + $snddevice = $row->{snddevice}; + $playerapp = $row->{playerapp}; + $playerparams = $row->{playerparams}; + $ptlogger = $row->{ptlogger}; + $shufflepar = $row->{shufflepar}; + } + $sth->finish; + + return ($snddevice, $playerapp, $playerparams, $ptlogger, $shufflepar); +} + + +############################################################ +# Returns the main player parameters +# ($ipaddr, $uichannel, $logtarget, $cdripper, $mp3encoder, $cdromdev, $cdrwdev) +sub pll_get_mainparams{ + my ($dbh, $playerid) = @_; + my ($ipaddr, $uichannel, $logtarget, $cdripper, $mp3encoder, $cdromdev, $cdrwdev); + + my $sth = $dbh->prepare( + "SELECT * FROM player " + ."WHERE id=$playerid" ); + my $nbrec = $sth->execute; + + if($row = $sth->fetchrow_hashref){ + $ipaddr = $row->{ipaddr}; + $uichannel = $row->{uichannel}; + $logtarget = $row->{logtarget}; + $cdripper = $row->{cdripper}; + $mp3encoder= $row->{mp3encoder}; + $cdromdev = $row->{cdromdev}; + $cdrwdev = $row->{cdrwdev}; + } + $sth->finish; + + return ($ipaddr, $uichannel, $logtarget, $cdripper, $mp3encoder, $cdromdev, $cdrwdev); +} + + +############################################################ +### General tracklist functions (table tracklistitem) +# +# 'tracknb' always starts with 0 and is contiguous +# ex: 0,1,2,3 is legal, 0,1,2,4,5 is illegal +# + +sub tracklist_get_nb_items{ + # returns the of tracks in the specified tracklist + my ($dbh, $playerid, $listtype) = @_; + my $listlen = 0; + my $sth = $dbh->prepare( + "SELECT COUNT(*) FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$listtype" ); + $sth->execute; + my @row; + if(@row = $sth->fetchrow_array){ + $listlen = $row[0]; + } + $sth->finish; + return $listlen; +} + +sub tracklist_append_list{ + # appends a list of trackid's to the specified tracklist. + # Parameters: dbh, playerid, listtype, trackids... + my ($dbh) = shift(@_); + my ($playerid) = shift(@_); + my ($listtype) = shift(@_); + + my $curritem = tracklist_get_nb_items($dbh, $playerid, $listtype); + + while($trackid = shift(@_)){ + $dbh->do("INSERT INTO tracklistitem SET " + ."playerid=$playerid, " + ."listtype=$listtype, " + ."tracknb=$curritem, " + ."trackid=$trackid "); + $curritem++; + } +} + + +# moves the specified list chunk down by one +sub tracklist_move_chunk_up{ + my ($dbh, $playerid, $listtype, $first, $last) = @_; + # "shift" specified items up (to higher index) by 1 + # have to do increment item by item because order is important + my $sth = $dbh->prepare( + "SELECT * FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb>=$first AND tracknb<=$last " + ."ORDER BY tracknb DESC" ); # order: highest index first! + $sth->execute; + my $row; + while($row = $sth->fetchrow_hashref){ + $dbh->do( + "UPDATE tracklistitem " + ."SET tracknb=tracknb+1 " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=".$row->{tracknb} ); + } + $sth->finish; +} + + +# removes the specified list item from the list (item index starts with 0) +sub tracklist_del_item{ + my ($dbh, $playerid, $listtype, $trackindex) = @_; + $dbh->do( + "DELETE FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$trackindex" ); + +### should write a routine 'tracklist_move_chunk_down' (like tracklist_move_chunk_up) + # "shift" following items down by 1 + # have to do decrement item by item because order is important + my $sth = $dbh->prepare( + "SELECT * FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb>$trackindex " + ."ORDER BY tracknb" ); + $sth->execute; + my $row; + while($row = $sth->fetchrow_hashref){ + $dbh->do( + "UPDATE tracklistitem " + ."SET tracknb=tracknb-1 " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=".$row->{tracknb} ); + } + $sth->finish; +} + +# removes the all list items from 0 to 'trackindex' +sub tracklist_del_upto_item{ + +### VERY INEFFICIENT IMPLEMENTATION!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + my ($dbh, $playerid, $listtype, $trackindex) = @_; + print("deleting up to $trackindex\n"); + my $counter=0; + while ($counter < $trackindex){ + tracklist_del_item($dbh, $playerid, $listtype, 0); + $counter++; + } +} + + +# reorders the specified list item in the list (item index starts with 0) +# -> 'destpos' must be lower than 'srcpos'! +sub tracklist_reorder_item{ + my ($dbh, $playerid, $listtype, $srcpos, $destpos) = @_; + + ### "move" src-item to a save location + my $savepos = -1000; # a bit too hacky? + $dbh->do( + "UPDATE tracklistitem " + ."SET tracknb=$savepos " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$srcpos" ); + + # "shift" following items up by 1 + tracklist_move_chunk_up($dbh, $playerid, $listtype, $destpos, ($srcpos)-1); + + ### "move" src-item from save location to destination + $dbh->do( + "UPDATE tracklistitem " + ."SET tracknb=$destpos " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$savepos" ); +} + + + +# empties the specified tracklist +sub tracklist_delete{ + my ($dbh, $playerid, $listtype) = @_; + $dbh->do( + "DELETE FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$listtype" ); +} + + +# gets the trackid of the specified list item (item index starts with 0) +# If an error occurs, 0 is returned. +sub tracklist_get_item{ + my ($dbh, $playerid, $listtype, $trackindex) = @_; + my $trackid = 0; + my $sth = $dbh->prepare( + "SELECT * FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$trackindex" ); + my $nbrec = $sth->execute; + if($row = $sth->fetchrow_hashref){ + $trackid = $row->{trackid}; + } + $sth->finish; + return $trackid; +} + + +# Returns the current playlist (list of track ID's) +sub tracklist_get_all{ + my ($dbh, $playerid, $listtype) = @_; + my @playlist=(); + + my $sth = $dbh->prepare( + "SELECT * FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$listtype " + ."ORDER BY tracknb" ); + my $nbrec = $sth->execute; + while($row = $sth->fetchrow_hashref){ + $trackid = $row->{trackid}; + push @playlist, $row->{trackid}; + } + $sth->finish; + + return @playlist; +} + + + +############################################################ +### id3tag functions + +### returns the value of a id3 tag or other mp3 parameters for the specified file +### Possible tagcodes are + +### NEW version: + +# %a Artist [string] +# %b Number of corrupt audio frames [integer] +# %c Comment [string] +# %C Copyright flag [string] +# %e Emphasis [string] +# %E CRC Error protection [string] +# %f Filename without the path [string] +# %F Filename with the path [string] +# %g Musical genre [string] +# %G Musical genre [integer] +# %l Album name [string] +# %L MPEG Layer [string] +# %m Playing time: minutes only [integer] +# %n Track [integer] +# %O Original material flag [string] +# %o Stereo/mono mode [string] +# %p Padding [string] +# %Q Sampling frequency in Hz [integer] +# %q Sampling frequency in KHz [integer] +# %r Bit Rate in KB/s (type and meaning +# affected by -r option) +# %s Playing time: seconds only [integer] +# (usually used in conjunction with # %m) +# %S Total playing time in seconds [integer] +# %t Track Title [string] +# %u Number of good audio frames [integer] +# %v MPEG Version [float] +# %y Year [string] +# %% A single percent sign + +sub get_mp3info{ + my ($tagcode, $filename) = @_; + my $base = gdparams::gdbase(); + my $res = `mp3info -p "$tagcode" "$filename"`; + + ### Error cases + if ($tagcode eq "%S" && length($res)==0){ + my $res2 = `mpg123 -v -t -n 0 "$filename" 2> $base/tmp/gdinfo.tmp;grep Frame $base/tmp/gdinfo.tmp`; + $res2 =~ m/.*\[(.+):(.+).(.+)\].*/; + my $min = $1; + my $sec = $2; + $res = $min*60 + $sec; + print("playlength fixed to $res\n"); + } + if($tagcode eq "%r" && ($res eq "Variable")) + { #check for Variable Bitrate +# $res="Var".`mp3info -p %r -r m "$filename"`; ### don't predeed wit 'wav' - it messes up bitrate calculations of streamer + $res=`mp3info -p %r -r m "$filename"`; + return $res; + } + if ($tagcode eq "%r" && ($res <= 0 )){ + $res = "128"; + } + + return $res; +} + + +############################################################ +### audio metadata with ogg support. +# +# standard ogg tags according to +# http://www.xiph.org/ogg/vorbis/doc/v-comment.html +# -> ARTIST TITLE ALBUM TRACKNUMBER YEAR GENRE COMMENT +# and + +### typical output of ogginfo +#>ogginfo file.ogg +#filename=file.ogg +# +#serial=6039 +#header_integrity=pass +#ALBUM=ob die Engel auch Beine haben +#TITLE=Die Schampullamaschine +#ARTIST=Zentriert ins Antlitz +#DATE=2002 +#TRACKNUMBER=7 +#GENRE=Industrial +#ORGANIZATION=- +#COMMENT=ZIA Ogg Vorbis 1.0 Final +#vendor=Xiph.Org libVorbis I 20020717 +#version=0 +#channels=2 +#rate=44100 +#bitrate_upper=none +#bitrate_nominal=128000 +#bitrate_lower=none +#stream_integrity=pass +#bitrate_average=126915 +#length=50.248005 +#playtime=0:50 +#stream_truncated=false +# +#total_length=50.248005 +#total_playtime=0:50 + +sub oggfile_title{ + my($audiofile) = @_; + $line = `ogginfo "$audiofile" |grep --ignore-case ^title=`; + chop $line; + return substr($line, 6); +} +sub oggfile_artist{ + my($audiofile) = @_; + $line = `ogginfo "$audiofile" |grep --ignore-case ^artist=`; + chop $line; + return substr($line, 7); +} +sub oggfile_album{ + my($audiofile) = @_; + $line = `ogginfo "$audiofile" |grep --ignore-case ^album=`; + chop $line; + return substr($line, 6); +} +sub oggfile_year{ + my($audiofile) = @_; + $line = `ogginfo "$audiofile" |grep --ignore-case ^date=`; + chop $line; + $line = substr($line, 5); + if ($line =~ /\D*(\d*).*/){ + return $1; + } + else { + return 0; + } +} +sub oggfile_lengthsec{ + my($audiofile) = @_; + $line = `ogginfo "$audiofile" |grep --ignore-case ^length=`; + chop $line; + my $lengthsec = substr($line, 7); + return int($lengthsec); +} +sub oggfile_bitrate{ + my($audiofile) = @_; + $line = `ogginfo "$audiofile" |grep --ignore-case ^bitrate_nominal=`; + chop $line; + my $bitrate = substr($line, 16); + return (int(($bitrate/4000)+0.5))*4; # round to modulo 4 +} +sub oggfile_tracknumber{ + my($audiofile) = @_; + $line = `ogginfo "$audiofile" |grep --ignore-case ^tracknumber=`; + chop $line; + $line = substr($line, 12); + return int($line); +} +sub oggfile_genre{ + return ""; +} + +###~CU~ #FLAC_BEGIN + +############################################################ +### FLAC metadata functions +### Autor: Christian Uebber +### +### Uses metaflac (see http://flac.sourceforge.net/) +### for extracting VORBIS_COMMENT metadata. +### +### Future versions may also support extracting +### cue-sheet information. +### + +sub flacfile_title{ + my($audiofile) = @_; + $line = `metaflac --show-vc-field=title "$audiofile"`; + chop $line; + return substr($line, 6); +} + +sub flacfile_artist{ + my($audiofile) = @_; + $line = `metaflac --show-vc-field=artist "$audiofile"`; + chop $line; + return substr($line, 7); +} +sub flacfile_album{ + my($audiofile) = @_; + $line = `metaflac --show-vc-field=album "$audiofile"`; + chop $line; + return substr($line, 6); +} + +sub flacfile_year{ + my($audiofile) = @_; + $line = `metaflac --show-vc-field=date "$audiofile"`; + chop $line; + $line = substr($line, 5); + if ($line =~ /\D*(\d*).*/){ + return $1; + } + else { + return 0; + } +} + +sub flacfile_lengthsec{ + my($audiofile) = @_; + $line = `metaflac --show-total-samples "$audiofile"`; + chop $line; + return int($line/44100); ## Please verify (theoretically correct) +} + +sub flacfile_bitrate{ + my($audiofile) = @_; + $line = `metaflac --show-sample-rate "$audiofile"`; + chop $line; + return (int($line/1000)); # respect maximum field length +# return ($line/1000); # respect maximum field length +# return (int($line)/100); # respect maximum field length +} + +### Alternative: +#sub flacfile_type{ +# my($audiofile) = @_; +# $line = `metaflac --show-sample-rate "$audiofile"`; +# chop $line; +# if ($line="44100"){ +# return "cda"; +# } +# else { +# return ""; +# } +#} + +sub flacfile_tracknumber{ + my($audiofile) = @_; + $line = `metaflac --show-vc-field=tracknumber "$audiofile"`; + chop $line; + $line = substr($line, 12); + return int($line); +} +sub flacfile_genre{ + my($audiofile) = @_; + $line = `metaflac --show-vc-field=genre "$audiofile"`; + chop $line; + return substr($line, 6); +} + +sub flacfile_tracknumber{ + my($audiofile) = @_; + $line = `metaflac --show-vc-field=tracknumber "$audiofile"`; + chop $line; + $line = substr($line, 12); + return int($line); +} + +###~CU~ #FLAC_END + +############################################################ + +sub audiofile_title{ + my($audiofile) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + my $title = ""; + if ($ftype eq "mp3"){ + $title = get_mp3info("%t", $audiofile); + } + elsif ($ftype eq "ogg"){ + $title = oggfile_title($audiofile); + } + elsif ($ftype eq "flac"){ ###~CU~ + $title = flacfile_title($audiofile); + } + + if (length($title) > 0){ + return $title; + } + else { + use File::Basename; + return basename($audiofile); + } +} + + +sub audiofile_artist{ + my($audiofile) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + my $artist = ""; + if ($ftype eq "mp3"){ + $artist = get_mp3info("%a", $audiofile); + } + elsif ($ftype eq "ogg"){ + $artist = oggfile_artist($audiofile); + } + elsif ($ftype eq "flac"){ + $artist = flacfile_artist($audiofile); ###~CU~ + } + + if (length($artist) > 0){ + return $artist; + } + else { + use File::Basename; + return basename($audiofile); + } +} + + +sub audiofile_album{ + my($audiofile) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + if ($ftype eq "mp3"){ + return get_mp3info("%l", $audiofile); + } + elsif ($ftype eq "ogg"){ + return oggfile_album($audiofile); + } + elsif ($ftype eq "flac"){ + return flacfile_album($audiofile); ###~CU~ + } + else { + return "Album name"; + } +} + + +sub audiofile_year{ + my($audiofile) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + if ($ftype eq "mp3"){ + return get_mp3info("%y", $audiofile); + } + elsif ($ftype eq "ogg"){ + return oggfile_year($audiofile); + } + elsif ($ftype eq "flac"){ + return flacfile_year($audiofile); ###~CU~ + } + else { + return 1990; + } +} + + +sub audiofile_lengthsec{ + my($audiofile) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + if ($ftype eq "mp3"){ + return get_mp3info("%S", $audiofile); + } + elsif ($ftype eq "ogg"){ + return oggfile_lengthsec($audiofile); + } + elsif ($ftype eq "flac"){ + return flacfile_lengthsec($audiofile); ###~CU~ + } + else { + return 0; + } +} + + +sub audiofile_bitrate{ + my($audiofile) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + if ($ftype eq "mp3"){ + return get_mp3info("%r", $audiofile); + } + elsif ($ftype eq "ogg"){ + return oggfile_bitrate($audiofile); + } + elsif ($ftype eq "flac"){ + return flacfile_bitrate($audiofile); # ###~CU~ flac: return sampling rate / please check alternative + } + else { + return 128; + } +} + + +sub audiofile_tracknumber{ + my($audiofile, $default_tracknb) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + my $tracknb=0; + if ($ftype eq "mp3"){ + $tracknb = get_mp3info("%n", $audiofile); + } + elsif ($ftype eq "ogg"){ + $tracknb = oggfile_tracknumber($audiofile); + } + elsif ($ftype eq "flac"){ + $tracknb = flacfile_tracknumber($audiofile); + } + + if($tracknb > 0){ + return $tracknb; + } + else { + return $default_tracknb; + } +} + + +sub audiofile_genre{ + ### extracts id3 gerne code or genre string (depends on filetype) + ### returns GD-genre code + my($dbh, $audiofile) = @_; # must be full filename with path! + my $ftype = audio_filetype($audiofile); + if ($ftype eq "mp3"){ + my $id3genre = get_mp3info("%G", $audiofile); + return genre_id3togd($dbh, $id3genre); + } + elsif ($ftype eq "ogg"){ + my $genrestring = oggfile_genre($audiofile); + return genre_stringtogd($dbh, $genrestring); + } + elsif ($ftype eq "flac"){ + my $genrestring = flacfile_genre($audiofile); + return genre_stringtogd($dbh, $genrestring); + } + else { + return ""; + } +} + + +############################################################ + +sub audio_filetype{ + ### the filetype is derived form the filename extension + my($audiofile) = @_; + + if ($audiofile =~ /[Mm][Pp]3$/){ + return "mp3"; + } + if ($audiofile =~ /ogg$/){ + return "ogg"; + } + if ($audiofile =~ /flac$/){ + return "flac"; + } +} + + + +############################################################ + +sub get_bitrate_str{ + # returns format/bitrate in kBit/s (ex. "mp3 128", "ogg 112") + # takes as argument the audio file name with its full path. + my ($audiofile) = @_; + my $bitrate = audiofile_bitrate($audiofile); + my $ftype = audio_filetype($audiofile); + return "$ftype $bitrate"; +} + +sub bitrate_str_to_format_param{ + # gets a bitrate string like "mp3 128" and returns array ("mp3", "128") + my ($bitratestr) = @_; + my ($audiofmt, $audioparam) = split ' ', $bitratestr; # split "mp3 128" + if (length($audiofmt)<2){ # something went wrong + $audiofmt = "mp3"; # set reasonable default values + $audioparam = 128; + } + return ($audiofmt, $audioparam); +} + + +sub get_full_audiofile_path{ + # returns the full path of an audio file in a 00, 01, 02, ... directory + # or an empty string if the file doesn't exist + my ($audiofile) = @_; + my $base = gdparams::gdbase(); + my $fname = `ls $base/[0-9][0-9]/$audiofile`; # get full path + chop($fname); + return $fname; +} + +############################################################ +### Returns the first music directory with enough space + # to save ripped files. If no large enough directory is found + # an empty string is returned. + # Parameter: minimum free space in Mbytes + +sub get_ripdirectory{ + my ($minfreeMbytes) = @_; + my $base = gdparams::gdbase(); + + ### Get mp3 directories + my @mdir = gdparams::mp3dirs(); + + ### Get an mp3 directory with enough space left (1GB) + my $i=0; + my @dfres; + my $mbfree; + my $ripdir=""; + + while($i < @mdir){ + if (-d "$base/$mdir[$i]"){ + @dfres = split / +/, `df -m $base/$mdir[$i]|tail -1`; + $mbfree = $dfres[3]; + #print "$base/$mdir[$i] has $mbfree MB free \n"; + if($mbfree > $minfreeMbytes){ + $ripdir = $mdir[$i]; + last; # break + } + } + else{print "$base/$mdir[$i] is not a directory or does not exist\n";} + $i++; + } + #print("Rip directory: $ripdir \n"); + return $ripdir; +} + + +############################################################ +### import booklet/cover images +sub import_cover_images{ + # imports the jpeg images in a directory and associates them to an album + # the images are imported in lexical order. + # Naming scheme: trxx(cd-id)-(num).jpg, where num is an automatically + # incremented counter. The file imgxx(cd-id)-00.jpg is the front cover, + # the other are the following pages in a booklet. + + # Parameters: 1) dbh, + # 2) full directory path, + # 3) cd-id (like 0x10ac77e0, xx00001496) + # 4) test? (if set to 1, only show what would be done) + + my ($dbh, $fullpath, $cdid, $test) = @_; + + my ($sourcefile, $targetfile); + my $base = gdparams::gdbase(); + print "import images at $fullpath for id $cdid\n"; + + if (length($cdid)<10){ + print "Error (import_cover_images): illegal format of cdid '$cdid'\n"; + return; + } + + opendir INBOX, "$fullpath"; + my @imgfilelist = sort (grep /\.jpg$/, readdir INBOX); + closedir INBOX; + + #print "imgs: ".join (":",@imgfilelist)."\n"; + if (scalar(@imgfilelist) > 0){ + + ### Get a directory with enough space left (1GB) + my $ripdir=get_ripdirectory($gdparams::minfreehdspace); + + if($ripdir ne ""){### put image-file in music directory + my $imgnum = 0; + + foreach $sourcefile (@imgfilelist){ + $targetfile = sprintf ("$base/$ripdir/img%s-%02ld.jpg", $cdid, $imgnum); + if ($test){ + print "test: move '$fullpath/$sourcefile' to '$targetfile'\n"; + } + else{ + print "move '$fullpath/$sourcefile' to '$targetfile'\n"; + system "mv \"$fullpath/$sourcefile\" \"$targetfile\""; + if ($imgnum == 0){ + + my $sqlcmd = ("UPDATE album SET coverimg='img$cdid-00.jpg' WHERE cddbid = SUBSTRING('$cdid',3)"); + #print"\$dbh->do($sqlcmd);\n" + $dbh->do($sqlcmd); + } + } + $imgnum += 1; + }# end foreach + } + else{ + print("Not enough space left on disc \n"); + } + } + else{ + print "no jpeg images found in $fullpath \n"; + } +} + + +############################################################ +### common tool routines + +### returns highest number of imported (=not recorded from audio-CD, =no +### CDDB-ID associated) mp3 tracks. +### Parameter: database handle. +sub last_imported_tracknb{ + my ($dbh) = @_; + + my $mp3num; + my $trkseth = $dbh->prepare('select mp3file from tracks '. # get last record + 'where mp3file like "trxx________%" order by mp3file desc limit 1'); +# 'where mp3file like "trxx%" order by mp3file desc limit 1'); + my $nbmp3files = $trkseth->execute; + if ($nbmp3files > 0){ + $tracks = $trkseth->fetchrow_hashref; + $tracks->{mp3file} =~ m/trxx([0-9]*)\.\w/; # extract number + $mp3num = $1; + } + else{ + $mp3num = 0; + } + $trkseth->finish; + return $mp3num; +} + + +### Translates an id3 genre (numeric) to a GiantDisc genre code (string) +### Parameters: database handle, id3-genre. +### Returns: GiantDisc genre code (or empty string, if no match found) +sub genre_id3togd{ + my ($dbh, $id3genre)= @_; + + my $gdcode = ""; + if(length($id3genre)>0 && $id3genre >= 0){ + my $genseth = $dbh->prepare('select * from genre where id3genre="'. $id3genre . '" '); + my $nbgenres = $genseth->execute; + if ($nbgenres > 0){ + my $genres = $genseth->fetchrow_hashref; + $gdcode = $genres->{id}; + } + $genseth->finish; + } + #print("Translating id3:\"$id3genre\" to gdgenre \"$gdcode\" \n\n"); + return $gdcode; +} + +### Translates a genre string to a GiantDisc genre code (string) +### Parameters: database handle, genre string. +### Returns: GiantDisc genre code (or empty string, if no match found) +sub genre_stringtogd{ + my ($dbh, $genrestring)= @_; + + my $gdcode = ""; + if(length($genrestring)>0){ + my $genseth = $dbh->prepare( # get best=shortest match + "SELECT id,length(genre) AS len FROM genre " + ."WHERE genre like \"%".$genrestring."%\" ORDER BY len"); +# 'select id, from genre where genre="'. $genrestring . '" '); + my $nbgenres = $genseth->execute; + if ($nbgenres > 0){ + my $genres = $genseth->fetchrow_hashref; + $gdcode = $genres->{id}; + } + $genseth->finish; + } + #print("Translating genrestring:\"$genrestring\" to gdgenre \"$gdcode\" \n\n"); + return $gdcode; +} + + + +############################################################################### + +sub iso2ascii{ + # converts all non-ascii characters in the passed string as good + # as possible to ascii characters, and returns the result + + my ($str) = @_; + + $str =~ tr/\xc0-\xc6/A/; + $str =~ tr/\xc7/C/; + $str =~ tr/\xc8-\xcb/E/; + $str =~ tr/\xcc-\xcf/I/; + $str =~ tr/\xd0\xd1/DN/; + $str =~ tr/\xd2-\xd8/O/; + $str =~ tr/\xd9-\xdc/U/; + $str =~ tr/\xdd\xde\xdf/YTs/; + + $str =~ tr/\xe0-\xe6/a/; + $str =~ tr/\xe7/c/; + $str =~ tr/\xe8-\xeb/e/; + $str =~ tr/\xec-\xef/i/; + $str =~ tr/\xf0\xf1/dn/; + $str =~ tr/\xf2-\xf8/o/; + $str =~ tr/\xf9-\xfc/u/; + $str =~ tr/\xfd\xfe\xff/yts/; + + $str =~ tr/\xa0-\xff/_/; + + return $str; +} + +sub ascii2filename{ + # converts all ascii characters in the passed string as good + # as possible to ascii characters that are allowed in filenames, + #and returns the result + + my ($str) = @_; + + $str =~ tr/\//-/; # translate / to - + $str =~ tr/\x2f/_/; + $str =~ s/"/''/g; + + return $str; +} + +############################################################################### + + +1; +# diff --git a/scripts/gdimport.pl b/scripts/gdimport.pl new file mode 100755 index 0000000..6c8f548 --- /dev/null +++ b/scripts/gdimport.pl @@ -0,0 +1,254 @@ +#!/usr/bin/perl + +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2000, Rolf Brugger +# +################################################## + + +# Import script for mp3 tracks. +# +# Non interactive batch import of mp3 tracks. Mp3 informations (title, artist, etc) +# are read from the id3-tags. Well defined id3-tags are mandatory for a +# inclusion of mp3 tracks and a proper update of the database. + + +use lib '/home/andi/muggle/import'; +use gdparams; +use gdgentools; +use gddb; +use DBI; +use Cwd; # get current working dir + +### main + #print ("Args are: @ARGV \n"); + if ($#ARGV < 0){ + print "\nusage: gdimport.pl [options] audiofiles... \n"; + print "\nimports the specified audio files into the GiantDisc system.\n"; + print "It reads the associated metadata (id3/vorbis tags) of each file and \n"; + print "creates a track record for each of it. \n"; + print "Other than in GD, files are not copied!!\n"; + print "\n"; + print "Currently supported file formats: mp3, ogg vorbis, flac.\n"; + print "\n"; + print "Instead of a set of audio files a directory can be specified. In\n"; + print "this case, all audio tracks in the directory are imported. This is\n"; + print "particularly useful to import a bunch of albums with the following\n"; + print "command typed in the shell:\n"; + print "\n"; + print " find . -type d -print -exec gdimport.pl -a {} \\;\n"; + print "\n"; + print "Options:\n"; + print " -t Test. Just display what would be done, whithout changing\n"; + print " the database or copying/moving/linking files\n"; + print " It is recommended to test an import session before really doing it\n"; + print "\n"; + print " -a Interpret the audio files as tracks of an album. An album record\n"; + print " is created and the tracks are imported as usual.\n"; + print " Note: - The first audio file must have a valid album metadata tag\n"; + print " (i.e. id3 tag) to get the album title.\n"; + print " - only one album can be imported at a time\n"; + print " - default values: rating=-, type=1, source=CD.\n"; + print "\n"; + print " -i cddbid importdir\n"; + print " Import all jpg images of the directory 'importdir' and associate\n"; + print " them to the album specified by 'cddbid' (include prefix '0x', 'xx'!). \n"; + print "\n"; + print "\n"; + exit; + } + + #exit; + ### Init constants/variables + my ($fileformat, $cddbid); + my $makealbum = 0; + my $album_created = 0; + my $linkfiles = 1; + my $movefiles = 0; + my $moveimages = 0; + my $test = 0; + my $tracknb = 1; + my ($dbh, $trifile); + my $base = gdparams::gdbase(); # music files base + + ### Open database connection + print ("### Open database connection\n"); + $dbh = DBI->connect("DBI:mysql:GiantDisc", "music", undef) + or die "unable to connect to GiantDisc db"; + + ### init other variables + my $fileid = gdgentools::last_imported_tracknb($dbh); + my @trackrow; + + + ### extract all options first + my $shifted=1; + while ($shifted){ + if ($ARGV[0] eq "-a"){ + print "set option make album\n"; + $makealbum = 1; + shift @ARGV; + } + elsif ($ARGV[0] eq "-l"){ + print "set option link files\n"; + $linkfiles = 1; + shift @ARGV; + } + elsif ($ARGV[0] eq "-m"){ + print "set option move files\n"; + $movefiles = 1; + shift @ARGV; + } + elsif ($ARGV[0] eq "-i"){ + print "import booklet images\n"; + $moveimages = 1; + shift @ARGV; + $cddbid = shift @ARGV; + } + elsif ($ARGV[0] eq "-t"){ + print "set option TEST\n"; + $test = 1; + shift @ARGV; + } + else { + $shifted = 0; + } + } + + if ($moveimages){ + $dirname = $ARGV[0]; + if (-d $dirname){ + # ok. It's a directory + gdgentools::import_cover_images($dbh, $dirname, $cddbid, $test); + } + else{ + print "Error importing images: '$dirname' is not a directory\n"; + } + exit; + } + + + my @musicfiles; + my $dirname=""; + ### Is the first file argument a directory? + if (-d $ARGV[0]){ + # It's a directory -> read the audio filenames in it + $dirname = $ARGV[0]; + opendir CURDIR, $dirname; + @musicfiles = grep /[\.f](mp3$)|(ogg$)|(flac$)/, readdir CURDIR; # match .mp3, .ogg, flac -- Thanks to Merlot! +# @musicfiles = grep /[\.f][Mmol][Ppga][c3g]$/, readdir CURDIR; # match .mp3, .ogg, flac + @musicfiles = sort @musicfiles; + closedir CURDIR; + chdir $dirname; + } + else{ + # the remaining arguments seem to be audio filenames + @musicfiles = @ARGV; + } + + ### Loop through audio files + while ($curfile = shift @musicfiles){ + $fileformat = gdgentools::audio_filetype($curfile); + print "Importing $fileformat file: $curfile\n"; + $fileid += 1; +# $audiofile = sprintf("trxx%08ld.%s", $fileid, $fileformat); + $audiofile = $dirname ."/". $curfile; + + # get info from metatags + $trackrow->{title} = gdgentools::audiofile_title($curfile); + $trackrow->{artist} = gdgentools::audiofile_artist($curfile); + $trackrow->{genre2} = ""; + $trackrow->{year} = gdgentools::audiofile_year($curfile); + $trackrow->{lang} = "-"; + $trackrow->{type} = 1; # medium + $trackrow->{rating} = 0; + $trackrow->{length} = gdgentools::audiofile_lengthsec($curfile); + $trackrow->{source} = 0; # CD + if(!$album_created){ + if($makealbum && !$album_created){ + $trackrow->{sourceid} = sprintf("%08ld", $fileid); # fake CDDB-ID + $albumtitle = gdgentools::audiofile_album($curfile); + if (length($albumtitle)<1){ + $albumtitle = $dirname; + } + } + else{ + $trackrow->{sourceid} = ""; # no CDDB-ID available + } + } + $trackrow->{tracknb} = gdgentools::audiofile_tracknumber($curfile, $tracknb); + $trackrow->{mp3file} = $audiofile; + $trackrow->{condition} = 0; # OK + $trackrow->{voladjust} = 0; + $trackrow->{lengthfrm} = 0; + $trackrow->{startfrm} = 0; + $trackrow->{bpm} = 0; + $trackrow->{bitrate} = $fileformat." ".gdgentools::audiofile_bitrate($curfile); + $trackrow->{created} = ""; + $trackrow->{modified} = ""; + $trackrow->{id} = ""; # a new one will be automatically generated + + # get genre if available and translate to gd-genre + $trackrow->{genre1} = gdgentools::audiofile_genre($dbh, $curfile); + + if (length($trackrow->{title})>0 && length($trackrow->{artist})>0){ + ### create track record + + print "Insert track in database:$trackrow->{artist} - $trackrow->{title} \n"; + if (!$test){ + if (-e $curfile){ + $trid = gddb::insert_track_record($dbh, + $trackrow->{artist},$trackrow->{title}, + $trackrow->{genre1},$trackrow->{genre2},$trackrow->{year}, + $trackrow->{lang},$trackrow->{type},$trackrow->{rating}, + $trackrow->{length},$trackrow->{source},$trackrow->{sourceid}, + $trackrow->{tracknb},$trackrow->{mp3file}, + $trackrow->{condition},$trackrow->{voladjust}, + $trackrow->{lengthfrm},$trackrow->{startfrm},$trackrow->{bpm}, + $trackrow->{bitrate}, + $trackrow->{created},$trackrow->{id}); + } + else{ + print "Error: file ".$trackrow->{mp3file}." dows not exist. Skipping\n"; + } + } + else{ + print "TEST>".join ':', " art",$trackrow->{artist}," tit",$trackrow->{title}, ," gen1",$trackrow->{genre1}," gen2",$trackrow->{genre2}," year",$trackrow->{year}, " lang",$trackrow->{lang}," tp",$trackrow->{type}," ratng",$trackrow->{rating}, " len",$trackrow->{length}," src",$trackrow->{source}," cddbid",$trackrow->{sourceid}, " trnb",$trackrow->{tracknb}," file",$trackrow->{mp3file}, " cond",$trackrow->{condition}," vol",$trackrow->{voladjust}, " lenfrm",$trackrow->{lengthfrm}," strt",$trackrow->{startfrm}," bpm",$trackrow->{bpm}, " bitrate",$trackrow->{bitrate}, " created",$trackrow->{created}," id",$trackrow->{id},"\n\n"; + } + + ### create album record + if ($makealbum && !$album_created && length($albumtitle)>0){ + print "Insert album in database:$trackrow->{artist} - $albumtitle \n"; + if (!$test){ + $dbh->do("INSERT INTO album (artist,title,modified,cddbid) " + ."VALUES (".$dbh->quote($trackrow->{artist}) + .",".$dbh->quote($albumtitle) + .",CURDATE() " + .",'".$trackrow->{sourceid}."') "); + } + $album_created = 1; + } + + ### put mp3file to target directory + $tracknb++; + } + else{ + print "No title or artist available for $curfile (skipped)\n"; + } + + }#end loop + print("### close database connection\n"); + $dbh->disconnect; + + exit; + + + +############################################################################### + + + + diff --git a/scripts/gdio.pm b/scripts/gdio.pm new file mode 100755 index 0000000..0fd6e2f --- /dev/null +++ b/scripts/gdio.pm @@ -0,0 +1,349 @@ +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2000, Rolf Brugger +# +################################################## + +package gdio; + +#use lib '/home/music/bin'; + +use IO::Socket; +use strict; + +# GiantDisc IO routines + +### Constants & global variables +my $no_mode = 0; +my $rs232_mode = 1; +my $tcpip_srv_mode = 2; +my $tcpip_cli_mode = 3; + +my $commMode = $no_mode; + + +### Serial communication parameters: +my $serdev; +my $serbaud; + +### TCP/IP communication parameters +my $gdsocket; +my $connection; + +undef $connection; + +################################################################# +### initialization routines: + +sub serio_init{ + ### Init serial port + my $logtarget; + ($logtarget, $serdev, $serbaud) = @_; + + print("### Init serial port $serdev at $serbaud bps\n"); + + # Config serial port: + # - disable echo + # - enable hardware flow control + system("stty $serbaud -echo crtscts <$serdev"); + open (SERIN, "<$serdev"); + open (SEROUT, ">$serdev"); + + ### Alternative? + #sysopen (SERIN, $serdev, O_RDRW | O_NODELAY | O_NOCTTY) + #or die "Can't open $serdev: $!"; + #my $ofh = select(SEROUT); $| = 1; + #select($ofh); + + $commMode = $rs232_mode; +} + + + +sub tcpip_srv_init{ + my ($logtarget, $tcpiphost, $tcpipport) = @_; + + print "Open socket on $tcpiphost at port $tcpipport as server\n"; + $gdsocket = new IO::Socket::INET( LocalHost => $tcpiphost, + LocalPort => $tcpipport, + Proto => 'tcp', + Listen => 1, + Reuse => 1); + + die "could not open socket: $!\n" unless $gdsocket; + + $commMode = $tcpip_srv_mode; +} + +sub tcpip_cli_init{ + my ($logtarget, $tcpiphost, $tcpipport) = @_; + + print "Open socket on $tcpiphost at port $tcpipport as client\n"; + $gdsocket = new IO::Socket::INET( PeerAddr => $tcpiphost, + PeerPort => $tcpipport, + Proto => 'tcp'); + + die "could not open socket: $!\n" unless $gdsocket; + + $commMode = $tcpip_cli_mode; +} + +################################################################# + +sub serio_getline { # returns one line + my $line = ; + return $line; +} + +sub putline_nack { # sends one line, without waiting for acknowledge (currently not used) + my ($line)=@_; + print (SEROUT "$line\n"); +} + +sub serio_putline +### The soutine returns true, if the client does not want to receive +### more lines (acknowledged with a 'stop' command). +### +### If the routines has to wait too long for an acknowledge (>20sec), the +### transmission is aborted. Such situations could be caused by crashed +### clients or broken transmission lines. +{ + my ($line)=@_; + my $ackstring; + print (SEROUT "$line\n"); + ### wait for acknowledge (max 20 sec) + eval{ + local $SIG{ALRM} = sub { die "ACK timeout" }; # set Alarm event handler + alarm 20; # 20 sec timeout + + $ackstring=gdio::getline(); + chop($ackstring); + + alarm 0; # deactivate Alarm + }; + if ($@ and $@ =~ /ACK timeout/ ) { + print ("\nACKNOWLEDGE TIMEOUT\n\n"); + $ackstring = "s"; #abort current transmission + }; + + if($ackstring eq "s"){ + print ("\nACK-stop received\n"); + return 1; # stop transmission + } + else{ + return 0; + } +} + +################################################################# +# +# If we experienced and error reading for the socket, then +# $connection would be set to "0" and so we need to wait for the +# palm client to reconnect. +# +# Thanks to Viktor for the tcpip code snippets + +sub tcpip_srv_check_connection{ + my $timeout; + print "connection open? ... "; + if ( ! defined $connection ) { + print "no\n"; + print "Server accepting socket ... \n"; + $connection = $gdsocket->accept(); + $timeout = $connection->timeout(); + #print "Server Connection time out is [$timeout] \n"; + $timeout = $connection->timeout(60); + #print "Server Connection time out set to [$timeout] \n"; + } + else{ + print "yes\n"; + } + + return $connection; +} + +# +# If we have an error reading the input assume client has +# disconnected. We close the socket and return a "NULL" command +# +sub tcpip_srv_getline{ + print "server listening ...\n"; + my $line; + if ( $line = <$connection> ) { + #print "line recieved: [$line] \n"; + return $line; + } + else { + print "Client disconnected ...\n"; + close ($connection) ; + undef $connection; + return "NULL\n"; + } +} + +sub tcpip_cli_getline{ + print "listening ...\n"; + my $line; + if ( $line = <$gdsocket> ) { + #print "line recieved: [$line] \n"; + return $line; + } +} + + +sub tcpip_srv_getline_SINGLECHAR{ ### just for tests ... + print "server listening ...\n"; + my $line=""; + my $cbuf; + while ( read $connection, $cbuf, 1 ) { + print "char recieved: [$cbuf] \n"; + $line .= $cbuf; + last if ($cbuf eq "\n"); + } + print "line recieved: [$line] \n"; + return $line; +} + + +sub tcpip_srv_putline{ +# There is currently a problem with this routine. +# The Palm client should be able to interrupt the transmission, if too many +# lines are sent to it. When the client shuts down the socket, the server does +# not directly recognize this. The 'if (print $connection $line."\n" )' is never +# false, unless the Palm is turned off. +# This has probably to do with the fact, that there is already sent data in a +# buffer? +# +# The current workaround is, that the Palm receives the supernumerous lines, but +# doesn't store them. Not very efficient ... :-( + + my ($line)=@_; + #print "Sending to socket: \"$line\"\n"; +#if (defined($connection->connected())){ +#print "WE ARE CONNECTED\n";} +#else{ +#print "WE ARE _NOT_ CONNECTED\n";} + + if (print $connection $line."\n" ) { + return 0; #don't stop + } + else{ + print "Client disconnected ...\n"; + close ($connection) ; + undef $connection; + return 1; #stop transmission + } +} + + +sub tcpip_cli_putline_NOACK{ + my ($line)=@_; + #print "Sending to socket: \"$line\"\n"; + + if (print $gdsocket $line."\n" ) { + return 0; #don't stop + } + return 0; +} +sub tcpip_cli_putline +{ + my ($line)=@_; + my $ackstring; + print $gdsocket $line."\n"; + ### wait for acknowledge (max 20 sec) + eval{ + local $SIG{ALRM} = sub { die "ACK timeout" }; # set Alarm event handler + alarm 20; # 20 sec timeout + + $ackstring=<$gdsocket>; + chop($ackstring); + + alarm 0; # deactivate Alarm + }; + if ($@ and $@ =~ /ACK timeout/ ) { + print ("\nACKNOWLEDGE TIMEOUT\n\n"); + $ackstring = "s"; #abort current transmission + }; + + if($ackstring eq "s"){ + print ("\nACK-stop received\n"); + return 1; # stop transmission + } + else{ + return 0; + } +} + + + +#sub close_connection +#{ +# print "Closing Connection - $connection \n"; +# close($connection) +#} + +################################################################# +### public routines: + +sub check_connection +{ + if ($commMode == $rs232_mode){ + return 1; + } + elsif ($commMode == $tcpip_srv_mode){ + return tcpip_srv_check_connection(); + } + elsif ($commMode == $tcpip_cli_mode){ + return 1; + } + else{ + print "Error: unknown communication mode\n"; + exit; + } +} + +sub putline +{ + if ($commMode == $rs232_mode){ + return serio_putline(@_); + } + elsif ($commMode == $tcpip_srv_mode){ + return tcpip_srv_putline(@_); + } + elsif ($commMode == $tcpip_cli_mode){ + return tcpip_cli_putline(@_); + } + else{ + print "Error: unknown communication mode\n"; + exit; + } +} + +sub getline +{ + if ($commMode == $rs232_mode){ + return serio_getline(); + } + elsif ($commMode == $tcpip_srv_mode){ + return tcpip_srv_getline(); + } + elsif ($commMode == $tcpip_cli_mode){ + return tcpip_cli_getline(); + } + else{ + print "Error: unknown communication mode\n"; + exit; + } +} + + +################################################################# + +END{ + print ("io modul finished\n"); +} + +# +1; diff --git a/scripts/gdparams.pm b/scripts/gdparams.pm new file mode 100755 index 0000000..35aa319 --- /dev/null +++ b/scripts/gdparams.pm @@ -0,0 +1,336 @@ +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2000, Rolf Brugger +# +################################################## + +package gdparams; + +#use lib '/usr/local/bin'; +use strict; +use Getopt::Long; + +my @mp3dirs; + +############################################################ +### Global variables (accessible from anywhere) + +# IMPORTANT ERROR! all modules refer to gdparms::varname instead +# of gdparams::varname +# ... however, it works ... I just don't know why :-/ + + +#my ($dbhost, $shutdowncmd, $extmp3player, $defrecbitrate, +# $systemonline); + + +### Global constants +my $minfreehdspace = 1000; # minimal space required for a directory, + # that it can be used to record and + # compress a cd (in MB) + + +############################################################ +### + +sub get_configfile_params{ + ### Parameters: Call-by-reference + + my ($dbhost, + $commmode, + $serialdevice, $serialspeed, + $tcpiphost, $tcpipport, + $playerhost, $playertype, $snddevice, + $playerapp, $mp3playerparams, $oggplayerparams, $ptlogger, + $mp3encoder, $logtarget, $shutdowncmd, + $extmp3player, $defrecbitrate, $systemonline + ) = @_; + + + ### Read configuration file im home directory + open (CONF, "< ".gdbase()."/.gdconfig") + or die "Error: could not open configuration file .gdconfig\n"; + + my $line; + while(){ + $line = $_; + chop $line; + $line =~ tr/\r/ /; + # \w word-char + # \w non-word-char + # \s whitespace-char + # \S non-whitespace-char + + if($line =~ m/^dbhost.*=\s*(\S+)/i ){ + $$dbhost = $1; + } + + if($line =~ m/^commmode.*=\s*([0-9]+)/i ){ + $$commmode = $1; + } + + if($line =~ m/^serialdevice.*=\s*([\w\/]+)/i ){ + $$serialdevice = $1; + } + + if($line =~ m/^serialspeed.*=\s*([0-9]+)/i ){ + $$serialspeed = $1; + } + + if($line =~ m/^tcpiphost.*=\s*(\S+)/i ){ + $$tcpiphost = $1; + } + + if($line =~ m/^tcpipport.*=\s*([0-9]+)/i ){ + $$tcpipport = $1; + } + + if($line =~ m/^playerhost.*=\s*(\S+)/i ){ + $$playerhost = $1; + } + + if($line =~ m/^playertype.*=\s*([0-9]+)/i ){ + $$playertype = $1; + } + + if($line =~ m/^playerapp.*=\s*(\S+)/i ){ + $$playerapp = $1; + } + + if($line =~ m/^playerparams.*=\s*(.*)$/i ){ # match anything to end of line + print "\nWARNING: as of v1.20 the option 'playerparams' has been replaced by\n"; + print " 'mp3playerparams' and 'oggplayerparams'. Update .gdconfig accordingly\n\n"; + #$$playerparams = $1; + } + if($line =~ m/^mp3playerparams.*=\s*(.*)$/i ){ # match anything to end of line + $$mp3playerparams = $1; + } + if($line =~ m/^oggplayerparams.*=\s*(.*)$/i ){ # match anything to end of line + $$oggplayerparams = $1; + } + + if($line =~ m/^shutdowncmd.*=\s*(.*)$/i ){ # match anything to end of line + $$shutdowncmd = $1; + } + + if($line =~ m/^extmp3player.*=\s*(.*)$/i ){ # match anything to end of line + $$extmp3player = $1; + } + + if($line =~ m/^sounddevice.*=\s*(\S+)/i ){ + $$snddevice = $1; + } + + if($line =~ m/^mp3encoder.*=\s*(\w+)/i ){ + $$mp3encoder = $1; + } + + if($line =~ m/^logtarget.*=\s*(\w+)/i ){ + if ($1 eq "stdout" || $1 eq "logfile" || $1 eq "devnull"){ + $$logtarget = $1; + } + } + + if($line =~ m/^ptlogger.*=\s*(\S+)/i ){ + $$ptlogger = $1; + } + + if($line =~ m/^defrecbitrate.*=\s*(.+)/i ){ # match anything to end of line + $$defrecbitrate = $1; + } + + if($line =~ m/^systemonline.*=\s*([0-9])/i ){ + $$systemonline = $1; + } + + } + + close (CONF); +} + + +############################################################ +### + +sub get_otherclients_params{ + ### Parameters: Call-by-reference + + my ($keymap # type: reference to an empty hash + )= @_; + + + ### Read configuration file im home directory + open (CONF, "< ".gdbase()."/.gdconfig") + or die "Error: could not open configuration file .gdconfig\n"; + + my $line; + while(){ + $line = $_; + chop $line; + $line =~ tr/\r/ /; + # \w word-char + # \w non-word-char + # \s whitespace-char + # \S non-whitespace-char + + if($line =~ m/^keymap.*=\s*(\S+)\s*-\s*(\S+)\s*$/i ){ + print ("keymap: key: $1, val: $2\n"); + $$keymap{$1} = $2; + } + + + } + + close (CONF); +} + + +############################################################ +### translate logtarget string to integer +sub logtarget_to_int{ + my ($logtargetstr) = @_; + if ($logtargetstr eq "devnull") {return 0;} + if ($logtargetstr eq "logfile") {return 1;} + if ($logtargetstr eq "stdout" ) {return 2;} + return 2; # default +} + + +############################################################ +### + +sub get_commandline_params{ + + my ($dbhost, + $commmode, + $serialdevice, $serialspeed, + $tcpiphost, $tcpipport, + $playerhost, $playertype, $snddevice, + $playerapp, + $mp3playerparams, $oggplayerparams, + $ptlogger, + $mp3encoder, $logtarget) = @_; + # ARGV passed implicitly + + + my $help; + $Getopt::Long::autoabbrev=1; + GetOptions( + "help" => \$help, + "dbhost:s" => $dbhost, # $dbhost is already a reference + "commmode:i" => $commmode, + "serialdevice:s" => $serialdevice, + "serialspeed:i" => $serialspeed, + "tcpiphost:s" => $tcpiphost, + "tcpipport:i" => $tcpipport, + "playertype:i" => $playertype, + "playerhost:s" => $playerhost, + "sounddevice:s" => $snddevice, + "mp3playerparams=s" => $mp3playerparams, + "oggplayerparams=s" => $oggplayerparams, + "ptlogger:s" => $ptlogger, + "mp3encoder:s" => $mp3encoder, + "logtarget:s" => $logtarget + ); + + if ($help){ + print <connect("DBI:mysql:GiantDisc:".$dbhost, "music", undef) + or die "unable to connect to GiantDisc db"; + + + if (gdupdate::db_check_update_132($dbh)){ + print "\n Update to database structure version 1.32 required\n"; + print " please run command 'gdupdatedb.pl'\n\n"; + exit; + } + + ### fix records (bug in recording routines prior to version 0.92) + #gdupdate::fix_leading_slash_bug($dbh); + + ### UPDATE DATABASE to new versions +# gdupdate::db_update_094($dbh); +# gdupdate::db_update_095($dbh); +# gdupdate::db_update_096($dbh); +# gdupdate::db_update_097($dbh); +# gdupdate::db_update_111($dbh); +# gdupdate::db_update_112($dbh); +# gdupdate::db_update_114($dbh); +# gdupdate::db_update_131($dbh); +} + + +sub init_player{ +# Checks, if a player record with the same ipaddr and uichannel exists. +# If it doesn't exist, a new record is created. +# It returns the new generated player-ID +# +# +# + my ($ipaddr, $uichannel, $logtarget, $cdripper, + $mp3encoder, $cdromdev, $cdrwdev) = @_; + $playerid = 0; #default + my $sth = $dbh->prepare( + "SELECT * FROM player " + ."WHERE ipaddr='$ipaddr' AND uichannel='$uichannel'" ); + $sth->execute; + + my $row; + if($row = $sth->fetchrow_hashref){ + # matching player record found + $playerid = $row->{id}; + } + else{ + # get highest id in db + my $sth2 = $dbh->prepare( + "SELECT * FROM player ORDER BY id DESC LIMIT 1" ); + $sth2->execute; + if($row = $sth2->fetchrow_hashref){ + # matching player record found + $playerid = $row->{id}+1; + } + #else playerid has default value 0 + $sth2->finish; + + ### Create player record + my $retval = $dbh->do("INSERT INTO player SET " + ."ipaddr ='$ipaddr', " + ."uichannel ='$uichannel', " + ."id = $playerid, " + ."logtarget = $logtarget, " + ."cdripper ='$cdripper', " + ."mp3encoder='$mp3encoder', " + ."cdromdev ='$cdromdev', " + ."cdrwdev ='$cdrwdev'"); + } + $sth->finish; + + ### delete all tracklists (riplist, burnlist and comprlist) of this player + $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$rp_list"); #riplist + $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$co_list"); #compressionlist + $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$cr_list"); #cdrecordlist + + return $playerid; +} + + +sub init_playerstate{ +# +# global var: playerid + + my ($playerhost, $snddevice, $playerapp, $ptlogger, $playertype); + my ($mp3playerparams, $oggplayerparams); + ($playerhost, $playertype, $snddevice, $playerapp, $mp3playerparams, $oggplayerparams, $ptlogger) = @_; + my $playerparams = join '\t', $mp3playerparams, $oggplayerparams; + my $audiochannel = $audchannel0; + gdgentools::pll_new_playstate($dbh, $playerid, $audiochannel, + $playertype, $snddevice, $playerapp, $playerparams, $ptlogger); + + ### create message queue for this player-id/audio-channel + gdgentools::plmsg_newqueue($playerid, $audiochannel); + + ### init soundcard driver + gdsoundcard::sndc_init($playertype, $playerhost, $audiochannel); +} + + + +sub open_rippipe{ + ### Open recording pipe + open(RIPPIPE, "| gdripcd.pl $dbhost $playerid $audchannel0") + or die "can't fork RIP-PIPE\n"; + autoflush RIPPIPE 1; + $rippipe_open=1; +} + +sub close_rippipe{ + ### close recording pipe + close(RIPPIPE); + $rippipe_open=0; +} + +############################################################ +### TOOL ROUTINES ### +############################################################ + +############################################################ + +sub truncstr{ +### truncstr (string, length, noellipsis) + # If the string is longer than 'length' characters, it is truncated and + # '..' is appended => max length is 'length'+2 + if (length($_[0]) > $_[1]){ + $_[0] = substr ($_[0], 0, $_[1]); + if(!$_[2]){ # add ellipsis + $_[0] .= ".."; + } + } +} + + +############################################################ +### DB CREATION & UPDATE ### +############################################################ + +############################################################ +### Creates/updates an album record +sub replace_album_record{ + my ($artist,$title,$cddbid) = @_; + + my $base = gdparams::gdbase(); + + if(length($artist)==0){$artist="-";}; + if(length($title)==0) {$title="-";}; + if(length($cddbid)==0){print("ERROR: no CDDB-ID defined!\n");}; + my $sqlcmd = + "REPLACE INTO album " + ."(artist,title,cddbid,modified) " + ."VALUES " + ."(".$dbh->quote($artist) + .",".$dbh->quote($title) + .",'$cddbid', CURDATE()) "; + + #print("$sqlcmd \n"); + $dbh->do($sqlcmd); +} + + +############################################################ +### +sub record_cd_track{ + my ($artist,$title,$genre1,$genre2,$year, + $lang,$type,$rating,$length,$source,$sourceid, + $tracknb,$audiofile,$condition,$voladjust, #$lengthfrm,$startfrm,$bpm, + $created,$id,$bitrate + ) = @_; + + my $base = gdparams::gdbase(); + + ### Get an audio directory with enough space left (1GB) + my $ripdir=gdgentools::get_ripdirectory($gdparams::minfreehdspace); #1000 MBytes should be available + + my $trid; + ### create a new track record + if($ripdir ne ""){ + my ($audiofmt, $audioparam) = gdgentools::bitrate_str_to_format_param($bitrate); # split "mp3 128" + $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year, + $lang,$type,$rating,$length,$source,$sourceid, + $tracknb,"tr0x".$sourceid."-".$tracknb.".".$audiofmt, + $condition,$voladjust,0,0,0, #$lengthfrm,$startfrm,$bpm, + $bitrate, + $created,$id); + + unlink glob("$base/??/tr0x".$sourceid."-".$tracknb.".".$audiofmt); # delete old audio file + ### send rip command to pipe + if(!$rippipe_open){open_rippipe();} + print ("printing to RIPPIPE: tr=$tracknb id=$trid " + ."br=$bitrate len=$length " + ."dir=$base/$ripdir " + ."file=tr0x$sourceid art=$artist tit=$title\n"); + gdgentools::tracklist_append_list($dbh, $playerid, $rp_list, $trid); + ### action is "rip" + print (RIPPIPE "rip\t$tracknb\t$trid\t$bitrate\t$length\t" + ."$base/$ripdir\ttr0x$sourceid\t$artist\t$title\n"); + } + else{ + print("Not enough space left on disc \n"); + } +} + + +############################################################ +### +sub import_audio_track{ +# creates a new record and moves the specified audio-file to a +# music directory which has enough space left + + my ($artist,$title,$genre1,$genre2,$year, + $lang,$type,$rating,$length,$source,$sourceid, + $tracknb,$audiofile,$condition,$voladjust,#$lengthfrm,$startfrm,$bpm, + $created,$id,$bitrate + ) = @_; + + my $base = gdparams::gdbase(); + + ### Get an audio directory with enough space left (1GB) + my $ripdir=gdgentools::get_ripdirectory($gdparams::minfreehdspace); + + my $trid; + ### create a new track record + if($ripdir ne ""){ + ### put audio-file in music directory + + my $origaudiofile = readlink "$base/inbox/$audiofile"; + + if(!$rippipe_open){open_rippipe();} + + ### action is "move" + #format: "move, sourcefile, targetfile, linkname + print ("printing to RIPPIPE: move\t$origaudiofile\t$base/$ripdir/$audiofile\t$base/inbox/$audiofile\n"); + print (RIPPIPE "move\t$origaudiofile\t$base/$ripdir/$audiofile\t$base/inbox/$audiofile\n"); + + ### create track record + $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year, + $lang,$type,$rating,$length,$source,$sourceid, + $tracknb,$audiofile,$condition,$voladjust, + 0,0,0, #$lengthfrm,$startfrm,$bpm, + $bitrate, + $created,$id); + + } + else{ + print("Not enough space left on disc \n"); + } +} + + + +############################################################ +### +# these variables are st by the routine, that sends track details to the +# client. The value in there gives a hint, if the frequencies should be recalculated +my ($trk_last_id, $trk_last_lang, $trk_last_genre1, $trk_last_genre2); + +sub update_track_record{ + my ($artist,$title,$genre1,$genre2,$year, + $lang,$type,$rating,$length,$source,$sourceid, + $tracknb,$audiofile,$condition,$voladjust,#$lengthfrm,$startfrm,$bpm, + $created,$id,$bitrate + ) = @_; + + my ($trid, $audiofpath); + + ### always recalculate bitrate and track length + $audiofpath = gdgentools::get_full_audiofile_path($audiofile); + if(gdgentools::is_mp3stream($audiofile) || (length($audiofpath)>0)){ + if(length($audiofpath)>0){ ## real mp3 file exists + $bitrate = gdgentools::get_bitrate_str("$audiofpath"); + $length = gdgentools::audiofile_lengthsec("$audiofpath"); + } + else{ + $length = 0; + } + + #print("UPDATING: $artist,$title,$genre1,$genre2,$year," + # ."$lang,$type,$rating,$length,$source,$sourceid," + # ."$tracknb,$audiofile,$condition,$voladjust,$created,$id\n"); + $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year, + $lang,$type,$rating,$length,$source,$sourceid, + $tracknb,$audiofile,$condition,$voladjust, + 0,0,0,#$lengthfrm,$startfrm,$bpm, + $bitrate, + $created,$id); + + if ( $id != $trk_last_id + || $lang ne $trk_last_lang){ ### language changed? + system("killall -q gdtablefreq.pl"); + system("nice gdtablefreq.pl --dbhost $dbhost --trklanguage&"); +# system("nice gdtablefreq.pl --dbhost $dbhost --trklanguage --verbose&"); + } + if ( $id != $trk_last_id + || $genre1 ne $trk_last_genre1 + || $genre2 ne $trk_last_genre2){ ### genre changed? + # recalculate genre frequencies + system("killall -q gdtablefreq.pl"); + if (length($sourceid)>=8){ + system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre $sourceid&"); +# system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre $sourceid --verbose&"); + } + else{ + system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre&"); +# system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --verbose&"); + } + } + } + else{ + print("Warning: can't update record (id=$id) because the associated mp3file does not exist, and no mp3 streaming url is specified\n"); + } +} + + + +sub recalc_table_frequencies{ + system("killall -q gdtablefreq.pl"); + system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre&"); +} + +############################################################ +### QUERIES ### +############################################################ + + +############################################################ +### +sub do_tr_query{ # returns a query handle + + my $where = gddb::track_where_clause(@_); + my $order = gddb::track_order_clause(@_); + + #print("WHERE clause: $where $order\n"); + my $sth = $dbh->prepare( "SELECT * FROM tracks WHERE $where $order" ); + + my $rv = $sth->execute; + print("$rv records found\n"); + return $sth; +} + +############################################################ +### +sub do_album_query{ # does an album query and returns a query handle + ### +# EXAMPLE: +# SELECT album.* FROM tracks JOIN album +# WHERE album.cddbid=tracks.sourceid AND tracks.genre1='mdh' +# AND album.title like "%de%" +# GROUP BY album.cddbid; + +# my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, +# $lang,$type,$rating) = @_; + + my $tmpcmd; + + my $where = gddb::album_where_clause(@_); + my $order = gddb::album_order_clause(@_); + +#print "alb-order: $order\n\n"; + if (length($order)<5){ + $order = "ORDER BY album.artist"; + } + + my $sth = $dbh->prepare( +# " SELECT album.* FROM tracks JOIN album " + " SELECT album.artist AS artist, album.title AS title, " + ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year " + ."FROM tracks JOIN album " + ."WHERE album.cddbid=tracks.sourceid AND $where " + ."GROUP BY album.cddbid $order" + ); + + $sth->execute; + return $sth; +} + +############################################################ +### +sub do_playlist_query{ # does a playlist query and returns a query handle + ### + my ($author,$title,$genre1,$genre2,$yearfrom,$yearto, + $lang,$type,$rating) = @_; + + my $where=" 1 "; # true AND ... + + ### Artist + $where .= gddb::field_where_clause("author",$author); + + ### Title + $where .= gddb::field_where_clause("title",$title); + + my $sth = $dbh->prepare( + 'SELECT * FROM playlist ' + ."WHERE $where" + ); + + $sth->execute; + return $sth; +} + +############################################################ +### +sub get_track{ # returns a query handle + my ($trackid) = @_; + + my $sth = $dbh->prepare( + 'SELECT * FROM tracks ' + ."WHERE id = $trackid" + ); + + $sth->execute; + return $sth; +} + +############################################################ +### +sub do_tracks_of_album_query{ # returns a query handle + my ($albumid) = @_; + my $sth = $dbh->prepare( + 'SELECT * FROM tracks ' + ."WHERE sourceid = \"$albumid\" ORDER BY tracknb" + ); + + $sth->execute; + return $sth; +} + +############################################################ +### +sub do_tracks_of_playlist_query{ # returns a query handle + my ($playlistid) = @_; + my $sth = $dbh->prepare( + "SELECT * FROM playlistitem " + ."WHERE playlist = $playlistid ORDER BY tracknumber" + ); + + $sth->execute; + return $sth; +} + + +############################################################ +### LISTINGS ### +############################################################ + +############################################################ +### send tracks +sub send_tracks_v1{ # send a record set to the serial line + # NEW VERSION + my ($sth) = @_; + my ($row); + my $stop=0; + while($row = $sth->fetchrow_hashref){ + $stop = send_track($row); + last if ($stop); + } + if (!$stop){ + gdio::putline ("");} # End of result + $sth->finish; +} +sub send_track{ + my ($row)= @_; + my ($stop); + truncstr($row->{artist}, $shrtFieldLen); + truncstr($row->{title}, $shrtFieldLen); + print ("$row->{id}\t"); + print ("$row->{artist}\t"); + print ("$row->{title}\t"); + print ("$row->{length}\t"); + print ("$row->{genre1}\t"); + #print ("$row->{genre2}\t"); + print ("$row->{year}\t"); + print ("$row->{type}\t"); + print ("$row->{rating}\n"); + $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" + ."$row->{length}\t$row->{genre1}\t" + ."$row->{year}\t$row->{type}\t$row->{rating}"); + return $stop +} +#sub send_tracks_v0{ # send a record set to the serial line +# # OLD VERSION: will be obsolete when all +# # routines are ported to 'send_tracks' +# my ($sth) = @_; +# my ($row); +# my $stop=0; +# while($row = $sth->fetchrow_hashref){ +# truncstr($row->{artist}, $shrtFieldLen); +# truncstr($row->{title}, $shrtFieldLen); +# print ("$row->{id}\t"); +# print ("$row->{artist}\t"); +# print ("$row->{title}\t"); +# print ("$row->{length}\n"); +# $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" +# ."$row->{length}\t$row->{tracknb}"); +# last if ($stop); +# } +# if (!$stop){ +# gdio::putline ("");} # End of result +# $sth->finish; +#} + +############################################################ +### Query tracks & List +sub query_tracks_list{ #query tracks records and list them + my $sth = do_tr_query(1, @_); # perform database query + send_tracks_v1($sth); +} + +sub query_streams_list{ #query tracks records and list them + my $sth = do_tr_query(2, @_); # perform database query + send_tracks_v1($sth); +} + +############################################################ +### Generic Query tracks & List +sub generic_track_query{ #query tracks records and list them + my ($where) = @_; + #print("WHERE clause: $where\n"); + my $sth = $dbh->prepare( "SELECT * FROM tracks $where" ); + my $rv = $sth->execute; + print("$rv records found\n"); + send_tracks_v1($sth); +} + +### Query a single tracks & List +#sub query_single_track{ #query one trackfor a given id +# my ($trackid) = @_; +# my $sth = $dbh->prepare( "SELECT * FROM tracks WHERE id='$trackid'" ); +# $sth->execute; +# send_tracks_v0($sth); +#} + + +############################################################ +### send artists +sub send_artist_line_v1{ # send a single record to the serial line + my ($artist) = @_; + my $stop=0; + print ("\t"); # send an empty ID + truncstr($artist, 10+$shrtFieldLen, 1); # dont send ellipsis + print ("$artist\n"); + $stop = gdio::putline("\t$artist"); + return $stop; +} + +#sub send_artist_line_v0{ # send a single record to the serial line +# my ($artist) = @_; +# my $stop=0; +# print ("ESCn\t"); # ESC n: name (=artist) +# print ("0\t"); # send an empty ID to be compatible with album record format +# truncstr($artist, 10+$shrtFieldLen, 1); # dont send ellipsis +# print ("$artist\t"); +# print (" \n"); # send an empty title to be compatible with album record format +# $stop = gdio::putline("\en\t0\t$artist\t "); +# return $stop; +#} + +############################################################ +### Query artist & List +sub query_artists_list{ #query track records and list their distinct artists + #my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, + # $lang,$type,$rating) = @_; + + ### Prepare Query + my $where = gddb::track_where_clause(1, @_); + + #print("WHERE clause: $where\n"); + + generic_artists_query( + "SELECT DISTINCT artist FROM tracks WHERE $where ORDER BY artist" ); +} + + +sub generic_artists_query{ #query track records and list their distinct artists + ### Prepare Query + my ($querystr) = @_; + my $sth = $dbh->prepare( $querystr ); + my $rv = $sth->execute; + #print("$rv artists found\n"); + ### Send results + my $row; + my $stop=0; + while($row = $sth->fetchrow_hashref){ + $stop = send_artist_line_v1($row->{artist}); + last if ($stop); + } + if (!$stop){ + gdio::putline ("");} # End of result + $sth->finish; +} + + +############################################################ +### Query albums + + +#sub query_single_album{ + # query one albums with its tracks and list them + # the album is specified by the 'albumid'. +# my ($albumid) = @_; +# my $albsth; +# $albsth = $dbh->prepare( +# " SELECT * FROM album WHERE cddbid='$albumid'"); +# $albsth->execute; +# send_albums_v0(1, $albsth); # 1: with tracks +#} + + +sub generic_album_query{ + # query one albums with its tracks and list them + # the album is specified by the 'albumid'. + my ($querystr) = @_; + my $albsth; + my $albsth = $dbh->prepare( $querystr ); + $albsth->execute; + send_albums_v1($albsth); +} + + +############################################################ +### Query albums & list +sub query_albums{ + # query albums and list them + my $albsth = do_album_query(@_); # perform database query + send_albums_v1($albsth); +} + + +############################################################ +### Query albums at random +sub query_random_albums{ + # prepares a ramdom query operation + # 1) performs an album query + # 2) selects album-ids and store them in a gobally accessible array + # 3) returns the number of albums found + my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, + $lang,$type,$rating) = @_; + my $nbRecords; + + my $where = gddb::album_where_clause(@_); +# my $where=" 1 "; # true AND ... + ### Album: Artist +# $where .= gddb::field_where_clause("album.artist",$artist); + ### Album: Title +# $where .= gddb::field_where_clause("album.title",$title); + ### Track: genre, etc ... +# $where.= gddb::attrib_where_clause($genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating); + + my $sth = $dbh->prepare( + " SELECT album.cddbid,(ASCII(album.cddbid)*0)+RAND() AS randnum " + ."FROM tracks JOIN album " + ."WHERE album.cddbid=tracks.sourceid AND $where " + ."GROUP BY album.cddbid ORDER BY randnum" + ); # ORDER BY RAND() does not work in version 3.22 + $nbRecords = $sth->execute; + + ### store result in global array + $random_result_table = $sth->fetchall_arrayref; + $random_result_type = "alb"; + # my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); + $sth->finish; + + ### Send number of records + print ("random album query: $nbRecords albums found\n"); + gdio::putline ("$nbRecords\n"); + gdio::putline (""); # End of result +} + + +############################################################ +### Query tracks at random +sub query_random_tracks{ + # prepares a ramdom query operation + # 1) performs a track query + # 2) selects track-ids and store them in a gobally accessible array + # 3) returns the number of tracks found + my $nbRecords; + + my $where = gddb::track_where_clause(@_); + my $sth = $dbh->prepare( + " SELECT id,(id*0)+RAND() AS randnum FROM tracks " + ."WHERE $where ORDER BY randnum" + ); # ORDER BY RAND() does not work in version 3.22 + $nbRecords = $sth->execute; + + ### store result in global array + $random_result_table = $sth->fetchall_arrayref; + $random_result_type = "trk"; +# my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); + $sth->finish; + + ### Send number of records + print ("random album query: $nbRecords tracks found\n"); + gdio::putline ("$nbRecords\n"); + gdio::putline (""); # End of result +} + + +############################################################ +### Query artists at random +sub query_random_artists{ + # prepares a ramdom query operation + # 1) performs a track query + # 2) selects track-ids and store them in a gobally accessible array + # 3) returns the number of tracks found + my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, + $lang,$type,$rating) = @_; + my $nbRecords; + + my $where = gddb::track_where_clause(1, @_); + my $sth = $dbh->prepare( + " SELECT DISTINCT artist FROM tracks WHERE $where" + ); # can't "ORDER BY randnum" here! + $nbRecords = $sth->execute; + + ### store result in global array + $random_result_table = $sth->fetchall_arrayref; + $random_result_type = "art"; +# my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); + $sth->finish; + ### randomize it - because SQL query can't do it + shuffle_random_table(); + + ### Send number of records + print ("random artist query: $nbRecords artists found\n"); + gdio::putline ("$nbRecords\n"); + gdio::putline (""); # End of result +} + + +############################################################ +### send next slice of random result table to the palm client +sub collect_ids{ + my ($position,$nbRecords,$idIdent) = @_; + my ($whereclause, $i); + $whereclause = "0"; + # if $position < $#{$random_result_table} + for $i( $position .. ($position+$nbRecords-1)){ + #print ($random_result_table->[$i][0]."\n"); + $whereclause .= " OR $idIdent='".$random_result_table->[$i][0]."'"; + } + return $whereclause; +} + +sub shuffle_random_table{ + my ($i, $j, $element); + for $i( 0 .. $#{$random_result_table}){ + $j = int rand ($#{$random_result_table}); +# print ("exchange $i and $j\n"); + $element = $random_result_table->[$i][0]; + $random_result_table->[$i][0] = $random_result_table->[$j][0]; + $random_result_table->[$j][0] = $element; + } +} + +sub get_random_result{ + # prepares a ramdom query operation + # 1) performs an album query + # 2) selects album-ids and store them in a gobally accessibe array + # 3) randomizes the order of the album-ids + # 4) returns the number of albums found + my ($position,$nbRecords) = @_; + + my ($whereclause, $sth); + if ($random_result_type eq "alb"){ + $whereclause = collect_ids ($position,$nbRecords, "tracks.sourceid=album.cddbid AND album.cddbid"); + ### Perform the query + #print("SELECT album.artist AS artist, album.title AS title, " + # ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year " + # ."FROM tracks JOIN album " + # ."WHERE $whereclause" + # ."GROUP BY album.cddbid\n"); + + $sth = $dbh->prepare(" SELECT album.artist AS artist, album.title AS title, " + ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year " + ."FROM tracks JOIN album " + ."WHERE $whereclause" + ."GROUP BY album.cddbid"); + $sth->execute; + send_albums_v1($sth); + $sth->finish; + } + elsif ($random_result_type eq "pll"){ + $whereclause = collect_ids ($position,$nbRecords, "id"); + ### Perform the query + print("SELECT * FROM playlist WHERE $whereclause\n"); + $sth = $dbh->prepare("SELECT * FROM playlist WHERE $whereclause"); + $sth->execute; + my $prow; + while($prow = $sth->fetchrow_hashref){ + print("$prow->{id}\t$prow->{author}\t$prow->{title}\n"); + gdio::putline("$prow->{id}\t$prow->{author}\t$prow->{title}"); + } + gdio::putline (""); # End of result + $sth->finish; + } + elsif ($random_result_type eq "trk"){ + $whereclause = collect_ids ($position,$nbRecords, "id"); + ### Perform the query + print("SELECT * FROM tracks WHERE $whereclause\n"); + $sth = $dbh->prepare("SELECT * FROM tracks WHERE $whereclause"); + $sth->execute; + send_tracks_v1($sth); + $sth->finish; + } + elsif ($random_result_type eq "art"){ + ### no additional query necessary with artists + my $i; + for $i( $position .. ($position+$nbRecords-1)){ + truncstr($random_result_table->[$i][0], 10+$shrtFieldLen); + #print ($random_result_table->[$i][0]."\n"); + send_artist_line_v1($random_result_table->[$i][0]); + } + gdio::putline (""); # End of result + } + else{ + print ("Error: random_result_type '$random_result_type' not properly initialized"); + } +} + + +############################################################ +### Query albums with or without tracks & list +#sub send_albums_v0{ +# # sends the albums (specified by the record set handle '$albsth') +# # if $withTracks is true, the according tracks are sent too +# +# my ($withTracks, $albsth) = @_; +# my ($arow, $trow); # album and track row +# my $trksth; +# my $stop=0; +# +# while($arow = $albsth->fetchrow_hashref){ +# truncstr($arow->{artist}, $shrtFieldLen); +# truncstr($arow->{title}, $shrtFieldLen); +# print ("ESCa\t"); +# print ("$arow->{cddbid}\t"); +# print ("$arow->{artist}\t"); +# print ("$arow->{title}\n"); +# # print album (preceded by ESC a) +# $stop = gdio::putline("\ea\t$arow->{cddbid}\t$arow->{artist}\t$arow->{title}"); +# +# if ($withTracks){ +# ### get tracks of the album +# my $trksth = do_tracks_of_album_query($arow->{cddbid}); +# while($trow = $trksth->fetchrow_hashref){ +# truncstr($trow->{artist}, $shrtFieldLen); +# truncstr($trow->{title}, $shrtFieldLen); +# print ("$trow->{id}\t"); +# print ("$trow->{artist}\t"); +# print ("$trow->{title}\t"); +# print ("$trow->{length}\n"); +# # print track +# $stop = gdio::putline("$trow->{id}\t$trow->{artist}\t$trow->{title}\t" +# ."$trow->{length}\t$trow->{tracknb}"); +# last if ($stop); +# } +# } +# last if ($stop); +# } +# if (!$stop){ +# gdio::putline ("");} # End of result +# $albsth->finish; +#} + +sub send_albums_v1{ + # sends the albums (specified by the record set handle '$albsth') + # if $withTracks is true, the according tracks are sent too + + my ($albsth) = @_; + my ($arow, $trow); # album and track row + my $trksth; + my $stop=0; + + while($arow = $albsth->fetchrow_hashref){ + truncstr($arow->{artist}, $shrtFieldLen); + truncstr($arow->{title}, $shrtFieldLen); + print ("$arow->{cddbid}\t"); + print ("$arow->{artist}\t"); + print ("$arow->{title}\t"); + print ("\t$arow->{genre}\t$arow->{year}\n"); # length,genre,year + # print album + $stop = gdio::putline("$arow->{cddbid}\t$arow->{artist}\t$arow->{title}\t\t$arow->{genre}\t$arow->{year}"); + last if ($stop); + } + if (!$stop){ + gdio::putline ("");} # End of result + $albsth->finish; +} + +############################################################ +### Query playlists & list +sub query_saved_playlists_list{ + # query playlists and list them + # + # List format: id, author, title + + my $pllsth = do_playlist_query(@_); # perform database query + my ($prow, $trow); # playlist and track row + my ($trksth, $trow, $irow, $plisth); + my $stop=0; + + while($prow = $pllsth->fetchrow_hashref){ + truncstr($prow->{artist}, $shrtFieldLen); + truncstr($prow->{title}, $shrtFieldLen); + print ("$prow->{id}\t"); + print ("$prow->{author}\t"); + print ("$prow->{title}\n"); + + $stop = gdio::putline("$prow->{id}\t$prow->{author}\t$prow->{title}"); + last if ($stop); + } + if (!$stop){ + gdio::putline ("");} # End of result + $pllsth->finish; +} + + + +############################################################ +### Query playlists at random +sub query_random_playlists{ + # prepares a ramdom query operation + # 1) performs an playlist query + # 2) selects playlist-ids and store them in a gobally accessible array + # 3) returns the number of playlists found + my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, + $lang,$type,$rating) = @_; + my $nbRecords; + + my $where=" 1 "; # true AND ... + + ### Artist + $where .= gddb::field_where_clause("author",$artist); + ### Title + $where .= gddb::field_where_clause("title",$title); + + my $sth = $dbh->prepare( + 'SELECT id FROM playlist ' + ."WHERE $where ORDER BY RAND()" + ); + + $nbRecords = $sth->execute; + + ### store result in global array + $random_result_table = $sth->fetchall_arrayref; + $random_result_type = "pll"; + # my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); + $sth->finish; + + ### Send number of records + print ("random album query: $nbRecords albums found\n"); + gdio::putline ("$nbRecords\n"); + gdio::putline (""); # End of result +} + + +sub tracks_of_saved_playlist{ + # lists tracks of a saved playlist + + my ($plid) = @_; + + my ($trksth, $irow, $plisth, $row); + #my $stop=0; + + + ### get tracks of the playlist + $plisth = do_tracks_of_playlist_query($plid); + while($irow = $plisth->fetchrow_hashref){ + $trksth = $dbh->prepare("SELECT * FROM tracks WHERE id=".$irow->{trackid}); + $trksth->execute; + if($row = $trksth->fetchrow_hashref){ + send_track($row); + } + $trksth->finish; + } + $plisth->finish; + gdio::putline (""); # End of result +} + + + +############################################################ +### Query tracks and send result to Palm client +sub simple_track_query{ + my ($query_cmd) = @_; + my ($sth,$row); + my $stop=0; + + my $sth = $dbh->prepare($query_cmd); + my $rv = $sth->execute; + print("$rv records found\n"); + + while($row = $sth->fetchrow_hashref){ + truncstr($row->{artist}, $shrtFieldLen); + truncstr($row->{title}, $shrtFieldLen); + print ("$row->{id} $row->{artist} $row->{title}\t $row->{length}\n"); + $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" + ."$row->{length}\t$row->{tracknb}"); + last if ($stop); + } + if (!$stop){ + gdio::putline ("");} # End of result + $sth->finish; +} + + + +############################################################ +### Play + Playlist commands ### +############################################################ + + +############################################################ +### New Playlist commands + +sub pl_play{ gdgentools::pl_play ($dbh, $playerid, $audchannel0);} +sub pl_play_at{gdgentools::pl_play_at($dbh, $playerid, $audchannel0, @_);} +sub pl_stop{ gdgentools::pl_stop ($dbh, $playerid, $audchannel0);} +sub pl_pause{ gdgentools::pl_pause ($dbh, $playerid, $audchannel0);} +sub pl_rw{ gdgentools::pl_rw ($dbh, $playerid, $audchannel0);} +sub pl_ff{ gdgentools::pl_ff ($dbh, $playerid, $audchannel0);} +sub pl_goto{ gdgentools::pl_goto ($dbh, $playerid, $audchannel0, @_);} +sub pl_prev{ gdgentools::pl_prev ($dbh, $playerid, $audchannel0);} +sub pl_next{ gdgentools::pl_next ($dbh, $playerid, $audchannel0);} + + + +sub pl_append_tracks{ +# appends tracks to the playlist file +# Parameters: list of track id's + gdgentools::tracklist_append_list($dbh, $playerid, $pl_list, @_); +} + +sub pl_new_list{ +# Sets a new playlist. First, playing is stopped, then the current playlist +# is emptied, the new playlist is written and the current track index is set +# to zero. +# An example, where this routine is used is the shuffle function (Palm +# randomizes playlist order and sends new playlist). +# Parameters: list of track id's + #my $tracklist = @_; + pl_empty(); + pl_append_tracks(@_); +} + +sub pl_empty{ +# empties (deletes) the playlist. The playstate is also reset + my $base = gdparams::gdbase(); + # stop playing + gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); + # reset list+state + gdgentools::tracklist_delete($dbh, $playerid, 0); + gdgentools::pll_write_playstate($dbh, $playerid, $audchannel0, 0, "st"); + gdgentools::pll_write_playtime($dbh, $playerid, $audchannel0, 0, 0); + gdgentools::pll_write_shuffleparameters($dbh, $playerid, $audchannel0, "", ""); +} + + +sub pl_remove_one{ +# Removes the specified playlist item (index) from the playlist +# If the index corresponds to the currently played track, playing is +# stopped first. +# It the index is lower than the the index of the currently played track, +# then nothing is done. +# Parameter: index of the item to be removed + + my ($delitem) = @_; + my ($trackind, $state, $frame, $shufflestat) = gdgentools::pll_get_playstate($dbh, $playerid, $audchannel0); + if ($delitem >= $trackind){ + if (($delitem == $trackind) && $state ne "st" && $state ne "in"){ + gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); + gdgentools::pll_write_playstate($dbh, $playerid, $audchannel0, $trackind, "st"); + } + + ### delete it + gdgentools::tracklist_del_item($dbh, $playerid, 0, $delitem); + } +} + + + +sub pl_reorder_one{ +# Reorders the playlist. +# The specified playlist item (src) is moved to a new location (dest) +# src and dest must be higher than the currently played track. They +# therefore don't interfere with the track playing process. + + my ($srcpos, $destpos) = @_; + ### move it + gdgentools::tracklist_reorder_item($dbh, $playerid, 0, $srcpos, $destpos); +} + + +############################################################ +### Shuffle play (query only) +sub pl_calc_shufflestats{ # query tracks and send back statistics + my $where = gddb::track_where_clause(1, @_); + my $sth = $dbh->prepare( + "SELECT SUM(length) AS playlen, COUNT(*) AS nbtracks " + ."FROM tracks WHERE $where" ); + $sth->execute; + my ($row, $nbtracks, $playlen); + if($row = $sth->fetchrow_hashref){ + $nbtracks = $row->{nbtracks}; + $playlen = $row->{playlen}; + } + else{ + print("Shuffle play: query error!\n"); + } + $sth->finish; + return ($nbtracks, $playlen); +} + + + +sub pl_check_shuffleplay{ # query tracks and send back statistics + my ($nbtracks, $playlen) = pl_calc_shufflestats(@_); + + if($nbtracks > 10){ + gdio::putline("$nbtracks tracks found. Total play length is " + . gdgentools::seconds_to_hm($playlen) + .". Play them in random order?"); + } + else{ + gdio::putline("Sorry! $nbtracks tracks are not enough to be played in shuffle mode."); + } + gdio::putline (""); +} + + + +sub pl_start_shuffleplay{ + pl_empty(); # stop playing, empty playlist and clear playstate + my ($nbtracks, $playlen) = pl_calc_shufflestats(@_); + my $shuffleparam = join ("\t",@_); ### Attention: does not join trailing empty parameters! + if (length($shuffleparam)==0){$shuffleparam ="\t\t";} ### join does not work on empty parameters -> cheat an empty parameter string! + gdgentools::pll_write_shuffleparameters($dbh, $playerid, $audchannel0, + $shuffleparam, "$nbtracks Tr [".gdgentools::seconds_to_hm($playlen)."]"); + gdgentools::pl_play($dbh, $playerid, $audchannel0); # start playing - the player script knows how to handle the shuffle play parameters +} + + +############################################################ + + +# send current playlist to the client (tracklist with ID, artist, title, length) +sub pl_reload_playlist{ + my @playlist = gdgentools::tracklist_get_all($dbh, $playerid, 0); + my ($stop, $trackid, $sth, $trackrow, $row); + + while($trackid = shift @playlist){ + $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); + $sth->execute; + + if($row = $sth->fetchrow_hashref){ + $stop = send_track($row); +# truncstr($row->{artist}, $shrtFieldLen); +# truncstr($row->{title}, $shrtFieldLen); +# print ("$row->{id}\t$row->{artist}\t$row->{title}\t$row->{length}\n"); +# gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" +# ."$row->{length}\t$row->{tracknb}"); + last if ($stop); + } + $sth->finish; + } + if (!$stop){ + gdio::putline ("");} # End of result +} + + +# send current playstate to the client +sub pl_get_curr_playstate{ + # sends: pll-length, current-index, current-id, playtime, tracklength, state + use integer; + +print "get playstate for channel ".$gdgentools::audchannel1."\n"; + my ($trackind, $state, $frame, $shufflestat) = + gdgentools::pll_get_playstate($dbh, $playerid, $audchannel0); + my ($played, $total) = gdgentools::pll_get_playtime($dbh, $playerid, $audchannel0); + my @playlist = gdgentools::tracklist_get_all($dbh, $playerid, 0); + my $playlistlen = @playlist; + my $pltime = $played/gdgentools::frames_per_second(); + my $pllen = $total/gdgentools::frames_per_second(); + + #print ("Sending state: pl-len:$playlistlen ind:$trackind id:$playlist[$trackind] ptime:$pltime ptot:$pllen state:$state\n"); + gdio::putline ("$playlistlen\t$trackind\t$playlist[$trackind]\t$pltime\t$pllen\t$state\t$shufflestat"); + gdio::putline (""); # End of result +} + + + + +################################################################ +### Export Playlist: + +sub pl_export_empty{ + my $base = gdparams::gdbase(); + + unlink <$base/outbox/*>; +} + +sub pl_export_audiolinks{ +# exports the current playlist as links to mp3-files to the outbox + my $base = gdparams::gdbase(); + my ($trackid, $row, $tracknum, $trh, $trkrow, $linkname, $mp3fp); + my ($audiofmt, $audioparam); + + ### run through playlist + my $sth = $dbh->prepare( + "SELECT * FROM tracklistitem " + ."WHERE playerid=$playerid AND listtype=$pl_list " + ."ORDER BY tracknb " ); + my $nbrec = $sth->execute; + while($row = $sth->fetchrow_hashref){ + $trackid = $row->{trackid}; + + ### get track details + $trh = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); + $trh->execute; + if($trkrow = $trh->fetchrow_hashref){ + ####$trackid = $row->{trackid}; + ### create link + $mp3fp = `ls $base/??/$trkrow->{mp3file}`; # get full filename and path + ($audiofmt, $audioparam) = gdgentools::bitrate_str_to_format_param($trkrow->{bitrate}); # split "mp3 128" + chop($mp3fp); + $linkname = gdgentools::ascii2filename( + gdgentools::iso2ascii($trkrow->{artist}." - ".$trkrow->{title})); + #print("exporting link: "."$mp3fp --- $base/outbox/$linkname.mp3"."\n"); + symlink ("$mp3fp", "$base/outbox/$linkname.$audiofmt"); + } + $trh->finish; + + } + $sth->finish; +} + + + +################################################################ +### Upload Playlist to mp3 Player: + +sub pl_upload_to_player_info{ +print("EXTMP3 is:".$gdparms::extmp3player."\n"); + if (length($gdparms::extmp3player)>0){ + pl_export_empty(); + pl_export_audiolinks(); +print("CMD: gdupload2player.pl --$gdparms::extmp3player --info\n"); + my $resline = `gdupload2player.pl --$gdparms::extmp3player --info`; + gdio::putline ($resline); + } + gdio::putline (""); # End of result +} + +sub pl_upload_to_player{ + my ($addtrks) = @_; + + system( "gdupload2player.pl --$gdparms::extmp3player $addtrks&"); +} + + + + +################################################################ +### Saved Playlists: + +sub pl_save_playlist{ +# saves the playlist with the specified name +# Parameters: title, author, note, followed by list of track id's + my $title = shift(@_); + my $author = shift(@_); + my $note = shift(@_); + my ($playlistid, $trackid, $tracknum); + + my $nbtracks = @_; + if (length($title)>0 && $nbtracks>0){ + ### check if playlist with that name already exists + my $sth = $dbh->prepare("SELECT * FROM playlist WHERE title = ".$dbh->quote($title)); + my $count = $sth->execute; + if ($count > 0){ + ### A playlist with this name exists already, get it's ID + my $row = $sth->fetchrow_hashref; + $playlistid = $row->{id}; + my $plh = $dbh->prepare( + "SELECT * FROM playlistitem WHERE playlist = ".$playlistid + ." ORDER BY tracknumber DESC LIMIT 1"); + $count = $plh->execute; + if ($count>0){ + $row = $plh->fetchrow_hashref; + $tracknum = $row->{tracknumber} +1; + } + else{ + $tracknum = 1; + } + $plh->finish; + + } + else{ + ### A playlist with this name does not exist, create it + my $sqlcmd = + "INSERT INTO playlist (title,author,note,created) " + ."VALUES (".$dbh->quote($title).",".$dbh->quote($author) + .",'$note',CURDATE()) "; + $dbh->do($sqlcmd); + $playlistid = $dbh->{'mysql_insertid'}; + $tracknum = 1; + } + $sth->finish; + + + ### Append tracks to playlist $playlistid starting with number $tracknum + while($trackid = shift(@_)){ + #print("appending track-ID $trackid as itemnb $tracknum\n"); + my $sqlcmd = + "INSERT INTO playlistitem (playlist,tracknumber,trackid) " + ."VALUES ($playlistid,$tracknum,$trackid) "; + $dbh->do($sqlcmd); + $tracknum++; + } + } + else{ + print("Can't save playlist: title or playlist are empty\n"); + } +} + + +sub pl_delete_playlist{ +# deletes the playlist with the specified id + my ($playlistid) = @_; + my $sqlcmd; + + ### Delete all associated tracks + $sqlcmd = ("DELETE FROM playlistitem WHERE playlist = ".$playlistid); + $dbh->do($sqlcmd); + + ### Delete playlist + $sqlcmd = ("DELETE FROM playlist WHERE id = ".$playlistid); + $dbh->do($sqlcmd); +} + + + +############################################################ +### Play mp3 file commands + +### directly play a track (in inbox, before trimming, etc) +sub cmd_direct_play_mp3_track{ +#parameters: filename of mp3 file (without path) +# startframe (optional) +# nbframes (optional) + + my ($audiofile, $startframe, $nbframes) = @_; + my $base = gdparams::gdbase(); + + ### kill early, for /dev/dsp to recover + gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); + system("sleep 1s"); # sleep a second + + if($startframe > 0){ + $startframe = "-k ".$startframe; + } + else{ + $startframe = "-k 0"; + } + if($nbframes > 0){ + $nbframes = "-n ".$nbframes; + } + else{ + $nbframes = ""; + } + + print("\nplaying inbox $audiofile $startframe $nbframes\n"); + # play it in inbox or in 00, 01 ... + if ( gdgentools::audio_filetype($audiofile) eq "mp3"){ + system( "mpg123 $startframe $nbframes $base/[0-9i][0-9n]*/$audiofile&"); + } + elsif(gdgentools::audio_filetype($audiofile) eq "ogg"){ + system( "ogg123 $base/[0-9i][0-9n]*/$audiofile&"); + } +} + + +### Command stop playing +sub cmd_stop_playing{ # + gdgentools::kill_all_play_processes(); +# gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); + print("Playing stopped\n"); +} + +############################################################ +### Play CD track commands + +### Command stop playing cd +sub cmd_stop_playing_cd{ # + system("cdplay stop"); +} + +### Command play CD track +sub cmd_play_cd_track{ # + my ($tracknb) = @_; + system("cdplay play $tracknb"); +} + + +############################################################ +### Listings ### +############################################################ + +############################################################ +### List used genres +sub list_used_genres{ #get all used genres, count and list them + + my $sth = $dbh->prepare( + 'SELECT id, genre, freq ' + .'FROM genre ' + .'ORDER BY genre ' + ); + + $sth->execute; + my (@row, $stop); + + # "no genre" first + print ("\t\t1\t0\t0\n"); + gdio::putline ("\t\t1\t0\t0"); + + while(@row = $sth->fetchrow_array){ + print ("$row[0]\t$row[1]\t$row[2]\t0\t0\n"); + $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]\t0\t0"); + last if ($stop); + } + gdio::putline (""); # End of result + $sth->finish; +} + +############################################################ +### List used languages +sub list_used_languages{ #get all used languages, count and list them + my $sth = $dbh->prepare( + 'SELECT id, language, freq ' + .'FROM language ' + .'ORDER BY freq DESC ' + ); + + my $rv = $sth->execute; + my (@row, $stop); + + # "no language" first + print ("\t\t1\n"); + gdio::putline ("\t\t1"); + + while(@row = $sth->fetchrow_array){ + print ("$row[0]\t$row[1]\t$row[2]\n"); + $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]"); + last if ($stop); + } + gdio::putline (""); # End of result + $sth->finish; +} + +############################################################ +### List music types +sub list_music_types{ + my $sth = $dbh->prepare('SELECT * FROM musictype ORDER BY id '); + my $rv = $sth->execute; + my ($row, $stop); + + while($row = $sth->fetchrow_hashref){ + print ("$row->{musictype}\n"); + $stop = gdio::putline ("$row->{musictype}"); + last if ($stop); + } + gdio::putline (""); # End of result + $sth->finish; +} + +############################################################ +### List music sources +sub list_music_sources{ + my $sth = $dbh->prepare('SELECT * FROM source ORDER BY id '); + my $rv = $sth->execute; + my ($row, $stop); + + while($row = $sth->fetchrow_hashref){ + print ("$row->{source}\n"); + $stop = gdio::putline ("$row->{source}"); + last if ($stop); + } + gdio::putline (""); # End of result + $sth->finish; +} + + +############################################################ +### Calculate language hash +sub language_hash{ # returns the hash value of the language + # table +# my $sth = $dbh->prepare( # get used languages only +# 'SELECT language.id, language.language, COUNT(*) AS freq ' +# .'FROM tracks,language ' +# .'WHERE tracks.lang=language.id ' +# .'GROUP BY language.id ' +# .'ORDER BY freq DESC ' +# ); + + my $sth = $dbh->prepare( + 'SELECT id, language, freq ' + .'FROM language ' + .'ORDER BY freq DESC ' + ); + + my $rv = $sth->execute; + my @row; + my $newval; + + my $hashval = 0; + + while(@row = $sth->fetchrow_array){ + ### calc hash value + $newval = ord(chop($row[0]))<<8; + $newval += ord(chop($row[0])); + $hashval = myhash::addvaltohash($hashval, $newval); + } + $sth->finish; + +# my $sth = $dbh->prepare( # get all languages alphabetically +# 'SELECT language.id ' +# .'FROM language ' +# .'ORDER BY language.id ' +# ); +# +# my $rv = $sth->execute; +# my @row; +# +# while(@row = $sth->fetchrow_array){ +# ### calc hash value +# $newval = ord(chop($row[0]))<<8; +# $newval += ord(chop($row[0])); +# $hashval = myhash::addvaltohash($hashval, $newval); +# } +# $sth->finish; + + return $hashval; +} + + +############################################################ +### Get language hash +sub get_language_hash{ # sends the language hash val to th client + # + + my $hashval = language_hash(); + + print("language hash value = $hashval \n"); + gdio::putline ("$hashval"); # send hash value + gdio::putline (""); # End of result +} + + +############################################################ +### calculate genre hash value +sub genre_hash{ # returns hash value of the language + # table + +# my $sth = $dbh->prepare( # get used genres only +# 'SELECT genre.id,genre.genre,COUNT(*) AS freq ' +# .'FROM tracks,genre ' +# .'WHERE tracks.genre1=genre.id OR tracks.genre2=genre.id ' +# .'GROUP BY genre.id ' +# .'ORDER BY freq ' +# ); + + my $sth = $dbh->prepare( + 'SELECT id, genre, freq ' + .'FROM genre ' + .'ORDER BY freq ' + ); + + my $rv = $sth->execute; + my @row; + my $newval; + + my $hashval = 0; + + while(@row = $sth->fetchrow_array){ + ### calc hash value + $newval = ord(chop($row[0]))<<16; + $newval += ord(chop($row[0]))<<8; + $newval += ord(chop($row[0])); + $hashval = myhash::addvaltohash($hashval, $newval); + } + $sth->finish; + +# my $sth = $dbh->prepare( # get all genres alphabetically +# 'SELECT genre.id ' +# .'FROM genre ' +# .'ORDER BY genre.id ' +# ); +# +# my $rv = $sth->execute; +# +# while(@row = $sth->fetchrow_array){ +# ### calc hash value +# $newval = ord(chop($row[0]))<<8; +# $newval += ord(chop($row[0])); +# $hashval = myhash::addvaltohash($hashval, $newval); +# } +# +# $sth->finish; + + return $hashval; +} + + + +############################################################ +### Get genre hash +sub get_genre_hash{ # send genre hash value to client + # + + my $hashval = genre_hash(); + + print("genre hash value = $hashval \n"); + gdio::putline ("$hashval"); # send hash value + gdio::putline (""); # End of result +} + + +############################################################ +### calculate types hash value +sub types_hash{ # returns hash value of the + # musictype table + + my $sth = $dbh->prepare('SELECT * FROM musictype ORDER BY id '); + my $rv = $sth->execute; + my $row; + my $hashval = 0; + my $newval; + + while($row = $sth->fetchrow_hashref){ + ### calc hash value + $newval = ord(chop($row->{musictype}))<<16; + $newval += ord(chop($row->{musictype}))<<8; + $newval += ord(chop($row->{musictype})); + $hashval = myhash::addvaltohash($hashval, $newval); + } + $sth->finish; + + return $hashval; +} + +############################################################ +### Get types hash +sub get_types_hash{ # send types hash value to client + # + + my $hashval = types_hash(); + + print("types hash value = $hashval \n"); + gdio::putline ("$hashval"); # send hash value + gdio::putline (""); # End of result +} + + +############################################################ +### calculate sources hash value +sub sources_hash{ # returns hash value of the + # source table + + my $sth = $dbh->prepare('SELECT * FROM source ORDER BY id '); + my $rv = $sth->execute; + my $row; + my $hashval = 0; + my $newval; + + while($row = $sth->fetchrow_hashref){ + ### calc hash value + $newval = ord(chop($row->{source}))<<16; + $newval += ord(chop($row->{source}))<<8; + $newval += ord(chop($row->{source})); + $hashval = myhash::addvaltohash($hashval, $newval); + } + $sth->finish; + + return $hashval; +} + +############################################################ +### Get sources hash +sub get_sources_hash{ # send sources hash value to client + # + + my $hashval = sources_hash(); + + print("sources hash value = $hashval \n"); + gdio::putline ("$hashval"); # send hash value + gdio::putline (""); # End of result +} + +############################################################ +### Get all hash +sub get_all_hashes{ # send hash values to client + # of all dynamic tables: + # language, genre, type + + my $languagehash = language_hash(); + my $genrehash = genre_hash(); + my $typeshash = types_hash(); + my $sourceshash = sources_hash(); + + print("hash values: $languagehash $genrehash $typeshash $sourceshash\n"); + gdio::putline ("$languagehash\t$genrehash\t$typeshash\t$sourceshash"); + gdio::putline (""); # End of result +} + + +############################################################ +### List all languages ordered +sub list_all_languages_ordered{ + #get all languages and list them ordered by their usage count +# my $sth = $dbh->prepare( +# 'SELECT language.id, language.language, ' +# .' (COUNT(*)-1) AS freq ' +# .'FROM language LEFT JOIN tracks ' +# .'ON tracks.lang=language.id ' +# .'GROUP BY language.id ' +# .'ORDER BY freq DESC, language.language ASC ' +# ); + my $sth = $dbh->prepare( + 'SELECT id, language, freq ' + .'FROM language ' + .'ORDER BY freq DESC, language ASC ' + ); + + my $rv = $sth->execute; + my (@row, $stop); + + while(@row = $sth->fetchrow_array){ + #if (length($row[2])==0){$row[2]=0;} + print ("$row[0]\t$row[1]\t$row[2]\n"); + $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]"); + last if ($stop); + } + gdio::putline (""); # End of result + $sth->finish; +} + + +############################################################ +### List all genres ordered directly +sub list_all_genres_ordered{ + # get all genres and list them ordered alphabetically by genre + # fields: id, genre, freq + + ### Get all genre entries + my $sthA = $dbh->prepare( + 'SELECT id, genre FROM genre ORDER BY genre.id ' + ); + my $totcnt = $sthA->execute; + + ### Get used entries only with count +# my $sthB = $dbh->prepare( +# 'SELECT genre.id,genre.genre,COUNT(*) AS freq ' +# .'FROM tracks,genre ' +# .'WHERE tracks.genre1=genre.id OR tracks.genre2=genre.id ' +# .'GROUP BY genre.id ORDER BY genre.id ' +# ); + my $sthB = $dbh->prepare( + 'SELECT id, genre, freq ' + .'FROM genre ' + .'ORDER BY id ' + ); + + my $usedcnt = $sthB->execute; + +### The following code should actually go to 'gdtablefreq.pl' ! + + ### Merge frequencies of list B into list A (both lists must be ordered by id!) + # -> build "hierarchical sum": freq(node) = sum-of each freq(subnode) + proper freq + my $rowB = $sthB->fetchrow_hashref; + my ($i, $rowA, @listA, $rec); + while ($rowA = $sthA->fetchrow_hashref){ + $rec = {}; + $rec->{"id"} = $rowA->{id}; + $rec->{genre} = $rowA->{genre}; + $rec->{freq} = 0; # init freq counter + if($rowA->{id} eq $rowB->{id}){ + # copy frequency of B to A + $rec->{freq} = $rowB->{freq}; + $rowB = $sthB->fetchrow_hashref; # get next entry from B + } + push @listA, $rec; + } + + recalc_genre_freq_arrayofhash(@listA); + + @listA = sort { $gdserv::a->{genre} cmp $gdserv::b->{genre} } @listA; + + # "no genre" first: freq=1 + print ("\t\t1\n"); + gdio::putline ("\t\t1"); + + my $stop; + for $i (0 .. $#listA){ + print ("$listA[$i]{id}\t$listA[$i]{genre}\t$listA[$i]{freq}\n"); + $stop = gdio::putline + ("$listA[$i]{id}\t$listA[$i]{genre}\t$listA[$i]{freq}"); + last if ($stop); + } + gdio::putline (""); # End of result + $sthA->finish; + $sthB->finish; +} + + +############################################################ +sub recalc_genre_freq_arrayofhash{ + ### new version: works on array of hashes (id, genre, freq) + # requires that the array is ordered by id! + + ### first pass, get maximal genre id length + my $arrlen = @_; + my $maxlen = 0; + my $i; + + for $i( 0 .. $arrlen ){ + if(length($_[$i]{id}) > $maxlen){ + $maxlen = length($_[$i]{id}); + } + } + ### next passes: cumulate freq of ID with maxlen to ID with maxlen-1 + my ($parent, $ID); + while ($maxlen > 1){ + for $i( 0 .. $arrlen ){ + # 0:id, 1:genre, 2:freq + if(length($_[$i]{id}) == $maxlen){ + ### add freq of this item to freq of parent item + $ID = $_[$i]{id}; + chop($ID); + $parent=0; + while($parent<$arrlen && $_[$parent]{id} ne $ID){ + $parent++; + } + if($_[$parent]{id} eq $ID){ + $_[$parent]{freq} += $_[$i]{freq}; + } + else{print("ERROR: cant' find parent ID $ID \n");exit;} + } + } + $maxlen--; + } +} + + +############################################################ +### Modify genres table + +############################################################ +### Modify Genres: Delete +sub gen_delete{ + ### Delete a genre and move all it's tracks to the supergenre + my($genreid) = @_; # the ID of the genre to be deleted + + my $targetid = $genreid; + chop ($targetid); + print ("Delete genre id $genreid translate it to $targetid\n"); + + my ($retval1, $retval2, $gensdel); + $retval1 = $dbh->do('UPDATE tracks SET genre1="'.$targetid.'" WHERE genre1 LIKE "'.$genreid.'%"'); + $retval2 = $dbh->do('UPDATE tracks SET genre2="'.$targetid.'" WHERE genre2 LIKE "'.$genreid.'%"'); + $gensdel= $dbh->do('DELETE FROM genre WHERE id LIKE "'.$genreid.'%"'); + print("$gensdel genre entries deleted. $retval1 + $retval2 track records changed to supergenre.\n"); + + if($retval1 eq '0E0'){$retval1=0;} + if($retval2 eq '0E0'){$retval2=0;} + gdio::putline ("$gensdel genre entries deleted. ".($retval1+$retval2)." track records changed."); + gdio::putline (""); # End of result +} + + +############################################################ +### find a free genre for a given prefix. It returns only the suffix. +sub gen_new_suffix{ + my ($prefix) = @_; + my $suffix = "a"; + ### search for a new id + my ($sth, $count); + while($suffix ne "z"){ + $sth = $dbh->prepare("SELECT * FROM genre WHERE id = '".$prefix.$suffix."'"); + $count = $sth->execute; + $sth->finish; + last if ($count eq '0E0'); + $suffix++; + } + return $suffix; +} + +############################################################ +### Modify Genres: Move +sub gen_move{ + ### Move a genre and all its subgenres to a new location in the hierarchy + my($genreidFrom, $genreidTo) = @_; # the two IDs + + print ("Move genre $genreidFrom to $genreidTo\n"); + + my $suffix = gen_new_suffix($genreidTo); # new subgenre in target genre + + if($suffix eq "z"){ + print ("No genre moved! You can't put more than 26 subgenres in a genre. \n"); + gdio::putline ("No genre moved! You can't put more than 26 subgenres in a genre."); + } + elsif( (length($genreidFrom) <= length($genreidTo)) + && (substr($genreidTo, 0, length($genreidFrom)) eq $genreidFrom) + ){ + print ("No genre moved! Target is a child of Source. \n"); + gdio::putline ("No genre moved! Target is a child of Source."); + } + else{ + ### replace all genre prefixes "$genreidFrom" by "$genreidTo.$suffix" + ### in tracks and genre table + + my $retval1 = $dbh->do( + 'UPDATE tracks SET genre1=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(genre1, '.(length($genreidFrom)+1).')) ' + .'WHERE genre1 LIKE "'.$genreidFrom.'%"'); + my $retval2 = $dbh->do( + 'UPDATE tracks SET genre2=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(genre2, '.(length($genreidFrom)+1).')) ' + .'WHERE genre2 LIKE "'.$genreidFrom.'%"'); + my $retval3 = $dbh->do( + 'UPDATE genre SET id=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(id, '.(length($genreidFrom)+1).')) ' + .'WHERE id LIKE "'.$genreidFrom.'%"'); + gdio::putline (($retval1+$retval2)." track genres, $retval3 genre IDs moved."); +# gdio::putline (" track genres moved."); + } + gdio::putline (""); # End of result +} + + +############################################################ +### Modify Genres: Rename +sub gen_rename{ + ### Rename an existing genre + my($genreid, $newgenrename) = @_; # the ID and the new name of the genre + print ("Add genre $newgenrename to $genreid\n"); + + my $retval = $dbh->do("UPDATE genre SET genre=".$dbh->quote($newgenrename)." WHERE id = '".$genreid."'"); + + gdio::putline ("Genre renamed to $newgenrename."); + gdio::putline (""); # End of result +} + + +############################################################ +### Modify Genres: Add +sub gen_add{ + ### Add a new genre + my($genreid, $newgenrename) = @_; # the ID of the genre where the new genre is added + print ("Add genre $newgenrename to $genreid\n"); + + my $suffix = gen_new_suffix($genreid); + + if($suffix eq "z"){ + print ("No genre added! You can't put more than 26 subgenres in a genre. \n"); + gdio::putline ("No genre added! You can't put more than 26 subgenres in a genre."); + } + else{ + my $retval = $dbh->do("INSERT INTO genre " + ."(id, id3genre, genre) " + ."VALUES('".$genreid.$suffix."',NULL,".$dbh->quote($newgenrename).")"); + gdio::putline ("Genre $newgenrename added."); + } + gdio::putline (""); # End of result +} + + + + +############################################################ +### Modify music types table + +############################################################ +### returns the number of records in the musictype table +sub typ_nb_records{ + my ($sth, $count); + $sth = $dbh->prepare("SELECT * FROM musictype"); + $count = $sth->execute; + $sth->finish; + return $count; +} + +############################################################ +### Modify Types: Add +sub typ_add{ + ### Add a new type + my($newtypename) = @_; + print ("Add type $newtypename\n"); + + my $index = typ_nb_records() + 1; + + my $retval = $dbh->do("INSERT INTO musictype " + ."(musictype, id) VALUES(".$dbh->quote($newtypename).",'".$index."')"); + gdio::putline ("Music type $newtypename added."); + gdio::putline (""); # End of result +} + +############################################################ +### Modify Types: Delete +sub typ_delete{ + ### Delete a type + + my($delid) = @_; # the ID of the type to be deleted + my $lastid = typ_nb_records() - 1; + + # sequence of id-numbers must be contiguous: -> "move" lastid over delid, + # overwriting delid. + + # Note: track.type starts from 0, musictype.id starts from 1 + # Bad, I know, but it's too late to change it. I ignore the num value + # of musictype.id, and only use it to define an order. + print ("Delete type id $delid, put $lastid to its place\n"); + + ### modify tracks table + my ($retval1, $retval2, $typsdel); + $retval1 = $dbh->do('UPDATE tracks SET type= 0 WHERE type = '.$delid); + $retval2 = $dbh->do('UPDATE tracks SET type='.$delid.' WHERE type = '.$lastid); + + ### modify musictypes table + $typsdel= $dbh->do('DELETE FROM musictype WHERE id ='.($delid+1)); + $retval2= $dbh->do('UPDATE musictype SET id='.($delid+1).' WHERE id = '.($lastid+1)); + + print("Type deleted. Type of ".$retval1." tracks set to NULL.\n"); + + if($retval1 eq '0E0'){$retval1=0;} + if($retval2 eq '0E0'){$retval2=0;} + gdio::putline ("Type deleted. Type of ".$retval1." tracks set to NULL."); + gdio::putline (""); # End of result +} + + +############################################################ +### Send complete track +sub send_track_row{ + # Sends a complete track row obained from a SELECT * FROM tracks + # query. + # The parameter is a track row (record) obtained by a + # $trackset->fetchrow_hashref command + + my ($trackrow) = @_; + ## if ($nbtrks > 0){ + print( + $trackrow->{artist}.", " + .$trackrow->{title}.", " + .$trackrow->{genre1}.", " + .$trackrow->{genre2}.", " + .$trackrow->{year}.", " + .$trackrow->{lang}.", " + .$trackrow->{type}.", " + .$trackrow->{rating}.", " + .$trackrow->{length}.", " + .$trackrow->{source}.", " + .$trackrow->{sourceid}.", " + .$trackrow->{tracknb}.", " + .$trackrow->{mp3file}.", " + .$trackrow->{condition}.", " + .$trackrow->{voladjust}.", " + .$trackrow->{created}.", " + .$trackrow->{modified}.", " + .$trackrow->{id}.", " + .$trackrow->{bitrate}.", " + .$trackrow->{haslyrics} + ."\n" + ); + + gdio::putline ( + $trackrow->{artist}."\t" + .$trackrow->{title}."\t" + .$trackrow->{genre1}."\t" + .$trackrow->{genre2}."\t" + .$trackrow->{year}."\t" + .$trackrow->{lang}."\t" + .$trackrow->{type}."\t" + .$trackrow->{rating}."\t" + .$trackrow->{length}."\t" + .$trackrow->{source}."\t" + .$trackrow->{sourceid}."\t" + .$trackrow->{tracknb}."\t" + .$trackrow->{mp3file}."\t" + .$trackrow->{condition}."\t" + .$trackrow->{voladjust}."\t" + .$trackrow->{created}."\t" + .$trackrow->{modified}."\t" + .$trackrow->{id}."\t" + .$trackrow->{bitrate}."\t" + .$trackrow->{haslyrics} + ); + + # These variables give a hint, if the frequencies should be recalculated (when a track is updated) + $trk_last_id = $trackrow->{id}; + $trk_last_lang = $trackrow->{lang}; + $trk_last_genre1 = $trackrow->{genre1}; + $trk_last_genre2 = $trackrow->{genre2}; +} + + + +############################################################ +### Set CDDB_get parameters +my %cddb_config; +# following variables just need to be declared if different from defaults +$cddb_config{CDDB_HOST}="freedb.freedb.org"; # set cddb host +$cddb_config{CDDB_PORT}=8880; # set cddb port +$cddb_config{CDDB_MODE}="cddb"; # set cddb mode: cddb or http +$cddb_config{CD_DEVICE}="/dev/cdrom"; # set cd device +# user interaction welcome? +$cddb_config{input}=0; # 1: ask user if more than one possibility + # 0: no user interaction + + +############################################################ +### Get and return id and toc of currently inserted CD +sub get_cd_diskid{ + use CDDB_get; + # get id and track lengths + my $diskid=CDDB_get::get_discids($cddb_config{CD_DEVICE}); +# $track[0]{cddbid} = sprintf "%lx", $diskid->[0]; # get hex notation + $diskid->[0] = sprintf "%08lx", $diskid->[0]; # get hex notation + return $diskid; +} + +############################################################ +### Get and return directory of currently inserted CD +sub get_cd_directory{ +# returns array of hashes: index 0: cddbid; index 1..: length (in seconds) +# index 0: {cddbid (hex, without '0x')}, {title}, {artist} +# other indexes: {tracklength-sec}, {title}, {artist} +# +# title and artist are only defined, if a cddb entry was found +# +# 1) if the system has internet access, a matching record is +# searched at freedb.org +# 2) searches for a matching cddb record in ~/cddb/* +# 3) If a record couldn't be found, an array with default strings +# is returned +# + + use CDDB_get; + #use Net::Ping; #can't use it because it requires root privileges + + my @track; + my $diskid = get_cd_diskid(); + my $base = gdparams::gdbase(); + $track[0]{artist} = "Artist"; + $track[0]{title} = "Album Title"; + $track[0]{cddbid} = $diskid->[0]; + my $nbtracks = $diskid->[1]; + my $toc = $diskid->[2]; + my $cddbid = $diskid->[0]; + my ($lengthsec); + my $i=1; + while ($i <= $nbtracks) {#i[$i] ->{min}*60+$toc->[$i] ->{sec}) + -($toc->[$i-1]->{min}*60+$toc->[$i-1]->{sec}); + $track[$i]{track}= $i; + $track[$i]{length}= $lengthsec; + $track[$i]{artist}=" "; # some default values + $track[$i]{title} ="CD Track $i"; + $i++; + } + + +#print "\nBEGIN ping\n"; +#my $p = Net::Ping->new("icmp"); +#print "freedb.freedb.org is alive.\n" if $p->ping("freedb.freedb.org", 2); +#$p->close(); +#print "END ping\n"; + + if ($gdparms::systemonline){# Are we online? + ################################################################## + ### We are online, Try to get matching freedb entry + my %cd=CDDB_get::get_cddb(\%cddb_config); + + if(defined $cd{title}) { + $track[0]{artist} = $cd{artist}; + $track[0]{title} = $cd{title}; + #$track[0]{year} = $cd{year}; + ### Add track info to @track + my $i=1; + while ($i <= $nbtracks) { + #print "$i: $cd{track}[$i-1] \n"; + $track[$i]{artist} = $cd{artist}; + $track[$i]{title} = $cd{track}[$i-1]; + # ...{genre} = $cd{cat}; + $i++; + } + } + else{ + print "freedb: NOT FOUND cddbid: $cd{id}\n"; + } + } + else{ + ################################################################## + ### We are offline, try to find a cddb record on the local machine + my $cddb_file = `find $base/cddb -name $cddbid -print`; + $cddb_file = (split /\s+/, $cddb_file)[0]; # get first match only + if(length($cddb_file) > 0){ + ### a cddb-file was found - get it + my @cddblst = get_cddb_rec_offline("$cddb_file"); + $i = 0; + while ($i <= $nbtracks){ + $track[$i]{artist} = $cddblst[$i]{artist}; + $track[$i]{title} = $cddblst[$i]{title}; + $i++; + } + } + } + return @track; +} + + +#sub get_cd__directory___OLD___{ +#sub get_cd__directory{ +# returns array of hashes: index 0: cddbid; index 1..: length (in seconds) +# my $tmpout="/tmp/cdda2wav.index.out"; +# my $nbtracks = 0; +# my @track; +# my $discid; +# my $line; +# +# system("rm $tmpout"); +# system("cdda2wav -D /dev/cdrom -N -d1 &> $tmpout"); +# open(TMPOUT, $tmpout); +# while(){ +# $line = $_; +# if($line=~m/total tracks/){ +# $line=~m/total tracks:([0-9]*)/; +# $nbtracks = $1; +# } +# while($line=~m/[0-9]*\.\(\s*[0-9]*:[0-9]*\.[0-9]*\)/){ +# $line=~m/([0-9]*)\.\(\s*([0-9]*):([0-9]*)\.[0-9]*\)/; +# $track[$1]{track}= $1; +# $track[$1]{length}= ($2*60)+$3; +# +# #push @track, $rec; +# $line=~s/[0-9]*\.\(\s*[0-9]*:[0-9]*\.[0-9]*\)/xx/; +# } +# if($line=~m/CDDB discid:\s*[0-9a-z]*/){ +# $line=~m/CDDB discid:\s*([0-9a-z]*)/; +# #print "Disc ID: $1\n"; +# $discid=$1; +# $discid =~ m/0x(\S*)/; # remove 0x +# $track[0]{cddbid}= $1; +# } +# } +# close(TMPOUT); +# return @track; +#} + + + +############################################################ +### Read the specified cddb-file and return its content +sub get_cddb_rec_offline{ +# returns array: index 0: album artist, album title; +# index 1..: track title; +# examples: -> separator: '-' or '/' +# DTITLE=Die Aerzte - Die Bestie in Menschengestalt +# DTITLE=MC Lyte / Ain't No Other + + my ($cddb_file) = @_; + + my @track; + my $line; + + open(CDDBF, $cddb_file) or die "can't open file $cddb_file\n"; + while(){ + $line = $_; + chop($line); + if($line=~m/DTITLE=.+/){ + if ($line=~m/DTITLE=(.+) \/ (.+)/){ + $track[0]{artist}= $1; + $track[0]{title} = $2; + } + elsif($line=~m/DTITLE=(.+) \- (.+)/){ + $track[0]{artist}= $1; + $track[0]{title} = $2; + } + elsif($line=~m/DTITLE=(.+)/){ + $track[0]{artist}= $1; + $track[0]{title} = ""; + } + } + + if($line=~m/TTITLE([0-9]+)=(.+)/){ + $track[$1+1]{artist}= $track[0]{artist}; + $track[$1+1]{title} = $2; + } + } + close(CDDBF); + + return @track; + +} + +############################################################ +### List directory of currently inserted CD plus informations from DB +sub list_cd_directory_info{ + # takes no parameter or the cddb-id + + my ($cddbid) = @_; + + my @track; + my $nbtracks; + my $i; + my $nbtrks; + my $nbalbs; + my $trseth; # track set handle + my @row; + my $trackrow; + my $base = gdparams::gdbase(); + my $cddb_file=""; + my @cddblst; + + ######################################################### + ### Set default values + @track = get_cd_directory(); + $nbtracks = @track - 1; + + ######################################################### + ### Send album record first + $trseth = $dbh->prepare( + 'SELECT * FROM album WHERE cddbid ="'. $cddbid . '" '); + $nbalbs = $trseth->execute; + if($nbalbs > 0){ + print("Matching album in database found"); + $trackrow = $trseth->fetchrow_hashref; + $trackrow->{cddbid} = $cddbid; + print("$trackrow->{artist}\t$trackrow->{title}\t$cddbid\n"); + gdio::putline("$trackrow->{artist}\t$trackrow->{title}\t$cddbid"); + } + else{ + if(defined($track[0]{title})){ + ### a matching online freedb entry was found + print("$track[0]{artist}\t$track[0]{title}\t$track[0]{cddbid}\n"); + gdio::putline("$track[0]{artist}\t$track[0]{title}\t$track[0]{cddbid}"); + } + else{ + print "Error: can't read CD directory at all\n"; + } + } + $trseth->finish; + + ######################################################### + ### Send each track + for ($i=1; $i<=$nbtracks; $i++){ + ### get first track with given cddb-id and track number + + if (length($cddbid)>=8){ + $trseth = $dbh->prepare( + 'SELECT * FROM tracks ' + .'WHERE sourceid ="'. $cddbid . '" ' + .' AND tracknb = '.$i ); + $nbtrks = $trseth->execute; + $trackrow = $trseth->fetchrow_hashref; + } + + if ((length($cddbid)<8) || ($nbtrks < 1)){ + ### set default values + $nbtrks = 0; + $trackrow->{artist} = $track[$i]{artist}; + $trackrow->{title} = $track[$i]{title}; + $trackrow->{genre1} = ""; + $trackrow->{genre2} = ""; + $trackrow->{year} = ""; + $trackrow->{lang} = ""; + $trackrow->{type} = 0; + $trackrow->{rating} = 0; + $trackrow->{length} = $track[$i]{length}; + $trackrow->{source} = 0; + $trackrow->{sourceid} = $track[0]{cddbid}; + $trackrow->{tracknb} = $i; + $trackrow->{mp3file} = ""; + $trackrow->{condition}= 0; + $trackrow->{voladjust}= 0; + #$trackrow->{created} = + #$trackrow->{modified} = + $trackrow->{bitrate} = $gdparms::defrecbitrate; + $trackrow->{haslyrics}=""; + } + + ### send all fields + send_track_row($trackrow); + + $trseth->finish; + }# end for + + gdio::putline (""); # End of result +} +############################################################ +### List directory of currently inserted CD plus informations from DB +sub list_inbox_album_directory{ + # takes as parameter the album directory (without /home/music/inbox prefix) + + my ($path) = @_; + + my ($curfile); + my $base = gdparams::gdbase(); + my $fullpath = "$base/inbox/albums/$path"; + print "import album at $fullpath\n"; + + unlink <$base/inbox/trxx*>; + unlink <$base/inbox/tmp-album-dir>; + system "cd \"$fullpath\"; rm -f trxx*"; + opendir INBOX, "$fullpath"; + my @inboxfile = readdir INBOX; + closedir INBOX; + + ### generate new mp3-filename (get highest trxx... filename) + my $fileid = gdgentools::last_imported_tracknb($dbh); + $fileid += 1; # next available id + my $cddbid = sprintf("%08ld", $fileid); + my $tracknb = 1; + + ######################################################### + ### Send album record first + + print "$path\t$path\t$cddbid\n"; + gdio::putline("$path\t$path\t$cddbid"); + #print "ln -s \"$fullpath\" \"$base/inbox/tmp-album-dir\"\n"; + system "ln -s \"$fullpath\" \"$base/inbox/tmp-album-dir\""; + + ######################################################### + ### Send each track + foreach $curfile (@inboxfile){ + ### check if file is audio format + if ( gdgentools::audio_filetype($curfile) eq "mp3" + || gdgentools::audio_filetype($curfile) eq "ogg" + || gdgentools::audio_filetype($curfile) eq "flac"){ + send_inbox_track_info($curfile, $fileid, "$cddbid", $tracknb, "$fullpath"); + $fileid += 1; + $tracknb += 1; + } + }# end foreach + + gdio::putline (""); # End of result +} + + + +############################################################ +### List directory of current inbox plus informations from the metatags +sub list_inbox_directory_info{ + + my $base = gdparams::gdbase(); + + unlink <$base/inbox/trxx*>; + opendir INBOX, "$base/inbox"; + my @ibfile = readdir INBOX; + closedir INBOX; + + my ($curfile); + + + ### generate new mp3-filename (get highest trxx... filename) + my $fileid = gdgentools::last_imported_tracknb($dbh); + + ### Send each track + foreach $curfile (@ibfile){ + ### check if file is mp3 format + if ( gdgentools::audio_filetype($curfile) eq "mp3" + || gdgentools::audio_filetype($curfile) eq "ogg" + || gdgentools::audio_filetype($curfile) eq "flac"){ + $fileid += 1; #next available id + send_inbox_track_info($curfile, $fileid, "", 1, "$base/inbox"); + } + }# end foreach + + gdio::putline (""); # End of result +} + +############################################################ +### the file $curfile must be valid audio file +# if it is a audiofile, all available metadata and other track +# informations are sent to the server +sub send_inbox_track_info{ + + my ($curfile, $fileid, $cddbid, $tracknb, $path) = @_; + my ($id3genre, $title, $audiofile, $trackrow); + + my $base = gdparams::gdbase(); + + ### check if file has a legal audio format + + if ( gdgentools::audio_filetype($curfile) eq "mp3" + || gdgentools::audio_filetype($curfile) eq "ogg" + || gdgentools::audio_filetype($curfile) eq "flac"){ + print ("\ncurrentfile is: $curfile \n"); + + $audiofile = sprintf("trxx%08ld.%s", $fileid, gdgentools::audio_filetype($curfile)); + symlink "$path/$curfile", "$base/inbox/$audiofile"; + + # get info from meta tags + $trackrow->{title} = gdgentools::audiofile_title("$path/$curfile"); # takes filename as default of no metatag present + $trackrow->{artist} = gdgentools::audiofile_artist("$path/$curfile"); + $trackrow->{genre2} = ""; + $trackrow->{year} = gdgentools::audiofile_year("$path/$curfile"); + $trackrow->{lang} = "-"; + $trackrow->{type} = 1; # medium + $trackrow->{rating} = 0; + $trackrow->{length} = gdgentools::audiofile_lengthsec("$path/$curfile"); + $trackrow->{source} = 0; # CD + $trackrow->{sourceid} = $cddbid; # could also be empty + $trackrow->{tracknb} = $tracknb; + $trackrow->{mp3file} = $audiofile; + $trackrow->{condition} = 0; # OK + $trackrow->{voladjust} = 0; + $trackrow->{created} = ""; + $trackrow->{modified} = ""; + $trackrow->{id} = ""; # a new one will be automatically generated + $trackrow->{bitrate} = gdgentools::get_bitrate_str("$path/$curfile"); + + # get genre if available and translate to gd-genre + $trackrow->{genre1} = gdgentools::audiofile_genre($dbh, "$path/$curfile"); +# $id3genre = gdgentools::audiofile_genre("$path/$curfile"); +# $trackrow->{genre1} = gdgentools::genre_id3togd($dbh, $id3genre); + + ### send all fields + $trackrow->{haslyrics} = ""; + send_track_row($trackrow); + + }# end if is mp3file +} + +############################################################ +### import booklet/cover images +sub import_cover_img{ + # imports the jpeg images in a directory and associates them to an album + # the images are imported in lexical order. + # Naming scheme: trxx(cd-id)-(num).jpg, where num is an automatically + # incremented counter. The file imgxx(cd-id)-00.jpg is the front cover, + # the other are the following pages in a booklet. + + # Parameters: 1) full directory path, 2) cd-id (like 0x10ac77e0, xx00001496) + + gdgentools::import_cover_images($dbh, @_); +} + + +############################################################ +### Get one track and send all information details from DB +sub get_track_details{ + # takes as parameter the track-ID + + my ($trackid) = @_; + my $base = gdparams::gdbase(); + + + my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); + $sth->execute; + my $trackrow = $sth->fetchrow_hashref; + + if(length($trackrow->{mp3file})>0){ + if(length($trackrow->{lyrics})>0){ + $trackrow->{haslyrics} = "1"; + } + } + ### send all fields + send_track_row($trackrow); + + $sth->finish; + + gdio::putline (""); # End of result +} + + +############################################################ +### Delete specified track record and mp3 file +sub delete_track{ +# deletes the playlist with the specified id + my ($trackid) = @_; + my $sqlcmd; + my $base = gdparams::gdbase(); + + ### Get track record + my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); + $sth->execute; + my $trackrow = $sth->fetchrow_hashref; + my $trackfile = $trackrow->{mp3file}; + truncstr($trackrow->{artist}, $shrtFieldLen); + truncstr($trackrow->{title}, $shrtFieldLen); + print("Deleting ($trackrow->{id}) $trackrow->{artist}, $trackrow->{title}\n"); + + ### Delete track record + $sqlcmd = ("DELETE FROM tracks WHERE id = ".$trackid); +#print" \$dbh->do($sqlcmd);\n"; + $dbh->do($sqlcmd); + + ### Delete mp3 file +#print("rm $base/[0-9][0-9]/$trackfile \n"); + if (length($trackfile)>4){ + system("rm $base/[0-9][0-9]/$trackfile"); + } + $sth->finish; + +} + + +############################################################ +### Delete specified album and all associated track records and mp3 files +sub delete_album_tracks{ +# deletes the playlist with the specified id + my ($albumid) = @_; + + if(length($albumid)>=6) { + ### Delete all associated tracks + ### get tracks of the album + my $trksth = do_tracks_of_album_query($albumid); + my $trow; + while($trow = $trksth->fetchrow_hashref){ + delete_track($trow->{id}); + } + $trksth->finish; + + ### Delete album record + print("Deleting Album $albumid\n"); + my $sqlcmd = ("DELETE FROM album WHERE cddbid = '".$albumid."'"); +#print"\$dbh->do($sqlcmd);\n" + $dbh->do($sqlcmd); + } +} + + +############################################################ +### Get one track and send lyrics (or empty text) +sub get_track_lyrics{ + # takes as parameter the track-ID + + my ($trackid) = @_; + + my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); + $sth->execute; + my $trackrow = $sth->fetchrow_hashref; + + ### insert one space into empty lines (protocol requires non empty content lines) + my $lyrics = $trackrow->{lyrics}; + + ### send lyrics + my @lyrlines = split /\n/, $trackrow->{lyrics}; + chomp @lyrlines; + my $line; + print ("Sending lyrics: ".scalar(@lyrlines)." lines\n"); + foreach $line (@lyrlines){ # line end is a \015\012 sequence + $line =~ s/\012$//; # chop off very last newline, if there is one + $line =~ s/\015$//; # chop off very last newline, if there is one + if (length($line)==0) {$line = " ";} + gdio::putline ($line); + } + gdio::putline (""); # End of result + + $sth->finish; +} + +############################################################ +### Update the lyrics of a track +sub update_track_lyrics{ + # parameters: track-ID, lyrics lines + + my $trackid = shift @_; + my $lyrics = join "\n", @_; + + print("Lyrics recieved for $trackid:\n$lyrics\n"); + $dbh->do("UPDATE tracks " + ."SET lyrics=".$dbh->quote($lyrics) + ."WHERE id=$trackid"); + +} + + +############################################################ +### Trim mp3 file +sub trim_mp3_file{ + # parameters: track-ID, lyrics lines + + my ($mp3file, $startframe, $endframe) = @_; + my ($mp3fpath, $mp3directory, $undofname); + my $base = gdparams::gdbase(); + + $mp3fpath = gdgentools::get_full_audiofile_path($mp3file); + $mp3directory = dirname($mp3fpath); + $undofname = $mp3directory."/TrimUndo-".$mp3file; + + + print ("rm $mp3directory/TrimUndo-*\n"); # remove old undofile + system("rm $mp3directory/TrimUndo-*"); + print ("mv $mp3fpath $undofname\n"); # save original mp3file + system("mv $mp3fpath $undofname"); + + print ("gdmp3cutter $undofname $startframe $endframe $mp3fpath\n"); + #system("gdmp3cutter $undofname $startframe $endframe $mp3fpath"); + ### continuously send results, line by line + my ($res, $resline); + open CMDRES, "gdmp3cutter $undofname $startframe $endframe $mp3fpath |"; + #autoflush CMDRES 1; + while($resline = ){ + chop($resline); + print ("\"$resline\"\n"); + gdio::putline ($resline); + } + + print ("END\n"); + gdio::putline (""); # End of result +} + +############################################################ +### Get one album and send all information details from DB +sub trim_mp3_file_undo{ # restores the trimmed mp3 file (if possible) + # parameters: track-ID, lyrics lines + + my ($mp3file) = @_; + my ($mp3fpath, $mp3directory, $undofname); + + + my $base = gdparams::gdbase(); + my $undofname = `ls $base/[0-9][0-9]/TrimUndo-$mp3file`; # get full path + chop($undofname); + + $mp3directory = dirname($undofname); + $mp3fpath = $mp3directory."/".$mp3file; + + #gdio::putline ("mv $undofname $mp3fpath"); + print ("mv $undofname $mp3fpath \n"); # restore original mp3file + system("mv $undofname $mp3fpath"); + + gdio::putline (""); # End of result +} + + +############################################################ +### Get one album and send all information details from DB +sub get_album_details{ + # takes as parameter the cddb-ID + + my ($albumid) = @_; + + if (length($albumid) > 8) { ### sometimes, a distorted album id preceeded by 4 trashy characters + # is passed to this routine (reason is unknown). + # this is just a workaround sent in by Frank (slightly modified by me) + print "warning: albumid preceeded by trash! fixing it.\n"; + ($albumid) = substr($albumid,-8,8); + } + + my $base = gdparams::gdbase(); + + + my $sth = $dbh->prepare("SELECT * FROM album WHERE cddbid=\"$albumid\""); + $sth->execute; + my $row; + if($row = $sth->fetchrow_hashref){ + ### send all fields + print("$row->{artist}\n"); + print("$row->{title}\n"); + gdio::putline ("$row->{artist}"); + gdio::putline ("$row->{title}"); + } + gdio::putline (""); # End of result + + $sth->finish; +} + +############################################################ +### check currently inserted CD and compare to GDDB +sub check_current_cd{ + +# This routine finds all distinct cddb-ID's in the GDDB that have +# the same stem as the cddb-ID of the currently inserted CD. +# The routine is needed to solve the problem of potentially ambiguous +# cddb-ID's. It is usually called before grabbing a CD. + +# my @track = get_cd_directory(); + #$track[0]{cddbid} =~ m/0x(\S*)/; + #my $cddbid = $track[0]{cddbid}; + my $diskid = get_cd_diskid(); + my $cddbid = $diskid->[0]; + + ### Get all cddb-id's with root $cddbid + my $sth = $dbh->prepare( + 'SELECT DISTINCT sourceid ' + .'FROM tracks ' + .'WHERE sourceid LIKE "'. $cddbid. '%" ' + .'ORDER BY sourceid ' + ); + + my $rv = $sth->execute; + + ### Get first track for each distinct cddb-id + my $trseth; # track set handle + my $row; + my $trackrow; + while($row = $sth->fetchrow_hashref){ + ### get first track with current cddb-id + + $trseth = $dbh->prepare( + 'SELECT artist, title FROM tracks ' + .'WHERE sourceid ="'. $row->{sourceid} . '" ORDER BY tracknb '); + $trseth->execute; + $trackrow = $trseth->fetchrow_hashref; + + print("$row->{sourceid}\t$trackrow->{artist}, $trackrow->{title}\n"); + gdio::putline ("$row->{sourceid}\t$trackrow->{artist}, " + ."$trackrow->{title}"); + $trseth->finish; + } + if($rv > 0){ ### propose also new unambiguous key + ### bulletproof is to take biggest extension instead of $rv!!! + + print ("$cddbid.$rv\tNone of these: New CD\n"); + gdio::putline("$cddbid.$rv\tNone of these: New CD"); + } + gdio::putline (""); # End of result + + $sth->finish; +} + +############################################################ +### read inbox album directories +sub list_inbox_album_directories{ + +# This routine finds all directoies in ~music/inbox/albums that +# contain mp3 or ogg files. Each directory is considered as an +# album + + my $base = gdparams::gdbase(); + + my @filelist = `ls -R1 $base/inbox/albums`; + my ($curfile, $directory); + + $directory=""; + + foreach $curfile (@filelist){ + chop($curfile); + if ($curfile =~ m/\/inbox\/albums\/(.*):/ ){ + #print "$curfile is a directory\n"; + $directory = $1; + } + if(length($directory)>0 and + ( gdgentools::audio_filetype($curfile) eq "ogg" + or gdgentools::audio_filetype($curfile) eq "mp3" + or gdgentools::audio_filetype($curfile) eq "flac") + ){ + # we have a audio file in a valid directory (directory not yet printed) + print "$directory\n"; + gdio::putline "$directory"; + $directory = ""; # mark as printed + } + } + + gdio::putline (""); # End of result + +} + + +############################################################ +### Open/close the CD tray +sub open_cd_tray{ + system("eject"); +} +sub close_cd_tray{ + system("eject -t"); +} + +############################################################ +### Get rip state +sub get_rip_state{ # send CD rip status to client + my $base = gdparams::gdbase(); + # get first item of rip-list + my $trid = gdgentools::tracklist_get_item($dbh, $playerid, $rp_list, 0); + if($trid != 0){ + my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trid"); + my $rv = $sth->execute; + my ($wavf, $track, $wavfsize); + if($track = $sth->fetchrow_hashref){ + if($track->{mp3file} =~ /(.*)\.\w+/){ + $wavf = `ls $base/[0-9][0-9]/$1.wav`; + chop($wavf); + } + else{print("getripstate: can't find wavfile\n");} + truncstr($track->{artist}, 20); + truncstr($track->{title}, 20); + print ("$track->{tracknb}. $track->{artist} - $track->{title}\n"); + gdio::putline ("$track->{tracknb}. $track->{artist} - $track->{title}"); + + $wavfsize = (-s "$wavf"); + my $wavtotsize = $track->{length} * 44100 * 4; # totalSize = length(sec)*sampRate*bytesPerSample + $wavtotsize++; # prevent from divison by zero + #print("length: $track->{length}, fsize: $wavfsize, soll: $wavtotsize\n"); + my $percent = sprintf("%.2f%%", ($wavfsize * 100) / $wavtotsize); + print ("$percent percent\n"); + gdio::putline ("$percent"); + my $queuelen = gdgentools::tracklist_get_nb_items($dbh, $playerid, $rp_list); + $queuelen--; + print ("$queuelen tracks queued\n"); + gdio::putline ("$queuelen tracks queued"); + } + else{ + print ("ERROR: can't find track in database\n"); + gdio::putline ("ERROR: can't find track in database"); + } + $sth->finish; + } + gdio::putline (""); # End of result +} + + +############################################################ +### Get compress state +sub get_compress_state{ # send track compress status to client + my $base = gdparams::gdbase(); + # get first item of compression-list + my $trid = gdgentools::tracklist_get_item($dbh, $playerid, $co_list, 0); +#print("top of compression list: track $trid\n"); + if($trid != 0){ + my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trid"); + my $rv = $sth->execute; + my ($track, $audiofile, $currfilesize, $totalfilesize, $percent, $queuelen, $datarate); + my ($audiofmt, $audioparam); + if($track = $sth->fetchrow_hashref){ + $audiofile = `ls $base/[0-9][0-9]/$track->{mp3file}`; + chop($audiofile); + truncstr($track->{artist}, 20); + truncstr($track->{title}, 20); + print ("$track->{tracknb}. $track->{artist} - $track->{title}\n"); + gdio::putline ("$track->{tracknb}. $track->{artist} - $track->{title}"); + + $currfilesize = (-s "$audiofile"); + ($audiofmt, $datarate) = gdgentools::bitrate_str_to_format_param($track->{bitrate}); # split "mp3 128" + if ($audiofmt eq "mp3" || $audiofmt eq "ogg"){ + ### datarate is bitrate + $totalfilesize = ($track->{length} * $datarate * 1000) / 8; # totalSize = length*bitrate/BitsPerByte + } + if ($audiofmt eq "flac"){ + ### datarate is sampling rate + $totalfilesize = ($track->{length} * $datarate * 1010 * 2); # totalSize = length*bitrate*BytesPerByte + } + $totalfilesize++; # prevent from divison by zero + #print("file: $audiofile, length: $track->{length}, bitrate: $datarate, fsize: $currfilesize, soll: $totalfilesize\n"); + $percent = sprintf("%.2f%%", ($currfilesize * 100) / $totalfilesize); + print ("$percent\n"); + gdio::putline ("$percent"); + + $queuelen = gdgentools::tracklist_get_nb_items($dbh, $playerid, $co_list); + $queuelen--; + print ("$queuelen wav-files queued\n"); + gdio::putline ("$queuelen wav-files queued"); + } + $sth->finish; + } + gdio::putline (""); # End of result +} + + +############################################################ +### Misc commands ### +############################################################ + +############################################################ +### command burn audio CD +sub pl_burn_cd{ # Arguments in @_ are the + + my ($trackid, $sth); + my $mp3list; + my $row; + my $base = gdparams::gdbase(); + + $mp3list = ""; + + ### Write track tiles+artists to file (for booklet printing etc.) + open(RECLIST, ">$base/tmp/gdburnlist.txt"); + my $trkcnt = 1; + + foreach $trackid (@_){ + $sth = get_track($trackid); # perform database query + + $row = $sth->fetchrow_hashref; + $sth->finish; + print("add to CD $base/??/$row->{mp3file} ($row->{title}) \n"); + print(RECLIST "$trkcnt. $row->{artist} $row->{title} \n"); + $mp3list = $mp3list . " " . "$base/??/" . $row->{mp3file}; + $trkcnt ++; + } + close(RECLIST); + print("gdburn.sh " . $mp3list . "\n"); + system("gdburn.sh " . $mp3list . "&"); +} + + + +############################################################ +### Update browse directory (external script) +sub update_browse_directory{ + my $base = gdparams::gdbase(); + system("$base/bin/gdmakelinks.pl -s &"); # -s silent +} + +### Update browse directory (external script) +sub export_id3_tags{ + my $base = gdparams::gdbase(); +# system("cd $base; $base/bin/gdexportdb.pl --id3 &"); + system("cd $base; $base/bin/gdexportdb.pl --metatags &"); +} + +### Shut down entire server (external script) +sub shut_down_server{ + use vars qw($shutdowncmd); + print("Executing $gdparms::shutdowncmd \n"); + system($gdparms::shutdowncmd); +# system("/usr/sbin/usershutdown -h now &"); +} + + + +############################################################ +### General Query Command + +### General database query +sub general_db_query{ + my ($dbquery) = @_; + my ($sth, @row, $rowstr); + + ### Get # tracks + $sth = $dbh->prepare($dbquery); + $sth->execute; + if(@row = $sth->fetchrow_array){ + $rowstr = join "\t", @row; + print "$rowstr\n"; + gdio::putline ($rowstr); + } + gdio::putline (""); # End of result + $sth->finish; +} + +sub general_db_query_count{ # needed because older mysql-versions have a bug with COUNT(DISTINCT ... + my ($dbquery) = @_; + my ($sth, @row, $rowstr, $nbrec); + + ### Get # tracks +#print "Database Query(count): $dbquery\n"; + $sth = $dbh->prepare($dbquery); + $nbrec = $sth->execute; + print "$nbrec\n"; + gdio::putline ($nbrec); + gdio::putline (""); # End of result + $sth->finish; +} + + +############################################################ +### Soundcard/Volume Commands +sub set_volume{ + my ($vol) = @_; + gdsoundcard::sndc_set_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0), $vol); +} + +sub get_volume{ + my $vol = gdsoundcard::sndc_get_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); + + gdio::putline ($vol); + gdio::putline (""); # End of result +} + +sub save_volume{ + gdsoundcard::sndc_save_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); +} +sub inc_volume{ + gdsoundcard::sndc_inc_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); +} +sub dec_volume{ + gdsoundcard::sndc_dec_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); +} + +############################################################ +### General Shell Command + +### General shell command +sub general_sh_command{ + my ($shcommand) = @_; + system($shcommand); +} + +### General shell command and send back result +sub general_sh_command_res{ + my ($shcommand) = @_; + my ($res, $resline); + $res = `$shcommand`; + my @reslines = split /\n/, $res; + while ($resline = shift @reslines){ + print ("\"$resline\"\n"); + gdio::putline ($resline); + } + gdio::putline (""); # End of result +} + +sub general_sh_command_res_continuous{ +# continuously sends results, line by line + my ($shcommand) = @_; + my ($res, $resline); + open CMDRES, "$shcommand |"; + #autoflush CMDRES 1; + while($resline = ){ + chop($resline); + print ("\"$resline\"\n"); + gdio::putline ($resline); + } + gdio::putline (""); # End of result +} + +### General shell command and send back result +sub gd_basedir{ + my $base = gdparams::gdbase(); + print ("$base\n"); + gdio::putline ($base); + gdio::putline (""); # End of result +} + + + +############################################################ +### Database and Disc statistics +sub full_statistics{ + my $base = gdparams::gdbase(); + my ($sth, $row, $msg); + + gdio::putline ("DB statistics"); + ### Get # tracks + $sth = $dbh->prepare("SELECT COUNT(*) AS cnt FROM tracks"); + $sth->execute; + if($row = $sth->fetchrow_hashref){ + $msg = " ".$row->{cnt}." Tracks"; + print "$msg \n"; + gdio::putline ($msg); + } + $sth->finish; + + ### Get # albums + $sth = $dbh->prepare("SELECT COUNT(*) AS cnt FROM album"); + $sth->execute; + if($row = $sth->fetchrow_hashref){ + $msg = " ".$row->{cnt}." Albums"; + print "$msg \n"; + gdio::putline ($msg); + } + $sth->finish; + + gdio::putline (" "); + gdio::putline ("Disc statistics"); + gdio::putline (" (dir: used / free)"); + + ### Get mp3 directories and check each directory + my @mdir = gdparams::mp3dirs(); + my $i=0; + my (@dfres, $totused, $totfree); + + $totused=0; $totfree=0; + while($i < @mdir){ + if (-d "$base/$mdir[$i]"){ + @dfres = split / +/, `df -m $base/$mdir[$i]|tail -1`; + $msg = " ".$mdir[$i].": ".$dfres[2]."M / ".$dfres[3]."M"; + print "$msg \n"; + gdio::putline ($msg); + $totused += $dfres[2]; + $totfree += $dfres[3]; + } + else{print "$base/$mdir[$i] is not a directory or does not exist\n";} + $i++; + } + + $msg = " tot: ".$totused."M / ".$totfree."M"; + print "$msg \n"; + gdio::putline ($msg); + + gdio::putline (""); # End of result + + ### "Side Effect": print player-id and -type + print "\nStatus: playerid=$playerid, audiochannel=$audchannel0\n"; + +} + + +############################################################ + +sub server_alive_test{ # Ping + print ("Ping: GiantDisc Server alive\n"); + gdio::putline ("GiantDisc Server alive"); + gdio::putline (""); # End of result +} + + +sub serial_test{ + my $i; + for($i=0; $i<5; $i++){ + print("$i "); + gdio::putline(" 123456789 123456789 123456789 123456789 123456789 123456789 123456789"); + } + gdio::putline (""); # End of result + print("\n"); +} + + +END{ + ### close database connection + print("### close database connection\n"); + $dbh->disconnect; + + if($rippipe_open){close_rippipe();} +} +# +1; +# diff --git a/scripts/gdsoundcard.pm b/scripts/gdsoundcard.pm new file mode 100755 index 0000000..519181f --- /dev/null +++ b/scripts/gdsoundcard.pm @@ -0,0 +1,149 @@ +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2003, Rolf Brugger +# +################################################## + +package gdsoundcard; + +# +# soundcard drivers to control volume etc +# +# +# + +#use lib '/usr/local/bin'; +use strict; + +############################################################ +### Constants & global variables + + + +############################################################ + +sub sndc_init{ +### initialize + +# actually not used yet + + my ($playertype, $playerhost, $sounddevice) = @_; + + if ($playertype == 0){ # local oss soundcard + ; + } + + elsif ($playertype == 20){ # exstreamer + ; + } + else{ + print "Warning: unknown player type $playertype\n"; + } +} + + + +############################################################ +### Volume routines +# 0 <= volume <= 100 + + +sub sndc_set_volume{ +### Set volume + + my ($playertype, $playerhost, $sounddevice, $volume) = @_; + + if ($playertype == 0){ # local oss soundcard + system "aumix -v$volume"; + } + + elsif ($playertype == 20){ # exstreamer + use integer; + my $cmd = "v=".($volume/5)."\n"; + gdgentools::exstreamer_command($playerhost, $cmd); + } + else{ + print "Warning: unknown player type $playertype\n"; + } +} + + +sub sndc_get_volume{ +### Get volume +# returns the currently set volume. + + my ($playertype, $playerhost, $sounddevice) = @_; + my $volume = 0; + + if ($playertype == 0){ # local oss soundcard + my ($shcommand) = @_; + my ($res, $resline, @reslines); + $res = `aumix -vq`; + @reslines = split /\n/, $res; + $resline = shift (@reslines); + if ($resline =~ m/\D*(\d+).*/){ + $volume = $1; + } + else{print "Warning: Get volume - can't match aumix output\n";} + } + + elsif ($playertype == 20){ # exstreamer + my $cmd = "v=\n"; # cmd get volume + my $res = gdgentools::exstreamer_command_res($playerhost, $cmd); + if ($res =~ m/\<.*\>(\d+)\<.*\>/){ + $volume = ($1)*5; + } + else{ + $volume = 50; + } + } + else{ + print "Warning: unknown player type $playertype\n"; + } + return $volume; +} + + +sub sndc_save_volume{ +### Save default volume + + my ($playertype, $playerhost, $sounddevice, $volume) = @_; + + if ($playertype == 0){ # local oss soundcard + system "aumix -S"; + } + elsif ($playertype == 20){ # exstreamer + # the exstreamer always saves the volume setting in its flash rom + ; + } + else{ + print "Warning: unknown player type $playertype\n"; + } +} + +############################################################ + +sub sndc_inc_volume{ +### Increases volume by 5% + my ($playertype, $playerhost, $sounddevice) = @_; + my $volume = sndc_get_volume($playertype, $playerhost, $sounddevice); + $volume += 5; + if ($volume>100){$volume=100;} + sndc_set_volume($playertype, $playerhost, $sounddevice, $volume); +} + +sub sndc_dec_volume{ +### decreases volume by 5% + my ($playertype, $playerhost, $sounddevice) = @_; + my $volume = sndc_get_volume($playertype, $playerhost, $sounddevice); + $volume -= 5; + if ($volume<0){$volume=0;} + sndc_set_volume($playertype, $playerhost, $sounddevice, $volume); +} + +############################################################ + +1; +# diff --git a/scripts/gdupdate.pm b/scripts/gdupdate.pm new file mode 100755 index 0000000..8242c6c --- /dev/null +++ b/scripts/gdupdate.pm @@ -0,0 +1,509 @@ +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2000-2003, Rolf Brugger +# +################################################## + + +# Package for database modifications and consistency checks related +# to version updates + + +package gdupdate; + +use strict; + + +BEGIN{ +} + + +############################################################################### +### Version 1.32 +############################################################################### + +sub db_check_update_132{ + my ($dbh) = @_; + my ($sth, $count, $res); + + my $update = 0; + + ### new field 'audio channel' + $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'audiochannel'"); + if ($res < 1){ + $update = 1; + } + + $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'processid'"); + if ($res < 1){ + $update = 1; + } + + + return $update; +} + + +sub db_update_132{ + my ($dbh) = @_; + my ($sth, $count, $res); + + ### usage frequencies + $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'audiochannel'"); + if ($res < 1){ + print("Update table playerstate to version 1.32\n"); + print("rename index column playertype into audiochannel\n"); + $dbh->do("ALTER TABLE playerstate CHANGE playertype audiochannel INT NOT NULL"); + print("Adding field playertype to table playerstate\n"); + $dbh->do("ALTER TABLE playerstate ADD COLUMN playertype INT AFTER audiochannel"); + } + + ### player process id + $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'processid'"); + if ($res < 1){ + print("Update table playerstate to version 1.12\n"); + print("Adding field processid to table playerstate\n"); + $dbh->do("ALTER TABLE playerstate ADD COLUMN processid INT AFTER audiochannel"); + } + +} + + +############################################################################### +### Version 1.31 +############################################################################### + +sub db_check_update_131{ + my ($dbh) = @_; + my ($sth, $count, $res); + + my $update = 0; + + ### usage frequencies + $res = $dbh->do("SHOW COLUMNS FROM album LIKE 'genre'"); + if ($res < 1){ + $update = 1; + } + + ### Optimizations + my $row; + $sth = $dbh->prepare("SHOW TABLE STATUS FROM GiantDisc LIKE 'playerstate'"); + $count = $sth->execute; + if($row = $sth->fetchrow_hashref){ + if ($row->{Type} ne "HEAP"){ + $update = 1; + } + } + $sth->finish; + + + my ($dbh, $table, $column, $indexspec) = @_; + + my ($sth, $count, $row); + $sth = $dbh->prepare("SHOW index FROM tracks"); + $count = $sth->execute; + while($row = $sth->fetchrow_hashref){ + last if ($row->{Key_name} eq "artist"); + } + if($row->{Key_name} eq "artist"){ + #print "index artist exists\n"; + ; + } + else{ + print "Alert: no additional indexes defined\n"; + print " consider upgrading the db-structure with 'gdupdatedb.pl'\n"; + } + $sth->finish; + + + return $update; +} + + +sub db_update_131{ + my ($dbh) = @_; + my ($sth, $count, $res); + + ### usage frequencies + $res = $dbh->do("SHOW COLUMNS FROM album LIKE 'genre'"); + if ($res < 1){ + print("Update table album to version 1.31\n"); + print("Adding field genre to table album\n"); + $dbh->do("ALTER TABLE album ADD COLUMN genre VARCHAR(10) AFTER modified"); + } + + ### Optimizations + my $row; + $sth = $dbh->prepare("SHOW TABLE STATUS FROM GiantDisc LIKE 'playerstate'"); + $count = $sth->execute; + if($row = $sth->fetchrow_hashref){ + if ($row->{Type} ne "HEAP"){ + print "Set table 'playerstate' to type HEAP\n"; + $dbh->do("ALTER TABLE playerstate TYPE=HEAP"); + } + } + $sth->finish; + + + test_and_add_index($dbh, "tracks", "artist", "artist(artist(10))"); + test_and_add_index($dbh, "tracks", "title", "title(title(10))"); + test_and_add_index($dbh, "tracks", "genre1", "(genre1)"); + test_and_add_index($dbh, "tracks", "genre2", "(genre2)"); + test_and_add_index($dbh, "tracks", "year", "(year)"); + test_and_add_index($dbh, "tracks", "lang", "(lang)"); + test_and_add_index($dbh, "tracks", "type", "(type)"); + test_and_add_index($dbh, "tracks", "rating", "(rating)"); + test_and_add_index($dbh, "tracks", "sourceid","(sourceid)"); + test_and_add_index($dbh, "tracks", "mp3file", "mp3file(mp3file(10))"); + + test_and_add_index($dbh, "album", "artist", "artist(artist(10))"); + test_and_add_index($dbh, "album", "title", "title(title(10))"); + test_and_add_index($dbh, "album", "genre", "(genre)"); + test_and_add_index($dbh, "album", "modified", "(modified)"); + +} + +sub test_and_add_index +{ + my ($dbh, $table, $column, $indexspec) = @_; + + my ($sth, $count, $row); + $sth = $dbh->prepare("SHOW index FROM $table"); + $count = $sth->execute; + while($row = $sth->fetchrow_hashref){ + last if ($row->{Key_name} eq $column); + } + if($row->{Key_name} eq $column){ + #print "index $column exists\n"; + ; + } + else{ + print "creating index $column\n"; + $dbh->do("ALTER TABLE $table ADD INDEX $indexspec"); + } + $sth->finish; +} + + +############################################################################### +### Version 1.14 +############################################################################### + +sub db_update_114{ + my ($dbh) = @_; + my ($sth, $count, $res); + + ### bitrate + $sth = $dbh->prepare("SELECT id FROM tracks WHERE length(bitrate)<4"); + $count = $sth->execute; + if ($count > 0){ + print("Update table tracks to version 1.14\n"); + print("enlarging field bitrate to 10 characters\n"); + $dbh->do("ALTER TABLE tracks MODIFY COLUMN bitrate VARCHAR(10)"); + + ### add prefix "mp3 " to all bitrate fields of mp3 tracks + $res=print("add prefix \"mp3 \" to all bitrate fields of mp3 tracks\n"); + $dbh->do("UPDATE tracks SET bitrate=CONCAT('mp3 ',bitrate) " + ."WHERE length(bitrate)<4 AND ( mp3file LIKE '%.mp3'" + ." OR mp3file LIKE 'http%')"); + print "mp3 track records updated\n"; + $dbh->do("UPDATE tracks SET bitrate=CONCAT('ogg ',bitrate) " + ."WHERE length(bitrate)<4 AND mp3file LIKE '%.ogg'"); + + ### check result + $sth = $dbh->prepare("SELECT id FROM tracks WHERE length(bitrate)<4"); + $count = $sth->execute; + if ($count > 0){ + print "\n"; + print "Warning: some track records could not be properly translated.\n"; + print " You might have tracks in your database with no audio-\n"; + print " file associated!\n\n"; + } + } + $sth->finish; +} + + + +############################################################################### +### Version 1.12 +############################################################################### + +sub db_update_112{ + my ($dbh) = @_; + my $res; + + ### add player process id -> killing pid with killall and killfam is unstable and too slow! + #$res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'processid'"); + #if ($res < 1){ + # print("Update table playerstate to version 1.12\n"); + # print("Adding field processid to table playerstate\n"); + # $dbh->do("ALTER TABLE playerstate ADD COLUMN processid INT AFTER snddevice"); + #} + + ### usage frequencies + $res = $dbh->do("SHOW COLUMNS FROM language LIKE 'freq'"); + if ($res < 1){ + print("Update table language to version 1.12\n"); + print("Adding field freq to table language\n"); + $dbh->do("ALTER TABLE language ADD COLUMN freq INT AFTER language"); + } + $res = $dbh->do("SHOW COLUMNS FROM genre LIKE 'freq'"); + if ($res < 1){ + print("Update table genre to version 1.12\n"); + print("Adding field freq to table genre\n"); + $dbh->do("ALTER TABLE genre ADD COLUMN freq INT AFTER genre"); + } +#else {print("-- column modified exists -> DB needs not be updated\n");} +} + + + +############################################################################### +### Version 1.11 +############################################################################### + +sub db_update_111{ + my ($dbh) = @_; + my $res; + + ### Table recordingitem + $res = $dbh->do("SHOW TABLES LIKE 'recordingitem'"); + if ($res < 1){ + print("recordingitem does not exist (upgrading...)\n"); + $dbh->do( + "create table recordingitem(" + ."trackid int," + ."recdate date," + ."rectime time," + ."reclength int," + ."enddate date," + ."endtime time," + ."repeat varchar(10)," + ."initcmd varchar(255)," + ."parameters varchar(255)," + ."atqjob int," + ."id int not null," + ."primary key(id)" + .")"); + } +#else{print("-- recordingitem does exist\n");} +} + + + +############################################################################### +### Version 0.97 +############################################################################### + +sub db_update_097{ + my ($dbh) = @_; + my $res; + + ### shuffle parameter + $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'shufflepar'"); + if ($res < 1){ + print("Update table album to version 0.97\n"); + print("Adding modified field shufflepar,shufflestat to table playerstate\n"); + $dbh->do("ALTER TABLE playerstate ADD COLUMN shufflepar varchar(255) AFTER state"); + $dbh->do("ALTER TABLE playerstate ADD COLUMN shufflestat varchar(255) AFTER shufflepar"); + } +#else {print("-- column modified exists -> DB needs not be updated\n");} +} + + + +############################################################################### +### Version 0.96 +############################################################################### + +sub db_update_096{ + my ($dbh) = @_; + my $res; + + ### album modification time + $res = $dbh->do("SHOW COLUMNS FROM album LIKE 'modified'"); + if ($res < 1){ + print("Update table album to version 0.96\n"); + print("Adding modified field to table album\n"); + $dbh->do("ALTER TABLE album ADD COLUMN modified date AFTER covertxt"); + } +#else {print("-- column modified exists -> DB needs not be updated\n");} +} + + +############################################################################### +### Version 0.95 +############################################################################### + +sub db_update_095{ + my ($dbh) = @_; + my $res; + + ### anchortime + $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'anchortime'"); + if ($res < 1){ + print("Update table playerstate to version 0.95\n"); + print("Adding anchortime field to table playerstate\n"); + $dbh->do("ALTER TABLE playerstate ADD COLUMN anchortime bigint AFTER framesremain"); + } +#else {print("-- column anchortime exists -> DB needs not be updated\n");} + + ### framestotal + $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'framestotal'"); + if ($res < 1){ + print("Update table playerstate to version 0.95\n"); + print("Renaming framesremain field to framestotal\n"); + $dbh->do("ALTER TABLE playerstate CHANGE framesremain framestotal INT"); + } +#else {print("-- column framestotal exists -> DB needs not be updated\n");} +} + + +############################################################################### +### Version 0.94 +############################################################################### + +sub check_new_mp3info080{ +# This routine checks if the version of mp3info is at least 0.8.0, which +# is required as of GD-version 0.94 +# A warning message is printed if mp3info should be updated + + my $infostr = `mp3info`; + if ($infostr =~ /MP3Info\D*([0-9]*).([0-9]*).([0-9]*)/){ + if ($2 < 8){ + print ("\n\n"); + print ("Warning: The Version of 'mp3info' on your system is $1.$2.$3\n"); + print (" At least version 0.8.0 is required. You can get it\n"); + print (" from http://www.ibiblio.org/mp3info\n"); + print ("\n\n"); + exit(0); + } + } + else{ + print ("Warning: could not extract version number of 'mp3info'\n"); + print (" 'mp3info' is not installed?\n"); + exit(0); + } +} + + + +sub db_update_094{ + my ($dbh) = @_; + my $res; + + ### Bitrate + $res = $dbh->do("SHOW COLUMNS FROM tracks LIKE 'bitrate'"); + if ($res < 1){ + print("Update table tracks to version 0.94\n"); + print("Adding bitrate field to table tracks\n"); + $dbh->do("ALTER TABLE tracks ADD COLUMN bitrate CHAR(4) AFTER lyrics"); + + ### Update records + my $base = gdparams::gdbase(); + my ($sth, $count, $row, $fname, $bitrate); + $sth = $dbh->prepare("SELECT * FROM tracks WHERE bitrate IS NULL OR bitrate=''"); + $count = $sth->execute; + print("I have to update the bitrate of $count records\n"); + while($row = $sth->fetchrow_hashref){ + $fname = `ls $base/[0-9][0-9]/$row->{mp3file}`; # get full path + chop($fname); + $bitrate = gdgentools::get_bitrate_str($fname); + print("Set bitrate of $row->{artist}/$row->{title} to $bitrate\n"); + $dbh->do( "UPDATE tracks SET bitrate='$bitrate' WHERE id=$row->{id}"); + + } + $sth->finish; + } +#else {print("-- column bitrate exists -> DB needs not be updated\n");} + + + ### Table player + $res = $dbh->do("SHOW TABLES LIKE 'player'"); + if ($res < 1){ + print("player does not exist (upgrading...)\n"); + $dbh->do( + "create table player( " + ."ipaddr varchar(255) not null," + ."uichannel varchar(255) not null," + ."logtarget int," + ."cdripper varchar(255)," + ."mp3encoder varchar(255)," + ."cdromdev varchar(255)," + ."cdrwdev varchar(255)," + ."id int not null," + ."primary key(id)" + .")"); + } +#else{print("-- player does exist\n");} + + + ### Table playerstate + $res = $dbh->do("SHOW TABLES LIKE 'playerstate'"); + if ($res < 1){ + print("playerstate does not exist (upgrading...)\n"); + $dbh->do( + "create table playerstate(" + ."playerid int not null," + ."playertype int not null," + ."snddevice varchar(255)," + ."playerapp varchar(255)," + ."playerparams varchar(255)," + ."ptlogger varchar(255)," + ."currtracknb int," + ."state varchar(4)," + ."pauseframe int, " + ."framesplayed int," + ."framesremain int," + ."primary key(playerid, playertype)" + .")"); + } +#else{print("-- playerstate does exist\n");} + + + ### Table tracklistitem + $res = $dbh->do("SHOW TABLES LIKE 'tracklistitem'"); + if ($res < 1){ + print("tracklistitem does not exist (upgrading...)\n"); + $dbh->do( + "create table tracklistitem(" + ."playerid int not null," + ."listtype smallint not null," + ."tracknb int not null," + ."trackid int not null," + ."primary key(playerid, listtype, tracknb)" + .")"); + } +#else{print("-- tracklistitem does exist\n");} + +} + +############################################################################### +### Version 0.92 +############################################################################### + +sub fix_leading_slash_bug +### removes leading / in the column tracks.mp3file +{ + my ($dbh) = @_; + my $numrec = $dbh->do( "UPDATE tracks SET mp3file=SUBSTRING(mp3file,2) " + ."WHERE mp3file LIKE '/%'"); + if ($numrec>0){ + print("fix_leading_slash_bug: $numrec records fixed!\n\n"); + } +} + + + +############################################################################### + +END{ + ; +} + + +# +1; diff --git a/scripts/genres.txt b/scripts/genres.txt new file mode 100755 index 0000000..2a734f0 --- /dev/null +++ b/scripts/genres.txt @@ -0,0 +1,224 @@ +b 20 Alternative +ba 40 Alternative General +bb \N Art Rock +bc 90 Avant Rock +be \N Experimental +bh 6 Grunge +bi \N Indie +bm 12 Unclassifiable +bn \N Crossover +c \N Books & Spoken +ca \N Short Stories +cb 57 Comedy +cc 77 Musicals/Broadway +cd \N Poetry +ce \N Cabaret / Satire +cf \N Religion +cg 101 Spoken Word +ch \N Stories/Fairytales +ci \N Radio Play +cia \N Literary Radio Play +cib \N Thriller +e 32 Classical +ea 104 Chamber Music +eaa 105 Sonata +eb \N Classical General +ec \N Contemporary +eca \N Contemp. Crossover +ecb \N Electronic Classical +ecc \N Experimental Classical +ecd \N Minimal Music +ed \N Film Music +ee 33 Instrumental +ef \N Period Music +efa \N Baroque +efb \N Medieval +efc \N Renaissance +efd \N Romantic +efda \N 19th Century +eg \N Solo Instruments +ega \N Guitar +egb \N Percussion +egc \N Piano +eh 106 Symphonic +ei 28 Classic Vocal +eia 97 Choral +eib \N Ensembles +eic 103 Opera +ej \N Baroque +f 2 Country +fa \N Alternative Country +fb 89 Bluegrass +fd \N Country Blues +fe \N Country General +fg \N Country and Western +fh 80 Folk +fha \N Irish Folk +fi \N Rockabilly +g 98 Easy Listening +gb \N Lounge +gc \N Love Songs +gca 116 Ballads +gd \N Mood Music +ge 10 New Age +gf \N Soft Rock +gfa \N Acoustic Rock +gg \N Schlager +gh \N Soft Pop +gi 45 Meditative +gj \N Pair Dance +gja \N Walz +gjb \N Tango +h 102 Songs/Chansons +ha \N Singer-Songwriter +hb 65 Children's Music +i 52 Electronic +ia 34 Acid +ib 26 Ambient +ic \N Breakbeat/Breaks +ica \N Breakbeat +icb \N Darkside +icc 63 Jungle +icd \N Ragga +ice 27 Trip Hop +id 3 Dance +ie \N Drum n' Bass +if \N Electronica +ig \N Envir. Soundscapes +ih \N Experimental Elect. +iha \N Minimal Experimental +ihb 39 Noise +ii 37 Game Soundtracks +ij 35 House +ija \N Acid House +ijb \N Funk House +ijc \N Hard House +ijd \N Progressive House +ik 19 Industrial +il 18 Techno +ilb \N Dub +ild \N Goa +ile \N Hardcore Techno +ilf \N Illbient +ilg \N Minimal +ilh 25 Old Skool Techno +ili 68 Rave +ilj 31 Trance +im 44 Space Music +j \N Hip Hop/Rap +ja 7 Hip Hop +jb 15 Rap +jbb 61 Christian Rap +jbd \N Hardcore Rap +jbf 59 Gangsta +k \N Blues/R&B +ka 0 Blues +kaa \N Acoustic Blues +kab \N Blues Rock +kac \N Blues Vocalist +kae \N Electric Blues +kag \N Jazz Blues +kb 38 Gospel +kc \N Improvised +kd 14 R&B +ke 42 Soul +kea \N Sweet Soul +l 8 Jazz +la 73 Acid Jazz +lb 85 Bebop +lc \N Dancefloor Jazz +lf 30 Jazz Fusion +lh \N Jazz Vocals +lj \N Ragtime +lk \N Smooth Jazz +ll 83 Swing +lla 96 Big Band +llb 76 Retro-Swing +lm \N Cool Jazz +ln \N Ethno Jazz +lna \N African Ethno Jazz +lnb \N Arab Ethno Jazz +lnc \N Cuban Jazz +lnd \N Latin Jazz +lne \N Far East Jazz +lo \N Modern Jazz +lp \N New Orleans Brass +m \N Pop & Rock +ma \N Country Rock +mb 5 Funk +mba \N Acid Funk +mc 9 Metal +mcc 22 Death Metal +mcd \N Doom Metal +mcf \N Hard Core Metal +mcg \N Heavy Metal +mck \N Thrash/Speed Metal +md 13 Pop +mda 99 Acoustic +mdb \N Synthesizer Pop +mdd \N Latin Pop +mdg 123 A capella/Pop Vocals +mdh \N Neue Deutsche Welle +mdi \N Disco +mdj \N Dance Pop +mdja \N Twist +mdk \N Doo-Wop +me 43 Punk +mea 121 Hardcore/Punk Rock +meb 71 Lo-Fi/Garage +mec \N Old School Punk +med 21 Ska +mf 17 Rock +mfb \N Acid Rock +mfe 81 Folk Rock +mfg \N Groove Rock +mfh \N Guitar Rock +mfha 1 Classic Rock +mfhb \N Improv Rock +mfhc 47 Instrumental Rock +mfhf \N Surf Rock +mfj 66 New Wave +mfk 67 Psychedelic +mfl 78 Rock & Roll +mfm 94 Symphonic Rock +mg \N Rock En Espanol +n \N World +na 16 Reggae +nb \N Steel Drums +nc \N World Popular +nca \N African Pop +ncb \N Oriental Pop +ncc \N Scandinavian Ethnopop +ncd \N Asian Pop +nce \N Arabian Pop +ncf \N European Ethnopop +ncg \N Latin Pop +nch \N Caribbean Pop +nd 82 World Traditions +nda \N African +ndaa \N Mali Blues +ndb \N Arabic +ndc \N Asian +ndd \N Bossa Nova +nde \N Caribbean +ndf 88 Celtic +ndg 53 European Folk/Pop +ndga \N Jodel +ndh \N France +ndi \N Germany +ndj \N India +ndk \N Ireland +ndl 86 Latin +ndlb \N Flamenco +ndlc \N Mambo +ndld \N Mariachi +ndle \N Meringue +ndlg \N Salsa +ndlh 114 Samba +ndm 64 Native American +ndn \N Quebecois +ndo \N Russian +ndp \N South/Cent. American +ndq \N Spain +ndr 113 Tango + NULl diff --git a/scripts/languages.txt b/scripts/languages.txt new file mode 100755 index 0000000..68dfdec --- /dev/null +++ b/scripts/languages.txt @@ -0,0 +1,45 @@ +- Instrumental +CHde Swiss German +af Afrikaans +ar Arabic +bg Bulgarian +bn Bengali; Bangla +bo Tibetan +cs Czech +da Danish +de German +el Greek +en English +eo Esperanto +es Spanish +fi Finnish +fr French +hi Hindi +hu Hungarian +is Icelandic +it Italian +iw Hebrew +ja Japanese +ku Kurdish +la Latin +lt Lithuanian +lv Latvian, Lettish +nl Dutch +no Norwegian +pl Polish +pt Portuguese +rm Rhaeto-Romance +ro Romanian +ru Russian +sh Serbo-Croatian +sk Slovak +sl Slovenian +sq Albanian +sr Serbian +sv Swedish +ta Tamil +th Thai +tr Turkish +vi Vietnamese +zh Chinese +zu African diff --git a/scripts/make-db b/scripts/make-db new file mode 100755 index 0000000..ec25531 --- /dev/null +++ b/scripts/make-db @@ -0,0 +1,8 @@ +#!/bin/sh + +export SCRIPTDIR=/home/andi/muggle/import +cd $SCRIPTDIR +echo "creating db" +mysql < createdb.mysql + +# diff --git a/scripts/make-empty-db b/scripts/make-empty-db new file mode 100755 index 0000000..7157539 --- /dev/null +++ b/scripts/make-empty-db @@ -0,0 +1,24 @@ +#!/bin/sh + +export SCRIPTDIR=/home/andi/muggle/import +cd $SCRIPTDIR + +echo "creating db" +mysql < createdb.mysql + +echo "creating tables" +mysql < $SCRIPTDIR/createtables.mysql + +echo "reading genres" +echo " use GiantDisc; load data local infile '$SCRIPTDIR/genres.txt' into table genre;"| mysql --local-infile=1 + + +echo "reading languages" +echo "use GiantDisc; load data local infile '$SCRIPTDIR/languages.txt' into table language;" | mysql --local-infile=1 + +echo "reading musictypes" +echo "use GiantDisc; load data local infile '$SCRIPTDIR/musictypes.txt' into table language;" | mysql --local-infile=1 + + +echo "reading sources" +echo "use GiantDisc; load data local infile '$SCRIPTDIR/sources.txt' into table language;" | mysql --local-infile=1 diff --git a/scripts/make-tables b/scripts/make-tables new file mode 100755 index 0000000..1dc9c01 --- /dev/null +++ b/scripts/make-tables @@ -0,0 +1,26 @@ +#!/bin/sh + +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2000-2002, Rolf Brugger +# +################################################## + +cd /home/andi/muggle/import + +echo "creating tables" +mysql < createtables.mysql + +echo "reading genres" +mysql --local-infile=1 < readgenres.mysql + +echo "reading languages" +mysql --local-infile=1 < readlanguages.mysql + +echo "reading musictypes" +mysql --local-infile=1 < readmusictypes.mysql + +echo "reading sources" +mysql --local-infile=1 < readsources.mysql diff --git a/scripts/musictypes.txt b/scripts/musictypes.txt new file mode 100755 index 0000000..50ca2c3 --- /dev/null +++ b/scripts/musictypes.txt @@ -0,0 +1,4 @@ +soft/slow +medium +groovy +hard diff --git a/scripts/myhash.pm b/scripts/myhash.pm new file mode 100755 index 0000000..5d8f1bd --- /dev/null +++ b/scripts/myhash.pm @@ -0,0 +1,29 @@ +package myhash; + + +################################################## +# +# GiantDisc mp3 Jukebox +# +# © 2000, Rolf Brugger +# +################################################## + +#use lib '/usr/local/bin'; +#BEGIN{;} +#END{;} + + +############################################################ +### +sub addvaltohash{ # gets a current hashval and a new elment + # returns new hashval + my ($hashval,$newelement) = @_; + + return (($hashval << 5) ^ ($hashval >> 27)) ^ $newelement; + # (^ is bitwise EXOR) +} + + +1; +# diff --git a/scripts/sources.txt b/scripts/sources.txt new file mode 100755 index 0000000..b900632 --- /dev/null +++ b/scripts/sources.txt @@ -0,0 +1,6 @@ +cd +radio +vinyl +tape +tv +video -- cgit v1.2.3 From b75e54f2120bd2def70ba18ee4b79f147dcda4dc Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 2 Jun 2004 19:29:22 +0000 Subject: Use asprintf to create messages git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@100 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index dcc442f..4545deb 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.20 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \version $Revision: 1.21 $ + * \date $Date: 2004/06/02 19:29:22 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * - * $Id: vdr_menu.c,v 1.20 2004/05/28 15:29:19 lvw Exp $ + * $Id: vdr_menu.c,v 1.21 2004/06/02 19:29:22 lvw Exp $ */ #include @@ -155,11 +155,13 @@ eOSState mgMainMenu::ProcessKey(eKeys key) if( tracks ) { - char buffer[256]; - sprintf( buffer, "%d tracks sent to current playlist", (int) tracks->size() ); + char *buffer = 0; + asprintf( &buffer, "%d tracks sent to current playlist", (int) tracks->size() ); m_current_playlist->appendList(tracks); Interface->Status( buffer ); Interface->Flush(); + + free( buffer ); } else { @@ -734,6 +736,9 @@ void mgMainMenu::Play(mgPlaylist *plist) /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.21 2004/06/02 19:29:22 lvw + * Use asprintf to create messages + * * Revision 1.20 2004/05/28 15:29:19 lvw * Merged player branch back on HEAD branch. * -- cgit v1.2.3 From 3a022772f759a5db8100bb04393c6aef5c1579bb Mon Sep 17 00:00:00 2001 From: MountainMan Date: Tue, 6 Jul 2004 00:20:51 +0000 Subject: loading and saving playlists git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@101 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 118 +++++++++++++++++++++++++------------------------ gd_content_interface.h | 18 ++++---- mg_playlist.h | 9 ++-- vdr_menu.c | 103 ++++++++++++++++++++++++++++++++++++++---- vdr_menu.h | 19 +++++--- 5 files changed, 184 insertions(+), 83 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 9c339d6..874159e 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,10 +1,10 @@ /*! \file content_interface.cpp * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugindatabase * - * \version $Revision: 1.22 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.23 $ + * \date $Date: 2004/07/06 00:20:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: MountainMan $ * * Implements main classes of for content items and interfaces to SQL databases * @@ -709,6 +709,19 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) ""); // creates current time as timestamp m_author = "VDR"; m_listname = listname; + + // now read thenew list to get the id + result=mgSqlReadQuery(&m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + listname.c_str()); + nrows = mysql_num_rows(result); + row = mysql_fetch_row(result); + + if(sscanf(row [0], "%d", & m_sqlId) !=1) + { + mgError("Invalid id '%s' in database", row [5]); + } + } else // playlist exists, read data { @@ -728,51 +741,6 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists } -/*! - ***************************************************************************** - * \brief Constructor: construct playlist object from existing sql playlist - * - * \param sql_identifier: sql internal identifier for the playlist - * \param db_handl database which stores the playlist - * - * This constructor is typically used when a playlist is selected from an - * internal list of playlists - ****************************************************************************/ -GdPlaylist::GdPlaylist(unsigned int sql_identifier, MYSQL db_handle) -{ - MYSQL_RES *result; - int nrows; - - m_db = db_handle; - - // check, if the database really exists - result = mgSqlReadQuery(&m_db, - "SELECT title,author FROM playlist where id=%d", - sql_identifier); - nrows = mysql_num_rows(result); - if(nrows == 0) - { - mgDebug(3, "No playlist with id %d found. Creating new playlist\n", - sql_identifier); - - // create new database entry - // DUMMY - } - else // playlist exists, read data - { - MYSQL_ROW row; - row = mysql_fetch_row(result); - - m_listname = row[0]; - m_author = row[1]; - m_sqlId = sql_identifier; - // now read all entries of the playlist and - // write them into the tracklist - insertDataFromSQL(); - } - m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists -} - /*! ***************************************************************************** * \brief empty destructor @@ -783,7 +751,11 @@ GdPlaylist::~GdPlaylist() { } - +void GdPlaylist::setListname(std::string name) +{ + m_listname = name; + m_sqlId = -1; +} /*! ***************************************************************************** * \brief reads the track list from the sql database into a locallist @@ -820,26 +792,55 @@ bool GdPlaylist::storePlaylist() { vector::iterator iter; int num; + MYSQL_RES *result; + MYSQL_ROW row; + int nrows; - if(m_listname =="") + if(m_listname ==" ") { mgWarning("Can not store Tracklist without name"); return false; } - // remove old playlist items from db - mgSqlWriteQuery(&m_db, - "DELETE FROM playlistitem WHERE playlist = %d", - m_sqlId); - + if(m_sqlId >= 0) + { + // playlist alreay exists in SQL database + // remove old items first + cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; + // remove old playlist items from db + mgSqlWriteQuery(&m_db, + "DELETE FROM playlistitem WHERE playlist = %d", + m_sqlId); + } + else + { + // create new database entry + mgSqlWriteQuery(&m_db, "INSERT into playlist " + "SET title=\"%s\", author=\"%s\"", + m_listname.c_str(), + "VDR", // default author + ""); // creates current time as timestamp + m_author = "VDR"; + + // now read thenew list to get the id + result=mgSqlReadQuery(&m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + m_listname.c_str()); + nrows = mysql_num_rows(result); + row = mysql_fetch_row(result); + + if(sscanf(row [0], "%d", & m_sqlId) !=1) + { + mgError("Invalid id '%s' in database", row [5]); + } + } // add new playlist items to db for(iter= m_list.begin(), num=0; iter != m_list.end(); iter++, num++) { - mgSqlWriteQuery(&m_db, "INSERT into playlistitem " - "SET tracknumber=\"%s\", trackid=\"%s\", playlist=%d", + "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", num, (*iter)->getId(), m_sqlId); } return true; @@ -1391,6 +1392,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.23 2004/07/06 00:20:51 MountainMan + * loading and saving playlists + * * Revision 1.22 2004/05/28 15:29:18 lvw * Merged player branch back on HEAD branch. * diff --git a/gd_content_interface.h b/gd_content_interface.h index 8e95267..491e4b9 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -3,10 +3,10 @@ * \brief Data objects for content (e.g. mp3 files, movies) * for the vdr muggle plugin database * - * \version $Revision: 1.7 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.8 $ + * \date $Date: 2004/07/06 00:20:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: MountainMan $ * * Declares main classes for content items and interfaces to SQL databases * @@ -250,15 +250,14 @@ class GdPlaylist : public mgPlaylist /*==== constructors ====*/ GdPlaylist(std::string listname, MYSQL db_handle); /* opens existing or creates empty playlist */ - - GdPlaylist(unsigned int sql_identifier, MYSQL db_handle); - /* construct from the db by internal id*/ - + /*==== destructor ====*/ virtual ~GdPlaylist(); - + virtual void setListname(std::string name); + /* changes the listname of the playlist (and unset the sql id */ + int getPlayTime(); /* returns the total duration of all songs in the list in seconds */ @@ -307,6 +306,9 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.8 2004/07/06 00:20:51 MountainMan + * loading and saving playlists + * * Revision 1.7 2004/05/28 15:29:18 lvw * Merged player branch back on HEAD branch. * diff --git a/mg_playlist.h b/mg_playlist.h index dcec71c..caf3cdc 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -2,10 +2,10 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/07/06 00:20:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: MountainMan $ * * This file implements the class mgPlaylist which maintains a playlist * and supports editing (e.g. adding or moving tracks), navigating it @@ -116,7 +116,7 @@ public: * * \param name - the new name of this list */ - void setListname(std::string name); + virtual void setListname(std::string name); //! \brief returns the count of items in the list int count(); @@ -138,6 +138,7 @@ public: //! \brief obtain the next item without skipping the current position virtual mgContentItem* sneakNext(); + virtual bool storePlaylist()=0; }; #endif diff --git a/vdr_menu.c b/vdr_menu.c index 4545deb..018cbb5 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.21 $ - * \date $Date: 2004/06/02 19:29:22 $ + * \version $Revision: 1.22 $ + * \date $Date: 2004/07/06 00:20:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: MountainMan $ * - * $Id: vdr_menu.c,v 1.21 2004/06/02 19:29:22 lvw Exp $ + * $Id: vdr_menu.c,v 1.22 2004/07/06 00:20:51 MountainMan Exp $ */ #include @@ -313,6 +313,32 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } } } + else if( m_state == LOAD_PLAYLIST ) + { + if( state == osUnknown ) + { + switch( key ) + { + case kOk: + { + // load the selected playlist + + m_current_playlist -> clear(); + string selected = (*m_plists)[Current()]; + m_current_playlist = m_media->loadPlaylist(selected.c_str()); + // clean the list of playlist + m_plists->clear(); + m_last_osd_index =0; + DisplayPlaylist(0); + state = osContinue; + } break; + default: + { + state = osContinue; + }; + } + } + } else if( m_state == PLAYLIST_SUBMENU ) { if( state == osUnknown ) @@ -561,6 +587,49 @@ void mgMainMenu::DisplayPlaylist( int index_current ) Display(); } +void mgMainMenu::LoadPlaylist() +{ + m_state = LOAD_PLAYLIST; + static char titlestr[80]; + + // make sure we have a current playlist + Clear(); + SetButtons(); + sprintf( titlestr, "Muggle - %s %s ",tr("load"), tr("Playlist")); + SetTitle( titlestr ); + + // retrieve list of available playlists + m_plists = m_media->getStoredPlaylists(); + + for(vector::iterator iter = m_plists->begin(); + iter != m_plists->end() ; iter++) + { + + Add( new cOsdItem( iter->c_str() ) ); + } + + + Display(); +} + +void mgMainMenu::SavePlaylist() +{ + if(m_current_playlist->getListname() == "") + { + // create dummy listname with current date and time + time_t currentTime = time(NULL); + m_current_playlist->setListname(ctime(¤tTime)); + } + m_current_playlist->storePlaylist(); +} + +void mgMainMenu::RenamePlaylist() +{ + // dummy function. USes current date as name + time_t currentTime = time(NULL); + m_current_playlist->setListname(ctime(¤tTime)); +} + void mgMainMenu::DisplayPlaylistSubmenu() { m_state = PLAYLIST_SUBMENU; @@ -572,8 +641,9 @@ void mgMainMenu::DisplayPlaylistSubmenu() // Add items Add( new cOsdItem( "1 - Load playlist" ) ); Add( new cOsdItem( "2 - Save playlist" ) ); - Add( new cOsdItem( "3 - Clear playlist" ) ); - Add( new cOsdItem( "4 - Remove entry from list" ) ); + Add( new cOsdItem( "3 - Rename playlist" ) ); + Add( new cOsdItem( "4 - Clear playlist" ) ); + Add( new cOsdItem( "5 - Remove entry from list" ) ); Display(); } @@ -587,15 +657,27 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { case 0: { - Interface->Status( "Load not yet implemented" ); + LoadPlaylist(); Interface->Flush(); } break; case 1: { - Interface->Status( "Save not yet implemented" ); + + SavePlaylist(); + Interface->Status( "Playlist saved"); Interface->Flush(); } break; case 2: + { // renamer playlist + RenamePlaylist(); + + // confirmation + Interface->Status( "Playlist renamed (dummy)" ); + Interface->Flush(); + + state = osContinue; + } break; + case 3: { // clear playlist m_current_playlist->clear(); @@ -605,7 +687,7 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) state = osContinue; } break; - case 3: + case 4: { // remove selected title m_current_playlist->remove( m_last_osd_index ); if( m_last_osd_index > 0 ) @@ -736,6 +818,9 @@ void mgMainMenu::Play(mgPlaylist *plist) /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.22 2004/07/06 00:20:51 MountainMan + * loading and saving playlists + * * Revision 1.21 2004/06/02 19:29:22 lvw * Use asprintf to create messages * diff --git a/vdr_menu.h b/vdr_menu.h index a4a1f7c..43f29d4 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,18 +2,19 @@ * \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR * - * \version $Revision: 1.10 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \version $Revision: 1.11 $ + * \date $Date: 2004/07/06 00:20:51 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: MountainMan $ * - * $Id: vdr_menu.h,v 1.10 2004/05/28 15:29:19 lvw Exp $ + * $Id: vdr_menu.h,v 1.11 2004/07/06 00:20:51 MountainMan Exp $ */ #ifndef _VDR_MENU_H #define _VDR_MENU_H #include +#include #include @@ -64,7 +65,8 @@ class mgMainMenu : public cOsdMenu enum MuggleStatus { TREE, TREE_SUBMENU, - PLAYLIST, PLAYLIST_SUBMENU, + PLAYLIST, LOAD_PLAYLIST, SAVE_PLAYLIST, + PLAYLIST_SUBMENU, FILTER, FILTER_SUBMENU }; @@ -83,6 +85,9 @@ class mgMainMenu : public cOsdMenu void DisplayTrackInfo(); void DisplayAlbumInfo(); + void LoadPlaylist(); + void SavePlaylist(); + void RenamePlaylist(); void DisplayPlaylistSubmenu(); eOSState PlaylistSubmenuAction( int n ); @@ -98,6 +103,7 @@ class mgMainMenu : public cOsdMenu mgSelectionTreeNode *m_root; mgSelectionTreeNode *m_node; mgPlaylist *m_current_playlist; + std::vector *m_plists; MuggleStatus m_state; std::list m_history; @@ -110,6 +116,9 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.11 2004/07/06 00:20:51 MountainMan + * loading and saving playlists + * * Revision 1.10 2004/05/28 15:29:19 lvw * Merged player branch back on HEAD branch. * -- cgit v1.2.3 From e6ea7cc8e5a2af383f6e90c700dd3a00aa8c84ed Mon Sep 17 00:00:00 2001 From: LarsAC Date: Fri, 9 Jul 2004 12:22:00 +0000 Subject: Untested extensions for exporting plalists git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@102 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 31 ++++++++++++++-- mg_playlist.h | 9 +++-- muggle.c | 30 ++++++++++++--- muggle.h | 13 ++++--- vdr_menu.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++------- vdr_menu.h | 21 +++++++---- 6 files changed, 183 insertions(+), 38 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index f6387b0..19e24d5 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -2,10 +2,10 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/07/09 12:22:00 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: LarsAC $ * * This file implements the class mgPlaylist which maintains a playlist * and supports editing (e.g. adding or moving tracks), navigating it @@ -207,3 +207,28 @@ mgContentItem* mgPlaylist::sneakNext() } } +bool mgPlaylist::exportM3U( const char *m3u_file ) +{ + vector::iterator iter; + bool result = true; + + // open a file for writing + FILE *listfile = fopen( m3u_file, "w" ); + + if( !listfile ) + { + return false; + } + + fprintf( listfile, "#EXTM3U" ); + + for( iter = m_list.begin(); iter != m_list.end(); iter++ ) + { // each item in the list + fprintf( listfile, "#EXTINF:0,%s", (*iter)->getLabel().c_str() ); + fprintf( listfile, "%s", (*iter)->getSourceFile().c_str() ); + } + + fclose( listfile ); + + return result; +} diff --git a/mg_playlist.h b/mg_playlist.h index caf3cdc..aaac699 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -2,10 +2,10 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.3 $ - * \date $Date: 2004/07/06 00:20:51 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/07/09 12:22:00 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: MountainMan $ + * \author Responsible author: $Author: LarsAC $ * * This file implements the class mgPlaylist which maintains a playlist * and supports editing (e.g. adding or moving tracks), navigating it @@ -139,6 +139,9 @@ public: //! \brief obtain the next item without skipping the current position virtual mgContentItem* sneakNext(); virtual bool storePlaylist()=0; + + //! \brief export the playlist in m3u format + virtual void exportM3U( const char *m3u_file ); }; #endif diff --git a/muggle.c b/muggle.c index 86e5c9d..dbaebf3 100644 --- a/muggle.c +++ b/muggle.c @@ -2,16 +2,18 @@ * \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR * - * \version $Revision: 1.7 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.8 $ + * \date $Date: 2004/07/09 12:22:00 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: LarsAC $ * - * $Id: muggle.c,v 1.7 2004/05/28 15:29:18 lvw Exp $ + * $Id: muggle.c,v 1.8 2004/07/09 12:22:00 LarsAC Exp $ */ #include +#include + #include "muggle.h" #include "vdr_menu.h" @@ -140,12 +142,27 @@ bool mgMuggle::Start(void) { // Start any background activities the plugin shall perform. mgSetDebugLevel( 99 ); - RegisterI18n( Phrases ); + + // Database initialization m_media = new mgMedia( mgMedia::GD_MP3 ); m_root = m_media->getSelectionRoot(); m_playlist = m_media->createTemporaryPlaylist(); m_media->initFilterSet(); + + // Read commands for playlists in etc. /video/muggle/playlist_commands.conf + m_playlist_cmds = new cCommands(); + + char *cmd_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), "playlist_commands.conf" ); + bool have_cmd_file = m_playlist_cmds->Load( cmd_file ); + free( cmd_file ); + + if( !have_cmd_file ) + { + delete m_playlist_cmds; + m_playlist_cmds = NULL; + } + return true; } @@ -157,7 +174,8 @@ void mgMuggle::Housekeeping(void) cOsdObject *mgMuggle::MainMenuAction(void) { // Perform the action when selected from the main VDR menu. - cOsdObject* osd = new mgMainMenu( m_media, m_root, m_playlist ); + cOsdObject* osd = new mgMainMenu( m_media, m_root, m_playlist, + m_playlist_commands ); return osd; } diff --git a/muggle.h b/muggle.h index 957ce5e..835957f 100644 --- a/muggle.h +++ b/muggle.h @@ -2,12 +2,12 @@ * \file muggle.h * \brief Implements a plugin for browsing media libraries within VDR * - * \version $Revision: 1.5 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/07/09 12:22:00 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: lvw $ + * \author file owner: $Author: LarsAC $ * - * $Id: muggle.h,v 1.5 2004/05/28 15:29:18 lvw Exp $ + * $Id: muggle.h,v 1.6 2004/07/09 12:22:00 LarsAC Exp $ */ #ifndef _MUGGLE_H @@ -19,6 +19,8 @@ class mgMedia; class mgSelectionTreeNode; class mgPlaylist; +class cCommands; + class mgMuggle : public cPlugin { public: @@ -54,7 +56,8 @@ private: mgMedia *m_media; mgSelectionTreeNode *m_root; mgPlaylist *m_playlist; - + cCommands *m_playlist_commands; + }; #endif diff --git a/vdr_menu.c b/vdr_menu.c index 018cbb5..59746bd 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.22 $ - * \date $Date: 2004/07/06 00:20:51 $ + * \version $Revision: 1.23 $ + * \date $Date: 2004/07/09 12:22:00 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: MountainMan $ + * \author Responsible author: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.22 2004/07/06 00:20:51 MountainMan Exp $ + * $Id: vdr_menu.c,v 1.23 2004/07/09 12:22:00 LarsAC Exp $ */ #include @@ -55,8 +55,10 @@ void mgMenuTreeItem::Set() // ----------------------- mgMainMenu ---------------------- -mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *playlist) - : cOsdMenu( "" ), m_media(media), m_root(root), m_current_playlist(playlist) +mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, + mgPlaylist *playlist, cCommands playlist_commands) + : cOsdMenu( "" ), m_media(media), m_root(root), + m_current_playlist(playlist), m_playlist_commands(playlist_commands) { mgDebug( 1, "Creating Muggle Main Menu" ); @@ -324,8 +326,9 @@ eOSState mgMainMenu::ProcessKey(eKeys key) // load the selected playlist m_current_playlist -> clear(); - string selected = (*m_plists)[Current()]; - m_current_playlist = m_media->loadPlaylist(selected.c_str()); + string selected = (*m_plists)[ Current() ]; + m_current_playlist = m_media->loadPlaylist( selected.c_str() ); + // clean the list of playlist m_plists->clear(); m_last_osd_index =0; @@ -372,6 +375,19 @@ eOSState mgMainMenu::ProcessKey(eKeys key) state = osContinue; } break; } + else if( m_state == PLAYLIST_COMMANDS ) + { + if( state == osUnknown ) + { + switch( key ) + { + kOk: + { + state = Execute(); + } + } + } + } } else if( state == osBack ) { @@ -603,11 +619,9 @@ void mgMainMenu::LoadPlaylist() for(vector::iterator iter = m_plists->begin(); iter != m_plists->end() ; iter++) - { - + { Add( new cOsdItem( iter->c_str() ) ); } - Display(); } @@ -644,10 +658,73 @@ void mgMainMenu::DisplayPlaylistSubmenu() Add( new cOsdItem( "3 - Rename playlist" ) ); Add( new cOsdItem( "4 - Clear playlist" ) ); Add( new cOsdItem( "5 - Remove entry from list" ) ); + Add( new cOsdItem( "6 - Export playlist" ) ); + + if( m_playlist_commands ) + { + Add( new cOsdItem( "7 - External playlist commands" ) ); + } Display(); } +void mgMainMenu::DisplayPlaylistCommands() +{ + m_state = PLAYLIST_COMMANDS; + + cCommand *command; + int i = 0; + + Clear(); + SetTitle( "Muggle - External Playlist Commands" ); + + while( ( command = m_playlist_commands->Get(i) ) != NULL ) + { + Add( new cOsdItem( hk( command->Title() ) ) ); + i++; + } + + Display(); +} + +eOSState cMenuCommands::ExecutePlaylistCommand( int current, char *parameters ) +{ + cCommand *command = m_playlist_commands->Get( current ); + if( command ) + { + char *buffer = NULL; + bool confirmed = true; + if( command->Confirm() ) + { + asprintf( &buffer, "%s?", command->Title() ); + confirmed = Interface->Confirm( buffer ); + free( buffer ); + } + if( confirmed ) + { + asprintf( &buffer, "%s...", command->Title() ); + Interface->Status( buffer ); + Interface->Flush(); + free( buffer ); + + char *tmp_m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), "current.m3u" ); + m_current_playlist->exportM3U( tmp_m3u_file ); + free( tmp_m3u_file ); + + const char *result = command->Execute( tmp_m3u_file ); + + /* What to do? Recode cMenuText (not much)? + if( result ) + { + return AddSubMenu( new cMenuText( command->Title(), result ) ); + } + */ + return osEnd; + } + } + return osContinue; +} + eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { cout << "mgMainMenu::PlaylistSubmenuAction: " << n << endl << flush; @@ -659,10 +736,11 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { LoadPlaylist(); Interface->Flush(); + + // jump to playlist view from here? } break; case 1: - { - + { SavePlaylist(); Interface->Status( "Playlist saved"); Interface->Flush(); @@ -700,6 +778,16 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) Interface->Status( "Entry removed" ); Interface->Flush(); } + case 5: + { + char *m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), m_current_playlist->getListname() ); + m_current_playlist->exportM3U( m3u_file ); + free( m3u_file ); + } + case 6: + { + DisplayPlaylistCommands(); + } default: { // undefined action @@ -818,6 +906,9 @@ void mgMainMenu::Play(mgPlaylist *plist) /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.23 2004/07/09 12:22:00 LarsAC + * Untested extensions for exporting plalists + * * Revision 1.22 2004/07/06 00:20:51 MountainMan * loading and saving playlists * diff --git a/vdr_menu.h b/vdr_menu.h index 43f29d4..81fe2f4 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,12 +2,12 @@ * \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR * - * \version $Revision: 1.11 $ - * \date $Date: 2004/07/06 00:20:51 $ + * \version $Revision: 1.12 $ + * \date $Date: 2004/07/09 12:22:00 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: MountainMan $ + * \author Responsible author: $Author: LarsAC $ * - * $Id: vdr_menu.h,v 1.11 2004/07/06 00:20:51 MountainMan Exp $ + * $Id: vdr_menu.h,v 1.12 2004/07/09 12:22:00 LarsAC Exp $ */ #ifndef _VDR_MENU_H @@ -17,8 +17,7 @@ #include #include - -#include "i18n.h" +#include #include "i18n.h" @@ -53,7 +52,8 @@ class mgMainMenu : public cOsdMenu { public: - mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *playlist); + mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, + mgPlaylist *playlist, cCommands playlist_commands ); mgSelectionTreeNode *CurrentNode(); mgMenuTreeItem *CurrentItem(); @@ -66,7 +66,7 @@ class mgMainMenu : public cOsdMenu { TREE, TREE_SUBMENU, PLAYLIST, LOAD_PLAYLIST, SAVE_PLAYLIST, - PLAYLIST_SUBMENU, + PLAYLIST_SUBMENU, PLAYLIST_COMMANDS, FILTER, FILTER_SUBMENU }; @@ -108,6 +108,8 @@ class mgMainMenu : public cOsdMenu MuggleStatus m_state; std::list m_history; + cCommands m_playlist_commands; + int m_last_osd_index; }; @@ -116,6 +118,9 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.12 2004/07/09 12:22:00 LarsAC + * Untested extensions for exporting plalists + * * Revision 1.11 2004/07/06 00:20:51 MountainMan * loading and saving playlists * -- cgit v1.2.3 From dd63bc6cd9cfb4d117392c64b7e7bd046d9b1139 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Fri, 9 Jul 2004 13:47:07 +0000 Subject: Updated next steps git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@103 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index 93a5255..e0b1ea5 100644 --- a/TODO +++ b/TODO @@ -1,35 +1,40 @@ TODO File for Muggle ==================== +Testing/bugs +============ +- Save/load playlists to database +- Export playlists +- Execute playlist commands +- Check play speed +- Playlists starts with 2nd song +- Check for getSourceFile + Code polishing ============== - Check for memory leaks - Check for (reasonably) consistent usage of char*/string - mgDatabase is not used? mgPlayer used what for? - Check for unnecessary log commands -- Add logs, documentation (generate in HTML) - Generate HTML documentation using doxygen, dotty/gv for state machines Short term items ================ -- Adapt scripts - - use working directory by default or argument - - how to install into correct path +- Import existing m3u playlists -OSD ---- +OSD (general) +------------- +- Add separators for first letter (A, B, C, ...) that can be skipped using left/right Content ------- - Handle loop mode in mgPlaylist - Handle shuffle mode in mgPlaylist -- Save/load playlists to database Player ------ -- Determine max. framecount (needed for rewinding) ? +- Determine max. framecount (needed for rewinding)? - Init scale/level/normalize? -- Check play speed - Add ogg decoder - Add a simple progress display (song title, artist, ...) @@ -40,12 +45,11 @@ Medium term items ================= - really abstract from specific queries etc. -- mgDatabase should abstract database stuff!? +- mgDatabase should abstract database (mySQL) stuff!? OSD --- - Type numbers to enter characters and jump to first title accordingly -- Add separators for first letter (A, B, C, ...) that can be skipped using left/right Content ------- @@ -98,6 +102,9 @@ Already Done ************************************************************ * * $Log: TODO,v $ +* Revision 1.8 2004/07/09 13:47:07 LarsAC +* Updated next steps +* * Revision 1.7 2004/05/28 15:29:18 lvw * Merged player branch back on HEAD branch. * -- cgit v1.2.3 From c5ced15ac3cb7365914b75378ac8d746480e65df Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 12 Jul 2004 11:06:23 +0000 Subject: No longer skip first file on playlist when starting replay. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@104 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 090bbc6..e02414e 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -2,12 +2,12 @@ * \file vdr_player.c * \brief A generic PCM player for a VDR media plugin (muggle) * - * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/07/12 11:06:23 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: LarsAC $ * - * $Id: vdr_player.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id: vdr_player.c,v 1.3 2004/07/12 11:06:23 LarsAC Exp $ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -246,10 +246,7 @@ void mgPCMPlayer::Activate(bool on) Lock(); m_playlist->initialize(); - if( NextFile() ) - { - Play(); - } + Play(); Unlock(); } } @@ -1045,6 +1042,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) /************************************************************ * * $Log: vdr_player.c,v $ + * Revision 1.3 2004/07/12 11:06:23 LarsAC + * No longer skip first file on playlist when starting replay. + * * Revision 1.2 2004/05/28 15:29:19 lvw * Merged player branch back on HEAD branch. * -- cgit v1.2.3 From 6d085c1c2c9fd7270f96d22b1781d28005ff3674 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 25 Jul 2004 21:33:35 +0000 Subject: Removed bugs in finding track files and playlist indexing. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@105 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 46 ++++++++++++++++------------ mg_content_interface.c | 38 ++++++++++++----------- mg_playlist.c | 21 ++++++++----- mg_playlist.h | 10 +++--- mg_tools.c | 28 ++++++++++------- muggle.c | 34 ++++++++++++--------- vdr_menu.c | 83 +++++++++++++++++++++++++++----------------------- vdr_menu.h | 21 ++++++++----- vdr_player.c | 16 ++++++---- vdr_setup.c | 7 +++-- 10 files changed, 175 insertions(+), 129 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 874159e..759c32c 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,10 +1,10 @@ /*! \file content_interface.cpp * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugindatabase * - * \version $Revision: 1.23 $ - * \date $Date: 2004/07/06 00:20:51 $ + * \version $Revision: 1.24 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: MountainMan $ + * \author Responsible author: $Author: lvw $ * * Implements main classes of for content items and interfaces to SQL databases * @@ -330,8 +330,7 @@ mgGdTrack::~mgGdTrack() bool mgGdTrack::readData() { MYSQL_RES *result; - int nrows; - int nfields; + int nrows, nfields; // note: this does not work with empty album or genre fields result = mgSqlReadQuery(&m_db, @@ -346,6 +345,7 @@ bool mgGdTrack::readData() nrows = mysql_num_rows(result); nfields = mysql_num_fields(result); + if(nrows == 0) { mgWarning("No entries found \n"); @@ -353,30 +353,33 @@ bool mgGdTrack::readData() } else { - if (nrows >1 ) + if( nrows > 1 ) { - mgWarning("More than one entry found"); + mgWarning("mgGdTrack::readData: More than one entry found. Using first entry."); } - MYSQL_ROW row; - - row = mysql_fetch_row(result); - m_artist = row[0]; - m_album = row[1]; - m_title = row [2]; - m_mp3file = row [3]; - m_genre = row [4]; - if(sscanf(row [5], "%d", &m_year) !=1) + MYSQL_ROW row = mysql_fetch_row(result); + + m_artist = row[0]; + m_album = row[1]; + m_title = row[2]; + m_mp3file = row[3]; + m_genre = row[4]; + + if( sscanf( row[5], "%d", &m_year) != 1 ) { mgError("Invalid year '%s' in database", row [5]); } - if(sscanf(row [6], "%d", &m_rating) !=1) + + if( row[6] && sscanf( row[6], "%d", &m_rating ) != 1 ) { - mgError("Invalid rating '%s' in database", row [6]); + mgError( "Invalid rating '%s' in database", row [6] ); } - if(sscanf(row [7], "%d", &m_length) !=1) + + if( row[7] && sscanf( row[7], "%d", &m_length) != 1 ) { - mgError("Invalid duration '%s' in database", row [7]); + mgError( "Invalid duration '%s' in database", row [7]); } + } m_retrieved = true; return true; @@ -1392,6 +1395,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.24 2004/07/25 21:33:35 lvw + * Removed bugs in finding track files and playlist indexing. + * * Revision 1.23 2004/07/06 00:20:51 MountainMan * loading and saving playlists * diff --git a/mg_content_interface.c b/mg_content_interface.c index 437d8b5..c62bd2d 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -1,8 +1,8 @@ /*! \file mg_content_interface.c * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin * - * \version $Revision: 1.5 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * @@ -104,7 +104,6 @@ void mgTracklist::sortBy(int col, bool direction) ****************************************************************************/ void mgTracklist::setDisplayColumns(vector cols) { - m_columns = cols; } @@ -124,36 +123,38 @@ unsigned int mgTracklist::getNumColumns() * \brief creates the label string for an item * * The list can create a label with different fields (columns). - * The fields used in the list and their order is set by the function - using the function setDisplayColumns - * function getLabel(). + * The fields used in the list and their order is set using the function setDisplayColumns + * * This function creates a string from these columns, separated by the string * 'separator' * in the label and their order ****************************************************************************/ string mgTracklist::getLabel(unsigned int position, const string separator) { - string label =""; + string label = ""; mgContentItem* item; - if(position >= m_list.size()) + if( position >= m_list.size() ) + { return ""; - + } else - { - item = *(m_list.begin()+position); - } - + { + item = *( m_list.begin() + position ); + } + + mgDebug( 1, "mgTracklist::getLabel: Starting to iterate columns." ); for( vector::iterator iter = m_columns.begin(); iter != m_columns.end(); iter++ ) - { + { if( iter != m_columns.begin() ) - { + { label += separator; - } + } + mgDebug( 1, "mgTracklist::getLabel: obtaining label from item %d", *iter ); label += item->getLabel(*iter); - } + } return label; } @@ -302,6 +303,9 @@ string mgSelectionTreeNode::getRestrictions() /* -------------------- begin CVS log --------------------------------- * $Log: mg_content_interface.c,v $ + * Revision 1.6 2004/07/25 21:33:35 lvw + * Removed bugs in finding track files and playlist indexing. + * * Revision 1.5 2004/05/28 15:29:18 lvw * Merged player branch back on HEAD branch. * diff --git a/mg_playlist.c b/mg_playlist.c index 19e24d5..8f9303d 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -2,10 +2,10 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.3 $ - * \date $Date: 2004/07/09 12:22:00 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ * * This file implements the class mgPlaylist which maintains a playlist * and supports editing (e.g. adding or moving tracks), navigating it @@ -16,7 +16,6 @@ #include "mg_playlist.h" #include "mg_tools.h" -#include #include using namespace std; @@ -35,7 +34,7 @@ mgPlaylist::mgPlaylist() mgPlaylist::mgPlaylist(string listname) { - m_current_idx = 0; + m_current_idx = -1; m_listname = listname; } @@ -55,7 +54,7 @@ void mgPlaylist::toggleLoop() void mgPlaylist::initialize() { - m_current = m_list.begin(); + m_current_idx = -1; } /* ==== add/remove tracks ==== */ @@ -140,6 +139,7 @@ int mgPlaylist::count() mgContentItem* mgPlaylist::getFirst() { m_current = m_list.begin(); + m_current_idx = 0; return *m_current; } @@ -162,6 +162,11 @@ mgContentItem* mgPlaylist::skipFwd() { mgContentItem* next; + if( m_current_idx < 0 ) + { + return getFirst(); + } + if( m_current + 1 == m_list.end() ) // unless loop mode { // TODO: why not return a NULL pointer? LVW @@ -207,13 +212,13 @@ mgContentItem* mgPlaylist::sneakNext() } } -bool mgPlaylist::exportM3U( const char *m3u_file ) +bool mgPlaylist::exportM3U( string m3u_file ) { vector::iterator iter; bool result = true; // open a file for writing - FILE *listfile = fopen( m3u_file, "w" ); + FILE *listfile = fopen( m3u_file.c_str(), "w" ); if( !listfile ) { diff --git a/mg_playlist.h b/mg_playlist.h index aaac699..4a0da71 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -2,10 +2,10 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.4 $ - * \date $Date: 2004/07/09 12:22:00 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ * * This file implements the class mgPlaylist which maintains a playlist * and supports editing (e.g. adding or moving tracks), navigating it @@ -15,6 +15,8 @@ #ifndef __MG_PLAYLIST #define __MG_PLAYLIST +#include + #include "mg_content_interface.h" /*! @@ -141,7 +143,7 @@ public: virtual bool storePlaylist()=0; //! \brief export the playlist in m3u format - virtual void exportM3U( const char *m3u_file ); + virtual bool exportM3U( std::string m3u_file ); }; #endif diff --git a/mg_tools.c b/mg_tools.c index 9b8fe91..d12d0df 100644 --- a/mg_tools.c +++ b/mg_tools.c @@ -3,10 +3,10 @@ * \brief A few util functions for standalone and plugin messaging * for the vdr muggle plugindatabase ******************************************************************** - * \version $Revision: 1.2 $ - * \date $Date: 2004/02/02 22:48:04 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: MountainMan $ + * \author file owner: $Author: lvw $ * */ /*******************************************************************/ @@ -21,8 +21,8 @@ extern "C" #include -#define MAX_BUFLEN 1024 -#define MAX_QUERY_BUFLEN 1024 +#define MAX_BUFLEN 2048 +#define MAX_QUERY_BUFLEN 2048 static char buffer[MAX_BUFLEN]; static char querybuf[MAX_QUERY_BUFLEN]; @@ -113,16 +113,19 @@ void mgError(const char *fmt, ...) MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { va_list ap; + va_start( ap, fmt ); + vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); - va_start(ap, fmt); - - vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap); - mgDebug(9, "SQL-Query: '%s'",querybuf); - if(mysql_query(db,querybuf)) + mgDebug(9, "mgSqlReadQuery: SQL query: '%s'", querybuf); + + if( mysql_query(db, querybuf) ) { mgError("SQL error in MUGGLE\n%s\n", querybuf); } - return mysql_store_result(db); + + MYSQL_RES *result = mysql_store_result(db); + + return result; } void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) @@ -140,6 +143,9 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) /* -------------------- begin CVS log --------------------------------- * $Log: mg_tools.c,v $ + * Revision 1.3 2004/07/25 21:33:35 lvw + * Removed bugs in finding track files and playlist indexing. + * * Revision 1.2 2004/02/02 22:48:04 MountainMan * added CVS $Log * diff --git a/muggle.c b/muggle.c index dbaebf3..6e75705 100644 --- a/muggle.c +++ b/muggle.c @@ -2,12 +2,12 @@ * \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR * - * \version $Revision: 1.8 $ - * \date $Date: 2004/07/09 12:22:00 $ + * \version $Revision: 1.9 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ * - * $Id: muggle.c,v 1.8 2004/07/09 12:22:00 LarsAC Exp $ + * $Id: muggle.c,v 1.9 2004/07/25 21:33:35 lvw Exp $ */ #include @@ -50,11 +50,11 @@ mgMuggle::mgMuggle(void) // defaults for database arguments the_setup.DbHost = "localhost"; the_setup.DbPort = 0; - the_setup.DbName = "GiantDisc2"; + the_setup.DbName = "GiantDisc"; the_setup.DbUser = ""; the_setup.DbPass = "" ; the_setup.GdCompatibility = false; - the_setup.ToplevelDir = ""; + the_setup.ToplevelDir = "/mnt/music/"; } mgMuggle::~mgMuggle() @@ -71,13 +71,13 @@ const char *mgMuggle::CommandLineHelp(void) " -p PPPP, --port=PPPP specify port of database server (default is )\n" " -u UUUU, --user=UUUU specify database user (default is )\n" " -w WWWW, --password=WWWW specify database password (default is empty)\n" - " -t TTTT, --toplevel=TTTT specify toplevel directory for music\n" + " -t TTTT, --toplevel=TTTT specify toplevel directory for music (default is /mnt/music)\n" " -g, --giantdisc enable full Giantdisc compatibility mode\n"; } bool mgMuggle::ProcessArgs(int argc, char *argv[]) { - cout << "mgMuggle::ProcessArgs" << endl << flush; + mgDebug( 1, "mgMuggle::ProcessArgs" ); // Implement command line argument processing here if applicable. static struct option long_options[] = @@ -119,10 +119,12 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) } break; case 't': { - the_setup.ToplevelDir = optarg; + string res = string(optarg) + "/"; + the_setup.ToplevelDir = strdup( res.c_str() ); } break; case 'g': { + the_setup.DbName = "GiantDisc"; the_setup.GdCompatibility = true; } break; default: return false; @@ -151,16 +153,16 @@ bool mgMuggle::Start(void) m_media->initFilterSet(); // Read commands for playlists in etc. /video/muggle/playlist_commands.conf - m_playlist_cmds = new cCommands(); + m_playlist_commands = new cCommands(); - char *cmd_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), "playlist_commands.conf" ); - bool have_cmd_file = m_playlist_cmds->Load( cmd_file ); - free( cmd_file ); + char *cmd_file = (char *) AddDirectory( cPlugin::ConfigDirectory("muggle"), "playlist_commands.conf" ); + mgDebug( 1, "mgMuggle::Start: Looking for file %s", cmd_file ); + bool have_cmd_file = m_playlist_commands->Load( (const char*) cmd_file ); if( !have_cmd_file ) { - delete m_playlist_cmds; - m_playlist_cmds = NULL; + delete m_playlist_commands; + m_playlist_commands = NULL; } return true; @@ -187,6 +189,8 @@ cMenuSetupPage *mgMuggle::SetupMenu(void) bool mgMuggle::SetupParse(const char *Name, const char *Value) { + mgDebug( 1, "mgMuggle::SetupParse" ); + if (!strcasecmp(Name, "InitLoopMode")) the_setup.InitLoopMode = atoi(Value); else if (!strcasecmp(Name, "InitShuffleMode")) the_setup.InitShuffleMode = atoi(Value); else if (!strcasecmp(Name, "AudioMode")) the_setup.AudioMode = atoi(Value); diff --git a/vdr_menu.c b/vdr_menu.c index 59746bd..09e169d 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.23 $ - * \date $Date: 2004/07/09 12:22:00 $ + * \version $Revision: 1.24 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ * - * $Id: vdr_menu.c,v 1.23 2004/07/09 12:22:00 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.24 2004/07/25 21:33:35 lvw Exp $ */ #include @@ -17,6 +17,8 @@ #include #include +#include +#include #include "vdr_menu.h" #include "vdr_player.h" @@ -56,7 +58,7 @@ void mgMenuTreeItem::Set() // ----------------------- mgMainMenu ---------------------- mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, - mgPlaylist *playlist, cCommands playlist_commands) + mgPlaylist *playlist, cCommands *playlist_commands) : cOsdMenu( "" ), m_media(media), m_root(root), m_current_playlist(playlist), m_playlist_commands(playlist_commands) { @@ -135,11 +137,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { case kOk: { - mgDebug( 1, "mgMainMenu: expand and descend" ); - m_history.push_back( Current() ); - mgDebug( 1, "Remember current node #%i", Current() ); - mgSelectionTreeNode *child = CurrentNode(); DisplayTree( child ); @@ -340,7 +338,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) state = osContinue; }; } - } + } } else if( m_state == PLAYLIST_SUBMENU ) { @@ -375,25 +373,35 @@ eOSState mgMainMenu::ProcessKey(eKeys key) state = osContinue; } break; } - else if( m_state == PLAYLIST_COMMANDS ) - { - if( state == osUnknown ) - { - switch( key ) - { - kOk: - { - state = Execute(); - } - } - } - } } else if( state == osBack ) { m_state = PLAYLIST; DisplayPlaylist( m_last_osd_index ); - + + state = osContinue; + } + } + else if( m_state == PLAYLIST_COMMANDS ) + { + if( state == osUnknown ) + { + switch( key ) + { + case kOk: + { + state = ExecutePlaylistCommand( Current() ); + } break; + default: + { + } + } + } + else if( state == osBack ) + { + m_state = PLAYLIST_SUBMENU; + DisplayPlaylistSubmenu(); + state = osContinue; } } @@ -480,11 +488,8 @@ void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) SetButtons(); m_node = node; - mgDebug( 1, "mgBrowseMenu::DisplaySelection: node %s received", node->getLabel().c_str() ); vector children = node->getChildren(); - mgDebug( 1, "mgBrowseMenu::DisplaySelection: %d elements received", children.size() ); - for( vector::iterator iter = children.begin(); iter != children.end(); iter ++ ) @@ -513,10 +518,7 @@ void mgMainMenu::DisplayTreeSubmenu() m_state = TREE_SUBMENU; Clear(); - - mgDebug( 1, "Creating Muggle tree view submenu" ); SetButtons(); - SetTitle( strcat("Muggle - ",tr("Tree View Commands") ) ); // Add items @@ -573,6 +575,7 @@ eOSState mgMainMenu::TreeSubmenuAction( int n ) void mgMainMenu::DisplayPlaylist( int index_current ) { + mgDebug( 1, "mgMainMenu::DisplayPlaylist: entering." ); m_state = PLAYLIST; // make sure we have a current playlist @@ -585,7 +588,7 @@ void mgMainMenu::DisplayPlaylist( int index_current ) list->size() , tr("items") ); SetTitle( titlestr ); - + for( unsigned int i = 0; i < m_current_playlist->getNumItems(); i++) { string label = m_current_playlist->getLabel( i, " " ); @@ -687,7 +690,7 @@ void mgMainMenu::DisplayPlaylistCommands() Display(); } -eOSState cMenuCommands::ExecutePlaylistCommand( int current, char *parameters ) +eOSState mgMainMenu::ExecutePlaylistCommand( int current ) { cCommand *command = m_playlist_commands->Get( current ); if( command ) @@ -700,18 +703,17 @@ eOSState cMenuCommands::ExecutePlaylistCommand( int current, char *parameters ) confirmed = Interface->Confirm( buffer ); free( buffer ); } - if( confirmed ) + if( confirmed ) { asprintf( &buffer, "%s...", command->Title() ); Interface->Status( buffer ); Interface->Flush(); free( buffer ); - char *tmp_m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), "current.m3u" ); + string tmp_m3u_file = (char *) AddDirectory( cPlugin::ConfigDirectory("muggle"), "current.m3u" ); m_current_playlist->exportM3U( tmp_m3u_file ); - free( tmp_m3u_file ); - const char *result = command->Execute( tmp_m3u_file ); + char *result = (char *)command->Execute( tmp_m3u_file.c_str() ); /* What to do? Recode cMenuText (not much)? if( result ) @@ -719,6 +721,9 @@ eOSState cMenuCommands::ExecutePlaylistCommand( int current, char *parameters ) return AddSubMenu( new cMenuText( command->Title(), result ) ); } */ + + free( result ); + return osEnd; } } @@ -780,9 +785,8 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) } case 5: { - char *m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), m_current_playlist->getListname() ); + string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), m_current_playlist->getListname().c_str() ); m_current_playlist->exportM3U( m3u_file ); - free( m3u_file ); } case 6: { @@ -906,6 +910,9 @@ void mgMainMenu::Play(mgPlaylist *plist) /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.24 2004/07/25 21:33:35 lvw + * Removed bugs in finding track files and playlist indexing. + * * Revision 1.23 2004/07/09 12:22:00 LarsAC * Untested extensions for exporting plalists * diff --git a/vdr_menu.h b/vdr_menu.h index 81fe2f4..5ff7f34 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -2,12 +2,12 @@ * \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR * - * \version $Revision: 1.12 $ - * \date $Date: 2004/07/09 12:22:00 $ + * \version $Revision: 1.13 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ * - * $Id: vdr_menu.h,v 1.12 2004/07/09 12:22:00 LarsAC Exp $ + * $Id: vdr_menu.h,v 1.13 2004/07/25 21:33:35 lvw Exp $ */ #ifndef _VDR_MENU_H @@ -17,10 +17,11 @@ #include #include -#include #include "i18n.h" +class cCommands; + class mgMedia; class mgSelectionTreeNode; class mgPlaylist; @@ -53,7 +54,7 @@ class mgMainMenu : public cOsdMenu public: mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, - mgPlaylist *playlist, cCommands playlist_commands ); + mgPlaylist *playlist, cCommands *playlist_commands ); mgSelectionTreeNode *CurrentNode(); mgMenuTreeItem *CurrentItem(); @@ -90,11 +91,14 @@ class mgMainMenu : public cOsdMenu void RenamePlaylist(); void DisplayPlaylistSubmenu(); eOSState PlaylistSubmenuAction( int n ); + void DisplayPlaylistCommands(); + eOSState ExecutePlaylistCommand( int current ); // Filter view handling void DisplayFilter(); void DisplayFilterSelector(); + private: void Play(mgPlaylist *plist); @@ -108,7 +112,7 @@ class mgMainMenu : public cOsdMenu MuggleStatus m_state; std::list m_history; - cCommands m_playlist_commands; + cCommands *m_playlist_commands; int m_last_osd_index; }; @@ -118,6 +122,9 @@ class mgMainMenu : public cOsdMenu /************************************************************ * * $Log: vdr_menu.h,v $ + * Revision 1.13 2004/07/25 21:33:35 lvw + * Removed bugs in finding track files and playlist indexing. + * * Revision 1.12 2004/07/09 12:22:00 LarsAC * Untested extensions for exporting plalists * diff --git a/vdr_player.c b/vdr_player.c index e02414e..48f7be3 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -2,12 +2,12 @@ * \file vdr_player.c * \brief A generic PCM player for a VDR media plugin (muggle) * - * \version $Revision: 1.3 $ - * \date $Date: 2004/07/12 11:06:23 $ + * \version $Revision: 1.4 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ * - * $Id: vdr_player.c,v 1.3 2004/07/12 11:06:23 LarsAC Exp $ + * $Id: vdr_player.c,v 1.4 2004/07/25 21:33:35 lvw Exp $ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -337,7 +337,6 @@ void mgPCMPlayer::Action(void) dvbSampleRate = 48000; m_state = msStop; SetPlayMode( pmStopped ); - cout << "Playmode set." << endl << flush; while( m_active ) { @@ -362,6 +361,7 @@ void mgPCMPlayer::Action(void) m_playing = m_current; string filename = getSourceFile(); + mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); if( ( m_decoder = mgDecoders::findDecoder( filename ) ) && m_decoder->start() ) { @@ -696,7 +696,8 @@ string mgPCMPlayer::getSourceFile() } cout << "mgPCMPlayer::getSourceFile: found filename " << filename << endl << flush; - return "/test.mp3"; + return filename; + // return "/test.mp3"; } bool mgPCMPlayer::NextFile() @@ -1042,6 +1043,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) /************************************************************ * * $Log: vdr_player.c,v $ + * Revision 1.4 2004/07/25 21:33:35 lvw + * Removed bugs in finding track files and playlist indexing. + * * Revision 1.3 2004/07/12 11:06:23 LarsAC * No longer skip first file on playlist when starting replay. * diff --git a/vdr_setup.c b/vdr_setup.c index 04ba06b..13f44ac 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -2,12 +2,12 @@ * \file vdr_setup.c * \brief A setup class for a VDR media plugin (muggle) * - * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \version $Revision: 1.3 $ + * \date $Date: 2004/07/25 21:33:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * - * $Id: vdr_setup.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id: vdr_setup.c,v 1.3 2004/07/25 21:33:35 lvw Exp $ * * Partially adapted from * MP3/MPlayer plugin to VDR (C++) @@ -67,4 +67,5 @@ mgSetup::mgSetup() TargetLevel = DEFAULT_TARGET_LEVEL; LimiterLevel = DEFAULT_LIMITER_LEVEL; Only48kHz = 0; + ToplevelDir = "/mnt/music/"; } -- cgit v1.2.3 From 417a4a091109c0ad0ab6308cf815828419bbb80b Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 26 Jul 2004 20:02:38 +0000 Subject: Bug in handling playlist menu removed git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@106 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 09e169d..b663a9c 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.24 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \version $Revision: 1.25 $ + * \date $Date: 2004/07/26 20:02:38 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * - * $Id: vdr_menu.c,v 1.24 2004/07/25 21:33:35 lvw Exp $ + * $Id: vdr_menu.c,v 1.25 2004/07/26 20:02:38 lvw Exp $ */ #include @@ -787,11 +787,11 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), m_current_playlist->getListname().c_str() ); m_current_playlist->exportM3U( m3u_file ); - } + } break; case 6: { DisplayPlaylistCommands(); - } + } break; default: { // undefined action @@ -910,6 +910,9 @@ void mgMainMenu::Play(mgPlaylist *plist) /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.25 2004/07/26 20:02:38 lvw + * Bug in handling playlist menu removed + * * Revision 1.24 2004/07/25 21:33:35 lvw * Removed bugs in finding track files and playlist indexing. * -- cgit v1.2.3 From 74efd15adb6085352a812798839781f53bbb45b4 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 26 Jul 2004 20:03:00 +0000 Subject: Bug in initalizing playlist removed git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@107 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 48f7be3..36a712b 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -2,12 +2,12 @@ * \file vdr_player.c * \brief A generic PCM player for a VDR media plugin (muggle) * - * \version $Revision: 1.4 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/07/26 20:03:00 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * - * $Id: vdr_player.c,v 1.4 2004/07/25 21:33:35 lvw Exp $ + * $Id: vdr_player.c,v 1.5 2004/07/26 20:03:00 lvw Exp $ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -245,8 +245,11 @@ void mgPCMPlayer::Activate(bool on) m_playmode_mutex.Unlock(); Lock(); + m_playlist->initialize(); + NextFile(); Play(); + Unlock(); } } @@ -761,6 +764,7 @@ void mgPCMPlayer::Play(void) MGLOG( "mgPCMPlayer::Play" ); Lock(); + if( m_playmode != pmPlay && m_current ) { if( m_playmode == pmStopped ) @@ -1043,6 +1047,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) /************************************************************ * * $Log: vdr_player.c,v $ + * Revision 1.5 2004/07/26 20:03:00 lvw + * Bug in initalizing playlist removed + * * Revision 1.4 2004/07/25 21:33:35 lvw * Removed bugs in finding track files and playlist indexing. * -- cgit v1.2.3 From b8ba19db3565ea7392f45906ed34706651d5fd6f Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 26 Jul 2004 22:20:55 +0000 Subject: Reworked playlist indexing git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@108 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 82 ++++++++++++++++++++++++----------------------------------- mg_playlist.h | 22 ++++++++-------- vdr_player.c | 20 ++++++++++----- 3 files changed, 56 insertions(+), 68 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index 8f9303d..38d4d0f 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -2,8 +2,8 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.4 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \version $Revision: 1.5 $ + * \date $Date: 2004/07/26 22:20:54 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * @@ -24,7 +24,7 @@ using namespace std; mgPlaylist::mgPlaylist() { - m_current_idx = 0; + m_current_idx = -1; char *buffer; asprintf( &buffer, "Playlist-%ld", random() ); @@ -54,7 +54,7 @@ void mgPlaylist::toggleLoop() void mgPlaylist::initialize() { - m_current_idx = -1; + m_current_idx = 0; } /* ==== add/remove tracks ==== */ @@ -66,7 +66,7 @@ void mgPlaylist::append(mgContentItem* item) } /* append a list of tracks at the end of the playlist */ -void mgPlaylist::appendList(vector *tracks) +void mgPlaylist::appendList( vector *tracks ) { vector::iterator iter; @@ -82,7 +82,7 @@ void mgPlaylist::appendList(vector *tracks) } /* add a song after 'position' */ -void mgPlaylist::insert(mgContentItem* item, unsigned int position) +void mgPlaylist::insert( mgContentItem* item, unsigned int position ) { if( position >= m_list.size() ) { @@ -136,80 +136,64 @@ int mgPlaylist::count() } // returns the first item of the list -mgContentItem* mgPlaylist::getFirst() +mgContentItem* mgPlaylist::getCurrent() { - m_current = m_list.begin(); - m_current_idx = 0; - - return *m_current; + return *( m_list.begin() + m_current_idx ); } // returns the nth track from the playlist -mgContentItem* mgPlaylist::getPosition(unsigned int position) +void mgPlaylist::gotoPosition(unsigned int position) { if( position >= m_list.size() ) { // TODO: why not return a NULL pointer? LVW - return &(mgContentItem::UNDEFINED); //invalid + m_current_idx = -1; + } + else + { + m_current_idx = position; } - m_current = m_list.begin() + position; - - return *m_current; } // proceeds to the next item -mgContentItem* mgPlaylist::skipFwd() +void mgPlaylist::skipFwd() { - mgContentItem* next; - - if( m_current_idx < 0 ) + if( m_current_idx + 1 <= m_list.size() ) // unless loop mode { - return getFirst(); - } - - if( m_current + 1 == m_list.end() ) // unless loop mode - { - // TODO: why not return a NULL pointer? LVW - next = &(mgContentItem::UNDEFINED); //invalid item + m_current_idx ++; } else { - return * (++ m_current); + // or goto 1 in case of loop mode + m_current_idx = -1; } - - return next; } // goes back to the previous item -mgContentItem* mgPlaylist::skipBack() +void mgPlaylist::skipBack() { - mgContentItem* prev; - - if( m_current == m_list.begin() ) + if( m_current_idx > 0 ) { - // TODO: why not return a NULL pointer? LVW - prev = &(mgContentItem::UNDEFINED); //invalid + m_current_idx --; } else { - prev = * (--m_current); + // or goto last in case of loop mode + m_current_idx = -1; } - - return prev; } // get next track, do not update data structures mgContentItem* mgPlaylist::sneakNext() { - if( m_current+1 == m_list.end() ) - { - // TODO: why not return a NULL pointer? LVW - return &(mgContentItem::UNDEFINED); //invalid - } - else - { - return * (m_current+1); - } + if( m_current_idx + 1 <= m_list.size() ) // unless loop mode + { + return *(m_list.begin() + m_current_idx + 1); + } + else + { + return &(mgContentItem::UNDEFINED); + } } bool mgPlaylist::exportM3U( string m3u_file ) @@ -229,7 +213,7 @@ bool mgPlaylist::exportM3U( string m3u_file ) for( iter = m_list.begin(); iter != m_list.end(); iter++ ) { // each item in the list - fprintf( listfile, "#EXTINF:0,%s", (*iter)->getLabel().c_str() ); + fprintf( listfile, "#EXTINF:0,%s\n", (*iter)->getLabel().c_str() ); fprintf( listfile, "%s", (*iter)->getSourceFile().c_str() ); } diff --git a/mg_playlist.h b/mg_playlist.h index 4a0da71..df677d4 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -2,8 +2,8 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.5 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/07/26 22:20:55 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * @@ -29,6 +29,7 @@ class mgPlaylist : public mgTracklist { private: + //! \brief current index in the playlist // TODO: should be a property of the player? int m_current_idx; @@ -40,9 +41,6 @@ protected: //! \brief the name of the playlist std::string m_listname; - //! \brief the current item as an iterator - std::vector::iterator m_current; - public: /* ==== constructors and initialization ==== */ @@ -114,6 +112,9 @@ public: //! \brief obtain the listname std::string getListname() ; + //! \brief returns the current item of the list + virtual mgContentItem* getCurrent(); + /*! \brief set the listname * * \param name - the new name of this list @@ -123,24 +124,21 @@ public: //! \brief returns the count of items in the list int count(); - //! \brief returns the first item of the list - virtual mgContentItem* getFirst(); - /*! \brief returns the nth track from the playlist * * \param position - the position to skip to */ - virtual mgContentItem* getPosition(unsigned int position); + virtual void gotoPosition(unsigned int position); //! \brief proceeds to the next item - virtual mgContentItem* skipFwd(); + virtual void skipFwd(); //! \brief goes back to the previous item - virtual mgContentItem* skipBack(); + virtual void skipBack(); //! \brief obtain the next item without skipping the current position virtual mgContentItem* sneakNext(); - virtual bool storePlaylist()=0; + virtual bool storePlaylist() = 0; //! \brief export the playlist in m3u format virtual bool exportM3U( std::string m3u_file ); diff --git a/vdr_player.c b/vdr_player.c index 36a712b..bc790f4 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -2,12 +2,12 @@ * \file vdr_player.c * \brief A generic PCM player for a VDR media plugin (muggle) * - * \version $Revision: 1.5 $ - * \date $Date: 2004/07/26 20:03:00 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/07/26 22:20:55 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * - * $Id: vdr_player.c,v 1.5 2004/07/26 20:03:00 lvw Exp $ + * $Id: vdr_player.c,v 1.6 2004/07/26 22:20:55 lvw Exp $ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -707,7 +707,8 @@ bool mgPCMPlayer::NextFile() { bool res = false; - mgContentItem *newcurr = m_playlist->skipFwd(); + m_playlist->skipFwd(); + mgContentItem *newcurr = m_playlist->getCurrent(); if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) { @@ -722,7 +723,8 @@ bool mgPCMPlayer::PrevFile(void) { bool res = false; - mgContentItem *newcurr = m_playlist->skipBack(); + m_playlist->skipBack(); + mgContentItem *newcurr = m_playlist->getCurrent(); if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) { @@ -803,9 +805,10 @@ void mgPCMPlayer::Backward(void) void mgPCMPlayer::Goto( int index, bool still ) { - mgContentItem *next = m_playlist->getPosition( index-1 ); + m_playlist->gotoPosition( index-1 ); + mgContentItem *next = m_playlist->getCurrent(); - if( next != &(mgContentItem::UNDEFINED) ) //invalid + if( next && next != &(mgContentItem::UNDEFINED) ) //invalid { Lock(); StopPlay(); @@ -1047,6 +1050,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) /************************************************************ * * $Log: vdr_player.c,v $ + * Revision 1.6 2004/07/26 22:20:55 lvw + * Reworked playlist indexing + * * Revision 1.5 2004/07/26 20:03:00 lvw * Bug in initalizing playlist removed * -- cgit v1.2.3 From ea18c6fab4df48664bc78add2cd0f5da529cc860 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 27 Jul 2004 06:57:35 +0000 Subject: Inserted missing break git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@109 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index b663a9c..11f7168 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.25 $ - * \date $Date: 2004/07/26 20:02:38 $ + * \version $Revision: 1.26 $ + * \date $Date: 2004/07/27 06:57:35 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: LarsAC $ * - * $Id: vdr_menu.c,v 1.25 2004/07/26 20:02:38 lvw Exp $ + * $Id: vdr_menu.c,v 1.26 2004/07/27 06:57:35 LarsAC Exp $ */ #include @@ -741,7 +741,6 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { LoadPlaylist(); Interface->Flush(); - // jump to playlist view from here? } break; case 1: @@ -782,7 +781,7 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) // confirmation Interface->Status( "Entry removed" ); Interface->Flush(); - } + } break; case 5: { string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), m_current_playlist->getListname().c_str() ); @@ -910,6 +909,9 @@ void mgMainMenu::Play(mgPlaylist *plist) /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.26 2004/07/27 06:57:35 LarsAC + * Inserted missing break + * * Revision 1.25 2004/07/26 20:02:38 lvw * Bug in handling playlist menu removed * -- cgit v1.2.3 From 72967d5bbec4325e6cbe47699fb35d23a254abd8 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Tue, 27 Jul 2004 07:06:00 +0000 Subject: Added file for doxygen documentation generation git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@110 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 21 +- muggle.doxygen | 1078 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1091 insertions(+), 8 deletions(-) create mode 100644 muggle.doxygen diff --git a/TODO b/TODO index e0b1ea5..6f9afa0 100644 --- a/TODO +++ b/TODO @@ -14,17 +14,18 @@ Code polishing ============== - Check for memory leaks - Check for (reasonably) consistent usage of char*/string -- mgDatabase is not used? mgPlayer used what for? +- mgDatabase is not used? +- mgPlayer used what for? - Check for unnecessary log commands -- Generate HTML documentation using doxygen, dotty/gv for state machines +- Generate HTML documentation using doxygen, + use dotty/gv for state machines of player Short term items ================ -- Import existing m3u playlists +- Import existing m3u playlists (in import) -OSD (general) -------------- -- Add separators for first letter (A, B, C, ...) that can be skipped using left/right +OSD in general +-------------- Content ------- @@ -43,13 +44,13 @@ Player Medium term items ================= - - really abstract from specific queries etc. - mgDatabase should abstract database (mySQL) stuff!? OSD --- - Type numbers to enter characters and jump to first title accordingly +- Check whether hierarchical menus are more suitable Content ------- @@ -64,11 +65,12 @@ Player Web interface ------------- -- Look at PHP stuff from c't 11/04 and adapt to schema +- Look at PHP stuff from c't 11/04 and adapt to schema? Long term ideas =============== - daapd integration? +- netjuke integration? - Display arbitrary images while playing music Visions @@ -102,6 +104,9 @@ Already Done ************************************************************ * * $Log: TODO,v $ +* Revision 1.9 2004/07/27 07:06:00 LarsAC +* Added file for doxygen documentation generation +* * Revision 1.8 2004/07/09 13:47:07 LarsAC * Updated next steps * diff --git a/muggle.doxygen b/muggle.doxygen new file mode 100644 index 0000000..f973c51 --- /dev/null +++ b/muggle.doxygen @@ -0,0 +1,1078 @@ +# Doxyfile 1.3.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Muggle media plugin + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = /home/lvw/muggle-plugin + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc + +FILE_PATTERNS = *.h *.c + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output dir. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similiar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO -- cgit v1.2.3 From 68fa54d8979d78bd307bd580027d4f30f39900c1 Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 27 Jul 2004 20:50:54 +0000 Subject: Playlist indexing now working git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@111 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 6 +++--- vdr_menu.c | 14 +++++++++----- vdr_player.c | 11 +++++++---- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index 38d4d0f..83bdbd8 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -2,8 +2,8 @@ * \file mg_playlist.c * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * - * \version $Revision: 1.5 $ - * \date $Date: 2004/07/26 22:20:54 $ + * \version $Revision: 1.6 $ + * \date $Date: 2004/07/27 20:50:54 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * @@ -158,7 +158,7 @@ void mgPlaylist::gotoPosition(unsigned int position) // proceeds to the next item void mgPlaylist::skipFwd() { - if( m_current_idx + 1 <= m_list.size() ) // unless loop mode + if( m_current_idx + 1 < m_list.size() ) // unless loop mode { m_current_idx ++; } diff --git a/vdr_menu.c b/vdr_menu.c index 11f7168..fd02c11 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -2,12 +2,12 @@ * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.26 $ - * \date $Date: 2004/07/27 06:57:35 $ + * \version $Revision: 1.27 $ + * \date $Date: 2004/07/27 20:50:54 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author: lvw $ * - * $Id: vdr_menu.c,v 1.26 2004/07/27 06:57:35 LarsAC Exp $ + * $Id: vdr_menu.c,v 1.27 2004/07/27 20:50:54 lvw Exp $ */ #include @@ -784,7 +784,8 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) } break; case 5: { - string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), m_current_playlist->getListname().c_str() ); + string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), + m_current_playlist->getListname().c_str() ); m_current_playlist->exportM3U( m3u_file ); } break; case 6: @@ -909,6 +910,9 @@ void mgMainMenu::Play(mgPlaylist *plist) /************************************************************ * * $Log: vdr_menu.c,v $ + * Revision 1.27 2004/07/27 20:50:54 lvw + * Playlist indexing now working + * * Revision 1.26 2004/07/27 06:57:35 LarsAC * Inserted missing break * diff --git a/vdr_player.c b/vdr_player.c index bc790f4..105072b 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -2,12 +2,12 @@ * \file vdr_player.c * \brief A generic PCM player for a VDR media plugin (muggle) * - * \version $Revision: 1.6 $ - * \date $Date: 2004/07/26 22:20:55 $ + * \version $Revision: 1.7 $ + * \date $Date: 2004/07/27 20:50:54 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * - * $Id: vdr_player.c,v 1.6 2004/07/26 22:20:55 lvw Exp $ + * $Id: vdr_player.c,v 1.7 2004/07/27 20:50:54 lvw Exp $ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -247,7 +247,7 @@ void mgPCMPlayer::Activate(bool on) Lock(); m_playlist->initialize(); - NextFile(); + m_current = m_playlist->getCurrent(); Play(); Unlock(); @@ -1050,6 +1050,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) /************************************************************ * * $Log: vdr_player.c,v $ + * Revision 1.7 2004/07/27 20:50:54 lvw + * Playlist indexing now working + * * Revision 1.6 2004/07/26 22:20:55 lvw * Reworked playlist indexing * -- cgit v1.2.3 From 584267906a160cdb699b88da0f5e5e495052ab44 Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 29 Jul 2004 05:58:14 +0000 Subject: TODO list changed git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@112 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO b/TODO index 6f9afa0..06a91e3 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,7 @@ Code polishing - Check for unnecessary log commands - Generate HTML documentation using doxygen, use dotty/gv for state machines of player +- Check compatibility for 1.3.12 Short term items ================ @@ -104,6 +105,9 @@ Already Done ************************************************************ * * $Log: TODO,v $ +* Revision 1.10 2004/07/29 05:58:14 lvw +* TODO list changed +* * Revision 1.9 2004/07/27 07:06:00 LarsAC * Added file for doxygen documentation generation * -- cgit v1.2.3 From 37ce84e62b46619cc0bfc5733159c07b88556754 Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 29 Jul 2004 06:18:07 +0000 Subject: Added todo entries git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@113 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 4 ++ gd_content_interface.h | 22 ++++++--- mg_media.c | 124 ++++++++++++++++++++++++++----------------------- muggle.c | 8 ++-- 4 files changed, 90 insertions(+), 68 deletions(-) diff --git a/TODO b/TODO index 06a91e3..509d3cb 100644 --- a/TODO +++ b/TODO @@ -13,6 +13,7 @@ Testing/bugs Code polishing ============== - Check for memory leaks + - Why do filters use pointers? - Check for (reasonably) consistent usage of char*/string - mgDatabase is not used? - mgPlayer used what for? @@ -105,6 +106,9 @@ Already Done ************************************************************ * * $Log: TODO,v $ +* Revision 1.11 2004/07/29 06:18:07 lvw +* Added todo entries +* * Revision 1.10 2004/07/29 05:58:14 lvw * TODO list changed * diff --git a/gd_content_interface.h b/gd_content_interface.h index 491e4b9..d564f7d 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -3,10 +3,10 @@ * \brief Data objects for content (e.g. mp3 files, movies) * for the vdr muggle plugin database * - * \version $Revision: 1.8 $ - * \date $Date: 2004/07/06 00:20:51 $ + * \version $Revision: 1.9 $ + * \date $Date: 2004/07/29 06:17:50 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: MountainMan $ + * \author Responsible author: $Author: lvw $ * * Declares main classes for content items and interfaces to SQL databases * @@ -65,8 +65,9 @@ class gdFilterSets : public mgFilterSets * the active filter sets. * * \param viewPort - after call, contains the index of the appropriate default view in + * \todo should viewPort be a reference? */ - virtual std::string computeRestriction(int *viewPrt); + virtual std::string computeRestriction(int *viewPrt); }; @@ -77,15 +78,19 @@ class gdFilterSets : public mgFilterSets * The object is initially created with a database identifier. * The actual data is only read when a content field is accessed for * the first time. For subsequent access, cached values are used. + * + * \todo does each track node need a reference to the database? + * maybe we can use a static db handle in mgDatabase? + * \ */ class mgGdTrack : public mgContentItem { private: - /*! - * \brief the database in which the track resides - * */ + /*! + * \brief the database in which the track resides + * */ MYSQL m_db; /*! @@ -306,6 +311,9 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.9 2004/07/29 06:17:50 lvw + * Added todo entries + * * Revision 1.8 2004/07/06 00:20:51 MountainMan * loading and saving playlists * diff --git a/mg_media.c b/mg_media.c index 18e44f3..96dc3f7 100644 --- a/mg_media.c +++ b/mg_media.c @@ -1,8 +1,8 @@ /*! \file mg_media.c * \brief Top level access to media in vdr plugin muggle * - * \version $Revision: 1.13 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \version $Revision: 1.14 $ + * \date $Date: 2004/07/29 06:17:40 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ */ @@ -245,55 +245,61 @@ mgSelectionTreeNode* mgMedia::getSelectionRoot() mgPlaylist* mgMedia::createTemporaryPlaylist() { string tmpname = "current"; - return loadPlaylist(tmpname); + return loadPlaylist( tmpname ); } /*! - ******************************************************************* - * \brief - ********************************************************************/ + * \brief Load a playlist from the database + * + */ mgPlaylist* mgMedia::loadPlaylist(string name) { mgPlaylist *list; - switch(m_mediatype) - { - case GD_MP3: - list = new GdPlaylist(name, m_db); - list->setDisplayColumns(getDefaultCols()); - return list; + switch( m_mediatype ) + { + case GD_MP3: + { + list = new GdPlaylist(name, m_db); + list->setDisplayColumns(getDefaultCols()); + + return list; + } break; } - mgError("implementation Error"); // we should never get here + mgError("Implementation error: Unknown media type"); // we should never get here return NULL; } /*! - ******************************************************************* - * \brief - ********************************************************************/ + * \brief Obtain a list of stored playlists + */ vector *mgMedia::getStoredPlaylists() { switch(m_mediatype) { case GD_MP3: - return GdGetStoredPlaylists(m_db); + { + return GdGetStoredPlaylists( m_db ); + } break; } mgError("implementation Error"); // we should never get here return new vector(); } /*! - ******************************************************************* - * \brief - ********************************************************************/ + * \brief obtain the indices of columns which are presented by default + */ vector mgMedia::getDefaultCols() { vector cols; switch(m_mediatype) { case GD_MP3: + { cols.push_back(1); // artist cols.push_back(0); // track + return cols; + } break; } mgError("implementation Error"); // we should never get here @@ -301,9 +307,8 @@ vector mgMedia::getDefaultCols() } /*! - ******************************************************************* - * \brief - ********************************************************************/ + * \brief + */ mgTracklist* mgMedia::getTracks() { mgTracklist *tracks; @@ -320,18 +325,18 @@ mgTracklist* mgMedia::getTracks() } /*! - ******************************************************************* * \brief creates FiliterSetObject for the selected media type * and activates set n (if available) - ********************************************************************/ + */ void mgMedia::initFilterSet(int num) { - switch(m_mediatype) + switch(m_mediatype) { - case GD_MP3: - m_filters = new gdFilterSets(); - m_filters->select(num); - break; + case GD_MP3: + { + m_filters = new gdFilterSets(); + m_filters->select(num); + } break; } } @@ -351,56 +356,54 @@ vector *mgMedia::getActiveFilters() } /*! - ******************************************************************* * \brief returns title of the active filter set - ********************************************************************/ + */ string mgMedia::getActiveFilterTitle() { switch(m_mediatype) { case GD_MP3: - if(!m_filters) { - mgError("ImplementationError:getActiveFilterTitle m_filters == NULL"); - } - return m_filters->getTitle(); + if( !m_filters ) + { + mgError("ImplementationError:getActiveFilterTitle m_filters == NULL"); + } + return m_filters->getTitle(); + } break; } return ""; } /*! - ******************************************************************* * \brief proceeds to the next filter set in a cirlucar fashion - ********************************************************************/ + */ void mgMedia::nextFilterSet() { if(!m_filters) - { - mgError("ImplementationError: nextFilterSet m_filters == NULL"); - } + { + mgError("ImplementationError: nextFilterSet m_filters == NULL"); + } m_filters->nextSet(); } /*! - ******************************************************************* * \brief clears the current filter values and restores defaults - ********************************************************************/ + */ void mgMedia::clearActiveFilter() { - if(!m_filters) - { - mgError("ImplementationError: clearActiveFilter m_filters == NULL"); - } + if( !m_filters ) + { + mgError("ImplementationError: clearActiveFilter m_filters == NULL"); + } m_filters->clear(); } /*! - ******************************************************************* * \brief Applies the active filter set and returns a root node for the * selection in the default view for this filter set - ********************************************************************/ -mgSelectionTreeNode *mgMedia::applyActiveFilter() + */ +mgSelectionTreeNode* mgMedia::applyActiveFilter() { int view; GdTreeNode* node; @@ -408,15 +411,17 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() switch(m_mediatype) { case GD_MP3: - if(!m_filters) { - mgError("ImplementationError: applyActiveFilter() m_filters == NULL"); - } - m_filters->accept(); - m_sql_filter = m_filters->computeRestriction(&view); - node = new GdTreeNode(m_db, view, m_sql_filter); - node->expand(); - return node->getChildren()[0]; + if(!m_filters) + { + mgError("ImplementationError: applyActiveFilter() m_filters == NULL"); + } + m_filters->accept(); + m_sql_filter = m_filters->computeRestriction(&view); + node = new GdTreeNode(m_db, view, m_sql_filter); + node->expand(); + return node->getChildren()[0]; + } break; } return NULL; } @@ -424,6 +429,9 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter() /* -------------------- begin CVS log --------------------------------- * $Log: mg_media.c,v $ + * Revision 1.14 2004/07/29 06:17:40 lvw + * Added todo entries + * * Revision 1.13 2004/05/28 15:29:18 lvw * Merged player branch back on HEAD branch. * diff --git a/muggle.c b/muggle.c index 6e75705..5a5df21 100644 --- a/muggle.c +++ b/muggle.c @@ -2,12 +2,12 @@ * \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR * - * \version $Revision: 1.9 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \version $Revision: 1.10 $ + * \date $Date: 2004/07/29 06:18:07 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * - * $Id: muggle.c,v 1.9 2004/07/25 21:33:35 lvw Exp $ + * $Id: muggle.c,v 1.10 2004/07/29 06:18:07 lvw Exp $ */ #include @@ -131,6 +131,8 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) } } + // check for GD compatibility and override + return true; } -- cgit v1.2.3 From 622c22ea45654d4741a900fa078bafa947b24177 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 23 Aug 2004 06:36:25 +0000 Subject: Initial version of an import module added git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@114 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 8 ++- mugglei.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++ scripts/gdgentools.pm | 2 +- scripts/gdimport.pl | 3 +- scripts/make-empty-db | 16 ++--- 5 files changed, 186 insertions(+), 13 deletions(-) create mode 100755 mugglei.c diff --git a/gd_content_interface.c b/gd_content_interface.c index 759c32c..d7d157a 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,8 +1,8 @@ /*! \file content_interface.cpp * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugindatabase * - * \version $Revision: 1.24 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \version $Revision: 1.25 $ + * \date $Date: 2004/08/23 06:36:25 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * @@ -791,6 +791,7 @@ int GdPlaylist::insertDataFromSQL() } return nrows; } + bool GdPlaylist::storePlaylist() { vector::iterator iter; @@ -1395,6 +1396,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.25 2004/08/23 06:36:25 lvw + * Initial version of an import module added + * * Revision 1.24 2004/07/25 21:33:35 lvw * Removed bugs in finding track files and playlist indexing. * diff --git a/mugglei.c b/mugglei.c new file mode 100755 index 0000000..6587006 --- /dev/null +++ b/mugglei.c @@ -0,0 +1,170 @@ +#include +using namespace std; + +#include +#include + +MYSQL *db; + +int init_database() +{ + if( mysql_init(db) == NULL ) + { + return -1; + } + + if( mysql_real_connect( db, "host", "user", "pass", + "dbname", 0, NULL, 0 ) == NULL ) + { + return -2; + } + + return 0; +} + +time_t get_fs_modification_time( string filename ) +{ + struct stat *buf = malloc( sizeof( struct stat ) ); + + // yes: obtain modification date for file and db entry + int res = stat( filename.c_str(), buf ); + + time_t res = buf->st_mtime; + free( buf ); + + return res; +} + +time_t get_db_modification_time( long uid ) +{ + char *query; + time_t mt; + + asprintf( &query, "SELECT modification_time FROM tracks WHERE id='%d'", uid ); + + if( mysql_real_query( &db, query, strlen( query ) ) ) + { + printf( mysql_error(&db) ); + exit(1); + } + else + { + MYSQL_RES *result = mysql_store_result( db ); + MYSQL_ROW row = mysql_fetch_row( result ); + + string mod_time = row[0]; + mt = (time_t) atol( mod_time.c_str() ); + } + + free( query ); + return mt; +} + +long find_file_in_database( string filename ) +{ + char *query; + + asprintf( &query, "SELECT id FROM tracks WHERE mp3file='%s'", filename.c_str() ); + + if( mysql_real_query( &demo_db, query, strlen( query ) ) ) + { + printf( mysql_error(&demo_db) ); + exit(1); + } + else + { + MYSQL_RES *result = mysql_store_result( db ); + row = mysql_fetch_row( result ); + + // obtain ID and return + return atol( row[0] ); + } + + free( query ); +} + +void evaluate_file( string filename ) +{ + if( 0 == init_database() ) + { + // is filename stored in database? + long uid = find_file_in_database( filename ); + if( uid >= 0 ) + { + // determine modification times in database and on filesystem + time_t db_time = get_db_modification_time( uid ); + time_t fs_time = get_fs_modification_time( filename ); + + if( db_time > fs_time ) + { + // db is newer: update id3 tags from db + update_tags( uid, filename ); + } + else + { + // file is newer: update db from id3 tags + update_db( uid, filename ); + } + } + else + { + // not in db yet: import file + import_file( filename ); + } + } +} + +void update_db( long uid, string filename ) +{ + char title[1024], album[1024]; + ID3_Tag filetag( filename.c_str() ); + + // obtain album value + ID3_Frame* album_frame = filetag.Find( ID3FID_ALBUM ); + if( NULL != album_frame ) + { + album_frame->Field ( ID3FN_TEXT ).Get ( album, 1024 ); + } + + // obtain title value + ID3_Frame* title_frame = filetag.Find( ID3FID_TITLE ); + if( NULL != title_frame ) + { + title_frame->Field ( ID3FN_TEXT ).Get ( title, 1024 ); + } + + // obtain year value (ID3FID_YEAR) + + // obtain artist value (ID3FID_LEADARTIST) + + // see what else may be needed later + + // finally update the database + + // update tracks table + mgSqlWriteQuery( db, + "UPDATE tracks " + "SET artist=\"%s\", title=\"%s\", year=%d, rating=%d " + "WHERE id=%d", + artist, title, + m_year, m_rating, uid ); + + // obtain associated album or create + + +} + +void update_tags( long uid, string filename ) +{ + +} + +void import_file( string filename ) +{ + +} + +void main( int argc, char *argv[] ) +{ + +} diff --git a/scripts/gdgentools.pm b/scripts/gdgentools.pm index 3522302..aadc5c7 100755 --- a/scripts/gdgentools.pm +++ b/scripts/gdgentools.pm @@ -12,7 +12,7 @@ package gdgentools; ### General tool routines -use lib '/home/andi/muggle/import'; +use lib '/home/lvw/Development/muggle-import/scripts'; use gdparams; use IO::Socket; diff --git a/scripts/gdimport.pl b/scripts/gdimport.pl index 6c8f548..438b07c 100755 --- a/scripts/gdimport.pl +++ b/scripts/gdimport.pl @@ -16,7 +16,8 @@ # inclusion of mp3 tracks and a proper update of the database. -use lib '/home/andi/muggle/import'; +use lib '/home/lvw/Development/muggle-plugin/scripts'; +#use lib '/home/andi/muggle/import'; use gdparams; use gdgentools; use gddb; diff --git a/scripts/make-empty-db b/scripts/make-empty-db index 7157539..59d2c21 100755 --- a/scripts/make-empty-db +++ b/scripts/make-empty-db @@ -1,24 +1,22 @@ #!/bin/sh -export SCRIPTDIR=/home/andi/muggle/import +export SCRIPTDIR=/home/lvw/Development/muggle-plugin/scripts cd $SCRIPTDIR echo "creating db" -mysql < createdb.mysql +mysql -u root < createdb.mysql echo "creating tables" -mysql < $SCRIPTDIR/createtables.mysql +mysql -u root < $SCRIPTDIR/createtables.mysql echo "reading genres" -echo " use GiantDisc; load data local infile '$SCRIPTDIR/genres.txt' into table genre;"| mysql --local-infile=1 - +echo " use GiantDisc; load data local infile '$SCRIPTDIR/genres.txt' into table genre;"| mysql -u root --local-infile=1 echo "reading languages" -echo "use GiantDisc; load data local infile '$SCRIPTDIR/languages.txt' into table language;" | mysql --local-infile=1 +echo "use GiantDisc; load data local infile '$SCRIPTDIR/languages.txt' into table language;" | mysql -u root --local-infile=1 echo "reading musictypes" -echo "use GiantDisc; load data local infile '$SCRIPTDIR/musictypes.txt' into table language;" | mysql --local-infile=1 - +echo "use GiantDisc; load data local infile '$SCRIPTDIR/musictypes.txt' into table language;" | mysql -u root --local-infile=1 echo "reading sources" -echo "use GiantDisc; load data local infile '$SCRIPTDIR/sources.txt' into table language;" | mysql --local-infile=1 +echo "use GiantDisc; load data local infile '$SCRIPTDIR/sources.txt' into table language;" | mysql -u root --local-infile=1 -- cgit v1.2.3 From 98ee6a57138869a8a0756fb3b8b5419258da550f Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 23 Aug 2004 15:22:40 +0000 Subject: Extended import tool git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@115 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 220 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 136 insertions(+), 84 deletions(-) diff --git a/mugglei.c b/mugglei.c index 6587006..b540834 100755 --- a/mugglei.c +++ b/mugglei.c @@ -1,11 +1,18 @@ #include using namespace std; -#include +#include + +#include +#include #include +#include "mg_tools.h" + MYSQL *db; +char host[255], user[32], pass[32], dbname[32]; + int init_database() { if( mysql_init(db) == NULL ) @@ -13,8 +20,8 @@ int init_database() return -1; } - if( mysql_real_connect( db, "host", "user", "pass", - "dbname", 0, NULL, 0 ) == NULL ) + if( mysql_real_connect( db, host, user, pass, + dbname, 0, NULL, 0 ) == NULL ) { return -2; } @@ -24,63 +31,146 @@ int init_database() time_t get_fs_modification_time( string filename ) { - struct stat *buf = malloc( sizeof( struct stat ) ); + struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); - // yes: obtain modification date for file and db entry - int res = stat( filename.c_str(), buf ); + // yes: obtain modification date for file and db entry + int statres = stat( filename.c_str(), buf ); - time_t res = buf->st_mtime; + time_t mod = buf->st_mtime; free( buf ); - return res; + return mod; } time_t get_db_modification_time( long uid ) { - char *query; time_t mt; - asprintf( &query, "SELECT modification_time FROM tracks WHERE id='%d'", uid ); - - if( mysql_real_query( &db, query, strlen( query ) ) ) - { - printf( mysql_error(&db) ); - exit(1); - } - else - { - MYSQL_RES *result = mysql_store_result( db ); - MYSQL_ROW row = mysql_fetch_row( result ); - - string mod_time = row[0]; - mt = (time_t) atol( mod_time.c_str() ); - } + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id='%d'", uid ); + MYSQL_ROW row = mysql_fetch_row( result ); + + string mod_time = row[0]; + mt = (time_t) atol( mod_time.c_str() ); - free( query ); return mt; } long find_file_in_database( string filename ) { - char *query; + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file='%s'", filename.c_str() ); + MYSQL_ROW row = mysql_fetch_row( result ); + + // obtain ID and return + return atol( row[0] ); +} + +// read tags from the mp3 file and store them into the corresponding database entry +void update_db( long uid, string filename ) +{ + char title[1024], album[1024], year[5], artist[1024], cddbid[20], trackno[5]; + ID3_Tag filetag( filename.c_str() ); + + // obtain album value + ID3_Frame* album_frame = filetag.Find( ID3FID_ALBUM ); + if( NULL != album_frame ) + { + album_frame->Field ( ID3FN_TEXT ).Get ( album, 1024 ); + } + else + { + strcpy( "Unassigned", album ); + } + + // obtain title value + ID3_Frame* title_frame = filetag.Find( ID3FID_TITLE ); + if( NULL != title_frame ) + { + title_frame->Field ( ID3FN_TEXT ).Get ( title, 1024 ); + } + else + { + strcpy( "Unknown title", title ); + } - asprintf( &query, "SELECT id FROM tracks WHERE mp3file='%s'", filename.c_str() ); + // obtain year value (ID3FID_YEAR) + ID3_Frame* year_frame = filetag.Find( ID3FID_YEAR ); + if( NULL != year_frame ) + { + year_frame->Field ( ID3FN_TEXT ).Get ( year, 5 ); + } + else + { + strcpy( "0", title ); + } - if( mysql_real_query( &demo_db, query, strlen( query ) ) ) - { - printf( mysql_error(&demo_db) ); - exit(1); + // obtain artist value (ID3FID_LEADARTIST) + ID3_Frame* artist_frame = filetag.Find( ID3FID_LEADARTIST ); + if( NULL != artist_frame ) + { + artist_frame->Field ( ID3FN_TEXT ).Get ( artist, 5 ); } else { - MYSQL_RES *result = mysql_store_result( db ); - row = mysql_fetch_row( result ); + strcpy( "Unknown artist", artist ); + } - // obtain ID and return - return atol( row[0] ); + // obtain track number ID3FID_TRACKNUM + ID3_Frame* trackno_frame = filetag.Find( ID3FID_TRACKNUM ); + if( NULL != trackno_frame ) + { + trackno_frame->Field ( ID3FN_TEXT ).Get ( trackno, 5 ); + } + else + { + strcpy( "0", trackno ); } - free( query ); + // obtain associated album or create + if( NULL == album_frame ) + { // no album found, associate with default album + strcpy( cddbid, "0000unknown0000" ); + } + else + { // album tag found, associate or create + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title='%s' AND artist='%s'", + album, artist ); + MYSQL_ROW row = mysql_fetch_row( result ); + + // num rows == 0 ? + int nrows = mysql_num_rows(result); + if( nrows == 0 ) + { + // create new album entry + long id = random(); + snprintf( cddbid, 19, "%d-%s", id, album ); + + mgSqlWriteQuery( db, "INSERT INTO album VALUES ('%s', '%s', '%s')", artist, title, cddbid ); + } + else + { // use first album found as source id for the track + strncpy( cddbid, row[0], 19 ); + } + } + + // TODO: genre(s), CD identifier (?), playcounter, popularimeter?, volume adjustment + + // finally update the database + + // update tracks table + if( uid > 0 ) + { // the entry is known to exist already, hence update it + mgSqlWriteQuery( db, "UPDATE tracks SET artist='%s', title='%s', year='%s', sourceid='%s'" + "WHERE id=%d", artist, title, year, cddbid, uid ); + } + else + { // the entry does not exist, create it + mgSqlWriteQuery( db, "INSERT INTO tracks VALUES ('%s', '%s', NULL, NULL, '%s', NULL, NULL, NULL, NULL, NULL, '%s', '%s', '%s'", artist, title, year, cddbid, trackno, filename.c_str() ); + } +} + +void update_tags( long uid, string filename ) +{ + } void evaluate_file( string filename ) @@ -109,62 +199,24 @@ void evaluate_file( string filename ) else { // not in db yet: import file - import_file( filename ); + update_db( -1, filename ); } } } -void update_db( long uid, string filename ) +int main( int argc, char *argv[] ) { - char title[1024], album[1024]; - ID3_Tag filetag( filename.c_str() ); + int res = init_database(); - // obtain album value - ID3_Frame* album_frame = filetag.Find( ID3FID_ALBUM ); - if( NULL != album_frame ) - { - album_frame->Field ( ID3FN_TEXT ).Get ( album, 1024 ); + if( !res ) + { + update_db( 0, string( argv[1] ) ); } - - // obtain title value - ID3_Frame* title_frame = filetag.Find( ID3FID_TITLE ); - if( NULL != title_frame ) - { - title_frame->Field ( ID3FN_TEXT ).Get ( title, 1024 ); + else + { + printf( "Database initialization failed. Exiting.\n" ); } - // obtain year value (ID3FID_YEAR) - - // obtain artist value (ID3FID_LEADARTIST) - - // see what else may be needed later - - // finally update the database - - // update tracks table - mgSqlWriteQuery( db, - "UPDATE tracks " - "SET artist=\"%s\", title=\"%s\", year=%d, rating=%d " - "WHERE id=%d", - artist, title, - m_year, m_rating, uid ); - - // obtain associated album or create - - -} - -void update_tags( long uid, string filename ) -{ - -} - -void import_file( string filename ) -{ - + return res; } -void main( int argc, char *argv[] ) -{ - -} -- cgit v1.2.3 From cc31b63b858a1cb6902fedd6b882cb62e8ad7ba4 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 23 Aug 2004 15:30:13 +0000 Subject: File tag updates currently disabled git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@116 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mugglei.c b/mugglei.c index b540834..3aee76a 100755 --- a/mugglei.c +++ b/mugglei.c @@ -181,6 +181,8 @@ void evaluate_file( string filename ) long uid = find_file_in_database( filename ); if( uid >= 0 ) { + // currently only update database, do not consider writing changes from the db back + /* // determine modification times in database and on filesystem time_t db_time = get_db_modification_time( uid ); time_t fs_time = get_fs_modification_time( filename ); @@ -195,6 +197,9 @@ void evaluate_file( string filename ) // file is newer: update db from id3 tags update_db( uid, filename ); } + */ + + update_db( uid, filename ); } else { -- cgit v1.2.3 From bd5912cc0c84dec1f6136dbc3b8965587f8cf664 Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 24 Aug 2004 22:32:44 +0000 Subject: Updated import git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@117 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/mugglei.c b/mugglei.c index 3aee76a..d241482 100755 --- a/mugglei.c +++ b/mugglei.c @@ -11,11 +11,12 @@ using namespace std; MYSQL *db; -char host[255], user[32], pass[32], dbname[32]; +char *host, *user, *pass, *dbname; int init_database() { - if( mysql_init(db) == NULL ) + db = mysql_init(NULL); + if( db == NULL ) { return -1; } @@ -70,17 +71,24 @@ void update_db( long uid, string filename ) char title[1024], album[1024], year[5], artist[1024], cddbid[20], trackno[5]; ID3_Tag filetag( filename.c_str() ); + printf( "ID3 tag created.\n" ); + // obtain album value ID3_Frame* album_frame = filetag.Find( ID3FID_ALBUM ); + printf( "Album frame obtained.\n" ); if( NULL != album_frame ) { - album_frame->Field ( ID3FN_TEXT ).Get ( album, 1024 ); + album_frame->Field( ID3FN_TEXT ).Get( album, 1024 ); + printf( "Field obtained.\n" ); } else { - strcpy( "Unassigned", album ); + printf( "No album info found.\n" ); + strncpy( album, "Unassigned", 1023 ); } + printf( "Album frame evaluated.\n" ); + // obtain title value ID3_Frame* title_frame = filetag.Find( ID3FID_TITLE ); if( NULL != title_frame ) @@ -89,9 +97,12 @@ void update_db( long uid, string filename ) } else { - strcpy( "Unknown title", title ); + printf( "No title info found.\n" ); + strncpy( title, "Unknown title", 1023); } + printf( "Title frame evaluated.\n" ); + // obtain year value (ID3FID_YEAR) ID3_Frame* year_frame = filetag.Find( ID3FID_YEAR ); if( NULL != year_frame ) @@ -100,9 +111,12 @@ void update_db( long uid, string filename ) } else { - strcpy( "0", title ); + printf( "No year info found.\n" ); + strncpy( title, "0", 1023); } + printf( "Year frame evaluated.\n" ); + // obtain artist value (ID3FID_LEADARTIST) ID3_Frame* artist_frame = filetag.Find( ID3FID_LEADARTIST ); if( NULL != artist_frame ) @@ -111,9 +125,12 @@ void update_db( long uid, string filename ) } else { - strcpy( "Unknown artist", artist ); + printf( "No artist info found.\n" ); + strncpy( artist, "Unknown artist", 1023); } + printf( "Artist frame evaluated.\n" ); + // obtain track number ID3FID_TRACKNUM ID3_Frame* trackno_frame = filetag.Find( ID3FID_TRACKNUM ); if( NULL != trackno_frame ) @@ -122,9 +139,13 @@ void update_db( long uid, string filename ) } else { - strcpy( "0", trackno ); + strncpy( trackno, "0", 5); } + printf( "Trackno frame evaluated.\n" ); + + printf( "ID3 frames/fields read.\n" ); + // obtain associated album or create if( NULL == album_frame ) { // no album found, associate with default album @@ -135,6 +156,7 @@ void update_db( long uid, string filename ) MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title='%s' AND artist='%s'", album, artist ); MYSQL_ROW row = mysql_fetch_row( result ); + printf( "\nAlbum query set.\n" ); // num rows == 0 ? int nrows = mysql_num_rows(result); @@ -211,10 +233,18 @@ void evaluate_file( string filename ) int main( int argc, char *argv[] ) { + host = "127.0.0.1"; + user = "music"; + dbname = "GiantDisc"; + pass = NULL; + int res = init_database(); + printf( "Database initialized.\n" ); + if( !res ) { + printf( "Evaluating %s\n", argv[1] ); update_db( 0, string( argv[1] ) ); } else -- cgit v1.2.3 From cb9b8e3d80006addbfc9002c331fef19d27af6aa Mon Sep 17 00:00:00 2001 From: LarsAC Date: Wed, 25 Aug 2004 15:32:41 +0000 Subject: Corrected SQL syntax errors. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@118 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mugglei.c b/mugglei.c index d241482..2a94c1f 100755 --- a/mugglei.c +++ b/mugglei.c @@ -166,7 +166,7 @@ void update_db( long uid, string filename ) long id = random(); snprintf( cddbid, 19, "%d-%s", id, album ); - mgSqlWriteQuery( db, "INSERT INTO album VALUES ('%s', '%s', '%s')", artist, title, cddbid ); + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES ('%s', '%s', '%s')", artist, title, cddbid ); } else { // use first album found as source id for the track @@ -186,7 +186,7 @@ void update_db( long uid, string filename ) } else { // the entry does not exist, create it - mgSqlWriteQuery( db, "INSERT INTO tracks VALUES ('%s', '%s', NULL, NULL, '%s', NULL, NULL, NULL, NULL, NULL, '%s', '%s', '%s'", artist, title, year, cddbid, trackno, filename.c_str() ); + mgSqlWriteQuery( db, "INSERT INTO tracks (artist,title,year,sourceid,tracknb,mp3file) VALUES ('%s', '%s', '%s', '%s', '%s', '%s')", artist, title, year, cddbid, trackno, filename.c_str() ); } } @@ -233,9 +233,9 @@ void evaluate_file( string filename ) int main( int argc, char *argv[] ) { - host = "127.0.0.1"; - user = "music"; - dbname = "GiantDisc"; + host = "134.130.124.222"; + user = "root"; + dbname = "giantdisc"; pass = NULL; int res = init_database(); -- cgit v1.2.3 From c1034493e9da65c302ffbff0af0e07099f55dfe1 Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 26 Aug 2004 06:32:06 +0000 Subject: Corrected array size error. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@119 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mugglei.c b/mugglei.c index 2a94c1f..bdfd765 100755 --- a/mugglei.c +++ b/mugglei.c @@ -79,7 +79,7 @@ void update_db( long uid, string filename ) if( NULL != album_frame ) { album_frame->Field( ID3FN_TEXT ).Get( album, 1024 ); - printf( "Field obtained.\n" ); + printf( "Field obtained: %s\n", album ); } else { @@ -94,6 +94,7 @@ void update_db( long uid, string filename ) if( NULL != title_frame ) { title_frame->Field ( ID3FN_TEXT ).Get ( title, 1024 ); + printf( "Field obtained: %s\n", title); } else { @@ -108,6 +109,7 @@ void update_db( long uid, string filename ) if( NULL != year_frame ) { year_frame->Field ( ID3FN_TEXT ).Get ( year, 5 ); + printf( "Field obtained: %s\n", year ); } else { @@ -121,7 +123,8 @@ void update_db( long uid, string filename ) ID3_Frame* artist_frame = filetag.Find( ID3FID_LEADARTIST ); if( NULL != artist_frame ) { - artist_frame->Field ( ID3FN_TEXT ).Get ( artist, 5 ); + artist_frame->Field ( ID3FN_TEXT ).Get ( artist, 1023 ); + printf( "Field obtained: \n" ); } else { @@ -136,6 +139,7 @@ void update_db( long uid, string filename ) if( NULL != trackno_frame ) { trackno_frame->Field ( ID3FN_TEXT ).Get ( trackno, 5 ); + printf( "Field obtained: %s\n", trackno ); } else { @@ -233,10 +237,16 @@ void evaluate_file( string filename ) int main( int argc, char *argv[] ) { + /* host = "134.130.124.222"; user = "root"; dbname = "giantdisc"; pass = NULL; + */ + host = "localhost"; + user = "vdr"; + dbname = "GiantDisc"; + pass = NULL; int res = init_database(); -- cgit v1.2.3 From ad853f57b9c341e34454a225e2d58f63c4adbf52 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 26 Aug 2004 11:11:42 +0000 Subject: Import changed to use taglib git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@120 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 36 ++++++++--- mugglei.c | 215 ++++++++++++++++++++++++++------------------------------------ 2 files changed, 118 insertions(+), 133 deletions(-) diff --git a/TODO b/TODO index 509d3cb..c2582ad 100644 --- a/TODO +++ b/TODO @@ -3,12 +3,9 @@ TODO File for Muggle Testing/bugs ============ -- Save/load playlists to database -- Export playlists -- Execute playlist commands -- Check play speed -- Playlists starts with 2nd song -- Check for getSourceFile +- Test execution of playlist commands +- Test mgPCMPlayer::getSourceFile() for GD case (find) +- Test saving/loading playlists to database Code polishing ============== @@ -16,7 +13,13 @@ Code polishing - Why do filters use pointers? - Check for (reasonably) consistent usage of char*/string - mgDatabase is not used? + - should handle a static object with a MySQL connection + - execute queries? + - + - mgPlayer used what for? + - Could save IP/host name and associate last playlist loaded + - Check for unnecessary log commands - Generate HTML documentation using doxygen, use dotty/gv for state machines of player @@ -25,6 +28,7 @@ Code polishing Short term items ================ - Import existing m3u playlists (in import) +- Import genres OSD in general -------------- @@ -47,7 +51,11 @@ Player Medium term items ================= - really abstract from specific queries etc. -- mgDatabase should abstract database (mySQL) stuff!? +- mgDatabase should completely abstract database (mySQL) stuff!? + - read/write queries + - return results (needs a homogeneous representation of results?) + +- Run import/update from within OSD? OSD --- @@ -57,13 +65,19 @@ OSD Content ------- - Save/load filter sets -- Apply filter set as dynamic playlist +- Apply filter set as dynamic playlist (i.e. show filters when loading playlists) +- Handle ratings (increase/decrease during replay) Player ------ - Shuffle: toggle and loop keys. Shuffle only songs not already played, not easy though - Display covers as still pictures - Add flac decoder +- Set ratings + +Import +------ +- Handle updates Web interface ------------- @@ -85,6 +99,9 @@ Visions Already Done ============ +- Check play speed (probably XINE related) +- Playlists starts with 2nd song (DONE) +- Export playlists - Delete selected item - Add command line option for top level directory - prepended to filename in non-GD-mode @@ -106,6 +123,9 @@ Already Done ************************************************************ * * $Log: TODO,v $ +* Revision 1.12 2004/08/26 11:11:42 LarsAC +* Import changed to use taglib +* * Revision 1.11 2004/07/29 06:18:07 lvw * Added todo entries * diff --git a/mugglei.c b/mugglei.c index bdfd765..b6cdb31 100755 --- a/mugglei.c +++ b/mugglei.c @@ -1,12 +1,16 @@ + #include using namespace std; #include - +#include #include -#include + #include +#include +#include + #include "mg_tools.h" MYSQL *db; @@ -47,7 +51,7 @@ time_t get_db_modification_time( long uid ) { time_t mt; - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id='%d'", uid ); + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id=\"%d\"", uid ); MYSQL_ROW row = mysql_fetch_row( result ); string mod_time = row[0]; @@ -58,7 +62,7 @@ time_t get_db_modification_time( long uid ) long find_file_in_database( string filename ) { - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file='%s'", filename.c_str() ); + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", filename.c_str() ); MYSQL_ROW row = mysql_fetch_row( result ); // obtain ID and return @@ -68,130 +72,94 @@ long find_file_in_database( string filename ) // read tags from the mp3 file and store them into the corresponding database entry void update_db( long uid, string filename ) { - char title[1024], album[1024], year[5], artist[1024], cddbid[20], trackno[5]; - ID3_Tag filetag( filename.c_str() ); - - printf( "ID3 tag created.\n" ); - - // obtain album value - ID3_Frame* album_frame = filetag.Find( ID3FID_ALBUM ); - printf( "Album frame obtained.\n" ); - if( NULL != album_frame ) - { - album_frame->Field( ID3FN_TEXT ).Get( album, 1024 ); - printf( "Field obtained: %s\n", album ); - } - else - { - printf( "No album info found.\n" ); - strncpy( album, "Unassigned", 1023 ); - } + // char title[1024], album[1024], year[5], artist[1024], cddbid[20], trackno[5]; + TagLib::String title, album, artist, genre; + uint trackno, year; + string cddbid; - printf( "Album frame evaluated.\n" ); + // ID3_Tag filetag( filename.c_str() ); + TagLib::FileRef f( filename.c_str() ); - // obtain title value - ID3_Frame* title_frame = filetag.Find( ID3FID_TITLE ); - if( NULL != title_frame ) - { - title_frame->Field ( ID3FN_TEXT ).Get ( title, 1024 ); - printf( "Field obtained: %s\n", title); - } - else + if( !f.isNull() && f.tag() ) { - printf( "No title info found.\n" ); - strncpy( title, "Unknown title", 1023); - } - - printf( "Title frame evaluated.\n" ); - - // obtain year value (ID3FID_YEAR) - ID3_Frame* year_frame = filetag.Find( ID3FID_YEAR ); - if( NULL != year_frame ) - { - year_frame->Field ( ID3FN_TEXT ).Get ( year, 5 ); - printf( "Field obtained: %s\n", year ); - } - else - { - printf( "No year info found.\n" ); - strncpy( title, "0", 1023); - } - - printf( "Year frame evaluated.\n" ); - - // obtain artist value (ID3FID_LEADARTIST) - ID3_Frame* artist_frame = filetag.Find( ID3FID_LEADARTIST ); - if( NULL != artist_frame ) - { - artist_frame->Field ( ID3FN_TEXT ).Get ( artist, 1023 ); - printf( "Field obtained: \n" ); - } - else - { - printf( "No artist info found.\n" ); - strncpy( artist, "Unknown artist", 1023); - } - - printf( "Artist frame evaluated.\n" ); - - // obtain track number ID3FID_TRACKNUM - ID3_Frame* trackno_frame = filetag.Find( ID3FID_TRACKNUM ); - if( NULL != trackno_frame ) - { - trackno_frame->Field ( ID3FN_TEXT ).Get ( trackno, 5 ); - printf( "Field obtained: %s\n", trackno ); - } - else - { - strncpy( trackno, "0", 5); - } - - printf( "Trackno frame evaluated.\n" ); - - printf( "ID3 frames/fields read.\n" ); - - // obtain associated album or create - if( NULL == album_frame ) - { // no album found, associate with default album - strcpy( cddbid, "0000unknown0000" ); - } - else - { // album tag found, associate or create - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title='%s' AND artist='%s'", - album, artist ); - MYSQL_ROW row = mysql_fetch_row( result ); - printf( "\nAlbum query set.\n" ); - - // num rows == 0 ? - int nrows = mysql_num_rows(result); - if( nrows == 0 ) - { - // create new album entry - long id = random(); - snprintf( cddbid, 19, "%d-%s", id, album ); + cout << "Evaluating " << filename << endl; + TagLib::Tag *tag = f.tag(); + + // obtain tag information + title = tag->title(); + album = tag->album(); + year = tag->year(); + artist = tag->artist(); + trackno = tag->track(); + genre = tag->genre(); + + // TODO: CD identifier (?), playcounter, popularimeter?, volume adjustment + + // finally update the database + + // obtain associated album or create + if( album == "" ) + { // no album found, associate with default album + cddbid = "0000unknown0000"; + } + else + { // album tag found, associate or create + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"%s\"", + album.toCString(), artist.toCString() ); + MYSQL_ROW row = mysql_fetch_row( result ); + + // num rows == 0 ? + int nrows = mysql_num_rows(result); + if( nrows == 0 ) + { + // create new album entry + long id = random(); + char *buf; + asprintf( &buf, "%d-%s", id, album.toCString() ); + cddbid = buf; + free( buf ); - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES ('%s', '%s', '%s')", artist, title, cddbid ); + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.c_str() ); + } + else + { // use first album found as source id for the track + cddbid = row[0]; + } + } + + // update tracks table + if( uid > 0 ) + { // the entry is known to exist already, hence update it + mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\", sourceid=\"%s\", mp3file=\"%s\"" + "WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.c_str(), filename.c_str(), uid ); } else - { // use first album found as source id for the track - strncpy( cddbid, row[0], 19 ); + { // the entry does not exist, create it + // int t = title.find( "'" ); + // int a = artist.find( "'" ); + mgSqlWriteQuery( db, + "INSERT INTO tracks (artist,title,year,sourceid,tracknb,mp3file)" + " VALUES (\"%s\", \"%s\", %d, \"%s\", %d, \"%s\")", + artist.toCString(), title.toCString(), year, cddbid.c_str(), trackno, filename.c_str() ); + /* + if( !( t > 0 || a > 0 ) ) + { + } + else + { + cout << filename << " skipped." << endl; + cout << "-- TAG --" << endl; + cout << "title - \"" << tag->title() << "\"" << endl; + cout << "artist - \"" << tag->artist() << "\"" << endl; + cout << "album - \"" << tag->album() << "\"" << endl; + cout << "year - \"" << tag->year() << "\"" << endl; + cout << "comment - \"" << tag->comment() << "\"" << endl; + cout << "track - \"" << tag->track() << "\"" << endl; + cout << "genre - \"" << tag->genre() << "\"" << endl; + } + */ } } - - // TODO: genre(s), CD identifier (?), playcounter, popularimeter?, volume adjustment - - // finally update the database - - // update tracks table - if( uid > 0 ) - { // the entry is known to exist already, hence update it - mgSqlWriteQuery( db, "UPDATE tracks SET artist='%s', title='%s', year='%s', sourceid='%s'" - "WHERE id=%d", artist, title, year, cddbid, uid ); - } - else - { // the entry does not exist, create it - mgSqlWriteQuery( db, "INSERT INTO tracks (artist,title,year,sourceid,tracknb,mp3file) VALUES ('%s', '%s', '%s', '%s', '%s', '%s')", artist, title, year, cddbid, trackno, filename.c_str() ); - } } void update_tags( long uid, string filename ) @@ -237,24 +205,21 @@ void evaluate_file( string filename ) int main( int argc, char *argv[] ) { - /* host = "134.130.124.222"; user = "root"; dbname = "giantdisc"; pass = NULL; - */ + /* host = "localhost"; user = "vdr"; dbname = "GiantDisc"; pass = NULL; + */ int res = init_database(); - printf( "Database initialized.\n" ); - if( !res ) { - printf( "Evaluating %s\n", argv[1] ); update_db( 0, string( argv[1] ) ); } else -- cgit v1.2.3 From e501021d03abe4e55dc87e3af13d66488eb54337 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 26 Aug 2004 11:20:53 +0000 Subject: Corrected null entry. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@121 e10066b5-e1e2-0310-b819-94efdf66514b --- scripts/genres.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/genres.txt b/scripts/genres.txt index 2a734f0..43124e9 100755 --- a/scripts/genres.txt +++ b/scripts/genres.txt @@ -221,4 +221,4 @@ ndo \N Russian ndp \N South/Cent. American ndq \N Spain ndr 113 Tango - NULl + NULL -- cgit v1.2.3 From 02bd167da851b05eb5934740ba94c52cf3f25b1d Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 26 Aug 2004 15:04:36 +0000 Subject: Use proper random numbers git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@122 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 52 +++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/mugglei.c b/mugglei.c index b6cdb31..191bebb 100755 --- a/mugglei.c +++ b/mugglei.c @@ -5,6 +5,7 @@ using namespace std; #include #include #include +#include #include @@ -73,16 +74,15 @@ long find_file_in_database( string filename ) void update_db( long uid, string filename ) { // char title[1024], album[1024], year[5], artist[1024], cddbid[20], trackno[5]; - TagLib::String title, album, artist, genre; + TagLib::String title, album, artist, genre, cddbid; uint trackno, year; - string cddbid; // ID3_Tag filetag( filename.c_str() ); TagLib::FileRef f( filename.c_str() ); if( !f.isNull() && f.tag() ) { - cout << "Evaluating " << filename << endl; + // cout << "Evaluating " << filename << endl; TagLib::Tag *tag = f.tag(); // obtain tag information @@ -116,10 +116,10 @@ void update_db( long uid, string filename ) long id = random(); char *buf; asprintf( &buf, "%d-%s", id, album.toCString() ); - cddbid = buf; + cddbid = TagLib::String( buf ).substr( 0, 20 ); free( buf ); - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.c_str() ); + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); } else { // use first album found as source id for the track @@ -131,33 +131,26 @@ void update_db( long uid, string filename ) if( uid > 0 ) { // the entry is known to exist already, hence update it mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\", sourceid=\"%s\", mp3file=\"%s\"" - "WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.c_str(), filename.c_str(), uid ); + "WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.toCString(), filename.c_str(), uid ); } else { // the entry does not exist, create it // int t = title.find( "'" ); // int a = artist.find( "'" ); - mgSqlWriteQuery( db, - "INSERT INTO tracks (artist,title,year,sourceid,tracknb,mp3file)" - " VALUES (\"%s\", \"%s\", %d, \"%s\", %d, \"%s\")", - artist.toCString(), title.toCString(), year, cddbid.c_str(), trackno, filename.c_str() ); - /* - if( !( t > 0 || a > 0 ) ) - { - } - else - { - cout << filename << " skipped." << endl; - cout << "-- TAG --" << endl; - cout << "title - \"" << tag->title() << "\"" << endl; - cout << "artist - \"" << tag->artist() << "\"" << endl; - cout << "album - \"" << tag->album() << "\"" << endl; - cout << "year - \"" << tag->year() << "\"" << endl; - cout << "comment - \"" << tag->comment() << "\"" << endl; - cout << "track - \"" << tag->track() << "\"" << endl; - cout << "genre - \"" << tag->genre() << "\"" << endl; - } - */ + mgSqlWriteQuery( db, + "INSERT INTO tracks (artist,title,year,sourceid,tracknb,mp3file)" + " VALUES (\"%s\", \"%s\", %d, \"%s\", %d, \"%s\")", + artist.toCString(), title.toCString(), year, cddbid.toCString(), trackno, filename.c_str() ); + /* + cout << "-- TAG --" << endl; + cout << "title - \"" << tag->title() << "\"" << endl; + cout << "artist - \"" << tag->artist() << "\"" << endl; + cout << "album - \"" << tag->album() << "\"" << endl; + cout << "year - \"" << tag->year() << "\"" << endl; + cout << "comment - \"" << tag->comment() << "\"" << endl; + cout << "track - \"" << tag->track() << "\"" << endl; + cout << "genre - \"" << tag->genre() << "\"" << endl; + */ } } } @@ -216,6 +209,11 @@ int main( int argc, char *argv[] ) pass = NULL; */ + struct timeval tv; + struct timezone tz; + gettimeofday( &tv, &tz ); + srandom( tv.tv_usec ); + int res = init_database(); if( !res ) -- cgit v1.2.3 From dbcee365b07ddd99a8b0d6423019bc6412d03690 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Fri, 27 Aug 2004 15:19:34 +0000 Subject: Changed formatting and documentation git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@123 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 880 ++++++++++++++++++++++++++----------------------- gd_content_interface.h | 259 +++++++-------- 2 files changed, 589 insertions(+), 550 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index d7d157a..06dd35a 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,17 +1,17 @@ -/*! \file content_interface.cpp - * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugindatabase +/*! \file gd_content_interface.c + * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin * - * \version $Revision: 1.25 $ - * \date $Date: 2004/08/23 06:36:25 $ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \version $Revision: 1.26 $ + * \date $Date: 2004/08/27 15:19:34 $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: LarsAC $ * - * Implements main classes of for content items and interfaces to SQL databases + * Implements main classes of for content items and interfaces to SQL databases * - * This file implements the following classes - * - GdPlaylist a playlist - * - mgGdTrack a single track (content item). e.g. an mp3 file - * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * This file implements the following classes + * - GdPlaylist a playlist + * - mgGdTrack a single track (content item). e.g. an mp3 file + * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) * */ /*******************************************************************/ @@ -31,27 +31,24 @@ using namespace std; #define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax #define DUMMY -// non-member function -int GdInitDatabase(MYSQL *db) +int GdInitDatabase( MYSQL *db ) { - if( mysql_init(db) == NULL ) + if( mysql_init(db) == NULL ) { - return -1; + return -1; } - - // if(mysql_real_connect( db, "localhost", "root", "", - // "GiantDisc2", 0, NULL, 0) == NULL) - if( mysql_real_connect( db, - the_setup.DbHost, - the_setup.DbUser, - the_setup.DbPass, - the_setup.DbName, - the_setup.DbPort, - NULL, 0 ) == NULL ) - { - return -2; - } - return 0; + + if( mysql_real_connect( db, + the_setup.DbHost, + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + the_setup.DbPort, + NULL, 0 ) == NULL ) + { + return -2; + } + return 0; } vector *GdGetStoredPlaylists(MYSQL db) @@ -60,7 +57,7 @@ vector *GdGetStoredPlaylists(MYSQL db) MYSQL_RES *result; MYSQL_ROW row; - result=mgSqlReadQuery(&db, "SELECT title FROM playlist"); + result = mgSqlReadQuery(&db, "SELECT title FROM playlist"); while((row = mysql_fetch_row(result)) != NULL) { @@ -69,12 +66,6 @@ vector *GdGetStoredPlaylists(MYSQL db) return list; } -/*! - * - * \brief constructor, constracts a number >=1 of filter sets - * - * the first set (index 0 ) is active by default - */ gdFilterSets::gdFilterSets() { mgFilter* filter; @@ -150,21 +141,11 @@ gdFilterSets::gdFilterSets() m_activeSet = m_sets[m_activeSetId]; } -/*! - ******************************************************************* - * \briefdestructor - ********************************************************************/ gdFilterSets::~gdFilterSets() { // everything is done in the destructor of the base class } -/*! - ******************************************************************* - * \brief computes the restrictions specified by the active filter set - * \param viewPrt index of the appropriate defualt view - * \return sql string representing the restrictions - ********************************************************************/ string gdFilterSets::computeRestriction(int *viewPrt) { string sql_str = "1"; @@ -172,111 +153,108 @@ string gdFilterSets::computeRestriction(int *viewPrt) switch (m_activeSetId) { case 0: - *viewPrt = 100; // tracks (flatlist for mountain man ;-)) - break; - case 1: - *viewPrt = 101; // album -> tracks - break; - case 2: // playlist -> tracks - *viewPrt = 102; // playlist -> tracks - break; - default: - mgWarning("Ignoring Filter Set %i", m_activeSetId); - break; - } - - for(vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - if((*iter)->isSet()) - { - if(strcmp((*iter)->getName(), tr("playlist title")) == 0 ) - { - sql_str = sql_str + " AND playlist.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("playlist author")) == 0 ) { - sql_str = sql_str + " AND playlist.author like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("album title")) == 0 ) + // tracks (flatlist for mountain man ;-)) + *viewPrt = 100; + } break; + case 1: { - sql_str = sql_str + " AND album.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("album artist")) == 0 ) - { - sql_str = sql_str + " AND album.artist like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("title")) == 0 ) + // album -> tracks + *viewPrt = 101; + } break; + case 2: { - sql_str = sql_str + " AND tracks.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("artist")) == 0 ) - { - sql_str = sql_str + " AND tracks.artist like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("genre")) == 0 ) - { - sql_str = sql_str + " AND (genre1.genre like '" - + (*iter)->getStrVal() + "'"; - sql_str = sql_str + " OR genre2.genre like '" - + (*iter)->getStrVal() + "')"; - } - else if(strcmp((*iter)->getName(), tr("year (from)")) == 0 ) - { - sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), tr("year (to)")) == 0 ) - { - sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), tr("rating")) == 0 ) - { - if ((*iter)->getStrVal() == "-") { - sql_str = sql_str + " AND tracks.rating >= 0 "; - } else if ((*iter)->getStrVal() == "O") { - sql_str = sql_str + " AND tracks.rating >= 1 "; - } else if ((*iter)->getStrVal() == "+") { - sql_str = sql_str + " AND tracks.rating >= 2 "; - } else if ((*iter)->getStrVal() == "++") { - sql_str = sql_str + " AND tracks.rating >= 3 "; - } - } - else + // playlist -> tracks + *viewPrt = 102; + } break; + default: { - mgWarning("Ignoring unknown filter %s", (*iter)->getName()); - } - } + mgWarning( "Ignoring Filter Set %i", m_activeSetId ); + } break; } - mgDebug(1, "Applying sql string %s (view=%d)",sql_str.c_str(), *viewPrt); + for( vector::iterator iter = m_activeSet->begin(); + iter != m_activeSet->end(); + iter++ ) + { + if( (*iter)->isSet() ) + { + if( strcmp((*iter)->getName(), tr("playlist title") ) == 0 ) + { + sql_str = sql_str + " AND playlist.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp( (*iter)->getName(), tr("playlist author") ) == 0 ) + { + sql_str = sql_str + " AND playlist.author like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), tr("album title")) == 0 ) + { + sql_str = sql_str + " AND album.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), tr("album artist")) == 0 ) + { + sql_str = sql_str + " AND album.artist like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), tr("title")) == 0 ) + { + sql_str = sql_str + " AND tracks.title like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), tr("artist")) == 0 ) + { + sql_str = sql_str + " AND tracks.artist like '%%" + + (*iter)->getStrVal() + "%%'"; + } + else if(strcmp((*iter)->getName(), tr("genre")) == 0 ) + { + sql_str = sql_str + " AND (genre1.genre like '" + + (*iter)->getStrVal() + "'"; + sql_str = sql_str + " OR genre2.genre like '" + + (*iter)->getStrVal() + "')"; + } + else if(strcmp((*iter)->getName(), tr("year (from)")) == 0 ) + { + sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), tr("year (to)")) == 0 ) + { + sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); + } + else if(strcmp((*iter)->getName(), tr("rating")) == 0 ) + { + if ((*iter)->getStrVal() == "-") + { + sql_str = sql_str + " AND tracks.rating >= 0 "; + } + else if ((*iter)->getStrVal() == "O") + { + sql_str = sql_str + " AND tracks.rating >= 1 "; + } + else if ((*iter)->getStrVal() == "+") + { + sql_str = sql_str + " AND tracks.rating >= 2 "; + } + else if ((*iter)->getStrVal() == "++") + { + sql_str = sql_str + " AND tracks.rating >= 3 "; + } + } + else + { + mgWarning( "Ignoring unknown filter %s", (*iter)->getName() ); + } + } + } + mgDebug(1, "Applying sql string %s (view=%d)", sql_str.c_str(), *viewPrt ); return sql_str; } -//------------------------------------------------------------------ -//------------------------------------------------------------------ -// -// class mgTrack -// -//------------------------------------------------------------------ -//------------------------------------------------------------------ mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack(); -/*! - ***************************************************************************** - * \brief Constructor: creates a mgGdTrack object - * - * \param sqlIdentifier identifies a unique row in the track database - * \param dbase database which stores the track table - * - * On creation, the object contains only the idea. The actual data fields - * are filled when readData() is called for the first time. - ****************************************************************************/ mgGdTrack::mgGdTrack(int sqlIdentifier, MYSQL dbase) { m_uniqID = sqlIdentifier; @@ -840,13 +818,15 @@ bool GdPlaylist::storePlaylist() } // add new playlist items to db - for(iter= m_list.begin(), num=0; iter != m_list.end(); iter++, num++) - { + for( iter=m_list.begin(), num=0; + iter != m_list.end(); + iter++, num++) + { mgSqlWriteQuery(&m_db, "INSERT into playlistitem " "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", num, (*iter)->getId(), m_sqlId); - } + } return true; } /*! @@ -892,11 +872,11 @@ int GdPlaylist::getPlayTimeRemaining() GdTreeNode::GdTreeNode(MYSQL db, int view, string filters) : mgSelectionTreeNode(db, view) { - // create a root node - // everything is done in the parent class - m_restriction = filters; - m_view = view; - m_label = tr("Browser"); + // create a root node + // everything is done in the parent class + m_restriction = filters; + m_view = view; + m_label = tr("Browser"); } GdTreeNode::GdTreeNode(mgSelectionTreeNode* parent, string id, string label, string restriction) @@ -926,8 +906,10 @@ GdTreeNode::~GdTreeNode() ****************************************************************************/ bool GdTreeNode::isLeafNode() { - if( m_level == 0) + if( m_level == 0 ) + { return false; + } switch(m_view) { case 1: // artist -> album -> title @@ -985,7 +967,6 @@ bool GdTreeNode::isLeafNode() } /*! - ***************************************************************************** * \brief compute children on the fly * * \return: true, if the node could be expanded (or was already), false,of @@ -994,10 +975,10 @@ bool GdTreeNode::isLeafNode() * retrieves all entries for the next level that satisfy the restriction of * the current level and create a child-arc for each distinct entry * - ****************************************************************************/ + * \todo use asnprintf! + */ bool GdTreeNode::expand() -{ - +{ MYSQL_ROW row; MYSQL_RES *result; int nrows; @@ -1006,272 +987,331 @@ bool GdTreeNode::expand() char idbuf[255]; int numchild; - string labelfield; // human readable db field for the column to be expanded - string idfield; // unique id field for the column to be expanded + string labelfield; // human readable db field for the column to be expanded + string idfield; // unique id field for the column to be expanded string new_restriction_field; // field to be restricted by the new level - string new_restriction; // complete restriction str for the current child - string new_label; + string new_restriction; // complete restriction str for the current child + string new_label; GdTreeNode* new_child; string tables; // stores the db tables used + +#define FROMJOIN " FROM tracks, genre as genre1, genre as genre2, album WHERE tracks.sourceid=album.cddbid AND genre1.id=tracks.genre1 AND genre2.id=tracks.genre2 AND %s " - #define FROMJOIN " FROM tracks, genre as genre1, genre as genre2, album WHERE tracks.sourceid=album.cddbid AND genre1.id=tracks.genre1 AND genre2.id=tracks.genre2 AND %s " - - if (m_expanded) + if( m_expanded ) { - mgWarning("Node already expanded\n"); - return true; + mgWarning("Node already expanded\n"); + return true; } - if (m_level == 1 && m_view < 100) - { - m_view = atoi(m_id.c_str()); - } - mgDebug(5, "Expanding level %d view %d\n", m_level,m_view); - if (m_level > 0) - { - switch(m_view) - { - case 1: // artist -> album -> title - if(m_level == 1) { - sprintf(sqlbuff, - "SELECT DISTINCT album.artist,album.artist" - FROMJOIN - " ORDER BY album.artist" - , m_restriction.c_str() ); - idfield = "album.artist"; - } else if(m_level == 2) { // artist -> album - sprintf(sqlbuff, - "SELECT DISTINCT album.title,album.cddbid" - FROMJOIN - " ORDER BY album.title" - , m_restriction.c_str() ); - idfield = "album.cddbid"; - } else if(m_level == 3) { // album -> title - sprintf(sqlbuff, - "SELECT tracks.title,tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb" - , m_restriction.c_str() ); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; - case 2: // genre -> artist -> album -> track - if(m_level == 1) { // genre - sprintf(sqlbuff, - "SELECT DISTINCT genre1.genre,tracks.genre1" - FROMJOIN - " ORDER BY genre1.id" - , m_restriction.c_str()); - idfield = "tracks.genre1"; - } else if(m_level == 2) { // genre -> artist - sprintf(sqlbuff, - "SELECT DISTINCT album.artist,album.artist" - FROMJOIN - " ORDER BY album.artist", + + if( m_level == 1 && m_view < 100 ) + { + m_view = atoi( m_id.c_str() ); + } + + mgDebug( 5, "Expanding level %d view %d\n", m_level, m_view ); + if( m_level > 0 ) + { + switch( m_view ) + { + case 1: + { // artist -> album -> title + if( m_level == 1 ) + { + sprintf(sqlbuff, + "SELECT DISTINCT album.artist,album.artist" + FROMJOIN + " ORDER BY album.artist" + , m_restriction.c_str() ); + idfield = "album.artist"; + } + else if( m_level == 2 ) + { // artist -> album + sprintf(sqlbuff, + "SELECT DISTINCT album.title,album.cddbid" + FROMJOIN + " ORDER BY album.title" + , m_restriction.c_str() ); + idfield = "album.cddbid"; + } + else if(m_level == 3) + { // album -> title + sprintf(sqlbuff, + "SELECT tracks.title,tracks.id" + FROMJOIN + " ORDER BY tracks.tracknb" + , m_restriction.c_str() ); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + } break; + case 2: + { // genre -> artist -> album -> track + if( m_level == 1 ) + { // genre + sprintf(sqlbuff, + "SELECT DISTINCT genre1.genre,tracks.genre1" + FROMJOIN + " ORDER BY genre1.id" + , m_restriction.c_str()); + idfield = "tracks.genre1"; + } + else if( m_level == 2 ) + { // genre -> artist + sprintf(sqlbuff, + "SELECT DISTINCT album.artist,album.artist" + FROMJOIN + " ORDER BY album.artist", m_restriction.c_str()); - idfield = "album.artist"; - } else if(m_level == 3) { // genre -> artist -> album - sprintf(sqlbuff, - "SELECT DISTINCT album.title,tracks.sourceid" - FROMJOIN - " ORDER BY album.title" - , m_restriction.c_str()); - idfield = "tracks.sourceid"; - } else if(m_level == 4) { // genre -> artist -> album -> track - sprintf(sqlbuff, - "SELECT DISTINCT tracks.title, tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb" - , m_restriction.c_str()); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; - case 3: // Artist -> Track - if(m_level ==1) - { - sprintf(sqlbuff, - "SELECT DISTINCT tracks.artist,tracks.artist" - FROMJOIN - " ORDER BY tracks.artist" - , m_restriction.c_str()); - idfield = "tracks.artist"; - } else if (m_level == 2) { // Track - sprintf(sqlbuff, - "SELECT DISTINCT tracks.title,tracks.id" - FROMJOIN - " ORDER BY tracks.title" - , m_restriction.c_str()); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; - case 4: // Genre -> Year -> Track - if(m_level == 1) { // Genre - sprintf(sqlbuff, - "SELECT DISTINCT genre1.genre,tracks.genre1" - FROMJOIN - " ORDER BY genre1.genre" - , m_restriction.c_str()); - idfield = "tracks.genre1"; - } else if (m_level == 2) { // Year - sprintf(sqlbuff, - "SELECT DISTINCT tracks.year,tracks.year" - FROMJOIN - " ORDER BY tracks.year" - , m_restriction.c_str()); - idfield = "tracks.year"; - } else if (m_level == 3) { // Track - sprintf(sqlbuff, - "SELECT DISTINCT" - " CONCAT(tracks.artist,' - ',tracks.title) AS title" - " ,tracks.id" - FROMJOIN - " ORDER BY tracks.title" - , m_restriction.c_str()); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; + idfield = "album.artist"; + } + else if( m_level == 3 ) + { // genre -> artist -> album + sprintf(sqlbuff, + "SELECT DISTINCT album.title,tracks.sourceid" + FROMJOIN + " ORDER BY album.title" + , m_restriction.c_str()); + idfield = "tracks.sourceid"; + } + else if( m_level == 4 ) + { // genre -> artist -> album -> track + sprintf(sqlbuff, + "SELECT DISTINCT tracks.title, tracks.id" + FROMJOIN + " ORDER BY tracks.tracknb" + , m_restriction.c_str()); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + } break; + case 3: + { // Artist -> Track + if( m_level ==1 ) + { + sprintf( sqlbuff, + "SELECT DISTINCT tracks.artist,tracks.artist" + FROMJOIN + " ORDER BY tracks.artist", + m_restriction.c_str()); + idfield = "tracks.artist"; + } + else if( m_level == 2) + { // Track + sprintf(sqlbuff, + "SELECT DISTINCT tracks.title,tracks.id" + FROMJOIN + " ORDER BY tracks.title", + m_restriction.c_str()); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + } break; + case 4: + { // Genre -> Year -> Track + if( m_level == 1 ) + { // Genre + sprintf(sqlbuff, + "SELECT DISTINCT genre1.genre,tracks.genre1" + FROMJOIN + " ORDER BY genre1.genre", + m_restriction.c_str()); + idfield = "tracks.genre1"; + } + else if (m_level == 2) + { // Year + sprintf(sqlbuff, + "SELECT DISTINCT tracks.year,tracks.year" + FROMJOIN + " ORDER BY tracks.year" + , m_restriction.c_str()); + idfield = "tracks.year"; + } + else if( m_level == 3 ) + { // Track + sprintf(sqlbuff, + "SELECT DISTINCT" + " CONCAT(tracks.artist,' - ',tracks.title) AS title" + " ,tracks.id" + FROMJOIN + " ORDER BY tracks.title", + m_restriction.c_str()); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + } break; case 5: // Album -> Tracks - if(m_level == 1) { // Album - sprintf(sqlbuff, - "SELECT DISTINCT" - " CONCAT(album.artist,' - ',album.title) AS title," - " album.cddbid" - FROMJOIN - " ORDER BY title" - , m_restriction.c_str()); - idfield = "tracks.sourceid"; - } else if (m_level == 2) { // - sprintf(sqlbuff, - "SELECT DISTINCT tracks.title, tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb" - , m_restriction.c_str()); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; + if( m_level == 1 ) + { // Album + sprintf(sqlbuff, + "SELECT DISTINCT" + " CONCAT(album.artist,' - ',album.title) AS title," + " album.cddbid" + FROMJOIN + " ORDER BY title" + , m_restriction.c_str()); + idfield = "tracks.sourceid"; + } + else if (m_level == 2) + { // Track + sprintf(sqlbuff, + "SELECT DISTINCT tracks.title, tracks.id" + FROMJOIN + " ORDER BY tracks.tracknb", + m_restriction.c_str()); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + break; + case 100: + if (m_level == 1) + { + sprintf(sqlbuff, + "SELECT CONCAT(tracks.artist,' - ',tracks.title)," + " tracks.id" + FROMJOIN + " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" + , m_restriction.c_str()); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; } - break; - case 100: - if (m_level == 1) { - sprintf(sqlbuff, - "SELECT CONCAT(tracks.artist,' - ',tracks.title)," - " tracks.id" - FROMJOIN - " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" - , m_restriction.c_str()); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; - case 101: // Albumsearch result - if (m_level == 1) { - sprintf(sqlbuff, - "SELECT DISTINCT" - " CONCAT(album.artist,' - ',album.title) as title," - " album.cddbid" - FROMJOIN - " ORDER BY CONCAT(album.artist,' - ',album.title)" - , m_restriction.c_str()); - idfield = "tracks.sourceid"; - } else if (m_level == 2) { - sprintf(sqlbuff, - "SELECT tracks.title,tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb" - , m_restriction.c_str()); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; + break; + case 101: + { // Albumsearch result + if( m_level == 1 ) + { + sprintf(sqlbuff, + "SELECT DISTINCT" + " CONCAT(album.artist,' - ',album.title) as title," + " album.cddbid" + FROMJOIN + " ORDER BY CONCAT(album.artist,' - ',album.title)", + m_restriction.c_str()); + idfield = "tracks.sourceid"; + } else if( m_level == 2 ) + { + sprintf(sqlbuff, + "SELECT tracks.title,tracks.id" + FROMJOIN + " ORDER BY tracks.tracknb", + m_restriction.c_str()); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + } break; case 102: - if (m_level == 1) { - sprintf(sqlbuff, - "SELECT DISTINCT playlist.title," - " playlist.id" - " FROM playlist,playlistitem,tracks,genre as genre1,genre as genre2" - " WHERE playlist.id=playlistitem.playlist AND" - " playlistitem.trackid=tracks.id AND" - " genre1.id=tracks.genre1 AND" - " genre2.id=tracks.genre2 AND" - " %s" - " ORDER BY playlist.title," - , m_restriction.c_str()); - idfield = "playlist.id"; - } else if (m_level == 2) { - sprintf(sqlbuff, - "SELECT CONCAT(tracks.artist,' - ',tracks.title)," - " tracks.id" - " FROM playlist,playlistitem,tracks" - " WHERE playlist.id=playlistitem.playlist AND" - " playlistitem.trackid=tracks.id AND" - " %s" - " ORDER BY playlistitem.tracknumber" - , m_restriction.c_str()); - idfield = "tracks.id"; - } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; - default: - mgError("View '%d' not yet implemented", m_view); - } - - // now get all childrean ofthe current node fromthe database - result = mgSqlReadQuery(&m_db, sqlbuff); - nrows = mysql_num_rows(result); - nfields = mysql_num_fields(result); - - numchild=1; - while((row = mysql_fetch_row(result)) != NULL) - { - // row[0] is the printable label for the new child - // row[1] is the unique id for the new child - sprintf(idbuf, "%s_%03d", m_id.c_str(), numchild); - - // Zweite ebene zeigt alle Tracks des Albums und nicht nur - // diese die den Filterkriterien entsprechen. - // das betrifft nur die Search Views! - if(m_view <100) { - new_restriction = m_restriction + " AND " - + idfield + "='" + row[1] + "'"; - } else { - new_restriction = idfield + "='" + row[1] + "'"; - } - - new_child = new GdTreeNode(this, // parent - (string) idbuf, // id - row[0], // label, - new_restriction); - m_children.push_back(new_child); - numchild++; - } - } else if (m_view <100) { + { + if (m_level == 1) + { + sprintf(sqlbuff, + "SELECT DISTINCT playlist.title," + " playlist.id" + " FROM playlist,playlistitem,tracks,genre as genre1,genre as genre2" + " WHERE playlist.id=playlistitem.playlist AND" + " playlistitem.trackid=tracks.id AND" + " genre1.id=tracks.genre1 AND" + " genre2.id=tracks.genre2 AND" + " %s" + " ORDER BY playlist.title,", + m_restriction.c_str()); + idfield = "playlist.id"; + } + else if (m_level == 2) + { + sprintf(sqlbuff, + "SELECT CONCAT(tracks.artist,' - ',tracks.title)," + " tracks.id" + " FROM playlist,playlistitem,tracks" + " WHERE playlist.id=playlistitem.playlist AND" + " playlistitem.trackid=tracks.id AND" + " %s" + " ORDER BY playlistitem.tracknumber", + m_restriction.c_str()); + idfield = "tracks.id"; + } + else + { + mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + m_expanded = false; + return false; + } + } break; + default: + { + mgError("View '%d' not yet implemented", m_view); + } + } + + // now get all childrean ofthe current node fromthe database + result = mgSqlReadQuery(&m_db, sqlbuff); + nrows = mysql_num_rows(result); + nfields = mysql_num_fields(result); + + numchild=1; + while((row = mysql_fetch_row(result)) != NULL) + { + // row[0] is the printable label for the new child + // row[1] is the unique id for the new child + sprintf(idbuf, "%s_%03d", m_id.c_str(), numchild); + + // Zweite ebene zeigt alle Tracks des Albums und nicht nur + // diese die den Filterkriterien entsprechen. + // das betrifft nur die Search Views! + if(m_view < 100) + { + new_restriction = m_restriction + " AND " + + idfield + "='" + row[1] + "'"; + } + else + { + new_restriction = idfield + "='" + row[1] + "'"; + } + + new_child = new GdTreeNode(this, // parent + (string) idbuf, // id + row[0], // label, + new_restriction); + m_children.push_back(new_child); + numchild++; + } + } + else if (m_view <100) + { new_child = new GdTreeNode(this, // parent "1" , // id tr("Artist -> Album -> Track"), // label, @@ -1297,24 +1337,24 @@ bool GdTreeNode::expand() tr("Album -> Track") , // label, m_restriction); m_children.push_back(new_child); - } else { + } + else + { new_child = new GdTreeNode(this, // parent "" , // id tr("Search Result"), // label, m_restriction); m_children.push_back(new_child); - } - - m_expanded = true; - mgDebug(5, "%d children expanded\n", m_children.size()); - return true; + } + + m_expanded = true; + mgDebug(5, "%d children expanded\n", m_children.size()); + return true; } /*! - ***************************************************************************** - * \brief go over all children recursively to find the tracks - * - ****************************************************************************/ + * \brief iterate all children recursively to find the tracks + */ vector* GdTreeNode::getTracks() { MYSQL_ROW row; @@ -1396,6 +1436,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.26 2004/08/27 15:19:34 LarsAC + * Changed formatting and documentation + * * Revision 1.25 2004/08/23 06:36:25 lvw * Initial version of an import module added * @@ -1469,7 +1512,6 @@ mgContentItem* GdTreeNode::getSingleTrack() * Revision 1.15 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * ->>>>>>> 1.14.2.4 * Revision 1.14 2004/02/12 07:56:46 RaK * - SQL Fehler bei der Playlist Search korrigiert * diff --git a/gd_content_interface.h b/gd_content_interface.h index d564f7d..7d43d74 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -3,21 +3,21 @@ * \brief Data objects for content (e.g. mp3 files, movies) * for the vdr muggle plugin database * - * \version $Revision: 1.9 $ - * \date $Date: 2004/07/29 06:17:50 $ + * \version $Revision: 1.10 $ + * \date $Date: 2004/08/27 15:19:34 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author: LarsAC $ * * Declares main classes for content items and interfaces to SQL databases * * This file defines the following classes - * - gdFilterSets - * - mgGdTrack a single track (content item). e.g. an mp3 file - * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) - * + * - gdFilterSets: filters to specifically search for GD items + * - mgGdTrack: a single track (content item). e.g. an mp3 file + * - GdTracklist: + * - GdPlaylist: + * - GdTreeNode: */ -/* makes sure we dont use the same declarations twice */ #ifndef _GD_CONTENT_INTERFACE_H #define _GD_CONTENT_INTERFACE_H @@ -33,8 +33,16 @@ #include "mg_filters.h" #include "i18n.h" -// non-member functions +/*! + * \brief Initialize a database for GD use + * \todo Should be a static member of some GD class + */ int GdInitDatabase(MYSQL *db); + +/*! + * \brief Obtain the playlists stored within the GD schema + * \todo Should be a static member of some GD class + */ std::vector *GdGetStoredPlaylists(MYSQL db); /*! @@ -65,15 +73,16 @@ class gdFilterSets : public mgFilterSets * the active filter sets. * * \param viewPort - after call, contains the index of the appropriate default view in + * \return sql string representing the restrictions * \todo should viewPort be a reference? */ - virtual std::string computeRestriction(int *viewPrt); + virtual std::string computeRestriction(int *viewPort); }; /*! * \brief represents a a single track - + * * This may be any content item. e.g. a mp3 fileselection * The object is initially created with a database identifier. * The actual data is only read when a content field is accessed for @@ -81,10 +90,84 @@ class gdFilterSets : public mgFilterSets * * \todo does each track node need a reference to the database? * maybe we can use a static db handle in mgDatabase? - * \ */ -class mgGdTrack : public mgContentItem +class mgGdTrack : public mgContentItem { + public: + + /*! + * \brief a constructor + * + * Creates an invalid item. + * + * \todo does this make sense? used anywhere? + */ + mgGdTrack() + { + m_uniqID = -1; + } + + /*! + * \brief a constructor for a specific item + * + * The constructor creates a specific item in a given database + * On creation, the object is only a wrapper without data. Actual + * data fields are filled when readData() is called for the first time. + * + * \param sqlIdentifier - a unique ID of the item which will be represented + * \param dbase - the database in which the item exists + */ + mgGdTrack( int sqlIdentifier, MYSQL dbase ); + + /*! + * \brief a copy constructor + */ + mgGdTrack(const mgGdTrack&); + + /*! + * \brief the destructor + */ + virtual ~mgGdTrack(); + + virtual mgContentItem::contentType getContentType(){return mgContentItem::GD_AUDIO;} + + virtual mgMediaPlayer getPlayer() + { + return mgMediaPlayer(); + } + + /*! \addtogroup Data read access */ + /*\@{*/ + virtual std::string getLabel( int col = 0 ); + + virtual std::string getTitle(); + virtual std::string getSourceFile(); + virtual std::string getGenre(); + std::string getArtist(); + std::string getAlbum(); + std::string getImageFile(); + int getYear(); + int getDuration(); + virtual int getRating(); + + virtual std::vector *getTrackInfo(); + /*\@}*/ + + /*! \addtogroup Data write access */ + /*\@{*/ + void setTitle(std::string new_title); + void setArtist(std::string new_artist); + void setAlbum(std::string new_album); + void setGenre(std::string new_genre); + void setYear(int new_rating); + void setRating(int new_rating); + + virtual bool setTrackInfo(std::vector*); + + bool writeData(); + /*\@}*/ + + static mgGdTrack UNDEFINED; private: @@ -102,10 +185,7 @@ private: */ bool m_retrieved; - // content fields - /*! - * \brief the artist name - */ + //! \brief the artist name std::string m_artist; /*! @@ -149,81 +229,6 @@ private: */ bool readData(); - public: - - /*! - * \brief a constructor - * - * Creates an invalid item. - * - * \todo does this make sense? - */ - mgGdTrack() - { - m_uniqID = -1; - } - - /*! - * \brief a constructor for a specific item - * - * The constructor creates a specific item in a given database - * - * \param sqlIdentifier - a unique ID of the item which will be represented - * \param dbase - the database in which the item exists - */ - mgGdTrack( int sqlIdentifier, MYSQL dbase ); - - /*! - * \brief a copy constructor - */ - mgGdTrack(const mgGdTrack&); - - /*! - * \brief the destructor - */ - virtual ~mgGdTrack(); - - virtual mgContentItem::contentType getContentType(){return mgContentItem::GD_AUDIO;} - - virtual mgMediaPlayer getPlayer() - { - return mgMediaPlayer(); - } - - /* data acess */ - //virtual functions of the base class - virtual std::string getSourceFile(); - - virtual std::string getTitle(); - - virtual std::string getLabel(int col = 0); - - virtual std::vector *getTrackInfo(); - - virtual bool setTrackInfo(std::vector*); - - virtual std::string getGenre(); - - virtual int getRating(); - - // additional class-specific functions - std::string getArtist(); - std::string getAlbum(); - int getYear(); - int getDuration(); - std::string getImageFile(); - - /* data manipulation */ - void setTitle(std::string new_title); - void setArtist(std::string new_artist); - void setAlbum(std::string new_album); - void setGenre(std::string new_genre); - void setYear(int new_rating); - void setRating(int new_rating); - - bool writeData(); - static mgGdTrack UNDEFINED; - }; class GdTracklist : public mgTracklist @@ -233,50 +238,42 @@ class GdTracklist : public mgTracklist }; /*! - ******************************************************************* * \class GdPlaylist * * \brief represents a playlist, i.e. an ordered collection of tracks - ********************************************************************/ + */ class GdPlaylist : public mgPlaylist { - private: - int m_sqlId; /* -1 means: not valid */ - int m_listtype; // used in GiantDisc db queries - std::string m_author; - MYSQL m_db; - - private: - int insertDataFromSQL(); - - public: - - - /*==== constructors ====*/ - GdPlaylist(std::string listname, MYSQL db_handle); - /* opens existing or creates empty playlist */ - + public: - /*==== destructor ====*/ - virtual ~GdPlaylist(); + GdPlaylist(std::string listname, MYSQL db_handle); + /* opens existing or creates empty playlist */ + + virtual ~GdPlaylist(); - virtual void setListname(std::string name); - /* changes the listname of the playlist (and unset the sql id */ - - int getPlayTime(); - /* returns the total duration of all songs in the list in seconds */ - - int getPlayTimeRemaining(); - /* returns the duration of all remaining songs in the list in seconds */ - + virtual void setListname(std::string name); + /* changes the listname of the playlist (and unset the sql id */ + + int getPlayTime(); + /* returns the total duration of all songs in the list in seconds */ + + int getPlayTimeRemaining(); + /* returns the duration of all remaining songs in the list in seconds */ + + bool storePlaylist(); - bool storePlaylist(); + private: + int m_sqlId; /* -1 means: not valid */ + int m_listtype; // used in GiantDisc db queries + std::string m_author; + MYSQL m_db; + + int insertDataFromSQL(); }; + /*! - ******************************************************************* - * \class mgSelectionTreeNode - * * \brief hierarchical representation of a set of tracks + * * The selection can be based on the whole database or a subset of it. * Within this selection, the data is organized in a tree hierarchy * The levels hof the hierarchy can be expanded dynamically by specifying @@ -285,11 +282,9 @@ class GdPlaylist : public mgPlaylist * When a node is expanded, a list of children is created. * Each child inherits the restrictions of its father and an additional * restriction on the recently expanded db field - ********************************************************************/ -class GdTreeNode : public mgSelectionTreeNode{ - -private: - // everything is in the base class + */ +class GdTreeNode : public mgSelectionTreeNode +{ public: /*==== constructors ====*/ @@ -311,6 +306,9 @@ public: /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.10 2004/08/27 15:19:34 LarsAC + * Changed formatting and documentation + * * Revision 1.9 2004/07/29 06:17:50 lvw * Added todo entries * @@ -351,7 +349,6 @@ public: * Revision 1.5 2004/02/12 09:15:07 LarsAC * Moved filter classes into separate files * ->>>>>>> 1.4.2.6 * Revision 1.4 2004/02/09 19:27:52 MountainMan * filter set implemented * -- cgit v1.2.3 From 71a5323ca0e92000baaa4c276c3a6e2e734a607e Mon Sep 17 00:00:00 2001 From: LarsAC Date: Fri, 27 Aug 2004 15:20:33 +0000 Subject: Updated open issues w.r.t. import git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@124 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/TODO b/TODO index c2582ad..eaf5f5c 100644 --- a/TODO +++ b/TODO @@ -7,6 +7,24 @@ Testing/bugs - Test mgPCMPlayer::getSourceFile() for GD case (find) - Test saving/loading playlists to database +Import +====== +- Album + - Cover text + - Cover images (based on filename or tag) + - Genre + - Modified + +- Tracks + - Language (?) + - Genre1, 2 + - Rating? + - Bitrate + - Modified, created + - Lyrics + +- Playlist from m3u + Code polishing ============== - Check for memory leaks @@ -123,6 +141,9 @@ Already Done ************************************************************ * * $Log: TODO,v $ +* Revision 1.13 2004/08/27 15:20:33 LarsAC +* Updated open issues w.r.t. import +* * Revision 1.12 2004/08/26 11:11:42 LarsAC * Import changed to use taglib * -- cgit v1.2.3 From e287c9b102fe10877f19f7b3c3e10c62209adbce Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 29 Aug 2004 13:48:14 +0000 Subject: Removed GiantDisc scripts. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@125 e10066b5-e1e2-0310-b819-94efdf66514b --- scripts/gddb.pm | 344 ----- scripts/gdgentools.pm | 1606 ----------------------- scripts/gdimport.pl | 255 ---- scripts/gdio.pm | 349 ----- scripts/gdparams.pm | 336 ----- scripts/gdserv.pm | 3305 ------------------------------------------------ scripts/gdsoundcard.pm | 149 --- scripts/gdupdate.pm | 509 -------- scripts/myhash.pm | 29 - 9 files changed, 6882 deletions(-) delete mode 100755 scripts/gddb.pm delete mode 100755 scripts/gdgentools.pm delete mode 100755 scripts/gdimport.pl delete mode 100755 scripts/gdio.pm delete mode 100755 scripts/gdparams.pm delete mode 100755 scripts/gdserv.pm delete mode 100755 scripts/gdsoundcard.pm delete mode 100755 scripts/gdupdate.pm delete mode 100755 scripts/myhash.pm diff --git a/scripts/gddb.pm b/scripts/gddb.pm deleted file mode 100755 index 88f5bea..0000000 --- a/scripts/gddb.pm +++ /dev/null @@ -1,344 +0,0 @@ -################################################## -# -# GiantDisc mp3 Jukebok -# -# © 2000, Rolf Brugger -# -################################################## - - -### -### GiantDisc: Common database routines (mainly db modification) -### - -package gddb; - -use strict; - -### Init constants - - -#BEGIN{ -#} - -############################################################ -### TOOL ROUTINES ### -############################################################ - -############################################################ - -sub sql_escape_squotes{ - ### escapes single quotes of the string passed as argument - # - # Parameter: string to be quoted - # Returns: quoted string - - # Usually you would use $dbh->quote instead, except if you don't want - # to add single quotes around the string - - my $sqlstring = $_[0]; - $sqlstring =~ s/'/\\'/g; - return $sqlstring; -} - - -############################################################ -### -sub field_where_clause{ # returns a string with a SQL-where clause - # for a text field like artist or title. If - # keyword is empty, an empty string is returned - # - # - Multiple keywords are space separated - # - Field begins are matched using the * wildcard - - # Examples: - # field="artist" keyword="abc" - # -> "AND artist LIKE - # - # field="artist" keyword="abc xyz" - # -> "AND artist LIKE "%abc%" AND artist LIKE "%xyz%" " - # - # field="artist" keyword="abc*" (everything after * is ignored) - # -> "AND artist LIKE "abc%" " - # - - my ($fieldname,$keywords) = @_; - $keywords = sql_escape_squotes($keywords); - my $cmd = ""; - - if ($keywords ne ""){ - if($keywords =~ m/\*/ ){ - ### wildcard expression - my @words = split (/\*/, $keywords ); - $cmd.=" AND $fieldname LIKE '$words[0]%' "; - } - else{ ### non wildcard expression - my @words = split (/ /, $keywords ); - my $current; - while($current = shift(@words)){ - $cmd.=" AND $fieldname LIKE '%$current%' "; - } - } - } - return $cmd; -} - -############################################################ -### -my $get_all=0; -my $get_tracks=1; -my $get_streams=2; - -sub attrib_where_clause{ # returns a string with a SQL-where clause - # for genre, year, language, type and rating - # field names are prefixed by 'tracks.' that the clauses can also be - # used in JOIN queries. - - my ($get_what, $genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating) = @_; - - my $tmpcmd; - my $cmd=" "; - - ### Genre - $tmpcmd="0 "; # false OR ... - if ($genre1 ne ""){ - $tmpcmd.="OR tracks.genre1 LIKE '$genre1%' OR tracks.genre2 LIKE '$genre1%' ";}; - if ($genre2 ne ""){ - $tmpcmd.="OR tracks.genre1 LIKE '$genre2%' OR tracks.genre2 LIKE '$genre2%' ";}; - if (length($tmpcmd)>3){ # genre query not empty? - $cmd .= " AND ($tmpcmd) "; - } - - ### Year - if(length($yearfrom)==4){ $cmd.=" AND tracks.year >= ".$yearfrom;} - if(length($yearto)==4){ $cmd.=" AND tracks.year <= ".$yearto;} - - ### Language - if ($lang ne ""){ $cmd.=" AND tracks.lang = '$lang' ";}; - - ### type - if ($type ne ""){ $cmd.=" AND tracks.type = '$type' ";}; - - ### rating - if ($rating ne ""){ - if ($rating == 0) { - $cmd.=" AND tracks.rating = $rating "; - } - else{ - $cmd.=" AND tracks.rating >= $rating "; - } - } - - ### track/stream/all - if($get_what==$get_tracks){ - $cmd.=" AND mp3file NOT LIKE 'http://%' "; - } - if($get_what==$get_streams){ - $cmd.=" AND mp3file LIKE 'http://%' "; - } - - return $cmd; -} - -############################################################ -### -sub track_where_clause{ # returns the where clause without keyword "WHERE" - my ($get_what, $artist,$title,$genre1,$genre2,$yearfrom,$yearto, - $lang,$type,$rating, - $ordercmd) = @_; - - my $where=" 1 "; # true AND ... - - ### Artist - $where .= field_where_clause("artist",$artist); - ### Title - $where .= field_where_clause("title",$title); - ### genre, etc ... - $where.= attrib_where_clause($get_what, $genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating); - - return $where; -} - -############################################################ - -sub track_order_clause{ # returns the order clause with keyword "ORDER BY" - my ($get_what, $artist,$title,$genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating, - $ordercmd) = @_; - - my $order = ""; - - if(length($ordercmd)>1){ - $order = "ORDER BY "; - if ($ordercmd =~ m/random/ ){ - $order .= "RAND() "; - } - elsif($ordercmd =~ m/year/ ){ - $order .= "year "; - } - elsif($ordercmd =~ m/recd/ ){ - $order .= "created "; - } - elsif($ordercmd =~ m/modd/ ){ - $order .= "modified "; - } - elsif($ordercmd =~ m/recmod/ ){ - $order .= "GREATEST(created,modified) "; - } - - if($ordercmd =~ m/-inv/ ){ - $order .= " DESC "; - } - } - - return $order; -} - -############################################################ -### -sub album_where_clause{ - my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, - $lang,$type,$rating, - $ordercmd) = @_; - - my $where=" 1 "; # true AND ... - ### Album: Artist - $where .= gddb::field_where_clause("album.artist",$artist); - ### Album: Title - $where .= gddb::field_where_clause("album.title",$title); - ### Track: genre, etc ... - $where.= gddb::attrib_where_clause(1, $genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating); - -#print "ALBUM WHERE: $where\n"; - return $where; -} - -############################################################ - -sub album_order_clause{ # returns the order clause with keyword "ORDER BY" - my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating, - $ordercmd) = @_; - - my $order = ""; - - if(length($ordercmd)>1){ - $order = "ORDER BY "; - if ($ordercmd =~ m/random/ ){ - $order .= "RAND() "; - } - elsif($ordercmd =~ m/year/ ){ - $order .= "tracks.year "; - } - elsif($ordercmd =~ m/recd/ ){ - $order .= "album.modified "; - } - elsif($ordercmd =~ m/modd/ ){ - $order .= "album.modified "; - } - elsif($ordercmd =~ m/recmod/ ){ - $order .= "album.modified "; - } - - if($ordercmd =~ m/-inv/ ){ - $order .= " DESC "; - } - } - - return $order; -} - -############################################################ -### DB CREATION & UPDATE ### -############################################################ - -############################################################ -### Creates/updates a new track record - # If $id is empty, a new record is created. Otherwise, the record - # is updated. - # Returns the (new) id - -sub insert_track_record{ - my ($dbh,$artist,$title,$genre1,$genre2,$year, - $lang,$type,$rating,$length,$source,$sourceid, - $tracknb,$mp3file,$condition,$voladjust, - $lengthfrm,$startfrm,$bpm,$bitrate, - $created,$id, ### these two fields are only defined on update! - ) = @_; - - if(length($artist)==0){$artist="-";}; - if(length($title)==0) {$title="-";}; - if(length($year)<4) {$year="0";}; - if(length($type)==0) {$type="NULL";}; - if(length($rating)==0){$rating="NULL";}; - if(length($length)==0){$length="0";}; - if(length($source)==0){$source="0";}; - if(length($tracknb)==0){$tracknb="0";}; - if(length($condition)==0){$condition="0";}; - if(length($voladjust)==0){$voladjust="0";}; - if(length($lengthfrm)==0){$lengthfrm="0";}; - if(length($startfrm)==0) {$startfrm="0";}; - if(length($bpm)==0) {$bpm="0";}; - if(length($bitrate)==0) {$bitrate="128";}; - if(length($created)==0) {$created="CURDATE()";}; - - - my $sqlcmd; - $sqlcmd = - "artist=".$dbh->quote($artist).", " # quote adds single quotes around the string! - ."title=".$dbh->quote($title).", " - ."genre1='$genre1', " - ."genre2='$genre2', " - ."year = $year, " - ."lang ='$lang', " - ."type = $type, " - ."rating=$rating, " - ."length=$length, " - ."source=$source, " - ."sourceid='$sourceid', " - ."tracknb=$tracknb, " - ."mp3file='$mp3file', " - ."condition=$condition, " - ."voladjust=$voladjust, " - ."lengthfrm=$lengthfrm, " - ."startfrm=$startfrm, " - ."bpm=$bpm, " - ."bitrate='$bitrate', "; - - if(length($id)==0){ - ### INSERT a new record - $sqlcmd = "INSERT tracks SET ".$sqlcmd - ."created=CURDATE() "; - } - else{ - ### REPLACE an existing record - $sqlcmd = "UPDATE tracks SET ".$sqlcmd - ."created='$created', " - ."modified=CURDATE() " - ."WHERE id=$id "; - } - - #print("SQL: $sqlcmd \n"); - my $sth = $dbh->prepare($sqlcmd); - $sth->execute; - - if(length($id)==0){ ### if new record created - $id = $sth->{mysql_insertid}; - } - $sth->finish; - return $id; -} - - - - - -############################################################ -### QUERIES ### -############################################################ -# - - - - - -#### end -1; diff --git a/scripts/gdgentools.pm b/scripts/gdgentools.pm deleted file mode 100755 index aadc5c7..0000000 --- a/scripts/gdgentools.pm +++ /dev/null @@ -1,1606 +0,0 @@ -package gdgentools; - -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2000, Rolf Brugger -# -################################################## - - - -### General tool routines - -use lib '/home/lvw/Development/muggle-import/scripts'; -use gdparams; -use IO::Socket; - - -### Constants -my $pl_list = 0; # play list -my $rp_list = 1; # rip list -my $co_list = 2; # compression list -my $cr_list = 3; # cd recording list - - - - - -#use lib '/usr/local/bin'; -#BEGIN{;} -#END{;} - - -############################################################ -### Shuffle routine -sub fisher_yates_shuffle {# generate a random permutation of @array in place - my $array = shift; - my $i; - for ($i = @$array; --$i; ) { - my $j = int rand ($i+1); - next if $i == $j; - @$array[$i,$j] = @$array[$j,$i]; - } -} -# USAGE: -# fisher_yates_shuffle( \@array ); # permutes @array in placesub query_random_artists{ - - - - - -############################################################ -### barix exstreamer routines - -sub exstreamer_command{ - # sends a command string to the exstreamer at 'playerhost', without - # waiting for a result that is sent back by the exstreamer - - my ($playerhost, $command) = @_; - my $port = 12302; # default exstreamer tcp command port - my $sock = new IO::Socket::INET( PeerAddr => $playerhost, - PeerPort => $port, - Proto => 'tcp'); - die "Error: could not open socket $opt_h:$port. $!\n" unless $sock; - - ### send the command - print $sock $command."\n"; - close($sock); -} - - -sub exstreamer_command_res{ - # sends a command string to the exstreamer at 'playerhost'. - # It returns the first line that is sent back by the exstreamer (last \n chopped off) - - my ($playerhost, $command) = @_; - my $port = 12302; # default exstreamer tcp command port - my $sock = new IO::Socket::INET( PeerAddr => $playerhost, - PeerPort => $port, - Proto => 'tcp'); - die "Error: could not open socket $opt_h:$port. $!\n" unless $sock; - - ### send a command - print $sock $command."\n"; - my $res = <$sock>; - close($sock); - chop $res; - return $res; -} - - -############################################################ -### Playlist routines - - -# creates new playstate record. If one with the same playerid/audiochannel -# exists, it is overwritten. -sub pll_new_playstate{ - my ($dbh, $playerid, $audiochannel, $playertype, $snddevice, $playerapp, $playerparams, $ptlogger) = @_; - $dbh->do("REPLACE INTO playerstate SET " - ."playerid=$playerid, " - ."audiochannel=$audiochannel, " - ."playertype=$playertype, " - ."snddevice='$snddevice', " - ."playerapp='$playerapp', " - ."playerparams='$playerparams', " - ."ptlogger='$ptlogger', " - ."currtracknb=0, " - ."state='st', " # stopped - ."framesplayed=0, " - ."framestotal=0 " - ); -} - -# deletes playstate record -sub pll_del_playstate{ - my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it - $dbh->do("REPLACE FROM playerstate " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel "); -} - - -# Returns th specified player-type -sub pll_get_playertype{ - my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it - - my $sth = $dbh->prepare( - "SELECT playertype,ipaddr,snddevice FROM player,playerstate " - ."WHERE player.id=playerstate.playerid " - ." AND player.id=$playerid " - ." AND playerstate.audiochannel=$audiochannel"); - - my $nbrec = $sth->execute; - #print("$nbrec playstates found (should be exactly 1)\n"); - - my ($playertype,$playerhost,$snddevice); - - if($row = $sth->fetchrow_hashref){ - $playertype = $row->{playertype}; - $playerhost = $row->{ipaddr}; - $snddevice = $row->{snddevice}; - } - else{ - ### This case should not happen! make the best out of it - print ("ERROR: can't get playertype for player $playerid channel $audiochannel\n"); - print (" no player/playerstate record found\n"); - } - $sth->finish; - - return ($playertype,$playerhost,$snddevice); -} - - -# Returns the current playstate -sub pll_get_playstate{ - my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it - - my $sth = $dbh->prepare( - "SELECT * FROM playerstate " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); - - my $nbrec = $sth->execute; - #print("$nbrec playstates found (should be exactly 1)\n"); - - my $currtracknb = 0; - my $state = "st"; - my $shufflestat = ""; - - if($row = $sth->fetchrow_hashref){ - $currtracknb = $row->{currtracknb}; - $state = $row->{state}; - $framesplayed= $row->{framesplayed}; - $shufflestat = $row->{shufflestat}; - } - else{ - ### This case should not happen! make the best out of it - #pll_new_playstate($dbh, $playerid, $audiochannel, "", "", ""); - print ("ERROR: can't get playstate for player $playerid channel $audiochannel\n"); - print (" no playerstate record found\n"); - } - $sth->finish; - - return ($currtracknb, $state, $framesplayed, $shufflestat); -} - - -# Returns -sub playerdefinition{ - my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it - - my $sth = $dbh->prepare( - "SELECT playertype, ipaddr, snddevice FROM player,playerstate " - ."WHERE player.id=$playerid AND playerstate.playerid=$playerid AND audiochannel=$audiochannel"); - - my $nbrec = $sth->execute; - #print("$nbrec playstates found (should be exactly 1)\n"); - - my ($playertype, $playerhost, $sounddevice); - my $playertype = 0; - my $playerhost = "localhost"; - my $sounddevice = "/dev/dsp"; - - if($row = $sth->fetchrow_hashref){ - $playertype = $row->{playertype}; - $playerhost = $row->{ipaddr}; - $sounddevice = $row->{snddevice}; - } - else{ - ### This case should not happen! - print ("ERROR: can't get player definition for player $playerid channel $audiochannel\n"); - print (" no playerstate record found\n"); - } - $sth->finish; - - return ($playertype, $playerhost, $sounddevice); -} - - -# Returns the process id of the player (that was previously stored in the DB) -sub pll_get_player_processid{ - my ($dbh, $playerid, $audiochannel) = @_; #parameters: database handle, player id, sound out it - my $sth = $dbh->prepare( - "SELECT processid FROM playerstate " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); - my $nbrec = $sth->execute; - my $playerpid = -1; - if($row = $sth->fetchrow_hashref){ - $playerpid = $row->{processid}; - } - else{ - ### This case should not happen! make the best out of it - print ("ERROR: can't get playstate for player $playerid channel $audiochannel\n no playerstate record found\n"); - } - $sth->finish; - return ($playerpid); -} - - - -# Writes the playstate to the playstate record -# Parameters: -# currtracknb -# state (one of: pl, st, in, ff, Ff, rw, Rw) [in=pause(interrupted)] -sub pll_write_playstate{ - my ($dbh, $playerid, $audiochannel, $currtracknb, $state) = @_; - my $retval = $dbh->do( - "UPDATE playerstate " - ."SET currtracknb=$currtracknb, state='$state' " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); -} - - -# Writes the players process id to the playstate record -# The programs 'gdplayd.pl' and 'gdplaytmsim.pl' write their process id into -# the DB. This is necessary to efficiently stop playing or rewinding. -sub pll_write_player_processid{ - my ($dbh, $playerid, $audiochannel, $playerpid) = @_; - my $retval = $dbh->do( - "UPDATE playerstate " - ."SET processid=$playerpid " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); -} - -#sub pll_clear_player_processid{ -# my ($dbh, $playerid, $audiochannel, $playerpid) = @_; -# if ($gdparms::multiclients){ -##print "clearing pid \n"; -# my $retval = $dbh->do( -# "UPDATE playerstate " -# ."SET processid=0 " -# ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); -# } -#} - - -### Shuffle Play Parameters -sub pll_write_shuffleparameters{ - my ($dbh, $playerid, $audiochannel, $parameterstring, $statisticsstring) = @_; - print("writing shuffleparamstring: '$parameterstring'\n"); - my $retval = $dbh->do( - "UPDATE playerstate " - ."SET shufflepar =".$dbh->quote($parameterstring).", " - ." shufflestat=".$dbh->quote($statisticsstring)." " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); -} - - - -####################################################################### -### Stop playing -# -# Stopping to play means killing the specifig playing-deamon 'gdplayd.pl' -# and all the sub-processes it has spawned. -# -# As long as only one instance of a GiantDisc server is running on a -# machine, we can just blindly kill all potentially spawned processes by -# name (see routine 'kill_all_play_processes'). -# -# If we have more than one server instance on the same machine, this blindly -# killing method doesn't work anymore. Stopping to play on one instance would -# terminate the play processes of all other server instances. It is therefore -# necessary to specifically kill the involved processes. I have tried many -# (really a lot of) approaches during 2 years - nothing really worked well. -# There were 2 main problems: -# - killing the processes was far too slow -# - multiple instances of gdplayd.pl and it sub-processes appeared leading -# to locked soundcards etc. ( This especially happened, when playing was -# quickly stopped and restarted, or after fast sequences of 'play next' -# commands. -# -# The currently adopted however method seems to work fine, it is robust and -# efficient enough. It is explained in detail below, see routines plmsg_... -# - - -### tested and not well working: Killall and Killfam! -### Comment: Proc::Killall is terribly slow (much slower than system("killall...")) -### using Proc::Killfam is unstable because of overlapping playing -### commands, that inevitably lead to multiple instances of gdplayd.pl -### (mainly because pid of gdplayd.pl is known too late?) -### Finally - Proc::Killfam is too slow too! - - -sub stop_play_processes{ - - my ($dbh, $playerid, $audiochannel) = @_; - my $new_kill_player = 1; -# if ($gdparms::multiclients){ - if ($new_kill_player){ - my $playerpid = pll_get_player_processid($dbh, $playerid, $audiochannel); - - if ($playerpid >0){ - ### get all child processes of $playerpid - my $chprocs = `pstree $playerpid -p |tr -d '\012'`; # get process id's, on one text line - my @chpids = split /\(/, $chprocs; - shift @chpids; # pop off first element - foreach $elem (@chpids){ - $elem =~ s/\).*//; - } - - if (scalar(@chpids)>0){ - #print " specifically killing ".join (",",@chpids)."\n"; - system "kill ".join (" ",@chpids); - } - } - } - else{ - kill_all_play_processes(); - } -} - - -sub kill_all_play_processes{ - #print "killing blindly\n"; - system("killall -q gdplayd.pl; " - ."killall -q mpg123; " - ."killall -q ogg123; " - ."killall -q flac; " - ."killall -q rawplay; " - ."killall -q gdplaytmsim.pl; " - ."killall -q gdstream.pl"); - - #killall and killfam are terribly slow! -} - - -####################################################################### -### IPC Message routines to make sure, that a stop operation really kills -# all player processes. -# Method: -# 1) the server starts the player 'gdplayd.pl' in background -# 2) right after starting gdplayd.pl, the server waits for a message of -# gdplayd.pl -# 3) in the init phase of gdplayd.pl, it writes it's own process-id to the -# database and sends then a message to the message queue -# 4) the server receives the message and can continue to process requests -# from the palm -# 5) when the server should now stop playing, it is 100% sure, that the -# correct process-id o the playing process is in the db. -# Killing it (and it's subprocesses) stops playing. - -use IPC::SysV qw(IPC_CREAT S_IRWXU ftok); -use IPC::Msg; - -# Die folgenden Konstanten kennzeichnen die Message-Queue im System. -# RENDEZVOUS muss der Name einer _existierenden_ Datei sein! -# Nur die unteren 8 Bits von RVID sind wichtig und muessen !=0 sein! -# ftok(RENDEZVOUS, RVID) erzeugt einen immer identischen Schluessel, -# so lange die Datei RENDEZVOUS nicht neu angelegt wurde. - -use constant RENDEZVOUS => "/etc"; -#use constant RVID => 121; - -sub rendevous_id{ - my ($playerid, $audiochannel) = @_; -#print "p=$playerid, chn=$audiochannel -> rdvid=".($playerid*16 + $audiochannel + 1)."\n"; - if ($playerid>15 || $audiochannel>15){ - print "\n WARNING:\n "; - print "too many palyers/audiochannels\n"; - print " p=$playerid, chn=$audiochannel -> rdvid=".($playerid*16 + $audiochannel + 1)."\n\n"; - } - return $playerid*16 + $audiochannel + 1; -} -sub plmsg_newqueue{ - ### creates messae queue RENDEVOUS, if it doesn't already exist - my ($playerid, $audiochannel) = @_; - my $rdvid = rendevous_id($playerid, $audiochannel); - use vars qw($msg); - $msg = new IPC::Msg(ftok(RENDEZVOUS, $rdvid), S_IRWXU | IPC_CREAT); - -} - -sub plmsg_send{ - ### appends a message to the queue - my ($playerid, $audiochannel) = @_; - my $rdvid = rendevous_id($playerid, $audiochannel); - my $msg = new IPC::Msg(ftok(RENDEZVOUS, $rdvid), 0); - my ($prio, $text)=(1,"player started");# = @MESSAGES[$i,$i+1]; - $msg->snd($prio, $text, 0); -} - -sub plmsg_waitrcv{ - my ($playerid, $audiochannel) = @_; - ### pulls one message from the message queue, waits until one message is there - my $rdvid = rendevous_id($playerid, $audiochannel); - my $msg = new IPC::Msg(ftok(RENDEZVOUS, $rdvid), 0); - my $buflen = 256; - $prio = $msg->rcv($buf, $buflen, 0, 0); -# print "Found: ($buf, $prio)\n"; -} - - -sub pl_start_playd_and_wait{ -# starts playing-deamon and waits until it has written it's pid in the db - my ($dbhost, $playerid, $audiochannel) = @_; -#print "--> start gdplayd.pl\n"; - system("gdplayd.pl $dbhost $playerid $audiochannel & "); -#print "--> wait for gdplayd.pl to be started\n"; - plmsg_waitrcv($playerid, $audiochannel); -#print "--> message received\n"; -} - - -####################################################################### -####################################################################### -# -# Basic Play Controls (also used as API) - - -sub pl_play{ -# starts playing - my ($dbh, $playerid, $audiochannel) = @_; - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - if(tracklist_get_item($dbh, $playerid, 0, $trackind) < 1){$trackind=1;} # playstate error - stop_play_processes($dbh, $playerid, $audiochannel); - pll_write_playstate($dbh, $playerid, $audiochannel, $trackind, "pl"); - pl_start_playd_and_wait($gdparms::dbhost, $playerid, $audiochannel); -# system("gdplayd.pl $gdparms::dbhost $playerid $audiochannel & "); -} - -sub pl_play_at{ -# starts playing at specified position (seconds) - my ($dbh, $playerid, $audiochannel, $songpos_sec) = @_; -# my ($songpos_sec) = @_; - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - if(tracklist_get_item($dbh, $playerid, 0, $trackind) < 1){$trackind=1;} # playstate error - stop_play_processes($dbh, $playerid, $audiochannel); - my $startframe = $songpos_sec * frames_per_second(); - pll_write_playtime_only($dbh, $playerid, $audiochannel, $startframe); - pll_write_playstate($dbh, $playerid, $audiochannel, $trackind, "pl"); - pl_start_playd_and_wait($gdparms::dbhost, $playerid, $audiochannel); -# system("gdplayd.pl $gdparms::dbhost $playerid $audiochannel & "); -} - -sub pl_stop{ -# stops playing and reset playtime-state - my ($dbh, $playerid, $audiochannel) = @_; - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - my($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); - stop_play_processes($dbh, $playerid, $audiochannel); - pll_write_playstate($dbh, $playerid, $audiochannel, $trackind, "st"); - pll_write_playtime($dbh, $playerid, $audiochannel, 0, $total); -} - -sub pl_pause{ -# stops playing and preserve playtime-state - my ($dbh, $playerid, $audiochannel) = @_; - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - my($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); - stop_play_processes($dbh, $playerid, $audiochannel); - pll_write_playstate($dbh, $playerid, $audiochannel, - $trackind, "in"); # state: interrupted -} - -sub pl_rw{ -# similar as pause - my ($dbh, $playerid, $audiochannel) = @_; - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - my ($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); - stop_play_processes($dbh, $playerid, $audiochannel); - pll_write_playstate($dbh, $playerid, $audiochannel, - $trackind, "rw"); # state: rw - - ### continuously write current playtime to db (every second) - system("gdplaytmsim.pl $gdparms::dbhost $playerid $audiochannel & "); -} - -sub pl_ff{ -# similar as pause - my ($dbh, $playerid, $audiochannel) = @_; - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - my ($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); - stop_play_processes($dbh, $playerid, $audiochannel); - pll_write_playstate($dbh, $playerid, $audiochannel, - $trackind, "ff"); # state: ff - - ### continuously write current playtime to db (every second) - system("gdplaytmsim.pl $gdparms::dbhost $playerid $audiochannel & "); -} - -sub pl_goto{ - ### makes a new track the current track - # sets the current playtime-posititon to zero - # the rest of the playstate is preserved - - use integer; - my ($dbh, $playerid, $audiochannel, $newind) = @_; -# my ($newind) = @_; # the new index (must be valid) - - stop_play_processes($dbh, $playerid, $audiochannel); - - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - pll_write_playstate($dbh, $playerid, $audiochannel, $newind, $state); - pll_write_playtime($dbh, $playerid, $audiochannel, 0, 0); - if($state eq "pl"){ # restart player - pl_start_playd_and_wait($gdparms::dbhost, $playerid, $audiochannel); -# system("gdplayd.pl $gdparms::dbhost $playerid $audiochannel & "); - } - else{ # new 'current' track: load it's total length - my $trackid = tracklist_get_item($dbh, $playerid, 0, $newind); - if (length($trackid)>0) { - my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id = $trackid"); - my $cnt = $sth->execute; - if ($cnt > 0){ - my $row = $sth->fetchrow_hashref; - pll_write_playtime($dbh, $playerid, $audiochannel, 0, $row->{length}*frames_per_second()); - } - else{ - pll_write_playtime($dbh, $playerid, $audiochannel, 0, 0); - } - $sth->finish; - } - else{ - pll_write_playtime($dbh, $playerid, $audiochannel, 0, 0); - } - } -} - -sub pl_next{ - my ($dbh, $playerid, $audiochannel) = @_; - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - my $listlen = tracklist_get_nb_items($dbh, $playerid, $pl_list); - if ($trackind < $listlen) {$trackind++;} - pl_goto($dbh, $playerid, $audiochannel, $trackind); -} - -sub pl_prev{ - my ($dbh, $playerid, $audiochannel) = @_; - my $frames5sec = 5*frames_per_second(); # nb frames in 5 sec - my ($trackind, $state, $frame, $shufflestat) = pll_get_playstate($dbh, $playerid, $audiochannel); - my ($played, $total) = pll_get_playtime($dbh, $playerid, $audiochannel); - if ($trackind>0 && $played<$frames5sec){$trackind--;} - pl_goto($dbh, $playerid, $audiochannel, $trackind); -} - -####################################################################### -####################################################################### - - - - - -####################################################################### -# -# Playtime routines: -# The current position is always saved in the field 'framesplayed' -# If the player app can't continuously write this field, another realtime -# app has to write the current playtime (and ff, rew position) - -# mp3 constants -my $samplesPerFrame = 1152; -my $samplesPerSecond = 44100; - -sub frames_per_second{ - use integer; - return $samplesPerSecond/$samplesPerFrame; # = 38.281 -} -sub samples_per_frame{ - return $samplesPerFrame; -} -sub samples_per_second{ - return $samplesPerSecond; -} - - -# Writes the playtime to the playtime record. -# Parameters: played, total (units: frames) -sub pll_write_playtime{ - my ($dbh, $playerid, $audiochannel, $played, $total) = @_; - my $retval = $dbh->do( - "UPDATE playerstate " - ."SET framesplayed=$played, framestotal=$total " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); -} - -sub pll_write_playtime_only{ # like pll_write_playtime without changing 'framestotal' - my ($dbh, $playerid, $audiochannel, $played) = @_; - my $retval = $dbh->do( - "UPDATE playerstate " - ."SET framesplayed=$played " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel"); -} - - -# Returns the current playtime (frames played and total frames) -sub pll_get_playtime{ - my ($dbh, $playerid, $audiochannel) = @_; - my $played = 0; - my $total = 0; - - use integer; - - my $sth = $dbh->prepare( - "SELECT * FROM playerstate " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); - - my $nbrec = $sth->execute; - #print("$nbrec playtime found (should be exactly 1)\n"); - - if($row = $sth->fetchrow_hashref){ - $total = $row->{framestotal}; - $played = $row->{framesplayed}; - } - $sth->finish; - - return ($played, $total); -} - - -sub seconds_to_hm{ - my ($seconds) = @_; - my ($hours, $minutes); - use integer; # switch to int math - $hours = $seconds / 3600; - $minutes = ($seconds % 3600)/60; - return sprintf("%ih%02im", $hours, $minutes); -} - -sub seconds_to_sm{ - my ($seconds) = @_; - my ($minutes, $sec); - use integer; # switch to int math - $minutes = $seconds / 60; - $sec = $seconds % 60; - return sprintf("%i:%02i", $minutes, $sec); -} - - -### is the string a mp3-file or a mp3-stream? -sub is_mp3stream{ - my ($mp3filename) = @_; - return ($mp3filename =~ /^http:\/\/.*/); # matches "http://" at the beginning? -} - - -####################################################################### - -# Returns the player parameter ($snddevice, $playerapp, $playerparams, $ptlogger, $shufflepar) -sub pll_get_playparams{ - my ($dbh, $playerid, $audiochannel) = @_; - my ($snddevice, $playerapp, $playerparams, $ptlogger); - - my $sth = $dbh->prepare( - "SELECT * FROM playerstate " - ."WHERE playerid=$playerid AND audiochannel=$audiochannel" ); - my $nbrec = $sth->execute; - - if($row = $sth->fetchrow_hashref){ - $snddevice = $row->{snddevice}; - $playerapp = $row->{playerapp}; - $playerparams = $row->{playerparams}; - $ptlogger = $row->{ptlogger}; - $shufflepar = $row->{shufflepar}; - } - $sth->finish; - - return ($snddevice, $playerapp, $playerparams, $ptlogger, $shufflepar); -} - - -############################################################ -# Returns the main player parameters -# ($ipaddr, $uichannel, $logtarget, $cdripper, $mp3encoder, $cdromdev, $cdrwdev) -sub pll_get_mainparams{ - my ($dbh, $playerid) = @_; - my ($ipaddr, $uichannel, $logtarget, $cdripper, $mp3encoder, $cdromdev, $cdrwdev); - - my $sth = $dbh->prepare( - "SELECT * FROM player " - ."WHERE id=$playerid" ); - my $nbrec = $sth->execute; - - if($row = $sth->fetchrow_hashref){ - $ipaddr = $row->{ipaddr}; - $uichannel = $row->{uichannel}; - $logtarget = $row->{logtarget}; - $cdripper = $row->{cdripper}; - $mp3encoder= $row->{mp3encoder}; - $cdromdev = $row->{cdromdev}; - $cdrwdev = $row->{cdrwdev}; - } - $sth->finish; - - return ($ipaddr, $uichannel, $logtarget, $cdripper, $mp3encoder, $cdromdev, $cdrwdev); -} - - -############################################################ -### General tracklist functions (table tracklistitem) -# -# 'tracknb' always starts with 0 and is contiguous -# ex: 0,1,2,3 is legal, 0,1,2,4,5 is illegal -# - -sub tracklist_get_nb_items{ - # returns the of tracks in the specified tracklist - my ($dbh, $playerid, $listtype) = @_; - my $listlen = 0; - my $sth = $dbh->prepare( - "SELECT COUNT(*) FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$listtype" ); - $sth->execute; - my @row; - if(@row = $sth->fetchrow_array){ - $listlen = $row[0]; - } - $sth->finish; - return $listlen; -} - -sub tracklist_append_list{ - # appends a list of trackid's to the specified tracklist. - # Parameters: dbh, playerid, listtype, trackids... - my ($dbh) = shift(@_); - my ($playerid) = shift(@_); - my ($listtype) = shift(@_); - - my $curritem = tracklist_get_nb_items($dbh, $playerid, $listtype); - - while($trackid = shift(@_)){ - $dbh->do("INSERT INTO tracklistitem SET " - ."playerid=$playerid, " - ."listtype=$listtype, " - ."tracknb=$curritem, " - ."trackid=$trackid "); - $curritem++; - } -} - - -# moves the specified list chunk down by one -sub tracklist_move_chunk_up{ - my ($dbh, $playerid, $listtype, $first, $last) = @_; - # "shift" specified items up (to higher index) by 1 - # have to do increment item by item because order is important - my $sth = $dbh->prepare( - "SELECT * FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb>=$first AND tracknb<=$last " - ."ORDER BY tracknb DESC" ); # order: highest index first! - $sth->execute; - my $row; - while($row = $sth->fetchrow_hashref){ - $dbh->do( - "UPDATE tracklistitem " - ."SET tracknb=tracknb+1 " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=".$row->{tracknb} ); - } - $sth->finish; -} - - -# removes the specified list item from the list (item index starts with 0) -sub tracklist_del_item{ - my ($dbh, $playerid, $listtype, $trackindex) = @_; - $dbh->do( - "DELETE FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$trackindex" ); - -### should write a routine 'tracklist_move_chunk_down' (like tracklist_move_chunk_up) - # "shift" following items down by 1 - # have to do decrement item by item because order is important - my $sth = $dbh->prepare( - "SELECT * FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb>$trackindex " - ."ORDER BY tracknb" ); - $sth->execute; - my $row; - while($row = $sth->fetchrow_hashref){ - $dbh->do( - "UPDATE tracklistitem " - ."SET tracknb=tracknb-1 " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=".$row->{tracknb} ); - } - $sth->finish; -} - -# removes the all list items from 0 to 'trackindex' -sub tracklist_del_upto_item{ - -### VERY INEFFICIENT IMPLEMENTATION!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - my ($dbh, $playerid, $listtype, $trackindex) = @_; - print("deleting up to $trackindex\n"); - my $counter=0; - while ($counter < $trackindex){ - tracklist_del_item($dbh, $playerid, $listtype, 0); - $counter++; - } -} - - -# reorders the specified list item in the list (item index starts with 0) -# -> 'destpos' must be lower than 'srcpos'! -sub tracklist_reorder_item{ - my ($dbh, $playerid, $listtype, $srcpos, $destpos) = @_; - - ### "move" src-item to a save location - my $savepos = -1000; # a bit too hacky? - $dbh->do( - "UPDATE tracklistitem " - ."SET tracknb=$savepos " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$srcpos" ); - - # "shift" following items up by 1 - tracklist_move_chunk_up($dbh, $playerid, $listtype, $destpos, ($srcpos)-1); - - ### "move" src-item from save location to destination - $dbh->do( - "UPDATE tracklistitem " - ."SET tracknb=$destpos " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$savepos" ); -} - - - -# empties the specified tracklist -sub tracklist_delete{ - my ($dbh, $playerid, $listtype) = @_; - $dbh->do( - "DELETE FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$listtype" ); -} - - -# gets the trackid of the specified list item (item index starts with 0) -# If an error occurs, 0 is returned. -sub tracklist_get_item{ - my ($dbh, $playerid, $listtype, $trackindex) = @_; - my $trackid = 0; - my $sth = $dbh->prepare( - "SELECT * FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$listtype AND tracknb=$trackindex" ); - my $nbrec = $sth->execute; - if($row = $sth->fetchrow_hashref){ - $trackid = $row->{trackid}; - } - $sth->finish; - return $trackid; -} - - -# Returns the current playlist (list of track ID's) -sub tracklist_get_all{ - my ($dbh, $playerid, $listtype) = @_; - my @playlist=(); - - my $sth = $dbh->prepare( - "SELECT * FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$listtype " - ."ORDER BY tracknb" ); - my $nbrec = $sth->execute; - while($row = $sth->fetchrow_hashref){ - $trackid = $row->{trackid}; - push @playlist, $row->{trackid}; - } - $sth->finish; - - return @playlist; -} - - - -############################################################ -### id3tag functions - -### returns the value of a id3 tag or other mp3 parameters for the specified file -### Possible tagcodes are - -### NEW version: - -# %a Artist [string] -# %b Number of corrupt audio frames [integer] -# %c Comment [string] -# %C Copyright flag [string] -# %e Emphasis [string] -# %E CRC Error protection [string] -# %f Filename without the path [string] -# %F Filename with the path [string] -# %g Musical genre [string] -# %G Musical genre [integer] -# %l Album name [string] -# %L MPEG Layer [string] -# %m Playing time: minutes only [integer] -# %n Track [integer] -# %O Original material flag [string] -# %o Stereo/mono mode [string] -# %p Padding [string] -# %Q Sampling frequency in Hz [integer] -# %q Sampling frequency in KHz [integer] -# %r Bit Rate in KB/s (type and meaning -# affected by -r option) -# %s Playing time: seconds only [integer] -# (usually used in conjunction with # %m) -# %S Total playing time in seconds [integer] -# %t Track Title [string] -# %u Number of good audio frames [integer] -# %v MPEG Version [float] -# %y Year [string] -# %% A single percent sign - -sub get_mp3info{ - my ($tagcode, $filename) = @_; - my $base = gdparams::gdbase(); - my $res = `mp3info -p "$tagcode" "$filename"`; - - ### Error cases - if ($tagcode eq "%S" && length($res)==0){ - my $res2 = `mpg123 -v -t -n 0 "$filename" 2> $base/tmp/gdinfo.tmp;grep Frame $base/tmp/gdinfo.tmp`; - $res2 =~ m/.*\[(.+):(.+).(.+)\].*/; - my $min = $1; - my $sec = $2; - $res = $min*60 + $sec; - print("playlength fixed to $res\n"); - } - if($tagcode eq "%r" && ($res eq "Variable")) - { #check for Variable Bitrate -# $res="Var".`mp3info -p %r -r m "$filename"`; ### don't predeed wit 'wav' - it messes up bitrate calculations of streamer - $res=`mp3info -p %r -r m "$filename"`; - return $res; - } - if ($tagcode eq "%r" && ($res <= 0 )){ - $res = "128"; - } - - return $res; -} - - -############################################################ -### audio metadata with ogg support. -# -# standard ogg tags according to -# http://www.xiph.org/ogg/vorbis/doc/v-comment.html -# -> ARTIST TITLE ALBUM TRACKNUMBER YEAR GENRE COMMENT -# and - -### typical output of ogginfo -#>ogginfo file.ogg -#filename=file.ogg -# -#serial=6039 -#header_integrity=pass -#ALBUM=ob die Engel auch Beine haben -#TITLE=Die Schampullamaschine -#ARTIST=Zentriert ins Antlitz -#DATE=2002 -#TRACKNUMBER=7 -#GENRE=Industrial -#ORGANIZATION=- -#COMMENT=ZIA Ogg Vorbis 1.0 Final -#vendor=Xiph.Org libVorbis I 20020717 -#version=0 -#channels=2 -#rate=44100 -#bitrate_upper=none -#bitrate_nominal=128000 -#bitrate_lower=none -#stream_integrity=pass -#bitrate_average=126915 -#length=50.248005 -#playtime=0:50 -#stream_truncated=false -# -#total_length=50.248005 -#total_playtime=0:50 - -sub oggfile_title{ - my($audiofile) = @_; - $line = `ogginfo "$audiofile" |grep --ignore-case ^title=`; - chop $line; - return substr($line, 6); -} -sub oggfile_artist{ - my($audiofile) = @_; - $line = `ogginfo "$audiofile" |grep --ignore-case ^artist=`; - chop $line; - return substr($line, 7); -} -sub oggfile_album{ - my($audiofile) = @_; - $line = `ogginfo "$audiofile" |grep --ignore-case ^album=`; - chop $line; - return substr($line, 6); -} -sub oggfile_year{ - my($audiofile) = @_; - $line = `ogginfo "$audiofile" |grep --ignore-case ^date=`; - chop $line; - $line = substr($line, 5); - if ($line =~ /\D*(\d*).*/){ - return $1; - } - else { - return 0; - } -} -sub oggfile_lengthsec{ - my($audiofile) = @_; - $line = `ogginfo "$audiofile" |grep --ignore-case ^length=`; - chop $line; - my $lengthsec = substr($line, 7); - return int($lengthsec); -} -sub oggfile_bitrate{ - my($audiofile) = @_; - $line = `ogginfo "$audiofile" |grep --ignore-case ^bitrate_nominal=`; - chop $line; - my $bitrate = substr($line, 16); - return (int(($bitrate/4000)+0.5))*4; # round to modulo 4 -} -sub oggfile_tracknumber{ - my($audiofile) = @_; - $line = `ogginfo "$audiofile" |grep --ignore-case ^tracknumber=`; - chop $line; - $line = substr($line, 12); - return int($line); -} -sub oggfile_genre{ - return ""; -} - -###~CU~ #FLAC_BEGIN - -############################################################ -### FLAC metadata functions -### Autor: Christian Uebber -### -### Uses metaflac (see http://flac.sourceforge.net/) -### for extracting VORBIS_COMMENT metadata. -### -### Future versions may also support extracting -### cue-sheet information. -### - -sub flacfile_title{ - my($audiofile) = @_; - $line = `metaflac --show-vc-field=title "$audiofile"`; - chop $line; - return substr($line, 6); -} - -sub flacfile_artist{ - my($audiofile) = @_; - $line = `metaflac --show-vc-field=artist "$audiofile"`; - chop $line; - return substr($line, 7); -} -sub flacfile_album{ - my($audiofile) = @_; - $line = `metaflac --show-vc-field=album "$audiofile"`; - chop $line; - return substr($line, 6); -} - -sub flacfile_year{ - my($audiofile) = @_; - $line = `metaflac --show-vc-field=date "$audiofile"`; - chop $line; - $line = substr($line, 5); - if ($line =~ /\D*(\d*).*/){ - return $1; - } - else { - return 0; - } -} - -sub flacfile_lengthsec{ - my($audiofile) = @_; - $line = `metaflac --show-total-samples "$audiofile"`; - chop $line; - return int($line/44100); ## Please verify (theoretically correct) -} - -sub flacfile_bitrate{ - my($audiofile) = @_; - $line = `metaflac --show-sample-rate "$audiofile"`; - chop $line; - return (int($line/1000)); # respect maximum field length -# return ($line/1000); # respect maximum field length -# return (int($line)/100); # respect maximum field length -} - -### Alternative: -#sub flacfile_type{ -# my($audiofile) = @_; -# $line = `metaflac --show-sample-rate "$audiofile"`; -# chop $line; -# if ($line="44100"){ -# return "cda"; -# } -# else { -# return ""; -# } -#} - -sub flacfile_tracknumber{ - my($audiofile) = @_; - $line = `metaflac --show-vc-field=tracknumber "$audiofile"`; - chop $line; - $line = substr($line, 12); - return int($line); -} -sub flacfile_genre{ - my($audiofile) = @_; - $line = `metaflac --show-vc-field=genre "$audiofile"`; - chop $line; - return substr($line, 6); -} - -sub flacfile_tracknumber{ - my($audiofile) = @_; - $line = `metaflac --show-vc-field=tracknumber "$audiofile"`; - chop $line; - $line = substr($line, 12); - return int($line); -} - -###~CU~ #FLAC_END - -############################################################ - -sub audiofile_title{ - my($audiofile) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - my $title = ""; - if ($ftype eq "mp3"){ - $title = get_mp3info("%t", $audiofile); - } - elsif ($ftype eq "ogg"){ - $title = oggfile_title($audiofile); - } - elsif ($ftype eq "flac"){ ###~CU~ - $title = flacfile_title($audiofile); - } - - if (length($title) > 0){ - return $title; - } - else { - use File::Basename; - return basename($audiofile); - } -} - - -sub audiofile_artist{ - my($audiofile) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - my $artist = ""; - if ($ftype eq "mp3"){ - $artist = get_mp3info("%a", $audiofile); - } - elsif ($ftype eq "ogg"){ - $artist = oggfile_artist($audiofile); - } - elsif ($ftype eq "flac"){ - $artist = flacfile_artist($audiofile); ###~CU~ - } - - if (length($artist) > 0){ - return $artist; - } - else { - use File::Basename; - return basename($audiofile); - } -} - - -sub audiofile_album{ - my($audiofile) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - if ($ftype eq "mp3"){ - return get_mp3info("%l", $audiofile); - } - elsif ($ftype eq "ogg"){ - return oggfile_album($audiofile); - } - elsif ($ftype eq "flac"){ - return flacfile_album($audiofile); ###~CU~ - } - else { - return "Album name"; - } -} - - -sub audiofile_year{ - my($audiofile) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - if ($ftype eq "mp3"){ - return get_mp3info("%y", $audiofile); - } - elsif ($ftype eq "ogg"){ - return oggfile_year($audiofile); - } - elsif ($ftype eq "flac"){ - return flacfile_year($audiofile); ###~CU~ - } - else { - return 1990; - } -} - - -sub audiofile_lengthsec{ - my($audiofile) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - if ($ftype eq "mp3"){ - return get_mp3info("%S", $audiofile); - } - elsif ($ftype eq "ogg"){ - return oggfile_lengthsec($audiofile); - } - elsif ($ftype eq "flac"){ - return flacfile_lengthsec($audiofile); ###~CU~ - } - else { - return 0; - } -} - - -sub audiofile_bitrate{ - my($audiofile) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - if ($ftype eq "mp3"){ - return get_mp3info("%r", $audiofile); - } - elsif ($ftype eq "ogg"){ - return oggfile_bitrate($audiofile); - } - elsif ($ftype eq "flac"){ - return flacfile_bitrate($audiofile); # ###~CU~ flac: return sampling rate / please check alternative - } - else { - return 128; - } -} - - -sub audiofile_tracknumber{ - my($audiofile, $default_tracknb) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - my $tracknb=0; - if ($ftype eq "mp3"){ - $tracknb = get_mp3info("%n", $audiofile); - } - elsif ($ftype eq "ogg"){ - $tracknb = oggfile_tracknumber($audiofile); - } - elsif ($ftype eq "flac"){ - $tracknb = flacfile_tracknumber($audiofile); - } - - if($tracknb > 0){ - return $tracknb; - } - else { - return $default_tracknb; - } -} - - -sub audiofile_genre{ - ### extracts id3 gerne code or genre string (depends on filetype) - ### returns GD-genre code - my($dbh, $audiofile) = @_; # must be full filename with path! - my $ftype = audio_filetype($audiofile); - if ($ftype eq "mp3"){ - my $id3genre = get_mp3info("%G", $audiofile); - return genre_id3togd($dbh, $id3genre); - } - elsif ($ftype eq "ogg"){ - my $genrestring = oggfile_genre($audiofile); - return genre_stringtogd($dbh, $genrestring); - } - elsif ($ftype eq "flac"){ - my $genrestring = flacfile_genre($audiofile); - return genre_stringtogd($dbh, $genrestring); - } - else { - return ""; - } -} - - -############################################################ - -sub audio_filetype{ - ### the filetype is derived form the filename extension - my($audiofile) = @_; - - if ($audiofile =~ /[Mm][Pp]3$/){ - return "mp3"; - } - if ($audiofile =~ /ogg$/){ - return "ogg"; - } - if ($audiofile =~ /flac$/){ - return "flac"; - } -} - - - -############################################################ - -sub get_bitrate_str{ - # returns format/bitrate in kBit/s (ex. "mp3 128", "ogg 112") - # takes as argument the audio file name with its full path. - my ($audiofile) = @_; - my $bitrate = audiofile_bitrate($audiofile); - my $ftype = audio_filetype($audiofile); - return "$ftype $bitrate"; -} - -sub bitrate_str_to_format_param{ - # gets a bitrate string like "mp3 128" and returns array ("mp3", "128") - my ($bitratestr) = @_; - my ($audiofmt, $audioparam) = split ' ', $bitratestr; # split "mp3 128" - if (length($audiofmt)<2){ # something went wrong - $audiofmt = "mp3"; # set reasonable default values - $audioparam = 128; - } - return ($audiofmt, $audioparam); -} - - -sub get_full_audiofile_path{ - # returns the full path of an audio file in a 00, 01, 02, ... directory - # or an empty string if the file doesn't exist - my ($audiofile) = @_; - my $base = gdparams::gdbase(); - my $fname = `ls $base/[0-9][0-9]/$audiofile`; # get full path - chop($fname); - return $fname; -} - -############################################################ -### Returns the first music directory with enough space - # to save ripped files. If no large enough directory is found - # an empty string is returned. - # Parameter: minimum free space in Mbytes - -sub get_ripdirectory{ - my ($minfreeMbytes) = @_; - my $base = gdparams::gdbase(); - - ### Get mp3 directories - my @mdir = gdparams::mp3dirs(); - - ### Get an mp3 directory with enough space left (1GB) - my $i=0; - my @dfres; - my $mbfree; - my $ripdir=""; - - while($i < @mdir){ - if (-d "$base/$mdir[$i]"){ - @dfres = split / +/, `df -m $base/$mdir[$i]|tail -1`; - $mbfree = $dfres[3]; - #print "$base/$mdir[$i] has $mbfree MB free \n"; - if($mbfree > $minfreeMbytes){ - $ripdir = $mdir[$i]; - last; # break - } - } - else{print "$base/$mdir[$i] is not a directory or does not exist\n";} - $i++; - } - #print("Rip directory: $ripdir \n"); - return $ripdir; -} - - -############################################################ -### import booklet/cover images -sub import_cover_images{ - # imports the jpeg images in a directory and associates them to an album - # the images are imported in lexical order. - # Naming scheme: trxx(cd-id)-(num).jpg, where num is an automatically - # incremented counter. The file imgxx(cd-id)-00.jpg is the front cover, - # the other are the following pages in a booklet. - - # Parameters: 1) dbh, - # 2) full directory path, - # 3) cd-id (like 0x10ac77e0, xx00001496) - # 4) test? (if set to 1, only show what would be done) - - my ($dbh, $fullpath, $cdid, $test) = @_; - - my ($sourcefile, $targetfile); - my $base = gdparams::gdbase(); - print "import images at $fullpath for id $cdid\n"; - - if (length($cdid)<10){ - print "Error (import_cover_images): illegal format of cdid '$cdid'\n"; - return; - } - - opendir INBOX, "$fullpath"; - my @imgfilelist = sort (grep /\.jpg$/, readdir INBOX); - closedir INBOX; - - #print "imgs: ".join (":",@imgfilelist)."\n"; - if (scalar(@imgfilelist) > 0){ - - ### Get a directory with enough space left (1GB) - my $ripdir=get_ripdirectory($gdparams::minfreehdspace); - - if($ripdir ne ""){### put image-file in music directory - my $imgnum = 0; - - foreach $sourcefile (@imgfilelist){ - $targetfile = sprintf ("$base/$ripdir/img%s-%02ld.jpg", $cdid, $imgnum); - if ($test){ - print "test: move '$fullpath/$sourcefile' to '$targetfile'\n"; - } - else{ - print "move '$fullpath/$sourcefile' to '$targetfile'\n"; - system "mv \"$fullpath/$sourcefile\" \"$targetfile\""; - if ($imgnum == 0){ - - my $sqlcmd = ("UPDATE album SET coverimg='img$cdid-00.jpg' WHERE cddbid = SUBSTRING('$cdid',3)"); - #print"\$dbh->do($sqlcmd);\n" - $dbh->do($sqlcmd); - } - } - $imgnum += 1; - }# end foreach - } - else{ - print("Not enough space left on disc \n"); - } - } - else{ - print "no jpeg images found in $fullpath \n"; - } -} - - -############################################################ -### common tool routines - -### returns highest number of imported (=not recorded from audio-CD, =no -### CDDB-ID associated) mp3 tracks. -### Parameter: database handle. -sub last_imported_tracknb{ - my ($dbh) = @_; - - my $mp3num; - my $trkseth = $dbh->prepare('select mp3file from tracks '. # get last record - 'where mp3file like "trxx________%" order by mp3file desc limit 1'); -# 'where mp3file like "trxx%" order by mp3file desc limit 1'); - my $nbmp3files = $trkseth->execute; - if ($nbmp3files > 0){ - $tracks = $trkseth->fetchrow_hashref; - $tracks->{mp3file} =~ m/trxx([0-9]*)\.\w/; # extract number - $mp3num = $1; - } - else{ - $mp3num = 0; - } - $trkseth->finish; - return $mp3num; -} - - -### Translates an id3 genre (numeric) to a GiantDisc genre code (string) -### Parameters: database handle, id3-genre. -### Returns: GiantDisc genre code (or empty string, if no match found) -sub genre_id3togd{ - my ($dbh, $id3genre)= @_; - - my $gdcode = ""; - if(length($id3genre)>0 && $id3genre >= 0){ - my $genseth = $dbh->prepare('select * from genre where id3genre="'. $id3genre . '" '); - my $nbgenres = $genseth->execute; - if ($nbgenres > 0){ - my $genres = $genseth->fetchrow_hashref; - $gdcode = $genres->{id}; - } - $genseth->finish; - } - #print("Translating id3:\"$id3genre\" to gdgenre \"$gdcode\" \n\n"); - return $gdcode; -} - -### Translates a genre string to a GiantDisc genre code (string) -### Parameters: database handle, genre string. -### Returns: GiantDisc genre code (or empty string, if no match found) -sub genre_stringtogd{ - my ($dbh, $genrestring)= @_; - - my $gdcode = ""; - if(length($genrestring)>0){ - my $genseth = $dbh->prepare( # get best=shortest match - "SELECT id,length(genre) AS len FROM genre " - ."WHERE genre like \"%".$genrestring."%\" ORDER BY len"); -# 'select id, from genre where genre="'. $genrestring . '" '); - my $nbgenres = $genseth->execute; - if ($nbgenres > 0){ - my $genres = $genseth->fetchrow_hashref; - $gdcode = $genres->{id}; - } - $genseth->finish; - } - #print("Translating genrestring:\"$genrestring\" to gdgenre \"$gdcode\" \n\n"); - return $gdcode; -} - - - -############################################################################### - -sub iso2ascii{ - # converts all non-ascii characters in the passed string as good - # as possible to ascii characters, and returns the result - - my ($str) = @_; - - $str =~ tr/\xc0-\xc6/A/; - $str =~ tr/\xc7/C/; - $str =~ tr/\xc8-\xcb/E/; - $str =~ tr/\xcc-\xcf/I/; - $str =~ tr/\xd0\xd1/DN/; - $str =~ tr/\xd2-\xd8/O/; - $str =~ tr/\xd9-\xdc/U/; - $str =~ tr/\xdd\xde\xdf/YTs/; - - $str =~ tr/\xe0-\xe6/a/; - $str =~ tr/\xe7/c/; - $str =~ tr/\xe8-\xeb/e/; - $str =~ tr/\xec-\xef/i/; - $str =~ tr/\xf0\xf1/dn/; - $str =~ tr/\xf2-\xf8/o/; - $str =~ tr/\xf9-\xfc/u/; - $str =~ tr/\xfd\xfe\xff/yts/; - - $str =~ tr/\xa0-\xff/_/; - - return $str; -} - -sub ascii2filename{ - # converts all ascii characters in the passed string as good - # as possible to ascii characters that are allowed in filenames, - #and returns the result - - my ($str) = @_; - - $str =~ tr/\//-/; # translate / to - - $str =~ tr/\x2f/_/; - $str =~ s/"/''/g; - - return $str; -} - -############################################################################### - - -1; -# diff --git a/scripts/gdimport.pl b/scripts/gdimport.pl deleted file mode 100755 index 438b07c..0000000 --- a/scripts/gdimport.pl +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/perl - -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2000, Rolf Brugger -# -################################################## - - -# Import script for mp3 tracks. -# -# Non interactive batch import of mp3 tracks. Mp3 informations (title, artist, etc) -# are read from the id3-tags. Well defined id3-tags are mandatory for a -# inclusion of mp3 tracks and a proper update of the database. - - -use lib '/home/lvw/Development/muggle-plugin/scripts'; -#use lib '/home/andi/muggle/import'; -use gdparams; -use gdgentools; -use gddb; -use DBI; -use Cwd; # get current working dir - -### main - #print ("Args are: @ARGV \n"); - if ($#ARGV < 0){ - print "\nusage: gdimport.pl [options] audiofiles... \n"; - print "\nimports the specified audio files into the GiantDisc system.\n"; - print "It reads the associated metadata (id3/vorbis tags) of each file and \n"; - print "creates a track record for each of it. \n"; - print "Other than in GD, files are not copied!!\n"; - print "\n"; - print "Currently supported file formats: mp3, ogg vorbis, flac.\n"; - print "\n"; - print "Instead of a set of audio files a directory can be specified. In\n"; - print "this case, all audio tracks in the directory are imported. This is\n"; - print "particularly useful to import a bunch of albums with the following\n"; - print "command typed in the shell:\n"; - print "\n"; - print " find . -type d -print -exec gdimport.pl -a {} \\;\n"; - print "\n"; - print "Options:\n"; - print " -t Test. Just display what would be done, whithout changing\n"; - print " the database or copying/moving/linking files\n"; - print " It is recommended to test an import session before really doing it\n"; - print "\n"; - print " -a Interpret the audio files as tracks of an album. An album record\n"; - print " is created and the tracks are imported as usual.\n"; - print " Note: - The first audio file must have a valid album metadata tag\n"; - print " (i.e. id3 tag) to get the album title.\n"; - print " - only one album can be imported at a time\n"; - print " - default values: rating=-, type=1, source=CD.\n"; - print "\n"; - print " -i cddbid importdir\n"; - print " Import all jpg images of the directory 'importdir' and associate\n"; - print " them to the album specified by 'cddbid' (include prefix '0x', 'xx'!). \n"; - print "\n"; - print "\n"; - exit; - } - - #exit; - ### Init constants/variables - my ($fileformat, $cddbid); - my $makealbum = 0; - my $album_created = 0; - my $linkfiles = 1; - my $movefiles = 0; - my $moveimages = 0; - my $test = 0; - my $tracknb = 1; - my ($dbh, $trifile); - my $base = gdparams::gdbase(); # music files base - - ### Open database connection - print ("### Open database connection\n"); - $dbh = DBI->connect("DBI:mysql:GiantDisc", "music", undef) - or die "unable to connect to GiantDisc db"; - - ### init other variables - my $fileid = gdgentools::last_imported_tracknb($dbh); - my @trackrow; - - - ### extract all options first - my $shifted=1; - while ($shifted){ - if ($ARGV[0] eq "-a"){ - print "set option make album\n"; - $makealbum = 1; - shift @ARGV; - } - elsif ($ARGV[0] eq "-l"){ - print "set option link files\n"; - $linkfiles = 1; - shift @ARGV; - } - elsif ($ARGV[0] eq "-m"){ - print "set option move files\n"; - $movefiles = 1; - shift @ARGV; - } - elsif ($ARGV[0] eq "-i"){ - print "import booklet images\n"; - $moveimages = 1; - shift @ARGV; - $cddbid = shift @ARGV; - } - elsif ($ARGV[0] eq "-t"){ - print "set option TEST\n"; - $test = 1; - shift @ARGV; - } - else { - $shifted = 0; - } - } - - if ($moveimages){ - $dirname = $ARGV[0]; - if (-d $dirname){ - # ok. It's a directory - gdgentools::import_cover_images($dbh, $dirname, $cddbid, $test); - } - else{ - print "Error importing images: '$dirname' is not a directory\n"; - } - exit; - } - - - my @musicfiles; - my $dirname=""; - ### Is the first file argument a directory? - if (-d $ARGV[0]){ - # It's a directory -> read the audio filenames in it - $dirname = $ARGV[0]; - opendir CURDIR, $dirname; - @musicfiles = grep /[\.f](mp3$)|(ogg$)|(flac$)/, readdir CURDIR; # match .mp3, .ogg, flac -- Thanks to Merlot! -# @musicfiles = grep /[\.f][Mmol][Ppga][c3g]$/, readdir CURDIR; # match .mp3, .ogg, flac - @musicfiles = sort @musicfiles; - closedir CURDIR; - chdir $dirname; - } - else{ - # the remaining arguments seem to be audio filenames - @musicfiles = @ARGV; - } - - ### Loop through audio files - while ($curfile = shift @musicfiles){ - $fileformat = gdgentools::audio_filetype($curfile); - print "Importing $fileformat file: $curfile\n"; - $fileid += 1; -# $audiofile = sprintf("trxx%08ld.%s", $fileid, $fileformat); - $audiofile = $dirname ."/". $curfile; - - # get info from metatags - $trackrow->{title} = gdgentools::audiofile_title($curfile); - $trackrow->{artist} = gdgentools::audiofile_artist($curfile); - $trackrow->{genre2} = ""; - $trackrow->{year} = gdgentools::audiofile_year($curfile); - $trackrow->{lang} = "-"; - $trackrow->{type} = 1; # medium - $trackrow->{rating} = 0; - $trackrow->{length} = gdgentools::audiofile_lengthsec($curfile); - $trackrow->{source} = 0; # CD - if(!$album_created){ - if($makealbum && !$album_created){ - $trackrow->{sourceid} = sprintf("%08ld", $fileid); # fake CDDB-ID - $albumtitle = gdgentools::audiofile_album($curfile); - if (length($albumtitle)<1){ - $albumtitle = $dirname; - } - } - else{ - $trackrow->{sourceid} = ""; # no CDDB-ID available - } - } - $trackrow->{tracknb} = gdgentools::audiofile_tracknumber($curfile, $tracknb); - $trackrow->{mp3file} = $audiofile; - $trackrow->{condition} = 0; # OK - $trackrow->{voladjust} = 0; - $trackrow->{lengthfrm} = 0; - $trackrow->{startfrm} = 0; - $trackrow->{bpm} = 0; - $trackrow->{bitrate} = $fileformat." ".gdgentools::audiofile_bitrate($curfile); - $trackrow->{created} = ""; - $trackrow->{modified} = ""; - $trackrow->{id} = ""; # a new one will be automatically generated - - # get genre if available and translate to gd-genre - $trackrow->{genre1} = gdgentools::audiofile_genre($dbh, $curfile); - - if (length($trackrow->{title})>0 && length($trackrow->{artist})>0){ - ### create track record - - print "Insert track in database:$trackrow->{artist} - $trackrow->{title} \n"; - if (!$test){ - if (-e $curfile){ - $trid = gddb::insert_track_record($dbh, - $trackrow->{artist},$trackrow->{title}, - $trackrow->{genre1},$trackrow->{genre2},$trackrow->{year}, - $trackrow->{lang},$trackrow->{type},$trackrow->{rating}, - $trackrow->{length},$trackrow->{source},$trackrow->{sourceid}, - $trackrow->{tracknb},$trackrow->{mp3file}, - $trackrow->{condition},$trackrow->{voladjust}, - $trackrow->{lengthfrm},$trackrow->{startfrm},$trackrow->{bpm}, - $trackrow->{bitrate}, - $trackrow->{created},$trackrow->{id}); - } - else{ - print "Error: file ".$trackrow->{mp3file}." dows not exist. Skipping\n"; - } - } - else{ - print "TEST>".join ':', " art",$trackrow->{artist}," tit",$trackrow->{title}, ," gen1",$trackrow->{genre1}," gen2",$trackrow->{genre2}," year",$trackrow->{year}, " lang",$trackrow->{lang}," tp",$trackrow->{type}," ratng",$trackrow->{rating}, " len",$trackrow->{length}," src",$trackrow->{source}," cddbid",$trackrow->{sourceid}, " trnb",$trackrow->{tracknb}," file",$trackrow->{mp3file}, " cond",$trackrow->{condition}," vol",$trackrow->{voladjust}, " lenfrm",$trackrow->{lengthfrm}," strt",$trackrow->{startfrm}," bpm",$trackrow->{bpm}, " bitrate",$trackrow->{bitrate}, " created",$trackrow->{created}," id",$trackrow->{id},"\n\n"; - } - - ### create album record - if ($makealbum && !$album_created && length($albumtitle)>0){ - print "Insert album in database:$trackrow->{artist} - $albumtitle \n"; - if (!$test){ - $dbh->do("INSERT INTO album (artist,title,modified,cddbid) " - ."VALUES (".$dbh->quote($trackrow->{artist}) - .",".$dbh->quote($albumtitle) - .",CURDATE() " - .",'".$trackrow->{sourceid}."') "); - } - $album_created = 1; - } - - ### put mp3file to target directory - $tracknb++; - } - else{ - print "No title or artist available for $curfile (skipped)\n"; - } - - }#end loop - print("### close database connection\n"); - $dbh->disconnect; - - exit; - - - -############################################################################### - - - - diff --git a/scripts/gdio.pm b/scripts/gdio.pm deleted file mode 100755 index 0fd6e2f..0000000 --- a/scripts/gdio.pm +++ /dev/null @@ -1,349 +0,0 @@ -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2000, Rolf Brugger -# -################################################## - -package gdio; - -#use lib '/home/music/bin'; - -use IO::Socket; -use strict; - -# GiantDisc IO routines - -### Constants & global variables -my $no_mode = 0; -my $rs232_mode = 1; -my $tcpip_srv_mode = 2; -my $tcpip_cli_mode = 3; - -my $commMode = $no_mode; - - -### Serial communication parameters: -my $serdev; -my $serbaud; - -### TCP/IP communication parameters -my $gdsocket; -my $connection; - -undef $connection; - -################################################################# -### initialization routines: - -sub serio_init{ - ### Init serial port - my $logtarget; - ($logtarget, $serdev, $serbaud) = @_; - - print("### Init serial port $serdev at $serbaud bps\n"); - - # Config serial port: - # - disable echo - # - enable hardware flow control - system("stty $serbaud -echo crtscts <$serdev"); - open (SERIN, "<$serdev"); - open (SEROUT, ">$serdev"); - - ### Alternative? - #sysopen (SERIN, $serdev, O_RDRW | O_NODELAY | O_NOCTTY) - #or die "Can't open $serdev: $!"; - #my $ofh = select(SEROUT); $| = 1; - #select($ofh); - - $commMode = $rs232_mode; -} - - - -sub tcpip_srv_init{ - my ($logtarget, $tcpiphost, $tcpipport) = @_; - - print "Open socket on $tcpiphost at port $tcpipport as server\n"; - $gdsocket = new IO::Socket::INET( LocalHost => $tcpiphost, - LocalPort => $tcpipport, - Proto => 'tcp', - Listen => 1, - Reuse => 1); - - die "could not open socket: $!\n" unless $gdsocket; - - $commMode = $tcpip_srv_mode; -} - -sub tcpip_cli_init{ - my ($logtarget, $tcpiphost, $tcpipport) = @_; - - print "Open socket on $tcpiphost at port $tcpipport as client\n"; - $gdsocket = new IO::Socket::INET( PeerAddr => $tcpiphost, - PeerPort => $tcpipport, - Proto => 'tcp'); - - die "could not open socket: $!\n" unless $gdsocket; - - $commMode = $tcpip_cli_mode; -} - -################################################################# - -sub serio_getline { # returns one line - my $line = ; - return $line; -} - -sub putline_nack { # sends one line, without waiting for acknowledge (currently not used) - my ($line)=@_; - print (SEROUT "$line\n"); -} - -sub serio_putline -### The soutine returns true, if the client does not want to receive -### more lines (acknowledged with a 'stop' command). -### -### If the routines has to wait too long for an acknowledge (>20sec), the -### transmission is aborted. Such situations could be caused by crashed -### clients or broken transmission lines. -{ - my ($line)=@_; - my $ackstring; - print (SEROUT "$line\n"); - ### wait for acknowledge (max 20 sec) - eval{ - local $SIG{ALRM} = sub { die "ACK timeout" }; # set Alarm event handler - alarm 20; # 20 sec timeout - - $ackstring=gdio::getline(); - chop($ackstring); - - alarm 0; # deactivate Alarm - }; - if ($@ and $@ =~ /ACK timeout/ ) { - print ("\nACKNOWLEDGE TIMEOUT\n\n"); - $ackstring = "s"; #abort current transmission - }; - - if($ackstring eq "s"){ - print ("\nACK-stop received\n"); - return 1; # stop transmission - } - else{ - return 0; - } -} - -################################################################# -# -# If we experienced and error reading for the socket, then -# $connection would be set to "0" and so we need to wait for the -# palm client to reconnect. -# -# Thanks to Viktor for the tcpip code snippets - -sub tcpip_srv_check_connection{ - my $timeout; - print "connection open? ... "; - if ( ! defined $connection ) { - print "no\n"; - print "Server accepting socket ... \n"; - $connection = $gdsocket->accept(); - $timeout = $connection->timeout(); - #print "Server Connection time out is [$timeout] \n"; - $timeout = $connection->timeout(60); - #print "Server Connection time out set to [$timeout] \n"; - } - else{ - print "yes\n"; - } - - return $connection; -} - -# -# If we have an error reading the input assume client has -# disconnected. We close the socket and return a "NULL" command -# -sub tcpip_srv_getline{ - print "server listening ...\n"; - my $line; - if ( $line = <$connection> ) { - #print "line recieved: [$line] \n"; - return $line; - } - else { - print "Client disconnected ...\n"; - close ($connection) ; - undef $connection; - return "NULL\n"; - } -} - -sub tcpip_cli_getline{ - print "listening ...\n"; - my $line; - if ( $line = <$gdsocket> ) { - #print "line recieved: [$line] \n"; - return $line; - } -} - - -sub tcpip_srv_getline_SINGLECHAR{ ### just for tests ... - print "server listening ...\n"; - my $line=""; - my $cbuf; - while ( read $connection, $cbuf, 1 ) { - print "char recieved: [$cbuf] \n"; - $line .= $cbuf; - last if ($cbuf eq "\n"); - } - print "line recieved: [$line] \n"; - return $line; -} - - -sub tcpip_srv_putline{ -# There is currently a problem with this routine. -# The Palm client should be able to interrupt the transmission, if too many -# lines are sent to it. When the client shuts down the socket, the server does -# not directly recognize this. The 'if (print $connection $line."\n" )' is never -# false, unless the Palm is turned off. -# This has probably to do with the fact, that there is already sent data in a -# buffer? -# -# The current workaround is, that the Palm receives the supernumerous lines, but -# doesn't store them. Not very efficient ... :-( - - my ($line)=@_; - #print "Sending to socket: \"$line\"\n"; -#if (defined($connection->connected())){ -#print "WE ARE CONNECTED\n";} -#else{ -#print "WE ARE _NOT_ CONNECTED\n";} - - if (print $connection $line."\n" ) { - return 0; #don't stop - } - else{ - print "Client disconnected ...\n"; - close ($connection) ; - undef $connection; - return 1; #stop transmission - } -} - - -sub tcpip_cli_putline_NOACK{ - my ($line)=@_; - #print "Sending to socket: \"$line\"\n"; - - if (print $gdsocket $line."\n" ) { - return 0; #don't stop - } - return 0; -} -sub tcpip_cli_putline -{ - my ($line)=@_; - my $ackstring; - print $gdsocket $line."\n"; - ### wait for acknowledge (max 20 sec) - eval{ - local $SIG{ALRM} = sub { die "ACK timeout" }; # set Alarm event handler - alarm 20; # 20 sec timeout - - $ackstring=<$gdsocket>; - chop($ackstring); - - alarm 0; # deactivate Alarm - }; - if ($@ and $@ =~ /ACK timeout/ ) { - print ("\nACKNOWLEDGE TIMEOUT\n\n"); - $ackstring = "s"; #abort current transmission - }; - - if($ackstring eq "s"){ - print ("\nACK-stop received\n"); - return 1; # stop transmission - } - else{ - return 0; - } -} - - - -#sub close_connection -#{ -# print "Closing Connection - $connection \n"; -# close($connection) -#} - -################################################################# -### public routines: - -sub check_connection -{ - if ($commMode == $rs232_mode){ - return 1; - } - elsif ($commMode == $tcpip_srv_mode){ - return tcpip_srv_check_connection(); - } - elsif ($commMode == $tcpip_cli_mode){ - return 1; - } - else{ - print "Error: unknown communication mode\n"; - exit; - } -} - -sub putline -{ - if ($commMode == $rs232_mode){ - return serio_putline(@_); - } - elsif ($commMode == $tcpip_srv_mode){ - return tcpip_srv_putline(@_); - } - elsif ($commMode == $tcpip_cli_mode){ - return tcpip_cli_putline(@_); - } - else{ - print "Error: unknown communication mode\n"; - exit; - } -} - -sub getline -{ - if ($commMode == $rs232_mode){ - return serio_getline(); - } - elsif ($commMode == $tcpip_srv_mode){ - return tcpip_srv_getline(); - } - elsif ($commMode == $tcpip_cli_mode){ - return tcpip_cli_getline(); - } - else{ - print "Error: unknown communication mode\n"; - exit; - } -} - - -################################################################# - -END{ - print ("io modul finished\n"); -} - -# -1; diff --git a/scripts/gdparams.pm b/scripts/gdparams.pm deleted file mode 100755 index 35aa319..0000000 --- a/scripts/gdparams.pm +++ /dev/null @@ -1,336 +0,0 @@ -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2000, Rolf Brugger -# -################################################## - -package gdparams; - -#use lib '/usr/local/bin'; -use strict; -use Getopt::Long; - -my @mp3dirs; - -############################################################ -### Global variables (accessible from anywhere) - -# IMPORTANT ERROR! all modules refer to gdparms::varname instead -# of gdparams::varname -# ... however, it works ... I just don't know why :-/ - - -#my ($dbhost, $shutdowncmd, $extmp3player, $defrecbitrate, -# $systemonline); - - -### Global constants -my $minfreehdspace = 1000; # minimal space required for a directory, - # that it can be used to record and - # compress a cd (in MB) - - -############################################################ -### - -sub get_configfile_params{ - ### Parameters: Call-by-reference - - my ($dbhost, - $commmode, - $serialdevice, $serialspeed, - $tcpiphost, $tcpipport, - $playerhost, $playertype, $snddevice, - $playerapp, $mp3playerparams, $oggplayerparams, $ptlogger, - $mp3encoder, $logtarget, $shutdowncmd, - $extmp3player, $defrecbitrate, $systemonline - ) = @_; - - - ### Read configuration file im home directory - open (CONF, "< ".gdbase()."/.gdconfig") - or die "Error: could not open configuration file .gdconfig\n"; - - my $line; - while(){ - $line = $_; - chop $line; - $line =~ tr/\r/ /; - # \w word-char - # \w non-word-char - # \s whitespace-char - # \S non-whitespace-char - - if($line =~ m/^dbhost.*=\s*(\S+)/i ){ - $$dbhost = $1; - } - - if($line =~ m/^commmode.*=\s*([0-9]+)/i ){ - $$commmode = $1; - } - - if($line =~ m/^serialdevice.*=\s*([\w\/]+)/i ){ - $$serialdevice = $1; - } - - if($line =~ m/^serialspeed.*=\s*([0-9]+)/i ){ - $$serialspeed = $1; - } - - if($line =~ m/^tcpiphost.*=\s*(\S+)/i ){ - $$tcpiphost = $1; - } - - if($line =~ m/^tcpipport.*=\s*([0-9]+)/i ){ - $$tcpipport = $1; - } - - if($line =~ m/^playerhost.*=\s*(\S+)/i ){ - $$playerhost = $1; - } - - if($line =~ m/^playertype.*=\s*([0-9]+)/i ){ - $$playertype = $1; - } - - if($line =~ m/^playerapp.*=\s*(\S+)/i ){ - $$playerapp = $1; - } - - if($line =~ m/^playerparams.*=\s*(.*)$/i ){ # match anything to end of line - print "\nWARNING: as of v1.20 the option 'playerparams' has been replaced by\n"; - print " 'mp3playerparams' and 'oggplayerparams'. Update .gdconfig accordingly\n\n"; - #$$playerparams = $1; - } - if($line =~ m/^mp3playerparams.*=\s*(.*)$/i ){ # match anything to end of line - $$mp3playerparams = $1; - } - if($line =~ m/^oggplayerparams.*=\s*(.*)$/i ){ # match anything to end of line - $$oggplayerparams = $1; - } - - if($line =~ m/^shutdowncmd.*=\s*(.*)$/i ){ # match anything to end of line - $$shutdowncmd = $1; - } - - if($line =~ m/^extmp3player.*=\s*(.*)$/i ){ # match anything to end of line - $$extmp3player = $1; - } - - if($line =~ m/^sounddevice.*=\s*(\S+)/i ){ - $$snddevice = $1; - } - - if($line =~ m/^mp3encoder.*=\s*(\w+)/i ){ - $$mp3encoder = $1; - } - - if($line =~ m/^logtarget.*=\s*(\w+)/i ){ - if ($1 eq "stdout" || $1 eq "logfile" || $1 eq "devnull"){ - $$logtarget = $1; - } - } - - if($line =~ m/^ptlogger.*=\s*(\S+)/i ){ - $$ptlogger = $1; - } - - if($line =~ m/^defrecbitrate.*=\s*(.+)/i ){ # match anything to end of line - $$defrecbitrate = $1; - } - - if($line =~ m/^systemonline.*=\s*([0-9])/i ){ - $$systemonline = $1; - } - - } - - close (CONF); -} - - -############################################################ -### - -sub get_otherclients_params{ - ### Parameters: Call-by-reference - - my ($keymap # type: reference to an empty hash - )= @_; - - - ### Read configuration file im home directory - open (CONF, "< ".gdbase()."/.gdconfig") - or die "Error: could not open configuration file .gdconfig\n"; - - my $line; - while(){ - $line = $_; - chop $line; - $line =~ tr/\r/ /; - # \w word-char - # \w non-word-char - # \s whitespace-char - # \S non-whitespace-char - - if($line =~ m/^keymap.*=\s*(\S+)\s*-\s*(\S+)\s*$/i ){ - print ("keymap: key: $1, val: $2\n"); - $$keymap{$1} = $2; - } - - - } - - close (CONF); -} - - -############################################################ -### translate logtarget string to integer -sub logtarget_to_int{ - my ($logtargetstr) = @_; - if ($logtargetstr eq "devnull") {return 0;} - if ($logtargetstr eq "logfile") {return 1;} - if ($logtargetstr eq "stdout" ) {return 2;} - return 2; # default -} - - -############################################################ -### - -sub get_commandline_params{ - - my ($dbhost, - $commmode, - $serialdevice, $serialspeed, - $tcpiphost, $tcpipport, - $playerhost, $playertype, $snddevice, - $playerapp, - $mp3playerparams, $oggplayerparams, - $ptlogger, - $mp3encoder, $logtarget) = @_; - # ARGV passed implicitly - - - my $help; - $Getopt::Long::autoabbrev=1; - GetOptions( - "help" => \$help, - "dbhost:s" => $dbhost, # $dbhost is already a reference - "commmode:i" => $commmode, - "serialdevice:s" => $serialdevice, - "serialspeed:i" => $serialspeed, - "tcpiphost:s" => $tcpiphost, - "tcpipport:i" => $tcpipport, - "playertype:i" => $playertype, - "playerhost:s" => $playerhost, - "sounddevice:s" => $snddevice, - "mp3playerparams=s" => $mp3playerparams, - "oggplayerparams=s" => $oggplayerparams, - "ptlogger:s" => $ptlogger, - "mp3encoder:s" => $mp3encoder, - "logtarget:s" => $logtarget - ); - - if ($help){ - print <connect("DBI:mysql:GiantDisc:".$dbhost, "music", undef) - or die "unable to connect to GiantDisc db"; - - - if (gdupdate::db_check_update_132($dbh)){ - print "\n Update to database structure version 1.32 required\n"; - print " please run command 'gdupdatedb.pl'\n\n"; - exit; - } - - ### fix records (bug in recording routines prior to version 0.92) - #gdupdate::fix_leading_slash_bug($dbh); - - ### UPDATE DATABASE to new versions -# gdupdate::db_update_094($dbh); -# gdupdate::db_update_095($dbh); -# gdupdate::db_update_096($dbh); -# gdupdate::db_update_097($dbh); -# gdupdate::db_update_111($dbh); -# gdupdate::db_update_112($dbh); -# gdupdate::db_update_114($dbh); -# gdupdate::db_update_131($dbh); -} - - -sub init_player{ -# Checks, if a player record with the same ipaddr and uichannel exists. -# If it doesn't exist, a new record is created. -# It returns the new generated player-ID -# -# -# - my ($ipaddr, $uichannel, $logtarget, $cdripper, - $mp3encoder, $cdromdev, $cdrwdev) = @_; - $playerid = 0; #default - my $sth = $dbh->prepare( - "SELECT * FROM player " - ."WHERE ipaddr='$ipaddr' AND uichannel='$uichannel'" ); - $sth->execute; - - my $row; - if($row = $sth->fetchrow_hashref){ - # matching player record found - $playerid = $row->{id}; - } - else{ - # get highest id in db - my $sth2 = $dbh->prepare( - "SELECT * FROM player ORDER BY id DESC LIMIT 1" ); - $sth2->execute; - if($row = $sth2->fetchrow_hashref){ - # matching player record found - $playerid = $row->{id}+1; - } - #else playerid has default value 0 - $sth2->finish; - - ### Create player record - my $retval = $dbh->do("INSERT INTO player SET " - ."ipaddr ='$ipaddr', " - ."uichannel ='$uichannel', " - ."id = $playerid, " - ."logtarget = $logtarget, " - ."cdripper ='$cdripper', " - ."mp3encoder='$mp3encoder', " - ."cdromdev ='$cdromdev', " - ."cdrwdev ='$cdrwdev'"); - } - $sth->finish; - - ### delete all tracklists (riplist, burnlist and comprlist) of this player - $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$rp_list"); #riplist - $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$co_list"); #compressionlist - $dbh->do("DELETE FROM tracklistitem WHERE playerid=$playerid AND listtype=$cr_list"); #cdrecordlist - - return $playerid; -} - - -sub init_playerstate{ -# -# global var: playerid - - my ($playerhost, $snddevice, $playerapp, $ptlogger, $playertype); - my ($mp3playerparams, $oggplayerparams); - ($playerhost, $playertype, $snddevice, $playerapp, $mp3playerparams, $oggplayerparams, $ptlogger) = @_; - my $playerparams = join '\t', $mp3playerparams, $oggplayerparams; - my $audiochannel = $audchannel0; - gdgentools::pll_new_playstate($dbh, $playerid, $audiochannel, - $playertype, $snddevice, $playerapp, $playerparams, $ptlogger); - - ### create message queue for this player-id/audio-channel - gdgentools::plmsg_newqueue($playerid, $audiochannel); - - ### init soundcard driver - gdsoundcard::sndc_init($playertype, $playerhost, $audiochannel); -} - - - -sub open_rippipe{ - ### Open recording pipe - open(RIPPIPE, "| gdripcd.pl $dbhost $playerid $audchannel0") - or die "can't fork RIP-PIPE\n"; - autoflush RIPPIPE 1; - $rippipe_open=1; -} - -sub close_rippipe{ - ### close recording pipe - close(RIPPIPE); - $rippipe_open=0; -} - -############################################################ -### TOOL ROUTINES ### -############################################################ - -############################################################ - -sub truncstr{ -### truncstr (string, length, noellipsis) - # If the string is longer than 'length' characters, it is truncated and - # '..' is appended => max length is 'length'+2 - if (length($_[0]) > $_[1]){ - $_[0] = substr ($_[0], 0, $_[1]); - if(!$_[2]){ # add ellipsis - $_[0] .= ".."; - } - } -} - - -############################################################ -### DB CREATION & UPDATE ### -############################################################ - -############################################################ -### Creates/updates an album record -sub replace_album_record{ - my ($artist,$title,$cddbid) = @_; - - my $base = gdparams::gdbase(); - - if(length($artist)==0){$artist="-";}; - if(length($title)==0) {$title="-";}; - if(length($cddbid)==0){print("ERROR: no CDDB-ID defined!\n");}; - my $sqlcmd = - "REPLACE INTO album " - ."(artist,title,cddbid,modified) " - ."VALUES " - ."(".$dbh->quote($artist) - .",".$dbh->quote($title) - .",'$cddbid', CURDATE()) "; - - #print("$sqlcmd \n"); - $dbh->do($sqlcmd); -} - - -############################################################ -### -sub record_cd_track{ - my ($artist,$title,$genre1,$genre2,$year, - $lang,$type,$rating,$length,$source,$sourceid, - $tracknb,$audiofile,$condition,$voladjust, #$lengthfrm,$startfrm,$bpm, - $created,$id,$bitrate - ) = @_; - - my $base = gdparams::gdbase(); - - ### Get an audio directory with enough space left (1GB) - my $ripdir=gdgentools::get_ripdirectory($gdparams::minfreehdspace); #1000 MBytes should be available - - my $trid; - ### create a new track record - if($ripdir ne ""){ - my ($audiofmt, $audioparam) = gdgentools::bitrate_str_to_format_param($bitrate); # split "mp3 128" - $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year, - $lang,$type,$rating,$length,$source,$sourceid, - $tracknb,"tr0x".$sourceid."-".$tracknb.".".$audiofmt, - $condition,$voladjust,0,0,0, #$lengthfrm,$startfrm,$bpm, - $bitrate, - $created,$id); - - unlink glob("$base/??/tr0x".$sourceid."-".$tracknb.".".$audiofmt); # delete old audio file - ### send rip command to pipe - if(!$rippipe_open){open_rippipe();} - print ("printing to RIPPIPE: tr=$tracknb id=$trid " - ."br=$bitrate len=$length " - ."dir=$base/$ripdir " - ."file=tr0x$sourceid art=$artist tit=$title\n"); - gdgentools::tracklist_append_list($dbh, $playerid, $rp_list, $trid); - ### action is "rip" - print (RIPPIPE "rip\t$tracknb\t$trid\t$bitrate\t$length\t" - ."$base/$ripdir\ttr0x$sourceid\t$artist\t$title\n"); - } - else{ - print("Not enough space left on disc \n"); - } -} - - -############################################################ -### -sub import_audio_track{ -# creates a new record and moves the specified audio-file to a -# music directory which has enough space left - - my ($artist,$title,$genre1,$genre2,$year, - $lang,$type,$rating,$length,$source,$sourceid, - $tracknb,$audiofile,$condition,$voladjust,#$lengthfrm,$startfrm,$bpm, - $created,$id,$bitrate - ) = @_; - - my $base = gdparams::gdbase(); - - ### Get an audio directory with enough space left (1GB) - my $ripdir=gdgentools::get_ripdirectory($gdparams::minfreehdspace); - - my $trid; - ### create a new track record - if($ripdir ne ""){ - ### put audio-file in music directory - - my $origaudiofile = readlink "$base/inbox/$audiofile"; - - if(!$rippipe_open){open_rippipe();} - - ### action is "move" - #format: "move, sourcefile, targetfile, linkname - print ("printing to RIPPIPE: move\t$origaudiofile\t$base/$ripdir/$audiofile\t$base/inbox/$audiofile\n"); - print (RIPPIPE "move\t$origaudiofile\t$base/$ripdir/$audiofile\t$base/inbox/$audiofile\n"); - - ### create track record - $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year, - $lang,$type,$rating,$length,$source,$sourceid, - $tracknb,$audiofile,$condition,$voladjust, - 0,0,0, #$lengthfrm,$startfrm,$bpm, - $bitrate, - $created,$id); - - } - else{ - print("Not enough space left on disc \n"); - } -} - - - -############################################################ -### -# these variables are st by the routine, that sends track details to the -# client. The value in there gives a hint, if the frequencies should be recalculated -my ($trk_last_id, $trk_last_lang, $trk_last_genre1, $trk_last_genre2); - -sub update_track_record{ - my ($artist,$title,$genre1,$genre2,$year, - $lang,$type,$rating,$length,$source,$sourceid, - $tracknb,$audiofile,$condition,$voladjust,#$lengthfrm,$startfrm,$bpm, - $created,$id,$bitrate - ) = @_; - - my ($trid, $audiofpath); - - ### always recalculate bitrate and track length - $audiofpath = gdgentools::get_full_audiofile_path($audiofile); - if(gdgentools::is_mp3stream($audiofile) || (length($audiofpath)>0)){ - if(length($audiofpath)>0){ ## real mp3 file exists - $bitrate = gdgentools::get_bitrate_str("$audiofpath"); - $length = gdgentools::audiofile_lengthsec("$audiofpath"); - } - else{ - $length = 0; - } - - #print("UPDATING: $artist,$title,$genre1,$genre2,$year," - # ."$lang,$type,$rating,$length,$source,$sourceid," - # ."$tracknb,$audiofile,$condition,$voladjust,$created,$id\n"); - $trid = gddb::insert_track_record($dbh,$artist,$title,$genre1,$genre2,$year, - $lang,$type,$rating,$length,$source,$sourceid, - $tracknb,$audiofile,$condition,$voladjust, - 0,0,0,#$lengthfrm,$startfrm,$bpm, - $bitrate, - $created,$id); - - if ( $id != $trk_last_id - || $lang ne $trk_last_lang){ ### language changed? - system("killall -q gdtablefreq.pl"); - system("nice gdtablefreq.pl --dbhost $dbhost --trklanguage&"); -# system("nice gdtablefreq.pl --dbhost $dbhost --trklanguage --verbose&"); - } - if ( $id != $trk_last_id - || $genre1 ne $trk_last_genre1 - || $genre2 ne $trk_last_genre2){ ### genre changed? - # recalculate genre frequencies - system("killall -q gdtablefreq.pl"); - if (length($sourceid)>=8){ - system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre $sourceid&"); -# system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre $sourceid --verbose&"); - } - else{ - system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre&"); -# system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --verbose&"); - } - } - } - else{ - print("Warning: can't update record (id=$id) because the associated mp3file does not exist, and no mp3 streaming url is specified\n"); - } -} - - - -sub recalc_table_frequencies{ - system("killall -q gdtablefreq.pl"); - system("nice gdtablefreq.pl --dbhost $dbhost --trkgenre --albgenre&"); -} - -############################################################ -### QUERIES ### -############################################################ - - -############################################################ -### -sub do_tr_query{ # returns a query handle - - my $where = gddb::track_where_clause(@_); - my $order = gddb::track_order_clause(@_); - - #print("WHERE clause: $where $order\n"); - my $sth = $dbh->prepare( "SELECT * FROM tracks WHERE $where $order" ); - - my $rv = $sth->execute; - print("$rv records found\n"); - return $sth; -} - -############################################################ -### -sub do_album_query{ # does an album query and returns a query handle - ### -# EXAMPLE: -# SELECT album.* FROM tracks JOIN album -# WHERE album.cddbid=tracks.sourceid AND tracks.genre1='mdh' -# AND album.title like "%de%" -# GROUP BY album.cddbid; - -# my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, -# $lang,$type,$rating) = @_; - - my $tmpcmd; - - my $where = gddb::album_where_clause(@_); - my $order = gddb::album_order_clause(@_); - -#print "alb-order: $order\n\n"; - if (length($order)<5){ - $order = "ORDER BY album.artist"; - } - - my $sth = $dbh->prepare( -# " SELECT album.* FROM tracks JOIN album " - " SELECT album.artist AS artist, album.title AS title, " - ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year " - ."FROM tracks JOIN album " - ."WHERE album.cddbid=tracks.sourceid AND $where " - ."GROUP BY album.cddbid $order" - ); - - $sth->execute; - return $sth; -} - -############################################################ -### -sub do_playlist_query{ # does a playlist query and returns a query handle - ### - my ($author,$title,$genre1,$genre2,$yearfrom,$yearto, - $lang,$type,$rating) = @_; - - my $where=" 1 "; # true AND ... - - ### Artist - $where .= gddb::field_where_clause("author",$author); - - ### Title - $where .= gddb::field_where_clause("title",$title); - - my $sth = $dbh->prepare( - 'SELECT * FROM playlist ' - ."WHERE $where" - ); - - $sth->execute; - return $sth; -} - -############################################################ -### -sub get_track{ # returns a query handle - my ($trackid) = @_; - - my $sth = $dbh->prepare( - 'SELECT * FROM tracks ' - ."WHERE id = $trackid" - ); - - $sth->execute; - return $sth; -} - -############################################################ -### -sub do_tracks_of_album_query{ # returns a query handle - my ($albumid) = @_; - my $sth = $dbh->prepare( - 'SELECT * FROM tracks ' - ."WHERE sourceid = \"$albumid\" ORDER BY tracknb" - ); - - $sth->execute; - return $sth; -} - -############################################################ -### -sub do_tracks_of_playlist_query{ # returns a query handle - my ($playlistid) = @_; - my $sth = $dbh->prepare( - "SELECT * FROM playlistitem " - ."WHERE playlist = $playlistid ORDER BY tracknumber" - ); - - $sth->execute; - return $sth; -} - - -############################################################ -### LISTINGS ### -############################################################ - -############################################################ -### send tracks -sub send_tracks_v1{ # send a record set to the serial line - # NEW VERSION - my ($sth) = @_; - my ($row); - my $stop=0; - while($row = $sth->fetchrow_hashref){ - $stop = send_track($row); - last if ($stop); - } - if (!$stop){ - gdio::putline ("");} # End of result - $sth->finish; -} -sub send_track{ - my ($row)= @_; - my ($stop); - truncstr($row->{artist}, $shrtFieldLen); - truncstr($row->{title}, $shrtFieldLen); - print ("$row->{id}\t"); - print ("$row->{artist}\t"); - print ("$row->{title}\t"); - print ("$row->{length}\t"); - print ("$row->{genre1}\t"); - #print ("$row->{genre2}\t"); - print ("$row->{year}\t"); - print ("$row->{type}\t"); - print ("$row->{rating}\n"); - $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" - ."$row->{length}\t$row->{genre1}\t" - ."$row->{year}\t$row->{type}\t$row->{rating}"); - return $stop -} -#sub send_tracks_v0{ # send a record set to the serial line -# # OLD VERSION: will be obsolete when all -# # routines are ported to 'send_tracks' -# my ($sth) = @_; -# my ($row); -# my $stop=0; -# while($row = $sth->fetchrow_hashref){ -# truncstr($row->{artist}, $shrtFieldLen); -# truncstr($row->{title}, $shrtFieldLen); -# print ("$row->{id}\t"); -# print ("$row->{artist}\t"); -# print ("$row->{title}\t"); -# print ("$row->{length}\n"); -# $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" -# ."$row->{length}\t$row->{tracknb}"); -# last if ($stop); -# } -# if (!$stop){ -# gdio::putline ("");} # End of result -# $sth->finish; -#} - -############################################################ -### Query tracks & List -sub query_tracks_list{ #query tracks records and list them - my $sth = do_tr_query(1, @_); # perform database query - send_tracks_v1($sth); -} - -sub query_streams_list{ #query tracks records and list them - my $sth = do_tr_query(2, @_); # perform database query - send_tracks_v1($sth); -} - -############################################################ -### Generic Query tracks & List -sub generic_track_query{ #query tracks records and list them - my ($where) = @_; - #print("WHERE clause: $where\n"); - my $sth = $dbh->prepare( "SELECT * FROM tracks $where" ); - my $rv = $sth->execute; - print("$rv records found\n"); - send_tracks_v1($sth); -} - -### Query a single tracks & List -#sub query_single_track{ #query one trackfor a given id -# my ($trackid) = @_; -# my $sth = $dbh->prepare( "SELECT * FROM tracks WHERE id='$trackid'" ); -# $sth->execute; -# send_tracks_v0($sth); -#} - - -############################################################ -### send artists -sub send_artist_line_v1{ # send a single record to the serial line - my ($artist) = @_; - my $stop=0; - print ("\t"); # send an empty ID - truncstr($artist, 10+$shrtFieldLen, 1); # dont send ellipsis - print ("$artist\n"); - $stop = gdio::putline("\t$artist"); - return $stop; -} - -#sub send_artist_line_v0{ # send a single record to the serial line -# my ($artist) = @_; -# my $stop=0; -# print ("ESCn\t"); # ESC n: name (=artist) -# print ("0\t"); # send an empty ID to be compatible with album record format -# truncstr($artist, 10+$shrtFieldLen, 1); # dont send ellipsis -# print ("$artist\t"); -# print (" \n"); # send an empty title to be compatible with album record format -# $stop = gdio::putline("\en\t0\t$artist\t "); -# return $stop; -#} - -############################################################ -### Query artist & List -sub query_artists_list{ #query track records and list their distinct artists - #my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, - # $lang,$type,$rating) = @_; - - ### Prepare Query - my $where = gddb::track_where_clause(1, @_); - - #print("WHERE clause: $where\n"); - - generic_artists_query( - "SELECT DISTINCT artist FROM tracks WHERE $where ORDER BY artist" ); -} - - -sub generic_artists_query{ #query track records and list their distinct artists - ### Prepare Query - my ($querystr) = @_; - my $sth = $dbh->prepare( $querystr ); - my $rv = $sth->execute; - #print("$rv artists found\n"); - ### Send results - my $row; - my $stop=0; - while($row = $sth->fetchrow_hashref){ - $stop = send_artist_line_v1($row->{artist}); - last if ($stop); - } - if (!$stop){ - gdio::putline ("");} # End of result - $sth->finish; -} - - -############################################################ -### Query albums - - -#sub query_single_album{ - # query one albums with its tracks and list them - # the album is specified by the 'albumid'. -# my ($albumid) = @_; -# my $albsth; -# $albsth = $dbh->prepare( -# " SELECT * FROM album WHERE cddbid='$albumid'"); -# $albsth->execute; -# send_albums_v0(1, $albsth); # 1: with tracks -#} - - -sub generic_album_query{ - # query one albums with its tracks and list them - # the album is specified by the 'albumid'. - my ($querystr) = @_; - my $albsth; - my $albsth = $dbh->prepare( $querystr ); - $albsth->execute; - send_albums_v1($albsth); -} - - -############################################################ -### Query albums & list -sub query_albums{ - # query albums and list them - my $albsth = do_album_query(@_); # perform database query - send_albums_v1($albsth); -} - - -############################################################ -### Query albums at random -sub query_random_albums{ - # prepares a ramdom query operation - # 1) performs an album query - # 2) selects album-ids and store them in a gobally accessible array - # 3) returns the number of albums found - my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, - $lang,$type,$rating) = @_; - my $nbRecords; - - my $where = gddb::album_where_clause(@_); -# my $where=" 1 "; # true AND ... - ### Album: Artist -# $where .= gddb::field_where_clause("album.artist",$artist); - ### Album: Title -# $where .= gddb::field_where_clause("album.title",$title); - ### Track: genre, etc ... -# $where.= gddb::attrib_where_clause($genre1,$genre2,$yearfrom,$yearto,$lang,$type,$rating); - - my $sth = $dbh->prepare( - " SELECT album.cddbid,(ASCII(album.cddbid)*0)+RAND() AS randnum " - ."FROM tracks JOIN album " - ."WHERE album.cddbid=tracks.sourceid AND $where " - ."GROUP BY album.cddbid ORDER BY randnum" - ); # ORDER BY RAND() does not work in version 3.22 - $nbRecords = $sth->execute; - - ### store result in global array - $random_result_table = $sth->fetchall_arrayref; - $random_result_type = "alb"; - # my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); - $sth->finish; - - ### Send number of records - print ("random album query: $nbRecords albums found\n"); - gdio::putline ("$nbRecords\n"); - gdio::putline (""); # End of result -} - - -############################################################ -### Query tracks at random -sub query_random_tracks{ - # prepares a ramdom query operation - # 1) performs a track query - # 2) selects track-ids and store them in a gobally accessible array - # 3) returns the number of tracks found - my $nbRecords; - - my $where = gddb::track_where_clause(@_); - my $sth = $dbh->prepare( - " SELECT id,(id*0)+RAND() AS randnum FROM tracks " - ."WHERE $where ORDER BY randnum" - ); # ORDER BY RAND() does not work in version 3.22 - $nbRecords = $sth->execute; - - ### store result in global array - $random_result_table = $sth->fetchall_arrayref; - $random_result_type = "trk"; -# my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); - $sth->finish; - - ### Send number of records - print ("random album query: $nbRecords tracks found\n"); - gdio::putline ("$nbRecords\n"); - gdio::putline (""); # End of result -} - - -############################################################ -### Query artists at random -sub query_random_artists{ - # prepares a ramdom query operation - # 1) performs a track query - # 2) selects track-ids and store them in a gobally accessible array - # 3) returns the number of tracks found - my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, - $lang,$type,$rating) = @_; - my $nbRecords; - - my $where = gddb::track_where_clause(1, @_); - my $sth = $dbh->prepare( - " SELECT DISTINCT artist FROM tracks WHERE $where" - ); # can't "ORDER BY randnum" here! - $nbRecords = $sth->execute; - - ### store result in global array - $random_result_table = $sth->fetchall_arrayref; - $random_result_type = "art"; -# my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); - $sth->finish; - ### randomize it - because SQL query can't do it - shuffle_random_table(); - - ### Send number of records - print ("random artist query: $nbRecords artists found\n"); - gdio::putline ("$nbRecords\n"); - gdio::putline (""); # End of result -} - - -############################################################ -### send next slice of random result table to the palm client -sub collect_ids{ - my ($position,$nbRecords,$idIdent) = @_; - my ($whereclause, $i); - $whereclause = "0"; - # if $position < $#{$random_result_table} - for $i( $position .. ($position+$nbRecords-1)){ - #print ($random_result_table->[$i][0]."\n"); - $whereclause .= " OR $idIdent='".$random_result_table->[$i][0]."'"; - } - return $whereclause; -} - -sub shuffle_random_table{ - my ($i, $j, $element); - for $i( 0 .. $#{$random_result_table}){ - $j = int rand ($#{$random_result_table}); -# print ("exchange $i and $j\n"); - $element = $random_result_table->[$i][0]; - $random_result_table->[$i][0] = $random_result_table->[$j][0]; - $random_result_table->[$j][0] = $element; - } -} - -sub get_random_result{ - # prepares a ramdom query operation - # 1) performs an album query - # 2) selects album-ids and store them in a gobally accessibe array - # 3) randomizes the order of the album-ids - # 4) returns the number of albums found - my ($position,$nbRecords) = @_; - - my ($whereclause, $sth); - if ($random_result_type eq "alb"){ - $whereclause = collect_ids ($position,$nbRecords, "tracks.sourceid=album.cddbid AND album.cddbid"); - ### Perform the query - #print("SELECT album.artist AS artist, album.title AS title, " - # ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year " - # ."FROM tracks JOIN album " - # ."WHERE $whereclause" - # ."GROUP BY album.cddbid\n"); - - $sth = $dbh->prepare(" SELECT album.artist AS artist, album.title AS title, " - ."album.cddbid AS cddbid, album.genre AS genre, tracks.year AS year " - ."FROM tracks JOIN album " - ."WHERE $whereclause" - ."GROUP BY album.cddbid"); - $sth->execute; - send_albums_v1($sth); - $sth->finish; - } - elsif ($random_result_type eq "pll"){ - $whereclause = collect_ids ($position,$nbRecords, "id"); - ### Perform the query - print("SELECT * FROM playlist WHERE $whereclause\n"); - $sth = $dbh->prepare("SELECT * FROM playlist WHERE $whereclause"); - $sth->execute; - my $prow; - while($prow = $sth->fetchrow_hashref){ - print("$prow->{id}\t$prow->{author}\t$prow->{title}\n"); - gdio::putline("$prow->{id}\t$prow->{author}\t$prow->{title}"); - } - gdio::putline (""); # End of result - $sth->finish; - } - elsif ($random_result_type eq "trk"){ - $whereclause = collect_ids ($position,$nbRecords, "id"); - ### Perform the query - print("SELECT * FROM tracks WHERE $whereclause\n"); - $sth = $dbh->prepare("SELECT * FROM tracks WHERE $whereclause"); - $sth->execute; - send_tracks_v1($sth); - $sth->finish; - } - elsif ($random_result_type eq "art"){ - ### no additional query necessary with artists - my $i; - for $i( $position .. ($position+$nbRecords-1)){ - truncstr($random_result_table->[$i][0], 10+$shrtFieldLen); - #print ($random_result_table->[$i][0]."\n"); - send_artist_line_v1($random_result_table->[$i][0]); - } - gdio::putline (""); # End of result - } - else{ - print ("Error: random_result_type '$random_result_type' not properly initialized"); - } -} - - -############################################################ -### Query albums with or without tracks & list -#sub send_albums_v0{ -# # sends the albums (specified by the record set handle '$albsth') -# # if $withTracks is true, the according tracks are sent too -# -# my ($withTracks, $albsth) = @_; -# my ($arow, $trow); # album and track row -# my $trksth; -# my $stop=0; -# -# while($arow = $albsth->fetchrow_hashref){ -# truncstr($arow->{artist}, $shrtFieldLen); -# truncstr($arow->{title}, $shrtFieldLen); -# print ("ESCa\t"); -# print ("$arow->{cddbid}\t"); -# print ("$arow->{artist}\t"); -# print ("$arow->{title}\n"); -# # print album (preceded by ESC a) -# $stop = gdio::putline("\ea\t$arow->{cddbid}\t$arow->{artist}\t$arow->{title}"); -# -# if ($withTracks){ -# ### get tracks of the album -# my $trksth = do_tracks_of_album_query($arow->{cddbid}); -# while($trow = $trksth->fetchrow_hashref){ -# truncstr($trow->{artist}, $shrtFieldLen); -# truncstr($trow->{title}, $shrtFieldLen); -# print ("$trow->{id}\t"); -# print ("$trow->{artist}\t"); -# print ("$trow->{title}\t"); -# print ("$trow->{length}\n"); -# # print track -# $stop = gdio::putline("$trow->{id}\t$trow->{artist}\t$trow->{title}\t" -# ."$trow->{length}\t$trow->{tracknb}"); -# last if ($stop); -# } -# } -# last if ($stop); -# } -# if (!$stop){ -# gdio::putline ("");} # End of result -# $albsth->finish; -#} - -sub send_albums_v1{ - # sends the albums (specified by the record set handle '$albsth') - # if $withTracks is true, the according tracks are sent too - - my ($albsth) = @_; - my ($arow, $trow); # album and track row - my $trksth; - my $stop=0; - - while($arow = $albsth->fetchrow_hashref){ - truncstr($arow->{artist}, $shrtFieldLen); - truncstr($arow->{title}, $shrtFieldLen); - print ("$arow->{cddbid}\t"); - print ("$arow->{artist}\t"); - print ("$arow->{title}\t"); - print ("\t$arow->{genre}\t$arow->{year}\n"); # length,genre,year - # print album - $stop = gdio::putline("$arow->{cddbid}\t$arow->{artist}\t$arow->{title}\t\t$arow->{genre}\t$arow->{year}"); - last if ($stop); - } - if (!$stop){ - gdio::putline ("");} # End of result - $albsth->finish; -} - -############################################################ -### Query playlists & list -sub query_saved_playlists_list{ - # query playlists and list them - # - # List format: id, author, title - - my $pllsth = do_playlist_query(@_); # perform database query - my ($prow, $trow); # playlist and track row - my ($trksth, $trow, $irow, $plisth); - my $stop=0; - - while($prow = $pllsth->fetchrow_hashref){ - truncstr($prow->{artist}, $shrtFieldLen); - truncstr($prow->{title}, $shrtFieldLen); - print ("$prow->{id}\t"); - print ("$prow->{author}\t"); - print ("$prow->{title}\n"); - - $stop = gdio::putline("$prow->{id}\t$prow->{author}\t$prow->{title}"); - last if ($stop); - } - if (!$stop){ - gdio::putline ("");} # End of result - $pllsth->finish; -} - - - -############################################################ -### Query playlists at random -sub query_random_playlists{ - # prepares a ramdom query operation - # 1) performs an playlist query - # 2) selects playlist-ids and store them in a gobally accessible array - # 3) returns the number of playlists found - my ($artist,$title,$genre1,$genre2,$yearfrom,$yearto, - $lang,$type,$rating) = @_; - my $nbRecords; - - my $where=" 1 "; # true AND ... - - ### Artist - $where .= gddb::field_where_clause("author",$artist); - ### Title - $where .= gddb::field_where_clause("title",$title); - - my $sth = $dbh->prepare( - 'SELECT id FROM playlist ' - ."WHERE $where ORDER BY RAND()" - ); - - $nbRecords = $sth->execute; - - ### store result in global array - $random_result_table = $sth->fetchall_arrayref; - $random_result_type = "pll"; - # my $i; for $i( 0 .. $#{$random_result_table}){print ($random_result_table->[$i][0]."-".$random_result_table->[$i][1].",");} print ("\n"); - $sth->finish; - - ### Send number of records - print ("random album query: $nbRecords albums found\n"); - gdio::putline ("$nbRecords\n"); - gdio::putline (""); # End of result -} - - -sub tracks_of_saved_playlist{ - # lists tracks of a saved playlist - - my ($plid) = @_; - - my ($trksth, $irow, $plisth, $row); - #my $stop=0; - - - ### get tracks of the playlist - $plisth = do_tracks_of_playlist_query($plid); - while($irow = $plisth->fetchrow_hashref){ - $trksth = $dbh->prepare("SELECT * FROM tracks WHERE id=".$irow->{trackid}); - $trksth->execute; - if($row = $trksth->fetchrow_hashref){ - send_track($row); - } - $trksth->finish; - } - $plisth->finish; - gdio::putline (""); # End of result -} - - - -############################################################ -### Query tracks and send result to Palm client -sub simple_track_query{ - my ($query_cmd) = @_; - my ($sth,$row); - my $stop=0; - - my $sth = $dbh->prepare($query_cmd); - my $rv = $sth->execute; - print("$rv records found\n"); - - while($row = $sth->fetchrow_hashref){ - truncstr($row->{artist}, $shrtFieldLen); - truncstr($row->{title}, $shrtFieldLen); - print ("$row->{id} $row->{artist} $row->{title}\t $row->{length}\n"); - $stop = gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" - ."$row->{length}\t$row->{tracknb}"); - last if ($stop); - } - if (!$stop){ - gdio::putline ("");} # End of result - $sth->finish; -} - - - -############################################################ -### Play + Playlist commands ### -############################################################ - - -############################################################ -### New Playlist commands - -sub pl_play{ gdgentools::pl_play ($dbh, $playerid, $audchannel0);} -sub pl_play_at{gdgentools::pl_play_at($dbh, $playerid, $audchannel0, @_);} -sub pl_stop{ gdgentools::pl_stop ($dbh, $playerid, $audchannel0);} -sub pl_pause{ gdgentools::pl_pause ($dbh, $playerid, $audchannel0);} -sub pl_rw{ gdgentools::pl_rw ($dbh, $playerid, $audchannel0);} -sub pl_ff{ gdgentools::pl_ff ($dbh, $playerid, $audchannel0);} -sub pl_goto{ gdgentools::pl_goto ($dbh, $playerid, $audchannel0, @_);} -sub pl_prev{ gdgentools::pl_prev ($dbh, $playerid, $audchannel0);} -sub pl_next{ gdgentools::pl_next ($dbh, $playerid, $audchannel0);} - - - -sub pl_append_tracks{ -# appends tracks to the playlist file -# Parameters: list of track id's - gdgentools::tracklist_append_list($dbh, $playerid, $pl_list, @_); -} - -sub pl_new_list{ -# Sets a new playlist. First, playing is stopped, then the current playlist -# is emptied, the new playlist is written and the current track index is set -# to zero. -# An example, where this routine is used is the shuffle function (Palm -# randomizes playlist order and sends new playlist). -# Parameters: list of track id's - #my $tracklist = @_; - pl_empty(); - pl_append_tracks(@_); -} - -sub pl_empty{ -# empties (deletes) the playlist. The playstate is also reset - my $base = gdparams::gdbase(); - # stop playing - gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); - # reset list+state - gdgentools::tracklist_delete($dbh, $playerid, 0); - gdgentools::pll_write_playstate($dbh, $playerid, $audchannel0, 0, "st"); - gdgentools::pll_write_playtime($dbh, $playerid, $audchannel0, 0, 0); - gdgentools::pll_write_shuffleparameters($dbh, $playerid, $audchannel0, "", ""); -} - - -sub pl_remove_one{ -# Removes the specified playlist item (index) from the playlist -# If the index corresponds to the currently played track, playing is -# stopped first. -# It the index is lower than the the index of the currently played track, -# then nothing is done. -# Parameter: index of the item to be removed - - my ($delitem) = @_; - my ($trackind, $state, $frame, $shufflestat) = gdgentools::pll_get_playstate($dbh, $playerid, $audchannel0); - if ($delitem >= $trackind){ - if (($delitem == $trackind) && $state ne "st" && $state ne "in"){ - gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); - gdgentools::pll_write_playstate($dbh, $playerid, $audchannel0, $trackind, "st"); - } - - ### delete it - gdgentools::tracklist_del_item($dbh, $playerid, 0, $delitem); - } -} - - - -sub pl_reorder_one{ -# Reorders the playlist. -# The specified playlist item (src) is moved to a new location (dest) -# src and dest must be higher than the currently played track. They -# therefore don't interfere with the track playing process. - - my ($srcpos, $destpos) = @_; - ### move it - gdgentools::tracklist_reorder_item($dbh, $playerid, 0, $srcpos, $destpos); -} - - -############################################################ -### Shuffle play (query only) -sub pl_calc_shufflestats{ # query tracks and send back statistics - my $where = gddb::track_where_clause(1, @_); - my $sth = $dbh->prepare( - "SELECT SUM(length) AS playlen, COUNT(*) AS nbtracks " - ."FROM tracks WHERE $where" ); - $sth->execute; - my ($row, $nbtracks, $playlen); - if($row = $sth->fetchrow_hashref){ - $nbtracks = $row->{nbtracks}; - $playlen = $row->{playlen}; - } - else{ - print("Shuffle play: query error!\n"); - } - $sth->finish; - return ($nbtracks, $playlen); -} - - - -sub pl_check_shuffleplay{ # query tracks and send back statistics - my ($nbtracks, $playlen) = pl_calc_shufflestats(@_); - - if($nbtracks > 10){ - gdio::putline("$nbtracks tracks found. Total play length is " - . gdgentools::seconds_to_hm($playlen) - .". Play them in random order?"); - } - else{ - gdio::putline("Sorry! $nbtracks tracks are not enough to be played in shuffle mode."); - } - gdio::putline (""); -} - - - -sub pl_start_shuffleplay{ - pl_empty(); # stop playing, empty playlist and clear playstate - my ($nbtracks, $playlen) = pl_calc_shufflestats(@_); - my $shuffleparam = join ("\t",@_); ### Attention: does not join trailing empty parameters! - if (length($shuffleparam)==0){$shuffleparam ="\t\t";} ### join does not work on empty parameters -> cheat an empty parameter string! - gdgentools::pll_write_shuffleparameters($dbh, $playerid, $audchannel0, - $shuffleparam, "$nbtracks Tr [".gdgentools::seconds_to_hm($playlen)."]"); - gdgentools::pl_play($dbh, $playerid, $audchannel0); # start playing - the player script knows how to handle the shuffle play parameters -} - - -############################################################ - - -# send current playlist to the client (tracklist with ID, artist, title, length) -sub pl_reload_playlist{ - my @playlist = gdgentools::tracklist_get_all($dbh, $playerid, 0); - my ($stop, $trackid, $sth, $trackrow, $row); - - while($trackid = shift @playlist){ - $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); - $sth->execute; - - if($row = $sth->fetchrow_hashref){ - $stop = send_track($row); -# truncstr($row->{artist}, $shrtFieldLen); -# truncstr($row->{title}, $shrtFieldLen); -# print ("$row->{id}\t$row->{artist}\t$row->{title}\t$row->{length}\n"); -# gdio::putline("$row->{id}\t$row->{artist}\t$row->{title}\t" -# ."$row->{length}\t$row->{tracknb}"); - last if ($stop); - } - $sth->finish; - } - if (!$stop){ - gdio::putline ("");} # End of result -} - - -# send current playstate to the client -sub pl_get_curr_playstate{ - # sends: pll-length, current-index, current-id, playtime, tracklength, state - use integer; - -print "get playstate for channel ".$gdgentools::audchannel1."\n"; - my ($trackind, $state, $frame, $shufflestat) = - gdgentools::pll_get_playstate($dbh, $playerid, $audchannel0); - my ($played, $total) = gdgentools::pll_get_playtime($dbh, $playerid, $audchannel0); - my @playlist = gdgentools::tracklist_get_all($dbh, $playerid, 0); - my $playlistlen = @playlist; - my $pltime = $played/gdgentools::frames_per_second(); - my $pllen = $total/gdgentools::frames_per_second(); - - #print ("Sending state: pl-len:$playlistlen ind:$trackind id:$playlist[$trackind] ptime:$pltime ptot:$pllen state:$state\n"); - gdio::putline ("$playlistlen\t$trackind\t$playlist[$trackind]\t$pltime\t$pllen\t$state\t$shufflestat"); - gdio::putline (""); # End of result -} - - - - -################################################################ -### Export Playlist: - -sub pl_export_empty{ - my $base = gdparams::gdbase(); - - unlink <$base/outbox/*>; -} - -sub pl_export_audiolinks{ -# exports the current playlist as links to mp3-files to the outbox - my $base = gdparams::gdbase(); - my ($trackid, $row, $tracknum, $trh, $trkrow, $linkname, $mp3fp); - my ($audiofmt, $audioparam); - - ### run through playlist - my $sth = $dbh->prepare( - "SELECT * FROM tracklistitem " - ."WHERE playerid=$playerid AND listtype=$pl_list " - ."ORDER BY tracknb " ); - my $nbrec = $sth->execute; - while($row = $sth->fetchrow_hashref){ - $trackid = $row->{trackid}; - - ### get track details - $trh = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); - $trh->execute; - if($trkrow = $trh->fetchrow_hashref){ - ####$trackid = $row->{trackid}; - ### create link - $mp3fp = `ls $base/??/$trkrow->{mp3file}`; # get full filename and path - ($audiofmt, $audioparam) = gdgentools::bitrate_str_to_format_param($trkrow->{bitrate}); # split "mp3 128" - chop($mp3fp); - $linkname = gdgentools::ascii2filename( - gdgentools::iso2ascii($trkrow->{artist}." - ".$trkrow->{title})); - #print("exporting link: "."$mp3fp --- $base/outbox/$linkname.mp3"."\n"); - symlink ("$mp3fp", "$base/outbox/$linkname.$audiofmt"); - } - $trh->finish; - - } - $sth->finish; -} - - - -################################################################ -### Upload Playlist to mp3 Player: - -sub pl_upload_to_player_info{ -print("EXTMP3 is:".$gdparms::extmp3player."\n"); - if (length($gdparms::extmp3player)>0){ - pl_export_empty(); - pl_export_audiolinks(); -print("CMD: gdupload2player.pl --$gdparms::extmp3player --info\n"); - my $resline = `gdupload2player.pl --$gdparms::extmp3player --info`; - gdio::putline ($resline); - } - gdio::putline (""); # End of result -} - -sub pl_upload_to_player{ - my ($addtrks) = @_; - - system( "gdupload2player.pl --$gdparms::extmp3player $addtrks&"); -} - - - - -################################################################ -### Saved Playlists: - -sub pl_save_playlist{ -# saves the playlist with the specified name -# Parameters: title, author, note, followed by list of track id's - my $title = shift(@_); - my $author = shift(@_); - my $note = shift(@_); - my ($playlistid, $trackid, $tracknum); - - my $nbtracks = @_; - if (length($title)>0 && $nbtracks>0){ - ### check if playlist with that name already exists - my $sth = $dbh->prepare("SELECT * FROM playlist WHERE title = ".$dbh->quote($title)); - my $count = $sth->execute; - if ($count > 0){ - ### A playlist with this name exists already, get it's ID - my $row = $sth->fetchrow_hashref; - $playlistid = $row->{id}; - my $plh = $dbh->prepare( - "SELECT * FROM playlistitem WHERE playlist = ".$playlistid - ." ORDER BY tracknumber DESC LIMIT 1"); - $count = $plh->execute; - if ($count>0){ - $row = $plh->fetchrow_hashref; - $tracknum = $row->{tracknumber} +1; - } - else{ - $tracknum = 1; - } - $plh->finish; - - } - else{ - ### A playlist with this name does not exist, create it - my $sqlcmd = - "INSERT INTO playlist (title,author,note,created) " - ."VALUES (".$dbh->quote($title).",".$dbh->quote($author) - .",'$note',CURDATE()) "; - $dbh->do($sqlcmd); - $playlistid = $dbh->{'mysql_insertid'}; - $tracknum = 1; - } - $sth->finish; - - - ### Append tracks to playlist $playlistid starting with number $tracknum - while($trackid = shift(@_)){ - #print("appending track-ID $trackid as itemnb $tracknum\n"); - my $sqlcmd = - "INSERT INTO playlistitem (playlist,tracknumber,trackid) " - ."VALUES ($playlistid,$tracknum,$trackid) "; - $dbh->do($sqlcmd); - $tracknum++; - } - } - else{ - print("Can't save playlist: title or playlist are empty\n"); - } -} - - -sub pl_delete_playlist{ -# deletes the playlist with the specified id - my ($playlistid) = @_; - my $sqlcmd; - - ### Delete all associated tracks - $sqlcmd = ("DELETE FROM playlistitem WHERE playlist = ".$playlistid); - $dbh->do($sqlcmd); - - ### Delete playlist - $sqlcmd = ("DELETE FROM playlist WHERE id = ".$playlistid); - $dbh->do($sqlcmd); -} - - - -############################################################ -### Play mp3 file commands - -### directly play a track (in inbox, before trimming, etc) -sub cmd_direct_play_mp3_track{ -#parameters: filename of mp3 file (without path) -# startframe (optional) -# nbframes (optional) - - my ($audiofile, $startframe, $nbframes) = @_; - my $base = gdparams::gdbase(); - - ### kill early, for /dev/dsp to recover - gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); - system("sleep 1s"); # sleep a second - - if($startframe > 0){ - $startframe = "-k ".$startframe; - } - else{ - $startframe = "-k 0"; - } - if($nbframes > 0){ - $nbframes = "-n ".$nbframes; - } - else{ - $nbframes = ""; - } - - print("\nplaying inbox $audiofile $startframe $nbframes\n"); - # play it in inbox or in 00, 01 ... - if ( gdgentools::audio_filetype($audiofile) eq "mp3"){ - system( "mpg123 $startframe $nbframes $base/[0-9i][0-9n]*/$audiofile&"); - } - elsif(gdgentools::audio_filetype($audiofile) eq "ogg"){ - system( "ogg123 $base/[0-9i][0-9n]*/$audiofile&"); - } -} - - -### Command stop playing -sub cmd_stop_playing{ # - gdgentools::kill_all_play_processes(); -# gdgentools::stop_play_processes($dbh, $playerid, $audchannel0); - print("Playing stopped\n"); -} - -############################################################ -### Play CD track commands - -### Command stop playing cd -sub cmd_stop_playing_cd{ # - system("cdplay stop"); -} - -### Command play CD track -sub cmd_play_cd_track{ # - my ($tracknb) = @_; - system("cdplay play $tracknb"); -} - - -############################################################ -### Listings ### -############################################################ - -############################################################ -### List used genres -sub list_used_genres{ #get all used genres, count and list them - - my $sth = $dbh->prepare( - 'SELECT id, genre, freq ' - .'FROM genre ' - .'ORDER BY genre ' - ); - - $sth->execute; - my (@row, $stop); - - # "no genre" first - print ("\t\t1\t0\t0\n"); - gdio::putline ("\t\t1\t0\t0"); - - while(@row = $sth->fetchrow_array){ - print ("$row[0]\t$row[1]\t$row[2]\t0\t0\n"); - $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]\t0\t0"); - last if ($stop); - } - gdio::putline (""); # End of result - $sth->finish; -} - -############################################################ -### List used languages -sub list_used_languages{ #get all used languages, count and list them - my $sth = $dbh->prepare( - 'SELECT id, language, freq ' - .'FROM language ' - .'ORDER BY freq DESC ' - ); - - my $rv = $sth->execute; - my (@row, $stop); - - # "no language" first - print ("\t\t1\n"); - gdio::putline ("\t\t1"); - - while(@row = $sth->fetchrow_array){ - print ("$row[0]\t$row[1]\t$row[2]\n"); - $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]"); - last if ($stop); - } - gdio::putline (""); # End of result - $sth->finish; -} - -############################################################ -### List music types -sub list_music_types{ - my $sth = $dbh->prepare('SELECT * FROM musictype ORDER BY id '); - my $rv = $sth->execute; - my ($row, $stop); - - while($row = $sth->fetchrow_hashref){ - print ("$row->{musictype}\n"); - $stop = gdio::putline ("$row->{musictype}"); - last if ($stop); - } - gdio::putline (""); # End of result - $sth->finish; -} - -############################################################ -### List music sources -sub list_music_sources{ - my $sth = $dbh->prepare('SELECT * FROM source ORDER BY id '); - my $rv = $sth->execute; - my ($row, $stop); - - while($row = $sth->fetchrow_hashref){ - print ("$row->{source}\n"); - $stop = gdio::putline ("$row->{source}"); - last if ($stop); - } - gdio::putline (""); # End of result - $sth->finish; -} - - -############################################################ -### Calculate language hash -sub language_hash{ # returns the hash value of the language - # table -# my $sth = $dbh->prepare( # get used languages only -# 'SELECT language.id, language.language, COUNT(*) AS freq ' -# .'FROM tracks,language ' -# .'WHERE tracks.lang=language.id ' -# .'GROUP BY language.id ' -# .'ORDER BY freq DESC ' -# ); - - my $sth = $dbh->prepare( - 'SELECT id, language, freq ' - .'FROM language ' - .'ORDER BY freq DESC ' - ); - - my $rv = $sth->execute; - my @row; - my $newval; - - my $hashval = 0; - - while(@row = $sth->fetchrow_array){ - ### calc hash value - $newval = ord(chop($row[0]))<<8; - $newval += ord(chop($row[0])); - $hashval = myhash::addvaltohash($hashval, $newval); - } - $sth->finish; - -# my $sth = $dbh->prepare( # get all languages alphabetically -# 'SELECT language.id ' -# .'FROM language ' -# .'ORDER BY language.id ' -# ); -# -# my $rv = $sth->execute; -# my @row; -# -# while(@row = $sth->fetchrow_array){ -# ### calc hash value -# $newval = ord(chop($row[0]))<<8; -# $newval += ord(chop($row[0])); -# $hashval = myhash::addvaltohash($hashval, $newval); -# } -# $sth->finish; - - return $hashval; -} - - -############################################################ -### Get language hash -sub get_language_hash{ # sends the language hash val to th client - # - - my $hashval = language_hash(); - - print("language hash value = $hashval \n"); - gdio::putline ("$hashval"); # send hash value - gdio::putline (""); # End of result -} - - -############################################################ -### calculate genre hash value -sub genre_hash{ # returns hash value of the language - # table - -# my $sth = $dbh->prepare( # get used genres only -# 'SELECT genre.id,genre.genre,COUNT(*) AS freq ' -# .'FROM tracks,genre ' -# .'WHERE tracks.genre1=genre.id OR tracks.genre2=genre.id ' -# .'GROUP BY genre.id ' -# .'ORDER BY freq ' -# ); - - my $sth = $dbh->prepare( - 'SELECT id, genre, freq ' - .'FROM genre ' - .'ORDER BY freq ' - ); - - my $rv = $sth->execute; - my @row; - my $newval; - - my $hashval = 0; - - while(@row = $sth->fetchrow_array){ - ### calc hash value - $newval = ord(chop($row[0]))<<16; - $newval += ord(chop($row[0]))<<8; - $newval += ord(chop($row[0])); - $hashval = myhash::addvaltohash($hashval, $newval); - } - $sth->finish; - -# my $sth = $dbh->prepare( # get all genres alphabetically -# 'SELECT genre.id ' -# .'FROM genre ' -# .'ORDER BY genre.id ' -# ); -# -# my $rv = $sth->execute; -# -# while(@row = $sth->fetchrow_array){ -# ### calc hash value -# $newval = ord(chop($row[0]))<<8; -# $newval += ord(chop($row[0])); -# $hashval = myhash::addvaltohash($hashval, $newval); -# } -# -# $sth->finish; - - return $hashval; -} - - - -############################################################ -### Get genre hash -sub get_genre_hash{ # send genre hash value to client - # - - my $hashval = genre_hash(); - - print("genre hash value = $hashval \n"); - gdio::putline ("$hashval"); # send hash value - gdio::putline (""); # End of result -} - - -############################################################ -### calculate types hash value -sub types_hash{ # returns hash value of the - # musictype table - - my $sth = $dbh->prepare('SELECT * FROM musictype ORDER BY id '); - my $rv = $sth->execute; - my $row; - my $hashval = 0; - my $newval; - - while($row = $sth->fetchrow_hashref){ - ### calc hash value - $newval = ord(chop($row->{musictype}))<<16; - $newval += ord(chop($row->{musictype}))<<8; - $newval += ord(chop($row->{musictype})); - $hashval = myhash::addvaltohash($hashval, $newval); - } - $sth->finish; - - return $hashval; -} - -############################################################ -### Get types hash -sub get_types_hash{ # send types hash value to client - # - - my $hashval = types_hash(); - - print("types hash value = $hashval \n"); - gdio::putline ("$hashval"); # send hash value - gdio::putline (""); # End of result -} - - -############################################################ -### calculate sources hash value -sub sources_hash{ # returns hash value of the - # source table - - my $sth = $dbh->prepare('SELECT * FROM source ORDER BY id '); - my $rv = $sth->execute; - my $row; - my $hashval = 0; - my $newval; - - while($row = $sth->fetchrow_hashref){ - ### calc hash value - $newval = ord(chop($row->{source}))<<16; - $newval += ord(chop($row->{source}))<<8; - $newval += ord(chop($row->{source})); - $hashval = myhash::addvaltohash($hashval, $newval); - } - $sth->finish; - - return $hashval; -} - -############################################################ -### Get sources hash -sub get_sources_hash{ # send sources hash value to client - # - - my $hashval = sources_hash(); - - print("sources hash value = $hashval \n"); - gdio::putline ("$hashval"); # send hash value - gdio::putline (""); # End of result -} - -############################################################ -### Get all hash -sub get_all_hashes{ # send hash values to client - # of all dynamic tables: - # language, genre, type - - my $languagehash = language_hash(); - my $genrehash = genre_hash(); - my $typeshash = types_hash(); - my $sourceshash = sources_hash(); - - print("hash values: $languagehash $genrehash $typeshash $sourceshash\n"); - gdio::putline ("$languagehash\t$genrehash\t$typeshash\t$sourceshash"); - gdio::putline (""); # End of result -} - - -############################################################ -### List all languages ordered -sub list_all_languages_ordered{ - #get all languages and list them ordered by their usage count -# my $sth = $dbh->prepare( -# 'SELECT language.id, language.language, ' -# .' (COUNT(*)-1) AS freq ' -# .'FROM language LEFT JOIN tracks ' -# .'ON tracks.lang=language.id ' -# .'GROUP BY language.id ' -# .'ORDER BY freq DESC, language.language ASC ' -# ); - my $sth = $dbh->prepare( - 'SELECT id, language, freq ' - .'FROM language ' - .'ORDER BY freq DESC, language ASC ' - ); - - my $rv = $sth->execute; - my (@row, $stop); - - while(@row = $sth->fetchrow_array){ - #if (length($row[2])==0){$row[2]=0;} - print ("$row[0]\t$row[1]\t$row[2]\n"); - $stop = gdio::putline ("$row[0]\t$row[1]\t$row[2]"); - last if ($stop); - } - gdio::putline (""); # End of result - $sth->finish; -} - - -############################################################ -### List all genres ordered directly -sub list_all_genres_ordered{ - # get all genres and list them ordered alphabetically by genre - # fields: id, genre, freq - - ### Get all genre entries - my $sthA = $dbh->prepare( - 'SELECT id, genre FROM genre ORDER BY genre.id ' - ); - my $totcnt = $sthA->execute; - - ### Get used entries only with count -# my $sthB = $dbh->prepare( -# 'SELECT genre.id,genre.genre,COUNT(*) AS freq ' -# .'FROM tracks,genre ' -# .'WHERE tracks.genre1=genre.id OR tracks.genre2=genre.id ' -# .'GROUP BY genre.id ORDER BY genre.id ' -# ); - my $sthB = $dbh->prepare( - 'SELECT id, genre, freq ' - .'FROM genre ' - .'ORDER BY id ' - ); - - my $usedcnt = $sthB->execute; - -### The following code should actually go to 'gdtablefreq.pl' ! - - ### Merge frequencies of list B into list A (both lists must be ordered by id!) - # -> build "hierarchical sum": freq(node) = sum-of each freq(subnode) + proper freq - my $rowB = $sthB->fetchrow_hashref; - my ($i, $rowA, @listA, $rec); - while ($rowA = $sthA->fetchrow_hashref){ - $rec = {}; - $rec->{"id"} = $rowA->{id}; - $rec->{genre} = $rowA->{genre}; - $rec->{freq} = 0; # init freq counter - if($rowA->{id} eq $rowB->{id}){ - # copy frequency of B to A - $rec->{freq} = $rowB->{freq}; - $rowB = $sthB->fetchrow_hashref; # get next entry from B - } - push @listA, $rec; - } - - recalc_genre_freq_arrayofhash(@listA); - - @listA = sort { $gdserv::a->{genre} cmp $gdserv::b->{genre} } @listA; - - # "no genre" first: freq=1 - print ("\t\t1\n"); - gdio::putline ("\t\t1"); - - my $stop; - for $i (0 .. $#listA){ - print ("$listA[$i]{id}\t$listA[$i]{genre}\t$listA[$i]{freq}\n"); - $stop = gdio::putline - ("$listA[$i]{id}\t$listA[$i]{genre}\t$listA[$i]{freq}"); - last if ($stop); - } - gdio::putline (""); # End of result - $sthA->finish; - $sthB->finish; -} - - -############################################################ -sub recalc_genre_freq_arrayofhash{ - ### new version: works on array of hashes (id, genre, freq) - # requires that the array is ordered by id! - - ### first pass, get maximal genre id length - my $arrlen = @_; - my $maxlen = 0; - my $i; - - for $i( 0 .. $arrlen ){ - if(length($_[$i]{id}) > $maxlen){ - $maxlen = length($_[$i]{id}); - } - } - ### next passes: cumulate freq of ID with maxlen to ID with maxlen-1 - my ($parent, $ID); - while ($maxlen > 1){ - for $i( 0 .. $arrlen ){ - # 0:id, 1:genre, 2:freq - if(length($_[$i]{id}) == $maxlen){ - ### add freq of this item to freq of parent item - $ID = $_[$i]{id}; - chop($ID); - $parent=0; - while($parent<$arrlen && $_[$parent]{id} ne $ID){ - $parent++; - } - if($_[$parent]{id} eq $ID){ - $_[$parent]{freq} += $_[$i]{freq}; - } - else{print("ERROR: cant' find parent ID $ID \n");exit;} - } - } - $maxlen--; - } -} - - -############################################################ -### Modify genres table - -############################################################ -### Modify Genres: Delete -sub gen_delete{ - ### Delete a genre and move all it's tracks to the supergenre - my($genreid) = @_; # the ID of the genre to be deleted - - my $targetid = $genreid; - chop ($targetid); - print ("Delete genre id $genreid translate it to $targetid\n"); - - my ($retval1, $retval2, $gensdel); - $retval1 = $dbh->do('UPDATE tracks SET genre1="'.$targetid.'" WHERE genre1 LIKE "'.$genreid.'%"'); - $retval2 = $dbh->do('UPDATE tracks SET genre2="'.$targetid.'" WHERE genre2 LIKE "'.$genreid.'%"'); - $gensdel= $dbh->do('DELETE FROM genre WHERE id LIKE "'.$genreid.'%"'); - print("$gensdel genre entries deleted. $retval1 + $retval2 track records changed to supergenre.\n"); - - if($retval1 eq '0E0'){$retval1=0;} - if($retval2 eq '0E0'){$retval2=0;} - gdio::putline ("$gensdel genre entries deleted. ".($retval1+$retval2)." track records changed."); - gdio::putline (""); # End of result -} - - -############################################################ -### find a free genre for a given prefix. It returns only the suffix. -sub gen_new_suffix{ - my ($prefix) = @_; - my $suffix = "a"; - ### search for a new id - my ($sth, $count); - while($suffix ne "z"){ - $sth = $dbh->prepare("SELECT * FROM genre WHERE id = '".$prefix.$suffix."'"); - $count = $sth->execute; - $sth->finish; - last if ($count eq '0E0'); - $suffix++; - } - return $suffix; -} - -############################################################ -### Modify Genres: Move -sub gen_move{ - ### Move a genre and all its subgenres to a new location in the hierarchy - my($genreidFrom, $genreidTo) = @_; # the two IDs - - print ("Move genre $genreidFrom to $genreidTo\n"); - - my $suffix = gen_new_suffix($genreidTo); # new subgenre in target genre - - if($suffix eq "z"){ - print ("No genre moved! You can't put more than 26 subgenres in a genre. \n"); - gdio::putline ("No genre moved! You can't put more than 26 subgenres in a genre."); - } - elsif( (length($genreidFrom) <= length($genreidTo)) - && (substr($genreidTo, 0, length($genreidFrom)) eq $genreidFrom) - ){ - print ("No genre moved! Target is a child of Source. \n"); - gdio::putline ("No genre moved! Target is a child of Source."); - } - else{ - ### replace all genre prefixes "$genreidFrom" by "$genreidTo.$suffix" - ### in tracks and genre table - - my $retval1 = $dbh->do( - 'UPDATE tracks SET genre1=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(genre1, '.(length($genreidFrom)+1).')) ' - .'WHERE genre1 LIKE "'.$genreidFrom.'%"'); - my $retval2 = $dbh->do( - 'UPDATE tracks SET genre2=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(genre2, '.(length($genreidFrom)+1).')) ' - .'WHERE genre2 LIKE "'.$genreidFrom.'%"'); - my $retval3 = $dbh->do( - 'UPDATE genre SET id=CONCAT("'.$genreidTo.'","'.$suffix.'",SUBSTRING(id, '.(length($genreidFrom)+1).')) ' - .'WHERE id LIKE "'.$genreidFrom.'%"'); - gdio::putline (($retval1+$retval2)." track genres, $retval3 genre IDs moved."); -# gdio::putline (" track genres moved."); - } - gdio::putline (""); # End of result -} - - -############################################################ -### Modify Genres: Rename -sub gen_rename{ - ### Rename an existing genre - my($genreid, $newgenrename) = @_; # the ID and the new name of the genre - print ("Add genre $newgenrename to $genreid\n"); - - my $retval = $dbh->do("UPDATE genre SET genre=".$dbh->quote($newgenrename)." WHERE id = '".$genreid."'"); - - gdio::putline ("Genre renamed to $newgenrename."); - gdio::putline (""); # End of result -} - - -############################################################ -### Modify Genres: Add -sub gen_add{ - ### Add a new genre - my($genreid, $newgenrename) = @_; # the ID of the genre where the new genre is added - print ("Add genre $newgenrename to $genreid\n"); - - my $suffix = gen_new_suffix($genreid); - - if($suffix eq "z"){ - print ("No genre added! You can't put more than 26 subgenres in a genre. \n"); - gdio::putline ("No genre added! You can't put more than 26 subgenres in a genre."); - } - else{ - my $retval = $dbh->do("INSERT INTO genre " - ."(id, id3genre, genre) " - ."VALUES('".$genreid.$suffix."',NULL,".$dbh->quote($newgenrename).")"); - gdio::putline ("Genre $newgenrename added."); - } - gdio::putline (""); # End of result -} - - - - -############################################################ -### Modify music types table - -############################################################ -### returns the number of records in the musictype table -sub typ_nb_records{ - my ($sth, $count); - $sth = $dbh->prepare("SELECT * FROM musictype"); - $count = $sth->execute; - $sth->finish; - return $count; -} - -############################################################ -### Modify Types: Add -sub typ_add{ - ### Add a new type - my($newtypename) = @_; - print ("Add type $newtypename\n"); - - my $index = typ_nb_records() + 1; - - my $retval = $dbh->do("INSERT INTO musictype " - ."(musictype, id) VALUES(".$dbh->quote($newtypename).",'".$index."')"); - gdio::putline ("Music type $newtypename added."); - gdio::putline (""); # End of result -} - -############################################################ -### Modify Types: Delete -sub typ_delete{ - ### Delete a type - - my($delid) = @_; # the ID of the type to be deleted - my $lastid = typ_nb_records() - 1; - - # sequence of id-numbers must be contiguous: -> "move" lastid over delid, - # overwriting delid. - - # Note: track.type starts from 0, musictype.id starts from 1 - # Bad, I know, but it's too late to change it. I ignore the num value - # of musictype.id, and only use it to define an order. - print ("Delete type id $delid, put $lastid to its place\n"); - - ### modify tracks table - my ($retval1, $retval2, $typsdel); - $retval1 = $dbh->do('UPDATE tracks SET type= 0 WHERE type = '.$delid); - $retval2 = $dbh->do('UPDATE tracks SET type='.$delid.' WHERE type = '.$lastid); - - ### modify musictypes table - $typsdel= $dbh->do('DELETE FROM musictype WHERE id ='.($delid+1)); - $retval2= $dbh->do('UPDATE musictype SET id='.($delid+1).' WHERE id = '.($lastid+1)); - - print("Type deleted. Type of ".$retval1." tracks set to NULL.\n"); - - if($retval1 eq '0E0'){$retval1=0;} - if($retval2 eq '0E0'){$retval2=0;} - gdio::putline ("Type deleted. Type of ".$retval1." tracks set to NULL."); - gdio::putline (""); # End of result -} - - -############################################################ -### Send complete track -sub send_track_row{ - # Sends a complete track row obained from a SELECT * FROM tracks - # query. - # The parameter is a track row (record) obtained by a - # $trackset->fetchrow_hashref command - - my ($trackrow) = @_; - ## if ($nbtrks > 0){ - print( - $trackrow->{artist}.", " - .$trackrow->{title}.", " - .$trackrow->{genre1}.", " - .$trackrow->{genre2}.", " - .$trackrow->{year}.", " - .$trackrow->{lang}.", " - .$trackrow->{type}.", " - .$trackrow->{rating}.", " - .$trackrow->{length}.", " - .$trackrow->{source}.", " - .$trackrow->{sourceid}.", " - .$trackrow->{tracknb}.", " - .$trackrow->{mp3file}.", " - .$trackrow->{condition}.", " - .$trackrow->{voladjust}.", " - .$trackrow->{created}.", " - .$trackrow->{modified}.", " - .$trackrow->{id}.", " - .$trackrow->{bitrate}.", " - .$trackrow->{haslyrics} - ."\n" - ); - - gdio::putline ( - $trackrow->{artist}."\t" - .$trackrow->{title}."\t" - .$trackrow->{genre1}."\t" - .$trackrow->{genre2}."\t" - .$trackrow->{year}."\t" - .$trackrow->{lang}."\t" - .$trackrow->{type}."\t" - .$trackrow->{rating}."\t" - .$trackrow->{length}."\t" - .$trackrow->{source}."\t" - .$trackrow->{sourceid}."\t" - .$trackrow->{tracknb}."\t" - .$trackrow->{mp3file}."\t" - .$trackrow->{condition}."\t" - .$trackrow->{voladjust}."\t" - .$trackrow->{created}."\t" - .$trackrow->{modified}."\t" - .$trackrow->{id}."\t" - .$trackrow->{bitrate}."\t" - .$trackrow->{haslyrics} - ); - - # These variables give a hint, if the frequencies should be recalculated (when a track is updated) - $trk_last_id = $trackrow->{id}; - $trk_last_lang = $trackrow->{lang}; - $trk_last_genre1 = $trackrow->{genre1}; - $trk_last_genre2 = $trackrow->{genre2}; -} - - - -############################################################ -### Set CDDB_get parameters -my %cddb_config; -# following variables just need to be declared if different from defaults -$cddb_config{CDDB_HOST}="freedb.freedb.org"; # set cddb host -$cddb_config{CDDB_PORT}=8880; # set cddb port -$cddb_config{CDDB_MODE}="cddb"; # set cddb mode: cddb or http -$cddb_config{CD_DEVICE}="/dev/cdrom"; # set cd device -# user interaction welcome? -$cddb_config{input}=0; # 1: ask user if more than one possibility - # 0: no user interaction - - -############################################################ -### Get and return id and toc of currently inserted CD -sub get_cd_diskid{ - use CDDB_get; - # get id and track lengths - my $diskid=CDDB_get::get_discids($cddb_config{CD_DEVICE}); -# $track[0]{cddbid} = sprintf "%lx", $diskid->[0]; # get hex notation - $diskid->[0] = sprintf "%08lx", $diskid->[0]; # get hex notation - return $diskid; -} - -############################################################ -### Get and return directory of currently inserted CD -sub get_cd_directory{ -# returns array of hashes: index 0: cddbid; index 1..: length (in seconds) -# index 0: {cddbid (hex, without '0x')}, {title}, {artist} -# other indexes: {tracklength-sec}, {title}, {artist} -# -# title and artist are only defined, if a cddb entry was found -# -# 1) if the system has internet access, a matching record is -# searched at freedb.org -# 2) searches for a matching cddb record in ~/cddb/* -# 3) If a record couldn't be found, an array with default strings -# is returned -# - - use CDDB_get; - #use Net::Ping; #can't use it because it requires root privileges - - my @track; - my $diskid = get_cd_diskid(); - my $base = gdparams::gdbase(); - $track[0]{artist} = "Artist"; - $track[0]{title} = "Album Title"; - $track[0]{cddbid} = $diskid->[0]; - my $nbtracks = $diskid->[1]; - my $toc = $diskid->[2]; - my $cddbid = $diskid->[0]; - my ($lengthsec); - my $i=1; - while ($i <= $nbtracks) {#i[$i] ->{min}*60+$toc->[$i] ->{sec}) - -($toc->[$i-1]->{min}*60+$toc->[$i-1]->{sec}); - $track[$i]{track}= $i; - $track[$i]{length}= $lengthsec; - $track[$i]{artist}=" "; # some default values - $track[$i]{title} ="CD Track $i"; - $i++; - } - - -#print "\nBEGIN ping\n"; -#my $p = Net::Ping->new("icmp"); -#print "freedb.freedb.org is alive.\n" if $p->ping("freedb.freedb.org", 2); -#$p->close(); -#print "END ping\n"; - - if ($gdparms::systemonline){# Are we online? - ################################################################## - ### We are online, Try to get matching freedb entry - my %cd=CDDB_get::get_cddb(\%cddb_config); - - if(defined $cd{title}) { - $track[0]{artist} = $cd{artist}; - $track[0]{title} = $cd{title}; - #$track[0]{year} = $cd{year}; - ### Add track info to @track - my $i=1; - while ($i <= $nbtracks) { - #print "$i: $cd{track}[$i-1] \n"; - $track[$i]{artist} = $cd{artist}; - $track[$i]{title} = $cd{track}[$i-1]; - # ...{genre} = $cd{cat}; - $i++; - } - } - else{ - print "freedb: NOT FOUND cddbid: $cd{id}\n"; - } - } - else{ - ################################################################## - ### We are offline, try to find a cddb record on the local machine - my $cddb_file = `find $base/cddb -name $cddbid -print`; - $cddb_file = (split /\s+/, $cddb_file)[0]; # get first match only - if(length($cddb_file) > 0){ - ### a cddb-file was found - get it - my @cddblst = get_cddb_rec_offline("$cddb_file"); - $i = 0; - while ($i <= $nbtracks){ - $track[$i]{artist} = $cddblst[$i]{artist}; - $track[$i]{title} = $cddblst[$i]{title}; - $i++; - } - } - } - return @track; -} - - -#sub get_cd__directory___OLD___{ -#sub get_cd__directory{ -# returns array of hashes: index 0: cddbid; index 1..: length (in seconds) -# my $tmpout="/tmp/cdda2wav.index.out"; -# my $nbtracks = 0; -# my @track; -# my $discid; -# my $line; -# -# system("rm $tmpout"); -# system("cdda2wav -D /dev/cdrom -N -d1 &> $tmpout"); -# open(TMPOUT, $tmpout); -# while(){ -# $line = $_; -# if($line=~m/total tracks/){ -# $line=~m/total tracks:([0-9]*)/; -# $nbtracks = $1; -# } -# while($line=~m/[0-9]*\.\(\s*[0-9]*:[0-9]*\.[0-9]*\)/){ -# $line=~m/([0-9]*)\.\(\s*([0-9]*):([0-9]*)\.[0-9]*\)/; -# $track[$1]{track}= $1; -# $track[$1]{length}= ($2*60)+$3; -# -# #push @track, $rec; -# $line=~s/[0-9]*\.\(\s*[0-9]*:[0-9]*\.[0-9]*\)/xx/; -# } -# if($line=~m/CDDB discid:\s*[0-9a-z]*/){ -# $line=~m/CDDB discid:\s*([0-9a-z]*)/; -# #print "Disc ID: $1\n"; -# $discid=$1; -# $discid =~ m/0x(\S*)/; # remove 0x -# $track[0]{cddbid}= $1; -# } -# } -# close(TMPOUT); -# return @track; -#} - - - -############################################################ -### Read the specified cddb-file and return its content -sub get_cddb_rec_offline{ -# returns array: index 0: album artist, album title; -# index 1..: track title; -# examples: -> separator: '-' or '/' -# DTITLE=Die Aerzte - Die Bestie in Menschengestalt -# DTITLE=MC Lyte / Ain't No Other - - my ($cddb_file) = @_; - - my @track; - my $line; - - open(CDDBF, $cddb_file) or die "can't open file $cddb_file\n"; - while(){ - $line = $_; - chop($line); - if($line=~m/DTITLE=.+/){ - if ($line=~m/DTITLE=(.+) \/ (.+)/){ - $track[0]{artist}= $1; - $track[0]{title} = $2; - } - elsif($line=~m/DTITLE=(.+) \- (.+)/){ - $track[0]{artist}= $1; - $track[0]{title} = $2; - } - elsif($line=~m/DTITLE=(.+)/){ - $track[0]{artist}= $1; - $track[0]{title} = ""; - } - } - - if($line=~m/TTITLE([0-9]+)=(.+)/){ - $track[$1+1]{artist}= $track[0]{artist}; - $track[$1+1]{title} = $2; - } - } - close(CDDBF); - - return @track; - -} - -############################################################ -### List directory of currently inserted CD plus informations from DB -sub list_cd_directory_info{ - # takes no parameter or the cddb-id - - my ($cddbid) = @_; - - my @track; - my $nbtracks; - my $i; - my $nbtrks; - my $nbalbs; - my $trseth; # track set handle - my @row; - my $trackrow; - my $base = gdparams::gdbase(); - my $cddb_file=""; - my @cddblst; - - ######################################################### - ### Set default values - @track = get_cd_directory(); - $nbtracks = @track - 1; - - ######################################################### - ### Send album record first - $trseth = $dbh->prepare( - 'SELECT * FROM album WHERE cddbid ="'. $cddbid . '" '); - $nbalbs = $trseth->execute; - if($nbalbs > 0){ - print("Matching album in database found"); - $trackrow = $trseth->fetchrow_hashref; - $trackrow->{cddbid} = $cddbid; - print("$trackrow->{artist}\t$trackrow->{title}\t$cddbid\n"); - gdio::putline("$trackrow->{artist}\t$trackrow->{title}\t$cddbid"); - } - else{ - if(defined($track[0]{title})){ - ### a matching online freedb entry was found - print("$track[0]{artist}\t$track[0]{title}\t$track[0]{cddbid}\n"); - gdio::putline("$track[0]{artist}\t$track[0]{title}\t$track[0]{cddbid}"); - } - else{ - print "Error: can't read CD directory at all\n"; - } - } - $trseth->finish; - - ######################################################### - ### Send each track - for ($i=1; $i<=$nbtracks; $i++){ - ### get first track with given cddb-id and track number - - if (length($cddbid)>=8){ - $trseth = $dbh->prepare( - 'SELECT * FROM tracks ' - .'WHERE sourceid ="'. $cddbid . '" ' - .' AND tracknb = '.$i ); - $nbtrks = $trseth->execute; - $trackrow = $trseth->fetchrow_hashref; - } - - if ((length($cddbid)<8) || ($nbtrks < 1)){ - ### set default values - $nbtrks = 0; - $trackrow->{artist} = $track[$i]{artist}; - $trackrow->{title} = $track[$i]{title}; - $trackrow->{genre1} = ""; - $trackrow->{genre2} = ""; - $trackrow->{year} = ""; - $trackrow->{lang} = ""; - $trackrow->{type} = 0; - $trackrow->{rating} = 0; - $trackrow->{length} = $track[$i]{length}; - $trackrow->{source} = 0; - $trackrow->{sourceid} = $track[0]{cddbid}; - $trackrow->{tracknb} = $i; - $trackrow->{mp3file} = ""; - $trackrow->{condition}= 0; - $trackrow->{voladjust}= 0; - #$trackrow->{created} = - #$trackrow->{modified} = - $trackrow->{bitrate} = $gdparms::defrecbitrate; - $trackrow->{haslyrics}=""; - } - - ### send all fields - send_track_row($trackrow); - - $trseth->finish; - }# end for - - gdio::putline (""); # End of result -} -############################################################ -### List directory of currently inserted CD plus informations from DB -sub list_inbox_album_directory{ - # takes as parameter the album directory (without /home/music/inbox prefix) - - my ($path) = @_; - - my ($curfile); - my $base = gdparams::gdbase(); - my $fullpath = "$base/inbox/albums/$path"; - print "import album at $fullpath\n"; - - unlink <$base/inbox/trxx*>; - unlink <$base/inbox/tmp-album-dir>; - system "cd \"$fullpath\"; rm -f trxx*"; - opendir INBOX, "$fullpath"; - my @inboxfile = readdir INBOX; - closedir INBOX; - - ### generate new mp3-filename (get highest trxx... filename) - my $fileid = gdgentools::last_imported_tracknb($dbh); - $fileid += 1; # next available id - my $cddbid = sprintf("%08ld", $fileid); - my $tracknb = 1; - - ######################################################### - ### Send album record first - - print "$path\t$path\t$cddbid\n"; - gdio::putline("$path\t$path\t$cddbid"); - #print "ln -s \"$fullpath\" \"$base/inbox/tmp-album-dir\"\n"; - system "ln -s \"$fullpath\" \"$base/inbox/tmp-album-dir\""; - - ######################################################### - ### Send each track - foreach $curfile (@inboxfile){ - ### check if file is audio format - if ( gdgentools::audio_filetype($curfile) eq "mp3" - || gdgentools::audio_filetype($curfile) eq "ogg" - || gdgentools::audio_filetype($curfile) eq "flac"){ - send_inbox_track_info($curfile, $fileid, "$cddbid", $tracknb, "$fullpath"); - $fileid += 1; - $tracknb += 1; - } - }# end foreach - - gdio::putline (""); # End of result -} - - - -############################################################ -### List directory of current inbox plus informations from the metatags -sub list_inbox_directory_info{ - - my $base = gdparams::gdbase(); - - unlink <$base/inbox/trxx*>; - opendir INBOX, "$base/inbox"; - my @ibfile = readdir INBOX; - closedir INBOX; - - my ($curfile); - - - ### generate new mp3-filename (get highest trxx... filename) - my $fileid = gdgentools::last_imported_tracknb($dbh); - - ### Send each track - foreach $curfile (@ibfile){ - ### check if file is mp3 format - if ( gdgentools::audio_filetype($curfile) eq "mp3" - || gdgentools::audio_filetype($curfile) eq "ogg" - || gdgentools::audio_filetype($curfile) eq "flac"){ - $fileid += 1; #next available id - send_inbox_track_info($curfile, $fileid, "", 1, "$base/inbox"); - } - }# end foreach - - gdio::putline (""); # End of result -} - -############################################################ -### the file $curfile must be valid audio file -# if it is a audiofile, all available metadata and other track -# informations are sent to the server -sub send_inbox_track_info{ - - my ($curfile, $fileid, $cddbid, $tracknb, $path) = @_; - my ($id3genre, $title, $audiofile, $trackrow); - - my $base = gdparams::gdbase(); - - ### check if file has a legal audio format - - if ( gdgentools::audio_filetype($curfile) eq "mp3" - || gdgentools::audio_filetype($curfile) eq "ogg" - || gdgentools::audio_filetype($curfile) eq "flac"){ - print ("\ncurrentfile is: $curfile \n"); - - $audiofile = sprintf("trxx%08ld.%s", $fileid, gdgentools::audio_filetype($curfile)); - symlink "$path/$curfile", "$base/inbox/$audiofile"; - - # get info from meta tags - $trackrow->{title} = gdgentools::audiofile_title("$path/$curfile"); # takes filename as default of no metatag present - $trackrow->{artist} = gdgentools::audiofile_artist("$path/$curfile"); - $trackrow->{genre2} = ""; - $trackrow->{year} = gdgentools::audiofile_year("$path/$curfile"); - $trackrow->{lang} = "-"; - $trackrow->{type} = 1; # medium - $trackrow->{rating} = 0; - $trackrow->{length} = gdgentools::audiofile_lengthsec("$path/$curfile"); - $trackrow->{source} = 0; # CD - $trackrow->{sourceid} = $cddbid; # could also be empty - $trackrow->{tracknb} = $tracknb; - $trackrow->{mp3file} = $audiofile; - $trackrow->{condition} = 0; # OK - $trackrow->{voladjust} = 0; - $trackrow->{created} = ""; - $trackrow->{modified} = ""; - $trackrow->{id} = ""; # a new one will be automatically generated - $trackrow->{bitrate} = gdgentools::get_bitrate_str("$path/$curfile"); - - # get genre if available and translate to gd-genre - $trackrow->{genre1} = gdgentools::audiofile_genre($dbh, "$path/$curfile"); -# $id3genre = gdgentools::audiofile_genre("$path/$curfile"); -# $trackrow->{genre1} = gdgentools::genre_id3togd($dbh, $id3genre); - - ### send all fields - $trackrow->{haslyrics} = ""; - send_track_row($trackrow); - - }# end if is mp3file -} - -############################################################ -### import booklet/cover images -sub import_cover_img{ - # imports the jpeg images in a directory and associates them to an album - # the images are imported in lexical order. - # Naming scheme: trxx(cd-id)-(num).jpg, where num is an automatically - # incremented counter. The file imgxx(cd-id)-00.jpg is the front cover, - # the other are the following pages in a booklet. - - # Parameters: 1) full directory path, 2) cd-id (like 0x10ac77e0, xx00001496) - - gdgentools::import_cover_images($dbh, @_); -} - - -############################################################ -### Get one track and send all information details from DB -sub get_track_details{ - # takes as parameter the track-ID - - my ($trackid) = @_; - my $base = gdparams::gdbase(); - - - my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); - $sth->execute; - my $trackrow = $sth->fetchrow_hashref; - - if(length($trackrow->{mp3file})>0){ - if(length($trackrow->{lyrics})>0){ - $trackrow->{haslyrics} = "1"; - } - } - ### send all fields - send_track_row($trackrow); - - $sth->finish; - - gdio::putline (""); # End of result -} - - -############################################################ -### Delete specified track record and mp3 file -sub delete_track{ -# deletes the playlist with the specified id - my ($trackid) = @_; - my $sqlcmd; - my $base = gdparams::gdbase(); - - ### Get track record - my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); - $sth->execute; - my $trackrow = $sth->fetchrow_hashref; - my $trackfile = $trackrow->{mp3file}; - truncstr($trackrow->{artist}, $shrtFieldLen); - truncstr($trackrow->{title}, $shrtFieldLen); - print("Deleting ($trackrow->{id}) $trackrow->{artist}, $trackrow->{title}\n"); - - ### Delete track record - $sqlcmd = ("DELETE FROM tracks WHERE id = ".$trackid); -#print" \$dbh->do($sqlcmd);\n"; - $dbh->do($sqlcmd); - - ### Delete mp3 file -#print("rm $base/[0-9][0-9]/$trackfile \n"); - if (length($trackfile)>4){ - system("rm $base/[0-9][0-9]/$trackfile"); - } - $sth->finish; - -} - - -############################################################ -### Delete specified album and all associated track records and mp3 files -sub delete_album_tracks{ -# deletes the playlist with the specified id - my ($albumid) = @_; - - if(length($albumid)>=6) { - ### Delete all associated tracks - ### get tracks of the album - my $trksth = do_tracks_of_album_query($albumid); - my $trow; - while($trow = $trksth->fetchrow_hashref){ - delete_track($trow->{id}); - } - $trksth->finish; - - ### Delete album record - print("Deleting Album $albumid\n"); - my $sqlcmd = ("DELETE FROM album WHERE cddbid = '".$albumid."'"); -#print"\$dbh->do($sqlcmd);\n" - $dbh->do($sqlcmd); - } -} - - -############################################################ -### Get one track and send lyrics (or empty text) -sub get_track_lyrics{ - # takes as parameter the track-ID - - my ($trackid) = @_; - - my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trackid"); - $sth->execute; - my $trackrow = $sth->fetchrow_hashref; - - ### insert one space into empty lines (protocol requires non empty content lines) - my $lyrics = $trackrow->{lyrics}; - - ### send lyrics - my @lyrlines = split /\n/, $trackrow->{lyrics}; - chomp @lyrlines; - my $line; - print ("Sending lyrics: ".scalar(@lyrlines)." lines\n"); - foreach $line (@lyrlines){ # line end is a \015\012 sequence - $line =~ s/\012$//; # chop off very last newline, if there is one - $line =~ s/\015$//; # chop off very last newline, if there is one - if (length($line)==0) {$line = " ";} - gdio::putline ($line); - } - gdio::putline (""); # End of result - - $sth->finish; -} - -############################################################ -### Update the lyrics of a track -sub update_track_lyrics{ - # parameters: track-ID, lyrics lines - - my $trackid = shift @_; - my $lyrics = join "\n", @_; - - print("Lyrics recieved for $trackid:\n$lyrics\n"); - $dbh->do("UPDATE tracks " - ."SET lyrics=".$dbh->quote($lyrics) - ."WHERE id=$trackid"); - -} - - -############################################################ -### Trim mp3 file -sub trim_mp3_file{ - # parameters: track-ID, lyrics lines - - my ($mp3file, $startframe, $endframe) = @_; - my ($mp3fpath, $mp3directory, $undofname); - my $base = gdparams::gdbase(); - - $mp3fpath = gdgentools::get_full_audiofile_path($mp3file); - $mp3directory = dirname($mp3fpath); - $undofname = $mp3directory."/TrimUndo-".$mp3file; - - - print ("rm $mp3directory/TrimUndo-*\n"); # remove old undofile - system("rm $mp3directory/TrimUndo-*"); - print ("mv $mp3fpath $undofname\n"); # save original mp3file - system("mv $mp3fpath $undofname"); - - print ("gdmp3cutter $undofname $startframe $endframe $mp3fpath\n"); - #system("gdmp3cutter $undofname $startframe $endframe $mp3fpath"); - ### continuously send results, line by line - my ($res, $resline); - open CMDRES, "gdmp3cutter $undofname $startframe $endframe $mp3fpath |"; - #autoflush CMDRES 1; - while($resline = ){ - chop($resline); - print ("\"$resline\"\n"); - gdio::putline ($resline); - } - - print ("END\n"); - gdio::putline (""); # End of result -} - -############################################################ -### Get one album and send all information details from DB -sub trim_mp3_file_undo{ # restores the trimmed mp3 file (if possible) - # parameters: track-ID, lyrics lines - - my ($mp3file) = @_; - my ($mp3fpath, $mp3directory, $undofname); - - - my $base = gdparams::gdbase(); - my $undofname = `ls $base/[0-9][0-9]/TrimUndo-$mp3file`; # get full path - chop($undofname); - - $mp3directory = dirname($undofname); - $mp3fpath = $mp3directory."/".$mp3file; - - #gdio::putline ("mv $undofname $mp3fpath"); - print ("mv $undofname $mp3fpath \n"); # restore original mp3file - system("mv $undofname $mp3fpath"); - - gdio::putline (""); # End of result -} - - -############################################################ -### Get one album and send all information details from DB -sub get_album_details{ - # takes as parameter the cddb-ID - - my ($albumid) = @_; - - if (length($albumid) > 8) { ### sometimes, a distorted album id preceeded by 4 trashy characters - # is passed to this routine (reason is unknown). - # this is just a workaround sent in by Frank (slightly modified by me) - print "warning: albumid preceeded by trash! fixing it.\n"; - ($albumid) = substr($albumid,-8,8); - } - - my $base = gdparams::gdbase(); - - - my $sth = $dbh->prepare("SELECT * FROM album WHERE cddbid=\"$albumid\""); - $sth->execute; - my $row; - if($row = $sth->fetchrow_hashref){ - ### send all fields - print("$row->{artist}\n"); - print("$row->{title}\n"); - gdio::putline ("$row->{artist}"); - gdio::putline ("$row->{title}"); - } - gdio::putline (""); # End of result - - $sth->finish; -} - -############################################################ -### check currently inserted CD and compare to GDDB -sub check_current_cd{ - -# This routine finds all distinct cddb-ID's in the GDDB that have -# the same stem as the cddb-ID of the currently inserted CD. -# The routine is needed to solve the problem of potentially ambiguous -# cddb-ID's. It is usually called before grabbing a CD. - -# my @track = get_cd_directory(); - #$track[0]{cddbid} =~ m/0x(\S*)/; - #my $cddbid = $track[0]{cddbid}; - my $diskid = get_cd_diskid(); - my $cddbid = $diskid->[0]; - - ### Get all cddb-id's with root $cddbid - my $sth = $dbh->prepare( - 'SELECT DISTINCT sourceid ' - .'FROM tracks ' - .'WHERE sourceid LIKE "'. $cddbid. '%" ' - .'ORDER BY sourceid ' - ); - - my $rv = $sth->execute; - - ### Get first track for each distinct cddb-id - my $trseth; # track set handle - my $row; - my $trackrow; - while($row = $sth->fetchrow_hashref){ - ### get first track with current cddb-id - - $trseth = $dbh->prepare( - 'SELECT artist, title FROM tracks ' - .'WHERE sourceid ="'. $row->{sourceid} . '" ORDER BY tracknb '); - $trseth->execute; - $trackrow = $trseth->fetchrow_hashref; - - print("$row->{sourceid}\t$trackrow->{artist}, $trackrow->{title}\n"); - gdio::putline ("$row->{sourceid}\t$trackrow->{artist}, " - ."$trackrow->{title}"); - $trseth->finish; - } - if($rv > 0){ ### propose also new unambiguous key - ### bulletproof is to take biggest extension instead of $rv!!! - - print ("$cddbid.$rv\tNone of these: New CD\n"); - gdio::putline("$cddbid.$rv\tNone of these: New CD"); - } - gdio::putline (""); # End of result - - $sth->finish; -} - -############################################################ -### read inbox album directories -sub list_inbox_album_directories{ - -# This routine finds all directoies in ~music/inbox/albums that -# contain mp3 or ogg files. Each directory is considered as an -# album - - my $base = gdparams::gdbase(); - - my @filelist = `ls -R1 $base/inbox/albums`; - my ($curfile, $directory); - - $directory=""; - - foreach $curfile (@filelist){ - chop($curfile); - if ($curfile =~ m/\/inbox\/albums\/(.*):/ ){ - #print "$curfile is a directory\n"; - $directory = $1; - } - if(length($directory)>0 and - ( gdgentools::audio_filetype($curfile) eq "ogg" - or gdgentools::audio_filetype($curfile) eq "mp3" - or gdgentools::audio_filetype($curfile) eq "flac") - ){ - # we have a audio file in a valid directory (directory not yet printed) - print "$directory\n"; - gdio::putline "$directory"; - $directory = ""; # mark as printed - } - } - - gdio::putline (""); # End of result - -} - - -############################################################ -### Open/close the CD tray -sub open_cd_tray{ - system("eject"); -} -sub close_cd_tray{ - system("eject -t"); -} - -############################################################ -### Get rip state -sub get_rip_state{ # send CD rip status to client - my $base = gdparams::gdbase(); - # get first item of rip-list - my $trid = gdgentools::tracklist_get_item($dbh, $playerid, $rp_list, 0); - if($trid != 0){ - my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trid"); - my $rv = $sth->execute; - my ($wavf, $track, $wavfsize); - if($track = $sth->fetchrow_hashref){ - if($track->{mp3file} =~ /(.*)\.\w+/){ - $wavf = `ls $base/[0-9][0-9]/$1.wav`; - chop($wavf); - } - else{print("getripstate: can't find wavfile\n");} - truncstr($track->{artist}, 20); - truncstr($track->{title}, 20); - print ("$track->{tracknb}. $track->{artist} - $track->{title}\n"); - gdio::putline ("$track->{tracknb}. $track->{artist} - $track->{title}"); - - $wavfsize = (-s "$wavf"); - my $wavtotsize = $track->{length} * 44100 * 4; # totalSize = length(sec)*sampRate*bytesPerSample - $wavtotsize++; # prevent from divison by zero - #print("length: $track->{length}, fsize: $wavfsize, soll: $wavtotsize\n"); - my $percent = sprintf("%.2f%%", ($wavfsize * 100) / $wavtotsize); - print ("$percent percent\n"); - gdio::putline ("$percent"); - my $queuelen = gdgentools::tracklist_get_nb_items($dbh, $playerid, $rp_list); - $queuelen--; - print ("$queuelen tracks queued\n"); - gdio::putline ("$queuelen tracks queued"); - } - else{ - print ("ERROR: can't find track in database\n"); - gdio::putline ("ERROR: can't find track in database"); - } - $sth->finish; - } - gdio::putline (""); # End of result -} - - -############################################################ -### Get compress state -sub get_compress_state{ # send track compress status to client - my $base = gdparams::gdbase(); - # get first item of compression-list - my $trid = gdgentools::tracklist_get_item($dbh, $playerid, $co_list, 0); -#print("top of compression list: track $trid\n"); - if($trid != 0){ - my $sth = $dbh->prepare("SELECT * FROM tracks WHERE id=$trid"); - my $rv = $sth->execute; - my ($track, $audiofile, $currfilesize, $totalfilesize, $percent, $queuelen, $datarate); - my ($audiofmt, $audioparam); - if($track = $sth->fetchrow_hashref){ - $audiofile = `ls $base/[0-9][0-9]/$track->{mp3file}`; - chop($audiofile); - truncstr($track->{artist}, 20); - truncstr($track->{title}, 20); - print ("$track->{tracknb}. $track->{artist} - $track->{title}\n"); - gdio::putline ("$track->{tracknb}. $track->{artist} - $track->{title}"); - - $currfilesize = (-s "$audiofile"); - ($audiofmt, $datarate) = gdgentools::bitrate_str_to_format_param($track->{bitrate}); # split "mp3 128" - if ($audiofmt eq "mp3" || $audiofmt eq "ogg"){ - ### datarate is bitrate - $totalfilesize = ($track->{length} * $datarate * 1000) / 8; # totalSize = length*bitrate/BitsPerByte - } - if ($audiofmt eq "flac"){ - ### datarate is sampling rate - $totalfilesize = ($track->{length} * $datarate * 1010 * 2); # totalSize = length*bitrate*BytesPerByte - } - $totalfilesize++; # prevent from divison by zero - #print("file: $audiofile, length: $track->{length}, bitrate: $datarate, fsize: $currfilesize, soll: $totalfilesize\n"); - $percent = sprintf("%.2f%%", ($currfilesize * 100) / $totalfilesize); - print ("$percent\n"); - gdio::putline ("$percent"); - - $queuelen = gdgentools::tracklist_get_nb_items($dbh, $playerid, $co_list); - $queuelen--; - print ("$queuelen wav-files queued\n"); - gdio::putline ("$queuelen wav-files queued"); - } - $sth->finish; - } - gdio::putline (""); # End of result -} - - -############################################################ -### Misc commands ### -############################################################ - -############################################################ -### command burn audio CD -sub pl_burn_cd{ # Arguments in @_ are the - - my ($trackid, $sth); - my $mp3list; - my $row; - my $base = gdparams::gdbase(); - - $mp3list = ""; - - ### Write track tiles+artists to file (for booklet printing etc.) - open(RECLIST, ">$base/tmp/gdburnlist.txt"); - my $trkcnt = 1; - - foreach $trackid (@_){ - $sth = get_track($trackid); # perform database query - - $row = $sth->fetchrow_hashref; - $sth->finish; - print("add to CD $base/??/$row->{mp3file} ($row->{title}) \n"); - print(RECLIST "$trkcnt. $row->{artist} $row->{title} \n"); - $mp3list = $mp3list . " " . "$base/??/" . $row->{mp3file}; - $trkcnt ++; - } - close(RECLIST); - print("gdburn.sh " . $mp3list . "\n"); - system("gdburn.sh " . $mp3list . "&"); -} - - - -############################################################ -### Update browse directory (external script) -sub update_browse_directory{ - my $base = gdparams::gdbase(); - system("$base/bin/gdmakelinks.pl -s &"); # -s silent -} - -### Update browse directory (external script) -sub export_id3_tags{ - my $base = gdparams::gdbase(); -# system("cd $base; $base/bin/gdexportdb.pl --id3 &"); - system("cd $base; $base/bin/gdexportdb.pl --metatags &"); -} - -### Shut down entire server (external script) -sub shut_down_server{ - use vars qw($shutdowncmd); - print("Executing $gdparms::shutdowncmd \n"); - system($gdparms::shutdowncmd); -# system("/usr/sbin/usershutdown -h now &"); -} - - - -############################################################ -### General Query Command - -### General database query -sub general_db_query{ - my ($dbquery) = @_; - my ($sth, @row, $rowstr); - - ### Get # tracks - $sth = $dbh->prepare($dbquery); - $sth->execute; - if(@row = $sth->fetchrow_array){ - $rowstr = join "\t", @row; - print "$rowstr\n"; - gdio::putline ($rowstr); - } - gdio::putline (""); # End of result - $sth->finish; -} - -sub general_db_query_count{ # needed because older mysql-versions have a bug with COUNT(DISTINCT ... - my ($dbquery) = @_; - my ($sth, @row, $rowstr, $nbrec); - - ### Get # tracks -#print "Database Query(count): $dbquery\n"; - $sth = $dbh->prepare($dbquery); - $nbrec = $sth->execute; - print "$nbrec\n"; - gdio::putline ($nbrec); - gdio::putline (""); # End of result - $sth->finish; -} - - -############################################################ -### Soundcard/Volume Commands -sub set_volume{ - my ($vol) = @_; - gdsoundcard::sndc_set_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0), $vol); -} - -sub get_volume{ - my $vol = gdsoundcard::sndc_get_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); - - gdio::putline ($vol); - gdio::putline (""); # End of result -} - -sub save_volume{ - gdsoundcard::sndc_save_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); -} -sub inc_volume{ - gdsoundcard::sndc_inc_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); -} -sub dec_volume{ - gdsoundcard::sndc_dec_volume(gdgentools::playerdefinition($dbh, $playerid, $audchannel0)); -} - -############################################################ -### General Shell Command - -### General shell command -sub general_sh_command{ - my ($shcommand) = @_; - system($shcommand); -} - -### General shell command and send back result -sub general_sh_command_res{ - my ($shcommand) = @_; - my ($res, $resline); - $res = `$shcommand`; - my @reslines = split /\n/, $res; - while ($resline = shift @reslines){ - print ("\"$resline\"\n"); - gdio::putline ($resline); - } - gdio::putline (""); # End of result -} - -sub general_sh_command_res_continuous{ -# continuously sends results, line by line - my ($shcommand) = @_; - my ($res, $resline); - open CMDRES, "$shcommand |"; - #autoflush CMDRES 1; - while($resline = ){ - chop($resline); - print ("\"$resline\"\n"); - gdio::putline ($resline); - } - gdio::putline (""); # End of result -} - -### General shell command and send back result -sub gd_basedir{ - my $base = gdparams::gdbase(); - print ("$base\n"); - gdio::putline ($base); - gdio::putline (""); # End of result -} - - - -############################################################ -### Database and Disc statistics -sub full_statistics{ - my $base = gdparams::gdbase(); - my ($sth, $row, $msg); - - gdio::putline ("DB statistics"); - ### Get # tracks - $sth = $dbh->prepare("SELECT COUNT(*) AS cnt FROM tracks"); - $sth->execute; - if($row = $sth->fetchrow_hashref){ - $msg = " ".$row->{cnt}." Tracks"; - print "$msg \n"; - gdio::putline ($msg); - } - $sth->finish; - - ### Get # albums - $sth = $dbh->prepare("SELECT COUNT(*) AS cnt FROM album"); - $sth->execute; - if($row = $sth->fetchrow_hashref){ - $msg = " ".$row->{cnt}." Albums"; - print "$msg \n"; - gdio::putline ($msg); - } - $sth->finish; - - gdio::putline (" "); - gdio::putline ("Disc statistics"); - gdio::putline (" (dir: used / free)"); - - ### Get mp3 directories and check each directory - my @mdir = gdparams::mp3dirs(); - my $i=0; - my (@dfres, $totused, $totfree); - - $totused=0; $totfree=0; - while($i < @mdir){ - if (-d "$base/$mdir[$i]"){ - @dfres = split / +/, `df -m $base/$mdir[$i]|tail -1`; - $msg = " ".$mdir[$i].": ".$dfres[2]."M / ".$dfres[3]."M"; - print "$msg \n"; - gdio::putline ($msg); - $totused += $dfres[2]; - $totfree += $dfres[3]; - } - else{print "$base/$mdir[$i] is not a directory or does not exist\n";} - $i++; - } - - $msg = " tot: ".$totused."M / ".$totfree."M"; - print "$msg \n"; - gdio::putline ($msg); - - gdio::putline (""); # End of result - - ### "Side Effect": print player-id and -type - print "\nStatus: playerid=$playerid, audiochannel=$audchannel0\n"; - -} - - -############################################################ - -sub server_alive_test{ # Ping - print ("Ping: GiantDisc Server alive\n"); - gdio::putline ("GiantDisc Server alive"); - gdio::putline (""); # End of result -} - - -sub serial_test{ - my $i; - for($i=0; $i<5; $i++){ - print("$i "); - gdio::putline(" 123456789 123456789 123456789 123456789 123456789 123456789 123456789"); - } - gdio::putline (""); # End of result - print("\n"); -} - - -END{ - ### close database connection - print("### close database connection\n"); - $dbh->disconnect; - - if($rippipe_open){close_rippipe();} -} -# -1; -# diff --git a/scripts/gdsoundcard.pm b/scripts/gdsoundcard.pm deleted file mode 100755 index 519181f..0000000 --- a/scripts/gdsoundcard.pm +++ /dev/null @@ -1,149 +0,0 @@ -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2003, Rolf Brugger -# -################################################## - -package gdsoundcard; - -# -# soundcard drivers to control volume etc -# -# -# - -#use lib '/usr/local/bin'; -use strict; - -############################################################ -### Constants & global variables - - - -############################################################ - -sub sndc_init{ -### initialize - -# actually not used yet - - my ($playertype, $playerhost, $sounddevice) = @_; - - if ($playertype == 0){ # local oss soundcard - ; - } - - elsif ($playertype == 20){ # exstreamer - ; - } - else{ - print "Warning: unknown player type $playertype\n"; - } -} - - - -############################################################ -### Volume routines -# 0 <= volume <= 100 - - -sub sndc_set_volume{ -### Set volume - - my ($playertype, $playerhost, $sounddevice, $volume) = @_; - - if ($playertype == 0){ # local oss soundcard - system "aumix -v$volume"; - } - - elsif ($playertype == 20){ # exstreamer - use integer; - my $cmd = "v=".($volume/5)."\n"; - gdgentools::exstreamer_command($playerhost, $cmd); - } - else{ - print "Warning: unknown player type $playertype\n"; - } -} - - -sub sndc_get_volume{ -### Get volume -# returns the currently set volume. - - my ($playertype, $playerhost, $sounddevice) = @_; - my $volume = 0; - - if ($playertype == 0){ # local oss soundcard - my ($shcommand) = @_; - my ($res, $resline, @reslines); - $res = `aumix -vq`; - @reslines = split /\n/, $res; - $resline = shift (@reslines); - if ($resline =~ m/\D*(\d+).*/){ - $volume = $1; - } - else{print "Warning: Get volume - can't match aumix output\n";} - } - - elsif ($playertype == 20){ # exstreamer - my $cmd = "v=\n"; # cmd get volume - my $res = gdgentools::exstreamer_command_res($playerhost, $cmd); - if ($res =~ m/\<.*\>(\d+)\<.*\>/){ - $volume = ($1)*5; - } - else{ - $volume = 50; - } - } - else{ - print "Warning: unknown player type $playertype\n"; - } - return $volume; -} - - -sub sndc_save_volume{ -### Save default volume - - my ($playertype, $playerhost, $sounddevice, $volume) = @_; - - if ($playertype == 0){ # local oss soundcard - system "aumix -S"; - } - elsif ($playertype == 20){ # exstreamer - # the exstreamer always saves the volume setting in its flash rom - ; - } - else{ - print "Warning: unknown player type $playertype\n"; - } -} - -############################################################ - -sub sndc_inc_volume{ -### Increases volume by 5% - my ($playertype, $playerhost, $sounddevice) = @_; - my $volume = sndc_get_volume($playertype, $playerhost, $sounddevice); - $volume += 5; - if ($volume>100){$volume=100;} - sndc_set_volume($playertype, $playerhost, $sounddevice, $volume); -} - -sub sndc_dec_volume{ -### decreases volume by 5% - my ($playertype, $playerhost, $sounddevice) = @_; - my $volume = sndc_get_volume($playertype, $playerhost, $sounddevice); - $volume -= 5; - if ($volume<0){$volume=0;} - sndc_set_volume($playertype, $playerhost, $sounddevice, $volume); -} - -############################################################ - -1; -# diff --git a/scripts/gdupdate.pm b/scripts/gdupdate.pm deleted file mode 100755 index 8242c6c..0000000 --- a/scripts/gdupdate.pm +++ /dev/null @@ -1,509 +0,0 @@ -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2000-2003, Rolf Brugger -# -################################################## - - -# Package for database modifications and consistency checks related -# to version updates - - -package gdupdate; - -use strict; - - -BEGIN{ -} - - -############################################################################### -### Version 1.32 -############################################################################### - -sub db_check_update_132{ - my ($dbh) = @_; - my ($sth, $count, $res); - - my $update = 0; - - ### new field 'audio channel' - $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'audiochannel'"); - if ($res < 1){ - $update = 1; - } - - $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'processid'"); - if ($res < 1){ - $update = 1; - } - - - return $update; -} - - -sub db_update_132{ - my ($dbh) = @_; - my ($sth, $count, $res); - - ### usage frequencies - $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'audiochannel'"); - if ($res < 1){ - print("Update table playerstate to version 1.32\n"); - print("rename index column playertype into audiochannel\n"); - $dbh->do("ALTER TABLE playerstate CHANGE playertype audiochannel INT NOT NULL"); - print("Adding field playertype to table playerstate\n"); - $dbh->do("ALTER TABLE playerstate ADD COLUMN playertype INT AFTER audiochannel"); - } - - ### player process id - $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'processid'"); - if ($res < 1){ - print("Update table playerstate to version 1.12\n"); - print("Adding field processid to table playerstate\n"); - $dbh->do("ALTER TABLE playerstate ADD COLUMN processid INT AFTER audiochannel"); - } - -} - - -############################################################################### -### Version 1.31 -############################################################################### - -sub db_check_update_131{ - my ($dbh) = @_; - my ($sth, $count, $res); - - my $update = 0; - - ### usage frequencies - $res = $dbh->do("SHOW COLUMNS FROM album LIKE 'genre'"); - if ($res < 1){ - $update = 1; - } - - ### Optimizations - my $row; - $sth = $dbh->prepare("SHOW TABLE STATUS FROM GiantDisc LIKE 'playerstate'"); - $count = $sth->execute; - if($row = $sth->fetchrow_hashref){ - if ($row->{Type} ne "HEAP"){ - $update = 1; - } - } - $sth->finish; - - - my ($dbh, $table, $column, $indexspec) = @_; - - my ($sth, $count, $row); - $sth = $dbh->prepare("SHOW index FROM tracks"); - $count = $sth->execute; - while($row = $sth->fetchrow_hashref){ - last if ($row->{Key_name} eq "artist"); - } - if($row->{Key_name} eq "artist"){ - #print "index artist exists\n"; - ; - } - else{ - print "Alert: no additional indexes defined\n"; - print " consider upgrading the db-structure with 'gdupdatedb.pl'\n"; - } - $sth->finish; - - - return $update; -} - - -sub db_update_131{ - my ($dbh) = @_; - my ($sth, $count, $res); - - ### usage frequencies - $res = $dbh->do("SHOW COLUMNS FROM album LIKE 'genre'"); - if ($res < 1){ - print("Update table album to version 1.31\n"); - print("Adding field genre to table album\n"); - $dbh->do("ALTER TABLE album ADD COLUMN genre VARCHAR(10) AFTER modified"); - } - - ### Optimizations - my $row; - $sth = $dbh->prepare("SHOW TABLE STATUS FROM GiantDisc LIKE 'playerstate'"); - $count = $sth->execute; - if($row = $sth->fetchrow_hashref){ - if ($row->{Type} ne "HEAP"){ - print "Set table 'playerstate' to type HEAP\n"; - $dbh->do("ALTER TABLE playerstate TYPE=HEAP"); - } - } - $sth->finish; - - - test_and_add_index($dbh, "tracks", "artist", "artist(artist(10))"); - test_and_add_index($dbh, "tracks", "title", "title(title(10))"); - test_and_add_index($dbh, "tracks", "genre1", "(genre1)"); - test_and_add_index($dbh, "tracks", "genre2", "(genre2)"); - test_and_add_index($dbh, "tracks", "year", "(year)"); - test_and_add_index($dbh, "tracks", "lang", "(lang)"); - test_and_add_index($dbh, "tracks", "type", "(type)"); - test_and_add_index($dbh, "tracks", "rating", "(rating)"); - test_and_add_index($dbh, "tracks", "sourceid","(sourceid)"); - test_and_add_index($dbh, "tracks", "mp3file", "mp3file(mp3file(10))"); - - test_and_add_index($dbh, "album", "artist", "artist(artist(10))"); - test_and_add_index($dbh, "album", "title", "title(title(10))"); - test_and_add_index($dbh, "album", "genre", "(genre)"); - test_and_add_index($dbh, "album", "modified", "(modified)"); - -} - -sub test_and_add_index -{ - my ($dbh, $table, $column, $indexspec) = @_; - - my ($sth, $count, $row); - $sth = $dbh->prepare("SHOW index FROM $table"); - $count = $sth->execute; - while($row = $sth->fetchrow_hashref){ - last if ($row->{Key_name} eq $column); - } - if($row->{Key_name} eq $column){ - #print "index $column exists\n"; - ; - } - else{ - print "creating index $column\n"; - $dbh->do("ALTER TABLE $table ADD INDEX $indexspec"); - } - $sth->finish; -} - - -############################################################################### -### Version 1.14 -############################################################################### - -sub db_update_114{ - my ($dbh) = @_; - my ($sth, $count, $res); - - ### bitrate - $sth = $dbh->prepare("SELECT id FROM tracks WHERE length(bitrate)<4"); - $count = $sth->execute; - if ($count > 0){ - print("Update table tracks to version 1.14\n"); - print("enlarging field bitrate to 10 characters\n"); - $dbh->do("ALTER TABLE tracks MODIFY COLUMN bitrate VARCHAR(10)"); - - ### add prefix "mp3 " to all bitrate fields of mp3 tracks - $res=print("add prefix \"mp3 \" to all bitrate fields of mp3 tracks\n"); - $dbh->do("UPDATE tracks SET bitrate=CONCAT('mp3 ',bitrate) " - ."WHERE length(bitrate)<4 AND ( mp3file LIKE '%.mp3'" - ." OR mp3file LIKE 'http%')"); - print "mp3 track records updated\n"; - $dbh->do("UPDATE tracks SET bitrate=CONCAT('ogg ',bitrate) " - ."WHERE length(bitrate)<4 AND mp3file LIKE '%.ogg'"); - - ### check result - $sth = $dbh->prepare("SELECT id FROM tracks WHERE length(bitrate)<4"); - $count = $sth->execute; - if ($count > 0){ - print "\n"; - print "Warning: some track records could not be properly translated.\n"; - print " You might have tracks in your database with no audio-\n"; - print " file associated!\n\n"; - } - } - $sth->finish; -} - - - -############################################################################### -### Version 1.12 -############################################################################### - -sub db_update_112{ - my ($dbh) = @_; - my $res; - - ### add player process id -> killing pid with killall and killfam is unstable and too slow! - #$res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'processid'"); - #if ($res < 1){ - # print("Update table playerstate to version 1.12\n"); - # print("Adding field processid to table playerstate\n"); - # $dbh->do("ALTER TABLE playerstate ADD COLUMN processid INT AFTER snddevice"); - #} - - ### usage frequencies - $res = $dbh->do("SHOW COLUMNS FROM language LIKE 'freq'"); - if ($res < 1){ - print("Update table language to version 1.12\n"); - print("Adding field freq to table language\n"); - $dbh->do("ALTER TABLE language ADD COLUMN freq INT AFTER language"); - } - $res = $dbh->do("SHOW COLUMNS FROM genre LIKE 'freq'"); - if ($res < 1){ - print("Update table genre to version 1.12\n"); - print("Adding field freq to table genre\n"); - $dbh->do("ALTER TABLE genre ADD COLUMN freq INT AFTER genre"); - } -#else {print("-- column modified exists -> DB needs not be updated\n");} -} - - - -############################################################################### -### Version 1.11 -############################################################################### - -sub db_update_111{ - my ($dbh) = @_; - my $res; - - ### Table recordingitem - $res = $dbh->do("SHOW TABLES LIKE 'recordingitem'"); - if ($res < 1){ - print("recordingitem does not exist (upgrading...)\n"); - $dbh->do( - "create table recordingitem(" - ."trackid int," - ."recdate date," - ."rectime time," - ."reclength int," - ."enddate date," - ."endtime time," - ."repeat varchar(10)," - ."initcmd varchar(255)," - ."parameters varchar(255)," - ."atqjob int," - ."id int not null," - ."primary key(id)" - .")"); - } -#else{print("-- recordingitem does exist\n");} -} - - - -############################################################################### -### Version 0.97 -############################################################################### - -sub db_update_097{ - my ($dbh) = @_; - my $res; - - ### shuffle parameter - $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'shufflepar'"); - if ($res < 1){ - print("Update table album to version 0.97\n"); - print("Adding modified field shufflepar,shufflestat to table playerstate\n"); - $dbh->do("ALTER TABLE playerstate ADD COLUMN shufflepar varchar(255) AFTER state"); - $dbh->do("ALTER TABLE playerstate ADD COLUMN shufflestat varchar(255) AFTER shufflepar"); - } -#else {print("-- column modified exists -> DB needs not be updated\n");} -} - - - -############################################################################### -### Version 0.96 -############################################################################### - -sub db_update_096{ - my ($dbh) = @_; - my $res; - - ### album modification time - $res = $dbh->do("SHOW COLUMNS FROM album LIKE 'modified'"); - if ($res < 1){ - print("Update table album to version 0.96\n"); - print("Adding modified field to table album\n"); - $dbh->do("ALTER TABLE album ADD COLUMN modified date AFTER covertxt"); - } -#else {print("-- column modified exists -> DB needs not be updated\n");} -} - - -############################################################################### -### Version 0.95 -############################################################################### - -sub db_update_095{ - my ($dbh) = @_; - my $res; - - ### anchortime - $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'anchortime'"); - if ($res < 1){ - print("Update table playerstate to version 0.95\n"); - print("Adding anchortime field to table playerstate\n"); - $dbh->do("ALTER TABLE playerstate ADD COLUMN anchortime bigint AFTER framesremain"); - } -#else {print("-- column anchortime exists -> DB needs not be updated\n");} - - ### framestotal - $res = $dbh->do("SHOW COLUMNS FROM playerstate LIKE 'framestotal'"); - if ($res < 1){ - print("Update table playerstate to version 0.95\n"); - print("Renaming framesremain field to framestotal\n"); - $dbh->do("ALTER TABLE playerstate CHANGE framesremain framestotal INT"); - } -#else {print("-- column framestotal exists -> DB needs not be updated\n");} -} - - -############################################################################### -### Version 0.94 -############################################################################### - -sub check_new_mp3info080{ -# This routine checks if the version of mp3info is at least 0.8.0, which -# is required as of GD-version 0.94 -# A warning message is printed if mp3info should be updated - - my $infostr = `mp3info`; - if ($infostr =~ /MP3Info\D*([0-9]*).([0-9]*).([0-9]*)/){ - if ($2 < 8){ - print ("\n\n"); - print ("Warning: The Version of 'mp3info' on your system is $1.$2.$3\n"); - print (" At least version 0.8.0 is required. You can get it\n"); - print (" from http://www.ibiblio.org/mp3info\n"); - print ("\n\n"); - exit(0); - } - } - else{ - print ("Warning: could not extract version number of 'mp3info'\n"); - print (" 'mp3info' is not installed?\n"); - exit(0); - } -} - - - -sub db_update_094{ - my ($dbh) = @_; - my $res; - - ### Bitrate - $res = $dbh->do("SHOW COLUMNS FROM tracks LIKE 'bitrate'"); - if ($res < 1){ - print("Update table tracks to version 0.94\n"); - print("Adding bitrate field to table tracks\n"); - $dbh->do("ALTER TABLE tracks ADD COLUMN bitrate CHAR(4) AFTER lyrics"); - - ### Update records - my $base = gdparams::gdbase(); - my ($sth, $count, $row, $fname, $bitrate); - $sth = $dbh->prepare("SELECT * FROM tracks WHERE bitrate IS NULL OR bitrate=''"); - $count = $sth->execute; - print("I have to update the bitrate of $count records\n"); - while($row = $sth->fetchrow_hashref){ - $fname = `ls $base/[0-9][0-9]/$row->{mp3file}`; # get full path - chop($fname); - $bitrate = gdgentools::get_bitrate_str($fname); - print("Set bitrate of $row->{artist}/$row->{title} to $bitrate\n"); - $dbh->do( "UPDATE tracks SET bitrate='$bitrate' WHERE id=$row->{id}"); - - } - $sth->finish; - } -#else {print("-- column bitrate exists -> DB needs not be updated\n");} - - - ### Table player - $res = $dbh->do("SHOW TABLES LIKE 'player'"); - if ($res < 1){ - print("player does not exist (upgrading...)\n"); - $dbh->do( - "create table player( " - ."ipaddr varchar(255) not null," - ."uichannel varchar(255) not null," - ."logtarget int," - ."cdripper varchar(255)," - ."mp3encoder varchar(255)," - ."cdromdev varchar(255)," - ."cdrwdev varchar(255)," - ."id int not null," - ."primary key(id)" - .")"); - } -#else{print("-- player does exist\n");} - - - ### Table playerstate - $res = $dbh->do("SHOW TABLES LIKE 'playerstate'"); - if ($res < 1){ - print("playerstate does not exist (upgrading...)\n"); - $dbh->do( - "create table playerstate(" - ."playerid int not null," - ."playertype int not null," - ."snddevice varchar(255)," - ."playerapp varchar(255)," - ."playerparams varchar(255)," - ."ptlogger varchar(255)," - ."currtracknb int," - ."state varchar(4)," - ."pauseframe int, " - ."framesplayed int," - ."framesremain int," - ."primary key(playerid, playertype)" - .")"); - } -#else{print("-- playerstate does exist\n");} - - - ### Table tracklistitem - $res = $dbh->do("SHOW TABLES LIKE 'tracklistitem'"); - if ($res < 1){ - print("tracklistitem does not exist (upgrading...)\n"); - $dbh->do( - "create table tracklistitem(" - ."playerid int not null," - ."listtype smallint not null," - ."tracknb int not null," - ."trackid int not null," - ."primary key(playerid, listtype, tracknb)" - .")"); - } -#else{print("-- tracklistitem does exist\n");} - -} - -############################################################################### -### Version 0.92 -############################################################################### - -sub fix_leading_slash_bug -### removes leading / in the column tracks.mp3file -{ - my ($dbh) = @_; - my $numrec = $dbh->do( "UPDATE tracks SET mp3file=SUBSTRING(mp3file,2) " - ."WHERE mp3file LIKE '/%'"); - if ($numrec>0){ - print("fix_leading_slash_bug: $numrec records fixed!\n\n"); - } -} - - - -############################################################################### - -END{ - ; -} - - -# -1; diff --git a/scripts/myhash.pm b/scripts/myhash.pm deleted file mode 100755 index 5d8f1bd..0000000 --- a/scripts/myhash.pm +++ /dev/null @@ -1,29 +0,0 @@ -package myhash; - - -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2000, Rolf Brugger -# -################################################## - -#use lib '/usr/local/bin'; -#BEGIN{;} -#END{;} - - -############################################################ -### -sub addvaltohash{ # gets a current hashval and a new elment - # returns new hashval - my ($hashval,$newelement) = @_; - - return (($hashval << 5) ^ ($hashval >> 27)) ^ $newelement; - # (^ is bitwise EXOR) -} - - -1; -# -- cgit v1.2.3 From 27a9843da016ffb5d2d36f03b3ae20776528989b Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 29 Aug 2004 14:07:53 +0000 Subject: Cleaned up input helper scripts git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@126 e10066b5-e1e2-0310-b819-94efdf66514b --- scripts/createdb.mysql | 5 ++--- scripts/make-db | 8 -------- scripts/make-empty-db | 13 +++++-------- scripts/make-tables | 26 -------------------------- 4 files changed, 7 insertions(+), 45 deletions(-) delete mode 100755 scripts/make-db delete mode 100755 scripts/make-tables diff --git a/scripts/createdb.mysql b/scripts/createdb.mysql index 8b9ea42..b68d90c 100755 --- a/scripts/createdb.mysql +++ b/scripts/createdb.mysql @@ -4,7 +4,6 @@ DROP DATABASE IF EXISTS GiantDisc; CREATE DATABASE GiantDisc; use GiantDisc; -grant all privileges on GiantDisc.* to music@'%'; -grant all privileges on GiantDisc.* to music@localhost; -grant all privileges on GiantDisc.* to apache@localhost with grant option; +grant all privileges on GiantDisc.* to vdr@'%'; +grant all privileges on GiantDisc.* to vdr@localhost; diff --git a/scripts/make-db b/scripts/make-db deleted file mode 100755 index ec25531..0000000 --- a/scripts/make-db +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -export SCRIPTDIR=/home/andi/muggle/import -cd $SCRIPTDIR -echo "creating db" -mysql < createdb.mysql - -# diff --git a/scripts/make-empty-db b/scripts/make-empty-db index 59d2c21..3acb512 100755 --- a/scripts/make-empty-db +++ b/scripts/make-empty-db @@ -1,22 +1,19 @@ #!/bin/sh -export SCRIPTDIR=/home/lvw/Development/muggle-plugin/scripts -cd $SCRIPTDIR - echo "creating db" mysql -u root < createdb.mysql echo "creating tables" -mysql -u root < $SCRIPTDIR/createtables.mysql +mysql -u root < createtables.mysql echo "reading genres" -echo " use GiantDisc; load data local infile '$SCRIPTDIR/genres.txt' into table genre;"| mysql -u root --local-infile=1 +echo " use GiantDisc; load data local infile 'genres.txt' into table genre;"| mysql -u root --local-infile=1 echo "reading languages" -echo "use GiantDisc; load data local infile '$SCRIPTDIR/languages.txt' into table language;" | mysql -u root --local-infile=1 +echo "use GiantDisc; load data local infile 'languages.txt' into table language;" | mysql -u root --local-infile=1 echo "reading musictypes" -echo "use GiantDisc; load data local infile '$SCRIPTDIR/musictypes.txt' into table language;" | mysql -u root --local-infile=1 +echo "use GiantDisc; load data local infile 'musictypes.txt' into table language;" | mysql -u root --local-infile=1 echo "reading sources" -echo "use GiantDisc; load data local infile '$SCRIPTDIR/sources.txt' into table language;" | mysql -u root --local-infile=1 +echo "use GiantDisc; load data local infile 'sources.txt' into table language;" | mysql -u root --local-infile=1 diff --git a/scripts/make-tables b/scripts/make-tables deleted file mode 100755 index 1dc9c01..0000000 --- a/scripts/make-tables +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -################################################## -# -# GiantDisc mp3 Jukebox -# -# © 2000-2002, Rolf Brugger -# -################################################## - -cd /home/andi/muggle/import - -echo "creating tables" -mysql < createtables.mysql - -echo "reading genres" -mysql --local-infile=1 < readgenres.mysql - -echo "reading languages" -mysql --local-infile=1 < readlanguages.mysql - -echo "reading musictypes" -mysql --local-infile=1 < readmusictypes.mysql - -echo "reading sources" -mysql --local-infile=1 < readsources.mysql -- cgit v1.2.3 From 068ea169c5d357d192b35c3544f4f9e90be96b29 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 29 Aug 2004 14:39:33 +0000 Subject: Import running in basic version git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@127 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 8 ++- README | 168 +++++++++++++++++++++++++++++++++++++++++++++++- mg_tools.c | 87 ++++++++++++------------- mugglei.c | 170 ++++++++++++++++++++++++++++++++++++++++--------- scripts/createdb.mysql | 7 +- 5 files changed, 358 insertions(+), 82 deletions(-) diff --git a/Makefile b/Makefile index 46bac1e..870bd6e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.9 2004/05/28 15:29:18 lvw Exp $ +# $Id: Makefile,v 1.10 2004/08/29 14:39:33 lvw Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -44,6 +44,7 @@ INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/m DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +MIFLAGS += -I/usr/include/taglib -ltag -lmysqlclient ### The object files (add further files here): OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o @@ -72,6 +73,9 @@ libvdr-$(PLUGIN).so: $(OBJS) $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@ @cp $@ $(LIBDIR)/$@.$(VDRVERSION) +mugglei: mg_tools.o mugglei.o + $(CXX) $(CXXFLAGS) $(MIFLAGS) -o $^ + dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @@ -82,5 +86,3 @@ dist: clean clean: @-rm -f $(OBJS) $(BINOBJS) $(DEPFILE) *.so *.tgz core* *~ - @-rm -f sh_muggle sh_muggle2 - diff --git a/README b/README index 4f6cbbf..209dd46 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ This is a "plugin" for the Video Disk Recorder (VDR). -Written by: Andi Kellner, +Written by: Andi Kellner Lars von Wedel - Ralf Klüber , + Ralf Klueber , Project's homepage: URL @@ -10,4 +10,166 @@ Latest version available at: URL See the file COPYING for license information. -Description: + +PLEASE! + +This is a difficult plugin. It's nice but difficult. +With difficult I mean, that due to the underlying +database, many more sources of error can occur as +opposed to other plugins. + +Take some time to carefully read these instructions. +Please provide feedback to the authors whenever you +think, these instructions are not appropriate, wrong, +or useless in any other sense. + +1 DESCRIPTION + +The muggle plugin provides a database link for VDR so that selection of media becomes more flexible. +Prerequisites are describedin Section 2, Notes on Compilation are in Section 3. Before using the plugin, +you need to import your media into the database (cf. Section 4). The configuration of VDR and startup +parameters are descibed in Section 5. + +2 PREREQUISITES + +The plugin is written for VDR 1.2.6. In addition, the following pieces of +software are required: + +- mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) +- mySQL client libraries (Debian package libmysqlclient-dev) +- libmad (for mp3 decoding) (Debian package libmad0-dev) +- libtag (for ID3 tag reading/writing) (Debian package libtag1-dev) + +The server need not be on the same machine as the VDR. Also, music tracks can reside somewhere else, +if they are available through a remote filesystem (NFS, Samba). However, in this case you should +know what you are doing in terms of networking and security issues. + + +3 INSTALLING + +Unpack the sources in PLUGINS/src below your VDR directory (i.e. where all your other plugins are. +For example (paths and version numbers may vary) + + cd /usr/local/src/VDR/PLUGINS/src + tar xvjf muggle-0.1.7.tar.bz2 + +Establish a symlink as you would for other plugins: + + ln -s muggle-0.1.7 muggle + +Within the VDR main directory (e.g. /usr/local/src/VDR) issue a + + make plugins + +This should build all relevant stuff. If you have difficulties, check that required libraries are +in the library directories stated in the muggle Makefile. + + +4 IMPORT + +The import is done in two steps: First, a database is created and initialized with proper data structures (so-called schema). +Then, these data structures are filled from the ID3 tags of your music tracks. + +4.1 Setup Database + +This step can be done on the database server or on some other client machine. +Within the directory scripts there are a few helpful files to support setting +up the database. Change into that directory:# + + cd scripts + +The first step is to essentially create the database: + + mysql -u root -p < createdb.mysql + +You will need to enter your root password that you choose during mySQL installation. +Next, we generate the database tables (schema): + + mysql -u root -p < createtables.mysql + +Further, initial data about known languages, genres, sources and musictypes is fed into the database: +Execute these commands on a single line, the \ for the linebreak ist just for presentation purposes here. + + echo " use GiantDisc; load data local infile 'genres.txt' into table genre;" | \ + mysql -u root -p --local-infile=1 + + echo "use GiantDisc; load data local infile 'languages.txt' into table language;" | \ + mysql -u root --local-infile=1 + + echo "use GiantDisc; load data local infile 'musictypes.txt' into table language;" | \ + mysql -u root --local-infile=1 + + echo "use GiantDisc; load data local infile '$SCRIPTDIR/sources.txt' into table language;" | \ + mysql -u root --local-infile=1 + +You can find the sequence of commands in the file scripts/make-empty-db. Use it at your own luck. + +Please note, that the scripts and commands above are quite basic in terms of security (e.g. no +password set for the vdr user, no proper selection of privileges). You may want to spend some +time reading the mySQL documentation in order to set up a proper configuration. Especially when +VDR and mySQL will run on different machines you'll have to invest some time into mySQL +networking and access rights. + +If you want your database name to be different than 'GiantDisc' you will need to adapt the name +in the files createdb.mysql and createtables.mysql and in the commands above. Now your database +is ready for import. + +4.2 Import Music + +The next step is to feed all music information into the database. There is a small tool called 'mugglei' +in the muggle main directory. It connects to the database, evaluates ID3 tags from a file, and writes +the tags into the database. It runs on just one file, so you need some more effort using the Unix command +'find' to take all files into consideration. + +For this step, it is helpful, that all music files are somehow gathered under a toplevel directory. +It does not matter whether there are further subdirectories which organize files into genres, artists, +album or whatever. If this is not the case, you may want to take some time to do this. Read on before +you start + +You probably do not want to import all files in one go: albums on which tracks of various artists are found +(samplers) require different treatment than files of just one artist. What I did: all samplers are collected +below a special subdirectory "Assorted". Import is then run separately for those tracks. + +For now, let's assume your music tracks are located in /home/music and samplers are in /home/music/Assorted. + +First, import the files in Assorted. This requires the flag -a to mugglei. Further flags -h, -n, -u, and -p +specify database host, name, user and password, respectively. The filename to import is given using the -f +directive. Using 'find' you can import all files for assorted albums with a command like: + + find Assorted -name '*' -type f -exec mugglei -a -f {} \; + +For reasons of simplicity, the arguments -h, -n, -u and -p are not shown. You will need them if the default +values do not apply or modify the source code accordingly (beginning of function main). Also, make sure +that either mugglei is on your path or specify an absolute or relative path in the above command line. + +For "regular" albums, the following command may be helpful: + + find * -path 'Assorted' -prune -o -type f -exec mugglei -f {} \; + +It is important that you perform all these steps from the same location so the filenames are relative to +exactly the same directory (e.g. /home/music in the example case). + +Speed should not be an issue: on my machine, it takes about 10 secs to run the import of 60 assorted +albums with more than 600 tracks. Further 1200 tracks or so require 20 more secs. This depends on machine +configuration, of course. + +5 MUGGLE CONFIGURATION + +Muggle uses a small set of command line parameters in order to control the interaction with the mySQL server. +Let's look at an example: + + -P'muggle -h localhost -u vdr -n GiantDisc -t/home/music' + +The -h parameter specifies the database host, -u specifies the user, -n is the database name. The scripts mentioned +above do not make use of passwords, but restrict database acccess on a server basis. + +The -t argument specifies the top level directory of the music files. On a local installation, this is the +directory in which you executed the import steps (Chapter 4.2). + +6 USING MUGGLE + +Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. +Then turn to the playlist view using yellow and start play using again the red function key. + + + diff --git a/mg_tools.c b/mg_tools.c index d12d0df..b581396 100644 --- a/mg_tools.c +++ b/mg_tools.c @@ -1,15 +1,11 @@ -/*******************************************************************/ -/*! \file muggle_tools.cpp - * \brief A few util functions for standalone and plugin messaging - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/07/25 21:33:35 $ +/*! \file mg_tools.c + * \brief A few util functions for standalone and plugin messaging for the vdr muggle plugindatabase + * + * \version $Revision: 1.4 $ + * \date $Date: 2004/08/29 14:39:33 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author: lvw $ - * */ -/*******************************************************************/ #include "mg_tools.h" @@ -18,11 +14,12 @@ extern "C" #include #include } -#include +#include #define MAX_BUFLEN 2048 #define MAX_QUERY_BUFLEN 2048 + static char buffer[MAX_BUFLEN]; static char querybuf[MAX_QUERY_BUFLEN]; @@ -68,22 +65,20 @@ void mgWarning(const char *fmt, ...) { va_list ap; - - va_start(ap, fmt); - + va_start(ap, fmt); vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); + if(STANDALONE) { fprintf(stderr, "warning: %s\n", buffer); } else - { + { #if !STANDALONE - isyslog( "Warning: %s\n", buffer); + isyslog( "Warning: %s\n", buffer); #endif - - } - + } + va_end(ap); } @@ -91,58 +86,60 @@ void mgError(const char *fmt, ...) { va_list ap; - - va_start(ap, fmt); - + va_start(ap, fmt); vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); + if(STANDALONE) - { - fprintf(stderr, "Error: %s\n", buffer); - exit(1); - } + { + fprintf(stderr, "Error: %s\n", buffer); + exit(1); + } else - { + { #if !STANDALONE - isyslog( "Error in Muggle: %s\n", buffer); + isyslog( "Error in Muggle: %s\n", buffer); #endif - } + } va_end(ap); } MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { - va_list ap; - va_start( ap, fmt ); - vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); - - mgDebug(9, "mgSqlReadQuery: SQL query: '%s'", querybuf); + va_list ap; + va_start( ap, fmt ); + vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); - if( mysql_query(db, querybuf) ) + if( mysql_query(db, querybuf) ) { - mgError("SQL error in MUGGLE\n%s\n", querybuf); + mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } - - MYSQL_RES *result = mysql_store_result(db); - - return result; + + MYSQL_RES *result = mysql_store_result(db); + + va_end(ap); + return result; } void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) { - va_list ap; - - va_start(ap, fmt); + va_list ap; + va_start(ap, fmt); + vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap); - vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap); - if(mysql_query(db,querybuf)) + if( mysql_query(db, querybuf) ) { - mgError("SQL error in MUGGLE\n%s\n", querybuf); + mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } + + va_end(ap); } /* -------------------- begin CVS log --------------------------------- * $Log: mg_tools.c,v $ + * Revision 1.4 2004/08/29 14:39:33 lvw + * Import running in basic version + * * Revision 1.3 2004/07/25 21:33:35 lvw * Removed bugs in finding track files and playlist indexing. * diff --git a/mugglei.c b/mugglei.c index 191bebb..66fb766 100755 --- a/mugglei.c +++ b/mugglei.c @@ -16,7 +16,8 @@ using namespace std; MYSQL *db; -char *host, *user, *pass, *dbname; +string host, user, pass, dbname; +bool import_assorted; int init_database() { @@ -26,8 +27,8 @@ int init_database() return -1; } - if( mysql_real_connect( db, host, user, pass, - dbname, 0, NULL, 0 ) == NULL ) + if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), + 0, NULL, 0 ) == NULL ) { return -2; } @@ -61,9 +62,22 @@ time_t get_db_modification_time( long uid ) return mt; } -long find_file_in_database( string filename ) +TagLib::String escape_string( MYSQL *db, TagLib::String s ) { - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", filename.c_str() ); + char *buf = strdup( s.toCString() ); + char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); + + int len = mysql_real_escape_string( db, escbuf, buf, strlen( buf ) ); + + return TagLib::String( escbuf ); +} + +long find_file_in_database( MYSQL *db, string filename ) +{ + TagLib::String file = TagLib::String( filename.c_str() ); + file = escape_string( db, file ); + + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", file.toCString() ); MYSQL_ROW row = mysql_fetch_row( result ); // obtain ID and return @@ -73,7 +87,6 @@ long find_file_in_database( string filename ) // read tags from the mp3 file and store them into the corresponding database entry void update_db( long uid, string filename ) { - // char title[1024], album[1024], year[5], artist[1024], cddbid[20], trackno[5]; TagLib::String title, album, artist, genre, cddbid; uint trackno, year; @@ -92,20 +105,53 @@ void update_db( long uid, string filename ) artist = tag->artist(); trackno = tag->track(); genre = tag->genre(); + + title = escape_string( db, title ); + album = escape_string( db, album ); + artist = escape_string( db, artist ); - // TODO: CD identifier (?), playcounter, popularimeter?, volume adjustment + // TODO: CD identifier (if it exists), playcounter, popularimeter (rating?), volume adjustment, lyrics, cover // finally update the database // obtain associated album or create if( album == "" ) - { // no album found, associate with default album - cddbid = "0000unknown0000"; + { // no album found, create default album for artist + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() ); + MYSQL_ROW row = mysql_fetch_row( result ); + + // Default album does not yet exist (num rows == 0) + int nrows = mysql_num_rows(result); + if( nrows == 0 ) + { + // create new album entry "Unassigned" for this artist + long id = random(); + char *buf; + asprintf( &buf, "%d-%s", id, tag->artist().toCString() ); + cddbid = TagLib::String( buf ).substr( 0, 20 ); + cddbid = escape_string( db, cddbid ); + free( buf ); + + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"Unassigned\", \"%s\")", artist.toCString(), cddbid.toCString() ); + } + else + { // use first album found as source id for the track + cddbid = row[0]; + } } else { // album tag found, associate or create - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"%s\"", - album.toCString(), artist.toCString() ); + MYSQL_RES *result; + if( import_assorted ) + { // lookup an existing album by title only (artist should be "Various Artists" + result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"Various Artists\"", + album.toCString(), artist.toCString() ); + } + else + { + result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"%s\"", + album.toCString(), artist.toCString() ); + } MYSQL_ROW row = mysql_fetch_row( result ); // num rows == 0 ? @@ -115,11 +161,17 @@ void update_db( long uid, string filename ) // create new album entry long id = random(); char *buf; - asprintf( &buf, "%d-%s", id, album.toCString() ); + asprintf( &buf, "%d-%s", id, tag->album().toCString() ); cddbid = TagLib::String( buf ).substr( 0, 20 ); + cddbid = escape_string( db, cddbid ); free( buf ); - - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); + + if( import_assorted ) + { // in this case, the album author is "Various Artists" + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"Various Artists\", \"%s\", \"%s\")", album.toCString(), cddbid.toCString() ); } + else + { + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); } } else { // use first album found as source id for the track @@ -138,8 +190,8 @@ void update_db( long uid, string filename ) // int t = title.find( "'" ); // int a = artist.find( "'" ); mgSqlWriteQuery( db, - "INSERT INTO tracks (artist,title,year,sourceid,tracknb,mp3file)" - " VALUES (\"%s\", \"%s\", %d, \"%s\", %d, \"%s\")", + "INSERT INTO tracks (artist,title,genre1,genre2,year,sourceid,tracknb,mp3file)" + " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\")", artist.toCString(), title.toCString(), year, cddbid.toCString(), trackno, filename.c_str() ); /* cout << "-- TAG --" << endl; @@ -160,12 +212,12 @@ void update_tags( long uid, string filename ) } -void evaluate_file( string filename ) +void evaluate_file( MYSQL *db, string filename ) { if( 0 == init_database() ) { // is filename stored in database? - long uid = find_file_in_database( filename ); + long uid = find_file_in_database( db, filename ); if( uid >= 0 ) { // currently only update database, do not consider writing changes from the db back @@ -198,33 +250,91 @@ void evaluate_file( string filename ) int main( int argc, char *argv[] ) { - host = "134.130.124.222"; - user = "root"; - dbname = "giantdisc"; - pass = NULL; - /* + int option_index; + string filename; + + if( argc < 2 ) + { // we need at least a filename! + cout << "mugglei -- import helper for Muggle VDR plugin" << endl; + cout << "(C) Lars von Wedel" << endl; + cout << "This is free software; see the source for copying conditions." << endl; + cout << "" << endl; + cout << "Options:" << endl; + cout << " -h - specify host of mySql database server (default is 'localhost')" << endl; + cout << " -n - specify database name (default is 'GiantDisc')" << endl; + cout << " -u - specify user of mySql database (default is empty)" << endl; + cout << " -p - specify password of user (default is empty password)" << endl; + cout << " -f - name of music file to import" << endl; + cout << " -a - import track as if it was on an assorted album" << endl; + + exit( 1 ); + } + + // option defaults host = "localhost"; - user = "vdr"; dbname = "GiantDisc"; - pass = NULL; - */ + user = ""; + pass = ""; + import_assorted = false; + + // parse command line options + while( 1 ) + { + int c = getopt(argc, argv, "h:u:p:n:af:"); + + if (c == -1) + break; + + switch (c) + { + case 0: + { // long option + + } break; + case 'h': + { + host = optarg; + } break; + case 'u': + { + user = optarg; + } break; + case 'p': + { + pass = optarg; + } break; + case 'd': + { + dbname = optarg; + } break; + case 'a': + { + import_assorted = true; + } break; + case 'f': + { + filename = optarg; + } break; + } + } + // init random number generator struct timeval tv; struct timezone tz; gettimeofday( &tv, &tz ); - srandom( tv.tv_usec ); + srandom( tv.tv_usec ); int res = init_database(); if( !res ) { - update_db( 0, string( argv[1] ) ); + update_db( 0, filename ); } else { - printf( "Database initialization failed. Exiting.\n" ); + cout << "Database initialization failed. Exiting.\n" << endl; } - + return res; } diff --git a/scripts/createdb.mysql b/scripts/createdb.mysql index b68d90c..7ccdc89 100755 --- a/scripts/createdb.mysql +++ b/scripts/createdb.mysql @@ -3,7 +3,12 @@ DROP DATABASE IF EXISTS GiantDisc; CREATE DATABASE GiantDisc; + use GiantDisc; -grant all privileges on GiantDisc.* to vdr@'%'; + +/* The first line is useful for granting access to user vdr on all computers in a network. */ +/* grant all privileges on GiantDisc.* to vdr@'%'; */ + +/* Grant access to user vdr on the local machine */ grant all privileges on GiantDisc.* to vdr@localhost; -- cgit v1.2.3 From f352045259e69420324b1cb3877a8abf8fc0e3b4 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 30 Aug 2004 14:31:43 +0000 Subject: Documentation added git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@128 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 18 +++- gd_content_interface.c | 261 ++++++++++--------------------------------------- gd_content_interface.h | 254 +++++++++++++++++++++++++++++++++++++++++------ mg_tools.h | 33 ++++--- 4 files changed, 310 insertions(+), 256 deletions(-) diff --git a/TODO b/TODO index eaf5f5c..d11098e 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,14 @@ TODO File for Muggle ==================== +- Script to publish a version + - Checkout + - make dist + - copy .tgz, README, CHANGES, HISTORY into web directory + - generate documentation + - copy into web directory + - sync with web + Testing/bugs ============ - Test execution of playlist commands @@ -33,14 +41,15 @@ Code polishing - mgDatabase is not used? - should handle a static object with a MySQL connection - execute queries? - - - mgPlayer used what for? - Could save IP/host name and associate last playlist loaded - Check for unnecessary log commands - Generate HTML documentation using doxygen, - use dotty/gv for state machines of player + - use dotty/gv for state machines of player + - make available online + - Check compatibility for 1.3.12 Short term items @@ -137,10 +146,15 @@ Already Done - create tracklist from filter - create tree from filter - i18n (english and german) +- Album import + - Various artists ************************************************************ * * $Log: TODO,v $ +* Revision 1.14 2004/08/30 14:31:43 LarsAC +* Documentation added +* * Revision 1.13 2004/08/27 15:20:33 LarsAC * Updated open issues w.r.t. import * diff --git a/gd_content_interface.c b/gd_content_interface.c index 06dd35a..8df7c48 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,8 +1,8 @@ /*! \file gd_content_interface.c * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin * - * \version $Revision: 1.26 $ - * \date $Date: 2004/08/27 15:19:34 $ + * \version $Revision: 1.27 $ + * \date $Date: 2004/08/30 14:31:43 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: LarsAC $ * @@ -14,7 +14,7 @@ * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) * */ -/*******************************************************************/ + #define DEBUG #include "gd_content_interface.h" @@ -263,11 +263,6 @@ mgGdTrack::mgGdTrack(int sqlIdentifier, MYSQL dbase) } -/*! - ***************************************************************************** - * \brief copy constructor - * - ****************************************************************************/ mgGdTrack::mgGdTrack(const mgGdTrack& org) { m_uniqID = org.m_uniqID; @@ -285,26 +280,11 @@ mgGdTrack::mgGdTrack(const mgGdTrack& org) } } - -/*! - ***************************************************************************** - * \brief destructor - * - ****************************************************************************/ mgGdTrack::~mgGdTrack() { // nothing to be done } -/*! - ***************************************************************************** - * \brief accesses the database to fill the actual data fields - * - * In order to avoid innecessary queries to the database, the content fields - * of the mgGdTrack object may not be filled upon creation. - * As soon as the first content field is needed, this private function - * is called to fill all content fields at once - ****************************************************************************/ bool mgGdTrack::readData() { MYSQL_RES *result; @@ -363,42 +343,24 @@ bool mgGdTrack::readData() return true; } -/*! - ***************************************************************************** - * \brief returns value for _mp3file - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ string mgGdTrack::getSourceFile() { - if(!m_retrieved) - { + if( !m_retrieved ) + { readData(); - } + } return m_mp3file; } -/*! - ***************************************************************************** - * \brief returns value for m_title - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ string mgGdTrack::getTitle() { - if(!m_retrieved) + if( !m_retrieved ) { - readData(); + readData(); } - return m_title; + return m_title; } -/*! - ***************************************************************************** - * \brief returns value for m_artist - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ string mgGdTrack::getArtist() { if(!m_retrieved) @@ -408,30 +370,24 @@ string mgGdTrack::getArtist() return m_artist; } -/*! - ***************************************************************************** - * \brief returns a string for one field of the item - * - * This is a generic function that shozld work for all content items - ****************************************************************************/ string mgGdTrack::getLabel(int col) { - if(!m_retrieved) + if( !m_retrieved ) { - readData(); + readData(); } - switch(col) + switch(col) { - case 0: - return m_title; - case 1: - return m_artist; - case 2: - return m_album; - case 3: - return m_genre; - default: - return ""; + case 0: + return m_title; + case 1: + return m_artist; + case 2: + return m_album; + case 3: + return m_genre; + default: + return ""; } } @@ -445,27 +401,15 @@ bool mgGdTrack::setTrackInfo(vector *info) return false; } -/*! - ***************************************************************************** - * \brief returns value for m_album - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ string mgGdTrack::getAlbum() { - if(!m_retrieved) + if( !m_retrieved ) { - readData(); + readData(); } - return m_album; + return m_album; } -/*! - ***************************************************************************** - * \brief returns value for m_genre - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ string mgGdTrack::getGenre() { if(!m_retrieved) @@ -475,12 +419,6 @@ string mgGdTrack::getGenre() return m_genre; } -/*! - ***************************************************************************** - * \brief returns value for m_year - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ int mgGdTrack::getYear() { if(!m_retrieved) @@ -490,12 +428,6 @@ int mgGdTrack::getYear() return m_year; } -/*! - ***************************************************************************** - * \brief returns value for m_rating - * - * If value has not been retrieved from the database, radData() is called first - ****************************************************************************/ int mgGdTrack::getRating() { if(!m_retrieved) @@ -518,90 +450,36 @@ string mgGdTrack::getImageFile() return "dummyImg.jpg"; } -/*! - ***************************************************************************** - * \brief sets the field for m_title to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ void mgGdTrack::setTitle(string new_title) { m_title = new_title; } -/*! - ***************************************************************************** - * \brief sets the field for m_artist to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ void mgGdTrack::setArtist(string new_artist) { m_artist = new_artist; } - -/*! - ***************************************************************************** - * \brief sets the field for m_album to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ void mgGdTrack::setAlbum(string new_album) { m_album = new_album; } - -/*! - ***************************************************************************** - * \brief sets the field for m_genre to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ void mgGdTrack::setGenre(string new_genre) { m_genre = new_genre; } - -/*! - ***************************************************************************** - * \brief sets the field for m_year to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ void mgGdTrack::setYear(int new_year) { m_year = new_year; } - -/*! - ***************************************************************************** - * \brief sets the field for m_rating to the specified value - * - * Note: The new value is not stored in the database. - * This is only done, when writeData() is called - ****************************************************************************/ void mgGdTrack::setRating(int new_rating) { m_rating = new_rating; } - -/*! - ***************************************************************************** - * \brief stores current values in the sql database - * - * Note: At the moment, only the values stored directly in the 'tracks' - * database are updated - ****************************************************************************/ bool mgGdTrack::writeData() { mgSqlWriteQuery(&m_db, "UPDATE tracks " @@ -612,19 +490,12 @@ bool mgGdTrack::writeData() return true; } -//------------------------------------------------------------------ -//------------------------------------------------------------------ -// -// class GdTracklist -// -//------------------------------------------------------------------ -//------------------------------------------------------------------ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) { MYSQL_RES *result; MYSQL_ROW row; int trackid; - + result = mgSqlReadQuery(&db_handle, "SELECT tracks.id " " FROM tracks, album, genre WHERE %s" @@ -634,34 +505,15 @@ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) while((row = mysql_fetch_row(result)) != NULL) { // row[0] is the trackid - if(sscanf(row[0], "%d", &trackid) != 1) + if(sscanf(row[0], "%d", &trackid) != 1) { - mgError("Can not extract integer track id from '%s'", - row[0]); + mgError("Can not extract integer track id from '%s'", + row[0]); } - m_list.push_back(new mgGdTrack(trackid, db_handle)); + m_list.push_back(new mgGdTrack(trackid, db_handle)); } - } - -//------------------------------------------------------------------ -//------------------------------------------------------------------ -// -// class GdPlaylist -// -//------------------------------------------------------------------ -//------------------------------------------------------------------ - -/*! - ***************************************************************************** - * \brief Constructor: opens playlist by name - * - * \param listname user-readable identifier of the paylist - * \param db_handl database which stores the playlist - * - * If the playlist does not yet exist, an empty playlist is created - ****************************************************************************/ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) { MYSQL_RES *result; @@ -671,14 +523,14 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) m_db = db_handle; // - // check, if the database really exists + // check, if the playlist already exists // - result=mgSqlReadQuery(&m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - listname.c_str()); + result = mgSqlReadQuery(&m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + listname.c_str()); nrows = mysql_num_rows(result); - if(nrows == 0) - { + if( nrows == 0 ) + { mgDebug(3, "No playlist with name %s found. Creating new playlist\n", listname.c_str()); @@ -692,55 +544,47 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) m_listname = listname; // now read thenew list to get the id - result=mgSqlReadQuery(&m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - listname.c_str()); + result = mgSqlReadQuery(&m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + listname.c_str()); nrows = mysql_num_rows(result); row = mysql_fetch_row(result); if(sscanf(row [0], "%d", & m_sqlId) !=1) - { - mgError("Invalid id '%s' in database", row [5]); - } + { + mgError("Invalid id '%s' in database", row [5]); + } - } + } else // playlist exists, read data - { + { row = mysql_fetch_row(result); if(sscanf(row [0], "%d", & m_sqlId) !=1) - { - mgError("Invalid id '%s' in database", row [5]); - } + { + mgError("Invalid id '%s' in database", row [5]); + } m_author = row[1]; m_listname = listname; // now read allentries of the playlist and // write them into the tracklist insertDataFromSQL(); - }// end 'else (playlist exists) - + }// end 'else (playlist exists) + m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists } -/*! - ***************************************************************************** - * \brief empty destructor - * - * Nothing to be done. Constructor of parent class takes care - ****************************************************************************/ GdPlaylist::~GdPlaylist() { } + void GdPlaylist::setListname(std::string name) { m_listname = name; m_sqlId = -1; } -/*! - ***************************************************************************** - * \brief reads the track list from the sql database into a locallist - ****************************************************************************/ + int GdPlaylist::insertDataFromSQL() { MYSQL_RES *result; @@ -1436,6 +1280,9 @@ mgContentItem* GdTreeNode::getSingleTrack() /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.c,v $ + * Revision 1.27 2004/08/30 14:31:43 LarsAC + * Documentation added + * * Revision 1.26 2004/08/27 15:19:34 LarsAC * Changed formatting and documentation * diff --git a/gd_content_interface.h b/gd_content_interface.h index 7d43d74..5d2626b 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -3,8 +3,8 @@ * \brief Data objects for content (e.g. mp3 files, movies) * for the vdr muggle plugin database * - * \version $Revision: 1.10 $ - * \date $Date: 2004/08/27 15:19:34 $ + * \version $Revision: 1.11 $ + * \date $Date: 2004/08/30 14:31:43 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: LarsAC $ * @@ -53,6 +53,9 @@ class gdFilterSets : public mgFilterSets public: + /*! \addtogroup Object creation and destruction */ + /*\@{*/ + /*! * \brief the default constructor * @@ -66,6 +69,8 @@ class gdFilterSets : public mgFilterSets */ virtual ~gdFilterSets(); + /*\@}*/ + /*! * \brief compute restriction w.r.t active filters * @@ -95,6 +100,9 @@ class mgGdTrack : public mgContentItem { public: + /*! \addtogroup Object creation and destruction */ + /*\@{*/ + /*! * \brief a constructor * @@ -128,9 +136,22 @@ class mgGdTrack : public mgContentItem * \brief the destructor */ virtual ~mgGdTrack(); + + /*\@}*/ - virtual mgContentItem::contentType getContentType(){return mgContentItem::GD_AUDIO;} + /*! + * \brief obtain the content type of the item + */ + virtual mgContentItem::contentType getContentType() + { + return mgContentItem::GD_AUDIO; + } + /*! + * \brief obtain the associated player object + * + * \todo what is this used for? + */ virtual mgMediaPlayer getPlayer() { return mgMediaPlayer(); @@ -138,42 +159,129 @@ class mgGdTrack : public mgContentItem /*! \addtogroup Data read access */ /*\@{*/ + + /*! + * \brief returns a certain field of the item as a string + * + * 0 - title + * 1 - artist + * 2 - album + * 3 - genre + */ virtual std::string getLabel( int col = 0 ); + /*! + * \brief returns value for the track title + */ virtual std::string getTitle(); + + /*! + * \brief returns value for the location of the track on disk + */ virtual std::string getSourceFile(); + + /*! + * \brief returns the genre of the track + */ virtual std::string getGenre(); + + /*! + * \brief returns the artist of the track + */ std::string getArtist(); + + /*! + * \brief returns value the album to which the track belongs + */ std::string getAlbum(); + + /*! + * \brief obtain the location of the image file + */ std::string getImageFile(); + + /*! + * \brief obtain the year of the track + */ int getYear(); + + /*! + * \brief obtain the duration of the track + */ int getDuration(); + + /*! + * \brief obtain the rating of the track + */ virtual int getRating(); + /*! + * \brief obtain the complete track information + */ virtual std::vector *getTrackInfo(); + /*\@}*/ /*! \addtogroup Data write access */ /*\@{*/ + + /*! + * \brief set the title of the track + */ void setTitle(std::string new_title); + + /*! + * \brief set the title of the track + */ void setArtist(std::string new_artist); + + /*! + * \brief set the album name of the track + */ void setAlbum(std::string new_album); + + /*! + * \brief set the genre of the track + */ void setGenre(std::string new_genre); - void setYear(int new_rating); + + /*! + * \brief set the year of the track + */ + void setYear(int new_year); + + /*! + * \brief set the rating of the track + */ void setRating(int new_rating); + /*! + * \brief set complete information of the track + */ virtual bool setTrackInfo(std::vector*); + /*! + * \brief make changes persistent + * + * The changes made using the setXxx methods are not + * stored persistently in the database until writeData + * is called. + * + * \note only the tracks table in the SQL database is updated. + * Genre and album field information is lost. + */ bool writeData(); + /*\@}*/ - + + //! \brief a special instance denoting an undefined track static mgGdTrack UNDEFINED; private: /*! * \brief the database in which the track resides - * */ + */ MYSQL m_db; /*! @@ -185,7 +293,9 @@ private: */ bool m_retrieved; - //! \brief the artist name + /*! + * \brief the artist name + */ std::string m_artist; /*! @@ -225,16 +335,25 @@ private: int m_length; /*! - * \brief Access the data of the item from the database + * \brief Access the data of the item from the database and fill actual data fields + * + * In order to avoid abundant queries to the database, the content fields + * of the mgGdTrack object may not be filled upon creation. As soon as the + * first content field is needed, this private function is called to fill + * all content fields at once. */ bool readData(); }; +/* + * \brief a list of tracks stored in the databas + */ class GdTracklist : public mgTracklist { public: - GdTracklist(MYSQL db_handle, std::string restrictions); + + GdTracklist(MYSQL db_handle, std::string restrictions); }; /*! @@ -246,29 +365,67 @@ class GdPlaylist : public mgPlaylist { public: + /*! \addtogroup Object creation and destruction */ + /*\@{*/ + + /*! + * \brief opens an existing or creates empty playlist by name + * + * If the playlist does not yet exist, an empty playlist is created. + * + * \param listname - user-readable identifier of the paylist + * \param db_handle - database which stores the playlist + */ GdPlaylist(std::string listname, MYSQL db_handle); - /* opens existing or creates empty playlist */ - + + /*! + * \brief destructor + * + * \note the destructor only destroys the in-memory footprint of the playlist, + * it does not modify the database. + */ virtual ~GdPlaylist(); + + /* \@} */ + /*! + * changes the listname of the playlist (and unset the sql id) + */ virtual void setListname(std::string name); - /* changes the listname of the playlist (and unset the sql id */ + /*! + * returns the total duration of all songs in the list in seconds + */ int getPlayTime(); - /* returns the total duration of all songs in the list in seconds */ + /*! + * returns the duration of all remaining songs in the list in seconds + */ int getPlayTimeRemaining(); - /* returns the duration of all remaining songs in the list in seconds */ - + + /*! + * \brief write data back to the database + */ bool storePlaylist(); private: - int m_sqlId; /* -1 means: not valid */ - int m_listtype; // used in GiantDisc db queries + + /*! + * \brief reads the track list from the sql database into memory + */ + int insertDataFromSQL(); + + //! \brief the database identifier of the list + int m_sqlId; + + //! \brief the list type used in GiantDisc db queries + int m_listtype; + + //! \brief the author of the playlist std::string m_author; - MYSQL m_db; - int insertDataFromSQL(); + //! \brief the handle to the database in which the list is stored + MYSQL m_db; }; /*! @@ -283,29 +440,62 @@ class GdPlaylist : public mgPlaylist * Each child inherits the restrictions of its father and an additional * restriction on the recently expanded db field */ -class GdTreeNode : public mgSelectionTreeNode +class GdTreeNode : public mgSelectionTreeNode { public: - /*==== constructors ====*/ - GdTreeNode(MYSQL db, int view, std::string filters); // top level - GdTreeNode(mgSelectionTreeNode* parent, - std::string id, std::string label, std::string restriction); + /*! \addtogroup Object creation and destruction */ + /*\@{*/ + + /*! + * \brief Construct a top level tree node in a certain view with certain filters + */ + GdTreeNode(MYSQL db, int view, std::string filters); - /*==== destructor ====*/ - virtual ~GdTreeNode(); + /*! + * \brief Construct a subordinate tree node with given labels and restrictions + */ + GdTreeNode(mgSelectionTreeNode* parent, + std::string id, std::string label, std::string restriction); + + /*! + * \brief destructor to clean up the object + */ + virtual ~GdTreeNode(); - // compute children on the fly - virtual bool isLeafNode(); - virtual bool expand(); + /* \@} */ - // access data in current node - virtual std::vector* getTracks(); - virtual mgContentItem* getSingleTrack(); + /*! \addtogroup Access node data */ + /*\@{*/ + + /*! + * \brief check, whether the node is a leaf node (i.e. has no more children) + */ + virtual bool isLeafNode(); + + /*! + * \brief expand the node and generate child nodes according to restrictions passed in the constructor + */ + virtual bool expand(); + + /*! + * \brief obtain the tracks in this node + */ + virtual std::vector* getTracks(); + + /*! + * \brief obtain a single track in this node + */ + virtual mgContentItem* getSingleTrack(); + + /* \@} */ }; /* -------------------- begin CVS log --------------------------------- * $Log: gd_content_interface.h,v $ + * Revision 1.11 2004/08/30 14:31:43 LarsAC + * Documentation added + * * Revision 1.10 2004/08/27 15:19:34 LarsAC * Changed formatting and documentation * diff --git a/mg_tools.h b/mg_tools.h index d56f7d8..3b7bf12 100644 --- a/mg_tools.h +++ b/mg_tools.h @@ -1,25 +1,29 @@ -/*******************************************************************/ /*! \file muggle_tools.h - * \brief A few util functions for standalone and plugin messaging - * for the vdr muggle plugindatabase - ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \brief A few utility functions for standalone and plugin messaging for the vdr muggle plugindatabase + * + * \version $Revision: 1.4 $ + * \date $Date: 2004/08/30 14:31:43 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: lvw $ + * \author file owner: $Author: LarsAC $ * */ -/*******************************************************************/ -/* makes sur we dont use parse the same declarations twice */ + +/* makes sure we don't use the same declarations twice */ #ifndef _MUGGLE_TOOLS_H #define _MUGGLE_TOOLS_H #include #include +#include + +#define STANDALONE 1 // what's this? + +// mySql helper functions -#include "mysql/mysql.h" +MYSQL_RES* mgSqlReadQuery( MYSQL *db, const char *fmt, ... ); +void mgSqlWriteQuery( MYSQL *db, const char *fmt, ... ); -#define STANDALONE 1 +// Messaging functions void mgSetDebugLevel(int new_level); void mgDebug(int level, const char *fmt, ...); @@ -27,9 +31,6 @@ void mgDebug( const char *fmt, ... ); void mgWarning(const char *fmt, ...); void mgError(const char *fmt, ...); -MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...); -void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...); - #ifdef DEBUG #define MGLOG(x) mgLog __thelog(x) #else @@ -42,7 +43,6 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...); #define MGLOGSTREAM __thelog.getStream() #endif - class mgLog { public: @@ -74,6 +74,9 @@ class mgLog /* -------------------- begin CVS log --------------------------------- * $Log: mg_tools.h,v $ + * Revision 1.4 2004/08/30 14:31:43 LarsAC + * Documentation added + * * Revision 1.3 2004/05/28 15:29:18 lvw * Merged player branch back on HEAD branch. * -- cgit v1.2.3 From 22cedb0c0bb5345d92b67216f792810e75c75241 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 4 Sep 2004 09:24:00 +0000 Subject: Corrected command line option parse string. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@142 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/muggle.c b/muggle.c index 5a5df21..a9b6c75 100644 --- a/muggle.c +++ b/muggle.c @@ -3,11 +3,11 @@ * \brief Implements a plugin for browsing media libraries within VDR * * \version $Revision: 1.10 $ - * \date $Date: 2004/07/29 06:18:07 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: muggle.c,v 1.10 2004/07/29 06:18:07 lvw Exp $ + * $Id$ */ #include @@ -93,7 +93,7 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) }; int c, option_index = 0; - while( ( c = getopt_long( argc, argv, "h:p:u:w:g:", long_options, &option_index ) ) != -1 ) + while( ( c = getopt_long( argc, argv, "h:p:u:n:t:w:g:", long_options, &option_index ) ) != -1 ) { switch (c) { @@ -104,7 +104,7 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) case 'n': { the_setup.DbName = optarg; - } + } break; case 'p': { the_setup.DbPort = atoi( optarg ); -- cgit v1.2.3 From 653ca204d10b3481670a80769baa3d768bb5f64a Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 7 Sep 2004 17:40:47 +0000 Subject: Merged ogg vorbis player to trunk git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@148 e10066b5-e1e2-0310-b819-94efdf66514b --- HISTORY | 6 +- Makefile | 10 +- TODO | 46 ++- gd_content_interface.c | 69 ++++- gd_content_interface.h | 84 +++--- mg_content_interface.c | 25 +- mg_content_interface.h | 75 +++-- mg_tools.h | 28 +- muggle.h | 26 +- mugglei.c | 690 +++++++++++++++++++++++---------------------- scripts/createtables.mysql | 4 + vdr_decoder.c | 73 +++-- vdr_decoder.h | 25 +- vdr_decoder_mp3.c | 22 +- vdr_decoder_mp3.h | 13 +- vdr_decoder_ogg.c | 393 ++++++++++++++++++++++++++ vdr_decoder_ogg.h | 60 ++++ vdr_player.c | 43 +-- 18 files changed, 1150 insertions(+), 542 deletions(-) create mode 100644 vdr_decoder_ogg.c create mode 100644 vdr_decoder_ogg.h diff --git a/HISTORY b/HISTORY index 0f92024..7350748 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,8 @@ VDR Plugin 'muggle' Revision History ------------------------------------ -2004-01-15: Version 0.0.1 +2004-08-31: Version 0.0.1-ALPHA + +- An initial revision given to a few people. + -- Initial revision. diff --git a/Makefile b/Makefile index 870bd6e..d9bf48c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile,v 1.10 2004/08/29 14:39:33 lvw Exp $ +# $Id$ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. @@ -21,7 +21,7 @@ CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g ### The directory environment: DVBDIR = ../../../../DVB -VDRDIR = ../../.. +VDRDIR = /usr/local/src/VDR LIBDIR = ../../lib TMPDIR = /tmp @@ -42,14 +42,14 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/mysql/ -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE MIFLAGS += -I/usr/include/taglib -ltag -lmysqlclient ### The object files (add further files here): -OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o +OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o vdr_decoder_ogg.o -LIBS = -lmad -lmysqlclient +LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis ### Targets: diff --git a/TODO b/TODO index d11098e..cac1f2c 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,12 @@ TODO File for Muggle ==================== +Ogg/Vorbis decoder integration +------------------------------ +- Think, whether type (mp3, ogg, flac) should be stored in database + +Deployment +---------- - Script to publish a version - Checkout - make dist @@ -9,14 +15,16 @@ TODO File for Muggle - copy into web directory - sync with web +- How to track bugs and feature requests? + Testing/bugs -============ +------------ - Test execution of playlist commands -- Test mgPCMPlayer::getSourceFile() for GD case (find) +- Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) - Test saving/loading playlists to database Import -====== +------ - Album - Cover text - Cover images (based on filename or tag) @@ -27,35 +35,32 @@ Import - Language (?) - Genre1, 2 - Rating? - - Bitrate - Modified, created - Lyrics - Playlist from m3u Code polishing -============== +-------------- +- Check for unnecessary log commands +- Generate HTML documentation using doxygen, + - use dotty/gv for state machines of player + - make available online +- Clean up mugglei - Check for memory leaks - Why do filters use pointers? - Check for (reasonably) consistent usage of char*/string + - mgDatabase is not used? - should handle a static object with a MySQL connection - execute queries? - - mgPlayer used what for? - Could save IP/host name and associate last playlist loaded -- Check for unnecessary log commands -- Generate HTML documentation using doxygen, - - use dotty/gv for state machines of player - - make available online - - Check compatibility for 1.3.12 Short term items ================ -- Import existing m3u playlists (in import) -- Import genres OSD in general -------------- @@ -148,6 +153,21 @@ Already Done - i18n (english and german) - Album import - Various artists +- Ogg/Vorbis decoder integration + - cOggFile kept + - cOggInfo dismissed in favor of obtaining info from DB + - coding conventions adapted + - Schema extended to keep audio properties + - Import (mugglei) extended to store audio properties in DB + (most notably samplerate, no. channels) + - Extended mgContentItem with audio properties + - Extended mgGdTrack with audio properties (bitrate, samplerate, channels) + - in mgPCMPlayer/vdr_player.c: + - pass m_playing to mp3/ogg decoder (instead of filename) + - mgOggDecoder: obtain audio properties from DB (channels, sampling rate via mgContentItem) + - mgPCMPlayer::getSourceFile moved to abstract data layer (mgContentItem) + and made concrete in subclasses (mgGdTrack) + - mgDecoders::findDecoder: extend decoder detection ************************************************************ * diff --git a/gd_content_interface.c b/gd_content_interface.c index 8df7c48..6c2f496 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,10 +1,11 @@ /*! \file gd_content_interface.c * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin + * \ingroup giantdisc * * \version $Revision: 1.27 $ - * \date $Date: 2004/08/30 14:31:43 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author$ * * Implements main classes of for content items and interfaces to SQL databases * @@ -293,35 +294,35 @@ bool mgGdTrack::readData() // note: this does not work with empty album or genre fields result = mgSqlReadQuery(&m_db, "SELECT tracks.artist, album.title, tracks.title, " - " tracks.mp3file, genre.genre, tracks.year, " - " tracks.rating, tracks.length " - "FROM tracks, album, genre " + "tracks.mp3file, genre.genre, tracks.year, " + "tracks.rating, tracks.length, tracks.samplerate, tracks.channels, tracks.bitrate " + "FROM tracks, album, genre " "WHERE tracks.id=%d " "AND album.cddbid=tracks.sourceid AND " - " genre.id=tracks.genre1", + "genre.id=tracks.genre1", m_uniqID); nrows = mysql_num_rows(result); nfields = mysql_num_fields(result); - if(nrows == 0) + if( nrows == 0 ) { - mgWarning("No entries found \n"); + mgWarning( "No entries found \n" ); return false; } else { - if( nrows > 1 ) - { - mgWarning("mgGdTrack::readData: More than one entry found. Using first entry."); - } + if( nrows > 1 ) + { + mgWarning("mgGdTrack::readData: More than one entry found. Using first entry."); + } MYSQL_ROW row = mysql_fetch_row(result); m_artist = row[0]; m_album = row[1]; m_title = row[2]; - m_mp3file = row[3]; - m_genre = row[4]; + m_mp3file = string( the_setup.ToplevelDir ) + row[3]; + m_genre = row[4]; if( sscanf( row[5], "%d", &m_year) != 1 ) { @@ -338,6 +339,18 @@ bool mgGdTrack::readData() mgError( "Invalid duration '%s' in database", row [7]); } + if( row[8] && sscanf( row[8], "%d", &m_samplerate ) != 1 ) + { + mgError( "Invalid samplerate '%s' in database", row [7]); + } + + if( row[9] && sscanf( row[9], "%d", &m_channels ) != 1 ) + { + mgError( "Invalid channels '%s' in database", row [7]); + } + + m_bitrate = row[10]; + } m_retrieved = true; return true; @@ -445,6 +458,34 @@ int mgGdTrack::getDuration() } return m_rating; } + +int mgGdTrack::getSampleRate() +{ + if(!m_retrieved) + { + readData(); + } + return m_samplerate; +} + +int mgGdTrack::getChannels() +{ + if(!m_retrieved) + { + readData(); + } + return m_channels; +} + +string mgGdTrack::getBitrate() +{ + if(!m_retrieved) + { + readData(); + } + return m_bitrate; +} + string mgGdTrack::getImageFile() { return "dummyImg.jpg"; diff --git a/gd_content_interface.h b/gd_content_interface.h index 5d2626b..d810fa5 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -2,11 +2,12 @@ * \file gd_content_interface.h * \brief Data objects for content (e.g. mp3 files, movies) * for the vdr muggle plugin database + * \ingroup giantdisc * * \version $Revision: 1.11 $ - * \date $Date: 2004/08/30 14:31:43 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: LarsAC $ + * \author Responsible author: $Author$ * * Declares main classes for content items and interfaces to SQL databases * @@ -35,26 +36,28 @@ /*! * \brief Initialize a database for GD use + * \ingroup giantdisc * \todo Should be a static member of some GD class */ int GdInitDatabase(MYSQL *db); /*! * \brief Obtain the playlists stored within the GD schema + * \ingroup giantdisc * \todo Should be a static member of some GD class */ std::vector *GdGetStoredPlaylists(MYSQL db); /*! * \brief A set of filters to search for content + * \ingroup giantdisc */ class gdFilterSets : public mgFilterSets { public: - /*! \addtogroup Object creation and destruction */ - /*\@{*/ + //\@{ /*! * \brief the default constructor @@ -69,7 +72,7 @@ class gdFilterSets : public mgFilterSets */ virtual ~gdFilterSets(); - /*\@}*/ + //\@ /*! * \brief compute restriction w.r.t active filters @@ -86,7 +89,8 @@ class gdFilterSets : public mgFilterSets /*! - * \brief represents a a single track + * \brief represents a a single track + * \ingroup giantdisc * * This may be any content item. e.g. a mp3 fileselection * The object is initially created with a database identifier. @@ -100,8 +104,7 @@ class mgGdTrack : public mgContentItem { public: - /*! \addtogroup Object creation and destruction */ - /*\@{*/ + //@{ /*! * \brief a constructor @@ -137,7 +140,7 @@ class mgGdTrack : public mgContentItem */ virtual ~mgGdTrack(); - /*\@}*/ + //@} /*! * \brief obtain the content type of the item @@ -157,8 +160,7 @@ class mgGdTrack : public mgContentItem return mgMediaPlayer(); } - /*! \addtogroup Data read access */ - /*\@{*/ + //\@{ /*! * \brief returns a certain field of the item as a string @@ -215,16 +217,26 @@ class mgGdTrack : public mgContentItem */ virtual int getRating(); + /*! \brief obtain the samplerate of the track + */ + virtual int getSampleRate(); + + /*! \brief obtain the number of audio channels of the track + */ + virtual int getChannels(); + + /*! \brief obtain the bitrate of the track + */ + virtual std::string getBitrate(); + /*! * \brief obtain the complete track information */ virtual std::vector *getTrackInfo(); - /*\@}*/ - - /*! \addtogroup Data write access */ - /*\@{*/ + //\@} + //\@{ /*! * \brief set the title of the track */ @@ -271,8 +283,7 @@ class mgGdTrack : public mgContentItem * Genre and album field information is lost. */ bool writeData(); - - /*\@}*/ + //\@} //! \brief a special instance denoting an undefined track static mgGdTrack UNDEFINED; @@ -318,6 +329,11 @@ private: */ std::string m_genre; + /*! + * \brief The bitrate of the music + */ + std::string m_bitrate; + /*! * \brief The year in which the track appeared */ @@ -329,11 +345,20 @@ private: int m_rating; /*! - * \brief The length of the file in bytes. - * \todo TODO: is this true? Or what length are we talking about? + * \brief The length of the track in seconds */ int m_length; + /*! + * \brief The sampling rate of the track in Hz + */ + int m_samplerate; + + /*! + * \brief The number of channels of the track + */ + int m_channels; + /*! * \brief Access the data of the item from the database and fill actual data fields * @@ -348,6 +373,7 @@ private: /* * \brief a list of tracks stored in the databas + * \ingroup giantdisc */ class GdTracklist : public mgTracklist { @@ -365,9 +391,7 @@ class GdPlaylist : public mgPlaylist { public: - /*! \addtogroup Object creation and destruction */ - /*\@{*/ - + //@{ /*! * \brief opens an existing or creates empty playlist by name * @@ -386,7 +410,7 @@ class GdPlaylist : public mgPlaylist */ virtual ~GdPlaylist(); - /* \@} */ + //@} /*! * changes the listname of the playlist (and unset the sql id) @@ -430,6 +454,7 @@ class GdPlaylist : public mgPlaylist /*! * \brief hierarchical representation of a set of tracks + * \ingroup giantdisc * * The selection can be based on the whole database or a subset of it. * Within this selection, the data is organized in a tree hierarchy @@ -444,9 +469,7 @@ class GdTreeNode : public mgSelectionTreeNode { public: - /*! \addtogroup Object creation and destruction */ - /*\@{*/ - + //@{ /*! * \brief Construct a top level tree node in a certain view with certain filters */ @@ -462,12 +485,9 @@ public: * \brief destructor to clean up the object */ virtual ~GdTreeNode(); + //@} - /* \@} */ - - /*! \addtogroup Access node data */ - /*\@{*/ - + //@{ /*! * \brief check, whether the node is a leaf node (i.e. has no more children) */ @@ -488,7 +508,7 @@ public: */ virtual mgContentItem* getSingleTrack(); - /* \@} */ + //@} }; /* -------------------- begin CVS log --------------------------------- diff --git a/mg_content_interface.c b/mg_content_interface.c index c62bd2d..5c4890b 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -2,9 +2,9 @@ * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin * * \version $Revision: 1.6 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * * Implements main classes of for content items and interfaces to SQL databases * @@ -22,7 +22,7 @@ #define DUMMY /* constructor */ -mgContentItem mgContentItem::UNDEFINED = mgContentItem(); +mgContentItem mgContentItem::UNDEFINED = mgContentItem(); using namespace std; @@ -37,7 +37,7 @@ mgTracklist::mgTracklist() } /*! - * \brief destrucor + * \brief destructor * * Deletes all items in the tracklist and removes the list itself */ @@ -46,7 +46,7 @@ mgTracklist::~mgTracklist() mgContentItem* ptr; vector::iterator iter; - for(iter = m_list.begin(); iter != m_list.end();iter++) + for( iter = m_list.begin(); iter != m_list.end(); iter++ ) { ptr = *iter; delete ptr; @@ -191,10 +191,8 @@ bool mgTracklist::remove(unsigned int position) } /*! - ***************************************************************************** * \brief remove all occurences of item - * - ****************************************************************************/ + */ int mgTracklist::remove(mgContentItem* item) { int retval = 0; @@ -211,13 +209,6 @@ int mgTracklist::remove(mgContentItem* item) return retval; } - - -/*=================================================================*/ -/* */ -/* class mgSelectionTreeNode */ -/* */ -/*=================================================================*/ mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view) { m_db = db; @@ -239,7 +230,7 @@ mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, string id, m_expanded = false; } - /*==== destructor ====*/ +/*==== destructor ====*/ mgSelectionTreeNode::~mgSelectionTreeNode() { collapse(); @@ -275,7 +266,7 @@ vector &mgSelectionTreeNode::getChildren() return m_children; } -// access data in current node +// access data in current node string mgSelectionTreeNode::getID() { return m_id; diff --git a/mg_content_interface.h b/mg_content_interface.h index bb99999..44658e8 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -1,13 +1,10 @@ -/*******************************************************************/ /*! \file mg_content_interface.h * \brief data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin * - ******************************************************************** - * * \version $Revision: 1.4 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: lvw $ + * \author file owner: $Author$ * * Declares generic classes of for content items and interfaces to SQL databases * @@ -16,9 +13,7 @@ * - mgContentItem * - mgTracklist * - mgSelectionTreeNode - * */ -/*******************************************************************/ /* makes sure we dont parse the same declarations twice */ #ifndef _CONTENT_INTERFACE_H @@ -115,14 +110,15 @@ class mgContentItem return mgMediaPlayer(); } - /*! \brief get the "file" (or URL) that is passed to the player + //@{ + + /*! \brief return a specific label */ - virtual std::string getSourceFile() + virtual std::string getLabel(int col = 0) { return ""; } - // ============ data access ================= /*! \brief return the title */ virtual std::string getTitle() @@ -130,9 +126,9 @@ class mgContentItem return ""; } - /*! \brief return a specific label + /*! \brief get the "file" (or URL) that is passed to the player */ - virtual std::string getLabel(int col = 0) + virtual std::string getSourceFile() { return ""; } @@ -144,26 +140,62 @@ class mgContentItem return ""; } - virtual std::vector *getTrackInfo() + /*! \brief obtain the genre to which the track belongs + */ + virtual std::string getGenre() { - return NULL; + return ""; } - virtual bool updateTrackInfo(std::vector*) + /*! \brief obtain the rating of the title + */ + virtual int getRating() { - return false; + return 0; } - virtual std::string getGenre() + /*! \brief obtain the samplerate of the track + */ + virtual int getSampleRate() { - return ""; + return 0; } - virtual int getRating() + /*! \brief obtain the length of the track (in seconds) + */ + virtual int getLength() { return 0; } + /*! \brief obtain the number of audio channels of the track + */ + virtual int getChannels() + { + return 0; + } + + /*! \brief obtain the bitrate of the track + */ + virtual std::string getBitrate() + { + return ""; + } + + /*! \brief obtain track information in aggregated form + */ + virtual std::vector *getTrackInfo() + { + return NULL; + } + + //@} + + virtual bool updateTrackInfo(std::vector*) + { + return false; + } + virtual bool operator == (mgContentItem o) { return m_uniqID == o.m_uniqID; @@ -206,7 +238,10 @@ class mgTracklist virtual bool remove(unsigned int position); // remove item at position }; - +/*! + * \brief represent a node in a tree of selections + * \ingroup muggle + */ class mgSelectionTreeNode { diff --git a/mg_tools.h b/mg_tools.h index 3b7bf12..3307e3e 100644 --- a/mg_tools.h +++ b/mg_tools.h @@ -1,10 +1,11 @@ /*! \file muggle_tools.h + * \ingroup muggle * \brief A few utility functions for standalone and plugin messaging for the vdr muggle plugindatabase * * \version $Revision: 1.4 $ - * \date $Date: 2004/08/30 14:31:43 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author$ * */ @@ -18,18 +19,30 @@ #define STANDALONE 1 // what's this? -// mySql helper functions +/*! \brief mySql helper function to execute read queries + * \ingroup muggle + * + * \todo Could be a member of mgDatabase? + */ MYSQL_RES* mgSqlReadQuery( MYSQL *db, const char *fmt, ... ); + +/*! \brief mySql helper function to execute write queries + * \ingroup muggle + * + * \todo Could be a member of mgDatabase? + */ void mgSqlWriteQuery( MYSQL *db, const char *fmt, ... ); -// Messaging functions +/*! \brief Logging utilities */ +//@{ void mgSetDebugLevel(int new_level); void mgDebug(int level, const char *fmt, ...); void mgDebug( const char *fmt, ... ); void mgWarning(const char *fmt, ...); void mgError(const char *fmt, ...); +//@} #ifdef DEBUG #define MGLOG(x) mgLog __thelog(x) @@ -43,6 +56,13 @@ void mgError(const char *fmt, ...); #define MGLOGSTREAM __thelog.getStream() #endif +/*! \brief simplified logging class + * \ingroup muggle + * + * Create a local instance at the beginning of the method + * and entering/leaving the function will be logged + * as constructors/destructors are called. + */ class mgLog { public: diff --git a/muggle.h b/muggle.h index 835957f..be074c8 100644 --- a/muggle.h +++ b/muggle.h @@ -1,13 +1,33 @@ /*! * \file muggle.h + * \ingroup vdr * \brief Implements a plugin for browsing media libraries within VDR * * \version $Revision: 1.6 $ - * \date $Date: 2004/07/09 12:22:00 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: LarsAC $ + * \author file owner: $Author$ * - * $Id: muggle.h,v 1.6 2004/07/09 12:22:00 LarsAC Exp $ + * $Id$ + */ + + +// Some notes about the general structure of the plugin + +/* \defgroup giantdisc GiantDisc integration layer + * The GiantDisc integration layer contains functions and classes + * which enable interoperability with a database schema that conforms + * to the GiantDisc layout. + * + * \defgroup vdr VDR integration layer + * The VDR integration layer contains components which allow the + * plugin functionality to be accessed by VDR. These are mainly + * related to the OSD and a player/control combination. + * + * \defgroup muggle Main muggle business + * The core of the plugin is an abstract representation of information + * organized in trees (thus suitable for OSD navigation) as well as + * means to organize */ #ifndef _MUGGLE_H diff --git a/mugglei.c b/mugglei.c index 66fb766..3db2f04 100755 --- a/mugglei.c +++ b/mugglei.c @@ -1,340 +1,350 @@ - -#include -using namespace std; - -#include -#include -#include -#include - -#include - -#include -#include - -#include "mg_tools.h" - -MYSQL *db; - -string host, user, pass, dbname; -bool import_assorted; - -int init_database() -{ - db = mysql_init(NULL); - if( db == NULL ) - { - return -1; - } - - if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), - 0, NULL, 0 ) == NULL ) - { - return -2; - } - - return 0; -} - -time_t get_fs_modification_time( string filename ) -{ - struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); - - // yes: obtain modification date for file and db entry - int statres = stat( filename.c_str(), buf ); - - time_t mod = buf->st_mtime; - free( buf ); - - return mod; -} - -time_t get_db_modification_time( long uid ) -{ - time_t mt; - - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id=\"%d\"", uid ); - MYSQL_ROW row = mysql_fetch_row( result ); - - string mod_time = row[0]; - mt = (time_t) atol( mod_time.c_str() ); - - return mt; -} - -TagLib::String escape_string( MYSQL *db, TagLib::String s ) -{ - char *buf = strdup( s.toCString() ); - char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); - - int len = mysql_real_escape_string( db, escbuf, buf, strlen( buf ) ); - - return TagLib::String( escbuf ); -} - -long find_file_in_database( MYSQL *db, string filename ) -{ - TagLib::String file = TagLib::String( filename.c_str() ); - file = escape_string( db, file ); - - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", file.toCString() ); - MYSQL_ROW row = mysql_fetch_row( result ); - - // obtain ID and return - return atol( row[0] ); -} - -// read tags from the mp3 file and store them into the corresponding database entry -void update_db( long uid, string filename ) -{ - TagLib::String title, album, artist, genre, cddbid; - uint trackno, year; - - // ID3_Tag filetag( filename.c_str() ); - TagLib::FileRef f( filename.c_str() ); - - if( !f.isNull() && f.tag() ) - { - // cout << "Evaluating " << filename << endl; - TagLib::Tag *tag = f.tag(); - - // obtain tag information - title = tag->title(); - album = tag->album(); - year = tag->year(); - artist = tag->artist(); - trackno = tag->track(); - genre = tag->genre(); - - title = escape_string( db, title ); - album = escape_string( db, album ); - artist = escape_string( db, artist ); - - // TODO: CD identifier (if it exists), playcounter, popularimeter (rating?), volume adjustment, lyrics, cover - - // finally update the database - - // obtain associated album or create - if( album == "" ) - { // no album found, create default album for artist - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() ); - MYSQL_ROW row = mysql_fetch_row( result ); - - // Default album does not yet exist (num rows == 0) - int nrows = mysql_num_rows(result); - if( nrows == 0 ) - { - // create new album entry "Unassigned" for this artist - long id = random(); - char *buf; - asprintf( &buf, "%d-%s", id, tag->artist().toCString() ); - cddbid = TagLib::String( buf ).substr( 0, 20 ); - cddbid = escape_string( db, cddbid ); - free( buf ); - - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"Unassigned\", \"%s\")", artist.toCString(), cddbid.toCString() ); - } - else - { // use first album found as source id for the track - cddbid = row[0]; - } - } - else - { // album tag found, associate or create - MYSQL_RES *result; - if( import_assorted ) - { // lookup an existing album by title only (artist should be "Various Artists" - result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"Various Artists\"", - album.toCString(), artist.toCString() ); - } - else - { - result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"%s\"", - album.toCString(), artist.toCString() ); - } - MYSQL_ROW row = mysql_fetch_row( result ); - - // num rows == 0 ? - int nrows = mysql_num_rows(result); - if( nrows == 0 ) - { - // create new album entry - long id = random(); - char *buf; - asprintf( &buf, "%d-%s", id, tag->album().toCString() ); - cddbid = TagLib::String( buf ).substr( 0, 20 ); - cddbid = escape_string( db, cddbid ); - free( buf ); - - if( import_assorted ) - { // in this case, the album author is "Various Artists" - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"Various Artists\", \"%s\", \"%s\")", album.toCString(), cddbid.toCString() ); } - else - { - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); } - } - else - { // use first album found as source id for the track - cddbid = row[0]; - } - } - - // update tracks table - if( uid > 0 ) - { // the entry is known to exist already, hence update it - mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\", sourceid=\"%s\", mp3file=\"%s\"" - "WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.toCString(), filename.c_str(), uid ); - } - else - { // the entry does not exist, create it - // int t = title.find( "'" ); - // int a = artist.find( "'" ); - mgSqlWriteQuery( db, - "INSERT INTO tracks (artist,title,genre1,genre2,year,sourceid,tracknb,mp3file)" - " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\")", - artist.toCString(), title.toCString(), year, cddbid.toCString(), trackno, filename.c_str() ); - /* - cout << "-- TAG --" << endl; - cout << "title - \"" << tag->title() << "\"" << endl; - cout << "artist - \"" << tag->artist() << "\"" << endl; - cout << "album - \"" << tag->album() << "\"" << endl; - cout << "year - \"" << tag->year() << "\"" << endl; - cout << "comment - \"" << tag->comment() << "\"" << endl; - cout << "track - \"" << tag->track() << "\"" << endl; - cout << "genre - \"" << tag->genre() << "\"" << endl; - */ - } - } -} - -void update_tags( long uid, string filename ) -{ - -} - -void evaluate_file( MYSQL *db, string filename ) -{ - if( 0 == init_database() ) - { - // is filename stored in database? - long uid = find_file_in_database( db, filename ); - if( uid >= 0 ) - { - // currently only update database, do not consider writing changes from the db back - /* - // determine modification times in database and on filesystem - time_t db_time = get_db_modification_time( uid ); - time_t fs_time = get_fs_modification_time( filename ); - - if( db_time > fs_time ) - { - // db is newer: update id3 tags from db - update_tags( uid, filename ); - } - else - { - // file is newer: update db from id3 tags - update_db( uid, filename ); - } - */ - - update_db( uid, filename ); - } - else - { - // not in db yet: import file - update_db( -1, filename ); - } - } -} - -int main( int argc, char *argv[] ) -{ - int option_index; - string filename; - - if( argc < 2 ) - { // we need at least a filename! - cout << "mugglei -- import helper for Muggle VDR plugin" << endl; - cout << "(C) Lars von Wedel" << endl; - cout << "This is free software; see the source for copying conditions." << endl; - cout << "" << endl; - cout << "Options:" << endl; - cout << " -h - specify host of mySql database server (default is 'localhost')" << endl; - cout << " -n - specify database name (default is 'GiantDisc')" << endl; - cout << " -u - specify user of mySql database (default is empty)" << endl; - cout << " -p - specify password of user (default is empty password)" << endl; - cout << " -f - name of music file to import" << endl; - cout << " -a - import track as if it was on an assorted album" << endl; - - exit( 1 ); - } - - // option defaults - host = "localhost"; - dbname = "GiantDisc"; - user = ""; - pass = ""; - import_assorted = false; - - // parse command line options - while( 1 ) - { - int c = getopt(argc, argv, "h:u:p:n:af:"); - - if (c == -1) - break; - - switch (c) - { - case 0: - { // long option - - } break; - case 'h': - { - host = optarg; - } break; - case 'u': - { - user = optarg; - } break; - case 'p': - { - pass = optarg; - } break; - case 'd': - { - dbname = optarg; - } break; - case 'a': - { - import_assorted = true; - } break; - case 'f': - { - filename = optarg; - } break; - } - } - - // init random number generator - struct timeval tv; - struct timezone tz; - gettimeofday( &tv, &tz ); - srandom( tv.tv_usec ); - - int res = init_database(); - - if( !res ) - { - update_db( 0, filename ); - } - else - { - cout << "Database initialization failed. Exiting.\n" << endl; - } - - return res; -} - + +#include +using namespace std; + +#include +#include +#include +#include + +#include + +#include +#include + +#include "mg_tools.h" + +MYSQL *db; + +string host, user, pass, dbname; +bool import_assorted; + +int init_database() +{ + db = mysql_init(NULL); + if( db == NULL ) + { + return -1; + } + + if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), + 0, NULL, 0 ) == NULL ) + { + return -2; + } + + return 0; +} + +time_t get_fs_modification_time( string filename ) +{ + struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); + + // yes: obtain modification date for file and db entry + int statres = stat( filename.c_str(), buf ); + + time_t mod = buf->st_mtime; + free( buf ); + + return mod; +} + +time_t get_db_modification_time( long uid ) +{ + time_t mt; + + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id=\"%d\"", uid ); + MYSQL_ROW row = mysql_fetch_row( result ); + + string mod_time = row[0]; + mt = (time_t) atol( mod_time.c_str() ); + + return mt; +} + +TagLib::String escape_string( MYSQL *db, TagLib::String s ) +{ + char *buf = strdup( s.toCString() ); + char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); + + int len = mysql_real_escape_string( db, escbuf, buf, strlen( buf ) ); + + return TagLib::String( escbuf ); +} + +long find_file_in_database( MYSQL *db, string filename ) +{ + TagLib::String file = TagLib::String( filename.c_str() ); + file = escape_string( db, file ); + + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", file.toCString() ); + MYSQL_ROW row = mysql_fetch_row( result ); + + // obtain ID and return + return atol( row[0] ); +} + +// read tags from the mp3 file and store them into the corresponding database entry +void update_db( long uid, string filename ) +{ + TagLib::String title, album, artist, genre, cddbid; + uint trackno, year; + + // ID3_Tag filetag( filename.c_str() ); + TagLib::FileRef f( filename.c_str() ); + + if( !f.isNull() && f.tag() ) + { + // cout << "Evaluating " << filename << endl; + TagLib::Tag *tag = f.tag(); + + // obtain tag information + title = tag->title(); + album = tag->album(); + year = tag->year(); + artist = tag->artist(); + trackno = tag->track(); + genre = tag->genre(); + + AudioProperties *ap = f.audioProperties(); + int len = ap->length(); // tracks.length + int bitrate = ap->bitrate(); // tracks.bitrate + int sample = ap->sampleRate(); //tracks.samplerate + int channels = ap->channels(); //tracks.channels + + title = escape_string( db, title ); + album = escape_string( db, album ); + artist = escape_string( db, artist ); + + // TODO: CD identifier (if it exists), playcounter, popularimeter (rating?), volume adjustment, lyrics, cover + + // finally update the database + + // obtain associated album or create + if( album == "" ) + { // no album found, create default album for artist + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() ); + MYSQL_ROW row = mysql_fetch_row( result ); + + // Default album does not yet exist (num rows == 0) + int nrows = mysql_num_rows(result); + if( nrows == 0 ) + { + // create new album entry "Unassigned" for this artist + long id = random(); + char *buf; + asprintf( &buf, "%d-%s", id, tag->artist().toCString() ); + cddbid = TagLib::String( buf ).substr( 0, 20 ); + cddbid = escape_string( db, cddbid ); + free( buf ); + + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"Unassigned\", \"%s\")", artist.toCString(), cddbid.toCString() ); + } + else + { // use first album found as source id for the track + cddbid = row[0]; + } + } + else + { // album tag found, associate or create + MYSQL_RES *result; + if( import_assorted ) + { // lookup an existing album by title only (artist should be "Various Artists" + result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"Various Artists\"", + album.toCString(), artist.toCString() ); + } + else + { + result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"%s\" AND artist=\"%s\"", + album.toCString(), artist.toCString() ); + } + MYSQL_ROW row = mysql_fetch_row( result ); + + // num rows == 0 ? + int nrows = mysql_num_rows(result); + if( nrows == 0 ) + { + // create new album entry + long id = random(); + char *buf; + asprintf( &buf, "%d-%s", id, tag->album().toCString() ); + cddbid = TagLib::String( buf ).substr( 0, 20 ); + cddbid = escape_string( db, cddbid ); + free( buf ); + + if( import_assorted ) + { // in this case, the album author is "Various Artists" + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"Various Artists\", \"%s\", \"%s\")", album.toCString(), cddbid.toCString() ); } + else + { + mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); } + } + else + { // use first album found as source id for the track + cddbid = row[0]; + } + } + + // update tracks table + if( uid > 0 ) + { // the entry is known to exist already, hence update it + + mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\"," + "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\"," + "samplerate=%d, channels=%d WHERE id=%d", + artist.toCString(), title.toCString(), year, + cddbid.toCString(), filename.c_str(), len, bitrate, + sample, channels, uid ); + } + else + { // the entry does not exist, create it + mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,genre1,genre2,year," + "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels)" + " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d)", + artist.toCString(), title.toCString(), year, cddbid.toCString(), + trackno, filename.c_str(), len, bitrate, sample, channels ); + /* + cout << "-- TAG --" << endl; + cout << "title - \"" << tag->title() << "\"" << endl; + cout << "artist - \"" << tag->artist() << "\"" << endl; + cout << "album - \"" << tag->album() << "\"" << endl; + cout << "year - \"" << tag->year() << "\"" << endl; + cout << "comment - \"" << tag->comment() << "\"" << endl; + cout << "track - \"" << tag->track() << "\"" << endl; + cout << "genre - \"" << tag->genre() << "\"" << endl; + */ + } + } +} + +void update_tags( long uid, string filename ) +{ + +} + +void evaluate_file( MYSQL *db, string filename ) +{ + if( 0 == init_database() ) + { + // is filename stored in database? + long uid = find_file_in_database( db, filename ); + if( uid >= 0 ) + { + // currently only update database, do not consider writing changes from the db back + /* + // determine modification times in database and on filesystem + time_t db_time = get_db_modification_time( uid ); + time_t fs_time = get_fs_modification_time( filename ); + + if( db_time > fs_time ) + { + // db is newer: update id3 tags from db + update_tags( uid, filename ); + } + else + { + // file is newer: update db from id3 tags + update_db( uid, filename ); + } + */ + + update_db( uid, filename ); + } + else + { + // not in db yet: import file + update_db( -1, filename ); + } + } +} + +int main( int argc, char *argv[] ) +{ + int option_index; + string filename; + + if( argc < 2 ) + { // we need at least a filename! + cout << "mugglei -- import helper for Muggle VDR plugin" << endl; + cout << "(C) Lars von Wedel" << endl; + cout << "This is free software; see the source for copying conditions." << endl; + cout << "" << endl; + cout << "Options:" << endl; + cout << " -h - specify host of mySql database server (default is 'localhost')" << endl; + cout << " -n - specify database name (default is 'GiantDisc')" << endl; + cout << " -u - specify user of mySql database (default is empty)" << endl; + cout << " -p - specify password of user (default is empty password)" << endl; + cout << " -f - name of music file to import" << endl; + cout << " -a - import track as if it was on an assorted album" << endl; + + exit( 1 ); + } + + // option defaults + host = "localhost"; + dbname = "GiantDisc"; + user = ""; + pass = ""; + import_assorted = false; + + // parse command line options + while( 1 ) + { + int c = getopt(argc, argv, "h:u:p:n:af:"); + + if (c == -1) + break; + + switch (c) + { + case 0: + { // long option + + } break; + case 'h': + { + host = optarg; + } break; + case 'u': + { + user = optarg; + } break; + case 'p': + { + pass = optarg; + } break; + case 'd': + { + dbname = optarg; + } break; + case 'a': + { + import_assorted = true; + } break; + case 'f': + { + filename = optarg; + } break; + } + } + + // init random number generator + struct timeval tv; + struct timezone tz; + gettimeofday( &tv, &tz ); + srandom( tv.tv_usec ); + + int res = init_database(); + + if( !res ) + { + update_db( 0, filename ); + } + else + { + cout << "Database initialization failed. Exiting.\n" << endl; + } + + return res; +} + diff --git a/scripts/createtables.mysql b/scripts/createtables.mysql index 5969ce2..079b3bd 100755 --- a/scripts/createtables.mysql +++ b/scripts/createtables.mysql @@ -219,6 +219,10 @@ CREATE TABLE tracks ( created date default NULL, modified date default NULL, backup tinyint(3) unsigned default NULL, + + samplerate int(7) unsigned default NULL, + channels tinyint(3) unsigned default NULL, + id int(11) NOT NULL auto_increment, PRIMARY KEY (id), KEY title (title(10)), diff --git a/vdr_decoder.c b/vdr_decoder.c index 8b4cf70..1c0b154 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -1,13 +1,14 @@ /*! * \file vdr_decoder.h * \brief A generic decoder for a VDR media plugin (muggle) + * \ingroup vdr * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_decoder.c,v 1.2 2004/05/28 15:29:18 lvw Exp $ + * $Id$ * * Adapted from: * MP3/MPlayer plugin to VDR (C++) @@ -23,15 +24,11 @@ #include #include -// #include "common.h" -// #include "data-mp3.h" -// #include "decoder-core.h" -// #include "decoder-mp3-stream.h" -// #include "decoder-snd.h" -// #include "decoder-ogg.h" - #include "vdr_decoder.h" #include "vdr_decoder_mp3.h" +#include "vdr_decoder_ogg.h" + +#include "mg_content_interface.h" using namespace std; @@ -39,38 +36,58 @@ using namespace std; mgMediaType mgDecoders::getMediaType( string s ) { + mgMediaType mt = MT_UNKNOWN; + // TODO: currently handles only mp3. LVW - return MT_MP3; + char *f = (char *)s.c_str(); + char *p = f + strlen( f ) - 1; // point to the end + + while( p >= f && *p != '.') --p; + + if( !strcmp( p, ".mp3" ) ) + { + mt = MT_MP3; + } + else + { + if( !strcmp( p, ".ogg" ) ) + { + mt = MT_OGG; + } + } + return mt; } -mgDecoder *mgDecoders::findDecoder( string filename ) +mgDecoder *mgDecoders::findDecoder( mgContentItem *item ) { mgDecoder *decoder = 0; + string filename = item->getSourceFile(); + switch( getMediaType( filename ) ) { case MT_MP3: { - // prepend filename with path to music library?? TODO - printf( "mp3 file detected, launching mgMP3Decoder\n" ); - decoder = new mgMP3Decoder(filename); + decoder = new mgMP3Decoder( item ); } break; - - /* - case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; - #ifdef HAVE_SNDFILE - case MT_SND: decoder = new cSndDecoder(full); break; - #endif - #ifdef HAVE_VORBISFILE - case MT_OGG: decoder = new mgOggDecoder(full); break; - #endif - */ +#ifdef HAVE_VORBISFILE + case MT_OGG: + { + decoder = new mgOggDecoder( item ); + } break; +#endif + /* + case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; + #ifdef HAVE_SNDFILE + case MT_SND: decoder = new cSndDecoder(full); break; + #endif + */ default: { esyslog("ERROR: unknown media type" ); } break; } - + if( decoder && !decoder->valid() ) { // no decoder found or decoder doesn't match @@ -85,9 +102,9 @@ mgDecoder *mgDecoders::findDecoder( string filename ) // --- mgDecoder ---------------------------------------------------------------- -mgDecoder::mgDecoder(string filename) +mgDecoder::mgDecoder( mgContentItem *item ) { - m_filename = filename; + m_item = item; m_locked = 0; m_urgentLock = false; m_playing = false; diff --git a/vdr_decoder.h b/vdr_decoder.h index 74b200e..a673cbc 100644 --- a/vdr_decoder.h +++ b/vdr_decoder.h @@ -1,13 +1,14 @@ /*! * \file vdr_decoder.h * \brief A generic decoder for a VDR media plugin (muggle) + * \ingroup vdr * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_decoder.h,v 1.2 2004/05/28 15:29:18 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -24,10 +25,13 @@ #define DEC_ID(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d)) +class mgContentItem; + // --------From decoder_core.h ------------------------------------ /*! * \brief The current status of the decoder + * \ingroup vdr */ enum eDecodeStatus { @@ -38,6 +42,7 @@ enum eDecodeStatus /*! * \brief A data structure to put decoded PCM data + * \ingroup vdr */ struct mgDecode { @@ -50,6 +55,7 @@ struct mgDecode /*! * \brief Information about ??? + * \ingroup vdr */ class mgPlayInfo { @@ -61,19 +67,24 @@ public: /*! * \brief Media types + * \ingroup vdr */ enum mgMediaType { - MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC + MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC, MT_UNKNOWN }; /*! * \brief A generic decoder class to handle conversion into PCM format + * \ingroup vdr */ class mgDecoder { protected: + /*! \brief database handle to the track being decoded */ + mgContentItem *m_item; + /*! \brief The currently playing file */ std::string m_filename; @@ -99,11 +110,13 @@ protected: public: + //@{ /*! \brief The constructor */ - mgDecoder( std::string filename ); + mgDecoder( mgContentItem *item ); /*! \brief The destructor */ virtual ~mgDecoder(); + //@} /*! \brief Whether a decoder instance is able to play the given file */ virtual bool valid() = 0; @@ -147,7 +160,7 @@ public: /*! \brief Try to find a valid decoder for a file */ - static mgDecoder *findDecoder( std::string filename ); + static mgDecoder *findDecoder( mgContentItem *item ); /*! \brief determine the media type for a given source */ diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c index 436d7b6..20fca5e 100644 --- a/vdr_decoder_mp3.c +++ b/vdr_decoder_mp3.c @@ -3,11 +3,11 @@ * \brief An mp3 decoder for a VDR media plugin (muggle) * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_decoder_mp3.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -27,6 +27,7 @@ #include "vdr_decoder_mp3.h" #include "vdr_stream.h" +#include "mg_content_interface.h" #include "mg_tools.h" #define d(x) x @@ -52,17 +53,16 @@ int mgMadStream(struct mad_stream *stream, mgStream *str) // --- mgMP3Decoder ------------------------------------------------------------- -mgMP3Decoder::mgMP3Decoder(string filename, bool preinit) - : mgDecoder(filename) +mgMP3Decoder::mgMP3Decoder( mgContentItem *item, bool preinit) + : mgDecoder(item) { - MGLOG( "mgMP3Decoder::mgMP3Decoder" ); - m_stream = 0; m_isStream = false; + m_filename = item->getSourceFile(); if( preinit ) { - m_stream = new mgStream( filename ); + m_stream = new mgStream( m_filename ); } m_madframe = 0; m_madsynth = 0; @@ -181,8 +181,6 @@ mgPlayInfo *mgMP3Decoder::playInfo() bool mgMP3Decoder::start() { - MGLOG( "mgMP3Decoder::start" ); - lock(true); init(); m_playing = true; @@ -219,8 +217,6 @@ bool mgMP3Decoder::start() bool mgMP3Decoder::stop(void) { - MGLOG( "mgMP3Decoder::stop" ); - lock(); if( m_playing ) @@ -234,8 +230,6 @@ bool mgMP3Decoder::stop(void) struct mgDecode *mgMP3Decoder::done(eDecodeStatus status) { - // MGLOG( "mgMP3Decoder::done" ); - m_ds.status = status; m_ds.index = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS ); m_ds.pcm = &m_madsynth->pcm; diff --git a/vdr_decoder_mp3.h b/vdr_decoder_mp3.h index abc8eed..f9f3bde 100644 --- a/vdr_decoder_mp3.h +++ b/vdr_decoder_mp3.h @@ -3,11 +3,11 @@ * \brief An mp3 decoder for a VDR media plugin (muggle) * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_decoder_mp3.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -30,6 +30,7 @@ #endif class mgStream; +class mgContentItem; // ---------------------------------------------------------------- @@ -55,7 +56,7 @@ private: } *m_frameinfo; int m_framenum, m_framemax, m_errcount, m_mute; - // + void init(); void clean(); @@ -65,7 +66,7 @@ private: virtual mgPlayInfo *playInfo(); eDecodeStatus decodeError(bool hdr); - + void makeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, int secs, int avail, int dvbrate); @@ -78,7 +79,7 @@ public: /*! * \brief construct a decoder from a filename */ - mgMP3Decoder( std::string filename, bool preinit = true ); + mgMP3Decoder( mgContentItem *item, bool preinit = true ); /*! * \brief the destructor diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c new file mode 100644 index 0000000..9525979 --- /dev/null +++ b/vdr_decoder_ogg.c @@ -0,0 +1,393 @@ +/*! \file vdr_decoder_ogg.c + * \ingroup vdr + * + * The file implements a decoder which is used by the player to decode ogg vorbis audio files. + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001-2003 Stefan Huelswitt + */ + + +#ifdef HAVE_VORBISFILE + +#include "vdr_decoder_ogg.h" + +#include +#include + +#include +#include +#include + +#include "mg_content_interface.h" + +using namespace std; + +// --- mgOggFile ---------------------------------------------------------------- + +class mgOggFile // : public mgFileInfo +{ + private: + + bool m_opened, m_canSeek; + + OggVorbis_File vf; + + void error( const char *action, const int err ); + + string m_filename; + + public: + + mgOggFile( string filename ); + ~mgOggFile(); + + bool open(bool log=true); + + void close(void); + + long long seek(long long posMs=0, bool relativ=false); + + int stream(short *buffer, int samples); + + bool canSeek() { return m_canSeek; } + + long long indexMs(void); +}; + +mgOggFile::mgOggFile( string filename ) : + m_filename( filename ) +{ + m_canSeek = false; + m_opened = false; +} + +mgOggFile::~mgOggFile() +{ + close(); +} + +bool mgOggFile::open(bool log) +{ + if( m_opened ) + { + if( m_canSeek ) + { + return ( seek() >= 0 ); + } + return true; + } + + FILE *f = fopen( m_filename.c_str(), "r" ); + if( f ) + { + int r = ov_open( f, &vf, 0, 0 ); + if( !r ) + { + m_canSeek = ( ov_seekable( &vf ) !=0 ); + m_opened = true; + } + else + { + fclose( f ); + if( log ) + { + error( "open", r ); + } + } + } + else + { + if(log) + { + // esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) ); + } + } + return m_opened; +} + +void mgOggFile::close() +{ + if( m_opened ) + { + ov_clear( &vf ); + m_opened = false; + } +} + +void mgOggFile::error( const char *action, const int err ) +{ + char *errstr; + switch(err) + { + case OV_FALSE: errstr = "false/no data available"; break; + case OV_EOF: errstr = "EOF"; break; + case OV_HOLE: errstr = "missing or corrupted data"; break; + case OV_EREAD: errstr = "read error"; break; + case OV_EFAULT: errstr = "internal error"; break; + case OV_EIMPL: errstr = "unimplemented feature"; break; + case OV_EINVAL: errstr = "invalid argument"; break; + case OV_ENOTVORBIS: errstr = "no Ogg Vorbis stream"; break; + case OV_EBADHEADER: errstr = "corrupted Ogg Vorbis stream"; break; + case OV_EVERSION: errstr = "unsupported bitstream version"; break; + case OV_ENOTAUDIO: errstr = "ENOTAUDIO"; break; + case OV_EBADPACKET: errstr = "EBADPACKET"; break; + case OV_EBADLINK: errstr = "corrupted link"; break; + case OV_ENOSEEK: errstr = "stream not seekable"; break; + default: errstr = "unspecified error"; break; + } + // esyslog( "ERROR: vorbisfile %s failed on %s: %s", action, m_filename.c_str(), errstr ); +} + +long long mgOggFile::indexMs(void) +{ + double p = ov_time_tell(&vf); + if( p < 0.0 ) + { + p = 0.0; + } + + return (long long)( p*1000.0 ); +} + +long long mgOggFile::seek( long long posMs, bool relativ ) +{ + if( relativ ) + { + posMs += indexMs(); + } + + int r = ov_time_seek( &vf, (double) posMs/1000.0 ); + + if(r) + { + error( "seek", r ); + return -1; + } + + posMs = indexMs(); + return posMs; +} + +int mgOggFile::stream( short *buffer, int samples ) +{ + int n; + do + { + int stream; + n = ov_read( &vf, (char *)buffer, samples*2, 0, 2, 1, &stream ); + } + while( n == OV_HOLE ); + + if(n < 0) + { + error( "read", n ); + } + + return (n/2); +} + +// --- mgOggDecoder ------------------------------------------------------------- + +mgOggDecoder::mgOggDecoder( mgContentItem *item ) + : mgDecoder( item ) +{ + m_filename = item->getSourceFile(); + m_file = new mgOggFile( m_filename ); + m_pcm = 0; + init(); +} + +mgOggDecoder::~mgOggDecoder() +{ + delete m_file; + clean(); +} + +bool mgOggDecoder::valid() +{ + bool res = false; + if( tryLock() ) + { + if( m_file->open( false ) ) + { + res = true; + } + unlock(); + } + return res; +} + +mgPlayInfo *mgOggDecoder::playInfo(void) +{ + if( m_playing ) + { + // m_playinfo.m_index = index/1000; + // m_playinfo.m_total = info.Total; + + return &m_playinfo; + } + + return 0; +} + +void mgOggDecoder::init() +{ + clean(); + m_pcm = new struct mad_pcm; + m_index = 0; +} + +bool mgOggDecoder::clean() +{ + m_playing = false; + + delete m_pcm; + m_pcm = 0; + + m_file->close(); + return false; +} + +#define SF_SAMPLES (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t)) + +bool mgOggDecoder::start() +{ + lock(true); + init(); + m_playing = true; + + if( m_file->open() /*&& info.DoScan(true)*/ ) + { + // obtain from database: rate, channels + /* d(printf("ogg: open rate=%d channels=%d seek=%d\n", + info.SampleFreq,info.Channels,file.CanSeek())) + */ + if( m_item->getChannels() <= 2 ) + { + unlock(); + return true; + } + else + { + // esyslog( "ERROR: cannot play ogg file %s: more than 2 channels", m_filename.c_str() ); + } + } + + clean(); + unlock(); + + return false; +} + +bool mgOggDecoder::stop(void) +{ + lock(); + + if( m_playing ) + { + clean(); + } + unlock(); + + return true; +} + +struct mgDecode *mgOggDecoder::done(eDecodeStatus status) +{ + m_ds.status = status; + m_ds.index = m_index; + m_ds.pcm = m_pcm; + + unlock(); // release the lock from decode() + + return &m_ds; +} + +struct mgDecode *mgOggDecoder::decode(void) +{ + lock(); // this is released in Done() + + if( m_playing ) + { + short framebuff[2*SF_SAMPLES]; + int n = m_file->stream( framebuff, SF_SAMPLES ); + + if( n < 0 ) + { + return done(dsError); + } + + if( n == 0 ) + { + return done(dsEof); + } + + // should be done during initialization + m_pcm->samplerate = m_item->getSampleRate(); // from database + m_pcm->channels = m_item->getChannels(); // from database + + n /= m_pcm->channels; + m_pcm->length = n; + m_index = m_file->indexMs(); + + short *data = framebuff; + mad_fixed_t *sam0 = m_pcm->samples[0], *sam1 = m_pcm->samples[1]; + + const int s = MAD_F_FRACBITS + 1 - ( sizeof(short)*8 ); // shift value for mad_fixed conversion + + if( m_pcm->channels>1 ) + { + for(; n > 0 ; n-- ) + { + *sam0++=(*data++) << s; + *sam1++=(*data++) << s; + } + } + else + { + for(; n>0 ; n--) + { + *sam0++=(*data++) << s; + } + } + return done(dsPlay); + } + return done(dsError); +} + +bool mgOggDecoder::skip(int Seconds, int Avail, int Rate) +{ + lock(); + bool res = false; + + if( m_playing && m_file->canSeek() ) + { + float fsecs = (float)Seconds - ( (float)Avail / (float)(Rate * (16/8 * 2) ) ); + // Byte/s = samplerate * 16 bit * 2 chan + + long long newpos = m_file->indexMs() + (long long)(fsecs*1000.0); + + if( newpos < 0 ) + { + newpos=0; + } + + newpos = m_file->seek( newpos, false ); + + if( newpos >= 0 ) + { + m_index = m_file->indexMs(); +#ifdef DEBUG + int i = index/1000; + printf( "ogg: skipping to %02d:%02d\n", i/60, i%60 ); +#endif + res = true; + } + } + unlock(); + return res; +} + +#endif //HAVE_VORBISFILE diff --git a/vdr_decoder_ogg.h b/vdr_decoder_ogg.h new file mode 100644 index 0000000..b6bfc17 --- /dev/null +++ b/vdr_decoder_ogg.h @@ -0,0 +1,60 @@ +/*! \file vdr_decoder_ogg.h + * \ingroup vdr + * + * The file contains a decoder which is used by the player to decode ogg vorbis audio files. + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001-2003 Stefan Huelswitt + */ + +#ifndef ___DECODER_OGG_H +#define ___DECODER_OGG_H + +#define DEC_OGG DEC_ID('O','G','G',' ') +#define DEC_OGG_STR "OGG" + +#ifdef HAVE_VORBISFILE + +#include "vdr_decoder.h" + +// ---------------------------------------------------------------- + +class mgOggFile; + +class mgOggDecoder : public mgDecoder +{ + private: + + mgOggFile *m_file; + struct mgDecode m_ds; + struct mad_pcm *m_pcm; + unsigned long long m_index; + + // + void init(void); + bool clean(void); + struct mgDecode *done( eDecodeStatus status ); + + public: + + mgOggDecoder( mgContentItem *item ); + ~mgOggDecoder(); + + virtual mgPlayInfo *playInfo(); + + virtual bool valid(); + + virtual bool start(); + + virtual bool stop(); + + virtual bool skip(int Seconds, int Avail, int Rate); + + virtual struct mgDecode *decode(void); +}; + +// ---------------------------------------------------------------- + +#endif //HAVE_VORBISFILE +#endif //___DECODER_OGG_H diff --git a/vdr_player.c b/vdr_player.c index 105072b..5cde3fe 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -3,11 +3,11 @@ * \brief A generic PCM player for a VDR media plugin (muggle) * * \version $Revision: 1.7 $ - * \date $Date: 2004/07/27 20:50:54 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_player.c,v 1.7 2004/07/27 20:50:54 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -176,7 +176,6 @@ private: void SetPlayMode(ePlayMode mode); void WaitPlayMode(ePlayMode mode, bool inv); - string getSourceFile(); protected: virtual void Activate(bool On); virtual void Action(void); @@ -363,10 +362,10 @@ void mgPCMPlayer::Action(void) m_index = 0; m_playing = m_current; - string filename = getSourceFile(); + string filename = m_playing->getSourceFile(); mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); - if( ( m_decoder = mgDecoders::findDecoder( filename ) ) && m_decoder->start() ) + if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() ) { levelgood = true; haslevel = false; @@ -671,38 +670,6 @@ void mgPCMPlayer::StopPlay() } } -string mgPCMPlayer::getSourceFile() -{ - string filename; - - if( !the_setup.GdCompatibility ) - { // use filename itself - filename = string( the_setup.ToplevelDir ) + m_playing->getSourceFile(); - } - else - { // find the unique name within any directory, but what is top? video? - char *cmd = NULL; - asprintf( &cmd, "find %s -follow -type f -name '%s'|sort ", - the_setup.ToplevelDir, m_playing->getSourceFile().c_str() ); - - FILE *p = popen(cmd, "r"); - if (p) - { - char *s; - if( (s = readline(p) ) != NULL ) - { - filename = string( s ); - } - } - pclose( p ); - delete cmd; - } - - cout << "mgPCMPlayer::getSourceFile: found filename " << filename << endl << flush; - return filename; - // return "/test.mp3"; -} - bool mgPCMPlayer::NextFile() { bool res = false; -- cgit v1.2.3 From 0edf468feae65872a854be3cc3949d8d0a11587f Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 9 Sep 2004 06:25:57 +0000 Subject: Enhanced documentation for Doxygen git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@150 e10066b5-e1e2-0310-b819-94efdf66514b --- HISTORY | 4 +-- README | 54 +++++++++++++++++++--------- TODO | 22 +++++++++++- muggle.doxygen | 6 ++-- stylesheet.css | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 stylesheet.css diff --git a/HISTORY b/HISTORY index 7350748..e0960d4 100644 --- a/HISTORY +++ b/HISTORY @@ -2,7 +2,7 @@ VDR Plugin 'muggle' Revision History ------------------------------------ 2004-08-31: Version 0.0.1-ALPHA - - An initial revision given to a few people. - +2004-09-05: Version 0.0.2-ALPHA +- Added an Ogg Vorbis decoder diff --git a/README b/README index 209dd46..254e344 100644 --- a/README +++ b/README @@ -1,3 +1,5 @@ +/*! \mainpage Muggle: Media Juggler for VDR + This is a "plugin" for the Video Disk Recorder (VDR). Written by: Andi Kellner @@ -11,7 +13,7 @@ Latest version available at: URL See the file COPYING for license information. -PLEASE! +\section foreword PLEASE! This is a difficult plugin. It's nice but difficult. With difficult I mean, that due to the underlying @@ -23,73 +25,87 @@ Please provide feedback to the authors whenever you think, these instructions are not appropriate, wrong, or useless in any other sense. -1 DESCRIPTION +\section desc DESCRIPTION The muggle plugin provides a database link for VDR so that selection of media becomes more flexible. Prerequisites are describedin Section 2, Notes on Compilation are in Section 3. Before using the plugin, you need to import your media into the database (cf. Section 4). The configuration of VDR and startup parameters are descibed in Section 5. -2 PREREQUISITES +\section prereq PREREQUISITES The plugin is written for VDR 1.2.6. In addition, the following pieces of software are required: -- mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) -- mySQL client libraries (Debian package libmysqlclient-dev) -- libmad (for mp3 decoding) (Debian package libmad0-dev) -- libtag (for ID3 tag reading/writing) (Debian package libtag1-dev) + - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) + - mySQL client libraries (Debian package libmysqlclient-dev) + - libmad (for mp3 decoding) (Debian package libmad0-dev) + - libtag (for ID3 tag reading/writing) (Debian package libtag1-dev) + - optionally libvorbis and libvorbisfile (Debian packages ) +The developer versions are needed because their headers are required for compilation. The server need not be on the same machine as the VDR. Also, music tracks can reside somewhere else, if they are available through a remote filesystem (NFS, Samba). However, in this case you should know what you are doing in terms of networking and security issues. - -3 INSTALLING +\section install INSTALLING Unpack the sources in PLUGINS/src below your VDR directory (i.e. where all your other plugins are. For example (paths and version numbers may vary) +\verbatim cd /usr/local/src/VDR/PLUGINS/src tar xvjf muggle-0.1.7.tar.bz2 +\endverbatim Establish a symlink as you would for other plugins: +\verbatim ln -s muggle-0.1.7 muggle +\endverbatim Within the VDR main directory (e.g. /usr/local/src/VDR) issue a +\verbatim make plugins +\endverbatim This should build all relevant stuff. If you have difficulties, check that required libraries are in the library directories stated in the muggle Makefile. -4 IMPORT +\section import IMPORT The import is done in two steps: First, a database is created and initialized with proper data structures (so-called schema). Then, these data structures are filled from the ID3 tags of your music tracks. -4.1 Setup Database +\subsection dbsetup Setup Database This step can be done on the database server or on some other client machine. Within the directory scripts there are a few helpful files to support setting up the database. Change into that directory:# +\verbatim cd scripts +\endverbatim The first step is to essentially create the database: +\verbatim mysql -u root -p < createdb.mysql +\endverbatim You will need to enter your root password that you choose during mySQL installation. Next, we generate the database tables (schema): +\verbatim mysql -u root -p < createtables.mysql +\endverbatim Further, initial data about known languages, genres, sources and musictypes is fed into the database: Execute these commands on a single line, the \ for the linebreak ist just for presentation purposes here. +\verbatim echo " use GiantDisc; load data local infile 'genres.txt' into table genre;" | \ mysql -u root -p --local-infile=1 @@ -101,6 +117,7 @@ Execute these commands on a single line, the \ for the linebreak ist just for pr echo "use GiantDisc; load data local infile '$SCRIPTDIR/sources.txt' into table language;" | \ mysql -u root --local-infile=1 +\endverbatim You can find the sequence of commands in the file scripts/make-empty-db. Use it at your own luck. @@ -114,7 +131,7 @@ If you want your database name to be different than 'GiantDisc' you will need to in the files createdb.mysql and createtables.mysql and in the commands above. Now your database is ready for import. -4.2 Import Music +\subsection importfile Import Music The next step is to feed all music information into the database. There is a small tool called 'mugglei' in the muggle main directory. It connects to the database, evaluates ID3 tags from a file, and writes @@ -136,7 +153,9 @@ First, import the files in Assorted. This requires the flag -a to mugglei. Furth specify database host, name, user and password, respectively. The filename to import is given using the -f directive. Using 'find' you can import all files for assorted albums with a command like: +\verbatim find Assorted -name '*' -type f -exec mugglei -a -f {} \; +\endverbatim For reasons of simplicity, the arguments -h, -n, -u and -p are not shown. You will need them if the default values do not apply or modify the source code accordingly (beginning of function main). Also, make sure @@ -144,7 +163,9 @@ that either mugglei is on your path or specify an absolute or relative path in t For "regular" albums, the following command may be helpful: +\verbatim find * -path 'Assorted' -prune -o -type f -exec mugglei -f {} \; +\endverbatim It is important that you perform all these steps from the same location so the filenames are relative to exactly the same directory (e.g. /home/music in the example case). @@ -153,12 +174,14 @@ Speed should not be an issue: on my machine, it takes about 10 secs to run the i albums with more than 600 tracks. Further 1200 tracks or so require 20 more secs. This depends on machine configuration, of course. -5 MUGGLE CONFIGURATION +\section config MUGGLE CONFIGURATION Muggle uses a small set of command line parameters in order to control the interaction with the mySQL server. Let's look at an example: +\verbatim -P'muggle -h localhost -u vdr -n GiantDisc -t/home/music' +\endverbatim The -h parameter specifies the database host, -u specifies the user, -n is the database name. The scripts mentioned above do not make use of passwords, but restrict database acccess on a server basis. @@ -166,10 +189,9 @@ above do not make use of passwords, but restrict database acccess on a server ba The -t argument specifies the top level directory of the music files. On a local installation, this is the directory in which you executed the import steps (Chapter 4.2). -6 USING MUGGLE +\section use USING MUGGLE Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. Then turn to the playlist view using yellow and start play using again the red function key. - - +*/ \ No newline at end of file diff --git a/TODO b/TODO index cac1f2c..402aa85 100644 --- a/TODO +++ b/TODO @@ -8,13 +8,33 @@ Ogg/Vorbis decoder integration Deployment ---------- - Script to publish a version - - Checkout - make dist - copy .tgz, README, CHANGES, HISTORY into web directory - generate documentation - copy into web directory - sync with web +----------------- publish_muggle.sh ----------------- +# $1: version name (e.g. 0.0.5-BETA) + +# how to determine current path? +svn copy ... http://.../svn/muggle/tags/$1 + +make dist +# obtain name from output? or copy commands and make correctly +mv vdr-muggle-0.0.1.tgz ~/Web/current/htpc/muggle/vdr-muggle-$1.tgz + +cp README ... +cp TODO ... +cp CHANGES ... + +doxygen muggle.doxygen +cp -R doc ~/Web/current/htpc/muggle/ + +sitecopy --update htpctech + +----------------------------------------------------- + - How to track bugs and feature requests? Testing/bugs diff --git a/muggle.doxygen b/muggle.doxygen index f973c51..c5796c6 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.1 +PROJECT_NUMBER = 0.0.2 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -361,7 +361,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = +INPUT = README # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp @@ -535,7 +535,7 @@ HTML_FOOTER = # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet -HTML_STYLESHEET = +HTML_STYLESHEET = stylesheet.css # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 0000000..dde7de6 --- /dev/null +++ b/stylesheet.css @@ -0,0 +1,109 @@ +body { background-color: white + } + +P { font-family: arial, sans-serif; + font-size: 10pt; + color: black; + text-align: justify; + vertical-align: top; + margin-right: 10pt + } + +P.header { font-family: arial,sans-serif; + font-size: 10pt; + color: black; + text-align: right; + margin-right: 10pt + } + +P.klein { font-family: arial,sans-serif; + font-size: 8pt; + color: black; + text-align: justify; + margin-right: 10pt + } + +P.kleinmitte { font-family: arial,sans-serif; + font-size: 8pt; + color: black; + text-align: center; + margin-right: 10pt + } + +P.kleinrechts { font-family: arial,sans-serif; + font-size: 8pt; + color: black; + text-align: right; + margin-right: 10pt + } + +P.kleinlinks { font-family: arial,sans-serif; + font-size: 8pt; + color: black; + text-align: left; + margin-right: 10pt + } + +P.bildtitel { font-family: arial,sans-serif; + font-size: 8pt; + font-weight: 900; + color: black; + text-align: left; + vertikal-align: top; + margin-left: 10pt + } + +P.bild { font-family: arial,sans-serif; + font-size: 8pt; + font-weight: 900; + color: black; + text-align: left; + vertikal-align: top; + margin-right: 10pt + } + +H1 { font-family: arial,sans-serif; + font-size: 20pt; + color: black; + text-align: left; + text-transform: uppercase; + letter-spacing: 10pt; + margin-right: 10pt + } + +H2 { font-family: arial,sans-serif; + font-size: 12pt; + color:black; + text-align: left; + margin-right: 10pt + } + +H3 { font-family: arial,sans-serif; + font-size: 10pt; + color:black; + text-align:left; + margin-right: 10pt; + } + +UL { font-family: arial,sans-serif; + font-size: 10pt; + color:black; + text-align: justify; + margin-right: 10pt + } +DT { font-family: arial,sans-serif; + font-size: 10pt; + font-weight: bold; + color:black; + text-align: left; + } + +DD { font-family: arial,sans-serif; + font-size: 10pt; + color:black; + text-align: justify; + margin-right: 10pt; + } +TD { vertical-align: top + } + -- cgit v1.2.3 From c69242aac5f4d5b3f5c9cf2dae4f519ec3787cae Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 10 Sep 2004 14:35:28 +0000 Subject: Stylesheet changed so that all text is sans serif. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@153 e10066b5-e1e2-0310-b819-94efdf66514b --- stylesheet.css | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/stylesheet.css b/stylesheet.css index dde7de6..15ad718 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -1,5 +1,11 @@ -body { background-color: white - } +body { background-color: white ; + font-family: arial, sans-serif; + font-size: 10pt; + color: black; + text-align: justify; + vertical-align: top; + margin-right: 10pt + } P { font-family: arial, sans-serif; font-size: 10pt; @@ -7,14 +13,14 @@ P { font-family: arial, sans-serif; text-align: justify; vertical-align: top; margin-right: 10pt - } + } P.header { font-family: arial,sans-serif; font-size: 10pt; color: black; text-align: right; margin-right: 10pt - } + } P.klein { font-family: arial,sans-serif; font-size: 8pt; -- cgit v1.2.3 From 0281b129e7bdde83400eaa9cb26d5dbe0635591a Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 11 Sep 2004 12:07:54 +0000 Subject: Documentation added git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@154 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_content_interface.c | 8 ++---- mg_media.c | 32 ++++------------------- mg_media.h | 69 +++++++++++++++++++++++++++++++++++--------------- vdr_menu.c | 25 +++++++++--------- vdr_menu.h | 8 +++--- 5 files changed, 72 insertions(+), 70 deletions(-) diff --git a/mg_content_interface.c b/mg_content_interface.c index 5c4890b..65a564f 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -55,20 +55,16 @@ mgTracklist::~mgTracklist() } /*! - ***************************************************************************** * \brief returns a pointer to the list of elements - * - ****************************************************************************/ + */ vector *mgTracklist::getAll() { return &m_list; } /*! - ***************************************************************************** * \brief returns the number of elements in the list - * - ****************************************************************************/ + */ unsigned int mgTracklist::getNumItems() { return m_list.size(); diff --git a/mg_media.c b/mg_media.c index 96dc3f7..6cf5339 100644 --- a/mg_media.c +++ b/mg_media.c @@ -2,9 +2,9 @@ * \brief Top level access to media in vdr plugin muggle * * \version $Revision: 1.14 $ - * \date $Date: 2004/07/29 06:17:40 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ */ /* makes sure we dont parse the same declarations twice */ @@ -173,7 +173,7 @@ mgMedia::mgMedia(contentType mediatype) case GD_MP3: { errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully conntected to sql database %s", the_setup.DbName ); + mgDebug(3, "Successfully connected to sql database %s", the_setup.DbName ); } } if(errval < 0) @@ -192,10 +192,6 @@ mgMedia::mgMedia(contentType mediatype) } } -/*! - ******************************************************************* - * \brief - ********************************************************************/ mgMedia::~mgMedia() { if( m_filters ) @@ -204,10 +200,6 @@ mgMedia::~mgMedia() } } -/*! - ******************************************************************* - * \brief - ********************************************************************/ string mgMedia::getMediaTypeName() { switch(m_mediatype) @@ -221,11 +213,6 @@ string mgMedia::getMediaTypeName() return ""; } - -/*! - ******************************************************************* - * \brief - ********************************************************************/ mgSelectionTreeNode* mgMedia::getSelectionRoot() { switch(m_mediatype) @@ -237,21 +224,12 @@ mgSelectionTreeNode* mgMedia::getSelectionRoot() return NULL; } - -/*! - ******************************************************************* - * \brief - ********************************************************************/ mgPlaylist* mgMedia::createTemporaryPlaylist() { string tmpname = "current"; return loadPlaylist( tmpname ); } -/*! - * \brief Load a playlist from the database - * - */ mgPlaylist* mgMedia::loadPlaylist(string name) { mgPlaylist *list; @@ -259,9 +237,9 @@ mgPlaylist* mgMedia::loadPlaylist(string name) { case GD_MP3: { - list = new GdPlaylist(name, m_db); + list = new GdPlaylist( name, m_db ); list->setDisplayColumns(getDefaultCols()); - + return list; } break; } diff --git a/mg_media.h b/mg_media.h index 394db13..90a43f2 100644 --- a/mg_media.h +++ b/mg_media.h @@ -3,9 +3,9 @@ * \brief Top level access to media in vdr plugin muggle * * \version $Revision: 1.11 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ */ // makes sure we dont use parse the same declarations twice @@ -41,39 +41,66 @@ class mgFilterSets std::vector m_titles; public: - // constructor, constracts a number >=1 of filter sets - // the first set (index 0 ) is active by default + + /*! + * \brief a constructor + * + * constracts a number >=1 of filter sets the first set (index 0 ) is active by default + */ mgFilterSets(); - // destructor + /*! + * \brief the destructor + */ virtual ~mgFilterSets(); - // returns the number of available sets + /*! + * \brief returns the number of available sets + */ int numSets(); - // proceeds to the next one in a circular fashion + /*! + * \brief proceeds to the next one in a circular fashion + */ void nextSet(); - // activates a specific set by index + /*! + * \brief activates a specific set by index + */ void select(int n); - // restores the default values for all filter values in the active set - // normally, the default values represent 'no restrictions' + /*! + * \brief restore empty state + * + * Restores the default values for all filter values in the active set + * normally, the default values represent 'no restrictions' + */ virtual void clear(); - // stores the current filter values + /*! + * \brief stores the current filter values + */ void accept(); - // returns the active set to the application - // the application may temporarily modify the filter values - // accept() needs to be called to memorize the changed values + /*! + * \brief returns the active set to the application + * + * The application may temporarily modify the filter values + * accept() needs to be called to memorize the changed values + */ std::vector *getFilters(); - // computes the (e.g. sql-) restrictions specified by the active filter set - // and returns the index of the appropriate defualt view in viewPrt - virtual std::string computeRestriction(int *viewPrt)=0; - - // returns title of active filter set + /*! + * \brief compute restrictions + * + * computes the (e.g. sql-) restrictions specified by the active filter set + * and returns the index of the appropriate defualt view in viewPrt + */ + virtual std::string computeRestriction(int *viewPrt) = 0; + + /*! + * \brief returns title of active filter set + */ std::string getTitle(); }; @@ -112,10 +139,12 @@ class mgMedia mgSelectionTreeNode* getSelectionRoot(); - // playlist management + /*! \brief playlist management */ + //@{ mgPlaylist* createTemporaryPlaylist(); mgPlaylist* loadPlaylist( std::string name ); std::vector *getStoredPlaylists(); + //@} std::vector getDefaultCols(); mgTracklist* getTracks(); diff --git a/vdr_menu.c b/vdr_menu.c index fd02c11..54a9318 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -3,11 +3,11 @@ * \brief Implements menu handling for browsing media libraries within VDR * * \version $Revision: 1.27 $ - * \date $Date: 2004/07/27 20:50:54 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_menu.c,v 1.27 2004/07/27 20:50:54 lvw Exp $ + * $Id$ */ #include @@ -324,6 +324,8 @@ eOSState mgMainMenu::ProcessKey(eKeys key) // load the selected playlist m_current_playlist -> clear(); + delete m_current_playlist; + string selected = (*m_plists)[ Current() ]; m_current_playlist = m_media->loadPlaylist( selected.c_str() ); @@ -523,8 +525,6 @@ void mgMainMenu::DisplayTreeSubmenu() // Add items Add( new cOsdItem( "1 - Instant play" ) ); - Add( new cOsdItem( "9 - Test9" ) ); - Add( new cOsdItem( "0 - Test0" ) ); Display(); } @@ -575,7 +575,6 @@ eOSState mgMainMenu::TreeSubmenuAction( int n ) void mgMainMenu::DisplayPlaylist( int index_current ) { - mgDebug( 1, "mgMainMenu::DisplayPlaylist: entering." ); m_state = PLAYLIST; // make sure we have a current playlist @@ -656,16 +655,16 @@ void mgMainMenu::DisplayPlaylistSubmenu() SetTitle( "Muggle - Playlist View Commands" ); // Add items - Add( new cOsdItem( "1 - Load playlist" ) ); - Add( new cOsdItem( "2 - Save playlist" ) ); - Add( new cOsdItem( "3 - Rename playlist" ) ); - Add( new cOsdItem( "4 - Clear playlist" ) ); - Add( new cOsdItem( "5 - Remove entry from list" ) ); - Add( new cOsdItem( "6 - Export playlist" ) ); + Add( new cOsdItem( "Load playlist" ) ); + Add( new cOsdItem( "Save playlist" ) ); + Add( new cOsdItem( "Rename playlist" ) ); + Add( new cOsdItem( "Clear playlist" ) ); + Add( new cOsdItem( "Remove entry from list" ) ); + Add( new cOsdItem( "Export playlist" ) ); if( m_playlist_commands ) { - Add( new cOsdItem( "7 - External playlist commands" ) ); + Add( new cOsdItem( "External playlist commands" ) ); } Display(); diff --git a/vdr_menu.h b/vdr_menu.h index 5ff7f34..e827a33 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -3,11 +3,11 @@ * \brief Implements menu handling for broswing media libraries within VDR * * \version $Revision: 1.13 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_menu.h,v 1.13 2004/07/25 21:33:35 lvw Exp $ + * $Id$ */ #ifndef _VDR_MENU_H @@ -107,7 +107,7 @@ class mgMainMenu : public cOsdMenu mgSelectionTreeNode *m_root; mgSelectionTreeNode *m_node; mgPlaylist *m_current_playlist; - std::vector *m_plists; + std::vector *m_plists; MuggleStatus m_state; std::list m_history; -- cgit v1.2.3 From 61f932e7c8eb73927e2673a1dbdb8bd88d2e69dd Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 12 Sep 2004 22:34:41 +0000 Subject: Integrated patch from eloy for use with VDR >= 1.3.7 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@160 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 44 +++++++++++++++++++++++++++++++++++++++++++- vdr_player.c | 24 ++++++++++++++++++++++++ vdr_player.h | 13 ++++++++++--- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 54a9318..094acd3 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -20,6 +20,11 @@ #include #include +#if VDRVERSNUM >= 10307 +#include +#include +#endif + #include "vdr_menu.h" #include "vdr_player.h" #include "i18n.h" @@ -158,9 +163,14 @@ eOSState mgMainMenu::ProcessKey(eKeys key) char *buffer = 0; asprintf( &buffer, "%d tracks sent to current playlist", (int) tracks->size() ); m_current_playlist->appendList(tracks); +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo,buffer); + Skins.Flush(); +#else Interface->Status( buffer ); Interface->Flush(); - +#endif + free( buffer ); } else @@ -699,14 +709,22 @@ eOSState mgMainMenu::ExecutePlaylistCommand( int current ) if( command->Confirm() ) { asprintf( &buffer, "%s?", command->Title() ); +//#if VDRVERSNUM < 10307 confirmed = Interface->Confirm( buffer ); +//#else +//#endif free( buffer ); } if( confirmed ) { asprintf( &buffer, "%s...", command->Title() ); +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo,buffer); + Skins.Flush(); +#else Interface->Status( buffer ); Interface->Flush(); +#endif free( buffer ); string tmp_m3u_file = (char *) AddDirectory( cPlugin::ConfigDirectory("muggle"), "current.m3u" ); @@ -739,22 +757,36 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) case 0: { LoadPlaylist(); +#if VDRVERSNUM < 10307 Interface->Flush(); +#else + Skins.Flush(); +#endif // jump to playlist view from here? } break; case 1: { SavePlaylist(); +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo,"Playlist saved"); + Skins.Flush(); +#else Interface->Status( "Playlist saved"); Interface->Flush(); +#endif } break; case 2: { // renamer playlist RenamePlaylist(); // confirmation +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo,"Playlist renamed (dummy)"); + Skins.Flush(); +#else Interface->Status( "Playlist renamed (dummy)" ); Interface->Flush(); +#endif state = osContinue; } break; @@ -763,8 +795,13 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) m_current_playlist->clear(); // confirmation +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo,"Playlist cleared"); + Skins.Flush(); +#else Interface->Status( "Playlist cleared" ); Interface->Flush(); +#endif state = osContinue; } break; @@ -778,8 +815,13 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) DisplayPlaylist( m_last_osd_index ); // confirmation +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo,"Entry removed"); + Skins.Flush(); +#else Interface->Status( "Entry removed" ); Interface->Flush(); +#endif } break; case 5: { diff --git a/vdr_player.c b/vdr_player.c index 5cde3fe..6161dd9 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -196,6 +196,7 @@ public: virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false); // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW void NewPlaylist(mgPlaylist *plist); + mgContentItem *GetCurrent () { return m_current; } }; mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist) @@ -924,12 +925,29 @@ void mgPlayerControl::ShowProgress() if( !m_has_osd ) { // open the osd if its not already there... +#if VDRVERSNUM >= 10307 + osd = cOsdProvider::NewOsd (Setup.OSDLeft, Setup.OSDTop); + tArea Areas[] = { { 0, 0, Setup.OSDWidth, Setup.OSDHeight, 2 } }; + osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea)); + font = cFont::GetFont (fontOsd); +#else Interface->Open(); +#endif m_has_osd = true; } // now an osd is open, go on +#if VDRVERSNUM >= 10307 + int w = Setup.OSDWidth; + int h = Setup.OSDHeight; + osd->DrawRectangle (0, 0, w - 1, h - 1, clrGray50); + mgContentItem *item = m_player->GetCurrent (); + string msg = item->getGenre () + " " + item->getTitle (); + osd->DrawText (0, h - font->Height () - 2, msg.c_str (), clrBlack, clrCyan, + cFont::GetFont (fontOsd), w, font->Height () + 2, taCenter); + osd->Flush(); +#else int w = Interface->Width(); int h = Interface->Height(); @@ -939,6 +957,7 @@ void mgPlayerControl::ShowProgress() // Add: progress bar Interface->Flush(); +#endif } else { @@ -950,7 +969,12 @@ void mgPlayerControl::Hide() { if( m_has_osd ) { +#if VDRVERSNUM >= 10307 + osd->Flush(); + delete osd; +#else Interface->Close(); +#endif m_has_osd = false; } } diff --git a/vdr_player.h b/vdr_player.h index 6ce2f1b..b7ec374 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -3,11 +3,11 @@ * \brief A player/control combination to let VDR play music * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_player.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -19,6 +19,9 @@ #define ___DVB_MP3_H #include +#if VDRVERSNUM >= 10307 +class cOsd; +#endif // ------------------------------------------------------------------- @@ -45,6 +48,10 @@ private: //! \brief indicates, whether an osd is currently displayed bool m_has_osd; +#if VDRVERSNUM >= 10307 + cOsd *osd; + const cFont *font; +#endif public: -- cgit v1.2.3 From 9f93b669ca88ee86990665fdf7683b908b711e8a Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 12 Sep 2004 22:35:18 +0000 Subject: Integrated patch from eloy for command line option handling git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@161 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 87 ++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/muggle.c b/muggle.c index a9b6c75..ecb1824 100644 --- a/muggle.c +++ b/muggle.c @@ -48,13 +48,13 @@ const char* mgMuggle::MainMenuEntry(void) mgMuggle::mgMuggle(void) { // defaults for database arguments - the_setup.DbHost = "localhost"; + the_setup.DbHost = strdup ("localhost"); the_setup.DbPort = 0; - the_setup.DbName = "GiantDisc"; - the_setup.DbUser = ""; - the_setup.DbPass = "" ; + the_setup.DbName = strdup ("GiantDisc"); + the_setup.DbUser = strdup (""); + the_setup.DbPass = strdup (""); the_setup.GdCompatibility = false; - the_setup.ToplevelDir = "/mnt/music/"; + the_setup.ToplevelDir = strdup ("/mnt/music/"); } mgMuggle::~mgMuggle() @@ -93,46 +93,51 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) }; int c, option_index = 0; - while( ( c = getopt_long( argc, argv, "h:p:u:n:t:w:g:", long_options, &option_index ) ) != -1 ) - { - switch (c) - { - case 'h': - { - the_setup.DbHost = optarg; - } break; - case 'n': - { - the_setup.DbName = optarg; - } break; - case 'p': - { - the_setup.DbPort = atoi( optarg ); - } break; - case 'u': - { - the_setup.DbUser = optarg; + while( ( c = getopt_long( argc, argv, "gh:n:p:t:u:w:", long_options, &option_index ) ) != -1 ) + { + switch (c) + { + case 'h': + { + the_setup.DbHost = strcpyrealloc (the_setup.DbHost, optarg); + } break; + case 'n': + { + the_setup.DbName = strcpyrealloc (the_setup.DbName, optarg); } break; - case 'w': - { - the_setup.DbPass = optarg; - } break; - case 't': - { - string res = string(optarg) + "/"; - the_setup.ToplevelDir = strdup( res.c_str() ); - } break; - case 'g': - { - the_setup.DbName = "GiantDisc"; - the_setup.GdCompatibility = true; - } break; - default: return false; + case 'p': + { + the_setup.DbPort = atoi( optarg ); + } break; + case 'u': + { + the_setup.DbUser = strcpyrealloc (the_setup.DbUser, optarg); + } break; + case 'w': + { + the_setup.DbPass = strcpyrealloc (the_setup.DbPass, optarg); + } break; + case 't': + { + if (optarg[strlen(optarg) - 1] != '/') + { + string res = string(optarg) + "/"; + the_setup.ToplevelDir = strdup( res.c_str() ); + } + else + { + the_setup.ToplevelDir = strcpyrealloc (the_setup.ToplevelDir, optarg); + } + } break; + case 'g': + { + the_setup.DbName = strcpyrealloc (the_setup.DbName, "GiantDisc"); + the_setup.GdCompatibility = true; + } break; + default: return false; } } - // check for GD compatibility and override - return true; } -- cgit v1.2.3 From b3b973a1309feb725802ecaa4cb77c9cd03fb2a5 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 12 Sep 2004 22:36:05 +0000 Subject: Integrated patch from eloy for building mugglei git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@162 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index d9bf48c..df90d7e 100644 --- a/Makefile +++ b/Makefile @@ -40,11 +40,12 @@ PACKAGE = vdr-$(ARCHIVE) ### Includes and Defines (add further entries here): -INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/mysql/ +INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ + -I/usr/include/mysql/ -I/usr/include/taglib DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE -MIFLAGS += -I/usr/include/taglib -ltag -lmysqlclient +MIFLAGS += -I/usr/include/taglib -lmysqlclient ### The object files (add further files here): OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o vdr_decoder_ogg.o @@ -53,7 +54,7 @@ LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis ### Targets: -all: libvdr-$(PLUGIN).so +all: libvdr-$(PLUGIN).so mugglei # Dependencies: @@ -67,14 +68,14 @@ $(DEPFILE): Makefile ### Implicit rules: %.o: %.c %.h - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + $(CXX) $(CXXFLAGS) $(DEFINES) $(INCLUDES) -c $< libvdr-$(PLUGIN).so: $(OBJS) $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@ @cp $@ $(LIBDIR)/$@.$(VDRVERSION) mugglei: mg_tools.o mugglei.o - $(CXX) $(CXXFLAGS) $(MIFLAGS) -o $^ + $(CXX) $(CXXFLAGS) $(LIBS) -ltag -o $@ $^ dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @@ -86,3 +87,4 @@ dist: clean clean: @-rm -f $(OBJS) $(BINOBJS) $(DEPFILE) *.so *.tgz core* *~ + -- cgit v1.2.3 From 3e28731e7cbaeb8c368c245cfc38c1724a4450b9 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 13 Sep 2004 18:06:07 +0000 Subject: Minor changes in build process and i18n git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@163 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 6 ++++-- i18n.h | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index df90d7e..97783b0 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,8 @@ CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g ### The directory environment: DVBDIR = ../../../../DVB -VDRDIR = /usr/local/src/VDR +VDRDIR = ../../../ +# /usr/local/src/VDR LIBDIR = ../../lib TMPDIR = /tmp @@ -54,7 +55,8 @@ LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis ### Targets: -all: libvdr-$(PLUGIN).so mugglei +all: libvdr-$(PLUGIN).so +# mugglei # Dependencies: diff --git a/i18n.h b/i18n.h index ef5a31f..8ea1157 100644 --- a/i18n.h +++ b/i18n.h @@ -3,13 +3,14 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.h,v 1.2 2004/05/28 15:29:18 lvw Exp $ + * $Id$ */ #ifndef _I18N__H #define _I18N__H -#include +// #include +#include extern const tI18nPhrase Phrases[]; -- cgit v1.2.3 From ba85624940c295af07e558e3bff16d0a8657ed1e Mon Sep 17 00:00:00 2001 From: rak Date: Mon, 13 Sep 2004 20:30:01 +0000 Subject: Added URLs for required packages git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@165 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 3 ++- README | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 97783b0..7e5d7af 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,8 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE +#DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' MIFLAGS += -I/usr/include/taglib -lmysqlclient ### The object files (add further files here): diff --git a/README b/README index 254e344..75b2d20 100644 --- a/README +++ b/README @@ -38,10 +38,18 @@ The plugin is written for VDR 1.2.6. In addition, the following pieces of software are required: - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) - - mySQL client libraries (Debian package libmysqlclient-dev) - - libmad (for mp3 decoding) (Debian package libmad0-dev) - - libtag (for ID3 tag reading/writing) (Debian package libtag1-dev) - - optionally libvorbis and libvorbisfile (Debian packages ) + - mySQL client libraries + (Debian package libmysqlclient-devi or + http://www.mysql.org) + - libmad (for mp3 decoding) + (Debian package libmad0-dev or + http://www.underbit.com/products/mad/) + - libtag (for ID3 tag reading/writing) + (Debian package libtag1-dev or + http://developer.kde.org/~wheeler/taglib.html) + - optionally libvorbis and libvorbisfile + (Debian packages or + http://www.xiph.org/ogg/vorbis/) The developer versions are needed because their headers are required for compilation. The server need not be on the same machine as the VDR. Also, music tracks can reside somewhere else, @@ -194,4 +202,4 @@ directory in which you executed the import steps (Chapter 4.2). Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. Then turn to the playlist view using yellow and start play using again the red function key. -*/ \ No newline at end of file +*/ -- cgit v1.2.3 From c4c3fabddb122710ee0d478d03ed98d82d24c435 Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 16 Sep 2004 08:02:00 +0000 Subject: Documentation updated. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@168 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 3 +- TODO | 454 +++++++++++++++++++++++++++++---------------------------------- muggle.c | 2 +- 3 files changed, 213 insertions(+), 246 deletions(-) diff --git a/README b/README index 75b2d20..c23fb5a 100644 --- a/README +++ b/README @@ -39,7 +39,7 @@ software are required: - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) - mySQL client libraries - (Debian package libmysqlclient-devi or + (Debian package libmysqlclient-dev or http://www.mysql.org) - libmad (for mp3 decoding) (Debian package libmad0-dev or @@ -81,7 +81,6 @@ Within the VDR main directory (e.g. /usr/local/src/VDR) issue a This should build all relevant stuff. If you have difficulties, check that required libraries are in the library directories stated in the muggle Makefile. - \section import IMPORT The import is done in two steps: First, a database is created and initialized with proper data structures (so-called schema). diff --git a/TODO b/TODO index 402aa85..3be604e 100644 --- a/TODO +++ b/TODO @@ -1,243 +1,211 @@ -TODO File for Muggle -==================== - -Ogg/Vorbis decoder integration ------------------------------- -- Think, whether type (mp3, ogg, flac) should be stored in database - -Deployment ----------- -- Script to publish a version - - make dist - - copy .tgz, README, CHANGES, HISTORY into web directory - - generate documentation - - copy into web directory - - sync with web - ------------------ publish_muggle.sh ----------------- -# $1: version name (e.g. 0.0.5-BETA) - -# how to determine current path? -svn copy ... http://.../svn/muggle/tags/$1 - -make dist -# obtain name from output? or copy commands and make correctly -mv vdr-muggle-0.0.1.tgz ~/Web/current/htpc/muggle/vdr-muggle-$1.tgz - -cp README ... -cp TODO ... -cp CHANGES ... - -doxygen muggle.doxygen -cp -R doc ~/Web/current/htpc/muggle/ - -sitecopy --update htpctech - ------------------------------------------------------ - -- How to track bugs and feature requests? - -Testing/bugs ------------- -- Test execution of playlist commands -- Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) -- Test saving/loading playlists to database - -Import ------- -- Album - - Cover text - - Cover images (based on filename or tag) - - Genre - - Modified - -- Tracks - - Language (?) - - Genre1, 2 - - Rating? - - Modified, created - - Lyrics - -- Playlist from m3u - -Code polishing --------------- -- Check for unnecessary log commands -- Generate HTML documentation using doxygen, - - use dotty/gv for state machines of player - - make available online -- Clean up mugglei -- Check for memory leaks - - Why do filters use pointers? -- Check for (reasonably) consistent usage of char*/string - -- mgDatabase is not used? - - should handle a static object with a MySQL connection - - execute queries? -- mgPlayer used what for? - - Could save IP/host name and associate last playlist loaded - -- Check compatibility for 1.3.12 - -Short term items -================ - -OSD in general --------------- - -Content -------- -- Handle loop mode in mgPlaylist -- Handle shuffle mode in mgPlaylist - -Player ------- -- Determine max. framecount (needed for rewinding)? -- Init scale/level/normalize? -- Add ogg decoder - -- Add a simple progress display (song title, artist, ...) -- DisplayTrackInfo (part of the player!) -- DisplayAlbumInfo (part of the player!) - -Medium term items -================= -- really abstract from specific queries etc. -- mgDatabase should completely abstract database (mySQL) stuff!? - - read/write queries - - return results (needs a homogeneous representation of results?) - -- Run import/update from within OSD? - -OSD ---- -- Type numbers to enter characters and jump to first title accordingly -- Check whether hierarchical menus are more suitable - -Content -------- -- Save/load filter sets -- Apply filter set as dynamic playlist (i.e. show filters when loading playlists) -- Handle ratings (increase/decrease during replay) - -Player ------- -- Shuffle: toggle and loop keys. Shuffle only songs not already played, not easy though -- Display covers as still pictures -- Add flac decoder -- Set ratings - -Import ------- -- Handle updates - -Web interface -------------- -- Look at PHP stuff from c't 11/04 and adapt to schema? - -Long term ideas -=============== -- daapd integration? -- netjuke integration? -- Display arbitrary images while playing music - -Visions -======= -- handle off-line films (CDs, DVDs, recordings) -- handle streams (live TV with channel list, MP3 radio,..., EPG) -- handle images (possibly in sync with music/radio) -- muggle content syndication (e.g. via DAAPD) -- provide a stream (e.g. icecast) of the currently played music? - -Already Done -============ -- Check play speed (probably XINE related) -- Playlists starts with 2nd song (DONE) -- Export playlists -- Delete selected item -- Add command line option for top level directory - - prepended to filename in non-GD-mode - - searched in GD-mode -- Edit playlist (move tracks like channels in VDR channel list) - (OK in playlist view) -- Instant play = empty current playlist, append tracks of current node and play - (easy, in submenu of browser) -- Clear playlist (submenu action) -- Find files from database entry based on GD compatibility flag -- Handle Next/PrevFile in mgPlaylist (vdr_player.c) -- Add plugin parameters for database name/host/user/pass -- Add plugin parameter for GD filename compatibility -- handle filters: - - create tracklist from filter - - create tree from filter -- i18n (english and german) -- Album import - - Various artists -- Ogg/Vorbis decoder integration - - cOggFile kept - - cOggInfo dismissed in favor of obtaining info from DB - - coding conventions adapted - - Schema extended to keep audio properties - - Import (mugglei) extended to store audio properties in DB - (most notably samplerate, no. channels) - - Extended mgContentItem with audio properties - - Extended mgGdTrack with audio properties (bitrate, samplerate, channels) - - in mgPCMPlayer/vdr_player.c: - - pass m_playing to mp3/ogg decoder (instead of filename) - - mgOggDecoder: obtain audio properties from DB (channels, sampling rate via mgContentItem) - - mgPCMPlayer::getSourceFile moved to abstract data layer (mgContentItem) - and made concrete in subclasses (mgGdTrack) - - mgDecoders::findDecoder: extend decoder detection - -************************************************************ -* -* $Log: TODO,v $ -* Revision 1.14 2004/08/30 14:31:43 LarsAC -* Documentation added -* -* Revision 1.13 2004/08/27 15:20:33 LarsAC -* Updated open issues w.r.t. import -* -* Revision 1.12 2004/08/26 11:11:42 LarsAC -* Import changed to use taglib -* -* Revision 1.11 2004/07/29 06:18:07 lvw -* Added todo entries -* -* Revision 1.10 2004/07/29 05:58:14 lvw -* TODO list changed -* -* Revision 1.9 2004/07/27 07:06:00 LarsAC -* Added file for doxygen documentation generation -* -* Revision 1.8 2004/07/09 13:47:07 LarsAC -* Updated next steps -* -* Revision 1.7 2004/05/28 15:29:18 lvw -* Merged player branch back on HEAD branch. -* -* Revision 1.1.2.12 2004/05/27 07:58:38 lvw -* Removed bugs in moving and removing tracks from playlists -* -* Revision 1.1.2.11 2004/05/25 21:57:58 lvw -* Updated TODO list -* -* Revision 1.1.2.10 2004/05/25 00:10:45 lvw -* Code cleanup and added use of real database source files -* -* Revision 1.1.2.9 2004/05/24 11:48:35 lvw -* Extended TODO list -* -* Revision 1.1.2.8 2004/05/13 06:48:00 lvw -* Updated TODO list -* -* Revision 1.1.2.7 2004/05/12 22:38:37 lvw -* Some cleanup -* -* Revision 1.1.2.6 2004/05/11 06:35:16 lvw -* Added debugging while hunting stop bug. -* -* Revision 1.1.2.5 2004/05/07 06:46:41 lvw -* Removed a bug in playlist deallocation. Added infrastructure to display information while playing. -* -***********************************************************/ +/*! \page issues Muggle Issue List + + The page lists a number of open issues and possible ideas for improvement. + It can be seen as a notepad for the developers or as an entry point for + volunteers. There is no real order among those things and even the occurrence + of an issue does not mean that it will be implemented/resolved at some time. + + If you feel, something is really urgent, go ahead. We'll help you. + + \section urgent Urgent issues + + \subsection bugs Bugs and testing needed + + - Test execution of playlist commands + - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) + - Test saving/loading playlists to database + + \subsection urgentosd OSD-related Issues + + - Add play indicators + - Toggle display with Ok (off -> track info -> album info -> playlist info -> off) + - Display track info + - Song title + - Artist + - Genre + - Album + - Bitrate, channels, samplerate, format, ... + - Progress display + - Display album info + - Album name + - Artist + - Year + - No. Tracks + - Track names? + - Display playlist info + - Overall progress + - Next track(s) + + \subsection urgentcode Code polishing + + - Check for unnecessary log commands + - Generate HTML documentation using doxygen, + - use dotty/gv for state machines of player + - make available online + - Clean up mugglei (abstract code where possible) + - Check for memory leaks + - Why do filters use pointers? + - Check for (reasonably) consistent usage of char pointers and strings + + - mgDatabase is not used? + - should handle a static object with a MySQL connection + - execute queries? + - mgPlayer used what for? + - Could save IP/host name and associate last playlist loaded + + \subsection urgentcontent Content handling + + - Think, whether type (mp3, ogg, flac) should be stored in database + - Handle loop mode in mgPlaylist (already done?) + - Handle shuffle mode in mgPlaylist + - Party mode (see iTunes) + + \subsection urgentplayer Player extensions + + - Determine max. framecount (needed for rewinding)? + - Init scale/level/normalize? + - The max. level should be recognized during play + - Store max. level in the database + + \subsection deploy Deployment + + - Script to publish a version + - make dist + - copy .tgz, README, CHANGES, HISTORY into web directory + - generate documentation + - copy into web directory + - sync with web + - How to track bugs and feature requests? + + \verbatim + # $1: version name (e.g. 0.0.5-BETA) + + # how to determine current path? + svn copy ... http://.../svn/muggle/tags/$1 + + make dist + # obtain name from output? or copy commands and make correctly + mv vdr-muggle-0.0.1.tgz ~/Web/current/htpc/muggle/vdr-muggle-$1.tgz + + cp README ... + cp TODO ... + cp CHANGES ... + + doxygen muggle.doxygen + cp -R doc ~/Web/current/htpc/muggle/ + + sitecopy --update htpctech + \endverbatim + + \section mid ToDo items of moderate importance + + \subsection midimport Import stuff + + - Album + - Genre + - Modified + - Cover text + - Cover images (based on filename or tag) + + - Tracks + - Language (?) + - Genre1, 2 + - Rating? + - Modified, created + - Lyrics + + - Import playlist from m3u + - Run import/update from within OSD? + - Handle updates in both directions + - Check modification date in DB/fstat + - if file is newer: update tags into db + - if DB is newer: update db into tags + + \subsection midcode Code issues + + - really abstract from specific queries etc. + - mgDatabase should completely abstract database (mySQL) stuff!? + - read/write queries + - return results (needs a homogeneous representation of results?) + + \subsection midosd OSD-related issues + + - Incremental search + - Type numbers to enter characters and jump to first title accordingly + - Check whether submenus (as implemented in VDR) are more suitable + - do not permit jumping to arbitrary menus though + + \subsection midcontent Content issues + + - Save/load filter sets + - Apply filter set as dynamic playlist (i.e. show filters when loading playlists) + - Handle ratings (increase/decrease during replay) + - Keys to directly increase + - handle a playcounter + - when playfrequency reaches lower level x from above: decrease rating + - when playfrequency reaches lower level x from below: increase rating + - when playfrequency reaches upper level y from above: decrease rating + - when playfrequency reaches upper level y from below: increase rating + + \subsection midplayer Player issues + + - Display covers + - Add flac decoder + + \section vision Long term ideas and visions + + - daapd integration? + - netjuke integration? + - Display arbitrary images while playing music + + - handle off-line media (CDs, DVDs, recordings) + - handle streams (live TV with channel list, MP3 radio,..., EPG) + + - handle images (possibly in sync with music/radio) + - muggle content syndication (e.g. via DAAPD) + - access media on someone elses computer + - provide a stream (e.g. icecast) of the currently played music? + - allow remote stations to attach to this + + \section done Done + + - Check play speed (was XINE related) + - Playlists starts with 2nd song (DONE) + - Export playlists + - Delete selected item + - Add command line option for top level directory + - prepended to filename in non-GD-mode + - searched in GD-mode + - Edit playlist (move tracks like channels in VDR channel list) + (OK in playlist view) + - Instant play = empty current playlist, append tracks of current node and play + (easy, in submenu of browser) + - Clear playlist (submenu action) + - Find files from database entry based on GD compatibility flag + - Handle Next/PrevFile in mgPlaylist (vdr_player.c) + - Add plugin parameters for database name/host/user/pass + - Add plugin parameter for GD filename compatibility + - handle filters: + - create tracklist from filter + - create tree from filter + - i18n (english and german) + - Album import + - Various artists + - Ogg/Vorbis decoder integration + - cOggFile kept + - cOggInfo dismissed in favor of obtaining info from DB + - coding conventions adapted + - Schema extended to keep audio properties + - Import (mugglei) extended to store audio properties in DB + (most notably samplerate, no. channels) + - Extended mgContentItem with audio properties + - Extended mgGdTrack with audio properties (bitrate, samplerate, channels) + - in mgPCMPlayer/vdr_player.c: + - pass m_playing to mp3/ogg decoder (instead of filename) + - mgOggDecoder: obtain audio properties from DB (channels, sampling rate via mgContentItem) + - mgPCMPlayer::getSourceFile moved to abstract data layer (mgContentItem) + and made concrete in subclasses (mgGdTrack) + - mgDecoders::findDecoder: extend decoder detection + - Check compatibility for 1.3.12 (DONE) + +*/ \ No newline at end of file diff --git a/muggle.c b/muggle.c index ecb1824..92e9831 100644 --- a/muggle.c +++ b/muggle.c @@ -24,7 +24,7 @@ #include "i18n.h" -static const char *VERSION = "0.0.1"; +static const char *VERSION = "0.0.4"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; -- cgit v1.2.3 From cabfa76750250d2fd628f57edda4f894bb237adc Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 17 Sep 2004 15:45:58 +0000 Subject: Added ideas about shuffle and party mode. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@169 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/TODO b/TODO index 3be604e..9a4817c 100644 --- a/TODO +++ b/TODO @@ -58,7 +58,18 @@ - Think, whether type (mp3, ogg, flac) should be stored in database - Handle loop mode in mgPlaylist (already done?) - Handle shuffle mode in mgPlaylist + - shuffle mode on + - for next file: + - generate a random number 0..n-1 + - move corresponding playlist item to front + - continue - Party mode (see iTunes) + - generate a random uid + - if file exists: + - determine maximum playcount of all tracks + - generate a random number n + - if n < playcount / max. playcount + - play the file \subsection urgentplayer Player extensions -- cgit v1.2.3 From dc4959dfdf918f6cd7b448cd2102a97e29c82af7 Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 17 Sep 2004 15:46:24 +0000 Subject: Decoder documentation added. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@170 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_decoder_ogg.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vdr_decoder_ogg.h b/vdr_decoder_ogg.h index b6bfc17..d249ab2 100644 --- a/vdr_decoder_ogg.h +++ b/vdr_decoder_ogg.h @@ -22,6 +22,10 @@ class mgOggFile; +/*! + * \brief A decoder for Ogg Vorbis files + * + */ class mgOggDecoder : public mgDecoder { private: -- cgit v1.2.3 From eb22852ae7bbd762fd5e6c0048c54f0fe45326fc Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 20 Sep 2004 15:25:39 +0000 Subject: Code reviews regarding playlist handling. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@171 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 6 +++++- gd_content_interface.c | 7 ++++++- mg_playlist.h | 26 ++++++++++++++++++++------ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index 9a4817c..7b4a54f 100644 --- a/TODO +++ b/TODO @@ -12,12 +12,16 @@ \subsection bugs Bugs and testing needed - Test execution of playlist commands + - Test Save/Rename/Load of playlist + - Plugin crashes when selecting entries with special characters + - Escape query strings correctly + - Do not allow playlist deletion while playing or stop or similar - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) - - Test saving/loading playlists to database \subsection urgentosd OSD-related Issues - Add play indicators + - mgPCMPlayer::GetIndex: obtain total length from database - Toggle display with Ok (off -> track info -> album info -> playlist info -> off) - Display track info - Song title diff --git a/gd_content_interface.c b/gd_content_interface.c index 6c2f496..01e71b2 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -26,12 +26,17 @@ using namespace std; -#define GD_PLAYLIST_TYPE 0 // listtype for giant disc db +#define GD_PLAYLIST_TYPE 0 //< listtype for giant disc db // some dummies to keep the compiler happy #define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax #define DUMMY +/*! + * \brief initialize a database used by Giantdisc + * + * \todo should be a static function in some Gd class + */ int GdInitDatabase( MYSQL *db ) { if( mysql_init(db) == NULL ) diff --git a/mg_playlist.h b/mg_playlist.h index df677d4..a46d29d 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -3,9 +3,9 @@ * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase * * \version $Revision: 1.6 $ - * \date $Date: 2004/07/26 22:20:55 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * * This file implements the class mgPlaylist which maintains a playlist * and supports editing (e.g. adding or moving tracks), navigating it @@ -23,7 +23,9 @@ * \class mgPlaylist * * \brief Represents a generic playlist, i.e. an ordered collection of tracks - * Derived classes take care of specifics of certain media types + * Derived classes may take care of specifics of certain media types + * + * \todo Are loop mode, shuffle mode, and party mode implemented here? */ class mgPlaylist : public mgTracklist { @@ -112,7 +114,11 @@ public: //! \brief obtain the listname std::string getListname() ; - //! \brief returns the current item of the list + /*! + * \brief returns the current item of the list + * + * \todo Return null in case of an empty list or invalid index + */ virtual mgContentItem* getCurrent(); /*! \brief set the listname @@ -130,10 +136,18 @@ public: */ virtual void gotoPosition(unsigned int position); - //! \brief proceeds to the next item + /*! + * \brief proceeds to the next item + * + * \todo Handle loop mode + */ virtual void skipFwd(); - //! \brief goes back to the previous item + /*! + * \brief goes back to the previous item + * + * \todo Handle loop mode + */ virtual void skipBack(); //! \brief obtain the next item without skipping the current position -- cgit v1.2.3 From 63b6bd44d76c0d4c9c2177ca129aaa8fd3dc9f44 Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 22 Sep 2004 13:40:13 +0000 Subject: Next steps towards release candidate documented git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@172 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 7b4a54f..874c421 100644 --- a/TODO +++ b/TODO @@ -10,11 +10,20 @@ \section urgent Urgent issues \subsection bugs Bugs and testing needed - + + - Playlist indexing not correct + - Player jumps e.g. from track 1 to track 3 + - Make sure jumping beyond the end of the list is not permitted - Test execution of playlist commands - - Test Save/Rename/Load of playlist - Plugin crashes when selecting entries with special characters - Escape query strings correctly + - Plugin crashes when deleting playlist while playing + - should stop playing immediately + - Check deletion of entries while playing + - only allowed, if item is not currently played + - adapt index in playlist + - adapt playlist display + - Possible to resume play instead of restarting list from the beginning - Do not allow playlist deletion while playing or stop or similar - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) @@ -222,5 +231,7 @@ and made concrete in subclasses (mgGdTrack) - mgDecoders::findDecoder: extend decoder detection - Check compatibility for 1.3.12 (DONE) + - Test Save/Rename/Load of playlist (DONE) + */ \ No newline at end of file -- cgit v1.2.3 From 0ff09355725ac5cc52124b0d29472ca30135430c Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 24 Sep 2004 06:07:55 +0000 Subject: Improved playlist handling (skipping) git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@173 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 22 +++++++++++------- mg_playlist.c | 56 +++++++++++++++++++++++++++++++------------- vdr_player.c | 75 ++++++++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 110 insertions(+), 43 deletions(-) diff --git a/TODO b/TODO index 874c421..859774c 100644 --- a/TODO +++ b/TODO @@ -76,14 +76,19 @@ - generate a random number 0..n-1 - move corresponding playlist item to front - continue - - Party mode (see iTunes) - - generate a random uid - - if file exists: - - determine maximum playcount of all tracks - - generate a random number n - - if n < playcount / max. playcount - - play the file - + - Party mode (see iTunes) + - initialization + - find 15 titles according to the scheme below + - playing + - before entering next title perform track selection + - track selection + - generate a random uid + - if file exists: + - determine maximum playcount of all tracks + - generate a random number n + - if n < playcount / max. playcount + - add the file to the end of the list + \subsection urgentplayer Player extensions - Determine max. framecount (needed for rewinding)? @@ -175,6 +180,7 @@ - Display covers - Add flac decoder + - Handle recoding samplerate, limiter etc correctly \section vision Long term ideas and visions diff --git a/mg_playlist.c b/mg_playlist.c index 83bdbd8..82df4cb 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -1,11 +1,11 @@ /*! * \file mg_playlist.c - * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase + * \brief defines functions to be executed on playlists for the vdr muggle plugin * * \version $Revision: 1.6 $ - * \date $Date: 2004/07/27 20:50:54 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * * This file implements the class mgPlaylist which maintains a playlist * and supports editing (e.g. adding or moving tracks), navigating it @@ -17,6 +17,7 @@ #include "mg_tools.h" #include +#include using namespace std; @@ -84,14 +85,14 @@ void mgPlaylist::appendList( vector *tracks ) /* add a song after 'position' */ void mgPlaylist::insert( mgContentItem* item, unsigned int position ) { - if( position >= m_list.size() ) - { - m_list.push_back(item); - } - else - { - m_list.insert( m_list.begin() + position, item ); - } + if( position >= m_list.size() ) + { + m_list.push_back(item); + } + else + { + m_list.insert( m_list.begin() + position, item ); + } } void mgPlaylist::clear() @@ -107,6 +108,9 @@ void mgPlaylist::clear() // finally clear the list itself m_list.clear(); + + // reset index + m_current_idx = 0; } void mgPlaylist::move( int from, int to ) @@ -116,6 +120,15 @@ void mgPlaylist::move( int from, int to ) m_list.insert( to_iter, *from_iter); m_list.erase( from_iter ); + + if( from < m_current_idx ) + { + m_current_idx--; + } + if( to < m_current_idx ) + { + m_current_idx++; + } } /*==== access tracks ====*/ @@ -135,18 +148,28 @@ int mgPlaylist::count() return m_list.size(); } -// returns the first item of the list +// returns the current item of the list mgContentItem* mgPlaylist::getCurrent() { - return *( m_list.begin() + m_current_idx ); + mgContentItem *result; + + if( 0 <= m_current_idx && m_current_idx < m_list.size() ) + { + result = *( m_list.begin() + m_current_idx ); + } + else + { + result = &(mgContentItem::UNDEFINED); + } + + return result; } -// returns the nth track from the playlist +// returns the nth track from the playlist void mgPlaylist::gotoPosition(unsigned int position) { if( position >= m_list.size() ) { - // TODO: why not return a NULL pointer? LVW m_current_idx = -1; } else @@ -160,7 +183,8 @@ void mgPlaylist::skipFwd() { if( m_current_idx + 1 < m_list.size() ) // unless loop mode { - m_current_idx ++; + m_current_idx ++; + cout << "mgPlaylist::skipFwd: " << m_current_idx << endl; } else { diff --git a/vdr_player.c b/vdr_player.c index 6161dd9..32ca216 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -363,22 +363,24 @@ void mgPCMPlayer::Action(void) m_index = 0; m_playing = m_current; - string filename = m_playing->getSourceFile(); - mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); + if( m_playing && m_playing != &(mgContentItem::UNDEFINED) ) + { + string filename = m_playing->getSourceFile(); + // mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); - if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() ) - { - levelgood = true; - haslevel = false; - - scale.Init(); - level.Init(); - - m_state = msDecode; + if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() ) + { + levelgood = true; + haslevel = false; - break; - } - + scale.Init(); + level.Init(); + + m_state = msDecode; + + break; + } + } m_state = msEof; } break; case msDecode: @@ -673,17 +675,52 @@ void mgPCMPlayer::StopPlay() bool mgPCMPlayer::NextFile() { + mgContentItem *newcurr; + bool res = false; - - m_playlist->skipFwd(); - mgContentItem *newcurr = m_playlist->getCurrent(); + bool m_partymode = false; + bool m_shufflemode = false; + if( m_partymode ) + { + /* + - Party mode (see iTunes) + - initialization + - find 15 titles according to the scheme below + - playing + - before entering next title perform track selection + - track selection + - generate a random uid + - if file exists: + - determine maximum playcount of all tracks + - generate a random number n + - if n < playcount / max. playcount + - add the file to the end of the list + */ + } + else if( m_shufflemode ) + { + /* + - Handle shuffle mode in mgPlaylist + - for next file: + - generate a random number 0..n-1 + - move corresponding playlist item to front + - continue + */ + + } + else + { + m_playlist->skipFwd(); + newcurr = m_playlist->getCurrent(); + } + if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) { m_current = newcurr; res = true; } - + return res; } @@ -735,7 +772,7 @@ void mgPCMPlayer::Play(void) Lock(); - if( m_playmode != pmPlay && m_current ) + if( m_playmode != pmPlay && m_current && m_current != &(mgContentItem::UNDEFINED) ) { if( m_playmode == pmStopped ) { -- cgit v1.2.3 From 3eeac149c434d4e30eb9bf5530334184b60ee6c2 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 25 Sep 2004 12:38:59 +0000 Subject: Documentation enhanced git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@174 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_content_interface.h | 172 ++++++++++++++++++++++++++++++------------------- 1 file changed, 105 insertions(+), 67 deletions(-) diff --git a/mg_content_interface.h b/mg_content_interface.h index 44658e8..6bb5d13 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -30,8 +30,10 @@ class mgFilter; class mgPlaylist; /*! - * \class mgMediaPlayer * \brief dummy player class + * \ingroup muggle + * + * \todo what to do with this */ class mgMediaPlayer { @@ -47,79 +49,101 @@ class mgMediaPlayer /*! * \brief Generic class that represents a single content item. * - * This is the parent class from which classes like mgGdTrack are derived - * + * This is the parent class from which classes like mgGdTrack are derived. + * */ class mgContentItem { public: + + /*! + * \brief defines the content type of the item + * \todo rethink this mechanism because adding new subclasses + * breaks existing ones (makes recompile cycle necessary). + */ typedef enum contentType { - ABSTRACT =0, - GD_AUDIO, - EPG + ABSTRACT =0, //< an abstract item which cannot be used + GD_AUDIO, //< a GiantDisc audio track + EPG //< an EPG item (i.e. a TV show) }; protected: - int m_uniqID; // internal identifier to uniquely identify a content item; + + /*! + * \brief internal identifier to uniquely identify a content item; + */ + int m_uniqID; public: - - /*! \brief default constructor + + /*! + * \brief default constructor */ mgContentItem() : m_uniqID( -1 ) { } - /*! \brief constructor with explicit id + /*! + * \brief constructor with explicit id */ mgContentItem( int id ) : m_uniqID( id ) { } - /*! \brief copy constructor + /*! + * \brief copy constructor */ mgContentItem( const mgContentItem& org ) : m_uniqID( org.m_uniqID ) { } - /*! \brief destructor + /*! + * \brief the destructor */ virtual ~mgContentItem() { }; - /*! \brief acess unique id + /*! + * \brief acess unique id */ int getId() { return m_uniqID; } - - /*! \brief determine what type of content are we looking at (e.g. audio, video, epg) + + /*! + * \brief determine what type of content are we looking at (e.g. audio, video, epg) + * + * The method should be overriden for concrete subclasses to return concrete a contentType. */ virtual contentType getContentType() { return ABSTRACT; } - - /*! \brief return a (global?) object that is used to play content items - * \todo What for? Interesting properties? Last state, play info, ...? + + /*! + * \brief return a (global?) object that is used to play content items + * \todo What for? Interesting properties? Last state, play info, ...? */ virtual mgMediaPlayer getPlayer() { return mgMediaPlayer(); } + //! \brief Access item data //@{ - /*! \brief return a specific label + /*! + * \brief return a specific label */ virtual std::string getLabel(int col = 0) { return ""; } - /*! \brief return the title + /*! + * \brief return the title */ virtual std::string getTitle() { @@ -210,6 +234,9 @@ class mgContentItem }; +/*! + * \brief a list of content items + */ class mgTracklist { protected: @@ -218,23 +245,79 @@ class mgTracklist int sorting; public: - mgTracklist(); // creates empty tracklist; + + /*! + * \brief constructor + * + * create an empty tracklist + */ + mgTracklist(); + /*! + * \brief the destructor + * + * Deletes all items in the tracklist and removes the list itself + */ virtual ~mgTracklist(); + /*! + * \brief returns a pointer to the list of elements + */ std::vector *getAll(); + + /*! + * \brief returns the number of elements in the list + */ unsigned int getNumItems(); + /*! + * \brief randomizes the order of the elements in the list + */ virtual void shuffle(); + + /*! + * \brief sorts the elements in the list by the nth column + */ virtual void sortBy(int col, bool direction); + /*! + * \brief stores the ids of columns to be used in label creation + * + * The list can create a label with different fields (columns) using the + * function getLabel(). This function defines the fields of the contentItems + * to be used in the label and their order. + */ void setDisplayColumns(std::vector cols); + + /*! + * \brief returns the number of display columns + */ unsigned int getNumColumns(); + + /*! + * \brief creates the label string for an item + * + * The list can create a label with different fields (columns). + * The fields used in the list and their order is set using the function setDisplayColumns. + * + * This function creates a string from these columns, separated by the string + * 'separator' in the label and their order. + */ virtual std::string getLabel(unsigned int position, const std::string separator); + /*! + * \brief returns an item from the list at the specified position + */ virtual mgContentItem* mgTracklist::getItem(unsigned int position); + /*! + * \brief remove item at position + */ virtual int remove(mgContentItem* item); // remove all occurences of item + + /*! + * \brief remove all occurences of item + */ virtual bool remove(unsigned int position); // remove item at position }; @@ -302,49 +385,4 @@ public: virtual mgContentItem* getSingleTrack()=0; }; -/* -------------------- begin CVS log --------------------------------- - * $Log: mg_content_interface.h,v $ - * Revision 1.4 2004/05/28 15:29:18 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.3.2.4 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.3.2.3 2004/04/01 21:35:32 lvw - * Minor corrections, some debugging aid. - * - * Revision 1.3.2.2 2004/03/08 21:42:22 lvw - * Added count method. Some comments for further todos added. - * - * Revision 1.3.2.1 2004/03/08 07:14:28 lvw - * Preliminary changes to muggle player - * - * Revision 1.3 2004/02/09 19:27:52 MountainMan - * filter set implemented - * - * Revision 1.2 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ - -#endif /* END _CONTENT_INTERFACE_H */ - - - - - - - - - - - - - - - - - - +#endif -- cgit v1.2.3 From 9e1d6c39975a44844f36c101d363f3aa95e17614 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 25 Sep 2004 12:39:37 +0000 Subject: Documentation enhanced git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@175 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_content_interface.c | 186 +++++++++++-------------------------------------- 1 file changed, 42 insertions(+), 144 deletions(-) diff --git a/mg_content_interface.c b/mg_content_interface.c index 65a564f..5b78079 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -12,7 +12,6 @@ * - GdTracklist a playlist * - mgGdTrack a single track (content item). e.g. an mp3 file * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) - * */ #define DEBUG @@ -21,26 +20,16 @@ #define DUMMY -/* constructor */ +//! \brief a special item representing an undefined state mgContentItem mgContentItem::UNDEFINED = mgContentItem(); using namespace std; -/*! - * \brief constructor - * - * create an empty tracklist - */ mgTracklist::mgTracklist() { } -/*! - * \brief destructor - * - * Deletes all items in the tracklist and removes the list itself - */ mgTracklist::~mgTracklist() { mgContentItem* ptr; @@ -54,77 +43,35 @@ mgTracklist::~mgTracklist() m_list.clear(); } -/*! - * \brief returns a pointer to the list of elements - */ vector *mgTracklist::getAll() { return &m_list; } -/*! - * \brief returns the number of elements in the list - */ unsigned int mgTracklist::getNumItems() { return m_list.size(); } -/*! - ***************************************************************************** - * \brief randomizes the order of the elements in the list - * - ****************************************************************************/ void mgTracklist::shuffle() { random_shuffle(m_list.begin(),m_list.end ()); } -/*! - ***************************************************************************** - * \brief sorts the elements in the list by the nth column - * - ****************************************************************************/ void mgTracklist::sortBy(int col, bool direction) { } -/*! - ***************************************************************************** - * \brief stores the ids of columns to be used in label creation - * - * The list can create a label with different fields (columns) using the - * function getLabel() - * This function defines the fields of the contentItems to be used - * in the label and their order - ****************************************************************************/ void mgTracklist::setDisplayColumns(vector cols) { m_columns = cols; } -/*! - ***************************************************************************** - * \brief returns the number of dsplay coulmns - * - ****************************************************************************/ unsigned int mgTracklist::getNumColumns() { return m_columns.size(); } - -/*! - ***************************************************************************** - * \brief creates the label string for an item - * - * The list can create a label with different fields (columns). - * The fields used in the list and their order is set using the function setDisplayColumns - * - * This function creates a string from these columns, separated by the string - * 'separator' - * in the label and their order - ****************************************************************************/ string mgTracklist::getLabel(unsigned int position, const string separator) { string label = ""; @@ -154,41 +101,32 @@ string mgTracklist::getLabel(unsigned int position, const string separator) return label; } - -/*! - ***************************************************************************** - * \brief returns an item from the list at the specified position - * - ****************************************************************************/ mgContentItem* mgTracklist::getItem(unsigned int position) { - if(position >= m_list.size()) - return &(mgContentItem::UNDEFINED); //invalid - return *(m_list.begin()+position); + if( position >= m_list.size() ) + { + return &(mgContentItem::UNDEFINED); //invalid + } + return *( m_list.begin() + position); } -/*! - ***************************************************************************** - * \brief remove item at position - * - ****************************************************************************/ bool mgTracklist::remove(unsigned int position) { - if( position >= m_list.size() ) - { - return false; - } - vector::iterator iter; - - iter = m_list.begin()+ position; - m_list.erase(iter); + bool result = false; - return true; + if( position < m_list.size() ) + { + vector::iterator iter; + + iter = m_list.begin() + position; + m_list.erase(iter); + + result = true; + } + + return result; } -/*! - * \brief remove all occurences of item - */ int mgTracklist::remove(mgContentItem* item) { int retval = 0; @@ -226,20 +164,24 @@ mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, string id, m_expanded = false; } -/*==== destructor ====*/ mgSelectionTreeNode::~mgSelectionTreeNode() { collapse(); -// _children.clear(); + // TODO - lvw + // _children.clear(); } mgSelectionTreeNode* mgSelectionTreeNode::getParent() { - if (m_view < 100 || m_level > 1) { - return m_parent; - } else { - return NULL; - } + // TODO: why 100? + if (m_view < 100 || m_level > 1) + { + return m_parent; + } + else + { + return NULL; + } } void mgSelectionTreeNode::collapse() // removes all children (recursively) @@ -248,89 +190,45 @@ void mgSelectionTreeNode::collapse() // removes all children (recursively) mgSelectionTreeNode* ptr; for(iter = m_children.begin(); iter != m_children.end();iter++) - { - ptr = *iter; - delete ptr; - } + { + ptr = *iter; + delete ptr; + } m_expanded = false; m_children.clear(); } -// access children + vector &mgSelectionTreeNode::getChildren() { - mgDebug(5," returning %d children", m_children.size()); + // mgDebug(5," returning %d children", m_children.size()); return m_children; } -// access data in current node string mgSelectionTreeNode::getID() { return m_id; } + string mgSelectionTreeNode::getLabel() { return m_label; } + string mgSelectionTreeNode::getLabel(int n) { mgSelectionTreeNode* node = this; int d = m_level; while(n < d) - { + { + // TODO: check for NULL node = node->m_parent; d--; - } + } + return node->m_label; } string mgSelectionTreeNode::getRestrictions() { - return m_restriction; + return m_restriction; } - -/* -------------------- begin CVS log --------------------------------- - * $Log: mg_content_interface.c,v $ - * Revision 1.6 2004/07/25 21:33:35 lvw - * Removed bugs in finding track files and playlist indexing. - * - * Revision 1.5 2004/05/28 15:29:18 lvw - * Merged player branch back on HEAD branch. - * - * - * Revision 1.4 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.3.2.6 2004/05/27 07:58:38 lvw - * Removed bugs in moving and removing tracks from playlists - * - * Revision 1.3.2.5 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.3.2.4 2004/05/04 16:51:53 lvw - * Debugging aids added. - * - * Revision 1.3.2.3 2004/03/08 21:42:22 lvw - * Added count method. Some comments for further todos added. - * - * Revision 1.3.2.2 2004/03/08 07:14:27 lvw - * Preliminary changes to muggle player - * - * Revision 1.3.2.1 2004/03/02 07:08:12 lvw - * 118 additions - * - * Revision 1.4 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.3 2004/02/10 23:47:23 RaK - * - views konsitent gemacht. siehe FROMJOIN - * - isLeafNode angepasst fuer neue views 4,5,100,101 - * - like '%abba%' eingebaut - * - filter ist default mit abba gefuellt, zum leichteren testen. - * - search results werden jetzt gleich im ROOT expanded - * - * Revision 1.2 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ -- cgit v1.2.3 From b9b8dd18c4c4798c168d42ce7c0d2651930f4e55 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 25 Sep 2004 12:40:40 +0000 Subject: Bug corrected which made muggle crash on displaying the tree submenu git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@176 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 094acd3..314e16a 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -164,13 +164,12 @@ eOSState mgMainMenu::ProcessKey(eKeys key) asprintf( &buffer, "%d tracks sent to current playlist", (int) tracks->size() ); m_current_playlist->appendList(tracks); #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,buffer); - Skins.Flush(); + Skins.Message(mtInfo,buffer); + Skins.Flush(); #else Interface->Status( buffer ); Interface->Flush(); -#endif - +#endif free( buffer ); } else @@ -531,10 +530,14 @@ void mgMainMenu::DisplayTreeSubmenu() Clear(); SetButtons(); - SetTitle( strcat("Muggle - ",tr("Tree View Commands") ) ); + + char *buffer; + asprintf( &buffer, "Muggle - %s", tr("Tree View Commands") ); + SetTitle( buffer ); + free( buffer ); // Add items - Add( new cOsdItem( "1 - Instant play" ) ); + Add( new cOsdItem( "Instant play" ) ); Display(); } @@ -567,7 +570,7 @@ eOSState mgMainMenu::TreeSubmenuAction( int n ) Play( m_current_playlist ); state = osEnd; - } + } } } break; case 1: @@ -579,7 +582,7 @@ eOSState mgMainMenu::TreeSubmenuAction( int n ) // undefined action } break; } - + return state; } @@ -781,8 +784,8 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) // confirmation #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,"Playlist renamed (dummy)"); - Skins.Flush(); + Skins.Message(mtInfo,"Playlist renamed (dummy)"); + Skins.Flush(); #else Interface->Status( "Playlist renamed (dummy)" ); Interface->Flush(); @@ -793,21 +796,22 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) case 3: { // clear playlist m_current_playlist->clear(); - + // confirmation #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,"Playlist cleared"); - Skins.Flush(); + Skins.Message(mtInfo,"Playlist cleared"); + Skins.Flush(); #else Interface->Status( "Playlist cleared" ); Interface->Flush(); #endif - + state = osContinue; } break; case 4: { // remove selected title - m_current_playlist->remove( m_last_osd_index ); + bool res = m_current_playlist->remove( m_last_osd_index ); + if( m_last_osd_index > 0 ) { m_last_osd_index --; @@ -815,11 +819,13 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) DisplayPlaylist( m_last_osd_index ); // confirmation + string confirm = res? "Entry deleted": "Cannot delete entry"; + #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,"Entry removed"); - Skins.Flush(); + Skins.Message( mtInfo, confirm.c_str() ); + Skins.Flush(); #else - Interface->Status( "Entry removed" ); + Interface->Status( confirm.c_str() ); Interface->Flush(); #endif } break; -- cgit v1.2.3 From e88482d287b0d0628462b2478a8be5f1cf2a223c Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 25 Sep 2004 12:41:19 +0000 Subject: Playlist indexing enhanced git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@177 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 33 +++++++++++++++++++++------------ mg_playlist.h | 2 +- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index 82df4cb..6da5905 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -95,6 +95,23 @@ void mgPlaylist::insert( mgContentItem* item, unsigned int position ) } } +bool mgPlaylist::remove( unsigned int pos ) +{ + bool result = false; + + if( pos != m_current_idx ) + { + result = mgTracklist::remove( pos ); + + if( result && pos < m_current_idx ) + { + m_current_idx --; + } + } + + return result; +} + void mgPlaylist::clear() { // TODO: who takes care of memory allocation/deallocation of mgItems? @@ -170,7 +187,8 @@ void mgPlaylist::gotoPosition(unsigned int position) { if( position >= m_list.size() ) { - m_current_idx = -1; + // go to end -- a safe bet + m_current_idx = m_list.size() - 1; } else { @@ -184,13 +202,8 @@ void mgPlaylist::skipFwd() if( m_current_idx + 1 < m_list.size() ) // unless loop mode { m_current_idx ++; - cout << "mgPlaylist::skipFwd: " << m_current_idx << endl; - } - else - { - // or goto 1 in case of loop mode - m_current_idx = -1; } + // if we are already at the end -- just do nothing unless in loop mode } // goes back to the previous item @@ -200,11 +213,7 @@ void mgPlaylist::skipBack() { m_current_idx --; } - else - { - // or goto last in case of loop mode - m_current_idx = -1; - } + // if we are at the beginning -- just do nothing unless in loop mode } // get next track, do not update data structures diff --git a/mg_playlist.h b/mg_playlist.h index a46d29d..addf52e 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -107,7 +107,7 @@ public: * * \param pos - the index of the track to be removed */ - // bool remove( int pos ); + bool remove( unsigned int pos ); /* ==== access tracks ==== */ -- cgit v1.2.3 From 292388a7cf2234486c97b6ea0927edaa46c24f8e Mon Sep 17 00:00:00 2001 From: rak Date: Sat, 25 Sep 2004 13:44:06 +0000 Subject: Added missing includes and namespace qualifier git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@178 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mugglei.c b/mugglei.c index 3db2f04..c7206bd 100755 --- a/mugglei.c +++ b/mugglei.c @@ -9,8 +9,10 @@ using namespace std; #include -#include -#include +#include + +#include +#include #include "mg_tools.h" @@ -106,7 +108,7 @@ void update_db( long uid, string filename ) trackno = tag->track(); genre = tag->genre(); - AudioProperties *ap = f.audioProperties(); + TagLib::AudioProperties *ap = f.audioProperties(); int len = ap->length(); // tracks.length int bitrate = ap->bitrate(); // tracks.bitrate int sample = ap->sampleRate(); //tracks.samplerate -- cgit v1.2.3 From 44a755e932b67f1d0d5c53a8383367b3785bbc16 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 25 Sep 2004 14:16:29 +0000 Subject: Playlists seem now to work correctly git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@181 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index 6da5905..3c7dae0 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -183,37 +183,47 @@ mgContentItem* mgPlaylist::getCurrent() } // returns the nth track from the playlist -void mgPlaylist::gotoPosition(unsigned int position) +bool mgPlaylist::gotoPosition(unsigned int position) { - if( position >= m_list.size() ) - { - // go to end -- a safe bet - m_current_idx = m_list.size() - 1; - } - else - { - m_current_idx = position; - } + bool result = false; + + if( position < m_list.size() ) + { + m_current_idx = position; + result = true; + } + + return result; } // proceeds to the next item -void mgPlaylist::skipFwd() +bool mgPlaylist::skipFwd() { + bool result = false; + if( m_current_idx + 1 < m_list.size() ) // unless loop mode { m_current_idx ++; + result = true; } + // if we are already at the end -- just do nothing unless in loop mode + return result; } // goes back to the previous item -void mgPlaylist::skipBack() +bool mgPlaylist::skipBack() { + bool result = false; + if( m_current_idx > 0 ) { m_current_idx --; + result = true; } + // if we are at the beginning -- just do nothing unless in loop mode + return result; } // get next track, do not update data structures -- cgit v1.2.3 From 5879bd13d018fa9074e304507d3eb0f26a27e2a7 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 25 Sep 2004 14:17:11 +0000 Subject: Playlists seem now to work correctly git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@182 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.h | 13 ++++++++----- vdr_player.c | 49 ++++++++++++++++++++++++++++--------------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/mg_playlist.h b/mg_playlist.h index addf52e..30443f4 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -133,22 +133,25 @@ public: /*! \brief returns the nth track from the playlist * * \param position - the position to skip to + * \return true if position was okay and changed, false otherwise */ - virtual void gotoPosition(unsigned int position); + virtual bool gotoPosition(unsigned int position); /*! * \brief proceeds to the next item * - * \todo Handle loop mode + * \return true if position was okay and changed, false otherwise + * \todo Handle play modes */ - virtual void skipFwd(); + virtual bool skipFwd(); /*! * \brief goes back to the previous item * - * \todo Handle loop mode + * \return true if position was okay and changed, false otherwise + * \todo Handle play modes */ - virtual void skipBack(); + virtual bool skipBack(); //! \brief obtain the next item without skipping the current position virtual mgContentItem* sneakNext(); diff --git a/vdr_player.c b/vdr_player.c index 32ca216..c2a65ca 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -168,10 +168,10 @@ private: // int m_index; - void Empty(void); - bool NextFile(void); - bool PrevFile(void); - void StopPlay(void); + void Empty(); + bool NextFile( ); + bool PrevFile(); + void StopPlay(); void SetPlayMode(ePlayMode mode); void WaitPlayMode(ePlayMode mode, bool inv); @@ -184,11 +184,11 @@ public: mgPCMPlayer(mgPlaylist *plist); virtual ~mgPCMPlayer(); - bool Active(void) { return m_active; } - void Pause(void); - void Play(void); - void Forward(void); - void Backward(void); + bool Active() { return m_active; } + void Pause(); + void Play(); + void Forward(); + void Backward(); void Goto(int Index, bool Still=false); void SkipSeconds(int secs); void ToggleShuffle(void); @@ -673,7 +673,7 @@ void mgPCMPlayer::StopPlay() } } -bool mgPCMPlayer::NextFile() +bool mgPCMPlayer::NextFile( ) { mgContentItem *newcurr; @@ -707,12 +707,17 @@ bool mgPCMPlayer::NextFile() - move corresponding playlist item to front - continue */ - } else { - m_playlist->skipFwd(); - newcurr = m_playlist->getCurrent(); + if( m_playlist->skipFwd() ) + { + newcurr = m_playlist->getCurrent(); + } + else + { + newcurr = &(mgContentItem::UNDEFINED); + } } if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) @@ -727,14 +732,16 @@ bool mgPCMPlayer::NextFile() bool mgPCMPlayer::PrevFile(void) { bool res = false; - - m_playlist->skipBack(); - mgContentItem *newcurr = m_playlist->getCurrent(); - if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) + if( m_playlist->skipBack() ) { - m_current = newcurr; - res = true; + mgContentItem *newcurr = m_playlist->getCurrent(); + + if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) + { + m_current = newcurr; + res = true; + } } return res; @@ -784,12 +791,12 @@ void mgPCMPlayer::Play(void) Unlock(); } -void mgPCMPlayer::Forward(void) +void mgPCMPlayer::Forward() { MGLOG( "mgPCMPlayer::Forward" ); Lock(); - if( NextFile() ) + if( NextFile() ) { StopPlay(); Play(); -- cgit v1.2.3 From def6f649cda5f35306edfc7ee0f6c02746711a12 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 25 Sep 2004 14:17:59 +0000 Subject: Avoid crashing by prohibiting playlist deletion while playing git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@183 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 314e16a..b4b2ab9 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -795,14 +795,27 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) } break; case 3: { // clear playlist - m_current_playlist->clear(); + + cControl *control = cControl::Control(); + string buffer; + + if( control && typeid(*control) == typeid(mgPlayerControl) ) + { + buffer = "Cannot clear playlist while playing."; + } + else + { + m_current_playlist->clear(); + + buffer = "Playlist cleared"; + } // confirmation #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,"Playlist cleared"); + Skins.Message( mtInfo, buffer.c_str() ); Skins.Flush(); #else - Interface->Status( "Playlist cleared" ); + Interface->Status( buffer.c_str() ); Interface->Flush(); #endif @@ -944,12 +957,10 @@ void mgMainMenu::Play(mgPlaylist *plist) if( control && typeid(*control) == typeid(mgPlayerControl) ) { // is there a running MP3 player? - cout << "mgMainMenu::Play: signal new playlist to existing control" << endl; static_cast(control)->NewPlaylist(plist); // signal the running player to load the new playlist } else { - cout << "mgMainMenu::Play: starting new control" << endl; cControl::Launch( new mgPlayerControl(plist) ); } } -- cgit v1.2.3 From 229e88f00d2914b5da72991bbd34d25e7149b589 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 27 Sep 2004 12:55:55 +0000 Subject: Code reviewed and documented git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@184 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_content_interface.c | 63 ++++++++-------- mg_content_interface.h | 195 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 171 insertions(+), 87 deletions(-) diff --git a/mg_content_interface.c b/mg_content_interface.c index 5b78079..3f502ab 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -6,25 +6,23 @@ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author$ * - * Implements main classes of for content items and interfaces to SQL databases + * Implements main classes of for content items and abstract interfaces to media access * * This file implements the following classes - * - GdTracklist a playlist - * - mgGdTrack a single track (content item). e.g. an mp3 file - * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * - mgContentItem + * - mgTracklist + * - mgSelectionTreeNode */ #define DEBUG #include "mg_content_interface.h" #include "mg_tools.h" -#define DUMMY +using namespace std; //! \brief a special item representing an undefined state mgContentItem mgContentItem::UNDEFINED = mgContentItem(); -using namespace std; - mgTracklist::mgTracklist() { @@ -55,7 +53,7 @@ unsigned int mgTracklist::getNumItems() void mgTracklist::shuffle() { - random_shuffle(m_list.begin(),m_list.end ()); + random_shuffle( m_list.begin(), m_list.end () ); } void mgTracklist::sortBy(int col, bool direction) @@ -86,8 +84,6 @@ string mgTracklist::getLabel(unsigned int position, const string separator) item = *( m_list.begin() + position ); } - mgDebug( 1, "mgTracklist::getLabel: Starting to iterate columns." ); - for( vector::iterator iter = m_columns.begin(); iter != m_columns.end(); iter++ ) { @@ -95,7 +91,6 @@ string mgTracklist::getLabel(unsigned int position, const string separator) { label += separator; } - mgDebug( 1, "mgTracklist::getLabel: obtaining label from item %d", *iter ); label += item->getLabel(*iter); } return label; @@ -129,18 +124,19 @@ bool mgTracklist::remove(unsigned int position) int mgTracklist::remove(mgContentItem* item) { - int retval = 0; - vector::iterator iter; - for(iter=m_list.begin(); iter != m_list.end (); iter++) + int retval = 0; + vector::iterator iter; + + for( iter = m_list.begin(); iter != m_list.end (); iter++ ) { - if(*iter == item) - { - m_list.erase(iter); - retval++; - break; - } + if( *iter == item ) + { + m_list.erase(iter); + retval++; + break; + } } - return retval; + return retval; } mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view) @@ -173,7 +169,6 @@ mgSelectionTreeNode::~mgSelectionTreeNode() mgSelectionTreeNode* mgSelectionTreeNode::getParent() { - // TODO: why 100? if (m_view < 100 || m_level > 1) { return m_parent; @@ -206,26 +201,26 @@ vector &mgSelectionTreeNode::getChildren() string mgSelectionTreeNode::getID() { - return m_id; + return m_id; } string mgSelectionTreeNode::getLabel() { - return m_label; + return m_label; } string mgSelectionTreeNode::getLabel(int n) { - mgSelectionTreeNode* node = this; - int d = m_level; - while(n < d) - { - // TODO: check for NULL - node = node->m_parent; - d--; - } - - return node->m_label; + mgSelectionTreeNode* node = this; + int d = m_level; + while(n < d) + { + // TODO: check for NULL + node = node->m_parent; + d--; + } + + return node->m_label; } string mgSelectionTreeNode::getRestrictions() diff --git a/mg_content_interface.h b/mg_content_interface.h index 6bb5d13..35eb7cd 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -9,7 +9,7 @@ * Declares generic classes of for content items and interfaces to SQL databases * * This file defines the following classes - * - mgMediaMplayer + * - mgMediaPlayer * - mgContentItem * - mgTracklist * - mgSelectionTreeNode @@ -236,6 +236,9 @@ class mgContentItem /*! * \brief a list of content items + * \ingroup muggle + * + * \todo check, whether this class really needs a current item etc. */ class mgTracklist { @@ -312,6 +315,8 @@ class mgTracklist /*! * \brief remove item at position + * + * \todo needed? if so, it hides bool remove(int) */ virtual int remove(mgContentItem* item); // remove all occurences of item @@ -322,67 +327,151 @@ class mgTracklist }; /*! - * \brief represent a node in a tree of selections - * \ingroup muggle + * \brief represent a node in a tree of selections + * \ingroup muggle + * + * The class represents a tree representation. Each node can have a parent node and + * an arbitrary number of children nodes. */ class mgSelectionTreeNode { + protected: -protected: - MYSQL m_db; - bool m_expanded; // already expanded ? - std::string m_restriction; // list of active restrictions at this level - std::string m_id; // ID of the node, used for further expand - int m_level; // depth of tree (0 = root) - int m_view; - std::string m_label; - -// std::vector _labels; // Labels used for interaction with the user - // about this node + /*! + * \brief the database in which a node is stored + * \todo should this be in the authority of concrete subclasses? + */ + MYSQL m_db; -// vector _children; // if expanded the links to the - // children are stopred here - mgSelectionTreeNode* m_parent; - std::vector m_children; - -public: + //! \brief maintain a flag, whether the node is currently expanded + bool m_expanded; - /*==== constructors ====*/ - mgSelectionTreeNode(MYSQL db, int view); + //! \brief list of active restrictions at this level + std::string m_restriction; - mgSelectionTreeNode(mgSelectionTreeNode* parent, std::string id, std::string label); + //! \brief depth of node in the tree (0 = root) + int m_level; + + //! \brief unknown + int m_view; + + //! \brief ID of the node, used for further expand + std::string m_id; + + //! \brief label of the node, used for user interaction + std::string m_label; - /*==== destructor ====*/ - virtual ~mgSelectionTreeNode(); - - // compute children on the fly - virtual bool isLeafNode()=0; - virtual bool expand()=0; - virtual void collapse(); // removes all children (recursively) - - mgSelectionTreeNode* getParent(); - - // access children - virtual std::vector &getChildren(); - - // access data in current node - bool isExpanded(){return m_expanded;} - int getLevel(){return m_level;} // for debugging - std::string getID(); - virtual std::string getRestrictions(); - - std::string getLabel(); - virtual std::string getLabel(int n); - #if 0 - virtual std::string viewTitle(int level)=0; - virtual std::vector viewChoices(int level, int choice); -#endif + //! \brief parent of this node + mgSelectionTreeNode* m_parent; + + //! \brief hold the set of immediate children if expanded, empty if collapsed + std::vector m_children; - // returns all tracks below this node - // Note: This function allocates memory for the vector and for all elements of the vector - // The calling function is in charge of releasing this memory - virtual std::vector* getTracks()=0; - virtual mgContentItem* getSingleTrack()=0; + public: + + //! \brief Object lifecycle management + //@{ + + /*! + * \brief a constructor for an empty node + */ + mgSelectionTreeNode(MYSQL db, int view); + + /*! + * \brief a constructor for a node with a parent + */ + mgSelectionTreeNode(mgSelectionTreeNode* parent, std::string id, std::string label); + + /*! + * \brief the destructor + */ + virtual ~mgSelectionTreeNode(); + + //@} + + //! \brief expand and collapse tree + //@{ + + /*! + * \brief whether the node is a leaf (i.e. has no more children) + */ + virtual bool isLeafNode() = 0; + + /*! + * \brief expand the node + * + * The method will obtain all its children node, e.g. from a database + */ + virtual bool expand() = 0; + + /*! + * \brief collapse all children nodes + * + * The method will collapse the subtree below this node and + * destroy all children node objects. + */ + virtual void collapse(); // removes all children (recursively) + + /*! + * \brief obtain parent node + * + * \todo what is that magic number 100 for in the implementation? + */ + mgSelectionTreeNode* getParent(); + + /*! + * \brief access direct children of the node + */ + virtual std::vector &getChildren(); + + /*! + * \brief returns all tracks which are children of this node (transitive closure!) + * + * This function allocates memory for the vector and for all elements of the vector + * The calling function is in charge of releasing this memory + */ + virtual std::vector* getTracks() = 0; + + /*! + * \brief obtain a single track + */ + virtual mgContentItem* getSingleTrack() = 0; + + bool isExpanded() + { return m_expanded; } + + int getLevel() + { return m_level; } + + //@} + + //! \brief obtain node information + //@{ + + /*! + * \brief obtain the ID of this node + */ + std::string getID(); + + /*! + * \brief obtain the label of this node + */ + virtual std::string getLabel(int n); + + /*! + * \brief obtain the label from the topmost parent of this node + */ + std::string getLabel(); + + /*! + * \brief obtain a SQL restriction + * + * The restriction returned is part of a SQL query string which will restrict + * results to nodes that belong to the set of items grouped by this node + virtual std::string getRestrictions(); + + //@} + }; #endif -- cgit v1.2.3 From eb9e6e35d36dee3011bfc2a67a5b8bb9138a259e Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 27 Sep 2004 22:14:02 +0000 Subject: Syntax error corrected git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@185 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_content_interface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mg_content_interface.h b/mg_content_interface.h index 35eb7cd..d8bb0ec 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -468,6 +468,7 @@ class mgSelectionTreeNode * * The restriction returned is part of a SQL query string which will restrict * results to nodes that belong to the set of items grouped by this node + */ virtual std::string getRestrictions(); //@} -- cgit v1.2.3 From 0c95d9079fc11e8b07a03e92ea2483456a696215 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 27 Sep 2004 22:14:37 +0000 Subject: Status display for VDR 1.3.12 added (buggy) git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@186 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index c2a65ca..713ae18 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -185,19 +185,23 @@ public: virtual ~mgPCMPlayer(); bool Active() { return m_active; } + void Pause(); void Play(); void Forward(); void Backward(); + void Goto(int Index, bool Still=false); void SkipSeconds(int secs); void ToggleShuffle(void); void ToggleLoop(void); + virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false); // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW + void NewPlaylist(mgPlaylist *plist); - mgContentItem *GetCurrent () { return m_current; } - }; + mgContentItem *GetCurrent () { return m_current; } +}; mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist) : cPlayer( the_setup.BackgrMode? pmAudioOnly: pmAudioOnlyBlack ) @@ -851,7 +855,7 @@ void mgPCMPlayer::SkipSeconds(int secs) bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) { bool res = false; - current = SecondsToFrames(m_index); + current = SecondsToFrames( m_index ); total = -1; return res; @@ -970,10 +974,12 @@ void mgPlayerControl::ShowProgress() { // open the osd if its not already there... #if VDRVERSNUM >= 10307 - osd = cOsdProvider::NewOsd (Setup.OSDLeft, Setup.OSDTop); - tArea Areas[] = { { 0, 0, Setup.OSDWidth, Setup.OSDHeight, 2 } }; - osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea)); - font = cFont::GetFont (fontOsd); + /* + osd = cOsdProvider::NewOsd (Setup.OSDLeft, Setup.OSDTop); + tArea Areas[] = { { 0, 0, Setup.OSDWidth, Setup.OSDHeight, 2 } }; + osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea)); + font = cFont::GetFont (fontOsd); + */ #else Interface->Open(); #endif @@ -983,14 +989,27 @@ void mgPlayerControl::ShowProgress() // now an osd is open, go on #if VDRVERSNUM >= 10307 - int w = Setup.OSDWidth; - int h = Setup.OSDHeight; - osd->DrawRectangle (0, 0, w - 1, h - 1, clrGray50); - mgContentItem *item = m_player->GetCurrent (); - string msg = item->getGenre () + " " + item->getTitle (); - osd->DrawText (0, h - font->Height () - 2, msg.c_str (), clrBlack, clrCyan, - cFont::GetFont (fontOsd), w, font->Height () + 2, taCenter); - osd->Flush(); + cSkinDisplayReplay *m_display; + + m_display = Skins.Current()->DisplayReplay(false); + if( m_player && m_display ) + { + int current_frame, total_frames; + m_player->GetIndex( current_frame, total_frames ); + + m_display->SetProgress( current_frame, total_frames ); + m_display->SetCurrent( IndexToHMSF( current_frame ) ); + m_display->SetTotal( IndexToHMSF( total_frames ) ); + + char *buf; + asprintf( &buf, "%s", m_player->GetCurrent()->getTitle().c_str() ); + m_display->SetTitle( buf ); + free( buf ); + + bool play = true, forward = true; + int speed = -1; + m_display->SetMode( play, forward, speed ); + } #else int w = Interface->Width(); int h = Interface->Height(); -- cgit v1.2.3 From cae0381ec6de30ac7e31b3be0f703894b44722f4 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 27 Sep 2004 22:24:33 +0000 Subject: Improved status display git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@187 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdr_player.c b/vdr_player.c index 713ae18..1629a0b 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -856,7 +856,7 @@ bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) { bool res = false; current = SecondsToFrames( m_index ); - total = -1; + total = SecondsToFrames( m_current->getLength() ); return res; } -- cgit v1.2.3 From 11f256059600e71a2e20f727d8d5e1600de35a1d Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 28 Sep 2004 13:06:36 +0000 Subject: Added function to escape query strings git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@188 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_database.c | 16 ++++++++++++++-- mg_database.h | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/mg_database.c b/mg_database.c index eabff6d..b25ee23 100644 --- a/mg_database.c +++ b/mg_database.c @@ -3,9 +3,9 @@ * \brief A capsule around MySql database access ******************************************************************** * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: lvw $ + * \author file owner: $Author$ */ /*******************************************************************/ @@ -27,6 +27,18 @@ mgDB::~mgDB() { } +string mgDB::escape_string( MYSQL *db, string s ) +{ + char *escbuf = (char *) malloc( 2*s.length() + 1 ); + + int len = mysql_real_escape_string( db, escbuf, s.c_str(), s.length() ); + + string res( escbuf ); + free( resbuf ); + + return res; +} + MYSQL mgDB::getDBHandle() { diff --git a/mg_database.h b/mg_database.h index 4e00826..3f2c323 100644 --- a/mg_database.h +++ b/mg_database.h @@ -3,9 +3,9 @@ * \brief A capsule around MySql database access * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ */ #ifndef __MG_DATABASE_H @@ -14,6 +14,11 @@ #include #include +/*! + * \brief an abstract database class + * + * \todo Currently unused. This class could abstract database connection and query handling, but this will be tedious as an abstract representation of results would be needed. + */ class mgDB { public: @@ -39,6 +44,13 @@ class mgDB /*! \brief obtain database handle*/ MYSQL getDBHandle(); + + /*! + * \brief escape arguments to be contained in a query + * + * \todo use m_dbase member of this class + */ + static std::string escape_string( MYSQL *db, std::string s ); private: MYSQL m_dbase; -- cgit v1.2.3 From f8aa2ddee993d8873d0baf66c256e06db817d9ea Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 28 Sep 2004 13:07:07 +0000 Subject: TODO list extended git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@189 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 92 +++++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/TODO b/TODO index 859774c..7d603b1 100644 --- a/TODO +++ b/TODO @@ -11,28 +11,19 @@ \subsection bugs Bugs and testing needed - - Playlist indexing not correct - - Player jumps e.g. from track 1 to track 3 - - Make sure jumping beyond the end of the list is not permitted - Test execution of playlist commands + - Attach to graphlcd plugin via replay string - Plugin crashes when selecting entries with special characters - Escape query strings correctly - - Plugin crashes when deleting playlist while playing - - should stop playing immediately - - Check deletion of entries while playing - - only allowed, if item is not currently played - - adapt index in playlist - - adapt playlist display - - Possible to resume play instead of restarting list from the beginning - - Do not allow playlist deletion while playing or stop or similar + - Possible to resume play instead of restarting list from the beginning? - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) \subsection urgentosd OSD-related Issues - Add play indicators - - mgPCMPlayer::GetIndex: obtain total length from database + - mgPCMPlayer::GetIndex: obtain total length from database (DONE) - Toggle display with Ok (off -> track info -> album info -> playlist info -> off) - - Display track info + - Display track info (use vdr 1.3.12 progress display) - Song title - Artist - Genre @@ -50,7 +41,8 @@ - Next track(s) \subsection urgentcode Code polishing - + + - Clean up coding style and documentation in general - Check for unnecessary log commands - Generate HTML documentation using doxygen, - use dotty/gv for state machines of player @@ -69,13 +61,14 @@ \subsection urgentcontent Content handling - Think, whether type (mp3, ogg, flac) should be stored in database + - could be used in searching/structuring as well - Handle loop mode in mgPlaylist (already done?) - Handle shuffle mode in mgPlaylist - shuffle mode on - for next file: - - generate a random number 0..n-1 - - move corresponding playlist item to front - - continue + - generate a set of random numbers as long as the playlist + - re-generate when removing or adding entries + - in mgPlaylist::getCurrent use this additional set as a mapping - Party mode (see iTunes) - initialization - find 15 titles according to the scheme below @@ -98,13 +91,13 @@ \subsection deploy Deployment - - Script to publish a version - - make dist - - copy .tgz, README, CHANGES, HISTORY into web directory - - generate documentation - - copy into web directory - - sync with web - - How to track bugs and feature requests? + - Script to publish a version + - make dist + - copy .tgz, README, CHANGES, HISTORY into web directory + - generate documentation + - copy into web directory + - sync with web + - How to track bugs and feature requests? \verbatim # $1: version name (e.g. 0.0.5-BETA) @@ -130,25 +123,26 @@ \subsection midimport Import stuff - - Album - - Genre - - Modified - - Cover text - - Cover images (based on filename or tag) - - - Tracks - - Language (?) - - Genre1, 2 - - Rating? - - Modified, created - - Lyrics - - - Import playlist from m3u - - Run import/update from within OSD? - - Handle updates in both directions - - Check modification date in DB/fstat - - if file is newer: update tags into db - - if DB is newer: update db into tags + - Album + - Genre + - Modified + - Cover text + - Cover images (based on filename or tag) + + - Tracks + - Language (?) + - Genre1, 2 + - Rating? + - Modified, created + - Lyrics + + - Import playlist from m3u + - Run import/update from within OSD? + + - Handle updates in both directions + - Check modification date in DB/fstat + - if file is newer: update tags into db + - if DB is newer: update db into tags \subsection midcode Code issues @@ -179,7 +173,8 @@ \subsection midplayer Player issues - Display covers - - Add flac decoder + - Add FLAC decoder + - Use single CD files with cuesheets in metadata for FLAC - Handle recoding samplerate, limiter etc correctly \section vision Long term ideas and visions @@ -238,6 +233,13 @@ - mgDecoders::findDecoder: extend decoder detection - Check compatibility for 1.3.12 (DONE) - Test Save/Rename/Load of playlist (DONE) - + - Plugin crashes when deleting playlist while playing + - should stop playing immediately + - Check deletion of entries while playing + - only allowed, if item is not currently played + - adapt index in playlist + - Playlist indexing not correct + - Player jumps e.g. from track 1 to track 3 + - Make sure jumping beyond the end of the list is not permitted */ \ No newline at end of file -- cgit v1.2.3 From 24a870f6c1824281711afbc75ed30128353fa6c7 Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 28 Sep 2004 13:08:14 +0000 Subject: Formatting and documentation issues git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@190 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 197 +++++++++++++++++++++++-------------------------- mg_filters.h | 59 ++++++++------- mg_tools.c | 26 ++----- mg_tools.h | 29 ++------ mugglei.c | 63 ++++++++++------ 5 files changed, 177 insertions(+), 197 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 01e71b2..b805500 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -65,10 +65,10 @@ vector *GdGetStoredPlaylists(MYSQL db) result = mgSqlReadQuery(&db, "SELECT title FROM playlist"); - while((row = mysql_fetch_row(result)) != NULL) - { + while( (row = mysql_fetch_row(result) ) != NULL ) + { list->push_back(row[0]); - } + } return list; } @@ -311,24 +311,24 @@ bool mgGdTrack::readData() nfields = mysql_num_fields(result); if( nrows == 0 ) - { - mgWarning( "No entries found \n" ); - return false; - } + { + mgWarning( "No entries found \n" ); + return false; + } else - { - if( nrows > 1 ) - { - mgWarning("mgGdTrack::readData: More than one entry found. Using first entry."); - } + { + if( nrows > 1 ) + { + mgWarning("mgGdTrack::readData: More than one entry found. Using first entry."); + } MYSQL_ROW row = mysql_fetch_row(result); - + m_artist = row[0]; m_album = row[1]; m_title = row[2]; m_mp3file = string( the_setup.ToplevelDir ) + row[3]; m_genre = row[4]; - + if( sscanf( row[5], "%d", &m_year) != 1 ) { mgError("Invalid year '%s' in database", row [5]); @@ -542,22 +542,23 @@ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) MYSQL_ROW row; int trackid; - result = mgSqlReadQuery(&db_handle, - "SELECT tracks.id " - " FROM tracks, album, genre WHERE %s" - " AND album.cddbid=tracks.sourceid " - " AND genre.id=tracks.genre1", - restrictions.c_str()); + result = mgSqlReadQuery( &db_handle, + "SELECT tracks.id " + " FROM tracks, album, genre WHERE %s" + " AND album.cddbid=tracks.sourceid " + " AND genre.id=tracks.genre1", + restrictions.c_str() ); + while((row = mysql_fetch_row(result)) != NULL) - { - // row[0] is the trackid - if(sscanf(row[0], "%d", &trackid) != 1) - { - mgError("Can not extract integer track id from '%s'", - row[0]); - } - m_list.push_back(new mgGdTrack(trackid, db_handle)); - } + { + // row[0] is the trackid + if(sscanf(row[0], "%d", &trackid) != 1) + { + mgError("Can not extract integer track id from '%s'", + row[0]); + } + m_list.push_back( new mgGdTrack(trackid, db_handle) ); + } } GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) @@ -602,8 +603,8 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) } } - else // playlist exists, read data - { + else + { // playlist exists, read data row = mysql_fetch_row(result); if(sscanf(row [0], "%d", & m_sqlId) !=1) @@ -612,10 +613,12 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) } m_author = row[1]; m_listname = listname; + // now read allentries of the playlist and // write them into the tracklist insertDataFromSQL(); - }// end 'else (playlist exists) + + } // end 'else (playlist exists) m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists } @@ -643,20 +646,21 @@ int GdPlaylist::insertDataFromSQL() "SELECT tracknumber, trackid FROM playlistitem " "WHERE playlist = %d ORDER BY tracknumber", m_sqlId); + nrows = mysql_num_rows(result); - while((row = mysql_fetch_row(result)) != NULL) - { + while( (row = mysql_fetch_row(result) ) != NULL ) + { // add antry to tracklist if(sscanf(row[1], "%d", &id) !=1) - { + { mgWarning("Track id '%s' is not an integer. Ignoring \n", row[1]); - } + } else - { + { trackptr = new mgGdTrack(id, m_db); m_list.push_back(trackptr); - } - } + } + } return nrows; } @@ -670,44 +674,44 @@ bool GdPlaylist::storePlaylist() if(m_listname ==" ") - { + { mgWarning("Can not store Tracklist without name"); return false; - } + } if(m_sqlId >= 0) - { - // playlist alreay exists in SQL database - // remove old items first - cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; - // remove old playlist items from db - mgSqlWriteQuery(&m_db, - "DELETE FROM playlistitem WHERE playlist = %d", - m_sqlId); - } + { + // playlist alreay exists in SQL database + // remove old items first + cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; + // remove old playlist items from db + mgSqlWriteQuery(&m_db, + "DELETE FROM playlistitem WHERE playlist = %d", + m_sqlId); + } else - { - // create new database entry - mgSqlWriteQuery(&m_db, "INSERT into playlist " - "SET title=\"%s\", author=\"%s\"", - m_listname.c_str(), - "VDR", // default author - ""); // creates current time as timestamp - m_author = "VDR"; + { + // create new database entry + mgSqlWriteQuery(&m_db, "INSERT into playlist " + "SET title=\"%s\", author=\"%s\"", + m_listname.c_str(), + "VDR", // default author + ""); // creates current time as timestamp + m_author = "VDR"; - // now read thenew list to get the id - result=mgSqlReadQuery(&m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - m_listname.c_str()); - nrows = mysql_num_rows(result); - row = mysql_fetch_row(result); + // now read thenew list to get the id + result=mgSqlReadQuery(&m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + m_listname.c_str()); + nrows = mysql_num_rows(result); + row = mysql_fetch_row(result); - if(sscanf(row [0], "%d", & m_sqlId) !=1) - { - mgError("Invalid id '%s' in database", row [5]); + if(sscanf(row [0], "%d", & m_sqlId) !=1) + { + mgError("Invalid id '%s' in database", row [5]); + } } - } // add new playlist items to db - + for( iter=m_list.begin(), num=0; iter != m_list.end(); iter++, num++) @@ -719,11 +723,10 @@ bool GdPlaylist::storePlaylist() } return true; } + /*! - ***************************************************************************** * \brief returns the total duration of all songs in the list in seconds - * - ****************************************************************************/ + */ int GdPlaylist::getPlayTime() { //DUMMY @@ -733,10 +736,8 @@ int GdPlaylist::getPlayTime() } /*! - ***************************************************************************** * \brief returns the duration of all remaining songs in the list in seconds - * - ****************************************************************************/ + */ int GdPlaylist::getPlayTimeRemaining() { //DUMMY @@ -746,19 +747,9 @@ int GdPlaylist::getPlayTimeRemaining() return 0; // dummy } -//------------------------------------------------------------------ -//------------------------------------------------------------------ -// -// class GdTreeNode -// -//------------------------------------------------------------------ -//------------------------------------------------------------------ - /*! - ***************************************************************************** * \brief constructor - * - ****************************************************************************/ + */ GdTreeNode::GdTreeNode(MYSQL db, int view, string filters) : mgSelectionTreeNode(db, view) { @@ -777,10 +768,8 @@ GdTreeNode::GdTreeNode(mgSelectionTreeNode* parent, } /*! - ***************************************************************************** * \brief destructor - * - ****************************************************************************/ + */ GdTreeNode::~GdTreeNode() { // _children.clear(); @@ -789,11 +778,9 @@ GdTreeNode::~GdTreeNode() /*! - ***************************************************************************** * \brief checks if this node can be further expandded or not * \true, if node ia leaf node, false if node can be expanded - * - ****************************************************************************/ + */ bool GdTreeNode::isLeafNode() { if( m_level == 0 ) @@ -887,12 +874,12 @@ bool GdTreeNode::expand() string tables; // stores the db tables used #define FROMJOIN " FROM tracks, genre as genre1, genre as genre2, album WHERE tracks.sourceid=album.cddbid AND genre1.id=tracks.genre1 AND genre2.id=tracks.genre2 AND %s " - + if( m_expanded ) - { - mgWarning("Node already expanded\n"); - return true; - } + { + mgWarning("Node already expanded\n"); + return true; + } if( m_level == 1 && m_view < 100 ) { @@ -1091,7 +1078,7 @@ bool GdTreeNode::expand() } else { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); + mgWarning( "View #%d level %d' not yet implemented", m_view, m_level ); m_expanded = false; return false; } @@ -1167,22 +1154,22 @@ bool GdTreeNode::expand() } } - // now get all childrean ofthe current node fromthe database - result = mgSqlReadQuery(&m_db, sqlbuff); - nrows = mysql_num_rows(result); + // now get all childrean of the current node fromthe database + result = mgSqlReadQuery( &m_db, sqlbuff ); + nrows = mysql_num_rows( result ); nfields = mysql_num_fields(result); - numchild=1; - while((row = mysql_fetch_row(result)) != NULL) + numchild = 1; + while( (row = mysql_fetch_row(result) ) != NULL ) { // row[0] is the printable label for the new child // row[1] is the unique id for the new child - sprintf(idbuf, "%s_%03d", m_id.c_str(), numchild); + sprintf( idbuf, "%s_%03d", m_id.c_str(), numchild ); // Zweite ebene zeigt alle Tracks des Albums und nicht nur // diese die den Filterkriterien entsprechen. // das betrifft nur die Search Views! - if(m_view < 100) + if( m_view < 100 ) { new_restriction = m_restriction + " AND " + idfield + "='" + row[1] + "'"; diff --git a/mg_filters.h b/mg_filters.h index 5501d63..658d943 100644 --- a/mg_filters.h +++ b/mg_filters.h @@ -1,14 +1,12 @@ -/*******************************************************************/ /*! \file mg_filters.h * \brief Top level access to media in vdr plugin muggle * for the vdr muggle plugindatabase - ******************************************************************** + * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: lvw $ + * \author file owner: $Author$ */ -/*******************************************************************/ #ifndef _MG_FILTERS_H #define _MG_FILTERS_H @@ -17,40 +15,52 @@ #include /*! - ******************************************************************* - * \class mgFilter - * - * Abstract base class for representation of filter values with boundaries - ********************************************************************/ + * \brief abstract base class for representation of filter values with boundaries + */ class mgFilter { public: + typedef enum filterType { - UNDEF=0, INT, STRING, BOOL, CHOICE + UNDEF = 0, + INT, + STRING, + BOOL, + CHOICE } filterType; - + protected: filterType m_type; char* m_name; public: + mgFilter(const char* name); + virtual ~mgFilter(); + filterType getType(); + const char* getName(); - virtual std::string getStrVal()=0; - virtual int getIntVal(){return 0;} - virtual void store()=0; - virtual void restore()=0; - virtual void clear()=0; - virtual bool isSet()=0; + + virtual std::string getStrVal() = 0; + + virtual int getIntVal() + { return 0; } + + virtual void store() = 0; + + virtual void restore() = 0; + + virtual void clear() = 0; + + virtual bool isSet() = 0; }; /*! - ******************************************************************* * \class mgFilterInt - ********************************************************************/ + */ class mgFilterInt : public mgFilter { private: @@ -77,9 +87,8 @@ class mgFilterInt : public mgFilter }; /*! - ******************************************************************* * \class mgFilterString - ********************************************************************/ + */ class mgFilterString : public mgFilter { private: @@ -106,9 +115,8 @@ class mgFilterString : public mgFilter }; /*! - ******************************************************************* * \class mgFilterBool - ********************************************************************/ + */ class mgFilterBool : public mgFilter { private: @@ -136,9 +144,8 @@ class mgFilterBool : public mgFilter }; /*! - ******************************************************************* * \class mgFilterChoice - ********************************************************************/ + */ class mgFilterChoice : public mgFilter { private: diff --git a/mg_tools.c b/mg_tools.c index b581396..5b20d70 100644 --- a/mg_tools.c +++ b/mg_tools.c @@ -1,10 +1,11 @@ -/*! \file mg_tools.c - * \brief A few util functions for standalone and plugin messaging for the vdr muggle plugindatabase +/*! + * \file mg_tools.c + * \brief A few util functions for standalone and plugin messaging for the vdr muggle plugindatabase * * \version $Revision: 1.4 $ - * \date $Date: 2004/08/29 14:39:33 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: lvw $ + * \author file owner: $Author$ */ #include "mg_tools.h" @@ -109,7 +110,7 @@ MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) va_list ap; va_start( ap, fmt ); vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); - + if( mysql_query(db, querybuf) ) { mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); @@ -134,18 +135,3 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) va_end(ap); } - -/* -------------------- begin CVS log --------------------------------- - * $Log: mg_tools.c,v $ - * Revision 1.4 2004/08/29 14:39:33 lvw - * Import running in basic version - * - * Revision 1.3 2004/07/25 21:33:35 lvw - * Removed bugs in finding track files and playlist indexing. - * - * Revision 1.2 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ diff --git a/mg_tools.h b/mg_tools.h index 3307e3e..7785ece 100644 --- a/mg_tools.h +++ b/mg_tools.h @@ -32,10 +32,15 @@ MYSQL_RES* mgSqlReadQuery( MYSQL *db, const char *fmt, ... ); * * \todo Could be a member of mgDatabase? */ -void mgSqlWriteQuery( MYSQL *db, const char *fmt, ... ); +void mgSqlWriteQuery( MYSQL *db, const char *fmt, ... ); -/*! \brief Logging utilities */ +/*! + * \brief Logging utilities + * + * \todo these could be static members in the mgLog class + * \todo code of these functions should be compiled conditionally + */ //@{ void mgSetDebugLevel(int new_level); void mgDebug(int level, const char *fmt, ...); @@ -92,24 +97,4 @@ class mgLog }; -/* -------------------- begin CVS log --------------------------------- - * $Log: mg_tools.h,v $ - * Revision 1.4 2004/08/30 14:31:43 LarsAC - * Documentation added - * - * Revision 1.3 2004/05/28 15:29:18 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.2.2.2 2004/04/18 14:08:41 lvw - * Added some more logging/debugging code - * - * Revision 1.2.2.1 2004/04/09 16:14:48 lvw - * Added further code for logging/debugging. - * - * Revision 1.2 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ #endif /* _MUGGLE_TOOLS_H */ diff --git a/mugglei.c b/mugglei.c index c7206bd..4d8477b 100755 --- a/mugglei.c +++ b/mugglei.c @@ -1,3 +1,9 @@ +/*! + * \file mugglei.c + * \brief implement a small utility for importing files + * + * \author Lars von Wedel + */ #include using namespace std; @@ -176,10 +182,18 @@ void update_db( long uid, string filename ) if( import_assorted ) { // in this case, the album author is "Various Artists" - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"Various Artists\", \"%s\", \"%s\")", album.toCString(), cddbid.toCString() ); } + mgSqlWriteQuery( db, + "INSERT INTO album (artist,title,cddbid) " + "VALUES (\"Various Artists\", \"%s\", \"%s\")", + album.toCString(), cddbid.toCString() ); + } else { - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"%s\", \"%s\")", artist.toCString(), album.toCString(), cddbid.toCString() ); } + mgSqlWriteQuery( db, + "INSERT INTO album (artist,title,cddbid) " + "VALUES (\"%s\", \"%s\", \"%s\")", + artist.toCString(), album.toCString(), cddbid.toCString() ); + } } else { // use first album found as source id for the track @@ -187,34 +201,35 @@ void update_db( long uid, string filename ) } } - // update tracks table + // update tracks table if( uid > 0 ) { // the entry is known to exist already, hence update it - + mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\"," - "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\"," - "samplerate=%d, channels=%d WHERE id=%d", - artist.toCString(), title.toCString(), year, - cddbid.toCString(), filename.c_str(), len, bitrate, - sample, channels, uid ); + "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\"," + "samplerate=%d, channels=%d WHERE id=%d", + artist.toCString(), title.toCString(), year, + cddbid.toCString(), filename.c_str(), len, bitrate, + sample, channels, uid ); } else { // the entry does not exist, create it - mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,genre1,genre2,year," - "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels)" - " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d)", - artist.toCString(), title.toCString(), year, cddbid.toCString(), - trackno, filename.c_str(), len, bitrate, sample, channels ); - /* - cout << "-- TAG --" << endl; - cout << "title - \"" << tag->title() << "\"" << endl; - cout << "artist - \"" << tag->artist() << "\"" << endl; - cout << "album - \"" << tag->album() << "\"" << endl; - cout << "year - \"" << tag->year() << "\"" << endl; - cout << "comment - \"" << tag->comment() << "\"" << endl; - cout << "track - \"" << tag->track() << "\"" << endl; - cout << "genre - \"" << tag->genre() << "\"" << endl; - */ + mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,genre1,genre2,year," + "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels)" + " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d)", + artist.toCString(), title.toCString(), year, cddbid.toCString(), + trackno, filename.c_str(), len, bitrate, sample, channels ); + +#ifdef VERBOSE + cout << "-- TAG --" << endl; + cout << "title - \"" << tag->title() << "\"" << endl; + cout << "artist - \"" << tag->artist() << "\"" << endl; + cout << "album - \"" << tag->album() << "\"" << endl; + cout << "year - \"" << tag->year() << "\"" << endl; + cout << "comment - \"" << tag->comment() << "\"" << endl; + cout << "track - \"" << tag->track() << "\"" << endl; + cout << "genre - \"" << tag->genre() << "\"" << endl; +#endif } } } -- cgit v1.2.3 From 6b58be9953109307dbd5cc0b3af4bb1e97e5cca6 Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 30 Sep 2004 21:30:34 +0000 Subject: Added progress display for VDR 1.3.7+ git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@191 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 1629a0b..4ef339c 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -868,6 +868,7 @@ mgPlayerControl::mgPlayerControl( mgPlaylist *plist ) { MGLOG( "mgPlayerControl::mgPlayerControl" ); + m_display = NULL; m_visible = false; m_has_osd = false; } @@ -989,14 +990,15 @@ void mgPlayerControl::ShowProgress() // now an osd is open, go on #if VDRVERSNUM >= 10307 - cSkinDisplayReplay *m_display; - - m_display = Skins.Current()->DisplayReplay(false); + if( !m_display ) + { + m_display = Skins.Current()->DisplayReplay(false); + } if( m_player && m_display ) { int current_frame, total_frames; m_player->GetIndex( current_frame, total_frames ); - + m_display->SetProgress( current_frame, total_frames ); m_display->SetCurrent( IndexToHMSF( current_frame ) ); m_display->SetTotal( IndexToHMSF( total_frames ) ); @@ -1004,11 +1006,13 @@ void mgPlayerControl::ShowProgress() char *buf; asprintf( &buf, "%s", m_player->GetCurrent()->getTitle().c_str() ); m_display->SetTitle( buf ); - free( buf ); - + // free( buf ); + bool play = true, forward = true; int speed = -1; m_display->SetMode( play, forward, speed ); + + m_display->Flush(); } #else int w = Interface->Width(); @@ -1033,8 +1037,15 @@ void mgPlayerControl::Hide() if( m_has_osd ) { #if VDRVERSNUM >= 10307 + /* osd->Flush(); delete osd; + */ + if( m_display ) + { + delete m_display; + m_display = NULL; + } #else Interface->Close(); #endif -- cgit v1.2.3 From 9056b7936ce029d76c8d9e7dea912a6a88b41b01 Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 30 Sep 2004 21:41:15 +0000 Subject: Added function to escape query strings git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@192 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_database.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/mg_database.c b/mg_database.c index b25ee23..3ad6fc9 100644 --- a/mg_database.c +++ b/mg_database.c @@ -1,13 +1,11 @@ -/*******************************************************************/ /*! \file mg_database.c * \brief A capsule around MySql database access - ******************************************************************** + * * \version $Revision: 1.2 $ * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author$ */ -/*******************************************************************/ #include "mg_database.h" @@ -27,22 +25,20 @@ mgDB::~mgDB() { } -string mgDB::escape_string( MYSQL *db, string s ) +MYSQL mgDB::getDBHandle() { - char *escbuf = (char *) malloc( 2*s.length() + 1 ); - - int len = mysql_real_escape_string( db, escbuf, s.c_str(), s.length() ); - string res( escbuf ); - free( resbuf ); - - return res; + return m_dbase; } -MYSQL mgDB::getDBHandle() +string mgDB::escape_string( MYSQL *db, string s ) { + char *escbuf = (char *) malloc( 2*s.size() + 1 ); - return m_dbase; -} + int len = mysql_real_escape_string( db, escbuf, s.c_str(), s.size() ); + string r = string( escbuf ); + free( escbuf ); + return r; +} -- cgit v1.2.3 From 16942456d865cbe9e5fa8cff617c8bc12d3f151f Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 30 Sep 2004 21:41:38 +0000 Subject: Added progress display for VDR 1.3.7+ git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@193 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vdr_player.h b/vdr_player.h index b7ec374..1ab8d9e 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -49,6 +49,9 @@ private: //! \brief indicates, whether an osd is currently displayed bool m_has_osd; #if VDRVERSNUM >= 10307 + //! \brief a replay display to show the progress during playback + cSkinDisplayReplay *m_display; + cOsd *osd; const cFont *font; #endif -- cgit v1.2.3 From 28761e369bccc7a42cbd9301d6b71416606bca7d Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 30 Sep 2004 21:43:03 +0000 Subject: Worked around problems with special characters in labels git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@194 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 533 ++++++++++++++++++++----------------------------- gd_content_interface.h | 4 + 2 files changed, 225 insertions(+), 312 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index b805500..f609990 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1,19 +1,19 @@ -/*! \file gd_content_interface.c - * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin - * \ingroup giantdisc +/*! + * \file gd_content_interface.c + * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin + * \ingroup giantdisc * - * \version $Revision: 1.27 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ + * \version $Revision: 1.27 $ + * \date $Date$ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author$ * - * Implements main classes of for content items and interfaces to SQL databases - * - * This file implements the following classes - * - GdPlaylist a playlist - * - mgGdTrack a single track (content item). e.g. an mp3 file - * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) + * Implements main classes of for content items and interfaces to SQL databases * + * This file implements the following classes + * - GdPlaylist a playlist + * - mgGdTrack a single track (content item). e.g. an mp3 file + * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) */ #define DEBUG @@ -21,9 +21,11 @@ #include "gd_content_interface.h" #include "mg_tools.h" -#include "i18n.h" +#include "mg_database.h" #include "vdr_setup.h" +#include "i18n.h" + using namespace std; #define GD_PLAYLIST_TYPE 0 //< listtype for giant disc db @@ -77,7 +79,7 @@ gdFilterSets::gdFilterSets() mgFilter* filter; vector* set; vector* rating; - m_titles.push_back(tr("Track Search")); + m_titles.push_back( tr("Track Search") ); // create an initial set of filters with empty values set = new vector(); @@ -89,17 +91,21 @@ gdFilterSets::gdFilterSets() // year-from filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); + // year-to filter = new mgFilterInt(tr("year (to)"), 2099, 1900, 2100); set->push_back(filter); + // title filter = new mgFilterString(tr("title"), ""); set->push_back(filter); + // artist filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); + // genre filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); // rating. TODO: Currently buggy. LVW - // filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); + // filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); m_sets.push_back(set); @@ -156,7 +162,7 @@ string gdFilterSets::computeRestriction(int *viewPrt) { string sql_str = "1"; - switch (m_activeSetId) + switch( m_activeSetId ) { case 0: { @@ -261,12 +267,11 @@ string gdFilterSets::computeRestriction(int *viewPrt) mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack(); -mgGdTrack::mgGdTrack(int sqlIdentifier, MYSQL dbase) +mgGdTrack::mgGdTrack( int sqlIdentifier, MYSQL dbase ) { m_uniqID = sqlIdentifier; m_db = dbase; m_retrieved = false; - } mgGdTrack::mgGdTrack(const mgGdTrack& org) @@ -274,15 +279,17 @@ mgGdTrack::mgGdTrack(const mgGdTrack& org) m_uniqID = org.m_uniqID; m_db = org.m_db; m_retrieved = org.m_retrieved; - if(m_retrieved) + + if( m_retrieved ) { m_artist = org.m_artist; m_title = org.m_title; m_mp3file = org.m_mp3file; m_album = org.m_album; - m_genre = org.m_genre; - m_year = org.m_year; - m_rating = org.m_rating; + m_genre = org.m_genre; + m_year = org.m_year; + m_rating = org.m_rating; + m_length = org.m_length; } } @@ -297,16 +304,16 @@ bool mgGdTrack::readData() int nrows, nfields; // note: this does not work with empty album or genre fields - result = mgSqlReadQuery(&m_db, - "SELECT tracks.artist, album.title, tracks.title, " - "tracks.mp3file, genre.genre, tracks.year, " - "tracks.rating, tracks.length, tracks.samplerate, tracks.channels, tracks.bitrate " - "FROM tracks, album, genre " - "WHERE tracks.id=%d " - "AND album.cddbid=tracks.sourceid AND " - "genre.id=tracks.genre1", - m_uniqID); - + result = mgSqlReadQuery( &m_db, + "SELECT tracks.artist, album.title, tracks.title, " + "tracks.mp3file, genre.genre, tracks.year, " + "tracks.rating, tracks.length, tracks.samplerate, tracks.channels, tracks.bitrate " + "FROM tracks, album, genre " + "WHERE tracks.id = %d " + "AND album.cddbid = tracks.sourceid AND " + "genre.id = tracks.genre1", + m_uniqID ); + nrows = mysql_num_rows(result); nfields = mysql_num_fields(result); @@ -321,7 +328,8 @@ bool mgGdTrack::readData() { mgWarning("mgGdTrack::readData: More than one entry found. Using first entry."); } - MYSQL_ROW row = mysql_fetch_row(result); + + MYSQL_ROW row = mysql_fetch_row( result ); m_artist = row[0]; m_album = row[1]; @@ -330,44 +338,44 @@ bool mgGdTrack::readData() m_genre = row[4]; if( sscanf( row[5], "%d", &m_year) != 1 ) - { - mgError("Invalid year '%s' in database", row [5]); - } - + { + mgError("Invalid year '%s' in database", row [5]); + } + if( row[6] && sscanf( row[6], "%d", &m_rating ) != 1 ) - { - mgError( "Invalid rating '%s' in database", row [6] ); - } - + { + mgError( "Invalid rating '%s' in database", row [6] ); + } + if( row[7] && sscanf( row[7], "%d", &m_length) != 1 ) - { - mgError( "Invalid duration '%s' in database", row [7]); - } + { + mgError( "Invalid duration '%s' in database", row [7]); + } if( row[8] && sscanf( row[8], "%d", &m_samplerate ) != 1 ) - { - mgError( "Invalid samplerate '%s' in database", row [7]); - } - + { + mgError( "Invalid samplerate '%s' in database", row [7]); + } + if( row[9] && sscanf( row[9], "%d", &m_channels ) != 1 ) - { - mgError( "Invalid channels '%s' in database", row [7]); - } - - m_bitrate = row[10]; + { + mgError( "Invalid channels '%s' in database", row [7]); + } - } + m_bitrate = row[10]; + + } m_retrieved = true; return true; } string mgGdTrack::getSourceFile() { - if( !m_retrieved ) - { - readData(); - } - return m_mp3file; + if( !m_retrieved ) + { + readData(); + } + return m_mp3file; } string mgGdTrack::getTitle() @@ -388,6 +396,16 @@ string mgGdTrack::getArtist() return m_artist; } +int mgGdTrack::getLength() +{ + if( !m_retrieved ) + { + readData(); + } + return m_length; +} + + string mgGdTrack::getLabel(int col) { if( !m_retrieved ) @@ -518,22 +536,22 @@ void mgGdTrack::setGenre(string new_genre) void mgGdTrack::setYear(int new_year) { - m_year = new_year; + m_year = new_year; } void mgGdTrack::setRating(int new_rating) { - m_rating = new_rating; + m_rating = new_rating; } bool mgGdTrack::writeData() { - mgSqlWriteQuery(&m_db, "UPDATE tracks " - "SET artist=\"%s\", title=\"%s\", year=%d, rating=%d " - "WHERE id=%d", - m_artist.c_str(), m_title.c_str(), - m_year, m_rating, m_uniqID); - return true; + mgSqlWriteQuery( &m_db, "UPDATE tracks " + "SET artist=\"%s\", title=\"%s\", year=%d, rating=%d " + "WHERE id=%d", + m_artist.c_str(), m_title.c_str(), + m_year, m_rating, m_uniqID); + return true; } GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) @@ -547,9 +565,9 @@ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) " FROM tracks, album, genre WHERE %s" " AND album.cddbid=tracks.sourceid " " AND genre.id=tracks.genre1", - restrictions.c_str() ); - - while((row = mysql_fetch_row(result)) != NULL) + restrictions.c_str()); + + while( ( row = mysql_fetch_row(result) ) != NULL ) { // row[0] is the trackid if(sscanf(row[0], "%d", &trackid) != 1) @@ -557,10 +575,10 @@ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) mgError("Can not extract integer track id from '%s'", row[0]); } - m_list.push_back( new mgGdTrack(trackid, db_handle) ); + m_list.push_back(new mgGdTrack(trackid, db_handle)); } } - + GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) { MYSQL_RES *result; @@ -576,41 +594,42 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) "SELECT id,author FROM playlist where title=\"%s\"", listname.c_str()); nrows = mysql_num_rows(result); + if( nrows == 0 ) { mgDebug(3, "No playlist with name %s found. Creating new playlist\n", listname.c_str()); // create new database entry - mgSqlWriteQuery(&m_db, "INSERT into playlist " - "SET title=\"%s\", author=\"%s\"", - listname.c_str(), - "VDR", // default author - ""); // creates current time as timestamp + mgSqlWriteQuery( &m_db, "INSERT into playlist " + "SET title=\"%s\", author=\"%s\"", + listname.c_str(), + "VDR", // default author + ""); // creates current time as timestamp m_author = "VDR"; m_listname = listname; // now read thenew list to get the id - result = mgSqlReadQuery(&m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - listname.c_str()); + result = mgSqlReadQuery( &m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + listname.c_str() ); nrows = mysql_num_rows(result); - row = mysql_fetch_row(result); + row = mysql_fetch_row(result); - if(sscanf(row [0], "%d", & m_sqlId) !=1) + if( sscanf(row [0], "%d", & m_sqlId) !=1 ) { mgError("Invalid id '%s' in database", row [5]); } - } else { // playlist exists, read data row = mysql_fetch_row(result); - if(sscanf(row [0], "%d", & m_sqlId) !=1) + if( sscanf(row[0], "%d", & m_sqlId) !=1 ) { mgError("Invalid id '%s' in database", row [5]); } + m_author = row[1]; m_listname = listname; @@ -624,8 +643,7 @@ GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) } GdPlaylist::~GdPlaylist() -{ - +{ } void GdPlaylist::setListname(std::string name) @@ -636,32 +654,31 @@ void GdPlaylist::setListname(std::string name) int GdPlaylist::insertDataFromSQL() { - MYSQL_RES *result; - MYSQL_ROW row; - mgGdTrack* trackptr; - int id; - int nrows; - - result = mgSqlReadQuery(&m_db, - "SELECT tracknumber, trackid FROM playlistitem " - "WHERE playlist = %d ORDER BY tracknumber", - m_sqlId); - - nrows = mysql_num_rows(result); - while( (row = mysql_fetch_row(result) ) != NULL ) - { - // add antry to tracklist - if(sscanf(row[1], "%d", &id) !=1) + MYSQL_RES *result; + MYSQL_ROW row; + mgGdTrack* trackptr; + int id; + int nrows; + + result = mgSqlReadQuery( &m_db, + "SELECT tracknumber, trackid FROM playlistitem " + "WHERE playlist = %d ORDER BY tracknumber", + m_sqlId); + nrows = mysql_num_rows(result); + while( (row = mysql_fetch_row(result) ) != NULL ) + { + // add antry to tracklist + if( sscanf( row[1], "%d", &id ) !=1 ) { - mgWarning("Track id '%s' is not an integer. Ignoring \n", row[1]); + mgWarning( "Track id '%s' is not an integer. Ignoring \n", row[1] ); } - else + else { - trackptr = new mgGdTrack(id, m_db); - m_list.push_back(trackptr); + trackptr = new mgGdTrack( id, m_db ); + m_list.push_back( trackptr ); } } - return nrows; + return nrows; } bool GdPlaylist::storePlaylist() @@ -672,17 +689,18 @@ bool GdPlaylist::storePlaylist() MYSQL_ROW row; int nrows; - - if(m_listname ==" ") + if( m_listname == " " ) { mgWarning("Can not store Tracklist without name"); return false; } + if(m_sqlId >= 0) { // playlist alreay exists in SQL database // remove old items first - cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; + // cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; + // remove old playlist items from db mgSqlWriteQuery(&m_db, "DELETE FROM playlistitem WHERE playlist = %d", @@ -705,7 +723,7 @@ bool GdPlaylist::storePlaylist() nrows = mysql_num_rows(result); row = mysql_fetch_row(result); - if(sscanf(row [0], "%d", & m_sqlId) !=1) + if( sscanf( row [0], "%d", & m_sqlId ) !=1 ) { mgError("Invalid id '%s' in database", row [5]); } @@ -759,12 +777,15 @@ GdTreeNode::GdTreeNode(MYSQL db, int view, string filters) m_view = view; m_label = tr("Browser"); } -GdTreeNode::GdTreeNode(mgSelectionTreeNode* parent, - string id, string label, string restriction) + +GdTreeNode::GdTreeNode( mgSelectionTreeNode* parent, + string id, + string label, + string restriction ) : mgSelectionTreeNode(parent, id, label) { - m_restriction = restriction; - // everything else is done in the parent class + m_restriction = restriction; + // everything else is done in the parent class } /*! @@ -772,74 +793,73 @@ GdTreeNode::GdTreeNode(mgSelectionTreeNode* parent, */ GdTreeNode::~GdTreeNode() { - // _children.clear(); + // _children.clear(); } - - /*! * \brief checks if this node can be further expandded or not * \true, if node ia leaf node, false if node can be expanded */ bool GdTreeNode::isLeafNode() { - if( m_level == 0 ) - { - return false; - } - switch(m_view) - { - case 1: // artist -> album -> title - if( m_level <= 3 ) - { - return false; - } - break; - case 2: // genre -> artist -> album -> track - if( m_level <= 3 ) - { - return false; - } - break; - case 3: // Artist -> Track - if( m_level <= 2 ) - { - return false; - } - break; - case 4: - if( m_level <= 2 ) - { - return false; - } - break; - case 5: - if( m_level <= 1 ) - { - return false; - } - break; - case 100: - if( m_level <= 0 ) - { - return false; - } - break; - case 101: - if( m_level <= 1 ) - { - return false; - } - break; - case 102: - if( m_level <= 1 ) - { - return false; - } - break; - default: - mgError("View '%d' not yet implemented", m_view); - } + if( m_level == 0 ) + { + return false; + } + + switch(m_view) + { + case 1: // artist -> album -> title + if( m_level <= 3 ) + { + return false; + } + break; + case 2: // genre -> artist -> album -> track + if( m_level <= 3 ) + { + return false; + } + break; + case 3: // Artist -> Track + if( m_level <= 2 ) + { + return false; + } + break; + case 4: + if( m_level <= 2 ) + { + return false; + } + break; + case 5: + if( m_level <= 1 ) + { + return false; + } + break; + case 100: + if( m_level <= 0 ) + { + return false; + } + break; + case 101: + if( m_level <= 1 ) + { + return false; + } + break; + case 102: + if( m_level <= 1 ) + { + return false; + } + break; + default: + mgError("View '%d' not yet implemented", m_view); + } return true; } @@ -847,7 +867,7 @@ bool GdTreeNode::isLeafNode() * \brief compute children on the fly * * \return: true, if the node could be expanded (or was already), false,of - * node can not be expanded any further + * node can not be expanded any further * * retrieves all entries for the next level that satisfy the restriction of * the current level and create a child-arc for each distinct entry @@ -895,13 +915,13 @@ bool GdTreeNode::expand() { // artist -> album -> title if( m_level == 1 ) { - sprintf(sqlbuff, - "SELECT DISTINCT album.artist,album.artist" - FROMJOIN - " ORDER BY album.artist" - , m_restriction.c_str() ); + sprintf( sqlbuff, + "SELECT DISTINCT album.artist,album.artist" + FROMJOIN + " ORDER BY album.artist" + , m_restriction.c_str() ); idfield = "album.artist"; - } + } else if( m_level == 2 ) { // artist -> album sprintf(sqlbuff, @@ -1095,15 +1115,16 @@ bool GdTreeNode::expand() " ORDER BY CONCAT(album.artist,' - ',album.title)", m_restriction.c_str()); idfield = "tracks.sourceid"; - } else if( m_level == 2 ) - { - sprintf(sqlbuff, - "SELECT tracks.title,tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb", - m_restriction.c_str()); - idfield = "tracks.id"; - } + } + else if( m_level == 2 ) + { + sprintf(sqlbuff, + "SELECT tracks.title,tracks.id" + FROMJOIN + " ORDER BY tracks.tracknb", + m_restriction.c_str()); + idfield = "tracks.id"; + } else { mgWarning("View #%d level %d' not yet implemented", m_view, m_level); @@ -1169,19 +1190,24 @@ bool GdTreeNode::expand() // Zweite ebene zeigt alle Tracks des Albums und nicht nur // diese die den Filterkriterien entsprechen. // das betrifft nur die Search Views! + + string row0 = mgDB::escape_string( &m_db, string( row[0] ) ); + string row1 = mgDB::escape_string( &m_db, string( row[1] ) ); + if( m_view < 100 ) { new_restriction = m_restriction + " AND " - + idfield + "='" + row[1] + "'"; + + idfield + "='" + row1 + "'"; } else { - new_restriction = idfield + "='" + row[1] + "'"; + new_restriction = idfield + "='" + row1 + "'"; } new_child = new GdTreeNode(this, // parent (string) idbuf, // id - row[0], // label, + // row[0], // label, + row0, new_restriction); m_children.push_back(new_child); numchild++; @@ -1311,120 +1337,3 @@ mgContentItem* GdTreeNode::getSingleTrack() return track; } -/* -------------------- begin CVS log --------------------------------- - * $Log: gd_content_interface.c,v $ - * Revision 1.27 2004/08/30 14:31:43 LarsAC - * Documentation added - * - * Revision 1.26 2004/08/27 15:19:34 LarsAC - * Changed formatting and documentation - * - * Revision 1.25 2004/08/23 06:36:25 lvw - * Initial version of an import module added - * - * Revision 1.24 2004/07/25 21:33:35 lvw - * Removed bugs in finding track files and playlist indexing. - * - * Revision 1.23 2004/07/06 00:20:51 MountainMan - * loading and saving playlists - * - * Revision 1.22 2004/05/28 15:29:18 lvw - * Merged player branch back on HEAD branch. - * - * - * Revision 1.21 2004/02/23 17:03:24 RaK - * - error in filter view while trying to switch or using the colour keys - * workaround: first filter criteria is inttype. than it works, dont ask why ;-( - * - * Revision 1.20 2004/02/23 16:30:58 RaK - * - album search error because of i18n corrected - * - * Revision 1.19 2004/02/23 15:56:19 RaK - * - i18n - * - * Revision 1.18 2004/02/23 15:17:51 RaK - * - i18n - * - * Revision 1.17 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.16 2004/02/14 22:02:45 RaK - * - mgFilterChoice Debuged - * fehlendes m_type = CHOICE in mg_filters.c - * falscher iterator in vdr_menu.c - * - * Revision 1.15 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.14.2.4 2004/05/26 14:34:58 lvw - * Formatting changed - * - * Revision 1.14.2.3 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.14.2.2 2004/03/14 17:57:30 lvw - * Linked against libmad. Introduced config options into code. - * - * Revision 1.14.2.1 2004/03/02 07:05:50 lvw - * Initial adaptations from MP3 plugin added (untested) - * - * Revision 1.21 2004/02/23 17:03:24 RaK - * - error in filter view while trying to switch or using the colour keys - * workaround: first filter criteria is inttype. than it works, dont ask why ;-( - * - * Revision 1.20 2004/02/23 16:30:58 RaK - * - album search error because of i18n corrected - * - * Revision 1.19 2004/02/23 15:56:19 RaK - * - i18n - * - * Revision 1.18 2004/02/23 15:17:51 RaK - * - i18n - * - * Revision 1.17 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.16 2004/02/14 22:02:45 RaK - * - mgFilterChoice Debuged - * fehlendes m_type = CHOICE in mg_filters.c - * falscher iterator in vdr_menu.c - * - * Revision 1.15 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.14 2004/02/12 07:56:46 RaK - * - SQL Fehler bei der Playlist Search korrigiert - * - * Revision 1.13 2004/02/11 21:55:16 RaK - * - playlistsearch eingebaut - * - filter search liefert nun in der zweiten - * ebene alle tracks des albums/playlist - * - * Revision 1.12 2004/02/10 23:47:23 RaK - * - views konsitent gemacht. siehe FROMJOIN - * - isLeafNode angepasst fuer neue views 4,5,100,101 - * - like '%abba%' eingebaut - * - filter ist default mit abba gefuellt, zum leichteren testen. - * - search results werden jetzt gleich im ROOT expanded - * - * Revision 1.11 2004/02/10 01:23:06 RaK - * Ein fehler beim Tracksearch behoben. geht jetzt, aber nur einmal?!?! - * - * Revision 1.10 2004/02/09 23:21:33 MountainMan - * partial bug fixes - * - * Revision 1.9 2004/02/09 22:07:44 RaK - * secound filter set (album search incl. special view #101 - * - * Revision 1.8 2004/02/09 19:27:52 MountainMan - * filter set implemented - * - * Revision 1.7 2004/02/03 00:13:24 LarsAC - * Improved OSD handling of collapse/back - * - * Revision 1.6 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ diff --git a/gd_content_interface.h b/gd_content_interface.h index d810fa5..20906df 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -229,6 +229,10 @@ class mgGdTrack : public mgContentItem */ virtual std::string getBitrate(); + /*! \brief obtain the bitrate of the track + */ + virtual int getLength(); + /*! * \brief obtain the complete track information */ -- cgit v1.2.3 From 30f63d0333bf00bd9878eaf540ac8f8beb73c916 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 3 Oct 2004 08:55:05 +0000 Subject: Removed dependency on 1.3.12 version git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@195 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index 4ef339c..e70b8fa 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -868,7 +868,9 @@ mgPlayerControl::mgPlayerControl( mgPlaylist *plist ) { MGLOG( "mgPlayerControl::mgPlayerControl" ); +#if VDRVERSNUM >= 10307 m_display = NULL; +#endif m_visible = false; m_has_osd = false; } -- cgit v1.2.3 From 885c894656d26c246cc7757f3a29bf237b129227 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 3 Oct 2004 08:55:37 +0000 Subject: Added mugglei.h to make Makefile work git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@196 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mugglei.h diff --git a/mugglei.h b/mugglei.h new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3 From 50075887187e1801f3d3425f20a6b6127ffd7ff8 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 4 Oct 2004 07:30:45 +0000 Subject: Added patch for using sockets and improved menu translations git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@199 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 5 +- README | 2 +- TODO | 93 +++++++++++++++++++---------------- gd_content_interface.c | 44 +++++++++++++---- i18n.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++- mg_tools.h | 2 +- muggle.c | 9 +++- muggle.doxygen | 2 +- mugglei.c | 40 +++++++++++---- vdr_menu.c | 16 +++--- vdr_setup.h | 7 +-- 11 files changed, 269 insertions(+), 81 deletions(-) diff --git a/Makefile b/Makefile index 7e5d7af..a8fd95b 100644 --- a/Makefile +++ b/Makefile @@ -56,8 +56,7 @@ LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis ### Targets: -all: libvdr-$(PLUGIN).so -# mugglei +all: libvdr-$(PLUGIN).so mugglei # Dependencies: @@ -78,7 +77,7 @@ libvdr-$(PLUGIN).so: $(OBJS) @cp $@ $(LIBDIR)/$@.$(VDRVERSION) mugglei: mg_tools.o mugglei.o - $(CXX) $(CXXFLAGS) $(LIBS) -ltag -o $@ $^ + $(CXX) $(CXXFLAGS) $^ $(LIBS) -ltag -o $@ dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) diff --git a/README b/README index c23fb5a..dcd3832 100644 --- a/README +++ b/README @@ -122,7 +122,7 @@ Execute these commands on a single line, the \ for the linebreak ist just for pr echo "use GiantDisc; load data local infile 'musictypes.txt' into table language;" | \ mysql -u root --local-infile=1 - echo "use GiantDisc; load data local infile '$SCRIPTDIR/sources.txt' into table language;" | \ + echo "use GiantDisc; load data local infile 'sources.txt' into table language;" | \ mysql -u root --local-infile=1 \endverbatim diff --git a/TODO b/TODO index 7d603b1..b99a5e1 100644 --- a/TODO +++ b/TODO @@ -11,55 +11,40 @@ \subsection bugs Bugs and testing needed - - Test execution of playlist commands - - Attach to graphlcd plugin via replay string - - Plugin crashes when selecting entries with special characters - - Escape query strings correctly - - Possible to resume play instead of restarting list from the beginning? - - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) + - Test execution of playlist commands + - Attach to graphlcd plugin via replay string + - Possible to resume play instead of restarting list from the beginning? + - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) + - Crashes in filter selections? + - Save on exit \subsection urgentosd OSD-related Issues - - Add play indicators - - mgPCMPlayer::GetIndex: obtain total length from database (DONE) - - Toggle display with Ok (off -> track info -> album info -> playlist info -> off) - - Display track info (use vdr 1.3.12 progress display) - - Song title - - Artist - - Genre - - Album - - Bitrate, channels, samplerate, format, ... - - Progress display - - Display album info - - Album name - - Artist - - Year - - No. Tracks - - Track names? - - Display playlist info - - Overall progress - - Next track(s) + - Instant play \subsection urgentcode Code polishing - - Clean up coding style and documentation in general - - Check for unnecessary log commands - - Generate HTML documentation using doxygen, - - use dotty/gv for state machines of player - - make available online - - Clean up mugglei (abstract code where possible) - - Check for memory leaks - - Why do filters use pointers? - - Check for (reasonably) consistent usage of char pointers and strings - - - mgDatabase is not used? - - should handle a static object with a MySQL connection - - execute queries? + - Clean up coding style and documentation in general + - Logging + - extend mgLog with static logging methods + - in DEBUG mode, issue logs, warnings, errors to stderr + - otherwise issue errors only to syslog + - Check for unnecessary log commands + - Generate HTML documentation using doxygen, + - use dotty/gv for state machines of player + - make available online + - Clean up mugglei (abstract code where possible) + - Check for memory leaks + - Why do filters use pointers? + - Check for (reasonably) consistent usage of char pointers and strings + - mgDatabase class is not used + - should handle a static object with a MySQL connection + - execute queries + - escape query strings - mgPlayer used what for? - - Could save IP/host name and associate last playlist loaded + - Could save IP/host name and associate last playlist/index loaded - \subsection urgentcontent Content handling - + \subsection urgentcontent Content handling - Think, whether type (mp3, ogg, flac) should be stored in database - could be used in searching/structuring as well - Handle loop mode in mgPlaylist (already done?) @@ -157,6 +142,25 @@ - Type numbers to enter characters and jump to first title accordingly - Check whether submenus (as implemented in VDR) are more suitable - do not permit jumping to arbitrary menus though + - Add play indicators + - mgPCMPlayer::GetIndex: obtain total length from database (DONE) + - Toggle display with Ok (off -> track info -> album info -> playlist info -> off) + - Display track info (use vdr 1.3.12 progress display) + - Song title + - Artist + - Genre + - Album + - Bitrate, channels, samplerate, format, ... + - Progress display + - Display album info + - Album name + - Artist + - Year + - No. Tracks + - Track names? + - Display playlist info + - Overall progress + - Next track(s) \subsection midcontent Content issues @@ -231,7 +235,7 @@ - mgPCMPlayer::getSourceFile moved to abstract data layer (mgContentItem) and made concrete in subclasses (mgGdTrack) - mgDecoders::findDecoder: extend decoder detection - - Check compatibility for 1.3.12 (DONE) + - Check compatibility for 1.3.12 (DONE) - Test Save/Rename/Load of playlist (DONE) - Plugin crashes when deleting playlist while playing - should stop playing immediately @@ -241,5 +245,8 @@ - Playlist indexing not correct - Player jumps e.g. from track 1 to track 3 - Make sure jumping beyond the end of the list is not permitted - + - Plugin crashes when selecting entries with special characters + - Escape query strings correctly + - should go into gd_content_interface.c (row[] in lines 1175,1179,1882)? + - Simple progress indicator for 1.3.12 */ \ No newline at end of file diff --git a/gd_content_interface.c b/gd_content_interface.c index f609990..4537b21 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -45,17 +45,41 @@ int GdInitDatabase( MYSQL *db ) { return -1; } - - if( mysql_real_connect( db, - the_setup.DbHost, - the_setup.DbUser, - the_setup.DbPass, - the_setup.DbName, - the_setup.DbPort, - NULL, 0 ) == NULL ) + + if (the_setup.DbSocket != NULL) { - return -2; - } + mgDebug(1,"Using sockets for connecting to Database."); + + //mgDebug(3,"Socket is: '%s'",the_setup.DbSocket); + //mgDebug(3,"DbUser is: '%s'",the_setup.DbUser); + //mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass); + + if( mysql_real_connect( db, + "", + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + 0, + the_setup.DbSocket, 0 ) == NULL ) + { + return -2; + } // if mysql_real_connect + } //if DbSocket + else + { + mgDebug(1,"Using TCP-host for connecting to Database."); + if( mysql_real_connect( db, + the_setup.DbHost, + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + the_setup.DbPort, + "", 0 ) == NULL ) + { + return -2; + } // if mysql_real_connect + } // else (if DbSocket) + return 0; } diff --git a/i18n.c b/i18n.c index cb36790..d328160 100644 --- a/i18n.c +++ b/i18n.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: i18n.c,v 1.6 2004/05/28 15:29:18 lvw Exp $ + * $Id$ */ #include "i18n.h" @@ -618,5 +618,133 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO }, + { "%d tracks sent to current playlist", + "%d Titel in aktuelle Playlist?", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Load playlist", + "Playliste laden", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Save playlist", + "Playliste speichern", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Rename playlist", + "Playliste umbenennen", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Clear playlist", + "Playliste leeren", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Remove entry from list", + "Eintrag aus der Playliste entfernen", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Export playlist", + "Playliste exportieren", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "External playlist commands", + "Externe Playlist-Kommandos", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { NULL } }; diff --git a/mg_tools.h b/mg_tools.h index 7785ece..dda574b 100644 --- a/mg_tools.h +++ b/mg_tools.h @@ -15,7 +15,7 @@ #include #include -#include +#include #define STANDALONE 1 // what's this? diff --git a/muggle.c b/muggle.c index 92e9831..a952c43 100644 --- a/muggle.c +++ b/muggle.c @@ -49,6 +49,7 @@ mgMuggle::mgMuggle(void) { // defaults for database arguments the_setup.DbHost = strdup ("localhost"); + the_setup.DbSocket = NULL; the_setup.DbPort = 0; the_setup.DbName = strdup ("GiantDisc"); the_setup.DbUser = strdup (""); @@ -67,6 +68,7 @@ const char *mgMuggle::CommandLineHelp(void) // Return a string that describes all known command line options. return " -h HHHH, --host=HHHH specify database host (default is localhost)\n" + " -s SSSS --socket=PATH specify database socket (default is TCP connection)\n" " -n NNNN, --name=NNNN specify database name (overridden by -g)\n" " -p PPPP, --port=PPPP specify port of database server (default is )\n" " -u UUUU, --user=UUUU specify database user (default is )\n" @@ -83,6 +85,7 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) static struct option long_options[] = { { "host", required_argument, NULL, 'h' }, + { "socket", required_argument, NULL, 's' }, { "name", required_argument, NULL, 'n' }, { "port", required_argument, NULL, 'p' }, { "user", required_argument, NULL, 'u' }, @@ -93,7 +96,7 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) }; int c, option_index = 0; - while( ( c = getopt_long( argc, argv, "gh:n:p:t:u:w:", long_options, &option_index ) ) != -1 ) + while( ( c = getopt_long( argc, argv, "gh:s:n:p:t:u:w:", long_options, &option_index ) ) != -1 ) { switch (c) { @@ -101,6 +104,10 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) { the_setup.DbHost = strcpyrealloc (the_setup.DbHost, optarg); } break; + case 's': + { + the_setup.DbSocket = strcpyrealloc (the_setup.DbSocket, optarg); + } break; case 'n': { the_setup.DbName = strcpyrealloc (the_setup.DbName, optarg); diff --git a/muggle.doxygen b/muggle.doxygen index c5796c6..3459584 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.0.2 +PROJECT_NUMBER = 0.0.4 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/mugglei.c b/mugglei.c index 4d8477b..21aa90e 100755 --- a/mugglei.c +++ b/mugglei.c @@ -12,9 +12,7 @@ using namespace std; #include #include #include - #include - #include #include @@ -24,21 +22,38 @@ using namespace std; MYSQL *db; -string host, user, pass, dbname; +string host, user, pass, dbname, socket; bool import_assorted; int init_database() { - db = mysql_init(NULL); + db = mysql_init(0); // NULL? + if( db == NULL ) { + cout << "mysql_init failed." << endl; return -1; } - - if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), - 0, NULL, 0 ) == NULL ) + + // check for use of sockets + if( socket != "" ) + { + if( mysql_real_connect( db, NULL, user.c_str(), pass.c_str(), dbname.c_str(), + 0, socket.c_str(), 0 ) == NULL ) + + { + cout << "mysql_real_connect using sockets failed." << endl; + return -2; + } + } + else { - return -2; + if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), + 0, NULL, 0 ) == NULL ) + { + cout << "mysql_real_connect via TCP failed." << endl; + return -2; + } } return 0; @@ -288,6 +303,7 @@ int main( int argc, char *argv[] ) cout << "" << endl; cout << "Options:" << endl; cout << " -h - specify host of mySql database server (default is 'localhost')" << endl; + cout << " -s - specify a socket for mySQL communication (default is TCP)" << endl; cout << " -n - specify database name (default is 'GiantDisc')" << endl; cout << " -u - specify user of mySql database (default is empty)" << endl; cout << " -p - specify password of user (default is empty password)" << endl; @@ -302,12 +318,13 @@ int main( int argc, char *argv[] ) dbname = "GiantDisc"; user = ""; pass = ""; + socket = ""; import_assorted = false; // parse command line options while( 1 ) { - int c = getopt(argc, argv, "h:u:p:n:af:"); + int c = getopt(argc, argv, "h:u:p:n:af:s:"); if (c == -1) break; @@ -342,6 +359,10 @@ int main( int argc, char *argv[] ) { filename = optarg; } break; + case 's': + { + socket = optarg; + } break; } } @@ -353,6 +374,7 @@ int main( int argc, char *argv[] ) int res = init_database(); + if( !res ) { update_db( 0, filename ); diff --git a/vdr_menu.c b/vdr_menu.c index b4b2ab9..fe805b4 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -161,7 +161,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { char *buffer = 0; - asprintf( &buffer, "%d tracks sent to current playlist", (int) tracks->size() ); + asprintf( &buffer, tr("%d tracks sent to current playlist"), (int) tracks->size() ); m_current_playlist->appendList(tracks); #if VDRVERSNUM >= 10307 Skins.Message(mtInfo,buffer); @@ -668,16 +668,16 @@ void mgMainMenu::DisplayPlaylistSubmenu() SetTitle( "Muggle - Playlist View Commands" ); // Add items - Add( new cOsdItem( "Load playlist" ) ); - Add( new cOsdItem( "Save playlist" ) ); - Add( new cOsdItem( "Rename playlist" ) ); - Add( new cOsdItem( "Clear playlist" ) ); - Add( new cOsdItem( "Remove entry from list" ) ); - Add( new cOsdItem( "Export playlist" ) ); + Add( new cOsdItem( tr("Load playlist" ) ) ); + Add( new cOsdItem( tr("Save playlist" ) ) ); + Add( new cOsdItem( tr("Rename playlist" ) ) ); + Add( new cOsdItem( tr("Clear playlist" ) ) ); + Add( new cOsdItem( tr("Remove entry from list" ) ) ); + Add( new cOsdItem( tr("Export playlist" ) ) ); if( m_playlist_commands ) { - Add( new cOsdItem( "External playlist commands" ) ); + Add( new cOsdItem( tr("External playlist commands" ) ) ); } Display(); diff --git a/vdr_setup.h b/vdr_setup.h index a77cf75..5750146 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -3,11 +3,11 @@ * \brief A setup class for a VDR media plugin (muggle) * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_setup.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -44,6 +44,7 @@ public: int Only48kHz; char *DbHost; + char *DbSocket; char *DbName; char *DbUser; char *DbPass; -- cgit v1.2.3 From 44cd4c0319c75d4315fe0554d701effe77680776 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 4 Oct 2004 20:47:58 +0000 Subject: Added a patch to correct minor errors. Further, remove using namespace directives in order to remain compatible with g++ 2.95.4 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@200 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 5 +-- gd_content_interface.c | 95 +++++++++++++++++++++++++------------------------- mg_content_interface.c | 32 ++++++++--------- mg_database.c | 9 +++-- mg_filters.c | 32 ++++++++--------- mg_media.c | 37 ++++++++++---------- mg_playlist.c | 22 ++++++------ mg_tools.c | 8 ++--- muggle.c | 9 ++--- muggle.h | 2 +- mugglei.c | 71 +++++++++++++++++++------------------ vdr_decoder.c | 10 +++--- vdr_decoder_mp3.c | 1 - vdr_decoder_ogg.c | 7 ++-- vdr_menu.c | 37 ++++++++++---------- vdr_menu.h | 6 ++-- vdr_player.c | 7 ++-- vdr_stream.c | 11 +++--- 18 files changed, 194 insertions(+), 207 deletions(-) diff --git a/Makefile b/Makefile index a8fd95b..14de5fd 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ #DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -D_GNU_SOURCE MIFLAGS += -I/usr/include/taglib -lmysqlclient ### The object files (add further files here): @@ -60,7 +61,7 @@ all: libvdr-$(PLUGIN).so mugglei # Dependencies: -MAKEDEP = g++ -MM -MG +MAKEDEP = $(CXX) -MM -MG DEPFILE = .dependencies $(DEPFILE): Makefile @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ @@ -88,5 +89,5 @@ dist: clean @echo Distribution package created as $(PACKAGE).tgz clean: - @-rm -f $(OBJS) $(BINOBJS) $(DEPFILE) *.so *.tgz core* *~ + @-rm -f $(OBJS) $(BINOBJS) $(DEPFILE) *.so *.tgz core* *~ mugglei.o mugglei diff --git a/gd_content_interface.c b/gd_content_interface.c index 4537b21..6fda5e4 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -26,7 +26,6 @@ #include "i18n.h" -using namespace std; #define GD_PLAYLIST_TYPE 0 //< listtype for giant disc db @@ -83,9 +82,9 @@ int GdInitDatabase( MYSQL *db ) return 0; } -vector *GdGetStoredPlaylists(MYSQL db) +std::vector *GdGetStoredPlaylists(MYSQL db) { - vector* list = new vector(); + std::vector* list = new std::vector(); MYSQL_RES *result; MYSQL_ROW row; @@ -101,13 +100,13 @@ vector *GdGetStoredPlaylists(MYSQL db) gdFilterSets::gdFilterSets() { mgFilter* filter; - vector* set; - vector* rating; + std::vector* set; + std::vector* rating; m_titles.push_back( tr("Track Search") ); // create an initial set of filters with empty values - set = new vector(); - rating = new vector(); + set = new std::vector(); + rating = new std::vector(); rating->push_back("-"); rating->push_back("O"); rating->push_back("+"); @@ -135,7 +134,7 @@ gdFilterSets::gdFilterSets() m_titles.push_back(tr("Album Search")); - set = new vector(); + set = new std::vector(); // year-from filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); // year-to @@ -153,7 +152,7 @@ gdFilterSets::gdFilterSets() m_titles.push_back(tr("Playlist Search")); - set = new vector(); + set = new std::vector(); // year-from filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); // year-to @@ -182,9 +181,9 @@ gdFilterSets::~gdFilterSets() // everything is done in the destructor of the base class } -string gdFilterSets::computeRestriction(int *viewPrt) +std::string gdFilterSets::computeRestriction(int *viewPrt) { - string sql_str = "1"; + std::string sql_str = "1"; switch( m_activeSetId ) { @@ -209,7 +208,7 @@ string gdFilterSets::computeRestriction(int *viewPrt) } break; } - for( vector::iterator iter = m_activeSet->begin(); + for( std::vector::iterator iter = m_activeSet->begin(); iter != m_activeSet->end(); iter++ ) { @@ -285,7 +284,7 @@ string gdFilterSets::computeRestriction(int *viewPrt) } } } - mgDebug(1, "Applying sql string %s (view=%d)", sql_str.c_str(), *viewPrt ); + mgDebug(1, "Applying sql std::string %s (view=%d)", sql_str.c_str(), *viewPrt ); return sql_str; } @@ -358,7 +357,7 @@ bool mgGdTrack::readData() m_artist = row[0]; m_album = row[1]; m_title = row[2]; - m_mp3file = string( the_setup.ToplevelDir ) + row[3]; + m_mp3file = std::string( the_setup.ToplevelDir ) + row[3]; m_genre = row[4]; if( sscanf( row[5], "%d", &m_year) != 1 ) @@ -393,7 +392,7 @@ bool mgGdTrack::readData() return true; } -string mgGdTrack::getSourceFile() +std::string mgGdTrack::getSourceFile() { if( !m_retrieved ) { @@ -402,7 +401,7 @@ string mgGdTrack::getSourceFile() return m_mp3file; } -string mgGdTrack::getTitle() +std::string mgGdTrack::getTitle() { if( !m_retrieved ) { @@ -411,7 +410,7 @@ string mgGdTrack::getTitle() return m_title; } -string mgGdTrack::getArtist() +std::string mgGdTrack::getArtist() { if(!m_retrieved) { @@ -430,7 +429,7 @@ int mgGdTrack::getLength() } -string mgGdTrack::getLabel(int col) +std::string mgGdTrack::getLabel(int col) { if( !m_retrieved ) { @@ -451,17 +450,17 @@ string mgGdTrack::getLabel(int col) } } -vector *mgGdTrack::getTrackInfo() +std::vector *mgGdTrack::getTrackInfo() { - return new vector(); + return new std::vector(); } -bool mgGdTrack::setTrackInfo(vector *info) +bool mgGdTrack::setTrackInfo(std::vector *info) { return false; } -string mgGdTrack::getAlbum() +std::string mgGdTrack::getAlbum() { if( !m_retrieved ) { @@ -470,7 +469,7 @@ string mgGdTrack::getAlbum() return m_album; } -string mgGdTrack::getGenre() +std::string mgGdTrack::getGenre() { if(!m_retrieved) { @@ -524,7 +523,7 @@ int mgGdTrack::getChannels() return m_channels; } -string mgGdTrack::getBitrate() +std::string mgGdTrack::getBitrate() { if(!m_retrieved) { @@ -533,27 +532,27 @@ string mgGdTrack::getBitrate() return m_bitrate; } -string mgGdTrack::getImageFile() +std::string mgGdTrack::getImageFile() { return "dummyImg.jpg"; } -void mgGdTrack::setTitle(string new_title) +void mgGdTrack::setTitle(std::string new_title) { m_title = new_title; } -void mgGdTrack::setArtist(string new_artist) +void mgGdTrack::setArtist(std::string new_artist) { m_artist = new_artist; } -void mgGdTrack::setAlbum(string new_album) +void mgGdTrack::setAlbum(std::string new_album) { m_album = new_album; } -void mgGdTrack::setGenre(string new_genre) +void mgGdTrack::setGenre(std::string new_genre) { m_genre = new_genre; } @@ -578,7 +577,7 @@ bool mgGdTrack::writeData() return true; } -GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) +GdTracklist::GdTracklist(MYSQL db_handle, std::string restrictions) { MYSQL_RES *result; MYSQL_ROW row; @@ -603,7 +602,7 @@ GdTracklist::GdTracklist(MYSQL db_handle, string restrictions) } } -GdPlaylist::GdPlaylist(string listname, MYSQL db_handle) +GdPlaylist::GdPlaylist(std::string listname, MYSQL db_handle) { MYSQL_RES *result; MYSQL_ROW row; @@ -707,7 +706,7 @@ int GdPlaylist::insertDataFromSQL() bool GdPlaylist::storePlaylist() { - vector::iterator iter; + std::vector::iterator iter; int num; MYSQL_RES *result; MYSQL_ROW row; @@ -792,7 +791,7 @@ int GdPlaylist::getPlayTimeRemaining() /*! * \brief constructor */ -GdTreeNode::GdTreeNode(MYSQL db, int view, string filters) +GdTreeNode::GdTreeNode(MYSQL db, int view, std::string filters) : mgSelectionTreeNode(db, view) { // create a root node @@ -803,9 +802,9 @@ GdTreeNode::GdTreeNode(MYSQL db, int view, string filters) } GdTreeNode::GdTreeNode( mgSelectionTreeNode* parent, - string id, - string label, - string restriction ) + std::string id, + std::string label, + std::string restriction ) : mgSelectionTreeNode(parent, id, label) { m_restriction = restriction; @@ -908,14 +907,14 @@ bool GdTreeNode::expand() char idbuf[255]; int numchild; - string labelfield; // human readable db field for the column to be expanded - string idfield; // unique id field for the column to be expanded - string new_restriction_field; // field to be restricted by the new level - string new_restriction; // complete restriction str for the current child - string new_label; + std::string labelfield; // human readable db field for the column to be expanded + std::string idfield; // unique id field for the column to be expanded + std::string new_restriction_field; // field to be restricted by the new level + std::string new_restriction; // complete restriction str for the current child + std::string new_label; GdTreeNode* new_child; - string tables; // stores the db tables used + std::string tables; // stores the db tables used #define FROMJOIN " FROM tracks, genre as genre1, genre as genre2, album WHERE tracks.sourceid=album.cddbid AND genre1.id=tracks.genre1 AND genre2.id=tracks.genre2 AND %s " @@ -1215,8 +1214,8 @@ bool GdTreeNode::expand() // diese die den Filterkriterien entsprechen. // das betrifft nur die Search Views! - string row0 = mgDB::escape_string( &m_db, string( row[0] ) ); - string row1 = mgDB::escape_string( &m_db, string( row[1] ) ); + std::string row0 = mgDB::escape_string( &m_db, std::string( row[0] ) ); + std::string row1 = mgDB::escape_string( &m_db, std::string( row[1] ) ); if( m_view < 100 ) { @@ -1229,7 +1228,7 @@ bool GdTreeNode::expand() } new_child = new GdTreeNode(this, // parent - (string) idbuf, // id + (std::string) idbuf, // id // row[0], // label, row0, new_restriction); @@ -1282,16 +1281,16 @@ bool GdTreeNode::expand() /*! * \brief iterate all children recursively to find the tracks */ -vector* GdTreeNode::getTracks() +std::vector* GdTreeNode::getTracks() { MYSQL_ROW row; MYSQL_RES *result; int nrows; int nfields; - vector* retlist; + std::vector* retlist; int trackid; - retlist = new vector(); + retlist = new std::vector(); // get all tracks satisying the restrictions of this node mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str()); diff --git a/mg_content_interface.c b/mg_content_interface.c index 3f502ab..af16389 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -15,10 +15,10 @@ */ #define DEBUG +#include #include "mg_content_interface.h" #include "mg_tools.h" -using namespace std; //! \brief a special item representing an undefined state mgContentItem mgContentItem::UNDEFINED = mgContentItem(); @@ -31,7 +31,7 @@ mgTracklist::mgTracklist() mgTracklist::~mgTracklist() { mgContentItem* ptr; - vector::iterator iter; + std::vector::iterator iter; for( iter = m_list.begin(); iter != m_list.end(); iter++ ) { @@ -41,7 +41,7 @@ mgTracklist::~mgTracklist() m_list.clear(); } -vector *mgTracklist::getAll() +std::vector *mgTracklist::getAll() { return &m_list; } @@ -60,7 +60,7 @@ void mgTracklist::sortBy(int col, bool direction) { } -void mgTracklist::setDisplayColumns(vector cols) +void mgTracklist::setDisplayColumns(std::vector cols) { m_columns = cols; } @@ -70,9 +70,9 @@ unsigned int mgTracklist::getNumColumns() return m_columns.size(); } -string mgTracklist::getLabel(unsigned int position, const string separator) +std::string mgTracklist::getLabel(unsigned int position, const std::string separator) { - string label = ""; + std::string label = ""; mgContentItem* item; if( position >= m_list.size() ) @@ -84,7 +84,7 @@ string mgTracklist::getLabel(unsigned int position, const string separator) item = *( m_list.begin() + position ); } - for( vector::iterator iter = m_columns.begin(); + for( std::vector::iterator iter = m_columns.begin(); iter != m_columns.end(); iter++ ) { if( iter != m_columns.begin() ) @@ -111,7 +111,7 @@ bool mgTracklist::remove(unsigned int position) if( position < m_list.size() ) { - vector::iterator iter; + std::vector::iterator iter; iter = m_list.begin() + position; m_list.erase(iter); @@ -125,7 +125,7 @@ bool mgTracklist::remove(unsigned int position) int mgTracklist::remove(mgContentItem* item) { int retval = 0; - vector::iterator iter; + std::vector::iterator iter; for( iter = m_list.begin(); iter != m_list.end (); iter++ ) { @@ -149,7 +149,7 @@ mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view) m_label = "ROOT"; m_expanded = false; } -mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, string id, string label) +mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, std::string id, std::string label) { m_parent = parent; m_level = m_parent->m_level+1; @@ -181,7 +181,7 @@ mgSelectionTreeNode* mgSelectionTreeNode::getParent() void mgSelectionTreeNode::collapse() // removes all children (recursively) { - vector ::iterator iter; + std::vector ::iterator iter; mgSelectionTreeNode* ptr; for(iter = m_children.begin(); iter != m_children.end();iter++) @@ -193,23 +193,23 @@ void mgSelectionTreeNode::collapse() // removes all children (recursively) m_children.clear(); } -vector &mgSelectionTreeNode::getChildren() +std::vector &mgSelectionTreeNode::getChildren() { // mgDebug(5," returning %d children", m_children.size()); return m_children; } -string mgSelectionTreeNode::getID() +std::string mgSelectionTreeNode::getID() { return m_id; } -string mgSelectionTreeNode::getLabel() +std::string mgSelectionTreeNode::getLabel() { return m_label; } -string mgSelectionTreeNode::getLabel(int n) +std::string mgSelectionTreeNode::getLabel(int n) { mgSelectionTreeNode* node = this; int d = m_level; @@ -223,7 +223,7 @@ string mgSelectionTreeNode::getLabel(int n) return node->m_label; } -string mgSelectionTreeNode::getRestrictions() +std::string mgSelectionTreeNode::getRestrictions() { return m_restriction; } diff --git a/mg_database.c b/mg_database.c index 3ad6fc9..fa38e17 100644 --- a/mg_database.c +++ b/mg_database.c @@ -9,14 +9,13 @@ #include "mg_database.h" -using namespace std; mgDB::mgDB() { } -mgDB::mgDB(string host, string name, - string user, string pass, +mgDB::mgDB(std::string host, std::string name, + std::string user, std::string pass, int port) { } @@ -31,13 +30,13 @@ MYSQL mgDB::getDBHandle() return m_dbase; } -string mgDB::escape_string( MYSQL *db, string s ) +std::string mgDB::escape_string( MYSQL *db, std::string s ) { char *escbuf = (char *) malloc( 2*s.size() + 1 ); int len = mysql_real_escape_string( db, escbuf, s.c_str(), s.size() ); - string r = string( escbuf ); + std::string r = std::string( escbuf ); free( escbuf ); return r; diff --git a/mg_filters.c b/mg_filters.c index f8c164d..e0457da 100644 --- a/mg_filters.c +++ b/mg_filters.c @@ -3,17 +3,17 @@ * \brief ******************************************************************** * \version $Revision: 1.3 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author: lvw $ + * \author file owner: $Author$ */ /*******************************************************************/ /* makes sure we dont parse the same declarations twice */ +#include #include "mg_filters.h" #include "mg_tools.h" -using namespace std; //------------------------------------------------------------------- // mgFilter @@ -54,12 +54,12 @@ mgFilterInt::~mgFilterInt() { } -string mgFilterInt::getStrVal() +std::string mgFilterInt::getStrVal() { char buffer[20]; sprintf(buffer, "%d", m_intval); - return (string)buffer; + return (std::string)buffer; } int mgFilterInt::getIntVal() @@ -109,7 +109,7 @@ bool mgFilterInt::isSet() // mgFilterString //------------------------------------------------------------------- mgFilterString::mgFilterString(const char *name, const char* value, - int maxlen, string allowedchar) + int maxlen, std::string allowedchar) : mgFilter(name) { m_type = STRING; @@ -132,14 +132,14 @@ int mgFilterString::getMaxLength() return m_maxlen; } -string mgFilterString::getAllowedChars() +std::string mgFilterString::getAllowedChars() { return m_allowedchar; } -string mgFilterString::getStrVal() +std::string mgFilterString::getStrVal() { - return (string) m_strval; + return (std::string) m_strval; } void mgFilterString::store() { @@ -172,7 +172,7 @@ bool mgFilterString::isSet() // mgFilterBool //------------------------------------------------------------------- mgFilterBool::mgFilterBool(const char *name, bool value, - string truestr, string falsestr) + std::string truestr, std::string falsestr) : mgFilter(name) { m_type = BOOL; @@ -188,7 +188,7 @@ mgFilterBool::~mgFilterBool() { } -string mgFilterBool::getStrVal() +std::string mgFilterBool::getStrVal() { if(m_bval) return "true"; @@ -201,12 +201,12 @@ int mgFilterBool::getIntVal() return (int) m_bval; } -string mgFilterBool::getTrueString() +std::string mgFilterBool::getTrueString() { return m_truestr; } -string mgFilterBool::getFalseString() +std::string mgFilterBool::getFalseString() { return m_falsestr; } @@ -243,7 +243,7 @@ bool mgFilterBool::isSet() //------------------------------------------------------------------- // mgFilterChoice //------------------------------------------------------------------- -mgFilterChoice::mgFilterChoice(const char *name, int value, vector *choices) +mgFilterChoice::mgFilterChoice(const char *name, int value, std::vector *choices) : mgFilter(name) { m_type = CHOICE; @@ -260,7 +260,7 @@ mgFilterChoice::~mgFilterChoice() m_choices.clear(); } -string mgFilterChoice::getStrVal() +std::string mgFilterChoice::getStrVal() { if( m_selval < 0 || m_selval >= (int) m_choices.size() ) { @@ -268,7 +268,7 @@ string mgFilterChoice::getStrVal() } return m_choices[m_selval]; } -vector &mgFilterChoice::getChoices() +std::vector &mgFilterChoice::getChoices() { return m_choices; } diff --git a/mg_media.c b/mg_media.c index 6cf5339..2b25b73 100644 --- a/mg_media.c +++ b/mg_media.c @@ -17,7 +17,6 @@ #include "vdr_setup.h" -using namespace std; //------------------------------------------------------------------- // mgFilterSets @@ -35,12 +34,12 @@ mgFilterSets::mgFilterSets() mgFilterSets::~mgFilterSets() { - vector *set; - for(vector*>::iterator iter1 = m_sets.begin(); + std::vector *set; + for(std::vector*>::iterator iter1 = m_sets.begin(); iter1 != m_sets.end(); iter1++) { set = *iter1; - for(vector::iterator iter2 = set->begin(); + for(std::vector::iterator iter2 = set->begin(); iter2 != set->end(); iter2++) { delete (*iter2); @@ -99,7 +98,7 @@ void mgFilterSets::select(int n) ********************************************************************/ void mgFilterSets::clear() { - for(vector::iterator iter = m_activeSet->begin(); + for(std::vector::iterator iter = m_activeSet->begin(); iter != m_activeSet->end(); iter++) { (*iter)->clear(); @@ -111,7 +110,7 @@ void mgFilterSets::select(int n) ********************************************************************/ void mgFilterSets::accept() { - for(vector::iterator iter = m_activeSet->begin(); + for(std::vector::iterator iter = m_activeSet->begin(); iter != m_activeSet->end(); iter++) { (*iter)->store(); @@ -125,9 +124,9 @@ void mgFilterSets::select(int n) * the application may temporarily modify the filter values * accept() needs to be called to memorize the changed values ********************************************************************/ - vector *mgFilterSets::getFilters() + std::vector *mgFilterSets::getFilters() { - for(vector::iterator iter = m_activeSet->begin(); + for(std::vector::iterator iter = m_activeSet->begin(); iter != m_activeSet->end(); iter++) { (*iter)->restore(); @@ -139,7 +138,7 @@ void mgFilterSets::select(int n) /*! * \brief return title of the active filter set */ -string mgFilterSets::getTitle() +std::string mgFilterSets::getTitle() { if(m_activeSetId < (int) m_titles.size()) { @@ -147,7 +146,7 @@ string mgFilterSets::getTitle() } else { - mgWarning("Implementation error: No title string for filter set %d", + mgWarning("Implementation error: No title std::string for filter set %d", m_activeSetId); return "NO-TITLE"; } @@ -200,7 +199,7 @@ mgMedia::~mgMedia() } } -string mgMedia::getMediaTypeName() +std::string mgMedia::getMediaTypeName() { switch(m_mediatype) { @@ -226,11 +225,11 @@ mgSelectionTreeNode* mgMedia::getSelectionRoot() mgPlaylist* mgMedia::createTemporaryPlaylist() { - string tmpname = "current"; + std::string tmpname = "current"; return loadPlaylist( tmpname ); } -mgPlaylist* mgMedia::loadPlaylist(string name) +mgPlaylist* mgMedia::loadPlaylist(std::string name) { mgPlaylist *list; switch( m_mediatype ) @@ -250,7 +249,7 @@ mgPlaylist* mgMedia::loadPlaylist(string name) /*! * \brief Obtain a list of stored playlists */ -vector *mgMedia::getStoredPlaylists() +std::vector *mgMedia::getStoredPlaylists() { switch(m_mediatype) { @@ -260,15 +259,15 @@ vector *mgMedia::getStoredPlaylists() } break; } mgError("implementation Error"); // we should never get here - return new vector(); + return new std::vector(); } /*! * \brief obtain the indices of columns which are presented by default */ -vector mgMedia::getDefaultCols() +std::vector mgMedia::getDefaultCols() { - vector cols; + std::vector cols; switch(m_mediatype) { case GD_MP3: @@ -324,7 +323,7 @@ void mgMedia::initFilterSet(int num) * * Note: Modifications become only effective by calling applyActiveFilter() ********************************************************************/ -vector *mgMedia::getActiveFilters() +std::vector *mgMedia::getActiveFilters() { if(!m_filters) { @@ -336,7 +335,7 @@ vector *mgMedia::getActiveFilters() /*! * \brief returns title of the active filter set */ -string mgMedia::getActiveFilterTitle() +std::string mgMedia::getActiveFilterTitle() { switch(m_mediatype) diff --git a/mg_playlist.c b/mg_playlist.c index 3c7dae0..a63519e 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -13,13 +13,13 @@ * making it persistent in some database. */ +#include #include "mg_playlist.h" #include "mg_tools.h" #include #include -using namespace std; /* ==== constructors ==== */ @@ -33,7 +33,7 @@ mgPlaylist::mgPlaylist() m_listname = buffer; } -mgPlaylist::mgPlaylist(string listname) +mgPlaylist::mgPlaylist(std::string listname) { m_current_idx = -1; m_listname = listname; @@ -67,9 +67,9 @@ void mgPlaylist::append(mgContentItem* item) } /* append a list of tracks at the end of the playlist */ -void mgPlaylist::appendList( vector *tracks ) +void mgPlaylist::appendList( std::vector *tracks ) { - vector::iterator iter; + std::vector::iterator iter; mgDebug( 3, "Adding %d tracks to the playlist", tracks->size() ); @@ -116,7 +116,7 @@ void mgPlaylist::clear() { // TODO: who takes care of memory allocation/deallocation of mgItems? - vector::iterator iter; + std::vector::iterator iter; for( iter = m_list.begin(); iter != m_list.end(); iter++ ) { // delete each item in the list @@ -132,8 +132,8 @@ void mgPlaylist::clear() void mgPlaylist::move( int from, int to ) { - vector::iterator from_iter = m_list.begin() + from; - vector::iterator to_iter = m_list.begin() + to; + std::vector::iterator from_iter = m_list.begin() + from; + std::vector::iterator to_iter = m_list.begin() + to; m_list.insert( to_iter, *from_iter); m_list.erase( from_iter ); @@ -149,12 +149,12 @@ void mgPlaylist::move( int from, int to ) } /*==== access tracks ====*/ -string mgPlaylist::getListname() +std::string mgPlaylist::getListname() { return m_listname; } -void mgPlaylist::setListname(string name) +void mgPlaylist::setListname(std::string name) { m_listname = name; } @@ -239,9 +239,9 @@ mgContentItem* mgPlaylist::sneakNext() } } -bool mgPlaylist::exportM3U( string m3u_file ) +bool mgPlaylist::exportM3U( std::string m3u_file ) { - vector::iterator iter; + std::vector::iterator iter; bool result = true; // open a file for writing diff --git a/mg_tools.c b/mg_tools.c index 5b20d70..02df30b 100644 --- a/mg_tools.c +++ b/mg_tools.c @@ -10,12 +10,12 @@ #include "mg_tools.h" -extern "C" -{ +/*extern "C" +{*/ #include #include -} - +/*} +*/ #include #define MAX_BUFLEN 2048 diff --git a/muggle.c b/muggle.c index a952c43..94dc4ed 100644 --- a/muggle.c +++ b/muggle.c @@ -10,10 +10,6 @@ * $Id$ */ -#include - -#include - #include "muggle.h" #include "vdr_menu.h" @@ -23,12 +19,13 @@ #include "mg_media.h" #include "i18n.h" +#include +#include static const char *VERSION = "0.0.4"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; -using namespace std; const char* mgMuggle::Version(void) { @@ -128,7 +125,7 @@ bool mgMuggle::ProcessArgs(int argc, char *argv[]) { if (optarg[strlen(optarg) - 1] != '/') { - string res = string(optarg) + "/"; + std::string res = std::string(optarg) + "/"; the_setup.ToplevelDir = strdup( res.c_str() ); } else diff --git a/muggle.h b/muggle.h index be074c8..3c61646 100644 --- a/muggle.h +++ b/muggle.h @@ -32,7 +32,7 @@ #ifndef _MUGGLE_H #define _MUGGLE_H - +#include #include class mgMedia; diff --git a/mugglei.c b/mugglei.c index 21aa90e..efc4c07 100755 --- a/mugglei.c +++ b/mugglei.c @@ -4,9 +4,8 @@ * * \author Lars von Wedel */ - +//#define VERBOSE #include -using namespace std; #include #include @@ -22,7 +21,7 @@ using namespace std; MYSQL *db; -string host, user, pass, dbname, socket; +std::string host, user, pass, dbname, socket; bool import_assorted; int init_database() @@ -31,7 +30,7 @@ int init_database() if( db == NULL ) { - cout << "mysql_init failed." << endl; + std::cout << "mysql_init failed." << std::endl; return -1; } @@ -42,7 +41,7 @@ int init_database() 0, socket.c_str(), 0 ) == NULL ) { - cout << "mysql_real_connect using sockets failed." << endl; + std::cout << "mysql_real_connect using sockets failed." << std::endl; return -2; } } @@ -51,7 +50,7 @@ int init_database() if( mysql_real_connect( db, host.c_str(), user.c_str(), pass.c_str(), dbname.c_str(), 0, NULL, 0 ) == NULL ) { - cout << "mysql_real_connect via TCP failed." << endl; + std::cout << "mysql_real_connect via TCP failed." << std::endl; return -2; } } @@ -59,7 +58,7 @@ int init_database() return 0; } -time_t get_fs_modification_time( string filename ) +time_t get_fs_modification_time( std::string filename ) { struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); @@ -79,7 +78,7 @@ time_t get_db_modification_time( long uid ) MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id=\"%d\"", uid ); MYSQL_ROW row = mysql_fetch_row( result ); - string mod_time = row[0]; + std::string mod_time = row[0]; mt = (time_t) atol( mod_time.c_str() ); return mt; @@ -95,7 +94,7 @@ TagLib::String escape_string( MYSQL *db, TagLib::String s ) return TagLib::String( escbuf ); } -long find_file_in_database( MYSQL *db, string filename ) +long find_file_in_database( MYSQL *db, std::string filename ) { TagLib::String file = TagLib::String( filename.c_str() ); file = escape_string( db, file ); @@ -108,7 +107,7 @@ long find_file_in_database( MYSQL *db, string filename ) } // read tags from the mp3 file and store them into the corresponding database entry -void update_db( long uid, string filename ) +void update_db( long uid, std::string filename ) { TagLib::String title, album, artist, genre, cddbid; uint trackno, year; @@ -118,7 +117,7 @@ void update_db( long uid, string filename ) if( !f.isNull() && f.tag() ) { - // cout << "Evaluating " << filename << endl; + // std::cout << "Evaluating " << filename << std::endl; TagLib::Tag *tag = f.tag(); // obtain tag information @@ -220,7 +219,7 @@ void update_db( long uid, string filename ) if( uid > 0 ) { // the entry is known to exist already, hence update it - mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%s\"," + mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%d\"," "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\"," "samplerate=%d, channels=%d WHERE id=%d", artist.toCString(), title.toCString(), year, @@ -236,25 +235,25 @@ void update_db( long uid, string filename ) trackno, filename.c_str(), len, bitrate, sample, channels ); #ifdef VERBOSE - cout << "-- TAG --" << endl; - cout << "title - \"" << tag->title() << "\"" << endl; - cout << "artist - \"" << tag->artist() << "\"" << endl; - cout << "album - \"" << tag->album() << "\"" << endl; - cout << "year - \"" << tag->year() << "\"" << endl; - cout << "comment - \"" << tag->comment() << "\"" << endl; - cout << "track - \"" << tag->track() << "\"" << endl; - cout << "genre - \"" << tag->genre() << "\"" << endl; + std::cout << "-- TAG --" << std::endl; + std::cout << "title - \"" << tag->title() << "\"" << std::endl; + std::cout << "artist - \"" << tag->artist() << "\"" << std::endl; + std::cout << "album - \"" << tag->album() << "\"" << std::endl; + std::cout << "year - \"" << tag->year() << "\"" << std::endl; + std::cout << "comment - \"" << tag->comment() << "\"" << std::endl; + std::cout << "track - \"" << tag->track() << "\"" << std::endl; + std::cout << "genre - \"" << tag->genre() << "\"" << std::endl; #endif } } } -void update_tags( long uid, string filename ) +void update_tags( long uid, std::string filename ) { } -void evaluate_file( MYSQL *db, string filename ) +void evaluate_file( MYSQL *db, std::string filename ) { if( 0 == init_database() ) { @@ -293,22 +292,22 @@ void evaluate_file( MYSQL *db, string filename ) int main( int argc, char *argv[] ) { int option_index; - string filename; + std::string filename; if( argc < 2 ) { // we need at least a filename! - cout << "mugglei -- import helper for Muggle VDR plugin" << endl; - cout << "(C) Lars von Wedel" << endl; - cout << "This is free software; see the source for copying conditions." << endl; - cout << "" << endl; - cout << "Options:" << endl; - cout << " -h - specify host of mySql database server (default is 'localhost')" << endl; - cout << " -s - specify a socket for mySQL communication (default is TCP)" << endl; - cout << " -n - specify database name (default is 'GiantDisc')" << endl; - cout << " -u - specify user of mySql database (default is empty)" << endl; - cout << " -p - specify password of user (default is empty password)" << endl; - cout << " -f - name of music file to import" << endl; - cout << " -a - import track as if it was on an assorted album" << endl; + std::cout << "mugglei -- import helper for Muggle VDR plugin" << std::endl; + std::cout << "(C) Lars von Wedel" << std::endl; + std::cout << "This is free software; see the source for copying conditions." << std::endl; + std::cout << "" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h - specify host of mySql database server (default is 'localhost')" << std::endl; + std::cout << " -s - specify a socket for mySQL communication (default is TCP)" << std::endl; + std::cout << " -n - specify database name (default is 'GiantDisc')" << std::endl; + std::cout << " -u - specify user of mySql database (default is empty)" << std::endl; + std::cout << " -p - specify password of user (default is empty password)" << std::endl; + std::cout << " -f - name of music file to import" << std::endl; + std::cout << " -a - import track as if it was on an assorted album" << std::endl; exit( 1 ); } @@ -381,7 +380,7 @@ int main( int argc, char *argv[] ) } else { - cout << "Database initialization failed. Exiting.\n" << endl; + std::cout << "Database initialization failed. Exiting.\n" << std::endl; } return res; diff --git a/vdr_decoder.c b/vdr_decoder.c index 1c0b154..0384031 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -21,20 +21,18 @@ #include #include -#include -#include - #include "vdr_decoder.h" #include "vdr_decoder_mp3.h" #include "vdr_decoder_ogg.h" #include "mg_content_interface.h" -using namespace std; +#include +#include // --- mgDecoders --------------------------------------------------------------- -mgMediaType mgDecoders::getMediaType( string s ) +mgMediaType mgDecoders::getMediaType( std::string s ) { mgMediaType mt = MT_UNKNOWN; @@ -62,7 +60,7 @@ mgDecoder *mgDecoders::findDecoder( mgContentItem *item ) { mgDecoder *decoder = 0; - string filename = item->getSourceFile(); + std::string filename = item->getSourceFile(); switch( getMediaType( filename ) ) { diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c index 20fca5e..eb5cb42 100644 --- a/vdr_decoder_mp3.c +++ b/vdr_decoder_mp3.c @@ -32,7 +32,6 @@ #define d(x) x -using namespace std; // ---------------------------------------------------------------- diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c index 9525979..94bd37d 100644 --- a/vdr_decoder_ogg.c +++ b/vdr_decoder_ogg.c @@ -22,7 +22,6 @@ #include "mg_content_interface.h" -using namespace std; // --- mgOggFile ---------------------------------------------------------------- @@ -36,11 +35,11 @@ class mgOggFile // : public mgFileInfo void error( const char *action, const int err ); - string m_filename; + std::string m_filename; public: - mgOggFile( string filename ); + mgOggFile( std::string filename ); ~mgOggFile(); bool open(bool log=true); @@ -56,7 +55,7 @@ class mgOggFile // : public mgFileInfo long long indexMs(void); }; -mgOggFile::mgOggFile( string filename ) : +mgOggFile::mgOggFile( std::string filename ) : m_filename( filename ) { m_canSeek = false; diff --git a/vdr_menu.c b/vdr_menu.c index fe805b4..a680f9f 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -10,11 +10,11 @@ * $Id$ */ +#include #include #include #include - #include #include #include @@ -38,7 +38,6 @@ #include "gd_content_interface.h" -using namespace std; // ----------------------- mgMenuTreeItem ------------------ @@ -155,7 +154,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { mgDebug( 1, "mgMainMenu: add selection %s to playlist", current->getLabel().c_str() ); // Add selection to Play - vector *tracks = current->getTracks(); + std::vector *tracks = current->getTracks(); if( tracks ) { @@ -335,7 +334,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) m_current_playlist -> clear(); delete m_current_playlist; - string selected = (*m_plists)[ Current() ]; + std::string selected = (*m_plists)[ Current() ]; m_current_playlist = m_media->loadPlaylist( selected.c_str() ); // clean the list of playlist @@ -499,9 +498,9 @@ void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) SetButtons(); m_node = node; - vector children = node->getChildren(); + std::vector children = node->getChildren(); - for( vector::iterator iter = children.begin(); + for( std::vector::iterator iter = children.begin(); iter != children.end(); iter ++ ) { @@ -560,7 +559,7 @@ eOSState mgMainMenu::TreeSubmenuAction( int n ) m_current_playlist->clear(); // append current node - vector *tracks = current->getTracks(); + std::vector *tracks = current->getTracks(); if( tracks ) { @@ -594,7 +593,7 @@ void mgMainMenu::DisplayPlaylist( int index_current ) Clear(); SetButtons(); - vector* list = m_current_playlist-> getAll(); + std::vector* list = m_current_playlist-> getAll(); static char titlestr[80]; sprintf( titlestr, "Muggle - %s (%d %s)",tr("Playlist"), list->size() , @@ -603,7 +602,7 @@ void mgMainMenu::DisplayPlaylist( int index_current ) for( unsigned int i = 0; i < m_current_playlist->getNumItems(); i++) { - string label = m_current_playlist->getLabel( i, " " ); + std::string label = m_current_playlist->getLabel( i, " " ); Add( new cOsdItem( label.c_str() ) ); } @@ -632,7 +631,7 @@ void mgMainMenu::LoadPlaylist() // retrieve list of available playlists m_plists = m_media->getStoredPlaylists(); - for(vector::iterator iter = m_plists->begin(); + for(std::vector::iterator iter = m_plists->begin(); iter != m_plists->end() ; iter++) { Add( new cOsdItem( iter->c_str() ) ); @@ -730,7 +729,7 @@ eOSState mgMainMenu::ExecutePlaylistCommand( int current ) #endif free( buffer ); - string tmp_m3u_file = (char *) AddDirectory( cPlugin::ConfigDirectory("muggle"), "current.m3u" ); + std::string tmp_m3u_file = (char *) AddDirectory( cPlugin::ConfigDirectory("muggle"), "current.m3u" ); m_current_playlist->exportM3U( tmp_m3u_file ); char *result = (char *)command->Execute( tmp_m3u_file.c_str() ); @@ -752,7 +751,7 @@ eOSState mgMainMenu::ExecutePlaylistCommand( int current ) eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { - cout << "mgMainMenu::PlaylistSubmenuAction: " << n << endl << flush; + std::cout << "mgMainMenu::PlaylistSubmenuAction: " << n << std::endl << std::flush; eOSState state = osContinue; switch( n ) @@ -797,7 +796,7 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) { // clear playlist cControl *control = cControl::Control(); - string buffer; + std::string buffer; if( control && typeid(*control) == typeid(mgPlayerControl) ) { @@ -832,7 +831,7 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) DisplayPlaylist( m_last_osd_index ); // confirmation - string confirm = res? "Entry deleted": "Cannot delete entry"; + std::string confirm = res? "Entry deleted": "Cannot delete entry"; #if VDRVERSNUM >= 10307 Skins.Message( mtInfo, confirm.c_str() ); @@ -844,7 +843,7 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) } break; case 5: { - string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), + std::string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), m_current_playlist->getListname().c_str() ); m_current_playlist->exportM3U( m3u_file ); } break; @@ -869,10 +868,10 @@ void mgMainMenu::DisplayFilter() SetButtons(); SetTitle( m_media->getActiveFilterTitle().c_str() ); - vector *filter_list = m_media->getActiveFilters(); + std::vector *filter_list = m_media->getActiveFilters(); int i=0; - for( vector::iterator iter = filter_list->begin(); + for( std::vector::iterator iter = filter_list->begin(); iter != filter_list->end(); iter ++ ) { @@ -912,12 +911,12 @@ void mgMainMenu::DisplayFilter() case mgFilter::CHOICE: { mgFilterChoice *fc = (mgFilterChoice *) (*iter); - vector choices = fc->getChoices(); + std::vector choices = fc->getChoices(); char **choices_str = new (char *)[ choices.size() ]; int j = 0; - for( vector::iterator iter = choices.begin(); + for( std::vector::iterator iter = choices.begin(); iter != choices.end(); iter ++, j ++ ) { diff --git a/vdr_menu.h b/vdr_menu.h index e827a33..45f60bd 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -13,11 +13,11 @@ #ifndef _VDR_MENU_H #define _VDR_MENU_H +#include #include #include #include - #include "i18n.h" class cCommands; @@ -107,10 +107,10 @@ class mgMainMenu : public cOsdMenu mgSelectionTreeNode *m_root; mgSelectionTreeNode *m_node; mgPlaylist *m_current_playlist; - std::vector *m_plists; + std::vector< std::string > *m_plists; MuggleStatus m_state; - std::list m_history; + std::list< int > m_history; cCommands *m_playlist_commands; diff --git a/vdr_player.c b/vdr_player.c index e70b8fa..2ed1416 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -45,7 +45,6 @@ #include "mg_playlist.h" #include "mg_content_interface.h" -using namespace std; // ---------------------------------------------------------------- @@ -350,7 +349,7 @@ void mgPCMPlayer::Action(void) #ifdef DEBUG if(time(0)>=beat+30) { - cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer->Available() << endl << flush; + std::cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer->Available() << std::endl << std::flush; scale.Stats(); if(haslevel) norm.Stats(); beat=time(0); } @@ -369,7 +368,7 @@ void mgPCMPlayer::Action(void) if( m_playing && m_playing != &(mgContentItem::UNDEFINED) ) { - string filename = m_playing->getSourceFile(); + std::string filename = m_playing->getSourceFile(); // mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() ) @@ -436,7 +435,7 @@ void mgPCMPlayer::Action(void) static unsigned int oldrate=0; if(oldrate!=pcm->samplerate) { - cout << "mgPCMPlayer::Action: new input sample rate " << pcm->samplerate << endl << flush; + std::cout << "mgPCMPlayer::Action: new input sample rate " << pcm->samplerate << std::endl << std::flush; oldrate = pcm->samplerate; } } diff --git a/vdr_stream.c b/vdr_stream.c index d82877e..0c12075 100644 --- a/vdr_stream.c +++ b/vdr_stream.c @@ -3,11 +3,11 @@ * \brief Implementation of media stream classes * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_stream.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -31,7 +31,7 @@ // #include "i18n.h" // #include "version.h" -#define tr(x) x +//#define tr(x) x #ifdef USE_MMAP #include @@ -39,11 +39,10 @@ #define DEFAULT_PORT 80 // default port for streaming (HTTP) -using namespace std; // --- mgStream ----------------------------------------------------------------- -mgStream::mgStream( string filename ) +mgStream::mgStream( std::string filename ) : m_filename( filename ) { m_fd = -1; -- cgit v1.2.3 From 05f0b754ac1e951a65f6301b2749025ebe5d5849 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 4 Oct 2004 22:16:03 +0000 Subject: Going to 0.0.5 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@201 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 2 +- muggle.doxygen | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/muggle.c b/muggle.c index 94dc4ed..849e3eb 100644 --- a/muggle.c +++ b/muggle.c @@ -22,7 +22,7 @@ #include #include -static const char *VERSION = "0.0.4"; +static const char *VERSION = "0.0.5"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/muggle.doxygen b/muggle.doxygen index 3459584..ffac9c2 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.0.4 +PROJECT_NUMBER = 0.0.5 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. -- cgit v1.2.3 From ebdf27d9e7c5d167101adf3c9df5efe3d7d52586 Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 6 Oct 2004 06:28:44 +0000 Subject: Added more stuff to mg_database and added status display for gLCD git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@203 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 2 +- mg_database.c | 33 ++++++++++++- mg_database.h | 33 +++++++++++-- mg_playlist.c | 8 +++- mg_playlist.h | 5 +- vdr_player.c | 124 +++++++++++++++++++++++++++++-------------------- vdr_player.h | 36 +++++--------- 7 files changed, 158 insertions(+), 83 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 6fda5e4..e3ee5fc 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -73,7 +73,7 @@ int GdInitDatabase( MYSQL *db ) the_setup.DbPass, the_setup.DbName, the_setup.DbPort, - "", 0 ) == NULL ) + NULL, 0 ) == NULL ) { return -2; } // if mysql_real_connect diff --git a/mg_database.c b/mg_database.c index fa38e17..9dfa395 100644 --- a/mg_database.c +++ b/mg_database.c @@ -16,7 +16,7 @@ mgDB::mgDB() mgDB::mgDB(std::string host, std::string name, std::string user, std::string pass, - int port) + int port) { } @@ -41,3 +41,34 @@ std::string mgDB::escape_string( MYSQL *db, std::string s ) return r; } + +MYSQL_RES* mgDB::read_query( const char *fmt, ...) +{ + va_list ap; + va_start( ap, fmt ); + vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); + + if( mysql_query( &m_dbase, querybuf) ) + { + mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); + } + + MYSQL_RES *result = mysql_store_result( &m_dbase ); + + va_end(ap); + return result; +} + +void mgDB::write_query( const char *fmt, ... ) +{ + va_list ap; + va_start( ap, fmt ); + vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); + + if( mysql_query( &m_dbase, querybuf ) ) + { + mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); + } + + va_end(ap); +} diff --git a/mg_database.h b/mg_database.h index 3f2c323..54baef1 100644 --- a/mg_database.h +++ b/mg_database.h @@ -35,22 +35,47 @@ class mgDB * \param pass * \param port */ - mgDB( std::string host, std::string name, - std::string user, std::string pass, - int port ); + mgDB( std::string host, std::string name, + std::string user="", std::string pass="", + int port = 0 ); + + // add constructor for sockets /*! \brief constructor */ ~mgDB(); - /*! \brief obtain database handle*/ + /*! + * \brief obtain database handle + */ MYSQL getDBHandle(); + /*! + * \brief database initialization + */ + // int initialize(); + + /*! + * \brief helper function to execute read queries + * + * \todo Could be a member of mgDatabase? + */ + MYSQL_RES* read_query( const char *fmt, ... ); + + /*! + * \brief helper function to execute write queries + * + * \todo Could be a member of mgDatabase? + */ + void write_query( const char *fmt, ... ); + /*! * \brief escape arguments to be contained in a query * * \todo use m_dbase member of this class */ static std::string escape_string( MYSQL *db, std::string s ); + + private: MYSQL m_dbase; diff --git a/mg_playlist.c b/mg_playlist.c index a63519e..a0fbb0f 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -160,11 +160,17 @@ void mgPlaylist::setListname(std::string name) } // returns the count of items in the list -int mgPlaylist::count() +int mgPlaylist::getCount() { return m_list.size(); } +// returns current index in the playlist +int mgPlaylist::getIndex() const +{ + return m_current_idx; +} + // returns the current item of the list mgContentItem* mgPlaylist::getCurrent() { diff --git a/mg_playlist.h b/mg_playlist.h index 30443f4..e7c428d 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -128,7 +128,10 @@ public: virtual void setListname(std::string name); //! \brief returns the count of items in the list - int count(); + int getCount(); + + //! \brief returns current index in the playlist + int getIndex() const; /*! \brief returns the nth track from the playlist * diff --git a/vdr_player.c b/vdr_player.c index 2ed1416..865353e 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "vdr_player.h" #include "vdr_decoder.h" @@ -200,6 +201,7 @@ public: void NewPlaylist(mgPlaylist *plist); mgContentItem *GetCurrent () { return m_current; } + mgPlaylist *GetPlaylist () { return m_playlist; } }; mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist) @@ -221,6 +223,7 @@ mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist) m_index = 0; m_playing = 0; + m_current = 0; } mgPCMPlayer::~mgPCMPlayer() @@ -370,7 +373,7 @@ void mgPCMPlayer::Action(void) { std::string filename = m_playing->getSourceFile(); // mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); - + if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() ) { levelgood = true; @@ -853,11 +856,12 @@ void mgPCMPlayer::SkipSeconds(int secs) bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) { - bool res = false; - current = SecondsToFrames( m_index ); - total = SecondsToFrames( m_current->getLength() ); - - return res; + if(m_current) { + current = SecondsToFrames( m_index ); + total = SecondsToFrames( m_current->getLength() ); + return true; + } + return false; } // --- mgPlayerControl ------------------------------------------------------- @@ -872,10 +876,22 @@ mgPlayerControl::mgPlayerControl( mgPlaylist *plist ) #endif m_visible = false; m_has_osd = false; + + m_szLastShowStatusMsg = NULL; + // Notity all cStatusMonitor + StatusMsgReplaying(); } mgPlayerControl::~mgPlayerControl() { + // Notify cleanup all cStatusMonitor + cStatus::MsgReplaying(this, NULL); + if( m_szLastShowStatusMsg ) + { + free(m_szLastShowStatusMsg); + m_szLastShowStatusMsg = NULL; + } + Hide(); Stop(); } @@ -970,6 +986,8 @@ void mgPlayerControl::NewPlaylist(mgPlaylist *plist) void mgPlayerControl::ShowProgress() { + StatusMsgReplaying(); + if( m_visible ) { if( !m_has_osd ) @@ -1113,47 +1131,53 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) return osContinue; } -/************************************************************ - * - * $Log: vdr_player.c,v $ - * Revision 1.7 2004/07/27 20:50:54 lvw - * Playlist indexing now working - * - * Revision 1.6 2004/07/26 22:20:55 lvw - * Reworked playlist indexing - * - * Revision 1.5 2004/07/26 20:03:00 lvw - * Bug in initalizing playlist removed - * - * Revision 1.4 2004/07/25 21:33:35 lvw - * Removed bugs in finding track files and playlist indexing. - * - * Revision 1.3 2004/07/12 11:06:23 LarsAC - * No longer skip first file on playlist when starting replay. - * - * Revision 1.2 2004/05/28 15:29:19 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.1.2.19 2004/05/26 14:30:27 lvw - * Removed bug in finding correct mp3 file in GD mode - * - * Revision 1.1.2.18 2004/05/25 06:48:24 lvw - * Documentation and code polishing. - * - * Revision 1.1.2.17 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.1.2.16 2004/05/24 11:48:52 lvw - * Debugging info added to find deadlock - * - * Revision 1.1.2.15 2004/05/12 22:38:37 lvw - * Some cleanup - * - * Revision 1.1.2.14 2004/05/11 06:35:16 lvw - * Added debugging while hunting stop bug. - * - * Revision 1.1.2.13 2004/05/07 06:46:41 lvw - * Removed a bug in playlist deallocation. Added infrastructure to display information while playing. - * - * - ***********************************************************/ +void mgPlayerControl::StatusMsgReplaying() +{ + char *szBuf=NULL; + if(m_player + && m_player->GetCurrent() + && m_player->GetCurrent()->isValid() + && m_player->GetCurrent()->getContentType() == mgContentItem::GD_AUDIO + && m_player->GetPlaylist()) + { + /* + if(m_player->GetCurrent()->getAlbum().length() > 0) + { + asprintf(&szBuf,"[%c%c] (%d/%d) %s - %s", + m_player->GetPlaylist()->isLoop()?'L':'.', + m_player->GetPlaylist()->isShuffle()'S':'.', + m_player->GetPlaylist()->getIndex() + 1,m_player->GetPlaylist()->getCount(), + m_player->GetCurrent()->getTitle().c_str(), + m_player->GetCurrent()->getAlbum().c_str()); + } + else */ + { + asprintf(&szBuf,"[%c%c] (%d/%d) %s", + /* TODO m_player->GetPlaylist()->isLoop()?'L':*/'.', + /* TODO m_player->GetPlaylist()->isShuffle()'S':*/'.', + m_player->GetPlaylist()->getIndex() + 1,m_player->GetPlaylist()->getCount(), + m_player->GetCurrent()->getTitle().c_str()); + } + } + else + { + asprintf(&szBuf,"[muggle]"); + } + + if(szBuf + && ( m_szLastShowStatusMsg == NULL + || 0 != strcmp(szBuf,m_szLastShowStatusMsg) ) ) + { + cStatus::MsgReplaying(this,szBuf); + + if(m_szLastShowStatusMsg) + { + free(m_szLastShowStatusMsg); + } + m_szLastShowStatusMsg = szBuf; + } + else + { + free(szBuf); + } +} diff --git a/vdr_player.h b/vdr_player.h index 1ab8d9e..92b9e25 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -15,8 +15,8 @@ */ -#ifndef ___DVB_MP3_H -#define ___DVB_MP3_H +#ifndef ___VDR_PLAYER_H +#define ___VDR_PLAYER_H #include #if VDRVERSNUM >= 10307 @@ -40,7 +40,7 @@ class mgPlayerControl : public cControl { private: - //! \brief the reference to the player + //! \brief the reference to the player , don't rename it see cControl mgPCMPlayer *m_player; //! \brief indicates, whether the osd should be visible @@ -56,6 +56,9 @@ private: const cFont *font; #endif + //! \brief Last Message for Statusmonitor + char* m_szLastShowStatusMsg; + public: /*! \brief construct a control with a playlist @@ -121,27 +124,10 @@ public: //! \brief process key events eOSState ProcessKey(eKeys key); -}; -#endif //___DVB_MP3_H +protected: + //! \brief signal a played file to any cStatusMonitor inside vdr + void StatusMsgReplaying(); +}; -/************************************************************ - * - * $Log: vdr_player.h,v $ - * Revision 1.2 2004/05/28 15:29:19 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.1.2.9 2004/05/25 06:48:24 lvw - * Documentation and code polishing. - * - * Revision 1.1.2.8 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.1.2.7 2004/05/11 06:35:16 lvw - * Added debugging while hunting stop bug. - * - * Revision 1.1.2.6 2004/05/07 06:46:41 lvw - * Removed a bug in playlist deallocation. Added infrastructure to display information while playing. - * - * - ***********************************************************/ +#endif //___VDR_PLAYER_H -- cgit v1.2.3 From 250982ca9965c591d2c412bf39a0d04a9d4cbf4f Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 6 Oct 2004 09:16:12 +0000 Subject: Return to VDR main menu when pressing back in root view of browser git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@204 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.doxygen | 2 +- vdr_menu.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/muggle.doxygen b/muggle.doxygen index ffac9c2..8703db4 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -361,7 +361,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = README +INPUT = README TODO . # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp diff --git a/vdr_menu.c b/vdr_menu.c index a680f9f..810c106 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -227,10 +227,10 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } else { - // Back pressed on root level... Go back to Main VDR menu - - // state = osBack; - state = osContinue; + // Back pressed on root level... Go back to Main VDR menu + state = osBack; + + // state = osContinue; } } } -- cgit v1.2.3 From 53564f64951ace9983bf1afe6c13f7d07a220435 Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 6 Oct 2004 11:24:24 +0000 Subject: Documentation extended git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@205 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 3 +++ TODO | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README b/README index dcd3832..2934716 100644 --- a/README +++ b/README @@ -201,4 +201,7 @@ directory in which you executed the import steps (Chapter 4.2). Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. Then turn to the playlist view using yellow and start play using again the red function key. +During playback, Up/Down jumps forth and back in the current playlist. Yellow toggles play/pause mode and Ok +brings up a progress display. For VDR 1.3.6- the progress display is "quite simple". + */ diff --git a/TODO b/TODO index b99a5e1..6540f18 100644 --- a/TODO +++ b/TODO @@ -20,7 +20,8 @@ \subsection urgentosd OSD-related Issues - - Instant play + - Test Instant play from browser view + - Playlist view: start at selected on Ok \subsection urgentcode Code polishing @@ -68,7 +69,13 @@ - add the file to the end of the list \subsection urgentplayer Player extensions - + + - Toggle Track view/Playlist view (red) + - For playlist view show playlist name, total time + - For track view show name, artist, time + - Toggle Detail/progress view (green) + - Track view: all metadata + - Playlist view: all tracks, skip/jump - Determine max. framecount (needed for rewinding)? - Init scale/level/normalize? - The max. level should be recognized during play -- cgit v1.2.3 From bd285704d7cc8eb24725ee1af3a48c2ee18fa27b Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 6 Oct 2004 19:40:18 +0000 Subject: Removed compiler warnings and some errors git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@206 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.h | 1 - mg_content_interface.c | 6 +++--- mg_content_interface.h | 4 ++-- mg_database.c | 6 ++++++ mg_playlist.c | 12 ++++++------ mg_playlist.h | 2 +- mugglei.c | 9 ++++----- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/gd_content_interface.h b/gd_content_interface.h index 20906df..921f8d7 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -29,7 +29,6 @@ #include "mg_content_interface.h" #include "mg_media.h" - #include "mg_playlist.h" #include "mg_filters.h" #include "i18n.h" diff --git a/mg_content_interface.c b/mg_content_interface.c index af16389..defa0ac 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -105,11 +105,11 @@ mgContentItem* mgTracklist::getItem(unsigned int position) return *( m_list.begin() + position); } -bool mgTracklist::remove(unsigned int position) +bool mgTracklist::remove(int position) { bool result = false; - if( position < m_list.size() ) + if( position < (int)m_list.size() ) { std::vector::iterator iter; @@ -122,7 +122,7 @@ bool mgTracklist::remove(unsigned int position) return result; } -int mgTracklist::remove(mgContentItem* item) +int mgTracklist::removeItem(mgContentItem* item) { int retval = 0; std::vector::iterator iter; diff --git a/mg_content_interface.h b/mg_content_interface.h index d8bb0ec..b0a27eb 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -318,12 +318,12 @@ class mgTracklist * * \todo needed? if so, it hides bool remove(int) */ - virtual int remove(mgContentItem* item); // remove all occurences of item + virtual int removeItem(mgContentItem* item); // remove all occurences of item /*! * \brief remove all occurences of item */ - virtual bool remove(unsigned int position); // remove item at position + virtual bool remove(int position); // remove item at position }; /*! diff --git a/mg_database.c b/mg_database.c index 9dfa395..1636262 100644 --- a/mg_database.c +++ b/mg_database.c @@ -8,7 +8,11 @@ */ #include "mg_database.h" +#include "mg_tools.h" +#include + +static const int MAX_QUERY_BUFLEN = 2048; mgDB::mgDB() { @@ -44,6 +48,7 @@ std::string mgDB::escape_string( MYSQL *db, std::string s ) MYSQL_RES* mgDB::read_query( const char *fmt, ...) { + char querybuf[MAX_QUERY_BUFLEN]; va_list ap; va_start( ap, fmt ); vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); @@ -61,6 +66,7 @@ MYSQL_RES* mgDB::read_query( const char *fmt, ...) void mgDB::write_query( const char *fmt, ... ) { + char querybuf[MAX_QUERY_BUFLEN]; va_list ap; va_start( ap, fmt ); vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); diff --git a/mg_playlist.c b/mg_playlist.c index a0fbb0f..988f35d 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -95,14 +95,14 @@ void mgPlaylist::insert( mgContentItem* item, unsigned int position ) } } -bool mgPlaylist::remove( unsigned int pos ) +bool mgPlaylist::remove( int pos ) { bool result = false; - if( pos != m_current_idx ) + if( pos > 0 && pos != m_current_idx ) { result = mgTracklist::remove( pos ); - + if( result && pos < m_current_idx ) { m_current_idx --; @@ -176,7 +176,7 @@ mgContentItem* mgPlaylist::getCurrent() { mgContentItem *result; - if( 0 <= m_current_idx && m_current_idx < m_list.size() ) + if( 0 <= m_current_idx && m_current_idx < (int) m_list.size() ) { result = *( m_list.begin() + m_current_idx ); } @@ -207,7 +207,7 @@ bool mgPlaylist::skipFwd() { bool result = false; - if( m_current_idx + 1 < m_list.size() ) // unless loop mode + if( m_current_idx + 1 < (int) m_list.size() ) // unless loop mode { m_current_idx ++; result = true; @@ -235,7 +235,7 @@ bool mgPlaylist::skipBack() // get next track, do not update data structures mgContentItem* mgPlaylist::sneakNext() { - if( m_current_idx + 1 <= m_list.size() ) // unless loop mode + if( m_current_idx + 1 <= (int) m_list.size() ) // unless loop mode { return *(m_list.begin() + m_current_idx + 1); } diff --git a/mg_playlist.h b/mg_playlist.h index e7c428d..d35dec0 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -107,7 +107,7 @@ public: * * \param pos - the index of the track to be removed */ - bool remove( unsigned int pos ); + bool remove( int pos ); /* ==== access tracks ==== */ diff --git a/mugglei.c b/mugglei.c index efc4c07..286ee89 100755 --- a/mugglei.c +++ b/mugglei.c @@ -63,7 +63,7 @@ time_t get_fs_modification_time( std::string filename ) struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); // yes: obtain modification date for file and db entry - int statres = stat( filename.c_str(), buf ); + // int statres = stat( filename.c_str(), buf ); time_t mod = buf->st_mtime; free( buf ); @@ -89,7 +89,7 @@ TagLib::String escape_string( MYSQL *db, TagLib::String s ) char *buf = strdup( s.toCString() ); char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); - int len = mysql_real_escape_string( db, escbuf, buf, strlen( buf ) ); + mysql_real_escape_string( db, escbuf, buf, strlen( buf ) ); return TagLib::String( escbuf ); } @@ -155,7 +155,7 @@ void update_db( long uid, std::string filename ) // create new album entry "Unassigned" for this artist long id = random(); char *buf; - asprintf( &buf, "%d-%s", id, tag->artist().toCString() ); + asprintf( &buf, "%ld-%s", id, tag->artist().toCString() ); cddbid = TagLib::String( buf ).substr( 0, 20 ); cddbid = escape_string( db, cddbid ); free( buf ); @@ -189,7 +189,7 @@ void update_db( long uid, std::string filename ) // create new album entry long id = random(); char *buf; - asprintf( &buf, "%d-%s", id, tag->album().toCString() ); + asprintf( &buf, "%ld-%s", id, tag->album().toCString() ); cddbid = TagLib::String( buf ).substr( 0, 20 ); cddbid = escape_string( db, cddbid ); free( buf ); @@ -291,7 +291,6 @@ void evaluate_file( MYSQL *db, std::string filename ) int main( int argc, char *argv[] ) { - int option_index; std::string filename; if( argc < 2 ) -- cgit v1.2.3 From eeb06274f25ddabe7dc7521fcf04246091becaa8 Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 6 Oct 2004 21:26:13 +0000 Subject: Removed bug in instant play git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@207 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 123 +++---------------------------------------------------------- vdr_menu.h | 56 +--------------------------- 2 files changed, 6 insertions(+), 173 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index 810c106..1b5c9f1 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -197,6 +197,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) case kBlue: { m_last_osd_index = Current(); + m_menu_item = CurrentItem()->Node(); DisplayTreeSubmenu(); state = osContinue; @@ -242,7 +243,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { case k0 ... k9: { - int n = key - k0; + int n = key - k0; TreeSubmenuAction( n ); state = osContinue; @@ -543,7 +544,6 @@ void mgMainMenu::DisplayTreeSubmenu() eOSState mgMainMenu::TreeSubmenuAction( int n ) { - mgDebug( "mgMainMenu: TreeSubmenuAction( %d )", n ); eOSState state = osContinue; switch( n ) @@ -555,14 +555,13 @@ eOSState mgMainMenu::TreeSubmenuAction( int n ) mgSelectionTreeNode *current = CurrentNode(); if( current ) { - // clear playlist - m_current_playlist->clear(); - // append current node - std::vector *tracks = current->getTracks(); + std::vector *tracks = m_menu_item->getTracks(); if( tracks ) { + // clear playlist + m_current_playlist->clear(); m_current_playlist->appendList( tracks ); // play @@ -963,115 +962,3 @@ void mgMainMenu::Play(mgPlaylist *plist) cControl::Launch( new mgPlayerControl(plist) ); } } - -/************************************************************ - * - * $Log: vdr_menu.c,v $ - * Revision 1.27 2004/07/27 20:50:54 lvw - * Playlist indexing now working - * - * Revision 1.26 2004/07/27 06:57:35 LarsAC - * Inserted missing break - * - * Revision 1.25 2004/07/26 20:02:38 lvw - * Bug in handling playlist menu removed - * - * Revision 1.24 2004/07/25 21:33:35 lvw - * Removed bugs in finding track files and playlist indexing. - * - * Revision 1.23 2004/07/09 12:22:00 LarsAC - * Untested extensions for exporting plalists - * - * Revision 1.22 2004/07/06 00:20:51 MountainMan - * loading and saving playlists - * - * Revision 1.21 2004/06/02 19:29:22 lvw - * Use asprintf to create messages - * - * Revision 1.20 2004/05/28 15:29:19 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.19 2004/02/23 17:03:24 RaK - * - error in filter view while trying to switch or using the colour keys - * workaround: first filter criteria is inttype. than it works, dont ask why ;-( - * - * Revision 1.18 2004/02/23 16:18:15 RaK - * - i18n - * - * Revision 1.17 2004/02/23 15:56:19 RaK - * - i18n - * - * Revision 1.16 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.15 2004/02/14 22:02:45 RaK - * - mgFilterChoice Debuged - * fehlendes m_type = CHOICE in mg_filters.c - * falscher iterator in vdr_menu.c - * - * Revision 1.14 2004/02/12 09:08:48 LarsAC - * Added handling of filter choices (untested) - * - * Revision 1.13.2.12 2004/05/27 07:58:38 lvw - * Removed bugs in moving and removing tracks from playlists - * - * Revision 1.13.2.11 2004/05/26 14:31:04 lvw - * Added submenu for playlist view - * - * Revision 1.13.2.10 2004/05/25 21:58:45 lvw - * Handle submenus for views - * - * Revision 1.13.2.9 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.13.2.8 2004/05/11 06:35:16 lvw - * Added debugging while hunting stop bug. - * - * Revision 1.13.2.7 2004/05/04 16:51:53 lvw - * Debugging aids added. - * - * Revision 1.13.2.6 2004/04/29 06:48:21 lvw - * Output statements to aid debugging added - * - * Revision 1.13.2.5 2004/04/25 18:44:07 lvw - * Removed bugs in menu handling - * - * Revision 1.13.2.4 2004/03/14 12:30:56 lvw - * Menu now calls player - * - * Revision 1.13.2.3 2004/03/11 07:22:32 lvw - * Added setup menu - * - * Revision 1.13.2.2 2004/03/08 22:28:40 lvw - * Added documentation headers. - * - * Revision 1.13.2.1 2004/03/02 07:07:27 lvw - * Initial adaptations from MP3 plugin added (untested) - * - * Revision 1.13 2004/02/09 19:27:52 MountainMan - * filter set implemented - * - * Revision 1.12 2004/02/08 10:48:44 LarsAC - * Made major revisions in OSD behavior - * - * Revision 1.11 2004/02/03 21:53:32 RaK - * beak = break in l 212 - * - * Revision 1.10 2004/02/03 20:24:29 LarsAC - * Clear index history when jumping to root node in order to avoid overflow - * - * Revision 1.9 2004/02/03 19:34:51 LarsAC - * Back on root level now jumps back to VDR main menu. - * - * Revision 1.8 2004/02/03 19:28:46 LarsAC - * Playlist now created in plugin instead of in menu. - * - * Revision 1.7 2004/02/03 19:15:08 LarsAC - * OSD selection now jumps back to parent when collapsing. - * - * Revision 1.6 2004/02/03 00:13:24 LarsAC - * Improved OSD handling of collapse/back - * - * - ************************************************************ - */ diff --git a/vdr_menu.h b/vdr_menu.h index 45f60bd..14e16d8 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -77,7 +77,6 @@ class mgMainMenu : public cOsdMenu // Tree view handling void DisplayTree( mgSelectionTreeNode *node, int select = 0 ); void DisplayTreeViewSelector(); - void DisplayTreeSubmenu(); eOSState TreeSubmenuAction( int n ); @@ -98,7 +97,6 @@ class mgMainMenu : public cOsdMenu void DisplayFilter(); void DisplayFilterSelector(); - private: void Play(mgPlaylist *plist); @@ -106,6 +104,7 @@ class mgMainMenu : public cOsdMenu mgMedia *m_media; mgSelectionTreeNode *m_root; mgSelectionTreeNode *m_node; + mgSelectionTreeNode *m_menu_item; mgPlaylist *m_current_playlist; std::vector< std::string > *m_plists; @@ -118,56 +117,3 @@ class mgMainMenu : public cOsdMenu }; #endif - -/************************************************************ - * - * $Log: vdr_menu.h,v $ - * Revision 1.13 2004/07/25 21:33:35 lvw - * Removed bugs in finding track files and playlist indexing. - * - * Revision 1.12 2004/07/09 12:22:00 LarsAC - * Untested extensions for exporting plalists - * - * Revision 1.11 2004/07/06 00:20:51 MountainMan - * loading and saving playlists - * - * Revision 1.10 2004/05/28 15:29:19 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.9 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.8.2.7 2004/05/27 07:58:38 lvw - * Removed bugs in moving and removing tracks from playlists - * - * Revision 1.8.2.6 2004/05/26 14:31:04 lvw - * Added submenu for playlist view - * - * Revision 1.8.2.5 2004/05/25 21:58:54 lvw - * Handle submenus for views - * - * Revision 1.8.2.4 2004/03/14 12:30:56 lvw - * Menu now calls player - * - * Revision 1.8.2.3 2004/03/11 07:22:32 lvw - * Added setup menu - * - * Revision 1.8.2.2 2004/03/08 22:28:40 lvw - * Added documentation headers. - * - * Revision 1.8.2.1 2004/03/02 07:07:27 lvw - * Initial adaptations from MP3 plugin added (untested) - * - * Revision 1.8 2004/02/08 10:48:44 LarsAC - * Made major revisions in OSD behavior - * - * Revision 1.7 2004/02/03 19:28:46 LarsAC - * Playlist now created in plugin instead of in menu. - * - * Revision 1.6 2004/02/03 19:15:08 LarsAC - * OSD selection now jumps back to parent when collapsing. - * - * Revision 1.5 2004/02/03 00:13:24 LarsAC - * Improved OSD handling of collapse/back - * - */ -- cgit v1.2.3 From d3f5764bd8de119437dbc1a44ac8a29006f82b5b Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 6 Oct 2004 21:48:50 +0000 Subject: Avoid crash when replay display is active and menu is to be shown git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@208 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 16 ++++++++++++---- vdr_player.h | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 865353e..39410a8 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -892,7 +892,7 @@ mgPlayerControl::~mgPlayerControl() m_szLastShowStatusMsg = NULL; } - Hide(); + InternalHide(); Stop(); } @@ -1047,11 +1047,19 @@ void mgPlayerControl::ShowProgress() } else { - Hide(); + InternalHide(); } } void mgPlayerControl::Hide() +{ + m_visible = false; + + InternalHide(); +} + + +void mgPlayerControl::InternalHide() { if( m_has_osd ) { @@ -1103,7 +1111,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) case kStop: case kBlue: { - Hide(); + InternalHide(); Stop(); return osEnd; @@ -1117,7 +1125,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) } break; case kBack: { - Hide(); + InternalHide(); Stop(); return osEnd; diff --git a/vdr_player.h b/vdr_player.h index 92b9e25..c70c4e1 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -122,6 +122,9 @@ public: //! \brief hide the osd, if present void Hide(); + //! \brief hide the osd, if present + void InternalHide(); + //! \brief process key events eOSState ProcessKey(eKeys key); -- cgit v1.2.3 From 58177a6fe280784452dee940bb93f17bb6329a9a Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 8 Oct 2004 06:41:32 +0000 Subject: Added starting playlist at a certain index git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@209 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 22 +++++++++++++--------- mugglei.c | 10 +++++----- vdr_menu.c | 10 ++++++---- vdr_menu.h | 3 ++- vdr_player.c | 43 +++++++++++++++++++++++++++++-------------- vdr_player.h | 6 ++++-- 6 files changed, 59 insertions(+), 35 deletions(-) diff --git a/TODO b/TODO index 6540f18..0c2ef61 100644 --- a/TODO +++ b/TODO @@ -12,17 +12,16 @@ \subsection bugs Bugs and testing needed - Test execution of playlist commands - - Attach to graphlcd plugin via replay string - - Possible to resume play instead of restarting list from the beginning? - - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) + - Playlist view: start at selected on Ok + - Crashes in filter selections? - - Save on exit + - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) \subsection urgentosd OSD-related Issues - - Test Instant play from browser view - - Playlist view: start at selected on Ok - + - Possible to resume play instead of restarting list from the beginning? + - Save on exit + \subsection urgentcode Code polishing - Clean up coding style and documentation in general @@ -70,10 +69,10 @@ \subsection urgentplayer Player extensions - - Toggle Track view/Playlist view (red) + - Toggle Track view/playlist view (red) - For playlist view show playlist name, total time - For track view show name, artist, time - - Toggle Detail/progress view (green) + - Toggle detail/progress view (green) - Track view: all metadata - Playlist view: all tracks, skip/jump - Determine max. framecount (needed for rewinding)? @@ -256,4 +255,9 @@ - Escape query strings correctly - should go into gd_content_interface.c (row[] in lines 1175,1179,1882)? - Simple progress indicator for 1.3.12 + - Attach to graphlcd plugin via replay string + - Test Instant play from browser view + - Menu while progress display is active + - Check int/unsigned stuff in mg_playlist + */ \ No newline at end of file diff --git a/mugglei.c b/mugglei.c index 286ee89..33fa7bd 100755 --- a/mugglei.c +++ b/mugglei.c @@ -21,7 +21,7 @@ MYSQL *db; -std::string host, user, pass, dbname, socket; +std::string host, user, pass, dbname, sock; bool import_assorted; int init_database() @@ -35,10 +35,10 @@ int init_database() } // check for use of sockets - if( socket != "" ) + if( sock != "" ) { if( mysql_real_connect( db, NULL, user.c_str(), pass.c_str(), dbname.c_str(), - 0, socket.c_str(), 0 ) == NULL ) + 0, sock.c_str(), 0 ) == NULL ) { std::cout << "mysql_real_connect using sockets failed." << std::endl; @@ -316,7 +316,7 @@ int main( int argc, char *argv[] ) dbname = "GiantDisc"; user = ""; pass = ""; - socket = ""; + sock = ""; import_assorted = false; // parse command line options @@ -359,7 +359,7 @@ int main( int argc, char *argv[] ) } break; case 's': { - socket = optarg; + sock = optarg; } break; } } diff --git a/vdr_menu.c b/vdr_menu.c index 1b5c9f1..519c809 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -287,7 +287,9 @@ eOSState mgMainMenu::ProcessKey(eKeys key) { case kOk: { - // show some more information? + // start replay at selected index + int idx = Current(); + Play( m_current_playlist, idx ); state = osContinue; } break; case kRed: @@ -948,17 +950,17 @@ void mgMainMenu::DisplayFilterSelector() // show available filters, load on OK? } -void mgMainMenu::Play(mgPlaylist *plist) +void mgMainMenu::Play( mgPlaylist *plist, int first ) { MGLOG( "mgMainMenu::Play" ); cControl *control = cControl::Control(); if( control && typeid(*control) == typeid(mgPlayerControl) ) { // is there a running MP3 player? - static_cast(control)->NewPlaylist(plist); // signal the running player to load the new playlist + static_cast(control)->NewPlaylist(plist, first); // signal the running player to load the new playlist } else { - cControl::Launch( new mgPlayerControl(plist) ); + cControl::Launch( new mgPlayerControl(plist, first) ); } } diff --git a/vdr_menu.h b/vdr_menu.h index 14e16d8..41bba01 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -98,7 +98,8 @@ class mgMainMenu : public cOsdMenu void DisplayFilterSelector(); private: - void Play(mgPlaylist *plist); + //! \brief launch the actual player + void Play( mgPlaylist *plist, int first = -1 ); // content stuff mgMedia *m_media; diff --git a/vdr_player.c b/vdr_player.c index 39410a8..e06f6bf 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -142,6 +142,9 @@ private: //! \brief the decoder responsible for the currently playing item mgDecoder *m_decoder; + //! \brief the index where to start the playlist at + int m_first; + cFrame *m_rframe, *m_pframe; enum ePlayMode @@ -181,7 +184,7 @@ protected: virtual void Action(void); public: - mgPCMPlayer(mgPlaylist *plist); + mgPCMPlayer(mgPlaylist *plist, int first); virtual ~mgPCMPlayer(); bool Active() { return m_active; } @@ -199,13 +202,14 @@ public: virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false); // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW - void NewPlaylist(mgPlaylist *plist); + void NewPlaylist( mgPlaylist *plist, int first ); mgContentItem *GetCurrent () { return m_current; } mgPlaylist *GetPlaylist () { return m_playlist; } }; -mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist) - : cPlayer( the_setup.BackgrMode? pmAudioOnly: pmAudioOnlyBlack ) +mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist, int first) + : cPlayer( the_setup.BackgrMode? pmAudioOnly: pmAudioOnlyBlack ), + m_first( first ) { m_playlist = plist; @@ -221,7 +225,7 @@ mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist) m_playmode = pmStartup; m_state = msStop; - m_index = 0; + m_index = 0; m_playing = 0; m_current = 0; } @@ -252,7 +256,11 @@ void mgPCMPlayer::Activate(bool on) Lock(); - m_playlist->initialize(); + m_playlist->initialize( ); + if( m_first > 0 ) + { + m_playlist->gotoPosition( m_first ); + } m_current = m_playlist->getCurrent(); Play(); @@ -272,7 +280,7 @@ void mgPCMPlayer::Activate(bool on) } } -void mgPCMPlayer::NewPlaylist( mgPlaylist *plist ) +void mgPCMPlayer::NewPlaylist( mgPlaylist *plist, int start ) { MGLOG( "mgPCMPlayer::NewPlaylist" ); @@ -283,8 +291,14 @@ void mgPCMPlayer::NewPlaylist( mgPlaylist *plist ) // memory management of playlists should happen elsewhere (menu, content) m_playlist = plist; m_current = 0; - - if( NextFile() ) + + if( start > 0 ) + { + m_playlist->gotoPosition( (unsigned) start ); + m_current = m_playlist->getCurrent(); + Play(); + } + else if( NextFile() ) { Play(); } @@ -866,8 +880,8 @@ bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) // --- mgPlayerControl ------------------------------------------------------- -mgPlayerControl::mgPlayerControl( mgPlaylist *plist ) - : cControl( m_player = new mgPCMPlayer(plist) ) +mgPlayerControl::mgPlayerControl( mgPlaylist *plist, int start ) + : cControl( m_player = new mgPCMPlayer( plist, start ) ) { MGLOG( "mgPlayerControl::mgPlayerControl" ); @@ -878,7 +892,8 @@ mgPlayerControl::mgPlayerControl( mgPlaylist *plist ) m_has_osd = false; m_szLastShowStatusMsg = NULL; - // Notity all cStatusMonitor + + // Notify all cStatusMonitor StatusMsgReplaying(); } @@ -976,11 +991,11 @@ void mgPlayerControl::ToggleLoop(void) } } -void mgPlayerControl::NewPlaylist(mgPlaylist *plist) +void mgPlayerControl::NewPlaylist(mgPlaylist *plist, int start) { if( m_player ) { - m_player->NewPlaylist(plist); + m_player->NewPlaylist(plist, start); } } diff --git a/vdr_player.h b/vdr_player.h index c70c4e1..3731b9d 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -64,8 +64,9 @@ public: /*! \brief construct a control with a playlist * * \param plist - the playlist to be played + * \param first - the index where to start the playlist */ - mgPlayerControl(mgPlaylist *plist); + mgPlayerControl(mgPlaylist *plist, int first); /*! \brief destructor */ @@ -113,8 +114,9 @@ public: * The caller has to take care of deallocating the previous list * * \param plist - the new playlist to be played + * \param first - the index where to start the playlist */ - void NewPlaylist( mgPlaylist *plist ); + void NewPlaylist( mgPlaylist *plist, int start ); //! \brief a progress display void ShowProgress(); -- cgit v1.2.3 From a5b382c907acc3474b0800b4cc868cd0ed153851 Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 8 Oct 2004 15:10:50 +0000 Subject: Added loop mode handling git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@210 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 66 +++++++++++++++++++++++++++++++++++---------- mg_playlist.h | 86 +++++++++++++++++++++++++++++++++++++++++------------------ mugglei.c | 19 +++++++++++++ 3 files changed, 131 insertions(+), 40 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index 988f35d..86d3d8e 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -20,9 +20,6 @@ #include #include - -/* ==== constructors ==== */ - mgPlaylist::mgPlaylist() { m_current_idx = -1; @@ -45,17 +42,21 @@ mgPlaylist::~mgPlaylist() { } -void mgPlaylist::toggleShuffle() +void mgPlaylist::setLoopMode( LoopMode lm ) { + m_loop_mode = lm; } -void mgPlaylist::toggleLoop() +void mgPlaylist::setShuffleMode( ShuffleMode sm ) { + m_shuffle_mode = sm; } void mgPlaylist::initialize() { m_current_idx = 0; + m_loop_mode = mgPlaylist::LM_NONE; + m_shuffle_mode = mgPlaylist::SM_NONE; } /* ==== add/remove tracks ==== */ @@ -148,7 +149,6 @@ void mgPlaylist::move( int from, int to ) } } -/*==== access tracks ====*/ std::string mgPlaylist::getListname() { return m_listname; @@ -207,9 +207,23 @@ bool mgPlaylist::skipFwd() { bool result = false; - if( m_current_idx + 1 < (int) m_list.size() ) // unless loop mode + if( m_loop_mode == mgPlaylist::LM_SINGLE ) { - m_current_idx ++; + result = true; + } + else + { + if( m_current_idx + 1 < (int) m_list.size() ) // unless loop mode + { + m_current_idx ++; + } + else + { + if( m_loop_mode == mgPlaylist::LM_FULL ) + { + m_current_idx = 0; + } + } result = true; } @@ -222,12 +236,25 @@ bool mgPlaylist::skipBack() { bool result = false; - if( m_current_idx > 0 ) + if( m_loop_mode == mgPlaylist::LM_SINGLE ) { - m_current_idx --; result = true; } - + else + { + if( m_current_idx > 0 ) + { + m_current_idx --; + } + else + { + if( m_loop_mode == mgPlaylist::LM_FULL ) + { + m_current_idx = m_list.size() -1; + } + } + result = true; + } // if we are at the beginning -- just do nothing unless in loop mode return result; } @@ -235,14 +262,25 @@ bool mgPlaylist::skipBack() // get next track, do not update data structures mgContentItem* mgPlaylist::sneakNext() { + mgContentItem* result; + if( m_current_idx + 1 <= (int) m_list.size() ) // unless loop mode { - return *(m_list.begin() + m_current_idx + 1); + result = *(m_list.begin() + m_current_idx + 1); } else { - return &(mgContentItem::UNDEFINED); + if( m_loop_mode == mgPlaylist::LM_FULL ) + { + result = *(m_list.begin()); + } + else + { + return &(mgContentItem::UNDEFINED); + } } + + return result; } bool mgPlaylist::exportM3U( std::string m3u_file ) @@ -262,7 +300,7 @@ bool mgPlaylist::exportM3U( std::string m3u_file ) for( iter = m_list.begin(); iter != m_list.end(); iter++ ) { // each item in the list - fprintf( listfile, "#EXTINF:0,%s\n", (*iter)->getLabel().c_str() ); + fprintf( listfile, "#EXTINF:%d,%s\n", (*iter)->getLength(), (*iter)->getLabel().c_str() ); fprintf( listfile, "%s", (*iter)->getSourceFile().c_str() ); } diff --git a/mg_playlist.h b/mg_playlist.h index d35dec0..a0ff4c2 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -36,6 +36,12 @@ private: // TODO: should be a property of the player? int m_current_idx; + //! \brief the current loop mode + LoopMode m_loop_mode; + + //! \brief the current shuffle mode + ShuffleMode m_shuffle_mode; + protected: // TODO: Why not make these private? Subclasses should use access functions. LVW @@ -44,9 +50,26 @@ protected: std::string m_listname; public: - - /* ==== constructors and initialization ==== */ - + + //! \brief define various ways to play music in random order + enum + { + SM_NONE, //!< \brief play normal sequence + SM_NORMAL, //!< \brief a shuffle with a fair distribution + SM_PARTY //!< \brief select the next few songs randomly, continue forever + } ShuffleMode; + + //! \brief define various ways to play music in a neverending loop + enum + { + LM_NONE, //!< \brief do not loop + LM_SINGLE, //!< \brief loop a single track + LM_FULL //!< \brief loop the whole playlist + } LoopMode; + + //! \brief object construction and destruction + //@{ + //! \brief the default constructor (random listname) mgPlaylist(); @@ -58,20 +81,24 @@ public: void initialize(); - /* ==== destructor ==== */ //! \brief the destructor virtual ~mgPlaylist(); - /* === control behavior */ + //@} - //! \brief toggle the loop mode. TODO. - void toggleLoop(); + //! \brief control behavior + //@{ - //! \brief toggle the shuffle mode. TODO. - void toggleShuffle(); - - /* ==== add/ remove tracks ==== */ + //! \brief toggle the loop mode. + void setLoopMode( LoopMode lm ); + + //! \brief toggle the shuffle mode. + void setShuffleMode( ShuffleMode sm ); + + //@} + //! \brief modify playlist items + //@{ /*! \brief adds a song at the end of the playlist * * \param item - the item to be appended @@ -94,7 +121,7 @@ public: virtual void insert(mgContentItem* item, unsigned int position); //! \brief clear all tracks - virtual void clear(); +m virtual void clear(); /*! \brief move tracks within playlist * @@ -109,18 +136,11 @@ public: */ bool remove( int pos ); - /* ==== access tracks ==== */ - + //@} + //! \brief obtain the listname std::string getListname() ; - /*! - * \brief returns the current item of the list - * - * \todo Return null in case of an empty list or invalid index - */ - virtual mgContentItem* getCurrent(); - /*! \brief set the listname * * \param name - the new name of this list @@ -130,9 +150,25 @@ public: //! \brief returns the count of items in the list int getCount(); + //! \brief access playlist items + //@{ + //! \brief returns current index in the playlist int getIndex() const; + //! \brief make playlist persistent + virtual bool storePlaylist() = 0; + + //! \brief export the playlist in m3u format + virtual bool exportM3U( std::string m3u_file ); + + /*! + * \brief returns the current item of the list + * + * \todo Return null in case of an empty list or invalid index + */ + virtual mgContentItem* getCurrent(); + /*! \brief returns the nth track from the playlist * * \param position - the position to skip to @@ -155,13 +191,11 @@ public: * \todo Handle play modes */ virtual bool skipBack(); - + //! \brief obtain the next item without skipping the current position virtual mgContentItem* sneakNext(); - virtual bool storePlaylist() = 0; - - //! \brief export the playlist in m3u format - virtual bool exportM3U( std::string m3u_file ); + //@} + }; #endif diff --git a/mugglei.c b/mugglei.c index 33fa7bd..ed64a26 100755 --- a/mugglei.c +++ b/mugglei.c @@ -106,6 +106,25 @@ long find_file_in_database( MYSQL *db, std::string filename ) return atol( row[0] ); } +TagLib::String find_genre_id( TagLib::String genre ) +{ + TagLib::String result; + + if( genre.size() ) + { + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM genre WHERE genre=\"%s\"", genre.toCString() ); + + if( mysql_num_rows(result) ) + { + MYSQL_ROW row = mysql_fetch_row( result ); + + result = TagLib::String( row[0] ); + } + } + + return result; +} + // read tags from the mp3 file and store them into the corresponding database entry void update_db( long uid, std::string filename ) { -- cgit v1.2.3 From 4c3eea2b9b678538bfd1f9ff7b1ec0c43dd1154b Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 9 Oct 2004 07:28:33 +0000 Subject: Activated code to check against duplicate db entries git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@211 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/mugglei.c b/mugglei.c index ed64a26..2215d77 100755 --- a/mugglei.c +++ b/mugglei.c @@ -61,26 +61,32 @@ int init_database() time_t get_fs_modification_time( std::string filename ) { struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); - - // yes: obtain modification date for file and db entry - // int statres = stat( filename.c_str(), buf ); + time_t mod = 0; - time_t mod = buf->st_mtime; - free( buf ); + // yes: obtain modification date for file and db entry + if( !stat( filename.c_str(), buf ) ) + { + mod = buf->st_mtime; + free( buf ); + } return mod; } time_t get_db_modification_time( long uid ) { - time_t mt; + time_t mt = 0; - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT modification_time FROM tracks WHERE id=\"%d\"", uid ); - MYSQL_ROW row = mysql_fetch_row( result ); + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT UNIX_TIMESTAMP(modification_time) " + "FROM tracks WHERE id=\"%d\"", uid ); + if( mysql_num_rows(result) ) + { + MYSQL_ROW row = mysql_fetch_row( result ); + + std::string mod_time = row[0]; + mt = (time_t) atol( mod_time.c_str() ); + } - std::string mod_time = row[0]; - mt = (time_t) atol( mod_time.c_str() ); - return mt; } @@ -108,21 +114,22 @@ long find_file_in_database( MYSQL *db, std::string filename ) TagLib::String find_genre_id( TagLib::String genre ) { - TagLib::String result; + TagLib::String id = ""; if( genre.size() ) { - MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM genre WHERE genre=\"%s\"", genre.toCString() ); + MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM genre WHERE " + "genre=\"%s\"", genre.toCString() ); if( mysql_num_rows(result) ) { MYSQL_ROW row = mysql_fetch_row( result ); - - result = TagLib::String( row[0] ); + + id = row[0]; } } - return result; + return id; } // read tags from the mp3 file and store them into the corresponding database entry @@ -297,12 +304,14 @@ void evaluate_file( MYSQL *db, std::string filename ) update_db( uid, filename ); } */ - + + std::cout << "Updating " << filename << std::endl; update_db( uid, filename ); } else { // not in db yet: import file + std::cout << "Creating " << filename << std::endl; update_db( -1, filename ); } } @@ -389,9 +398,9 @@ int main( int argc, char *argv[] ) gettimeofday( &tv, &tz ); srandom( tv.tv_usec ); + /* int res = init_database(); - if( !res ) { update_db( 0, filename ); @@ -400,7 +409,9 @@ int main( int argc, char *argv[] ) { std::cout << "Database initialization failed. Exiting.\n" << std::endl; } - - return res; + */ + + evaluate_file( db, filename ); + return 0; } -- cgit v1.2.3 From 0793421773dd5740ddf81300b29b42cdb2350382 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 9 Oct 2004 07:28:56 +0000 Subject: Modes for loop and shuffle added git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@212 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 50 +++++++++++++++++++++++++++++++++++++++++++++----- mg_playlist.h | 56 ++++++++++++++++++++++++++++---------------------------- 2 files changed, 73 insertions(+), 33 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index 86d3d8e..7480563 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -42,14 +42,54 @@ mgPlaylist::~mgPlaylist() { } -void mgPlaylist::setLoopMode( LoopMode lm ) -{ - m_loop_mode = lm; +mgPlaylist::LoopMode mgPlaylist::toggleLoopMode( ) +{ + switch( m_loop_mode ) + { + case LM_NONE: + { + m_loop_mode = LM_SINGLE; + } break; + case LM_SINGLE: + { + m_loop_mode = LM_FULL; + } break; + case LM_FULL: + { + m_loop_mode = LM_NONE; + } break; + default: + { + m_loop_mode = LM_NONE; + } + } + + return m_loop_mode; } -void mgPlaylist::setShuffleMode( ShuffleMode sm ) +mgPlaylist::ShuffleMode mgPlaylist::toggleShuffleMode( ) { - m_shuffle_mode = sm; + switch( m_shuffle_mode ) + { + case SM_NONE: + { + m_shuffle_mode = SM_NORMAL; + } break; + case SM_NORMAL: + { + m_shuffle_mode = SM_PARTY; + } break; + case SM_PARTY: + { + m_shuffle_mode = SM_NONE; + } break; + default: + { + m_shuffle_mode = SM_NONE; + } + } + + return m_shuffle_mode; } void mgPlaylist::initialize() diff --git a/mg_playlist.h b/mg_playlist.h index a0ff4c2..975458a 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -1,6 +1,6 @@ /*! * \file mg_playlist.c - * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase + * \brief defines functions to be executed on playlists for the vdr muggle plugin * * \version $Revision: 1.6 $ * \date $Date$ @@ -30,42 +30,23 @@ class mgPlaylist : public mgTracklist { -private: - - //! \brief current index in the playlist - // TODO: should be a property of the player? - int m_current_idx; - - //! \brief the current loop mode - LoopMode m_loop_mode; - - //! \brief the current shuffle mode - ShuffleMode m_shuffle_mode; - -protected: - - // TODO: Why not make these private? Subclasses should use access functions. LVW - - //! \brief the name of the playlist - std::string m_listname; - public: //! \brief define various ways to play music in random order - enum + enum ShuffleMode { SM_NONE, //!< \brief play normal sequence SM_NORMAL, //!< \brief a shuffle with a fair distribution SM_PARTY //!< \brief select the next few songs randomly, continue forever - } ShuffleMode; + }; //! \brief define various ways to play music in a neverending loop - enum + enum LoopMode { LM_NONE, //!< \brief do not loop LM_SINGLE, //!< \brief loop a single track LM_FULL //!< \brief loop the whole playlist - } LoopMode; + }; //! \brief object construction and destruction //@{ @@ -90,10 +71,10 @@ public: //@{ //! \brief toggle the loop mode. - void setLoopMode( LoopMode lm ); + LoopMode toggleLoopMode( ); //! \brief toggle the shuffle mode. - void setShuffleMode( ShuffleMode sm ); + ShuffleMode toggleShuffleMode( ); //@} @@ -121,7 +102,7 @@ public: virtual void insert(mgContentItem* item, unsigned int position); //! \brief clear all tracks -m virtual void clear(); + virtual void clear(); /*! \brief move tracks within playlist * @@ -195,7 +176,26 @@ m virtual void clear(); //! \brief obtain the next item without skipping the current position virtual mgContentItem* sneakNext(); //@} - + +private: + + //! \brief current index in the playlist + // TODO: should be a property of the player? + int m_current_idx; + + //! \brief the current loop mode + LoopMode m_loop_mode; + + //! \brief the current shuffle mode + ShuffleMode m_shuffle_mode; + +protected: + + // TODO: Why not make these private? Subclasses should use access functions. LVW + + //! \brief the name of the playlist + std::string m_listname; + }; #endif -- cgit v1.2.3 From 6cce75232cf142808e21352381e31fe688d2396c Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 9 Oct 2004 08:03:08 +0000 Subject: Import now avoids duplicate tracks git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@213 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 3 ++- muggle.c | 8 ++++++++ mugglei.c | 47 +++++++++++++++++++++-------------------------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index 14de5fd..94df06f 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ MIFLAGS += -I/usr/include/taglib -lmysqlclient OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o vdr_decoder_ogg.o LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis +MILIBS = -lmysqlclient -ltag ### Targets: @@ -78,7 +79,7 @@ libvdr-$(PLUGIN).so: $(OBJS) @cp $@ $(LIBDIR)/$@.$(VDRVERSION) mugglei: mg_tools.o mugglei.o - $(CXX) $(CXXFLAGS) $^ $(LIBS) -ltag -o $@ + $(CXX) $(CXXFLAGS) $^ $(MILIBS) -o $@ dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) diff --git a/muggle.c b/muggle.c index 849e3eb..2adebbb 100644 --- a/muggle.c +++ b/muggle.c @@ -22,7 +22,15 @@ #include #include +<<<<<<< .mine +<<<<<<< .mine +static const char *VERSION = "0.0.3"; +======= +static const char *VERSION = "0.0.4"; +======= static const char *VERSION = "0.0.5"; +>>>>>>> .r202 +>>>>>>> .r177 static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/mugglei.c b/mugglei.c index 2215d77..d92fdcf 100755 --- a/mugglei.c +++ b/mugglei.c @@ -21,7 +21,7 @@ MYSQL *db; -std::string host, user, pass, dbname, sock; +std::string host, user, pass, dbname, sck; bool import_assorted; int init_database() @@ -35,10 +35,10 @@ int init_database() } // check for use of sockets - if( sock != "" ) + if( sck != "" ) { if( mysql_real_connect( db, NULL, user.c_str(), pass.c_str(), dbname.c_str(), - 0, sock.c_str(), 0 ) == NULL ) + 0, sck.c_str(), 0 ) == NULL ) { std::cout << "mysql_real_connect using sockets failed." << std::endl; @@ -95,21 +95,31 @@ TagLib::String escape_string( MYSQL *db, TagLib::String s ) char *buf = strdup( s.toCString() ); char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); - mysql_real_escape_string( db, escbuf, buf, strlen( buf ) ); + mysql_real_escape_string( db, escbuf, s.toCString(), s.size() ); + TagLib::String r = TagLib::String( escbuf ); - return TagLib::String( escbuf ); + free( escbuf ); + free( buf); + + return r; } long find_file_in_database( MYSQL *db, std::string filename ) { + long uid = -1; + TagLib::String file = TagLib::String( filename.c_str() ); file = escape_string( db, file ); MYSQL_RES *result = mgSqlReadQuery( db, "SELECT id FROM tracks WHERE mp3file=\"%s\"", file.toCString() ); - MYSQL_ROW row = mysql_fetch_row( result ); + if( mysql_num_rows(result) ) + { + MYSQL_ROW row = mysql_fetch_row( result ); + uid = atol( row[0] ); + } // obtain ID and return - return atol( row[0] ); + return uid; } TagLib::String find_genre_id( TagLib::String genre ) @@ -279,7 +289,7 @@ void update_tags( long uid, std::string filename ) } -void evaluate_file( MYSQL *db, std::string filename ) +void evaluate_file( std::string filename ) { if( 0 == init_database() ) { @@ -305,13 +315,11 @@ void evaluate_file( MYSQL *db, std::string filename ) } */ - std::cout << "Updating " << filename << std::endl; update_db( uid, filename ); } else { // not in db yet: import file - std::cout << "Creating " << filename << std::endl; update_db( -1, filename ); } } @@ -344,7 +352,7 @@ int main( int argc, char *argv[] ) dbname = "GiantDisc"; user = ""; pass = ""; - sock = ""; + sck = ""; import_assorted = false; // parse command line options @@ -387,7 +395,7 @@ int main( int argc, char *argv[] ) } break; case 's': { - sock = optarg; + sck = optarg; } break; } } @@ -398,20 +406,7 @@ int main( int argc, char *argv[] ) gettimeofday( &tv, &tz ); srandom( tv.tv_usec ); - /* - int res = init_database(); - - if( !res ) - { - update_db( 0, filename ); - } - else - { - std::cout << "Database initialization failed. Exiting.\n" << std::endl; - } - */ - - evaluate_file( db, filename ); + evaluate_file( filename ); return 0; } -- cgit v1.2.3 From 8da6cb9b3d1a60843c150eedb45f4ad7a7e4e582 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 9 Oct 2004 08:07:30 +0000 Subject: Remove errror in muggle.c git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@214 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 10 +--------- vdr_player.c | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/muggle.c b/muggle.c index 2adebbb..14ff37b 100644 --- a/muggle.c +++ b/muggle.c @@ -22,15 +22,7 @@ #include #include -<<<<<<< .mine -<<<<<<< .mine -static const char *VERSION = "0.0.3"; -======= -static const char *VERSION = "0.0.4"; -======= -static const char *VERSION = "0.0.5"; ->>>>>>> .r202 ->>>>>>> .r177 +static const char *VERSION = "0.0.6"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/vdr_player.c b/vdr_player.c index e06f6bf..41d2a53 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -769,12 +769,12 @@ bool mgPCMPlayer::PrevFile(void) void mgPCMPlayer::ToggleShuffle() { - m_playlist->toggleShuffle(); + m_playlist->toggleShuffleMode(); } void mgPCMPlayer::ToggleLoop(void) { - m_playlist->toggleLoop(); + m_playlist->toggleLoopMode(); } void mgPCMPlayer::Pause(void) -- cgit v1.2.3 From 479966d404403753227c5a51b0bbc02c7d6f0c29 Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 9 Oct 2004 10:47:45 +0000 Subject: Added loop and shuffle mode handling to player control git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@215 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index 41d2a53..f30a53d 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -1118,6 +1118,84 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) { Backward(); } break; + case kRed: + { + if( !m_visible && m_player ) + { + mgPlaylist *pl = m_player->GetPlaylist(); + + std::string s; + switch( pl->toggleLoopMode() ) + { + case mgPlaylist::LM_NONE: + { + s = tr( "Loop mode off" ); + } break; + case mgPlaylist::LM_SINGLE: + { + s = tr( "Loop mode single" ); + } break; + case mgPlaylist::LM_FULL: + { + s = tr( "Loop mode full" ); + } break; + default: + { + s = tr( "Unknown loop mode" ); + } +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo, s.c_str() ); + Skins.Flush(); +#else + Interface->Status( s.c_str() ); + Interface->Flush(); +#endif + } + } + else + { + // toggle progress display between simple and detail + } + } break; + case kGreen: + { + if( !m_visible && m_player ) + { + mgPlaylist *pl = m_player->GetPlaylist(); + + std::string s; + switch( pl->toggleShuffleMode() ) + { + case mgPlaylist::SM_NONE: + { + s = tr( "Shuffle mode off" ); + } break; + case mgPlaylist::SM_NORMAL: + { + s = tr( "Shuffle mode normal" ); + } break; + case mgPlaylist::SM_PARTY: + { + s = tr( "Shuffle mode party" ); + } break; + default: + { + s = tr( "Unknown shuffle mode" ); + } +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo, s.c_str() ); + Skins.Flush(); +#else + Interface->Status( s.c_str() ); + Interface->Flush(); +#endif + } + } + else + { + // toggle progress display between playlist and single track + } + } break; case kPause: case kYellow: { -- cgit v1.2.3 From 24b4dbac2666b10cffe805dc411de1cee9338c1a Mon Sep 17 00:00:00 2001 From: lvw Date: Sat, 9 Oct 2004 10:47:50 +0000 Subject: Added loop and shuffle mode handling to player control git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@216 e10066b5-e1e2-0310-b819-94efdf66514b --- i18n.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/i18n.c b/i18n.c index d328160..2231660 100644 --- a/i18n.c +++ b/i18n.c @@ -746,5 +746,101 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO }, + { "Loop mode off", + "Endlosmodus aus", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Loop mode single", + "Endlosmodus Einzeltitel", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Loop mode full", + "Endlosmodus Playliste", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Shuffle mode off", + "Zufallssmodus aus", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Shuffle mode normal", + "Zufallssmodus normal", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Shuffle mode party", + "Zufallssmodus Party", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { NULL } }; -- cgit v1.2.3 From 9775819bdc8d469e0d386612f7062140870f1496 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 10 Oct 2004 13:11:51 +0000 Subject: Import genre information git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@217 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/mugglei.c b/mugglei.c index d92fdcf..3460c2c 100755 --- a/mugglei.c +++ b/mugglei.c @@ -164,6 +164,8 @@ void update_db( long uid, std::string filename ) trackno = tag->track(); genre = tag->genre(); + TagLib::String gid = find_genre_id( genre ); + TagLib::AudioProperties *ap = f.audioProperties(); int len = ap->length(); // tracks.length int bitrate = ap->bitrate(); // tracks.bitrate @@ -257,18 +259,18 @@ void update_db( long uid, std::string filename ) mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%d\"," "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\"," - "samplerate=%d, channels=%d WHERE id=%d", + "samplerate=%d, channels=%d, genre1=\"%s\" WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.toCString(), filename.c_str(), len, bitrate, - sample, channels, uid ); + sample, channels, gid.toCString(), uid ); } else { // the entry does not exist, create it mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,genre1,genre2,year," - "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels)" - " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d)", + "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1)" + " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, %s)", artist.toCString(), title.toCString(), year, cddbid.toCString(), - trackno, filename.c_str(), len, bitrate, sample, channels ); + trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString() ); #ifdef VERBOSE std::cout << "-- TAG --" << std::endl; @@ -373,6 +375,10 @@ int main( int argc, char *argv[] ) { host = optarg; } break; + case 'n': + { + dbname = optarg; + } break; case 'u': { user = optarg; @@ -381,10 +387,6 @@ int main( int argc, char *argv[] ) { pass = optarg; } break; - case 'd': - { - dbname = optarg; - } break; case 'a': { import_assorted = true; -- cgit v1.2.3 From 72d3dbcc1ae7d3423d65a3ce0464e1995a91da25 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 10 Oct 2004 13:19:29 +0000 Subject: Greatly improved progress/info display during play git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@218 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 132 ++++++++++++------------ mg_content_interface.c | 14 ++- mg_content_interface.h | 18 ++-- mg_playlist.c | 21 ++-- mg_playlist.h | 6 +- vdr_decoder_mp3.c | 24 ----- vdr_player.c | 273 +++++++++++++++++++++++++++++++++++++------------ vdr_player.h | 9 ++ 8 files changed, 320 insertions(+), 177 deletions(-) diff --git a/TODO b/TODO index 0c2ef61..764986f 100644 --- a/TODO +++ b/TODO @@ -7,13 +7,11 @@ If you feel, something is really urgent, go ahead. We'll help you. - \section urgent Urgent issues + \section urgent Urgent/Short-term issues \subsection bugs Bugs and testing needed - - Test execution of playlist commands - - Playlist view: start at selected on Ok - + - Test execution of playlist commands - Crashes in filter selections? - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) @@ -47,7 +45,7 @@ \subsection urgentcontent Content handling - Think, whether type (mp3, ogg, flac) should be stored in database - could be used in searching/structuring as well - - Handle loop mode in mgPlaylist (already done?) + - Handle loop mode in mgPlaylist - Handle shuffle mode in mgPlaylist - shuffle mode on - for next file: @@ -69,12 +67,6 @@ \subsection urgentplayer Player extensions - - Toggle Track view/playlist view (red) - - For playlist view show playlist name, total time - - For track view show name, artist, time - - Toggle detail/progress view (green) - - Track view: all metadata - - Playlist view: all tracks, skip/jump - Determine max. framecount (needed for rewinding)? - Init scale/level/normalize? - The max. level should be recognized during play @@ -139,8 +131,8 @@ - really abstract from specific queries etc. - mgDatabase should completely abstract database (mySQL) stuff!? - - read/write queries - - return results (needs a homogeneous representation of results?) + - read/write queries + - return results (needs a homogeneous representation of results?) \subsection midosd OSD-related issues @@ -149,7 +141,6 @@ - Check whether submenus (as implemented in VDR) are more suitable - do not permit jumping to arbitrary menus though - Add play indicators - - mgPCMPlayer::GetIndex: obtain total length from database (DONE) - Toggle display with Ok (off -> track info -> album info -> playlist info -> off) - Display track info (use vdr 1.3.12 progress display) - Song title @@ -204,60 +195,67 @@ \section done Done - - Check play speed (was XINE related) - - Playlists starts with 2nd song (DONE) - - Export playlists - - Delete selected item - - Add command line option for top level directory - - prepended to filename in non-GD-mode - - searched in GD-mode - - Edit playlist (move tracks like channels in VDR channel list) + - BUG: Check play speed (was XINE related) + - BUG: Playlists starts with 2nd song (DONE) + - Export playlists + - Delete selected item + - Add command line option for top level directory + - Prepend top level dir to filename in non-GD-mode + - Edit playlist (move tracks like channels in VDR channel list) (OK in playlist view) - - Instant play = empty current playlist, append tracks of current node and play + - Instant play = empty current playlist, append tracks of current node and play (easy, in submenu of browser) - - Clear playlist (submenu action) - - Find files from database entry based on GD compatibility flag - - Handle Next/PrevFile in mgPlaylist (vdr_player.c) - - Add plugin parameters for database name/host/user/pass - - Add plugin parameter for GD filename compatibility - - handle filters: - - create tracklist from filter - - create tree from filter - - i18n (english and german) - - Album import - - Various artists - - Ogg/Vorbis decoder integration - - cOggFile kept - - cOggInfo dismissed in favor of obtaining info from DB - - coding conventions adapted - - Schema extended to keep audio properties - - Import (mugglei) extended to store audio properties in DB - (most notably samplerate, no. channels) - - Extended mgContentItem with audio properties - - Extended mgGdTrack with audio properties (bitrate, samplerate, channels) - - in mgPCMPlayer/vdr_player.c: - - pass m_playing to mp3/ogg decoder (instead of filename) - - mgOggDecoder: obtain audio properties from DB (channels, sampling rate via mgContentItem) - - mgPCMPlayer::getSourceFile moved to abstract data layer (mgContentItem) - and made concrete in subclasses (mgGdTrack) - - mgDecoders::findDecoder: extend decoder detection - - Check compatibility for 1.3.12 (DONE) - - Test Save/Rename/Load of playlist (DONE) - - Plugin crashes when deleting playlist while playing - - should stop playing immediately - - Check deletion of entries while playing - - only allowed, if item is not currently played - - adapt index in playlist - - Playlist indexing not correct - - Player jumps e.g. from track 1 to track 3 - - Make sure jumping beyond the end of the list is not permitted - - Plugin crashes when selecting entries with special characters - - Escape query strings correctly - - should go into gd_content_interface.c (row[] in lines 1175,1179,1882)? - - Simple progress indicator for 1.3.12 - - Attach to graphlcd plugin via replay string - - Test Instant play from browser view - - Menu while progress display is active - - Check int/unsigned stuff in mg_playlist + - Clear playlist (submenu action) + - Find files from database entry based on GD compatibility flag + - Handle Next/PrevFile in mgPlaylist (vdr_player.c) + - Add plugin parameters for database name/host/user/pass + - Add plugin parameter for GD filename compatibility + - handle filters: + - create tracklist from filter + - create tree from filter + - i18n (english and german) + - Album import + - Various artists import (assorted) + - Ogg/Vorbis decoder integration + - cOggFile kept + - cOggInfo dismissed in favor of obtaining info from DB + - coding conventions adapted + - Schema extended to keep audio properties + - Import (mugglei) extended to store audio properties in DB + (most notably samplerate, no. channels) + - Extended mgContentItem with audio properties + - Extended mgGdTrack with audio properties (bitrate, samplerate, channels) + - in mgPCMPlayer/vdr_player.c: + - pass m_playing to mp3/ogg decoder (instead of filename) + - mgOggDecoder: obtain audio properties from DB (channels, sampling rate via mgContentItem) + - mgPCMPlayer::getSourceFile moved to abstract data layer (mgContentItem) + and made concrete in subclasses (mgGdTrack) + - mgDecoders::findDecoder: extend decoder detection + - BUG: Check compatibility for 1.3.12 (DONE) + - BUG: Plugin crashes when deleting playlist while playing + - should stop playing immediately or not permit this + - BUG: Check deletion of entries while playing + - only allowed, if item is not currently played + - adapt index in playlist + - BUG: Playlist indexing not correct + - Player jumps e.g. from track 1 to track 3 + - Make sure jumping beyond the end of the list is not permitted + - BUG: Plugin crashes when selecting entries with special characters + - Escape query strings correctly + - should go into gd_content_interface.c (row[] in lines 1175,1179,1882)? + - Simple progress indicator for 1.3.12 + - Attach to graphlcd plugin via replay string + - Test Instant play from browser view + - Displaying the menu while progress display is shown makes VDR crash (DONE) + - Check int/unsigned stuff in mg_playlist + - mgPCMPlayer::GetIndex: obtain track length from database (DONE) + - Import (mugglei) now checks for duplicate entries based on filenames + - Playlist view: start at selected on Ok + - Toggle Track view/playlist view (red) + - For playlist view show playlist name, total time + - For track view show name, artist, time + - Toggle detail/progress view (green) + - Track view: all metadata + - Playlist view: all tracks (past three, upcoming ones) */ \ No newline at end of file diff --git a/mg_content_interface.c b/mg_content_interface.c index defa0ac..d017997 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -9,7 +9,6 @@ * Implements main classes of for content items and abstract interfaces to media access * * This file implements the following classes - * - mgContentItem * - mgTracklist * - mgSelectionTreeNode */ @@ -51,6 +50,19 @@ unsigned int mgTracklist::getNumItems() return m_list.size(); } +unsigned long mgTracklist::getLength() +{ + unsigned long result = 0; + std::vector::iterator iter; + + for( iter = m_list.begin(); iter != m_list.end (); iter++ ) + { + result += (*iter)->getLength(); + } + + return result; +} + void mgTracklist::shuffle() { random_shuffle( m_list.begin(), m_list.end () ); diff --git a/mg_content_interface.h b/mg_content_interface.h index b0a27eb..c127d03 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -1,5 +1,5 @@ /*! \file mg_content_interface.h - * \brief data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin + * \brief Data objects for content (e.g. mp3 files, movies) for the vdr muggle plugin * * \version $Revision: 1.4 $ * \date $Date$ @@ -27,7 +27,6 @@ #define ILLEGAL_ID -1 class mgFilter; -class mgPlaylist; /*! * \brief dummy player class @@ -63,9 +62,9 @@ class mgContentItem */ typedef enum contentType { - ABSTRACT =0, //< an abstract item which cannot be used - GD_AUDIO, //< a GiantDisc audio track - EPG //< an EPG item (i.e. a TV show) + ABSTRACT = 0, //< an abstract item which cannot be used + GD_AUDIO, //< a GiantDisc audio track + EPG //< an EPG item (i.e. a TV show) }; protected: @@ -273,6 +272,11 @@ class mgTracklist */ unsigned int getNumItems(); + /*! + * \brief returns the complete length of the playlist in seconds + */ + unsigned long getLength(); + /*! * \brief randomizes the order of the elements in the list */ @@ -311,12 +315,10 @@ class mgTracklist /*! * \brief returns an item from the list at the specified position */ - virtual mgContentItem* mgTracklist::getItem(unsigned int position); + virtual mgContentItem* getItem(unsigned int position); /*! * \brief remove item at position - * - * \todo needed? if so, it hides bool remove(int) */ virtual int removeItem(mgContentItem* item); // remove all occurences of item diff --git a/mg_playlist.c b/mg_playlist.c index 7480563..8e81c16 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -199,18 +199,25 @@ void mgPlaylist::setListname(std::string name) m_listname = name; } -// returns the count of items in the list -int mgPlaylist::getCount() -{ - return m_list.size(); -} - // returns current index in the playlist int mgPlaylist::getIndex() const { return m_current_idx; } +unsigned long mgPlaylist::getCompletedLength() +{ + unsigned long result = 0; + + std::vector::iterator iter; + for( iter = m_list.begin(); iter != m_list.begin() + m_current_idx; iter++ ) + { // each item in the list + result += (*iter)->getLength(); + } + + return result; +} + // returns the current item of the list mgContentItem* mgPlaylist::getCurrent() { @@ -228,7 +235,7 @@ mgContentItem* mgPlaylist::getCurrent() return result; } -// returns the nth track from the playlist +// skip to the nth track from the playlist bool mgPlaylist::gotoPosition(unsigned int position) { bool result = false; diff --git a/mg_playlist.h b/mg_playlist.h index 975458a..c5dd5fc 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -128,9 +128,6 @@ public: */ virtual void setListname(std::string name); - //! \brief returns the count of items in the list - int getCount(); - //! \brief access playlist items //@{ @@ -140,6 +137,9 @@ public: //! \brief make playlist persistent virtual bool storePlaylist() = 0; + //! \brief obtain length of content already played + unsigned long getCompletedLength(); + //! \brief export the playlist in m3u format virtual bool exportM3U( std::string m3u_file ); diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c index eb5cb42..6f2d7f6 100644 --- a/vdr_decoder_mp3.c +++ b/vdr_decoder_mp3.c @@ -73,16 +73,12 @@ mgMP3Decoder::mgMP3Decoder( mgContentItem *item, bool preinit) mgMP3Decoder::~mgMP3Decoder() { - MGLOG( "mgMP3Decoder::~mgMP3Decoder" ); - clean(); delete m_stream; } void mgMP3Decoder::init() { - MGLOG( "mgMP3Decoder::init" ); - clean(); mad_stream_init( &m_madstream ); @@ -102,8 +98,6 @@ void mgMP3Decoder::init() void mgMP3Decoder::clean() { - MGLOG( "mgMP3Decoder::clean" ); - m_playing = false; if( m_madsynth ) { @@ -119,22 +113,10 @@ void mgMP3Decoder::clean() m_madframe = 0; } mad_stream_finish( &m_madstream ); - - /* - if( m_frameinfo ) - { - cout << "mgMP3Decoder::clean: m_frameinfo deleted." << endl << flush; - delete m_frameinfo; - cout << "mgMP3Decoder::clean: m_frameinfo deleted." << endl << flush; - m_frameinfo = 0; - } - */ } bool mgMP3Decoder::valid(void) { - MGLOG( "mgMP3Decoder::valid" ); - bool res = false; if( tryLock() ) { @@ -167,11 +149,9 @@ bool mgMP3Decoder::valid(void) mgPlayInfo *mgMP3Decoder::playInfo() { - MGLOG( "mgMP3Decoder::playInfo" ); if( m_playing ) { m_playinfo.m_index = mad_timer_count( m_playtime, MAD_UNITS_SECONDS ); - // m_playinfo.Total = m_scan->Total; // TODO return &m_playinfo; } @@ -239,8 +219,6 @@ struct mgDecode *mgMP3Decoder::done(eDecodeStatus status) eDecodeStatus mgMP3Decoder::decodeError(bool hdr) { - MGLOG( "mgMP3Decoder::decodeError" ); - if( m_madstream.error == MAD_ERROR_BUFLEN || m_madstream.error == MAD_ERROR_BUFPTR ) { int s = mgMadStream( &m_madstream, m_stream ); @@ -274,8 +252,6 @@ eDecodeStatus mgMP3Decoder::decodeError(bool hdr) struct mgDecode *mgMP3Decoder::decode() { - // MGLOG( "mgMP3Decoder::decode" ); - lock(); // this is released in Done() eDecodeStatus r; diff --git a/vdr_player.c b/vdr_player.c index f30a53d..d69897f 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -199,12 +199,12 @@ public: void ToggleShuffle(void); void ToggleLoop(void); - virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false); + virtual bool getIndex(int &Current, int &Total, bool SnapToIFrame=false); // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW void NewPlaylist( mgPlaylist *plist, int first ); - mgContentItem *GetCurrent () { return m_current; } - mgPlaylist *GetPlaylist () { return m_playlist; } + mgContentItem *getCurrent () { return m_current; } + mgPlaylist *getPlaylist () { return m_playlist; } }; mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist, int first) @@ -868,13 +868,14 @@ void mgPCMPlayer::SkipSeconds(int secs) } } -bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) +bool mgPCMPlayer::getIndex( int ¤t, int &total, bool snaptoiframe ) { - if(m_current) { - current = SecondsToFrames( m_index ); - total = SecondsToFrames( m_current->getLength() ); - return true; - } + if( m_current ) + { + current = SecondsToFrames( m_index ); + total = SecondsToFrames( m_current->getLength() ); + return true; + } return false; } @@ -887,9 +888,12 @@ mgPlayerControl::mgPlayerControl( mgPlaylist *plist, int start ) #if VDRVERSNUM >= 10307 m_display = NULL; + m_menu = NULL; #endif m_visible = false; m_has_osd = false; + m_track_view = true; + m_progress_view = true; m_szLastShowStatusMsg = NULL; @@ -913,8 +917,6 @@ mgPlayerControl::~mgPlayerControl() bool mgPlayerControl::Active(void) { - MGLOG( "mgPlayerControl::Active" ); - return m_player && m_player->Active(); } @@ -999,53 +1001,149 @@ void mgPlayerControl::NewPlaylist(mgPlaylist *plist, int start) } } -void mgPlayerControl::ShowProgress() +void mgPlayerControl::ShowContents() { - StatusMsgReplaying(); +#if VDRVERSNUM >= 10307 + if( !m_menu ) + { + m_menu = Skins.Current()->DisplayMenu(); + } - if( m_visible ) + if( m_player && m_menu ) { - if( !m_has_osd ) + int num_items = m_menu->MaxItems(); + + if( m_track_view ) { - // open the osd if its not already there... -#if VDRVERSNUM >= 10307 - /* - osd = cOsdProvider::NewOsd (Setup.OSDLeft, Setup.OSDTop); - tArea Areas[] = { { 0, 0, Setup.OSDWidth, Setup.OSDHeight, 2 } }; - osd->SetAreas(Areas,sizeof(Areas)/sizeof(tArea)); - font = cFont::GetFont (fontOsd); - */ -#else - Interface->Open(); -#endif - m_has_osd = true; + m_menu->Clear(); + m_menu->SetTitle( "Track info view" ); + + m_menu->SetTabs( 15 ); + + char *buf; + if( num_items > 0 ) + { + asprintf( &buf, "Title:\t%s", m_player->getCurrent()->getLabel(0).c_str() ); + m_menu->SetItem( buf, 0, false, false ); + free( buf ); + } + if( num_items > 1 ) + { + asprintf( &buf, "Artist:\t%s", m_player->getCurrent()->getLabel(1).c_str() ); + m_menu->SetItem( buf, 1, false, false ); + free( buf ); + } + if( num_items > 2 ) + { + asprintf( &buf, "Album:\t%s", m_player->getCurrent()->getLabel(2).c_str() ); + m_menu->SetItem( buf, 2, false, false ); + free( buf ); + } + if( num_items > 3 ) + { + asprintf( &buf, "Genre:\t%s", m_player->getCurrent()->getLabel(3).c_str() ); + m_menu->SetItem( buf, 3, false, false ); + free( buf ); + } + if( num_items > 4 ) + { + int len = m_player->getCurrent()->getLength(); + asprintf( &buf, "Length:\t%s", IndexToHMSF( SecondsToFrames( len ) ) ); + m_menu->SetItem( buf, 4, false, false ); + free( buf ); + } + if( num_items > 5 ) + { + asprintf( &buf, "Bit rate:\t%s", m_player->getCurrent()->getBitrate().c_str() ); + m_menu->SetItem( buf, 5, false, false ); + free( buf ); + } + if( num_items > 6 ) + { + int sr = m_player->getCurrent()->getSampleRate(); + + asprintf( &buf, "Sampling rate:\t%d", sr ); + m_menu->SetItem( buf, 6, false, false ); + free( buf ); + } } + else + { + mgPlaylist *list = m_player->getPlaylist(); + if( list ) + { + // use items for playlist tag display + m_menu->Clear(); + m_menu->SetTitle( "Playlist info view" ); + + int cur = list->getIndex(); + + char *buf; + for( int i=0; i < num_items; i ++ ) + { + mgContentItem *item = list->getItem( cur-3+i ); + if( item->isValid() ) + { + asprintf( &buf, "%s\t%s", item->getLabel(0).c_str(), item->getLabel(1).c_str() ); + if( i < 3 ) + { // already played + m_menu->SetItem( buf, i, false, false ); + } + if( i > 3 ) + { // to be played + m_menu->SetItem( buf, i, false, true ); + } + if( i == 3 ) + { + m_menu->SetItem( buf, i, true, true ); + } + free( buf ); + } + } + } + } + } +#endif +} + +void mgPlayerControl::ShowProgress() +{ + if( m_player ) + { + char *buf; + bool play = true, forward = true; + int speed = -1; + + int current_frame, total_frames; + m_player->getIndex( current_frame, total_frames ); - // now an osd is open, go on - + if( !m_track_view ) + { // playlist stuff + mgPlaylist *list = m_player->getPlaylist(); + if( list ) + { + total_frames = SecondsToFrames( list->getLength() ); + current_frame += SecondsToFrames( list->getCompletedLength() ); + asprintf( &buf, "Playlist %s (%d/%d)", list->getListname().c_str(), list->getIndex()+1, list->getNumItems() ); + } + } + else + { // track view + asprintf( &buf, "%s: %s", m_player->getCurrent()->getLabel(1).c_str(), m_player->getCurrent()->getTitle().c_str() ); + } + #if VDRVERSNUM >= 10307 if( !m_display ) { m_display = Skins.Current()->DisplayReplay(false); } - if( m_player && m_display ) + if( m_display ) { - int current_frame, total_frames; - m_player->GetIndex( current_frame, total_frames ); - m_display->SetProgress( current_frame, total_frames ); m_display->SetCurrent( IndexToHMSF( current_frame ) ); - m_display->SetTotal( IndexToHMSF( total_frames ) ); - - char *buf; - asprintf( &buf, "%s", m_player->GetCurrent()->getTitle().c_str() ); + m_display->SetTotal( IndexToHMSF( total_frames ) ); m_display->SetTitle( buf ); - // free( buf ); - - bool play = true, forward = true; - int speed = -1; m_display->SetMode( play, forward, speed ); - m_display->Flush(); } #else @@ -1053,12 +1151,48 @@ void mgPlayerControl::ShowProgress() int h = Interface->Height(); Interface->WriteText( w/2, h/2, "Muggle is active!" ); - - // Add: song info (name, artist, pos in playlist, time, ...) - // Add: progress bar - Interface->Flush(); #endif + free( buf ); + } +} + +void mgPlayerControl::Display() +{ + StatusMsgReplaying(); + + if( m_visible ) + { + if( !m_has_osd ) + { + // open the osd if its not already there... +#if VDRVERSNUM >= 10307 +#else + Interface->Open(); +#endif + m_has_osd = true; + } + + // now an osd is open, go on + + if( m_progress_view ) + { + if( m_menu ) + { + delete m_menu; + m_menu = NULL; + } + ShowProgress(); + } + else + { + if( m_display ) + { + delete m_display; + m_display = NULL; + } + ShowContents(); + } } else { @@ -1079,15 +1213,16 @@ void mgPlayerControl::InternalHide() if( m_has_osd ) { #if VDRVERSNUM >= 10307 - /* - osd->Flush(); - delete osd; - */ if( m_display ) { delete m_display; m_display = NULL; } + if( m_menu ) + { + delete m_menu; + m_menu = NULL; + } #else Interface->Close(); #endif @@ -1102,7 +1237,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) return osEnd; } - ShowProgress(); + Display(); eOSState state = cControl::ProcessKey(key); @@ -1122,7 +1257,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) { if( !m_visible && m_player ) { - mgPlaylist *pl = m_player->GetPlaylist(); + mgPlaylist *pl = m_player->getPlaylist(); std::string s; switch( pl->toggleLoopMode() ) @@ -1155,13 +1290,15 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) else { // toggle progress display between simple and detail + m_progress_view = !m_progress_view; + Display(); } } break; case kGreen: { if( !m_visible && m_player ) { - mgPlaylist *pl = m_player->GetPlaylist(); + mgPlaylist *pl = m_player->getPlaylist(); std::string s; switch( pl->toggleShuffleMode() ) @@ -1193,7 +1330,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) } else { - // toggle progress display between playlist and single track + // toggle progress display between playlist and track + m_track_view = !m_track_view; + Display(); } } break; case kPause: @@ -1212,7 +1351,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) case kOk: { m_visible = !m_visible; - ShowProgress(); + Display(); return osContinue; } break; @@ -1236,28 +1375,28 @@ void mgPlayerControl::StatusMsgReplaying() { char *szBuf=NULL; if(m_player - && m_player->GetCurrent() - && m_player->GetCurrent()->isValid() - && m_player->GetCurrent()->getContentType() == mgContentItem::GD_AUDIO - && m_player->GetPlaylist()) + && m_player->getCurrent() + && m_player->getCurrent()->isValid() + && m_player->getCurrent()->getContentType() == mgContentItem::GD_AUDIO + && m_player->getPlaylist()) { /* - if(m_player->GetCurrent()->getAlbum().length() > 0) + if(m_player->getCurrent()->getAlbum().length() > 0) { asprintf(&szBuf,"[%c%c] (%d/%d) %s - %s", - m_player->GetPlaylist()->isLoop()?'L':'.', - m_player->GetPlaylist()->isShuffle()'S':'.', - m_player->GetPlaylist()->getIndex() + 1,m_player->GetPlaylist()->getCount(), - m_player->GetCurrent()->getTitle().c_str(), - m_player->GetCurrent()->getAlbum().c_str()); + m_player->getPlaylist()->isLoop()?'L':'.', + m_player->getPlaylist()->isShuffle()'S':'.', + m_player->getPlaylist()->getIndex() + 1,m_player->getPlaylist()->getCount(), + m_player->getCurrent()->getTitle().c_str(), + m_player->getCurrent()->getAlbum().c_str()); } else */ { asprintf(&szBuf,"[%c%c] (%d/%d) %s", /* TODO m_player->GetPlaylist()->isLoop()?'L':*/'.', /* TODO m_player->GetPlaylist()->isShuffle()'S':*/'.', - m_player->GetPlaylist()->getIndex() + 1,m_player->GetPlaylist()->getCount(), - m_player->GetCurrent()->getTitle().c_str()); + m_player->getPlaylist()->getIndex() + 1,m_player->getPlaylist()->getNumItems(), + m_player->getCurrent()->getTitle().c_str()); } } else diff --git a/vdr_player.h b/vdr_player.h index 3731b9d..d39d9d8 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -48,9 +48,14 @@ private: //! \brief indicates, whether an osd is currently displayed bool m_has_osd; + + bool m_track_view; + bool m_progress_view; + #if VDRVERSNUM >= 10307 //! \brief a replay display to show the progress during playback cSkinDisplayReplay *m_display; + cSkinDisplayMenu *m_menu; cOsd *osd; const cFont *font; @@ -121,6 +126,10 @@ public: //! \brief a progress display void ShowProgress(); + void Display(); + + void ShowContents(); + //! \brief hide the osd, if present void Hide(); -- cgit v1.2.3 From bc9a0e1a2e4580529e2e05e79026ec82f48f7d34 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 10 Oct 2004 14:00:31 +0000 Subject: Merged bugfixes from 0.0.7 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@221 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 2 +- muggle.doxygen | 2 +- vdr_player.c | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/muggle.c b/muggle.c index 14ff37b..4cc7b0d 100644 --- a/muggle.c +++ b/muggle.c @@ -22,7 +22,7 @@ #include #include -static const char *VERSION = "0.0.6"; +static const char *VERSION = "0.0.7"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/muggle.doxygen b/muggle.doxygen index 8703db4..d393679 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.0.5 +PROJECT_NUMBER = 0.0.7 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/vdr_player.c b/vdr_player.c index d69897f..d46a4af 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -1174,23 +1174,26 @@ void mgPlayerControl::Display() } // now an osd is open, go on - if( m_progress_view ) { +#if VDRVERSNUM >= 10307 if( m_menu ) { delete m_menu; m_menu = NULL; } +#endif ShowProgress(); } else { +#if VDRVERSNUM >= 10307 if( m_display ) { delete m_display; m_display = NULL; } +#endif ShowContents(); } } -- cgit v1.2.3 From a02be44fbf9748d635782fb75ba2cef18ba37e44 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 10 Oct 2004 14:07:32 +0000 Subject: Include i18n.h for 1.2.6 compatibility git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@222 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 4 ++-- vdr_player.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 94df06f..f9e61ef 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,8 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -#DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE +#DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -D_GNU_SOURCE MIFLAGS += -I/usr/include/taglib -lmysqlclient diff --git a/vdr_player.c b/vdr_player.c index d46a4af..cf2fb92 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -41,6 +41,7 @@ #include "vdr_decoder.h" #include "vdr_config.h" #include "vdr_setup.h" +#include "i18n.h" #include "mg_tools.h" #include "mg_playlist.h" -- cgit v1.2.3 From 2890ec56dc3724551aa60de0ddf3510836a090f8 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 10 Oct 2004 14:08:56 +0000 Subject: Removed vorbis option from Makefile for release git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@223 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f9e61ef..bb397d0 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,8 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE -#DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +# DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -D_GNU_SOURCE MIFLAGS += -I/usr/include/taglib -lmysqlclient -- cgit v1.2.3 From 5c67d1d9eae0cbcf4ced5dadc9434165d8aa0de6 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 10 Oct 2004 19:32:26 +0000 Subject: Corrected error in query string when adding new files git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@224 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mugglei.c b/mugglei.c index 3460c2c..bba1d42 100755 --- a/mugglei.c +++ b/mugglei.c @@ -266,9 +266,9 @@ void update_db( long uid, std::string filename ) } else { // the entry does not exist, create it - mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,genre1,genre2,year," - "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1)" - " VALUES (\"%s\", \"%s\", \"\", \"\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, %s)", + mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,year," + "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1, genre1)" + " VALUES (\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\", \"%s\")", artist.toCString(), title.toCString(), year, cddbid.toCString(), trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString() ); -- cgit v1.2.3 From b4d6bf0a31ec3b491e3e8b8bf197f11c69a42819 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 11 Oct 2004 06:49:53 +0000 Subject: TODOs updated git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@225 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 84 ++++++++++++++++++++++++++++---------------------------------------- 1 file changed, 34 insertions(+), 50 deletions(-) diff --git a/TODO b/TODO index 764986f..30f365b 100644 --- a/TODO +++ b/TODO @@ -17,8 +17,10 @@ \subsection urgentosd OSD-related Issues + - Red key does not work in Browser submenu - Possible to resume play instead of restarting list from the beginning? - Save on exit + - Rename playlist \subsection urgentcode Code polishing @@ -71,93 +73,75 @@ - Init scale/level/normalize? - The max. level should be recognized during play - Store max. level in the database + - Display covers \subsection deploy Deployment - - Script to publish a version - - make dist - - copy .tgz, README, CHANGES, HISTORY into web directory - - generate documentation - - copy into web directory - - sync with web - - How to track bugs and feature requests? + - Script to publish a version + - make dist + - copy .tgz, README, CHANGES, HISTORY into web directory + - generate documentation + - copy into web directory + - sync with web + - How to track bugs and feature requests? - \verbatim - # $1: version name (e.g. 0.0.5-BETA) + \verbatim + # $1: version name (e.g. 0.0.5-BETA) + # how to determine current path? + svn copy ... http://.../svn/muggle/tags/$1 - # how to determine current path? - svn copy ... http://.../svn/muggle/tags/$1 + make dist + # obtain name from output? or copy commands and make correctly + mv vdr-muggle-0.0.1.tgz ~/Web/current/htpc/muggle/vdr-muggle-$1.tgz - make dist - # obtain name from output? or copy commands and make correctly - mv vdr-muggle-0.0.1.tgz ~/Web/current/htpc/muggle/vdr-muggle-$1.tgz + cp README ... + cp TODO ... + cp CHANGES ... - cp README ... - cp TODO ... - cp CHANGES ... + doxygen muggle.doxygen + cp -R doc ~/Web/current/htpc/muggle/ - doxygen muggle.doxygen - cp -R doc ~/Web/current/htpc/muggle/ - - sitecopy --update htpctech - \endverbatim + sitecopy --update htpctech + \endverbatim \section mid ToDo items of moderate importance \subsection midimport Import stuff - Album + - Cover images (based on filename or tag) - Genre - Modified - Cover text - - Cover images (based on filename or tag) - Tracks - Language (?) - - Genre1, 2 - Rating? - Modified, created - Lyrics - - Import playlist from m3u - - Run import/update from within OSD? + - Import playlist from m3u + - Run import/update from within OSD? - - Handle updates in both directions - - Check modification date in DB/fstat - - if file is newer: update tags into db - - if DB is newer: update db into tags + - Handle updates in both directions + - Check modification date in DB/fstat + - if file is newer: update tags into db + - if DB is newer: update db into tags \subsection midcode Code issues - really abstract from specific queries etc. - mgDatabase should completely abstract database (mySQL) stuff!? + - initialization - read/write queries - - return results (needs a homogeneous representation of results?) + - return results (needs an abstract representation of results?) \subsection midosd OSD-related issues - Incremental search - - Type numbers to enter characters and jump to first title accordingly + - Type numbers to enter characters and jump to first title accordingly - Check whether submenus (as implemented in VDR) are more suitable - do not permit jumping to arbitrary menus though - - Add play indicators - - Toggle display with Ok (off -> track info -> album info -> playlist info -> off) - - Display track info (use vdr 1.3.12 progress display) - - Song title - - Artist - - Genre - - Album - - Bitrate, channels, samplerate, format, ... - - Progress display - - Display album info - - Album name - - Artist - - Year - - No. Tracks - - Track names? - - Display playlist info - - Overall progress - - Next track(s) \subsection midcontent Content issues -- cgit v1.2.3 From df038f3f74c04a8702cb572bf79e8095bb60f7d3 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 11 Oct 2004 16:15:32 +0000 Subject: Added basic stuff for cover displays git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@226 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index cf2fb92..ba87ecd 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -880,6 +880,229 @@ bool mgPCMPlayer::getIndex( int ¤t, int &total, bool snaptoiframe ) return false; } +/* +string mgPCMPlayer::CheckImage( string fileName, size_t j ) +{ + static char tmpFile[1024]; + char *tmp[2048]; + char *result = NULL; + FILE *fp; + + sprintf (tmpFile, "%s/%s", MP3Setup.ImageCacheDir, &fileName[j]); // ??? + strcpy (strrchr (tmpFile, '.'), ".mpg"); + d(printf("mp3[%d]: cache %s\n", getpid (), tmpFile)) + if ((fp = fopen (tmpFile, "rb"))) + { + fclose (fp); + result = tmpFile; + } + else + { + if ((fp = fopen (fileName, "rb"))) + { + fclose (fp); + d(printf("mp3[%d]: image %s found\n", getpid (), fileName)) + sprintf ((char *) tmp, "image_convert.sh \"%s\" \"%s\"", fileName, tmpFile); + system ((const char*) tmp); + result = tmpFile; + } + } + fp = fopen ("/tmp/vdr_mp3_current_image.txt", "w"); + fprintf (fp, "%s\n", fileName); + fclose (fp); + return result; +} + +char *cMP3Player::LoadImage(const char *fullname) +{ + size_t i, j = strlen (MP3Sources.GetSource()->BaseDir()) + 1; + char imageFile[1024]; ++ static char mpgFile[1024]; + char *p, *q = NULL; + char *imageSuffixes[] = { "png", "gif", "jpg" }; + + d(printf("mp3[%d]: checking %s for images\n", getpid (), fullname)) + strcpy (imageFile, fullname); + strcpy (mpgFile, ""); + // + // track specific image, e.g. .jpg + // + p = strrchr (imageFile, '.'); + if (p) + { + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + strcpy (p + 1, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, j); + if (q) + { + strcpy (mpgFile, q); + } + } + } + // + // album specific image, e.g. cover.jpg in song directory + // + if (!strlen (mpgFile)) + { + p = strrchr (imageFile, '/'); + if (p) + { + strcpy (p + 1, "cover."); + p += 6; + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + strcpy (p + 1, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, j); + if (q) + { + strcpy (mpgFile, q); + } + } + } + } + // + // artist specific image, e.g. artist.jpg in artist directory + // + if (!strlen (mpgFile)) + { + p = strrchr (imageFile, '/'); + if (p) + { + *p = '\0'; + p = strrchr (imageFile, '/'); + } + if (p) + { + strcpy (p + 1, "artist."); + p += 7; + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + strcpy (p + 1, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, j); + if (q) + { + strcpy (mpgFile, q); + } + } + } + } + // + // default background image + // + if (!strlen (mpgFile)) + { + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + sprintf (imageFile, "%s/background.%s", MP3Setup.ImageCacheDir, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, strlen(MP3Setup.ImageCacheDir) + 1); + if (q) + { + strcpy (mpgFile, q); + } + } + } + if (!strlen (mpgFile)) + { + sprintf (mpgFile, "%s/background.mpg", MP3Setup.ImageCacheDir); + } + return mpgFile; +} + +void mgPCMPlayer::ShowImage (char *file) +{ + uchar *buffer; + int fd; + struct stat st; + struct video_still_picture sp; + + if ((fd = open (file, O_RDONLY)) >= 0) + { + d(printf("mp3[%d]: cover still picture %s\n", getpid (), file)) + fstat (fd, &st); + sp.iFrame = (char *) malloc (st.st_size); + if (sp.iFrame) + { + sp.size = st.st_size; + if (read (fd, sp.iFrame, sp.size) > 0) + { + buffer = (uchar *) sp.iFrame; + d(printf("mp3[%d]: new image frame (size %d)\n", getpid(), sp.size)) + if(MP3Setup.UseDeviceStillPicture) + DeviceStillPicture (buffer, sp.size); + else + { + for (int i = 1; i <= 25; i++) + { + send_pes_packet (buffer, sp.size, i); + } + } + } + free (sp.iFrame); + } + else + { + esyslog ("mp3[%d]: cannot allocate memory (%d bytes) for still image", + getpid(), (int) st.st_size); + } + close (fd); + } + else + { + esyslog ("mp3[%d]: cannot open image file '%s'", + getpid(), file); + } +} + +void mgPCMPlayer::send_pes_packet(unsigned char *data, int len, int timestamp) +{ +#define PES_MAX_SIZE 2048 + int ptslen = timestamp ? 5 : 1; + static unsigned char pes_header[PES_MAX_SIZE]; + + pes_header[0] = pes_header[1] = 0; + pes_header[2] = 1; + pes_header[3] = 0xe0; + + while(len > 0) + { + int payload_size = len; + if(6 + ptslen + payload_size > PES_MAX_SIZE) + payload_size = PES_MAX_SIZE - (6 + ptslen); + + pes_header[4] = (ptslen + payload_size) >> 8; + pes_header[5] = (ptslen + payload_size) & 255; + + if(ptslen == 5) + { + int x; + x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1; + pes_header[8] = x; + x = ((((timestamp >> 15) & 0x7fff) << 1) | 1); + pes_header[7] = x >> 8; + pes_header[8] = x & 255; + x = ((((timestamp) & 0x7fff) < 1) | 1); + pes_header[9] = x >> 8; + pes_header[10] = x & 255; + } else + { + pes_header[6] = 0x0f; + } + + memcpy(&pes_header[6 + ptslen], data, payload_size); + PlayVideo(pes_header, 6 + ptslen + payload_size); + + len -= payload_size; + data += payload_size; + ptslen = 1; + } +} +*/ + // --- mgPlayerControl ------------------------------------------------------- mgPlayerControl::mgPlayerControl( mgPlaylist *plist, int start ) -- cgit v1.2.3 From c3138201aa15b89d76ef7b8f6cd68496a98c312c Mon Sep 17 00:00:00 2001 From: rak Date: Mon, 11 Oct 2004 19:19:20 +0000 Subject: changed sort mechanism of "Titel nach Genre und Jahr" as requested by wolfgang61 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@227 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index e3ee5fc..57a3f67 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -1069,7 +1069,8 @@ bool GdTreeNode::expand() " CONCAT(tracks.artist,' - ',tracks.title) AS title" " ,tracks.id" FROMJOIN - " ORDER BY tracks.title", + " ORDER BY title", + //" ORDER BY tracks.title", m_restriction.c_str()); idfield = "tracks.id"; } -- cgit v1.2.3 From 1761a1a8961e2ec1f6338c42af5c9e0aa56822b4 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 11 Oct 2004 22:02:14 +0000 Subject: Corrected position of code to confirm loop/shuffle modes git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@228 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 6 +- mg_playlist.h | 5 ++ vdr_menu.c | 4 ++ vdr_player.c | 217 +++++++++++++++++++++++++++++++++------------------------- vdr_player.h | 4 +- vdr_setup.c | 6 +- 6 files changed, 141 insertions(+), 101 deletions(-) diff --git a/TODO b/TODO index 30f365b..17c23d5 100644 --- a/TODO +++ b/TODO @@ -17,7 +17,6 @@ \subsection urgentosd OSD-related Issues - - Red key does not work in Browser submenu - Possible to resume play instead of restarting list from the beginning? - Save on exit - Rename playlist @@ -69,6 +68,8 @@ \subsection urgentplayer Player extensions + - Display covers + - Add FLAC decoder - Determine max. framecount (needed for rewinding)? - Init scale/level/normalize? - The max. level should be recognized during play @@ -157,8 +158,6 @@ \subsection midplayer Player issues - - Display covers - - Add FLAC decoder - Use single CD files with cuesheets in metadata for FLAC - Handle recoding samplerate, limiter etc correctly @@ -241,5 +240,6 @@ - Toggle detail/progress view (green) - Track view: all metadata - Playlist view: all tracks (past three, upcoming ones) + - BUG: Red key does not work in Browser submenu */ \ No newline at end of file diff --git a/mg_playlist.h b/mg_playlist.h index c5dd5fc..0c32297 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -76,6 +76,11 @@ public: //! \brief toggle the shuffle mode. ShuffleMode toggleShuffleMode( ); + //! \brief report the loop mode. + LoopMode getLoopMode( ) const { return m_loop_mode; } + + //! \brief report the shuffle mode. + ShuffleMode getShuffleMode( ) const { return m_shuffle_mode; } //@} //! \brief modify playlist items diff --git a/vdr_menu.c b/vdr_menu.c index 519c809..bea0db0 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -248,6 +248,10 @@ eOSState mgMainMenu::ProcessKey(eKeys key) state = osContinue; } break; + case kRed: + { + state = TreeSubmenuAction( 0 ); + } break; case kBlue: { m_state = TREE; diff --git a/vdr_player.c b/vdr_player.c index ba87ecd..4f16d66 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -200,7 +200,7 @@ public: void ToggleShuffle(void); void ToggleLoop(void); - virtual bool getIndex(int &Current, int &Total, bool SnapToIFrame=false); + virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false); // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW void NewPlaylist( mgPlaylist *plist, int first ); @@ -869,7 +869,7 @@ void mgPCMPlayer::SkipSeconds(int secs) } } -bool mgPCMPlayer::getIndex( int ¤t, int &total, bool snaptoiframe ) +bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) { if( m_current ) { @@ -1106,7 +1106,7 @@ void mgPCMPlayer::send_pes_packet(unsigned char *data, int len, int timestamp) // --- mgPlayerControl ------------------------------------------------------- mgPlayerControl::mgPlayerControl( mgPlaylist *plist, int start ) - : cControl( m_player = new mgPCMPlayer( plist, start ) ) + : cControl( player = new mgPCMPlayer( plist, start ) ) { MGLOG( "mgPlayerControl::mgPlayerControl" ); @@ -1141,87 +1141,87 @@ mgPlayerControl::~mgPlayerControl() bool mgPlayerControl::Active(void) { - return m_player && m_player->Active(); + return player && player->Active(); } void mgPlayerControl::Stop(void) { - if( m_player ) + if( player ) { - delete m_player; - m_player = 0; + delete player; + player = 0; } } void mgPlayerControl::Pause(void) { - if( m_player ) + if( player ) { - m_player->Pause(); + player->Pause(); } } void mgPlayerControl::Play(void) { - if( m_player ) + if( player ) { - m_player->Play(); + player->Play(); } } void mgPlayerControl::Forward(void) { - if( m_player ) + if( player ) { - m_player->Forward(); + player->Forward(); } } void mgPlayerControl::Backward(void) { - if( m_player ) + if( player ) { - m_player->Backward(); + player->Backward(); } } void mgPlayerControl::SkipSeconds(int Seconds) { - if( m_player ) + if( player ) { - m_player->SkipSeconds(Seconds); + player->SkipSeconds(Seconds); } } void mgPlayerControl::Goto(int Position, bool Still) { - if( m_player ) + if( player ) { - m_player->Goto(Position, Still); + player->Goto(Position, Still); } } void mgPlayerControl::ToggleShuffle(void) { - if( m_player ) + if( player ) { - m_player->ToggleShuffle(); + player->ToggleShuffle(); } } void mgPlayerControl::ToggleLoop(void) { - if( m_player ) + if( player ) { - m_player->ToggleLoop(); + player->ToggleLoop(); } } void mgPlayerControl::NewPlaylist(mgPlaylist *plist, int start) { - if( m_player ) + if( player ) { - m_player->NewPlaylist(plist, start); + player->NewPlaylist(plist, start); } } @@ -1233,7 +1233,7 @@ void mgPlayerControl::ShowContents() m_menu = Skins.Current()->DisplayMenu(); } - if( m_player && m_menu ) + if( player && m_menu ) { int num_items = m_menu->MaxItems(); @@ -1247,44 +1247,44 @@ void mgPlayerControl::ShowContents() char *buf; if( num_items > 0 ) { - asprintf( &buf, "Title:\t%s", m_player->getCurrent()->getLabel(0).c_str() ); + asprintf( &buf, "Title:\t%s", player->getCurrent()->getLabel(0).c_str() ); m_menu->SetItem( buf, 0, false, false ); free( buf ); } if( num_items > 1 ) { - asprintf( &buf, "Artist:\t%s", m_player->getCurrent()->getLabel(1).c_str() ); + asprintf( &buf, "Artist:\t%s", player->getCurrent()->getLabel(1).c_str() ); m_menu->SetItem( buf, 1, false, false ); free( buf ); } if( num_items > 2 ) { - asprintf( &buf, "Album:\t%s", m_player->getCurrent()->getLabel(2).c_str() ); + asprintf( &buf, "Album:\t%s", player->getCurrent()->getLabel(2).c_str() ); m_menu->SetItem( buf, 2, false, false ); free( buf ); } if( num_items > 3 ) { - asprintf( &buf, "Genre:\t%s", m_player->getCurrent()->getLabel(3).c_str() ); + asprintf( &buf, "Genre:\t%s", player->getCurrent()->getLabel(3).c_str() ); m_menu->SetItem( buf, 3, false, false ); free( buf ); } if( num_items > 4 ) { - int len = m_player->getCurrent()->getLength(); + int len = player->getCurrent()->getLength(); asprintf( &buf, "Length:\t%s", IndexToHMSF( SecondsToFrames( len ) ) ); m_menu->SetItem( buf, 4, false, false ); free( buf ); } if( num_items > 5 ) { - asprintf( &buf, "Bit rate:\t%s", m_player->getCurrent()->getBitrate().c_str() ); + asprintf( &buf, "Bit rate:\t%s", player->getCurrent()->getBitrate().c_str() ); m_menu->SetItem( buf, 5, false, false ); free( buf ); } if( num_items > 6 ) { - int sr = m_player->getCurrent()->getSampleRate(); + int sr = player->getCurrent()->getSampleRate(); asprintf( &buf, "Sampling rate:\t%d", sr ); m_menu->SetItem( buf, 6, false, false ); @@ -1293,7 +1293,7 @@ void mgPlayerControl::ShowContents() } else { - mgPlaylist *list = m_player->getPlaylist(); + mgPlaylist *list = player->getPlaylist(); if( list ) { // use items for playlist tag display @@ -1332,18 +1332,18 @@ void mgPlayerControl::ShowContents() void mgPlayerControl::ShowProgress() { - if( m_player ) + if( player ) { char *buf; bool play = true, forward = true; int speed = -1; int current_frame, total_frames; - m_player->getIndex( current_frame, total_frames ); + player->GetIndex( current_frame, total_frames ); if( !m_track_view ) { // playlist stuff - mgPlaylist *list = m_player->getPlaylist(); + mgPlaylist *list = player->getPlaylist(); if( list ) { total_frames = SecondsToFrames( list->getLength() ); @@ -1353,7 +1353,7 @@ void mgPlayerControl::ShowProgress() } else { // track view - asprintf( &buf, "%s: %s", m_player->getCurrent()->getLabel(1).c_str(), m_player->getCurrent()->getTitle().c_str() ); + asprintf( &buf, "%s: %s", player->getCurrent()->getLabel(1).c_str(), player->getCurrent()->getTitle().c_str() ); } #if VDRVERSNUM >= 10307 @@ -1383,8 +1383,6 @@ void mgPlayerControl::ShowProgress() void mgPlayerControl::Display() { - StatusMsgReplaying(); - if( m_visible ) { if( !m_has_osd ) @@ -1464,6 +1462,8 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) return osEnd; } + StatusMsgReplaying(); + Display(); eOSState state = cControl::ProcessKey(key); @@ -1482,9 +1482,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) } break; case kRed: { - if( !m_visible && m_player ) + if( !m_visible && player ) { - mgPlaylist *pl = m_player->getPlaylist(); + mgPlaylist *pl = player->getPlaylist(); std::string s; switch( pl->toggleLoopMode() ) @@ -1505,14 +1505,14 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) { s = tr( "Unknown loop mode" ); } + } #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo, s.c_str() ); - Skins.Flush(); + Skins.Message(mtInfo, s.c_str() ); + Skins.Flush(); #else - Interface->Status( s.c_str() ); - Interface->Flush(); + Interface->Status( s.c_str() ); + Interface->Flush(); #endif - } } else { @@ -1523,9 +1523,9 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) } break; case kGreen: { - if( !m_visible && m_player ) + if( !m_visible && player ) { - mgPlaylist *pl = m_player->getPlaylist(); + mgPlaylist *pl = player->getPlaylist(); std::string s; switch( pl->toggleShuffleMode() ) @@ -1546,14 +1546,14 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) { s = tr( "Unknown shuffle mode" ); } + } #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo, s.c_str() ); - Skins.Flush(); + Skins.Message(mtInfo, s.c_str() ); + Skins.Flush(); #else - Interface->Status( s.c_str() ); - Interface->Flush(); + Interface->Status( s.c_str() ); + Interface->Flush(); #endif - } } else { @@ -1601,50 +1601,81 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) void mgPlayerControl::StatusMsgReplaying() { char *szBuf=NULL; - if(m_player - && m_player->getCurrent() - && m_player->getCurrent()->isValid() - && m_player->getCurrent()->getContentType() == mgContentItem::GD_AUDIO - && m_player->getPlaylist()) + if(player + && player->getCurrent() + && player->getPlaylist()) { - /* - if(m_player->getCurrent()->getAlbum().length() > 0) - { - asprintf(&szBuf,"[%c%c] (%d/%d) %s - %s", - m_player->getPlaylist()->isLoop()?'L':'.', - m_player->getPlaylist()->isShuffle()'S':'.', - m_player->getPlaylist()->getIndex() + 1,m_player->getPlaylist()->getCount(), - m_player->getCurrent()->getTitle().c_str(), - m_player->getCurrent()->getAlbum().c_str()); - } - else */ - { - asprintf(&szBuf,"[%c%c] (%d/%d) %s", - /* TODO m_player->GetPlaylist()->isLoop()?'L':*/'.', - /* TODO m_player->GetPlaylist()->isShuffle()'S':*/'.', - m_player->getPlaylist()->getIndex() + 1,m_player->getPlaylist()->getNumItems(), - m_player->getCurrent()->getTitle().c_str()); - } - } - else - { - asprintf(&szBuf,"[muggle]"); - } - - if(szBuf - && ( m_szLastShowStatusMsg == NULL - || 0 != strcmp(szBuf,m_szLastShowStatusMsg) ) ) - { - cStatus::MsgReplaying(this,szBuf); - - if(m_szLastShowStatusMsg) + char cLoopMode; + char cShuffle; + + switch( player->getPlaylist()->getLoopMode() ) + { + default: + case mgPlaylist::LM_NONE: + cLoopMode = '.'; // Loop mode off + break; + case mgPlaylist::LM_SINGLE: + cLoopMode = 'S'; // Loop mode single + break; + case mgPlaylist::LM_FULL: + cLoopMode = 'P'; // Loop mode fuel + break; + } + + switch( player->getPlaylist()->getShuffleMode() ) { - free(m_szLastShowStatusMsg); + default: + case mgPlaylist::SM_NONE: + cShuffle = '.'; // Shuffle mode off + break; + case mgPlaylist::SM_NORMAL: + cShuffle = 'S'; // Shuffle mode normal + break; + case mgPlaylist::SM_PARTY: + cShuffle = 'P'; // Shuffle mode party + break; + } + + if(player->getCurrent()->getLabel(1).length() > 0) + { + asprintf(&szBuf,"[%c%c] (%d/%d) %s - %s", + cLoopMode, + cShuffle, + player->getPlaylist()->getIndex() + 1,player->getPlaylist()->getNumItems(), + player->getCurrent()->getLabel(1).c_str(), + player->getCurrent()->getTitle().c_str()); + } + else + { + asprintf(&szBuf,"[%c%c] (%d/%d) %s", + cLoopMode, + cShuffle, + player->getPlaylist()->getIndex() + 1,player->getPlaylist()->getNumItems(), + player->getCurrent()->getTitle().c_str()); } - m_szLastShowStatusMsg = szBuf; } else { - free(szBuf); + asprintf(&szBuf,"[muggle]"); + } + + //fprintf(stderr,"StatusMsgReplaying(%s)\n",szBuf); + if( szBuf ) + { + if( m_szLastShowStatusMsg == NULL + || 0 != strcmp(szBuf,m_szLastShowStatusMsg) ) + { + if(m_szLastShowStatusMsg) + { + free(m_szLastShowStatusMsg); + } + m_szLastShowStatusMsg = szBuf; + cStatus::MsgReplaying(this,m_szLastShowStatusMsg); + } + else + { + free(szBuf); + } } } + diff --git a/vdr_player.h b/vdr_player.h index d39d9d8..2c73771 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -40,8 +40,8 @@ class mgPlayerControl : public cControl { private: - //! \brief the reference to the player , don't rename it see cControl - mgPCMPlayer *m_player; + //! \brief the reference to the player + mgPCMPlayer *player; //! \brief indicates, whether the osd should be visible bool m_visible; diff --git a/vdr_setup.c b/vdr_setup.c index 13f44ac..afa7b85 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -3,11 +3,11 @@ * \brief A setup class for a VDR media plugin (muggle) * * \version $Revision: 1.3 $ - * \date $Date: 2004/07/25 21:33:35 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_setup.c,v 1.3 2004/07/25 21:33:35 lvw Exp $ + * $Id$ * * Partially adapted from * MP3/MPlayer plugin to VDR (C++) -- cgit v1.2.3 From fd8a88d9287e2c0ded3e0601665a785ce0ae5c55 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 18 Oct 2004 14:31:07 +0000 Subject: Added flag to generate position independent code git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@230 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bb397d0..80334c6 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri ### The C++ compiler and options: CXX ?= g++-3.3 -CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g +CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g ### The directory environment: -- cgit v1.2.3 From 379307489d4dad99a273ab687e471235ce6e3259 Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 19 Oct 2004 19:15:39 +0000 Subject: Added french translation thanks to Patrice git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@231 e10066b5-e1e2-0310-b819-94efdf66514b --- i18n.c | 105 +++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/i18n.c b/i18n.c index 2231660..4332a20 100644 --- a/i18n.c +++ b/i18n.c @@ -2,6 +2,7 @@ * i18n.c: Internationalization * * See the README file for copyright information and how to reach the author. + * Traduction en Français Patrice Staudt * * $Id$ */ @@ -16,7 +17,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Notation",// Traduction en Français Patrice Staudt "",// TODO "",// TODO "",// TODO @@ -32,7 +33,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Base de donnée Muggle Media",// TODO Francais "",// TODO "",// TODO "",// TODO @@ -48,7 +49,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Commande navigateur",// TODO "",// TODO "",// TODO "",// TODO @@ -64,7 +65,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Résultat de recherche",// TODO "",// TODO "",// TODO "",// TODO @@ -80,7 +81,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Titre après albume",// TODO "",// TODO "",// TODO "",// TODO @@ -96,7 +97,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Genre -> Année -> Titre",// TODO "",// TODO "",// TODO "",// TODO @@ -112,7 +113,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "interprète -> titre",// TODO "",// TODO "",// TODO "",// TODO @@ -128,7 +129,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Genre -> interprète -> album -> Titre",// TODO "",// TODO "",// TODO "",// TODO @@ -144,7 +145,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "interprète -> album -> Titre",// TODO "",// TODO "",// TODO "",// TODO @@ -160,7 +161,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Navigateur",// TODO "",// TODO "",// TODO "",// TODO @@ -176,7 +177,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Mode rechercher",// TODO "",// TODO "",// TODO "",// TODO @@ -192,7 +193,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Rechercher",// TODO "",// TODO "",// TODO "",// TODO @@ -208,7 +209,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Infos Albume",// TODO "",// TODO "",// TODO "",// TODO @@ -224,7 +225,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Édition PL?",// TODO "",// TODO "",// TODO "",// TODO @@ -240,7 +241,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Filtre",// TODO "",// TODO "",// TODO "",// TODO @@ -256,7 +257,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Détails trites",// TODO "",// TODO "",// TODO "",// TODO @@ -272,7 +273,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Édition PL",// TODO "",// TODO "",// TODO "",// TODO @@ -288,7 +289,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Chercher titre",// TODO "",// TODO "",// TODO "",// TODO @@ -304,7 +305,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Chercher albume",// TODO "",// TODO "",// TODO "",// TODO @@ -320,7 +321,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Chercher playlist",// TODO "",// TODO "",// TODO "",// TODO @@ -336,7 +337,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Playlist titre",// TODO "",// TODO "",// TODO "",// TODO @@ -352,7 +353,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Playlist auteur",// TODO "",// TODO "",// TODO "",// TODO @@ -368,7 +369,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Albume interpret",// TODO "",// TODO "",// TODO "",// TODO @@ -384,7 +385,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Albume titre",// TODO "",// TODO "",// TODO "",// TODO @@ -399,7 +400,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "estimation",// TODO "",// TODO "",// TODO "",// TODO @@ -416,7 +417,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Année (à)",// TODO "",// TODO "",// TODO "",// TODO @@ -432,7 +433,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Année (de)",// TODO "",// TODO "",// TODO "",// TODO @@ -448,7 +449,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Genre",// TODO "",// TODO "",// TODO "",// TODO @@ -464,7 +465,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "interprète",// TODO "",// TODO "",// TODO "",// TODO @@ -480,7 +481,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "titre",// TODO "",// TODO "",// TODO "",// TODO @@ -496,7 +497,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "ajouter",// TODO "",// TODO "",// TODO "",// TODO @@ -512,7 +513,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Mode Navigateur",// TODO "",// TODO "",// TODO "",// TODO @@ -528,7 +529,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Playlist",// TODO "",// TODO "",// TODO "",// TODO @@ -544,7 +545,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "sous menu",// TODO "",// TODO "",// TODO "",// TODO @@ -560,7 +561,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "charger",// TODO "",// TODO "",// TODO "",// TODO @@ -576,7 +577,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "sauvegarder",// TODO "",// TODO "",// TODO "",// TODO @@ -592,7 +593,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "effacer",// TODO "",// TODO "",// TODO "",// TODO @@ -608,7 +609,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Menu principal",// TODO "",// TODO "",// TODO "",// TODO @@ -624,7 +625,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "%d mettre titre dans la playlist actuel?",// TODO "",// TODO "",// TODO "",// TODO @@ -640,7 +641,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Charger playlist",// TODO "",// TODO "",// TODO "",// TODO @@ -656,7 +657,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "sauvegarder playlist",// TODO "",// TODO "",// TODO "",// TODO @@ -672,7 +673,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Playliste umbenennen",// TODO "",// TODO "",// TODO "",// TODO @@ -688,7 +689,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Playliste vider",// TODO "",// TODO "",// TODO "",// TODO @@ -704,7 +705,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Supprimer de la list",// TODO "",// TODO "",// TODO "",// TODO @@ -720,7 +721,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Exporter la playlist",// TODO "",// TODO "",// TODO "",// TODO @@ -736,7 +737,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "commande externe playlist",// TODO "",// TODO "",// TODO "",// TODO @@ -752,7 +753,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Déclancher le mode répétition",// TODO "",// TODO "",// TODO "",// TODO @@ -768,7 +769,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Mode répétition titre seul",// TODO "",// TODO "",// TODO "",// TODO @@ -784,7 +785,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Mode répétition playlist",// TODO "",// TODO "",// TODO "",// TODO @@ -800,7 +801,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "mode allèatoire déclenché",// TODO "",// TODO "",// TODO "",// TODO @@ -816,7 +817,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Mode allèatoire normal",// TODO "",// TODO "",// TODO "",// TODO @@ -832,7 +833,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "Mode allèatoire fêtes",// TODO "",// TODO "",// TODO "",// TODO -- cgit v1.2.3 From 8ea2d3a70af5ca6c1e060f3e9bb91b0630e0dce9 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 24 Oct 2004 18:04:35 +0000 Subject: Using an unsigned integer for playlist position git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@232 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 7 ++++--- mg_playlist.c | 17 +++++++++-------- mg_playlist.h | 8 ++++---- vdr_player.c | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/TODO b/TODO index 17c23d5..e8a2987 100644 --- a/TODO +++ b/TODO @@ -66,11 +66,12 @@ - if n < playcount / max. playcount - add the file to the end of the list - \subsection urgentplayer Player extensions - + \subsection urgentplayer Player extensions - Display covers + - Import filename + - Show image during replay - Add FLAC decoder - - Determine max. framecount (needed for rewinding)? + - Determine max. framecount (needed for rewinding?) - Init scale/level/normalize? - The max. level should be recognized during play - Store max. level in the database diff --git a/mg_playlist.c b/mg_playlist.c index 8e81c16..0b38925 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -22,7 +22,7 @@ mgPlaylist::mgPlaylist() { - m_current_idx = -1; + m_current_idx = 0; char *buffer; asprintf( &buffer, "Playlist-%ld", random() ); @@ -32,7 +32,7 @@ mgPlaylist::mgPlaylist() mgPlaylist::mgPlaylist(std::string listname) { - m_current_idx = -1; + m_current_idx = 0; m_listname = listname; } @@ -136,7 +136,7 @@ void mgPlaylist::insert( mgContentItem* item, unsigned int position ) } } -bool mgPlaylist::remove( int pos ) +bool mgPlaylist::remove( unsigned pos ) { bool result = false; @@ -171,7 +171,7 @@ void mgPlaylist::clear() m_current_idx = 0; } -void mgPlaylist::move( int from, int to ) +void mgPlaylist::move( unsigned from, unsigned to ) { std::vector::iterator from_iter = m_list.begin() + from; std::vector::iterator to_iter = m_list.begin() + to; @@ -200,7 +200,7 @@ void mgPlaylist::setListname(std::string name) } // returns current index in the playlist -int mgPlaylist::getIndex() const +unsigned mgPlaylist::getIndex() const { return m_current_idx; } @@ -223,7 +223,7 @@ mgContentItem* mgPlaylist::getCurrent() { mgContentItem *result; - if( 0 <= m_current_idx && m_current_idx < (int) m_list.size() ) + if( 0 <= m_current_idx && m_current_idx < m_list.size() ) { result = *( m_list.begin() + m_current_idx ); } @@ -260,7 +260,7 @@ bool mgPlaylist::skipFwd() } else { - if( m_current_idx + 1 < (int) m_list.size() ) // unless loop mode + if( m_current_idx + 1 < m_list.size() ) // unless loop mode { m_current_idx ++; } @@ -311,7 +311,8 @@ mgContentItem* mgPlaylist::sneakNext() { mgContentItem* result; - if( m_current_idx + 1 <= (int) m_list.size() ) // unless loop mode + // TODO: a bug? + if( m_current_idx + 1 <= m_list.size() ) // unless loop mode { result = *(m_list.begin() + m_current_idx + 1); } diff --git a/mg_playlist.h b/mg_playlist.h index 0c32297..bdffd0c 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -114,13 +114,13 @@ public: * \param from - the item of the index to be moved * \param to - the target index of the item */ - void move( int from, int to ); + void move( unsigned from, unsigned to ); /*! \brief remove a track from the playlist * * \param pos - the index of the track to be removed */ - bool remove( int pos ); + bool remove( unsigned pos ); //@} @@ -137,7 +137,7 @@ public: //@{ //! \brief returns current index in the playlist - int getIndex() const; + unsigned getIndex() const; //! \brief make playlist persistent virtual bool storePlaylist() = 0; @@ -186,7 +186,7 @@ private: //! \brief current index in the playlist // TODO: should be a property of the player? - int m_current_idx; + unsigned m_current_idx; //! \brief the current loop mode LoopMode m_loop_mode; diff --git a/vdr_player.c b/vdr_player.c index 4f16d66..bd05767 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -917,7 +917,7 @@ char *cMP3Player::LoadImage(const char *fullname) { size_t i, j = strlen (MP3Sources.GetSource()->BaseDir()) + 1; char imageFile[1024]; -+ static char mpgFile[1024]; + static char mpgFile[1024]; char *p, *q = NULL; char *imageSuffixes[] = { "png", "gif", "jpg" }; -- cgit v1.2.3 From 6d2f64a9aa538e9ec85fb9de062cb77bf12aac39 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 24 Oct 2004 18:09:34 +0000 Subject: Error when importing genres of new tracks removed. git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@233 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mugglei.c b/mugglei.c index bba1d42..fe30059 100755 --- a/mugglei.c +++ b/mugglei.c @@ -266,9 +266,10 @@ void update_db( long uid, std::string filename ) } else { // the entry does not exist, create it - mgSqlWriteQuery( db,"INSERT INTO tracks (artist,title,year," - "sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1, genre1)" - " VALUES (\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\", \"%s\")", + mgSqlWriteQuery( db, + "INSERT INTO tracks " + "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2) VALUES" + "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\")", artist.toCString(), title.toCString(), year, cddbid.toCString(), trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString() ); -- cgit v1.2.3 From 36f114c40af4d4c31941886084e597105a81eff2 Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 24 Oct 2004 22:57:02 +0000 Subject: Added checking of stale database entries git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@234 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 137 +++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 37 deletions(-) diff --git a/mugglei.c b/mugglei.c index fe30059..26da60e 100755 --- a/mugglei.c +++ b/mugglei.c @@ -4,7 +4,8 @@ * * \author Lars von Wedel */ -//#define VERBOSE + +#define VERBOSE #include #include @@ -22,7 +23,7 @@ MYSQL *db; std::string host, user, pass, dbname, sck; -bool import_assorted; +bool import_assorted, delete_mode; int init_database() { @@ -198,7 +199,10 @@ void update_db( long uid, std::string filename ) cddbid = escape_string( db, cddbid ); free( buf ); - mgSqlWriteQuery( db, "INSERT INTO album (artist,title,cddbid) VALUES (\"%s\", \"Unassigned\", \"%s\")", artist.toCString(), cddbid.toCString() ); + mgSqlWriteQuery( db, + "INSERT INTO album (artist, title, cddbid) " + "VALUES (\"%s\", \"Unassigned\", \"%s\")", + artist.toCString(), cddbid.toCString() ); } else { // use first album found as source id for the track @@ -287,44 +291,86 @@ void update_db( long uid, std::string filename ) } } -void update_tags( long uid, std::string filename ) +void update_tags( long uid ) { - -} + MYSQL_RES *result; + MYSQL_ROW row; -void evaluate_file( std::string filename ) -{ - if( 0 == init_database() ) - { - // is filename stored in database? - long uid = find_file_in_database( db, filename ); - if( uid >= 0 ) - { - // currently only update database, do not consider writing changes from the db back - /* - // determine modification times in database and on filesystem - time_t db_time = get_db_modification_time( uid ); - time_t fs_time = get_fs_modification_time( filename ); + if( uid >= 0 ) + { + result = mgSqlReadQuery( db, "SELECT artist,title,year,tracknb,mp3file,genre1,id FROM tracks where id=%d", uid ); + } + else + { + result = mgSqlReadQuery( db, "SELECT artist,title,year,tracknb,mp3file,genre1,id FROM tracks" ); + } - if( db_time > fs_time ) - { - // db is newer: update id3 tags from db - update_tags( uid, filename ); - } - else + // loop all results + char* cwd = getcwd( NULL, 0 ); + std::string wdir = std::string( cwd ); + free( cwd ); + + struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); + while(( row = mysql_fetch_row(result) ) != NULL ) + { + + std::string file = wdir + "/" + std::string( row[4] ); + + if( !stat( file.c_str(), buf ) ) + { + // set tags? + /* + std::string artist = row[0]; + std::string title = row[1]; + int year = atoi( row[2] ); + int track = atoi( row[3] ); + std::string genre = row[5]; + */ + } + else + { + if( delete_mode ) { - // file is newer: update db from id3 tags - update_db( uid, filename ); +#ifdef VERBOSE + std::cout << "Deleting entry " << row[6] << " from database because the file no longer exists." << std::endl; +#endif + mgSqlWriteQuery( db, "DELETE FROM tracks where id=%d", row[6] ); } - */ + } + } + free( buf ); +} - update_db( uid, filename ); - } +void evaluate_file( std::string filename ) +{ + // is filename stored in database? + long uid = find_file_in_database( db, filename ); + if( uid >= 0 ) + { + // currently only update database, do not consider writing changes from the db back + /* + // determine modification times in database and on filesystem + time_t db_time = get_db_modification_time( uid ); + time_t fs_time = get_fs_modification_time( filename ); + + if( db_time > fs_time ) + { + // db is newer: update id3 tags from db + update_tags( uid, filename ); + } else - { - // not in db yet: import file - update_db( -1, filename ); - } + { + // file is newer: update db from id3 tags + update_db( uid, filename ); + } + */ + + update_db( uid, filename ); + } + else + { + // not in db yet: import file + update_db( -1, filename ); } } @@ -344,8 +390,9 @@ int main( int argc, char *argv[] ) std::cout << " -n - specify database name (default is 'GiantDisc')" << std::endl; std::cout << " -u - specify user of mySql database (default is empty)" << std::endl; std::cout << " -p - specify password of user (default is empty password)" << std::endl; - std::cout << " -f - name of music file to import" << std::endl; + std::cout << " -f - name of music file to import or update" << std::endl; std::cout << " -a - import track as if it was on an assorted album" << std::endl; + std::cout << " -z - scan all database entries and delete entries for files not found" << std::endl; exit( 1 ); } @@ -357,11 +404,13 @@ int main( int argc, char *argv[] ) pass = ""; sck = ""; import_assorted = false; + delete_mode = false; + filename = ""; // parse command line options while( 1 ) { - int c = getopt(argc, argv, "h:u:p:n:af:s:"); + int c = getopt(argc, argv, "h:u:p:n:af:s:z"); if (c == -1) break; @@ -400,6 +449,10 @@ int main( int argc, char *argv[] ) { sck = optarg; } break; + case 'z': + { + delete_mode = true; + } break; } } @@ -409,7 +462,17 @@ int main( int argc, char *argv[] ) gettimeofday( &tv, &tz ); srandom( tv.tv_usec ); - evaluate_file( filename ); + if( 0 == init_database() ) + { + if( delete_mode ) + { + update_tags( -1 ); + } + if( filename.size() ) + { + evaluate_file( filename ); + } + } return 0; } -- cgit v1.2.3 From 6181db91cd9a7015480042929cfd6b8b8edc11db Mon Sep 17 00:00:00 2001 From: lvw Date: Sun, 24 Oct 2004 23:30:35 +0000 Subject: Mugglei now correctly removes stale database entries (option -z) git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@235 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mugglei.c b/mugglei.c index 26da60e..f090c01 100755 --- a/mugglei.c +++ b/mugglei.c @@ -334,7 +334,7 @@ void update_tags( long uid ) #ifdef VERBOSE std::cout << "Deleting entry " << row[6] << " from database because the file no longer exists." << std::endl; #endif - mgSqlWriteQuery( db, "DELETE FROM tracks where id=%d", row[6] ); + mgSqlWriteQuery( db, "DELETE FROM tracks where id=%s", row[6] ); } } } -- cgit v1.2.3 From d1f40a700e44fc856bbb83f533bc225b642b26cd Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 25 Oct 2004 00:25:15 +0000 Subject: Added SuSe packaging to Makefile (thanks to Patrice Staudt) git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@236 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 80334c6..12145c5 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,8 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -# DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE +# DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -D_GNU_SOURCE MIFLAGS += -I/usr/include/taglib -lmysqlclient @@ -81,6 +81,11 @@ libvdr-$(PLUGIN).so: $(OBJS) mugglei: mg_tools.o mugglei.o $(CXX) $(CXXFLAGS) $^ $(MILIBS) -o $@ +install: + @cp ../../lib/libvdr-muggle*.so.* /usr/lib/vdr/ + @cp mugglei /usr/local/bin/ + @install -m 755 mugglei /usr/local/bin/ + dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) -- cgit v1.2.3 From ebc27e732db857a58aaf8c1ba1e9dc2a7a08ac54 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 25 Oct 2004 00:43:35 +0000 Subject: Result of skipping back/forth incorrect when not in a loop mode git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@237 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mg_playlist.c b/mg_playlist.c index 0b38925..b4bc487 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -263,15 +263,20 @@ bool mgPlaylist::skipFwd() if( m_current_idx + 1 < m_list.size() ) // unless loop mode { m_current_idx ++; + result = true; } else { if( m_loop_mode == mgPlaylist::LM_FULL ) { m_current_idx = 0; + result = true; + } + else + { + result = false; } } - result = true; } // if we are already at the end -- just do nothing unless in loop mode @@ -292,15 +297,20 @@ bool mgPlaylist::skipBack() if( m_current_idx > 0 ) { m_current_idx --; + result = true; } else { if( m_loop_mode == mgPlaylist::LM_FULL ) { m_current_idx = m_list.size() -1; + result = true; + } + else + { + result = false; } } - result = true; } // if we are at the beginning -- just do nothing unless in loop mode return result; -- cgit v1.2.3 From 38dcaa6a0570b79c79aee397cef34fba7864c018 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 25 Oct 2004 00:43:47 +0000 Subject: Updated TODOs git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@238 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index e8a2987..3b185b1 100644 --- a/TODO +++ b/TODO @@ -17,8 +17,6 @@ \subsection urgentosd OSD-related Issues - - Possible to resume play instead of restarting list from the beginning? - - Save on exit - Rename playlist \subsection urgentcode Code polishing @@ -44,6 +42,7 @@ - Could save IP/host name and associate last playlist/index loaded \subsection urgentcontent Content handling + - Save on exit - Think, whether type (mp3, ogg, flac) should be stored in database - could be used in searching/structuring as well - Handle loop mode in mgPlaylist @@ -67,6 +66,7 @@ - add the file to the end of the list \subsection urgentplayer Player extensions + - Possible to resume play instead of restarting list from the beginning? - Display covers - Import filename - Show image during replay @@ -110,6 +110,13 @@ \subsection midimport Import stuff + - Handle updates in both directions + - Check modification date in DB/fstat + - if file is newer: update tags into db + - if DB is newer: update db into tags + - Second pass: check for all tracks, whether files do exist + - delete DB entry if not + - Album - Cover images (based on filename or tag) - Genre @@ -125,11 +132,6 @@ - Import playlist from m3u - Run import/update from within OSD? - - Handle updates in both directions - - Check modification date in DB/fstat - - if file is newer: update tags into db - - if DB is newer: update db into tags - \subsection midcode Code issues - really abstract from specific queries etc. -- cgit v1.2.3 From 049852735e5d91b183eb28229e32de5f1a563fd1 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 25 Oct 2004 08:32:45 +0000 Subject: Final changes for 0.0.8 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@240 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 2 +- muggle.doxygen | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/muggle.c b/muggle.c index 4cc7b0d..8a00883 100644 --- a/muggle.c +++ b/muggle.c @@ -22,7 +22,7 @@ #include #include -static const char *VERSION = "0.0.7"; +static const char *VERSION = "0.0.8"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/muggle.doxygen b/muggle.doxygen index d393679..fb0e1d0 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.0.7 +PROJECT_NUMBER = 0.0.8 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. -- cgit v1.2.3 From a754aba317b7d855d87e881b681569a46a14bb83 Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 25 Oct 2004 15:22:18 +0000 Subject: Added feature to resume git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@243 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 12 ++++++++++++ muggle.h | 4 ++++ vdr_menu.c | 11 +++++++---- vdr_menu.h | 2 +- vdr_player.c | 49 ++++++++++--------------------------------------- vdr_player.h | 2 +- 6 files changed, 35 insertions(+), 45 deletions(-) diff --git a/muggle.c b/muggle.c index 8a00883..00d747e 100644 --- a/muggle.c +++ b/muggle.c @@ -26,6 +26,7 @@ static const char *VERSION = "0.0.8"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; +static unsigned s_resume_idx = 0; const char* mgMuggle::Version(void) { @@ -215,4 +216,15 @@ bool mgMuggle::SetupParse(const char *Name, const char *Value) return true; } + +static void mgMuggle::setResumeIndex( unsigned index ) +{ + s_resume_idx = index; +} + +static unsigned mgMuggle::getResumeIndex( ) +{ + return s_resume_idx; +} + VDRPLUGINCREATOR(mgMuggle); // Don't touch this! diff --git a/muggle.h b/muggle.h index 3c61646..4377359 100644 --- a/muggle.h +++ b/muggle.h @@ -71,6 +71,10 @@ public: virtual bool SetupParse(const char *Name, const char *Value); + static void setResumeIndex( unsigned index ); + + static unsigned getResumeIndex( ); + private: mgMedia *m_media; diff --git a/vdr_menu.c b/vdr_menu.c index bea0db0..1f54e21 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -292,14 +292,16 @@ eOSState mgMainMenu::ProcessKey(eKeys key) case kOk: { // start replay at selected index - int idx = Current(); + unsigned idx = Current(); Play( m_current_playlist, idx ); state = osContinue; } break; case kRed: { // TODO: what happens if the user presses play and the player is already active? - Play( m_current_playlist ); + // TODO: resume? + unsigned resume = mgMuggle::getResumeIndex(); + Play( m_current_playlist, resume ); state = osEnd; } break; case kGreen: @@ -571,7 +573,8 @@ eOSState mgMainMenu::TreeSubmenuAction( int n ) m_current_playlist->appendList( tracks ); // play - Play( m_current_playlist ); + mgMuggle::setResumeIndex( 0 ); + Play( m_current_playlist, 0 ); state = osEnd; } @@ -954,7 +957,7 @@ void mgMainMenu::DisplayFilterSelector() // show available filters, load on OK? } -void mgMainMenu::Play( mgPlaylist *plist, int first ) +void mgMainMenu::Play( mgPlaylist *plist, unsigned first ) { MGLOG( "mgMainMenu::Play" ); cControl *control = cControl::Control(); diff --git a/vdr_menu.h b/vdr_menu.h index 41bba01..4c618c3 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -99,7 +99,7 @@ class mgMainMenu : public cOsdMenu private: //! \brief launch the actual player - void Play( mgPlaylist *plist, int first = -1 ); + void Play( mgPlaylist *plist, unsigned first ); // content stuff mgMedia *m_media; diff --git a/vdr_player.c b/vdr_player.c index bd05767..9365090 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -203,7 +203,7 @@ public: virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false); // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW - void NewPlaylist( mgPlaylist *plist, int first ); + void NewPlaylist( mgPlaylist *plist, unsigned first ); mgContentItem *getCurrent () { return m_current; } mgPlaylist *getPlaylist () { return m_playlist; } }; @@ -699,51 +699,20 @@ bool mgPCMPlayer::NextFile( ) mgContentItem *newcurr; bool res = false; - bool m_partymode = false; - bool m_shufflemode = false; - if( m_partymode ) + if( m_playlist->skipFwd() ) { - /* - - Party mode (see iTunes) - - initialization - - find 15 titles according to the scheme below - - playing - - before entering next title perform track selection - - track selection - - generate a random uid - - if file exists: - - determine maximum playcount of all tracks - - generate a random number n - - if n < playcount / max. playcount - - add the file to the end of the list - */ - } - else if( m_shufflemode ) - { - /* - - Handle shuffle mode in mgPlaylist - - for next file: - - generate a random number 0..n-1 - - move corresponding playlist item to front - - continue - */ + newcurr = m_playlist->getCurrent(); } else { - if( m_playlist->skipFwd() ) - { - newcurr = m_playlist->getCurrent(); - } - else - { - newcurr = &(mgContentItem::UNDEFINED); - } + newcurr = &(mgContentItem::UNDEFINED); } if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) { m_current = newcurr; + mgMuggle::setResumeIndex( m_playlist->getIndex() ); res = true; } @@ -760,7 +729,8 @@ bool mgPCMPlayer::PrevFile(void) if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) { - m_current = newcurr; + m_current = newcurr; + mgMuggle::setResumeIndex( m_playlist->getIndex() ); res = true; } } @@ -1105,7 +1075,7 @@ void mgPCMPlayer::send_pes_packet(unsigned char *data, int len, int timestamp) // --- mgPlayerControl ------------------------------------------------------- -mgPlayerControl::mgPlayerControl( mgPlaylist *plist, int start ) +mgPlayerControl::mgPlayerControl( mgPlaylist *plist, unsigned start ) : cControl( player = new mgPCMPlayer( plist, start ) ) { MGLOG( "mgPlayerControl::mgPlayerControl" ); @@ -1217,7 +1187,7 @@ void mgPlayerControl::ToggleLoop(void) } } -void mgPlayerControl::NewPlaylist(mgPlaylist *plist, int start) +void mgPlayerControl::NewPlaylist(mgPlaylist *plist, unsigned start) { if( player ) { @@ -1586,6 +1556,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) { InternalHide(); Stop(); + mgMuggle::setResumeIndex( 0 ); return osEnd; } break; diff --git a/vdr_player.h b/vdr_player.h index 2c73771..af39429 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -121,7 +121,7 @@ public: * \param plist - the new playlist to be played * \param first - the index where to start the playlist */ - void NewPlaylist( mgPlaylist *plist, int start ); + void NewPlaylist( mgPlaylist *plist, unsigned start ); //! \brief a progress display void ShowProgress(); -- cgit v1.2.3 From d616ab6f59cfa14a777d73346086f9052f55f0eb Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 25 Oct 2004 15:22:33 +0000 Subject: Added acknowledgements git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@244 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README b/README index 2934716..1d0ab9d 100644 --- a/README +++ b/README @@ -25,6 +25,15 @@ Please provide feedback to the authors whenever you think, these instructions are not appropriate, wrong, or useless in any other sense. +\section ack Acknowledgements + +Thanks to all who have supported the development of this plugin. Special thanks go to +- Muempf for the mp3 plugin. All code related to audio replay is largely taken over from this plugin. +- LordJaxom for constant support in the chat of VDR portal regarding OSD programming in VDR +- eloy (member of vdrportal.de) for alpha testing +- All beta testers at vdrportal.de and on the VDR mailing list +- decembersoul (member of vdrportal.de) for finding out how to run muggle on LinVDR + \section desc DESCRIPTION The muggle plugin provides a database link for VDR so that selection of media becomes more flexible. -- cgit v1.2.3 From 5cbca2e0dcfb32cd6a326205f93d66c8716dbdda Mon Sep 17 00:00:00 2001 From: lvw Date: Mon, 25 Oct 2004 15:34:17 +0000 Subject: Track and progress view now persistent across VDR sessions git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@245 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 4 ++-- vdr_player.h | 3 +++ vdr_setup.c | 6 ++++++ vdr_setup.h | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 9365090..571743c 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -1086,8 +1086,8 @@ mgPlayerControl::mgPlayerControl( mgPlaylist *plist, unsigned start ) #endif m_visible = false; m_has_osd = false; - m_track_view = true; - m_progress_view = true; + m_track_view = the_setup.trackview; + m_progress_view = the_setup.progressview; m_szLastShowStatusMsg = NULL; diff --git a/vdr_player.h b/vdr_player.h index af39429..70e8b3c 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -49,7 +49,10 @@ private: //! \brief indicates, whether an osd is currently displayed bool m_has_osd; + //! \brief indicates, whether the osd displays a track view (true) or a playlist view (false) bool m_track_view; + + //! \brief indicates, whether the osd presents progress (true) or detail information (false) bool m_progress_view; #if VDRVERSNUM >= 10307 diff --git a/vdr_setup.c b/vdr_setup.c index afa7b85..9b11185 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -39,6 +39,8 @@ mgMenuSetup::mgMenuSetup() Add(new cMenuEditBoolItem(tr("Setup.Muggle$Background mode"), &m_data.BackgrMode, tr("Black"), tr("Live"))); Add(new cMenuEditIntItem( tr("Setup.Muggle$Normalizer level"), &m_data.TargetLevel, 0, MAX_TARGET_LEVEL)); Add(new cMenuEditIntItem( tr("Setup.Muggle$Limiter level"), &m_data.LimiterLevel, MIN_LIMITER_LEVEL, 100)); + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with progress display"), &m_data.progressview )); + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with track display"), &m_data.trackview )); } void mgMenuSetup::Store(void) @@ -53,6 +55,8 @@ void mgMenuSetup::Store(void) SetupStore("TargetLevel", the_setup.TargetLevel ); SetupStore("LimiterLevel", the_setup.LimiterLevel ); SetupStore("Only48kHz", the_setup.Only48kHz ); + SetupStore("TrackView", the_setup.trackview ); + SetupStore("ProgressView", the_setup.progressview ); } // --- mgSetup --------------------------------------------------------------- @@ -68,4 +72,6 @@ mgSetup::mgSetup() LimiterLevel = DEFAULT_LIMITER_LEVEL; Only48kHz = 0; ToplevelDir = "/mnt/music/"; + trackview = true; + progressview = true; } diff --git a/vdr_setup.h b/vdr_setup.h index 5750146..b88a3ac 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -52,6 +52,9 @@ public: bool GdCompatibility; char *ToplevelDir; + bool trackview; + bool progressview; + char PathPrefix[MAX_STRING_LEN]; public: -- cgit v1.2.3 From c0be06a6d528738699a41bcf5fd7ee9fb68a78fd Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 26 Oct 2004 00:56:50 +0000 Subject: Tested resume feature git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@246 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 1 + mg_content_interface.c | 2 +- mg_content_interface.h | 2 +- muggle.c | 4 ++-- vdr_menu.c | 1 + vdr_player.c | 7 ++++--- vdr_player.h | 2 +- vdr_setup.h | 4 ++-- 8 files changed, 13 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index 3b185b1..86eb8cc 100644 --- a/TODO +++ b/TODO @@ -40,6 +40,7 @@ - escape query strings - mgPlayer used what for? - Could save IP/host name and associate last playlist/index loaded + - Move differences 1.3.7+- to link level \subsection urgentcontent Content handling - Save on exit diff --git a/mg_content_interface.c b/mg_content_interface.c index d017997..79dbd1f 100755 --- a/mg_content_interface.c +++ b/mg_content_interface.c @@ -117,7 +117,7 @@ mgContentItem* mgTracklist::getItem(unsigned int position) return *( m_list.begin() + position); } -bool mgTracklist::remove(int position) +bool mgTracklist::remove(unsigned position) { bool result = false; diff --git a/mg_content_interface.h b/mg_content_interface.h index c127d03..38edffa 100755 --- a/mg_content_interface.h +++ b/mg_content_interface.h @@ -325,7 +325,7 @@ class mgTracklist /*! * \brief remove all occurences of item */ - virtual bool remove(int position); // remove item at position + virtual bool remove(unsigned position); // remove item at position }; /*! diff --git a/muggle.c b/muggle.c index 00d747e..76542c5 100644 --- a/muggle.c +++ b/muggle.c @@ -217,12 +217,12 @@ bool mgMuggle::SetupParse(const char *Name, const char *Value) } -static void mgMuggle::setResumeIndex( unsigned index ) +void mgMuggle::setResumeIndex( unsigned index ) { s_resume_idx = index; } -static unsigned mgMuggle::getResumeIndex( ) +unsigned mgMuggle::getResumeIndex( ) { return s_resume_idx; } diff --git a/vdr_menu.c b/vdr_menu.c index 1f54e21..c0a8ef8 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -25,6 +25,7 @@ #include #endif +#include "muggle.h" #include "vdr_menu.h" #include "vdr_player.h" #include "i18n.h" diff --git a/vdr_player.c b/vdr_player.c index 571743c..322801e 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -37,6 +37,7 @@ #include #include +#include "muggle.h" #include "vdr_player.h" #include "vdr_decoder.h" #include "vdr_config.h" @@ -185,7 +186,7 @@ protected: virtual void Action(void); public: - mgPCMPlayer(mgPlaylist *plist, int first); + mgPCMPlayer(mgPlaylist *plist, unsigned first); virtual ~mgPCMPlayer(); bool Active() { return m_active; } @@ -208,7 +209,7 @@ public: mgPlaylist *getPlaylist () { return m_playlist; } }; -mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist, int first) +mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist, unsigned first) : cPlayer( the_setup.BackgrMode? pmAudioOnly: pmAudioOnlyBlack ), m_first( first ) { @@ -281,7 +282,7 @@ void mgPCMPlayer::Activate(bool on) } } -void mgPCMPlayer::NewPlaylist( mgPlaylist *plist, int start ) +void mgPCMPlayer::NewPlaylist( mgPlaylist *plist, unsigned start ) { MGLOG( "mgPCMPlayer::NewPlaylist" ); diff --git a/vdr_player.h b/vdr_player.h index 70e8b3c..d71149e 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -74,7 +74,7 @@ public: * \param plist - the playlist to be played * \param first - the index where to start the playlist */ - mgPlayerControl(mgPlaylist *plist, int first); + mgPlayerControl(mgPlaylist *plist, unsigned first); /*! \brief destructor */ diff --git a/vdr_setup.h b/vdr_setup.h index b88a3ac..1c59235 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -52,8 +52,8 @@ public: bool GdCompatibility; char *ToplevelDir; - bool trackview; - bool progressview; + int trackview; + int progressview; char PathPrefix[MAX_STRING_LEN]; -- cgit v1.2.3 From 3da32e2a7187e9a842f4b6d5299d621db1855f68 Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 26 Oct 2004 14:39:20 +0000 Subject: Added a function to store a plalist under a different name git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@247 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 161 ++++++++++++++++++++++++++++++++----------------- gd_content_interface.h | 5 ++ mg_playlist.h | 3 + 3 files changed, 114 insertions(+), 55 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 57a3f67..987bd70 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -26,7 +26,6 @@ #include "i18n.h" - #define GD_PLAYLIST_TYPE 0 //< listtype for giant disc db // some dummies to keep the compiler happy @@ -706,63 +705,115 @@ int GdPlaylist::insertDataFromSQL() bool GdPlaylist::storePlaylist() { - std::vector::iterator iter; - int num; - MYSQL_RES *result; - MYSQL_ROW row; - int nrows; + std::vector::iterator iter; + int num; + MYSQL_RES *result; + MYSQL_ROW row; + int nrows; + + if( m_listname == " " ) + { + mgWarning("Can not store Tracklist without name"); + return false; + } + + if( m_sqlId >= 0 ) + { + // playlist alreay exists in SQL database + // remove old items first + // cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; + + // remove old playlist items from db + mgSqlWriteQuery(&m_db, + "DELETE FROM playlistitem WHERE playlist = %d", + m_sqlId); + } + else + { + // create new database entry + mgSqlWriteQuery(&m_db, "INSERT into playlist " + "SET title=\"%s\", author=\"%s\"", + m_listname.c_str(), + "VDR", // default author + ""); // creates current time as timestamp + m_author = "VDR"; + + // now read the new list to get the id + result = mgSqlReadQuery( &m_db, + "SELECT id,author FROM playlist where title=\"%s\"", + m_listname.c_str()); + nrows = mysql_num_rows(result); + row = mysql_fetch_row(result); + + if( sscanf( row [0], "%d", & m_sqlId ) !=1 ) + { + mgError("Invalid id '%s' in database", row [5]); + } + } - if( m_listname == " " ) - { - mgWarning("Can not store Tracklist without name"); - return false; - } + // add new playlist items to db + for( iter=m_list.begin(), num=0; + iter != m_list.end(); + iter++, num++) + { + mgSqlWriteQuery(&m_db, + "INSERT into playlistitem " + "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", + num, (*iter)->getId(), m_sqlId); + } + return true; +} - if(m_sqlId >= 0) - { - // playlist alreay exists in SQL database - // remove old items first - // cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; +bool GdPlaylist::storeAs( std::string name ) +{ + int id; - // remove old playlist items from db - mgSqlWriteQuery(&m_db, - "DELETE FROM playlistitem WHERE playlist = %d", - m_sqlId); - } - else - { - // create new database entry - mgSqlWriteQuery(&m_db, "INSERT into playlist " - "SET title=\"%s\", author=\"%s\"", - m_listname.c_str(), - "VDR", // default author - ""); // creates current time as timestamp - m_author = "VDR"; - - // now read thenew list to get the id - result=mgSqlReadQuery(&m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - m_listname.c_str()); - nrows = mysql_num_rows(result); - row = mysql_fetch_row(result); - - if( sscanf( row [0], "%d", & m_sqlId ) !=1 ) - { - mgError("Invalid id '%s' in database", row [5]); - } - } - // add new playlist items to db - - for( iter=m_list.begin(), num=0; - iter != m_list.end(); - iter++, num++) - { - mgSqlWriteQuery(&m_db, - "INSERT into playlistitem " - "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", - num, (*iter)->getId(), m_sqlId); - } - return true; + result = mgSqlReadQuery( &m_db, + "SELECT id FROM playlist WHERE title=\"current\"" ); + + if( mysql_num_rows(result) ) + { + MYSQL row = mysql_fetch_row( result ); + } + else + { + // otherwise create a new database entry + mgSqlWriteQuery( &m_db, "INSERT into playlist SET " + "title=\"%s\", author=\"VDR\"", + name.c_str() ) + + // now read the new list to get the id + result = mgSqlReadQuery( &m_db, + "SELECT id,author FROM playlist where title=\"current\""); + + nrows = mysql_num_rows(result); + row = mysql_fetch_row(result); + } + + if( sscanf( row [0], "%d", &id ) !=1 ) + { + mgError("Invalid id '%s' in database", row [5]); + } + else + { + // now we know that the playlist 'name' has identifier id + + // remove old playlist items from db + mgSqlWriteQuery( &m_db, + "DELETE FROM playlistitem WHERE playlist = %d", + id ); + + // add new playlist items to db + for( iter=m_list.begin(), num=0; + iter != m_list.end(); + iter++, num++) + { + mgSqlWriteQuery(&m_db, + "INSERT into playlistitem " + "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", + num, (*iter)->getId(), m_sqlId); + } + } } /*! diff --git a/gd_content_interface.h b/gd_content_interface.h index 921f8d7..8823799 100644 --- a/gd_content_interface.h +++ b/gd_content_interface.h @@ -435,6 +435,11 @@ class GdPlaylist : public mgPlaylist */ bool storePlaylist(); + /*! + * \brief store playlist under a different name + */ + bool storeAs( std::string name ); + private: /*! diff --git a/mg_playlist.h b/mg_playlist.h index bdffd0c..060ebfa 100644 --- a/mg_playlist.h +++ b/mg_playlist.h @@ -142,6 +142,9 @@ public: //! \brief make playlist persistent virtual bool storePlaylist() = 0; + //! \brief make playlist persistent under a different name + virtual bool storeAs( std::string name ) = 0; + //! \brief obtain length of content already played unsigned long getCompletedLength(); -- cgit v1.2.3 From 569ecd0a822be1f599d93e252836e3108360cfba Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 26 Oct 2004 14:59:31 +0000 Subject: Updated to new status git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@248 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/TODO b/TODO index 86eb8cc..e250b93 100644 --- a/TODO +++ b/TODO @@ -10,17 +10,13 @@ \section urgent Urgent/Short-term issues \subsection bugs Bugs and testing needed - - - Test execution of playlist commands - Crashes in filter selections? - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) \subsection urgentosd OSD-related Issues - - Rename playlist \subsection urgentcode Code polishing - - Clean up coding style and documentation in general - Logging - extend mgLog with static logging methods @@ -44,6 +40,10 @@ \subsection urgentcontent Content handling - Save on exit + - implement storePlaylistAs( string name ) in gdPlaylist + - find id of playlist called name or create it + - delete all entries + - store current entries - Think, whether type (mp3, ogg, flac) should be stored in database - could be used in searching/structuring as well - Handle loop mode in mgPlaylist @@ -67,7 +67,6 @@ - add the file to the end of the list \subsection urgentplayer Player extensions - - Possible to resume play instead of restarting list from the beginning? - Display covers - Import filename - Show image during replay @@ -245,5 +244,7 @@ - Track view: all metadata - Playlist view: all tracks (past three, upcoming ones) - BUG: Red key does not work in Browser submenu + - Test execution of playlist commands + - Resume play instead of restarting list from the beginning when terminating play with blue instead of back */ \ No newline at end of file -- cgit v1.2.3 From d14a5c6167c3e6094a85ee40dd50748c8a442fdf Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 26 Oct 2004 15:00:27 +0000 Subject: Store playlist for next start git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@249 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/muggle.c b/muggle.c index 76542c5..d1bfed3 100644 --- a/muggle.c +++ b/muggle.c @@ -59,6 +59,11 @@ mgMuggle::mgMuggle(void) mgMuggle::~mgMuggle() { // Clean up after yourself! + // save current playlist as "current" and it will be retrieved at the next startup + if( m_playlist ) + { + m_playlist->storeAs( "current" ); + } } const char *mgMuggle::CommandLineHelp(void) -- cgit v1.2.3 From 08135daabe8390c0356f92a9b789b3ce834c088e Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 26 Oct 2004 15:01:13 +0000 Subject: Allow playlist editing git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@250 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 66 +++++++++++++++++++++++++++++++++++++------------------------- vdr_menu.h | 3 +++ 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index c0a8ef8..3ad6d97 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -39,7 +39,6 @@ #include "gd_content_interface.h" - // ----------------------- mgMenuTreeItem ------------------ mgMenuTreeItem::mgMenuTreeItem( mgSelectionTreeNode *node ) @@ -73,6 +72,10 @@ mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, SetButtons(); DisplayTree( m_root ); + + strncpy( m_listname, playlist->getListname(), 31 ); + m_listname[31] = "\0"; + m_editing_listname } mgSelectionTreeNode *mgMainMenu::CurrentNode() @@ -386,7 +389,18 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } break; case kOk: { - state = PlaylistSubmenuAction( Current() ); + if( Current() != 0 ) + { // not editing playlist name + state = PlaylistSubmenuAction( Current() ); + } + else + { // editing playlist name + s_editing_listname = !s_editing_listname; + if( s_editing_listname = false ) + { // we just changed from true to false so editing was terminated + m_playlist->setListname( s_listname ); + } + } } break; default: { @@ -660,15 +674,25 @@ void mgMainMenu::SavePlaylist() m_current_playlist->storePlaylist(); } -void mgMainMenu::RenamePlaylist() +void mgMainMenu::RenamePlaylist( std::string name ) { // dummy function. USes current date as name - time_t currentTime = time(NULL); - m_current_playlist->setListname(ctime(¤tTime)); + m_current_playlist->setListname( name ); + + // confirmation +#if VDRVERSNUM >= 10307 + Skins.Message(mtInfo, "Playlist renamed" ); + Skins.Flush(); +#else + Interface->Status( "Playlist renamed" ); + Interface->Flush(); +#endif } void mgMainMenu::DisplayPlaylistSubmenu() { + static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" }; + m_state = PLAYLIST_SUBMENU; Clear(); @@ -676,9 +700,9 @@ void mgMainMenu::DisplayPlaylistSubmenu() SetTitle( "Muggle - Playlist View Commands" ); // Add items + Add( new cMenuEditStrItem( tr("Playlist name"), s_listname, 32, allowed ) ); Add( new cOsdItem( tr("Load playlist" ) ) ); Add( new cOsdItem( tr("Save playlist" ) ) ); - Add( new cOsdItem( tr("Rename playlist" ) ) ); Add( new cOsdItem( tr("Clear playlist" ) ) ); Add( new cOsdItem( tr("Remove entry from list" ) ) ); Add( new cOsdItem( tr("Export playlist" ) ) ); @@ -730,8 +754,8 @@ eOSState mgMainMenu::ExecutePlaylistCommand( int current ) { asprintf( &buffer, "%s...", command->Title() ); #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,buffer); - Skins.Flush(); + Skins.Message(mtInfo,buffer); + Skins.Flush(); #else Interface->Status( buffer ); Interface->Flush(); @@ -766,6 +790,11 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) switch( n ) { case 0: + { // rename playlist - should never get here! + state = osContinue; + } break; + + case 1: { LoadPlaylist(); #if VDRVERSNUM < 10307 @@ -775,31 +804,16 @@ eOSState mgMainMenu::PlaylistSubmenuAction( int n ) #endif // jump to playlist view from here? } break; - case 1: + case 2: { SavePlaylist(); #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,"Playlist saved"); - Skins.Flush(); -#else - Interface->Status( "Playlist saved"); - Interface->Flush(); -#endif - } break; - case 2: - { // renamer playlist - RenamePlaylist(); - - // confirmation -#if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,"Playlist renamed (dummy)"); + Skins.Message(mtInfo,"Playlist saved"); Skins.Flush(); #else - Interface->Status( "Playlist renamed (dummy)" ); + Interface->Status( "Playlist saved"); Interface->Flush(); #endif - - state = osContinue; } break; case 3: { // clear playlist diff --git a/vdr_menu.h b/vdr_menu.h index 4c618c3..a5f0637 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -115,6 +115,9 @@ class mgMainMenu : public cOsdMenu cCommands *m_playlist_commands; int m_last_osd_index; + + char m_listname[32]; + bool m_editing_listname; }; #endif -- cgit v1.2.3 From fb3cae2da67d2e12d676997af9b59eb72ab5c6ac Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 26 Oct 2004 21:01:56 +0000 Subject: Removed compiler errors git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@251 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 12 ++++++++---- muggle.c | 1 + muggle.h | 1 - vdr_menu.c | 18 +++++++++++------- vdr_menu.h | 2 +- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index 987bd70..a7d5bac 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -767,26 +767,27 @@ bool GdPlaylist::storePlaylist() bool GdPlaylist::storeAs( std::string name ) { int id; - + MYSQL_ROW row; + MYSQL_RES *result; + result = mgSqlReadQuery( &m_db, "SELECT id FROM playlist WHERE title=\"current\"" ); if( mysql_num_rows(result) ) { - MYSQL row = mysql_fetch_row( result ); + row = mysql_fetch_row( result ); } else { // otherwise create a new database entry mgSqlWriteQuery( &m_db, "INSERT into playlist SET " "title=\"%s\", author=\"VDR\"", - name.c_str() ) + name.c_str() ); // now read the new list to get the id result = mgSqlReadQuery( &m_db, "SELECT id,author FROM playlist where title=\"current\""); - nrows = mysql_num_rows(result); row = mysql_fetch_row(result); } @@ -804,6 +805,8 @@ bool GdPlaylist::storeAs( std::string name ) id ); // add new playlist items to db + std::vector::iterator iter; + int num = 0; for( iter=m_list.begin(), num=0; iter != m_list.end(); iter++, num++) @@ -814,6 +817,7 @@ bool GdPlaylist::storeAs( std::string name ) num, (*iter)->getId(), m_sqlId); } } + return true; } /*! diff --git a/muggle.c b/muggle.c index d1bfed3..8346337 100644 --- a/muggle.c +++ b/muggle.c @@ -15,6 +15,7 @@ #include "vdr_menu.h" #include "vdr_setup.h" #include "mg_tools.h" +#include "mg_playlist.h" #include "mg_content_interface.h" #include "mg_media.h" diff --git a/muggle.h b/muggle.h index 4377359..6f787e1 100644 --- a/muggle.h +++ b/muggle.h @@ -11,7 +11,6 @@ * $Id$ */ - // Some notes about the general structure of the plugin /* \defgroup giantdisc GiantDisc integration layer diff --git a/vdr_menu.c b/vdr_menu.c index 3ad6d97..fbc7794 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -14,7 +14,10 @@ #include #include +// #include + #include + #include #include #include @@ -73,9 +76,9 @@ mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, DisplayTree( m_root ); - strncpy( m_listname, playlist->getListname(), 31 ); - m_listname[31] = "\0"; - m_editing_listname + strncpy( m_listname, playlist->getListname().c_str(), 31 ); + m_listname[31] = '\0'; + m_editing_listname = false; } mgSelectionTreeNode *mgMainMenu::CurrentNode() @@ -395,10 +398,11 @@ eOSState mgMainMenu::ProcessKey(eKeys key) } else { // editing playlist name - s_editing_listname = !s_editing_listname; - if( s_editing_listname = false ) + m_editing_listname = !m_editing_listname; + if( m_editing_listname = false ) { // we just changed from true to false so editing was terminated - m_playlist->setListname( s_listname ); + RenamePlaylist( std::string( m_listname ) ); + } } } break; @@ -700,7 +704,7 @@ void mgMainMenu::DisplayPlaylistSubmenu() SetTitle( "Muggle - Playlist View Commands" ); // Add items - Add( new cMenuEditStrItem( tr("Playlist name"), s_listname, 32, allowed ) ); + Add( new cMenuEditStrItem( tr("Playlist name"), m_listname, 31, allowed ) ); Add( new cOsdItem( tr("Load playlist" ) ) ); Add( new cOsdItem( tr("Save playlist" ) ) ); Add( new cOsdItem( tr("Clear playlist" ) ) ); diff --git a/vdr_menu.h b/vdr_menu.h index a5f0637..e9fd1d3 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -87,7 +87,7 @@ class mgMainMenu : public cOsdMenu void LoadPlaylist(); void SavePlaylist(); - void RenamePlaylist(); + void RenamePlaylist( std::string name ); void DisplayPlaylistSubmenu(); eOSState PlaylistSubmenuAction( int n ); void DisplayPlaylistCommands(); -- cgit v1.2.3 From 88d9de082450481ab2a4e93e40f29b918995e82a Mon Sep 17 00:00:00 2001 From: lvw Date: Tue, 26 Oct 2004 23:02:30 +0000 Subject: Store and restore last playlist git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@252 e10066b5-e1e2-0310-b819-94efdf66514b --- gd_content_interface.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gd_content_interface.c b/gd_content_interface.c index a7d5bac..890f2b6 100644 --- a/gd_content_interface.c +++ b/gd_content_interface.c @@ -769,13 +769,17 @@ bool GdPlaylist::storeAs( std::string name ) int id; MYSQL_ROW row; MYSQL_RES *result; + + mgDebug( 1, "GdPlaylist::storeAs" ); result = mgSqlReadQuery( &m_db, - "SELECT id FROM playlist WHERE title=\"current\"" ); + "SELECT id FROM playlist WHERE title=\"%s\"", + name.c_str() ); if( mysql_num_rows(result) ) { row = mysql_fetch_row( result ); + mgDebug( 1, "GdPlaylist::storeAs: found playlist" ); } else { @@ -786,9 +790,11 @@ bool GdPlaylist::storeAs( std::string name ) // now read the new list to get the id result = mgSqlReadQuery( &m_db, - "SELECT id,author FROM playlist where title=\"current\""); + "SELECT id,author FROM playlist where title=\"%s\"", + name.c_str() ); row = mysql_fetch_row(result); + mgDebug( 1, "GdPlaylist::storeAs: created playlist" ); } if( sscanf( row [0], "%d", &id ) !=1 ) @@ -811,10 +817,11 @@ bool GdPlaylist::storeAs( std::string name ) iter != m_list.end(); iter++, num++) { + mgDebug( 1, "GdPlaylist::storeAs: inserting track" ); mgSqlWriteQuery(&m_db, "INSERT into playlistitem " "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", - num, (*iter)->getId(), m_sqlId); + num, (*iter)->getId(), id ); } } return true; -- cgit v1.2.3 From 1ef194ae905bf0739c8853f1e26261bd1c73311a Mon Sep 17 00:00:00 2001 From: RaK Date: Wed, 27 Oct 2004 00:11:11 +0000 Subject: Detailed description of the concept behind the Muggle GUI git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@253 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/README b/README index 1d0ab9d..c4fef6a 100644 --- a/README +++ b/README @@ -25,15 +25,6 @@ Please provide feedback to the authors whenever you think, these instructions are not appropriate, wrong, or useless in any other sense. -\section ack Acknowledgements - -Thanks to all who have supported the development of this plugin. Special thanks go to -- Muempf for the mp3 plugin. All code related to audio replay is largely taken over from this plugin. -- LordJaxom for constant support in the chat of VDR portal regarding OSD programming in VDR -- eloy (member of vdrportal.de) for alpha testing -- All beta testers at vdrportal.de and on the VDR mailing list -- decembersoul (member of vdrportal.de) for finding out how to run muggle on LinVDR - \section desc DESCRIPTION The muggle plugin provides a database link for VDR so that selection of media becomes more flexible. @@ -205,7 +196,7 @@ above do not make use of passwords, but restrict database acccess on a server ba The -t argument specifies the top level directory of the music files. On a local installation, this is the directory in which you executed the import steps (Chapter 4.2). -\section use USING MUGGLE +\section use USING MUGGLE - OVERVIEW Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. Then turn to the playlist view using yellow and start play using again the red function key. @@ -213,4 +204,67 @@ Then turn to the playlist view using yellow and start play using again the red f During playback, Up/Down jumps forth and back in the current playlist. Yellow toggles play/pause mode and Ok brings up a progress display. For VDR 1.3.6- the progress display is "quite simple". +\section use-detail USING MUGGLE + +The idea behind muggle and the concept of the GUI was driven by the requirement, that almost all funtionalities must be possible with only using the cursor keys, the colour keys and menu, ok and back. + +Muggle consist of three main views. The different views could allways be switch with the yellow key. In every view there is a context sensitiv menu with certain commands suitable for the current view. This menu could be reached from every view with the blue key. The two most common commands for a view could be reached with the remaining two colour keys red and green. + +In all views the cursor keys are used like in every VDR menu. + +That is all you have to know to get full access to all functions of muggle. So now lets look in more detail in the three different views. + +\subsection browserview BROWSER VIEW + +The Browser view will be the most used view within muggle. It presents all stored media within the database in a sort of tree view. A tree consist of nodes wich have child nodes which are also nodes. A leaf of such a tree consist of one single media file. Just to make it clear beneath a node there are on the bottom line at least on, but in the higher regions of the tree several media files. + +At present there are 5 different main trees: +- artist -> album -> title +- genre -> artist -> album -> tilte +- artist -> title +- genre -> year -> title +- album -> title + +To add all songs from Abba to the active playlist just use the "artist -> title" tree and look for Abba in the resulting list. Press OK and here you are. All songs from Abba which are stored in the database are shown on the screen and the higlighted one could be added with the red key (you remember: most common commands are on red/green) to the active playlist. If you want to add all songs from Abba to the active playlist just go up one level, Abba is highlighted and then press red and ready you are. + +With the green colour key you could easily reach the main trees (this is not implemented yet). + +Thats all you have to know about this view. Please keep in mind that red adds always _ALL_ songs beneath the current node to the active playlist. So pressing red on one of the main trees leads to adding _ALL_ media files within the database (this could be thousands) to the active playlist. + +\subsection playlistview PLAYLIST VIEW + +In this view you see the active playlist. All songs currently added to the playlist are shown here. With the blue colour key you can reach the context sensitive menu. Here you can find some actions to modify the playlist, clear it totaly and many others. + +\subsection searchview SEARCH VIEW + +The search view is a more advanced method to look into the content of your database. If you search for all songs from Abba which are rated "++" this is your tool. You could find three different search methods which are descripte later. You switch betweeen this search methods with the green colour key. + +\subsubsection titlesearch TITLE SEARCH + +If you want to search for a single media file, this is for you. You could insert some search criteria and after pressing the red button you see all media files which fullfill the restrictions. + +Example: Search for all songs from Abba which have "water" in the title an are publisched befor 2000. + + Artist: Abba + Title: water + Year(till): 2000 + +\subsubsection albumsearch ALBUM SEARCH + +If you want to search for a certain album and are interested in all songs of that album even if the songs not fullfill the search criteria, than this search is for you. + +Example: Search for all songs on an album from "U2" with "tree" in the album title. + + Album Title: tree + Album Artist: U2 + +\subsubsection playlistsearch PLAYLIST SEARCH + +If you want to search for a playlist wích contains songs which fullfill the criteria. + +Example: Yesterday during your birthday party you played a song from Tina Turner during your party, your best friend is interested in the song which was played just behind that Tina Turner song. The search presents all titles of that playlist. You could easily browser the list, find Tinas song and you have the one behind. + + Playlist Title: Birthday Party 2004 + Artist: Tina Turner + */ -- cgit v1.2.3 From 597487de0076f2e47544285a24f6022accedd22a Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 27 Oct 2004 08:14:03 +0000 Subject: Resolved conflicts in README git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@254 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README b/README index c4fef6a..a8e3a4b 100644 --- a/README +++ b/README @@ -25,6 +25,16 @@ Please provide feedback to the authors whenever you think, these instructions are not appropriate, wrong, or useless in any other sense. +\section ack Acknowledgements + +Thanks to all who have supported the development of this plugin. Special thanks go to +- Muempf for the mp3 plugin. All code related to audio replay is largely taken over from this plugin. +- LordJaxom for constant support in the chat of VDR portal regarding OSD programming in VDR +- eloy (member of vdrportal.de) for alpha testing +- All beta testers at vdrportal.de and on the VDR mailing list +- decembersoul (member of vdrportal.de) for finding out how to run muggle on LinVDR +- Hulk (member of vdrportal.de) for submitting several patches and helping out + \section desc DESCRIPTION The muggle plugin provides a database link for VDR so that selection of media becomes more flexible. @@ -208,7 +218,7 @@ brings up a progress display. For VDR 1.3.6- the progress display is "quite simp The idea behind muggle and the concept of the GUI was driven by the requirement, that almost all funtionalities must be possible with only using the cursor keys, the colour keys and menu, ok and back. -Muggle consist of three main views. The different views could allways be switch with the yellow key. In every view there is a context sensitiv menu with certain commands suitable for the current view. This menu could be reached from every view with the blue key. The two most common commands for a view could be reached with the remaining two colour keys red and green. +Muggle consist of three main views. The different views could always be switched with the yellow key. In every view there is a context sensitive menu with certain commands suitable for the current view. This menu could be reached from every view with the blue key. The two most common commands for a view could be reached with the remaining two colour keys red and green. In all views the cursor keys are used like in every VDR menu. -- cgit v1.2.3 From 8a119afd3a313bdf66747c9fbbc751c70771fcb2 Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 27 Oct 2004 15:00:27 +0000 Subject: Extend documentation git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@255 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 48 +++++++++++++++++++++++++++++++++++++----------- TODO | 1 + 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/README b/README index a8e3a4b..641d98f 100644 --- a/README +++ b/README @@ -6,13 +6,12 @@ Written by: Andi Kellner Lars von Wedel Ralf Klueber , -Project's homepage: URL +Project's homepage: http://www.htpc-tech.de/htpc/muggle.htm -Latest version available at: URL +Latest version available at: http://www.htpc-tech.de/htpc/muggle_archive/vdr-muggle-0.0.8-BETA.tgz See the file COPYING for license information. - \section foreword PLEASE! This is a difficult plugin. It's nice but difficult. @@ -27,7 +26,7 @@ or useless in any other sense. \section ack Acknowledgements -Thanks to all who have supported the development of this plugin. Special thanks go to +Thanks to all who have supported the development of this plugin. Special thanks (order does not mean importance :-) go to - Muempf for the mp3 plugin. All code related to audio replay is largely taken over from this plugin. - LordJaxom for constant support in the chat of VDR portal regarding OSD programming in VDR - eloy (member of vdrportal.de) for alpha testing @@ -44,7 +43,7 @@ parameters are descibed in Section 5. \section prereq PREREQUISITES -The plugin is written for VDR 1.2.6. In addition, the following pieces of +The plugin has been tested with VDR versions up to 1.3.12. In addition, the following pieces of software are required: - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) @@ -64,7 +63,8 @@ software are required: The developer versions are needed because their headers are required for compilation. The server need not be on the same machine as the VDR. Also, music tracks can reside somewhere else, if they are available through a remote filesystem (NFS, Samba). However, in this case you should -know what you are doing in terms of networking and security issues. +know what you are doing in terms of networking and security issues. In my personal setup, the mySQL +database runs on a server where also all music files are stored. Muggle accesses them via Samba. \section install INSTALLING @@ -82,7 +82,7 @@ Establish a symlink as you would for other plugins: ln -s muggle-0.1.7 muggle \endverbatim -Within the VDR main directory (e.g. /usr/local/src/VDR) issue a +Note that the actual directory names may vary, e.g. the version number will changes. Within the VDR main directory (e.g. /usr/local/src/VDR) issue a \verbatim make plugins @@ -93,14 +93,13 @@ in the library directories stated in the muggle Makefile. \section import IMPORT -The import is done in two steps: First, a database is created and initialized with proper data structures (so-called schema). -Then, these data structures are filled from the ID3 tags of your music tracks. +The import is done in two steps: First, a database is created and initialized with proper data structures (so-called schema). Then, these data structures are filled from the ID3 tags of your music tracks. \subsection dbsetup Setup Database This step can be done on the database server or on some other client machine. Within the directory scripts there are a few helpful files to support setting -up the database. Change into that directory:# +up the database. Change into that directory: \verbatim cd scripts @@ -243,7 +242,18 @@ Thats all you have to know about this view. Please keep in mind that red adds al \subsection playlistview PLAYLIST VIEW -In this view you see the active playlist. All songs currently added to the playlist are shown here. With the blue colour key you can reach the context sensitive menu. Here you can find some actions to modify the playlist, clear it totaly and many others. +In this view you see the active playlist. All songs currently added to the playlist are shown here. With the blue colour key you can reach the context sensitive menu. Here you can find some actions to modify, load, or save the playlist, clear all entries and many others. In detail the commands are +- Red: start playing the list from the beginning or at the last played song (cf. section Replay) +- Green: Move the current entry to reorder the playlist + +In the submenu the following commands can be selected: +- Rename playlist: change the playlist name using the up/down cursor keys +- Load playlist: show playlists in the database and load one (using Ok) +- Save playlist: Store the current playlist status into the database +- Clear playlist: Remove all entries from the playlist +- Delete current entry: Remove the currently selected entry from the playlist +- Export playlist: export the playlist in m3u (version 2) into the muggle config directory as .m3u +- Playlist commands: similar to commands.conf or reccmds.conf for VDR a file containing commands to execute on a playlist can be specified in a file playlist_commands.conf which must reside in the muggle config directory. The commands listed in that file will be called with one argument which is the path of the playlist file in m3u (version 2). \subsection searchview SEARCH VIEW @@ -277,4 +287,20 @@ Example: Yesterday during your birthday party you played a song from Tina Turner Playlist Title: Birthday Party 2004 Artist: Tina Turner +\subsection useplayer DURING PLAYBACK + +The functions available furing playback mostly relate to navigation in the playlist +and displaying information about the current track or playlist. + +- Up: Skip to the next title +- Down: Skip to the previous title +- Ok: toggle display (progress or information view) + +- Red: When display shown: toggle between progress and information view +- Green: When display shown: toggle between track and playlist view +- Yellow: Play/Pause +- Blue: stop replay but remind resume index (so Play from browser view starts with the track played last) + +- Back: stop replay (Play from browser will start from the beginning) + */ diff --git a/TODO b/TODO index e250b93..39e1e39 100644 --- a/TODO +++ b/TODO @@ -10,6 +10,7 @@ \section urgent Urgent/Short-term issues \subsection bugs Bugs and testing needed + - Check whether track/progress view settings are saved - Crashes in filter selections? - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) -- cgit v1.2.3 From 4665fa3dac377fc251c4201aff3d9b15ee64474e Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 27 Oct 2004 15:01:02 +0000 Subject: Save visibility status git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@256 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 6 +++++- vdr_setup.c | 10 ++++++++-- vdr_setup.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 322801e..6ba5c9d 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -1085,8 +1085,10 @@ mgPlayerControl::mgPlayerControl( mgPlaylist *plist, unsigned start ) m_display = NULL; m_menu = NULL; #endif - m_visible = false; m_has_osd = false; + + // obtain settings from last run + m_visible = the_setup.visible; m_track_view = the_setup.trackview; m_progress_view = the_setup.progressview; @@ -1489,6 +1491,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) { // toggle progress display between simple and detail m_progress_view = !m_progress_view; + the_setup.progressview = m_progress_view; Display(); } } break; @@ -1530,6 +1533,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) { // toggle progress display between playlist and track m_track_view = !m_track_view; + the_setup.trackview = m_track_view ; Display(); } } break; diff --git a/vdr_setup.c b/vdr_setup.c index 9b11185..e6aa816 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -39,6 +39,8 @@ mgMenuSetup::mgMenuSetup() Add(new cMenuEditBoolItem(tr("Setup.Muggle$Background mode"), &m_data.BackgrMode, tr("Black"), tr("Live"))); Add(new cMenuEditIntItem( tr("Setup.Muggle$Normalizer level"), &m_data.TargetLevel, 0, MAX_TARGET_LEVEL)); Add(new cMenuEditIntItem( tr("Setup.Muggle$Limiter level"), &m_data.LimiterLevel, MIN_LIMITER_LEVEL, 100)); + + Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with open display"), &m_data.visible )); Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with progress display"), &m_data.progressview )); Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with track display"), &m_data.trackview )); } @@ -55,6 +57,8 @@ void mgMenuSetup::Store(void) SetupStore("TargetLevel", the_setup.TargetLevel ); SetupStore("LimiterLevel", the_setup.LimiterLevel ); SetupStore("Only48kHz", the_setup.Only48kHz ); + + SetupStore("Visible", the_setup.visible ); SetupStore("TrackView", the_setup.trackview ); SetupStore("ProgressView", the_setup.progressview ); } @@ -72,6 +76,8 @@ mgSetup::mgSetup() LimiterLevel = DEFAULT_LIMITER_LEVEL; Only48kHz = 0; ToplevelDir = "/mnt/music/"; - trackview = true; - progressview = true; + + visible = 1; + trackview = 1; + progressview = 1; } diff --git a/vdr_setup.h b/vdr_setup.h index 1c59235..e96df84 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -52,6 +52,7 @@ public: bool GdCompatibility; char *ToplevelDir; + int visible; int trackview; int progressview; -- cgit v1.2.3 From d671f02bd5f8b0bde5f48a44083efd4c50c511c4 Mon Sep 17 00:00:00 2001 From: lvw Date: Wed, 27 Oct 2004 15:01:15 +0000 Subject: Save visibility status git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@257 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vdr_player.h b/vdr_player.h index d71149e..e87d577 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -44,16 +44,16 @@ private: mgPCMPlayer *player; //! \brief indicates, whether the osd should be visible - bool m_visible; + int m_visible; //! \brief indicates, whether an osd is currently displayed bool m_has_osd; //! \brief indicates, whether the osd displays a track view (true) or a playlist view (false) - bool m_track_view; + int m_track_view; //! \brief indicates, whether the osd presents progress (true) or detail information (false) - bool m_progress_view; + int m_progress_view; #if VDRVERSNUM >= 10307 //! \brief a replay display to show the progress during playback -- cgit v1.2.3 From d89ce8d5a49861d4b54380d0660f676bba07522a Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 28 Oct 2004 09:55:54 +0000 Subject: Make stuff compile with g++ 2.95.4 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@258 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_database.c | 1 + mugglei.c | 1 + 2 files changed, 2 insertions(+) diff --git a/mg_database.c b/mg_database.c index 1636262..02d2e0a 100644 --- a/mg_database.c +++ b/mg_database.c @@ -10,6 +10,7 @@ #include "mg_database.h" #include "mg_tools.h" +#include #include static const int MAX_QUERY_BUFLEN = 2048; diff --git a/mugglei.c b/mugglei.c index f090c01..46ef6a1 100755 --- a/mugglei.c +++ b/mugglei.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From 0fe155eaff5361997c03243e263131da203f691c Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 28 Oct 2004 09:57:42 +0000 Subject: Avoid multiline comments as they seem to cause trouble git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@259 e10066b5-e1e2-0310-b819-94efdf66514b --- scripts/createdb.mysql | 10 +++++----- scripts/createtables.mysql | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/createdb.mysql b/scripts/createdb.mysql index 7ccdc89..68c12d7 100755 --- a/scripts/createdb.mysql +++ b/scripts/createdb.mysql @@ -1,14 +1,14 @@ -/* Creates DB and opens it to any user */ -/* Run this mysql macro as root! */ +-- Creates DB and opens it to any user +-- Run this mysql macro as root! DROP DATABASE IF EXISTS GiantDisc; CREATE DATABASE GiantDisc; use GiantDisc; -/* The first line is useful for granting access to user vdr on all computers in a network. */ -/* grant all privileges on GiantDisc.* to vdr@'%'; */ +-- The first line is useful for granting access to user vdr on all computers in a network. +-- grant all privileges on GiantDisc.* to vdr@'%'; -/* Grant access to user vdr on the local machine */ +-- Grant access to user vdr on the local machine grant all privileges on GiantDisc.* to vdr@localhost; diff --git a/scripts/createtables.mysql b/scripts/createtables.mysql index 079b3bd..402f66b 100755 --- a/scripts/createtables.mysql +++ b/scripts/createtables.mysql @@ -8,7 +8,7 @@ -- Current Database: GiantDisc -- ---CREATE DATABASE /*!32312 IF NOT EXISTS*/ GiantDisc; +-- CREATE DATABASE /*!32312 IF NOT EXISTS*/ GiantDisc; USE GiantDisc; -- cgit v1.2.3 From 4a3a0058a3cba24b4d646a165501587ecbe449fe Mon Sep 17 00:00:00 2001 From: lvw Date: Thu, 28 Oct 2004 13:14:41 +0000 Subject: Documentation update git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@260 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 19 ++++++++++--------- TODO | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/README b/README index 641d98f..9fae263 100644 --- a/README +++ b/README @@ -32,7 +32,7 @@ Thanks to all who have supported the development of this plugin. Special thanks - eloy (member of vdrportal.de) for alpha testing - All beta testers at vdrportal.de and on the VDR mailing list - decembersoul (member of vdrportal.de) for finding out how to run muggle on LinVDR -- Hulk (member of vdrportal.de) for submitting several patches and helping out +- Hulk (member of vdrportal.de) for submitting several patches (especially for gLCD display) \section desc DESCRIPTION @@ -159,13 +159,14 @@ It does not matter whether there are further subdirectories which organize files album or whatever. If this is not the case, you may want to take some time to do this. Read on before you start -You probably do not want to import all files in one go: albums on which tracks of various artists are found -(samplers) require different treatment than files of just one artist. What I did: all samplers are collected -below a special subdirectory "Assorted". Import is then run separately for those tracks. +You probably do not want to import all files in one go: albums on which tracks of various artists are +found (samplers) require different treatment than albums containing files of just one artist. What I did: +all samplers are collected below a special subdirectory "Assorted". Import is then run separately for those +tracks. For now, let's assume your music tracks are located in /home/music and samplers are in /home/music/Assorted. -First, import the files in Assorted. This requires the flag -a to mugglei. Further flags -h, -n, -u, and -p +First, let's import the files in Assorted. This requires the flag -a to mugglei. Further flags -h, -n, -u, and -p specify database host, name, user and password, respectively. The filename to import is given using the -f directive. Using 'find' you can import all files for assorted albums with a command like: @@ -196,7 +197,7 @@ Muggle uses a small set of command line parameters in order to control the inter Let's look at an example: \verbatim - -P'muggle -h localhost -u vdr -n GiantDisc -t/home/music' + vdr -P'muggle -h localhost -u vdr -n GiantDisc -t/home/music' \endverbatim The -h parameter specifies the database host, -u specifies the user, -n is the database name. The scripts mentioned @@ -205,7 +206,7 @@ above do not make use of passwords, but restrict database acccess on a server ba The -t argument specifies the top level directory of the music files. On a local installation, this is the directory in which you executed the import steps (Chapter 4.2). -\section use USING MUGGLE - OVERVIEW +\section use USING MUGGLE - QUICK OVERVIEW Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. Then turn to the playlist view using yellow and start play using again the red function key. @@ -296,8 +297,8 @@ and displaying information about the current track or playlist. - Down: Skip to the previous title - Ok: toggle display (progress or information view) -- Red: When display shown: toggle between progress and information view -- Green: When display shown: toggle between track and playlist view +- Red: When display shown: toggle between progress and information view, otherwise toggle loop mode (not yet functional) +- Green: When display shown: toggle between track and playlist view, otherwise toggle shuffle mode (not yet functional) - Yellow: Play/Pause - Blue: stop replay but remind resume index (so Play from browser view starts with the track played last) diff --git a/TODO b/TODO index 39e1e39..e662e54 100644 --- a/TODO +++ b/TODO @@ -40,14 +40,8 @@ - Move differences 1.3.7+- to link level \subsection urgentcontent Content handling - - Save on exit - - implement storePlaylistAs( string name ) in gdPlaylist - - find id of playlist called name or create it - - delete all entries - - store current entries - Think, whether type (mp3, ogg, flac) should be stored in database - could be used in searching/structuring as well - - Handle loop mode in mgPlaylist - Handle shuffle mode in mgPlaylist - shuffle mode on - for next file: @@ -141,14 +135,14 @@ - read/write queries - return results (needs an abstract representation of results?) - \subsection midosd OSD-related issues + \subsection midosd OSD-related issues - Incremental search - Type numbers to enter characters and jump to first title accordingly - Check whether submenus (as implemented in VDR) are more suitable - do not permit jumping to arbitrary menus though - \subsection midcontent Content issues + \subsection midcontent Content issues - Save/load filter sets - Apply filter set as dynamic playlist (i.e. show filters when loading playlists) @@ -160,12 +154,12 @@ - when playfrequency reaches upper level y from above: decrease rating - when playfrequency reaches upper level y from below: increase rating - \subsection midplayer Player issues + \subsection midplayer Player issues - Use single CD files with cuesheets in metadata for FLAC - Handle recoding samplerate, limiter etc correctly - \section vision Long term ideas and visions + \section vision Long term ideas and visions - daapd integration? - netjuke integration? @@ -247,5 +241,11 @@ - BUG: Red key does not work in Browser submenu - Test execution of playlist commands - Resume play instead of restarting list from the beginning when terminating play with blue instead of back + - Save on exit + - implement storePlaylistAs( string name ) in gdPlaylist + - find id of playlist called name or create it + - delete all entries + - store current entries + - Handle loop mode in mgPlaylist */ \ No newline at end of file -- cgit v1.2.3 From 22a0df57ec45522eb109d6458c151fb077578d47 Mon Sep 17 00:00:00 2001 From: RaK Date: Thu, 28 Oct 2004 17:07:33 +0000 Subject: added my suggestion for an other key setting during playback git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@261 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README b/README index 9fae263..3f787e8 100644 --- a/README +++ b/README @@ -304,4 +304,18 @@ and displaying information about the current track or playlist. - Back: stop replay (Play from browser will start from the beginning) +Alternativ Key Settings: + +- Down: Pause +- Up: Play +- Right: Fast Forward +- Left: Rewind + +- Ok: toggle Display (Progress View -> Information -> View -> Playlist View -> Off) + +- Red +- Green: Skip to previous song. +- Yellow: Skip to next song. +- Blue: stop replay but remind resume index (so Play from browser view starts with the track played last) + */ -- cgit v1.2.3 From 66b25b69a6c946f9132242c655bd9e42577b92d2 Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 29 Oct 2004 08:19:23 +0000 Subject: Allow to delete first entry of playlist git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@262 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_playlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mg_playlist.c b/mg_playlist.c index b4bc487..5ead8f7 100644 --- a/mg_playlist.c +++ b/mg_playlist.c @@ -140,7 +140,7 @@ bool mgPlaylist::remove( unsigned pos ) { bool result = false; - if( pos > 0 && pos != m_current_idx ) + if( pos != m_current_idx ) { result = mgTracklist::remove( pos ); -- cgit v1.2.3 From 3c2d5420c44246033f55ec31f877e58357de3466 Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 29 Oct 2004 08:22:34 +0000 Subject: Store OSD visibility in setup git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@263 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vdr_player.c b/vdr_player.c index 6ba5c9d..48182ef 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -1553,6 +1553,7 @@ eOSState mgPlayerControl::ProcessKey(eKeys key) case kOk: { m_visible = !m_visible; + the_setup.visible = m_visible; Display(); return osContinue; -- cgit v1.2.3 From 1823baf159eaa175dd0e5c7ef46fdeeabe92d779 Mon Sep 17 00:00:00 2001 From: lvw Date: Fri, 29 Oct 2004 08:31:19 +0000 Subject: Updated Documentation git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@264 e10066b5-e1e2-0310-b819-94efdf66514b --- TODO | 71 ++++++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/TODO b/TODO index e662e54..c5aef08 100644 --- a/TODO +++ b/TODO @@ -10,37 +10,24 @@ \section urgent Urgent/Short-term issues \subsection bugs Bugs and testing needed - - Check whether track/progress view settings are saved - Crashes in filter selections? - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) \subsection urgentosd OSD-related Issues - Rename playlist - \subsection urgentcode Code polishing - - Clean up coding style and documentation in general - - Logging - - extend mgLog with static logging methods - - in DEBUG mode, issue logs, warnings, errors to stderr - - otherwise issue errors only to syslog - - Check for unnecessary log commands - - Generate HTML documentation using doxygen, - - use dotty/gv for state machines of player - - make available online - - Clean up mugglei (abstract code where possible) - - Check for memory leaks - - Why do filters use pointers? - - Check for (reasonably) consistent usage of char pointers and strings - - mgDatabase class is not used - - should handle a static object with a MySQL connection - - execute queries - - escape query strings - - mgPlayer used what for? - - Could save IP/host name and associate last playlist/index loaded - - Move differences 1.3.7+- to link level + \subsection urgentplayer Player extensions + - Display covers + - Import filename + - Show image during replay + - Add FLAC decoder + - Determine max. framecount (needed for rewinding?) + - Init scale/level/normalize? + - The max. level should be recognized during play + - Store max. level in the database \subsection urgentcontent Content handling - - Think, whether type (mp3, ogg, flac) should be stored in database + - Think, whether type (mp3, ogg, flac) should be stored in database - could be used in searching/structuring as well - Handle shuffle mode in mgPlaylist - shuffle mode on @@ -61,18 +48,30 @@ - if n < playcount / max. playcount - add the file to the end of the list - \subsection urgentplayer Player extensions - - Display covers - - Import filename - - Show image during replay - - Add FLAC decoder - - Determine max. framecount (needed for rewinding?) - - Init scale/level/normalize? - - The max. level should be recognized during play - - Store max. level in the database - - Display covers - \subsection deploy Deployment + \subsection urgentcode Code polishing + - Clean up coding style and documentation in general + - Logging + - extend mgLog with static logging methods + - in DEBUG mode, issue logs, warnings, errors to stderr + - otherwise issue errors only to syslog + - Check for unnecessary log commands + - Generate HTML documentation using doxygen, + - use dotty/gv for state machines of player + - make available online + - Clean up mugglei (abstract code where possible) + - Check for memory leaks + - Why do filters use pointers? + - Check for (reasonably) consistent usage of char pointers and strings + - mgDatabase class is not used + - should handle a static object with a MySQL connection + - execute queries + - escape query strings + - mgPlayer used what for? + - Could save IP/host name and associate last playlist/index loaded + - Move differences 1.3.7+- to link level + + \subsection deploy Deployment - Script to publish a version - make dist @@ -241,11 +240,13 @@ - BUG: Red key does not work in Browser submenu - Test execution of playlist commands - Resume play instead of restarting list from the beginning when terminating play with blue instead of back + - Save current playlist on exit and restore on start - Save on exit - implement storePlaylistAs( string name ) in gdPlaylist - find id of playlist called name or create it - delete all entries - store current entries - Handle loop mode in mgPlaylist - + - Track/progress view settings are now saved in the setup + - BUG: could not delete first track of a playlist */ \ No newline at end of file -- cgit v1.2.3 From 0756ae40d68d403082c86adef042a4c0daba219f Mon Sep 17 00:00:00 2001 From: LarsAC Date: Fri, 7 Jan 2005 18:43:03 +0000 Subject: Merged branch osd_extensions back to main trunk git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@324 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 11 +- README | 162 ++- TODO | 125 +-- gd_content_interface.c | 1425 ------------------------ gd_content_interface.h | 582 ---------- i18n.c | 1961 +++++++++++++++++++-------------- i18n.h | 3 +- mg_actions.c | 986 +++++++++++++++++ mg_actions.h | 168 +++ mg_content_interface.c | 241 ---- mg_content_interface.h | 480 -------- mg_database.c | 1 - mg_db.c | 1517 +++++++++++++++++++++++++ mg_db.h | 1071 ++++++++++++++++++ mg_media.c | 453 -------- mg_media.h | 209 ---- mg_playlist.c | 368 ------- mg_playlist.h | 209 ---- mg_tools.c | 154 ++- mg_tools.h | 85 +- muggle.c | 342 +++--- muggle.doxygen | 2 +- muggle.h | 53 +- mugglei.c | 65 +- scripts/createdb.mysql | 10 +- scripts/createtables.mysql | 2 +- vdr_config.h | 11 +- vdr_decoder.c | 169 +-- vdr_decoder.h | 153 ++- vdr_decoder_mp3.c | 608 +++++----- vdr_decoder_mp3.h | 115 +- vdr_decoder_ogg.c | 599 +++++----- vdr_decoder_ogg.h | 49 +- vdr_menu.c | 1390 +++++++++-------------- vdr_menu.h | 386 +++++-- vdr_network.h | 54 +- vdr_player.c | 2632 +++++++++++++++++++++++--------------------- vdr_player.h | 167 ++- vdr_setup.c | 97 +- vdr_setup.h | 66 +- vdr_sound.c | 935 +++++++++------- vdr_stream.c | 488 ++++---- vdr_stream.h | 65 +- 43 files changed, 9508 insertions(+), 9161 deletions(-) delete mode 100644 gd_content_interface.c delete mode 100644 gd_content_interface.h create mode 100644 mg_actions.c create mode 100644 mg_actions.h delete mode 100755 mg_content_interface.c delete mode 100755 mg_content_interface.h create mode 100644 mg_db.c create mode 100644 mg_db.h delete mode 100644 mg_media.c delete mode 100644 mg_media.h delete mode 100644 mg_playlist.c delete mode 100644 mg_playlist.h diff --git a/Makefile b/Makefile index 12145c5..c5c8187 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri ### The C++ compiler and options: CXX ?= g++-3.3 -CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g +CXXFLAGS ?= -fPIC -O0 -Wall -Woverloaded-virtual -Wno-deprecated -g ### The directory environment: @@ -51,14 +51,16 @@ DEFINES += -D_GNU_SOURCE MIFLAGS += -I/usr/include/taglib -lmysqlclient ### The object files (add further files here): -OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o vdr_decoder_ogg.o +OBJS = $(PLUGIN).o i18n.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \ + vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \ + vdr_setup.o vdr_decoder_ogg.o LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis MILIBS = -lmysqlclient -ltag ### Targets: -all: libvdr-$(PLUGIN).so mugglei +all: libvdr-$(PLUGIN).so # Dependencies: @@ -80,11 +82,10 @@ libvdr-$(PLUGIN).so: $(OBJS) mugglei: mg_tools.o mugglei.o $(CXX) $(CXXFLAGS) $^ $(MILIBS) -o $@ - install: @cp ../../lib/libvdr-muggle*.so.* /usr/lib/vdr/ @cp mugglei /usr/local/bin/ - @install -m 755 mugglei /usr/local/bin/ +# @install -m 755 mugglei /usr/local/bin/ dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) diff --git a/README b/README index 3f787e8..1ec00c8 100644 --- a/README +++ b/README @@ -2,13 +2,14 @@ This is a "plugin" for the Video Disk Recorder (VDR). -Written by: Andi Kellner - Lars von Wedel +Written by: Andi Kellner, + Lars von Wedel , Ralf Klueber , + Wolfgang Rohdewald Project's homepage: http://www.htpc-tech.de/htpc/muggle.htm -Latest version available at: http://www.htpc-tech.de/htpc/muggle_archive/vdr-muggle-0.0.8-BETA.tgz +Latest version available at: http://www.htpc-tech.de/htpc/muggle-dev.htm See the file COPYING for license information. @@ -24,16 +25,6 @@ Please provide feedback to the authors whenever you think, these instructions are not appropriate, wrong, or useless in any other sense. -\section ack Acknowledgements - -Thanks to all who have supported the development of this plugin. Special thanks (order does not mean importance :-) go to -- Muempf for the mp3 plugin. All code related to audio replay is largely taken over from this plugin. -- LordJaxom for constant support in the chat of VDR portal regarding OSD programming in VDR -- eloy (member of vdrportal.de) for alpha testing -- All beta testers at vdrportal.de and on the VDR mailing list -- decembersoul (member of vdrportal.de) for finding out how to run muggle on LinVDR -- Hulk (member of vdrportal.de) for submitting several patches (especially for gLCD display) - \section desc DESCRIPTION The muggle plugin provides a database link for VDR so that selection of media becomes more flexible. @@ -43,7 +34,7 @@ parameters are descibed in Section 5. \section prereq PREREQUISITES -The plugin has been tested with VDR versions up to 1.3.12. In addition, the following pieces of +The plugin is written for VDR 1.2.6. In addition, the following pieces of software are required: - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) @@ -63,8 +54,7 @@ software are required: The developer versions are needed because their headers are required for compilation. The server need not be on the same machine as the VDR. Also, music tracks can reside somewhere else, if they are available through a remote filesystem (NFS, Samba). However, in this case you should -know what you are doing in terms of networking and security issues. In my personal setup, the mySQL -database runs on a server where also all music files are stored. Muggle accesses them via Samba. +know what you are doing in terms of networking and security issues. \section install INSTALLING @@ -82,7 +72,7 @@ Establish a symlink as you would for other plugins: ln -s muggle-0.1.7 muggle \endverbatim -Note that the actual directory names may vary, e.g. the version number will changes. Within the VDR main directory (e.g. /usr/local/src/VDR) issue a +Within the VDR main directory (e.g. /usr/local/src/VDR) issue \verbatim make plugins @@ -93,13 +83,14 @@ in the library directories stated in the muggle Makefile. \section import IMPORT -The import is done in two steps: First, a database is created and initialized with proper data structures (so-called schema). Then, these data structures are filled from the ID3 tags of your music tracks. +The import is done in two steps: First, a database is created and initialized with proper data structures (so-called schema). +Then, these data structures are filled from the ID3 tags of your music tracks. \subsection dbsetup Setup Database This step can be done on the database server or on some other client machine. Within the directory scripts there are a few helpful files to support setting -up the database. Change into that directory: +up the database. Change into that directory:# \verbatim cd scripts @@ -135,7 +126,7 @@ Execute these commands on a single line, the \ for the linebreak ist just for pr mysql -u root --local-infile=1 \endverbatim -You can find the sequence of commands in the file scripts/make-empty-db. Use it at your own luck. +You can find the sequence of commands in the file scripts/make-empty-db. Use it at your own luck after making necessary modification (program paths, database names, servers, users, etc.). Please note, that the scripts and commands above are quite basic in terms of security (e.g. no password set for the vdr user, no proper selection of privileges). You may want to spend some @@ -159,14 +150,14 @@ It does not matter whether there are further subdirectories which organize files album or whatever. If this is not the case, you may want to take some time to do this. Read on before you start -You probably do not want to import all files in one go: albums on which tracks of various artists are -found (samplers) require different treatment than albums containing files of just one artist. What I did: -all samplers are collected below a special subdirectory "Assorted". Import is then run separately for those -tracks. +You probably do not want to import all files in one go: albums on which tracks of various artists are found +(samplers) require different treatment than files of just one artist. What I did: all samplers are collected +below a special subdirectory "Assorted". Import is then run separately for those tracks. There has been discussion +about this and ideas for better solutions are welcome. For now, let's assume your music tracks are located in /home/music and samplers are in /home/music/Assorted. -First, let's import the files in Assorted. This requires the flag -a to mugglei. Further flags -h, -n, -u, and -p +First, import the files in Assorted. This requires the flag -a to mugglei. Further flags -h, -n, -u, and -p specify database host, name, user and password, respectively. The filename to import is given using the -f directive. Using 'find' you can import all files for assorted albums with a command like: @@ -197,7 +188,7 @@ Muggle uses a small set of command line parameters in order to control the inter Let's look at an example: \verbatim - vdr -P'muggle -h localhost -u vdr -n GiantDisc -t/home/music' + -P'muggle -h localhost -u vdr -n GiantDisc -t/home/music' \endverbatim The -h parameter specifies the database host, -u specifies the user, -n is the database name. The scripts mentioned @@ -206,116 +197,95 @@ above do not make use of passwords, but restrict database acccess on a server ba The -t argument specifies the top level directory of the music files. On a local installation, this is the directory in which you executed the import steps (Chapter 4.2). -\section use USING MUGGLE - QUICK OVERVIEW +\section quickuse QUICK INTRO Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. -Then turn to the playlist view using yellow and start play using again the red function key. +Music will start instantly while you can continue to browse and add tracks. During playback, Up/Down jumps forth and back in the current playlist. Yellow toggles play/pause mode and Ok -brings up a progress display. For VDR 1.3.6- the progress display is "quite simple". +toggles a display of the replay process. Using Green, the display can be switched between playlist and single display mode, red toggles info and progress view. For VDR 1.3.6- the progress display is "quite simple", unfortunately. + +\section use DETAILED USER'S GUIDE + +The core concept of the Muggle user interface is called a *selection*. That is, as the name suggests, a selection of music tracks. Note, that a selection can be as small as a single track (a very simple selection, indeed) or as large as the whole music library. + +Selections are used to structure all tracks (the music library) into sets (e.g. a selection of all tracks by an author) and subsets (e.g. the tracks of an author on a certain album). Such selections are built by means of keys (e.g. author or album) defined in the database and are displayed in the *music browser*. The current selection in the *music browser* contains all tracks defined by the line the cursor is on. So if you place the cursor on the line "Pop", all tracks with Genre Pop are selected. If you then enter Pop and go to the line "Beatles", you narrow your selection to pop songs from the beatles. + +A collection is a special selection. Collections can be defined by the user, and he can add or remove any selection to / from a collection. A collection has only ony order: a number which is incremented for every added track. Otherwise, since a collection is also a selection, everything that is valid for selections also holds for collections. -\section use-detail USING MUGGLE +Collections can be defined by the user in the sense of a playlist. This is done by adding/removing selections to/from the *default collection*. -The idea behind muggle and the concept of the GUI was driven by the requirement, that almost all funtionalities must be possible with only using the cursor keys, the colour keys and menu, ok and back. +Changing the contents of a collection changes them directly in the data base. Saving or loading collections is not needed. -Muggle consist of three main views. The different views could always be switched with the yellow key. In every view there is a context sensitive menu with certain commands suitable for the current view. This menu could be reached from every view with the blue key. The two most common commands for a view could be reached with the remaining two colour keys red and green. +A very important term while working with Muggle is the *default collection*. This is a special collection which is the target of commands working on collections. Whenever you add selections to somewhere, they will be added to the default collection. The same happens when you remove selections. -In all views the cursor keys are used like in every VDR menu. +Another important collection is the 'play' collection. This is a temporary collection. Whatever is added to it will be played in that order. If you add something while muggle is not playing anything, this collection will first be emptied. +However 'temporary' does not mean that its content is not saved to the data base. -That is all you have to know to get full access to all functions of muggle. So now lets look in more detail in the three different views. +\subsection general General remarks -\subsection browserview BROWSER VIEW +There are two main views in Muggle, the *Music browser* view and the *Collection browser* view. You can toggle between them using the yellow key. -The Browser view will be the most used view within muggle. It presents all stored media within the database in a sort of tree view. A tree consist of nodes wich have child nodes which are also nodes. A leaf of such a tree consist of one single media file. Just to make it clear beneath a node there are on the bottom line at least on, but in the higher regions of the tree several media files. +Each of the two views has associated commands. To show a summary of the commands available for the current view press the blue key. Note, that the red, green and yellow keys do not have a fixed meaning. Rather, while the commands for a certain view are displayed, you can press red/green/yellow to make the respective key execute the command currently selected (highlighted) by the cursor. The commands you choose for red/green/yellow will be saved for the next time you start muggle. You can define different commands in both view *Music browser* and *Collection browser*. -At present there are 5 different main trees: -- artist -> album -> title -- genre -> artist -> album -> tilte -- artist -> title -- genre -> year -> title -- album -> title +\subsection browse Music browser -To add all songs from Abba to the active playlist just use the "artist -> title" tree and look for Abba in the resulting list. Press OK and here you are. All songs from Abba which are stored in the database are shown on the screen and the higlighted one could be added with the red key (you remember: most common commands are on red/green) to the active playlist. If you want to add all songs from Abba to the active playlist just go up one level, Abba is highlighted and then press red and ready you are. +By default, Muggle starts in the *Music browser* display at the place where you left it last time. This browser displays the music library according to a search order, e.g. according to artists / albums / tracks or genre / year / track. These search orders are currently fixed in the code, but the objective is to make them editable by the user on the OSD. Browsing these search orders is done using Up/Down/Left/Right keys. To display the contents of a currently selected selection, press Ok. To return to the parent selection press Back. -With the green colour key you could easily reach the main trees (this is not implemented yet). +A set of commands can be displayed with the Blue key on the remote control. A new menu will open and show the commands explained below. Remember that pressing Red, Green or Yellow will make these keys execute the command currently highlighted by the cursor from now on. -Thats all you have to know about this view. Please keep in mind that red adds always _ALL_ songs beneath the current node to the active playlist. So pressing red on one of the main trees leads to adding _ALL_ media files within the database (this could be thousands) to the active playlist. +Those commands are currently available in the *music browser*: -\subsection playlistview PLAYLIST VIEW +- Instant Play: instantly play the current selection. This does not enter any collection. -In this view you see the active playlist. All songs currently added to the playlist are shown here. With the blue colour key you can reach the context sensitive menu. Here you can find some actions to modify, load, or save the playlist, clear all entries and many others. In detail the commands are -- Red: start playing the list from the beginning or at the last played song (cf. section Replay) -- Green: Move the current entry to reorder the playlist +- Add to 'play': add the current selection to the default collection. After the first start of muggle, the default collection is 'play' -In the submenu the following commands can be selected: -- Rename playlist: change the playlist name using the up/down cursor keys -- Load playlist: show playlists in the database and load one (using Ok) -- Save playlist: Store the current playlist status into the database -- Clear playlist: Remove all entries from the playlist -- Delete current entry: Remove the currently selected entry from the playlist -- Export playlist: export the playlist in m3u (version 2) into the muggle config directory as .m3u -- Playlist commands: similar to commands.conf or reccmds.conf for VDR a file containing commands to execute on a playlist can be specified in a file playlist_commands.conf which must reside in the muggle config directory. The commands listed in that file will be called with one argument which is the path of the playlist file in m3u (version 2). +- Remove from 'play': remove the current selection from the default collection. If there are more than one instances of a specific track in the collection, they are all removed. -\subsection searchview SEARCH VIEW +- Collections: switch to the collection view -The search view is a more advanced method to look into the content of your database. If you search for all songs from Abba which are rated "++" this is your tool. You could find three different search methods which are descripte later. You switch betweeen this search methods with the green colour key. +- Select search order: select another search order -\subsubsection titlesearch TITLE SEARCH +- Export tracklist: generate a file X.m3u containing all tracks from the current selection -If you want to search for a single media file, this is for you. You could insert some search criteria and after pressing the red button you see all media files which fullfill the restrictions. +- External commands: whatever you define -Example: Search for all songs from Abba which have "water" in the title an are publisched befor 2000. +By default, the red key adds the currently selected collection to the default collection. The green key instantly plays the currently selected collection. The yellow key toggles between the *Music browser* and the *Collection browser*. Thus, if you want to play an album, browse to it and press green. Remember that you can redefine commands executed by red, green and yellow by pressing them while displaying the command list. - Artist: Abba - Title: water - Year(till): 2000 +\subsection collections Collection browser -\subsubsection albumsearch ALBUM SEARCH +The *Collection browser* displays a list of available collections. Browse the list with Up/Down and display the collection contents with Ok. Returning to the collection list is done by pressing Back. One of the collections (the one called "play" when you start up muggle for the first time) is marked with a "->" in front of the name, meaning that it is the default collection. Whenever you add or remove selections, this default collection is the current target, meaning that selections will be added/removed to/from this collection. -If you want to search for a certain album and are interested in all songs of that album even if the songs not fullfill the search criteria, than this search is for you. +At the bottom of the list, the entry "Create collection" is displayed. Entering it with the right key will make the editor appear on the second half of the line and using the keys Up/Down/Left/Right you can enter the name of the new collection. Pressing Ok will terminate the editing process and add the new collection to the list. -Example: Search for all songs on an album from "U2" with "tree" in the album title. +Just like with the *music browser*, a set of commands can be displayed with the Blue key on the remote control. - Album Title: tree - Album Artist: U2 +Those commands are currently available in the list of collections. Depending on the current selection, not all of them are available: -\subsubsection playlistsearch PLAYLIST SEARCH +- Instant play: See *music browser* -If you want to search for a playlist wích contains songs which fullfill the criteria. +- Add to 'play': See *music browser* -Example: Yesterday during your birthday party you played a song from Tina Turner during your party, your best friend is interested in the song which was played just behind that Tina Turner song. The search presents all titles of that playlist. You could easily browser the list, find Tinas song and you have the one behind. +- Remove from 'play': See *music browser*. Not available when the cursor is on the default collection. - Playlist Title: Birthday Party 2004 - Artist: Tina Turner +- Remove all entries from 'play': Only available when the cursor is on the default collection. -\subsection useplayer DURING PLAYBACK +- Search: switch to the *music browser* -The functions available furing playback mostly relate to navigation in the playlist -and displaying information about the current track or playlist. +- Set default collection to 'X': as it says. -- Up: Skip to the next title -- Down: Skip to the previous title -- Ok: toggle display (progress or information view) +- Delete collection: Not available for the default collection and for the 'play' collection. -- Red: When display shown: toggle between progress and information view, otherwise toggle loop mode (not yet functional) -- Green: When display shown: toggle between track and playlist view, otherwise toggle shuffle mode (not yet functional) -- Yellow: Play/Pause -- Blue: stop replay but remind resume index (so Play from browser view starts with the track played last) +- Export track list: See *music browser* -- Back: stop replay (Play from browser will start from the beginning) +- External commands: whatever you define -Alternativ Key Settings: +Note that you cannot only add to/remove from collections in the *music browser*. Rather, also collections can be added/removed. The reason is that - as explained above - a collection is also a selection. So everything that can be done with selections can also be done with collections. An example: if you want to give a party, you could create a new collection "Party". Now, steer your cursor to the collection entitled "Lounge music" and select add. Then go to "Pop 80s" and add again. Finally, go to "Dance classics" and add. Now you have created a collection "Party" from three already existing collections. To continue this example, let us assume that one of your guests has a personal dislike against "Modern Talking". Switch to the browser view, go to the artist selection of "Modern Talking" and select "Remove". Now all tracks written by Modern Talking will be removed from your "Party" collection. -- Down: Pause -- Up: Play -- Right: Fast Forward -- Left: Rewind +Please note that "Remove" means removing from the default collection. "Delete" will delete a collection. -- Ok: toggle Display (Progress View -> Information -> View -> Playlist View -> Off) +It is possible that a collection holds the same track several times if you add it several times. However when you remove that track, all of its occurrences will be removed. -- Red -- Green: Skip to previous song. -- Yellow: Skip to next song. -- Blue: stop replay but remind resume index (so Play from browser view starts with the track played last) +The remote buttons Play, Pause, Stop are also supported while muggle displays its OSD. If Stop is pressed, muggle first stops playing what was started by Instant Play. Muggle will then continue playing the 'play' collection. A second Stop will stop playing the 'play' collection. */ diff --git a/TODO b/TODO index c5aef08..6594daf 100644 --- a/TODO +++ b/TODO @@ -10,36 +10,39 @@ \section urgent Urgent/Short-term issues \subsection bugs Bugs and testing needed - - Crashes in filter selections? - - Keep this? Test mgPCMPlayer::getSourceFile() for GD case (find) \subsection urgentosd OSD-related Issues - - Rename playlist - \subsection urgentplayer Player extensions - - Display covers - - Import filename - - Show image during replay - - Add FLAC decoder - - Determine max. framecount (needed for rewinding?) - - Init scale/level/normalize? - - The max. level should be recognized during play - - Store max. level in the database + \subsection urgentcode Code polishing + + - Clean up coding style and documentation in general + - Logging + - extend mgLog with static logging methods + - in DEBUG mode, issue logs, warnings, errors to stderr + - otherwise issue errors only to syslog + - Check for unnecessary log commands + - Generate HTML documentation using doxygen, + - use dotty/gv for state machines of player + - make available online + - Clean up mugglei (abstract code where possible) + - extend mgSelection with all SQL code needed by mugglei.c, maybe something like mgSelection.SyncWithPath(const char *path, options) + - then remove all SQL code from mugglei.c + - Check for memory leaks + - Check for (reasonably) consistent usage of char pointers and strings + - mgPlayer used what for? + - Could save IP/host name and associate last playlist/index loaded \subsection urgentcontent Content handling - - Think, whether type (mp3, ogg, flac) should be stored in database + - Save on exit + - Think, whether type (mp3, ogg, flac) should be stored in database - could be used in searching/structuring as well - - Handle shuffle mode in mgPlaylist - - shuffle mode on - - for next file: - - generate a set of random numbers as long as the playlist - - re-generate when removing or adding entries - - in mgPlaylist::getCurrent use this additional set as a mapping - Party mode (see iTunes) - initialization - find 15 titles according to the scheme below - playing - before entering next title perform track selection + - do not increment the playcount + - if more than 5 titles are found, make sure the same title is not directly repeated - track selection - generate a random uid - if file exists: @@ -48,30 +51,19 @@ - if n < playcount / max. playcount - add the file to the end of the list + \subsection urgentplayer Player extensions + - Possible to resume play instead of restarting list from the beginning? + - Display covers + - Import filename + - Show image during replay + - Add FLAC decoder + - Determine max. framecount (needed for rewinding?) + - Init scale/level/normalize? + - The max. level should be recognized during play + - Store max. level in the database + - Display covers - \subsection urgentcode Code polishing - - Clean up coding style and documentation in general - - Logging - - extend mgLog with static logging methods - - in DEBUG mode, issue logs, warnings, errors to stderr - - otherwise issue errors only to syslog - - Check for unnecessary log commands - - Generate HTML documentation using doxygen, - - use dotty/gv for state machines of player - - make available online - - Clean up mugglei (abstract code where possible) - - Check for memory leaks - - Why do filters use pointers? - - Check for (reasonably) consistent usage of char pointers and strings - - mgDatabase class is not used - - should handle a static object with a MySQL connection - - execute queries - - escape query strings - - mgPlayer used what for? - - Could save IP/host name and associate last playlist/index loaded - - Move differences 1.3.7+- to link level - - \subsection deploy Deployment + \subsection deploy Deployment - Script to publish a version - make dist @@ -118,7 +110,7 @@ - Cover text - Tracks - - Language (?) + - Language (?) - encoded by what standard? - Rating? - Modified, created - Lyrics @@ -128,23 +120,27 @@ \subsection midcode Code issues - - really abstract from specific queries etc. - - mgDatabase should completely abstract database (mySQL) stuff!? - - initialization - - read/write queries - - return results (needs an abstract representation of results?) - - \subsection midosd OSD-related issues - + \subsection midosd OSD-related issues + - can mgMenu and mgMainMenu be combined into one class? + - can mgActions inherit from cOsdItem? - Incremental search - Type numbers to enter characters and jump to first title accordingly - Check whether submenus (as implemented in VDR) are more suitable - do not permit jumping to arbitrary menus though - \subsection midcontent Content issues - - - Save/load filter sets - - Apply filter set as dynamic playlist (i.e. show filters when loading playlists) + \subsection midcontent filter issues + + - new OSD list for filters. Only ONE filter can be active at any time + - filters can be defined recursively, different filters can share subfilters + - Save/load filter set + - table filters and filterrules + - filters: id, name, created, author. Uses id 0 from filterrules. + - filterrules: PRIMARY(filterid, id), negate, operator, op1, op2, + - if operator is AND or OR, op1 and op2 are filterrule ids + - filters are always applied, even to "instant play" and "now playing" + + \subsection midcontent Content issues + - Handle ratings (increase/decrease during replay) - Keys to directly increase - handle a playcounter @@ -153,12 +149,12 @@ - when playfrequency reaches upper level y from above: decrease rating - when playfrequency reaches upper level y from below: increase rating - \subsection midplayer Player issues + \subsection midplayer Player issues - Use single CD files with cuesheets in metadata for FLAC - Handle recoding samplerate, limiter etc correctly - \section vision Long term ideas and visions + \section vision Long term ideas and visions - daapd integration? - netjuke integration? @@ -237,16 +233,5 @@ - Toggle detail/progress view (green) - Track view: all metadata - Playlist view: all tracks (past three, upcoming ones) - - BUG: Red key does not work in Browser submenu - - Test execution of playlist commands - - Resume play instead of restarting list from the beginning when terminating play with blue instead of back - - Save current playlist on exit and restore on start - - Save on exit - - implement storePlaylistAs( string name ) in gdPlaylist - - find id of playlist called name or create it - - delete all entries - - store current entries - - Handle loop mode in mgPlaylist - - Track/progress view settings are now saved in the setup - - BUG: could not delete first track of a playlist -*/ \ No newline at end of file + +*/ diff --git a/gd_content_interface.c b/gd_content_interface.c deleted file mode 100644 index 890f2b6..0000000 --- a/gd_content_interface.c +++ /dev/null @@ -1,1425 +0,0 @@ -/*! - * \file gd_content_interface.c - * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin - * \ingroup giantdisc - * - * \version $Revision: 1.27 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ - * - * Implements main classes of for content items and interfaces to SQL databases - * - * This file implements the following classes - * - GdPlaylist a playlist - * - mgGdTrack a single track (content item). e.g. an mp3 file - * - mgSelection a set of tracks (e.g. a database subset matching certain criteria) - */ - -#define DEBUG - -#include "gd_content_interface.h" - -#include "mg_tools.h" -#include "mg_database.h" -#include "vdr_setup.h" - -#include "i18n.h" - -#define GD_PLAYLIST_TYPE 0 //< listtype for giant disc db - -// some dummies to keep the compiler happy -#define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax -#define DUMMY - -/*! - * \brief initialize a database used by Giantdisc - * - * \todo should be a static function in some Gd class - */ -int GdInitDatabase( MYSQL *db ) -{ - if( mysql_init(db) == NULL ) - { - return -1; - } - - if (the_setup.DbSocket != NULL) - { - mgDebug(1,"Using sockets for connecting to Database."); - - //mgDebug(3,"Socket is: '%s'",the_setup.DbSocket); - //mgDebug(3,"DbUser is: '%s'",the_setup.DbUser); - //mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass); - - if( mysql_real_connect( db, - "", - the_setup.DbUser, - the_setup.DbPass, - the_setup.DbName, - 0, - the_setup.DbSocket, 0 ) == NULL ) - { - return -2; - } // if mysql_real_connect - } //if DbSocket - else - { - mgDebug(1,"Using TCP-host for connecting to Database."); - if( mysql_real_connect( db, - the_setup.DbHost, - the_setup.DbUser, - the_setup.DbPass, - the_setup.DbName, - the_setup.DbPort, - NULL, 0 ) == NULL ) - { - return -2; - } // if mysql_real_connect - } // else (if DbSocket) - - return 0; -} - -std::vector *GdGetStoredPlaylists(MYSQL db) -{ - std::vector* list = new std::vector(); - MYSQL_RES *result; - MYSQL_ROW row; - - result = mgSqlReadQuery(&db, "SELECT title FROM playlist"); - - while( (row = mysql_fetch_row(result) ) != NULL ) - { - list->push_back(row[0]); - } - return list; -} - -gdFilterSets::gdFilterSets() -{ - mgFilter* filter; - std::vector* set; - std::vector* rating; - m_titles.push_back( tr("Track Search") ); - - // create an initial set of filters with empty values - set = new std::vector(); - rating = new std::vector(); - rating->push_back("-"); - rating->push_back("O"); - rating->push_back("+"); - rating->push_back("++"); - - // year-from - filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); - - // year-to - filter = new mgFilterInt(tr("year (to)"), 2099, 1900, 2100); set->push_back(filter); - - // title - filter = new mgFilterString(tr("title"), ""); set->push_back(filter); - - // artist - filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); - - // genre - filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); - - // rating. TODO: Currently buggy. LVW - // filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); - - m_sets.push_back(set); - - m_titles.push_back(tr("Album Search")); - - set = new std::vector(); - // year-from - filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); - // year-to - filter = new mgFilterInt(tr("year (to)"), 2099, 1900, 2100); set->push_back(filter); - // title - filter = new mgFilterString(tr("album title"), ""); set->push_back(filter); - // artist - filter = new mgFilterString(tr("album artist"), ""); set->push_back(filter); - // genre - filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); - // rating - filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); - - m_sets.push_back(set); - - m_titles.push_back(tr("Playlist Search")); - - set = new std::vector(); - // year-from - filter = new mgFilterInt(tr("year (from)"), 1901, 1900, 2100); set->push_back(filter); - // year-to - filter = new mgFilterInt(tr("year (to)"), 2099, 1900, 2100); set->push_back(filter); - // title - filter = new mgFilterString(tr("playlist title"), ""); set->push_back(filter); - // artist - filter = new mgFilterString(tr("playlist author"), ""); set->push_back(filter); - // title - filter = new mgFilterString(tr("title"), ""); set->push_back(filter); - // artist - filter = new mgFilterString(tr("artist"), ""); set->push_back(filter); - // genre - filter = new mgFilterString(tr("genre"), ""); set->push_back(filter); - // rating - filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter); - - m_sets.push_back(set); - - m_activeSetId = 0; - m_activeSet = m_sets[m_activeSetId]; -} - -gdFilterSets::~gdFilterSets() -{ - // everything is done in the destructor of the base class -} - -std::string gdFilterSets::computeRestriction(int *viewPrt) -{ - std::string sql_str = "1"; - - switch( m_activeSetId ) - { - case 0: - { - // tracks (flatlist for mountain man ;-)) - *viewPrt = 100; - } break; - case 1: - { - // album -> tracks - *viewPrt = 101; - } break; - case 2: - { - // playlist -> tracks - *viewPrt = 102; - } break; - default: - { - mgWarning( "Ignoring Filter Set %i", m_activeSetId ); - } break; - } - - for( std::vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); - iter++ ) - { - if( (*iter)->isSet() ) - { - if( strcmp((*iter)->getName(), tr("playlist title") ) == 0 ) - { - sql_str = sql_str + " AND playlist.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp( (*iter)->getName(), tr("playlist author") ) == 0 ) - { - sql_str = sql_str + " AND playlist.author like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("album title")) == 0 ) - { - sql_str = sql_str + " AND album.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("album artist")) == 0 ) - { - sql_str = sql_str + " AND album.artist like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("title")) == 0 ) - { - sql_str = sql_str + " AND tracks.title like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("artist")) == 0 ) - { - sql_str = sql_str + " AND tracks.artist like '%%" - + (*iter)->getStrVal() + "%%'"; - } - else if(strcmp((*iter)->getName(), tr("genre")) == 0 ) - { - sql_str = sql_str + " AND (genre1.genre like '" - + (*iter)->getStrVal() + "'"; - sql_str = sql_str + " OR genre2.genre like '" - + (*iter)->getStrVal() + "')"; - } - else if(strcmp((*iter)->getName(), tr("year (from)")) == 0 ) - { - sql_str = sql_str + " AND tracks.year >= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), tr("year (to)")) == 0 ) - { - sql_str = sql_str + " AND tracks.year <= " + (*iter)->getStrVal(); - } - else if(strcmp((*iter)->getName(), tr("rating")) == 0 ) - { - if ((*iter)->getStrVal() == "-") - { - sql_str = sql_str + " AND tracks.rating >= 0 "; - } - else if ((*iter)->getStrVal() == "O") - { - sql_str = sql_str + " AND tracks.rating >= 1 "; - } - else if ((*iter)->getStrVal() == "+") - { - sql_str = sql_str + " AND tracks.rating >= 2 "; - } - else if ((*iter)->getStrVal() == "++") - { - sql_str = sql_str + " AND tracks.rating >= 3 "; - } - } - else - { - mgWarning( "Ignoring unknown filter %s", (*iter)->getName() ); - } - } - } - mgDebug(1, "Applying sql std::string %s (view=%d)", sql_str.c_str(), *viewPrt ); - return sql_str; -} - -mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack(); - -mgGdTrack::mgGdTrack( int sqlIdentifier, MYSQL dbase ) -{ - m_uniqID = sqlIdentifier; - m_db = dbase; - m_retrieved = false; -} - -mgGdTrack::mgGdTrack(const mgGdTrack& org) -{ - m_uniqID = org.m_uniqID; - m_db = org.m_db; - m_retrieved = org.m_retrieved; - - if( m_retrieved ) - { - m_artist = org.m_artist; - m_title = org.m_title; - m_mp3file = org.m_mp3file; - m_album = org.m_album; - m_genre = org.m_genre; - m_year = org.m_year; - m_rating = org.m_rating; - m_length = org.m_length; - } -} - -mgGdTrack::~mgGdTrack() -{ - // nothing to be done -} - -bool mgGdTrack::readData() -{ - MYSQL_RES *result; - int nrows, nfields; - - // note: this does not work with empty album or genre fields - result = mgSqlReadQuery( &m_db, - "SELECT tracks.artist, album.title, tracks.title, " - "tracks.mp3file, genre.genre, tracks.year, " - "tracks.rating, tracks.length, tracks.samplerate, tracks.channels, tracks.bitrate " - "FROM tracks, album, genre " - "WHERE tracks.id = %d " - "AND album.cddbid = tracks.sourceid AND " - "genre.id = tracks.genre1", - m_uniqID ); - - nrows = mysql_num_rows(result); - nfields = mysql_num_fields(result); - - if( nrows == 0 ) - { - mgWarning( "No entries found \n" ); - return false; - } - else - { - if( nrows > 1 ) - { - mgWarning("mgGdTrack::readData: More than one entry found. Using first entry."); - } - - MYSQL_ROW row = mysql_fetch_row( result ); - - m_artist = row[0]; - m_album = row[1]; - m_title = row[2]; - m_mp3file = std::string( the_setup.ToplevelDir ) + row[3]; - m_genre = row[4]; - - if( sscanf( row[5], "%d", &m_year) != 1 ) - { - mgError("Invalid year '%s' in database", row [5]); - } - - if( row[6] && sscanf( row[6], "%d", &m_rating ) != 1 ) - { - mgError( "Invalid rating '%s' in database", row [6] ); - } - - if( row[7] && sscanf( row[7], "%d", &m_length) != 1 ) - { - mgError( "Invalid duration '%s' in database", row [7]); - } - - if( row[8] && sscanf( row[8], "%d", &m_samplerate ) != 1 ) - { - mgError( "Invalid samplerate '%s' in database", row [7]); - } - - if( row[9] && sscanf( row[9], "%d", &m_channels ) != 1 ) - { - mgError( "Invalid channels '%s' in database", row [7]); - } - - m_bitrate = row[10]; - - } - m_retrieved = true; - return true; -} - -std::string mgGdTrack::getSourceFile() -{ - if( !m_retrieved ) - { - readData(); - } - return m_mp3file; -} - -std::string mgGdTrack::getTitle() -{ - if( !m_retrieved ) - { - readData(); - } - return m_title; -} - -std::string mgGdTrack::getArtist() -{ - if(!m_retrieved) - { - readData(); - } - return m_artist; -} - -int mgGdTrack::getLength() -{ - if( !m_retrieved ) - { - readData(); - } - return m_length; -} - - -std::string mgGdTrack::getLabel(int col) -{ - if( !m_retrieved ) - { - readData(); - } - switch(col) - { - case 0: - return m_title; - case 1: - return m_artist; - case 2: - return m_album; - case 3: - return m_genre; - default: - return ""; - } -} - -std::vector *mgGdTrack::getTrackInfo() -{ - return new std::vector(); -} - -bool mgGdTrack::setTrackInfo(std::vector *info) -{ - return false; -} - -std::string mgGdTrack::getAlbum() -{ - if( !m_retrieved ) - { - readData(); - } - return m_album; -} - -std::string mgGdTrack::getGenre() -{ - if(!m_retrieved) - { - readData(); - } - return m_genre; -} - -int mgGdTrack::getYear() -{ - if(!m_retrieved) - { - readData(); - } - return m_year; -} - -int mgGdTrack::getRating() -{ - if(!m_retrieved) - { - readData(); - } - return m_rating; -} - -int mgGdTrack::getDuration() -{ - if(!m_retrieved) - { - readData(); - } - return m_rating; -} - -int mgGdTrack::getSampleRate() -{ - if(!m_retrieved) - { - readData(); - } - return m_samplerate; -} - -int mgGdTrack::getChannels() -{ - if(!m_retrieved) - { - readData(); - } - return m_channels; -} - -std::string mgGdTrack::getBitrate() -{ - if(!m_retrieved) - { - readData(); - } - return m_bitrate; -} - -std::string mgGdTrack::getImageFile() -{ - return "dummyImg.jpg"; -} - -void mgGdTrack::setTitle(std::string new_title) -{ - m_title = new_title; -} - -void mgGdTrack::setArtist(std::string new_artist) -{ - m_artist = new_artist; -} - -void mgGdTrack::setAlbum(std::string new_album) -{ - m_album = new_album; -} - -void mgGdTrack::setGenre(std::string new_genre) -{ - m_genre = new_genre; -} - -void mgGdTrack::setYear(int new_year) -{ - m_year = new_year; -} - -void mgGdTrack::setRating(int new_rating) -{ - m_rating = new_rating; -} - -bool mgGdTrack::writeData() -{ - mgSqlWriteQuery( &m_db, "UPDATE tracks " - "SET artist=\"%s\", title=\"%s\", year=%d, rating=%d " - "WHERE id=%d", - m_artist.c_str(), m_title.c_str(), - m_year, m_rating, m_uniqID); - return true; -} - -GdTracklist::GdTracklist(MYSQL db_handle, std::string restrictions) -{ - MYSQL_RES *result; - MYSQL_ROW row; - int trackid; - - result = mgSqlReadQuery( &db_handle, - "SELECT tracks.id " - " FROM tracks, album, genre WHERE %s" - " AND album.cddbid=tracks.sourceid " - " AND genre.id=tracks.genre1", - restrictions.c_str()); - - while( ( row = mysql_fetch_row(result) ) != NULL ) - { - // row[0] is the trackid - if(sscanf(row[0], "%d", &trackid) != 1) - { - mgError("Can not extract integer track id from '%s'", - row[0]); - } - m_list.push_back(new mgGdTrack(trackid, db_handle)); - } -} - -GdPlaylist::GdPlaylist(std::string listname, MYSQL db_handle) -{ - MYSQL_RES *result; - MYSQL_ROW row; - int nrows; - - m_db = db_handle; - - // - // check, if the playlist already exists - // - result = mgSqlReadQuery(&m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - listname.c_str()); - nrows = mysql_num_rows(result); - - if( nrows == 0 ) - { - mgDebug(3, "No playlist with name %s found. Creating new playlist\n", - listname.c_str()); - - // create new database entry - mgSqlWriteQuery( &m_db, "INSERT into playlist " - "SET title=\"%s\", author=\"%s\"", - listname.c_str(), - "VDR", // default author - ""); // creates current time as timestamp - m_author = "VDR"; - m_listname = listname; - - // now read thenew list to get the id - result = mgSqlReadQuery( &m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - listname.c_str() ); - nrows = mysql_num_rows(result); - row = mysql_fetch_row(result); - - if( sscanf(row [0], "%d", & m_sqlId) !=1 ) - { - mgError("Invalid id '%s' in database", row [5]); - } - } - else - { // playlist exists, read data - row = mysql_fetch_row(result); - - if( sscanf(row[0], "%d", & m_sqlId) !=1 ) - { - mgError("Invalid id '%s' in database", row [5]); - } - - m_author = row[1]; - m_listname = listname; - - // now read allentries of the playlist and - // write them into the tracklist - insertDataFromSQL(); - - } // end 'else (playlist exists) - - m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists -} - -GdPlaylist::~GdPlaylist() -{ -} - -void GdPlaylist::setListname(std::string name) -{ - m_listname = name; - m_sqlId = -1; -} - -int GdPlaylist::insertDataFromSQL() -{ - MYSQL_RES *result; - MYSQL_ROW row; - mgGdTrack* trackptr; - int id; - int nrows; - - result = mgSqlReadQuery( &m_db, - "SELECT tracknumber, trackid FROM playlistitem " - "WHERE playlist = %d ORDER BY tracknumber", - m_sqlId); - nrows = mysql_num_rows(result); - while( (row = mysql_fetch_row(result) ) != NULL ) - { - // add antry to tracklist - if( sscanf( row[1], "%d", &id ) !=1 ) - { - mgWarning( "Track id '%s' is not an integer. Ignoring \n", row[1] ); - } - else - { - trackptr = new mgGdTrack( id, m_db ); - m_list.push_back( trackptr ); - } - } - return nrows; -} - -bool GdPlaylist::storePlaylist() -{ - std::vector::iterator iter; - int num; - MYSQL_RES *result; - MYSQL_ROW row; - int nrows; - - if( m_listname == " " ) - { - mgWarning("Can not store Tracklist without name"); - return false; - } - - if( m_sqlId >= 0 ) - { - // playlist alreay exists in SQL database - // remove old items first - // cout << " GdPlaylist::storePlaylist: removing items from " << m_sqlId << flush; - - // remove old playlist items from db - mgSqlWriteQuery(&m_db, - "DELETE FROM playlistitem WHERE playlist = %d", - m_sqlId); - } - else - { - // create new database entry - mgSqlWriteQuery(&m_db, "INSERT into playlist " - "SET title=\"%s\", author=\"%s\"", - m_listname.c_str(), - "VDR", // default author - ""); // creates current time as timestamp - m_author = "VDR"; - - // now read the new list to get the id - result = mgSqlReadQuery( &m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - m_listname.c_str()); - nrows = mysql_num_rows(result); - row = mysql_fetch_row(result); - - if( sscanf( row [0], "%d", & m_sqlId ) !=1 ) - { - mgError("Invalid id '%s' in database", row [5]); - } - } - - // add new playlist items to db - for( iter=m_list.begin(), num=0; - iter != m_list.end(); - iter++, num++) - { - mgSqlWriteQuery(&m_db, - "INSERT into playlistitem " - "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", - num, (*iter)->getId(), m_sqlId); - } - return true; -} - -bool GdPlaylist::storeAs( std::string name ) -{ - int id; - MYSQL_ROW row; - MYSQL_RES *result; - - mgDebug( 1, "GdPlaylist::storeAs" ); - - result = mgSqlReadQuery( &m_db, - "SELECT id FROM playlist WHERE title=\"%s\"", - name.c_str() ); - - if( mysql_num_rows(result) ) - { - row = mysql_fetch_row( result ); - mgDebug( 1, "GdPlaylist::storeAs: found playlist" ); - } - else - { - // otherwise create a new database entry - mgSqlWriteQuery( &m_db, "INSERT into playlist SET " - "title=\"%s\", author=\"VDR\"", - name.c_str() ); - - // now read the new list to get the id - result = mgSqlReadQuery( &m_db, - "SELECT id,author FROM playlist where title=\"%s\"", - name.c_str() ); - - row = mysql_fetch_row(result); - mgDebug( 1, "GdPlaylist::storeAs: created playlist" ); - } - - if( sscanf( row [0], "%d", &id ) !=1 ) - { - mgError("Invalid id '%s' in database", row [5]); - } - else - { - // now we know that the playlist 'name' has identifier id - - // remove old playlist items from db - mgSqlWriteQuery( &m_db, - "DELETE FROM playlistitem WHERE playlist = %d", - id ); - - // add new playlist items to db - std::vector::iterator iter; - int num = 0; - for( iter=m_list.begin(), num=0; - iter != m_list.end(); - iter++, num++) - { - mgDebug( 1, "GdPlaylist::storeAs: inserting track" ); - mgSqlWriteQuery(&m_db, - "INSERT into playlistitem " - "SET tracknumber=\"%d\", trackid=\"%d\", playlist=%d", - num, (*iter)->getId(), id ); - } - } - return true; -} - -/*! - * \brief returns the total duration of all songs in the list in seconds - */ -int GdPlaylist::getPlayTime() -{ - //DUMMY - // go over all entries in the playlist and accumulate their playtime - - return 0; -} - -/*! - * \brief returns the duration of all remaining songs in the list in seconds - */ -int GdPlaylist::getPlayTimeRemaining() -{ - //DUMMY - // go over all remaining entries in the playlist and accumulate their - // playtime - // The remaining playtime of the current song is only known by the mplayer - return 0; // dummy -} - -/*! - * \brief constructor - */ -GdTreeNode::GdTreeNode(MYSQL db, int view, std::string filters) - : mgSelectionTreeNode(db, view) -{ - // create a root node - // everything is done in the parent class - m_restriction = filters; - m_view = view; - m_label = tr("Browser"); -} - -GdTreeNode::GdTreeNode( mgSelectionTreeNode* parent, - std::string id, - std::string label, - std::string restriction ) - : mgSelectionTreeNode(parent, id, label) -{ - m_restriction = restriction; - // everything else is done in the parent class -} - -/*! - * \brief destructor - */ -GdTreeNode::~GdTreeNode() -{ - // _children.clear(); -} - -/*! - * \brief checks if this node can be further expandded or not - * \true, if node ia leaf node, false if node can be expanded - */ -bool GdTreeNode::isLeafNode() -{ - if( m_level == 0 ) - { - return false; - } - - switch(m_view) - { - case 1: // artist -> album -> title - if( m_level <= 3 ) - { - return false; - } - break; - case 2: // genre -> artist -> album -> track - if( m_level <= 3 ) - { - return false; - } - break; - case 3: // Artist -> Track - if( m_level <= 2 ) - { - return false; - } - break; - case 4: - if( m_level <= 2 ) - { - return false; - } - break; - case 5: - if( m_level <= 1 ) - { - return false; - } - break; - case 100: - if( m_level <= 0 ) - { - return false; - } - break; - case 101: - if( m_level <= 1 ) - { - return false; - } - break; - case 102: - if( m_level <= 1 ) - { - return false; - } - break; - default: - mgError("View '%d' not yet implemented", m_view); - } - return true; -} - -/*! - * \brief compute children on the fly - * - * \return: true, if the node could be expanded (or was already), false,of - * node can not be expanded any further - * - * retrieves all entries for the next level that satisfy the restriction of - * the current level and create a child-arc for each distinct entry - * - * \todo use asnprintf! - */ -bool GdTreeNode::expand() -{ - MYSQL_ROW row; - MYSQL_RES *result; - int nrows; - int nfields; - char sqlbuff[1024]; /* hope it's big enough ! */ - char idbuf[255]; - int numchild; - - std::string labelfield; // human readable db field for the column to be expanded - std::string idfield; // unique id field for the column to be expanded - std::string new_restriction_field; // field to be restricted by the new level - std::string new_restriction; // complete restriction str for the current child - std::string new_label; - GdTreeNode* new_child; - - std::string tables; // stores the db tables used - -#define FROMJOIN " FROM tracks, genre as genre1, genre as genre2, album WHERE tracks.sourceid=album.cddbid AND genre1.id=tracks.genre1 AND genre2.id=tracks.genre2 AND %s " - - if( m_expanded ) - { - mgWarning("Node already expanded\n"); - return true; - } - - if( m_level == 1 && m_view < 100 ) - { - m_view = atoi( m_id.c_str() ); - } - - mgDebug( 5, "Expanding level %d view %d\n", m_level, m_view ); - if( m_level > 0 ) - { - switch( m_view ) - { - case 1: - { // artist -> album -> title - if( m_level == 1 ) - { - sprintf( sqlbuff, - "SELECT DISTINCT album.artist,album.artist" - FROMJOIN - " ORDER BY album.artist" - , m_restriction.c_str() ); - idfield = "album.artist"; - } - else if( m_level == 2 ) - { // artist -> album - sprintf(sqlbuff, - "SELECT DISTINCT album.title,album.cddbid" - FROMJOIN - " ORDER BY album.title" - , m_restriction.c_str() ); - idfield = "album.cddbid"; - } - else if(m_level == 3) - { // album -> title - sprintf(sqlbuff, - "SELECT tracks.title,tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb" - , m_restriction.c_str() ); - idfield = "tracks.id"; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - } break; - case 2: - { // genre -> artist -> album -> track - if( m_level == 1 ) - { // genre - sprintf(sqlbuff, - "SELECT DISTINCT genre1.genre,tracks.genre1" - FROMJOIN - " ORDER BY genre1.id" - , m_restriction.c_str()); - idfield = "tracks.genre1"; - } - else if( m_level == 2 ) - { // genre -> artist - sprintf(sqlbuff, - "SELECT DISTINCT album.artist,album.artist" - FROMJOIN - " ORDER BY album.artist", - m_restriction.c_str()); - idfield = "album.artist"; - } - else if( m_level == 3 ) - { // genre -> artist -> album - sprintf(sqlbuff, - "SELECT DISTINCT album.title,tracks.sourceid" - FROMJOIN - " ORDER BY album.title" - , m_restriction.c_str()); - idfield = "tracks.sourceid"; - } - else if( m_level == 4 ) - { // genre -> artist -> album -> track - sprintf(sqlbuff, - "SELECT DISTINCT tracks.title, tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb" - , m_restriction.c_str()); - idfield = "tracks.id"; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - } break; - case 3: - { // Artist -> Track - if( m_level ==1 ) - { - sprintf( sqlbuff, - "SELECT DISTINCT tracks.artist,tracks.artist" - FROMJOIN - " ORDER BY tracks.artist", - m_restriction.c_str()); - idfield = "tracks.artist"; - } - else if( m_level == 2) - { // Track - sprintf(sqlbuff, - "SELECT DISTINCT tracks.title,tracks.id" - FROMJOIN - " ORDER BY tracks.title", - m_restriction.c_str()); - idfield = "tracks.id"; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - } break; - case 4: - { // Genre -> Year -> Track - if( m_level == 1 ) - { // Genre - sprintf(sqlbuff, - "SELECT DISTINCT genre1.genre,tracks.genre1" - FROMJOIN - " ORDER BY genre1.genre", - m_restriction.c_str()); - idfield = "tracks.genre1"; - } - else if (m_level == 2) - { // Year - sprintf(sqlbuff, - "SELECT DISTINCT tracks.year,tracks.year" - FROMJOIN - " ORDER BY tracks.year" - , m_restriction.c_str()); - idfield = "tracks.year"; - } - else if( m_level == 3 ) - { // Track - sprintf(sqlbuff, - "SELECT DISTINCT" - " CONCAT(tracks.artist,' - ',tracks.title) AS title" - " ,tracks.id" - FROMJOIN - " ORDER BY title", - //" ORDER BY tracks.title", - m_restriction.c_str()); - idfield = "tracks.id"; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - } break; - case 5: // Album -> Tracks - if( m_level == 1 ) - { // Album - sprintf(sqlbuff, - "SELECT DISTINCT" - " CONCAT(album.artist,' - ',album.title) AS title," - " album.cddbid" - FROMJOIN - " ORDER BY title" - , m_restriction.c_str()); - idfield = "tracks.sourceid"; - } - else if (m_level == 2) - { // Track - sprintf(sqlbuff, - "SELECT DISTINCT tracks.title, tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb", - m_restriction.c_str()); - idfield = "tracks.id"; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - break; - case 100: - if (m_level == 1) - { - sprintf(sqlbuff, - "SELECT CONCAT(tracks.artist,' - ',tracks.title)," - " tracks.id" - FROMJOIN - " ORDER BY CONCAT(tracks.artist,' - ',tracks.title)" - , m_restriction.c_str()); - idfield = "tracks.id"; - } - else - { - mgWarning( "View #%d level %d' not yet implemented", m_view, m_level ); - m_expanded = false; - return false; - } - break; - case 101: - { // Albumsearch result - if( m_level == 1 ) - { - sprintf(sqlbuff, - "SELECT DISTINCT" - " CONCAT(album.artist,' - ',album.title) as title," - " album.cddbid" - FROMJOIN - " ORDER BY CONCAT(album.artist,' - ',album.title)", - m_restriction.c_str()); - idfield = "tracks.sourceid"; - } - else if( m_level == 2 ) - { - sprintf(sqlbuff, - "SELECT tracks.title,tracks.id" - FROMJOIN - " ORDER BY tracks.tracknb", - m_restriction.c_str()); - idfield = "tracks.id"; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - } break; - case 102: - { - if (m_level == 1) - { - sprintf(sqlbuff, - "SELECT DISTINCT playlist.title," - " playlist.id" - " FROM playlist,playlistitem,tracks,genre as genre1,genre as genre2" - " WHERE playlist.id=playlistitem.playlist AND" - " playlistitem.trackid=tracks.id AND" - " genre1.id=tracks.genre1 AND" - " genre2.id=tracks.genre2 AND" - " %s" - " ORDER BY playlist.title,", - m_restriction.c_str()); - idfield = "playlist.id"; - } - else if (m_level == 2) - { - sprintf(sqlbuff, - "SELECT CONCAT(tracks.artist,' - ',tracks.title)," - " tracks.id" - " FROM playlist,playlistitem,tracks" - " WHERE playlist.id=playlistitem.playlist AND" - " playlistitem.trackid=tracks.id AND" - " %s" - " ORDER BY playlistitem.tracknumber", - m_restriction.c_str()); - idfield = "tracks.id"; - } - else - { - mgWarning("View #%d level %d' not yet implemented", m_view, m_level); - m_expanded = false; - return false; - } - } break; - default: - { - mgError("View '%d' not yet implemented", m_view); - } - } - - // now get all childrean of the current node fromthe database - result = mgSqlReadQuery( &m_db, sqlbuff ); - nrows = mysql_num_rows( result ); - nfields = mysql_num_fields(result); - - numchild = 1; - while( (row = mysql_fetch_row(result) ) != NULL ) - { - // row[0] is the printable label for the new child - // row[1] is the unique id for the new child - sprintf( idbuf, "%s_%03d", m_id.c_str(), numchild ); - - // Zweite ebene zeigt alle Tracks des Albums und nicht nur - // diese die den Filterkriterien entsprechen. - // das betrifft nur die Search Views! - - std::string row0 = mgDB::escape_string( &m_db, std::string( row[0] ) ); - std::string row1 = mgDB::escape_string( &m_db, std::string( row[1] ) ); - - if( m_view < 100 ) - { - new_restriction = m_restriction + " AND " - + idfield + "='" + row1 + "'"; - } - else - { - new_restriction = idfield + "='" + row1 + "'"; - } - - new_child = new GdTreeNode(this, // parent - (std::string) idbuf, // id - // row[0], // label, - row0, - new_restriction); - m_children.push_back(new_child); - numchild++; - } - } - else if (m_view <100) - { - new_child = new GdTreeNode(this, // parent - "1" , // id - tr("Artist -> Album -> Track"), // label, - m_restriction); - m_children.push_back(new_child); - new_child = new GdTreeNode(this, // parent - "2" , // id - tr("Genre -> Artist -> Album -> Track") , // label, - m_restriction); - m_children.push_back(new_child); - new_child = new GdTreeNode(this, // parent - "3" , // id - tr("Artist -> Track") , // label, - m_restriction); - m_children.push_back(new_child); - new_child = new GdTreeNode(this, // parent - "4" , // id - tr("Genre -> Year -> Track") , // label, - m_restriction); - m_children.push_back(new_child); - new_child = new GdTreeNode(this, // parent - "5" , // id - tr("Album -> Track") , // label, - m_restriction); - m_children.push_back(new_child); - } - else - { - new_child = new GdTreeNode(this, // parent - "" , // id - tr("Search Result"), // label, - m_restriction); - m_children.push_back(new_child); - } - - m_expanded = true; - mgDebug(5, "%d children expanded\n", m_children.size()); - return true; -} - -/*! - * \brief iterate all children recursively to find the tracks - */ -std::vector* GdTreeNode::getTracks() -{ - MYSQL_ROW row; - MYSQL_RES *result; - int nrows; - int nfields; - std::vector* retlist; - int trackid; - - retlist = new std::vector(); - - // get all tracks satisying the restrictions of this node - mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str()); - - result = mgSqlReadQuery(&m_db, - "SELECT tracks.id FROM tracks, album, genre WHERE %s" - " AND album.cddbid=tracks.sourceid AND genre.id=tracks.genre1", - m_restriction.c_str()); - nrows = mysql_num_rows(result); - nfields = mysql_num_fields(result); - - while((row = mysql_fetch_row(result)) != NULL) - { - // row[0] is the trackid - if(sscanf(row[0], "%d", &trackid) != 1) - { - mgError("Can not extract integer track id from '%s'", - row[0]); - } - retlist->push_back(new mgGdTrack(trackid, m_db)); - } - return retlist; -} - - -/*! - ***************************************************************************** - * \brief returns the first track matchin the restrictions of this node - * assuming we are in a leaf node, this returns the track represented by the - * the leaf - ****************************************************************************/ -mgContentItem* GdTreeNode::getSingleTrack() -{ - MYSQL_ROW row; - MYSQL_RES *result; - int nrows; - int nfields; - mgContentItem* track = NULL; - int trackid; - - // get all tracks satisying the restrictions of this node - mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str()); - - result = mgSqlReadQuery(&m_db, - "SELECT tracks.id FROM tracks, album, genre WHERE %s" - " AND album.cddbid=tracks.sourceid AND genre.id=tracks.genre1", - m_restriction.c_str()); - nrows = mysql_num_rows(result); - nfields = mysql_num_fields(result); - - if( nrows != 1 ) - { - mgWarning( "GdTreeNode::getSingleTrack() :SQL call returned %d tracks, using only the first", - nrows ); - } - // get the first row - if( ( row = mysql_fetch_row(result)) != NULL ) - { - // row[0] is the trackid - if(sscanf(row[0], "%d", &trackid) != 1) - { - mgError("Can not extract integer track id from '%s'", - row[0]); - } - track = new mgGdTrack(trackid, m_db); - } - return track; -} - diff --git a/gd_content_interface.h b/gd_content_interface.h deleted file mode 100644 index 8823799..0000000 --- a/gd_content_interface.h +++ /dev/null @@ -1,582 +0,0 @@ -/*! - * \file gd_content_interface.h - * \brief Data objects for content (e.g. mp3 files, movies) - * for the vdr muggle plugin database - * \ingroup giantdisc - * - * \version $Revision: 1.11 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ - * - * Declares main classes for content items and interfaces to SQL databases - * - * This file defines the following classes - * - gdFilterSets: filters to specifically search for GD items - * - mgGdTrack: a single track (content item). e.g. an mp3 file - * - GdTracklist: - * - GdPlaylist: - * - GdTreeNode: - */ - -#ifndef _GD_CONTENT_INTERFACE_H -#define _GD_CONTENT_INTERFACE_H - -#include -#include - -#include - -#include "mg_content_interface.h" -#include "mg_media.h" -#include "mg_playlist.h" -#include "mg_filters.h" -#include "i18n.h" - -/*! - * \brief Initialize a database for GD use - * \ingroup giantdisc - * \todo Should be a static member of some GD class - */ -int GdInitDatabase(MYSQL *db); - -/*! - * \brief Obtain the playlists stored within the GD schema - * \ingroup giantdisc - * \todo Should be a static member of some GD class - */ -std::vector *GdGetStoredPlaylists(MYSQL db); - -/*! - * \brief A set of filters to search for content - * \ingroup giantdisc - */ -class gdFilterSets : public mgFilterSets -{ - - public: - - //\@{ - - /*! - * \brief the default constructor - * - * Constructs a number ( >= 1 ) of filter sets where - * the first (index 0) is active by default. - */ - gdFilterSets(); - - /*! - * \brief the destructor - */ - virtual ~gdFilterSets(); - - //\@ - - /*! - * \brief compute restriction w.r.t active filters - * - * Computes the (e.g. sql) restrictions specified by - * the active filter sets. - * - * \param viewPort - after call, contains the index of the appropriate default view in - * \return sql string representing the restrictions - * \todo should viewPort be a reference? - */ - virtual std::string computeRestriction(int *viewPort); -}; - - -/*! - * \brief represents a a single track - * \ingroup giantdisc - * - * This may be any content item. e.g. a mp3 fileselection - * The object is initially created with a database identifier. - * The actual data is only read when a content field is accessed for - * the first time. For subsequent access, cached values are used. - * - * \todo does each track node need a reference to the database? - * maybe we can use a static db handle in mgDatabase? - */ -class mgGdTrack : public mgContentItem -{ - public: - - //@{ - - /*! - * \brief a constructor - * - * Creates an invalid item. - * - * \todo does this make sense? used anywhere? - */ - mgGdTrack() - { - m_uniqID = -1; - } - - /*! - * \brief a constructor for a specific item - * - * The constructor creates a specific item in a given database - * On creation, the object is only a wrapper without data. Actual - * data fields are filled when readData() is called for the first time. - * - * \param sqlIdentifier - a unique ID of the item which will be represented - * \param dbase - the database in which the item exists - */ - mgGdTrack( int sqlIdentifier, MYSQL dbase ); - - /*! - * \brief a copy constructor - */ - mgGdTrack(const mgGdTrack&); - - /*! - * \brief the destructor - */ - virtual ~mgGdTrack(); - - //@} - - /*! - * \brief obtain the content type of the item - */ - virtual mgContentItem::contentType getContentType() - { - return mgContentItem::GD_AUDIO; - } - - /*! - * \brief obtain the associated player object - * - * \todo what is this used for? - */ - virtual mgMediaPlayer getPlayer() - { - return mgMediaPlayer(); - } - - //\@{ - - /*! - * \brief returns a certain field of the item as a string - * - * 0 - title - * 1 - artist - * 2 - album - * 3 - genre - */ - virtual std::string getLabel( int col = 0 ); - - /*! - * \brief returns value for the track title - */ - virtual std::string getTitle(); - - /*! - * \brief returns value for the location of the track on disk - */ - virtual std::string getSourceFile(); - - /*! - * \brief returns the genre of the track - */ - virtual std::string getGenre(); - - /*! - * \brief returns the artist of the track - */ - std::string getArtist(); - - /*! - * \brief returns value the album to which the track belongs - */ - std::string getAlbum(); - - /*! - * \brief obtain the location of the image file - */ - std::string getImageFile(); - - /*! - * \brief obtain the year of the track - */ - int getYear(); - - /*! - * \brief obtain the duration of the track - */ - int getDuration(); - - /*! - * \brief obtain the rating of the track - */ - virtual int getRating(); - - /*! \brief obtain the samplerate of the track - */ - virtual int getSampleRate(); - - /*! \brief obtain the number of audio channels of the track - */ - virtual int getChannels(); - - /*! \brief obtain the bitrate of the track - */ - virtual std::string getBitrate(); - - /*! \brief obtain the bitrate of the track - */ - virtual int getLength(); - - /*! - * \brief obtain the complete track information - */ - virtual std::vector *getTrackInfo(); - - //\@} - - //\@{ - /*! - * \brief set the title of the track - */ - void setTitle(std::string new_title); - - /*! - * \brief set the title of the track - */ - void setArtist(std::string new_artist); - - /*! - * \brief set the album name of the track - */ - void setAlbum(std::string new_album); - - /*! - * \brief set the genre of the track - */ - void setGenre(std::string new_genre); - - /*! - * \brief set the year of the track - */ - void setYear(int new_year); - - /*! - * \brief set the rating of the track - */ - void setRating(int new_rating); - - /*! - * \brief set complete information of the track - */ - virtual bool setTrackInfo(std::vector*); - - /*! - * \brief make changes persistent - * - * The changes made using the setXxx methods are not - * stored persistently in the database until writeData - * is called. - * - * \note only the tracks table in the SQL database is updated. - * Genre and album field information is lost. - */ - bool writeData(); - //\@} - - //! \brief a special instance denoting an undefined track - static mgGdTrack UNDEFINED; - -private: - - /*! - * \brief the database in which the track resides - */ - MYSQL m_db; - - /*! - * \brief a dirty flag - * - * false, if content field values have not yet been retrieved - * from the database. Set to true when contents are retrieved - * (on demand only). - */ - bool m_retrieved; - - /*! - * \brief the artist name - */ - std::string m_artist; - - /*! - * \brief the track title - */ - std::string m_title; - - /*! - * \brief the filename - */ - std::string m_mp3file; - - /*! - * \brief The album to which the file belongs - */ - std::string m_album; - - /*! - * \brief The genre of the music - */ - std::string m_genre; - - /*! - * \brief The bitrate of the music - */ - std::string m_bitrate; - - /*! - * \brief The year in which the track appeared - */ - int m_year; - - /*! - * \brief The rating by the user - */ - int m_rating; - - /*! - * \brief The length of the track in seconds - */ - int m_length; - - /*! - * \brief The sampling rate of the track in Hz - */ - int m_samplerate; - - /*! - * \brief The number of channels of the track - */ - int m_channels; - - /*! - * \brief Access the data of the item from the database and fill actual data fields - * - * In order to avoid abundant queries to the database, the content fields - * of the mgGdTrack object may not be filled upon creation. As soon as the - * first content field is needed, this private function is called to fill - * all content fields at once. - */ - bool readData(); - -}; - -/* - * \brief a list of tracks stored in the databas - * \ingroup giantdisc - */ -class GdTracklist : public mgTracklist -{ - public: - - GdTracklist(MYSQL db_handle, std::string restrictions); -}; - -/*! - * \class GdPlaylist - * - * \brief represents a playlist, i.e. an ordered collection of tracks - */ -class GdPlaylist : public mgPlaylist -{ - public: - - //@{ - /*! - * \brief opens an existing or creates empty playlist by name - * - * If the playlist does not yet exist, an empty playlist is created. - * - * \param listname - user-readable identifier of the paylist - * \param db_handle - database which stores the playlist - */ - GdPlaylist(std::string listname, MYSQL db_handle); - - /*! - * \brief destructor - * - * \note the destructor only destroys the in-memory footprint of the playlist, - * it does not modify the database. - */ - virtual ~GdPlaylist(); - - //@} - - /*! - * changes the listname of the playlist (and unset the sql id) - */ - virtual void setListname(std::string name); - - /*! - * returns the total duration of all songs in the list in seconds - */ - int getPlayTime(); - - /*! - * returns the duration of all remaining songs in the list in seconds - */ - int getPlayTimeRemaining(); - - /*! - * \brief write data back to the database - */ - bool storePlaylist(); - - /*! - * \brief store playlist under a different name - */ - bool storeAs( std::string name ); - - private: - - /*! - * \brief reads the track list from the sql database into memory - */ - int insertDataFromSQL(); - - //! \brief the database identifier of the list - int m_sqlId; - - //! \brief the list type used in GiantDisc db queries - int m_listtype; - - //! \brief the author of the playlist - std::string m_author; - - //! \brief the handle to the database in which the list is stored - MYSQL m_db; -}; - -/*! - * \brief hierarchical representation of a set of tracks - * \ingroup giantdisc - * - * The selection can be based on the whole database or a subset of it. - * Within this selection, the data is organized in a tree hierarchy - * The levels hof the hierarchy can be expanded dynamically by specifying - * the database field for the next expansion step - * In this way, the expnasion scheme (order of the fields) is not static. - * When a node is expanded, a list of children is created. - * Each child inherits the restrictions of its father and an additional - * restriction on the recently expanded db field - */ -class GdTreeNode : public mgSelectionTreeNode -{ -public: - - //@{ - /*! - * \brief Construct a top level tree node in a certain view with certain filters - */ - GdTreeNode(MYSQL db, int view, std::string filters); - - /*! - * \brief Construct a subordinate tree node with given labels and restrictions - */ - GdTreeNode(mgSelectionTreeNode* parent, - std::string id, std::string label, std::string restriction); - - /*! - * \brief destructor to clean up the object - */ - virtual ~GdTreeNode(); - //@} - - //@{ - /*! - * \brief check, whether the node is a leaf node (i.e. has no more children) - */ - virtual bool isLeafNode(); - - /*! - * \brief expand the node and generate child nodes according to restrictions passed in the constructor - */ - virtual bool expand(); - - /*! - * \brief obtain the tracks in this node - */ - virtual std::vector* getTracks(); - - /*! - * \brief obtain a single track in this node - */ - virtual mgContentItem* getSingleTrack(); - - //@} -}; - -/* -------------------- begin CVS log --------------------------------- - * $Log: gd_content_interface.h,v $ - * Revision 1.11 2004/08/30 14:31:43 LarsAC - * Documentation added - * - * Revision 1.10 2004/08/27 15:19:34 LarsAC - * Changed formatting and documentation - * - * Revision 1.9 2004/07/29 06:17:50 lvw - * Added todo entries - * - * Revision 1.8 2004/07/06 00:20:51 MountainMan - * loading and saving playlists - * - * Revision 1.7 2004/05/28 15:29:18 lvw - * Merged player branch back on HEAD branch. - * - * - * Revision 1.6 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.5 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.4.2.6 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.4.2.5 2004/04/01 21:35:32 lvw - * Minor corrections, some debugging aid. - * - * Revision 1.4.2.4 2004/03/14 17:57:30 lvw - * Linked against libmad. Introduced config options into code. - * - * Revision 1.4.2.3 2004/03/10 13:11:24 lvw - * Added documentation - * - * Revision 1.4.2.2 2004/03/08 07:14:27 lvw - * Preliminary changes to muggle player - * - * Revision 1.4.2.1 2004/03/02 07:05:50 lvw - * Initial adaptations from MP3 plugin added (untested) - * - * Revision 1.6 2004/02/23 15:41:21 RaK - * - first i18n attempt - * - * Revision 1.5 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.4 2004/02/09 19:27:52 MountainMan - * filter set implemented - * - * Revision 1.3 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ -#endif /* END _GD_CONTENT_INTERFACE_H */ - - - diff --git a/i18n.c b/i18n.c index 4332a20..db5e673 100644 --- a/i18n.c +++ b/i18n.c @@ -9,839 +9,1130 @@ #include "i18n.h" -const tI18nPhrase Phrases[] = { +const tI18nPhrase Phrases[] = +{ - { "items", - "Einträge", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Notation",// Traduction en Français Patrice Staudt - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Muggle Media Database", - "Muggle Media Database", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Base de donnée Muggle Media",// TODO Francais - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Tree View Commands", - "Browser Befehle", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Commande navigateur",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Search Result", - "Suchergebnis", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Résultat de recherche",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Album -> Track", - "Titel nach Album", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Titre après albume",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Genre -> Year -> Track", - "Titel nach Genre und Jahr", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Genre -> Année -> Titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Artist -> Track", - "Titel nach Interpret", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "interprète -> titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Genre -> Artist -> Album -> Track", - "Album nach Genre und Interpret", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Genre -> interprète -> album -> Titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Artist -> Album -> Track", - "Album nach Interpret", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "interprète -> album -> Titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Browser", - "Browser", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Navigateur",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Other Search", - "Suchmodus", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Mode rechercher",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Query", - "Suche", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Rechercher",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Album info", - "Album Details", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Infos Albume",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Edit PL?", - "Edit PL?", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Édition PL?",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Filter", - "Filter", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Filtre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Track info", - "Titel Details", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Détails trites",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Edit PL", - "Edit PL", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Édition PL",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Track Search", - "Titel Suche", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Chercher titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Album Search", - "Album Suche", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Chercher albume",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Playlist Search", - "Playlist Suche", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Chercher playlist",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "playlist title", - "Playlist Titel", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Playlist titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "playlist author", - "Playlist Author", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Playlist auteur",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "album artist", - "Albuminterpret", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Albume interpret",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "album title", - "Albumtitel", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Albume titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "rating", - "Bewertung", - "",// TODO - "",// TODO - "",// TODO - "estimation",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "year (to)", - "Jahr (bis)", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Année (à)",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "year (from)", - "Jahr (von)", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Année (de)",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "genre", - "Genre", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Genre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "artist", - "Interpret", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "interprète",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "title", - "Titel", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "titre",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Add", - "Hinzu", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "ajouter",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Cycle tree", - "Browser Modus", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Mode Navigateur",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Playlist", - "Playliste", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Playlist",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Submenu", - "Untermenü", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "sous menu",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Load", - "Laden", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "charger",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Save", - "Speichern", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "sauvegarder",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Clear", - "Loeschen", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "effacer",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Mainmenu", - "Hauptmenue", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Menu principal",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "%d tracks sent to current playlist", - "%d Titel in aktuelle Playlist?", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "%d mettre titre dans la playlist actuel?",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Load playlist", - "Playliste laden", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Charger playlist",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Save playlist", - "Playliste speichern", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "sauvegarder playlist",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Rename playlist", - "Playliste umbenennen", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Playliste umbenennen",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Clear playlist", - "Playliste leeren", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Playliste vider",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Remove entry from list", - "Eintrag aus der Playliste entfernen", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Supprimer de la list",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Export playlist", - "Playliste exportieren", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Exporter la playlist",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "External playlist commands", - "Externe Playlist-Kommandos", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "commande externe playlist",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Loop mode off", - "Endlosmodus aus", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Déclancher le mode répétition",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Loop mode single", - "Endlosmodus Einzeltitel", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Mode répétition titre seul",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Loop mode full", - "Endlosmodus Playliste", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Mode répétition playlist",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Shuffle mode off", - "Zufallssmodus aus", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "mode allèatoire déclenché",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Shuffle mode normal", - "Zufallssmodus normal", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Mode allèatoire normal",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { "Shuffle mode party", - "Zufallssmodus Party", - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "Mode allèatoire fêtes",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - "",// TODO - }, - { NULL } - }; + { + "Search", + "Suchen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Chercher", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Collections", + "Sammlungen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Collections", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Create collection", + "Sammlung neu anlegen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Créer une nouvelle collection", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Delete collection", + "Sammlung löschen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer la collection", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Delete collection '%s'", + "Sammlung '%s' löschen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer la collection '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Collections", + "Sammlungen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Collections", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Commands", + "Befehle", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Commandes", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Commands:%s", + "Befehle:%s", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Commandes:%s", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Search Result", + "Suchergebnis", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Résultat de recherche", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Album -> Track", + "Titel nach Album", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Titre après albume", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Genre -> Year -> Track", + "Titel nach Genre und Jahr", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Genre -> Année -> Titre", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Artist -> Track", + "Titel nach Interpret", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "interprète -> titre", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Genre -> Artist -> Album -> Track", + "Album nach Genre und Interpret", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Genre -> interprète -> album -> Titre", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Artist -> Album -> Track", + "Album nach Interpret", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "interprète -> album -> Titre", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Collection", + "Sammlung", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Collection", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "List", + "Liste", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Liste", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Export track list", + "Stückliste exportieren", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Exporter la liste", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "External playlist commands", + "Externe Playlist-Kommandos", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "commande externe playlist", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Loop mode off", + "Endlosmodus aus", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Déclancher le mode répétition", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Loop mode single", + "Endlosmodus Einzeltitel", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Mode répétition titre seul", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Loop mode full", + "Endlosmodus alle", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Mode répétition playlist", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Shuffle mode off", + "Zufallsmodus aus", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "mode allèatoire déclenché", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Shuffle mode normal", + "Zufallsmodus normal", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Mode allèatoire normal", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Artist", + "Interpret", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Interprète", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Play all", + "Spiele alles", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Jouer tout", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Set", + "Setzen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Définir", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Instant play", + "Sofort spielen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Jouer en direct", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Instant play '%s'", + "'%s' sofort spielen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Jouer '%s' en direct", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Shuffle mode party", + "Zufallsmodus Party", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Mode allèatoire fêtes", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Default", + "Ziel", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Destinataire", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Set default to collection '%s'", + "Setze Ziel auf Sammlung '%s'", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Changer destination à la collection '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Default collection now is '%s'", + "Zielsammlung ist nun '%s'", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "La collection destinataire est maintenant '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Add", + "Hinzu", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ajouter", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Add to '%s'", + "Zu '%s' hinzufügen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ajouter à '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Add '%s' to '%s'", + "'%s' zu '%s' hinzufügen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ajouter '%s' à '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Add all to '%s'", + "Alles '%s' hinzufügen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ajouter tout à '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Remove", + "Entfernen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Remove from '%s'", + "Aus '%s' entfernen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer de '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Remove '%s' from '%s'", + "'%s' aus '%s' entfernen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer '%s' de '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Remove all entries from '%s'", + "Alle Einträge aus '%s' entfernen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer tout de '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Remove all from '%s'", + "Alles aus '%s' entfernen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer tout de '%s'", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "New collection", + "Neue Sammlung anlegen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ajouter une collection", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Remove this collection", + "Diese Sammlung entfernen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer cette collection", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Remove entry from this collection", + "Eintrag aus dieser Sammlung entfernen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer de cette collection", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Added %s entries", + "%s Einträge hinzugefügt", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ajouté %s pièces", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Removed %s entries", + "%s Einträge entfernt", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacé %s pièces", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Removed all entries", + "Alle Einträge entfernt", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacé toutes les pièces", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Now playing", + "Jetzt wird gespielt", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "En jouant", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Rating", + "Bewertung", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Decade", + "Dekade", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Décade", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Year", + "Jahr", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Année", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Album", + "Album", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Album", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Genre 1", + "Genre 1", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Genre 1", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Genre 2", + "Genre 2", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Genre 2", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Title", + "Titel", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Titre", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Track", + "Track", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Pièce", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Tree View Selection", + "Suchschema wählen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Choisir le schéma de recherche", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Title -> Album -> Track", + "Titel -> Album -> Track", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Titre -> Album -> Pièce", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Collection -> Item", + "Sammlung - Stück", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Collectin -> Pièce", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Genre -> Decade -> Artist -> Album -> Track", + "Genre -> Dekade -> Interpret -> Album -> Track", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Genre -> Décade -> Interprète -> Album -> Pièce", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Search", + "Suche", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Chercher", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "play", + "spielen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "jouer", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Collection item", + "Sammlungseintrag", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Pièce de collection", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Collection '%s' NOT deleted", + "Sammlung '%s' NICHT gelöscht", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Collection '%s' PAS effacée", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Collection '%s' deleted", + "Sammlung '%s' gelöscht", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Collection '%s' effacée", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Select search order", + "Suchschema wählen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Choisir le schéma de recherchage", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + {NULL} +}; diff --git a/i18n.h b/i18n.h index 8ea1157..884adc2 100644 --- a/i18n.h +++ b/i18n.h @@ -13,5 +13,4 @@ #include extern const tI18nPhrase Phrases[]; - -#endif //_I18N__H +#endif //_I18N__H diff --git a/mg_actions.c b/mg_actions.c new file mode 100644 index 0000000..d9e45af --- /dev/null +++ b/mg_actions.c @@ -0,0 +1,986 @@ +/*! + * \file mg_actions.c + * \brief Implements all actions for browsing media libraries within VDR + * + * \version $Revision: 1.27 $ * \date $Date: 2004-12-25 16:52:35 +0100 (Sat, 25 Dec 2004) $ + * \author Wolfgang Rohdewald + * \author Responsible author: $Author: wr61 $ + * + * $Id: mg_actions.c 276 2004-12-25 15:52:35Z wr61 $ + */ + +#include +#include +#include + +#include +#include +#include + +#include "vdr_setup.h" +#include "mg_actions.h" +#include "vdr_menu.h" +#include "i18n.h" +#include + +#define DEBUG +#include "mg_tools.h" + +void +mgAction::Notify() +{ + m->SetHelpKeys(); +} + +void +mgAction::SetMenu(mgMenu *menu) +{ + m = menu; +} + +eOSState +mgAction::Back() +{ + return osUnknown; +} + +bool +mgAction::Enabled() +{ + return true; +} + +mgAction::mgAction() +{ + m = NULL; +} + +mgAction::~mgAction() +{ +} + + +//! \brief used for normal data base items +class mgEntry : public mgOsdItem +{ + public: + void Notify(); + bool Enabled() { return true;} + const char *ButtonName() { return ""; } + const char *MenuName (const unsigned int idx,const string value); + eOSState ProcessKey(eKeys key); + void Execute(); + eOSState Back(); +}; + +class mgCommand : public mgOsdItem +{ + public: + virtual eOSState ProcessKey(eKeys key); + void Execute(); +}; + + +mgSelection* +mgAction::playselection () +{ + return m->playselection (); +} +mgMainMenu* +mgAction::osd () +{ + return m->osd (); +} + +eOSState +mgOsdItem::Back() +{ + osd()->newmenu = NULL; + return osContinue; +} + + +void +mgEntry::Notify() +{ + selection()->setPosition(osd()->Current()); + selection()->gotoPosition(); + osd()->SaveState(); + mgAction::Notify(); +} + +const char * +mgEntry::MenuName(const unsigned int idx,const string value) +{ + char *result; + if (selection()->isCollectionlist()) + { + if (value == osd()->default_collection) + asprintf(&result,"-> %s",value.c_str()); + else + asprintf(&result," %s",value.c_str()); + } + else if (selection()->inCollection()) + asprintf(&result,"%4d %s",idx,value.c_str()); + else + result = strdup(value.c_str()); + return result; +} + +void +mgEntry::Execute() +{ + if (selection ()->enter ()) + { + osd()->forcerefresh = true; + osd()->m_Status->IgnoreNextEventOn = this; + } + else + { + m->ExecuteAction(actInstantPlay); + } +} + +eOSState +mgEntry::ProcessKey(eKeys key) +{ + if (key!=kNone) + mgDebug(3,"mgEntry(%s):ProcessKey(%d)",Text(),(int)key); + + switch (key) { + case kOk: + Execute(); + return osContinue; + case kBack: + osd()->m_Status->IgnoreNextEventOn = this; + return Back(); + case kBlue: + osd ()->newmenu = new mgSubmenu; + return osContinue; + default: + return osUnknown; + } +} + + +eOSState +mgEntry::Back() +{ + osd()->forcerefresh = true; + if (!selection ()->leave ()) + osd()->newmenu = NULL; + return osContinue; +} + +eOSState +mgCommand::ProcessKey(eKeys key) +{ + if (key!=kNone) + mgDebug(3,"mgCommand::ProcessKey(%d)",(int)key); + mgMenu *parent = osd ()->Parent (); + mgMenu *n = osd ()->newmenu; + osd ()->newmenu = NULL; + eOSState result = osContinue; + switch (key) + { + case kRed: + if (osd()->UsingCollection) + parent->CollRedAction = Type(); + else + parent->TreeRedAction = Type(); + break; + case kGreen: + if (osd()->UsingCollection) + parent->CollGreenAction = Type(); + else + parent->TreeGreenAction = Type(); + break; + case kYellow: + if (osd()->UsingCollection) + parent->CollYellowAction = Type(); + else + parent->TreeYellowAction = Type(); + break; + case kBlue: + case kBack: + break; + case kOk: + Execute (); + break; + default: + osd ()->newmenu = n; // wrong key: stay in submenu + result = osUnknown; + break; + } + return result; +} + +void +mgCommand::Execute() +{ +} + +class mgExternal : public mgCommand +{ + public: + const char *ButtonName(); + const char *MenuName (const unsigned int idx,const string value); + void Execute(); + private: + cCommand * Command(); +}; + +class mgExternal0 : public mgExternal { }; +class mgExternal1 : public mgExternal { }; +class mgExternal2 : public mgExternal { }; +class mgExternal3 : public mgExternal { }; +class mgExternal4 : public mgExternal { }; +class mgExternal5 : public mgExternal { }; +class mgExternal6 : public mgExternal { }; +class mgExternal7 : public mgExternal { }; +class mgExternal8 : public mgExternal { }; +class mgExternal9 : public mgExternal { }; +class mgExternal10 : public mgExternal { }; +class mgExternal11 : public mgExternal { }; +class mgExternal12 : public mgExternal { }; +class mgExternal13 : public mgExternal { }; +class mgExternal14 : public mgExternal { }; +class mgExternal15 : public mgExternal { }; +class mgExternal16 : public mgExternal { }; +class mgExternal17 : public mgExternal { }; +class mgExternal18 : public mgExternal { }; +class mgExternal19 : public mgExternal { }; + +const char* +mgExternal::ButtonName() +{ + cCommand *command = Command(); + if (command) + { + return command->Title(); + } + else + return ""; +} + +const char* +mgExternal::MenuName(const unsigned int idx,const string value) +{ + return strdup(ButtonName()); +} + +cCommand * +mgExternal::Command() +{ + cCommand *command = NULL; + if (osd()->external_commands) + { + unsigned int idx = Type() - actExternal0; + command = osd()->external_commands->Get (idx); + } + return command; +} + +void +mgExternal::Execute() +{ + cCommand *command = Command(); + if (command) + { + mgDebug(1,"external command:%s",command->Title()); + bool confirmed = true; + if (command->Confirm ()) + { + char *buffer; + asprintf (&buffer, "%s?", command->Title ()); + confirmed = Interface->Confirm (buffer); + free (buffer); + } + if (confirmed) + { + osd()->Message1 ("%s...", command->Title ()); + selection ()->select (); + string m3u_file = selection ()->exportM3U (); + selection ()->leave (); + if (!m3u_file.empty ()) + { + /*char *result = (char *)*/ + command->Execute (m3u_file.c_str ()); +/* What to do? Recode cMenuText (not much)? + if (result) + { + free( result ); + return AddSubMenu( new cMenuText( command->Title(), result ) ); + } +*/ + } + } + } +} + +//! \brief select search order +class mgChooseSearch : public mgCommand +{ + public: + bool Enabled(); + eOSState ProcessKey(eKeys key); + void Execute (); + const char *ButtonName() { return tr("Search"); } + const char *MenuName(const unsigned int idx,const string value) + { return strdup(tr("Select search order")); } +}; + +bool +mgChooseSearch::Enabled() +{ + bool result = mgOsdItem::Enabled(); + result &= (!selection()->isCollectionlist()); + return result; +} + +eOSState +mgChooseSearch::ProcessKey(eKeys key) +{ + if (key!=kNone) + mgDebug(3,"mgChooseSearch::ProcessKey(%d)",int(key)); + if (key == kOk) + { + osd()->Menus.pop_back(); + Execute(); + return osContinue; + } + else + return mgCommand::ProcessKey(key); +} + +void mgChooseSearch::Execute() +{ + osd ()->newmenu = new mgTreeViewSelector; + +} + +//! \brief toggles between the normal and the collection selection +class mgToggleSelection:public mgCommand +{ + public: + void Execute (); + const char *ButtonName (); + const char *MenuName (const unsigned int idx,const string value); +}; + +const char * +mgToggleSelection::ButtonName () +{ + if (osd ()->UsingCollection) + return tr ("Search"); + else + return tr ("Collections"); +} + + +const char * +mgToggleSelection::MenuName (const unsigned int idx,const string value) +{ + if (osd ()->UsingCollection) + return strdup(tr ("Search")); + else + return strdup(tr ("Collections")); +} + + +void +mgToggleSelection::Execute () +{ + if (osd ()->UsingCollection) + osd ()->UseNormalSelection (); + else + { + osd ()->UseCollectionSelection (); + selection()->clearCache(); + } + osd()->newposition = selection ()->gotoPosition (); +} + + +//! \brief sets the default collection selection +class mgSetDefault:public mgCommand +{ + public: + bool Enabled(); + void Execute (); + const char *ButtonName () + { + return tr ("Default"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +const char * mgSetDefault::MenuName(const unsigned int idx,const string value) +{ + char *b; + asprintf (&b, tr("Set default to collection '%s'"), + selection ()->getCurrentValue().c_str()); + return b; +} + +class mgSetButton : public mgCommand +{ + const char *ButtonName() + { + return tr("Set"); + } + const char *MenuName(const unsigned int idx,const string value) { return strdup(""); } +}; + + +bool +mgSetDefault::Enabled() +{ + bool result = mgOsdItem::Enabled(); + result &= (!osd()->DefaultCollectionSelected()); + result &= osd()->UsingCollection; + result &= (selection ()->level () == 0); + return result; +} + +void +mgSetDefault::Execute () +{ + if (!Enabled()) + mgError("mgSetDefault not enabled"); + osd ()->default_collection = selection ()->getCurrentValue(); + osd()->Message1 ("Default collection now is '%s'", + osd ()->default_collection); +} + + +//! \brief instant play +class mgInstantPlay : public mgCommand { + public: + void Execute (); + const char *ButtonName () + { + return tr ("Instant play"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +const char * +mgInstantPlay::MenuName (const unsigned int idx,const string value) +{ + return strdup(tr("Instant play")); +} + +void +mgInstantPlay::Execute() +{ + osd()->PlayInstant(true); +} + +//! \brief add selected items to default collection +class mgAddAllToCollection:public mgCommand { + public: + void Execute (); + //! \brief adds the whole selection to a collection + // \param collection the target collection. Default is the default collection + void ExecuteSelection (mgSelection *s,const string collection=""); + const char *ButtonName () + { + return tr ("Add"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +const char * +mgAddAllToCollection::MenuName (const unsigned int idx,const string value) +{ + char *b; + asprintf (&b, tr ("Add all to '%s'"), + osd ()->default_collection.c_str ()); + return b; +} + +void +mgAddAllToCollection::Execute() +{ + ExecuteSelection(selection()); +} + +void +mgAddAllToCollection::ExecuteSelection (mgSelection *s, const string collection) +{ + string target = collection; + if (target.empty()) + target = osd()->default_collection; + if (target == osd()->play_collection) + if (!PlayerControl()) + collselection()->ClearCollection(target); + + osd()->Message1 ("Added %s entries",itos (s->AddToCollection (target))); + + if (target == osd()->play_collection) + { + playselection()->clearCache(); + mgPlayerControl *c = PlayerControl(); + if (c) + c->ReloadPlaylist(); + else + osd()->PlayQueue(); + } +} + +//! \brief add selected items to default collection +class mgAddThisToCollection:public mgAddAllToCollection +{ + public: + bool Enabled(); + void Execute (); + const char *ButtonName (); + const char *MenuName (const unsigned int idx,const string value); +}; + + +void +mgAddThisToCollection::Execute () +{ +// work on a copy, so we don't have to clear the cache of selection() +// which would result in an osd()->forcerefresh which could scroll. + mgSelection *s = new mgSelection(selection()); + s->select (); + mgAddAllToCollection::ExecuteSelection(s); + delete s; +} + +class mgSearchNew : public mgCommand +{ + virtual void NewSearch() = 0; + void Execute(); +}; + +void +mgSearchNew::Execute() +{ + mgSelection *oldsel = osd()->selection(); + map* oldkeys = oldsel->UsedKeyValues(); + osd()->UseNormalSelection(); // Default for all searches + NewSearch(); + mgSelection *newsel = osd()->selection(); + for (unsigned int idx = 0; idx < newsel->size(); idx++) + { + string new_keyname = newsel->getKeyChoice(idx); + if (oldkeys->count(new_keyname)==0) break; + newsel->select((*oldkeys)[new_keyname]); + } + newsel->leave(); + delete oldkeys; +} + +class mgSearchCollItem : public mgSearchNew +{ + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Collection -> Item")); } + void NewSearch() { osd ()->UseCollectionSelection (); } +}; + + +const char * +mgAddThisToCollection::ButtonName () +{ + return tr("Add"); +} + +bool +mgAddThisToCollection::Enabled() +{ + bool result = mgOsdItem::Enabled(); + result &= (!osd()->DefaultCollectionSelected()); + return result; +} + +const char * +mgAddThisToCollection::MenuName (const unsigned int idx,const string value) +{ + char *b; + asprintf (&b, tr ("Add to '%s'"), + osd ()->default_collection.c_str ()); + return b; +} + +//! \brief remove selected items from default collection +class mgRemoveAllFromCollection:public mgCommand +{ + public: + void Execute (); + const char *ButtonName () + { + return tr ("Remove"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +void +mgRemoveAllFromCollection::Execute () +{ + osd()->Message1 ("Removed %s entries", + itos (collselection ()->RemoveFromCollection ( + osd ()->default_collection.c_str ()))); + if (osd()->default_collection == osd()->play_collection) + { + mgPlayerControl *c = PlayerControl(); + if (c) + c->ReloadPlaylist(); + } +} + + +const char * +mgRemoveAllFromCollection::MenuName (const unsigned int idx,const string value) +{ + char *b; + asprintf (&b, tr ("Remove all from '%s'"), + osd ()->default_collection.c_str ()); + return b; +} + +//! \brief remove selected items from default collection +class mgRemoveThisFromCollection:public mgRemoveAllFromCollection +{ + public: + void Execute (); + const char *ButtonName () + { + return tr ("Remove"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +void +mgRemoveThisFromCollection::Execute () +{ + if (osd()->DefaultCollectionSelected()) + { + selection()->ClearCollection(selection()->getCurrentValue().c_str()); + osd()->Message ("Removed all entries"); + } + else + { + selection ()->select (); + osd()->Message1 ("Removed %s entries", + itos (selection ()->RemoveFromCollection (osd ()->default_collection.c_str ()))); + selection ()->leave (); + if (osd()->DefaultCollectionEntered()) + { + selection()->clearCache(); + osd()->forcerefresh = true; + } + } +} + + +const char * +mgRemoveThisFromCollection::MenuName (const unsigned int idx,const string value) +{ + char *b; + string this_sel = selection()->getCurrentValue(); + if (osd()->DefaultCollectionSelected()) + asprintf(&b,tr("Remove all entries from '%s'"),this_sel.c_str()); + else + asprintf (&b, tr ("Remove from '%s'"), + trim(osd ()->default_collection).c_str ()); + return b; +} + + +void +mgCreateCollection::Notify() +{ + if (!strchr(Text(),'[')) + if (!strchr(Text(),']')) + osd()->SetHelpKeys(NULL,NULL,NULL,NULL); +} + +const char* +mgCreateCollection::MenuName(const unsigned int idx,const string value) +{ + return strdup(tr ("Create collection")); +} + +mgCreateCollection::mgCreateCollection() : cMenuEditStrItem(MenuName(),value,30,tr(FileNameChars)), mgAction() +{ + value[0]=0; +} + +eOSState +mgCreateCollection::ProcessKey(eKeys key) +{ + if (key == kOk) + Execute(); + return cMenuEditStrItem::ProcessKey(key); +} + +bool +mgCreateCollection::Enabled() +{ + bool result = mgAction::Enabled(); + result &= selection()->isCollectionlist(); + return result; +} + +void +mgCreateCollection::Execute () +{ + string name = trim(value); + if (name.empty()) return; + bool created = selection ()->CreateCollection (name); + if (created) + { + mgDebug(1,"created collection %s",name.c_str()); + selection ()->clearCache(); + osd()->forcerefresh = true; + } + else + osd()->Message1 ("Collection '%s' NOT created", name); +} + +//! \brief delete collection +class mgDeleteCollection:public mgCommand +{ + public: + void Execute (); + bool Enabled(); + const char *ButtonName () + { + return tr ("Delete"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +bool +mgDeleteCollection::Enabled() +{ + bool result = mgOsdItem::Enabled(); + result &= selection()->isCollectionlist(); + result &= (!osd()->DefaultCollectionSelected()); + if (result) + { + string name = selection ()->getCurrentValue(); + result &= (name != osd()->play_collection); + } + return result; +} + +const char* mgDeleteCollection::MenuName(const unsigned int idx,const string value) +{ + return strdup(tr("Delete collection")); +} + + +void +mgDeleteCollection::Execute () +{ + string name = selection ()->getCurrentValue(); + if (selection ()->DeleteCollection (name)) + { + osd()->Message1 ("Collection '%s' deleted", name); + mgDebug(1,"Deleted collection %s",name.c_str()); + selection ()->clearCache(); + osd()->forcerefresh = true; + } + else + osd()->Message1 ("Collection '%s' NOT deleted", name); +} + + +//! \brief export track list for all selected items +class mgExportTracklist:public mgCommand +{ + public: + void Execute (); + const char *ButtonName () + { + return tr ("Export"); + } + const char *MenuName (const unsigned int idx,const string value) + { + return strdup(tr ("Export track list")); + } +}; + +void +mgExportTracklist::Execute () +{ + selection ()->select (); + string m3u_file = selection ()->exportM3U (); + selection ()->leave (); + osd()->Message1 ("written to %s", m3u_file); +} + +class mgSearchArtistAlbumTitle : public mgSearchNew +{ + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Album -> Title")); } + void NewSearch() + { + selection ()->setKey (0, tr ("Artist")); + selection ()->setKey (1, tr ("Album")); + selection ()->setKey (2, tr ("Title")); + } +}; + + +class mgSearchAlbumTitle : public mgSearchNew +{ + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Album -> Title")); } + void NewSearch() + { + selection ()->setKey (0, tr ("Album")); + selection ()->setKey (1, tr ("Title")); + } +}; + + +class mgSearchGenreYearTitle : public mgSearchNew +{ + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Year -> Title")); } + void NewSearch() + { + selection ()->setKey (0, tr ("Genre 1")); + selection ()->setKey (1, tr ("Year")); + selection ()->setKey (2, tr ("Title")); + } +}; + + +class mgSearchGenreArtistAlbumTitle : public mgSearchNew +{ + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Artist ->Album -> Title")); } + void NewSearch() + { + selection ()->setKey (0, tr ("Genre 1")); + selection ()->setKey (1, tr ("Artist")); + selection ()->setKey (2, tr ("Album")); + selection ()->setKey (3, tr ("Title")); + } +}; + + +class mgSearchArtistTitle : public mgSearchNew +{ + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Title")); } + void NewSearch() + { + selection ()->setKey (0, tr ("Artist")); + selection ()->setKey (1, tr ("Title")); + } +}; + + +mgActions +mgOsdItem::Type() +{ + const type_info& t = typeid(*this); + if (t == typeid(mgChooseSearch)) return actChooseSearch; + if (t == typeid(mgToggleSelection)) return actToggleSelection; + if (t == typeid(mgSetDefault)) return actSetDefault; + if (t == typeid(mgInstantPlay)) return actInstantPlay; + if (t == typeid(mgAddAllToCollection)) return actAddAllToCollection; + if (t == typeid(mgRemoveAllFromCollection)) return actRemoveAllFromCollection; + if (t == typeid(mgDeleteCollection)) return actDeleteCollection; + if (t == typeid(mgExportTracklist)) return actExportTracklist; + if (t == typeid(mgAddThisToCollection)) return actAddThisToCollection; + if (t == typeid(mgRemoveThisFromCollection)) return actRemoveThisFromCollection; + if (t == typeid(mgEntry)) return actEntry; + if (t == typeid(mgSetButton)) return actSetButton; + if (t == typeid(mgSearchCollItem)) return actSearchCollItem; + if (t == typeid(mgSearchArtistAlbumTitle)) return actSearchArtistAlbumTitle; + if (t == typeid(mgSearchArtistTitle)) return actSearchArtistTitle; + if (t == typeid(mgSearchAlbumTitle)) return actSearchAlbumTitle; + if (t == typeid(mgSearchGenreYearTitle)) return actSearchGenreYearTitle; + if (t == typeid(mgSearchGenreArtistAlbumTitle)) return actSearchArtistAlbumTitle; + if (t == typeid(mgExternal0)) return actExternal0; + if (t == typeid(mgExternal1)) return actExternal1; + if (t == typeid(mgExternal2)) return actExternal2; + if (t == typeid(mgExternal3)) return actExternal3; + if (t == typeid(mgExternal4)) return actExternal4; + if (t == typeid(mgExternal5)) return actExternal5; + if (t == typeid(mgExternal6)) return actExternal6; + if (t == typeid(mgExternal7)) return actExternal7; + if (t == typeid(mgExternal8)) return actExternal8; + if (t == typeid(mgExternal9)) return actExternal9; + if (t == typeid(mgExternal10)) return actExternal10; + if (t == typeid(mgExternal11)) return actExternal11; + if (t == typeid(mgExternal12)) return actExternal12; + if (t == typeid(mgExternal13)) return actExternal13; + if (t == typeid(mgExternal14)) return actExternal14; + if (t == typeid(mgExternal15)) return actExternal15; + if (t == typeid(mgExternal16)) return actExternal16; + if (t == typeid(mgExternal17)) return actExternal17; + if (t == typeid(mgExternal18)) return actExternal18; + if (t == typeid(mgExternal19)) return actExternal19; + mgError("Unknown mgOsdItem %s",t.name()); + return mgActions(0); +} + +mgOsdItem* +actGenerate(const mgActions action) +{ + mgOsdItem * result = NULL; + switch (action) + { + case actChooseSearch: result = new mgChooseSearch;break; + case actToggleSelection: result = new mgToggleSelection;break; + case actSetDefault: result = new mgSetDefault;break; + case actUnused1: break; + case actInstantPlay: result = new mgInstantPlay;break; + case actAddAllToCollection: result = new mgAddAllToCollection;break; + case actRemoveAllFromCollection:result = new mgRemoveAllFromCollection;break; + case actDeleteCollection: result = new mgDeleteCollection;break; + case actExportTracklist: result = new mgExportTracklist;break; + case actUnused2: break; + case actUnused3: break; + case actAddThisToCollection: result = new mgAddThisToCollection;break; + case actRemoveThisFromCollection: result = new mgRemoveThisFromCollection;break; + case actEntry: result = new mgEntry;break; + case actSetButton: result = new mgSetButton;break; + case actSearchCollItem: result = new mgSearchCollItem;break; + case actSearchArtistAlbumTitle: result = new mgSearchArtistAlbumTitle;break; + case actSearchArtistTitle: result = new mgSearchArtistTitle;break; + case actSearchAlbumTitle: result = new mgSearchAlbumTitle;break; + case actSearchGenreYearTitle: result = new mgSearchGenreYearTitle;break; + case actSearchGenreArtistAlbumTitle: result = new mgSearchGenreArtistAlbumTitle;break; + case actExternal0: result = new mgExternal0;break; + case actExternal1: result = new mgExternal1;break; + case actExternal2: result = new mgExternal2;break; + case actExternal3: result = new mgExternal3;break; + case actExternal4: result = new mgExternal4;break; + case actExternal5: result = new mgExternal5;break; + case actExternal6: result = new mgExternal6;break; + case actExternal7: result = new mgExternal7;break; + case actExternal8: result = new mgExternal8;break; + case actExternal9: result = new mgExternal9;break; + case actExternal10: result = new mgExternal10;break; + case actExternal11: result = new mgExternal11;break; + case actExternal12: result = new mgExternal12;break; + case actExternal13: result = new mgExternal13;break; + case actExternal14: result = new mgExternal14;break; + case actExternal15: result = new mgExternal15;break; + case actExternal16: result = new mgExternal16;break; + case actExternal17: result = new mgExternal17;break; + case actExternal18: result = new mgExternal18;break; + case actExternal19: result = new mgExternal19;break; + } + return result; +} + + +mgSelection * +mgAction::selection() +{ + return osd()->selection(); +} + + +mgSelection * +mgAction::collselection() +{ + return osd()->collselection(); +} + diff --git a/mg_actions.h b/mg_actions.h new file mode 100644 index 0000000..cb0602c --- /dev/null +++ b/mg_actions.h @@ -0,0 +1,168 @@ +/*! + * \file mg_actions.h + * \brief Implements all actions for broswing media libraries within VDR + * + * \version $Revision: 1.13 $ + * \date $Date: 2004-12-25 16:52:35 +0100 (Sat, 25 Dec 2004) $ + * \author Wolfgang Rohdewald + * \author Responsible author: $Author: wr61 $ + * + * $Id: mg_actions.h 276 2004-12-25 15:52:35Z wr61 $ + */ + +#ifndef _MG_ACTIONS_H +#define _MG_ACTIONS_H + +#include + +#include +#include +#include "i18n.h" + +using namespace std; + +class mgSelection; +class mgMenu; +class mgMainMenu; +class mgOsdItem; + +/*! \brief defines all actions which can appear in command submenus. + * Since these values are saved in muggle.state, new actions should + * always be appended. The order does not matter. Value 0 means undefined. + */ +enum mgActions { + actChooseSearch=1, //!< show a menu with all possible search schemas + actToggleSelection, //!< toggle between search and collection view + actSetDefault, //!< set default collection + actUnused1, + actInstantPlay, //!< instant play + actAddAllToCollection, //!< add all items of OSD list to default collection + actRemoveAllFromCollection,//!< remove from default collection + actDeleteCollection, //!< delete collection + actExportTracklist, //!< export track list into a *.m3u file + actUnused2, + actUnused3, + actAddThisToCollection, //!< add selected item to default collection + actRemoveThisFromCollection, //!< remove selected item from default collection + actEntry, //!< used for normal data base items + actSetButton, //!< connect a button with an action + actSearchCollItem, //!< search in the collections + actSearchArtistAlbumTitle, //!< search by Artist/Album/Title + actSearchArtistTitle, //!< search by Artist/Title + actSearchAlbumTitle, //!< search by Album/Title + actSearchGenreYearTitle, //!< search by Genre1/Year/Title + actSearchGenreArtistAlbumTitle, //!< search by Genre1/Artist/Album/Title + actExternal0, //!< used for external commands, the number is the entry number in the .conf file starting with line 0 + actExternal1, //!< used for external commands, the number is the entry number in the .conf file + actExternal2, //!< used for external commands, the number is the entry number in the .conf file + actExternal3, //!< used for external commands, the number is the entry number in the .conf file + actExternal4, //!< used for external commands, the number is the entry number in the .conf file + actExternal5, //!< used for external commands, the number is the entry number in the .conf file + actExternal6, //!< used for external commands, the number is the entry number in the .conf file + actExternal7, //!< used for external commands, the number is the entry number in the .conf file + actExternal8, //!< used for external commands, the number is the entry number in the .conf file + actExternal9, //!< used for external commands, the number is the entry number in the .conf file + actExternal10, //!< used for external commands, the number is the entry number in the .conf file + actExternal11, //!< used for external commands, the number is the entry number in the .conf file + actExternal12, //!< used for external commands, the number is the entry number in the .conf file + actExternal13, //!< used for external commands, the number is the entry number in the .conf file + actExternal14, //!< used for external commands, the number is the entry number in the .conf file + actExternal15, //!< used for external commands, the number is the entry number in the .conf file + actExternal16, //!< used for external commands, the number is the entry number in the .conf file + actExternal17, //!< used for external commands, the number is the entry number in the .conf file + actExternal18, //!< used for external commands, the number is the entry number in the .conf file + actExternal19, //!< used for external commands, the number is the entry number in the .conf file +}; + +//! \brief the highest possible actExternal value +const mgActions actExternalHigh = actExternal19; + +//! \brief a generic class for the definition of user actions +class mgAction +{ + public: + + //! \brief if true, can be displayed + virtual bool Enabled(); + + //! \brief the action to be executed + virtual void Execute () = 0; + + //! \brief handles the kBack key + virtual eOSState Back(); + +/*! \brief the name for a button. + * The returned C string should be static, it will never be freed. + */ + virtual const char *ButtonName () + { + return NULL; + } + +/*! \brief the name for a menu entry. If empty, no button will be able + * to execute this. The returned C string must be freeable at any time. + * \todo use virtual Set() instead? Compare definition of mgMenu::AddAction + */ + virtual const char *MenuName (const unsigned int idx=0,const string value="") = 0; + + //! \brief default constructor + mgAction (); + + //! \brief default destructor + virtual ~ mgAction (); + + //! \brief assoiates the action with the menu which created it + void SetMenu (mgMenu * const menu); + + //! \brief what to do when mgStatus::OsdCurrentItem is called + //for this one + virtual void Notify(); + protected: + //! \brief returns the OSD owning the menu owning this item + mgMainMenu* osd (); + + //! \brief the menu that created this object + mgMenu * m; + + //! \brief returns the active selection + mgSelection* selection (); + + //! \brief returns the collection selection + mgSelection* collselection (); + + //! \brief returns the playselection + mgSelection* playselection (); +}; + +//! \brief a generic class for all actions that can be used in the +// command submenu +class mgOsdItem : public mgAction, public cOsdItem +{ + public: + //! \brief the enum mgActions value of this class + virtual mgActions Type(); + + //! \brief to be executed for kBack. We might want to call this + // directly, so expose it. + virtual eOSState Back(); +}; + +//! \brief generate a mgOsdItem for action +mgOsdItem* actGenerate(const mgActions action); + + +/*! \brief create collection directly in the collection list + */ +class mgCreateCollection:public cMenuEditStrItem, public mgAction +{ + public: + mgCreateCollection(); + void Notify(); + bool Enabled(); + eOSState ProcessKey(eKeys key); + void Execute (); + const char *MenuName (const unsigned int idx=0,const string value=""); + private: + char value[30]; +}; +#endif diff --git a/mg_content_interface.c b/mg_content_interface.c deleted file mode 100755 index 79dbd1f..0000000 --- a/mg_content_interface.c +++ /dev/null @@ -1,241 +0,0 @@ -/*! \file mg_content_interface.c - * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin - * - * \version $Revision: 1.6 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ - * - * Implements main classes of for content items and abstract interfaces to media access - * - * This file implements the following classes - * - mgTracklist - * - mgSelectionTreeNode - */ -#define DEBUG - -#include -#include "mg_content_interface.h" -#include "mg_tools.h" - - -//! \brief a special item representing an undefined state -mgContentItem mgContentItem::UNDEFINED = mgContentItem(); - -mgTracklist::mgTracklist() -{ - -} - -mgTracklist::~mgTracklist() -{ - mgContentItem* ptr; - std::vector::iterator iter; - - for( iter = m_list.begin(); iter != m_list.end(); iter++ ) - { - ptr = *iter; - delete ptr; - } - m_list.clear(); -} - -std::vector *mgTracklist::getAll() -{ - return &m_list; -} - -unsigned int mgTracklist::getNumItems() -{ - return m_list.size(); -} - -unsigned long mgTracklist::getLength() -{ - unsigned long result = 0; - std::vector::iterator iter; - - for( iter = m_list.begin(); iter != m_list.end (); iter++ ) - { - result += (*iter)->getLength(); - } - - return result; -} - -void mgTracklist::shuffle() -{ - random_shuffle( m_list.begin(), m_list.end () ); -} - -void mgTracklist::sortBy(int col, bool direction) -{ -} - -void mgTracklist::setDisplayColumns(std::vector cols) -{ - m_columns = cols; -} - -unsigned int mgTracklist::getNumColumns() -{ - return m_columns.size(); -} - -std::string mgTracklist::getLabel(unsigned int position, const std::string separator) -{ - std::string label = ""; - mgContentItem* item; - - if( position >= m_list.size() ) - { - return ""; - } - else - { - item = *( m_list.begin() + position ); - } - - for( std::vector::iterator iter = m_columns.begin(); - iter != m_columns.end(); iter++ ) - { - if( iter != m_columns.begin() ) - { - label += separator; - } - label += item->getLabel(*iter); - } - return label; -} - -mgContentItem* mgTracklist::getItem(unsigned int position) -{ - if( position >= m_list.size() ) - { - return &(mgContentItem::UNDEFINED); //invalid - } - return *( m_list.begin() + position); -} - -bool mgTracklist::remove(unsigned position) -{ - bool result = false; - - if( position < (int)m_list.size() ) - { - std::vector::iterator iter; - - iter = m_list.begin() + position; - m_list.erase(iter); - - result = true; - } - - return result; -} - -int mgTracklist::removeItem(mgContentItem* item) -{ - int retval = 0; - std::vector::iterator iter; - - for( iter = m_list.begin(); iter != m_list.end (); iter++ ) - { - if( *iter == item ) - { - m_list.erase(iter); - retval++; - break; - } - } - return retval; -} - -mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view) -{ - m_db = db; - m_parent = NULL; - m_level = 0; - m_view = view; - m_id = ""; - m_label = "ROOT"; - m_expanded = false; -} -mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, std::string id, std::string label) -{ - m_parent = parent; - m_level = m_parent->m_level+1; - m_view = m_parent->m_view; - m_db = m_parent->m_db; - m_id = id; - m_label = label; - m_expanded = false; -} - -mgSelectionTreeNode::~mgSelectionTreeNode() -{ - collapse(); - // TODO - lvw - // _children.clear(); -} - -mgSelectionTreeNode* mgSelectionTreeNode::getParent() -{ - if (m_view < 100 || m_level > 1) - { - return m_parent; - } - else - { - return NULL; - } -} - -void mgSelectionTreeNode::collapse() // removes all children (recursively) -{ - std::vector ::iterator iter; - mgSelectionTreeNode* ptr; - - for(iter = m_children.begin(); iter != m_children.end();iter++) - { - ptr = *iter; - delete ptr; - } - m_expanded = false; - m_children.clear(); -} - -std::vector &mgSelectionTreeNode::getChildren() -{ - // mgDebug(5," returning %d children", m_children.size()); - return m_children; -} - -std::string mgSelectionTreeNode::getID() -{ - return m_id; -} - -std::string mgSelectionTreeNode::getLabel() -{ - return m_label; -} - -std::string mgSelectionTreeNode::getLabel(int n) -{ - mgSelectionTreeNode* node = this; - int d = m_level; - while(n < d) - { - // TODO: check for NULL - node = node->m_parent; - d--; - } - - return node->m_label; -} - -std::string mgSelectionTreeNode::getRestrictions() -{ - return m_restriction; -} diff --git a/mg_content_interface.h b/mg_content_interface.h deleted file mode 100755 index 38edffa..0000000 --- a/mg_content_interface.h +++ /dev/null @@ -1,480 +0,0 @@ -/*! \file mg_content_interface.h - * \brief Data objects for content (e.g. mp3 files, movies) for the vdr muggle plugin - * - * \version $Revision: 1.4 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author$ - * - * Declares generic classes of for content items and interfaces to SQL databases - * - * This file defines the following classes - * - mgMediaPlayer - * - mgContentItem - * - mgTracklist - * - mgSelectionTreeNode - */ - -/* makes sure we dont parse the same declarations twice */ -#ifndef _CONTENT_INTERFACE_H -#define _CONTENT_INTERFACE_H - -#include -#include - -#include - -#define ILLEGAL_ID -1 - -class mgFilter; - -/*! - * \brief dummy player class - * \ingroup muggle - * - * \todo what to do with this - */ -class mgMediaPlayer -{ - public: - - mgMediaPlayer() - { } - - ~mgMediaPlayer() - { } -}; - -/*! - * \brief Generic class that represents a single content item. - * - * This is the parent class from which classes like mgGdTrack are derived. - * - */ -class mgContentItem -{ - public: - - /*! - * \brief defines the content type of the item - * \todo rethink this mechanism because adding new subclasses - * breaks existing ones (makes recompile cycle necessary). - */ - typedef enum contentType - { - ABSTRACT = 0, //< an abstract item which cannot be used - GD_AUDIO, //< a GiantDisc audio track - EPG //< an EPG item (i.e. a TV show) - }; - - protected: - - /*! - * \brief internal identifier to uniquely identify a content item; - */ - int m_uniqID; - - public: - - /*! - * \brief default constructor - */ - mgContentItem() - : m_uniqID( -1 ) - { } - - /*! - * \brief constructor with explicit id - */ - mgContentItem( int id ) - : m_uniqID( id ) - { } - - /*! - * \brief copy constructor - */ - mgContentItem( const mgContentItem& org ) - : m_uniqID( org.m_uniqID ) - { } - - /*! - * \brief the destructor - */ - virtual ~mgContentItem() - { }; - - /*! - * \brief acess unique id - */ - int getId() - { - return m_uniqID; - } - - /*! - * \brief determine what type of content are we looking at (e.g. audio, video, epg) - * - * The method should be overriden for concrete subclasses to return concrete a contentType. - */ - virtual contentType getContentType() - { - return ABSTRACT; - } - - /*! - * \brief return a (global?) object that is used to play content items - * \todo What for? Interesting properties? Last state, play info, ...? - */ - virtual mgMediaPlayer getPlayer() - { - return mgMediaPlayer(); - } - - //! \brief Access item data - //@{ - - /*! - * \brief return a specific label - */ - virtual std::string getLabel(int col = 0) - { - return ""; - } - - /*! - * \brief return the title - */ - virtual std::string getTitle() - { - return ""; - } - - /*! \brief get the "file" (or URL) that is passed to the player - */ - virtual std::string getSourceFile() - { - return ""; - } - - /*! \brief return a short textual description - */ - virtual std::string getDescription() - { - return ""; - } - - /*! \brief obtain the genre to which the track belongs - */ - virtual std::string getGenre() - { - return ""; - } - - /*! \brief obtain the rating of the title - */ - virtual int getRating() - { - return 0; - } - - /*! \brief obtain the samplerate of the track - */ - virtual int getSampleRate() - { - return 0; - } - - /*! \brief obtain the length of the track (in seconds) - */ - virtual int getLength() - { - return 0; - } - - /*! \brief obtain the number of audio channels of the track - */ - virtual int getChannels() - { - return 0; - } - - /*! \brief obtain the bitrate of the track - */ - virtual std::string getBitrate() - { - return ""; - } - - /*! \brief obtain track information in aggregated form - */ - virtual std::vector *getTrackInfo() - { - return NULL; - } - - //@} - - virtual bool updateTrackInfo(std::vector*) - { - return false; - } - - virtual bool operator == (mgContentItem o) - { - return m_uniqID == o.m_uniqID; - } - - // check, if usable - virtual bool isValid() - { - return ( m_uniqID >= 0 ); - } - static mgContentItem UNDEFINED; -}; - - -/*! - * \brief a list of content items - * \ingroup muggle - * - * \todo check, whether this class really needs a current item etc. - */ -class mgTracklist -{ - protected: - std::vector m_list; - std::vector m_columns; - int sorting; - - public: - - /*! - * \brief constructor - * - * create an empty tracklist - */ - mgTracklist(); - - /*! - * \brief the destructor - * - * Deletes all items in the tracklist and removes the list itself - */ - virtual ~mgTracklist(); - - /*! - * \brief returns a pointer to the list of elements - */ - std::vector *getAll(); - - /*! - * \brief returns the number of elements in the list - */ - unsigned int getNumItems(); - - /*! - * \brief returns the complete length of the playlist in seconds - */ - unsigned long getLength(); - - /*! - * \brief randomizes the order of the elements in the list - */ - virtual void shuffle(); - - /*! - * \brief sorts the elements in the list by the nth column - */ - virtual void sortBy(int col, bool direction); - - /*! - * \brief stores the ids of columns to be used in label creation - * - * The list can create a label with different fields (columns) using the - * function getLabel(). This function defines the fields of the contentItems - * to be used in the label and their order. - */ - void setDisplayColumns(std::vector cols); - - /*! - * \brief returns the number of display columns - */ - unsigned int getNumColumns(); - - /*! - * \brief creates the label string for an item - * - * The list can create a label with different fields (columns). - * The fields used in the list and their order is set using the function setDisplayColumns. - * - * This function creates a string from these columns, separated by the string - * 'separator' in the label and their order. - */ - virtual std::string getLabel(unsigned int position, const std::string separator); - - /*! - * \brief returns an item from the list at the specified position - */ - virtual mgContentItem* getItem(unsigned int position); - - /*! - * \brief remove item at position - */ - virtual int removeItem(mgContentItem* item); // remove all occurences of item - - /*! - * \brief remove all occurences of item - */ - virtual bool remove(unsigned position); // remove item at position -}; - -/*! - * \brief represent a node in a tree of selections - * \ingroup muggle - * - * The class represents a tree representation. Each node can have a parent node and - * an arbitrary number of children nodes. - */ -class mgSelectionTreeNode -{ - protected: - - /*! - * \brief the database in which a node is stored - * \todo should this be in the authority of concrete subclasses? - */ - MYSQL m_db; - - //! \brief maintain a flag, whether the node is currently expanded - bool m_expanded; - - //! \brief list of active restrictions at this level - std::string m_restriction; - - //! \brief depth of node in the tree (0 = root) - int m_level; - - //! \brief unknown - int m_view; - - //! \brief ID of the node, used for further expand - std::string m_id; - - //! \brief label of the node, used for user interaction - std::string m_label; - - //! \brief parent of this node - mgSelectionTreeNode* m_parent; - - //! \brief hold the set of immediate children if expanded, empty if collapsed - std::vector m_children; - - public: - - //! \brief Object lifecycle management - //@{ - - /*! - * \brief a constructor for an empty node - */ - mgSelectionTreeNode(MYSQL db, int view); - - /*! - * \brief a constructor for a node with a parent - */ - mgSelectionTreeNode(mgSelectionTreeNode* parent, std::string id, std::string label); - - /*! - * \brief the destructor - */ - virtual ~mgSelectionTreeNode(); - - //@} - - //! \brief expand and collapse tree - //@{ - - /*! - * \brief whether the node is a leaf (i.e. has no more children) - */ - virtual bool isLeafNode() = 0; - - /*! - * \brief expand the node - * - * The method will obtain all its children node, e.g. from a database - */ - virtual bool expand() = 0; - - /*! - * \brief collapse all children nodes - * - * The method will collapse the subtree below this node and - * destroy all children node objects. - */ - virtual void collapse(); // removes all children (recursively) - - /*! - * \brief obtain parent node - * - * \todo what is that magic number 100 for in the implementation? - */ - mgSelectionTreeNode* getParent(); - - /*! - * \brief access direct children of the node - */ - virtual std::vector &getChildren(); - - /*! - * \brief returns all tracks which are children of this node (transitive closure!) - * - * This function allocates memory for the vector and for all elements of the vector - * The calling function is in charge of releasing this memory - */ - virtual std::vector* getTracks() = 0; - - /*! - * \brief obtain a single track - */ - virtual mgContentItem* getSingleTrack() = 0; - - bool isExpanded() - { return m_expanded; } - - int getLevel() - { return m_level; } - - //@} - - //! \brief obtain node information - //@{ - - /*! - * \brief obtain the ID of this node - */ - std::string getID(); - - /*! - * \brief obtain the label of this node - */ - virtual std::string getLabel(int n); - - /*! - * \brief obtain the label from the topmost parent of this node - */ - std::string getLabel(); - - /*! - * \brief obtain a SQL restriction - * - * The restriction returned is part of a SQL query string which will restrict - * results to nodes that belong to the set of items grouped by this node - */ - virtual std::string getRestrictions(); - - //@} - -}; - -#endif diff --git a/mg_database.c b/mg_database.c index 02d2e0a..1636262 100644 --- a/mg_database.c +++ b/mg_database.c @@ -10,7 +10,6 @@ #include "mg_database.h" #include "mg_tools.h" -#include #include static const int MAX_QUERY_BUFLEN = 2048; diff --git a/mg_db.c b/mg_db.c new file mode 100644 index 0000000..6f365fc --- /dev/null +++ b/mg_db.c @@ -0,0 +1,1517 @@ +/*! + * \file mg_db.c + * \brief A database interface to the GiantDisc + * + * \version $Revision: 1.0 $ + * \date $Date: 2004-12-07 10:10:35 +0200 (Tue, 07 Dec 2004) $ + * \author Wolfgang Rohdewald + * \author Responsible author: $Author: wr $ + * + */ + +#include "mg_db.h" +#include "vdr_setup.h" +#include "mg_tools.h" + +/*! \brief a RAM copy of the genres table for faster access + * and in order to avoid having to use genre as genre 1 etc. + */ +static map < string, string > genres; + +//! \brief adds string n to string s, using string sep to separate them +static string addsep (string & s, string sep, string n); + +//! \brief adds string n to string s, using a comma to separate them +static string comma (string & s, string n); + +//! \brief adds string n to string s, using AND to separate them +static string und (string & s, string n); + +/*! \brief returns a random integer within some range + */ +unsigned int +randrange (const unsigned int high) +{ + unsigned int result=0; + result = random () % high; + return result; +} + + +//! \brief adds n1=n2 to string s, using AND to separate several such items +static string +undequal (string & s, string n1, string op, string n2) +{ + if (n1.compare (n2) || op != "=") + return addsep (s, " AND ", n1 + op + n2); + else + return s; +} + +static string +comma (string & s, string n) +{ + return addsep (s, ",", n); +} + + +static string +und (string & s, string n) +{ + return addsep (s, " AND ", n); +} + + +static string +commalist (string prefix,list < string > v,bool sort=true) +{ + string result = ""; + if (sort) v.sort (); + v.unique (); + for (list < string >::iterator it = v.begin (); it != v.end (); it++) + { + comma (result, *it); + } + if (!result.empty()) + result.insert(0," "+prefix+" "); + return result; +} + +//! \brief converts long to string +string +itos (int i) +{ + stringstream s; + s << i; + return s.str (); +} + +//! \brief convert long to string +string +ltos (long l) +{ + stringstream s; + s << l; + return s.str (); +} + +static string zerostring; + +size_t +mgSelection::mgSelStrings::size() +{ + if (!m_sel) + mgError("mgSelStrings: m_sel is NULL"); + m_sel->refreshValues(); + return strings.size(); +} + +string& +mgSelection::mgSelStrings::operator[](unsigned int idx) +{ + if (!m_sel) + mgError("mgSelStrings: m_sel is NULL"); + m_sel->refreshValues(); + if (idx>=strings.size()) return zerostring; + return strings[idx]; +} + +void +mgSelection::mgSelStrings::setOwner(mgSelection* sel) +{ + m_sel = sel; +} + +mgValmap::mgValmap(const char *key) { + m_key = key; +} + +void mgValmap::Read(FILE *f) { + char *line=(char*)malloc(1000); + char *prefix=(char*)malloc(strlen(m_key)+2); + strcpy(prefix,m_key); + strcat(prefix,"."); + rewind(f); + while (fgets(line,1000,f)) { + if (strncmp(line,prefix,strlen(prefix))) continue; + if (line[strlen(line)-1]=='\n') + line[strlen(line)-1]=0; + char *name = line + strlen(prefix); + char *eq = strchr(name,'='); + if (!eq) continue; + *(eq-1)=0; + char *value = eq + 2; + (*this)[string(name)]=string(value); + } + free(prefix); + free(line); +} + +void mgValmap::Write(FILE *f) { + for (mgValmap::const_iterator it=begin();it!=end();++it) { + char b[1000]; + sprintf(b,"%s.%s = %s\n", + m_key,it->first.c_str(), + it->second.c_str()); + fputs(b,f); + } +} + +void mgValmap::put(const char* name, const string value) { + if (value.empty() || value==EMPTY) return; + (*this)[string(name)] = value; +} + +void mgValmap::put(const char* name, const char* value) { + if (!value || *value==0) return; + (*this)[string(name)] = value; +} + +void mgValmap::put(const char* name, const int value) { + put(name,ltos(value)); +} + +void mgValmap::put(const char* name, const unsigned int value) { + put(name,ltos(value)); +} + +void mgValmap::put(const char* name, const long value) { + put(name,ltos(value)); +} + +void mgValmap::put(const char* name, const bool value) { + string s; + if (value) + s = "true"; + else + s = "false"; + put(name,s); +} + + +void +mgSelection::clearCache() +{ + m_current_values = ""; + m_current_tracks = ""; +} + +string +mgSelection::getCurrentValue() +{ + return values[gotoPosition()]; +} + +MYSQL_RES * +mgSelection::exec_sql (string query) +{ + mgDebug(3,query.c_str()); + if (!m_db) return NULL; + if (mysql_query (m_db, (query + ';').c_str ())) + { + mgError("SQL Error in %s: %s",query.c_str(),mysql_error (m_db)); + return NULL; + } + return mysql_store_result (m_db); +} + + +/*! \brief executes a query and returns the first columnu of the + * first row. + * \param query the SQL query string to be executed + */ +string mgSelection::get_col0 (string query) +{ + MYSQL_RES * sql_result = exec_sql (query); + if (!sql_result) + return "NULL"; + MYSQL_ROW row = mysql_fetch_row (sql_result); + string result; + if (row == NULL) + result = "NULL"; + else if (row[0] == NULL) + result = "NULL"; + else + result = row[0]; + mysql_free_result (sql_result); + return result; +} + + +unsigned long +mgSelection::exec_count (string query) +{ + return atol (get_col0 (query).c_str ()); +} + + +/*! \brief extract table names. All words preceding a . are supposed to be + * table names. Table names are supposed to only contain letters. That is + * sufficient for GiantDisc + * \par spar the SQL command + * \return a list of table names + * \todo is this thread safe? + */ +static list < string > +tables (const string spar) +{ + list < string > result; + string s = spar; + string::size_type dot; + while ((dot = s.rfind ('.')) != string::npos) + { + s.erase (dot, string::npos); // cut the rest + string::size_type lword = s.size (); + while (strchr + ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + s[lword - 1])) + { + lword--; + if (lword == 0) + break; + } + result.push_back (s.substr (lword)); + } + return result; +} + + +/*! \brief if the SQL command works on only 1 table, remove all table + * qualifiers. Example: SELECT tracks.title FROM tracks becomes SELECT title + * FROM tracks + * \param spar the sql command. It will be edited in place + * \return the new sql command is also returned + */ +static string +optimize (string & spar) +{ + string s = spar; + string::size_type tmp = s.find (" WHERE"); + if (tmp != string::npos) + s.erase (tmp, 9999); + tmp = s.find (" ORDER"); + if (tmp != string::npos) + s.erase (tmp, 9999); + string::size_type frompos = s.find (" FROM ") + 6; + if (s.substr (frompos).find (",") == string::npos) + { + string from = s.substr (frompos, 999) + '.'; + string::size_type track; + while ((track = spar.find (from)) != string::npos) + { + spar.erase (track, from.size ()); + } + } + return spar; +} + +string keyfield::sql_string(const string s) const +{ + return selection->sql_string(s); +} + +keyfield::keyfield (const string choice) +{ + if (choice.empty()) + mgError("keyfield::keyfield: choice is empty"); + m_choice = choice; + m_id = EMPTY; + m_value = ""; + m_filter = ""; +} + + +string +mgSelection::sql_string (const string s) const +{ + char *buf = (char *) malloc (s.size () * 2 + 1); + mysql_real_escape_string (m_db, buf, s.c_str (), s.size ()); + string result = "'" + std::string (buf) + "'"; + free (buf); + return result; +} + + +string keyfield::restrict (string & result) const +{ + string id = ""; + string op; + if (m_id == EMPTY) + return result; + if (m_id == "NULL") + { + op = " is "; + id = "NULL"; + } + else + { + op = "="; + id = sql_string (m_id); + } + if (idfield () == valuefield ()) + undequal (result, idfield (), op, id); + else + undequal (result, basefield (), op, id); + return result; +} + + +string keyfield::join () const +{ + string result; + if (need_join ()) + return undequal (result, basefield (), "=", idfield ()); + else + return ""; +} + + +bool keyfield::need_join () const +{ + return lookup; +} + +void +keyfield::set(const string id,const string value) +{ + if (id != EMPTY) + if (m_id == id && m_value == value) return; + m_id = id; + m_value = value; + if (selection) + selection->clearCache(); +} + +void +keyfield::writeAt (ostream & s) const +{ + if (m_id == EMPTY) + s << choice () << '/'; + else + s << choice () << '=' << m_id; +} + + +const char * +toCString (mgSelection::ShuffleMode m) +{ + static const char *modes[] = + { + "SM_NONE", "SM_NORMAL", "SM_PARTY" + }; + return modes[m]; +} + + +string +toString (mgSelection::ShuffleMode m) +{ + return toCString (m); +} + +//! \brief dump keyfield +ostream & operator<< (ostream & s, keyfield & k) +{ + k.writeAt (s); + return s; +} + + +string +addsep (string & s, string sep, string n) +{ + if (!n.empty ()) + { + if (!s.empty ()) + s.append (sep); + s.append (n); + } + return s; +} + + +mgContentItem * +mgSelection::getTrack (unsigned int position) +{ + if (position >= getNumTracks ()) + return NULL; + return &(m_tracks[position]); +} + + +void +mgSelection::loadgenres () +{ + MYSQL_RES *rows = exec_sql ("select id,genre from genre;"); + if (rows) + { + MYSQL_ROW row; + while ((row = mysql_fetch_row (rows)) != NULL) + { + genres[row[0]] = row[1]; + } + mysql_free_result (rows); + } +} + + +string mgContentItem::getGenre1 () +{ + return genres[m_genre1]; +} + + +string mgContentItem::getGenre2 () +{ + return genres[m_genre2]; +} + + +mgSelection::ShuffleMode mgSelection::toggleShuffleMode () +{ + m_shuffle_mode = (m_shuffle_mode == SM_PARTY) ? SM_NONE : ShuffleMode (m_shuffle_mode + 1); + unsigned int tracksize = getNumTracks(); + switch (m_shuffle_mode) + { + case SM_NONE: + { + long id = m_tracks[m_tracks_position].getId (); + m_current_tracks = ""; // force a reload + for (unsigned int i = 0; i < tracksize; i++) + if (m_tracks[i].getId () == id) + { + m_tracks_position = i; + break; + } + } + break; + case SM_PARTY: + case SM_NORMAL: + { + // play all, beginning with current track: + mgContentItem tmp = m_tracks[m_tracks_position]; + m_tracks[m_tracks_position]=m_tracks[0]; + m_tracks[0]=tmp; + m_tracks_position=0; + // randomize all other tracks + for (unsigned int i = 1; i < tracksize; i++) + { + unsigned int j = 1+randrange (tracksize-1); + tmp = m_tracks[i]; + m_tracks[i] = m_tracks[j]; + m_tracks[j] = tmp; + } + } break; +/* + * das kapiere ich nicht... (wolfgang) + - Party mode (see iTunes) + - initialization + - find 15 titles according to the scheme below + - playing + - before entering next title perform track selection + - track selection + - generate a random uid + - if file exists: + - determine maximum playcount of all tracks +- generate a random number n +- if n < playcount / max. playcount +- add the file to the end of the list +*/ + } + return m_shuffle_mode; +} + + +mgSelection::LoopMode mgSelection::toggleLoopMode () +{ + m_loop_mode = (m_loop_mode == LM_FULL) ? LM_NONE : LoopMode (m_loop_mode + 1); + return m_loop_mode; +} + + +unsigned int +mgSelection::AddToCollection (const string Name) +{ + if (!m_db) return 0; + CreateCollection(Name); + string listid = sql_string (get_col0 + ("SELECT id FROM playlist WHERE title=" + sql_string (Name))); + string tmp = + get_col0 ("SELECT MAX(tracknumber) FROM playlistitem WHERE playlist=" + + listid); + int high; + if (tmp == "NULL") + high = 0; + else + high = atol (tmp.c_str ()); + unsigned int tracksize = getNumTracks (); + const char *sql_prefix = "INSERT INTO playlistitem VALUES "; + string sql = ""; + for (unsigned int i = 0; i < tracksize; i++) + { + string value = "(" + listid + "," + ltos (high + 1 + i) + "," + + ltos (m_tracks[i].getId ()) + ")"; + comma(sql, value); + if ((i%100)==99) + { + exec_sql (sql_prefix+sql); + sql = ""; + } + } + if (!sql.empty()) exec_sql (sql_prefix+sql); + if (inCollection(Name)) clearCache (); + return tracksize; +} + + +unsigned int +mgSelection::RemoveFromCollection (const string Name) +{ + if (!m_db) return 0; + string listid = get_col0 + ("SELECT id FROM playlist WHERE title=" + sql_string (Name)); + where(); + m_fromtables.push_back("playlistitem"); + m_fromtables.push_back("playlist"); + string sql = "DELETE playlistitem" + commalist("FROM",m_fromtables) + + m_where + " AND tracks.id = playlistitem.trackid " + " AND playlistitem.playlist = " + listid; + exec_sql (sql); + unsigned int removed = mysql_affected_rows (m_db); + if (inCollection(Name)) clearCache (); + return removed; +} + + +bool mgSelection::DeleteCollection (const string Name) +{ + if (!m_db) return false; + exec_sql ("DELETE FROM playlist WHERE title=" + sql_string (Name)); + if (isCollectionlist()) clearCache (); + return (mysql_affected_rows (m_db) == 1); +} + + +void mgSelection::ClearCollection (const string Name) +{ + if (!m_db) return; + exec_sql ("DELETE playlistitem FROM playlist,playlistitem " + "WHERE playlistitem.playlist=playlist.id " + " AND playlist.title=" + sql_string (Name)); + if (inCollection(Name)) clearCache (); +} + + +bool mgSelection::CreateCollection(const string Name) +{ + if (!m_db) return false; + string name = sql_string(Name); + if (exec_count("SELECT count(title) FROM playlist WHERE title = " + name)>0) + return false; + exec_sql ("INSERT playlist VALUES(" + name + ",'VDR',NULL,NULL,NULL)"); + if (isCollectionlist()) clearCache (); + return true; +} + + +string mgSelection::exportM3U () +{ + +// open a file for writing + string fn = m_Directory + '/' + ListFilename () + ".m3u"; + FILE * listfile = fopen (fn.c_str (), "w"); + if (!listfile) + return ""; + fprintf (listfile, "#EXTM3U"); + unsigned int tracksize = getNumTracks (); + for (unsigned i = 0; i < tracksize; i++) + { + mgContentItem* t = &m_tracks[i]; + fprintf (listfile, "#EXTINF:%d,%s\n", t->getDuration (), + t->getTitle ().c_str ()); + fprintf (listfile, "%s", t->getSourceFile ().c_str ()); + } + fclose (listfile); + return fn; +} + +bool +mgSelection::empty() +{ + if (m_level>= keys.size ()-1) + return ( getNumTracks () == 0); + else + return ( values.size () == 0); +} + +void +mgSelection::setPosition (unsigned int position) +{ + if (m_level < keys.size ()) + m_position[m_level] = position; + if (m_level >= keys.size ()-1) + m_tracks_position = position; +} + + +void +mgSelection::setTrack (unsigned int position) +{ + m_tracks_position = position; +} + + +unsigned int +mgSelection::getPosition (unsigned int level) const +{ + if (level == keys.size ()) + return getTrackPosition(); + else + return m_position[m_level]; +} + +unsigned int +mgSelection::gotoPosition (unsigned int level) +{ + if (level>keys.size()) + mgError("mgSelection::gotoPosition: level %u > keys.size %u", + level,keys.size()); + if (level == keys.size ()) + return gotoTrackPosition(); + else + { + unsigned int valsize = values.size(); + if (valsize==0) + m_position[m_level] = 0; + else if (m_position[m_level] >= valsize) + m_position[m_level] = valsize -1; + return m_position[m_level]; + } +} + +unsigned int +mgSelection::getTrackPosition() const +{ + return m_tracks_position; +} + +unsigned int +mgSelection::gotoTrackPosition() +{ + unsigned int tracksize = getNumTracks (); + if (tracksize == 0) + m_tracks_position = 0; + else if (m_tracks_position >= tracksize) + m_tracks_position = tracksize -1; + return m_tracks_position; +} + +bool mgSelection::skipTracks (int steps) +{ + unsigned int tracksize = getNumTracks(); + if (tracksize == 0) + return false; + if (m_loop_mode == LM_SINGLE) + return true; + unsigned int old_pos = getTrackPosition(); + unsigned int new_pos; + if (old_pos + steps < 0) + { + if (m_loop_mode == LM_NONE) + return false; + new_pos = tracksize - 1; + } + else + new_pos = old_pos + steps; + if (new_pos >= tracksize) + { + clearCache(); + tracksize = getNumTracks(); + if (new_pos >= tracksize) + { + if (m_loop_mode == LM_NONE) + return false; + new_pos = 0; + } + } + setTrack (new_pos); + return (new_pos == gotoTrackPosition()); +} + + +unsigned long +mgSelection::getLength () +{ + unsigned long result = 0; + unsigned int tracksize = getNumTracks (); + for (unsigned int i = 0; i < tracksize; i++) + result += m_tracks[i].getDuration (); + return result; +} + + +unsigned long +mgSelection::getCompletedLength () +{ + unsigned long result = 0; + tracks (); // make sure they are loaded + for (unsigned int i = 0; i < m_tracks_position; i++) + result += m_tracks[i].getDuration (); + return result; +} + + +string mgSelection::getListname () +{ + string + result = ""; + for (unsigned int i = 0; i < m_level; i++) + addsep (result, ":", keys[i]->value ()); + if (result.empty ()) + result = string(tr(keys[0]->choice ().c_str())); + return result; +} + + +string mgSelection::ListFilename () +{ + string res = getListname (); +#if 0 + geht so noch gar + nicht ... while (string::size_type p = res.find (" ")) + res.replace (p, ""); + while (string::size_type p = res.find ("/")) + res.replace (p, '-'); + while (string::size_type p = res.find ("\\")) + res.replace (p, '-'); +#endif + return res; +} + +void +mgSelection::AddOrder(const string sql,list& orderlist, const string item) +{ + string::size_type dot = item.rfind ('.'); + string itemtable = item.substr(0,dot); + if (sql.find(itemtable) != string::npos) + orderlist.push_back(item); +} + +const vector < mgContentItem > & +mgSelection::tracks () +{ + list < string > orderby; + orderby.clear(); + if (keys.empty()) + mgError("mgSelection::tracks(): keys is empty"); + if (genres.size () == 0) + loadgenres (); + string sql = "SELECT tracks.id, tracks.title, tracks.mp3file, " + "tracks.artist, album.title, tracks.genre1, tracks.genre2, " + "tracks.bitrate, tracks.year, tracks.rating, " + "tracks.length, tracks.samplerate, tracks.channels "; + sql += where (true); + for (unsigned int i = m_level; iorder ()); +} + if (m_level>= keys.size ()-1) + if (inCollection()) + AddOrder(sql,orderby,"playlistitem.tracknumber"); + else + AddOrder(sql,orderby,"tracks.title"); + + + sql += commalist("ORDER BY",orderby,false); + + optimize (sql); + if (m_current_tracks != sql) + { + m_current_tracks = sql; + m_tracks.clear (); + MYSQL_RES *rows = exec_sql (sql); + if (rows) + { + MYSQL_ROW row; + while ((row = mysql_fetch_row (rows)) != NULL) + { + m_tracks.push_back (mgContentItem (row, m_ToplevelDir)); + } + mysql_free_result (rows); + } + if (m_tracks_position>=m_tracks.size()) + if (m_tracks.size()==0) + m_tracks_position=0; + else + m_tracks_position = m_tracks.size()-1; + } + return m_tracks; +} + + +mgContentItem::mgContentItem (const mgContentItem* c) +{ + m_id = c->m_id; + m_title = c->m_title; + m_mp3file = c->m_mp3file; + m_artist = c->m_artist; + m_albumtitle = c->m_albumtitle; + m_genre1 = c->m_genre1; + m_genre2 = c->m_genre2; + m_bitrate = c->m_bitrate; + m_year = c->m_year; + m_rating = c->m_rating; + m_duration = c->m_duration; + m_samplerate = c->m_samplerate; + m_channels = c->m_channels; +} + +mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir) +{ + m_id = atol (row[0]); + m_title = row[1]; + m_mp3file = ToplevelDir + row[2]; + m_artist = row[3]; + m_albumtitle = row[4]; + m_genre1 = row[5]; + m_genre2 = row[6]; + m_bitrate = row[7]; + m_year = atol (row[8]); + if (row[9]) + m_rating = atol (row[9]); + m_duration = atol (row[10]); + m_samplerate = atol (row[11]); + m_channels = atol (row[12]); +}; + +string mgContentItem::getAlbum () +{ + return m_albumtitle; +} + + +string mgContentItem::getImageFile () +{ + return "Name of Imagefile"; +} + + +void +mgSelection::initkey (keyfield & f) +{ + f.setOwner(this); + all_keys[f.choice ()] = &f; + trall_keys[string(tr(f.choice ().c_str()))] = &f; +} + +void mgSelection::InitSelection() { + m_Directory="."; + m_ToplevelDir = string("/"); + InitDatabase(); + m_level = 0; + m_position.reserve (20); + m_tracks_position = 0; + m_trackid = -1; + m_shuffle_mode = SM_NONE; + m_loop_mode = LM_NONE; + clearCache(); + initkey (kartist); + initkey (kgenre1); + initkey (kgenre2); + initkey (klanguage); + initkey (krating); + initkey (kyear); + initkey (kdecade); + initkey (ktitle); + initkey (ktrack); + initkey (kalbum); + initkey (kcollection); + initkey (kcollectionitem); + keys.clear(); + keys.push_back (&kartist); + keys.push_back (&kalbum); + keys.push_back (&ktitle); + values.setOwner(this); +} + +mgSelection::mgSelection() +{ + m_db = NULL; + m_Host = ""; + m_User = ""; + m_Password = ""; + InitSelection (); + m_fall_through = false; +} + +mgSelection::mgSelection (const string Host, const string User, const string Password, const bool fall_through) +{ + m_db = NULL; + m_Host = Host; + m_User = User; + m_Password = Password; + InitSelection (); + m_fall_through = fall_through; +} + +mgSelection::mgSelection (const mgSelection &s) +{ + m_db = NULL; + InitFrom(&s); +} + +mgSelection::mgSelection (const mgSelection* s) +{ + m_db = NULL; + InitFrom(s); +} + +mgSelection::mgSelection (mgValmap& nv) +{ + // this is analog to the copy constructor, please keep in sync. + + m_db = NULL; + InitFrom(nv); +} + +void +mgSelection::InitFrom(mgValmap& nv) +{ + m_Host = nv.getstr("Host"); + m_User = nv.getstr("User"); + m_Password = nv.getstr("Password"); + InitSelection(); + m_fall_through = nv.getbool("FallThrough"); + m_Directory = nv.getstr("Directory"); + m_ToplevelDir = nv.getstr("ToplevelDir"); + for (unsigned int i = 0; i < 99 ; i++) + { + char *idx; + asprintf(&idx,"Keys.%u.Choice",i); + string v = nv.getstr(idx); + free(idx); + if (v.empty()) break; + setKey (i,v ); + } + while (m_level < nv.getuint("Level")) + { + char *idx; + asprintf(&idx,"Keys.%u.Position",m_level); + unsigned int newpos = nv.getuint(idx); + free(idx); + if (!enter (newpos)) + if (!select (newpos)) break; + } + m_trackid = nv.getlong("TrackId"); + // TODO do we really need Position AND TrackPosition in muggle.state? + setPosition(nv.getlong("Position")); + if (m_level>=keys.size()-1) + setTrack(nv.getlong("TrackPosition")); + setShuffleMode(ShuffleMode(nv.getuint("ShuffleMode"))); + setLoopMode(LoopMode(nv.getuint("LoopMode"))); +} + + +mgSelection::~mgSelection () +{ + mysql_close (m_db); +} + +void mgSelection::InitFrom(const mgSelection* s) +{ + m_Host = s->m_Host; + m_User = s->m_User; + m_Password = s->m_Password; + InitSelection(); + m_fall_through = s->m_fall_through; + m_Directory = s->m_Directory; + m_ToplevelDir = s->m_ToplevelDir; + keys.clear(); + for (unsigned int i = 0; i < s->keys.size (); i++) + { + keys.push_back(findKey(s->keys[i]->choice())); + keys[i]->set(s->keys[i]->id(),s->keys[i]->value()); + } + m_level = s->m_level; + m_position.reserve (s->m_position.capacity()); + for (unsigned int i = 0; i < s->m_position.capacity(); i++) + m_position[i] = s->m_position[i]; + m_trackid = s->m_trackid; + m_tracks_position = s->m_tracks_position; + setShuffleMode (s->getShuffleMode ()); + setLoopMode (s->getLoopMode ()); +} + +const mgSelection& mgSelection::operator=(const mgSelection &s) +{ + if ((&m_Host)==&(s.m_Host)) { // prevent s = s + return *this; + } + InitFrom(&s); + return *this; +} + + +void +mgSelection::writeAt (ostream & s) +{ + for (unsigned int i = 0; i < keys.size (); i++) + { + if (i == level ()) + s << '*'; + s << *keys[i] << ' '; + if (i == level ()) + { + for (unsigned int j = 0; j < values.size (); j++) + { + s << values[j]; + if (values[j] != m_ids[j]) + s << '(' << m_ids[j] << ")"; + s << ", "; + if (j == 7) + { + s << "(von " << values.size () << ") "; + break; + } + } + } + } + s << endl; +} + + +ostream & operator<< (ostream & s, mgSelection & sl) +{ + sl.writeAt (s); + return s; +} + + +unsigned int +mgSelection::size () +{ + return keys.size (); +} + + +unsigned int +mgSelection::valindex (const string val,const bool second_try) +{ + for (unsigned int i = 0; i < values.size (); i++) + { + if (values[i] == val) + return i; + } + // nochmal mit neuen Werten: + clearCache(); + if (second_try) { + mgWarning("valindex: Gibt es nicht:%s",val.c_str()); + return 0; + } + else + return valindex(val,true); +} + + +string mgSelection::where (bool want_trackinfo) +{ + m_from = ""; + m_where = ""; + m_fromtables.clear(); + if (m_level < keys.size ()) + { + for (unsigned int i = 0; i <= m_level; i++) + { + keyfield * k = keys[i]; + k->lookup = want_trackinfo || (i == m_level); + list < string > l = tables (k->join () + ' ' + k->basefield ()); + m_fromtables.merge (l); + und (m_where, k->join ()); + k->restrict (m_where); + } + } + else + { + m_fromtables.push_back ("tracks"); + m_where = "tracks.id='" + ltos (m_trackid) + "'"; + } + if (want_trackinfo) + { + if (m_level == keys.size () || !UsedBefore (&kalbum, m_level + 1)) + { + kalbum.lookup = false; + list < string > l = + tables (kalbum.join () + ' ' + kalbum.basefield ()); + m_fromtables.merge (l); + und (m_where, kalbum.join ()); + } + } + m_from = commalist ("FROM",m_fromtables); + if (!m_where.empty ()) + m_where.insert (0, " WHERE "); + return m_from + m_where; +} + + +void +mgSelection::refreshValues () +{ + if (m_current_values.empty()) + { + m_current_values = sql_values(); + values.strings.clear (); + m_ids.clear (); + MYSQL_RES *rows = exec_sql (m_current_values); + if (rows) + { + unsigned int num_fields = mysql_num_fields(rows); + MYSQL_ROW row; + while ((row = mysql_fetch_row (rows)) != NULL) + { + values.strings.push_back (row[0]); + if (num_fields==2) + m_ids.push_back (row[1]); + else + m_ids.push_back (row[0]); + } + mysql_free_result (rows); + } + if (m_position[m_level]>=values.size()) + if (values.size()==0) + m_position[m_level]=0; + else + m_position[m_level] = values.size()-1; + } +} + + +string mgSelection::sql_values () +{ + if (keys.empty()) + mgError("mgSelection::sql_values(): keys is empty"); + string result; + if (m_level < keys.size ()) + { + keyfield * last = keys[m_level]; + result = "SELECT "; + if (m_levelvaluefield (); + if (last->valuefield() != last->idfield()) + result += ',' + last->idfield (); + result += where (false); + result += " ORDER BY " + last->order (); + } + else + { + result = "SELECT title,id from tracks where id='" + ltos (m_trackid) + "'"; + } + optimize (result); + return result; +} + + +unsigned int +mgSelection::count () +{ + return values.size (); +} + + +void +mgSelection::InitDatabase () +{ + if (m_db) + { + mysql_close (m_db); + m_db = NULL; + } + if (m_Host == "") return; + m_db = mysql_init (0); + if (m_db == NULL) + return; + if (mysql_real_connect (m_db, m_Host.c_str (), m_User.c_str (), m_Password.c_str (), + "GiantDisc", 0, NULL, 0) == NULL) { + mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s", + m_Host.c_str(),m_User.c_str(),m_Password.c_str(),mysql_error(m_db)); + mysql_close (m_db); + m_db = NULL; + return; + } + return; +} + + +string keyfield::KeyCountquery () +{ + lookup = false; + string from; + from = commalist ("FROM",tables (countfield () + ' ' + countjoin ())); + string query = "SELECT COUNT(DISTINCT " + countfield () + ") " + from; + if (!countjoin ().empty ()) + query += " WHERE " + countjoin (); + optimize (query); + return query; +} + +keyfield* mgSelection::findKey(const string name) +{ + if (all_keys.find(name) != all_keys.end()) + return all_keys.find(name)->second; + if (trall_keys.find(name) != trall_keys.end()) + return trall_keys.find(name)->second; + return NULL; +} + +void +mgSelection::setKey (const unsigned int level, const string name) +{ + keyfield *newkey = findKey(name); + if (newkey == NULL) + mgError("mgSelection::setKey(%u,%s): keyname wrong", + level,name.c_str()); + if (level == 0 && newkey == &kcollection) + { + keys.clear (); + keys.push_back (&kcollection); + keys.push_back (&kcollectionitem); + return; + } + if (level == keys.size ()) + { + keys.push_back (newkey); + } + else + { + if (level >= keys.size()) + mgError("mgSelection::setKey(%u,%s): level greater than keys.size() %u", + level,name.c_str(),keys.size()); + keys[level] = newkey; +// remove this key from following lines: + for (unsigned int i = level + 1; i < keys.size (); i++) + if (keys[i] == keys[level]) + keys.erase (keys.begin () + i); + } + +// remove redundant lines: + bool album_found = false; + bool track_found = false; + bool title_found = false; + for (unsigned int i = 0; i < keys.size (); i++) + { + album_found |= (keys[i] == &kalbum); + track_found |= (keys[i] == &ktrack); + title_found |= (keys[i] == &ktitle); + if (track_found || (album_found && title_found)) + { + keys.erase (keys.begin () + i + 1, keys.end ()); + break; + } + } + +// clear values for this and following levels (needed for copy constructor) + for (unsigned int i = level; i < keys.size (); i++) + keys[i]->set (EMPTY, ""); + + if (m_level > level) + m_level = level; + if (m_level == level) setPosition(0); +} + + +bool mgSelection::enter (unsigned int position) +{ + if (keys.empty()) + mgError("mgSelection::enter(%u): keys is empty", position); + if (empty()) + return false; + setPosition (position); + position = gotoPosition(); // reload adjusted position + string value = values[position]; + string id = m_ids[position]; + while (1) + { + mgDebug(2,"enter(level=%u,pos=%u, value=%s)",m_level,position,value.c_str()); + if (m_level >= keys.size () - 1) + return false; + keys[m_level++]->set (id, value); + if (m_level >= keys.size()) + mgError("mgSelection::enter(%u): level greater than keys.size() %u", + m_level,keys.size()); + if (m_position.capacity () == m_position.size ()) + m_position.reserve (m_position.capacity () + 10); + m_position[m_level] = 0; + if (!m_fall_through) + break; + if (count () > 1) + break; + if (count () == 1) + { + id = m_ids[0]; + value = values[0]; + } + } + return true; +} + + +bool mgSelection::select (unsigned int position) +{ + mgDebug(2,"select(pos=%u)",position); + if (m_level == keys.size () - 1) + { + if (getNumTracks () <= position) + return false; + m_level++; + m_trackid = m_tracks[position].getId (); + clearCache(); + return true; + } + else + return enter (position); +} + + +bool mgSelection::leave () +{ + if (keys.empty()) + mgError("mgSelection::leave(): keys is empty"); + if (m_level == keys.size ()) + { + m_level--; + m_trackid = -1; + clearCache(); + return true; + } + while (1) + { + if (m_level < 1) + return false; + keys[--m_level]->set (EMPTY, ""); + if (!m_fall_through) + break; + if (count () > 1) + break; + } + return true; +} + + +bool mgSelection::UsedBefore (keyfield const *k, unsigned int level) +{ + if (level >= keys.size ()) + level = keys.size () - 1; + for (unsigned int i = 0; i < level; i++) + if (keys[i] == k) + return true; + return false; +} + + +bool mgSelection::isCollectionlist () +{ + return (keys[0] == &kcollection && m_level == 0); +} + +bool +mgSelection::inCollection(const string Name) +{ + bool result = (keys[0] == &kcollection && m_level == 1); + if (result) + if (keys[1] != &kcollectionitem) + mgError("inCollection: key[1] is not kcollectionitem"); + if (!Name.empty()) + result &= (keys[0]->value() == Name); + return result; +} + + +const strvector & +mgSelection::keychoice (const unsigned int level) +{ + m_keychoice.clear (); + if (level > keys.size ()) + return m_keychoice; + map < string, keyfield * >::iterator it; + map < string, keyfield * > possible_keys; + for (it = all_keys.begin (); it != all_keys.end (); it++) + { + keyfield*f = (*it).second; + if (keycounts.find (f->choice ()) == keycounts.end ()) + { + keycounts[f->choice ()] = exec_count (f->KeyCountquery ()); + } + unsigned int i = keycounts[f->choice ()]; + if ((&(*f) != &kcollection) && (&(*f) != &kcollectionitem) && (i < 2)) + ; + else + possible_keys[string(tr(f->choice ().c_str()))] = &(*f); + } + + for (it = possible_keys.begin (); it != possible_keys.end (); it++) + { + keyfield *k = (*it).second; + if (level != 0 && k == &kcollection) + continue; + if (level != 1 && k == &kcollectionitem) + continue; + if (level == 1 && keys[0] != &kcollection && k == &kcollectionitem) + continue; + if (level == 1 && keys[0] == &kcollection && k != &kcollectionitem) + continue; + if (level > 1 && keys[0] == &kcollection) + break; + if (k == &kdecade && UsedBefore (&kyear, level)) + continue; + if (!UsedBefore (k, level)) + m_keychoice.push_back (string(tr((*it).second->choice ().c_str()))); + } + return m_keychoice; +} + + +void mgSelection::DumpState(mgValmap& nv) +{ + nv.put("Host",m_Host); + nv.put("User",m_User); + nv.put("Password",m_Password); + nv.put("FallThrough",m_fall_through); + nv.put("ShuffleMode",int(m_shuffle_mode)); + nv.put("LoopMode",int(m_loop_mode)); + nv.put("Directory",m_Directory); + nv.put("ToplevelDir",m_ToplevelDir); + nv.put("Level",int(m_level)); + for (unsigned int i=0;ichoice()); + asprintf(&n,"Keys.%d.Filter",i); + nv.put(n,keys[i]->filter()); + if (i * +mgSelection::UsedKeyValues() +{ + map *result = new map; + for (unsigned int idx = 0 ; idx < level() ; idx++) + { + (*result)[keys[idx]->choice()] = keys[idx]->value(); + } + if (level() < keys.size()-1) + { + string ch = keys[level()]->choice(); + (*result)[ch] = getCurrentValue(); + } + return result; +} diff --git a/mg_db.h b/mg_db.h new file mode 100644 index 0000000..40e3308 --- /dev/null +++ b/mg_db.h @@ -0,0 +1,1071 @@ +/*! + * \file mg_db.h + * \brief A database interface to the GiantDisc + * + * \version $Revision: 1.0 $ + * \date $Date: 2004-12-07 10:10:35 +0200 (Tue, 07 Dec 2004) $ + * \author Wolfgang Rohdewald + * \author Responsible author: $Author: wr $ + * + */ + +#ifndef _DB_H +#define _DB_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "mg_tools.h" + +typedef vector strvector; + +//! \brief a map for reading / writing configuration data. +class mgValmap : public map { + private: + const char *m_key; + public: + /*! \brief constructor + * \param key all names will be prefixed with key. + */ + mgValmap(const char *key); + //! \brief read from file + void Read(FILE *f); + //! \brief write to file + void Write(FILE *f); + //! \brief enter a string value + void put(const char*name, string value); + //! \brief enter a C string value + void put(const char*name, const char* value); + //! \brief enter a long value + void put(const char*name, long value); + //! \brief enter a int value + void put(const char*name, int value); + //! \brief enter a unsigned int value + void put(const char*name, unsigned int value); + //! \brief enter a bool value + void put(const char*name, bool value); + //! \brief return a string + string getstr(const char* name) { + return (*this)[name]; + } + //! \brief return a C string + bool getbool(const char* name) { + return (getstr(name)=="true"); + } + //! \brief return a long + long getlong(const char* name) { + return atol(getstr(name).c_str()); + } + //! \brief return an unsigned int + unsigned int getuint(const char* name) { + return (unsigned long)getlong(name); + } +}; + +static const string EMPTY = "XNICHTGESETZTX"; + +class mgSelection; + +//! \brief a generic keyfield +class keyfield +{ + public: + //! \brief set the owning selection. + void setOwner(mgSelection *owner) { selection = owner; } + + //! \brief default constructor + keyfield () + { + }; + + /*! \brief constructs a simple key field which only needs info from + * the tracks table. + * \param choice the internationalized name of this key field, e.g. "Jahr" + */ + keyfield (const string choice); + + //! \brief default destructor + virtual ~ keyfield () + { + }; + +/*! \brief assigns a new id and value to the key field. + * This also invalidates the cache of the owning mgSelection. + * \param id used for lookups in the data base + * \param value used for display + */ + void set (const string id, const string value); + +//! \brief helper function for streaming debug info + void writeAt (ostream &) const; + +//! \brief adds lookup data to a WHERE SQL statement + string restrict (string & result) const; + +//! \brief returns the internationalized name of this key field + string choice () const + { + return m_choice; + } + +/*! \brief returns the id of this key field. This is the string used + * for lookups in the data base, not for display + */ + string id () const + { + return m_id; + } + +/*! \brief returns the filter for this key field. + * \todo filters are not yet implemented but we already dump them in DumpState + */ + string filter () const + { + return m_filter; + } + +/*! \brief returns the value of this key field. This is the string used + * for the display. + */ + string value () const + { + return m_value; + } + + virtual string order() const + { + return valuefield (); + } + +//! \brief returns the name of the corresponding field in the tracks table + virtual string basefield () const { return ""; } + +//! \brief returns the name of the field to be shown in the selection list + virtual string valuefield () const + { + return basefield (); + } + +//! \brief returns the name of the identification field + virtual string idfield () const + { + return basefield (); + } + +/*! \brief returns the name of the field needed to count how many + * different values for this key field exist + */ + virtual string countfield () const + { + return basefield (); + } + +/*! \brief returns a join clause needed for the composition of + * a WHERE statement + */ + virtual string join () const; + +/*! \brief returns a join clause needed for the composition of + * a WHERE statement especially for counting the number of items + */ + virtual string countjoin () const + { + return join (); + } + +/*! \brief if true, the WHERE clause should also return values from + * join tables + */ + bool lookup; + +//! \brief returns a SQL query command for counting different key +//values + string KeyCountquery (); + + protected: +//! \brief the owning selection. + mgSelection* selection; + //! \brief the english name for this key field + string m_choice; + //! \brief used for lookup in the data base + string m_id; + //! \brief used for OSD display + string m_value; + //! \brief an SQL restriction like 'tracks.year=1982' + string m_filter; + /*! \brief should be defined as true if we need to join another + * table for getting user friendly values (like the name of a genre) + */ + virtual bool need_join () const; + //! \brief escape the string as needed for calls to mysql + string sql_string(const string s) const; +}; + + +//! \brief orders by collection +class collectionkeyfield:public keyfield +{ + public: + collectionkeyfield ():keyfield ("Collection") + { + } + string basefield () const + { + return "playlist.id"; + } + string valuefield () const + { + return "playlist.title"; + } +/* this join() would ensure that empty collections be suppressed. But we + * want them all. so we don't need the join + */ + string join () const + { + return ""; + } +}; + +//! \brief orders by position in collection +class collectionitemkeyfield:public keyfield +{ + public: + collectionitemkeyfield ():keyfield ("Collection item") + { + } + string basefield () const + { + return "playlistitem.tracknumber"; + } + string valuefield () const + { + return "tracks.title"; + } + string order () const + { + return basefield (); + } + string join () const + { + return + "tracks.id=playlistitem.trackid and playlist.id=playlistitem.playlist"; + } +}; + +//! \brief orders by album.title +class albumkeyfield:public keyfield +{ + public: + albumkeyfield ():keyfield ("Album") + { + } + string basefield () const + { + return "tracks.sourceid"; + } + string valuefield () const + { + return "album.title"; + } + string idfield () const + { + return "album.title"; + } + string countfield () const + { + return "album.title"; + } + string join () const + { + return "tracks.sourceid=album.cddbid"; + } + protected: + //!brief we always need to join table album + bool need_join () const + { + return true; + }; +}; + +//! \brief orders by genre1 +class genre1keyfield:public keyfield +{ + public: + genre1keyfield ():keyfield ("Genre 1") + { + } + string basefield () const + { + return "tracks.genre1"; + } + string valuefield () const + { + return "genre.genre"; + } + string idfield () const + { + return "genre.id"; + } +}; + +//! \brief orders by genre2 +class genre2keyfield:public keyfield +{ + public: + genre2keyfield ():keyfield ("Genre 2") + { + } + string basefield () const + { + return "tracks.genre2"; + } + string valuefield () const + { + return "genre.genre"; + } + string idfield () const + { + return "genre.id"; + } +}; + +//! \brief orders by language +class langkeyfield:public keyfield +{ + public: + langkeyfield ():keyfield ("Language") + { + } + string basefield () const + { + return "tracks.lang"; + } + string valuefield () const + { + return "language.language"; + } + string idfield () const + { + return "language.id"; + } +}; + +//! \brief orders by tracks.artist +class artistkeyfield:public keyfield +{ + public: + artistkeyfield ():keyfield ("Artist") + { + } + string basefield () const + { + return "tracks.artist"; + } +}; + +//! \brief orders by tracks.rating +class ratingkeyfield:public keyfield +{ + public: + ratingkeyfield ():keyfield ("Rating") + { + } + string basefield () const + { + return "tracks.rating"; + } +}; + +//! \brief orders by tracks.year +class yearkeyfield:public keyfield +{ + public: + yearkeyfield ():keyfield ("Year") + { + } + string basefield () const + { + return "tracks.year"; + } +}; + +//! \brief orders by tracks.title +class titlekeyfield:public keyfield +{ + public: + titlekeyfield ():keyfield ("Title") + { + } + string basefield () const + { + return "tracks.title"; + } +}; + +//! \brief orders by tracks.tracknb and tracks.title +class trackkeyfield:public keyfield +{ + public: + trackkeyfield ():keyfield ("Track") + { + } + string basefield () const + { + return "tracks.tracknb"; + } + string valuefield () const + { + return + "concat(" + "if(tracks.tracknb>0," + "concat(" + "if(tracks.tracknb<10,' ','')," + "tracks.tracknb," + "' '" + "),''" + ")," + "tracks.title)"; + } +}; + +//! \brief orders by decade (deduced from tracks.year) +class decadekeyfield:public keyfield +{ + public: + decadekeyfield ():keyfield ("Decade") + { + } + string basefield () const + { + return "substring(convert(10 * floor(tracks.year/10), char),3)"; + } +}; + +//! \brief represents a content item like an mp3 file +class mgContentItem +{ + public: + mgContentItem () + { + } + //! \brief copy constructor + mgContentItem(const mgContentItem* c); + + //! \brief construct an item from an SQL row + mgContentItem (const MYSQL_ROW row, const string ToplevelDir); +//! \brief returns track id + long getId () const + { + return m_id; + } + +//! \brief returns title + string getTitle () const + { + return m_title; + } + +//! \brief returns filename + string getSourceFile () const + { + return m_mp3file; + } + +//! \brief returns artist + string getArtist () const + { + return m_artist; + } + +//! \brief returns the name of the album + string getAlbum (); + +//! \brief returns the name of genre 1 + string getGenre1 (); + +//! \brief returns the name of genre 2 + string getGenre2 (); + +//! \brief returns the name of genre 1 + string getGenre () + { + return getGenre1 (); + } + +//! \brief returns the bitrate + string getBitrate () const + { + return m_bitrate; + } + +//! \brief returns the file name of the album image + string getImageFile (); + +//! \brief returns year + int getYear () const + { + return m_year; + } + +//! \brief returns rating + int getRating () const + { + return m_rating; + } + +//! \brief returns duration + int getDuration () const + { + return m_duration; + } + +//! \brief returns samplerate + int getSampleRate () const + { + return m_samplerate; + } + +//! \brief returns # of channels + int getChannels () const + { + return m_channels; + } + private: + long m_id; + string m_title; + string m_mp3file; + string m_artist; + string m_albumtitle; + string m_genre1; + string m_genre2; + string m_bitrate; + int m_year; + int m_rating; + int m_duration; + int m_samplerate; + int m_channels; +}; + +/*! + * \brief the only interface to the database. + */ +class mgSelection +{ + private: + class mgSelStrings + { + friend class mgSelection; + private: + strvector strings; + mgSelection* m_sel; + void setOwner(mgSelection* sel); + public: + string& operator[](unsigned int idx); + size_t size(); + }; + + public: +/*! \brief define various ways to play music in random order + * \todo Party mode is not implemented, does same as SM_NORMAL + */ + enum ShuffleMode + { + SM_NONE, //!< \brief play normal sequence + SM_NORMAL, //!< \brief a shuffle with a fair distribution + SM_PARTY //!< \brief select the next few songs randomly, continue forever + }; + +//! \brief define various ways to play music in a neverending loop + enum LoopMode + { + LM_NONE, //!< \brief do not loop + LM_SINGLE, //!< \brief loop a single track + LM_FULL //!< \brief loop the whole track list + }; + +//! \brief escapes special characters + string sql_string(const string s) const; + +//! \brief the default constructor. Does not start a DB connection. + mgSelection(); + +/*! \brief the main constructor + * \param Host where the data base lives. If not localhost, TCP/IP is used. + * \param User if empty, the current user is used. + * \param Password no comment + * \param fall_through if TRUE: If enter() returns a choice + * containing only one item, that item is automatically entered. + * The analog happens with leave() + */ + mgSelection (const string Host, const string User = + "", const string Password = "", const bool fall_through = + false); + +/*! \brief a copy constructor. Does a deep copy. + * Some of the data base content will only be retrieved by the + * new mgSelection as needed, so some data base + * overhead is involved + */ + mgSelection (const mgSelection& s); +/*! \brief a copy constructor. Does a deep copy. + * Some of the data base content will only be retrieved by the + * new mgSelection as needed, so some data base + * overhead is involved + */ + mgSelection (const mgSelection* s); + +/*! \brief the assignment operator. Does a deep copy. + * Some of the data base content will only be retrieved by the + * new mgSelection as needed, so some data base + * overhead is involved + */ + const mgSelection& operator=(const mgSelection& s); + +//! \brief initializes from a map. + void InitFrom(mgValmap& nv); + +//! \brief the normal destructor + ~mgSelection (); + + //! \brief sets the top level directory where content is stored + void setToplevelDir(string ToplevelDir) { m_ToplevelDir = ToplevelDir; } + +/*! \brief represents all values for the current level. The result + * is cached in values, subsequent accesses to values only incur a + * small overhead for building the SQL WHERE command. The values will + * be reloaded when the SQL command changes + * \todo we should do more caching. The last 5 result sets should be cached. + */ + mgSelStrings values; + +/*! \brief defines a field to be used as key for selection + * + * \param level 0 is the top level + * \param name of the key field, internationalized. Possible values + * are defined by keychoice() + */ + void setKey (const unsigned int level, const string name); + +/*! \brief returns the name of a key + */ + string getKeyChoice (const unsigned int level) + { + return keys[level]->choice (); + } + //! \brief return the current value of this key + string getKeyValue (const unsigned int level) + { + return keys[level]->value (); + } + +//! \brief returns a map (new allocated) for all used key fields and their values + map * UsedKeyValues(); + +//! \brief helper function for << operator (dumps debug info) + void writeAt (ostream &); + +/*! \brief returns FROM and WHERE clauses for the current state + * of the selection. + * \param want_trackinfo work in progress, should disappear I hope + */ + string where (bool want_trackinfo = false); + +//! \brief the number of music items currently selected + unsigned int count (); + +//! \brief the number of key fields used for the query + unsigned int size (); + +//! \brief the current position in the current level + unsigned int gotoPosition () + { + return gotoPosition (m_level); + } + +//! \brief the current position + unsigned int getPosition (unsigned int level) const; + + //! \brief go to the current position. If it does not exist, + // go to the nearest. + unsigned int gotoPosition (unsigned int level); + +//! \brief the current position in the tracks list + unsigned int getTrackPosition () const; + + //! \brief go to the current track position. If it does not exist, + // go to the nearest. + unsigned int gotoTrackPosition (); + +/*! \brief enter the the next higher level, go one up in the tree. + * If fall_through (see constructor) is set to true, and the + * level entered by enter() contains only one item, automatically + * goes down further until a level with more than one item is reached. + * \param position is the position in the current level that is to be expanded + * \return returns false if there is no further level + */ + bool enter (unsigned int position); + + /*! \brief like enter but if we are at the leaf level simply select + * the entry at position + */ + bool select (unsigned int position); + +/*! \brief enter the next higher level, expanding the current position. + * See also enter(unsigned int position) + */ + bool enter () + { + return enter (gotoPosition ()); + } + + /*! \brief like enter but if we are at the leaf level simply select + * the current entry + */ + bool select () + { + return select (gotoPosition ()); + } + +/*! \brief enter the next higher level, expanding the position holding a certain value + * \param value the position holding value will be expanded. + */ + bool enter (const string value) + { + return enter (valindex (value)); + } + + /*! \brief like enter but if we are at the leaf level simply select + * the current entry + */ + bool select (const string value) + { + return select (valindex(value)); + } + +/*! \brief leave the current level, go one up in the tree. + * If fall_through (see constructor) is set to true, and the + * level entered by leave() contains only one item, automatically + * goes up further until a level with more than one item is reached. + * \return returns false if there is no further upper level + */ + bool leave (); + +/*! \brief leave the current level, go up in the tree until + * target level is reached. + * If fall_through (see constructor) is set to true, and the + * level entered by leave() contains only one item, automatically + * goes up further until a level with more than one item is reached. + * \return returns false if there is no further upper level + */ + bool leave (const unsigned int target_level) + { + while (m_level>target_level) + if (!leave()) return false; + return true; + } + +//! \brief the current level in the tree + unsigned int level () const + { + return m_level; + } + +/*! \brief the possible choices for a keyfield in this level. + * keyfields already used in upper levels are no possible + * choices, neither are most keyfields if their usage would + * allow less than 2 choices. + */ + const strvector &keychoice (const unsigned int level); + +/*! \brief returns the current item from the value() list + */ + string getCurrentValue(); + + //! \brief true if the selection holds no items + bool empty(); + +/*! \brief returns detailled info about all selected tracks. + * The ordering is done only by the keyfield of the current level. + * This might have to be changed - suborder by keyfields of detail + * levels. This list is cached so several consequent calls mean no + * loss of performance. See value(), the same warning applies. + * \todo call this more seldom. See getNumTracks() + */ + const vector < mgContentItem > &tracks (); + +/*! \brief returns an item from the tracks() list + * \param position the position in the tracks() list + * \return returns NULL if position is out of range + */ + mgContentItem* getTrack (unsigned int position); + +/*! \brief returns the current item from the tracks() list + */ + mgContentItem* getCurrentTrack () + { + return getTrack (gotoTrackPosition()); + } + +/*! \brief toggles the shuffle mode thru all possible values. + * When a shuffle modus SM_NORMAL or SM_PARTY is selected, the + * order of the tracks in the track list will be randomly changed. + */ + ShuffleMode toggleShuffleMode (); + +//! \brief toggles the loop mode thru all possible values + LoopMode toggleLoopMode (); + +//! \brief returns the current shuffle mode + ShuffleMode getShuffleMode () const + { + return m_shuffle_mode; + } + +//! \brief sets the current shuffle mode + void setShuffleMode (const ShuffleMode shuffle_mode) + { + m_shuffle_mode = shuffle_mode; + } + +//! \brief returns the current loop mode + LoopMode getLoopMode () const + { + return m_loop_mode; + } + +//! \brief sets the current loop mode + void setLoopMode (const LoopMode loop_mode) + { + m_loop_mode = loop_mode; + } + +/*! \brief adds the whole current track list to a collection + * \param Name the name of the collection. If it does not yet exist, + * it will be created. + */ + unsigned int AddToCollection (const string Name); + +/*! \brief removes the whole current track from a the collection + * Remember - this selection can be configured to hold exactly + * one list, so this command can be used to clear a selected list. + * \param Name the name of the collection + */ + unsigned int RemoveFromCollection (const string Name); +//! \brief delete a collection + bool DeleteCollection (const string Name); +/*! \brief create a collection only if it does not yet exist. + * \return true only if it has been created. false if it already existed. + */ + bool CreateCollection(const string Name); + +//! \brief remove all items from the collection + void ClearCollection (const string Name); + +/*! generates an m3u file containing all tracks. The directory + * can be indicated by SetDirectory(). + * The file name will be built from the list name, slashes + * and spaces converted + */ + string exportM3U (); + +/*! \brief go to a position in the current level. If we are at the + * most detailled level this also sets the track position since + * they are identical. + * \param position the wanted position. If it is too big, go to the + * last existing position + * \return only if no position exists, false will be returned + */ + void setPosition (unsigned int position); + +/*! \brief go to the position with value in the current level + * \param value the value of the wanted position + */ + void setPosition (const string value) + { + setPosition (valindex (value)); + } + +/*! \brief go to a position in the track list + * \param position the wanted position. If it is too big, go to the + * last existing position + * \return only if no position exists, false will be returned + */ + void setTrack (unsigned int position); + +/*! \brief skip some tracks in the track list + * \return false if new position does not exist + */ + bool skipTracks (int step=1); + +/*! \brief skip forward by 1 in the track list + * \return false if new position does not exist + */ + bool skipFwd () + { + return skipTracks (+1); + } + +/*! \brief skip back by 1 in the track list + * \return false if new position does not exist + */ + bool skipBack () + { + return skipTracks (-1); + } + +//! \brief returns the sum of the durations of all tracks + unsigned long getLength (); + +/*! \brief returns the sum of the durations of completed tracks + * those are tracks before the current track position + */ + unsigned long getCompletedLength (); + +/*! returns the number of tracks in the track list + * \todo should not call tracks () which loads all track info. + * instead, only count the tracks. If the size differs from + * m_tracks.size(), invalidate m_tracks + */ + unsigned int getNumTracks () + { + return tracks ().size (); + } + +//! sets the directory for the storage of m3u file + void SetDirectory (const string directory) + { + m_Directory = directory; + } + +/*! returns the name of the current play list. If no play list is active, + * the name is built from the name of the key fields. + */ + string getListname (); + +/*! \brief true if this selection currently selects a list of collections + */ + bool isCollectionlist (); + + //! \brief true if we have entered a collection + bool inCollection(const string Name=""); + + /*! \brief dumps the entire state of this selection into a map, + * \param nv the values will be entered into this map + */ + void DumpState(mgValmap& nv); + + /*! \brief creates a new selection using saved definitions + * \param nv this map contains the saved definitions + */ + mgSelection(mgValmap& nv); + + //! \brief clear the cache, next access will reload from data base + void clearCache(); + + //! \todo soll sql_values() nur noch bei Bedarf bauen, also muessen + // alle Aenderungen, die Einfluss darauf haben, clearCache machen + void refreshValues(); + + //! \brief true if values and tracks need to be reloaded + bool cacheIsEmpty() + { + return (m_current_values=="" && m_current_tracks==""); + } + private: + void AddOrder(const string sql,list& orderlist, const string item); + list < string > m_fromtables; //!< \brief part result from previous where() + string m_from; //!< \brief part result from previous where() + string m_where; //!< \brief part result from previous where() + bool m_fall_through; + vector < unsigned int >m_position; + unsigned int m_tracks_position; + ShuffleMode m_shuffle_mode; + LoopMode m_loop_mode; + MYSQL *m_db; + string m_Host; + string m_User; + string m_Password; + string m_ToplevelDir; + unsigned int m_level; + long m_trackid; + string m_current_values; + string m_current_tracks; + +//! \brief be careful when accessing this, see mgSelection::tracks() + vector < mgContentItem > m_tracks; + strvector m_ids; + strvector m_keychoice; + artistkeyfield kartist; + ratingkeyfield krating; + yearkeyfield kyear; + decadekeyfield kdecade; + albumkeyfield kalbum; + collectionkeyfield kcollection; + collectionitemkeyfield kcollectionitem; + genre1keyfield kgenre1; + genre2keyfield kgenre2; + langkeyfield klanguage; + titlekeyfield ktitle; + trackkeyfield ktrack; + map < string, keyfield * >all_keys; + map < string, keyfield * >trall_keys; + vector < keyfield * >keys; + bool UsedBefore (keyfield const *k, unsigned int level); + void InitSelection (); + void InitDatabase (); + void initkey (keyfield & f); + /*! \brief returns the SQL command for getting all values. + * For the leaf level, all values are returned. For upper + * levels, every distinct value is returned only once. + * This must be so for the leaf level because otherwise + * the value() entries do not correspond to the track() + * entries and the wrong tracks might be played. + */ + string sql_values (); + //! \todo das nach mgSelStrings verlagern + unsigned int valindex (const string val,const bool second_try=false); + string ListFilename (); + string m_Directory; + void loadgenres (); + MYSQL_RES *exec_sql (string query); + string get_col0 (string query); + + void InitFrom(const mgSelection* s); + +/*! \brief executes a query and returns the integer value from + * the first column in the first row. The query shold be a COUNT query + * returning only one row. + * \param query the SQL query to be executed + */ + unsigned long mgSelection::exec_count (string query); + + keyfield* findKey (const string name); + map < string, unsigned int > keycounts; +}; + +//! \brief streams debug info about a selection +ostream & operator<< (ostream &, mgSelection & s); + +//! \brief convert the shuffle mode into a string +// \return strings "SM_NONE" etc. +string toString (mgSelection::ShuffleMode); + +//! \brief same as toString but returns a C string +const char *toCString (mgSelection::ShuffleMode); + +//string toString(long int l); + +string itos (int i); +unsigned int randrange (const unsigned int high); + + +#endif // _DB_H diff --git a/mg_media.c b/mg_media.c deleted file mode 100644 index 2b25b73..0000000 --- a/mg_media.c +++ /dev/null @@ -1,453 +0,0 @@ -/*! \file mg_media.c - * \brief Top level access to media in vdr plugin muggle - * - * \version $Revision: 1.14 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ - */ - -/* makes sure we dont parse the same declarations twice */ -#include "mg_media.h" - -#include "mg_tools.h" -#include "mg_content_interface.h" - -#include "gd_content_interface.h" - -#include "vdr_setup.h" - - -//------------------------------------------------------------------- -// mgFilterSets -//------------------------------------------------------------------- -/*! - * \brief constructor - * - * constructor of the base class - */ - -mgFilterSets::mgFilterSets() -{ - // nothing to be done in the base class -} - -mgFilterSets::~mgFilterSets() -{ - std::vector *set; - for(std::vector*>::iterator iter1 = m_sets.begin(); - iter1 != m_sets.end(); iter1++) - { - set = *iter1; - for(std::vector::iterator iter2 = set->begin(); - iter2 != set->end(); iter2++) - { - delete (*iter2); - } - set->clear(); - delete set; - } - m_sets.clear(); -} -/*! - ******************************************************************* - * \brief returns the number of available sets - ********************************************************************/ -int mgFilterSets::numSets() -{ - return m_sets.size(); -} - - /*! - ******************************************************************* - * \brief proceeds to the next one in a circular fashion - ********************************************************************/ -void mgFilterSets::nextSet() -{ - m_activeSetId++; - if(m_activeSetId >= (int) m_sets.size()) - { - m_activeSetId = 0; - } - m_activeSet = m_sets[m_activeSetId]; -} - // - -/*! - ******************************************************************* - * \brief activates a specific set by index - * - * \par n : index of the set to be activated - * - * If n is not a valid filter set, the first set (index 0 ) is activated - ********************************************************************/ -void mgFilterSets::select(int n) -{ - m_activeSetId = n ; - if(m_activeSetId >= (int) m_sets.size()) - { - m_activeSetId = 0; - } - m_activeSet = m_sets[m_activeSetId]; - -} - - /*! - ******************************************************************* - * \brief restores the default values for all filter values in the active set - ********************************************************************/ - void mgFilterSets::clear() -{ - for(std::vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - (*iter)->clear(); - } -} - /*! - ******************************************************************* - * \brief stores the current filter values - ********************************************************************/ - void mgFilterSets::accept() -{ - for(std::vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - (*iter)->store(); - } -} - -/*! - ******************************************************************* - * \brief returns the active filter set to the application - * - * the application may temporarily modify the filter values - * accept() needs to be called to memorize the changed values - ********************************************************************/ - std::vector *mgFilterSets::getFilters() -{ - for(std::vector::iterator iter = m_activeSet->begin(); - iter != m_activeSet->end(); iter++) - { - (*iter)->restore(); - } - return m_activeSet; -} - - -/*! - * \brief return title of the active filter set - */ -std::string mgFilterSets::getTitle() -{ - if(m_activeSetId < (int) m_titles.size()) - { - return m_titles[m_activeSetId]; - } - else - { - mgWarning("Implementation error: No title std::string for filter set %d", - m_activeSetId); - return "NO-TITLE"; - } -} - -/*! - * \class mgMedia - * - * \brief mein class to access content in the vdr plugin muggle - */ -mgMedia::mgMedia(contentType mediatype) -{ - int errval = 0; - m_filters = NULL; - m_mediatype = mediatype; - m_sql_filter = "1"; - m_defaultView = 1; - - // now initialize the database - mgDebug(1, "Media Type %sselected", getMediaTypeName().c_str()); - switch(m_mediatype) - { - case GD_MP3: - { - errval = GdInitDatabase(&m_db); - mgDebug(3, "Successfully connected to sql database %s", the_setup.DbName ); - } - } - if(errval < 0) - { - mgError("Error connecting to database\n"); - } - - mgDebug(3, "Initializing track filters"); - switch(m_mediatype) - { - case GD_MP3: - { - errval = GdInitDatabase( &m_db ); // TODO: why duplicate this? LVW - mgDebug(3, "Successfully conntected to sql database %s", the_setup.DbName ); - } - } -} - -mgMedia::~mgMedia() -{ - if( m_filters ) - { - delete m_filters; - } -} - -std::string mgMedia::getMediaTypeName() -{ - switch(m_mediatype) - { - case GD_MP3: - { - return "GiantDisc"; - } break; - } - mgError("implementation Error"); // we should never get here - return ""; -} - -mgSelectionTreeNode* mgMedia::getSelectionRoot() -{ - switch(m_mediatype) - { - case GD_MP3: - return new GdTreeNode(m_db, m_defaultView, m_sql_filter); - } - mgError("implementation Error"); // we should never get here - return NULL; -} - -mgPlaylist* mgMedia::createTemporaryPlaylist() -{ - std::string tmpname = "current"; - return loadPlaylist( tmpname ); -} - -mgPlaylist* mgMedia::loadPlaylist(std::string name) -{ - mgPlaylist *list; - switch( m_mediatype ) - { - case GD_MP3: - { - list = new GdPlaylist( name, m_db ); - list->setDisplayColumns(getDefaultCols()); - - return list; - } break; - } - mgError("Implementation error: Unknown media type"); // we should never get here - return NULL; -} - -/*! - * \brief Obtain a list of stored playlists - */ -std::vector *mgMedia::getStoredPlaylists() -{ - switch(m_mediatype) - { - case GD_MP3: - { - return GdGetStoredPlaylists( m_db ); - } break; - } - mgError("implementation Error"); // we should never get here - return new std::vector(); -} - -/*! - * \brief obtain the indices of columns which are presented by default - */ -std::vector mgMedia::getDefaultCols() -{ - std::vector cols; - switch(m_mediatype) - { - case GD_MP3: - { - cols.push_back(1); // artist - cols.push_back(0); // track - - return cols; - } break; - } - mgError("implementation Error"); // we should never get here - - return cols; -} - -/*! - * \brief - */ -mgTracklist* mgMedia::getTracks() -{ - mgTracklist *tracks; - switch(m_mediatype) - { - case GD_MP3: - tracks = new GdTracklist(m_db, m_sql_filter); - tracks->setDisplayColumns(getDefaultCols()); - return tracks; - } - mgError("implementation Error"); // we should never get here - - return NULL; -} - -/*! - * \brief creates FiliterSetObject for the selected media type - * and activates set n (if available) - */ -void mgMedia::initFilterSet(int num) -{ - switch(m_mediatype) - { - case GD_MP3: - { - m_filters = new gdFilterSets(); - m_filters->select(num); - } break; - } -} - -/*! - ******************************************************************* - * \brief returns pointer to the activen filter set to be modified by the osd - * - * Note: Modifications become only effective by calling applyActiveFilter() - ********************************************************************/ -std::vector *mgMedia::getActiveFilters() -{ - if(!m_filters) - { - mgError("ImplementationError: getActiveFilters m_filters == NULL"); - } - return m_filters->getFilters(); -} - -/*! - * \brief returns title of the active filter set - */ -std::string mgMedia::getActiveFilterTitle() -{ - - switch(m_mediatype) - { - case GD_MP3: - { - if( !m_filters ) - { - mgError("ImplementationError:getActiveFilterTitle m_filters == NULL"); - } - return m_filters->getTitle(); - } break; - } - return ""; -} - -/*! - * \brief proceeds to the next filter set in a cirlucar fashion - */ -void mgMedia::nextFilterSet() -{ - if(!m_filters) - { - mgError("ImplementationError: nextFilterSet m_filters == NULL"); - } - m_filters->nextSet(); -} - -/*! - * \brief clears the current filter values and restores defaults - */ -void mgMedia::clearActiveFilter() -{ - if( !m_filters ) - { - mgError("ImplementationError: clearActiveFilter m_filters == NULL"); - } - m_filters->clear(); -} - -/*! - * \brief Applies the active filter set and returns a root node for the - * selection in the default view for this filter set - */ -mgSelectionTreeNode* mgMedia::applyActiveFilter() -{ - int view; - GdTreeNode* node; - - switch(m_mediatype) - { - case GD_MP3: - { - if(!m_filters) - { - mgError("ImplementationError: applyActiveFilter() m_filters == NULL"); - } - m_filters->accept(); - m_sql_filter = m_filters->computeRestriction(&view); - node = new GdTreeNode(m_db, view, m_sql_filter); - node->expand(); - return node->getChildren()[0]; - } break; - } - return NULL; -} - - -/* -------------------- begin CVS log --------------------------------- - * $Log: mg_media.c,v $ - * Revision 1.14 2004/07/29 06:17:40 lvw - * Added todo entries - * - * Revision 1.13 2004/05/28 15:29:18 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.12 2004/02/23 16:30:58 RaK - * - album search error because of i18n corrected - * - * Revision 1.11 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.10.2.3 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.10.2.2 2004/03/14 17:57:30 lvw - * Linked against libmad. Introduced config options into code. - * - * Revision 1.10.2.1 2004/03/02 07:05:50 lvw - * Initial adaptations from MP3 plugin added (untested) - * - * Revision 1.12 2004/02/23 16:30:58 RaK - * - album search error because of i18n corrected - * - * Revision 1.11 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.10 2004/02/10 23:47:23 RaK - * - views konsitent gemacht. siehe FROMJOIN - * - isLeafNode angepasst fuer neue views 4,5,100,101 - * - like '%abba%' eingebaut - * - filter ist default mit abba gefuellt, zum leichteren testen. - * - search results werden jetzt gleich im ROOT expanded - * - * Revision 1.9 2004/02/09 19:27:52 MountainMan - * filter set implemented - * - * Revision 1.8 2004/02/02 23:33:41 MountainMan - * impementation of gdTrackFilters - * - * Revision 1.7 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * --------------------- end CVS log ---------------------------------- - */ diff --git a/mg_media.h b/mg_media.h deleted file mode 100644 index 90a43f2..0000000 --- a/mg_media.h +++ /dev/null @@ -1,209 +0,0 @@ -/*! - * \file mgmedia.h - * \brief Top level access to media in vdr plugin muggle - * - * \version $Revision: 1.11 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ - */ - -// makes sure we dont use parse the same declarations twice -#ifndef _MG_MEDIA_H -#define _MG_MEDIA_H - -#include -#include - -#include - -class mgPlaylist; -class mgTracklist; -class mgSelectionTreeNode; -class mgFilter; -class mgFilterSets; - -/*! - * \class mgFilterSets - * - * Represents one or several sets of filters to set and memorize search constraint - */ -class mgFilterSets -{ - protected: - int m_activeSetId; - std::vector *m_activeSet; // pointer to the active filter set - - // stores name-value pairs, even if a different set is active - std::vector< std::vector*> m_sets; - - // stores the titles for all filters - std::vector m_titles; - - public: - - /*! - * \brief a constructor - * - * constracts a number >=1 of filter sets the first set (index 0 ) is active by default - */ - mgFilterSets(); - - /*! - * \brief the destructor - */ - virtual ~mgFilterSets(); - - /*! - * \brief returns the number of available sets - */ - int numSets(); - - /*! - * \brief proceeds to the next one in a circular fashion - */ - void nextSet(); - - /*! - * \brief activates a specific set by index - */ - void select(int n); - - /*! - * \brief restore empty state - * - * Restores the default values for all filter values in the active set - * normally, the default values represent 'no restrictions' - */ - virtual void clear(); - - /*! - * \brief stores the current filter values - */ - void accept(); - - /*! - * \brief returns the active set to the application - * - * The application may temporarily modify the filter values - * accept() needs to be called to memorize the changed values - */ - std::vector *getFilters(); - - /*! - * \brief compute restrictions - * - * computes the (e.g. sql-) restrictions specified by the active filter set - * and returns the index of the appropriate defualt view in viewPrt - */ - virtual std::string computeRestriction(int *viewPrt) = 0; - - /*! - * \brief returns title of active filter set - */ - std::string getTitle(); -}; - - -/*! - * \class mgMedia - * - * \brief main class to access content in the vdr plugin muggle - * - * The constructor of this class should be the only point in the plugin, - * where the data type is explicitelymentioned. - * The class provides a set of objects that abstract from the data - * type and source - */ -class mgMedia -{ - - public: - typedef enum contentType - { - GD_MP3 - } contentType; - - private: - MYSQL m_db; - contentType m_mediatype; - std::string m_sql_filter; - int m_defaultView; - mgFilterSets *m_filters; - - public: - mgMedia(contentType mediatype); - ~mgMedia(); - - std::string getMediaTypeName(); - - mgSelectionTreeNode* getSelectionRoot(); - - /*! \brief playlist management */ - //@{ - mgPlaylist* createTemporaryPlaylist(); - mgPlaylist* loadPlaylist( std::string name ); - std::vector *getStoredPlaylists(); - //@} - - std::vector getDefaultCols(); - mgTracklist* getTracks(); - - // filter management - - void initFilterSet(int num=0); - // creates FiliterSetObject for the selected media type - // and activates set n (if available) - - std::vector *getActiveFilters(); - // returns pointer to the activen filter set to be modified by the osd - // Note: Modifications become only active by calling applyActiveFilter() - - std::string getActiveFilterTitle(); - - void nextFilterSet(); - // proceeds to the next filter set in a cirlucar fashion - - void clearActiveFilter(); - // clears the current filter values and restores defaults - - mgSelectionTreeNode *applyActiveFilter(); - // Applies the active filter set and returns a root node for the - // selection in the default view for this filter set - -}; - -/* -------------------- begin CVS log --------------------------------- - * $Log: mg_media.h,v $ - * Revision 1.11 2004/05/28 15:29:18 lvw - * Merged player branch back on HEAD branch. - * - * Revision 1.10 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.9.2.2 2004/05/25 00:10:45 lvw - * Code cleanup and added use of real database source files - * - * Revision 1.9.2.1 2004/03/02 07:05:50 lvw - * Initial adaptations from MP3 plugin added (untested) - * - * Revision 1.10 2004/02/12 09:15:07 LarsAC - * Moved filter classes into separate files - * - * Revision 1.9 2004/02/09 22:07:44 RaK - * secound filter set (album search incl. special view #101 - * - * Revision 1.8 2004/02/09 19:27:52 MountainMan - * filter set implemented - * - * Revision 1.7 2004/02/02 23:33:41 MountainMan - * impementation of gdTrackFilters - * - * Revision 1.6 2004/02/02 22:48:04 MountainMan - * added CVS $Log - * - * - * --------------------- end CVS log ---------------------------------- - */ -#endif /* END _MG_MEDIA_H */ - diff --git a/mg_playlist.c b/mg_playlist.c deleted file mode 100644 index 5ead8f7..0000000 --- a/mg_playlist.c +++ /dev/null @@ -1,368 +0,0 @@ -/*! - * \file mg_playlist.c - * \brief defines functions to be executed on playlists for the vdr muggle plugin - * - * \version $Revision: 1.6 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ - * - * This file implements the class mgPlaylist which maintains a playlist - * and supports editing (e.g. adding or moving tracks), navigating it - * (e.g. obtaining arbitrary items or accessing them sequentially) or - * making it persistent in some database. - */ - -#include -#include "mg_playlist.h" -#include "mg_tools.h" - -#include -#include - -mgPlaylist::mgPlaylist() -{ - m_current_idx = 0; - - char *buffer; - asprintf( &buffer, "Playlist-%ld", random() ); - - m_listname = buffer; -} - -mgPlaylist::mgPlaylist(std::string listname) -{ - m_current_idx = 0; - m_listname = listname; -} - -/* ==== destructor ==== */ - -mgPlaylist::~mgPlaylist() -{ -} - -mgPlaylist::LoopMode mgPlaylist::toggleLoopMode( ) -{ - switch( m_loop_mode ) - { - case LM_NONE: - { - m_loop_mode = LM_SINGLE; - } break; - case LM_SINGLE: - { - m_loop_mode = LM_FULL; - } break; - case LM_FULL: - { - m_loop_mode = LM_NONE; - } break; - default: - { - m_loop_mode = LM_NONE; - } - } - - return m_loop_mode; -} - -mgPlaylist::ShuffleMode mgPlaylist::toggleShuffleMode( ) -{ - switch( m_shuffle_mode ) - { - case SM_NONE: - { - m_shuffle_mode = SM_NORMAL; - } break; - case SM_NORMAL: - { - m_shuffle_mode = SM_PARTY; - } break; - case SM_PARTY: - { - m_shuffle_mode = SM_NONE; - } break; - default: - { - m_shuffle_mode = SM_NONE; - } - } - - return m_shuffle_mode; -} - -void mgPlaylist::initialize() -{ - m_current_idx = 0; - m_loop_mode = mgPlaylist::LM_NONE; - m_shuffle_mode = mgPlaylist::SM_NONE; -} - -/* ==== add/remove tracks ==== */ - -/* adds a song at the end of the playlist */ -void mgPlaylist::append(mgContentItem* item) -{ - m_list.push_back(item); -} - -/* append a list of tracks at the end of the playlist */ -void mgPlaylist::appendList( std::vector *tracks ) -{ - std::vector::iterator iter; - - mgDebug( 3, "Adding %d tracks to the playlist", tracks->size() ); - - for( iter = tracks->begin(); iter != tracks->end(); iter++ ) - { - m_list.push_back(*iter); - } - - // TODO: why is this vector cleared? shouldn't the caller take care of this? LVW - tracks->clear(); -} - -/* add a song after 'position' */ -void mgPlaylist::insert( mgContentItem* item, unsigned int position ) -{ - if( position >= m_list.size() ) - { - m_list.push_back(item); - } - else - { - m_list.insert( m_list.begin() + position, item ); - } -} - -bool mgPlaylist::remove( unsigned pos ) -{ - bool result = false; - - if( pos != m_current_idx ) - { - result = mgTracklist::remove( pos ); - - if( result && pos < m_current_idx ) - { - m_current_idx --; - } - } - - return result; -} - -void mgPlaylist::clear() -{ - // TODO: who takes care of memory allocation/deallocation of mgItems? - - std::vector::iterator iter; - - for( iter = m_list.begin(); iter != m_list.end(); iter++ ) - { // delete each item in the list - delete *iter; - } - - // finally clear the list itself - m_list.clear(); - - // reset index - m_current_idx = 0; -} - -void mgPlaylist::move( unsigned from, unsigned to ) -{ - std::vector::iterator from_iter = m_list.begin() + from; - std::vector::iterator to_iter = m_list.begin() + to; - - m_list.insert( to_iter, *from_iter); - m_list.erase( from_iter ); - - if( from < m_current_idx ) - { - m_current_idx--; - } - if( to < m_current_idx ) - { - m_current_idx++; - } -} - -std::string mgPlaylist::getListname() -{ - return m_listname; -} - -void mgPlaylist::setListname(std::string name) -{ - m_listname = name; -} - -// returns current index in the playlist -unsigned mgPlaylist::getIndex() const -{ - return m_current_idx; -} - -unsigned long mgPlaylist::getCompletedLength() -{ - unsigned long result = 0; - - std::vector::iterator iter; - for( iter = m_list.begin(); iter != m_list.begin() + m_current_idx; iter++ ) - { // each item in the list - result += (*iter)->getLength(); - } - - return result; -} - -// returns the current item of the list -mgContentItem* mgPlaylist::getCurrent() -{ - mgContentItem *result; - - if( 0 <= m_current_idx && m_current_idx < m_list.size() ) - { - result = *( m_list.begin() + m_current_idx ); - } - else - { - result = &(mgContentItem::UNDEFINED); - } - - return result; -} - -// skip to the nth track from the playlist -bool mgPlaylist::gotoPosition(unsigned int position) -{ - bool result = false; - - if( position < m_list.size() ) - { - m_current_idx = position; - result = true; - } - - return result; -} - -// proceeds to the next item -bool mgPlaylist::skipFwd() -{ - bool result = false; - - if( m_loop_mode == mgPlaylist::LM_SINGLE ) - { - result = true; - } - else - { - if( m_current_idx + 1 < m_list.size() ) // unless loop mode - { - m_current_idx ++; - result = true; - } - else - { - if( m_loop_mode == mgPlaylist::LM_FULL ) - { - m_current_idx = 0; - result = true; - } - else - { - result = false; - } - } - } - - // if we are already at the end -- just do nothing unless in loop mode - return result; -} - -// goes back to the previous item -bool mgPlaylist::skipBack() -{ - bool result = false; - - if( m_loop_mode == mgPlaylist::LM_SINGLE ) - { - result = true; - } - else - { - if( m_current_idx > 0 ) - { - m_current_idx --; - result = true; - } - else - { - if( m_loop_mode == mgPlaylist::LM_FULL ) - { - m_current_idx = m_list.size() -1; - result = true; - } - else - { - result = false; - } - } - } - // if we are at the beginning -- just do nothing unless in loop mode - return result; -} - -// get next track, do not update data structures -mgContentItem* mgPlaylist::sneakNext() -{ - mgContentItem* result; - - // TODO: a bug? - if( m_current_idx + 1 <= m_list.size() ) // unless loop mode - { - result = *(m_list.begin() + m_current_idx + 1); - } - else - { - if( m_loop_mode == mgPlaylist::LM_FULL ) - { - result = *(m_list.begin()); - } - else - { - return &(mgContentItem::UNDEFINED); - } - } - - return result; -} - -bool mgPlaylist::exportM3U( std::string m3u_file ) -{ - std::vector::iterator iter; - bool result = true; - - // open a file for writing - FILE *listfile = fopen( m3u_file.c_str(), "w" ); - - if( !listfile ) - { - return false; - } - - fprintf( listfile, "#EXTM3U" ); - - for( iter = m_list.begin(); iter != m_list.end(); iter++ ) - { // each item in the list - fprintf( listfile, "#EXTINF:%d,%s\n", (*iter)->getLength(), (*iter)->getLabel().c_str() ); - fprintf( listfile, "%s", (*iter)->getSourceFile().c_str() ); - } - - fclose( listfile ); - - return result; -} diff --git a/mg_playlist.h b/mg_playlist.h deleted file mode 100644 index 060ebfa..0000000 --- a/mg_playlist.h +++ /dev/null @@ -1,209 +0,0 @@ -/*! - * \file mg_playlist.c - * \brief defines functions to be executed on playlists for the vdr muggle plugin - * - * \version $Revision: 1.6 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author$ - * - * This file implements the class mgPlaylist which maintains a playlist - * and supports editing (e.g. adding or moving tracks), navigating it - * (e.g. obtaining arbitrary items or accessing them sequentially) or - * making it persistent in some database. - */ -#ifndef __MG_PLAYLIST -#define __MG_PLAYLIST - -#include - -#include "mg_content_interface.h" - -/*! - * \class mgPlaylist - * - * \brief Represents a generic playlist, i.e. an ordered collection of tracks - * Derived classes may take care of specifics of certain media types - * - * \todo Are loop mode, shuffle mode, and party mode implemented here? - */ -class mgPlaylist : public mgTracklist -{ - -public: - - //! \brief define various ways to play music in random order - enum ShuffleMode - { - SM_NONE, //!< \brief play normal sequence - SM_NORMAL, //!< \brief a shuffle with a fair distribution - SM_PARTY //!< \brief select the next few songs randomly, continue forever - }; - - //! \brief define various ways to play music in a neverending loop - enum LoopMode - { - LM_NONE, //!< \brief do not loop - LM_SINGLE, //!< \brief loop a single track - LM_FULL //!< \brief loop the whole playlist - }; - - //! \brief object construction and destruction - //@{ - - //! \brief the default constructor (random listname) - mgPlaylist(); - - /*! \brief constructor with a specified listname - * - * \param listname - initial name of the list - */ - mgPlaylist( std::string listname ); - - void initialize(); - - //! \brief the destructor - virtual ~mgPlaylist(); - - //@} - - //! \brief control behavior - //@{ - - //! \brief toggle the loop mode. - LoopMode toggleLoopMode( ); - - //! \brief toggle the shuffle mode. - ShuffleMode toggleShuffleMode( ); - - //! \brief report the loop mode. - LoopMode getLoopMode( ) const { return m_loop_mode; } - - //! \brief report the shuffle mode. - ShuffleMode getShuffleMode( ) const { return m_shuffle_mode; } - //@} - - //! \brief modify playlist items - //@{ - /*! \brief adds a song at the end of the playlist - * - * \param item - the item to be appended - */ - virtual void append(mgContentItem* item); - - /*! \brief add a vector of songs at the end of the playlist - * - * \param tracks - the vector of tracks to be added - */ - virtual void appendList(std::vector *tracks); - - /*! \brief add a song after a specified position - * - * Might be merged with append by using a default argument for the position - * - * \param item - the item to be added - * \param position - the position where the item should be added - */ - virtual void insert(mgContentItem* item, unsigned int position); - - //! \brief clear all tracks - virtual void clear(); - - /*! \brief move tracks within playlist - * - * \param from - the item of the index to be moved - * \param to - the target index of the item - */ - void move( unsigned from, unsigned to ); - - /*! \brief remove a track from the playlist - * - * \param pos - the index of the track to be removed - */ - bool remove( unsigned pos ); - - //@} - - //! \brief obtain the listname - std::string getListname() ; - - /*! \brief set the listname - * - * \param name - the new name of this list - */ - virtual void setListname(std::string name); - - //! \brief access playlist items - //@{ - - //! \brief returns current index in the playlist - unsigned getIndex() const; - - //! \brief make playlist persistent - virtual bool storePlaylist() = 0; - - //! \brief make playlist persistent under a different name - virtual bool storeAs( std::string name ) = 0; - - //! \brief obtain length of content already played - unsigned long getCompletedLength(); - - //! \brief export the playlist in m3u format - virtual bool exportM3U( std::string m3u_file ); - - /*! - * \brief returns the current item of the list - * - * \todo Return null in case of an empty list or invalid index - */ - virtual mgContentItem* getCurrent(); - - /*! \brief returns the nth track from the playlist - * - * \param position - the position to skip to - * \return true if position was okay and changed, false otherwise - */ - virtual bool gotoPosition(unsigned int position); - - /*! - * \brief proceeds to the next item - * - * \return true if position was okay and changed, false otherwise - * \todo Handle play modes - */ - virtual bool skipFwd(); - - /*! - * \brief goes back to the previous item - * - * \return true if position was okay and changed, false otherwise - * \todo Handle play modes - */ - virtual bool skipBack(); - - //! \brief obtain the next item without skipping the current position - virtual mgContentItem* sneakNext(); - //@} - -private: - - //! \brief current index in the playlist - // TODO: should be a property of the player? - unsigned m_current_idx; - - //! \brief the current loop mode - LoopMode m_loop_mode; - - //! \brief the current shuffle mode - ShuffleMode m_shuffle_mode; - -protected: - - // TODO: Why not make these private? Subclasses should use access functions. LVW - - //! \brief the name of the playlist - std::string m_listname; - -}; - -#endif diff --git a/mg_tools.c b/mg_tools.c index 02df30b..3a484c5 100644 --- a/mg_tools.c +++ b/mg_tools.c @@ -1,4 +1,4 @@ -/*! +/*! * \file mg_tools.c * \brief A few util functions for standalone and plugin messaging for the vdr muggle plugindatabase * @@ -12,126 +12,116 @@ /*extern "C" {*/ - #include - #include +#include +#include /*} -*/ + */ #include +//! \brief buffer for messages #define MAX_BUFLEN 2048 -#define MAX_QUERY_BUFLEN 2048 static char buffer[MAX_BUFLEN]; -static char querybuf[MAX_QUERY_BUFLEN]; -static int DEBUG_LEVEL=3; +static int DEBUG_LEVEL = 3; -void mgSetDebugLevel(int new_level) +void +mgSetDebugLevel (int new_level) { - DEBUG_LEVEL = new_level; + DEBUG_LEVEL = new_level; } -void mgDebug(int level, const char *fmt, ...) + +void +mgDebug (int level, const char *fmt, ...) { - - va_list ap; - if(level <= DEBUG_LEVEL) - { - va_start(ap, fmt); - - vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); - if(STANDALONE) - { - fprintf(stderr, "dbg %d: %s\n", level, buffer); - } - else + + va_list ap; + if (level <= DEBUG_LEVEL) { + va_start (ap, fmt); + + vsnprintf (buffer, MAX_BUFLEN - 1, fmt, ap); + if (STANDALONE) + { + fprintf (stderr, "dbg %d: %s\n", level, buffer); + } + else + { #if !STANDALONE - isyslog( "%s\n", buffer); + isyslog ("%s\n", buffer); #endif + } } - } - va_end(ap); + va_end (ap); } -void mgDebug( const char *fmt, ... ) + +void +mgDebug (const char *fmt, ...) { - va_list ap; - va_start( ap, fmt ); - mgDebug( 1, fmt, ap ); + va_list ap; + va_start (ap, fmt); + mgDebug (1, fmt, ap); } -void mgWarning(const char *fmt, ...) +void +mgWarning (const char *fmt, ...) { - - va_list ap; - va_start(ap, fmt); - vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); - - if(STANDALONE) - { - fprintf(stderr, "warning: %s\n", buffer); - } - else + + va_list ap; + va_start (ap, fmt); + vsnprintf (buffer, MAX_BUFLEN - 1, fmt, ap); + + if (STANDALONE) + { + fprintf (stderr, "warning: %s\n", buffer); + } + else { #if !STANDALONE - isyslog( "Warning: %s\n", buffer); + isyslog ("Warning: %s\n", buffer); #endif } - - va_end(ap); + + va_end (ap); } -void mgError(const char *fmt, ...) + +void +mgError (const char *fmt, ...) { - - va_list ap; - va_start(ap, fmt); - vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap); - if(STANDALONE) + va_list ap; + va_start (ap, fmt); + vsnprintf (buffer, MAX_BUFLEN - 1, fmt, ap); + + if (STANDALONE) { - fprintf(stderr, "Error: %s\n", buffer); - exit(1); + fprintf (stderr, "Error: %s\n", buffer); + exit (1); } - else + else { #if !STANDALONE - isyslog( "Error in Muggle: %s\n", buffer); + isyslog ("Error in Muggle: %s\n", buffer); #endif } - va_end(ap); + va_end (ap); } -MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) -{ - va_list ap; - va_start( ap, fmt ); - vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); - - if( mysql_query(db, querybuf) ) - { - mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); - } - - MYSQL_RES *result = mysql_store_result(db); - - va_end(ap); - return result; -} -void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap); - - if( mysql_query(db, querybuf) ) - { - mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); - } - - va_end(ap); +std::string trim(std::string const& source, char const* delims ) { + std::string result(source); + std::string::size_type index = result.find_last_not_of(delims); + if(index != std::string::npos) + result.erase(++index); + index = result.find_first_not_of(delims); + if(index != std::string::npos) + result.erase(0, index); + else + result.erase(); + return result; } diff --git a/mg_tools.h b/mg_tools.h index dda574b..95d4663 100644 --- a/mg_tools.h +++ b/mg_tools.h @@ -1,4 +1,4 @@ -/*! \file muggle_tools.h +/*! \file mg_tools.h * \ingroup muggle * \brief A few utility functions for standalone and plugin messaging for the vdr muggle plugindatabase * @@ -6,7 +6,7 @@ * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author file owner: $Author$ - * + * */ /* makes sure we don't use the same declarations twice */ @@ -17,36 +17,21 @@ #include #include -#define STANDALONE 1 // what's this? - +#define STANDALONE 1 // what's this? -/*! \brief mySql helper function to execute read queries - * \ingroup muggle - * - * \todo Could be a member of mgDatabase? - */ -MYSQL_RES* mgSqlReadQuery( MYSQL *db, const char *fmt, ... ); - -/*! \brief mySql helper function to execute write queries - * \ingroup muggle +/*! + * \brief Logging utilities * - * \todo Could be a member of mgDatabase? - */ -void mgSqlWriteQuery( MYSQL *db, const char *fmt, ... ); - - -/*! - * \brief Logging utilities - * * \todo these could be static members in the mgLog class * \todo code of these functions should be compiled conditionally */ //@{ -void mgSetDebugLevel(int new_level); -void mgDebug(int level, const char *fmt, ...); -void mgDebug( const char *fmt, ... ); -void mgWarning(const char *fmt, ...); -void mgError(const char *fmt, ...); +void mgSetDebugLevel (int new_level); +void mgDebug (int level, const char *fmt, ...); +void mgDebug (const char *fmt, ...); +void mgWarning (const char *fmt, ...); +//! \todo mgError should display the message on the OSD. How? +void mgError (const char *fmt, ...); //@} #ifdef DEBUG @@ -63,38 +48,40 @@ void mgError(const char *fmt, ...); /*! \brief simplified logging class * \ingroup muggle - * + * * Create a local instance at the beginning of the method * and entering/leaving the function will be logged * as constructors/destructors are called. */ class mgLog { - public: - enum - { - LOG, WARNING, ERROR, FATAL - } mgLogLevel; + public: + enum + { + LOG, WARNING, ERROR, FATAL + } mgLogLevel; - std::ostream& getStream() - { - return std::cout; - } - - mgLog( std::string methodname ) : m_methodname( methodname ) - { - getStream() << m_methodname << " entered" << std::endl; - }; + std::ostream & getStream () + { + return std::cout; + } - ~mgLog() - { - getStream() << m_methodname << " terminated" << std::endl; - } + mgLog (std::string methodname):m_methodname (methodname) + { + getStream () << m_methodname << " entered" << std::endl; + }; - private: - - std::string m_methodname; + ~mgLog () + { + getStream () << m_methodname << " terminated" << std::endl; + } + + private: + + std::string m_methodname; }; -#endif /* _MUGGLE_TOOLS_H */ +std::string trim(std::string const& source, char const* delims = " \t\r\n"); + +#endif /* _MUGGLE_TOOLS_H */ diff --git a/muggle.c b/muggle.c index 8346337..f08a1d2 100644 --- a/muggle.c +++ b/muggle.c @@ -1,10 +1,10 @@ -/*! +/*! * \file muggle.c * \brief Implements a plugin for browsing media libraries within VDR * * \version $Revision: 1.10 $ * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald * \author Responsible author: $Author$ * * $Id$ @@ -15,222 +15,224 @@ #include "vdr_menu.h" #include "vdr_setup.h" #include "mg_tools.h" -#include "mg_playlist.h" -#include "mg_content_interface.h" -#include "mg_media.h" +#include "mg_db.h" #include "i18n.h" #include #include -static const char *VERSION = "0.0.8"; -static const char *DESCRIPTION = "Media juggle plugin for VDR"; -static const char *MAINMENUENTRY = "Muggle"; +static const char *VERSION = "0.1.0"; +static const char *DESCRIPTION = "Media juggle plugin for VDR"; +static const char *MAINMENUENTRY = "Muggle"; -static unsigned s_resume_idx = 0; - -const char* mgMuggle::Version(void) -{ - return VERSION; +const char * +mgMuggle::Version (void) +{ + return VERSION; } -const char* mgMuggle::Description(void) -{ - return DESCRIPTION; -} -const char* mgMuggle::MainMenuEntry(void) -{ - return MAINMENUENTRY; -} - -mgMuggle::mgMuggle(void) +const char * +mgMuggle::Description (void) { - // defaults for database arguments - the_setup.DbHost = strdup ("localhost"); - the_setup.DbSocket = NULL; - the_setup.DbPort = 0; - the_setup.DbName = strdup ("GiantDisc"); - the_setup.DbUser = strdup (""); - the_setup.DbPass = strdup (""); - the_setup.GdCompatibility = false; - the_setup.ToplevelDir = strdup ("/mnt/music/"); + return DESCRIPTION; } -mgMuggle::~mgMuggle() -{ - // Clean up after yourself! - // save current playlist as "current" and it will be retrieved at the next startup - if( m_playlist ) - { - m_playlist->storeAs( "current" ); - } -} -const char *mgMuggle::CommandLineHelp(void) +const char * +mgMuggle::MainMenuEntry (void) { - // Return a string that describes all known command line options. - return - " -h HHHH, --host=HHHH specify database host (default is localhost)\n" - " -s SSSS --socket=PATH specify database socket (default is TCP connection)\n" - " -n NNNN, --name=NNNN specify database name (overridden by -g)\n" - " -p PPPP, --port=PPPP specify port of database server (default is )\n" - " -u UUUU, --user=UUUU specify database user (default is )\n" - " -w WWWW, --password=WWWW specify database password (default is empty)\n" - " -t TTTT, --toplevel=TTTT specify toplevel directory for music (default is /mnt/music)\n" - " -g, --giantdisc enable full Giantdisc compatibility mode\n"; + return MAINMENUENTRY; } -bool mgMuggle::ProcessArgs(int argc, char *argv[]) -{ - mgDebug( 1, "mgMuggle::ProcessArgs" ); - - // Implement command line argument processing here if applicable. - static struct option long_options[] = - { - { "host", required_argument, NULL, 'h' }, - { "socket", required_argument, NULL, 's' }, - { "name", required_argument, NULL, 'n' }, - { "port", required_argument, NULL, 'p' }, - { "user", required_argument, NULL, 'u' }, - { "password", required_argument, NULL, 'w' }, - { "toplevel", required_argument, NULL, 't' }, - { "giantdisc", no_argument, NULL, 'g' }, - { NULL } - }; - - int c, option_index = 0; - while( ( c = getopt_long( argc, argv, "gh:s:n:p:t:u:w:", long_options, &option_index ) ) != -1 ) - { - switch (c) - { - case 'h': - { - the_setup.DbHost = strcpyrealloc (the_setup.DbHost, optarg); - } break; - case 's': - { - the_setup.DbSocket = strcpyrealloc (the_setup.DbSocket, optarg); - } break; - case 'n': - { - the_setup.DbName = strcpyrealloc (the_setup.DbName, optarg); - } break; - case 'p': - { - the_setup.DbPort = atoi( optarg ); - } break; - case 'u': - { - the_setup.DbUser = strcpyrealloc (the_setup.DbUser, optarg); - } break; - case 'w': - { - the_setup.DbPass = strcpyrealloc (the_setup.DbPass, optarg); - } break; - case 't': - { - if (optarg[strlen(optarg) - 1] != '/') - { - std::string res = std::string(optarg) + "/"; - the_setup.ToplevelDir = strdup( res.c_str() ); - } - else - { - the_setup.ToplevelDir = strcpyrealloc (the_setup.ToplevelDir, optarg); - } - } break; - case 'g': - { - the_setup.DbName = strcpyrealloc (the_setup.DbName, "GiantDisc"); - the_setup.GdCompatibility = true; - } break; - default: return false; - } - } - return true; +mgMuggle::mgMuggle (void) +{ + main = NULL; +// defaults for database arguments + the_setup.DbHost = strdup ("localhost"); + the_setup.DbSocket = NULL; + the_setup.DbPort = 0; + the_setup.DbName = strdup ("GiantDisc"); + the_setup.DbUser = strdup (""); + the_setup.DbPass = strdup (""); + the_setup.GdCompatibility = false; + the_setup.ToplevelDir = strdup ("/mnt/music/"); } -bool mgMuggle::Initialize(void) + +mgMuggle::~mgMuggle () { - // Initialize any background activities the plugin shall perform. - return true; +// Clean up after yourself! + if (main) main->SaveState(); } -bool mgMuggle::Start(void) + +const char * +mgMuggle::CommandLineHelp (void) { - // Start any background activities the plugin shall perform. - mgSetDebugLevel( 99 ); - RegisterI18n( Phrases ); +// Return a string that describes all known command line options. + return + " -h HHHH, --host=HHHH specify database host (default is localhost)\n" + " -s SSSS --socket=PATH specify database socket (default is TCP connection)\n" + " -n NNNN, --name=NNNN specify database name (overridden by -g)\n" + " -p PPPP, --port=PPPP specify port of database server (default is )\n" + " -u UUUU, --user=UUUU specify database user (default is )\n" + " -w WWWW, --password=WWWW specify database password (default is empty)\n" + " -t TTTT, --toplevel=TTTT specify toplevel directory for music (default is /mnt/music)\n" + " -g, --giantdisc enable full Giantdisc compatibility mode\n"; +} - // Database initialization - m_media = new mgMedia( mgMedia::GD_MP3 ); - m_root = m_media->getSelectionRoot(); - m_playlist = m_media->createTemporaryPlaylist(); - m_media->initFilterSet(); - // Read commands for playlists in etc. /video/muggle/playlist_commands.conf - m_playlist_commands = new cCommands(); +bool mgMuggle::ProcessArgs (int argc, char *argv[]) +{ + mgDebug (1, "mgMuggle::ProcessArgs"); - char *cmd_file = (char *) AddDirectory( cPlugin::ConfigDirectory("muggle"), "playlist_commands.conf" ); - mgDebug( 1, "mgMuggle::Start: Looking for file %s", cmd_file ); - bool have_cmd_file = m_playlist_commands->Load( (const char*) cmd_file ); +// Implement command line argument processing here if applicable. + static struct option + long_options[] = + { + {"host", required_argument, NULL, 'h'}, + {"socket", required_argument, NULL, 's'}, + {"name", required_argument, NULL, 'n'}, + {"port", required_argument, NULL, 'p'}, + {"user", required_argument, NULL, 'u'}, + {"password", required_argument, NULL, 'w'}, + {"toplevel", required_argument, NULL, 't'}, + {"giantdisc", no_argument, NULL, 'g'}, + {NULL} + }; - if( !have_cmd_file ) + int + c, + option_index = 0; + while ((c = + getopt_long (argc, argv, "gh:s:n:p:t:u:w:", long_options, + &option_index)) != -1) { - delete m_playlist_commands; - m_playlist_commands = NULL; + switch (c) + { + case 'h': + { + the_setup.DbHost = strcpyrealloc (the_setup.DbHost, optarg); + } + break; + case 's': + { + the_setup.DbSocket = strcpyrealloc (the_setup.DbSocket, optarg); + } + break; + case 'n': + { + the_setup.DbName = strcpyrealloc (the_setup.DbName, optarg); + } + break; + case 'p': + { + the_setup.DbPort = atoi (optarg); + } + break; + case 'u': + { + the_setup.DbUser = strcpyrealloc (the_setup.DbUser, optarg); + } + break; + case 'w': + { + the_setup.DbPass = strcpyrealloc (the_setup.DbPass, optarg); + } + break; + case 't': + { + if (optarg[strlen (optarg) - 1] != '/') + { + std::string res = std::string (optarg) + "/"; + the_setup.ToplevelDir = strdup (res.c_str ()); + } + else + { + the_setup.ToplevelDir = + strcpyrealloc (the_setup.ToplevelDir, optarg); + } + } + break; + case 'g': + { + the_setup.DbName = strcpyrealloc (the_setup.DbName, "GiantDisc"); + the_setup.GdCompatibility = true; + } + break; + default: + return false; + } } - return true; + return true; } -void mgMuggle::Housekeeping(void) + +bool mgMuggle::Initialize (void) { - // Perform any cleanup or other regular tasks. +// Initialize any background activities the plugin shall perform. + return true; } -cOsdObject *mgMuggle::MainMenuAction(void) -{ - // Perform the action when selected from the main VDR menu. - cOsdObject* osd = new mgMainMenu( m_media, m_root, m_playlist, - m_playlist_commands ); - return osd; +bool mgMuggle::Start (void) +{ +// Start any background activities the plugin shall perform. + mgSetDebugLevel (99); + RegisterI18n (Phrases); + return true; } -cMenuSetupPage *mgMuggle::SetupMenu(void) + +void +mgMuggle::Housekeeping (void) { - return new mgMenuSetup(); +// Perform any cleanup or other regular tasks. } -bool mgMuggle::SetupParse(const char *Name, const char *Value) + +cOsdObject * +mgMuggle::MainMenuAction (void) { - mgDebug( 1, "mgMuggle::SetupParse" ); - - if (!strcasecmp(Name, "InitLoopMode")) the_setup.InitLoopMode = atoi(Value); - else if (!strcasecmp(Name, "InitShuffleMode")) the_setup.InitShuffleMode = atoi(Value); - else if (!strcasecmp(Name, "AudioMode")) the_setup.AudioMode = atoi(Value); - else if (!strcasecmp(Name, "DisplayMode")) the_setup.DisplayMode = atoi(Value); - else if (!strcasecmp(Name, "BackgrMode")) the_setup.BackgrMode = atoi(Value); - else if (!strcasecmp(Name, "TargetLevel")) the_setup.TargetLevel = atoi(Value); - else if (!strcasecmp(Name, "LimiterLevel")) the_setup.LimiterLevel = atoi(Value); - else if (!strcasecmp(Name, "Only48kHz")) the_setup.Only48kHz = atoi(Value); - else return false; - - return true; +// Perform the action when selected from the main VDR menu. + main = new mgMainMenu (); + return main; } -void mgMuggle::setResumeIndex( unsigned index ) +cMenuSetupPage * +mgMuggle::SetupMenu (void) { - s_resume_idx = index; + return new mgMenuSetup (); } -unsigned mgMuggle::getResumeIndex( ) + +bool mgMuggle::SetupParse (const char *Name, const char *Value) { - return s_resume_idx; + if (!strcasecmp (Name, "InitLoopMode")) + the_setup.InitLoopMode = atoi (Value); + else if (!strcasecmp (Name, "InitShuffleMode")) + the_setup.InitShuffleMode = atoi (Value); + else if (!strcasecmp (Name, "AudioMode")) + the_setup.AudioMode = atoi (Value); + else if (!strcasecmp (Name, "DisplayMode")) + the_setup.DisplayMode = atoi (Value); + else if (!strcasecmp (Name, "BackgrMode")) + the_setup.BackgrMode = atoi (Value); + else if (!strcasecmp (Name, "TargetLevel")) + the_setup.TargetLevel = atoi (Value); + else if (!strcasecmp (Name, "LimiterLevel")) + the_setup.LimiterLevel = atoi (Value); + else if (!strcasecmp (Name, "Only48kHz")) + the_setup.Only48kHz = atoi (Value); + else + return false; + + return true; } -VDRPLUGINCREATOR(mgMuggle); // Don't touch this! + +VDRPLUGINCREATOR (mgMuggle); // Don't touch this! diff --git a/muggle.doxygen b/muggle.doxygen index fb0e1d0..4e601ad 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.0.8 +PROJECT_NUMBER = 0.0.9 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/muggle.h b/muggle.h index 6f787e1..171cdb7 100644 --- a/muggle.h +++ b/muggle.h @@ -1,4 +1,4 @@ -/*! +/*! * \file muggle.h * \ingroup vdr * \brief Implements a plugin for browsing media libraries within VDR @@ -26,7 +26,7 @@ * \defgroup muggle Main muggle business * The core of the plugin is an abstract representation of information * organized in trees (thus suitable for OSD navigation) as well as - * means to organize + * means to organize */ #ifndef _MUGGLE_H @@ -34,53 +34,38 @@ #include #include -class mgMedia; -class mgSelectionTreeNode; -class mgPlaylist; +class mgMainMenu; -class cCommands; - -class mgMuggle : public cPlugin +class mgMuggle:public cPlugin { -public: - - mgMuggle(void); - - virtual ~mgMuggle(); + public: - virtual const char *Version(void); + mgMuggle (void); - virtual const char *Description(void); + virtual ~ mgMuggle (); - virtual const char *CommandLineHelp(void); + virtual const char *Version (void); - virtual bool ProcessArgs(int argc, char *argv[]); + virtual const char *Description (void); - virtual bool Initialize(void); + virtual const char *CommandLineHelp (void); - virtual bool Start(void); + virtual bool ProcessArgs (int argc, char *argv[]); - virtual void Housekeeping(void); + virtual bool Initialize (void); - virtual const char *MainMenuEntry(void); + virtual bool Start (void); - virtual cOsdObject *MainMenuAction(void); + virtual void Housekeeping (void); - virtual cMenuSetupPage *SetupMenu(void); + virtual const char *MainMenuEntry (void); - virtual bool SetupParse(const char *Name, const char *Value); + virtual cOsdObject *MainMenuAction (void); - static void setResumeIndex( unsigned index ); + virtual cMenuSetupPage *SetupMenu (void); - static unsigned getResumeIndex( ); - -private: - - mgMedia *m_media; - mgSelectionTreeNode *m_root; - mgPlaylist *m_playlist; - cCommands *m_playlist_commands; + virtual bool SetupParse (const char *Name, const char *Value); + mgMainMenu *main; }; - #endif diff --git a/mugglei.c b/mugglei.c index 46ef6a1..0abc856 100755 --- a/mugglei.c +++ b/mugglei.c @@ -5,7 +5,7 @@ * \author Lars von Wedel */ -#define VERBOSE +// #define VERBOSE #include #include @@ -14,7 +14,13 @@ #include #include #include -#include +/*extern "C" +{*/ + #include + #include +/*} +*/ +#include #include #include @@ -26,6 +32,40 @@ MYSQL *db; std::string host, user, pass, dbname, sck; bool import_assorted, delete_mode; +#define MAX_QUERY_BUFLEN 2048 +static char querybuf[MAX_QUERY_BUFLEN]; + +MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) +{ + va_list ap; + va_start( ap, fmt ); + vsnprintf( querybuf, MAX_QUERY_BUFLEN-1, fmt, ap ); + + if( mysql_query(db, querybuf) ) + { + mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); + } + + MYSQL_RES *result = mysql_store_result(db); + + va_end(ap); + return result; +} + +void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap); + + if( mysql_query(db, querybuf) ) + { + mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); + } + + va_end(ap); +} + int init_database() { db = mysql_init(0); // NULL? @@ -280,13 +320,13 @@ void update_db( long uid, std::string filename ) #ifdef VERBOSE std::cout << "-- TAG --" << std::endl; - std::cout << "title - \"" << tag->title() << "\"" << std::endl; - std::cout << "artist - \"" << tag->artist() << "\"" << std::endl; - std::cout << "album - \"" << tag->album() << "\"" << std::endl; - std::cout << "year - \"" << tag->year() << "\"" << std::endl; - std::cout << "comment - \"" << tag->comment() << "\"" << std::endl; - std::cout << "track - \"" << tag->track() << "\"" << std::endl; - std::cout << "genre - \"" << tag->genre() << "\"" << std::endl; + std::cout << "title - '" << tag->title() << "'" << std::endl; + std::cout << "artist - '" << tag->artist() << "'" << std::endl; + std::cout << "album - '" << tag->album() << "'" << std::endl; + std::cout << "year - '" << tag->year() << "'" << std::endl; + std::cout << "comment - '" << tag->comment() << "'" << std::endl; + std::cout << "track - '" << tag->track() << "'" << std::endl; + std::cout << "genre - '" << tag->genre() << "'" << std::endl; #endif } } @@ -348,7 +388,7 @@ void evaluate_file( std::string filename ) long uid = find_file_in_database( db, filename ); if( uid >= 0 ) { - // currently only update database, do not consider writing changes from the db back + // currently only update database, do not consider writing changes from the db back to tags /* // determine modification times in database and on filesystem time_t db_time = get_db_modification_time( uid ); @@ -457,6 +497,11 @@ int main( int argc, char *argv[] ) } } + if( filename.length() > 255 ) + { + std::cerr << "Warning: length of file exceeds database field capacity: " << filename << std::endl; + } + // init random number generator struct timeval tv; struct timezone tz; diff --git a/scripts/createdb.mysql b/scripts/createdb.mysql index 68c12d7..7ccdc89 100755 --- a/scripts/createdb.mysql +++ b/scripts/createdb.mysql @@ -1,14 +1,14 @@ --- Creates DB and opens it to any user --- Run this mysql macro as root! +/* Creates DB and opens it to any user */ +/* Run this mysql macro as root! */ DROP DATABASE IF EXISTS GiantDisc; CREATE DATABASE GiantDisc; use GiantDisc; --- The first line is useful for granting access to user vdr on all computers in a network. --- grant all privileges on GiantDisc.* to vdr@'%'; +/* The first line is useful for granting access to user vdr on all computers in a network. */ +/* grant all privileges on GiantDisc.* to vdr@'%'; */ --- Grant access to user vdr on the local machine +/* Grant access to user vdr on the local machine */ grant all privileges on GiantDisc.* to vdr@localhost; diff --git a/scripts/createtables.mysql b/scripts/createtables.mysql index 402f66b..079b3bd 100755 --- a/scripts/createtables.mysql +++ b/scripts/createtables.mysql @@ -8,7 +8,7 @@ -- Current Database: GiantDisc -- --- CREATE DATABASE /*!32312 IF NOT EXISTS*/ GiantDisc; +--CREATE DATABASE /*!32312 IF NOT EXISTS*/ GiantDisc; USE GiantDisc; diff --git a/vdr_config.h b/vdr_config.h index 6178a34..20f7d23 100644 --- a/vdr_config.h +++ b/vdr_config.h @@ -1,13 +1,13 @@ -/*! +/*! * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:18 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_config.h,v 1.2 2004/05/28 15:29:18 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -115,5 +115,4 @@ // "/tmp/limiter". The generated file will be about 3MB in size. This option shouldn't // be enabled for day-by-day operation. //#define ACC_DUMP - -#endif //___CONFIG_H +#endif //___CONFIG_H diff --git a/vdr_decoder.c b/vdr_decoder.c index 0384031..9b47fa8 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -25,128 +25,151 @@ #include "vdr_decoder_mp3.h" #include "vdr_decoder_ogg.h" -#include "mg_content_interface.h" +#include "mg_db.h" #include #include // --- mgDecoders --------------------------------------------------------------- -mgMediaType mgDecoders::getMediaType( std::string s ) +mgMediaType mgDecoders::getMediaType (std::string s) { - mgMediaType mt = MT_UNKNOWN; + mgMediaType + mt = MT_UNKNOWN; - // TODO: currently handles only mp3. LVW - char *f = (char *)s.c_str(); - char *p = f + strlen( f ) - 1; // point to the end +// TODO: currently handles only mp3. LVW + char * + f = (char *) s.c_str (); + char * + p = f + strlen (f) - 1; // point to the end - while( p >= f && *p != '.') --p; + while (p >= f && *p != '.') + --p; - if( !strcmp( p, ".mp3" ) ) + if (!strcmp (p, ".mp3")) { - mt = MT_MP3; + mt = MT_MP3; } - else + else { - if( !strcmp( p, ".ogg" ) ) - { - mt = MT_OGG; - } + if (!strcmp (p, ".ogg")) + { + mt = MT_OGG; + } } - return mt; + return mt; } -mgDecoder *mgDecoders::findDecoder( mgContentItem *item ) + +mgDecoder * +mgDecoders::findDecoder (mgContentItem * item) { - mgDecoder *decoder = 0; + mgDecoder *decoder = 0; - std::string filename = item->getSourceFile(); + std::string filename = item->getSourceFile (); - switch( getMediaType( filename ) ) + struct stat st; + if (stat (filename.c_str (), &st)) + { + esyslog ("ERROR: no valid decoder found for %s", filename.c_str ()); + return 0; + } + switch (getMediaType (filename)) { - case MT_MP3: - { - decoder = new mgMP3Decoder( item ); - } break; + case MT_MP3: + { + decoder = new mgMP3Decoder (item); + } + break; #ifdef HAVE_VORBISFILE - case MT_OGG: - { - decoder = new mgOggDecoder( item ); - } break; + case MT_OGG: + { + decoder = new mgOggDecoder (item); + } + break; #endif - /* - case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; - #ifdef HAVE_SNDFILE - case MT_SND: decoder = new cSndDecoder(full); break; - #endif - */ - default: - { - esyslog("ERROR: unknown media type" ); - } break; +/* + case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; + #ifdef HAVE_SNDFILE + case MT_SND: decoder = new cSndDecoder(full); break; + #endif + */ + default: + { + esyslog ("ERROR: unknown media type"); + } + break; } - - if( decoder && !decoder->valid() ) + + if (decoder && !decoder->valid ()) { - // no decoder found or decoder doesn't match - - delete decoder; // might be carried out on NULL pointer! - decoder = 0; - - esyslog("ERROR: no valid decoder found for %s", filename.c_str() ); +// no decoder found or decoder doesn't match + + delete decoder; // might be carried out on NULL pointer! + decoder = 0; + + esyslog ("ERROR: no valid decoder found for %s", filename.c_str ()); } - return decoder; + return decoder; } + // --- mgDecoder ---------------------------------------------------------------- -mgDecoder::mgDecoder( mgContentItem *item ) +mgDecoder::mgDecoder (mgContentItem * item) { - m_item = item; - m_locked = 0; - m_urgentLock = false; - m_playing = false; + m_item = item; + m_locked = 0; + m_urgentLock = false; + m_playing = false; } -mgDecoder::~mgDecoder() + +mgDecoder::~mgDecoder () { } -void mgDecoder::lock(bool urgent) + +void +mgDecoder::lock (bool urgent) { - m_locklock.Lock(); + m_locklock.Lock (); - if( urgent && m_locked ) + if (urgent && m_locked) { - m_urgentLock = true; // signal other locks to release quickly + m_urgentLock = true; // signal other locks to release quickly } - m_locked ++; + m_locked++; - m_locklock.Unlock(); // don't hold the "locklock" when locking "lock", may cause a deadlock - m_lock.Lock(); - m_urgentLock = false; + m_locklock.Unlock (); // don't hold the "locklock" when locking "lock", may cause a deadlock + m_lock.Lock (); + m_urgentLock = false; } -void mgDecoder::unlock(void) + +void +mgDecoder::unlock (void) { - m_locklock.Lock(); + m_locklock.Lock (); - m_locked--; + m_locked--; - m_lock.Unlock(); - m_locklock.Unlock(); + m_lock.Unlock (); + m_locklock.Unlock (); } -bool mgDecoder::tryLock(void) + +bool mgDecoder::tryLock (void) { - bool res = false; - m_locklock.Lock(); + bool + res = false; + m_locklock.Lock (); - if( !m_locked && !m_playing ) + if (!m_locked && !m_playing) { - lock(); - res = true; + lock (); + res = true; } - m_locklock.Unlock(); - return res; + m_locklock.Unlock (); + return res; } diff --git a/vdr_decoder.h b/vdr_decoder.h index a673cbc..ee943ce 100644 --- a/vdr_decoder.h +++ b/vdr_decoder.h @@ -10,7 +10,7 @@ * * $Id$ * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ @@ -29,143 +29,142 @@ class mgContentItem; // --------From decoder_core.h ------------------------------------ -/*! +/*! * \brief The current status of the decoder * \ingroup vdr */ enum eDecodeStatus - { - dsOK=0, dsPlay, dsSkip, dsEof, dsError, dsSoftError - }; +{ + dsOK = 0, dsPlay, dsSkip, dsEof, dsError, dsSoftError +}; // ---------------------------------------------------------------- -/*! +/*! * \brief A data structure to put decoded PCM data * \ingroup vdr */ struct mgDecode { - eDecodeStatus status; - int index; - struct mad_pcm *pcm; + eDecodeStatus status; + int index; + struct mad_pcm *pcm; }; // ---------------------------------------------------------------- -/*! +/*! * \brief Information about ??? * \ingroup vdr */ class mgPlayInfo { -public: - int m_index, m_total; + public: + int m_index, m_total; }; // ---------------------------------------------------------------- -/*! +/*! * \brief Media types * \ingroup vdr */ enum mgMediaType { - MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC, MT_UNKNOWN + MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC, MT_UNKNOWN }; -/*! +/*! * \brief A generic decoder class to handle conversion into PCM format * \ingroup vdr */ class mgDecoder { -protected: + protected: - /*! \brief database handle to the track being decoded */ - mgContentItem *m_item; +/*! \brief database handle to the track being decoded */ + mgContentItem * m_item; - /*! \brief The currently playing file */ - std::string m_filename; +/*! \brief The currently playing file */ + std::string m_filename; - /*! \brief Mutexes to coordinate threads */ - cMutex m_lock, m_locklock; - int m_locked; - bool m_urgentLock; - - /*! \brief Whether the decoder is currently active */ - bool m_playing; +/*! \brief Mutexes to coordinate threads */ + cMutex m_lock, m_locklock; + int m_locked; + bool m_urgentLock; - /*! \brief ??? */ - mgPlayInfo m_playinfo; +/*! \brief Whether the decoder is currently active */ + bool m_playing; - /*! \brief Place a lock */ - virtual void lock( bool urgent = false ); +/*! \brief ??? */ + mgPlayInfo m_playinfo; - /*! \brief Release a lock */ - virtual void unlock(void); +/*! \brief Place a lock */ + virtual void lock (bool urgent = false); - /*! \brief Try to obtain a lock */ - virtual bool tryLock(void); +/*! \brief Release a lock */ + virtual void unlock (void); -public: +/*! \brief Try to obtain a lock */ + virtual bool tryLock (void); - //@{ - /*! \brief The constructor */ - mgDecoder( mgContentItem *item ); + public: - /*! \brief The destructor */ - virtual ~mgDecoder(); - //@} +//@{ +/*! \brief The constructor */ + mgDecoder (mgContentItem * item); - /*! \brief Whether a decoder instance is able to play the given file */ - virtual bool valid() = 0; +/*! \brief The destructor */ + virtual ~ mgDecoder (); +//@} - /*! \brief Whether a stream (i.e. from the network is being decoded */ - virtual bool isStream() - { - return false; - } +/*! \brief Whether a decoder instance is able to play the given file */ + virtual bool valid () = 0; - /*! \brief Start decoding */ - virtual bool start() = 0; +/*! \brief Whether a stream (i.e. from the network is being decoded */ + virtual bool isStream () + { + return false; + } - /*! \brief Stop decoding */ - virtual bool stop() = 0; +/*! \brief Start decoding */ + virtual bool start () = 0; - /*! \brief Skip an amount of time. Impossible by default */ - virtual bool skip( int seconds, int avail, int rate) - { - return false; - } +/*! \brief Stop decoding */ + virtual bool stop () = 0; - /*! \brief Return decoded data */ - virtual struct mgDecode *decode() = 0; +/*! \brief Skip an amount of time. Impossible by default */ + virtual bool skip (int seconds, int avail, int rate) + { + return false; + } - /*! \brief Information about the current playback status */ - virtual mgPlayInfo *playInfo() - { - return 0; - } +/*! \brief Return decoded data */ + virtual struct mgDecode *decode () = 0; + +/*! \brief Information about the current playback status */ + virtual mgPlayInfo *playInfo () + { + return 0; + } }; - + // ---------------------------------------------------------------- -/*! +/*! * \brief A generic decoder manager class to handle different decoders */ class mgDecoders { -public: - - /*! \brief Try to find a valid decoder for a file - */ - static mgDecoder *findDecoder( mgContentItem *item ); + public: - /*! \brief determine the media type for a given source - */ - static mgMediaType getMediaType( std::string filename ); +/*! \brief Try to find a valid decoder for a file + */ + static mgDecoder *findDecoder (mgContentItem * item); -}; +/*! \brief determine the media type for a given source + */ + static mgMediaType getMediaType (std::string filename); -#endif //___DECODER_H +}; +#endif //___DECODER_H diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c index 6f2d7f6..d914dcd 100644 --- a/vdr_decoder_mp3.c +++ b/vdr_decoder_mp3.c @@ -9,7 +9,7 @@ * * $Id$ * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ @@ -27,393 +27,435 @@ #include "vdr_decoder_mp3.h" #include "vdr_stream.h" -#include "mg_content_interface.h" #include "mg_tools.h" +#include "mg_db.h" #define d(x) x - // ---------------------------------------------------------------- -int mgMadStream(struct mad_stream *stream, mgStream *str) +int +mgMadStream (struct mad_stream *stream, mgStream * str) { - unsigned char *data; - unsigned long len; - if( str->stream( data, len, stream->next_frame ) ) + unsigned char *data; + unsigned long len; + if (str->stream (data, len, stream->next_frame)) { - if( len > 0 ) - { - mad_stream_buffer(stream, data, len); - } - return len; + if (len > 0) + { + mad_stream_buffer (stream, data, len); + } + return len; } - return -1; + return -1; } + // --- mgMP3Decoder ------------------------------------------------------------- -mgMP3Decoder::mgMP3Decoder( mgContentItem *item, bool preinit) - : mgDecoder(item) +mgMP3Decoder::mgMP3Decoder (mgContentItem * item, bool preinit):mgDecoder +(item) { - m_stream = 0; - m_isStream = false; - m_filename = item->getSourceFile(); + m_stream = 0; + m_isStream = false; + m_filename = item->getSourceFile (); - if( preinit ) + if (preinit) { - m_stream = new mgStream( m_filename ); + m_stream = new mgStream (m_filename); + printf ("m_stream for %s\n", m_filename.c_str ()); + } - m_madframe = 0; - m_madsynth = 0; + m_madframe = 0; + m_madsynth = 0; - memset( &m_madstream, 0, sizeof(m_madstream) ); + memset (&m_madstream, 0, sizeof (m_madstream)); - init(); + init (); } -mgMP3Decoder::~mgMP3Decoder() + +mgMP3Decoder::~mgMP3Decoder () { - clean(); - delete m_stream; + clean (); + delete m_stream; } -void mgMP3Decoder::init() + +void +mgMP3Decoder::init () { - clean(); - mad_stream_init( &m_madstream ); + clean (); + mad_stream_init (&m_madstream); - m_madframe = new mad_frame; - mad_frame_init( m_madframe ); + m_madframe = new mad_frame; + mad_frame_init (m_madframe); - m_madsynth = new mad_synth; - mad_synth_init( m_madsynth ); + m_madsynth = new mad_synth; + mad_synth_init (m_madsynth); - mad_stream_options( &m_madstream, MAD_OPTION_IGNORECRC); + mad_stream_options (&m_madstream, MAD_OPTION_IGNORECRC); - m_playtime = mad_timer_zero; - m_skiptime = mad_timer_zero; - m_framenum = m_framemax = 0; - m_mute = m_errcount = 0; + m_playtime = mad_timer_zero; + m_skiptime = mad_timer_zero; + m_framenum = m_framemax = 0; + m_mute = m_errcount = 0; } -void mgMP3Decoder::clean() + +void +mgMP3Decoder::clean () { - m_playing = false; - if( m_madsynth ) + m_playing = false; + if (m_madsynth) { - mad_synth_finish( m_madsynth ); - delete m_madsynth; - m_madsynth = 0; + mad_synth_finish (m_madsynth); + delete m_madsynth; + m_madsynth = 0; } - if( m_madframe ) - { - mad_frame_finish( m_madframe ); - delete m_madframe; - m_madframe = 0; + if (m_madframe) + { + mad_frame_finish (m_madframe); + delete m_madframe; + m_madframe = 0; } - mad_stream_finish( &m_madstream ); + mad_stream_finish (&m_madstream); } -bool mgMP3Decoder::valid(void) + +bool mgMP3Decoder::valid (void) { - bool res = false; - if( tryLock() ) + bool + res = false; + if (tryLock ()) { - if( start() ) - { - struct mgDecode *dd; - int count = 10; - do - { - dd = decode(); - if( dd->status == dsEof ) - { - count=0; - } - if( dd->status != dsPlay) - { - break; - } - } while( --count ); - if( !count ) - { - res=true; - } - stop(); - } - unlock(); + if (start ()) + { + struct mgDecode * + dd; + int + count = 10; + do + { + dd = decode (); + if (dd->status == dsEof) + { + count = 0; + } + if (dd->status != dsPlay) + { + break; + } + } + while (--count); + if (!count) + { + res = true; + } + stop (); + } + unlock (); } - return res; + return res; } -mgPlayInfo *mgMP3Decoder::playInfo() + +mgPlayInfo * +mgMP3Decoder::playInfo () { - if( m_playing ) + if (m_playing) { - m_playinfo.m_index = mad_timer_count( m_playtime, MAD_UNITS_SECONDS ); + m_playinfo.m_index = mad_timer_count (m_playtime, MAD_UNITS_SECONDS); - return &m_playinfo; + return &m_playinfo; } - return 0; + return 0; } -bool mgMP3Decoder::start() + +bool mgMP3Decoder::start () { - lock(true); - init(); - m_playing = true; + lock (true); + init (); + m_playing = true; - if( m_stream->open(true) ) + if (m_stream->open (true)) { - if(!m_isStream) - { - m_stream->seek(); - - printf( "mgMP3Decoder::start: m_framemax not determined, rewinding disabled!!!\n" ); - - /* m_framemax = scan->Frames+20; // TODO - - m_frameinfo = new struct FrameInfo[m_framemax]; - if(!m_frameinfo) - { - printf( "mgMP3Decoder::start: no memory for frame index, rewinding disabled" ); - } - */ - } - unlock(); - - printf( "mgMP3Decoder::start: true\n" ); - return true; + if (!m_isStream) + { + m_stream->seek (); + + printf + ("mgMP3Decoder::start: m_framemax not determined, rewinding disabled!!!\n"); + +/* m_framemax = scan->Frames+20; // TODO + + m_frameinfo = new struct FrameInfo[m_framemax]; + if(!m_frameinfo) + { + printf( "mgMP3Decoder::start: no memory for frame index, rewinding disabled" ); + } + */ + } + unlock (); + + printf ("mgMP3Decoder::start: true\n"); + return true; } - m_stream->close(); - clean(); - unlock(); - - printf( "mgMP3Decoder::start: false" ); - return false; + m_stream->close (); + clean (); + unlock (); + printf ("mgMP3Decoder::start: false"); + return false; } -bool mgMP3Decoder::stop(void) + +bool mgMP3Decoder::stop (void) { - lock(); + lock (); - if( m_playing ) + if (m_playing) { - m_stream->close(); - clean(); + m_stream->close (); + clean (); } - unlock(); - return true; + unlock (); + return true; } -struct mgDecode *mgMP3Decoder::done(eDecodeStatus status) + +struct mgDecode * +mgMP3Decoder::done (eDecodeStatus status) { - m_ds.status = status; - m_ds.index = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS ); - m_ds.pcm = &m_madsynth->pcm; - unlock(); // release the lock from Decode() + m_ds.status = status; + m_ds.index = mad_timer_count (m_playtime, MAD_UNITS_MILLISECONDS); + m_ds.pcm = &m_madsynth->pcm; + unlock (); // release the lock from Decode() - return &m_ds; + return &m_ds; } -eDecodeStatus mgMP3Decoder::decodeError(bool hdr) + +eDecodeStatus mgMP3Decoder::decodeError (bool hdr) { - if( m_madstream.error == MAD_ERROR_BUFLEN || m_madstream.error == MAD_ERROR_BUFPTR ) + if (m_madstream.error == MAD_ERROR_BUFLEN + || m_madstream.error == MAD_ERROR_BUFPTR) { - int s = mgMadStream( &m_madstream, m_stream ); - if( s < 0 ) - { - printf( "mgMP3Decoder::decodeError: dsError returned\n" ); - return dsError; - } - if( s == 0 ) - { - printf( "mgMP3Decoder::decodeError: dsEof returned\n" ); - return dsEof; - } + int + s = mgMadStream (&m_madstream, m_stream); + if (s < 0) + { + printf ("mgMP3Decoder::decodeError: dsError returned\n"); + return dsError; + } + if (s == 0) + { + printf ("mgMP3Decoder::decodeError: dsEof returned\n"); + return dsEof; + } } - else if( !MAD_RECOVERABLE( m_madstream.error ) ) + else if (!MAD_RECOVERABLE (m_madstream.error)) { - printf( "mgMP3Decoder::decodeError: mad decode %sfailed, frame=%d: %s. Returning dsError\n", - hdr? "hdr " : "", m_framenum, - mad_stream_errorstr( &m_madstream ) ); - return dsError; + printf + ("mgMP3Decoder::decodeError: mad decode %sfailed, frame=%d: %s. Returning dsError\n", + hdr ? "hdr " : "", m_framenum, mad_stream_errorstr (&m_madstream)); + return dsError; } - else - { - m_errcount += hdr? 1: 100; - printf( "mgMP3Decoder::decodeError: mad decode %s error, frame=%d count=%d: %s. Returning dsOK\n", - hdr? "hdr ": "", m_framenum, m_errcount, - mad_stream_errorstr( &m_madstream ) ); + else + { + m_errcount += hdr ? 1 : 100; + printf + ("mgMP3Decoder::decodeError: mad decode %s error, frame=%d count=%d: %s. Returning dsOK\n", + hdr ? "hdr " : "", m_framenum, m_errcount, + mad_stream_errorstr (&m_madstream)); } - return dsOK; + return dsOK; } -struct mgDecode *mgMP3Decoder::decode() + +struct mgDecode * +mgMP3Decoder::decode () { - lock(); // this is released in Done() - eDecodeStatus r; + lock (); // this is released in Done() + eDecodeStatus r; - while( m_playing ) + while (m_playing) { - if( m_errcount >= MAX_FRAME_ERR*100 ) - { - printf( "mgMP3Decoder::decode: excessive decoding errors," - " aborting file %s\n", m_filename.c_str() ); - return done(dsError); - } - - if( mad_header_decode( &m_madframe->header, &m_madstream) == -1) - { - if( (r = decodeError(true) ) ) - { - return done(r); - } - } - else - { - if(!m_isStream) - { + if (m_errcount >= MAX_FRAME_ERR * 100) + { + printf ("mgMP3Decoder::decode: excessive decoding errors," + " aborting file %s\n", m_filename.c_str ()); + return done (dsError); + } + + if (mad_header_decode (&m_madframe->header, &m_madstream) == -1) + { + if ((r = decodeError (true))) + { + return done (r); + } + } + else + { + if (!m_isStream) + { #ifdef DEBUG2 - if( m_framenum >= m_framemax ) - { - printf( "mgMP3Decoder::start: framenum >= framemax!!!!\n" ); - } + if (m_framenum >= m_framemax) + { + printf ("mgMP3Decoder::start: framenum >= framemax!!!!\n"); + } #endif - if( m_frameinfo && m_framenum < m_framemax ) - { - m_frameinfo[m_framenum].Pos = - m_stream->bufferPos() + - ( m_madstream.this_frame - m_madstream.buffer ); - m_frameinfo[m_framenum].Time = m_playtime; - } - } - - mad_timer_add( &m_playtime, m_madframe->header.duration); - m_framenum ++; - - if( mad_timer_compare(m_playtime, m_skiptime) >= 0 ) - { - m_skiptime = mad_timer_zero; - } - else - { - return done(dsSkip); // skipping, decode next header - } - - if( mad_frame_decode( m_madframe, &m_madstream ) == -1 ) - { - if( ( r = decodeError(false) ) ) - { - return done(r); - } - } - else - { - m_errcount = 0; - - // TODO: // m_scan->InfoHook( &frame->header ); - - mad_synth_frame( m_madsynth, m_madframe); - - if( m_mute ) - { - m_mute--; - return done( dsSkip ); - } - return done( dsPlay ); - } - } + if (m_frameinfo && m_framenum < m_framemax) + { + m_frameinfo[m_framenum].Pos = + m_stream->bufferPos () + + (m_madstream.this_frame - m_madstream.buffer); + m_frameinfo[m_framenum].Time = m_playtime; + } + } + + mad_timer_add (&m_playtime, m_madframe->header.duration); + m_framenum++; + + if (mad_timer_compare (m_playtime, m_skiptime) >= 0) + { + m_skiptime = mad_timer_zero; + } + else + { + return done (dsSkip); // skipping, decode next header + } + + if (mad_frame_decode (m_madframe, &m_madstream) == -1) + { + if ((r = decodeError (false))) + { + return done (r); + } + } + else + { + m_errcount = 0; + +// TODO: // m_scan->InfoHook( &frame->header ); + + mad_synth_frame (m_madsynth, m_madframe); + + if (m_mute) + { + m_mute--; + return done (dsSkip); + } + return done (dsPlay); + } + } } - return done( dsError ); + return done (dsError); } -void mgMP3Decoder::makeSkipTime( mad_timer_t *skiptime, - mad_timer_t playtime, - int secs, int avail, int dvbrate) + +void +mgMP3Decoder::makeSkipTime (mad_timer_t * skiptime, +mad_timer_t playtime, +int secs, int avail, int dvbrate) { - mad_timer_t time; + mad_timer_t time; - *skiptime = playtime; + *skiptime = playtime; - mad_timer_set( &time, abs(secs), 0, 0 ); + mad_timer_set (&time, abs (secs), 0, 0); - if( secs < 0 ) + if (secs < 0) { - mad_timer_negate(&time); + mad_timer_negate (&time); } - mad_timer_add( skiptime, time ); + mad_timer_add (skiptime, time); - float bufsecs = (float)avail / (float)(dvbrate * (16/8 * 2)); // Byte/s = samplerate * 16 bit * 2 chan + // Byte/s = samplerate * 16 bit * 2 chan + float bufsecs = (float) avail / (float) (dvbrate * (16 / 8 * 2)); - printf( "mgMP3Decoder::makeSkipTime: skip: avail=%d bufsecs=%f\n", avail, bufsecs ); + printf ("mgMP3Decoder::makeSkipTime: skip: avail=%d bufsecs=%f\n", avail, + bufsecs); - int full = (int)bufsecs; - bufsecs -= (float)full; + int full = (int) bufsecs; + bufsecs -= (float) full; - mad_timer_set( &time, full, (int)(bufsecs*1000.0), 1000); + mad_timer_set (&time, full, (int) (bufsecs * 1000.0), 1000); - mad_timer_negate(&time); + mad_timer_negate (&time); - mad_timer_add( skiptime, time ); + mad_timer_add (skiptime, time); - printf( "mgMP3Decoder::makeSkipTime: skip: playtime=%ld secs=%d full=%d bufsecs=%f skiptime=%ld\n", - mad_timer_count(playtime,MAD_UNITS_MILLISECONDS), - secs, full, bufsecs, - mad_timer_count(*skiptime,MAD_UNITS_MILLISECONDS ) ); + printf + ("mgMP3Decoder::makeSkipTime: skip: playtime=%ld secs=%d full=%d bufsecs=%f skiptime=%ld\n", + mad_timer_count (playtime, MAD_UNITS_MILLISECONDS), secs, full, bufsecs, + mad_timer_count (*skiptime, MAD_UNITS_MILLISECONDS)); } -bool mgMP3Decoder::skip( int seconds, int avail, int rate ) + +bool mgMP3Decoder::skip (int seconds, int avail, int rate) { - lock(); + lock (); - bool res = false; - if( m_playing && !m_isStream ) + bool + res = false; + if (m_playing && !m_isStream) { - if( !mad_timer_compare( m_skiptime, mad_timer_zero ) ) - { // allow only one skip at any time - mad_timer_t time; - makeSkipTime( &time, m_playtime, seconds, avail, rate); - - if( mad_timer_compare( m_playtime, time ) <= 0 ) - { // forward skip + if (!mad_timer_compare (m_skiptime, mad_timer_zero)) + { // allow only one skip at any time + mad_timer_t + time; + makeSkipTime (&time, m_playtime, seconds, avail, rate); + + if (mad_timer_compare (m_playtime, time) <= 0) + { // forward skip #ifdef DEBUG - int i = mad_timer_count( time, MAD_UNITS_SECONDS ); - printf( "mgMP3Decoder::skip: forward skipping to %02d:%02d\n", i/60, i%60 ); + int + i = mad_timer_count (time, MAD_UNITS_SECONDS); + printf ("mgMP3Decoder::skip: forward skipping to %02d:%02d\n", + i / 60, i % 60); #endif - m_skiptime = time; - m_mute=1; - res = true; - } - else - { // backward skip - if( m_frameinfo ) - { + m_skiptime = time; + m_mute = 1; + res = true; + } + else + { // backward skip + if (m_frameinfo) + { #ifdef DEBUG - int i = mad_timer_count( time, MAD_UNITS_SECONDS ); - printf( "mgMP3Decoder::skip: rewinding to %02d:%02d\n", i/60, i%60 ); + int + i = mad_timer_count (time, MAD_UNITS_SECONDS); + printf ("mgMP3Decoder::skip: rewinding to %02d:%02d\n", + i / 60, i % 60); #endif - while( m_framenum && mad_timer_compare( time, m_frameinfo[--m_framenum].Time) < 0) ; - m_mute = 2; - if( m_framenum >= 2) - { - m_framenum-=2; - } - m_playtime = m_frameinfo[m_framenum].Time; - m_stream->seek( m_frameinfo[m_framenum].Pos ); - mad_stream_finish( &m_madstream ); // reset stream buffer - mad_stream_init( &m_madstream ); + while (m_framenum + && mad_timer_compare (time, + m_frameinfo[--m_framenum]. + Time) < 0); + m_mute = 2; + if (m_framenum >= 2) + { + m_framenum -= 2; + } + m_playtime = m_frameinfo[m_framenum].Time; + m_stream->seek (m_frameinfo[m_framenum].Pos); + // reset stream buffer + mad_stream_finish (&m_madstream); + mad_stream_init (&m_madstream); #ifdef DEBUG - i = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS ); - printf( "mgMP3Decoder::skip: new playtime=%d framenum=%d filepos=%lld\n", i, m_framenum, m_frameinfo[m_framenum].Pos); + i = mad_timer_count (m_playtime, MAD_UNITS_MILLISECONDS); + printf + ("mgMP3Decoder::skip: new playtime=%d framenum=%d filepos=%lld\n", + i, m_framenum, m_frameinfo[m_framenum].Pos); #endif - res = true; - } - } - } + res = true; + } + } + } } - unlock(); - return res; + unlock (); + return res; } diff --git a/vdr_decoder_mp3.h b/vdr_decoder_mp3.h index f9f3bde..47d6eb2 100644 --- a/vdr_decoder_mp3.h +++ b/vdr_decoder_mp3.h @@ -9,7 +9,7 @@ * * $Id$ * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ @@ -37,79 +37,78 @@ class mgContentItem; /*! * \brief A class to decode mp3 songs into PCM using libmad */ -class mgMP3Decoder : public mgDecoder +class mgMP3Decoder:public mgDecoder { -private: - struct mgDecode m_ds; + private: + struct mgDecode m_ds; - // - struct mad_stream m_madstream; - struct mad_frame *m_madframe; - struct mad_synth *m_madsynth; - mad_timer_t m_playtime, m_skiptime; +// + struct mad_stream m_madstream; + struct mad_frame *m_madframe; + struct mad_synth *m_madsynth; + mad_timer_t m_playtime, m_skiptime; - // - struct FrameInfo - { - unsigned long long Pos; - mad_timer_t Time; - } *m_frameinfo; +// + struct FrameInfo + { + unsigned long long Pos; + mad_timer_t Time; + } *m_frameinfo; - int m_framenum, m_framemax, m_errcount, m_mute; + int m_framenum, m_framemax, m_errcount, m_mute; - void init(); + void init (); - void clean(); + void clean (); - struct mgDecode *done( eDecodeStatus status ); + struct mgDecode *done (eDecodeStatus status); - virtual mgPlayInfo *playInfo(); + virtual mgPlayInfo *playInfo (); - eDecodeStatus decodeError(bool hdr); - - void makeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime, - int secs, int avail, int dvbrate); + eDecodeStatus decodeError (bool hdr); -protected: - mgStream *m_stream; - bool m_isStream; + void makeSkipTime (mad_timer_t * skiptime, mad_timer_t playtime, + int secs, int avail, int dvbrate); -public: + protected: + mgStream * m_stream; + bool m_isStream; - /*! - * \brief construct a decoder from a filename - */ - mgMP3Decoder( mgContentItem *item, bool preinit = true ); + public: - /*! - * \brief the destructor - */ - virtual ~mgMP3Decoder(); +/*! + * \brief construct a decoder from a filename + */ + mgMP3Decoder (mgContentItem * item, bool preinit = true); + +/*! + * \brief the destructor + */ + virtual ~ mgMP3Decoder (); - /*! - * \brief check, whether the file contains useable MP3 content - */ - virtual bool valid(); +/*! + * \brief check, whether the file contains useable MP3 content + */ + virtual bool valid (); - /*! - * \brief start the decoding process - */ - virtual bool start(); +/*! + * \brief start the decoding process + */ + virtual bool start (); - /*! - * \brief stop the decoding process - */ - virtual bool stop(); +/*! + * \brief stop the decoding process + */ + virtual bool stop (); - /*! - * \brief skip an amount of seconds - */ - virtual bool skip( int seconds, int avail, int rate ); +/*! + * \brief skip an amount of seconds + */ + virtual bool skip (int seconds, int avail, int rate); - /*! - * \brief the actual decoding function (uses libmad) - */ - virtual struct mgDecode *decode(); +/*! + * \brief the actual decoding function (uses libmad) + */ + virtual struct mgDecode *decode (); }; - -#endif //___DECODER_MP3_H +#endif //___DECODER_MP3_H diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c index 94bd37d..d720c81 100644 --- a/vdr_decoder_ogg.c +++ b/vdr_decoder_ogg.c @@ -1,14 +1,13 @@ /*! \file vdr_decoder_ogg.c * \ingroup vdr - * + * * The file implements a decoder which is used by the player to decode ogg vorbis audio files. * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001-2003 Stefan Huelswitt */ - #ifdef HAVE_VORBISFILE #include "vdr_decoder_ogg.h" @@ -20,373 +19,441 @@ #include #include -#include "mg_content_interface.h" - +#include "mg_db.h" // --- mgOggFile ---------------------------------------------------------------- -class mgOggFile // : public mgFileInfo +class mgOggFile // : public mgFileInfo { - private: + private: + + bool m_opened, m_canSeek; - bool m_opened, m_canSeek; + OggVorbis_File vf; - OggVorbis_File vf; + void error (const char *action, const int err); - void error( const char *action, const int err ); + std::string m_filename; - std::string m_filename; + public: - public: + mgOggFile (std::string filename); + ~mgOggFile (); - mgOggFile( std::string filename ); - ~mgOggFile(); + bool open (bool log = true); - bool open(bool log=true); + void close (void); - void close(void); + long long seek (long long posMs = 0, bool relativ = false); - long long seek(long long posMs=0, bool relativ=false); + int stream (short *buffer, int samples); - int stream(short *buffer, int samples); - - bool canSeek() { return m_canSeek; } + bool canSeek () + { + return m_canSeek; + } - long long indexMs(void); + long long indexMs (void); }; -mgOggFile::mgOggFile( std::string filename ) : - m_filename( filename ) +mgOggFile::mgOggFile (std::string filename): +m_filename (filename) { - m_canSeek = false; - m_opened = false; + m_canSeek = false; + m_opened = false; } -mgOggFile::~mgOggFile() + +mgOggFile::~mgOggFile () { - close(); + close (); } -bool mgOggFile::open(bool log) + +bool mgOggFile::open (bool log) { - if( m_opened ) - { - if( m_canSeek ) - { - return ( seek() >= 0 ); - } - return true; - } - - FILE *f = fopen( m_filename.c_str(), "r" ); - if( f ) + if (m_opened) { - int r = ov_open( f, &vf, 0, 0 ); - if( !r ) - { - m_canSeek = ( ov_seekable( &vf ) !=0 ); - m_opened = true; - } - else - { - fclose( f ); - if( log ) - { - error( "open", r ); - } - } + if (m_canSeek) + { + return (seek () >= 0); + } + return true; } - else + + FILE * + f = fopen (m_filename.c_str (), "r"); + if (f) + { + int + r = ov_open (f, &vf, 0, 0); + if (!r) + { + m_canSeek = (ov_seekable (&vf) != 0); + m_opened = true; + } + else + { + fclose (f); + if (log) + { + error ("open", r); + } + } + } + else { - if(log) - { - // esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) ); - } + if (log) + { +// esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) ); + } } - return m_opened; + return m_opened; } - -void mgOggFile::close() + + +void +mgOggFile::close () { - if( m_opened ) - { - ov_clear( &vf ); - m_opened = false; + if (m_opened) + { + ov_clear (&vf); + m_opened = false; } } -void mgOggFile::error( const char *action, const int err ) + +void +mgOggFile::error (const char *action, const int err) { - char *errstr; - switch(err) - { - case OV_FALSE: errstr = "false/no data available"; break; - case OV_EOF: errstr = "EOF"; break; - case OV_HOLE: errstr = "missing or corrupted data"; break; - case OV_EREAD: errstr = "read error"; break; - case OV_EFAULT: errstr = "internal error"; break; - case OV_EIMPL: errstr = "unimplemented feature"; break; - case OV_EINVAL: errstr = "invalid argument"; break; - case OV_ENOTVORBIS: errstr = "no Ogg Vorbis stream"; break; - case OV_EBADHEADER: errstr = "corrupted Ogg Vorbis stream"; break; - case OV_EVERSION: errstr = "unsupported bitstream version"; break; - case OV_ENOTAUDIO: errstr = "ENOTAUDIO"; break; - case OV_EBADPACKET: errstr = "EBADPACKET"; break; - case OV_EBADLINK: errstr = "corrupted link"; break; - case OV_ENOSEEK: errstr = "stream not seekable"; break; - default: errstr = "unspecified error"; break; - } - // esyslog( "ERROR: vorbisfile %s failed on %s: %s", action, m_filename.c_str(), errstr ); + char *errstr; + switch (err) + { + case OV_FALSE: + errstr = "false/no data available"; + break; + case OV_EOF: + errstr = "EOF"; + break; + case OV_HOLE: + errstr = "missing or corrupted data"; + break; + case OV_EREAD: + errstr = "read error"; + break; + case OV_EFAULT: + errstr = "internal error"; + break; + case OV_EIMPL: + errstr = "unimplemented feature"; + break; + case OV_EINVAL: + errstr = "invalid argument"; + break; + case OV_ENOTVORBIS: + errstr = "no Ogg Vorbis stream"; + break; + case OV_EBADHEADER: + errstr = "corrupted Ogg Vorbis stream"; + break; + case OV_EVERSION: + errstr = "unsupported bitstream version"; + break; + case OV_ENOTAUDIO: + errstr = "ENOTAUDIO"; + break; + case OV_EBADPACKET: + errstr = "EBADPACKET"; + break; + case OV_EBADLINK: + errstr = "corrupted link"; + break; + case OV_ENOSEEK: + errstr = "stream not seekable"; + break; + default: + errstr = "unspecified error"; + break; + } +// esyslog( "ERROR: vorbisfile %s failed on %s: %s", action, m_filename.c_str(), errstr ); } -long long mgOggFile::indexMs(void) + +long long +mgOggFile::indexMs (void) { - double p = ov_time_tell(&vf); - if( p < 0.0 ) + double p = ov_time_tell (&vf); + if (p < 0.0) { - p = 0.0; + p = 0.0; } - return (long long)( p*1000.0 ); + return (long long) (p * 1000.0); } -long long mgOggFile::seek( long long posMs, bool relativ ) + +long long +mgOggFile::seek (long long posMs, bool relativ) { - if( relativ ) + if (relativ) { - posMs += indexMs(); + posMs += indexMs (); } - int r = ov_time_seek( &vf, (double) posMs/1000.0 ); - - if(r) + int r = ov_time_seek (&vf, (double) posMs / 1000.0); + + if (r) { - error( "seek", r ); - return -1; + error ("seek", r); + return -1; } - - posMs = indexMs(); - return posMs; + + posMs = indexMs (); + return posMs; } -int mgOggFile::stream( short *buffer, int samples ) + +int +mgOggFile::stream (short *buffer, int samples) { - int n; - do + int n; + do { - int stream; - n = ov_read( &vf, (char *)buffer, samples*2, 0, 2, 1, &stream ); - } - while( n == OV_HOLE ); - - if(n < 0) + int stream; + n = ov_read (&vf, (char *) buffer, samples * 2, 0, 2, 1, &stream); + } + while (n == OV_HOLE); + + if (n < 0) { - error( "read", n ); + error ("read", n); } - return (n/2); + return (n / 2); } + // --- mgOggDecoder ------------------------------------------------------------- -mgOggDecoder::mgOggDecoder( mgContentItem *item ) - : mgDecoder( item ) +mgOggDecoder::mgOggDecoder (mgContentItem * item):mgDecoder (item) { - m_filename = item->getSourceFile(); - m_file = new mgOggFile( m_filename ); - m_pcm = 0; - init(); + m_filename = item->getSourceFile (); + m_file = new mgOggFile (m_filename); + m_pcm = 0; + init (); } -mgOggDecoder::~mgOggDecoder() + +mgOggDecoder::~mgOggDecoder () { - delete m_file; - clean(); + delete m_file; + clean (); } -bool mgOggDecoder::valid() + +bool mgOggDecoder::valid () { - bool res = false; - if( tryLock() ) - { - if( m_file->open( false ) ) - { - res = true; - } - unlock(); - } - return res; + bool + res = false; + if (tryLock ()) + { + if (m_file->open (false)) + { + res = true; + } + unlock (); + } + return res; } -mgPlayInfo *mgOggDecoder::playInfo(void) + +mgPlayInfo * +mgOggDecoder::playInfo (void) { - if( m_playing ) - { - // m_playinfo.m_index = index/1000; - // m_playinfo.m_total = info.Total; + if (m_playing) + { +// m_playinfo.m_index = index/1000; +// m_playinfo.m_total = info.Total; - return &m_playinfo; - } + return &m_playinfo; + } - return 0; + return 0; } -void mgOggDecoder::init() + +void +mgOggDecoder::init () { - clean(); - m_pcm = new struct mad_pcm; - m_index = 0; + clean (); + m_pcm = new struct mad_pcm; + m_index = 0; } -bool mgOggDecoder::clean() + +bool mgOggDecoder::clean () { - m_playing = false; - - delete m_pcm; - m_pcm = 0; - - m_file->close(); - return false; + m_playing = false; + + delete + m_pcm; + m_pcm = 0; + + m_file->close (); + return false; } + #define SF_SAMPLES (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t)) -bool mgOggDecoder::start() +bool mgOggDecoder::start () { - lock(true); - init(); - m_playing = true; + lock (true); + init (); + m_playing = true; - if( m_file->open() /*&& info.DoScan(true)*/ ) + if (m_file->open () /*&& info.DoScan(true) */ ) { - // obtain from database: rate, channels - /* d(printf("ogg: open rate=%d channels=%d seek=%d\n", - info.SampleFreq,info.Channels,file.CanSeek())) - */ - if( m_item->getChannels() <= 2 ) - { - unlock(); - return true; - } - else - { - // esyslog( "ERROR: cannot play ogg file %s: more than 2 channels", m_filename.c_str() ); - } +// obtain from database: rate, channels +/* d(printf("ogg: open rate=%d channels=%d seek=%d\n", + info.SampleFreq,info.Channels,file.CanSeek())) + */ + if (m_item->getChannels () <= 2) + { + unlock (); + return true; + } + else + { +// esyslog( "ERROR: cannot play ogg file %s: more than 2 channels", m_filename.c_str() ); + } } - - clean(); - unlock(); - return false; + clean (); + unlock (); + + return false; } -bool mgOggDecoder::stop(void) + +bool mgOggDecoder::stop (void) { - lock(); + lock (); - if( m_playing ) - { - clean(); - } - unlock(); + if (m_playing) + { + clean (); + } + unlock (); - return true; + return true; } -struct mgDecode *mgOggDecoder::done(eDecodeStatus status) + +struct mgDecode * +mgOggDecoder::done (eDecodeStatus status) { - m_ds.status = status; - m_ds.index = m_index; - m_ds.pcm = m_pcm; + m_ds.status = status; + m_ds.index = m_index; + m_ds.pcm = m_pcm; - unlock(); // release the lock from decode() + unlock (); // release the lock from decode() - return &m_ds; + return &m_ds; } -struct mgDecode *mgOggDecoder::decode(void) + +struct mgDecode * +mgOggDecoder::decode (void) { - lock(); // this is released in Done() - - if( m_playing ) - { - short framebuff[2*SF_SAMPLES]; - int n = m_file->stream( framebuff, SF_SAMPLES ); - - if( n < 0 ) - { - return done(dsError); - } - - if( n == 0 ) - { - return done(dsEof); - } - - // should be done during initialization - m_pcm->samplerate = m_item->getSampleRate(); // from database - m_pcm->channels = m_item->getChannels(); // from database - - n /= m_pcm->channels; - m_pcm->length = n; - m_index = m_file->indexMs(); - - short *data = framebuff; - mad_fixed_t *sam0 = m_pcm->samples[0], *sam1 = m_pcm->samples[1]; - - const int s = MAD_F_FRACBITS + 1 - ( sizeof(short)*8 ); // shift value for mad_fixed conversion - - if( m_pcm->channels>1 ) - { - for(; n > 0 ; n-- ) - { - *sam0++=(*data++) << s; - *sam1++=(*data++) << s; - } - } - else - { - for(; n>0 ; n--) - { - *sam0++=(*data++) << s; - } - } - return done(dsPlay); - } - return done(dsError); + lock (); // this is released in Done() + + if (m_playing) + { + short framebuff[2 * SF_SAMPLES]; + int n = m_file->stream (framebuff, SF_SAMPLES); + + if (n < 0) + { + return done (dsError); + } + + if (n == 0) + { + return done (dsEof); + } + +// should be done during initialization + // from database + m_pcm->samplerate = m_item->getSampleRate (); + m_pcm->channels = m_item->getChannels (); // from database + + n /= m_pcm->channels; + m_pcm->length = n; + m_index = m_file->indexMs (); + + short *data = framebuff; + mad_fixed_t *sam0 = m_pcm->samples[0], *sam1 = m_pcm->samples[1]; + + // shift value for mad_fixed conversion + const int s = MAD_F_FRACBITS + 1 - (sizeof (short) * 8); + + if (m_pcm->channels > 1) + { + for (; n > 0; n--) + { + *sam0++ = (*data++) << s; + *sam1++ = (*data++) << s; + } + } + else + { + for (; n > 0; n--) + { + *sam0++ = (*data++) << s; + } + } + return done (dsPlay); + } + return done (dsError); } -bool mgOggDecoder::skip(int Seconds, int Avail, int Rate) + +bool mgOggDecoder::skip (int Seconds, int Avail, int Rate) { - lock(); - bool res = false; + lock (); + bool + res = false; - if( m_playing && m_file->canSeek() ) + if (m_playing && m_file->canSeek ()) { - float fsecs = (float)Seconds - ( (float)Avail / (float)(Rate * (16/8 * 2) ) ); - // Byte/s = samplerate * 16 bit * 2 chan - - long long newpos = m_file->indexMs() + (long long)(fsecs*1000.0); - - if( newpos < 0 ) - { - newpos=0; - } - - newpos = m_file->seek( newpos, false ); - - if( newpos >= 0 ) - { - m_index = m_file->indexMs(); -#ifdef DEBUG - int i = index/1000; - printf( "ogg: skipping to %02d:%02d\n", i/60, i%60 ); + float + fsecs = + (float) Seconds - ((float) Avail / (float) (Rate * (16 / 8 * 2))); +// Byte/s = samplerate * 16 bit * 2 chan + + long long + newpos = m_file->indexMs () + (long long) (fsecs * 1000.0); + + if (newpos < 0) + { + newpos = 0; + } + + newpos = m_file->seek (newpos, false); + + if (newpos >= 0) + { + m_index = m_file->indexMs (); +#ifdef xDEBUG + int + i = index / 1000; + printf ("ogg: skipping to %02d:%02d\n", i / 60, i % 60); #endif - res = true; - } + res = true; + } } - unlock(); - return res; + unlock (); + return res; } - -#endif //HAVE_VORBISFILE +#endif //HAVE_VORBISFILE diff --git a/vdr_decoder_ogg.h b/vdr_decoder_ogg.h index d249ab2..1f5fcfc 100644 --- a/vdr_decoder_ogg.h +++ b/vdr_decoder_ogg.h @@ -1,9 +1,9 @@ /*! \file vdr_decoder_ogg.h * \ingroup vdr - * + * * The file contains a decoder which is used by the player to decode ogg vorbis audio files. * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001-2003 Stefan Huelswitt */ @@ -22,43 +22,42 @@ class mgOggFile; -/*! +/*! * \brief A decoder for Ogg Vorbis files * */ -class mgOggDecoder : public mgDecoder +class mgOggDecoder:public mgDecoder { - private: + private: - mgOggFile *m_file; - struct mgDecode m_ds; - struct mad_pcm *m_pcm; - unsigned long long m_index; + mgOggFile * m_file; + struct mgDecode m_ds; + struct mad_pcm *m_pcm; + unsigned long long m_index; - // - void init(void); - bool clean(void); - struct mgDecode *done( eDecodeStatus status ); +// + void init (void); + bool clean (void); + struct mgDecode *done (eDecodeStatus status); - public: + public: - mgOggDecoder( mgContentItem *item ); - ~mgOggDecoder(); + mgOggDecoder (mgContentItem * item); + ~mgOggDecoder (); - virtual mgPlayInfo *playInfo(); + virtual mgPlayInfo *playInfo (); - virtual bool valid(); + virtual bool valid (); - virtual bool start(); + virtual bool start (); - virtual bool stop(); + virtual bool stop (); - virtual bool skip(int Seconds, int Avail, int Rate); + virtual bool skip (int Seconds, int Avail, int Rate); - virtual struct mgDecode *decode(void); + virtual struct mgDecode *decode (void); }; // ---------------------------------------------------------------- - -#endif //HAVE_VORBISFILE -#endif //___DECODER_OGG_H +#endif //HAVE_VORBISFILE +#endif //___DECODER_OGG_H diff --git a/vdr_menu.c b/vdr_menu.c index fbc7794..e64688f 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -1,10 +1,9 @@ -/*! +/*! * \file vdr_menu.c * \brief Implements menu handling for browsing media libraries within VDR * - * \version $Revision: 1.27 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \version $Revision: 1.27 $ * \date $Date$ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald * \author Responsible author: $Author$ * * $Id$ @@ -14,10 +13,6 @@ #include #include -// #include - -#include - #include #include #include @@ -28,965 +23,666 @@ #include #endif -#include "muggle.h" +#include "vdr_setup.h" #include "vdr_menu.h" #include "vdr_player.h" #include "i18n.h" -#include "mg_content_interface.h" -#include "mg_playlist.h" #define DEBUG #include "mg_tools.h" -#include "mg_media.h" -#include "mg_filters.h" -#include "gd_content_interface.h" - -// ----------------------- mgMenuTreeItem ------------------ +#include +#if VDRVERSNUM >= 10307 +#include +#include +#endif -mgMenuTreeItem::mgMenuTreeItem( mgSelectionTreeNode *node ) - : m_node( node ) +void +mgStatus::OsdCurrentItem(const char* Text) { - Set(); + cOsdItem* i = main->Get(main->Current()); + if (!i) return; + if (i == IgnoreNextEventOn) + { + IgnoreNextEventOn = NULL; + return; + } + mgAction * a = dynamic_cast(i); + if (!a) + mgError("mgStatus::OsdCurrentItem expected an mgAction*"); + a->Notify(); +} + +void Play(mgSelection *sel,const bool select) { + mgSelection *s = new mgSelection(sel); + if (select) s->select(); + if (s->empty()) + { + delete s; + return; + } + mgPlayerControl *c = PlayerControl (); + if (c) + c->NewPlaylist (s); + else + cControl::Launch (new mgPlayerControl (s)); } -mgSelectionTreeNode* mgMenuTreeItem::Node() + +//! \brief queue the selection for playing, abort ongoing instant play +void +mgMainMenu::PlayQueue() { - return m_node; + queue_playing=true; + instant_playing=false; + Play(playselection()); } -void mgMenuTreeItem::Set() +//! \brief queue the selection for playing, abort ongoing queue playing +void +mgMainMenu::PlayInstant(const bool select) { - char *buffer = 0; - asprintf( &buffer, m_node->getLabel().c_str() ); - SetText( buffer, false ); + instant_playing=true; + Play(selection(),select); } -// ----------------------- mgMainMenu ---------------------- -mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, - mgPlaylist *playlist, cCommands *playlist_commands) - : cOsdMenu( "" ), m_media(media), m_root(root), - m_current_playlist(playlist), m_playlist_commands(playlist_commands) +bool +mgMainMenu::ShowingCollections() { - mgDebug( 1, "Creating Muggle Main Menu" ); - - SetTitle( tr("Muggle Media Database") ); - SetButtons(); + return (UsingCollection && selection ()->level () == 0); +} - DisplayTree( m_root ); - strncpy( m_listname, playlist->getListname().c_str(), 31 ); - m_listname[31] = '\0'; - m_editing_listname = false; +bool +mgMainMenu::DefaultCollectionSelected() +{ + string this_sel = trim(selection ()->getCurrentValue()); + return (ShowingCollections () && this_sel == default_collection); } -mgSelectionTreeNode *mgMainMenu::CurrentNode() + +bool +mgMainMenu::DefaultCollectionEntered() { - mgMenuTreeItem *item = (mgMenuTreeItem *)Get( Current() ); - return item? item->Node(): 0; + if (!UsingCollection) return false; + if (selection()->level()==0) return false; + string collection = trim(selection ()->getKeyValue(0)); + return (collection == default_collection); } -mgMenuTreeItem *mgMainMenu::CurrentItem() -{ - mgMenuTreeItem *item = (mgMenuTreeItem *)Get( Current() ); - return item; +mgMenu * +mgMainMenu::Parent () +{ + if (Menus.size () < 2) + return NULL; + return Menus[Menus.size () - 2]; } -void mgMainMenu::SetButtons( ) + +mgOsdItem* +mgMenu::GenerateAction(const mgActions action) { - SetHasHotkeys(); + mgOsdItem *result = actGenerate(action); + if (result) + { + result->SetMenu(this); + if (!result->Enabled()) + { + delete result; + result=NULL; + } + } + return result; +} - if( m_state == TREE ) - { - SetHelp( tr("Add"), tr("Cycle tree"), tr("Playlist"), tr("Submenu") ); - } - else if( m_state == TREE_SUBMENU ) - { - SetHelp( tr("Instant Play"), tr("2"), tr("3"), tr("Mainmenu") ); - } - else if( m_state == PLAYLIST ) - { - SetHelp( tr("Play"), tr("Move"), tr("Filter"), tr("Submenu") ); - } - else if( m_state == PLAYLIST_SUBMENU ) - { - SetHelp( tr("Load"), tr("Save"), tr("Delete"), tr("Mainmenu") ); - } - else if( m_state == FILTER ) +void +mgMenu::ExecuteAction(const mgActions action) +{ + mgAction *a = GenerateAction (action); + if (a) { - SetHelp( tr("Query"), tr("Other Search"), tr("Browser"), tr("Submenu") ); + a->Execute (); + delete a; } - else - { - SetHelp( "t", "o", "d", "o" ); - } } -void mgMainMenu::Move( int from, int to ) -{ - m_current_playlist->move( from, to ); - cOsdMenu::Move( from, to ); - Display(); +mgPlayerControl * +PlayerControl () +{ + mgPlayerControl *result = NULL; + cControl *control = cControl::Control (); + if (control && typeid (*control) == typeid (mgPlayerControl)) +// is there a running MP3 player? + result = static_cast < mgPlayerControl * >(control); + return result; } -eOSState mgMainMenu::ProcessKey(eKeys key) -{ - MGLOG( "mgMainMenu::ProcessKey" ); - eOSState state = cOsdMenu::ProcessKey(key); - if( m_state == TREE ) - { - mgDebug( 1, "mgMainMenu: in state TREE" ); - // Navigate with up/dn, left/right (pgup, pgdn) - // Expand with OK, Collapse with Back - if( state == osUnknown ) - { - switch( key ) - { - case kOk: - { - m_history.push_back( Current() ); - mgSelectionTreeNode *child = CurrentNode(); - DisplayTree( child ); - - state = osContinue; - } break; - case kRed: - { - mgSelectionTreeNode *current = CurrentNode(); - if( current ) - { - mgDebug( 1, "mgMainMenu: add selection %s to playlist", current->getLabel().c_str() ); - // Add selection to Play - std::vector *tracks = current->getTracks(); - - if( tracks ) - { - - char *buffer = 0; - asprintf( &buffer, tr("%d tracks sent to current playlist"), (int) tracks->size() ); - m_current_playlist->appendList(tracks); -#if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,buffer); - Skins.Flush(); -#else - Interface->Status( buffer ); - Interface->Flush(); -#endif - free( buffer ); - } - else - { - mgDebug(1, "No tracks for current selection" ); - } - } - else - { - mgDebug(1, "Cannot find currently selected node!" ); - } - state = osContinue; - } break; - case kGreen: - { - mgDebug( 1, "mgMainMenu: cycle treeviews (todo)" ); - - state = osContinue; - } break; - case kYellow: - { - mgDebug( 1, "mgMainMenu: cycle to playlist view" ); - - DisplayPlaylist(); - } break; - case kBlue: - { - m_last_osd_index = Current(); - m_menu_item = CurrentItem()->Node(); - DisplayTreeSubmenu(); - - state = osContinue; - } break; - default: - { - state = osContinue; - } break; - } - } - else if( state == osBack ) - { - mgSelectionTreeNode *parent = m_node->getParent(); - - if( parent ) - { - mgDebug( 1, "mgMainMenu: collapse current node" ); - - m_node->collapse(); +mgMenu::mgMenu () +{ + m_osd = NULL; + TreeRedAction = mgActions(0); + TreeGreenAction = mgActions(0); + TreeYellowAction = mgActions(0); + CollRedAction = mgActions(0); + CollGreenAction = mgActions(0); + CollYellowAction = mgActions(0); +} - // restore last selected entry - int last = m_history.back(); - m_history.pop_back(); - DisplayTree( parent, last ); - - state = osContinue; - } - else - { - // Back pressed on root level... Go back to Main VDR menu - state = osBack; +// ----------------------- mgMainMenu ---------------------- - // state = osContinue; - } - } - } - else if( m_state == TREE_SUBMENU ) - { - if( state == osUnknown ) - { - switch( key ) - { - case k0 ... k9: - { - int n = key - k0; - TreeSubmenuAction( n ); - - state = osContinue; - } break; - case kRed: - { - state = TreeSubmenuAction( 0 ); - } break; - case kBlue: - { - m_state = TREE; - - // restore last selected entry - int last = m_history.back(); - DisplayTree( m_node, last ); - - state = osContinue; - } break; - case kOk: - { - state = TreeSubmenuAction( Current() ); - } break; - default: - { - state = osContinue; - } break; - } - } - else if( state == osBack ) - { - m_state = TREE; - // restore last selected entry - int last = m_history.back(); - DisplayTree( m_node, last ); - - state = osContinue; - } - } - else if( m_state == PLAYLIST ) - { - if( state == osUnknown ) - { - switch( key ) - { - case kOk: - { - // start replay at selected index - unsigned idx = Current(); - Play( m_current_playlist, idx ); - state = osContinue; - } break; - case kRed: - { - // TODO: what happens if the user presses play and the player is already active? - // TODO: resume? - unsigned resume = mgMuggle::getResumeIndex(); - Play( m_current_playlist, resume ); - state = osEnd; - } break; - case kGreen: - { - Mark(); - - state = osContinue; - } break; - case kYellow: - { - DisplayFilter(); - state = osContinue; - } break; - case kBlue: - { - // Submenu - m_last_osd_index = Current(); - DisplayPlaylistSubmenu(); - - state = osContinue; - } break; - default: - { - state = osContinue; - }; - } - } - } - else if( m_state == LOAD_PLAYLIST ) - { - if( state == osUnknown ) - { - switch( key ) - { - case kOk: - { - // load the selected playlist - - m_current_playlist -> clear(); - delete m_current_playlist; - - std::string selected = (*m_plists)[ Current() ]; - m_current_playlist = m_media->loadPlaylist( selected.c_str() ); - - // clean the list of playlist - m_plists->clear(); - m_last_osd_index =0; - DisplayPlaylist(0); - state = osContinue; - } break; - default: - { - state = osContinue; - }; - } - } - } - else if( m_state == PLAYLIST_SUBMENU ) - { - if( state == osUnknown ) - { - switch( key ) - { - case k0 ... k9: - { - int n = key - k0; - PlaylistSubmenuAction( n ); - - state = osContinue; - } break; - case kYellow: - { - PlaylistSubmenuAction( 3 ); - } break; - case kBlue: - { - m_state = PLAYLIST; - DisplayPlaylist( m_last_osd_index ); - - state = osContinue; - } break; - case kOk: - { - if( Current() != 0 ) - { // not editing playlist name - state = PlaylistSubmenuAction( Current() ); - } - else - { // editing playlist name - m_editing_listname = !m_editing_listname; - if( m_editing_listname = false ) - { // we just changed from true to false so editing was terminated - RenamePlaylist( std::string( m_listname ) ); - - } - } - } break; - default: - { - state = osContinue; - } break; - } - } - else if( state == osBack ) - { - m_state = PLAYLIST; - DisplayPlaylist( m_last_osd_index ); - - state = osContinue; - } - } - else if( m_state == PLAYLIST_COMMANDS ) - { - if( state == osUnknown ) - { - switch( key ) - { - case kOk: - { - state = ExecutePlaylistCommand( Current() ); - } break; - default: - { - } - } - } - else if( state == osBack ) - { - m_state = PLAYLIST_SUBMENU; - DisplayPlaylistSubmenu(); - - state = osContinue; - } - } - else if( m_state == FILTER ) - { - if( state == osUnknown ) - { - switch( key ) - { - case kRed: // - { - mgDebug( 1, "mgMainMenu: query and display results" ); - - if( m_root ) - { - delete m_root; - } - - m_root = m_media->applyActiveFilter(); - // collapse all? - DisplayTree( m_root ); - - state = osContinue; - } break; - case kGreen: - { - // cycle FILTER -> TREE - mgDebug( 1, "mgMainMenu: next filters " ); - - m_media->nextFilterSet(); - DisplayFilter(); - - state = osContinue; - } break; - case kYellow: - { - // Green: treeview - mgDebug( 1, "mgMainMenu: switch to treeview" ); - - DisplayTreeViewSelector(); - - state = osContinue; - } break; - case kBlue: - { - mgDebug( 1, "mgMainMenu: submenu" ); - state = osContinue; - } break; - default: - { - state = osContinue; - } - } - } - else if( state == osBack ) - { - // m_media->resetFilters();? - } - // RaK: Verhindert, dass die Help Buttons verschwinden, - // ist aber keine schöne Lösung - //SetHelp( tr("Query"), tr("Other Search"), tr("Browser"), tr("Submenu") ); - } - else - { - mgDebug(1, "Process key: else"); - mgDebug(1, "Process key: %d", (int) state); - } - - return state; +void mgMainMenu::SaveState() +{ + char *b; + asprintf(&b,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle")); + FILE *f = fopen(b,"w"); + free(b); + if (!f) return; + mgValmap nmain("MainMenu"); + nmain.put("DefaultCollection",default_collection); + nmain.put("UsingCollection",UsingCollection); + nmain.put("TreeRedAction",int(Menus.front()->TreeRedAction)); + nmain.put("TreeGreenAction",int(Menus.front()->TreeGreenAction)); + nmain.put("TreeYellowAction",int(Menus.front()->TreeYellowAction)); + nmain.put("CollRedAction",int(Menus.front()->CollRedAction)); + nmain.put("CollGreenAction",int(Menus.front()->CollGreenAction)); + nmain.put("CollYellowAction",int(Menus.front()->CollYellowAction)); + mgValmap nsel("tree"); + m_treesel.DumpState(nsel); + mgValmap ncol("collection"); + m_collectionsel.DumpState(ncol); + nmain.Write(f); + nsel.Write(f); + ncol.Write(f); + fclose(f); } -void mgMainMenu::DisplayTree( mgSelectionTreeNode* node, int select ) +mgMainMenu::mgMainMenu ():cOsdMenu ("") { - m_state = TREE; + m_Status = new mgStatus(this); + m_message = NULL; + queue_playing=false; + instant_playing=false; + play_collection = tr("play"); + mgValmap nsel("tree"); + mgValmap ncol("collection"); + mgValmap nmain("MainMenu"); + + // define defaults for values missing in state file: + ncol.put("Keys.0.Choice","Collection"); + ncol.put("Keys.1.Choice","Collection item"); + nmain.put("DefaultCollection",play_collection); + nmain.put("UsingCollection","false"); + nmain.put("TreeRedAction",int(actAddThisToCollection)); + nmain.put("TreeGreenAction",int(actInstantPlay)); + nmain.put("TreeYellowAction",int(actToggleSelection)); + nmain.put("CollRedAction",int(actAddThisToCollection)); + nmain.put("CollGreenAction",int(actInstantPlay)); + nmain.put("CollYellowAction",int(actToggleSelection)); + + // load values from state file + char *b; + asprintf(&b,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle")); + FILE *f = fopen(b,"r"); + free(b); + if (f) { + nsel.Read(f); + ncol.Read(f); + nmain.Read(f); + fclose(f); + } - if( node->expand( ) ) + // get values from mgValmaps + default_collection = nmain.getstr("DefaultCollection"); + UsingCollection = nmain.getbool("UsingCollection"); + InitMapFromSetup(nsel); + m_treesel.InitFrom (nsel); + InitMapFromSetup(ncol); + m_collectionsel.InitFrom (ncol); + m_playsel.InitFrom(ncol); + + // initialize + m_collectionsel.CreateCollection(default_collection); + m_collectionsel.CreateCollection(play_collection); + m_playsel.setKey(0,"Collection"); + m_playsel.setKey(1,"Collection item"); + m_playsel.enter(play_collection); + UseNormalSelection (); + unsigned int posi = selection()->gotoPosition(); + mgMenu *root = new mgTree; + root->TreeRedAction = mgActions(nmain.getuint("TreeRedAction")); + root->TreeGreenAction = mgActions(nmain.getuint("TreeGreenAction")); + root->TreeYellowAction = mgActions(nmain.getuint("TreeYellowAction")); + root->CollRedAction = mgActions(nmain.getuint("CollRedAction")); + root->CollGreenAction = mgActions(nmain.getuint("CollGreenAction")); + root->CollYellowAction = mgActions(nmain.getuint("CollYellowAction")); + AddMenu (root); + + SetCurrent (Get (posi)); + +// Read commands for collections in etc. /video/muggle/playlist_commands.conf + external_commands = new cCommands (); + + char * + cmd_file = (char *) AddDirectory (cPlugin::ConfigDirectory ("muggle"), + "playlist_commands.conf"); + mgDebug (1, "mgMuggle::Start: Looking for file %s", cmd_file); + bool have_cmd_file = external_commands->Load ((const char *) cmd_file); + + if (!have_cmd_file) { - Clear(); - - char buffer[256]; - sprintf( buffer, "Muggle - %s", node->getLabel().c_str() ); - - SetTitle( buffer ); - SetButtons(); - - m_node = node; - std::vector children = node->getChildren(); - - for( std::vector::iterator iter = children.begin(); - iter != children.end(); - iter ++ ) - { - Add( new mgMenuTreeItem( *iter ) ); - } - - cOsdItem *item = Get( select ); - SetCurrent( item ); - - RefreshCurrent(); - DisplayCurrent(true); + delete external_commands; + external_commands = NULL; } - Display(); + forcerefresh = false; } -void mgMainMenu::DisplayTreeViewSelector() +mgMainMenu::~mgMainMenu() { - m_history.clear(); - // collapse all! - DisplayTree( m_root ); + delete m_Status; } -void mgMainMenu::DisplayTreeSubmenu() +void +mgMainMenu::InitMapFromSetup (mgValmap& nv) { - m_state = TREE_SUBMENU; - - Clear(); - SetButtons(); + // values from setup override saved values + nv["Host"] = the_setup.DbHost; + nv["User"] = the_setup.DbUser; + nv["Password"] = the_setup.DbPass; + nv["Directory"] = cPlugin::ConfigDirectory ("muggle"); + nv["ToplevelDir"] = the_setup.ToplevelDir; +} - char *buffer; - asprintf( &buffer, "Muggle - %s", tr("Tree View Commands") ); - SetTitle( buffer ); - free( buffer ); +void +mgMenu::AddAction (const mgActions action, const bool hotkey) +{ + mgOsdItem *a = GenerateAction(action); + if (!a) return; + const char *mn = a->MenuName(); + if (strlen(mn)==0) + mgError("AddAction(%d):MenuName is empty",int(action)); + if (hotkey) + a->SetText(osd()->hk(mn)); + else + a->SetText(mn); + free(const_cast(mn)); + osd()->Add(a); +} - // Add items - Add( new cOsdItem( "Instant play" ) ); - Display(); +void +mgMenu::AddExternalAction(const mgActions action, const char *title) +{ + mgOsdItem *a = GenerateAction(action); + if (!a) return; + a->SetText(osd()->hk(title)); + osd()->Add(a); } -eOSState mgMainMenu::TreeSubmenuAction( int n ) +void +mgMenu::AddSelectionItems () { - eOSState state = osContinue; - - switch( n ) + for (unsigned int i = 0; i < selection()->values.size (); i++) + { + mgOsdItem *a = GenerateAction(actEntry); + if (!a) continue; + a->SetText(a->MenuName(i+1,selection()->values[i]),false); + osd()->Add(a); + } + if (osd()->ShowingCollections ()) { - case 0: - { - // action 0: instant play of current node, might need a security question - - mgSelectionTreeNode *current = CurrentNode(); - if( current ) - { - // append current node - std::vector *tracks = m_menu_item->getTracks(); - - if( tracks ) - { - // clear playlist - m_current_playlist->clear(); - m_current_playlist->appendList( tracks ); - - // play - mgMuggle::setResumeIndex( 0 ); - Play( m_current_playlist, 0 ); - - state = osEnd; - } - } - } break; - case 1: - { - // action 0 - } break; - default: - { - // undefined action - } break; + mgCreateCollection *a = new mgCreateCollection; + if (!a) return; + a->SetMenu(this); + if (!a->Enabled()) + { + delete a; + a=NULL; + } + if (!a) return; + a->SetText(a->MenuName(),false); + osd()->Add(a); } - - return state; } -void mgMainMenu::DisplayPlaylist( int index_current ) -{ - m_state = PLAYLIST; - - // make sure we have a current playlist - Clear(); - SetButtons(); - std::vector* list = m_current_playlist-> getAll(); - static char titlestr[80]; - sprintf( titlestr, "Muggle - %s (%d %s)",tr("Playlist"), - list->size() , - tr("items") ); - SetTitle( titlestr ); +const char * +mgMenu::BlueName () +{ + if (typeid (*this) == typeid (mgTree)) + return tr("Commands"); + else + return tr ("List"); +} - for( unsigned int i = 0; i < m_current_playlist->getNumItems(); i++) +void +mgMenu::SetHelpKeys() +{ + const char *Red = NULL; + const char *Green = NULL; + const char *Yellow = NULL; + mgOsdItem *a; + if (osd()->UsingCollection) { - std::string label = m_current_playlist->getLabel( i, " " ); - Add( new cOsdItem( label.c_str() ) ); + if ((a = GenerateAction (CollRedAction))) + Red = a->ButtonName (); + if ((a = GenerateAction (CollGreenAction))) + Green = a->ButtonName (); + if ((a = GenerateAction (CollYellowAction))) + Yellow = a->ButtonName (); } - - if( index_current >= 0 ) + else { - cOsdItem *item = Get( m_last_osd_index ); - SetCurrent( item ); - RefreshCurrent(); - DisplayCurrent( true ); - } - - Display(); -} - -void mgMainMenu::LoadPlaylist() -{ - m_state = LOAD_PLAYLIST; - static char titlestr[80]; - - // make sure we have a current playlist - Clear(); - SetButtons(); - sprintf( titlestr, "Muggle - %s %s ",tr("load"), tr("Playlist")); - SetTitle( titlestr ); - - // retrieve list of available playlists - m_plists = m_media->getStoredPlaylists(); - - for(std::vector::iterator iter = m_plists->begin(); - iter != m_plists->end() ; iter++) - { - Add( new cOsdItem( iter->c_str() ) ); + if ((a = GenerateAction (TreeRedAction))) + Red = a->ButtonName (); + if ((a = GenerateAction (TreeGreenAction))) + Green = a->ButtonName (); + if ((a = GenerateAction (TreeYellowAction))) + Yellow = a->ButtonName (); } - - Display(); + osd()->SetHelpKeys(Red,Green,Yellow,BlueName()); } -void mgMainMenu::SavePlaylist() -{ - if(m_current_playlist->getListname() == "") - { - // create dummy listname with current date and time - time_t currentTime = time(NULL); - m_current_playlist->setListname(ctime(¤tTime)); - } - m_current_playlist->storePlaylist(); -} -void mgMainMenu::RenamePlaylist( std::string name ) +void +mgMenu::InitOsd (const char *title,const bool hashotkeys) { - // dummy function. USes current date as name - m_current_playlist->setListname( name ); - - // confirmation -#if VDRVERSNUM >= 10307 - Skins.Message(mtInfo, "Playlist renamed" ); - Skins.Flush(); -#else - Interface->Status( "Playlist renamed" ); - Interface->Flush(); -#endif + osd ()->InitOsd (title,hashotkeys); } -void mgMainMenu::DisplayPlaylistSubmenu() + +void +mgMainMenu::InitOsd (const char *title,const bool hashotkeys) { - static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" }; + Clear (); + SetTitle (title); + if (hashotkeys) SetHasHotkeys (); +} - m_state = PLAYLIST_SUBMENU; - Clear(); - SetButtons(); - SetTitle( "Muggle - Playlist View Commands" ); +void +mgSubmenu::BuildOsd () +{ + static char b[100]; + snprintf(b,99,tr("Commands:%s"),trim(osd()->selection()->getCurrentValue()).c_str()); + InitOsd (b); + mgMenu *p = osd ()->Parent (); + if (!p) + return; + AddAction(actInstantPlay); + AddAction(actAddThisToCollection); + AddAction(actRemoveThisFromCollection); + AddAction(actToggleSelection); + AddAction(actSetDefault); + AddAction(actDeleteCollection); + AddAction(actChooseSearch); + AddAction(actExportTracklist); + cCommand *command; + if (osd()->external_commands) + { + int idx=0; + while ((command = osd ()->external_commands->Get (idx)) != NULL) + { + if (idx>actExternalHigh-actExternal0) + { + mgWarning("Too many external commands"); + break; + } + AddExternalAction (mgActions(idx+int(actExternal0)),command->Title()); + idx++; + } + } + TreeRedAction = actSetButton; + TreeGreenAction = actSetButton; + TreeYellowAction = actSetButton; + CollRedAction = actSetButton; + CollGreenAction = actSetButton; + CollYellowAction = actSetButton; +} - // Add items - Add( new cMenuEditStrItem( tr("Playlist name"), m_listname, 31, allowed ) ); - Add( new cOsdItem( tr("Load playlist" ) ) ); - Add( new cOsdItem( tr("Save playlist" ) ) ); - Add( new cOsdItem( tr("Clear playlist" ) ) ); - Add( new cOsdItem( tr("Remove entry from list" ) ) ); - Add( new cOsdItem( tr("Export playlist" ) ) ); - if( m_playlist_commands ) +eOSState +mgTree::Process (eKeys key) +{ + eOSState result = osUnknown; + switch (key) { - Add( new cOsdItem( tr("External playlist commands" ) ) ); + case kRed: + if (osd()->UsingCollection) + ExecuteAction (CollRedAction); + else + ExecuteAction (TreeRedAction); + return osContinue; + case kGreen: + if (osd()->UsingCollection) + ExecuteAction (CollGreenAction); + else + ExecuteAction (TreeGreenAction); + return osContinue; + case kYellow: + if (osd()->UsingCollection) + ExecuteAction (CollYellowAction); + else + ExecuteAction (TreeYellowAction); + return osContinue; + default: + result = osUnknown; + break; } + return result; +} - Display(); +void +mgTree::BuildOsd () +{ + InitOsd (selection ()->getListname ().c_str (), false); + AddSelectionItems (); } -void mgMainMenu::DisplayPlaylistCommands() +void +mgMainMenu::Message1(const char *msg, const char *arg1) { - m_state = PLAYLIST_COMMANDS; + if (strlen(msg)==0) return; + asprintf (&m_message, tr (msg), arg1); +} - cCommand *command; - int i = 0; - Clear(); - SetTitle( "Muggle - External Playlist Commands" ); +eOSState mgMainMenu::ProcessKey (eKeys key) +{ + eOSState result = osContinue; + if (key!=kNone) + mgDebug (3, "MainMenu::ProcessKey(%d)", (int) key); - while( ( command = m_playlist_commands->Get(i) ) != NULL ) + if (Menus.size()<1) + mgError("mgMainMenu::ProcessKey: Menus is empty"); + + mgPlayerControl * c = PlayerControl (); + if (c) { - Add( new cOsdItem( hk( command->Title() ) ) ); - i++; + if (!c->Active ()) + { + c->Shutdown (); + if (instant_playing && queue_playing) { + PlayQueue(); + } + else + { + instant_playing = false; + queue_playing = false; + } + } + else + { + switch (key) + { + case kPause: + c->Pause (); + break; + case kStop: + if (instant_playing && queue_playing) { + PlayQueue(); + } + else + { + queue_playing = false; + c->Stop (); + } + break; + case kChanUp: + c->Forward (); + break; + case kChanDn: + c->Backward (); + break; + default: + goto otherkeys; + } + goto pr_exit; + } } + else + if (key==kPlay) { + PlayQueue(); + goto pr_exit; + } +otherkeys: + newmenu = Menus.back(); // Default: Stay in current menu + newposition = -1; - Display(); -} - -eOSState mgMainMenu::ExecutePlaylistCommand( int current ) -{ - cCommand *command = m_playlist_commands->Get( current ); - if( command ) { - char *buffer = NULL; - bool confirmed = true; - if( command->Confirm() ) - { - asprintf( &buffer, "%s?", command->Title() ); -//#if VDRVERSNUM < 10307 - confirmed = Interface->Confirm( buffer ); -//#else -//#endif - free( buffer ); - } - if( confirmed ) - { - asprintf( &buffer, "%s...", command->Title() ); -#if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,buffer); - Skins.Flush(); -#else - Interface->Status( buffer ); - Interface->Flush(); -#endif - free( buffer ); + mgMenu * oldmenu = newmenu; - std::string tmp_m3u_file = (char *) AddDirectory( cPlugin::ConfigDirectory("muggle"), "current.m3u" ); - m_current_playlist->exportM3U( tmp_m3u_file ); +// item specific key logic: + result = cOsdMenu::ProcessKey (key); - char *result = (char *)command->Execute( tmp_m3u_file.c_str() ); +// mgMenu specific key logic: + if (result == osUnknown) + result = oldmenu->Process (key); + } +// catch osBack for empty OSD lists +// (because if the list was empty, no mgOsdItem::ProcessKey was ever called) + if (result == osBack) + { + // do as if there was an entry + mgOsdItem *a = Menus.back()->GenerateAction(actEntry); + if (a) + { + result = a->Back(); + delete a; + } + } - /* What to do? Recode cMenuText (not much)? - if( result ) - { - return AddSubMenu( new cMenuText( command->Title(), result ) ); - } - */ - - free( result ); +// do nothing for unknown keys: + if (result == osUnknown) + goto pr_exit; - return osEnd; - } +// change OSD menu as requested: + if (newmenu == NULL) + { + if (Menus.size () > 1) + { + Menus.pop_back (); + forcerefresh = true; + } + else + { + result = osBack; // game over + goto pr_exit; + } } - return osContinue; -} - -eOSState mgMainMenu::PlaylistSubmenuAction( int n ) -{ - std::cout << "mgMainMenu::PlaylistSubmenuAction: " << n << std::endl << std::flush; - eOSState state = osContinue; - - switch( n ) + else if (newmenu != Menus.back ()) { - case 0: - { // rename playlist - should never get here! - state = osContinue; - } break; - - case 1: - { - LoadPlaylist(); -#if VDRVERSNUM < 10307 - Interface->Flush(); -#else - Skins.Flush(); -#endif - // jump to playlist view from here? - } break; - case 2: - { - SavePlaylist(); -#if VDRVERSNUM >= 10307 - Skins.Message(mtInfo,"Playlist saved"); - Skins.Flush(); -#else - Interface->Status( "Playlist saved"); - Interface->Flush(); -#endif - } break; - case 3: - { // clear playlist + AddMenu (newmenu); + } - cControl *control = cControl::Control(); - std::string buffer; + if (UsingCollection) + forcerefresh |= m_collectionsel.cacheIsEmpty(); + else + forcerefresh |= m_treesel.cacheIsEmpty(); - if( control && typeid(*control) == typeid(mgPlayerControl) ) - { - buffer = "Cannot clear playlist while playing."; - } - else - { - m_current_playlist->clear(); + forcerefresh |= (newposition>=0); - buffer = "Playlist cleared"; - } - - // confirmation -#if VDRVERSNUM >= 10307 - Skins.Message( mtInfo, buffer.c_str() ); - Skins.Flush(); -#else - Interface->Status( buffer.c_str() ); - Interface->Flush(); -#endif - - state = osContinue; - } break; - case 4: - { // remove selected title - bool res = m_current_playlist->remove( m_last_osd_index ); - - if( m_last_osd_index > 0 ) - { - m_last_osd_index --; - } - DisplayPlaylist( m_last_osd_index ); - - // confirmation - std::string confirm = res? "Entry deleted": "Cannot delete entry"; + if (forcerefresh) + { + mgDebug(2,"forced refresh"); + forcerefresh = false; + if (newposition<0) + newposition = selection()->gotoPosition(); + Menus.back ()->Display (newposition); + } +pr_exit: + showMessage(); + return result; +} +void +mgMainMenu::showMessage() +{ + if (m_message) + { #if VDRVERSNUM >= 10307 - Skins.Message( mtInfo, confirm.c_str() ); - Skins.Flush(); + Skins.Message (mtInfo, m_message); + Skins.Flush (); #else - Interface->Status( confirm.c_str() ); - Interface->Flush(); + Interface->Status (m_message); + Interface->Flush (); #endif - } break; - case 5: - { - std::string m3u_file = AddDirectory( cPlugin::ConfigDirectory("muggle"), - m_current_playlist->getListname().c_str() ); - m_current_playlist->exportM3U( m3u_file ); - } break; - case 6: - { - DisplayPlaylistCommands(); - } break; - default: - { - // undefined action - } break; + free(m_message); + m_message = NULL; } - - return state; } -void mgMainMenu::DisplayFilter() -{ - m_state = FILTER; - Clear(); - SetButtons(); +void +mgMainMenu::AddMenu (mgMenu * m) +{ + Menus.push_back (m); + m->setosd (this); + m->Display (0); +} - SetTitle( m_media->getActiveFilterTitle().c_str() ); - std::vector *filter_list = m_media->getActiveFilters(); - - int i=0; - for( std::vector::iterator iter = filter_list->begin(); - iter != filter_list->end(); - iter ++ ) - { - mgDebug( 1, "Filter %d/%dint filter %s='%s'", - i, filter_list->size(), - (*iter)->getName(), - (*iter)->getStrVal().c_str()); - switch( (*iter)->getType() ) - { - case mgFilter::INT: - { - mgFilterInt *fi = (mgFilterInt *) (*iter); - - Add( new cMenuEditIntItem( fi->getName(), - &(fi->m_intval), - fi->getMin(), fi->getMax() ) ); - } break; - case mgFilter::STRING: - { - mgFilterString *fs = (mgFilterString *) (*iter); - - // BUG: This might be buggy as fs->getAllowedChars() may become - // invalid while VDR is still trying to access it - Add( new cMenuEditStrItem( fs->getName(), fs->m_strval, - fs->getMaxLength(), - fs->getAllowedChars().c_str() ) ); - - } break; - case mgFilter::BOOL: - { - mgFilterBool *fb = (mgFilterBool *) (*iter); - Add( new cMenuEditBoolItem( fb->getName(), &( fb->m_bval), - fb->getTrueString().c_str(), - fb->getFalseString().c_str() ) ); - } break; - case mgFilter::CHOICE: - { - mgFilterChoice *fc = (mgFilterChoice *) (*iter); - std::vector choices = fc->getChoices(); - - char **choices_str = new (char *)[ choices.size() ]; - - int j = 0; - for( std::vector::iterator iter = choices.begin(); - iter != choices.end(); - iter ++, j ++ ) - { - // BUG: Is this a big memory leak!? When to delete and who? - // RaK: zweiter Iterator war "i" richtig: "j" - // choices_str[j] = strndup( choices[i].c_str(), 128 ); - choices_str[j] = strndup( choices[j].c_str(), 128 ); - } - - Add( new cMenuEditStraItem( fc->getName(), &( fc->m_selval ), - choices.size(), choices_str ) ); - - // delete all choices_str elements! - // delete[] choices_str; // ??? - - } break; - default: - case mgFilter::UNDEF: - { - } break; - } - i++; - } - - Display(); +eOSState +mgSubmenu::Process (eKeys key) +{ + return osUnknown; } -void mgMainMenu::DisplayFilterSelector() + +void +mgTreeViewSelector::BuildOsd () { - // show available filters, load on OK? + InitOsd (tr ("Tree View Selection")); + AddAction(actSearchCollItem); + AddAction(actSearchArtistAlbumTitle); + AddAction(actSearchArtistTitle); + AddAction(actSearchAlbumTitle); + AddAction(actSearchGenreYearTitle); + AddAction(actSearchGenreArtistAlbumTitle); } -void mgMainMenu::Play( mgPlaylist *plist, unsigned first ) +void +mgMainMenu::DisplayGoto (unsigned int select) { - MGLOG( "mgMainMenu::Play" ); - cControl *control = cControl::Control(); - - if( control && typeid(*control) == typeid(mgPlayerControl) ) - { // is there a running MP3 player? - static_cast(control)->NewPlaylist(plist, first); // signal the running player to load the new playlist - } - else + if (select >= 0) { - cControl::Launch( new mgPlayerControl(plist, first) ); + SetCurrent (Get (select)); + RefreshCurrent (); } + Display (); + DisplayMenu()->SetTabs(25); +} + + +void +mgMenu::Display (const unsigned int position) +{ + BuildOsd (); + osd ()->DisplayGoto (position); } + diff --git a/vdr_menu.h b/vdr_menu.h index e9fd1d3..1a92370 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -1,10 +1,10 @@ -/*! +/*! * \file vdr_menu.h * \brief Implements menu handling for broswing media libraries within VDR * * \version $Revision: 1.13 $ * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald * \author Responsible author: $Author$ * * $Id$ @@ -18,106 +18,324 @@ #include #include +#include +#include #include "i18n.h" +#include "mg_actions.h" + +#include "vdr_player.h" + +using namespace std; + +//! \brief play a selection, aborting what is currently played +//! \param select if true, play only what the current position selects +void Play(mgSelection *sel,const bool select=false); class cCommands; -class mgMedia; -class mgSelectionTreeNode; -class mgPlaylist; -class mgTracklist; +class mgSelection; +class mgMenu; +class mgMainMenu; + +//! \brief if a player is running, return it +mgPlayerControl * PlayerControl (); + +//! \brief callback class, monitors state changes in vdr +class mgStatus : public cStatus +{ + private: + //! \brief the mgMainMenu that wants to be notified + mgMainMenu *main; + public: + //! \brief default constructor + mgStatus(mgMainMenu* m) { main = m;IgnoreNextEventOn=NULL;} + /*! \brief vdr calls OsdCurrentItem more often than we + * want. This tells mgStatus to ignore the next call + * for a specific item. + * \todo is this behaviour intended or a bug in vdr + * or in muggle ? + */ + cOsdItem* IgnoreNextEventOn; + protected: + //! \brief the event we want to know about + virtual void OsdCurrentItem(const char *Text); +}; + /*! - * \brief a special menu item + * \brief the muggle main OSD */ -class mgMenuTreeItem : public cOsdItem + +class mgMainMenu:public cOsdMenu { - public: + private: + mgSelection m_treesel; + mgSelection m_playsel; + mgSelection m_collectionsel; + char *m_message; + void showMessage(); + public: + //! \brief syntactic sugar: expose the protected cOsdMenu::SetHelp + void SetHelpKeys(const char *Red,const char *Green, const char *Yellow, const char *Blue) { SetHelp(Red,Green,Yellow,Blue); } + + //! \brief callback object, lets vdr notify us about OSD changes + mgStatus *m_Status; + + //! \brief play the play selection, abort ongoing instant play + void PlayQueue(); + + //! \brief instant play the selection, abort ongoing queue playing + void PlayInstant(const bool select=false); + + //! \brief true if we are browsing m_collectionsel + bool UsingCollection; + + //! \brief the different menus, the last one is active + vector < mgMenu * >Menus; + + //! \brief true if an item from the "playing" selection is being played + bool queue_playing; + + //! \brief true if an item is being instant played + bool instant_playing; + + //! \brief parent menu if any + mgMenu * Parent (); + + //! \brief default constructor + mgMainMenu (); + + //! \brief default destructor + ~mgMainMenu (); + + //! \brief save the entire muggle state + void SaveState(); + + //! \brief adds a new mgMenu to the stack + void AddMenu (mgMenu * m); + + //! \brief initializes using values from nv + void InitMapFromSetup (mgValmap& nv); + + //! \brief main entry point, called from vdr + eOSState ProcessKey (eKeys Key); + + //! \brief from now on use the normal selection + void UseNormalSelection () + { + UsingCollection= false; + } + + //! \brief from now on use the collection selection + void UseCollectionSelection () + { + UsingCollection= true; + } + + //! \brief this is the collection things will be added to + string default_collection; + +/*! \brief this is the "now playing" collection translated in + * the current language. When changing the OSD language, this + * collection will NOT be renamed in the data base, but a new + * empty collection will be started. The collection for the + * previous language will stay, so the user can copy from the + * old one to the new one. + */ + string play_collection; + +/*! \brief selects a certain line on the OSD and displays the OSD + * \param select the line that we want to be selected + */ + void DisplayGoto (unsigned int select); + + //! \brief external commands + cCommands *external_commands; + + //! \brief Actions can set newmenu which will then be displayed next + mgMenu *newmenu; + + //! \brief Actions can set newstate which will then be returned to main vdr + eOSState newstate; - mgMenuTreeItem( mgSelectionTreeNode *node ); + //! \brief Actions can set forcerefresh. This will force a redisplay of the OSD + bool forcerefresh; - mgSelectionTreeNode *Node(); + //! \brief show a message. Can be called by actions. It will + // only be shown at the end of the next mgMainMenu::ProcessKey + // because that might do forcerefresh which overwrites the message + void Message (const char *msg) { m_message = strdup(msg); } + void Message1 (const char *msg, const char *arg1); + void Message1 (const char *msg, string arg1) { Message1(msg,arg1.c_str()); } - void Set(); + //! \brief Actions can request a new position. -1 means none wanted + int newposition; - private: + //! \brief clears the screen, sets a title and buttons with text on them + void InitOsd (const char *title,const bool hashotkeys); - mgSelectionTreeNode *m_node; + //! \brief expose the protected DisplayMenu() from cOsdMenu + cSkinDisplayMenu *DisplayMenu(void) + { + return cOsdMenu::DisplayMenu(); + } + + //! \brief expose the protected cOsdMenu::hk() + const char *hk (const char *s) + { + return cOsdMenu::hk (s); + } + + //! \brief the current selection + mgSelection* selection () + { + if (UsingCollection) + return &m_collectionsel; + else + return &m_treesel; + } + + //! \brief the collection selection + mgSelection* collselection() + { + return &m_collectionsel; + } + +//! \brief the "now playing" selection + mgSelection* playselection () + { + return &m_playsel; + } + +//! \brief true if the cursor is placed in the collection list + bool ShowingCollections(); + +//! \brief true if the cursor is placed on the default collection + bool DefaultCollectionSelected(); + +//! \brief true if the cursor is placed in the default collection + bool DefaultCollectionEntered(); }; -/*! - * \brief the muggle main OSD +//! \brief a generic muggle menu +class mgMenu +{ + private: + mgMainMenu* m_osd; + protected: +//! \brief adds the wanted action to the OSD menu +// \param hotkey if true, add this as a hotkey + void AddAction(const mgActions action, const bool hotkey=true); + + //! \brief add an external action, always with hotkey + void AddExternalAction(const mgActions action, const char *title); + +//! \brief adds entries for all selected data base items to the OSD menu. +// If this is the list of collections, appends a command for collection +// creation. + void AddSelectionItems (); + //! \brief the name of the blue button depends of where we are + const char *mgMenu::BlueName (); + public: + /*! sets the correct help keys. + * \todo without data from mysql, no key is shown, + * not even yellow or blue + */ + void SetHelpKeys(); +//! \brief generates an object for the wanted action + mgOsdItem* GenerateAction(const mgActions action); + +//! \brief executes the wanted action + void ExecuteAction (const mgActions action); + +//! \brief sets the pointer to the owning mgMainMenu + void setosd (mgMainMenu* osd) + { + m_osd = osd; + } + +//! \brief the pointer to the owning mgMainMenu + mgMainMenu* osd () + { + return m_osd; + } + +//! \brief the currently active selection of the owning mgMainMenu + mgSelection* selection () + { + return osd ()->selection (); + } +//! \brief the playselection of the owning mgMainMenu + mgSelection* playselection () + { + return osd ()->playselection (); + } + + mgMenu (); + + virtual ~ mgMenu () + { + } + +//! \brief clears the screen, sets a title and buttons with text on them + void InitOsd (const char *title,const bool hashotkeys=true); + +//! \brief display OSD and go to position + void Display (const unsigned int position); + +//! \brief BuildOsd() should be abstract but then we cannot compile + virtual void BuildOsd () + { + } + +/*! \brief Process() should be abstract but then we cannot compile. + * \return Process may decide that we want another screen to be displayed. + * If the mgMenu* returned is not "this", the caller will use the return + * value for a new display. If NULL is returned, the caller will display + * the previous menu. */ -class mgMainMenu : public cOsdMenu + virtual eOSState Process (eKeys Key) + { + return osUnknown; + } + +//! \brief the ID of the action defined by the red button. + mgActions TreeRedAction; + mgActions CollRedAction; + +//! \brief the ID of the action defined by the green button. + mgActions TreeGreenAction; + mgActions CollGreenAction; + +//! \brief the action defined by the yellow button. + mgActions TreeYellowAction; + mgActions CollYellowAction; +}; + +//! \brief an mgMenu class for navigating through the data base +class mgTree:public mgMenu +{ + public: + eOSState Process (eKeys Key); + protected: + void BuildOsd (); +}; + +//! \brief an mgMenu class for submenus +class mgSubmenu:public mgMenu +{ + public: + eOSState Process (eKeys Key); + protected: + void BuildOsd (); +}; + +//! \brief an mgMenu class for selecting a search view +class mgTreeViewSelector:public mgMenu { - public: - - mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, - mgPlaylist *playlist, cCommands *playlist_commands ); - - mgSelectionTreeNode *CurrentNode(); - mgMenuTreeItem *CurrentItem(); - - eOSState ProcessKey(eKeys Key); - - protected: - - enum MuggleStatus - { - TREE, TREE_SUBMENU, - PLAYLIST, LOAD_PLAYLIST, SAVE_PLAYLIST, - PLAYLIST_SUBMENU, PLAYLIST_COMMANDS, - FILTER, FILTER_SUBMENU - }; - - void SetButtons(); - void Move( int from, int to ); - - // Tree view handling - void DisplayTree( mgSelectionTreeNode *node, int select = 0 ); - void DisplayTreeViewSelector(); - void DisplayTreeSubmenu(); - eOSState TreeSubmenuAction( int n ); - - // Playlist view handling - void DisplayPlaylist( int index_current = -1 ); - void DisplayTrackInfo(); - void DisplayAlbumInfo(); - - void LoadPlaylist(); - void SavePlaylist(); - void RenamePlaylist( std::string name ); - void DisplayPlaylistSubmenu(); - eOSState PlaylistSubmenuAction( int n ); - void DisplayPlaylistCommands(); - eOSState ExecutePlaylistCommand( int current ); - - // Filter view handling - void DisplayFilter(); - void DisplayFilterSelector(); - - private: - //! \brief launch the actual player - void Play( mgPlaylist *plist, unsigned first ); - - // content stuff - mgMedia *m_media; - mgSelectionTreeNode *m_root; - mgSelectionTreeNode *m_node; - mgSelectionTreeNode *m_menu_item; - mgPlaylist *m_current_playlist; - std::vector< std::string > *m_plists; - - MuggleStatus m_state; - std::list< int > m_history; - - cCommands *m_playlist_commands; - - int m_last_osd_index; - - char m_listname[32]; - bool m_editing_listname; + protected: + void BuildOsd (); }; #endif diff --git a/vdr_network.h b/vdr_network.h index 182fa23..4725d2a 100644 --- a/vdr_network.h +++ b/vdr_network.h @@ -15,31 +15,33 @@ class cRingBufferLinear; // ---------------------------------------------------------------- -class mgNet : public cRingBufferLinear, cThread +class mgNet:public cRingBufferLinear, cThread { -private: - int m_fd; - bool m_connected, m_netup; - int m_deferedErrno; - int m_rwTimeout, m_conTimeout; - unsigned char m_lineBuff[4096]; - int m_count; - // - void close(void); - int ringRead(unsigned char *dest, int len); - void copyFromBuff(unsigned char *dest, int n); -protected: - virtual void action(void); -public: - mgNet(int size, int ConTimeoutMs, int RwTimeoutMs); - ~mgNet(); - bool connect(const char *hostname, const int port); - void disconnect(void); - bool connected(void) { return m_connected; } - int gets(char *dest, int len); - int puts(char *dest); - int read(unsigned char *dest, int len); - int write(unsigned char *dest, int len); + private: + int m_fd; + bool m_connected, m_netup; + int m_deferedErrno; + int m_rwTimeout, m_conTimeout; + unsigned char m_lineBuff[4096]; + int m_count; +// + void close (void); + int ringRead (unsigned char *dest, int len); + void copyFromBuff (unsigned char *dest, int n); + protected: + virtual void action (void); + public: + mgNet (int size, int ConTimeoutMs, int RwTimeoutMs); + ~mgNet (); + bool connect (const char *hostname, const int port); + void disconnect (void); + bool connected (void) + { + return m_connected; + } + int gets (char *dest, int len); + int puts (char *dest); + int read (unsigned char *dest, int len); + int write (unsigned char *dest, int len); }; - -#endif //___NETWORK_H +#endif //___NETWORK_H diff --git a/vdr_player.c b/vdr_player.c index 48182ef..b281746 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -4,12 +4,12 @@ * * \version $Revision: 1.7 $ * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald * \author Responsible author: $Author$ * * $Id$ * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ @@ -27,7 +27,6 @@ #include #include -#include #include #include @@ -37,45 +36,44 @@ #include #include -#include "muggle.h" #include "vdr_player.h" #include "vdr_decoder.h" #include "vdr_config.h" #include "vdr_setup.h" #include "i18n.h" +#include "mg_db.h" #include "mg_tools.h" -#include "mg_playlist.h" -#include "mg_content_interface.h" - // ---------------------------------------------------------------- // TODO: check for use of constants -#define OUT_BITS 16 // output 16 bit samples to DVB driver -#define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes +#define OUT_BITS 16 // output 16 bit samples to DVB driver +#define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes // cResample -#define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame +#define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame // cNormalize -#define MAX_GAIN 3.0 // max. allowed gain -#define LIM_ACC 12 // bit, accuracy for lookup table -#define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1) // max. value covered by lookup table -#define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC) // shift value for table lookup -#define F_LIM_JMP (mad_fixed_t)(1<getCurrentTrack (); + if (newcurr) { - if( m_playlist && !m_started ) - { - m_playmode = pmStartup; - Start(); - - m_started = true; - m_current = 0; + if (m_current) delete m_current; + m_current = new mgContentItem(newcurr); + } + Play (); +} - m_playmode_mutex.Lock(); - WaitPlayMode( pmStartup, true ); // wait for the decoder to become ready - m_playmode_mutex.Unlock(); +void +mgPCMPlayer::Activate (bool on) +{ + MGLOG ("mgPCMPlayer::Activate"); + if (on) + { + if (m_playlist && !m_started) + { + m_playmode = pmStartup; + Start (); - Lock(); + m_started = true; + if (m_current) delete m_current; + m_current = 0; - m_playlist->initialize( ); - if( m_first > 0 ) - { - m_playlist->gotoPosition( m_first ); - } - m_current = m_playlist->getCurrent(); - Play(); + m_playmode_mutex.Lock (); + WaitPlayMode (pmStartup, true); // wait for the decoder to become ready + m_playmode_mutex.Unlock (); - Unlock(); - } + Lock (); + PlayTrack(); + Unlock (); + } } - else if( m_started && m_active ) + else if (m_started && m_active) { - Lock(); - StopPlay(); - Unlock(); + Lock (); + StopPlay (); + Unlock (); - m_active = false; - SetPlayMode( pmStartup ); + m_active = false; + SetPlayMode (pmStartup); - Cancel(2); + Cancel (2); } } -void mgPCMPlayer::NewPlaylist( mgPlaylist *plist, unsigned start ) +void +mgPCMPlayer::ReloadPlaylist() { - MGLOG( "mgPCMPlayer::NewPlaylist" ); + Lock (); + m_playlist->clearCache(); + Unlock (); +} - Lock(); - StopPlay(); - Unlock(); +void +mgPCMPlayer::NewPlaylist (mgSelection * plist) +{ + MGLOG ("mgPCMPlayer::NewPlaylist"); - // memory management of playlists should happen elsewhere (menu, content) - m_playlist = plist; - m_current = 0; + Lock (); + StopPlay (); - if( start > 0 ) - { - m_playlist->gotoPosition( (unsigned) start ); - m_current = m_playlist->getCurrent(); - Play(); - } - else if( NextFile() ) - { - Play(); - } + delete m_playlist; + m_playlist = plist; + PlayTrack(); + Unlock (); } -void mgPCMPlayer::SetPlayMode(ePlayMode mode) +void +mgPCMPlayer::SetPlayMode (ePlayMode mode) { - m_playmode_mutex.Lock(); - if( mode != m_playmode ) + m_playmode_mutex.Lock (); + if (mode != m_playmode) { - m_playmode = mode; - m_playmode_cond.Broadcast(); + m_playmode = mode; + m_playmode_cond.Broadcast (); } - m_playmode_mutex.Unlock(); + m_playmode_mutex.Unlock (); } -void mgPCMPlayer::WaitPlayMode(ePlayMode mode, bool inv) + +void +mgPCMPlayer::WaitPlayMode (ePlayMode mode, bool inv) { - // must be called with m_playmode_mutex LOCKED !!! +// must be called with m_playmode_mutex LOCKED !!! - while( m_active && ( (!inv && mode != m_playmode) || (inv && mode == m_playmode) ) ) + while (m_active + && ((!inv && mode != m_playmode) || (inv && mode == m_playmode))) { - m_playmode_cond.Wait(m_playmode_mutex); + m_playmode_cond.Wait (m_playmode_mutex); } } -void mgPCMPlayer::Action(void) -{ - MGLOG( "mgPCMPlayer::Action" ); - struct mgDecode *ds=0; - struct mad_pcm *pcm=0; - cResample resample[2]; - unsigned int nsamples[2]; - const mad_fixed_t *data[2]; - cScale scale; - cLevel level; - cNormalize norm; - bool haslevel=false; - struct LPCMFrame lpcmFrame; - const unsigned char *p=0; - int pc = 0, only48khz = the_setup.Only48kHz; - cPoller poll; +void +mgPCMPlayer::Action (void) +{ + MGLOG ("mgPCMPlayer::Action"); + + struct mgDecode *ds = 0; + struct mad_pcm *pcm = 0; + cResample resample[2]; + unsigned int nsamples[2]; + const mad_fixed_t *data[2]; + cScale scale; + cLevel level; + cNormalize norm; + bool haslevel = false; + struct LPCMFrame lpcmFrame; + const unsigned char *p = 0; + int pc = 0, only48khz = the_setup.Only48kHz; + cPoller poll; #ifdef DEBUG - int beat=0; + int beat = 0; #endif - dsyslog( "muggle: player thread started (pid=%d)", getpid() ); - - memset( &lpcmFrame, 0, sizeof(lpcmFrame) ); - lpcmFrame.PES[2]=0x01; - lpcmFrame.PES[3]=0xbd; - lpcmFrame.PES[6]=0x87; - lpcmFrame.LPCM[0]=0xa0; // substream ID - lpcmFrame.LPCM[1]=0xff; - lpcmFrame.LPCM[5]=0x01; - lpcmFrame.LPCM[6]=0x80; - - dvbSampleRate = 48000; - m_state = msStop; - SetPlayMode( pmStopped ); - - while( m_active ) + dsyslog ("muggle: player thread started (pid=%d)", getpid ()); + + memset (&lpcmFrame, 0, sizeof (lpcmFrame)); + lpcmFrame.PES[2] = 0x01; + lpcmFrame.PES[3] = 0xbd; + lpcmFrame.PES[6] = 0x87; + lpcmFrame.LPCM[0] = 0xa0; // substream ID + lpcmFrame.LPCM[1] = 0xff; + lpcmFrame.LPCM[5] = 0x01; + lpcmFrame.LPCM[6] = 0x80; + + dvbSampleRate = 48000; + m_state = msStop; + SetPlayMode (pmStopped); + + while (m_active) { #ifdef DEBUG - if(time(0)>=beat+30) - { - std::cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer->Available() << std::endl << std::flush; - scale.Stats(); if(haslevel) norm.Stats(); - beat=time(0); - } + if (time (0) >= beat + 30) + { + std:: + cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer-> + Available () << std::endl << std::flush; + scale.Stats (); + if (haslevel) + norm.Stats (); + beat = time (0); + } #endif - Lock(); - - if( !m_rframe && m_playmode == pmPlay ) - { - switch( m_state ) - { - case msStart: - { - m_index = 0; - m_playing = m_current; - - if( m_playing && m_playing != &(mgContentItem::UNDEFINED) ) - { - std::string filename = m_playing->getSourceFile(); - // mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); - - if( ( m_decoder = mgDecoders::findDecoder( m_playing ) ) && m_decoder->start() ) - { - levelgood = true; - haslevel = false; - - scale.Init(); - level.Init(); - - m_state = msDecode; - - break; - } - } - m_state = msEof; - } break; - case msDecode: - { - ds = m_decoder->decode(); - switch( ds->status ) - { - case dsPlay: - { - pcm = ds->pcm; - m_index = ds->index/1000; - m_state = msNormalize; - } break; - case dsSkip: - case dsSoftError: - { - // skipping, state unchanged, next decode - } break; - case dsEof: - { - m_state = msEof; - } break; - case dsOK: - case dsError: - { - m_state = msError; - } break; - } - } break; - case msNormalize: - { - if(!haslevel) - { - if( levelgood ) - { - level.GetPower( pcm ); - } - } - else - { - norm.AddGain( pcm ); - } - m_state = msResample; - } break; - case msResample: - { + Lock (); + + if (!m_rframe && m_playmode == pmPlay) + { + switch (m_state) + { + case msStart: + { + m_index = 0; + m_playing = m_current; + + if (m_playing) + { + std::string filename = m_playing->getSourceFile (); +mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); + + if ((m_decoder = mgDecoders::findDecoder (m_playing)) + && m_decoder->start ()) + { + levelgood = true; + haslevel = false; + + scale.Init (); + level.Init (); + + m_state = msDecode; + + break; + } + } + m_state = msEof; + } + break; + case msDecode: + { + ds = m_decoder->decode (); + switch (ds->status) + { + case dsPlay: + { + pcm = ds->pcm; + m_index = ds->index / 1000; + m_state = msNormalize; + } + break; + case dsSkip: + case dsSoftError: + { +// skipping, state unchanged, next decode + } + break; + case dsEof: + { + m_state = msEof; + } + break; + case dsOK: + case dsError: + { + m_state = msError; + } + break; + } + } + break; + case msNormalize: + { + if (!haslevel) + { + if (levelgood) + { + level.GetPower (pcm); + } + } + else + { + norm.AddGain (pcm); + } + m_state = msResample; + } + break; + case msResample: + { #ifdef DEBUG - { - static unsigned int oldrate=0; - if(oldrate!=pcm->samplerate) - { - std::cout << "mgPCMPlayer::Action: new input sample rate " << pcm->samplerate << std::endl << std::flush; - oldrate = pcm->samplerate; - } - } + { + static unsigned int oldrate = 0; + if (oldrate != pcm->samplerate) + { + std:: + cout << "mgPCMPlayer::Action: new input sample rate " + << pcm->samplerate << std::endl << std::flush; + oldrate = pcm->samplerate; + } + } #endif - nsamples[0] = nsamples[1] = pcm->length; - data[0] = pcm->samples[0]; - data[1] = pcm->channels > 1 ? pcm->samples[1]: 0; - - lpcmFrame.LPCM[5]&=0xcf; - dvbSampleRate=48000; - if(!only48khz) - { - switch(pcm->samplerate) - { // If one of the supported frequencies, do it without resampling. - case 96000: - { // Select a "even" upsampling frequency if possible, too. - lpcmFrame.LPCM[5] |= 1 << 4; - dvbSampleRate = 96000; - } break; - - //case 48000: // this is already the default ... - // lpcmFrame.LPCM[5]|=0<<4; - // dvbSampleRate=48000; - // break; - case 11025: - case 22050: - case 44100: - { - lpcmFrame.LPCM[5]|=2<<4; - dvbSampleRate = 44100; - } break; - case 8000: - case 16000: - case 32000: - { - lpcmFrame.LPCM[5]|=3<<4; - dvbSampleRate = 32000; - } break; - } - } - - if( dvbSampleRate != pcm->samplerate ) - { - if( resample[0].SetInputRate( pcm->samplerate, dvbSampleRate ) ) - { - nsamples[0] = resample[0].ResampleBlock( nsamples[0], data[0] ); - data[0] = resample[0].Resampled(); - } - if(data[1] && resample[1].SetInputRate( pcm->samplerate, dvbSampleRate ) ) - { - nsamples[1] = resample[1].ResampleBlock( nsamples[1], data[1] ); - data[1] = resample[1].Resampled(); - } - } - m_state=msOutput; - } break; - case msOutput: - { - if( nsamples[0] > 0 ) - { - unsigned int outlen = scale.ScaleBlock( lpcmFrame.Data, - sizeof(lpcmFrame.Data), - nsamples[0], data[0], - data[1], - the_setup.AudioMode? amDither: amRound ); - if( outlen ) - { - outlen += sizeof(lpcmFrame.LPCM)+LEN_CORR; - lpcmFrame.PES[4] = outlen >> 8; - lpcmFrame.PES[5] = outlen; - m_rframe = new cFrame( (unsigned char *)&lpcmFrame, - outlen + sizeof( lpcmFrame.PES ) - LEN_CORR ); - } - } - else - { - m_state=msDecode; - } - } break; - case msError: - case msEof: - { - if( NextFile() ) - { - m_state = msStart; - } - else - { - m_state = msWait; - } - } // fall through - case msStop: - { - m_playing = 0; - if( m_decoder ) - { // who deletes decoder? - m_decoder->stop(); - m_decoder = 0; - } - - levelgood = false; - - scale.Stats(); - if( haslevel ) - { - norm.Stats(); - } - if( m_state == msStop ) - { // might be unequal in case of fall through from eof/error - SetPlayMode( pmStopped ); - } - } break; - case msWait: - { - if( m_ringbuffer->Available() == 0 ) - { - m_active = false; - SetPlayMode(pmStopped); - } - } break; - } - } - - if( m_rframe && m_ringbuffer->Put( m_rframe ) ) - { - m_rframe = 0; - } - - if( !m_pframe && m_playmode == pmPlay ) - { - m_pframe = m_ringbuffer->Get(); - if( m_pframe ) - { - p = m_pframe->Data(); - pc = m_pframe->Count(); - } - } - - if( m_pframe ) - { - int w = PlayVideo( p, pc ); - if( w > 0 ) - { - p += w; - pc -= w; - - if( pc <= 0 ) - { - m_ringbuffer->Drop(m_pframe); - m_pframe=0; - } - } - else if( w < 0 && FATALERRNO ) - { - LOG_ERROR; - break; - } - } - - Unlock(); - - if( (m_rframe || m_state == msWait) && m_pframe ) - { - // Wait for output to become ready - DevicePoll( poll, 500 ); - } - else - { - if( m_playmode != pmPlay ) - { - m_playmode_mutex.Lock(); - - if( m_playmode != pmPlay ) - { - WaitPlayMode( m_playmode, true ); // Wait on playMode change - } - m_playmode_mutex.Unlock(); - } - } + nsamples[0] = nsamples[1] = pcm->length; + data[0] = pcm->samples[0]; + data[1] = pcm->channels > 1 ? pcm->samples[1] : 0; + + lpcmFrame.LPCM[5] &= 0xcf; + dvbSampleRate = 48000; + if (!only48khz) + { + switch (pcm->samplerate) + { // If one of the supported frequencies, do it without resampling. + case 96000: + { // Select a "even" upsampling frequency if possible, too. + lpcmFrame.LPCM[5] |= 1 << 4; + dvbSampleRate = 96000; + } + break; + +//case 48000: // this is already the default ... +// lpcmFrame.LPCM[5]|=0<<4; +// dvbSampleRate=48000; +// break; + case 11025: + case 22050: + case 44100: + { + lpcmFrame.LPCM[5] |= 2 << 4; + dvbSampleRate = 44100; + } + break; + case 8000: + case 16000: + case 32000: + { + lpcmFrame.LPCM[5] |= 3 << 4; + dvbSampleRate = 32000; + } + break; + } + } + + if (dvbSampleRate != pcm->samplerate) + { + if (resample[0]. + SetInputRate (pcm->samplerate, dvbSampleRate)) + { + nsamples[0] = + resample[0].ResampleBlock (nsamples[0], data[0]); + data[0] = resample[0].Resampled (); + } + if (data[1] + && resample[1].SetInputRate (pcm->samplerate, + dvbSampleRate)) + { + nsamples[1] = + resample[1].ResampleBlock (nsamples[1], data[1]); + data[1] = resample[1].Resampled (); + } + } + m_state = msOutput; + } + break; + case msOutput: + { + if (nsamples[0] > 0) + { + unsigned int outlen = scale.ScaleBlock (lpcmFrame.Data, + sizeof (lpcmFrame. + Data), + nsamples[0], + data[0], + data[1], + the_setup. + AudioMode ? + amDither : + amRound); + if (outlen) + { + outlen += sizeof (lpcmFrame.LPCM) + LEN_CORR; + lpcmFrame.PES[4] = outlen >> 8; + lpcmFrame.PES[5] = outlen; + m_rframe = new cFrame ((unsigned char *) &lpcmFrame, + outlen + + sizeof (lpcmFrame.PES) - + LEN_CORR); + } + } + else + { + m_state = msDecode; + } + } + break; + case msError: + case msEof: + { + if (SkipFile ()) + { + m_state = msStart; + } + else + { + m_state = msWait; + } + } // fall through + case msStop: + { + m_playing = 0; + if (m_decoder) + { // who deletes decoder? + m_decoder->stop (); + m_decoder = 0; + } + + levelgood = false; + + scale.Stats (); + if (haslevel) + { + norm.Stats (); + } + if (m_state == msStop) + { // might be unequal in case of fall through from eof/error + SetPlayMode (pmStopped); + } + } + break; + case msWait: + { + if (m_ringbuffer->Available () == 0) + { + m_active = false; + SetPlayMode (pmStopped); + } + } + break; + } + } + + if (m_rframe && m_ringbuffer->Put (m_rframe)) + { + m_rframe = 0; + } + + if (!m_pframe && m_playmode == pmPlay) + { + m_pframe = m_ringbuffer->Get (); + if (m_pframe) + { + p = m_pframe->Data (); + pc = m_pframe->Count (); + } + } + + if (m_pframe) + { + int w = PlayVideo (p, pc); + if (w > 0) + { + p += w; + pc -= w; + + if (pc <= 0) + { + m_ringbuffer->Drop (m_pframe); + m_pframe = 0; + } + } + else if (w < 0 && FATALERRNO) + { + LOG_ERROR; + break; + } + } + + Unlock (); + + if ((m_rframe || m_state == msWait) && m_pframe) + { +// Wait for output to become ready + DevicePoll (poll, 500); + } + else + { + if (m_playmode != pmPlay) + { + m_playmode_mutex.Lock (); + + if (m_playmode != pmPlay) + { + // Wait on playMode change + WaitPlayMode (m_playmode, true); + } + m_playmode_mutex.Unlock (); + } + } } - - Lock(); - if( m_rframe ) + Lock (); + + if (m_rframe) { - delete m_rframe; - m_rframe=0; + delete m_rframe; + m_rframe = 0; } - if( m_decoder ) - { // who deletes decoder? - m_decoder->stop(); - m_decoder = 0; + if (m_decoder) + { // who deletes decoder? + m_decoder->stop (); + m_decoder = 0; } - - m_playing = 0; - SetPlayMode(pmStopped); + m_playing = 0; - Unlock(); + SetPlayMode (pmStopped); - m_active = false; - - dsyslog( "muggle: player thread ended (pid=%d)", getpid() ); + Unlock (); + + m_active = false; + + dsyslog ("muggle: player thread ended (pid=%d)", getpid ()); } -void mgPCMPlayer::Empty(void) + +void +mgPCMPlayer::Empty (void) { - MGLOG( "mgPCMPlayer::Empty" ); + MGLOG ("mgPCMPlayer::Empty"); - Lock(); + Lock (); - m_ringbuffer->Clear(); - DeviceClear(); + m_ringbuffer->Clear (); + DeviceClear (); - delete m_rframe; - m_rframe = 0; - m_pframe = 0; + delete m_rframe; + m_rframe = 0; + m_pframe = 0; - Unlock(); + Unlock (); } -void mgPCMPlayer::StopPlay() -{ // StopPlay() must be called in locked state!!! - MGLOG( "mgPCMPlayer::StopPlay" ); - if( m_playmode != pmStopped ) + +void +mgPCMPlayer::StopPlay () +{ // StopPlay() must be called in locked state!!! + MGLOG ("mgPCMPlayer::StopPlay"); + if (m_playmode != pmStopped) { - Empty(); - m_state = msStop; - SetPlayMode( pmPlay ); - Unlock(); // let the decode thread process the stop signal + Empty (); + m_state = msStop; + SetPlayMode (pmPlay); + Unlock (); // let the decode thread process the stop signal - m_playmode_mutex.Lock(); - WaitPlayMode( pmStopped, false ); - m_playmode_mutex.Unlock(); + m_playmode_mutex.Lock (); + WaitPlayMode (pmStopped, false); + m_playmode_mutex.Unlock (); - Lock(); + Lock (); } } -bool mgPCMPlayer::NextFile( ) -{ - mgContentItem *newcurr; - - bool res = false; - - if( m_playlist->skipFwd() ) - { - newcurr = m_playlist->getCurrent(); - } - else - { - newcurr = &(mgContentItem::UNDEFINED); - } - - if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) - { - m_current = newcurr; - mgMuggle::setResumeIndex( m_playlist->getIndex() ); - res = true; - } - - return res; -} -bool mgPCMPlayer::PrevFile(void) +bool mgPCMPlayer::SkipFile (int step) { - bool res = false; - - if( m_playlist->skipBack() ) + MGLOG("mgPCMPlayer::SkipFile"); + mgContentItem * newcurr = NULL; + if (m_playlist->skipTracks (step)) { - mgContentItem *newcurr = m_playlist->getCurrent(); - - if( newcurr && newcurr != &(mgContentItem::UNDEFINED) ) - { - m_current = newcurr; - mgMuggle::setResumeIndex( m_playlist->getIndex() ); - res = true; + newcurr = m_playlist->getCurrentTrack (); + if (newcurr) { + if (m_current) delete m_current; + m_current = new mgContentItem(newcurr); } } - - return res; + return (newcurr != NULL); } -void mgPCMPlayer::ToggleShuffle() +void +mgPCMPlayer::ToggleShuffle () { - m_playlist->toggleShuffleMode(); + m_playlist->toggleShuffleMode (); } -void mgPCMPlayer::ToggleLoop(void) + +void +mgPCMPlayer::ToggleLoop (void) { - m_playlist->toggleLoopMode(); + m_playlist->toggleLoopMode (); } -void mgPCMPlayer::Pause(void) + +void +mgPCMPlayer::Pause (void) { - if( m_playmode == pmPaused ) + if (m_playmode == pmPaused) { - Play(); + Play (); } - else + else { - if( m_playmode == pmPlay ) - { - // DeviceFreeze(); - SetPlayMode( pmPaused ); - } + if (m_playmode == pmPlay) + { +// DeviceFreeze(); + SetPlayMode (pmPaused); + } } } -void mgPCMPlayer::Play(void) + +void +mgPCMPlayer::Play (void) { - MGLOG( "mgPCMPlayer::Play" ); + MGLOG ("mgPCMPlayer::Play"); - Lock(); + Lock (); - if( m_playmode != pmPlay && m_current && m_current != &(mgContentItem::UNDEFINED) ) + if (m_playmode != pmPlay && m_current) { - if( m_playmode == pmStopped ) - { - m_state = msStart; - } - // DevicePlay(); // TODO? Commented out in original code, too - SetPlayMode( pmPlay ); + if (m_playmode == pmStopped) + { + m_state = msStart; + } +// DevicePlay(); // TODO? Commented out in original code, too + SetPlayMode (pmPlay); } - Unlock(); + Unlock (); } -void mgPCMPlayer::Forward() + +void +mgPCMPlayer::Forward () { - MGLOG( "mgPCMPlayer::Forward" ); + MGLOG ("mgPCMPlayer::Forward"); - Lock(); - if( NextFile() ) - { - StopPlay(); - Play(); + Lock (); + if (SkipFile ()) + { + StopPlay (); + Play (); } - Unlock(); + Unlock (); } -void mgPCMPlayer::Backward(void) + +void +mgPCMPlayer::Backward (void) { - Lock(); - if( PrevFile() ) - { - StopPlay(); - Play(); + MGLOG ("mgPCMPlayer::Backward"); + Lock (); + if (SkipFile (-1)) + { + StopPlay (); + Play (); } - Unlock(); + Unlock (); } -void mgPCMPlayer::Goto( int index, bool still ) + +void +mgPCMPlayer::Goto (int index, bool still) { - m_playlist->gotoPosition( index-1 ); - mgContentItem *next = m_playlist->getCurrent(); + m_playlist->setTrack (index - 1); + mgContentItem *next = m_playlist->getCurrentTrack (); - if( next && next != &(mgContentItem::UNDEFINED) ) //invalid + if (next) { - Lock(); - StopPlay(); - m_current = next; - Play(); - Unlock(); + Lock (); + StopPlay (); + if (m_current) delete m_current; + m_current = new mgContentItem(next); + Play (); + Unlock (); } } -void mgPCMPlayer::SkipSeconds(int secs) + +void +mgPCMPlayer::SkipSeconds (int secs) { - if( m_playmode != pmStopped ) + if (m_playmode != pmStopped) { - Lock(); - if( m_playmode == pmPaused ) - { - SetPlayMode( pmPlay ); - } - if( m_decoder && m_decoder->skip( secs, m_ringbuffer->Available(), dvbSampleRate ) ) - { - levelgood=false; - } - Empty(); - Unlock(); + Lock (); + if (m_playmode == pmPaused) + { + SetPlayMode (pmPlay); + } + if (m_decoder + && m_decoder->skip (secs, m_ringbuffer->Available (), + dvbSampleRate)) + { + levelgood = false; + } + Empty (); + Unlock (); } } -bool mgPCMPlayer::GetIndex( int ¤t, int &total, bool snaptoiframe ) + +bool mgPCMPlayer::GetIndex (int ¤t, int &total, bool snaptoiframe) { - if( m_current ) - { - current = SecondsToFrames( m_index ); - total = SecondsToFrames( m_current->getLength() ); - return true; + if (m_current) + { + current = SecondsToFrames (m_index); + total = SecondsToFrames (m_current->getDuration ()); + return true; } - return false; + return false; } + /* string mgPCMPlayer::CheckImage( string fileName, size_t j ) { - static char tmpFile[1024]; - char *tmp[2048]; - char *result = NULL; - FILE *fp; - - sprintf (tmpFile, "%s/%s", MP3Setup.ImageCacheDir, &fileName[j]); // ??? - strcpy (strrchr (tmpFile, '.'), ".mpg"); - d(printf("mp3[%d]: cache %s\n", getpid (), tmpFile)) - if ((fp = fopen (tmpFile, "rb"))) - { - fclose (fp); - result = tmpFile; - } - else - { - if ((fp = fopen (fileName, "rb"))) - { - fclose (fp); - d(printf("mp3[%d]: image %s found\n", getpid (), fileName)) - sprintf ((char *) tmp, "image_convert.sh \"%s\" \"%s\"", fileName, tmpFile); - system ((const char*) tmp); - result = tmpFile; - } - } - fp = fopen ("/tmp/vdr_mp3_current_image.txt", "w"); - fprintf (fp, "%s\n", fileName); - fclose (fp); - return result; +static char tmpFile[1024]; +char *tmp[2048]; +char *result = NULL; +FILE *fp; + +sprintf (tmpFile, "%s/%s", MP3Setup.ImageCacheDir, &fileName[j]); // ??? +strcpy (strrchr (tmpFile, '.'), ".mpg"); +d(printf("mp3[%d]: cache %s\n", getpid (), tmpFile)) +if ((fp = fopen (tmpFile, "rb"))) +{ +fclose (fp); +result = tmpFile; +} +else +{ +if ((fp = fopen (fileName, "rb"))) +{ +fclose (fp); +d(printf("mp3[%d]: image %s found\n", getpid (), fileName)) +sprintf ((char *) tmp, "image_convert.sh \"%s\" \"%s\"", fileName, tmpFile); +system ((const char*) tmp); +result = tmpFile; +} +} +fp = fopen ("/tmp/vdr_mp3_current_image.txt", "w"); +fprintf (fp, "%s\n", fileName); +fclose (fp); +return result; } char *cMP3Player::LoadImage(const char *fullname) { - size_t i, j = strlen (MP3Sources.GetSource()->BaseDir()) + 1; - char imageFile[1024]; - static char mpgFile[1024]; - char *p, *q = NULL; - char *imageSuffixes[] = { "png", "gif", "jpg" }; - - d(printf("mp3[%d]: checking %s for images\n", getpid (), fullname)) - strcpy (imageFile, fullname); - strcpy (mpgFile, ""); - // - // track specific image, e.g. .jpg - // - p = strrchr (imageFile, '.'); - if (p) - { - for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) - { - strcpy (p + 1, imageSuffixes[i]); - d(printf("mp3[%d]: %s\n", getpid (), imageFile)) - q = CheckImage (imageFile, j); - if (q) - { - strcpy (mpgFile, q); - } - } - } - // - // album specific image, e.g. cover.jpg in song directory - // - if (!strlen (mpgFile)) - { - p = strrchr (imageFile, '/'); - if (p) - { - strcpy (p + 1, "cover."); - p += 6; - for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) - { - strcpy (p + 1, imageSuffixes[i]); - d(printf("mp3[%d]: %s\n", getpid (), imageFile)) - q = CheckImage (imageFile, j); - if (q) - { - strcpy (mpgFile, q); - } - } - } - } - // - // artist specific image, e.g. artist.jpg in artist directory - // - if (!strlen (mpgFile)) - { - p = strrchr (imageFile, '/'); - if (p) - { - *p = '\0'; - p = strrchr (imageFile, '/'); - } - if (p) - { - strcpy (p + 1, "artist."); - p += 7; - for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) - { - strcpy (p + 1, imageSuffixes[i]); - d(printf("mp3[%d]: %s\n", getpid (), imageFile)) - q = CheckImage (imageFile, j); - if (q) - { - strcpy (mpgFile, q); - } - } - } - } - // - // default background image - // - if (!strlen (mpgFile)) - { - for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) - { - sprintf (imageFile, "%s/background.%s", MP3Setup.ImageCacheDir, imageSuffixes[i]); - d(printf("mp3[%d]: %s\n", getpid (), imageFile)) - q = CheckImage (imageFile, strlen(MP3Setup.ImageCacheDir) + 1); - if (q) - { - strcpy (mpgFile, q); - } - } - } - if (!strlen (mpgFile)) - { - sprintf (mpgFile, "%s/background.mpg", MP3Setup.ImageCacheDir); - } - return mpgFile; +size_t i, j = strlen (MP3Sources.GetSource()->BaseDir()) + 1; +char imageFile[1024]; +static char mpgFile[1024]; +char *p, *q = NULL; +char *imageSuffixes[] = { "png", "gif", "jpg" }; + +d(printf("mp3[%d]: checking %s for images\n", getpid (), fullname)) +strcpy (imageFile, fullname); +strcpy (mpgFile, ""); +// +// track specific image, e.g. .jpg +// +p = strrchr (imageFile, '.'); +if (p) +{ +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +strcpy (p + 1, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, j); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +// +// album specific image, e.g. cover.jpg in song directory +// +if (!strlen (mpgFile)) +{ +p = strrchr (imageFile, '/'); +if (p) +{ +strcpy (p + 1, "cover."); +p += 6; +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +strcpy (p + 1, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, j); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +} +// +// artist specific image, e.g. artist.jpg in artist directory +// +if (!strlen (mpgFile)) +{ +p = strrchr (imageFile, '/'); +if (p) +{ +*p = '\0'; +p = strrchr (imageFile, '/'); +} +if (p) +{ +strcpy (p + 1, "artist."); +p += 7; +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +strcpy (p + 1, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, j); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +} +// +// default background image +// +if (!strlen (mpgFile)) +{ +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +sprintf (imageFile, "%s/background.%s", MP3Setup.ImageCacheDir, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, strlen(MP3Setup.ImageCacheDir) + 1); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +if (!strlen (mpgFile)) +{ +sprintf (mpgFile, "%s/background.mpg", MP3Setup.ImageCacheDir); +} +return mpgFile; } void mgPCMPlayer::ShowImage (char *file) { - uchar *buffer; - int fd; - struct stat st; - struct video_still_picture sp; - - if ((fd = open (file, O_RDONLY)) >= 0) - { - d(printf("mp3[%d]: cover still picture %s\n", getpid (), file)) - fstat (fd, &st); - sp.iFrame = (char *) malloc (st.st_size); - if (sp.iFrame) - { - sp.size = st.st_size; - if (read (fd, sp.iFrame, sp.size) > 0) - { - buffer = (uchar *) sp.iFrame; - d(printf("mp3[%d]: new image frame (size %d)\n", getpid(), sp.size)) - if(MP3Setup.UseDeviceStillPicture) - DeviceStillPicture (buffer, sp.size); - else - { - for (int i = 1; i <= 25; i++) - { - send_pes_packet (buffer, sp.size, i); - } - } - } - free (sp.iFrame); - } - else - { - esyslog ("mp3[%d]: cannot allocate memory (%d bytes) for still image", - getpid(), (int) st.st_size); - } - close (fd); - } - else - { - esyslog ("mp3[%d]: cannot open image file '%s'", - getpid(), file); - } +uchar *buffer; +int fd; +struct stat st; +struct video_still_picture sp; + +if ((fd = open (file, O_RDONLY)) >= 0) +{ +d(printf("mp3[%d]: cover still picture %s\n", getpid (), file)) +fstat (fd, &st); +sp.iFrame = (char *) malloc (st.st_size); +if (sp.iFrame) +{ +sp.size = st.st_size; +if (read (fd, sp.iFrame, sp.size) > 0) +{ +buffer = (uchar *) sp.iFrame; +d(printf("mp3[%d]: new image frame (size %d)\n", getpid(), sp.size)) +if(MP3Setup.UseDeviceStillPicture) +DeviceStillPicture (buffer, sp.size); +else +{ +for (int i = 1; i <= 25; i++) +{ +send_pes_packet (buffer, sp.size, i); +} +} +} +free (sp.iFrame); +} +else +{ +esyslog ("mp3[%d]: cannot allocate memory (%d bytes) for still image", +getpid(), (int) st.st_size); +} +close (fd); +} +else +{ +esyslog ("mp3[%d]: cannot open image file '%s'", +getpid(), file); +} } void mgPCMPlayer::send_pes_packet(unsigned char *data, int len, int timestamp) { #define PES_MAX_SIZE 2048 - int ptslen = timestamp ? 5 : 1; - static unsigned char pes_header[PES_MAX_SIZE]; - - pes_header[0] = pes_header[1] = 0; - pes_header[2] = 1; - pes_header[3] = 0xe0; - - while(len > 0) - { - int payload_size = len; - if(6 + ptslen + payload_size > PES_MAX_SIZE) - payload_size = PES_MAX_SIZE - (6 + ptslen); - - pes_header[4] = (ptslen + payload_size) >> 8; - pes_header[5] = (ptslen + payload_size) & 255; - - if(ptslen == 5) - { - int x; - x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1; - pes_header[8] = x; - x = ((((timestamp >> 15) & 0x7fff) << 1) | 1); - pes_header[7] = x >> 8; - pes_header[8] = x & 255; - x = ((((timestamp) & 0x7fff) < 1) | 1); - pes_header[9] = x >> 8; - pes_header[10] = x & 255; - } else - { - pes_header[6] = 0x0f; - } - - memcpy(&pes_header[6 + ptslen], data, payload_size); - PlayVideo(pes_header, 6 + ptslen + payload_size); - - len -= payload_size; - data += payload_size; - ptslen = 1; - } +int ptslen = timestamp ? 5 : 1; +static unsigned char pes_header[PES_MAX_SIZE]; + +pes_header[0] = pes_header[1] = 0; +pes_header[2] = 1; +pes_header[3] = 0xe0; + +while(len > 0) +{ +int payload_size = len; +if(6 + ptslen + payload_size > PES_MAX_SIZE) +payload_size = PES_MAX_SIZE - (6 + ptslen); + +pes_header[4] = (ptslen + payload_size) >> 8; +pes_header[5] = (ptslen + payload_size) & 255; + +if(ptslen == 5) +{ +int x; +x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1; +pes_header[8] = x; +x = ((((timestamp >> 15) & 0x7fff) << 1) | 1); +pes_header[7] = x >> 8; +pes_header[8] = x & 255; +x = ((((timestamp) & 0x7fff) < 1) | 1); +pes_header[9] = x >> 8; +pes_header[10] = x & 255; +} else +{ +pes_header[6] = 0x0f; +} + +memcpy(&pes_header[6 + ptslen], data, payload_size); +PlayVideo(pes_header, 6 + ptslen + payload_size); + +len -= payload_size; +data += payload_size; +ptslen = 1; +} } */ // --- mgPlayerControl ------------------------------------------------------- -mgPlayerControl::mgPlayerControl( mgPlaylist *plist, unsigned start ) - : cControl( player = new mgPCMPlayer( plist, start ) ) +mgPlayerControl::mgPlayerControl (mgSelection * plist):cControl (player = +new +mgPCMPlayer (plist)) { - MGLOG( "mgPlayerControl::mgPlayerControl" ); - #if VDRVERSNUM >= 10307 - m_display = NULL; - m_menu = NULL; + m_display = NULL; + m_menu = NULL; #endif - m_has_osd = false; + m_visible = false; + m_has_osd = false; + m_track_view = true; + m_progress_view = true; - // obtain settings from last run - m_visible = the_setup.visible; - m_track_view = the_setup.trackview; - m_progress_view = the_setup.progressview; + m_szLastShowStatusMsg = NULL; - m_szLastShowStatusMsg = NULL; - - // Notify all cStatusMonitor - StatusMsgReplaying(); +// Notify all cStatusMonitor + StatusMsgReplaying (); } -mgPlayerControl::~mgPlayerControl() + +mgPlayerControl::~mgPlayerControl () { - // Notify cleanup all cStatusMonitor - cStatus::MsgReplaying(this, NULL); - if( m_szLastShowStatusMsg ) - { - free(m_szLastShowStatusMsg); - m_szLastShowStatusMsg = NULL; +// Stop(); +// Notify cleanup all cStatusMonitor + cStatus::MsgReplaying (this, NULL); + if (m_szLastShowStatusMsg) + { + free (m_szLastShowStatusMsg); + m_szLastShowStatusMsg = NULL; } - InternalHide(); - Stop(); + InternalHide (); + Stop (); } -bool mgPlayerControl::Active(void) + +bool mgPlayerControl::Active (void) { - return player && player->Active(); + return player && player->Active (); } -void mgPlayerControl::Stop(void) + +void +mgPlayerControl::Stop (void) { - if( player ) + if (player) { - delete player; - player = 0; + delete player; + player = 0; } } -void mgPlayerControl::Pause(void) + +void +mgPlayerControl::Pause (void) { - if( player ) + if (player) { - player->Pause(); + player->Pause (); } } -void mgPlayerControl::Play(void) + +void +mgPlayerControl::Play (void) { - if( player ) + if (player) { - player->Play(); + player->Play (); } } -void mgPlayerControl::Forward(void) + +void +mgPlayerControl::Forward (void) { - if( player ) + if (player) { - player->Forward(); + player->Forward (); } } -void mgPlayerControl::Backward(void) + +void +mgPlayerControl::Backward (void) { - if( player ) + if (player) { - player->Backward(); + player->Backward (); } } -void mgPlayerControl::SkipSeconds(int Seconds) + +void +mgPlayerControl::SkipSeconds (int Seconds) +{ + if (player) + { + player->SkipSeconds (Seconds); + } +} + + +void +mgPlayerControl::Goto (int Position, bool Still) { - if( player ) + if (player) { - player->SkipSeconds(Seconds); + player->Goto (Position, Still); } } -void mgPlayerControl::Goto(int Position, bool Still) + +void +mgPlayerControl::ToggleShuffle (void) { - if( player ) + if (player) { - player->Goto(Position, Still); + player->ToggleShuffle (); } } -void mgPlayerControl::ToggleShuffle(void) + +void +mgPlayerControl::ToggleLoop (void) { - if( player ) + if (player) { - player->ToggleShuffle(); + player->ToggleLoop (); } } -void mgPlayerControl::ToggleLoop(void) +void +mgPlayerControl::ReloadPlaylist () { - if( player ) + if (player) { - player->ToggleLoop(); + player->ReloadPlaylist (); } } -void mgPlayerControl::NewPlaylist(mgPlaylist *plist, unsigned start) +void +mgPlayerControl::NewPlaylist (mgSelection * plist) { - if( player ) + if (player) { - player->NewPlaylist(plist, start); + player->NewPlaylist (plist); } } -void mgPlayerControl::ShowContents() +void +mgPlayerControl::ShowContents () { #if VDRVERSNUM >= 10307 - if( !m_menu ) + if (!m_menu) { - m_menu = Skins.Current()->DisplayMenu(); + m_menu = Skins.Current ()->DisplayMenu (); } - if( player && m_menu ) + if (player && m_menu) { - int num_items = m_menu->MaxItems(); - - if( m_track_view ) - { - m_menu->Clear(); - m_menu->SetTitle( "Track info view" ); - - m_menu->SetTabs( 15 ); - - char *buf; - if( num_items > 0 ) - { - asprintf( &buf, "Title:\t%s", player->getCurrent()->getLabel(0).c_str() ); - m_menu->SetItem( buf, 0, false, false ); - free( buf ); - } - if( num_items > 1 ) - { - asprintf( &buf, "Artist:\t%s", player->getCurrent()->getLabel(1).c_str() ); - m_menu->SetItem( buf, 1, false, false ); - free( buf ); - } - if( num_items > 2 ) - { - asprintf( &buf, "Album:\t%s", player->getCurrent()->getLabel(2).c_str() ); - m_menu->SetItem( buf, 2, false, false ); - free( buf ); - } - if( num_items > 3 ) - { - asprintf( &buf, "Genre:\t%s", player->getCurrent()->getLabel(3).c_str() ); - m_menu->SetItem( buf, 3, false, false ); - free( buf ); - } - if( num_items > 4 ) - { - int len = player->getCurrent()->getLength(); - asprintf( &buf, "Length:\t%s", IndexToHMSF( SecondsToFrames( len ) ) ); - m_menu->SetItem( buf, 4, false, false ); - free( buf ); - } - if( num_items > 5 ) - { - asprintf( &buf, "Bit rate:\t%s", player->getCurrent()->getBitrate().c_str() ); - m_menu->SetItem( buf, 5, false, false ); - free( buf ); - } - if( num_items > 6 ) - { - int sr = player->getCurrent()->getSampleRate(); - - asprintf( &buf, "Sampling rate:\t%d", sr ); - m_menu->SetItem( buf, 6, false, false ); - free( buf ); - } - } - else - { - mgPlaylist *list = player->getPlaylist(); - if( list ) - { - // use items for playlist tag display - m_menu->Clear(); - m_menu->SetTitle( "Playlist info view" ); - - int cur = list->getIndex(); - - char *buf; - for( int i=0; i < num_items; i ++ ) - { - mgContentItem *item = list->getItem( cur-3+i ); - if( item->isValid() ) - { - asprintf( &buf, "%s\t%s", item->getLabel(0).c_str(), item->getLabel(1).c_str() ); - if( i < 3 ) - { // already played - m_menu->SetItem( buf, i, false, false ); - } - if( i > 3 ) - { // to be played - m_menu->SetItem( buf, i, false, true ); - } - if( i == 3 ) - { - m_menu->SetItem( buf, i, true, true ); - } - free( buf ); - } - } - } - } + int num_items = m_menu->MaxItems (); + + if (m_track_view) + { + m_menu->Clear (); + m_menu->SetTitle ("Track info view"); + + m_menu->SetTabs (15); + + char *buf; + if (num_items > 0) + { + asprintf (&buf, "Title:\t%s", + player->getCurrent ()->getTitle ().c_str ()); + m_menu->SetItem (buf, 0, false, false); + free (buf); + } + if (num_items > 1) + { + asprintf (&buf, "Artist:\t%s", + player->getCurrent ()->getArtist ().c_str ()); + m_menu->SetItem (buf, 1, false, false); + free (buf); + } + if (num_items > 2) + { + asprintf (&buf, "Album:\t%s", + player->getCurrent ()->getAlbum ().c_str ()); + m_menu->SetItem (buf, 2, false, false); + free (buf); + } + if (num_items > 3) + { + asprintf (&buf, "Genre:\t%s", + player->getCurrent ()->getGenre ().c_str ()); + m_menu->SetItem (buf, 3, false, false); + free (buf); + } + if (num_items > 4) + { + int len = player->getCurrent ()->getDuration (); + asprintf (&buf, "Length:\t%s", + IndexToHMSF (SecondsToFrames (len))); + m_menu->SetItem (buf, 4, false, false); + free (buf); + } + if (num_items > 5) + { + asprintf (&buf, "Bit rate:\t%s", + player->getCurrent ()->getBitrate ().c_str ()); + m_menu->SetItem (buf, 5, false, false); + free (buf); + } + if (num_items > 6) + { + int sr = player->getCurrent ()->getSampleRate (); + + asprintf (&buf, "Sampling rate:\t%d", sr); + m_menu->SetItem (buf, 6, false, false); + free (buf); + } + } + else + { + mgSelection *list = player->getPlaylist (); + if (list) + { +// use items for playlist tag display + m_menu->Clear (); + m_menu->SetTitle ("Now playing"); + m_menu->SetTabs (25); + + int cur = list->getTrackPosition (); + for (int i = 0; i < num_items; i++) + { + mgContentItem *item = list->getTrack (cur - 3 + i); + if (item) + { + char *buf; + asprintf (&buf, "%s\t%s", item->getTitle ().c_str (), + item->getArtist ().c_str ()); + m_menu->SetItem (buf, i, i == 3, i > 3); + free (buf); + } + } + } + } } #endif } -void mgPlayerControl::ShowProgress() + +void +mgPlayerControl::ShowProgress () { - if( player ) + if (player) { - char *buf; - bool play = true, forward = true; - int speed = -1; - - int current_frame, total_frames; - player->GetIndex( current_frame, total_frames ); - - if( !m_track_view ) - { // playlist stuff - mgPlaylist *list = player->getPlaylist(); - if( list ) - { - total_frames = SecondsToFrames( list->getLength() ); - current_frame += SecondsToFrames( list->getCompletedLength() ); - asprintf( &buf, "Playlist %s (%d/%d)", list->getListname().c_str(), list->getIndex()+1, list->getNumItems() ); - } - } - else - { // track view - asprintf( &buf, "%s: %s", player->getCurrent()->getLabel(1).c_str(), player->getCurrent()->getTitle().c_str() ); - } + char *buf; + bool play = true, forward = true; + int speed = -1; + + int current_frame, total_frames; + player->GetIndex (current_frame, total_frames); + + if (!m_track_view) + { // playlist stuff + mgSelection *list = player->getPlaylist (); + if (list) + { + list->clearCache(); // playlist can dynamically grow, force reload + total_frames = SecondsToFrames (list->getLength ()); + current_frame += SecondsToFrames (list->getCompletedLength ()); + asprintf (&buf, "%s (%d/%d)", list->getListname ().c_str (), + list->getTrackPosition () + 1, list->getNumTracks ()); + } + } + else + { // track view + asprintf (&buf, "%s: %s", + player->getCurrent ()->getArtist ().c_str (), + player->getCurrent ()->getTitle ().c_str ()); + } #if VDRVERSNUM >= 10307 - if( !m_display ) - { - m_display = Skins.Current()->DisplayReplay(false); - } - if( m_display ) - { - m_display->SetProgress( current_frame, total_frames ); - m_display->SetCurrent( IndexToHMSF( current_frame ) ); - m_display->SetTotal( IndexToHMSF( total_frames ) ); - m_display->SetTitle( buf ); - m_display->SetMode( play, forward, speed ); - m_display->Flush(); - } + if (!m_display) + { + m_display = Skins.Current ()->DisplayReplay (false); + } + if (m_display) + { + m_display->SetProgress (current_frame, total_frames); + m_display->SetCurrent (IndexToHMSF (current_frame)); + m_display->SetTotal (IndexToHMSF (total_frames)); + m_display->SetTitle (buf); + m_display->SetMode (play, forward, speed); + m_display->Flush (); + } #else - int w = Interface->Width(); - int h = Interface->Height(); - - Interface->WriteText( w/2, h/2, "Muggle is active!" ); - Interface->Flush(); + int w = Interface->Width (); + int h = Interface->Height (); + + Interface->WriteText (w / 2, h / 2, "Muggle is active!"); + Interface->Flush (); #endif - free( buf ); + free (buf); } } -void mgPlayerControl::Display() + +void +mgPlayerControl::Display () { - if( m_visible ) + if (m_visible) { - if( !m_has_osd ) - { - // open the osd if its not already there... + if (!m_has_osd) + { +// open the osd if its not already there... #if VDRVERSNUM >= 10307 #else - Interface->Open(); + Interface->Open (); #endif - m_has_osd = true; - } - - // now an osd is open, go on - if( m_progress_view ) - { + m_has_osd = true; + } + +// now an osd is open, go on + if (m_progress_view) + { #if VDRVERSNUM >= 10307 - if( m_menu ) - { - delete m_menu; - m_menu = NULL; - } + if (m_menu) + { + delete m_menu; + m_menu = NULL; + } #endif - ShowProgress(); - } - else - { + ShowProgress (); + } + else + { #if VDRVERSNUM >= 10307 - if( m_display ) - { - delete m_display; - m_display = NULL; - } + if (m_display) + { + delete m_display; + m_display = NULL; + } #endif - ShowContents(); - } + ShowContents (); + } } - else + else { - InternalHide(); + InternalHide (); } } -void mgPlayerControl::Hide() + +void +mgPlayerControl::Hide () { - m_visible = false; - - InternalHide(); + m_visible = false; + + InternalHide (); } -void mgPlayerControl::InternalHide() +void +mgPlayerControl::InternalHide () { - if( m_has_osd ) + if (m_has_osd) { #if VDRVERSNUM >= 10307 - if( m_display ) - { - delete m_display; - m_display = NULL; - } - if( m_menu ) - { - delete m_menu; - m_menu = NULL; - } + if (m_display) + { + delete m_display; + m_display = NULL; + } + if (m_menu) + { + delete m_menu; + m_menu = NULL; + } #else - Interface->Close(); + Interface->Close (); #endif - m_has_osd = false; + m_has_osd = false; } } -eOSState mgPlayerControl::ProcessKey(eKeys key) + +eOSState mgPlayerControl::ProcessKey (eKeys key) { - if( !Active() ) + if (key!=kNone) MGLOG ("mgPlayerControl::ProcessKey(eKeys key)"); + if (!Active ()) { - return osEnd; + return osEnd; } - StatusMsgReplaying(); + StatusMsgReplaying (); - Display(); + Display (); - eOSState state = cControl::ProcessKey(key); + eOSState + state = cControl::ProcessKey (key); - if( state == osUnknown ) + if (state == osUnknown) { - switch( key ) - { - case kUp: - { - Forward(); - } break; - case kDown: - { - Backward(); - } break; - case kRed: - { - if( !m_visible && player ) - { - mgPlaylist *pl = player->getPlaylist(); - - std::string s; - switch( pl->toggleLoopMode() ) - { - case mgPlaylist::LM_NONE: - { - s = tr( "Loop mode off" ); - } break; - case mgPlaylist::LM_SINGLE: - { - s = tr( "Loop mode single" ); - } break; - case mgPlaylist::LM_FULL: - { - s = tr( "Loop mode full" ); - } break; - default: - { - s = tr( "Unknown loop mode" ); - } - } + switch (key) + { + case kUp: + { + if (m_visible) + Backward(); + else + Forward (); + } + break; + case kDown: + { + if (m_visible) + Forward (); + else + Backward(); + } + break; + case kRed: + { + if (!m_visible && player) + { + mgSelection * + pl = player->getPlaylist (); + + std::string s; + switch (pl->toggleLoopMode ()) + { + case mgSelection::LM_NONE: + { + s = tr ("Loop mode off"); + } + break; + case mgSelection::LM_SINGLE: + { + s = tr ("Loop mode single"); + } + break; + case mgSelection::LM_FULL: + { + s = tr ("Loop mode full"); + } + break; + default: + { + s = tr ("Unknown loop mode"); + } + } #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo, s.c_str() ); - Skins.Flush(); + Skins.Message (mtInfo, s.c_str ()); + Skins.Flush (); #else - Interface->Status( s.c_str() ); - Interface->Flush(); + Interface->Status (s.c_str ()); + Interface->Flush (); #endif - } - else - { - // toggle progress display between simple and detail - m_progress_view = !m_progress_view; - the_setup.progressview = m_progress_view; - Display(); - } - } break; - case kGreen: - { - if( !m_visible && player ) - { - mgPlaylist *pl = player->getPlaylist(); - - std::string s; - switch( pl->toggleShuffleMode() ) - { - case mgPlaylist::SM_NONE: - { - s = tr( "Shuffle mode off" ); - } break; - case mgPlaylist::SM_NORMAL: - { - s = tr( "Shuffle mode normal" ); - } break; - case mgPlaylist::SM_PARTY: - { - s = tr( "Shuffle mode party" ); - } break; - default: - { - s = tr( "Unknown shuffle mode" ); - } - } + } + else + { +// toggle progress display between simple and detail + m_progress_view = !m_progress_view; + Display (); + } + } + break; + case kGreen: + { + if (!m_visible && player) + { + mgSelection * + pl = player->getPlaylist (); + + std::string s; + switch (pl->toggleShuffleMode ()) + { + case mgSelection::SM_NONE: + { + s = tr ("Shuffle mode off"); + } + break; + case mgSelection::SM_NORMAL: + { + s = tr ("Shuffle mode normal"); + } + break; + case mgSelection::SM_PARTY: + { + s = tr ("Shuffle mode party"); + } + break; + default: + { + s = tr ("Unknown shuffle mode"); + } + } #if VDRVERSNUM >= 10307 - Skins.Message(mtInfo, s.c_str() ); - Skins.Flush(); + Skins.Message (mtInfo, s.c_str ()); + Skins.Flush (); #else - Interface->Status( s.c_str() ); - Interface->Flush(); + Interface->Status (s.c_str ()); + Interface->Flush (); #endif - } - else - { - // toggle progress display between playlist and track - m_track_view = !m_track_view; - the_setup.trackview = m_track_view ; - Display(); - } - } break; - case kPause: - case kYellow: - { - Pause(); - } break; - case kStop: - case kBlue: - { - InternalHide(); - Stop(); - - return osEnd; - } break; - case kOk: - { - m_visible = !m_visible; - the_setup.visible = m_visible; - Display(); - - return osContinue; - } break; - case kBack: - { - InternalHide(); - Stop(); - mgMuggle::setResumeIndex( 0 ); - - return osEnd; - } break; - default: - { - return osUnknown; - } - } + } + else + { +// toggle progress display between playlist and track + m_track_view = !m_track_view; + Display (); + } + } + break; + case kPause: + case kYellow: + { + Pause (); + } + break; + case kStop: + case kBlue: + { + InternalHide (); + Stop (); + + return osEnd; + } + break; + case kOk: + { + m_visible = !m_visible; + Display (); + + return osContinue; + } + break; + case kBack: + { + InternalHide (); + Stop (); + + return osEnd; + } + break; + default: + { + return osUnknown; + } + } } - return osContinue; + return osContinue; } -void mgPlayerControl::StatusMsgReplaying() + +void +mgPlayerControl::StatusMsgReplaying () { - char *szBuf=NULL; - if(player - && player->getCurrent() - && player->getPlaylist()) + MGLOG ("mgPlayerControl::StatusMsgReplaying()"); + char *szBuf = NULL; + if (player && player->getCurrent () && player->getPlaylist ()) { - char cLoopMode; - char cShuffle; - - switch( player->getPlaylist()->getLoopMode() ) - { - default: - case mgPlaylist::LM_NONE: - cLoopMode = '.'; // Loop mode off - break; - case mgPlaylist::LM_SINGLE: - cLoopMode = 'S'; // Loop mode single - break; - case mgPlaylist::LM_FULL: - cLoopMode = 'P'; // Loop mode fuel - break; - } - - switch( player->getPlaylist()->getShuffleMode() ) - { - default: - case mgPlaylist::SM_NONE: - cShuffle = '.'; // Shuffle mode off - break; - case mgPlaylist::SM_NORMAL: - cShuffle = 'S'; // Shuffle mode normal - break; - case mgPlaylist::SM_PARTY: - cShuffle = 'P'; // Shuffle mode party - break; + char cLoopMode; + char cShuffle; + + switch (player->getPlaylist ()->getLoopMode ()) + { + default: + case mgSelection::LM_NONE: + cLoopMode = '.'; // Loop mode off + break; + case mgSelection::LM_SINGLE: + cLoopMode = 'S'; // Loop mode single + break; + case mgSelection::LM_FULL: + cLoopMode = 'P'; // Loop mode fuel + break; } - if(player->getCurrent()->getLabel(1).length() > 0) - { - asprintf(&szBuf,"[%c%c] (%d/%d) %s - %s", - cLoopMode, - cShuffle, - player->getPlaylist()->getIndex() + 1,player->getPlaylist()->getNumItems(), - player->getCurrent()->getLabel(1).c_str(), - player->getCurrent()->getTitle().c_str()); - } - else - { - asprintf(&szBuf,"[%c%c] (%d/%d) %s", - cLoopMode, - cShuffle, - player->getPlaylist()->getIndex() + 1,player->getPlaylist()->getNumItems(), - player->getCurrent()->getTitle().c_str()); - } + switch (player->getPlaylist ()->getShuffleMode ()) + { + default: + case mgSelection::SM_NONE: + cShuffle = '.'; // Shuffle mode off + break; + case mgSelection::SM_NORMAL: + cShuffle = 'S'; // Shuffle mode normal + break; + case mgSelection::SM_PARTY: + cShuffle = 'P'; // Shuffle mode party + break; + } + + mgContentItem *tmp = player->getCurrent (); + if (tmp == NULL) + mgError("mgPlayerControl::StatusMsgReplaying: getCurrent() is NULL"); + if (tmp->getArtist ().length () > 0) + { + asprintf (&szBuf, "[%c%c] (%d/%d) %s - %s", + cLoopMode, + cShuffle, + player->getPlaylist ()->getTrackPosition () + 1, + player->getPlaylist ()->getNumTracks (), + player->getCurrent ()->getArtist ().c_str (), + player->getCurrent ()->getTitle ().c_str ()); + } + else + { + asprintf (&szBuf, "[%c%c] (%d/%d) %s", + cLoopMode, + cShuffle, + player->getPlaylist ()->getTrackPosition () + 1, + player->getPlaylist ()->getNumTracks (), + player->getCurrent ()->getTitle ().c_str ()); + } } - else + else { - asprintf(&szBuf,"[muggle]"); + asprintf (&szBuf, "[muggle]"); } - - //fprintf(stderr,"StatusMsgReplaying(%s)\n",szBuf); - if( szBuf ) - { - if( m_szLastShowStatusMsg == NULL - || 0 != strcmp(szBuf,m_szLastShowStatusMsg) ) - { - if(m_szLastShowStatusMsg) - { - free(m_szLastShowStatusMsg); - } - m_szLastShowStatusMsg = szBuf; - cStatus::MsgReplaying(this,m_szLastShowStatusMsg); - } - else - { - free(szBuf); - } + +//fprintf(stderr,"StatusMsgReplaying(%s)\n",szBuf); + if (szBuf) + { + if (m_szLastShowStatusMsg == NULL + || 0 != strcmp (szBuf, m_szLastShowStatusMsg)) + { + if (m_szLastShowStatusMsg) + { + free (m_szLastShowStatusMsg); + } + m_szLastShowStatusMsg = szBuf; + cStatus::MsgReplaying (this, m_szLastShowStatusMsg); + } + else + { + free (szBuf); + } } } - diff --git a/vdr_player.h b/vdr_player.h index e87d577..5b8ea9f 100644 --- a/vdr_player.h +++ b/vdr_player.h @@ -9,16 +9,16 @@ * * $Id$ * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ - #ifndef ___VDR_PLAYER_H #define ___VDR_PLAYER_H #include +#include "mg_db.h" #if VDRVERSNUM >= 10307 class cOsd; #endif @@ -26,125 +26,124 @@ class cOsd; // ------------------------------------------------------------------- class mgPCMPlayer; -class mgPlaylist; // ------------------------------------------------------------------- -/*! +/*! * \brief exerts control over the player itself * * This control is launched from the main menu and manages a link * to the player. Key events are caught and signaled to the player. */ -class mgPlayerControl : public cControl +class mgPlayerControl:public cControl { -private: + private: - //! \brief the reference to the player - mgPCMPlayer *player; +//! \brief the reference to the player + mgPCMPlayer * player; - //! \brief indicates, whether the osd should be visible - int m_visible; +//! \brief indicates, whether the osd should be visible + bool m_visible; - //! \brief indicates, whether an osd is currently displayed - bool m_has_osd; +//! \brief indicates, whether an osd is currently displayed + bool m_has_osd; - //! \brief indicates, whether the osd displays a track view (true) or a playlist view (false) - int m_track_view; - - //! \brief indicates, whether the osd presents progress (true) or detail information (false) - int m_progress_view; + bool m_track_view; + bool m_progress_view; #if VDRVERSNUM >= 10307 - //! \brief a replay display to show the progress during playback - cSkinDisplayReplay *m_display; - cSkinDisplayMenu *m_menu; +//! \brief a replay display to show the progress during playback + cSkinDisplayReplay *m_display; + cSkinDisplayMenu *m_menu; - cOsd *osd; - const cFont *font; + cOsd *osd; + const cFont *font; #endif - //! \brief Last Message for Statusmonitor - char* m_szLastShowStatusMsg; +//! \brief Last Message for Statusmonitor + char *m_szLastShowStatusMsg; -public: + public: - /*! \brief construct a control with a playlist - * - * \param plist - the playlist to be played - * \param first - the index where to start the playlist - */ - mgPlayerControl(mgPlaylist *plist, unsigned first); +/*! \brief construct a control with a playlist + * + * \param plist - the playlist to be played + */ + mgPlayerControl (mgSelection * plist); - /*! \brief destructor - */ - virtual ~mgPlayerControl(); +/*! \brief destructor + */ + virtual ~ mgPlayerControl (); - //! \brief indicate, whether the corresponding player is active - bool Active(); +//! \brief indicate whether the corresponding player is active + bool Active (); - //! \brief stop the corresponding player - void Stop(); +//! \brief stop the corresponding player + void Stop (); - //! \brief toggle the pause mode of the corresponding player - void Pause(); +//! \brief toggle the pause mode of the corresponding player + void Pause (); - //! \brief start playing - void Play(); +//! \brief start playing + void Play (); - //! \brief skip to the next song - void Forward(); +//! \brief skip to the next song + void Forward (); - //! \brief skip to the previous song - void Backward(); +//! \brief skip to the previous song + void Backward (); - /*! \brief skip a specified number of seconds - * - * \param seconds - the number of seconds to skip - */ - void SkipSeconds(int seconds); +/*! \brief skip a specified number of seconds + * + * \param seconds - the number of seconds to skip + */ + void SkipSeconds (int seconds); - /*! \brief goto a certain position in the playlist - * - * \param index - the position in the playlist to skip to - * \param still - currently unused - */ - void Goto(int index, bool still = false); +/*! \brief goto a certain position in the playlist + * + * \param index - the position in the playlist to skip to + * \param still - currently unused + */ + void Goto (int index, bool still = false); - //! \brief toggle the shuffle mode of the corresponding player - void ToggleShuffle(); +//! \brief toggle the shuffle mode of the corresponding player + void ToggleShuffle (); - //! \brief toggle the loop mode of the corresponding player - void ToggleLoop(); +//! \brief toggle the loop mode of the corresponding player + void ToggleLoop (); - /*! \brief signal a new playlist - * - * The caller has to take care of deallocating the previous list - * - * \param plist - the new playlist to be played - * \param first - the index where to start the playlist - */ - void NewPlaylist( mgPlaylist *plist, unsigned start ); + /*! \brief tell the player to reload the play list. + * This is needed if we play a collection + * and the user changed the collection while playing it + */ + void ReloadPlaylist(); - //! \brief a progress display - void ShowProgress(); +/*! \brief signal a new playlist + * + * The caller has to take care of deallocating the previous list + * + * \param plist - the new playlist to be played + */ + void NewPlaylist (mgSelection * plist); - void Display(); +//! \brief a progress display + void ShowProgress (); - void ShowContents(); + void Display (); - //! \brief hide the osd, if present - void Hide(); + void ShowContents (); - //! \brief hide the osd, if present - void InternalHide(); +//! \brief hide the osd, if present + void Hide (); - //! \brief process key events - eOSState ProcessKey(eKeys key); +//! \brief hide the osd, if present + void InternalHide (); -protected: - //! \brief signal a played file to any cStatusMonitor inside vdr - void StatusMsgReplaying(); -}; +//! \brief process key events + eOSState ProcessKey (eKeys key); -#endif //___VDR_PLAYER_H + protected: +//! \brief signal a played file to any cStatusMonitor inside vdr + void StatusMsgReplaying (); +}; +#endif //___VDR_PLAYER_H diff --git a/vdr_setup.c b/vdr_setup.c index e6aa816..36a3b46 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -9,7 +9,7 @@ * * $Id$ * - * Partially adapted from + * Partially adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ @@ -23,61 +23,66 @@ mgSetup the_setup; // --- mgMenuSetup ----------------------------------------------------------- -mgMenuSetup::mgMenuSetup() +mgMenuSetup::mgMenuSetup () { - static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" }; + m_data = the_setup; - m_data = the_setup; + SetSection (tr ("Muggle")); - SetSection( tr("Muggle") ); - - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Initial loop mode"), &m_data.InitLoopMode)); - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Initial shuffle mode"), &m_data.InitShuffleMode)); - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Audio mode"), &m_data.AudioMode, tr("Round"), tr("Dither"))); - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Use 48kHz mode only"), &m_data.Only48kHz)); - Add(new cMenuEditIntItem( tr("Setup.Muggle$Display mode"), &m_data.DisplayMode, 1, 3)); - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Background mode"), &m_data.BackgrMode, tr("Black"), tr("Live"))); - Add(new cMenuEditIntItem( tr("Setup.Muggle$Normalizer level"), &m_data.TargetLevel, 0, MAX_TARGET_LEVEL)); - Add(new cMenuEditIntItem( tr("Setup.Muggle$Limiter level"), &m_data.LimiterLevel, MIN_LIMITER_LEVEL, 100)); - - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with open display"), &m_data.visible )); - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with progress display"), &m_data.progressview )); - Add(new cMenuEditBoolItem(tr("Setup.Muggle$Start replay with track display"), &m_data.trackview )); + Add (new + cMenuEditBoolItem (tr ("Setup.Muggle$Initial loop mode"), + &m_data.InitLoopMode)); + Add (new + cMenuEditBoolItem (tr ("Setup.Muggle$Initial shuffle mode"), + &m_data.InitShuffleMode)); + Add (new + cMenuEditBoolItem (tr ("Setup.Muggle$Audio mode"), &m_data.AudioMode, + tr ("Round"), tr ("Dither"))); + Add (new + cMenuEditBoolItem (tr ("Setup.Muggle$Use 48kHz mode only"), + &m_data.Only48kHz)); + Add (new + cMenuEditIntItem (tr ("Setup.Muggle$Display mode"), + &m_data.DisplayMode, 1, 3)); + Add (new + cMenuEditBoolItem (tr ("Setup.Muggle$Background mode"), + &m_data.BackgrMode, tr ("Black"), tr ("Live"))); + Add (new + cMenuEditIntItem (tr ("Setup.Muggle$Normalizer level"), + &m_data.TargetLevel, 0, MAX_TARGET_LEVEL)); + Add (new + cMenuEditIntItem (tr ("Setup.Muggle$Limiter level"), + &m_data.LimiterLevel, MIN_LIMITER_LEVEL, 100)); } -void mgMenuSetup::Store(void) -{ - the_setup = m_data; - SetupStore("InitLoopMode", the_setup.InitLoopMode ); - SetupStore("InitShuffleMode", the_setup.InitShuffleMode); - SetupStore("AudioMode", the_setup.AudioMode ); - SetupStore("DisplayMode", the_setup.DisplayMode ); - SetupStore("BackgrMode", the_setup.BackgrMode ); - SetupStore("TargetLevel", the_setup.TargetLevel ); - SetupStore("LimiterLevel", the_setup.LimiterLevel ); - SetupStore("Only48kHz", the_setup.Only48kHz ); +void +mgMenuSetup::Store (void) +{ + the_setup = m_data; - SetupStore("Visible", the_setup.visible ); - SetupStore("TrackView", the_setup.trackview ); - SetupStore("ProgressView", the_setup.progressview ); + SetupStore ("InitLoopMode", the_setup.InitLoopMode); + SetupStore ("InitShuffleMode", the_setup.InitShuffleMode); + SetupStore ("AudioMode", the_setup.AudioMode); + SetupStore ("DisplayMode", the_setup.DisplayMode); + SetupStore ("BackgrMode", the_setup.BackgrMode); + SetupStore ("TargetLevel", the_setup.TargetLevel); + SetupStore ("LimiterLevel", the_setup.LimiterLevel); + SetupStore ("Only48kHz", the_setup.Only48kHz); } + // --- mgSetup --------------------------------------------------------------- -mgSetup::mgSetup() +mgSetup::mgSetup () { - InitLoopMode = 0; - InitShuffleMode = 0; - AudioMode = 1; - DisplayMode = 3; - BackgrMode = 1; - TargetLevel = DEFAULT_TARGET_LEVEL; - LimiterLevel = DEFAULT_LIMITER_LEVEL; - Only48kHz = 0; - ToplevelDir = "/mnt/music/"; - - visible = 1; - trackview = 1; - progressview = 1; + InitLoopMode = 0; + InitShuffleMode = 0; + AudioMode = 1; + DisplayMode = 3; + BackgrMode = 1; + TargetLevel = DEFAULT_TARGET_LEVEL; + LimiterLevel = DEFAULT_LIMITER_LEVEL; + Only48kHz = 0; + ToplevelDir = "/mnt/music/"; } diff --git a/vdr_setup.h b/vdr_setup.h index e96df84..c21adb1 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -9,7 +9,7 @@ * * $Id$ * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ @@ -32,34 +32,30 @@ */ class mgSetup { -public: - int InitLoopMode; - int InitShuffleMode; - int AudioMode; - int DisplayMode; - int BackgrMode; - int MenuMode; - int TargetLevel; - int LimiterLevel; - int Only48kHz; + public: + int InitLoopMode; + int InitShuffleMode; + int AudioMode; + int DisplayMode; + int BackgrMode; + int MenuMode; + int TargetLevel; + int LimiterLevel; + int Only48kHz; - char *DbHost; - char *DbSocket; - char *DbName; - char *DbUser; - char *DbPass; - int DbPort; - bool GdCompatibility; - char *ToplevelDir; + char *DbHost; + char *DbSocket; + char *DbName; + char *DbUser; + char *DbPass; + int DbPort; + bool GdCompatibility; + char *ToplevelDir; - int visible; - int trackview; - int progressview; + char PathPrefix[MAX_STRING_LEN]; - char PathPrefix[MAX_STRING_LEN]; - - public: - mgSetup(void); + public: + mgSetup (void); }; extern mgSetup the_setup; @@ -67,15 +63,13 @@ extern mgSetup the_setup; /*! * \brief allow user to modify setup on OSD */ -class mgMenuSetup : public cMenuSetupPage +class mgMenuSetup:public cMenuSetupPage { -private: - mgSetup m_data; -protected: - virtual void Store(); -public: - mgMenuSetup(); + private: + mgSetup m_data; + protected: + virtual void Store (); + public: + mgMenuSetup (); }; - - -#endif //___SETUP_MP3_H +#endif //___SETUP_MP3_H diff --git a/vdr_sound.c b/vdr_sound.c index b06061c..7fc8361 100644 --- a/vdr_sound.c +++ b/vdr_sound.c @@ -3,13 +3,13 @@ * \brief Sound manipulation classes for a VDR media plugin (muggle) * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_sound.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id$ * - * Adapted from + * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ @@ -18,80 +18,97 @@ // The resample code has been adapted from the madplay project // (resample.c) found in the libmad distribution - + class cResample { -private: - mad_fixed_t ratio; - mad_fixed_t step; - mad_fixed_t last; - mad_fixed_t resampled[MAX_NSAMPLES]; -public: - bool SetInputRate(unsigned int oldrate, unsigned int newrate); - unsigned int ResampleBlock(unsigned int nsamples, const mad_fixed_t *old); - const mad_fixed_t *Resampled(void) { return resampled; } - }; - -bool cResample::SetInputRate(unsigned int oldrate, unsigned int newrate) + private: + mad_fixed_t ratio; + mad_fixed_t step; + mad_fixed_t last; + mad_fixed_t resampled[MAX_NSAMPLES]; + public: + bool SetInputRate (unsigned int oldrate, unsigned int newrate); + unsigned int ResampleBlock (unsigned int nsamples, const mad_fixed_t * old); + const mad_fixed_t *Resampled (void) + { + return resampled; + } +}; + +bool cResample::SetInputRate (unsigned int oldrate, unsigned int newrate) { - if(oldrate<8000 || oldrate>newrate*6) { // out of range - esyslog("WARNING: samplerate %d out of range 8000-%d\n",oldrate,newrate*6); - return 0; + if (oldrate < 8000 || oldrate > newrate * 6) + { // out of range + esyslog ("WARNING: samplerate %d out of range 8000-%d\n", oldrate, + newrate * 6); + return 0; } - ratio=mad_f_tofixed((double)oldrate/(double)newrate); - step=0; last=0; + ratio = mad_f_tofixed ((double) oldrate / (double) newrate); + step = 0; + last = 0; #ifdef DEBUG - static mad_fixed_t oldratio=0; - if(oldratio!=ratio) { - printf("mad: new resample ratio %f (from %d kHz to %d kHz)\n",mad_f_todouble(ratio),oldrate,newrate); - oldratio=ratio; + static mad_fixed_t + oldratio = 0; + if (oldratio != ratio) + { + printf ("mad: new resample ratio %f (from %d kHz to %d kHz)\n", + mad_f_todouble (ratio), oldrate, newrate); + oldratio = ratio; } #endif - return ratio!=MAD_F_ONE; + return ratio != MAD_F_ONE; } -unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t *old) + +unsigned int +cResample::ResampleBlock (unsigned int nsamples, const mad_fixed_t * old) { - // This resampling algorithm is based on a linear interpolation, which is - // not at all the best sounding but is relatively fast and efficient. - // - // A better algorithm would be one that implements a bandlimited - // interpolation. - - mad_fixed_t *nsam=resampled; - const mad_fixed_t *end=old+nsamples; - const mad_fixed_t *begin=nsam; - - if(step < 0) { - step = mad_f_fracpart(-step); - - while (step < MAD_F_ONE) { - *nsam++ = step ? last+mad_f_mul(*old-last,step) : last; - step += ratio; - if(((step + 0x00000080L) & 0x0fffff00L) == 0) - step = (step + 0x00000080L) & ~0x0fffffffL; - } - step -= MAD_F_ONE; +// This resampling algorithm is based on a linear interpolation, which is +// not at all the best sounding but is relatively fast and efficient. +// +// A better algorithm would be one that implements a bandlimited +// interpolation. + + mad_fixed_t *nsam = resampled; + const mad_fixed_t *end = old + nsamples; + const mad_fixed_t *begin = nsam; + + if (step < 0) + { + step = mad_f_fracpart (-step); + + while (step < MAD_F_ONE) + { + *nsam++ = step ? last + mad_f_mul (*old - last, step) : last; + step += ratio; + if (((step + 0x00000080L) & 0x0fffff00L) == 0) + step = (step + 0x00000080L) & ~0x0fffffffL; + } + step -= MAD_F_ONE; } - while (end - old > 1 + mad_f_intpart(step)) { - old += mad_f_intpart(step); - step = mad_f_fracpart(step); - *nsam++ = step ? *old + mad_f_mul(old[1] - old[0], step) : *old; - step += ratio; - if (((step + 0x00000080L) & 0x0fffff00L) == 0) - step = (step + 0x00000080L) & ~0x0fffffffL; + while (end - old > 1 + mad_f_intpart (step)) + { + old += mad_f_intpart (step); + step = mad_f_fracpart (step); + *nsam++ = step ? *old + mad_f_mul (old[1] - old[0], step) : *old; + step += ratio; + if (((step + 0x00000080L) & 0x0fffff00L) == 0) + step = (step + 0x00000080L) & ~0x0fffffffL; } - if (end - old == 1 + mad_f_intpart(step)) { - last = end[-1]; - step = -step; + if (end - old == 1 + mad_f_intpart (step)) + { + last = end[-1]; + step = -step; } - else step -= mad_f_fromint(end - old); + else + step -= mad_f_fromint (end - old); - return nsam-begin; + return nsam - begin; } + // --- cLevel ---------------------------------------------------------------- // The normalize algorithm and parts of the code has been adapted from the @@ -123,201 +140,253 @@ unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t * // We can then take the square root of the power to get maxi­ // mum sustained RMS amplitude. -class cLevel { -private: - double maxpow; - mad_fixed_t peak; - struct Power { - // smooth - int npow, wpow; - double powsum, pows[POW_WIN]; - // sum - unsigned int nsum; - double sum; - } power[2]; - // - inline void AddPower(struct Power *p, double pow); -public: - void Init(void); - void GetPower(struct mad_pcm *pcm); - double GetLevel(void); - double GetPeak(void); - }; - -void cLevel::Init(void) +class cLevel +{ + private: + double maxpow; + mad_fixed_t peak; + struct Power + { +// smooth + int npow, wpow; + double powsum, pows[POW_WIN]; +// sum + unsigned int nsum; + double sum; + } power[2]; +// + inline void AddPower (struct Power *p, double pow); + public: + void Init (void); + void GetPower (struct mad_pcm *pcm); + double GetLevel (void); + double GetPeak (void); +}; + +void +cLevel::Init (void) { - for(int l=0 ; l<2 ; l++) { - struct Power *p=&power[l]; - p->sum=p->powsum=0.0; p->wpow=p->npow=p->nsum=0; - for(int i=POW_WIN-1 ; i>=0 ; i--) p->pows[i]=0.0; + for (int l = 0; l < 2; l++) + { + struct Power *p = &power[l]; + p->sum = p->powsum = 0.0; + p->wpow = p->npow = p->nsum = 0; + for (int i = POW_WIN - 1; i >= 0; i--) + p->pows[i] = 0.0; } - maxpow=0.0; peak=0; + maxpow = 0.0; + peak = 0; } -void cLevel::GetPower(struct mad_pcm *pcm) + +void +cLevel::GetPower (struct mad_pcm *pcm) { - for(int i=0 ; ichannels ; i++) { - struct Power *p=&power[i]; - mad_fixed_t *data=pcm->samples[i]; - for(int n=pcm->length ; n>0 ; n--) { - if(*data < -peak) peak = -*data; - if(*data > peak) peak = *data; - double s=mad_f_todouble(*data++); - p->sum+=(s*s); - if(++(p->nsum)>=pcm->samplerate/100) { - AddPower(p,p->sum/(double)p->nsum); - p->sum=0.0; p->nsum=0; + for (int i = 0; i < pcm->channels; i++) + { + struct Power *p = &power[i]; + mad_fixed_t *data = pcm->samples[i]; + for (int n = pcm->length; n > 0; n--) + { + if (*data < -peak) + peak = -*data; + if (*data > peak) + peak = *data; + double s = mad_f_todouble (*data++); + p->sum += (s * s); + if (++(p->nsum) >= pcm->samplerate / 100) + { + AddPower (p, p->sum / (double) p->nsum); + p->sum = 0.0; + p->nsum = 0; + } } - } } } -void cLevel::AddPower(struct Power *p, double pow) + +void +cLevel::AddPower (struct Power *p, double pow) { - p->powsum+=pow; - if(p->npow>=POW_WIN) { - if(p->powsum>maxpow) maxpow=p->powsum; - p->powsum-=p->pows[p->wpow]; + p->powsum += pow; + if (p->npow >= POW_WIN) + { + if (p->powsum > maxpow) + maxpow = p->powsum; + p->powsum -= p->pows[p->wpow]; } - else p->npow++; - p->pows[p->wpow]=pow; - p->wpow=(p->wpow+1) % POW_WIN; + else + p->npow++; + p->pows[p->wpow] = pow; + p->wpow = (p->wpow + 1) % POW_WIN; } -double cLevel::GetLevel(void) -{ - if(maxpowmaxpow) maxpow=power[0].powsum; - if(power[1].powsum>maxpow) maxpow=power[1].powsum; +double +cLevel::GetLevel (void) +{ + if (maxpow < EPSILON) + { +// Either this whole file has zero power, or was too short to ever +// fill the smoothing buffer. In the latter case, we need to just +// get maxpow from whatever data we did collect. + + if (power[0].powsum > maxpow) + maxpow = power[0].powsum; + if (power[1].powsum > maxpow) + maxpow = power[1].powsum; } - double level=sqrt(maxpow/(double)POW_WIN); // adjust for the smoothing window size and root - printf("norm: new volumen level=%f peak=%f\n",level,mad_f_todouble(peak)); - return level; + // adjust for the smoothing window size and root + double level = sqrt (maxpow / (double) POW_WIN); + printf ("norm: new volumen level=%f peak=%f\n", level, + mad_f_todouble (peak)); + return level; } -double cLevel::GetPeak(void) + +double +cLevel::GetPeak (void) { - return mad_f_todouble(peak); + return mad_f_todouble (peak); } + // --- cNormalize ------------------------------------------------------------ -class cNormalize { -private: - mad_fixed_t gain; - double d_limlvl, one_limlvl; - mad_fixed_t limlvl; - bool dogain, dolimit; +class cNormalize +{ + private: + mad_fixed_t gain; + double d_limlvl, one_limlvl; + mad_fixed_t limlvl; + bool dogain, dolimit; #ifdef DEBUG - // stats - unsigned long limited, clipped, total; - mad_fixed_t peak; +// stats + unsigned long limited, clipped, total; + mad_fixed_t peak; #endif - // limiter +// limiter #ifdef USE_FAST_LIMITER - mad_fixed_t *table, tablestart; - int tablesize; - inline mad_fixed_t FastLimiter(mad_fixed_t x); + mad_fixed_t *table, tablestart; + int tablesize; + inline mad_fixed_t FastLimiter (mad_fixed_t x); #endif - inline mad_fixed_t Limiter(mad_fixed_t x); -public: - cNormalize(void); - ~cNormalize(); - void Init(double Level, double Peak); - void Stats(void); - void AddGain(struct mad_pcm *pcm); - }; - -cNormalize::cNormalize(void) + inline mad_fixed_t Limiter (mad_fixed_t x); + public: + cNormalize (void); + ~cNormalize (); + void Init (double Level, double Peak); + void Stats (void); + void AddGain (struct mad_pcm *pcm); +}; + +cNormalize::cNormalize (void) { - d_limlvl = (double)the_setup.LimiterLevel / 100.0; - one_limlvl = 1 - d_limlvl; - limlvl = mad_f_tofixed(d_limlvl); - printf( "norm: lim_lev=%f lim_acc=%d\n", d_limlvl, LIM_ACC ); + d_limlvl = (double) the_setup.LimiterLevel / 100.0; + one_limlvl = 1 - d_limlvl; + limlvl = mad_f_tofixed (d_limlvl); + printf ("norm: lim_lev=%f lim_acc=%d\n", d_limlvl, LIM_ACC); #ifdef USE_FAST_LIMITER - mad_fixed_t start=limlvl & ~(F_LIM_JMP-1); - tablestart=start; - tablesize=(unsigned int)(F_LIM_MAX-start)/F_LIM_JMP + 2; - table=new mad_fixed_t[tablesize]; - if(table) { - printf("norm: table size=%d start=%08x jump=%08x\n",tablesize,start,F_LIM_JMP); - for(int i=0 ; i=limlvl ; x-=mad_f_tofixed(1e-4)) { - mad_fixed_t diff=mad_f_abs(Limiter(x)-FastLimiter(x)); - if(diff>maxdiff) maxdiff=diff; + mad_fixed_t maxdiff = 0; + for (mad_fixed_t x = F_LIM_MAX; x >= limlvl; x -= mad_f_tofixed (1e-4)) + { + mad_fixed_t diff = mad_f_abs (Limiter (x) - FastLimiter (x)); + if (diff > maxdiff) + maxdiff = diff; #ifdef ACC_DUMP - fprintf(out,"%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n", - mad_f_todouble(x),mad_f_todouble(Limiter(x)),mad_f_todouble(FastLimiter(x)),mad_f_todouble(diff),mad_f_todouble(maxdiff)); - if(ferror(out)) break; + fprintf (out, "%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n", + mad_f_todouble (x), mad_f_todouble (Limiter (x)), + mad_f_todouble (FastLimiter (x)), mad_f_todouble (diff), + mad_f_todouble (maxdiff)); + if (ferror (out)) + break; #endif - } + } #ifdef ACC_DUMP - fclose(out); + fclose (out); #endif - printf("norm: accuracy %.12f\n",mad_f_todouble(maxdiff)); - if(mad_f_todouble(maxdiff)>1e-6) - { - esyslog("ERROR: accuracy check failed, normalizer disabled"); - delete table; table=0; - } + printf ("norm: accuracy %.12f\n", mad_f_todouble (maxdiff)); + if (mad_f_todouble (maxdiff) > 1e-6) + { + esyslog ("ERROR: accuracy check failed, normalizer disabled"); + delete table; + table = 0; + } } - else esyslog("ERROR: no memory for lookup table, normalizer disabled"); -#endif // USE_FAST_LIMITER + else + esyslog ("ERROR: no memory for lookup table, normalizer disabled"); +#endif // USE_FAST_LIMITER } -cNormalize::~cNormalize() + +cNormalize::~cNormalize () { #ifdef USE_FAST_LIMITER - delete table; + delete table; #endif } -void cNormalize::Init(double Level, double Peak) + +void +cNormalize::Init (double Level, double Peak) { - double Target=(double)the_setup.TargetLevel/100.0; - double dgain=Target/Level; - if(dgain>MAX_GAIN) dgain=MAX_GAIN; - gain=mad_f_tofixed(dgain); - // Check if we actually need to apply a gain - dogain=(Target>0.0 && fabs(1-dgain)>MIN_GAIN); + double Target = (double) the_setup.TargetLevel / 100.0; + double dgain = Target / Level; + if (dgain > MAX_GAIN) + dgain = MAX_GAIN; + gain = mad_f_tofixed (dgain); +// Check if we actually need to apply a gain + dogain = (Target > 0.0 && fabs (1 - dgain) > MIN_GAIN); #ifdef USE_FAST_LIMITER - if(!table) dogain=false; + if (!table) + dogain = false; #endif - // Check if we actually need to do limiting: - // we have to if limiter is enabled, if gain>1 and if the peaks will clip. - dolimit=(d_limlvl<1.0 && dgain>1.0 && Peak*dgain>1.0); +// Check if we actually need to do limiting: +// we have to if limiter is enabled, if gain>1 and if the peaks will clip. + dolimit = (d_limlvl < 1.0 && dgain > 1.0 && Peak * dgain > 1.0); #ifdef DEBUG - printf("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",dgain,dogain,dolimit,Target,Level,Peak); - limited=clipped=total=0; peak=0; + printf ("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n", + dgain, dogain, dolimit, Target, Level, Peak); + limited = clipped = total = 0; + peak = 0; #endif } -void cNormalize::Stats(void) + +void +cNormalize::Stats (void) { #ifdef DEBUG - if(total) - printf("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n", - total,limited,(double)limited/total*100.0,clipped,(double)clipped/total*100.0,mad_f_todouble(peak)); + if (total) + printf ("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n", + total, limited, (double) limited / total * 100.0, clipped, + (double) clipped / total * 100.0, mad_f_todouble (peak)); #endif } -mad_fixed_t cNormalize::Limiter(mad_fixed_t x) + +mad_fixed_t cNormalize::Limiter (mad_fixed_t x) { // Limiter function: // @@ -330,42 +399,57 @@ mad_fixed_t cNormalize::Limiter(mad_fixed_t x) // With limiter level = 0, this is equivalent to a tanh() function; // with limiter level = 1, this is equivalent to clipping. - if(x>limlvl) { + if (x > limlvl) + { #ifdef DEBUG - if(x>MAD_F_ONE) clipped++; - limited++; + if (x > MAD_F_ONE) + clipped++; + limited++; #endif - x=mad_f_tofixed(tanh((mad_f_todouble(x)-d_limlvl) / one_limlvl) * one_limlvl + d_limlvl); + x = + mad_f_tofixed (tanh ((mad_f_todouble (x) - d_limlvl) / one_limlvl) * + one_limlvl + d_limlvl); } - return x; + return x; } + #ifdef USE_FAST_LIMITER -mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x) +mad_fixed_t cNormalize::FastLimiter (mad_fixed_t x) { // The fast algorithm is based on a linear interpolation between the // the values in the lookup table. Relays heavly on libmads fixed point format. - if(x>limlvl) { - int i=(unsigned int)(x-tablestart)/F_LIM_JMP; + if (x > limlvl) + { + int + i = (unsigned int) (x - tablestart) / F_LIM_JMP; #ifdef DEBUG - if(x>MAD_F_ONE) clipped++; - limited++; - if(i>=tablesize) printf("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n", - mad_f_todouble(x),mad_f_todouble(x-tablestart),i,tablesize); + if (x > MAD_F_ONE) + clipped++; + limited++; + if (i >= tablesize) + printf ("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n", + mad_f_todouble (x), mad_f_todouble (x - tablestart), i, + tablesize); #endif - mad_fixed_t r=x & (F_LIM_JMP-1); - x=MAD_F_ONE; - if(i>MAD_F_FRACBITS - // which is senseless in the case of following <>LIM_SHIFT; // better, don't know if works on all machines - } + mad_fixed_t + r = x & (F_LIM_JMP - 1); + x = MAD_F_ONE; + if (i < tablesize) + { + mad_fixed_t * + ptr = &table[i]; + x = *ptr; + mad_fixed_t + d = *(ptr + 1) - x; +//x+=mad_f_mul(d,r)<>MAD_F_FRACBITS +// which is senseless in the case of following <> LIM_SHIFT; + } } - return x; + return x; } #endif @@ -375,235 +459,296 @@ mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x) #define LIMITER_FUNC Limiter #endif -void cNormalize::AddGain(struct mad_pcm *pcm) +void +cNormalize::AddGain (struct mad_pcm *pcm) { - if(dogain) { - for(int i=0 ; ichannels ; i++) { - mad_fixed_t *data=pcm->samples[i]; + if (dogain) + { + for (int i = 0; i < pcm->channels; i++) + { + mad_fixed_t *data = pcm->samples[i]; #ifdef DEBUG - total+=pcm->length; + total += pcm->length; #endif - if(dolimit) { - for(int n=pcm->length ; n>0 ; n--) { - mad_fixed_t s=mad_f_mul(*data,gain); - if(s<0) { - s=-s; + if (dolimit) + { + for (int n = pcm->length; n > 0; n--) + { + mad_fixed_t s = mad_f_mul (*data, gain); + if (s < 0) + { + s = -s; #ifdef DEBUG - if(s>peak) peak=s; + if (s > peak) + peak = s; #endif - s=LIMITER_FUNC(s); - s=-s; - } - else { + s = LIMITER_FUNC (s); + s = -s; + } + else + { #ifdef DEBUG - if(s>peak) peak=s; + if (s > peak) + peak = s; #endif - s=LIMITER_FUNC(s); + s = LIMITER_FUNC (s); + } + *data++ = s; + } } - *data++=s; - } - } - else { - for(int n=pcm->length ; n>0 ; n--) { - mad_fixed_t s=mad_f_mul(*data,gain); + else + { + for (int n = pcm->length; n > 0; n--) + { + mad_fixed_t s = mad_f_mul (*data, gain); #ifdef DEBUG - if(s>peak) peak=s; - else if(-s>peak) peak=-s; + if (s > peak) + peak = s; + else if (-s > peak) + peak = -s; #endif - if(s>MAD_F_ONE) s=MAD_F_ONE; // do clipping - if(s<-MAD_F_ONE) s=-MAD_F_ONE; - *data++=s; - } + if (s > MAD_F_ONE) + s = MAD_F_ONE; // do clipping + if (s < -MAD_F_ONE) + s = -MAD_F_ONE; + *data++ = s; + } + } } - } } } + // --- cScale ---------------------------------------------------------------- // The dither code has been adapted from the madplay project // (audio.c) found in the libmad distribution -enum eAudioMode { amRound, amDither }; +enum eAudioMode +{ amRound, amDither }; -class cScale { -private: - enum { MIN=-MAD_F_ONE, MAX=MAD_F_ONE - 1 }; +class cScale +{ + private: + enum + { MIN = -MAD_F_ONE, MAX = MAD_F_ONE - 1 }; #ifdef DEBUG - // audio stats - unsigned long clipped_samples; - mad_fixed_t peak_clipping; - mad_fixed_t peak_sample; +// audio stats + unsigned long clipped_samples; + mad_fixed_t peak_clipping; + mad_fixed_t peak_sample; #endif - // dither - struct dither { - mad_fixed_t error[3]; - mad_fixed_t random; - } leftD, rightD; - // - inline mad_fixed_t Clip(mad_fixed_t sample, bool stats=true); - inline signed long LinearRound(mad_fixed_t sample); - inline unsigned long Prng(unsigned long state); - inline signed long LinearDither(mad_fixed_t sample, struct dither *dither); -public: - void Init(void); - void Stats(void); - unsigned int ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode); - }; - -void cScale::Init(void) +// dither + struct dither + { + mad_fixed_t error[3]; + mad_fixed_t random; + } leftD, rightD; +// + inline mad_fixed_t Clip (mad_fixed_t sample, bool stats = true); + inline signed long LinearRound (mad_fixed_t sample); + inline unsigned long Prng (unsigned long state); + inline signed long LinearDither (mad_fixed_t sample, struct dither *dither); + public: + void Init (void); + void Stats (void); + unsigned int ScaleBlock (unsigned char *data, unsigned int size, + unsigned int &nsamples, const mad_fixed_t * &left, + const mad_fixed_t * &right, eAudioMode mode); +}; + +void +cScale::Init (void) { #ifdef DEBUG - clipped_samples=0; peak_clipping=peak_sample=0; + clipped_samples = 0; + peak_clipping = peak_sample = 0; #endif - memset(&leftD,0,sizeof(leftD)); - memset(&rightD,0,sizeof(rightD)); + memset (&leftD, 0, sizeof (leftD)); + memset (&rightD, 0, sizeof (rightD)); } -void cScale::Stats(void) + +void +cScale::Stats (void) { #ifdef DEBUG - printf("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n", - clipped_samples,mad_f_todouble(peak_clipping),mad_f_todouble(peak_sample)); + printf ("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n", + clipped_samples, mad_f_todouble (peak_clipping), + mad_f_todouble (peak_sample)); #endif } + // gather signal statistics while clipping -mad_fixed_t cScale::Clip(mad_fixed_t sample, bool stats) +mad_fixed_t cScale::Clip (mad_fixed_t sample, bool stats) { #ifndef DEBUG - if (sample > MAX) sample = MAX; - if (sample < MIN) sample = MIN; + if (sample > MAX) + sample = MAX; + if (sample < MIN) + sample = MIN; #else - if(!stats) { - if (sample > MAX) sample = MAX; - if (sample < MIN) sample = MIN; + if (!stats) + { + if (sample > MAX) + sample = MAX; + if (sample < MIN) + sample = MIN; } - else { - if (sample >= peak_sample) { - if (sample > MAX) { - ++clipped_samples; - if (sample - MAX > peak_clipping) - peak_clipping = sample - MAX; - sample = MAX; + else + { + if (sample >= peak_sample) + { + if (sample > MAX) + { + ++clipped_samples; + if (sample - MAX > peak_clipping) + peak_clipping = sample - MAX; + sample = MAX; + } + peak_sample = sample; } - peak_sample = sample; - } - else if (sample < -peak_sample) { - if (sample < MIN) { - ++clipped_samples; - if (MIN - sample > peak_clipping) - peak_clipping = MIN - sample; - sample = MIN; + else if (sample < -peak_sample) + { + if (sample < MIN) + { + ++clipped_samples; + if (MIN - sample > peak_clipping) + peak_clipping = MIN - sample; + sample = MIN; + } + peak_sample = -sample; } - peak_sample = -sample; - } } #endif - return sample; + return sample; } + // generic linear sample quantize routine -signed long cScale::LinearRound(mad_fixed_t sample) +signed long +cScale::LinearRound (mad_fixed_t sample) { - // round - sample += (1L << (MAD_F_FRACBITS - OUT_BITS)); - // clip - sample=Clip(sample); - // quantize and scale - return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS); +// round + sample += (1L << (MAD_F_FRACBITS - OUT_BITS)); +// clip + sample = Clip (sample); +// quantize and scale + return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS); } + // 32-bit pseudo-random number generator -unsigned long cScale::Prng(unsigned long state) +unsigned long +cScale::Prng (unsigned long state) { - return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; + return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; } + // generic linear sample quantize and dither routine -signed long cScale::LinearDither(mad_fixed_t sample, struct dither *dither) +signed long +cScale::LinearDither (mad_fixed_t sample, struct dither *dither) { - unsigned int scalebits; - mad_fixed_t output, mask, random; - - // noise shape - sample += dither->error[0] - dither->error[1] + dither->error[2]; - dither->error[2] = dither->error[1]; - dither->error[1] = dither->error[0] / 2; - // bias - output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1)); - scalebits = MAD_F_FRACBITS + 1 - OUT_BITS; - mask = (1L << scalebits) - 1; - // dither - random = Prng(dither->random); - output += (random & mask) - (dither->random & mask); - dither->random = random; - // clip - output=Clip(output); - sample=Clip(sample,false); - // quantize - output &= ~mask; - // error feedback - dither->error[0] = sample - output; - // scale - return output >> scalebits; + unsigned int scalebits; + mad_fixed_t output, mask, random; + +// noise shape + sample += dither->error[0] - dither->error[1] + dither->error[2]; + dither->error[2] = dither->error[1]; + dither->error[1] = dither->error[0] / 2; +// bias + output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1)); + scalebits = MAD_F_FRACBITS + 1 - OUT_BITS; + mask = (1L << scalebits) - 1; +// dither + random = Prng (dither->random); + output += (random & mask) - (dither->random & mask); + dither->random = random; +// clip + output = Clip (output); + sample = Clip (sample, false); +// quantize + output &= ~mask; +// error feedback + dither->error[0] = sample - output; +// scale + return output >> scalebits; } + // write a block of signed 16-bit big-endian PCM samples -unsigned int cScale::ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode) +unsigned int +cScale::ScaleBlock (unsigned char *data, unsigned int size, +unsigned int &nsamples, const mad_fixed_t * &left, +const mad_fixed_t * &right, eAudioMode mode) { - signed int sample; - unsigned int len, res; - - len=size/OUT_FACT; res=size; - if(len>nsamples) { len=nsamples; res=len*OUT_FACT; } - nsamples-=len; - - if(right) { // stereo - switch (mode) { - case amRound: - while (len--) { - sample = LinearRound(*left++); - *data++ = sample >> 8; - *data++ = sample >> 0; - sample = LinearRound(*right++); - *data++ = sample >> 8; - *data++ = sample >> 0; - } - break; - case amDither: - while (len--) { - sample = LinearDither(*left++,&leftD); - *data++ = sample >> 8; - *data++ = sample >> 0; - sample = LinearDither(*right++,&rightD); - *data++ = sample >> 8; - *data++ = sample >> 0; - } - break; - } + signed int sample; + unsigned int len, res; + + len = size / OUT_FACT; + res = size; + if (len > nsamples) + { + len = nsamples; + res = len * OUT_FACT; } - else { // mono, duplicate left channel - switch (mode) { - case amRound: - while (len--) { - sample = LinearRound(*left++); - *data++ = sample >> 8; - *data++ = sample >> 0; - *data++ = sample >> 8; - *data++ = sample >> 0; - } - break; - case amDither: - while (len--) { - sample = LinearDither(*left++,&leftD); - *data++ = sample >> 8; - *data++ = sample >> 0; - *data++ = sample >> 8; - *data++ = sample >> 0; - } - break; - } + nsamples -= len; + + if (right) + { // stereo + switch (mode) + { + case amRound: + while (len--) + { + sample = LinearRound (*left++); + *data++ = sample >> 8; + *data++ = sample >> 0; + sample = LinearRound (*right++); + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + case amDither: + while (len--) + { + sample = LinearDither (*left++, &leftD); + *data++ = sample >> 8; + *data++ = sample >> 0; + sample = LinearDither (*right++, &rightD); + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + } + } + else + { // mono, duplicate left channel + switch (mode) + { + case amRound: + while (len--) + { + sample = LinearRound (*left++); + *data++ = sample >> 8; + *data++ = sample >> 0; + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + case amDither: + while (len--) + { + sample = LinearDither (*left++, &leftD); + *data++ = sample >> 8; + *data++ = sample >> 0; + *data++ = sample >> 8; + *data++ = sample >> 0; + } + break; + } } - return res; + return res; } diff --git a/vdr_stream.c b/vdr_stream.c index 0c12075..4f79224 100644 --- a/vdr_stream.c +++ b/vdr_stream.c @@ -37,295 +37,315 @@ #include #endif -#define DEFAULT_PORT 80 // default port for streaming (HTTP) - +#define DEFAULT_PORT 80 // default port for streaming (HTTP) // --- mgStream ----------------------------------------------------------------- -mgStream::mgStream( std::string filename ) - : m_filename( filename ) +mgStream::mgStream (std::string filename):m_filename (filename) { - m_fd = -1; - m_ismmap = false; - m_buffer = 0; + m_fd = -1; + m_ismmap = false; + m_buffer = 0; } -mgStream::~mgStream() + +mgStream::~mgStream () { - close(); + close (); } -bool mgStream::open(bool log) + +bool mgStream::open (bool log) { - if( m_fd >= 0 ) + if (m_fd >= 0) { - return seek(); + return seek (); } - // just check, whether file exists? - if( fileinfo( log ) ) +// just check, whether file exists? + if (fileinfo (log)) { - printf( "mgStream::open: fileinfo == true\n"); - - if( ( m_fd = ::open( m_filename.c_str(), O_RDONLY ) ) >=0 ) - { - printf( "mgStream::open: file opened\n" ); +// printf ("mgStream::open: fileinfo == true\n"); - m_buffpos = m_readpos = 0; - m_fill = 0; + if ((m_fd =::open (m_filename.c_str (), O_RDONLY)) >= 0) + { + //printf ("mgStream::open: file opened\n"); - printf( "mgStream::open: buffpos, readpos, fill set\n" ); + m_buffpos = m_readpos = 0; + m_fill = 0; - /* -#ifdef USE_MMAP - if( m_filesize <= MAX_MMAP_SIZE ) - { - m_buffer = (unsigned char*)mmap( 0, m_filesize, PROT_READ, - MAP_SHARED, m_fd, 0 ); - if( m_buffer != MAP_FAILED ) - { - m_ismmap = true; - return true; - } - else - { - dsyslog("mmap() failed for %s: %s", m_filename.c_str(), strerror(errno) ); - } - } + //printf ("mgStream::open: buffpos, readpos, fill set\n"); + +/* + #ifdef USE_MMAP + if( m_filesize <= MAX_MMAP_SIZE ) + { + m_buffer = (unsigned char*)mmap( 0, m_filesize, PROT_READ, + MAP_SHARED, m_fd, 0 ); + if( m_buffer != MAP_FAILED ) + { + m_ismmap = true; + return true; + } +else +{ +dsyslog("mmap() failed for %s: %s", m_filename.c_str(), strerror(errno) ); +} +} #endif - */ - printf( "mgStream::open: allocating buffer: %d\n", MP3FILE_BUFSIZE ); - m_buffer = new unsigned char[MP3FILE_BUFSIZE]; - printf( "mgStream::open: buffer allocated\n" ); - - if( m_buffer ) - { - printf( "mgStream::open: buffer allocated, returning true\n" ); - - return true; - } - else - { - esyslog("ERROR: not enough memory for buffer: %s", m_filename.c_str() ); - } - } - else - { - if( log ) - { - esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) ); - } - } +*/ + //printf ("mgStream::open: allocating buffer: %d\n", MP3FILE_BUFSIZE); + m_buffer = new unsigned char[MP3FILE_BUFSIZE]; + //printf ("mgStream::open: buffer allocated\n"); + + if (m_buffer) + { + //printf ("mgStream::open: buffer allocated, returning true\n"); + + return true; + } + else + { + esyslog ("ERROR: not enough memory for buffer: %s", + m_filename.c_str ()); + } + } + else + { + if (log) + { + esyslog ("ERROR: failed to open file %s: %s", + m_filename.c_str (), strerror (errno)); + } + } } - close(); - printf( "mgStream::open: returning false\n" ); - return false; + close (); + //printf ("mgStream::open: returning false\n"); + return false; } -void mgStream::close(void) + +void +mgStream::close (void) { #ifdef USE_MMAP - if( m_ismmap ) - { - munmap( m_buffer, m_filesize ); - m_buffer = 0; - m_ismmap = false; + if (m_ismmap) + { + munmap (m_buffer, m_filesize); + m_buffer = 0; + m_ismmap = false; } - else + else { #endif - delete m_buffer; - m_buffer = 0; + delete m_buffer; + m_buffer = 0; #ifdef USE_MMAP } #endif - if( m_fd >= 0 ) - { - ::close( m_fd ); - m_fd = -1; + if (m_fd >= 0) + { + ::close (m_fd); + m_fd = -1; } } -bool mgStream::seek(unsigned long long pos) + +bool mgStream::seek (unsigned long long pos) { - printf( "mgStream::seek\n" ); - if( m_fd >= 0 && pos >= 0 && pos <= m_filesize ) + //printf ("mgStream::seek\n"); + if (m_fd >= 0 && pos >= 0 && pos <= m_filesize) { - printf( "mgStream::seek valid file and position detected\n" ); - - m_buffpos = 0; - m_fill = 0; - - if( m_ismmap ) - { - m_readpos = pos; - - printf( "mgStream::seek: returning true\n" ); - return true; - } - else - { - if( ( m_readpos = lseek64( m_fd, pos, SEEK_SET ) ) >=0 ) - { - if( m_readpos != pos ) - { - dsyslog( "seek mismatch in %s, wanted %lld, got %lld", m_filename.c_str(), pos, m_readpos ); - } - printf( "mgStream::seek: returning true\n" ); - return true; - } - else - { - esyslog( "ERROR: seeking failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) ); - } - } + //printf ("mgStream::seek valid file and position detected\n"); + + m_buffpos = 0; + m_fill = 0; + + if (m_ismmap) + { + m_readpos = pos; + + //printf ("mgStream::seek: returning true\n"); + return true; + } + else + { + if ((m_readpos = lseek64 (m_fd, pos, SEEK_SET)) >= 0) + { + if (m_readpos != pos) + { + dsyslog ("seek mismatch in %s, wanted %lld, got %lld", + m_filename.c_str (), pos, m_readpos); + } + //printf ("mgStream::seek: returning true\n"); + return true; + } + else + { + esyslog ("ERROR: seeking failed in %s: %d,%s", + m_filename.c_str (), errno, strerror (errno)); + } + } } - else + else { - printf( "mp3: bad seek call fd=%d pos=%lld name=%s\n", m_fd, pos, m_filename.c_str() ); + //printf ("mp3: bad seek call fd=%d pos=%lld name=%s\n", m_fd, pos, + //m_filename.c_str ()); } - - printf( "mgStream::seek: returning false\n" ); - return false; + + //printf ("mgStream::seek: returning false\n"); + return false; } -bool mgStream::stream(unsigned char * &data, - unsigned long &len, - const unsigned char *rest) + +bool +mgStream::stream (unsigned char *&data, +unsigned long &len, const unsigned char *rest) { - if( m_fd >= 0 ) + if (m_fd >= 0) { - if( m_readpos < m_filesize ) - { - if( m_ismmap ) - { - if( rest && m_fill ) - { - m_readpos = (rest - m_buffer); // take care of remaining data - } - m_fill = m_filesize - m_readpos; - data = m_buffer + m_readpos; - len = m_fill; - m_buffpos = m_readpos; - m_readpos += m_fill; - - return true; - } - else - { - if( rest && m_fill ) - { // copy remaining data to start of buffer - m_fill -= ( rest - m_buffer); // remaing bytes - memmove( m_buffer, rest, m_fill ); - } - else - { - m_fill = 0; - } - - int r; - do - { - r = read( m_fd, m_buffer + m_fill, - MP3FILE_BUFSIZE - m_fill ); - } while( r == -1 && errno == EINTR ); - - if( r >= 0 ) - { - m_buffpos = m_readpos - m_fill; - m_readpos += r; - m_fill += r; - data = m_buffer; - len = m_fill; - - return true; - } - else - { - esyslog("ERROR: read failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) ); - } - } - } - else - { - len = 0; - return true; - } + if (m_readpos < m_filesize) + { + if (m_ismmap) + { + if (rest && m_fill) + { + m_readpos = (rest - m_buffer);// take care of remaining data + } + m_fill = m_filesize - m_readpos; + data = m_buffer + m_readpos; + len = m_fill; + m_buffpos = m_readpos; + m_readpos += m_fill; + + return true; + } + else + { + if (rest && m_fill) + { // copy remaining data to start of buffer + m_fill -= (rest - m_buffer); // remaing bytes + memmove (m_buffer, rest, m_fill); + } + else + { + m_fill = 0; + } + + int r; + do + { + r = read (m_fd, m_buffer + m_fill, + MP3FILE_BUFSIZE - m_fill); + } + while (r == -1 && errno == EINTR); + + if (r >= 0) + { + m_buffpos = m_readpos - m_fill; + m_readpos += r; + m_fill += r; + data = m_buffer; + len = m_fill; + + return true; + } + else + { + esyslog ("ERROR: read failed in %s: %d,%s", + m_filename.c_str (), errno, strerror (errno)); + } + } + } + else + { + len = 0; + return true; + } } - return false; + return false; } -bool mgStream::removable() + +bool mgStream::removable () { - // we do not handle removable media at this time - return false; +// we do not handle removable media at this time + return false; } -bool mgStream::fileinfo( bool log ) + +bool mgStream::fileinfo (bool log) { - struct stat64 ds; - - if( !stat64( m_filename.c_str(), &ds ) ) + struct stat64 + ds; + + if (!stat64 (m_filename.c_str (), &ds)) { - printf( "mgStream::fileinfo: stat64 == 0\n" ); - - if( S_ISREG( ds.st_mode ) ) - { - m_fsID = ""; - m_fsType = 0; - - struct statfs64 sfs; - - if( !statfs64( m_filename.c_str(), &sfs) ) - { - if( removable() ) - { - char *tmpbuf; - asprintf( &tmpbuf, "%llx:%llx", sfs.f_blocks, sfs.f_files ); - m_fsID = tmpbuf; - free( tmpbuf ); - } - m_fsType = sfs.f_type; - } - else - { - if( errno != ENOSYS && log ) - { - esyslog("ERROR: can't statfs %s: %s", m_filename.c_str(), strerror(errno) ); - } - } - - m_filesize = ds.st_size; - m_ctime = ds.st_ctime; - + //printf ("mgStream::fileinfo: stat64 == 0\n"); + + if (S_ISREG (ds.st_mode)) + { + m_fsID = ""; + m_fsType = 0; + + struct statfs64 + sfs; + + if (!statfs64 (m_filename.c_str (), &sfs)) + { + if (removable ()) + { + char * + tmpbuf; + asprintf (&tmpbuf, "%llx:%llx", sfs.f_blocks, sfs.f_files); + m_fsID = tmpbuf; + free (tmpbuf); + } + m_fsType = sfs.f_type; + } + else + { + if (errno != ENOSYS && log) + { + esyslog ("ERROR: can't statfs %s: %s", m_filename.c_str (), + strerror (errno)); + } + } + + m_filesize = ds.st_size; + m_ctime = ds.st_ctime; + #ifdef CDFS_MAGIC - if( m_fsType == CDFS_MAGIC ) - { - m_ctime=0; // CDFS returns mount time as ctime - } + if (m_fsType == CDFS_MAGIC) + { + m_ctime = 0; // CDFS returns mount time as ctime + } #endif - // infodone tells that info has been read, like a cache flag - // InfoDone(); - return true; +// infodone tells that info has been read, like a cache flag +// InfoDone(); + return true; + } + else + { + if (log) + { + esyslog ("ERROR: %s is not a regular file", + m_filename.c_str ()); + } } - else - { - if(log) - { - esyslog("ERROR: %s is not a regular file", m_filename.c_str() ); - } - } } - else + else { - if(log) - { - esyslog("ERROR: can't stat %s: %s", m_filename.c_str(), strerror(errno) ); - } + if (log) + { + esyslog ("ERROR: can't stat %s: %s", m_filename.c_str (), + strerror (errno)); + } - printf( "mgStream::fileinfo: stat64 != 0 for %s\n", m_filename.c_str() ); + //printf ("mgStream::fileinfo: stat64 != 0 for %s\n", + // m_filename.c_str ()); } - - return false; + + return false; } diff --git a/vdr_stream.h b/vdr_stream.h index 68f7ea7..6b1769c 100644 --- a/vdr_stream.h +++ b/vdr_stream.h @@ -3,11 +3,11 @@ * \brief Definitions of media streams * * \version $Revision: 1.2 $ - * \date $Date: 2004/05/28 15:29:19 $ + * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author Responsible author: $Author: lvw $ + * \author Responsible author: $Author$ * - * $Id: vdr_stream.h,v 1.2 2004/05/28 15:29:19 lvw Exp $ + * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) @@ -25,33 +25,36 @@ class cNet; // ---------------------------------------------------------------- -class mgStream // : public mgFileInfo +class mgStream // : public mgFileInfo { -private: - int m_fd; - bool m_ismmap; - - // from cFileInfo - std::string m_filename, m_fsID; - unsigned long long m_filesize; - time_t m_ctime; - long m_fsType; - - bool fileinfo( bool log ); - bool removable(); - -protected: - unsigned char *m_buffer; - unsigned long long m_readpos, m_buffpos; - unsigned long m_fill; -public: - mgStream( std::string filename ); - virtual ~mgStream(); - virtual bool open(bool log = true); - virtual void close(); - virtual bool stream( unsigned char *&data, unsigned long &len, const unsigned char *rest=NULL ); - virtual bool seek( unsigned long long pos = 0 ); - virtual unsigned long long bufferPos() { return m_buffpos; } + private: + int m_fd; + bool m_ismmap; + +// from cFileInfo + std::string m_filename, m_fsID; + unsigned long long m_filesize; + time_t m_ctime; + long m_fsType; + + bool fileinfo (bool log); + bool removable (); + + protected: + unsigned char *m_buffer; + unsigned long long m_readpos, m_buffpos; + unsigned long m_fill; + public: + mgStream (std::string filename); + virtual ~ mgStream (); + virtual bool open (bool log = true); + virtual void close (); + virtual bool stream (unsigned char *&data, unsigned long &len, + const unsigned char *rest = NULL); + virtual bool seek (unsigned long long pos = 0); + virtual unsigned long long bufferPos () + { + return m_buffpos; + } }; - -#endif //___STREAM_H +#endif //___STREAM_H -- cgit v1.2.3 From b36c256ad747084ba4367f4c024cd0534dbeb37c Mon Sep 17 00:00:00 2001 From: wr61 Date: Sat, 8 Jan 2005 03:49:06 +0000 Subject: ported to 1.2.6 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@326 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 2 ++ vdr_menu.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/vdr_menu.c b/vdr_menu.c index e64688f..fc719a7 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -675,7 +675,9 @@ mgMainMenu::DisplayGoto (unsigned int select) RefreshCurrent (); } Display (); +#if VDRVERSNUM >= 10307 DisplayMenu()->SetTabs(25); +#endif } diff --git a/vdr_menu.h b/vdr_menu.h index 1a92370..834a36c 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -174,11 +174,13 @@ class mgMainMenu:public cOsdMenu //! \brief clears the screen, sets a title and buttons with text on them void InitOsd (const char *title,const bool hashotkeys); +#if VDRVERSNUM >= 10307 //! \brief expose the protected DisplayMenu() from cOsdMenu cSkinDisplayMenu *DisplayMenu(void) { return cOsdMenu::DisplayMenu(); } +#endif //! \brief expose the protected cOsdMenu::hk() const char *hk (const char *s) -- cgit v1.2.3 From 48fe8751a65c78ee7979e3936352ecc107fd32de Mon Sep 17 00:00:00 2001 From: wr61 Date: Sat, 8 Jan 2005 12:31:55 +0000 Subject: also compile mugglei, and optimize git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@330 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c5c8187..931c6ef 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri ### The C++ compiler and options: CXX ?= g++-3.3 -CXXFLAGS ?= -fPIC -O0 -Wall -Woverloaded-virtual -Wno-deprecated -g +CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g ### The directory environment: @@ -60,7 +60,7 @@ MILIBS = -lmysqlclient -ltag ### Targets: -all: libvdr-$(PLUGIN).so +all: libvdr-$(PLUGIN).so mugglei # Dependencies: -- cgit v1.2.3 From 67c9f997dd9fee87697d5058be905ee9eebe287f Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 9 Jan 2005 10:54:00 +0000 Subject: fixed a regression in shuffle mode git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@331 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mg_db.c b/mg_db.c index 6f365fc..9535612 100644 --- a/mg_db.c +++ b/mg_db.c @@ -477,6 +477,7 @@ mgSelection::ShuffleMode mgSelection::toggleShuffleMode () { long id = m_tracks[m_tracks_position].getId (); m_current_tracks = ""; // force a reload + tracksize = getNumTracks(); // getNumTracks also reloads for (unsigned int i = 0; i < tracksize; i++) if (m_tracks[i].getId () == id) { -- cgit v1.2.3 From cb8e8687bc484541aba272d63fd01656b78b0d64 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 9 Jan 2005 15:05:47 +0000 Subject: export m3u fixed for single track export git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@333 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mg_db.c b/mg_db.c index 9535612..b8b76b6 100644 --- a/mg_db.c +++ b/mg_db.c @@ -768,6 +768,8 @@ string mgSelection::getListname () result = ""; for (unsigned int i = 0; i < m_level; i++) addsep (result, ":", keys[i]->value ()); + if (m_level==keys.size()) + addsep (result,":",getCurrentValue()); if (result.empty ()) result = string(tr(keys[0]->choice ().c_str())); return result; -- cgit v1.2.3 From 9fa7b8aa75fc43291ade4daaeb0faf7f2e22f4c1 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 9 Jan 2005 20:33:52 +0000 Subject: merge patch to compile with 1.3.18 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@337 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 13 ++++++++++--- vdr_player.c | 12 ++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/vdr_menu.c b/vdr_menu.c index fc719a7..6871eb7 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -268,11 +268,18 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") // Read commands for collections in etc. /video/muggle/playlist_commands.conf external_commands = new cCommands (); - char * - cmd_file = (char *) AddDirectory (cPlugin::ConfigDirectory ("muggle"), +#if VDRVERSNUM >= 10318 + cString cmd_file = AddDirectory (cPlugin::ConfigDirectory ("muggle"), "playlist_commands.conf"); - mgDebug (1, "mgMuggle::Start: Looking for file %s", cmd_file); + mgDebug (1, "mgMuggle::Start: 10318 Looking for file %s", *cmd_file); + bool have_cmd_file = external_commands->Load (*cmd_file); +#else + const char * + cmd_file = (const char *) AddDirectory (cPlugin::ConfigDirectory ("muggle"), + "playlist_commands.conf"); + mgDebug (1, "mgMuggle::Start: 10317 Looking for file %s", cmd_file); bool have_cmd_file = external_commands->Load ((const char *) cmd_file); +#endif if (!have_cmd_file) { diff --git a/vdr_player.c b/vdr_player.c index b281746..1375a5e 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -643,7 +643,11 @@ mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); if (m_pframe) { +#if VDRVERSNUM >= 10318 + int w = PlayPes (p, pc); +#else int w = PlayVideo (p, pc); +#endif if (w > 0) { p += w; @@ -1111,7 +1115,11 @@ pes_header[6] = 0x0f; } memcpy(&pes_header[6 + ptslen], data, payload_size); +#if VDRVERSNUM >= 10318 +PlayPes(pes_header, 6 + ptslen + payload_size); +#else PlayVideo(pes_header, 6 + ptslen + payload_size); +#endif len -= payload_size; data += payload_size; @@ -1325,7 +1333,11 @@ mgPlayerControl::ShowContents () { int len = player->getCurrent ()->getDuration (); asprintf (&buf, "Length:\t%s", +#if VDRVERSNUM >= 10318 + *IndexToHMSF (SecondsToFrames (len))); +#else IndexToHMSF (SecondsToFrames (len))); +#endif m_menu->SetItem (buf, 4, false, false); free (buf); } -- cgit v1.2.3 From 6c1699dd3c79d70872d9c4f6a391386e7f5e0f31 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 9 Jan 2005 23:07:21 +0000 Subject: playing works with 1.3.18 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@338 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vdr_player.c b/vdr_player.c index 1375a5e..98571bd 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -378,6 +378,7 @@ mgPCMPlayer::Action (void) m_state = msStop; SetPlayMode (pmStopped); + cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttDolbyFirst); while (m_active) { #ifdef DEBUG -- cgit v1.2.3 From 37e5ef3df27d40608d7b34f5fcb09234c7906295 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 10 Jan 2005 07:30:54 +0000 Subject: Mugglei now provides defaults for missing tags git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@341 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 12 +++++- mugglei.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 108 insertions(+), 29 deletions(-) diff --git a/README b/README index 1ec00c8..ea2046f 100644 --- a/README +++ b/README @@ -34,7 +34,7 @@ parameters are descibed in Section 5. \section prereq PREREQUISITES -The plugin is written for VDR 1.2.6. In addition, the following pieces of +The plugin currently runs on VDR 1.3.7+. In addition, the following pieces of software are required: - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) @@ -69,7 +69,7 @@ For example (paths and version numbers may vary) Establish a symlink as you would for other plugins: \verbatim - ln -s muggle-0.1.7 muggle + ln -s muggle-0.1.0 muggle \endverbatim Within the VDR main directory (e.g. /usr/local/src/VDR) issue @@ -182,6 +182,14 @@ Speed should not be an issue: on my machine, it takes about 10 secs to run the i albums with more than 600 tracks. Further 1200 tracks or so require 20 more secs. This depends on machine configuration, of course. +If a track has no ID3 tags, the following defaults will be applied: + +- Title: Filename will be used +- Artist: "Unknown" +- Album: "Unassigned" +- Track: 0 +- Year: 0 + \section config MUGGLE CONFIGURATION Muggle uses a small set of command line parameters in order to control the interaction with the mySQL server. diff --git a/mugglei.c b/mugglei.c index 0abc856..7e284d7 100755 --- a/mugglei.c +++ b/mugglei.c @@ -5,7 +5,7 @@ * \author Lars von Wedel */ -// #define VERBOSE +#define VERBOSE #include #include @@ -45,6 +45,10 @@ MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } + +#ifdef VERBOSE + std::cout << querybuf << std::endl; +#endif MYSQL_RES *result = mysql_store_result(db); @@ -63,6 +67,10 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } +#ifdef VERBOSE + std::cout << querybuf << std::endl; +#endif + va_end(ap); } @@ -134,14 +142,23 @@ time_t get_db_modification_time( long uid ) TagLib::String escape_string( MYSQL *db, TagLib::String s ) { - char *buf = strdup( s.toCString() ); - char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); + TagLib::String r; - mysql_real_escape_string( db, escbuf, s.toCString(), s.size() ); - TagLib::String r = TagLib::String( escbuf ); + if( !s.isNull() && !s.isEmpty() ) + { + char *buf = strdup( s.toCString() ); + char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); - free( escbuf ); - free( buf); + mysql_real_escape_string( db, escbuf, s.toCString(), s.size() ); + r = TagLib::String( escbuf ); + + free( escbuf ); + free( buf); + } + else + { + r = s; + } return r; } @@ -193,18 +210,82 @@ void update_db( long uid, std::string filename ) // ID3_Tag filetag( filename.c_str() ); TagLib::FileRef f( filename.c_str() ); - if( !f.isNull() && f.tag() ) + if( !f.isNull() ) { - // std::cout << "Evaluating " << filename << std::endl; +#ifdef VERBOSE + std::cout << "Evaluating " << filename << std::endl; +#endif TagLib::Tag *tag = f.tag(); + if( tag ) + { + // obtain tag information + title = tag->title(); + album = tag->album(); + year = tag->year(); + artist = tag->artist(); + trackno = tag->track(); + genre = tag->genre(); + +#ifdef VERBOSE + std::cout << "-- TAG --" << std::endl; + std::cout << "title - '" << tag->title() << "'" << std::endl; + std::cout << "artist - '" << tag->artist() << "'" << std::endl; + std::cout << "album - '" << tag->album() << "'" << std::endl; + std::cout << "year - '" << tag->year() << "'" << std::endl; + std::cout << "comment - '" << tag->comment() << "'" << std::endl; + std::cout << "track - '" << tag->track() << "'" << std::endl; + std::cout << "genre - '" << tag->genre() << "'" << std::endl; +#endif + } + else + { +#ifdef VERBOSE + std::cerr << "No id3 tag found." << std::endl; +#endif + + // use basename + TagLib::String file( filename.c_str() ); + + int pos = title.size(); + while( pos > 0 && title[pos] != '\\' ) + { + pos --; + } + + title = file.substr( pos ); + + // will be associated to an Unassigned album for Unknown + album = ""; + artist = "Unknown"; + + year = 0; + trackno = 0; + } + + if( title.isNull() || title.isEmpty() ) + { +#ifdef VERBOSE + std::cout << "No title tag found." << std::endl; +#endif + // use basename + TagLib::String file( filename.c_str() ); + + int pos = title.size(); + while( pos > 0 && title[pos] != '\\' ) + { + pos --; + } + + title = file.substr( pos ); + } - // obtain tag information - title = tag->title(); - album = tag->album(); - year = tag->year(); - artist = tag->artist(); - trackno = tag->track(); - genre = tag->genre(); + if( artist.isNull() || artist.isEmpty() ) + { + artist = "Unknown"; +#ifdef VERBOSE + std::cout << "No artist tag found." << std::endl; +#endif + } TagLib::String gid = find_genre_id( genre ); @@ -223,7 +304,7 @@ void update_db( long uid, std::string filename ) // finally update the database // obtain associated album or create - if( album == "" ) + if( album.isNull() || album.isEmpty() ) { // no album found, create default album for artist MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() ); MYSQL_ROW row = mysql_fetch_row( result ); @@ -318,16 +399,6 @@ void update_db( long uid, std::string filename ) artist.toCString(), title.toCString(), year, cddbid.toCString(), trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString() ); -#ifdef VERBOSE - std::cout << "-- TAG --" << std::endl; - std::cout << "title - '" << tag->title() << "'" << std::endl; - std::cout << "artist - '" << tag->artist() << "'" << std::endl; - std::cout << "album - '" << tag->album() << "'" << std::endl; - std::cout << "year - '" << tag->year() << "'" << std::endl; - std::cout << "comment - '" << tag->comment() << "'" << std::endl; - std::cout << "track - '" << tag->track() << "'" << std::endl; - std::cout << "genre - '" << tag->genre() << "'" << std::endl; -#endif } } } -- cgit v1.2.3 From 64fca5091a46ee31604771d376ebbaabd7ee4824 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 10 Jan 2005 21:09:27 +0000 Subject: Compile track selection conditionally on 1.3.18 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@344 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index 98571bd..e5d3803 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -378,7 +378,9 @@ mgPCMPlayer::Action (void) m_state = msStop; SetPlayMode (pmStopped); +#if VDRVERSNUM >= 10318 cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttDolbyFirst); +#endif while (m_active) { #ifdef DEBUG -- cgit v1.2.3 From c9a77460e204b92d2efcf2678126000d97cac10a Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 11 Jan 2005 21:54:53 +0000 Subject: merged from osd_extensions: create collection can also be started with kOk git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@346 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_actions.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mg_actions.c b/mg_actions.c index d9e45af..32179a6 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -713,7 +713,10 @@ eOSState mgCreateCollection::ProcessKey(eKeys key) { if (key == kOk) - Execute(); + if (Editing()) + Execute(); + else + return cMenuEditStrItem::ProcessKey(kRight); return cMenuEditStrItem::ProcessKey(key); } -- cgit v1.2.3 From 97ef2c39156f0df490aa5b948464c5b3c48ae622 Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 11 Jan 2005 22:42:22 +0000 Subject: created collection becomes default git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@348 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_actions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mg_actions.c b/mg_actions.c index 32179a6..b2e21f8 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -737,6 +737,7 @@ mgCreateCollection::Execute () if (created) { mgDebug(1,"created collection %s",name.c_str()); + osd()->default_collection = name; selection ()->clearCache(); osd()->forcerefresh = true; } -- cgit v1.2.3 From 4ff783d18802d1334d1b06141132d9fe138a2927 Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 12 Jan 2005 10:50:38 +0000 Subject: fixed partial checkin of kOK for start of CreateCollection git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@349 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_actions.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mg_actions.c b/mg_actions.c index b2e21f8..502b1f6 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -689,13 +689,18 @@ mgRemoveThisFromCollection::MenuName (const unsigned int idx,const string value) return b; } +bool +mgCreateCollection::Editing() +{ + return (strchr(cOsdItem::Text(),'[') && strchr(cOsdItem::Text(),']')); +} + void mgCreateCollection::Notify() { - if (!strchr(Text(),'[')) - if (!strchr(Text(),']')) - osd()->SetHelpKeys(NULL,NULL,NULL,NULL); + if (!Editing()) + osd()->SetHelpKeys(NULL,NULL,NULL,NULL); } const char* @@ -717,7 +722,10 @@ mgCreateCollection::ProcessKey(eKeys key) Execute(); else return cMenuEditStrItem::ProcessKey(kRight); - return cMenuEditStrItem::ProcessKey(key); + if (key != kYellow || Editing()) + return cMenuEditStrItem::ProcessKey(key); + else + return osUnknown; } bool -- cgit v1.2.3 From c1bfacdf2b9ffc6381c3794b4816239bff3831bc Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 12 Jan 2005 10:58:58 +0000 Subject: fixed partial checkin of kOK for start of CreateCollection git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@350 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_actions.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mg_actions.h b/mg_actions.h index cb0602c..ecb5913 100644 --- a/mg_actions.h +++ b/mg_actions.h @@ -163,6 +163,7 @@ class mgCreateCollection:public cMenuEditStrItem, public mgAction void Execute (); const char *MenuName (const unsigned int idx=0,const string value=""); private: + bool Editing(); char value[30]; }; #endif -- cgit v1.2.3 From ce63587fe734b5234c76cca147c1e520b39d90a4 Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 12 Jan 2005 11:14:00 +0000 Subject: Warn if no decoder can be found for music file git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@351 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index e5d3803..68830b0 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -425,6 +425,8 @@ mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); break; } + else + mgWarning("Cannot find a decoder for %s",filename.c_str()); } m_state = msEof; } -- cgit v1.2.3 From a607bea23f0c6b34af9e8b0eb6e3c903432910d3 Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 12 Jan 2005 11:37:17 +0000 Subject: check all rows for NULL git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@354 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/mg_db.c b/mg_db.c index b8b76b6..e227b91 100644 --- a/mg_db.c +++ b/mg_db.c @@ -448,7 +448,8 @@ mgSelection::loadgenres () MYSQL_ROW row; while ((row = mysql_fetch_row (rows)) != NULL) { - genres[row[0]] = row[1]; + if (row[0] && row[1]) + genres[row[0]] = row[1]; } mysql_free_result (rows); } @@ -872,19 +873,54 @@ mgContentItem::mgContentItem (const mgContentItem* c) mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir) { m_id = atol (row[0]); - m_title = row[1]; - m_mp3file = ToplevelDir + row[2]; - m_artist = row[3]; - m_albumtitle = row[4]; - m_genre1 = row[5]; - m_genre2 = row[6]; - m_bitrate = row[7]; - m_year = atol (row[8]); + if (row[1]) + m_title = row[1]; + else + m_title = "NULL"; + if (row[2]) + m_mp3file = ToplevelDir + row[2]; + else + m_mp3file = "NULL"; + if (row[3]) + m_artist = row[3]; + else + m_artist = row[3]; + if (row[4]) + m_albumtitle = row[4]; + else + m_albumtitle = "NULL"; + if (row[5]) + m_genre1 = row[5]; + else + m_genre1 = "NULL"; + if (row[6]) + m_genre2 = row[6]; + else + m_genre2 = "NULL"; + if (row[7]) + m_bitrate = row[7]; + else + m_bitrate = "NULL"; + if (row[8]) + m_year = atol (row[8]); + else + m_year = 0; if (row[9]) m_rating = atol (row[9]); - m_duration = atol (row[10]); - m_samplerate = atol (row[11]); - m_channels = atol (row[12]); + else + m_rating = 0; + if (row[10]) + m_duration = atol (row[10]); + else + m_duration = 0; + if (row[11]) + m_samplerate = atol (row[11]); + else + m_samplerate = 0; + if (row[12]) + m_channels = atol (row[12]); + else + m_channels = 0; }; string mgContentItem::getAlbum () @@ -1171,11 +1207,20 @@ mgSelection::refreshValues () MYSQL_ROW row; while ((row = mysql_fetch_row (rows)) != NULL) { - values.strings.push_back (row[0]); + string r0,r1; + if (row[0]) + r0 = row[0]; + else + r0 = "NULL"; + if (row[1]) + r1 = row[1]; + else + r1 = "NULL"; + values.strings.push_back (r0); if (num_fields==2) - m_ids.push_back (row[1]); + m_ids.push_back (r1); else - m_ids.push_back (row[0]); + m_ids.push_back (r0); } mysql_free_result (rows); } -- cgit v1.2.3 From f3d430aa36ace2edfa471e8eeb541a46f9e9e2ea Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 16 Jan 2005 21:41:00 +0000 Subject: Add GD compatibility for unique filenames git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@362 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 5 +++-- muggle.doxygen | 2 +- vdr_player.c | 6 +++--- vdr_setup.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- vdr_setup.h | 3 +++ 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/mg_db.c b/mg_db.c index e227b91..a310d51 100644 --- a/mg_db.c +++ b/mg_db.c @@ -839,7 +839,7 @@ mgSelection::tracks () MYSQL_ROW row; while ((row = mysql_fetch_row (rows)) != NULL) { - m_tracks.push_back (mgContentItem (row, m_ToplevelDir)); + m_tracks.push_back (mgContentItem (row, m_ToplevelDir)); } mysql_free_result (rows); } @@ -878,7 +878,8 @@ mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir) else m_title = "NULL"; if (row[2]) - m_mp3file = ToplevelDir + row[2]; + // m_mp3file = ToplevelDir + row[2]; + m_mp3file = row[2]; else m_mp3file = "NULL"; if (row[3]) diff --git a/muggle.doxygen b/muggle.doxygen index 4e601ad..96a5394 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.0.9 +PROJECT_NUMBER = 0.1.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/vdr_player.c b/vdr_player.c index 68830b0..a62f9b5 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -409,9 +409,9 @@ mgPCMPlayer::Action (void) if (m_playing) { - std::string filename = m_playing->getSourceFile (); -mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); - + std::string filename = the_setup.getFilename( m_playing->getSourceFile () ); + mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); + if ((m_decoder = mgDecoders::findDecoder (m_playing)) && m_decoder->start ()) { diff --git a/vdr_setup.c b/vdr_setup.c index 36a3b46..093cf98 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -14,13 +14,19 @@ * (C) 2001,2002 Stefan Huelswitt */ -#include +#include +#include +#include +#include #include "vdr_setup.h" #include "i18n.h" mgSetup the_setup; +char *readline(FILE *f); +std::string GdFindFile( std::string mp3file, std::string ToplevelDir ); + // --- mgMenuSetup ----------------------------------------------------------- mgMenuSetup::mgMenuSetup () @@ -86,3 +92,55 @@ mgSetup::mgSetup () Only48kHz = 0; ToplevelDir = "/mnt/music/"; } + +std::string +mgSetup::getFilename( std::string basename ) +{ + if( GdCompatibility ) + { + return GdFindFile( basename, std::string( ToplevelDir ) ); + } + else + { + return string( ToplevelDir ) + basename; + } +} + +#define MAXPARSEBUFFER 1024 + +char *readline(FILE *f) +{ + static char buffer[MAXPARSEBUFFER]; + if (fgets(buffer, sizeof(buffer), f) > 0) + { + int l = strlen(buffer) - 1; + if (l >= 0 && buffer[l] == '\n') + buffer[l] = 0; + return buffer; + } + return NULL; +} + +#define FINDCMD "cd '%s' && find -follow -name '%s' 2> /dev/null" + +std::string GdFindFile( std::string mp3file, std::string tld ) +{ + std::string fullname = ""; + char *cmd = NULL; + asprintf( &cmd, FINDCMD, ToplevelDir.c_str(), mp3file.c_str() ); + FILE *p = popen( cmd, "r" ); + if (p) + { + char *s; + while( (s = readline(p) ) != NULL) + { + // printf( "Found: %s", s ); + fullname = std::string( tld ) + std::string( s ); + } + pclose(p); + } + + free( cmd ); + + return fullname; +} diff --git a/vdr_setup.h b/vdr_setup.h index c21adb1..b820857 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -19,6 +19,7 @@ // #include #include +#include #define MAX_STRING_LEN 128 @@ -56,6 +57,8 @@ class mgSetup public: mgSetup (void); + + std::string getFilename( std::string basename ); }; extern mgSetup the_setup; -- cgit v1.2.3 From 3f57eefda10f5461b6b98d8292d527061554c659 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 16 Jan 2005 21:45:34 +0000 Subject: Corrected syntax and removed readline definition git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@363 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_setup.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/vdr_setup.c b/vdr_setup.c index 093cf98..8e281cd 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -24,7 +24,6 @@ mgSetup the_setup; -char *readline(FILE *f); std::string GdFindFile( std::string mp3file, std::string ToplevelDir ); // --- mgMenuSetup ----------------------------------------------------------- @@ -102,32 +101,17 @@ mgSetup::getFilename( std::string basename ) } else { - return string( ToplevelDir ) + basename; + return std::string( ToplevelDir ) + basename; } } -#define MAXPARSEBUFFER 1024 - -char *readline(FILE *f) -{ - static char buffer[MAXPARSEBUFFER]; - if (fgets(buffer, sizeof(buffer), f) > 0) - { - int l = strlen(buffer) - 1; - if (l >= 0 && buffer[l] == '\n') - buffer[l] = 0; - return buffer; - } - return NULL; -} - #define FINDCMD "cd '%s' && find -follow -name '%s' 2> /dev/null" std::string GdFindFile( std::string mp3file, std::string tld ) { std::string fullname = ""; char *cmd = NULL; - asprintf( &cmd, FINDCMD, ToplevelDir.c_str(), mp3file.c_str() ); + asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() ); FILE *p = popen( cmd, "r" ); if (p) { -- cgit v1.2.3 From 356d968b27592d2b3b15fb28e7bd782d0c4b06d6 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 23 Jan 2005 10:59:29 +0000 Subject: Formatting beautified git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@383 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 178 ++++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 131 insertions(+), 47 deletions(-) diff --git a/README b/README index ea2046f..06e0064 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ /*! \mainpage Muggle: Media Juggler for VDR -This is a "plugin" for the Video Disk Recorder (VDR). +This is a plugin for the Video Disk Recorder (VDR). Written by: Andi Kellner, Lars von Wedel , @@ -34,8 +34,8 @@ parameters are descibed in Section 5. \section prereq PREREQUISITES -The plugin currently runs on VDR 1.3.7+. In addition, the following pieces of -software are required: +The plugin currently runs on versions 1.3.17- of VDR. It also compiles on 1.3.18 but +your mileage may vary. In addition, the following pieces of software are required: - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) - mySQL client libraries @@ -63,13 +63,13 @@ For example (paths and version numbers may vary) \verbatim cd /usr/local/src/VDR/PLUGINS/src - tar xvjf muggle-0.1.7.tar.bz2 + tar xvjf muggle-0.1.1.tgz \endverbatim Establish a symlink as you would for other plugins: \verbatim - ln -s muggle-0.1.0 muggle + ln -s muggle-0.1.1 muggle \endverbatim Within the VDR main directory (e.g. /usr/local/src/VDR) issue @@ -192,63 +192,116 @@ If a track has no ID3 tags, the following defaults will be applied: \section config MUGGLE CONFIGURATION -Muggle uses a small set of command line parameters in order to control the interaction with the mySQL server. -Let's look at an example: +Muggle uses a small set of command line parameters in order to control +the interaction with the mySQL server. Let's look at an example: \verbatim -P'muggle -h localhost -u vdr -n GiantDisc -t/home/music' \endverbatim -The -h parameter specifies the database host, -u specifies the user, -n is the database name. The scripts mentioned -above do not make use of passwords, but restrict database acccess on a server basis. +The -h parameter specifies the database host, -u specifies the user, +-n is the database name. The scripts mentioned above do not make use +of passwords, but restrict database acccess on a server basis. -The -t argument specifies the top level directory of the music files. On a local installation, this is the -directory in which you executed the import steps (Chapter 4.2). +The -t argument specifies the top level directory of the music files. +On a local installation, this is the directory in which you executed the +import steps (Chapter 4.2). \section quickuse QUICK INTRO -Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), add them using the red button. -Music will start instantly while you can continue to browse and add tracks. +Quick version: select Muggle on the OSD, browse titles (using up/down and Ok), +add them using the red button. Music will start instantly while you can continue +to browse and add tracks. -During playback, Up/Down jumps forth and back in the current playlist. Yellow toggles play/pause mode and Ok -toggles a display of the replay process. Using Green, the display can be switched between playlist and single display mode, red toggles info and progress view. For VDR 1.3.6- the progress display is "quite simple", unfortunately. +During playback, Up/Down jumps forth and back in the current playlist. Yellow +toggles play/pause mode and Ok toggles a display of the replay process. Using +Green, the display can be switched between playlist and single display mode, +Red toggles info and progress view. For VDR 1.3.6- the progress display is +"quite simple", unfortunately. \section use DETAILED USER'S GUIDE -The core concept of the Muggle user interface is called a *selection*. That is, as the name suggests, a selection of music tracks. Note, that a selection can be as small as a single track (a very simple selection, indeed) or as large as the whole music library. - -Selections are used to structure all tracks (the music library) into sets (e.g. a selection of all tracks by an author) and subsets (e.g. the tracks of an author on a certain album). Such selections are built by means of keys (e.g. author or album) defined in the database and are displayed in the *music browser*. The current selection in the *music browser* contains all tracks defined by the line the cursor is on. So if you place the cursor on the line "Pop", all tracks with Genre Pop are selected. If you then enter Pop and go to the line "Beatles", you narrow your selection to pop songs from the beatles. - -A collection is a special selection. Collections can be defined by the user, and he can add or remove any selection to / from a collection. A collection has only ony order: a number which is incremented for every added track. Otherwise, since a collection is also a selection, everything that is valid for selections also holds for collections. - -Collections can be defined by the user in the sense of a playlist. This is done by adding/removing selections to/from the *default collection*. - -Changing the contents of a collection changes them directly in the data base. Saving or loading collections is not needed. - -A very important term while working with Muggle is the *default collection*. This is a special collection which is the target of commands working on collections. Whenever you add selections to somewhere, they will be added to the default collection. The same happens when you remove selections. - -Another important collection is the 'play' collection. This is a temporary collection. Whatever is added to it will be played in that order. If you add something while muggle is not playing anything, this collection will first be emptied. -However 'temporary' does not mean that its content is not saved to the data base. +The core concept of the Muggle user interface is called a *selection*. That is, +as the name suggests, a selection of music tracks. Note, that a selection can be +as small as a single track (a very simple selection, indeed) or as large as the +whole music library. + +Selections are used to structure all tracks (the music library) into sets (e.g. +a selection of all tracks by an author) and subsets (e.g. the tracks of an author +on a certain album). Such selections are built by means of keys (e.g. author +or album) defined in the database and are displayed in the *music browser*. The +current selection in the *music browser* contains all tracks defined by the line +the cursor is on. So if you place the cursor on the line "Pop", all tracks with +Genre Pop are selected. If you then enter Pop and go to the line "Beatles", you +narrow your selection to pop songs from the beatles. + +A collection is a special selection. Collections can be defined by the user, and +he can add or remove any selection to / from a collection. A collection has only +one order: a number which is incremented for every added track. Otherwise, since +a collection is also a selection, everything that is valid for selections also +holds for collections. + +Collections can be defined by the user in the sense of a playlist. This is done by +adding/removing selections to/from the *default collection*. + +Changing the contents of a collection changes them directly in the data base. Saving +or loading collections is not needed. + +An important term while working with Muggle is the *default collection*. This +is a special collection which is the target of commands working on collections. +Whenever you add selections to somewhere, they will be added to the default +collection. The same happens when you remove selections. + +Another important collection is the 'play' collection. This is a temporary collection. +Whatever is added to it will be played in that order. If you add something while muggle +is not playing anything, this collection will first be emptied. However 'temporary' does +not mean that its content is not saved to the data base. + +Starting from release 0.1.1 Muggle can be also used without default playlists. There are +new menu entries "Add X to collection" and "Remove X from collection" which show a list +of all collections to choose from. The concept of a default collection still exists and +both approaches can be used in common. However, you can spceify which commands to use for +the special keys Red/Green/Blueas you like. \subsection general General remarks -There are two main views in Muggle, the *Music browser* view and the *Collection browser* view. You can toggle between them using the yellow key. +There are two main views in Muggle, the *Music browser* view and the *Collection browser* +view. You can toggle between them using the Yellow key by default, however the key binding +can be changed. -Each of the two views has associated commands. To show a summary of the commands available for the current view press the blue key. Note, that the red, green and yellow keys do not have a fixed meaning. Rather, while the commands for a certain view are displayed, you can press red/green/yellow to make the respective key execute the command currently selected (highlighted) by the cursor. The commands you choose for red/green/yellow will be saved for the next time you start muggle. You can define different commands in both view *Music browser* and *Collection browser*. +Each of the two views has associated commands. To show a summary of the commands available +for the current view press the blue key. Note, that the red, green and yellow keys do not +have a fixed meaning. Rather, while the commands for a certain view are displayed, you can +press red/green/yellow to make the respective key execute the command currently selected +(highlighted) by the cursor. The commands you choose for red/green/yellow will be saved for +the next time you start muggle. You can define different commands in both view *Music browser* +and *Collection browser*. \subsection browse Music browser -By default, Muggle starts in the *Music browser* display at the place where you left it last time. This browser displays the music library according to a search order, e.g. according to artists / albums / tracks or genre / year / track. These search orders are currently fixed in the code, but the objective is to make them editable by the user on the OSD. Browsing these search orders is done using Up/Down/Left/Right keys. To display the contents of a currently selected selection, press Ok. To return to the parent selection press Back. +By default, Muggle starts in the *Music browser* display at the place where you left it +last time. This browser displays the music library according to a search order, e.g. +according to artists / albums / tracks or genre / year / track. These search orders are +currently fixed in the code, but the objective is to make them editable by the user on the +OSD. Browsing these search orders is done using Up/Down/Left/Right keys. To display the +contents of a currently selected selection, press Ok. To return to the parent selection +press Back. -A set of commands can be displayed with the Blue key on the remote control. A new menu will open and show the commands explained below. Remember that pressing Red, Green or Yellow will make these keys execute the command currently highlighted by the cursor from now on. +A set of commands can be displayed with the Blue key on the remote control. A new menu +will open and show the commands explained below. Remember that pressing Red, Green or +Yellow will make these keys execute the command currently highlighted by the cursor +from now on. Those commands are currently available in the *music browser*: - Instant Play: instantly play the current selection. This does not enter any collection. -- Add to 'play': add the current selection to the default collection. After the first start of muggle, the default collection is 'play' +- Add to 'play': add the current selection to the default collection. After the first +start of Muggle, the default collection is 'play' -- Remove from 'play': remove the current selection from the default collection. If there are more than one instances of a specific track in the collection, they are all removed. +- Remove from 'play': remove the current selection from the default collection. If +there are more than one instances of a specific track in the collection, they are all +removed. - Collections: switch to the collection view @@ -258,17 +311,32 @@ Those commands are currently available in the *music browser*: - External commands: whatever you define -By default, the red key adds the currently selected collection to the default collection. The green key instantly plays the currently selected collection. The yellow key toggles between the *Music browser* and the *Collection browser*. Thus, if you want to play an album, browse to it and press green. Remember that you can redefine commands executed by red, green and yellow by pressing them while displaying the command list. +By default, the red key adds the currently selected collection to the default collection. +The green key instantly plays the currently selected collection. The yellow key toggles +between the *Music browser* and the *Collection browser*. Thus, if you want to play an +album, browse to it and press green. Remember that you can redefine commands executed by +Red, Green and Yellow by pressing them while displaying the command list. \subsection collections Collection browser -The *Collection browser* displays a list of available collections. Browse the list with Up/Down and display the collection contents with Ok. Returning to the collection list is done by pressing Back. One of the collections (the one called "play" when you start up muggle for the first time) is marked with a "->" in front of the name, meaning that it is the default collection. Whenever you add or remove selections, this default collection is the current target, meaning that selections will be added/removed to/from this collection. +The *Collection browser* displays a list of available collections. Browse the list with +Up/Down and display the collection contents with Ok. Returning to the collection list +is done by pressing Back. One of the collections (the one called "play" when you start +up Muggle for the first time) is marked with a "->" in front of the name, meaning that +it is the default collection. Whenever you add or remove selections, this default +collection is the current target, meaning that selections will be added/removed +to/from this collection. -At the bottom of the list, the entry "Create collection" is displayed. Entering it with the right key will make the editor appear on the second half of the line and using the keys Up/Down/Left/Right you can enter the name of the new collection. Pressing Ok will terminate the editing process and add the new collection to the list. +At the bottom of the list, the entry "Create collection" is displayed. Entering it with +the right key will make the editor appear on the second half of the line and using the +keys Up/Down/Left/Right you can enter the name of the new collection. Pressing Ok will +terminate the editing process and add the new collection to the list. -Just like with the *music browser*, a set of commands can be displayed with the Blue key on the remote control. +Just like with the *music browser*, a set of commands can be displayed with the Blue +key on the remote control. -Those commands are currently available in the list of collections. Depending on the current selection, not all of them are available: +Those commands are currently available in the list of collections. Depending on the +current selection, not all of them are available: - Instant play: See *music browser* @@ -288,12 +356,28 @@ Those commands are currently available in the list of collections. Depending on - External commands: whatever you define -Note that you cannot only add to/remove from collections in the *music browser*. Rather, also collections can be added/removed. The reason is that - as explained above - a collection is also a selection. So everything that can be done with selections can also be done with collections. An example: if you want to give a party, you could create a new collection "Party". Now, steer your cursor to the collection entitled "Lounge music" and select add. Then go to "Pop 80s" and add again. Finally, go to "Dance classics" and add. Now you have created a collection "Party" from three already existing collections. To continue this example, let us assume that one of your guests has a personal dislike against "Modern Talking". Switch to the browser view, go to the artist selection of "Modern Talking" and select "Remove". Now all tracks written by Modern Talking will be removed from your "Party" collection. - -Please note that "Remove" means removing from the default collection. "Delete" will delete a collection. - -It is possible that a collection holds the same track several times if you add it several times. However when you remove that track, all of its occurrences will be removed. - -The remote buttons Play, Pause, Stop are also supported while muggle displays its OSD. If Stop is pressed, muggle first stops playing what was started by Instant Play. Muggle will then continue playing the 'play' collection. A second Stop will stop playing the 'play' collection. +Note that you cannot only add to/remove from collections in the *music browser*. +Rather, also collections can be added/removed. The reason is that - as explained +above - a collection is also a selection. So everything that can be done with +selections can also be done with collections. An example: if you want to give a +party, you could create a new collection "Party". Now, steer your cursor to the +collection entitled "Lounge music" and select add. Then go to "Pop 80s" and add +again. Finally, go to "Dance classics" and add. Now you have created a collection +"Party" from three already existing collections. To continue this example, let us +assume that one of your guests has a personal dislike against "Modern Talking". +Switch to the browser view, go to the artist selection of "Modern Talking" and +select "Remove". Now all tracks written by Modern Talking will be removed from +your "Party" collection. + +Please note that "Remove" means removing from the default collection. "Delete" will +delete a collection. + +It is possible that a collection holds the same track several times if you add it +several times. However when you remove that track, all of its occurrences will be removed. + +The remote buttons Play, Pause, Stop are also supported while muggle displays its +OSD. If Stop is pressed, muggle first stops playing what was started by Instant +Play. Muggle will then continue playing the 'play' collection. A second Stop will +stop playing the 'play' collection. */ -- cgit v1.2.3 From 0c5c1deeb5ca6e2c572ceb9192437ee3d9178238 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 23 Jan 2005 11:00:03 +0000 Subject: Version number changes for 0.1.1 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@384 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 2 +- muggle.doxygen | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/muggle.c b/muggle.c index f08a1d2..62bfe75 100644 --- a/muggle.c +++ b/muggle.c @@ -21,7 +21,7 @@ #include #include -static const char *VERSION = "0.1.0"; +static const char *VERSION = "0.1.1"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/muggle.doxygen b/muggle.doxygen index 96a5394..2b401c4 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.1.0 +PROJECT_NUMBER = 0.1.1 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. -- cgit v1.2.3 From 05801055e91bef231bb6aa48a96034e69bd7f250 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 23 Jan 2005 13:03:34 +0000 Subject: Merged branches osd_extensions and flac_player for new release, yet untested git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@386 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 10 +- README | 4 +- i18n.c | 187 +++++---- mg_actions.c | 690 +++++++++++++++++++++---------- mg_actions.h | 81 ++-- mg_database.h | 1 - mg_db.c | 1137 ++++++++++++++++++---------------------------------- mg_db.h | 642 ++++------------------------- mg_filters.c | 297 -------------- mg_filters.h | 171 -------- mg_order.c | 775 +++++++++++++++++++++++++++++++++++ mg_order.h | 154 +++++++ mg_valmap.c | 70 ++++ mg_valmap.h | 50 +++ muggle.c | 1 - vdr_decoder.c | 49 ++- vdr_decoder.h | 150 +++---- vdr_decoder_flac.c | 380 ++++++++++++++++++ vdr_decoder_flac.h | 80 ++++ vdr_menu.c | 325 ++++++++++----- vdr_menu.h | 78 ++-- vdr_player.c | 30 +- 22 files changed, 2997 insertions(+), 2365 deletions(-) delete mode 100644 mg_filters.c delete mode 100644 mg_filters.h create mode 100644 mg_order.c create mode 100644 mg_order.h create mode 100644 mg_valmap.c create mode 100644 mg_valmap.h create mode 100644 vdr_decoder_flac.c create mode 100644 vdr_decoder_flac.h diff --git a/Makefile b/Makefile index 931c6ef..9213ab8 100644 --- a/Makefile +++ b/Makefile @@ -44,18 +44,16 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE -# DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DEFINES += -D_GNU_SOURCE +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE -DHAVE_FLAC MIFLAGS += -I/usr/include/taglib -lmysqlclient ### The object files (add further files here): -OBJS = $(PLUGIN).o i18n.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \ +OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \ vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \ - vdr_setup.o vdr_decoder_ogg.o + vdr_setup.o vdr_decoder_ogg.o vdr_decoder_flac.o -LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis +LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis -lFLAC++ MILIBS = -lmysqlclient -ltag ### Targets: diff --git a/README b/README index 06e0064..5a00e64 100644 --- a/README +++ b/README @@ -34,8 +34,8 @@ parameters are descibed in Section 5. \section prereq PREREQUISITES -The plugin currently runs on versions 1.3.17- of VDR. It also compiles on 1.3.18 but -your mileage may vary. In addition, the following pieces of software are required: +The plugin currently runs on versions 1.3.17- of VDR (including 1.2.6). It also compiles on 1.3.18 +but your mileage may vary. In addition, the following pieces of software are required: - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) - mySQL client libraries diff --git a/i18n.c b/i18n.c index db5e673..13b7fea 100644 --- a/i18n.c +++ b/i18n.c @@ -19,7 +19,24 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO "", // TODO - "Chercher", // TODO + "Rechercher", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Order", + "Sortierung", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Ordre", // TODO "", // TODO "", // TODO "", // TODO @@ -46,6 +63,40 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO }, + { + "Clear the collection?", + "Sammlung leeren?", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Vider la collection?", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Delete the collection?", + "Sammlung löschen?", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Effacer la collection?", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "Create collection", "Sammlung neu anlegen", @@ -64,7 +115,7 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Delete collection", + "Delete the collection", "Sammlung löschen", "", // TODO "", // TODO @@ -284,6 +335,23 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO }, + { + "Export", + "Exportieren", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Exporter", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "Export track list", "Stückliste exportieren", @@ -523,13 +591,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Set default to collection '%s'", - "Setze Ziel auf Sammlung '%s'", + "'%s' to collection", + "'%s' zu Sammlung", "", // TODO "", // TODO "", // TODO "", // TODO - "Changer destination à la collection '%s'", // TODO + "Ajoute '%s' à une collection", // TODO "", // TODO "", // TODO "", // TODO @@ -540,13 +608,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Default collection now is '%s'", - "Zielsammlung ist nun '%s'", + "Set default to collection '%s'", + "Setze Ziel auf Sammlung '%s'", "", // TODO "", // TODO "", // TODO "", // TODO - "La collection destinataire est maintenant '%s'", // TODO + "Changer destination à la collection '%s'", // TODO "", // TODO "", // TODO "", // TODO @@ -557,13 +625,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Add", - "Hinzu", + "Default collection now is '%s'", + "Zielsammlung ist nun '%s'", "", // TODO "", // TODO "", // TODO "", // TODO - "Ajouter", // TODO + "La collection destinataire est maintenant '%s'", // TODO "", // TODO "", // TODO "", // TODO @@ -574,13 +642,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Add to '%s'", - "Zu '%s' hinzufügen", + "Add", + "Hinzu", "", // TODO "", // TODO "", // TODO "", // TODO - "Ajouter à '%s'", // TODO + "Ajouter", // TODO "", // TODO "", // TODO "", // TODO @@ -591,13 +659,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Add '%s' to '%s'", - "'%s' zu '%s' hinzufügen", + "Add to a collection", + "Zu einer Sammlung hinzufügen", "", // TODO "", // TODO "", // TODO "", // TODO - "Ajouter '%s' à '%s'", // TODO + "Ajouter à une collection", // TODO "", // TODO "", // TODO "", // TODO @@ -608,13 +676,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Add all to '%s'", - "Alles '%s' hinzufügen", + "Add to '%s'", + "Zu '%s' hinzufügen", "", // TODO "", // TODO "", // TODO "", // TODO - "Ajouter tout à '%s'", // TODO + "Ajouter à '%s'", // TODO "", // TODO "", // TODO "", // TODO @@ -626,29 +694,12 @@ const tI18nPhrase Phrases[] = }, { "Remove", - "Entfernen", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Effacer", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, - { - "Remove from '%s'", - "Aus '%s' entfernen", + "Weg", "", // TODO "", // TODO "", // TODO "", // TODO - "Effacer de '%s'", // TODO + "Effacer", // TODO "", // TODO "", // TODO "", // TODO @@ -659,13 +710,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Remove '%s' from '%s'", - "'%s' aus '%s' entfernen", + "Clear", + "Leeren", "", // TODO "", // TODO "", // TODO "", // TODO - "Effacer '%s' de '%s'", // TODO + "Vider", // TODO "", // TODO "", // TODO "", // TODO @@ -676,13 +727,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Remove all entries from '%s'", - "Alle Einträge aus '%s' entfernen", + "Clear the collection", + "Sammlung leeren", "", // TODO "", // TODO "", // TODO "", // TODO - "Effacer tout de '%s'", // TODO + "Vider la collection", // TODO "", // TODO "", // TODO "", // TODO @@ -693,13 +744,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Remove all from '%s'", - "Alles aus '%s' entfernen", + "Remove from a collection", + "Aus einer Sammlung entfernen", "", // TODO "", // TODO "", // TODO "", // TODO - "Effacer tout de '%s'", // TODO + "Effacer d'une collection", // TODO "", // TODO "", // TODO "", // TODO @@ -964,23 +1015,6 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO }, - { - "Tree View Selection", - "Suchschema wählen", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Choisir le schéma de recherche", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, { "Title -> Album -> Track", "Titel -> Album -> Track", @@ -1032,23 +1066,6 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO }, - { - "Search", - "Suche", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Chercher", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, { "play", "spielen", @@ -1118,13 +1135,13 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Select search order", - "Suchschema wählen", + "Select an order", + "Sortierung wählen", "", // TODO "", // TODO "", // TODO "", // TODO - "Choisir le schéma de recherchage", // TODO + "Choisir un ordre", // TODO "", // TODO "", // TODO "", // TODO diff --git a/mg_actions.c b/mg_actions.c index 502b1f6..48b06f3 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -25,27 +25,163 @@ #define DEBUG #include "mg_tools.h" +#include "mg_order.h" + +static bool +IsEntry(mgActions i) +{ + return i == actEntry; +} + +class mgOsdItem : public mgAction, public cOsdItem +{ + public: + eOSState ProcessKey(eKeys key); + protected: + virtual eOSState Process(eKeys key) { return osUnknown; } +}; + +eOSState +mgOsdItem::ProcessKey(eKeys key) +{ + eOSState result = Process(key); + if (result != osUnknown) + IgnoreNextEvent = true; + return result; +} + +//! \brief used for normal data base items +class mgEntry : public mgOsdItem +{ + public: + void Notify(); + bool Enabled(mgActions on) { return IsEntry(on);} + const char *ButtonName() { return ""; } + const char *MenuName (const unsigned int idx,const string value); + virtual eOSState Process(eKeys key); + void Execute(); + eOSState Back(); +}; + +class mgDoCollEntry : public mgEntry +{ + public: + virtual eOSState Process(eKeys key); + protected: + string getTarget(); +}; + +class mgAddCollEntry : public mgDoCollEntry +{ + public: + void Execute(); +}; + +class mgRemoveCollEntry : public mgDoCollEntry +{ + public: + void Execute(); +}; + +void +mgAction::TryNotify() +{ + if (IgnoreNextEvent) + IgnoreNextEvent = false; + else + Notify(); +} + +eOSState +mgDoCollEntry::Process(eKeys key) +{ + mgMenu *n = osd ()->newmenu; + osd ()->newmenu = NULL; + eOSState result = osContinue; + switch (key) + { + case kBlue: + case kBack: + break; + case kOk: + Execute (); + break; + default: + osd ()->newmenu = n; // wrong key: stay in submenu + result = osUnknown; + break; + } + return result; +} + +string +mgDoCollEntry::getTarget() +{ + string result = cOsdItem::Text(); + if (result[0]==' ') + result.erase(0,5); + else + result.erase(0,3); + return result; +} + +void +mgAddCollEntry::Execute() +{ + string target = getTarget(); + osd()->default_collection = target; + if (target == osd()->play_collection) + if (!PlayerControl()) + collselection()->ClearCollection(target); + + osd()->Message1 ("Added %s entries",itos (osd()->moveselection->AddToCollection (target))); + osd()->CollectionChanged(target); +} + + +void +mgRemoveCollEntry::Execute() +{ + string target = getTarget(); + osd()->Message1 ("Removed %s entries",itos (osd()->moveselection->RemoveFromCollection (target))); + osd()->CollectionChanged(target); +} + void mgAction::Notify() { - m->SetHelpKeys(); + m->SetHelpKeys(Type()); } void mgAction::SetMenu(mgMenu *menu) { m = menu; + m_osd = m->osd(); } -eOSState -mgAction::Back() +void +mgAction::SetText(const char *text,bool copy) +{ + cOsdItem *c = dynamic_cast(this); + if (!c) + mgError("mgAction::SetText() on wrong type"); + c->SetText(text,copy); +} + +const char * +mgAction::Text() { - return osUnknown; + cOsdItem *c = dynamic_cast(this); + if (!c) + mgError("mgAction::Text() on wrong type"); + return c->Text(); } + bool -mgAction::Enabled() +mgAction::Enabled(mgActions on) { return true; } @@ -53,33 +189,27 @@ mgAction::Enabled() mgAction::mgAction() { m = NULL; + m_osd = NULL; + IgnoreNextEvent = false; } mgAction::~mgAction() { } - -//! \brief used for normal data base items -class mgEntry : public mgOsdItem -{ - public: - void Notify(); - bool Enabled() { return true;} - const char *ButtonName() { return ""; } - const char *MenuName (const unsigned int idx,const string value); - eOSState ProcessKey(eKeys key); - void Execute(); - eOSState Back(); -}; - class mgCommand : public mgOsdItem { public: - virtual eOSState ProcessKey(eKeys key); + bool Enabled(mgActions on); + virtual eOSState Process(eKeys key); void Execute(); }; +bool +mgCommand::Enabled(mgActions on) +{ + return IsEntry(on); +} mgSelection* mgAction::playselection () @@ -89,11 +219,11 @@ mgAction::playselection () mgMainMenu* mgAction::osd () { - return m->osd (); + return m_osd; } eOSState -mgOsdItem::Back() +mgAction::Back() { osd()->newmenu = NULL; return osContinue; @@ -106,7 +236,7 @@ mgEntry::Notify() selection()->setPosition(osd()->Current()); selection()->gotoPosition(); osd()->SaveState(); - mgAction::Notify(); + mgAction::Notify(); // only after selection is updated } const char * @@ -133,30 +263,22 @@ mgEntry::Execute() if (selection ()->enter ()) { osd()->forcerefresh = true; - osd()->m_Status->IgnoreNextEventOn = this; } else { - m->ExecuteAction(actInstantPlay); + m->ExecuteAction(actInstantPlay,Type()); } } eOSState -mgEntry::ProcessKey(eKeys key) +mgEntry::Process(eKeys key) { - if (key!=kNone) - mgDebug(3,"mgEntry(%s):ProcessKey(%d)",Text(),(int)key); - switch (key) { case kOk: Execute(); return osContinue; case kBack: - osd()->m_Status->IgnoreNextEventOn = this; return Back(); - case kBlue: - osd ()->newmenu = new mgSubmenu; - return osContinue; default: return osUnknown; } @@ -173,10 +295,8 @@ mgEntry::Back() } eOSState -mgCommand::ProcessKey(eKeys key) +mgCommand::Process(eKeys key) { - if (key!=kNone) - mgDebug(3,"mgCommand::ProcessKey(%d)",(int)key); mgMenu *parent = osd ()->Parent (); mgMenu *n = osd ()->newmenu; osd ()->newmenu = NULL; @@ -226,10 +346,12 @@ class mgExternal : public mgCommand const char *ButtonName(); const char *MenuName (const unsigned int idx,const string value); void Execute(); + bool Enabled(mgActions on) { return true; } private: cCommand * Command(); }; + class mgExternal0 : public mgExternal { }; class mgExternal1 : public mgExternal { }; class mgExternal2 : public mgExternal { }; @@ -287,7 +409,6 @@ mgExternal::Execute() cCommand *command = Command(); if (command) { - mgDebug(1,"external command:%s",command->Title()); bool confirmed = true; if (command->Confirm ()) { @@ -319,43 +440,41 @@ mgExternal::Execute() } //! \brief select search order -class mgChooseSearch : public mgCommand +class mgChooseOrder : public mgCommand { public: - bool Enabled(); - eOSState ProcessKey(eKeys key); + bool Enabled(mgActions on=mgActions(0)); + virtual eOSState Process(eKeys key); void Execute (); - const char *ButtonName() { return tr("Search"); } + const char *ButtonName() { return tr("Order"); } const char *MenuName(const unsigned int idx,const string value) - { return strdup(tr("Select search order")); } + { return strdup(tr("Select an order")); } }; bool -mgChooseSearch::Enabled() +mgChooseOrder::Enabled(mgActions on) { - bool result = mgOsdItem::Enabled(); - result &= (!selection()->isCollectionlist()); + bool result = !osd()->UsingCollection; + result &= IsEntry(on); return result; } eOSState -mgChooseSearch::ProcessKey(eKeys key) +mgChooseOrder::Process(eKeys key) { - if (key!=kNone) - mgDebug(3,"mgChooseSearch::ProcessKey(%d)",int(key)); if (key == kOk) { - osd()->Menus.pop_back(); + osd()->CloseMenu(); Execute(); return osContinue; } else - return mgCommand::ProcessKey(key); + return mgCommand::Process(key); } -void mgChooseSearch::Execute() +void mgChooseOrder::Execute() { - osd ()->newmenu = new mgTreeViewSelector; + osd ()->newmenu = new mgMenuOrders; } @@ -363,6 +482,7 @@ void mgChooseSearch::Execute() class mgToggleSelection:public mgCommand { public: + bool Enabled(mgActions on = mgActions(0)) { return true; } void Execute (); const char *ButtonName (); const char *MenuName (const unsigned int idx,const string value); @@ -403,10 +523,10 @@ mgToggleSelection::Execute () //! \brief sets the default collection selection -class mgSetDefault:public mgCommand +class mgSetDefaultCollection:public mgCommand { public: - bool Enabled(); + bool Enabled(mgActions on = mgActions(0)); void Execute (); const char *ButtonName () { @@ -415,7 +535,7 @@ class mgSetDefault:public mgCommand const char *MenuName (const unsigned int idx,const string value); }; -const char * mgSetDefault::MenuName(const unsigned int idx,const string value) +const char * mgSetDefaultCollection::MenuName(const unsigned int idx,const string value) { char *b; asprintf (&b, tr("Set default to collection '%s'"), @@ -423,20 +543,11 @@ const char * mgSetDefault::MenuName(const unsigned int idx,const string value) return b; } -class mgSetButton : public mgCommand -{ - const char *ButtonName() - { - return tr("Set"); - } - const char *MenuName(const unsigned int idx,const string value) { return strdup(""); } -}; - bool -mgSetDefault::Enabled() +mgSetDefaultCollection::Enabled(mgActions on) { - bool result = mgOsdItem::Enabled(); + bool result = IsEntry(on); result &= (!osd()->DefaultCollectionSelected()); result &= osd()->UsingCollection; result &= (selection ()->level () == 0); @@ -444,16 +555,26 @@ mgSetDefault::Enabled() } void -mgSetDefault::Execute () +mgSetDefaultCollection::Execute () { - if (!Enabled()) - mgError("mgSetDefault not enabled"); osd ()->default_collection = selection ()->getCurrentValue(); osd()->Message1 ("Default collection now is '%s'", osd ()->default_collection); } +class mgSetButton : public mgCommand +{ + public: + bool Enabled(mgActions on) { return true; } + const char *ButtonName() + { + return tr("Set"); + } + const char *MenuName(const unsigned int idx,const string value) { return strdup(""); } +}; + + //! \brief instant play class mgInstantPlay : public mgCommand { public: @@ -477,22 +598,65 @@ mgInstantPlay::Execute() osd()->PlayInstant(true); } -//! \brief add selected items to default collection +//! \brief add selected items to a collection class mgAddAllToCollection:public mgCommand { public: void Execute (); //! \brief adds the whole selection to a collection // \param collection the target collection. Default is the default collection - void ExecuteSelection (mgSelection *s,const string collection=""); const char *ButtonName () { return tr ("Add"); } const char *MenuName (const unsigned int idx,const string value); + protected: + void ExecuteMove(); }; const char * mgAddAllToCollection::MenuName (const unsigned int idx,const string value) +{ + return strdup(tr("Add all to a collection")); +} + +void +mgAddAllToCollection::Execute() +{ +// work on a copy, so we don't have to clear the cache of selection() +// which would result in an osd()->forcerefresh which could scroll. + osd()->moveselection = new mgSelection(selection()); + ExecuteMove(); +} + +void +mgAddAllToCollection::ExecuteMove() +{ + if (osd() ->Menus.size()>1) + osd ()->CloseMenu(); // TODO Gebastel... + char *b; + asprintf(&b,tr("'%s' to collection"),selection()->getCurrentValue().c_str()); + osd ()->newmenu = new mgTreeAddToCollSelector(string(b)); + osd ()->newposition = osd()->collselection()->getPosition(0); + free(b); +} + + +//! \brief add selected items to default collection +class mgAddAllToDefaultCollection:public mgCommand { + public: + void Execute (); + //! \brief adds the whole selection to the default collection + // \param collection the default collection. + void ExecuteSelection (mgSelection *s); + const char *ButtonName () + { + return tr ("Add"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +const char * +mgAddAllToDefaultCollection::MenuName (const unsigned int idx,const string value) { char *b; asprintf (&b, tr ("Add all to '%s'"), @@ -501,17 +665,19 @@ mgAddAllToCollection::MenuName (const unsigned int idx,const string value) } void -mgAddAllToCollection::Execute() +mgAddAllToDefaultCollection::Execute() { - ExecuteSelection(selection()); + mgSelection *sel = new mgSelection(selection()); + sel->select (); + ExecuteSelection(sel); + delete sel; } + void -mgAddAllToCollection::ExecuteSelection (mgSelection *s, const string collection) +mgAddAllToDefaultCollection::ExecuteSelection (mgSelection *s) { - string target = collection; - if (target.empty()) - target = osd()->default_collection; + string target = osd()->default_collection; if (target == osd()->play_collection) if (!PlayerControl()) collselection()->ClearCollection(target); @@ -529,11 +695,11 @@ mgAddAllToCollection::ExecuteSelection (mgSelection *s, const string collection) } } -//! \brief add selected items to default collection +//! \brief add selected items to a collection class mgAddThisToCollection:public mgAddAllToCollection { public: - bool Enabled(); + bool Enabled(mgActions on = mgActions(0)); void Execute (); const char *ButtonName (); const char *MenuName (const unsigned int idx,const string value); @@ -545,66 +711,127 @@ mgAddThisToCollection::Execute () { // work on a copy, so we don't have to clear the cache of selection() // which would result in an osd()->forcerefresh which could scroll. - mgSelection *s = new mgSelection(selection()); - s->select (); - mgAddAllToCollection::ExecuteSelection(s); - delete s; + osd()->moveselection = new mgSelection(selection()); + osd()->moveselection->select (); + mgAddAllToCollection::ExecuteMove(); } -class mgSearchNew : public mgCommand +const char * +mgAddThisToCollection::ButtonName () { - virtual void NewSearch() = 0; - void Execute(); -}; + return tr("Add"); +} -void -mgSearchNew::Execute() +bool +mgAddThisToCollection::Enabled(mgActions on) { - mgSelection *oldsel = osd()->selection(); - map* oldkeys = oldsel->UsedKeyValues(); - osd()->UseNormalSelection(); // Default for all searches - NewSearch(); - mgSelection *newsel = osd()->selection(); - for (unsigned int idx = 0; idx < newsel->size(); idx++) - { - string new_keyname = newsel->getKeyChoice(idx); - if (oldkeys->count(new_keyname)==0) break; - newsel->select((*oldkeys)[new_keyname]); - } - newsel->leave(); - delete oldkeys; + return IsEntry(on); } -class mgSearchCollItem : public mgSearchNew +const char * +mgAddThisToCollection::MenuName (const unsigned int idx,const string value) { - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Collection -> Item")); } - void NewSearch() { osd ()->UseCollectionSelection (); } + return strdup(tr("Add to a collection")); +} + +//! \brief add selected items to default collection +class mgAddThisToDefaultCollection:public mgAddAllToDefaultCollection +{ + public: + bool Enabled(mgActions on = mgActions(0)); + void Execute (); + const char *ButtonName (); + const char *MenuName (const unsigned int idx,const string value); }; +void +mgAddThisToDefaultCollection::Execute () +{ +// work on a copy, so we don't have to clear the cache of selection() +// which would result in an osd()->forcerefresh which could scroll. + mgSelection *sel = new mgSelection(selection()); + sel->select (); + mgAddAllToDefaultCollection::ExecuteSelection(sel); + delete sel; +} + const char * -mgAddThisToCollection::ButtonName () +mgAddThisToDefaultCollection::ButtonName () { return tr("Add"); } bool -mgAddThisToCollection::Enabled() +mgAddThisToDefaultCollection::Enabled(mgActions on) { - bool result = mgOsdItem::Enabled(); + bool result = IsEntry(on); result &= (!osd()->DefaultCollectionSelected()); return result; } const char * -mgAddThisToCollection::MenuName (const unsigned int idx,const string value) +mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const string value) { char *b; - asprintf (&b, tr ("Add to '%s'"), - osd ()->default_collection.c_str ()); + asprintf (&b, tr ("Add to '%s'"), osd ()->default_collection.c_str ()); return b; } +class mgOrderNew : public mgCommand +{ + protected: + virtual void NewOrder() = 0; + public: + bool Enabled(mgActions on = mgActions(0)) { return true; } + void Execute(); +}; + +void +mgOrderNew::Execute() +{ + mgSelection *s = osd()->selection(); + map* oldkeys = s->UsedKeyValues(); + mgContentItem o; + s->select(); + if (s->getNumTracks()==1) + { + o = s->getTrack(0); + } + osd()->UseNormalSelection(); // Default for all orders + NewOrder(); + mgSelection *newsel = osd()->selection(); + newsel->leave(0); + for (unsigned int idx = 0; idx < newsel->ordersize(); idx++) + { + string selval = ""; + mgKeyTypes new_kt = newsel->getKeyType(idx); + if (oldkeys->count(new_kt)>0) + selval=(*oldkeys)[new_kt]; + else if (o.getId()>=0) + selval=o.getKeyValue(new_kt); + if (!selval.empty()) + { + if (idxordersize()-1) + newsel->select(selval); + else + newsel->setPosition(selval); + continue; + } + break; + } + delete oldkeys; + osd()->newposition = newsel->getPosition(newsel->level()); + newsel->clearCache(); +} + +class mgOrderCollItem : public mgOrderNew +{ + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Collection -> Item")); } + void NewOrder() { osd ()->UseCollectionSelection (); } +}; + + //! \brief remove selected items from default collection class mgRemoveAllFromCollection:public mgCommand { @@ -620,25 +847,49 @@ class mgRemoveAllFromCollection:public mgCommand void mgRemoveAllFromCollection::Execute () { - osd()->Message1 ("Removed %s entries", - itos (collselection ()->RemoveFromCollection ( - osd ()->default_collection.c_str ()))); - if (osd()->default_collection == osd()->play_collection) - { - mgPlayerControl *c = PlayerControl(); - if (c) - c->ReloadPlaylist(); - } + if (osd() ->Menus.size()>1) + osd ()->CloseMenu(); // TODO Gebastel... + char *b; + asprintf(&b,tr("Remove '%s' from collection"),osd()->moveselection->getListname().c_str()); + osd ()->newmenu = new mgTreeRemoveFromCollSelector(string(b)); + free(b); } - const char * mgRemoveAllFromCollection::MenuName (const unsigned int idx,const string value) { - char *b; - asprintf (&b, tr ("Remove all from '%s'"), - osd ()->default_collection.c_str ()); - return b; + return strdup(tr("Remove all from a collection")); +} + +class mgClearCollection : public mgCommand +{ + public: + bool Enabled(mgActions on = mgActions(0)); + void Execute (); + const char *ButtonName () + { + return tr ("Clear"); + } + const char *MenuName (const unsigned int idx,const string value); +}; + +const char * +mgClearCollection::MenuName (const unsigned int idx,const string value) +{ + return strdup(tr("Clear the collection")); +} + +bool +mgClearCollection::Enabled(mgActions on) +{ + return selection()->isCollectionlist(); +} + +void +mgClearCollection::Execute() +{ + if (Interface->Confirm(tr("Clear the collection?"))) + selection()->ClearCollection(selection()->getCurrentValue()); } //! \brief remove selected items from default collection @@ -653,42 +904,41 @@ class mgRemoveThisFromCollection:public mgRemoveAllFromCollection const char *MenuName (const unsigned int idx,const string value); }; + void mgRemoveThisFromCollection::Execute () { - if (osd()->DefaultCollectionSelected()) - { - selection()->ClearCollection(selection()->getCurrentValue().c_str()); - osd()->Message ("Removed all entries"); - } - else - { - selection ()->select (); - osd()->Message1 ("Removed %s entries", - itos (selection ()->RemoveFromCollection (osd ()->default_collection.c_str ()))); - selection ()->leave (); - if (osd()->DefaultCollectionEntered()) - { - selection()->clearCache(); - osd()->forcerefresh = true; - } - } +// work on a copy, so we don't have to clear the cache of selection() +// which would result in an osd()->forcerefresh which could scroll. + osd()->moveselection = new mgSelection(selection()); + osd()->moveselection->select (); + mgRemoveAllFromCollection::Execute(); } const char * mgRemoveThisFromCollection::MenuName (const unsigned int idx,const string value) { - char *b; - string this_sel = selection()->getCurrentValue(); - if (osd()->DefaultCollectionSelected()) - asprintf(&b,tr("Remove all entries from '%s'"),this_sel.c_str()); - else - asprintf (&b, tr ("Remove from '%s'"), - trim(osd ()->default_collection).c_str ()); - return b; + return strdup(tr("Remove from a collection")); } +/*! \brief create collection directly in the collection list + */ +class mgCreateCollection: public mgAction, public cMenuEditStrItem +{ + public: + mgCreateCollection(); + void Notify(); + bool Enabled(mgActions on = mgActions(0)); + eOSState ProcessKey(eKeys key); + void Execute (); + const char *MenuName (const unsigned int idx=0,const string value=""); + private: + bool Editing(); + char value[30]; +}; + + bool mgCreateCollection::Editing() { @@ -699,8 +949,8 @@ mgCreateCollection::Editing() void mgCreateCollection::Notify() { - if (!Editing()) - osd()->SetHelpKeys(NULL,NULL,NULL,NULL); + if (!Editing()) + m->SetHelpKeys(); } const char* @@ -709,7 +959,7 @@ mgCreateCollection::MenuName(const unsigned int idx,const string value) return strdup(tr ("Create collection")); } -mgCreateCollection::mgCreateCollection() : cMenuEditStrItem(MenuName(),value,30,tr(FileNameChars)), mgAction() +mgCreateCollection::mgCreateCollection() : mgAction(), cMenuEditStrItem(MenuName(),value,30,tr(FileNameChars)) { value[0]=0; } @@ -729,11 +979,9 @@ mgCreateCollection::ProcessKey(eKeys key) } bool -mgCreateCollection::Enabled() +mgCreateCollection::Enabled(mgActions on) { - bool result = mgAction::Enabled(); - result &= selection()->isCollectionlist(); - return result; + return selection()->isCollectionlist(); } void @@ -747,6 +995,11 @@ mgCreateCollection::Execute () mgDebug(1,"created collection %s",name.c_str()); osd()->default_collection = name; selection ()->clearCache(); + if (selection()->isCollectionlist()) + { +// selection ()->setPosition(selection()->id(keyCollection,name)); + selection ()->setPosition(name); + } osd()->forcerefresh = true; } else @@ -758,7 +1011,7 @@ class mgDeleteCollection:public mgCommand { public: void Execute (); - bool Enabled(); + bool Enabled(mgActions on = mgActions(0)); const char *ButtonName () { return tr ("Delete"); @@ -767,11 +1020,10 @@ class mgDeleteCollection:public mgCommand }; bool -mgDeleteCollection::Enabled() +mgDeleteCollection::Enabled(mgActions on) { - bool result = mgOsdItem::Enabled(); + bool result = IsEntry(on); result &= selection()->isCollectionlist(); - result &= (!osd()->DefaultCollectionSelected()); if (result) { string name = selection ()->getCurrentValue(); @@ -782,13 +1034,14 @@ mgDeleteCollection::Enabled() const char* mgDeleteCollection::MenuName(const unsigned int idx,const string value) { - return strdup(tr("Delete collection")); + return strdup(tr("Delete the collection")); } void mgDeleteCollection::Execute () { + if (!Interface->Confirm(tr("Delete the collection?"))) return; string name = selection ()->getCurrentValue(); if (selection ()->DeleteCollection (name)) { @@ -826,87 +1079,93 @@ mgExportTracklist::Execute () osd()->Message1 ("written to %s", m3u_file); } -class mgSearchArtistAlbumTitle : public mgSearchNew +class mgOrderArtistAlbumTitle : public mgOrderNew { - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Album -> Title")); } - void NewSearch() + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Album -> Track")); } + void NewOrder() { - selection ()->setKey (0, tr ("Artist")); - selection ()->setKey (1, tr ("Album")); - selection ()->setKey (2, tr ("Title")); + selection ()->setKey (0, keyArtist); + selection ()->setKey (1, keyAlbum); + selection ()->setKey (2, keyTrack); } }; -class mgSearchAlbumTitle : public mgSearchNew +class mgOrderAlbumTitle : public mgOrderNew { - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Album -> Title")); } - void NewSearch() + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Album -> Track")); } + void NewOrder() { - selection ()->setKey (0, tr ("Album")); - selection ()->setKey (1, tr ("Title")); + selection ()->setKey (0, keyAlbum); + selection ()->setKey (1, keyTrack); } }; -class mgSearchGenreYearTitle : public mgSearchNew +class mgOrderGenreYearTitle : public mgOrderNew { - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Year -> Title")); } - void NewSearch() + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Year -> Track")); } + void NewOrder() { - selection ()->setKey (0, tr ("Genre 1")); - selection ()->setKey (1, tr ("Year")); - selection ()->setKey (2, tr ("Title")); + selection ()->setKey (0, keyGenre1); + selection ()->setKey (1, keyYear); + selection ()->setKey (2, keyTrack); } }; -class mgSearchGenreArtistAlbumTitle : public mgSearchNew +class mgOrderGenreArtistAlbumTitle : public mgOrderNew { - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Artist ->Album -> Title")); } - void NewSearch() + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Artist ->Album -> Track")); } + void NewOrder() { - selection ()->setKey (0, tr ("Genre 1")); - selection ()->setKey (1, tr ("Artist")); - selection ()->setKey (2, tr ("Album")); - selection ()->setKey (3, tr ("Title")); + selection ()->setKey (0, keyGenre1); + selection ()->setKey (1, keyArtist); + selection ()->setKey (2, keyAlbum); + selection ()->setKey (3, keyTrack); } }; -class mgSearchArtistTitle : public mgSearchNew +class mgOrderArtistTitle : public mgOrderNew { - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Title")); } - void NewSearch() + const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Track")); } + void NewOrder() { - selection ()->setKey (0, tr ("Artist")); - selection ()->setKey (1, tr ("Title")); + selection ()->setKey (0, keyArtist); + selection ()->setKey (1, keyTrack); } }; mgActions -mgOsdItem::Type() +mgAction::Type() { const type_info& t = typeid(*this); - if (t == typeid(mgChooseSearch)) return actChooseSearch; + if (t == typeid(mgChooseOrder)) return actChooseOrder; if (t == typeid(mgToggleSelection)) return actToggleSelection; - if (t == typeid(mgSetDefault)) return actSetDefault; + if (t == typeid(mgClearCollection)) return actClearCollection; + if (t == typeid(mgCreateCollection)) return actCreateCollection; if (t == typeid(mgInstantPlay)) return actInstantPlay; if (t == typeid(mgAddAllToCollection)) return actAddAllToCollection; + if (t == typeid(mgAddAllToDefaultCollection)) return actAddAllToDefaultCollection; if (t == typeid(mgRemoveAllFromCollection)) return actRemoveAllFromCollection; if (t == typeid(mgDeleteCollection)) return actDeleteCollection; if (t == typeid(mgExportTracklist)) return actExportTracklist; + if (t == typeid(mgAddCollEntry)) return actAddCollEntry; + if (t == typeid(mgRemoveCollEntry)) return actRemoveCollEntry; if (t == typeid(mgAddThisToCollection)) return actAddThisToCollection; + if (t == typeid(mgAddThisToDefaultCollection)) return actAddThisToDefaultCollection; if (t == typeid(mgRemoveThisFromCollection)) return actRemoveThisFromCollection; if (t == typeid(mgEntry)) return actEntry; if (t == typeid(mgSetButton)) return actSetButton; - if (t == typeid(mgSearchCollItem)) return actSearchCollItem; - if (t == typeid(mgSearchArtistAlbumTitle)) return actSearchArtistAlbumTitle; - if (t == typeid(mgSearchArtistTitle)) return actSearchArtistTitle; - if (t == typeid(mgSearchAlbumTitle)) return actSearchAlbumTitle; - if (t == typeid(mgSearchGenreYearTitle)) return actSearchGenreYearTitle; - if (t == typeid(mgSearchGenreArtistAlbumTitle)) return actSearchArtistAlbumTitle; + if (t == typeid(mgOrderCollItem)) return ActOrderCollItem; + if (t == typeid(mgOrderArtistAlbumTitle)) return ActOrderArtistAlbumTitle; + if (t == typeid(mgOrderArtistTitle)) return ActOrderArtistTitle; + if (t == typeid(mgOrderAlbumTitle)) return ActOrderAlbumTitle; + if (t == typeid(mgOrderGenreYearTitle)) return ActOrderGenreYearTitle; + if (t == typeid(mgOrderGenreArtistAlbumTitle)) return ActOrderArtistAlbumTitle; + if (t == typeid(mgSetDefaultCollection)) return actSetDefaultCollection; if (t == typeid(mgExternal0)) return actExternal0; if (t == typeid(mgExternal1)) return actExternal1; if (t == typeid(mgExternal2)) return actExternal2; @@ -927,37 +1186,40 @@ mgOsdItem::Type() if (t == typeid(mgExternal17)) return actExternal17; if (t == typeid(mgExternal18)) return actExternal18; if (t == typeid(mgExternal19)) return actExternal19; - mgError("Unknown mgOsdItem %s",t.name()); + mgError("Unknown mgAction %s",t.name()); return mgActions(0); } -mgOsdItem* +mgAction* actGenerate(const mgActions action) { - mgOsdItem * result = NULL; + mgAction * result = NULL; switch (action) { - case actChooseSearch: result = new mgChooseSearch;break; + case actChooseOrder: result = new mgChooseOrder;break; case actToggleSelection: result = new mgToggleSelection;break; - case actSetDefault: result = new mgSetDefault;break; - case actUnused1: break; + case actClearCollection: result = new mgClearCollection;break; + case actCreateCollection: result = new mgCreateCollection;break; case actInstantPlay: result = new mgInstantPlay;break; case actAddAllToCollection: result = new mgAddAllToCollection;break; + case actAddAllToDefaultCollection: result = new mgAddAllToDefaultCollection;break; case actRemoveAllFromCollection:result = new mgRemoveAllFromCollection;break; case actDeleteCollection: result = new mgDeleteCollection;break; case actExportTracklist: result = new mgExportTracklist;break; - case actUnused2: break; - case actUnused3: break; + case actAddCollEntry: result = new mgAddCollEntry;break; + case actRemoveCollEntry: result = new mgRemoveCollEntry;break; case actAddThisToCollection: result = new mgAddThisToCollection;break; + case actAddThisToDefaultCollection: result = new mgAddThisToDefaultCollection;break; case actRemoveThisFromCollection: result = new mgRemoveThisFromCollection;break; case actEntry: result = new mgEntry;break; case actSetButton: result = new mgSetButton;break; - case actSearchCollItem: result = new mgSearchCollItem;break; - case actSearchArtistAlbumTitle: result = new mgSearchArtistAlbumTitle;break; - case actSearchArtistTitle: result = new mgSearchArtistTitle;break; - case actSearchAlbumTitle: result = new mgSearchAlbumTitle;break; - case actSearchGenreYearTitle: result = new mgSearchGenreYearTitle;break; - case actSearchGenreArtistAlbumTitle: result = new mgSearchGenreArtistAlbumTitle;break; + case ActOrderCollItem: result = new mgOrderCollItem;break; + case ActOrderArtistAlbumTitle: result = new mgOrderArtistAlbumTitle;break; + case ActOrderArtistTitle: result = new mgOrderArtistTitle;break; + case ActOrderAlbumTitle: result = new mgOrderAlbumTitle;break; + case ActOrderGenreYearTitle: result = new mgOrderGenreYearTitle;break; + case ActOrderGenreArtistAlbumTitle: result = new mgOrderGenreArtistAlbumTitle;break; + case actSetDefaultCollection: result = new mgSetDefaultCollection;break; case actExternal0: result = new mgExternal0;break; case actExternal1: result = new mgExternal1;break; case actExternal2: result = new mgExternal2;break; diff --git a/mg_actions.h b/mg_actions.h index ecb5913..3712191 100644 --- a/mg_actions.h +++ b/mg_actions.h @@ -24,35 +24,37 @@ using namespace std; class mgSelection; class mgMenu; class mgMainMenu; -class mgOsdItem; /*! \brief defines all actions which can appear in command submenus. * Since these values are saved in muggle.state, new actions should * always be appended. The order does not matter. Value 0 means undefined. */ enum mgActions { - actChooseSearch=1, //!< show a menu with all possible search schemas + actChooseOrder=1, //!< show a menu with all possible orders actToggleSelection, //!< toggle between search and collection view - actSetDefault, //!< set default collection - actUnused1, + actClearCollection, //!< clear a collection, + actCreateCollection, actInstantPlay, //!< instant play actAddAllToCollection, //!< add all items of OSD list to default collection actRemoveAllFromCollection,//!< remove from default collection actDeleteCollection, //!< delete collection actExportTracklist, //!< export track list into a *.m3u file - actUnused2, - actUnused3, + actAddCollEntry, + actRemoveCollEntry, actAddThisToCollection, //!< add selected item to default collection actRemoveThisFromCollection, //!< remove selected item from default collection actEntry, //!< used for normal data base items actSetButton, //!< connect a button with an action - actSearchCollItem, //!< search in the collections - actSearchArtistAlbumTitle, //!< search by Artist/Album/Title - actSearchArtistTitle, //!< search by Artist/Title - actSearchAlbumTitle, //!< search by Album/Title - actSearchGenreYearTitle, //!< search by Genre1/Year/Title - actSearchGenreArtistAlbumTitle, //!< search by Genre1/Artist/Album/Title - actExternal0, //!< used for external commands, the number is the entry number in the .conf file starting with line 0 + ActOrderCollItem, //!< order by collections + ActOrderArtistAlbumTitle, //!< order by Artist/Album/Title + ActOrderArtistTitle, //!< order by Artist/Title + ActOrderAlbumTitle, //!< order by Album/Title + ActOrderGenreYearTitle, //!< order by Genre1/Year/Title + ActOrderGenreArtistAlbumTitle, //!< order by Genre1/Artist/Album/Title + actAddAllToDefaultCollection, + actAddThisToDefaultCollection, + actSetDefaultCollection, + actExternal0 = 1000, //!< used for external commands, the number is the entry number in the .conf file starting with line 0 actExternal1, //!< used for external commands, the number is the entry number in the .conf file actExternal2, //!< used for external commands, the number is the entry number in the .conf file actExternal3, //!< used for external commands, the number is the entry number in the .conf file @@ -83,7 +85,7 @@ class mgAction public: //! \brief if true, can be displayed - virtual bool Enabled(); + virtual bool Enabled(mgActions on = mgActions(0)); //! \brief the action to be executed virtual void Execute () = 0; @@ -101,7 +103,7 @@ class mgAction /*! \brief the name for a menu entry. If empty, no button will be able * to execute this. The returned C string must be freeable at any time. - * \todo use virtual Set() instead? Compare definition of mgMenu::AddAction + * \param value a string that can be used for building the menu name. */ virtual const char *MenuName (const unsigned int idx=0,const string value="") = 0; @@ -116,8 +118,21 @@ class mgAction //! \brief what to do when mgStatus::OsdCurrentItem is called //for this one - virtual void Notify(); + mgActions Type(); + void SetText(const char *text,bool copy=true); + const char *Text(); + + void TryNotify(); + + /*! \brief vdr calls OsdCurrentItem more often than we + * want. This tells mgStatus to ignore the next call + * for a specific item. + * \todo is this behaviour intended or a bug in vdr + * or in muggle ? + */ + bool IgnoreNextEvent; protected: + //! \brief returns the OSD owning the menu owning this item mgMainMenu* osd (); @@ -132,38 +147,14 @@ class mgAction //! \brief returns the playselection mgSelection* playselection (); -}; - -//! \brief a generic class for all actions that can be used in the -// command submenu -class mgOsdItem : public mgAction, public cOsdItem -{ - public: - //! \brief the enum mgActions value of this class - virtual mgActions Type(); - //! \brief to be executed for kBack. We might want to call this - // directly, so expose it. - virtual eOSState Back(); + virtual void Notify(); + private: + mgMainMenu *m_osd; }; -//! \brief generate a mgOsdItem for action -mgOsdItem* actGenerate(const mgActions action); +//! \brief generate an mgAction for action +mgAction* actGenerate(const mgActions action); -/*! \brief create collection directly in the collection list - */ -class mgCreateCollection:public cMenuEditStrItem, public mgAction -{ - public: - mgCreateCollection(); - void Notify(); - bool Enabled(); - eOSState ProcessKey(eKeys key); - void Execute (); - const char *MenuName (const unsigned int idx=0,const string value=""); - private: - bool Editing(); - char value[30]; -}; #endif diff --git a/mg_database.h b/mg_database.h index 54baef1..d974a33 100644 --- a/mg_database.h +++ b/mg_database.h @@ -17,7 +17,6 @@ /*! * \brief an abstract database class * - * \todo Currently unused. This class could abstract database connection and query handling, but this will be tedious as an abstract representation of results would be needed. */ class mgDB { diff --git a/mg_db.c b/mg_db.c index a310d51..9fa656d 100644 --- a/mg_db.c +++ b/mg_db.c @@ -13,20 +13,9 @@ #include "vdr_setup.h" #include "mg_tools.h" -/*! \brief a RAM copy of the genres table for faster access - * and in order to avoid having to use genre as genre 1 etc. - */ -static map < string, string > genres; - -//! \brief adds string n to string s, using string sep to separate them -static string addsep (string & s, string sep, string n); - //! \brief adds string n to string s, using a comma to separate them static string comma (string & s, string n); -//! \brief adds string n to string s, using AND to separate them -static string und (string & s, string n); - /*! \brief returns a random integer within some range */ unsigned int @@ -38,16 +27,6 @@ randrange (const unsigned int high) } -//! \brief adds n1=n2 to string s, using AND to separate several such items -static string -undequal (string & s, string n1, string op, string n2) -{ - if (n1.compare (n2) || op != "=") - return addsep (s, " AND ", n1 + op + n2); - else - return s; -} - static string comma (string & s, string n) { @@ -55,46 +34,6 @@ comma (string & s, string n) } -static string -und (string & s, string n) -{ - return addsep (s, " AND ", n); -} - - -static string -commalist (string prefix,list < string > v,bool sort=true) -{ - string result = ""; - if (sort) v.sort (); - v.unique (); - for (list < string >::iterator it = v.begin (); it != v.end (); it++) - { - comma (result, *it); - } - if (!result.empty()) - result.insert(0," "+prefix+" "); - return result; -} - -//! \brief converts long to string -string -itos (int i) -{ - stringstream s; - s << i; - return s.str (); -} - -//! \brief convert long to string -string -ltos (long l) -{ - stringstream s; - s << l; - return s.str (); -} - static string zerostring; size_t @@ -122,75 +61,8 @@ mgSelection::mgSelStrings::setOwner(mgSelection* sel) m_sel = sel; } -mgValmap::mgValmap(const char *key) { - m_key = key; -} - -void mgValmap::Read(FILE *f) { - char *line=(char*)malloc(1000); - char *prefix=(char*)malloc(strlen(m_key)+2); - strcpy(prefix,m_key); - strcat(prefix,"."); - rewind(f); - while (fgets(line,1000,f)) { - if (strncmp(line,prefix,strlen(prefix))) continue; - if (line[strlen(line)-1]=='\n') - line[strlen(line)-1]=0; - char *name = line + strlen(prefix); - char *eq = strchr(name,'='); - if (!eq) continue; - *(eq-1)=0; - char *value = eq + 2; - (*this)[string(name)]=string(value); - } - free(prefix); - free(line); -} - -void mgValmap::Write(FILE *f) { - for (mgValmap::const_iterator it=begin();it!=end();++it) { - char b[1000]; - sprintf(b,"%s.%s = %s\n", - m_key,it->first.c_str(), - it->second.c_str()); - fputs(b,f); - } -} - -void mgValmap::put(const char* name, const string value) { - if (value.empty() || value==EMPTY) return; - (*this)[string(name)] = value; -} - -void mgValmap::put(const char* name, const char* value) { - if (!value || *value==0) return; - (*this)[string(name)] = value; -} - -void mgValmap::put(const char* name, const int value) { - put(name,ltos(value)); -} - -void mgValmap::put(const char* name, const unsigned int value) { - put(name,ltos(value)); -} - -void mgValmap::put(const char* name, const long value) { - put(name,ltos(value)); -} - -void mgValmap::put(const char* name, const bool value) { - string s; - if (value) - s = "true"; - else - s = "false"; - put(name,s); -} - - void -mgSelection::clearCache() +mgSelection::clearCache() const { m_current_values = ""; m_current_tracks = ""; @@ -202,25 +74,29 @@ mgSelection::getCurrentValue() return values[gotoPosition()]; } -MYSQL_RES * -mgSelection::exec_sql (string query) +string +mgSelection::getKeyValue(const unsigned int level) const { - mgDebug(3,query.c_str()); - if (!m_db) return NULL; - if (mysql_query (m_db, (query + ';').c_str ())) - { - mgError("SQL Error in %s: %s",query.c_str(),mysql_error (m_db)); - return NULL; - } - return mysql_store_result (m_db); + return order.getKeyValue(level); +} + +mgKeyTypes +mgSelection::getKeyType (const unsigned int level) const +{ + return order.getKeyType(level); } +MYSQL_RES* +mgSelection::exec_sql(string query) const +{ + return ::exec_sql(m_db, query); +} /*! \brief executes a query and returns the first columnu of the * first row. * \param query the SQL query string to be executed */ -string mgSelection::get_col0 (string query) +string mgSelection::get_col0 (string query) const { MYSQL_RES * sql_result = exec_sql (query); if (!sql_result) @@ -239,87 +115,12 @@ string mgSelection::get_col0 (string query) unsigned long -mgSelection::exec_count (string query) +mgSelection::exec_count (string query) const { return atol (get_col0 (query).c_str ()); } -/*! \brief extract table names. All words preceding a . are supposed to be - * table names. Table names are supposed to only contain letters. That is - * sufficient for GiantDisc - * \par spar the SQL command - * \return a list of table names - * \todo is this thread safe? - */ -static list < string > -tables (const string spar) -{ - list < string > result; - string s = spar; - string::size_type dot; - while ((dot = s.rfind ('.')) != string::npos) - { - s.erase (dot, string::npos); // cut the rest - string::size_type lword = s.size (); - while (strchr - ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - s[lword - 1])) - { - lword--; - if (lword == 0) - break; - } - result.push_back (s.substr (lword)); - } - return result; -} - - -/*! \brief if the SQL command works on only 1 table, remove all table - * qualifiers. Example: SELECT tracks.title FROM tracks becomes SELECT title - * FROM tracks - * \param spar the sql command. It will be edited in place - * \return the new sql command is also returned - */ -static string -optimize (string & spar) -{ - string s = spar; - string::size_type tmp = s.find (" WHERE"); - if (tmp != string::npos) - s.erase (tmp, 9999); - tmp = s.find (" ORDER"); - if (tmp != string::npos) - s.erase (tmp, 9999); - string::size_type frompos = s.find (" FROM ") + 6; - if (s.substr (frompos).find (",") == string::npos) - { - string from = s.substr (frompos, 999) + '.'; - string::size_type track; - while ((track = spar.find (from)) != string::npos) - { - spar.erase (track, from.size ()); - } - } - return spar; -} - -string keyfield::sql_string(const string s) const -{ - return selection->sql_string(s); -} - -keyfield::keyfield (const string choice) -{ - if (choice.empty()) - mgError("keyfield::keyfield: choice is empty"); - m_choice = choice; - m_id = EMPTY; - m_value = ""; - m_filter = ""; -} - string mgSelection::sql_string (const string s) const @@ -331,140 +132,96 @@ mgSelection::sql_string (const string s) const return result; } +string +mgContentItem::getKeyValue(mgKeyTypes kt) +{ + if (m_id<0) + return ""; + switch (kt) { + case keyGenre1: return getGenre1(); + case keyGenre2: return getGenre2(); + case keyArtist: return getArtist(); + case keyAlbum: return getAlbum(); + case keyYear: return string(ltos(getYear())); + case keyDecade: return string(ltos(int((getYear() % 100) / 10) * 10)); + case keyTitle: return getTitle(); + case keyTrack: return getTitle(); + default: return ""; + } +} -string keyfield::restrict (string & result) const +mgContentItem * +mgSelection::getTrack (unsigned int position) { - string id = ""; - string op; - if (m_id == EMPTY) - return result; - if (m_id == "NULL") - { - op = " is "; - id = "NULL"; - } - else - { - op = "="; - id = sql_string (m_id); - } - if (idfield () == valuefield ()) - undequal (result, idfield (), op, id); - else - undequal (result, basefield (), op, id); - return result; + if (position >= getNumTracks ()) + return NULL; + return &(m_tracks[position]); } -string keyfield::join () const +string mgContentItem::getGenre () const { - string result; - if (need_join ()) - return undequal (result, basefield (), "=", idfield ()); - else - return ""; + return m_genre1; } -bool keyfield::need_join () const +string mgContentItem::getGenre1 () const { - return lookup; + return m_genre1; } -void -keyfield::set(const string id,const string value) -{ - if (id != EMPTY) - if (m_id == id && m_value == value) return; - m_id = id; - m_value = value; - if (selection) - selection->clearCache(); -} -void -keyfield::writeAt (ostream & s) const +string mgContentItem::getGenre2 () const { - if (m_id == EMPTY) - s << choice () << '/'; - else - s << choice () << '=' << m_id; + return m_genre2; } -const char * -toCString (mgSelection::ShuffleMode m) +string mgContentItem::getBitrate () const { - static const char *modes[] = - { - "SM_NONE", "SM_NORMAL", "SM_PARTY" - }; - return modes[m]; + return m_bitrate; } -string -toString (mgSelection::ShuffleMode m) +string mgContentItem::getImageFile () const { - return toCString (m); + return "Name of Imagefile"; } -//! \brief dump keyfield -ostream & operator<< (ostream & s, keyfield & k) + +string mgContentItem::getAlbum () const { - k.writeAt (s); - return s; + return m_albumtitle; } -string -addsep (string & s, string sep, string n) +int mgContentItem::getYear () const { - if (!n.empty ()) - { - if (!s.empty ()) - s.append (sep); - s.append (n); - } - return s; + return m_year; } -mgContentItem * -mgSelection::getTrack (unsigned int position) +int mgContentItem::getRating () const { - if (position >= getNumTracks ()) - return NULL; - return &(m_tracks[position]); + return m_rating; } -void -mgSelection::loadgenres () +int mgContentItem::getDuration () const { - MYSQL_RES *rows = exec_sql ("select id,genre from genre;"); - if (rows) - { - MYSQL_ROW row; - while ((row = mysql_fetch_row (rows)) != NULL) - { - if (row[0] && row[1]) - genres[row[0]] = row[1]; - } - mysql_free_result (rows); - } + return m_duration; } -string mgContentItem::getGenre1 () +int mgContentItem::getSampleRate () const { - return genres[m_genre1]; + return m_samplerate; } -string mgContentItem::getGenre2 () +int mgContentItem::getChannels () const { - return genres[m_genre2]; + return m_channels; } @@ -476,13 +233,13 @@ mgSelection::ShuffleMode mgSelection::toggleShuffleMode () { case SM_NONE: { - long id = m_tracks[m_tracks_position].getId (); + long id = m_tracks[getTrackPosition()].getId (); m_current_tracks = ""; // force a reload tracksize = getNumTracks(); // getNumTracks also reloads for (unsigned int i = 0; i < tracksize; i++) if (m_tracks[i].getId () == id) { - m_tracks_position = i; + setTrackPosition(i); break; } } @@ -491,10 +248,10 @@ mgSelection::ShuffleMode mgSelection::toggleShuffleMode () case SM_NORMAL: { // play all, beginning with current track: - mgContentItem tmp = m_tracks[m_tracks_position]; - m_tracks[m_tracks_position]=m_tracks[0]; + mgContentItem tmp = m_tracks[getTrackPosition()]; + m_tracks[getTrackPosition()]=m_tracks[0]; m_tracks[0]=tmp; - m_tracks_position=0; + setTrackPosition(0); // randomize all other tracks for (unsigned int i = 1; i < tracksize; i++) { @@ -551,9 +308,9 @@ mgSelection::AddToCollection (const string Name) string sql = ""; for (unsigned int i = 0; i < tracksize; i++) { - string value = "(" + listid + "," + ltos (high + 1 + i) + "," + + string item = "(" + listid + "," + ltos (high + 1 + i) + "," + ltos (m_tracks[i].getId ()) + ")"; - comma(sql, value); + comma(sql, item); if ((i%100)==99) { exec_sql (sql_prefix+sql); @@ -570,14 +327,8 @@ unsigned int mgSelection::RemoveFromCollection (const string Name) { if (!m_db) return 0; - string listid = get_col0 - ("SELECT id FROM playlist WHERE title=" + sql_string (Name)); - where(); - m_fromtables.push_back("playlistitem"); - m_fromtables.push_back("playlist"); - string sql = "DELETE playlistitem" + commalist("FROM",m_fromtables) - + m_where + " AND tracks.id = playlistitem.trackid " - " AND playlistitem.playlist = " + listid; + mgParts p = order.Parts(m_level,false); + string sql = p.sql_delete_from_collection(id(keyCollection,Name)); exec_sql (sql); unsigned int removed = mysql_affected_rows (m_db); if (inCollection(Name)) clearCache (); @@ -588,6 +339,7 @@ mgSelection::RemoveFromCollection (const string Name) bool mgSelection::DeleteCollection (const string Name) { if (!m_db) return false; + ClearCollection(Name); exec_sql ("DELETE FROM playlist WHERE title=" + sql_string (Name)); if (isCollectionlist()) clearCache (); return (mysql_affected_rows (m_db) == 1); @@ -597,9 +349,8 @@ bool mgSelection::DeleteCollection (const string Name) void mgSelection::ClearCollection (const string Name) { if (!m_db) return; - exec_sql ("DELETE playlistitem FROM playlist,playlistitem " - "WHERE playlistitem.playlist=playlist.id " - " AND playlist.title=" + sql_string (Name)); + string listid = id(keyCollection,Name); + exec_sql ("DELETE FROM playlistitem WHERE playlist="+listid); if (inCollection(Name)) clearCache (); } @@ -628,10 +379,10 @@ string mgSelection::exportM3U () unsigned int tracksize = getNumTracks (); for (unsigned i = 0; i < tracksize; i++) { - mgContentItem* t = &m_tracks[i]; - fprintf (listfile, "#EXTINF:%d,%s\n", t->getDuration (), - t->getTitle ().c_str ()); - fprintf (listfile, "%s", t->getSourceFile ().c_str ()); + mgContentItem& t = m_tracks[i]; + fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (), + t.getTitle ().c_str ()); + fprintf (listfile, "%s", t.getSourceFile ().c_str ()); } fclose (listfile); return fn; @@ -640,7 +391,7 @@ string mgSelection::exportM3U () bool mgSelection::empty() { - if (m_level>= keys.size ()-1) + if (m_level>= order.size ()-1) return ( getNumTracks () == 0); else return ( values.size () == 0); @@ -649,24 +400,24 @@ mgSelection::empty() void mgSelection::setPosition (unsigned int position) { - if (m_level < keys.size ()) + if (m_level < order.size ()) m_position[m_level] = position; - if (m_level >= keys.size ()-1) - m_tracks_position = position; + if (m_level >= order.size ()-1) + setTrackPosition(position); } void -mgSelection::setTrack (unsigned int position) +mgSelection::setTrackPosition (unsigned int position) { m_tracks_position = position; } unsigned int -mgSelection::getPosition (unsigned int level) const +mgSelection::getPosition (unsigned int level) const { - if (level == keys.size ()) + if (level == order.size ()) return getTrackPosition(); else return m_position[m_level]; @@ -675,10 +426,10 @@ mgSelection::getPosition (unsigned int level) const unsigned int mgSelection::gotoPosition (unsigned int level) { - if (level>keys.size()) - mgError("mgSelection::gotoPosition: level %u > keys.size %u", - level,keys.size()); - if (level == keys.size ()) + if (level>order.size()) + mgError("mgSelection::gotoPosition: level %u > order.size %u", + level,order.size()); + if (level == order.size ()) return gotoTrackPosition(); else { @@ -694,6 +445,11 @@ mgSelection::gotoPosition (unsigned int level) unsigned int mgSelection::getTrackPosition() const { + if (m_tracks_position>=m_tracks.size()) + if (m_tracks.size()==0) + m_tracks_position=0; + else + m_tracks_position = m_tracks.size()-1; return m_tracks_position; } @@ -702,9 +458,9 @@ mgSelection::gotoTrackPosition() { unsigned int tracksize = getNumTracks (); if (tracksize == 0) - m_tracks_position = 0; + setTrackPosition(0); else if (m_tracks_position >= tracksize) - m_tracks_position = tracksize -1; + setTrackPosition(tracksize -1); return m_tracks_position; } @@ -715,7 +471,7 @@ bool mgSelection::skipTracks (int steps) return false; if (m_loop_mode == LM_SINGLE) return true; - unsigned int old_pos = getTrackPosition(); + unsigned int old_pos = m_tracks_position; unsigned int new_pos; if (old_pos + steps < 0) { @@ -727,16 +483,11 @@ bool mgSelection::skipTracks (int steps) new_pos = old_pos + steps; if (new_pos >= tracksize) { - clearCache(); - tracksize = getNumTracks(); - if (new_pos >= tracksize) - { - if (m_loop_mode == LM_NONE) - return false; - new_pos = 0; - } + if (m_loop_mode == LM_NONE) + return false; + new_pos = 0; } - setTrack (new_pos); + setTrackPosition (new_pos); return (new_pos == gotoTrackPosition()); } @@ -753,102 +504,72 @@ mgSelection::getLength () unsigned long -mgSelection::getCompletedLength () +mgSelection::getCompletedLength () const { unsigned long result = 0; tracks (); // make sure they are loaded - for (unsigned int i = 0; i < m_tracks_position; i++) + for (unsigned int i = 0; i < getTrackPosition(); i++) result += m_tracks[i].getDuration (); return result; } -string mgSelection::getListname () + +string mgSelection::getListname () const { string result = ""; for (unsigned int i = 0; i < m_level; i++) - addsep (result, ":", keys[i]->value ()); - if (m_level==keys.size()) - addsep (result,":",getCurrentValue()); + addsep (result, ":", getKeyValue(i)); if (result.empty ()) - result = string(tr(keys[0]->choice ().c_str())); + if (order.size()>0) + result = string(ktName(order.Key(0)->Type ())); return result; } - string mgSelection::ListFilename () { string res = getListname (); -#if 0 - geht so noch gar - nicht ... while (string::size_type p = res.find (" ")) - res.replace (p, ""); - while (string::size_type p = res.find ("/")) - res.replace (p, '-'); - while (string::size_type p = res.find ("\\")) - res.replace (p, '-'); -#endif + // convert char set ? return res; } -void -mgSelection::AddOrder(const string sql,list& orderlist, const string item) -{ - string::size_type dot = item.rfind ('.'); - string itemtable = item.substr(0,dot); - if (sql.find(itemtable) != string::npos) - orderlist.push_back(item); -} - const vector < mgContentItem > & -mgSelection::tracks () -{ - list < string > orderby; - orderby.clear(); - if (keys.empty()) - mgError("mgSelection::tracks(): keys is empty"); - if (genres.size () == 0) - loadgenres (); - string sql = "SELECT tracks.id, tracks.title, tracks.mp3file, " - "tracks.artist, album.title, tracks.genre1, tracks.genre2, " - "tracks.bitrate, tracks.year, tracks.rating, " - "tracks.length, tracks.samplerate, tracks.channels "; - sql += where (true); - for (unsigned int i = m_level; iorder ()); -} - if (m_level>= keys.size ()-1) - if (inCollection()) - AddOrder(sql,orderby,"playlistitem.tracknumber"); - else - AddOrder(sql,orderby,"tracks.title"); - - - sql += commalist("ORDER BY",orderby,false); - - optimize (sql); - if (m_current_tracks != sql) - { - m_current_tracks = sql; - m_tracks.clear (); - MYSQL_RES *rows = exec_sql (sql); +mgSelection::tracks () const +{ + if (!m_db) return m_tracks; + if (!m_current_tracks.empty()) return m_tracks; + mgParts p = order.Parts(m_level); + p.fields.clear(); + p.fields.push_back("tracks.id"); + p.fields.push_back("tracks.title"); + p.fields.push_back("tracks.mp3file"); + p.fields.push_back("tracks.artist"); + p.fields.push_back("album.title"); + p.fields.push_back("tracks.genre1"); + p.fields.push_back("tracks.genre2"); + p.fields.push_back("tracks.bitrate"); + p.fields.push_back("tracks.year"); + p.fields.push_back("tracks.rating"); + p.fields.push_back("tracks.length"); + p.fields.push_back("tracks.samplerate"); + p.fields.push_back("tracks.channels"); + p.tables.push_back("tracks"); + p.tables.push_back("album"); + for (unsigned int i = m_level; iParts(true).orders; + m_current_tracks = p.sql_select(false); + m_tracks.clear (); + MYSQL_RES *rows = exec_sql (m_current_tracks); if (rows) { MYSQL_ROW row; while ((row = mysql_fetch_row (rows)) != NULL) { - m_tracks.push_back (mgContentItem (row, m_ToplevelDir)); + m_tracks.push_back (mgContentItem (this,row)); } mysql_free_result (rows); } - if (m_tracks_position>=m_tracks.size()) - if (m_tracks.size()==0) - m_tracks_position=0; - else - m_tracks_position = m_tracks.size()-1; - } return m_tracks; } @@ -870,7 +591,7 @@ mgContentItem::mgContentItem (const mgContentItem* c) m_channels = c->m_channels; } -mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir) +mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row) { m_id = atol (row[0]); if (row[1]) @@ -878,8 +599,7 @@ mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir) else m_title = "NULL"; if (row[2]) - // m_mp3file = ToplevelDir + row[2]; - m_mp3file = row[2]; + m_mp3file = row[2]; else m_mp3file = "NULL"; if (row[3]) @@ -891,11 +611,11 @@ mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir) else m_albumtitle = "NULL"; if (row[5]) - m_genre1 = row[5]; + m_genre1 = sel->value(keyGenre1,row[5]); else m_genre1 = "NULL"; if (row[6]) - m_genre2 = row[6]; + m_genre2 = sel->value(keyGenre2,row[5]); else m_genre2 = "NULL"; if (row[7]) @@ -924,29 +644,8 @@ mgContentItem::mgContentItem (const MYSQL_ROW row, const string ToplevelDir) m_channels = 0; }; -string mgContentItem::getAlbum () -{ - return m_albumtitle; -} - - -string mgContentItem::getImageFile () -{ - return "Name of Imagefile"; -} - - -void -mgSelection::initkey (keyfield & f) -{ - f.setOwner(this); - all_keys[f.choice ()] = &f; - trall_keys[string(tr(f.choice ().c_str()))] = &f; -} - void mgSelection::InitSelection() { m_Directory="."; - m_ToplevelDir = string("/"); InitDatabase(); m_level = 0; m_position.reserve (20); @@ -955,28 +654,12 @@ void mgSelection::InitSelection() { m_shuffle_mode = SM_NONE; m_loop_mode = LM_NONE; clearCache(); - initkey (kartist); - initkey (kgenre1); - initkey (kgenre2); - initkey (klanguage); - initkey (krating); - initkey (kyear); - initkey (kdecade); - initkey (ktitle); - initkey (ktrack); - initkey (kalbum); - initkey (kcollection); - initkey (kcollectionitem); - keys.clear(); - keys.push_back (&kartist); - keys.push_back (&kalbum); - keys.push_back (&ktitle); values.setOwner(this); } mgSelection::mgSelection() { - m_db = NULL; + setDB(0); m_Host = ""; m_User = ""; m_Password = ""; @@ -986,7 +669,7 @@ mgSelection::mgSelection() mgSelection::mgSelection (const string Host, const string User, const string Password, const bool fall_through) { - m_db = NULL; + setDB(0); m_Host = Host; m_User = User; m_Password = Password; @@ -996,42 +679,44 @@ mgSelection::mgSelection (const string Host, const string User, const string Pas mgSelection::mgSelection (const mgSelection &s) { - m_db = NULL; InitFrom(&s); } mgSelection::mgSelection (const mgSelection* s) { - m_db = NULL; InitFrom(s); } mgSelection::mgSelection (mgValmap& nv) { - // this is analog to the copy constructor, please keep in sync. - - m_db = NULL; InitFrom(nv); } +void +mgSelection::setDB(MYSQL *db) +{ + m_db = db; + order.setDB(db); +} + void mgSelection::InitFrom(mgValmap& nv) { + setDB(0); m_Host = nv.getstr("Host"); m_User = nv.getstr("User"); m_Password = nv.getstr("Password"); InitSelection(); m_fall_through = nv.getbool("FallThrough"); m_Directory = nv.getstr("Directory"); - m_ToplevelDir = nv.getstr("ToplevelDir"); for (unsigned int i = 0; i < 99 ; i++) { char *idx; asprintf(&idx,"Keys.%u.Choice",i); - string v = nv.getstr(idx); + unsigned int v = nv.getuint(idx); free(idx); - if (v.empty()) break; - setKey (i,v ); + if (v==0) break; + setKey (i,mgKeyTypes(v) ); } while (m_level < nv.getuint("Level")) { @@ -1045,8 +730,8 @@ mgSelection::InitFrom(mgValmap& nv) m_trackid = nv.getlong("TrackId"); // TODO do we really need Position AND TrackPosition in muggle.state? setPosition(nv.getlong("Position")); - if (m_level>=keys.size()-1) - setTrack(nv.getlong("TrackPosition")); + if (m_level>=order.size()-1) + setTrackPosition(nv.getlong("TrackPosition")); setShuffleMode(ShuffleMode(nv.getuint("ShuffleMode"))); setLoopMode(LoopMode(nv.getuint("LoopMode"))); } @@ -1054,24 +739,25 @@ mgSelection::InitFrom(mgValmap& nv) mgSelection::~mgSelection () { - mysql_close (m_db); + if (m_db) + { + mgDebug(3,"%X: closing m_db %X",this,m_db); + mysql_close (m_db); + } } void mgSelection::InitFrom(const mgSelection* s) { + setDB(0); m_Host = s->m_Host; m_User = s->m_User; m_Password = s->m_Password; InitSelection(); m_fall_through = s->m_fall_through; m_Directory = s->m_Directory; - m_ToplevelDir = s->m_ToplevelDir; - keys.clear(); - for (unsigned int i = 0; i < s->keys.size (); i++) - { - keys.push_back(findKey(s->keys[i]->choice())); - keys[i]->set(s->keys[i]->id(),s->keys[i]->value()); - } + map_values = s->map_values; + map_ids = s->map_ids; + order = s->order; m_level = s->m_level; m_position.reserve (s->m_position.capacity()); for (unsigned int i = 0; i < s->m_position.capacity(); i++) @@ -1092,48 +778,12 @@ const mgSelection& mgSelection::operator=(const mgSelection &s) } -void -mgSelection::writeAt (ostream & s) -{ - for (unsigned int i = 0; i < keys.size (); i++) - { - if (i == level ()) - s << '*'; - s << *keys[i] << ' '; - if (i == level ()) - { - for (unsigned int j = 0; j < values.size (); j++) - { - s << values[j]; - if (values[j] != m_ids[j]) - s << '(' << m_ids[j] << ")"; - s << ", "; - if (j == 7) - { - s << "(von " << values.size () << ") "; - break; - } - } - } - } - s << endl; -} - - -ostream & operator<< (ostream & s, mgSelection & sl) -{ - sl.writeAt (s); - return s; -} - - unsigned int -mgSelection::size () +mgSelection::ordersize () { - return keys.size (); + return order.size (); } - unsigned int mgSelection::valindex (const string val,const bool second_try) { @@ -1153,213 +803,107 @@ mgSelection::valindex (const string val,const bool second_try) } -string mgSelection::where (bool want_trackinfo) -{ - m_from = ""; - m_where = ""; - m_fromtables.clear(); - if (m_level < keys.size ()) - { - for (unsigned int i = 0; i <= m_level; i++) - { - keyfield * k = keys[i]; - k->lookup = want_trackinfo || (i == m_level); - list < string > l = tables (k->join () + ' ' + k->basefield ()); - m_fromtables.merge (l); - und (m_where, k->join ()); - k->restrict (m_where); - } - } - else - { - m_fromtables.push_back ("tracks"); - m_where = "tracks.id='" + ltos (m_trackid) + "'"; - } - if (want_trackinfo) - { - if (m_level == keys.size () || !UsedBefore (&kalbum, m_level + 1)) - { - kalbum.lookup = false; - list < string > l = - tables (kalbum.join () + ' ' + kalbum.basefield ()); - m_fromtables.merge (l); - und (m_where, kalbum.join ()); - } - } - m_from = commalist ("FROM",m_fromtables); - if (!m_where.empty ()) - m_where.insert (0, " WHERE "); - return m_from + m_where; -} - - void -mgSelection::refreshValues () +mgSelection::refreshValues () const { + if (!m_db) return; + mgOrder o1 = order; if (m_current_values.empty()) { - m_current_values = sql_values(); + mgParts p = order.Parts(m_level); + m_current_values = p.sql_select(); values.strings.clear (); m_ids.clear (); MYSQL_RES *rows = exec_sql (m_current_values); if (rows) { - unsigned int num_fields = mysql_num_fields(rows); + unsigned int num_fields = mysql_num_fields(rows); MYSQL_ROW row; while ((row = mysql_fetch_row (rows)) != NULL) { - string r0,r1; + string r0 = "NULL"; if (row[0]) r0 = row[0]; - else - r0 = "NULL"; - if (row[1]) - r1 = row[1]; - else - r1 = "NULL"; - values.strings.push_back (r0); if (num_fields==2) + { + string r1 = "NULL"; + if (row[1]) + r1 = row[1]; + values.strings.push_back (r0); m_ids.push_back (r1); + } else + { + values.strings.push_back (value(order.Key(m_level),r0)); m_ids.push_back (r0); + } } mysql_free_result (rows); } - if (m_position[m_level]>=values.size()) - if (values.size()==0) - m_position[m_level]=0; - else - m_position[m_level] = values.size()-1; - } -} - - -string mgSelection::sql_values () -{ - if (keys.empty()) - mgError("mgSelection::sql_values(): keys is empty"); - string result; - if (m_level < keys.size ()) - { - keyfield * last = keys[m_level]; - result = "SELECT "; - if (m_levelvaluefield (); - if (last->valuefield() != last->idfield()) - result += ',' + last->idfield (); - result += where (false); - result += " ORDER BY " + last->order (); } - else - { - result = "SELECT title,id from tracks where id='" + ltos (m_trackid) + "'"; - } - optimize (result); - return result; } - unsigned int -mgSelection::count () +mgSelection::count () const { return values.size (); } - void mgSelection::InitDatabase () { if (m_db) { + mgDebug(3,"%X: InitDatabase closes %X",this,m_db); mysql_close (m_db); - m_db = NULL; + setDB(0); } if (m_Host == "") return; - m_db = mysql_init (0); - if (m_db == NULL) + setDB(mysql_init (0)); + mgDebug(3,"%X: InitDatabase opens %X",this, m_db); + if (!m_db) return; if (mysql_real_connect (m_db, m_Host.c_str (), m_User.c_str (), m_Password.c_str (), "GiantDisc", 0, NULL, 0) == NULL) { mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s", m_Host.c_str(),m_User.c_str(),m_Password.c_str(),mysql_error(m_db)); mysql_close (m_db); - m_db = NULL; + setDB(0); return; } return; } -string keyfield::KeyCountquery () -{ - lookup = false; - string from; - from = commalist ("FROM",tables (countfield () + ' ' + countjoin ())); - string query = "SELECT COUNT(DISTINCT " + countfield () + ") " + from; - if (!countjoin ().empty ()) - query += " WHERE " + countjoin (); - optimize (query); - return query; -} - -keyfield* mgSelection::findKey(const string name) -{ - if (all_keys.find(name) != all_keys.end()) - return all_keys.find(name)->second; - if (trall_keys.find(name) != trall_keys.end()) - return trall_keys.find(name)->second; - return NULL; -} - void -mgSelection::setKey (const unsigned int level, const string name) +mgSelection::setKey (const unsigned int level, const mgKeyTypes kt) { - keyfield *newkey = findKey(name); - if (newkey == NULL) - mgError("mgSelection::setKey(%u,%s): keyname wrong", - level,name.c_str()); - if (level == 0 && newkey == &kcollection) + mgKey *newkey = ktGenerate(kt,m_db); + if (level == 0 && kt == keyCollection) { - keys.clear (); - keys.push_back (&kcollection); - keys.push_back (&kcollectionitem); + order.clear (); + order += newkey; + order += ktGenerate(keyCollectionItem,m_db); return; } - if (level == keys.size ()) + if (level == order.size ()) { - keys.push_back (newkey); + order += newkey; } else { - if (level >= keys.size()) - mgError("mgSelection::setKey(%u,%s): level greater than keys.size() %u", - level,name.c_str(),keys.size()); - keys[level] = newkey; -// remove this key from following lines: - for (unsigned int i = level + 1; i < keys.size (); i++) - if (keys[i] == keys[level]) - keys.erase (keys.begin () + i); + if (level >= order.size()) + mgError("mgSelection::setKey(%u,%s): level greater than order.size() %u", + level,ktName(kt),order.size()); + delete order[level]; + order[level] = newkey; } -// remove redundant lines: - bool album_found = false; - bool track_found = false; - bool title_found = false; - for (unsigned int i = 0; i < keys.size (); i++) - { - album_found |= (keys[i] == &kalbum); - track_found |= (keys[i] == &ktrack); - title_found |= (keys[i] == &ktitle); - if (track_found || (album_found && title_found)) - { - keys.erase (keys.begin () + i + 1, keys.end ()); - break; - } - } + order.clean(); // clear values for this and following levels (needed for copy constructor) - for (unsigned int i = level; i < keys.size (); i++) - keys[i]->set (EMPTY, ""); + for (unsigned int i = level; i < order.size (); i++) + order[i]->set ("",EMPTY); if (m_level > level) m_level = level; @@ -1369,8 +913,8 @@ mgSelection::setKey (const unsigned int level, const string name) bool mgSelection::enter (unsigned int position) { - if (keys.empty()) - mgError("mgSelection::enter(%u): keys is empty", position); + if (order.empty()) + mgWarning("mgSelection::enter(%u): order is empty", position); if (empty()) return false; setPosition (position); @@ -1379,13 +923,14 @@ bool mgSelection::enter (unsigned int position) string id = m_ids[position]; while (1) { - mgDebug(2,"enter(level=%u,pos=%u, value=%s)",m_level,position,value.c_str()); - if (m_level >= keys.size () - 1) + mgDebug(3,"enter(level=%u,pos=%u, id=%s)",m_level,position,id.c_str()); + if (m_level >= order.size () - 1) return false; - keys[m_level++]->set (id, value); - if (m_level >= keys.size()) - mgError("mgSelection::enter(%u): level greater than keys.size() %u", - m_level,keys.size()); + order[m_level++]->set (value,id); + clearCache(); + if (m_level >= order.size()) + mgError("mgSelection::enter(%u): level greater than order.size() %u", + m_level,order.size()); if (m_position.capacity () == m_position.size ()) m_position.reserve (m_position.capacity () + 10); m_position[m_level] = 0; @@ -1394,10 +939,10 @@ bool mgSelection::enter (unsigned int position) if (count () > 1) break; if (count () == 1) - { + { + value = values[0]; id = m_ids[0]; - value = values[0]; - } + } } return true; } @@ -1405,13 +950,15 @@ bool mgSelection::enter (unsigned int position) bool mgSelection::select (unsigned int position) { - mgDebug(2,"select(pos=%u)",position); - if (m_level == keys.size () - 1) + mgDebug(3,"select(%u) on Level %d",position,m_level); + if (m_level == order.size () - 1) { if (getNumTracks () <= position) return false; + order[m_level]->set (values[position],m_ids[position]); m_level++; m_trackid = m_tracks[position].getId (); + clearCache(); return true; } @@ -1422,9 +969,12 @@ bool mgSelection::select (unsigned int position) bool mgSelection::leave () { - if (keys.empty()) - mgError("mgSelection::leave(): keys is empty"); - if (m_level == keys.size ()) + if (order.empty()) + { + mgWarning("mgSelection::leave(): order is empty"); + return false; + } + if (m_level == order.size ()) { m_level--; m_trackid = -1; @@ -1435,7 +985,8 @@ bool mgSelection::leave () { if (m_level < 1) return false; - keys[--m_level]->set (EMPTY, ""); + order[--m_level]->set ("",EMPTY); + clearCache(); if (!m_fall_through) break; if (count () > 1) @@ -1444,81 +995,141 @@ bool mgSelection::leave () return true; } +string +mgSelection::value(mgKeyTypes kt, string id) const +{ + if (kt==keyGenre2) kt = keyGenre1; + if (loadvalues (kt)) + { + map& valmap = map_values[kt]; + map::iterator it; + it = valmap.find(id); + if (it!=valmap.end()) + { + string r = it->second; + if (!r.empty()) + return r; + } + map_ids[kt].clear(); + loadvalues(kt); + it = valmap.find(id); + if (it!=valmap.end()) + return valmap[id]; + } + return id; +} + +string +mgSelection::value(mgKey* k, string id) const +{ + return value(k->Type(),id); +} -bool mgSelection::UsedBefore (keyfield const *k, unsigned int level) +string +mgSelection::value(mgKey* k) const +{ + return value(k,k->id()); +} + +string +mgSelection::id(mgKeyTypes kt, string val) const { - if (level >= keys.size ()) - level = keys.size () - 1; - for (unsigned int i = 0; i < level; i++) - if (keys[i] == k) - return true; - return false; + if (kt==keyGenre2) kt = keyGenre1; + if (loadvalues (kt)) + { + map& idmap = map_ids[kt]; + return idmap[val]; + } + else + return val; } +string +mgSelection::id(mgKey* k, string val) const +{ + return id(k->Type(),val); +} -bool mgSelection::isCollectionlist () +string +mgSelection::id(mgKey* k) const { - return (keys[0] == &kcollection && m_level == 0); + return k->id(); } bool -mgSelection::inCollection(const string Name) +mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const { - bool result = (keys[0] == &kcollection && m_level == 1); + if (level>=order.size()) + level = order.size() -1; + for (unsigned int lx = 0; lx < level; lx++) + if (order.Key(lx)->Type()==kt) + return true; + return false; +} + + +bool mgSelection::isCollectionlist () const +{ + if (order.size()==0) return false; + return (order.Key(0)->Type() == keyCollection && m_level == 0); +} + +bool +mgSelection::inCollection(const string Name) const +{ + if (order.size()==0) return false; + bool result = (order.Key(0)->Type() == keyCollection && m_level == 1); if (result) - if (keys[1] != &kcollectionitem) - mgError("inCollection: key[1] is not kcollectionitem"); + if (order.Key(1)->Type() != keyCollectionItem) + mgError("inCollection: key[1] is not keyCollectionItem"); if (!Name.empty()) - result &= (keys[0]->value() == Name); + result &= (order.getKeyValue(0) == Name); return result; } -const strvector & -mgSelection::keychoice (const unsigned int level) +#if 0 +void +keychoice(mgOrder& o,const unsigned int level) { - m_keychoice.clear (); - if (level > keys.size ()) - return m_keychoice; - map < string, keyfield * >::iterator it; - map < string, keyfield * > possible_keys; - for (it = all_keys.begin (); it != all_keys.end (); it++) - { - keyfield*f = (*it).second; - if (keycounts.find (f->choice ()) == keycounts.end ()) - { - keycounts[f->choice ()] = exec_count (f->KeyCountquery ()); - } - unsigned int i = keycounts[f->choice ()]; - if ((&(*f) != &kcollection) && (&(*f) != &kcollectionitem) && (i < 2)) - ; - else - possible_keys[string(tr(f->choice ().c_str()))] = &(*f); - } - - for (it = possible_keys.begin (); it != possible_keys.end (); it++) + if (level > o.size ()) + return; + std::cout<<"possible choices:"; + for (mgKeyTypes kt = mgKeyTypes(1); kt <= mgKeyTypesHigh; kt = mgKeyTypes(int(kt)+1)) { - keyfield *k = (*it).second; - if (level != 0 && k == &kcollection) - continue; - if (level != 1 && k == &kcollectionitem) - continue; - if (level == 1 && keys[0] != &kcollection && k == &kcollectionitem) - continue; - if (level == 1 && keys[0] == &kcollection && k != &kcollectionitem) - continue; - if (level > 1 && keys[0] == &kcollection) - break; - if (k == &kdecade && UsedBefore (&kyear, level)) - continue; - if (!UsedBefore (k, level)) - m_keychoice.push_back (string(tr((*it).second->choice ().c_str()))); + if (level !=0 && kt == keyCollection) + continue; + if (level !=1 && kt == keyCollectionItem) + continue; + if (level == 1 && o[0]->Type() != keyCollection && kt == keyCollectionItem) + continue; + if (level == 1 && o[0]->Type() == keyCollection && kt != keyCollectionItem) + continue; + if (level >1 && o[0]->Type() == keyCollection) + break; + if (kt == keyDecade && UsedBefore(o,keyYear,level)) + continue; + if (o[0]->Type() == keyCollection) + { + std::cout<<" "<1) + std::cout<<" "<choice()); - asprintf(&n,"Keys.%d.Filter",i); - nv.put(n,keys[i]->filter()); + nv.put(n,int(order.Key(i)->Type())); + free(n); if (i * -mgSelection::UsedKeyValues() +map * +mgSelection::UsedKeyValues() { - map *result = new map; + map *result = new map; for (unsigned int idx = 0 ; idx < level() ; idx++) { - (*result)[keys[idx]->choice()] = keys[idx]->value(); + (*result)[order.Key(idx)->Type()] = order.getKeyValue(idx); } - if (level() < keys.size()-1) + if (level() < order.size()-1) { - string ch = keys[level()]->choice(); + mgKeyTypes ch = order.getKeyType(level()); (*result)[ch] = getCurrentValue(); } return result; } + +bool +mgSelection::loadvalues (mgKeyTypes kt) const +{ + mgKey* k = ktGenerate(kt,m_db); + if (k->map_idfield().empty()) + { + delete k; + return false; + } + map& idmap = map_ids[kt]; + if (!idmap.empty()) + { + delete k; + return true; + } + map& valmap = map_values[kt]; + char *b; + asprintf(&b,"select %s,%s from %s;",k->map_idfield().c_str(),k->map_valuefield().c_str(),k->map_valuetable().c_str()); + MYSQL_RES *rows = exec_sql (string(b)); + free(b); + if (rows) + { + MYSQL_ROW row; + while ((row = mysql_fetch_row (rows)) != NULL) + { + if (row[0] && row[1]) + valmap[row[0]] = row[1]; + idmap[row[1]] = row[0]; + } + mysql_free_result (rows); + } + delete k; + return true; +} diff --git a/mg_db.h b/mg_db.h index 40e3308..2ba02df 100644 --- a/mg_db.h +++ b/mg_db.h @@ -9,8 +9,8 @@ * */ -#ifndef _DB_H -#define _DB_H +#ifndef _MG_DB_H +#define _MG_DB_H #include #include #include @@ -25,443 +25,29 @@ using namespace std; #include "mg_tools.h" +#include "mg_valmap.h" +#include "mg_order.h" typedef vector strvector; -//! \brief a map for reading / writing configuration data. -class mgValmap : public map { - private: - const char *m_key; - public: - /*! \brief constructor - * \param key all names will be prefixed with key. - */ - mgValmap(const char *key); - //! \brief read from file - void Read(FILE *f); - //! \brief write to file - void Write(FILE *f); - //! \brief enter a string value - void put(const char*name, string value); - //! \brief enter a C string value - void put(const char*name, const char* value); - //! \brief enter a long value - void put(const char*name, long value); - //! \brief enter a int value - void put(const char*name, int value); - //! \brief enter a unsigned int value - void put(const char*name, unsigned int value); - //! \brief enter a bool value - void put(const char*name, bool value); - //! \brief return a string - string getstr(const char* name) { - return (*this)[name]; - } - //! \brief return a C string - bool getbool(const char* name) { - return (getstr(name)=="true"); - } - //! \brief return a long - long getlong(const char* name) { - return atol(getstr(name).c_str()); - } - //! \brief return an unsigned int - unsigned int getuint(const char* name) { - return (unsigned long)getlong(name); - } -}; - -static const string EMPTY = "XNICHTGESETZTX"; class mgSelection; -//! \brief a generic keyfield -class keyfield -{ - public: - //! \brief set the owning selection. - void setOwner(mgSelection *owner) { selection = owner; } - - //! \brief default constructor - keyfield () - { - }; - - /*! \brief constructs a simple key field which only needs info from - * the tracks table. - * \param choice the internationalized name of this key field, e.g. "Jahr" - */ - keyfield (const string choice); - - //! \brief default destructor - virtual ~ keyfield () - { - }; - -/*! \brief assigns a new id and value to the key field. - * This also invalidates the cache of the owning mgSelection. - * \param id used for lookups in the data base - * \param value used for display - */ - void set (const string id, const string value); - -//! \brief helper function for streaming debug info - void writeAt (ostream &) const; - -//! \brief adds lookup data to a WHERE SQL statement - string restrict (string & result) const; - -//! \brief returns the internationalized name of this key field - string choice () const - { - return m_choice; - } - -/*! \brief returns the id of this key field. This is the string used - * for lookups in the data base, not for display - */ - string id () const - { - return m_id; - } - -/*! \brief returns the filter for this key field. - * \todo filters are not yet implemented but we already dump them in DumpState - */ - string filter () const - { - return m_filter; - } - -/*! \brief returns the value of this key field. This is the string used - * for the display. - */ - string value () const - { - return m_value; - } - - virtual string order() const - { - return valuefield (); - } - -//! \brief returns the name of the corresponding field in the tracks table - virtual string basefield () const { return ""; } - -//! \brief returns the name of the field to be shown in the selection list - virtual string valuefield () const - { - return basefield (); - } - -//! \brief returns the name of the identification field - virtual string idfield () const - { - return basefield (); - } - -/*! \brief returns the name of the field needed to count how many - * different values for this key field exist - */ - virtual string countfield () const - { - return basefield (); - } - -/*! \brief returns a join clause needed for the composition of - * a WHERE statement - */ - virtual string join () const; - -/*! \brief returns a join clause needed for the composition of - * a WHERE statement especially for counting the number of items - */ - virtual string countjoin () const - { - return join (); - } - -/*! \brief if true, the WHERE clause should also return values from - * join tables - */ - bool lookup; - -//! \brief returns a SQL query command for counting different key -//values - string KeyCountquery (); - - protected: -//! \brief the owning selection. - mgSelection* selection; - //! \brief the english name for this key field - string m_choice; - //! \brief used for lookup in the data base - string m_id; - //! \brief used for OSD display - string m_value; - //! \brief an SQL restriction like 'tracks.year=1982' - string m_filter; - /*! \brief should be defined as true if we need to join another - * table for getting user friendly values (like the name of a genre) - */ - virtual bool need_join () const; - //! \brief escape the string as needed for calls to mysql - string sql_string(const string s) const; -}; - - -//! \brief orders by collection -class collectionkeyfield:public keyfield -{ - public: - collectionkeyfield ():keyfield ("Collection") - { - } - string basefield () const - { - return "playlist.id"; - } - string valuefield () const - { - return "playlist.title"; - } -/* this join() would ensure that empty collections be suppressed. But we - * want them all. so we don't need the join - */ - string join () const - { - return ""; - } -}; - -//! \brief orders by position in collection -class collectionitemkeyfield:public keyfield -{ - public: - collectionitemkeyfield ():keyfield ("Collection item") - { - } - string basefield () const - { - return "playlistitem.tracknumber"; - } - string valuefield () const - { - return "tracks.title"; - } - string order () const - { - return basefield (); - } - string join () const - { - return - "tracks.id=playlistitem.trackid and playlist.id=playlistitem.playlist"; - } -}; - -//! \brief orders by album.title -class albumkeyfield:public keyfield -{ - public: - albumkeyfield ():keyfield ("Album") - { - } - string basefield () const - { - return "tracks.sourceid"; - } - string valuefield () const - { - return "album.title"; - } - string idfield () const - { - return "album.title"; - } - string countfield () const - { - return "album.title"; - } - string join () const - { - return "tracks.sourceid=album.cddbid"; - } - protected: - //!brief we always need to join table album - bool need_join () const - { - return true; - }; -}; - -//! \brief orders by genre1 -class genre1keyfield:public keyfield -{ - public: - genre1keyfield ():keyfield ("Genre 1") - { - } - string basefield () const - { - return "tracks.genre1"; - } - string valuefield () const - { - return "genre.genre"; - } - string idfield () const - { - return "genre.id"; - } -}; - -//! \brief orders by genre2 -class genre2keyfield:public keyfield -{ - public: - genre2keyfield ():keyfield ("Genre 2") - { - } - string basefield () const - { - return "tracks.genre2"; - } - string valuefield () const - { - return "genre.genre"; - } - string idfield () const - { - return "genre.id"; - } -}; - -//! \brief orders by language -class langkeyfield:public keyfield -{ - public: - langkeyfield ():keyfield ("Language") - { - } - string basefield () const - { - return "tracks.lang"; - } - string valuefield () const - { - return "language.language"; - } - string idfield () const - { - return "language.id"; - } -}; - -//! \brief orders by tracks.artist -class artistkeyfield:public keyfield -{ - public: - artistkeyfield ():keyfield ("Artist") - { - } - string basefield () const - { - return "tracks.artist"; - } -}; - -//! \brief orders by tracks.rating -class ratingkeyfield:public keyfield -{ - public: - ratingkeyfield ():keyfield ("Rating") - { - } - string basefield () const - { - return "tracks.rating"; - } -}; - -//! \brief orders by tracks.year -class yearkeyfield:public keyfield -{ - public: - yearkeyfield ():keyfield ("Year") - { - } - string basefield () const - { - return "tracks.year"; - } -}; - -//! \brief orders by tracks.title -class titlekeyfield:public keyfield -{ - public: - titlekeyfield ():keyfield ("Title") - { - } - string basefield () const - { - return "tracks.title"; - } -}; - -//! \brief orders by tracks.tracknb and tracks.title -class trackkeyfield:public keyfield -{ - public: - trackkeyfield ():keyfield ("Track") - { - } - string basefield () const - { - return "tracks.tracknb"; - } - string valuefield () const - { - return - "concat(" - "if(tracks.tracknb>0," - "concat(" - "if(tracks.tracknb<10,' ','')," - "tracks.tracknb," - "' '" - "),''" - ")," - "tracks.title)"; - } -}; - -//! \brief orders by decade (deduced from tracks.year) -class decadekeyfield:public keyfield -{ - public: - decadekeyfield ():keyfield ("Decade") - { - } - string basefield () const - { - return "substring(convert(10 * floor(tracks.year/10), char),3)"; - } -}; - -//! \brief represents a content item like an mp3 file +//! \brief represents a content item like an mp3 file. class mgContentItem { public: mgContentItem () { } + + string getKeyValue(mgKeyTypes kt); + //! \brief copy constructor mgContentItem(const mgContentItem* c); //! \brief construct an item from an SQL row - mgContentItem (const MYSQL_ROW row, const string ToplevelDir); + mgContentItem (const mgSelection* sel, const MYSQL_ROW row); //! \brief returns track id long getId () const { @@ -487,58 +73,38 @@ class mgContentItem } //! \brief returns the name of the album - string getAlbum (); + string getAlbum () const; //! \brief returns the name of genre 1 - string getGenre1 (); + string getGenre1 () const; //! \brief returns the name of genre 2 - string getGenre2 (); + string getGenre2 () const; //! \brief returns the name of genre 1 - string getGenre () - { - return getGenre1 (); - } + string getGenre () const; //! \brief returns the bitrate - string getBitrate () const - { - return m_bitrate; - } + string getBitrate () const; //! \brief returns the file name of the album image - string getImageFile (); + string getImageFile () const; //! \brief returns year - int getYear () const - { - return m_year; - } + int getYear () const; //! \brief returns rating - int getRating () const - { - return m_rating; - } + int getRating () const; //! \brief returns duration - int getDuration () const - { - return m_duration; - } + int getDuration () const; //! \brief returns samplerate - int getSampleRate () const - { - return m_samplerate; - } + int getSampleRate () const; //! \brief returns # of channels - int getChannels () const - { - return m_channels; - } + int getChannels () const; + private: long m_id; string m_title; @@ -557,6 +123,10 @@ class mgContentItem /*! * \brief the only interface to the database. + * Some member functions are declared const although they can modify the inner state of mgSelection. + * But they only modify variables used for caching. With const, we want to express + * the logical constness. E.g. the selected tracks can change without breaking constness: + * The selection never defines concrete tracks but only how to choose them. */ class mgSelection { @@ -577,6 +147,13 @@ class mgSelection /*! \brief define various ways to play music in random order * \todo Party mode is not implemented, does same as SM_NORMAL */ +/*! \brief defines a field to be used as key for selection + * + * \param level 0 is the top level + * \param kt type of the key field. For possible values see mg_order.h + */ + void setKey (const unsigned int level, const mgKeyTypes kt); + enum ShuffleMode { SM_NONE, //!< \brief play normal sequence @@ -636,54 +213,32 @@ class mgSelection //! \brief the normal destructor ~mgSelection (); - //! \brief sets the top level directory where content is stored - void setToplevelDir(string ToplevelDir) { m_ToplevelDir = ToplevelDir; } - /*! \brief represents all values for the current level. The result * is cached in values, subsequent accesses to values only incur a * small overhead for building the SQL WHERE command. The values will * be reloaded when the SQL command changes - * \todo we should do more caching. The last 5 result sets should be cached. */ - mgSelStrings values; + mutable mgSelStrings values; -/*! \brief defines a field to be used as key for selection - * - * \param level 0 is the top level - * \param name of the key field, internationalized. Possible values - * are defined by keychoice() +/*! \brief returns the name of a key */ - void setKey (const unsigned int level, const string name); + mgKeyTypes getKeyType (const unsigned int level) const; -/*! \brief returns the name of a key +//! \brief return the current value of this key + string getKeyValue (const unsigned int level) const; + +/*! \brief returns the current item from the value() list */ - string getKeyChoice (const unsigned int level) - { - return keys[level]->choice (); - } - //! \brief return the current value of this key - string getKeyValue (const unsigned int level) - { - return keys[level]->value (); - } + string getCurrentValue(); //! \brief returns a map (new allocated) for all used key fields and their values - map * UsedKeyValues(); - -//! \brief helper function for << operator (dumps debug info) - void writeAt (ostream &); + map * UsedKeyValues(); -/*! \brief returns FROM and WHERE clauses for the current state - * of the selection. - * \param want_trackinfo work in progress, should disappear I hope - */ - string where (bool want_trackinfo = false); +//! \brief the number of key fields used for the query + unsigned int ordersize (); //! \brief the number of music items currently selected - unsigned int count (); - -//! \brief the number of key fields used for the query - unsigned int size (); + unsigned int count () const; //! \brief the current position in the current level unsigned int gotoPosition () @@ -692,12 +247,13 @@ class mgSelection } //! \brief the current position - unsigned int getPosition (unsigned int level) const; + unsigned int getPosition (unsigned int level)const; //! \brief go to the current position. If it does not exist, // go to the nearest. unsigned int gotoPosition (unsigned int level); + //! \brief the current position in the tracks list unsigned int getTrackPosition () const; @@ -779,28 +335,17 @@ class mgSelection return m_level; } -/*! \brief the possible choices for a keyfield in this level. - * keyfields already used in upper levels are no possible - * choices, neither are most keyfields if their usage would - * allow less than 2 choices. - */ - const strvector &keychoice (const unsigned int level); - -/*! \brief returns the current item from the value() list - */ - string getCurrentValue(); - //! \brief true if the selection holds no items bool empty(); -/*! \brief returns detailled info about all selected tracks. +/*! \brief returns detailed info about all selected tracks. * The ordering is done only by the keyfield of the current level. * This might have to be changed - suborder by keyfields of detail * levels. This list is cached so several consequent calls mean no * loss of performance. See value(), the same warning applies. * \todo call this more seldom. See getNumTracks() */ - const vector < mgContentItem > &tracks (); + const vector < mgContentItem > &tracks () const; /*! \brief returns an item from the tracks() list * \param position the position in the tracks() list @@ -899,7 +444,7 @@ class mgSelection * last existing position * \return only if no position exists, false will be returned */ - void setTrack (unsigned int position); + void setTrackPosition (unsigned int position); /*! \brief skip some tracks in the track list * \return false if new position does not exist @@ -928,7 +473,7 @@ class mgSelection /*! \brief returns the sum of the durations of completed tracks * those are tracks before the current track position */ - unsigned long getCompletedLength (); + unsigned long getCompletedLength () const; /*! returns the number of tracks in the track list * \todo should not call tracks () which loads all track info. @@ -949,19 +494,19 @@ class mgSelection /*! returns the name of the current play list. If no play list is active, * the name is built from the name of the key fields. */ - string getListname (); + string getListname () const; /*! \brief true if this selection currently selects a list of collections */ - bool isCollectionlist (); + bool isCollectionlist () const; //! \brief true if we have entered a collection - bool inCollection(const string Name=""); + bool inCollection(const string Name="") const; /*! \brief dumps the entire state of this selection into a map, * \param nv the values will be entered into this map */ - void DumpState(mgValmap& nv); + void DumpState(mgValmap& nv) const; /*! \brief creates a new selection using saved definitions * \param nv this map contains the saved definitions @@ -969,60 +514,49 @@ class mgSelection mgSelection(mgValmap& nv); //! \brief clear the cache, next access will reload from data base - void clearCache(); + void clearCache() const; - //! \todo soll sql_values() nur noch bei Bedarf bauen, also muessen - // alle Aenderungen, die Einfluss darauf haben, clearCache machen - void refreshValues(); + void refreshValues() const; //! \brief true if values and tracks need to be reloaded - bool cacheIsEmpty() + bool cacheIsEmpty() const { return (m_current_values=="" && m_current_tracks==""); } + string value(mgKeyTypes kt, string id) const; + string value(mgKey* k, string id) const; + string value(mgKey* k) const; + string id(mgKeyTypes kt, string val) const; + string id(mgKey* k, string val) const; + string id(mgKey* k) const; + private: - void AddOrder(const string sql,list& orderlist, const string item); - list < string > m_fromtables; //!< \brief part result from previous where() - string m_from; //!< \brief part result from previous where() - string m_where; //!< \brief part result from previous where() + mutable map > map_values; + mutable map > map_ids; + mutable string m_current_values; + mutable string m_current_tracks; +//! \brief be careful when accessing this, see mgSelection::tracks() + mutable vector < mgContentItem > m_tracks; + mutable strvector m_ids; + //! \brief initializes maps for id/value mapping in both direction + bool loadvalues (mgKeyTypes kt) const; bool m_fall_through; vector < unsigned int >m_position; - unsigned int m_tracks_position; + mutable unsigned int m_tracks_position; ShuffleMode m_shuffle_mode; LoopMode m_loop_mode; MYSQL *m_db; + void setDB(MYSQL *db); string m_Host; string m_User; string m_Password; - string m_ToplevelDir; unsigned int m_level; long m_trackid; - string m_current_values; - string m_current_tracks; -//! \brief be careful when accessing this, see mgSelection::tracks() - vector < mgContentItem > m_tracks; - strvector m_ids; - strvector m_keychoice; - artistkeyfield kartist; - ratingkeyfield krating; - yearkeyfield kyear; - decadekeyfield kdecade; - albumkeyfield kalbum; - collectionkeyfield kcollection; - collectionitemkeyfield kcollectionitem; - genre1keyfield kgenre1; - genre2keyfield kgenre2; - langkeyfield klanguage; - titlekeyfield ktitle; - trackkeyfield ktrack; - map < string, keyfield * >all_keys; - map < string, keyfield * >trall_keys; - vector < keyfield * >keys; - bool UsedBefore (keyfield const *k, unsigned int level); + mgOrder order; + bool UsedBefore (const mgKeyTypes kt, unsigned int level) const; void InitSelection (); void InitDatabase (); - void initkey (keyfield & f); /*! \brief returns the SQL command for getting all values. * For the leaf level, all values are returned. For upper * levels, every distinct value is returned only once. @@ -1031,13 +565,12 @@ class mgSelection * entries and the wrong tracks might be played. */ string sql_values (); - //! \todo das nach mgSelStrings verlagern unsigned int valindex (const string val,const bool second_try=false); string ListFilename (); string m_Directory; void loadgenres (); - MYSQL_RES *exec_sql (string query); - string get_col0 (string query); + MYSQL_RES * exec_sql(string query) const; + string get_col0 (string query) const; void InitFrom(const mgSelection* s); @@ -1046,25 +579,12 @@ class mgSelection * returning only one row. * \param query the SQL query to be executed */ - unsigned long mgSelection::exec_count (string query); + unsigned long mgSelection::exec_count (string query) const; - keyfield* findKey (const string name); - map < string, unsigned int > keycounts; -}; -//! \brief streams debug info about a selection -ostream & operator<< (ostream &, mgSelection & s); - -//! \brief convert the shuffle mode into a string -// \return strings "SM_NONE" etc. -string toString (mgSelection::ShuffleMode); - -//! \brief same as toString but returns a C string -const char *toCString (mgSelection::ShuffleMode); +}; -//string toString(long int l); -string itos (int i); unsigned int randrange (const unsigned int high); diff --git a/mg_filters.c b/mg_filters.c deleted file mode 100644 index e0457da..0000000 --- a/mg_filters.c +++ /dev/null @@ -1,297 +0,0 @@ -/*******************************************************************/ -/*! \file mg_filters.c - * \brief - ******************************************************************** - * \version $Revision: 1.3 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author$ - */ -/*******************************************************************/ - -/* makes sure we dont parse the same declarations twice */ -#include -#include "mg_filters.h" -#include "mg_tools.h" - - -//------------------------------------------------------------------- -// mgFilter -//------------------------------------------------------------------- -mgFilter::mgFilter(const char* name) -{ - m_name = strdup(name); -} -mgFilter::~mgFilter() -{ - free(m_name); -} - -const char* mgFilter::getName() -{ - return m_name; -} - -mgFilter::filterType mgFilter::getType() -{ - return m_type; -} - -//------------------------------------------------------------------- -// mgFilterInt -//------------------------------------------------------------------- -mgFilterInt::mgFilterInt(const char *name, int value, int min, int max) - : mgFilter(name) -{ - m_type = INT; - m_intval = value; - m_default_val = value; - m_stored_val = value; - m_max = max; - m_min = min; -} -mgFilterInt::~mgFilterInt() -{ -} - -std::string mgFilterInt::getStrVal() -{ - char buffer[20]; - sprintf(buffer, "%d", m_intval); - - return (std::string)buffer; -} - -int mgFilterInt::getIntVal() -{ - return (int) m_intval; -} - -int mgFilterInt::getVal() -{ - return m_intval; -} - -int mgFilterInt::getMin() -{ - return m_min; -} - -int mgFilterInt::getMax() -{ - return m_max; -} - -void mgFilterInt::store() -{ - m_stored_val = m_intval; -} -void mgFilterInt::restore() -{ - m_intval = m_stored_val; -} -void mgFilterInt::clear() -{ - m_stored_val = m_default_val; - m_intval = m_default_val; -} - -bool mgFilterInt::isSet() -{ - if(m_stored_val == m_default_val) - { - return false; - } - return true; -} - -//------------------------------------------------------------------- -// mgFilterString -//------------------------------------------------------------------- -mgFilterString::mgFilterString(const char *name, const char* value, - int maxlen, std::string allowedchar) - : mgFilter(name) -{ - m_type = STRING; - m_strval = strdup(value); - m_default_val = strdup(value); - m_stored_val = strdup(value); - m_allowedchar = allowedchar; - m_maxlen = maxlen; -} -mgFilterString::~mgFilterString() -{ - if(m_strval) - { - free(m_strval); - } -} - -int mgFilterString::getMaxLength() -{ - return m_maxlen; -} - -std::string mgFilterString::getAllowedChars() -{ - return m_allowedchar; -} -std::string mgFilterString::getStrVal() -{ - - return (std::string) m_strval; -} -void mgFilterString::store() -{ - if(m_stored_val) free(m_stored_val); - m_stored_val = strdup(m_strval); -} -void mgFilterString::restore() -{ - if(m_strval) free(m_strval); - m_strval = strdup(m_stored_val); -} -void mgFilterString::clear() -{ - if(m_stored_val) free(m_stored_val); - if(m_strval) free(m_strval); - - m_stored_val = strdup(m_default_val); - m_strval = strdup(m_default_val); -} - -bool mgFilterString::isSet() -{ - if(strlen(m_stored_val) == 0) - { - return false; - } - return true; -} -//------------------------------------------------------------------- -// mgFilterBool -//------------------------------------------------------------------- -mgFilterBool::mgFilterBool(const char *name, bool value, - std::string truestr, std::string falsestr) - : mgFilter(name) -{ - m_type = BOOL; - m_bval = (int) value; - m_default_val = value; - m_stored_val = value; - m_truestr = truestr; - m_falsestr = falsestr; - -} - -mgFilterBool::~mgFilterBool() -{ -} - -std::string mgFilterBool::getStrVal() -{ - if(m_bval) - return "true"; - else - return "false"; -} - -int mgFilterBool::getIntVal() -{ - return (int) m_bval; -} - -std::string mgFilterBool::getTrueString() -{ - return m_truestr; -} - -std::string mgFilterBool::getFalseString() -{ - return m_falsestr; -} - -bool mgFilterBool::getVal() -{ - return (bool) m_bval; -} - -void mgFilterBool::store() -{ - m_stored_val = (bool) m_bval; -} - -void mgFilterBool::restore() -{ - m_bval = (int) m_stored_val; -} - -void mgFilterBool::clear() -{ - m_stored_val = (int) m_default_val; - m_bval = (int) m_default_val; -} - -bool mgFilterBool::isSet() -{ - if(m_stored_val == m_default_val ) - { - return false; - } - return true; -} -//------------------------------------------------------------------- -// mgFilterChoice -//------------------------------------------------------------------- -mgFilterChoice::mgFilterChoice(const char *name, int value, std::vector *choices) - : mgFilter(name) -{ - m_type = CHOICE; - m_choices = *choices; - m_selval = value; - m_default_val = value; - if( m_selval < 0 || m_selval >= (int) m_choices.size() ) - { - mgError("mgFilterChoice::mgFilterChoice(..): Illegal index %d", m_selval); - } -} -mgFilterChoice::~mgFilterChoice() -{ - m_choices.clear(); -} - -std::string mgFilterChoice::getStrVal() -{ - if( m_selval < 0 || m_selval >= (int) m_choices.size() ) - { - mgError("mgFilterChoice::getStrVal(): Illegal index %d", m_selval); - } - return m_choices[m_selval]; -} -std::vector &mgFilterChoice::getChoices() -{ - return m_choices; -} -void mgFilterChoice::store() -{ - m_stored_val = m_selval; - -} -void mgFilterChoice::restore() -{ - m_selval = m_stored_val; -} -void mgFilterChoice::clear() -{ - m_stored_val = m_default_val; - m_selval = m_default_val; -} - -bool mgFilterChoice::isSet() -{ - if(m_stored_val == m_default_val) - { - return false; - } - return true; -} diff --git a/mg_filters.h b/mg_filters.h deleted file mode 100644 index 658d943..0000000 --- a/mg_filters.h +++ /dev/null @@ -1,171 +0,0 @@ -/*! \file mg_filters.h - * \brief Top level access to media in vdr plugin muggle - * for the vdr muggle plugindatabase - * - * \version $Revision: 1.2 $ - * \date $Date$ - * \author Ralf Klueber, Lars von Wedel, Andreas Kellner - * \author file owner: $Author$ - */ - -#ifndef _MG_FILTERS_H -#define _MG_FILTERS_H - -#include -#include - -/*! - * \brief abstract base class for representation of filter values with boundaries - */ -class mgFilter -{ - public: - - typedef enum filterType - { - UNDEF = 0, - INT, - STRING, - BOOL, - CHOICE - } filterType; - - protected: - filterType m_type; - char* m_name; - - public: - - mgFilter(const char* name); - - virtual ~mgFilter(); - - filterType getType(); - - const char* getName(); - - virtual std::string getStrVal() = 0; - - virtual int getIntVal() - { return 0; } - - virtual void store() = 0; - - virtual void restore() = 0; - - virtual void clear() = 0; - - virtual bool isSet() = 0; -}; - -/*! - * \class mgFilterInt - */ -class mgFilterInt : public mgFilter -{ - private: - int m_min; - int m_max; - int m_stored_val; - int m_default_val; - - public: - int m_intval; - - mgFilterInt(const char *name, int value, int min = 0, int max = 9999); - virtual ~mgFilterInt(); - - int getVal(); - int getMin(); - int getMax(); - virtual std::string getStrVal(); - virtual int getIntVal(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - -/*! - * \class mgFilterString - */ -class mgFilterString : public mgFilter -{ - private: - std::string m_allowedchar; - int m_maxlen; - char* m_stored_val; - char* m_default_val; - - public: - char* m_strval; - - mgFilterString(const char *name, const char* value, int maxlen=255, - std::string allowedchar="abcdefghijklmnopqrstuvwxyz0123456789-"); - - virtual ~mgFilterString(); - - int getMaxLength(); - std::string getAllowedChars(); - virtual std::string getStrVal(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - -/*! - * \class mgFilterBool - */ -class mgFilterBool : public mgFilter -{ - private: - std::string m_truestr; - std::string m_falsestr; - bool m_stored_val; - bool m_default_val; - - public: - int m_bval; - - mgFilterBool(const char *name, bool value, - std::string truestr="yes", std::string falsestr="no"); - virtual ~mgFilterBool(); - - virtual std::string getStrVal(); - virtual int getIntVal(); - std::string getTrueString(); - std::string getFalseString(); - bool getVal(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - -/*! - * \class mgFilterChoice - */ -class mgFilterChoice : public mgFilter -{ - private: - std::vector m_choices; - int m_stored_val; - int m_default_val; - - public: - int m_selval; // index of the currently selected item - - mgFilterChoice(const char *name, int val, std::vector *choices); - virtual ~mgFilterChoice(); - - virtual std::string getStrVal(); - virtual std::vector &getChoices(); - virtual void store(); - virtual void restore(); - virtual void clear(); - virtual bool isSet(); -}; - - -#endif diff --git a/mg_order.c b/mg_order.c new file mode 100644 index 0000000..ead9436 --- /dev/null +++ b/mg_order.c @@ -0,0 +1,775 @@ +#include "mg_order.h" +#include "mg_tools.h" +#include "i18n.h" + +class mgRefParts : public mgParts { + public: + mgRefParts(const mgReference& r); +}; + + +strlist& operator+=(strlist&a, strlist b) +{ + a.insert(a.end(), b.begin(),b.end()); + return a; +} + + +string +sql_string (MYSQL *db, const string s) +{ + if (!db) + return ""; + char *buf = (char *) malloc (s.size () * 2 + 1); + mysql_real_escape_string (db, buf, s.c_str (), s.size ()); + string result = "'" + std::string (buf) + "'"; + free (buf); + return result; +} + +//! \brief adds n1=n2 to string s, using AND to separate several such items +static string +undequal (string & s, string n1, string op, string n2) +{ + if (n1.compare (n2) || op != "=") + return addsep (s, " AND ", n1 + op + n2); + else + return s; +} + +/*! \brief if the SQL command works on only 1 table, remove all table +* qualifiers. Example: SELECT tracks.title FROM tracks becomes SELECT title +* FROM tracks +* \param spar the sql command. It will be edited in place +* \return the new sql command is also returned +*/ +static string +optimize (string & spar) +{ + string s = spar; + string::size_type tmp = s.find (" WHERE"); + if (tmp != string::npos) + s.erase (tmp, 9999); + tmp = s.find (" ORDER"); + if (tmp != string::npos) + s.erase (tmp, 9999); + string::size_type frompos = s.find (" FROM ") + 6; + if (s.substr (frompos).find (",") == string::npos) + { + string from = s.substr (frompos, 999) + '.'; + string::size_type track; + while ((track = spar.find (from)) != string::npos) + { + spar.erase (track, from.size ()); + } + } + return spar; +} + +MYSQL_RES * +exec_sql (MYSQL *db,string query) +{ + if (!db) + return 0; + if (query.empty()) + return 0; + mgDebug(3,"exec_sql(%X,%s)",db,query.c_str()); + if (mysql_query (db, (query + ';').c_str ())) + { + mgError("SQL Error in %s: %s",query.c_str(),mysql_error (db)); + std::cout<<"ERROR in " << query << ":" << mysql_error(db)<host)); + mgParts result; + result.tables.push_back(table()); + AddIdClause(result,expr()); + if (orderby) + { + result.fields.push_back(expr()); + result.orders.push_back(orderfield()); + } + return result; +} + +void +mgKeyNormal::AddIdClause(mgParts &result,string what) const +{ + assert(strlen(m_db->host)); + if (id() != EMPTY) + { + string op; + string xid; + if (id() == "'NULL'") + { + op = "is"; + xid = "NULL"; + } + else + { + op = "="; + xid = sql_string(m_db,id()); + } + string clause = ""; + result.clauses.push_back(undequal(clause,what,op,xid)); + } +} + +mgParts +mgKeyTrack::Parts(bool orderby) const +{ + mgParts result; + result.tables.push_back("tracks"); + AddIdClause(result,"tracks.title"); + if (orderby) + { + // if you change tracks.title, please also + // change mgContentItem::getKeyValue() + result.fields.push_back("tracks.title"); + result.orders.push_back("tracks.tracknb"); + } + return result; +} + +mgParts +mgKeyGenre1::Parts(bool orderby) const +{ + mgParts result; + AddIdClause(result,"tracks.genre1"); + result.tables.push_back("tracks"); + if (orderby) + { + result.fields.push_back("genre.genre"); + result.fields.push_back("tracks.genre1"); + result.tables.push_back("genre"); + result.orders.push_back("genre.genre"); + } + return result; +} + +mgParts +mgKeyGenre2::Parts(bool orderby) const +{ + mgParts result; + AddIdClause(result,"tracks.genre2"); + result.tables.push_back("tracks"); + if (orderby) + { + result.fields.push_back("genre.genre"); + result.fields.push_back("tracks.genre2"); + result.tables.push_back("genre"); + result.orders.push_back("genre.genre"); + } + return result; +} + +mgParts +mgKeyLanguage::Parts(bool orderby) const +{ + mgParts result; + AddIdClause(result,"tracks.lang"); + result.tables.push_back("tracks"); + if (orderby) + { + result.fields.push_back("language.language"); + result.fields.push_back("tracks.lang"); + result.tables.push_back("language"); + result.orders.push_back("language.language"); + } + return result; +} + +mgParts +mgKeyCollection::Parts(bool orderby) const +{ + mgParts result; + if (orderby) + { + result.tables.push_back("playlist"); + AddIdClause(result,"playlist.id"); + result.fields.push_back("playlist.title"); + result.fields.push_back("playlist.id"); + result.orders.push_back("playlist.title"); + } + else + { + result.tables.push_back("playlistitem"); + AddIdClause(result,"playlistitem.playlist"); + } + return result; +} + +mgParts +mgKeyCollectionItem::Parts(bool orderby) const +{ + assert(strlen(m_db->host)); + mgParts result; + result.tables.push_back("playlistitem"); + AddIdClause(result,"playlistitem.tracknumber"); + if (orderby) + { + // tracks nur hier, fuer sql_delete_from_coll wollen wir es nicht + result.tables.push_back("tracks"); + result.fields.push_back("tracks.title"); + result.fields.push_back("playlistitem.tracknumber"); + result.orders.push_back("playlistitem.tracknumber"); + } + return result; +} + +mgParts& +mgParts::operator+=(mgParts a) +{ + fields += a.fields; + tables += a.tables; + clauses += a.clauses; + orders += a.orders; + return *this; +} + + +string& +addsep (string & s, string sep, string n) +{ + if (!n.empty ()) + { + if (!s.empty ()) + s.append (sep); + s.append (n); + } + return s; +} + + +static string +sql_list (string prefix,list < string > v,string sep=",",string postfix="") +{ + string result = ""; + for (list < string >::iterator it = v.begin (); it != v.end (); it++) + { + addsep (result, sep, *it); + } + if (!result.empty()) + { + result.insert(0," "+prefix+" "); + result += postfix; + } + return result; +} + +//! \brief converts long to string +string +itos (int i) +{ + stringstream s; + s << i; + return s.str (); +} + +//! \brief convert long to string +string +ltos (long l) +{ + stringstream s; + s << l; + return s.str (); +} + +mgRefParts::mgRefParts(const mgReference& r) +{ + tables.push_back(r.t1()); + tables.push_back(r.t2()); + clauses.push_back(r.t1() + '.' + r.f1() + '=' + r.t2() + '.' + r.f2()); +} + +void +mgParts::Prepare() +{ + tables.sort(); + tables.unique(); + strlist::reverse_iterator it; + string prevtable = ""; + for (it = tables.rbegin(); it != tables.rend(); it++) + { + if (!prevtable.empty()) + *this += ref.Connect(prevtable,*it); + prevtable = *it; + } + tables.sort(); + tables.unique(); + clauses.sort(); + clauses.unique(); + orders.unique(); +} + +string +mgParts::sql_select(bool distinct) +{ + Prepare(); + string result = ""; + if (distinct) + result += sql_list("SELECT DISTINCT",fields); + else + result += sql_list("SELECT",fields); + if (result.empty()) + return result; + result += sql_list("FROM",tables); + result += sql_list("WHERE",clauses," AND "); + result += sql_list("ORDER BY",orders); + optimize(result); + return result; +} + +string +mgParts::sql_count() +{ + Prepare(); + string result = sql_list("SELECT COUNT(DISTINCT",fields,",",")"); + if (result.empty()) + return result; + result += sql_list("FROM",tables); + result += sql_list("WHERE",clauses," AND "); + optimize(result); + return result; +} + +bool +mgParts::UsesTracks() +{ + for (list < string >::iterator it = tables.begin (); it != tables.end (); it++) + if (*it == "tracks") return true; + return false; +} + +string +mgParts::sql_delete_from_collection(string pid) +{ + if (pid.empty()) + return ""; + Prepare(); + // del nach vorne, weil DELETE playlistitem die erste Table nimmt, + // die passt, egal ob alias oder nicht. + tables.push_front("playlistitem as del"); + clauses.push_back("del.playlist="+pid); + // todo geht so nicht fuer andere selections + if (UsesTracks()) + clauses.push_back("del.trackid=tracks.id"); + else + clauses.push_back("del.trackid=playlistitem.trackid"); + string result = "DELETE playlistitem"; + result += sql_list(" FROM",tables); + result += sql_list(" WHERE",clauses," AND "); + optimize(result); + return result; +} + +string +mgParts::sql_update(strlist new_values) +{ + Prepare(); + assert(fields.size()==new_values.size()); + string result = sql_list("UPDATE",fields); + result += sql_list(" FROM",tables); + result += sql_list(" WHERE",clauses," AND "); + result += sql_list("VALUES(",new_values,",",")"); + optimize(result); + return result; +} + +mgReference::mgReference(string t1,string f1,string t2,string f2) +{ + m_t1 = t1; + m_f1 = f1; + m_t2 = t2; + m_f2 = f2; +} + +mgOrder::mgOrder() +{ +} + +mgKey* +mgOrder::Key(unsigned int idx) const +{ + return Keys[idx]; +} + +mgKey*& +mgOrder::operator[](unsigned int idx) +{ + assert(idxset(from.getKeyValue(i),from.getKeyId(i)); + Keys.push_back(k); + } + setDB(from.m_db); + return *this; +} + +mgOrder& +mgOrder::operator+=(mgKey* k) { + k->setdb(m_db); + Keys.push_back(k); + return *this; +} + +mgKeyTypes +mgOrder::getKeyType(unsigned int idx) const +{ + assert(idxType(); +} + +string +mgOrder::getKeyValue(unsigned int idx) const +{ + assert(idxvalue(); +} + +string +mgOrder::getKeyId(unsigned int idx) const +{ + assert(idxid(); +} + +void +mgOrder::setDB(MYSQL *db) +{ + m_db = db; + keyvector::iterator i; + for (i = Keys.begin () ; i != Keys.end (); i++) + { + (*i)->setdb(db); + } +} + +mgKey* +mgOrder::find(const mgKeyTypes kt) +{ + keyvector::iterator i; + for (i = Keys.begin () ; i != Keys.end (); i++) + { + if ((*i)->Type() == kt) + return *i; + } + return 0; +} + +void +mgOrder::truncate(unsigned int i) +{ + while (size()>i) + { + delete Keys.back(); + Keys.pop_back(); + } +} + +void +mgOrder::clean() +{ + // remove double entries: + keyvector::iterator i; + keyvector::iterator j; + bool album_found = false; + bool tracknb_found = false; + bool title_found = false; + for (i = Keys.begin () ; i != Keys.end (); i++) + { + mgKeyNormal* k = dynamic_cast(*i); + album_found |= (k->Type()==keyAlbum); + tracknb_found |= (k->Type()==keyTrack); + title_found |= (k->Type()==keyTitle); + if (tracknb_found || (album_found && title_found)) + { + for (j = i+1 ; j !=Keys.end(); j++) + delete *j; + Keys.erase(i+1,Keys.end ()); + break; + } + for (j = i+1 ; j != Keys.end(); j++) + if (*i == *j) { + delete *j; + Keys.erase(j); + } + } +} + + +mgParts +mgOrder::Parts(unsigned int level,bool orderby) const +{ + assert(strlen(m_db->host)); + mgParts result; + for (unsigned int i=0;i<=level;i++) + { + if (i==Keys.size()) break; + mgKeyNormal *k = dynamic_cast(Keys[i]); + k->setdb(m_db); + result += k->Parts(orderby && (i==level)); + } + return result; +} + +mgReferences::mgReferences() +{ + push_back(mgReference ("tracks","id","playlistitem","trackid")); + push_back(mgReference ("playlist","id","playlistitem","playlist")); + push_back(mgReference ("tracks","sourceid","album","cddbid")); + push_back(mgReference ("tracks","genre1","genre","id")); + push_back(mgReference ("tracks","genre2","genre","id")); +} + +bool +mgReferences::Equal(unsigned int i,string table1, string table2) const +{ + return (((at(i).t1()==table1) && (at(i).t2()==table2)) + || ((at(i).t1()==table2) && (at(i).t2()==table1))); +} + +mgParts +mgReferences::FindConnectionBetween(string table1, string table2) const +{ + for (unsigned int i=0 ; isetdb(db); + return result; +} + +const char * const +ktName(const mgKeyTypes kt) +{ + const char * result = ""; + switch (kt) + { + case keyGenre1: result = "Genre";break; + case keyGenre2: result = "Genre 2";break; + case keyArtist: result = "Artist";break; + case keyTitle: result = "Title";break; + case keyTrack: result = "Track";break; + case keyDecade: result = "Decade";break; + case keyAlbum: result = "Album";break; + case keyCollection: result = "Collection";break; + case keyCollectionItem: result = "Collection item";break; + case keyLanguage: result = "Language";break; + case keyRating: result = "Rating";break; + case keyYear: result = "Year";break; + } + return tr(result); +} + diff --git a/mg_order.h b/mg_order.h new file mode 100644 index 0000000..d46f244 --- /dev/null +++ b/mg_order.h @@ -0,0 +1,154 @@ +#ifndef _MG_SQL_H +#define _MG_SQL_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +typedef list strlist; + +strlist& operator+=(strlist&a, strlist b); + +static const string EMPTY = "XNICHTGESETZTX"; + +//! \brief adds string n to string s, using string sep to separate them +string& addsep (string & s, string sep, string n); + +enum mgKeyTypes { + keyGenre1 = 1, + keyGenre2, + keyArtist, + keyTitle, + keyTrack, + keyDecade, + keyCollection, + keyCollectionItem, + keyAlbum, + keyLanguage, + keyRating, + keyYear, +}; +const mgKeyTypes mgKeyTypesHigh = keyYear; + +class mgParts; + +class mgReference { + public: + mgReference(string t1,string f1,string t2,string f2); + string t1() const { return m_t1; } + string t2() const { return m_t2; } + string f1() const { return m_f1; } + string f2() const { return m_f2; } + private: + string m_t1; + string m_t2; + string m_f1; + string m_f2; +}; + +class mgReferences : public vector { +public: + // \todo memory leak for vector ref? + mgReferences(); + mgParts Connect(string c1, string c2) const; +private: + bool Equal(unsigned int i,string table1, string table2) const; + mgParts FindConnectionBetween(string table1, string table2) const; + mgParts ConnectToTracks(string table) const; +}; + +class mgKey { + public: + mgKey(); + virtual ~mgKey(); + virtual mgParts Parts(bool orderby=false) const = 0; + virtual string id() const = 0; + virtual string value () const = 0; + //!\brief translate field into user friendly string + virtual void set(string value, string id) = 0; + virtual mgKeyTypes Type() const = 0; + virtual string map_idfield() const { return ""; } + virtual string map_valuefield() const { return ""; } + virtual string map_valuetable() const { return ""; } + void setdb(MYSQL *db) { m_db = db; } + protected: + MYSQL *m_db; +}; + + +mgKey* +ktGenerate(const mgKeyTypes kt,MYSQL *db); + +const char * const +ktName(const mgKeyTypes kt); + +typedef vector keyvector; + +class mgParts { +public: + mgParts(); + ~mgParts(); + strlist fields; + strlist tables; + strlist clauses; + strlist orders; + mgParts& operator+=(mgParts a); + void Prepare(); + string sql_count(); + string sql_select(bool distinct=true); + string sql_delete_from_collection(string pid); + string sql_update(strlist new_values); + bool empty() const { return tables.size()==0;} +private: + bool UsesTracks(); + mgReferences ref; +}; + +string +sql_string (MYSQL *db, const string s); + +MYSQL_RES * exec_sql (MYSQL *db,string query); + +//! \brief converts long to string +string itos (int i); + +//! \brief convert long to string +string ltos (long l); + + +const unsigned int MaxKeys = 20; + +class mgOrder { +public: + mgOrder(); + void setDB(MYSQL *db); + mgParts Parts(unsigned int level,bool orderby=true) const; + string Name; + const mgOrder& operator=(const mgOrder& from); + mgOrder& operator+=(mgKey* k); + mgKey*& operator[](unsigned int idx); + unsigned int size() const { return Keys.size(); } + void truncate(unsigned int i); + bool empty() const { return Keys.empty(); } + void clear() { Keys.clear();} + void clean(); + mgKey* Key(unsigned int idx) const; + mgKey* find(const mgKeyTypes kt) ; + mgKeyTypes getKeyType(unsigned int idx) const; + string getKeyValue(unsigned int idx) const; + string getKeyId(unsigned int idx) const; +private: + MYSQL *m_db; + keyvector Keys; +}; + +#endif // _MG_SQL_H diff --git a/mg_valmap.c b/mg_valmap.c new file mode 100644 index 0000000..f5d8e02 --- /dev/null +++ b/mg_valmap.c @@ -0,0 +1,70 @@ +#include "mg_valmap.h" +#include "mg_order.h" + +mgValmap::mgValmap(const char *key) { + m_key = key; +} + +void mgValmap::Read(FILE *f) { + char *line=(char*)malloc(1000); + char *prefix=(char*)malloc(strlen(m_key)+2); + strcpy(prefix,m_key); + strcat(prefix,"."); + rewind(f); + while (fgets(line,1000,f)) { + if (strncmp(line,prefix,strlen(prefix))) continue; + if (line[strlen(line)-1]=='\n') + line[strlen(line)-1]=0; + char *name = line + strlen(prefix); + char *eq = strchr(name,'='); + if (!eq) continue; + *(eq-1)=0; + char *value = eq + 2; + (*this)[string(name)]=string(value); + } + free(prefix); + free(line); +} + +void mgValmap::Write(FILE *f) { + for (mgValmap::const_iterator it=begin();it!=end();++it) { + char b[1000]; + sprintf(b,"%s.%s = %s\n", + m_key,it->first.c_str(), + it->second.c_str()); + fputs(b,f); + } +} + +void mgValmap::put(const char* name, const string value) { + if (value.empty() || value==EMPTY) return; + (*this)[string(name)] = value; +} + +void mgValmap::put(const char* name, const char* value) { + if (!value || *value==0) return; + (*this)[string(name)] = value; +} + +void mgValmap::put(const char* name, const int value) { + put(name,ltos(value)); +} + +void mgValmap::put(const char* name, const unsigned int value) { + put(name,ltos(value)); +} + +void mgValmap::put(const char* name, const long value) { + put(name,ltos(value)); +} + +void mgValmap::put(const char* name, const bool value) { + string s; + if (value) + s = "true"; + else + s = "false"; + put(name,s); +} + + diff --git a/mg_valmap.h b/mg_valmap.h new file mode 100644 index 0000000..a1da822 --- /dev/null +++ b/mg_valmap.h @@ -0,0 +1,50 @@ +#ifndef _MG_VALMAP_H +#define _MG_VALMAP_H +#include +#include + +using namespace std; + +//! \brief a map for reading / writing configuration data. +class mgValmap : public map { + private: + const char *m_key; + public: + /*! \brief constructor + * \param key all names will be prefixed with key. + */ + mgValmap(const char *key); + //! \brief read from file + void Read(FILE *f); + //! \brief write to file + void Write(FILE *f); + //! \brief enter a string value + void put(const char*name, string value); + //! \brief enter a C string value + void put(const char*name, const char* value); + //! \brief enter a long value + void put(const char*name, long value); + //! \brief enter a int value + void put(const char*name, int value); + //! \brief enter a unsigned int value + void put(const char*name, unsigned int value); + //! \brief enter a bool value + void put(const char*name, bool value); + //! \brief return a string + string getstr(const char* name) { + return (*this)[name]; + } + //! \brief return a C string + bool getbool(const char* name) { + return (getstr(name)=="true"); + } + //! \brief return a long + long getlong(const char* name) { + return atol(getstr(name).c_str()); + } + //! \brief return an unsigned int + unsigned int getuint(const char* name) { + return (unsigned long)getlong(name); + } +}; +#endif diff --git a/muggle.c b/muggle.c index 62bfe75..c4e1831 100644 --- a/muggle.c +++ b/muggle.c @@ -63,7 +63,6 @@ mgMuggle::mgMuggle (void) mgMuggle::~mgMuggle () { -// Clean up after yourself! if (main) main->SaveState(); } diff --git a/vdr_decoder.c b/vdr_decoder.c index 9b47fa8..9a7bb7c 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -23,7 +23,14 @@ #include "vdr_decoder.h" #include "vdr_decoder_mp3.h" + +#ifdef HAVE_VORBISFILE #include "vdr_decoder_ogg.h" +#endif + +#ifdef HAVE_FLAC +#include "vdr_decoder_flac.h" +#endif #include "mg_db.h" @@ -34,8 +41,7 @@ mgMediaType mgDecoders::getMediaType (std::string s) { - mgMediaType - mt = MT_UNKNOWN; + mgMediaType mt = MT_UNKNOWN; // TODO: currently handles only mp3. LVW char * @@ -56,6 +62,13 @@ mgMediaType mgDecoders::getMediaType (std::string s) { mt = MT_OGG; } + else + { + if (!strcmp (p, ".flac")) + { + mt = MT_FLAC; + } + } } return mt; } @@ -76,17 +89,21 @@ mgDecoders::findDecoder (mgContentItem * item) } switch (getMediaType (filename)) { - case MT_MP3: - { - decoder = new mgMP3Decoder (item); - } - break; + case MT_MP3: + { + decoder = new mgMP3Decoder (item); + } break; #ifdef HAVE_VORBISFILE - case MT_OGG: - { - decoder = new mgOggDecoder (item); - } - break; + case MT_OGG: + { + decoder = new mgOggDecoder (item); + } break; +#endif +#ifdef HAVE_FLAC + case MT_FLAC: + { + decoder = new mgFlacDecoder( item ); + } break; #endif /* case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; @@ -124,23 +141,21 @@ mgDecoder::mgDecoder (mgContentItem * item) m_playing = false; } - mgDecoder::~mgDecoder () { } - void mgDecoder::lock (bool urgent) { m_locklock.Lock (); if (urgent && m_locked) - { + { m_urgentLock = true; // signal other locks to release quickly - } + } m_locked++; - + m_locklock.Unlock (); // don't hold the "locklock" when locking "lock", may cause a deadlock m_lock.Lock (); m_urgentLock = false; diff --git a/vdr_decoder.h b/vdr_decoder.h index ee943ce..3dd6c00 100644 --- a/vdr_decoder.h +++ b/vdr_decoder.h @@ -82,71 +82,71 @@ class mgDecoder { protected: -/*! \brief database handle to the track being decoded */ - mgContentItem * m_item; - -/*! \brief The currently playing file */ - std::string m_filename; - -/*! \brief Mutexes to coordinate threads */ - cMutex m_lock, m_locklock; - int m_locked; - bool m_urgentLock; - -/*! \brief Whether the decoder is currently active */ - bool m_playing; - -/*! \brief ??? */ - mgPlayInfo m_playinfo; - -/*! \brief Place a lock */ - virtual void lock (bool urgent = false); - -/*! \brief Release a lock */ - virtual void unlock (void); - -/*! \brief Try to obtain a lock */ - virtual bool tryLock (void); - - public: - -//@{ -/*! \brief The constructor */ - mgDecoder (mgContentItem * item); - -/*! \brief The destructor */ - virtual ~ mgDecoder (); -//@} - -/*! \brief Whether a decoder instance is able to play the given file */ - virtual bool valid () = 0; - -/*! \brief Whether a stream (i.e. from the network is being decoded */ - virtual bool isStream () - { - return false; - } - -/*! \brief Start decoding */ - virtual bool start () = 0; - -/*! \brief Stop decoding */ - virtual bool stop () = 0; - -/*! \brief Skip an amount of time. Impossible by default */ - virtual bool skip (int seconds, int avail, int rate) - { - return false; - } - -/*! \brief Return decoded data */ - virtual struct mgDecode *decode () = 0; - -/*! \brief Information about the current playback status */ - virtual mgPlayInfo *playInfo () - { - return 0; - } + /*! \brief database handle to the track being decoded */ + mgContentItem * m_item; + + /*! \brief The currently playing file */ + std::string m_filename; + + /*! \brief Mutexes to coordinate threads */ + cMutex m_lock, m_locklock; + int m_locked; + bool m_urgentLock; + + /*! \brief Whether the decoder is currently active */ + bool m_playing; + + /*! \brief ??? */ + mgPlayInfo m_playinfo; + + /*! \brief Place a lock */ + virtual void lock (bool urgent = false); + + /*! \brief Release a lock */ + virtual void unlock (void); + + /*! \brief Try to obtain a lock */ + virtual bool tryLock (void); + + public: + + //@{ + /*! \brief The constructor */ + mgDecoder (mgContentItem * item); + + /*! \brief The destructor */ + virtual ~ mgDecoder (); + //@} + + /*! \brief Whether a decoder instance is able to play the given file */ + virtual bool valid () = 0; + + /*! \brief Whether a stream (i.e. from the network is being decoded */ + virtual bool isStream () + { + return false; + } + + /*! \brief Start decoding */ + virtual bool start () = 0; + + /*! \brief Stop decoding */ + virtual bool stop () = 0; + + /*! \brief Skip an amount of time. Impossible by default */ + virtual bool skip (int seconds, int avail, int rate) + { + return false; + } + + /*! \brief Return decoded data */ + virtual struct mgDecode *decode () = 0; + + /*! \brief Information about the current playback status */ + virtual mgPlayInfo *playInfo () + { + return 0; + } }; // ---------------------------------------------------------------- @@ -156,15 +156,15 @@ class mgDecoder */ class mgDecoders { - public: - -/*! \brief Try to find a valid decoder for a file - */ - static mgDecoder *findDecoder (mgContentItem * item); - -/*! \brief determine the media type for a given source - */ - static mgMediaType getMediaType (std::string filename); - + public: + + /*! \brief Try to find a valid decoder for a file + */ + static mgDecoder *findDecoder (mgContentItem * item); + + /*! \brief determine the media type for a given source + */ + static mgMediaType getMediaType (std::string filename); + }; #endif //___DECODER_H diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c new file mode 100644 index 0000000..1a66e8d --- /dev/null +++ b/vdr_decoder_flac.c @@ -0,0 +1,380 @@ +/*! \file vdr_decoder_flac.c + * \ingroup vdr + * + * The file implements a decoder which is used by the player to decode flac audio files. + * + * Based on code from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001-2003 Stefan Huelswitt + */ + +#ifdef HAVE_FLAC + +#define DEBUG +#include "vdr_decoder_flac.h" +#include "mg_tools.h" +#include "mg_db.h" + +#include + +#include +#include +#include + +using namespace std; + +static const unsigned MAX_RES_SIZE = 16384; + +// --- mgFlacDecoder ------------------------------------------------------------- + +mgFlacDecoder::mgFlacDecoder( mgContentItem *item ) + : mgDecoder( item ), FLAC::Decoder::File() +{ + mgLog lg( "mgFlacDecoder::mgFlacDecoder" ); + + m_filename = item->getSourceFile(); + // m_filename = "/test.flac"; + m_pcm = 0; + m_reservoir = 0; + + initialize(); +} + +mgFlacDecoder::~mgFlacDecoder() +{ + mgLog lg( "mgFlacDecoder::~mgFlacDecoder" ); + clean(); +} + +bool mgFlacDecoder::valid() +{ + // how to check whether this is a valid flac file? + return is_valid(); +} + +mgPlayInfo *mgFlacDecoder::playInfo(void) +{ + return 0; +} + +bool mgFlacDecoder::initialize() +{ + mgLog lg( "mgFlacDecoder::initialize" ); + bool state = true; + + clean(); + + // set_metadata_ignore_all(); + set_metadata_respond( FLAC__METADATA_TYPE_STREAMINFO ); + set_filename( m_filename.c_str() ); + + m_first = true; + m_reservoir_count = 0; + m_current_time_ms = 0; + m_len_decoded = 0; + m_index = 0; + m_pcm = new struct mad_pcm; + + // init reservoir buffer; this should be according to the maximum + // frame/sample size that we can probably obtain from metadata + m_reservoir = new (FLAC__int32*)[2]; + m_reservoir[0] = new FLAC__int32[MAX_RES_SIZE]; + m_reservoir[1] = new FLAC__int32[MAX_RES_SIZE]; + + FLAC::Decoder::File::State d = init(); // TODO check this? + + cout << "Status of decoder: " << string( d.as_cstring() ) << endl; + FLAC::Decoder::SeekableStream::State ssd = get_seekable_stream_decoder_state(); + cout << "Status of seekable stream decoder: " << string( ssd.as_cstring() ) << endl; + FLAC::Decoder::Stream::State sd = get_stream_decoder_state(); + cout << "Status of stream decoder: " << string( sd.as_cstring() ) << endl; + // set state accordingly + + process_until_end_of_metadata(); // basically just skip metadata + + return state; +} + +bool mgFlacDecoder::clean() +{ + mgLog lg( "mgFlacDecoder::clean" ); + m_playing = false; + + delete m_pcm; + m_pcm = 0; + + if( m_reservoir ) + { + delete m_reservoir[0]; + delete m_reservoir[1]; + } + delete m_reservoir; + + // why false? true? + return true; +} + +bool mgFlacDecoder::start() +{ + MGLOG( "mgFlacDecoder::start" ); + bool res = false; + lock(true); + + /* + FLAC::Decoder::File::State d = get_state(); + cout << "Status of decoder: " << string( d.as_cstring() ) << endl; + FLAC::Decoder::SeekableStream::State ssd = get_seekable_stream_decoder_state(); + cout << "Status of seekable stream decoder: " << string( ssd.as_cstring() ) << endl; + FLAC::Decoder::Stream::State sd = get_stream_decoder_state(); + cout << "Status of stream decoder: " << string( sd.as_cstring() ) << endl; + */ + + // can FLAC handle more than 2 channels anyway? + if( m_item->getChannels() <= 2 ) + { + m_playing = true; + res = true; + } + else + { + mgError( "ERROR: cannot play flac file %s: more than 2 channels", m_filename.c_str() ); + clean(); + } + + unlock(); + return res; +} + +bool mgFlacDecoder::stop(void) +{ + MGLOG( "mgFlacDecoder::stop" ); + lock(); + finish(); + + if( m_playing ) + { + clean(); + } + unlock(); + + return true; +} + +struct mgDecode *mgFlacDecoder::done( eDecodeStatus status ) +{ + m_ds.status = status; + m_ds.index = m_index; + m_ds.pcm = m_pcm; + + unlock(); // release the lock from Decode() ! + + return &m_ds; +} + +struct mgDecode *mgFlacDecoder::decode() +{ + // mgLog lg( "mgFlacDecoder::decode" ); + m_decode_status = dsPlay; + + const unsigned SF_SAMPLES = (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t)); + + lock(true); // this is released in done() + + if( m_playing ) + { + m_pcm->samplerate = m_item->getSampleRate(); // from database + m_pcm->channels = m_item->getChannels(); // from database + + // if there is enough data in the reservoir, don't start decoding + // PROBLEM: but we need a first time! + bool finished; + if( m_first ) + { + finished = false; + m_first = false; + } + else + { + finished = m_reservoir_count >= SF_SAMPLES; + } + + while( !finished ) + { // decode single frames until m_reservoir_count >= SF_SAMPLES or eof/error + m_first = false; + + // decode a single sample into reservoir_buffer (done by the write callback) + process_single(); + if (get_stream_decoder_state()==FLAC__STREAM_DECODER_END_OF_STREAM) + { + m_decode_status = dsEof; + finished = true; + } + + // check termination criterion + finished |= m_reservoir_count >= SF_SAMPLES || m_len_decoded == 0; // or error? + } + + // transfer min( SF_SAMPLES, m_reservoir_count ) to pcm buffer + + int n = ( SF_SAMPLES <= m_reservoir_count )? SF_SAMPLES: m_reservoir_count; + + m_pcm->length = n; + m_index = m_current_time_ms; + + // fill pcm container from reservoir buffer + FLAC__int32 *data0 = m_reservoir[0]; + FLAC__int32 *data1 = m_reservoir[1]; + + mad_fixed_t *sam0 = m_pcm->samples[0]; + mad_fixed_t *sam1 = m_pcm->samples[1]; + + // determine shift value for mad_fixed conversion + // TODO -- check for real bitsize and shit accordingly (left/right) + const int s = MAD_F_FRACBITS + 1 - ( sizeof(short)*8 ); + // const int s = ( sizeof(int)*8 ) - 1 - MAD_F_FRACBITS; // from libsoundfile decoder + + if( m_pcm->channels > 1 ) + { + for( int j=n; j > 0 ; j-- ) + { + // copy buffer and transform (cf. libsoundfile decoder) + *sam0++ = (*data0++) << s; + *sam1++ = (*data1++) << s; + } + // "delete" transferred samples from reservoir buffer + memmove( m_reservoir[0], m_reservoir[0] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) ); + memmove( m_reservoir[1], m_reservoir[1] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) ); + } + else + { + for( int j=n; j > 0 ; j--) + { + *sam0++ = (*data0++) << s; + } + memmove( m_reservoir[0], m_reservoir[0] + n, (m_reservoir_count - n)*sizeof(FLAC__int32) ); + } + m_reservoir_count -= n; + + FLAC::Decoder::File::State d = get_state(); + FLAC::Decoder::SeekableStream::State ssd = get_seekable_stream_decoder_state(); + FLAC::Decoder::Stream::State sd = get_stream_decoder_state(); + // if any states are not okay, abort and override m_decode_state + + // and return, indicating that playing will continue (unless an error has occurred) + return done( m_decode_status ); + } + + return done(dsError); +} + +::FLAC__StreamDecoderWriteStatus +mgFlacDecoder::write_callback(const ::FLAC__Frame *frame, + const FLAC__int32 * const buffer[]) +{ + mgLog lg( "mgFlacDecoder::write_callback" ); + ::FLAC__StreamDecoderWriteStatus retval; + + // add decoded buffer to reservoir + m_len_decoded = frame->header.blocksize; + m_current_samples += m_len_decoded; + m_current_time_ms += (m_len_decoded*1000) / m_pcm->samplerate; // in milliseconds + + // cout << "Samples decoded: " << m_len_decoded << ", current time: " << m_current_time_ms << ", bits per sample: "<< m_nBitsPerSample << endl << flush; + + // append buffer to m_reservoir + if( m_len_decoded > 0 ) + { + memmove( m_reservoir[0] + m_reservoir_count, buffer[0], m_len_decoded*sizeof(FLAC__int32) ); + + if( m_pcm->channels > 1 ) + { + memmove( m_reservoir[1] + m_reservoir_count, buffer[1], m_len_decoded*sizeof(FLAC__int32) ); + } + + m_reservoir_count += m_len_decoded; + } + else + { + m_decode_status = dsEof; + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void mgFlacDecoder::metadata_callback( const ::FLAC__StreamMetadata *metadata ) +{ + // not needed since metadata is ignored!? + mgLog lg( "mgFlacDecoder::metadata_callback" ); + + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) + { + m_nTotalSamples = (unsigned)(metadata->data.stream_info.total_samples & 0xfffffffful); + m_nBitsPerSample = metadata->data.stream_info.bits_per_sample; + m_nCurrentChannels = metadata->data.stream_info.channels; + m_nCurrentSampleRate = metadata->data.stream_info.sample_rate; + m_nFrameSize = metadata->data.stream_info.max_framesize; + m_nBlockSize = metadata->data.stream_info.max_blocksize; + + m_nCurrentSampleRate = (int)get_sample_rate(); + m_nCurrentChannels = (int)get_channels(); + m_nCurrentBitsPerSample = (int)get_bits_per_sample(); + + // m_nLengthMS = m_nTotalSamples * 10 / (m_nCurrentSampleRate / 100); + m_nBlockAlign = (m_nBitsPerSample / 8) * m_nCurrentChannels; + + // m_nAverageBitRate = ((m_pReader->GetLength() * 8) / (m_nLengthMS / 1000) / 1000); + // m_nCurrentBitrate = m_nAverageBitRate; + } +} + +void mgFlacDecoder::error_callback( ::FLAC__StreamDecoderErrorStatus status ) +{ + mgLog lg( "mgFlacDecoder::error_callback" ); + + // check status and return + switch( status ) + { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + { + m_error = "An error in the stream caused the decoder to lose synchronization"; + } break; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + { + m_error = "The decoder encountered a corrupted frame header."; + } break; + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + { + m_error = "The frame's data did not match the CRC in the footer."; + }; + default: + { + m_error = "Unknown error occurred."; + } + } + + cout << "Error occured: " << m_error << endl; + m_decode_status = dsError; +} + +bool mgFlacDecoder::skip(int seconds, int avail, int rate) +{ + lock(); + bool res = false; + + if( m_playing ) + { + const long target_time_ms = ((seconds-avail)*rate / 1000) + m_current_time_ms; + const double distance = target_time_ms / (double)m_nLengthMS; + const unsigned target_sample = (unsigned)(distance * (double)m_nTotalSamples); + + if( seek_absolute( (FLAC__uint64)target_sample) ) + { + m_current_time_ms = target_time_ms; + res = true; + } + } + unlock(); + return res; +} + +#endif //HAVE_FLAC diff --git a/vdr_decoder_flac.h b/vdr_decoder_flac.h new file mode 100644 index 0000000..581d1af --- /dev/null +++ b/vdr_decoder_flac.h @@ -0,0 +1,80 @@ +/*! \file vdr_decoder_flac.h + * \ingroup vdr + * + * The file contains a decoder which is used by the player to decode flac audio files. + * + * Based on code from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001-2003 Stefan Huelswitt + */ + +#ifndef ___DECODER_FLAC_H +#define ___DECODER_FLAC_H + +#define DEC_FLAC DEC_ID('F','L','A','C') +#define DEC_FLAC_STR "FLAC" + +#ifdef HAVE_FLAC + +#include "vdr_decoder.h" +#include + +// ---------------------------------------------------------------- + +class mgFlacDecoder : public mgDecoder, + public FLAC::Decoder::File +{ + private: + + struct mgDecode m_ds; + struct mad_pcm *m_pcm; + unsigned long long m_index, m_current_time_ms, m_current_samples; + unsigned int m_reservoir_count, m_len_decoded, m_samplerate; + + long m_nCurrentSampleRate, m_nCurrentChannels, m_nCurrentBitsPerSample; + long m_nLengthMS, m_nBlockAlign, m_nAverageBitRate, m_nCurrentBitrate; + long m_nTotalSamples, m_nBitsPerSample, m_nFrameSize, m_nBlockSize; + + bool m_state, m_first, m_continue; + std::string m_error; + + FLAC__int32 **m_reservoir; + + bool initialize(); + bool clean(); + + struct mgDecode* done(eDecodeStatus status); + eDecodeStatus m_decode_status; + + protected: + + /*! \brief FLAC decoder callback routines */ + //@{ + virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, + const FLAC__int32 * const buffer[]); + virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata); + virtual void error_callback(::FLAC__StreamDecoderErrorStatus status); + //@} + + public: + + mgFlacDecoder( mgContentItem *item ); + ~mgFlacDecoder(); + + virtual mgPlayInfo *playInfo(); + + virtual bool valid(); + + virtual bool start(); + + virtual struct mgDecode *decode(void); + + virtual bool skip(int Seconds, int Avail, int Rate); + + virtual bool stop(); +}; + +// ---------------------------------------------------------------- + +#endif //HAVE_FLAC +#endif //___DECODER_FLAC_H diff --git a/vdr_menu.c b/vdr_menu.c index 6871eb7..e3db994 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -42,15 +42,10 @@ mgStatus::OsdCurrentItem(const char* Text) { cOsdItem* i = main->Get(main->Current()); if (!i) return; - if (i == IgnoreNextEventOn) - { - IgnoreNextEventOn = NULL; - return; - } mgAction * a = dynamic_cast(i); if (!a) mgError("mgStatus::OsdCurrentItem expected an mgAction*"); - a->Notify(); + a->TryNotify(); } void Play(mgSelection *sel,const bool select) { @@ -86,6 +81,26 @@ mgMainMenu::PlayInstant(const bool select) Play(selection(),select); } +void +mgMainMenu::CollectionChanged(string name) +{ + delete moveselection; + moveselection = NULL; + forcerefresh = true; // TODO brauchen wir das? + if (name == play_collection) + { + playselection()->clearCache(); + mgPlayerControl *c = PlayerControl(); + if (c) + c->ReloadPlaylist(); + else + PlayQueue(); + } + if (CollectionEntered(name)) + { + selection()->clearCache(); + } +} bool mgMainMenu::ShowingCollections() @@ -101,14 +116,13 @@ mgMainMenu::DefaultCollectionSelected() return (ShowingCollections () && this_sel == default_collection); } - bool -mgMainMenu::DefaultCollectionEntered() +mgMainMenu::CollectionEntered(string name) { if (!UsingCollection) return false; if (selection()->level()==0) return false; string collection = trim(selection ()->getKeyValue(0)); - return (collection == default_collection); + return (collection == name); } @@ -121,14 +135,14 @@ mgMainMenu::Parent () } -mgOsdItem* -mgMenu::GenerateAction(const mgActions action) +mgAction* +mgMenu::GenerateAction(const mgActions action,mgActions on) { - mgOsdItem *result = actGenerate(action); + mgAction *result = actGenerate(action); if (result) { result->SetMenu(this); - if (!result->Enabled()) + if (!result->Enabled(on)) { delete result; result=NULL; @@ -138,9 +152,9 @@ mgMenu::GenerateAction(const mgActions action) } void -mgMenu::ExecuteAction(const mgActions action) +mgMenu::ExecuteAction(const mgActions action,mgActions on) { - mgAction *a = GenerateAction (action); + mgAction *a = GenerateAction (action,on); if (a) { a->Execute (); @@ -206,6 +220,8 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") { m_Status = new mgStatus(this); m_message = NULL; + moveselection = NULL; + external_commands = NULL; queue_playing=false; instant_playing=false; play_collection = tr("play"); @@ -214,8 +230,11 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") mgValmap nmain("MainMenu"); // define defaults for values missing in state file: - ncol.put("Keys.0.Choice","Collection"); - ncol.put("Keys.1.Choice","Collection item"); + nsel.put("Keys.0.Choice",keyArtist); + nsel.put("Keys.1.Choice",keyAlbum); + nsel.put("Keys.2.Choice",keyTitle); + ncol.put("Keys.0.Choice",keyCollection); + ncol.put("Keys.1.Choice",keyCollectionItem); nmain.put("DefaultCollection",play_collection); nmain.put("UsingCollection","false"); nmain.put("TreeRedAction",int(actAddThisToCollection)); @@ -249,11 +268,13 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") // initialize m_collectionsel.CreateCollection(default_collection); m_collectionsel.CreateCollection(play_collection); - m_playsel.setKey(0,"Collection"); - m_playsel.setKey(1,"Collection item"); + m_playsel.setKey(0,keyCollection); + m_playsel.setKey(1,keyCollectionItem); + m_playsel.leave(0); m_playsel.enter(play_collection); UseNormalSelection (); unsigned int posi = selection()->gotoPosition(); + LoadExternalCommands(); // before AddMenu() mgMenu *root = new mgTree; root->TreeRedAction = mgActions(nmain.getuint("TreeRedAction")); root->TreeGreenAction = mgActions(nmain.getuint("TreeGreenAction")); @@ -261,10 +282,16 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") root->CollRedAction = mgActions(nmain.getuint("CollRedAction")); root->CollGreenAction = mgActions(nmain.getuint("CollGreenAction")); root->CollYellowAction = mgActions(nmain.getuint("CollYellowAction")); - AddMenu (root); + AddMenu (root,posi); - SetCurrent (Get (posi)); + //SetCurrent (Get (posi)); + forcerefresh = false; +} + +void +mgMainMenu::LoadExternalCommands() +{ // Read commands for collections in etc. /video/muggle/playlist_commands.conf external_commands = new cCommands (); @@ -286,12 +313,13 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") delete external_commands; external_commands = NULL; } - forcerefresh = false; } mgMainMenu::~mgMainMenu() { delete m_Status; + if (moveselection) + delete moveselection; } void @@ -306,9 +334,9 @@ mgMainMenu::InitMapFromSetup (mgValmap& nv) } void -mgMenu::AddAction (const mgActions action, const bool hotkey) +mgMenu::AddAction (const mgActions action, mgActions on,const bool hotkey) { - mgOsdItem *a = GenerateAction(action); + mgAction *a = GenerateAction(action,on); if (!a) return; const char *mn = a->MenuName(); if (strlen(mn)==0) @@ -318,81 +346,88 @@ mgMenu::AddAction (const mgActions action, const bool hotkey) else a->SetText(mn); free(const_cast(mn)); - osd()->Add(a); + osd()->AddItem(a); } void mgMenu::AddExternalAction(const mgActions action, const char *title) { - mgOsdItem *a = GenerateAction(action); + mgAction *a = GenerateAction(action,mgActions(0)); if (!a) return; a->SetText(osd()->hk(title)); - osd()->Add(a); + osd()->AddItem(a); } void -mgMenu::AddSelectionItems () +mgMenu::AddSelectionItems (mgSelection *sel,mgActions act) { - for (unsigned int i = 0; i < selection()->values.size (); i++) + for (unsigned int i = 0; i < sel->values.size (); i++) { - mgOsdItem *a = GenerateAction(actEntry); + mgAction *a = GenerateAction(act, actEntry); if (!a) continue; - a->SetText(a->MenuName(i+1,selection()->values[i]),false); - osd()->Add(a); + a->SetText(a->MenuName(i+1,sel->values[i]),false); + osd()->AddItem(a); } if (osd()->ShowingCollections ()) { - mgCreateCollection *a = new mgCreateCollection; - if (!a) return; - a->SetMenu(this); - if (!a->Enabled()) + mgAction *a = GenerateAction(actCreateCollection,mgActions(0)); + if (a) { - delete a; - a=NULL; + a->SetText(a->MenuName(),false); + osd()->AddItem(a); } - if (!a) return; - a->SetText(a->MenuName(),false); - osd()->Add(a); } } const char * -mgMenu::BlueName () +mgMenu::BlueName (mgActions on) { - if (typeid (*this) == typeid (mgTree)) - return tr("Commands"); + // do not use typeid because we want to catch descendants too + if (dynamic_cast(this)) + return tr ("List"); + else if (osd()->Current()<0) + return tr("Commands"); + else if (dynamic_cast(this)) + return tr("Commands"); else return tr ("List"); } +const char* +mgMenu::HKey(const mgActions act,mgActions on) +{ + const char* result = NULL; + mgAction *a = GenerateAction(act,on); + if (a) + { + result = a->ButtonName(); + delete a; + } + return result; +} + void -mgMenu::SetHelpKeys() +mgMenu::SetHelpKeys(mgActions on) { - const char *Red = NULL; - const char *Green = NULL; - const char *Yellow = NULL; - mgOsdItem *a; + mgActions r,g,y; if (osd()->UsingCollection) { - if ((a = GenerateAction (CollRedAction))) - Red = a->ButtonName (); - if ((a = GenerateAction (CollGreenAction))) - Green = a->ButtonName (); - if ((a = GenerateAction (CollYellowAction))) - Yellow = a->ButtonName (); + r = CollRedAction; + g = CollGreenAction; + y = CollYellowAction; } - else + else { - if ((a = GenerateAction (TreeRedAction))) - Red = a->ButtonName (); - if ((a = GenerateAction (TreeGreenAction))) - Green = a->ButtonName (); - if ((a = GenerateAction (TreeYellowAction))) - Yellow = a->ButtonName (); + r = TreeRedAction; + g = TreeGreenAction; + y = TreeYellowAction; } - osd()->SetHelpKeys(Red,Green,Yellow,BlueName()); + osd()->SetHelpKeys(HKey(r,on), + HKey(g,on), + HKey(y,on), + BlueName(on)); } @@ -400,6 +435,7 @@ void mgMenu::InitOsd (const char *title,const bool hashotkeys) { osd ()->InitOsd (title,hashotkeys); + SetHelpKeys(); // Default, will be overridden by the single items } @@ -411,24 +447,35 @@ mgMainMenu::InitOsd (const char *title,const bool hashotkeys) if (hashotkeys) SetHasHotkeys (); } +void +mgMainMenu::AddItem(mgAction *a) +{ + cOsdItem *c = dynamic_cast(a); + if (!c) + mgError("AddItem with non cOsdItem"); + Add(c); +} void mgSubmenu::BuildOsd () { static char b[100]; snprintf(b,99,tr("Commands:%s"),trim(osd()->selection()->getCurrentValue()).c_str()); + mgActions on = osd()->CurrentType(); InitOsd (b); mgMenu *p = osd ()->Parent (); if (!p) return; - AddAction(actInstantPlay); - AddAction(actAddThisToCollection); - AddAction(actRemoveThisFromCollection); - AddAction(actToggleSelection); - AddAction(actSetDefault); - AddAction(actDeleteCollection); - AddAction(actChooseSearch); - AddAction(actExportTracklist); + AddAction(actInstantPlay,on); + AddAction(actAddThisToCollection,on); + AddAction(actAddThisToDefaultCollection,on); + AddAction(actSetDefaultCollection,on); + AddAction(actRemoveThisFromCollection,on); + AddAction(actToggleSelection,on); + AddAction(actDeleteCollection,on); + AddAction(actClearCollection,on); + AddAction(actChooseOrder,on); + AddAction(actExportTracklist,on); cCommand *command; if (osd()->external_commands) { @@ -452,43 +499,65 @@ mgSubmenu::BuildOsd () CollYellowAction = actSetButton; } +mgActions +mgMainMenu::CurrentType() +{ + mgActions result = mgActions(0); + cOsdItem* c = Get(Current()); + if (c) + { + mgAction *a = dynamic_cast(c); + if (!a) + mgError("Found an OSD item which is not mgAction:%s",c->Text()); + result = a->Type(); + } + return result; +} eOSState -mgTree::Process (eKeys key) +mgMenu::ExecuteButton(eKeys key) { - eOSState result = osUnknown; + mgActions on = osd()->CurrentType(); switch (key) { case kRed: if (osd()->UsingCollection) - ExecuteAction (CollRedAction); + ExecuteAction (CollRedAction,on); else - ExecuteAction (TreeRedAction); + ExecuteAction (TreeRedAction,on); return osContinue; case kGreen: if (osd()->UsingCollection) - ExecuteAction (CollGreenAction); + ExecuteAction (CollGreenAction,on); else - ExecuteAction (TreeGreenAction); + ExecuteAction (TreeGreenAction,on); return osContinue; case kYellow: if (osd()->UsingCollection) - ExecuteAction (CollYellowAction); + ExecuteAction (CollYellowAction,on); else - ExecuteAction (TreeYellowAction); + ExecuteAction (TreeYellowAction,on); + return osContinue; + case kBlue: + osd()->newmenu = new mgSubmenu; return osContinue; default: - result = osUnknown; break; } - return result; + return osUnknown; +} + +eOSState +mgTree::Process (eKeys key) +{ + return ExecuteButton(key); } void mgTree::BuildOsd () { InitOsd (selection ()->getListname ().c_str (), false); - AddSelectionItems (); + AddSelectionItems (selection()); } void @@ -502,8 +571,6 @@ mgMainMenu::Message1(const char *msg, const char *arg1) eOSState mgMainMenu::ProcessKey (eKeys key) { eOSState result = osContinue; - if (key!=kNone) - mgDebug (3, "MainMenu::ProcessKey(%d)", (int) key); if (Menus.size()<1) mgError("mgMainMenu::ProcessKey: Menus is empty"); @@ -562,21 +629,21 @@ otherkeys: newposition = -1; { - mgMenu * oldmenu = newmenu; + mgMenu * oldmenu = newmenu; // item specific key logic: - result = cOsdMenu::ProcessKey (key); + result = cOsdMenu::ProcessKey (key); // mgMenu specific key logic: - if (result == osUnknown) - result = oldmenu->Process (key); + if (result == osUnknown) + result = oldmenu->Process (key); } -// catch osBack for empty OSD lists -// (because if the list was empty, no mgOsdItem::ProcessKey was ever called) +// catch osBack for empty OSD lists . This should only happen for playlistitems +// (because if the list was empty, no mgActions::ProcessKey was ever called) if (result == osBack) { // do as if there was an entry - mgOsdItem *a = Menus.back()->GenerateAction(actEntry); + mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry); if (a) { result = a->Back(); @@ -593,7 +660,7 @@ otherkeys: { if (Menus.size () > 1) { - Menus.pop_back (); + CloseMenu(); forcerefresh = true; } else @@ -603,9 +670,7 @@ otherkeys: } } else if (newmenu != Menus.back ()) - { - AddMenu (newmenu); - } + AddMenu (newmenu,newposition); if (UsingCollection) forcerefresh |= m_collectionsel.cacheIsEmpty(); @@ -616,7 +681,6 @@ otherkeys: if (forcerefresh) { - mgDebug(2,"forced refresh"); forcerefresh = false; if (newposition<0) newposition = selection()->gotoPosition(); @@ -627,13 +691,21 @@ pr_exit: return result; } +void +mgMainMenu::CloseMenu() +{ + mgMenu* m = Menus.back(); + Menus.pop_back (); + delete m; +} + void mgMainMenu::showMessage() { if (m_message) { #if VDRVERSNUM >= 10307 - Skins.Message (mtInfo, m_message); + Skins.Message (mtInfo, m_message,2); Skins.Flush (); #else Interface->Status (m_message); @@ -646,13 +718,19 @@ mgMainMenu::showMessage() void -mgMainMenu::AddMenu (mgMenu * m) +mgMainMenu::AddMenu (mgMenu * m,unsigned int position) { Menus.push_back (m); m->setosd (this); - m->Display (0); + m->Display (position); } +void +mgMenu::setosd(mgMainMenu *osd) +{ + m_osd = osd; + m_prevUsingCollection = osd->UsingCollection; +} eOSState mgSubmenu::Process (eKeys key) @@ -662,15 +740,46 @@ mgSubmenu::Process (eKeys key) void -mgTreeViewSelector::BuildOsd () -{ - InitOsd (tr ("Tree View Selection")); - AddAction(actSearchCollItem); - AddAction(actSearchArtistAlbumTitle); - AddAction(actSearchArtistTitle); - AddAction(actSearchAlbumTitle); - AddAction(actSearchGenreYearTitle); - AddAction(actSearchGenreArtistAlbumTitle); +mgMenuOrders::BuildOsd () +{ + InitOsd (tr ("Select an order")); + AddAction(ActOrderCollItem); + AddAction(ActOrderArtistAlbumTitle); + AddAction(ActOrderArtistTitle); + AddAction(ActOrderAlbumTitle); + AddAction(ActOrderGenreYearTitle); + AddAction(ActOrderGenreArtistAlbumTitle); +} + + +mgTreeCollSelector::~mgTreeCollSelector() +{ + osd()->UsingCollection = m_prevUsingCollection; +} + +void +mgTreeCollSelector::BuildOsd () +{ + osd()->UsingCollection = true; + mgSelection *coll = osd()->collselection(); + InitOsd (m_title.c_str()); + coll->leave(0); + coll->setPosition(osd()->default_collection); + AddSelectionItems (coll,coll_action()); + osd()->newposition = coll->gotoPosition(0); + cOsdItem *c = osd()->Get(osd()->newposition); + mgAction *a = dynamic_cast(c); + a->IgnoreNextEvent = true; +} + +mgTreeAddToCollSelector::mgTreeAddToCollSelector(string title) +{ + m_title = title; +} + +mgTreeRemoveFromCollSelector::mgTreeRemoveFromCollSelector(string title) +{ + m_title = title; } void diff --git a/vdr_menu.h b/vdr_menu.h index 834a36c..faadb87 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -48,14 +48,7 @@ class mgStatus : public cStatus mgMainMenu *main; public: //! \brief default constructor - mgStatus(mgMainMenu* m) { main = m;IgnoreNextEventOn=NULL;} - /*! \brief vdr calls OsdCurrentItem more often than we - * want. This tells mgStatus to ignore the next call - * for a specific item. - * \todo is this behaviour intended or a bug in vdr - * or in muggle ? - */ - cOsdItem* IgnoreNextEventOn; + mgStatus(mgMainMenu* m) { main = m;} protected: //! \brief the event we want to know about virtual void OsdCurrentItem(const char *Text); @@ -74,7 +67,11 @@ class mgMainMenu:public cOsdMenu mgSelection m_collectionsel; char *m_message; void showMessage(); + void LoadExternalCommands(); public: + mgSelection *moveselection; + mgActions CurrentType(); + //! \brief syntactic sugar: expose the protected cOsdMenu::SetHelp void SetHelpKeys(const char *Red,const char *Green, const char *Yellow, const char *Blue) { SetHelp(Red,Green,Yellow,Blue); } @@ -112,7 +109,7 @@ class mgMainMenu:public cOsdMenu void SaveState(); //! \brief adds a new mgMenu to the stack - void AddMenu (mgMenu * m); + void AddMenu (mgMenu * m,unsigned int position=0); //! \brief initializes using values from nv void InitMapFromSetup (mgValmap& nv); @@ -171,7 +168,7 @@ class mgMainMenu:public cOsdMenu //! \brief Actions can request a new position. -1 means none wanted int newposition; - //! \brief clears the screen, sets a title and buttons with text on them + //! \brief clears the screen, sets a title and the hotkey flag void InitOsd (const char *title,const bool hashotkeys); #if VDRVERSNUM >= 10307 @@ -216,8 +213,13 @@ class mgMainMenu:public cOsdMenu bool DefaultCollectionSelected(); //! \brief true if the cursor is placed in the default collection - bool DefaultCollectionEntered(); + bool CollectionEntered(string name); + + void AddItem(mgAction *a); + + void CollectionChanged(string name); + void CloseMenu(); }; //! \brief a generic muggle menu @@ -225,10 +227,13 @@ class mgMenu { private: mgMainMenu* m_osd; + const char *HKey(const mgActions act,mgActions on); protected: + bool m_prevUsingCollection; + eOSState ExecuteButton(eKeys key); //! \brief adds the wanted action to the OSD menu // \param hotkey if true, add this as a hotkey - void AddAction(const mgActions action, const bool hotkey=true); + void AddAction(const mgActions action, mgActions on = mgActions(0),const bool hotkey=true); //! \brief add an external action, always with hotkey void AddExternalAction(const mgActions action, const char *title); @@ -236,26 +241,23 @@ class mgMenu //! \brief adds entries for all selected data base items to the OSD menu. // If this is the list of collections, appends a command for collection // creation. - void AddSelectionItems (); + void AddSelectionItems (mgSelection *sel,mgActions act = actEntry); //! \brief the name of the blue button depends of where we are - const char *mgMenu::BlueName (); + const char *mgMenu::BlueName (mgActions on); public: /*! sets the correct help keys. * \todo without data from mysql, no key is shown, * not even yellow or blue */ - void SetHelpKeys(); + void SetHelpKeys(mgActions on = mgActions(0)); //! \brief generates an object for the wanted action - mgOsdItem* GenerateAction(const mgActions action); + mgAction* GenerateAction(const mgActions action,mgActions on); //! \brief executes the wanted action - void ExecuteAction (const mgActions action); + void ExecuteAction (const mgActions action, const mgActions on); //! \brief sets the pointer to the owning mgMainMenu - void setosd (mgMainMenu* osd) - { - m_osd = osd; - } + void setosd (mgMainMenu* osd); //! \brief the pointer to the owning mgMainMenu mgMainMenu* osd () @@ -280,7 +282,7 @@ class mgMenu { } -//! \brief clears the screen, sets a title and buttons with text on them +//! \brief clears the screen, sets a title and the hotkey flag void InitOsd (const char *title,const bool hashotkeys=true); //! \brief display OSD and go to position @@ -333,11 +335,39 @@ class mgSubmenu:public mgMenu void BuildOsd (); }; -//! \brief an mgMenu class for selecting a search view -class mgTreeViewSelector:public mgMenu +//! \brief an mgMenu class for selecting an order +class mgMenuOrders:public mgMenu { protected: void BuildOsd (); }; +//! \brief an mgMenu class for selecting a collection +class mgTreeCollSelector:public mgMenu +{ + public: + ~mgTreeCollSelector(); + protected: + void BuildOsd (); + virtual mgActions coll_action() = 0; + string m_title; +}; + +class mgTreeAddToCollSelector:public mgTreeCollSelector +{ + public: + mgTreeAddToCollSelector(string title); + protected: + virtual mgActions coll_action() { return actAddCollEntry; } +}; + +//! \brief an mgMenu class for selecting a collection +class mgTreeRemoveFromCollSelector:public mgTreeCollSelector +{ + public: + mgTreeRemoveFromCollSelector(string title); + protected: + virtual mgActions coll_action() { return actRemoveCollEntry; } +}; + #endif diff --git a/vdr_player.c b/vdr_player.c index a62f9b5..56828ca 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -168,13 +168,15 @@ class mgPCMPlayer:public cPlayer, cThread int m_index; void Empty (); - bool SkipFile (int step=1); + bool SkipFile (int step=0); void PlayTrack(); void StopPlay (); void SetPlayMode (ePlayMode mode); void WaitPlayMode (ePlayMode mode, bool inv); + int skip_direction; + protected: virtual void Activate (bool On); virtual void Action (void); @@ -234,6 +236,7 @@ pmAudioOnlyBlack) m_index = 0; m_playing = 0; m_current = 0; + skip_direction = 1; } @@ -320,10 +323,10 @@ mgPCMPlayer::SetPlayMode (ePlayMode mode) { m_playmode_mutex.Lock (); if (mode != m_playmode) - { + { m_playmode = mode; m_playmode_cond.Broadcast (); - } + } m_playmode_mutex.Unlock (); } @@ -333,10 +336,10 @@ mgPCMPlayer::WaitPlayMode (ePlayMode mode, bool inv) { // must be called with m_playmode_mutex LOCKED !!! - while (m_active - && ((!inv && mode != m_playmode) || (inv && mode == m_playmode))) + while( m_active + && ((!inv && mode != m_playmode) || (inv && mode == m_playmode)) ) { - m_playmode_cond.Wait (m_playmode_mutex); + m_playmode_cond.Wait (m_playmode_mutex); } } @@ -411,7 +414,6 @@ mgPCMPlayer::Action (void) { std::string filename = the_setup.getFilename( m_playing->getSourceFile () ); mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); - if ((m_decoder = mgDecoders::findDecoder (m_playing)) && m_decoder->start ()) { @@ -422,6 +424,7 @@ mgPCMPlayer::Action (void) level.Init (); m_state = msDecode; + mgDebug(1,"found a decoder for %s",filename.c_str()); break; } @@ -761,8 +764,11 @@ mgPCMPlayer::StopPlay () bool mgPCMPlayer::SkipFile (int step) { MGLOG("mgPCMPlayer::SkipFile"); + mgDebug(1,"SkipFile:step=%d, skip_direction=%d",step,skip_direction); mgContentItem * newcurr = NULL; - if (m_playlist->skipTracks (step)) + if (step!=0) + skip_direction=step; + if (m_playlist->skipTracks (skip_direction)) { newcurr = m_playlist->getCurrentTrack (); if (newcurr) { @@ -831,7 +837,7 @@ mgPCMPlayer::Forward () MGLOG ("mgPCMPlayer::Forward"); Lock (); - if (SkipFile ()) + if (SkipFile (1)) { StopPlay (); Play (); @@ -857,7 +863,7 @@ mgPCMPlayer::Backward (void) void mgPCMPlayer::Goto (int index, bool still) { - m_playlist->setTrack (index - 1); + m_playlist->setTrackPosition (index - 1); mgContentItem *next = m_playlist->getCurrentTrack (); if (next) @@ -1409,7 +1415,6 @@ mgPlayerControl::ShowProgress () mgSelection *list = player->getPlaylist (); if (list) { - list->clearCache(); // playlist can dynamically grow, force reload total_frames = SecondsToFrames (list->getLength ()); current_frame += SecondsToFrames (list->getCompletedLength ()); asprintf (&buf, "%s (%d/%d)", list->getListname ().c_str (), @@ -1530,7 +1535,8 @@ mgPlayerControl::InternalHide () eOSState mgPlayerControl::ProcessKey (eKeys key) { - if (key!=kNone) MGLOG ("mgPlayerControl::ProcessKey(eKeys key)"); + if (key!=kNone) + mgDebug (1,"mgPlayerControl::ProcessKey(%u)",key); if (!Active ()) { return osEnd; -- cgit v1.2.3 From 8df8600565a9f2c0feb3c55ef4d9bfa5f378a273 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 23 Jan 2005 15:50:30 +0000 Subject: Final bugfixes for next release git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@388 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 7 ++-- mg_db.c | 1 + vdr_decoder.c | 101 +++++++++++++++++++++++++++-------------------------- vdr_decoder_flac.c | 34 ++++-------------- vdr_decoder_mp3.c | 5 ++- vdr_decoder_ogg.c | 11 +++--- vdr_player.c | 26 ++++++-------- 7 files changed, 84 insertions(+), 101 deletions(-) diff --git a/README b/README index 5a00e64..ff5e903 100644 --- a/README +++ b/README @@ -47,10 +47,11 @@ but your mileage may vary. In addition, the following pieces of software are req - libtag (for ID3 tag reading/writing) (Debian package libtag1-dev or http://developer.kde.org/~wheeler/taglib.html) - - optionally libvorbis and libvorbisfile - (Debian packages or + - optionally libvorbis and libvorbisfile to replay OGG Vorbis files + (Debian packages libvorbis-dev or http://www.xiph.org/ogg/vorbis/) - + - optionally libFLAC++ to replay FLAC files + (Debian package libflac++-dev or sources from flac.sourceforge.net) The developer versions are needed because their headers are required for compilation. The server need not be on the same machine as the VDR. Also, music tracks can reside somewhere else, if they are available through a remote filesystem (NFS, Samba). However, in this case you should diff --git a/mg_db.c b/mg_db.c index 9fa656d..7e4817f 100644 --- a/mg_db.c +++ b/mg_db.c @@ -985,6 +985,7 @@ bool mgSelection::leave () { if (m_level < 1) return false; + order[m_level]->set ("",EMPTY); order[--m_level]->set ("",EMPTY); clearCache(); if (!m_fall_through) diff --git a/vdr_decoder.c b/vdr_decoder.c index 9a7bb7c..d5e8004 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -21,6 +21,7 @@ #include #include +#include "vdr_setup.h" #include "vdr_decoder.h" #include "vdr_decoder_mp3.h" @@ -43,34 +44,33 @@ mgMediaType mgDecoders::getMediaType (std::string s) { mgMediaType mt = MT_UNKNOWN; -// TODO: currently handles only mp3. LVW - char * - f = (char *) s.c_str (); - char * - p = f + strlen (f) - 1; // point to the end + char * + f = (char *) s.c_str (); + char * + p = f + strlen (f) - 1; // point to the end - while (p >= f && *p != '.') - --p; + while (p >= f && *p != '.') + --p; - if (!strcmp (p, ".mp3")) + if (!strcmp (p, ".mp3")) { - mt = MT_MP3; + mt = MT_MP3; } - else + else { - if (!strcmp (p, ".ogg")) + if (!strcmp (p, ".ogg")) { - mt = MT_OGG; + mt = MT_OGG; } - else - { - if (!strcmp (p, ".flac")) - { - mt = MT_FLAC; - } - } + else + { + if (!strcmp (p, ".flac")) + { + mt = MT_FLAC; + } + } } - return mt; + return mt; } @@ -79,54 +79,55 @@ mgDecoders::findDecoder (mgContentItem * item) { mgDecoder *decoder = 0; - std::string filename = item->getSourceFile (); + std::string filename = the_setup.getFilename( item->getSourceFile () ); struct stat st; if (stat (filename.c_str (), &st)) { - esyslog ("ERROR: no valid decoder found for %s", filename.c_str ()); + esyslog ("ERROR: cannot stat %s. Meaning not found, not a valid file, or no access rights", filename.c_str ()); return 0; } + switch (getMediaType (filename)) - { - case MT_MP3: { - decoder = new mgMP3Decoder (item); - } break; + case MT_MP3: + { + decoder = new mgMP3Decoder (item); + } break; #ifdef HAVE_VORBISFILE - case MT_OGG: - { - decoder = new mgOggDecoder (item); - } break; + case MT_OGG: + { + decoder = new mgOggDecoder (item); + } break; #endif #ifdef HAVE_FLAC - case MT_FLAC: - { - decoder = new mgFlacDecoder( item ); - } break; + case MT_FLAC: + { + decoder = new mgFlacDecoder( item ); + } break; #endif -/* - case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; - #ifdef HAVE_SNDFILE - case MT_SND: decoder = new cSndDecoder(full); break; - #endif - */ - default: + /* + case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break; + #ifdef HAVE_SNDFILE + case MT_SND: decoder = new cSndDecoder(full); break; + #endif + */ + default: { - esyslog ("ERROR: unknown media type"); + esyslog ("ERROR: unknown media type "); } break; - } - + } + if (decoder && !decoder->valid ()) - { -// no decoder found or decoder doesn't match - - delete decoder; // might be carried out on NULL pointer! + { + // no decoder found or decoder doesn't match + + delete decoder; // might be carried out on NULL pointer decoder = 0; - + esyslog ("ERROR: no valid decoder found for %s", filename.c_str ()); - } + } return decoder; } diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c index 1a66e8d..c640205 100644 --- a/vdr_decoder_flac.c +++ b/vdr_decoder_flac.c @@ -11,7 +11,9 @@ #ifdef HAVE_FLAC #define DEBUG +#include "vdr_setup.h" #include "vdr_decoder_flac.h" + #include "mg_tools.h" #include "mg_db.h" @@ -32,8 +34,8 @@ mgFlacDecoder::mgFlacDecoder( mgContentItem *item ) { mgLog lg( "mgFlacDecoder::mgFlacDecoder" ); - m_filename = item->getSourceFile(); - // m_filename = "/test.flac"; + m_filename = the_setup.getFilename( item->getSourceFile () ); + // m_filename = item->getSourceFile(); m_pcm = 0; m_reservoir = 0; @@ -81,14 +83,7 @@ bool mgFlacDecoder::initialize() m_reservoir[0] = new FLAC__int32[MAX_RES_SIZE]; m_reservoir[1] = new FLAC__int32[MAX_RES_SIZE]; - FLAC::Decoder::File::State d = init(); // TODO check this? - - cout << "Status of decoder: " << string( d.as_cstring() ) << endl; - FLAC::Decoder::SeekableStream::State ssd = get_seekable_stream_decoder_state(); - cout << "Status of seekable stream decoder: " << string( ssd.as_cstring() ) << endl; - FLAC::Decoder::Stream::State sd = get_stream_decoder_state(); - cout << "Status of stream decoder: " << string( sd.as_cstring() ) << endl; - // set state accordingly + FLAC::Decoder::File::State d = init(); // TODO: check this process_until_end_of_metadata(); // basically just skip metadata @@ -120,15 +115,6 @@ bool mgFlacDecoder::start() bool res = false; lock(true); - /* - FLAC::Decoder::File::State d = get_state(); - cout << "Status of decoder: " << string( d.as_cstring() ) << endl; - FLAC::Decoder::SeekableStream::State ssd = get_seekable_stream_decoder_state(); - cout << "Status of seekable stream decoder: " << string( ssd.as_cstring() ) << endl; - FLAC::Decoder::Stream::State sd = get_stream_decoder_state(); - cout << "Status of stream decoder: " << string( sd.as_cstring() ) << endl; - */ - // can FLAC handle more than 2 channels anyway? if( m_item->getChannels() <= 2 ) { @@ -255,11 +241,6 @@ struct mgDecode *mgFlacDecoder::decode() } m_reservoir_count -= n; - FLAC::Decoder::File::State d = get_state(); - FLAC::Decoder::SeekableStream::State ssd = get_seekable_stream_decoder_state(); - FLAC::Decoder::Stream::State sd = get_stream_decoder_state(); - // if any states are not okay, abort and override m_decode_state - // and return, indicating that playing will continue (unless an error has occurred) return done( m_decode_status ); } @@ -271,8 +252,7 @@ struct mgDecode *mgFlacDecoder::decode() mgFlacDecoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) { - mgLog lg( "mgFlacDecoder::write_callback" ); - ::FLAC__StreamDecoderWriteStatus retval; + // mgLog lg( "mgFlacDecoder::write_callback" ); // add decoded buffer to reservoir m_len_decoded = frame->header.blocksize; @@ -352,7 +332,7 @@ void mgFlacDecoder::error_callback( ::FLAC__StreamDecoderErrorStatus status ) } } - cout << "Error occured: " << m_error << endl; + // cout << "Error occured: " << m_error << endl; m_decode_status = dsError; } diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c index d914dcd..754ab39 100644 --- a/vdr_decoder_mp3.c +++ b/vdr_decoder_mp3.c @@ -26,6 +26,7 @@ #include "vdr_config.h" #include "vdr_decoder_mp3.h" #include "vdr_stream.h" +#include "vdr_setup.h" #include "mg_tools.h" #include "mg_db.h" @@ -58,7 +59,9 @@ mgMP3Decoder::mgMP3Decoder (mgContentItem * item, bool preinit):mgDecoder { m_stream = 0; m_isStream = false; - m_filename = item->getSourceFile (); + + m_filename = the_setup.getFilename( item->getSourceFile () ); + // m_filename = item->getSourceFile (); if (preinit) { diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c index d720c81..24d253d 100644 --- a/vdr_decoder_ogg.c +++ b/vdr_decoder_ogg.c @@ -19,6 +19,8 @@ #include #include +#include "vdr_setup.h" + #include "mg_db.h" // --- mgOggFile ---------------------------------------------------------------- @@ -237,10 +239,11 @@ mgOggFile::stream (short *buffer, int samples) mgOggDecoder::mgOggDecoder (mgContentItem * item):mgDecoder (item) { - m_filename = item->getSourceFile (); - m_file = new mgOggFile (m_filename); - m_pcm = 0; - init (); + // m_filename = item->getSourceFile (); + m_filename = the_setup.getFilename( item->getSourceFile () ); + m_file = new mgOggFile (m_filename); + m_pcm = 0; + init (); } diff --git a/vdr_player.c b/vdr_player.c index 56828ca..232e0e5 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -412,24 +412,18 @@ mgPCMPlayer::Action (void) if (m_playing) { - std::string filename = the_setup.getFilename( m_playing->getSourceFile () ); - mgDebug( 1, "mgPCMPlayer::Action: music file is %s", filename.c_str() ); - if ((m_decoder = mgDecoders::findDecoder (m_playing)) - && m_decoder->start ()) + if ((m_decoder = mgDecoders::findDecoder (m_playing)) + && m_decoder->start ()) { - levelgood = true; - haslevel = false; - - scale.Init (); - level.Init (); - - m_state = msDecode; - mgDebug(1,"found a decoder for %s",filename.c_str()); - - break; + levelgood = true; + haslevel = false; + + scale.Init (); + level.Init (); + + m_state = msDecode; + break; } - else - mgWarning("Cannot find a decoder for %s",filename.c_str()); } m_state = msEof; } -- cgit v1.2.3 From 12d61530a965f63f2066ea0f804614697e0d3287 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 23 Jan 2005 16:14:51 +0000 Subject: Disabled verbose option for release git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@389 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mugglei.c b/mugglei.c index 7e284d7..2e780df 100755 --- a/mugglei.c +++ b/mugglei.c @@ -5,7 +5,7 @@ * \author Lars von Wedel */ -#define VERBOSE +// #define VERBOSE #include #include -- cgit v1.2.3 From d4537b770d7bd6d64ba2ffe1cbe020789fdefbfa Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 23 Jan 2005 19:51:57 +0000 Subject: Minor bugfixes git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@394 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 6 ++++-- README | 2 ++ vdr_setup.c | 13 +++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9213ab8..3e962b6 100644 --- a/Makefile +++ b/Makefile @@ -44,16 +44,17 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DHAVE_VORBISFILE -DHAVE_FLAC +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' # -DHAVE_VORBISFILE -DHAVE_FLAC MIFLAGS += -I/usr/include/taglib -lmysqlclient + ### The object files (add further files here): OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \ vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \ vdr_setup.o vdr_decoder_ogg.o vdr_decoder_flac.o -LIBS = -lmad -lmysqlclient -lvorbisfile -lvorbis -lFLAC++ +LIBS = -lmad -lmysqlclient # -lvorbisfile -lvorbis -lFLAC++ MILIBS = -lmysqlclient -ltag ### Targets: @@ -80,6 +81,7 @@ libvdr-$(PLUGIN).so: $(OBJS) mugglei: mg_tools.o mugglei.o $(CXX) $(CXXFLAGS) $^ $(MILIBS) -o $@ + install: @cp ../../lib/libvdr-muggle*.so.* /usr/lib/vdr/ @cp mugglei /usr/local/bin/ diff --git a/README b/README index ff5e903..ed769c0 100644 --- a/README +++ b/README @@ -73,6 +73,8 @@ Establish a symlink as you would for other plugins: ln -s muggle-0.1.1 muggle \endverbatim +Adapt the Makefile to your system. Define HAVE_VORBIS and/or HAVE_FLAC and adapt the LIBS variable accordingly. + Within the VDR main directory (e.g. /usr/local/src/VDR) issue \verbatim diff --git a/vdr_setup.c b/vdr_setup.c index 8e281cd..2239191 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -25,6 +25,7 @@ mgSetup the_setup; std::string GdFindFile( std::string mp3file, std::string ToplevelDir ); +char *readline(FILE *f); // --- mgMenuSetup ----------------------------------------------------------- @@ -107,6 +108,18 @@ mgSetup::getFilename( std::string basename ) #define FINDCMD "cd '%s' && find -follow -name '%s' 2> /dev/null" +char *readline(FILE *f) +{ + static char buffer[MAXPARSEBUFFER]; + if (fgets(buffer, sizeof(buffer), f) > 0) { + int l = strlen(buffer) - 1; + if (l >= 0 && buffer[l] == '\n') + buffer[l] = 0; + return buffer; + } + return NULL; +} + std::string GdFindFile( std::string mp3file, std::string tld ) { std::string fullname = ""; -- cgit v1.2.3 From 4f08b8150f9e09db85c25f6f81ecd344ad8ac89f Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 26 Jan 2005 04:11:59 +0000 Subject: set ttAudio instead of ttDolbyFirst git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@408 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdr_player.c b/vdr_player.c index 232e0e5..9330852 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -382,7 +382,7 @@ mgPCMPlayer::Action (void) SetPlayMode (pmStopped); #if VDRVERSNUM >= 10318 - cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttDolbyFirst); + cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio); #endif while (m_active) { -- cgit v1.2.3 From 7fae4048b4494f332363dab4a179b1a901c78e94 Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 26 Jan 2005 04:14:48 +0000 Subject: fix kUp/kDown in progress view git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@409 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 9330852..2e3e1f5 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -1549,7 +1549,7 @@ eOSState mgPlayerControl::ProcessKey (eKeys key) { case kUp: { - if (m_visible) + if (m_visible && !m_progress_view && !m_track_view) Backward(); else Forward (); @@ -1557,7 +1557,7 @@ eOSState mgPlayerControl::ProcessKey (eKeys key) break; case kDown: { - if (m_visible) + if (m_visible && !m_progress_view && !m_track_view) Forward (); else Backward(); -- cgit v1.2.3 From 85e8bd199eddfe0245b77c38813f402f29380de5 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 27 Jan 2005 20:20:44 +0000 Subject: Keep player waiting after end of collection is reached git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@411 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vdr_player.c b/vdr_player.c index 2e3e1f5..e4dd7ef 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -620,8 +620,8 @@ mgPCMPlayer::Action (void) { if (m_ringbuffer->Available () == 0) { - m_active = false; - SetPlayMode (pmStopped); + // m_active = false; + SetPlayMode (pmStopped); } } break; -- cgit v1.2.3 From 19b5fbe6f03d263ab8e427022156ed7d2bfcaea1 Mon Sep 17 00:00:00 2001 From: wr61 Date: Fri, 4 Feb 2005 17:08:13 +0000 Subject: fix import of sourc and musictype tables git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@435 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 4 ++-- scripts/make-empty-db | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index ed769c0..0b6fd1a 100644 --- a/README +++ b/README @@ -122,10 +122,10 @@ Execute these commands on a single line, the \ for the linebreak ist just for pr echo "use GiantDisc; load data local infile 'languages.txt' into table language;" | \ mysql -u root --local-infile=1 - echo "use GiantDisc; load data local infile 'musictypes.txt' into table language;" | \ + echo "use GiantDisc; load data local infile 'musictypes.txt' into table musictype;" | \ mysql -u root --local-infile=1 - echo "use GiantDisc; load data local infile 'sources.txt' into table language;" | \ + echo "use GiantDisc; load data local infile 'sources.txt' into table source;" | \ mysql -u root --local-infile=1 \endverbatim diff --git a/scripts/make-empty-db b/scripts/make-empty-db index 3acb512..92ccf75 100755 --- a/scripts/make-empty-db +++ b/scripts/make-empty-db @@ -13,7 +13,7 @@ echo "reading languages" echo "use GiantDisc; load data local infile 'languages.txt' into table language;" | mysql -u root --local-infile=1 echo "reading musictypes" -echo "use GiantDisc; load data local infile 'musictypes.txt' into table language;" | mysql -u root --local-infile=1 +echo "use GiantDisc; load data local infile 'musictypes.txt' into table musictype;" | mysql -u root --local-infile=1 echo "reading sources" -echo "use GiantDisc; load data local infile 'sources.txt' into table language;" | mysql -u root --local-infile=1 +echo "use GiantDisc; load data local infile 'sources.txt' into table source;" | mysql -u root --local-infile=1 -- cgit v1.2.3 From 14f5032e141da131f3d1f72979cf415d6105d819 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 7 Feb 2005 09:17:20 +0000 Subject: Merged most recent stuff from osd_extensions trunk for next release (0.1.2) git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@447 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 28 +- i18n.c | 51 ++++ mg_actions.c | 469 ++++++++++++++++++--------------- mg_actions.h | 40 ++- mg_db.c | 698 ++++++++++++++++++++++++++++++++------------------ mg_db.h | 89 +++---- mg_order.c | 492 +++++++++++++++++++++++++---------- mg_order.h | 41 ++- mg_tools.c | 3 +- muggle.c | 2 +- mugglei.c | 168 +++++------- scripts/languages.txt | 1 + vdr_decoder.c | 20 +- vdr_decoder_flac.c | 9 +- vdr_decoder_mp3.c | 4 +- vdr_decoder_ogg.c | 3 +- vdr_menu.c | 373 ++++++++++++++++++++------- vdr_menu.h | 53 +++- vdr_player.c | 22 +- vdr_setup.c | 51 ---- vdr_setup.h | 1 - vdr_sound.c | 7 +- vdr_stream.c | 2 +- 23 files changed, 1657 insertions(+), 970 deletions(-) diff --git a/Makefile b/Makefile index 3e962b6..1dd8773 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,15 @@ # PLUGIN = muggle +#if you want ogg / flac support, remove the trailing x in those two +#definitions: +define have_vorbisfile +1 +endef +define have_flac +1 +endef + ### The version number of this plugin (taken from the main source file): VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') @@ -16,7 +25,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri ### The C++ compiler and options: CXX ?= g++-3.3 -CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g +CXXFLAGS ?= -fPIC -O0 -Wall -Woverloaded-virtual -Wno-deprecated -g ### The directory environment: @@ -44,7 +53,7 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' # -DHAVE_VORBISFILE -DHAVE_FLAC +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' MIFLAGS += -I/usr/include/taglib -lmysqlclient @@ -52,11 +61,22 @@ MIFLAGS += -I/usr/include/taglib -lmysqlclient OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \ vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \ - vdr_setup.o vdr_decoder_ogg.o vdr_decoder_flac.o + vdr_setup.o -LIBS = -lmad -lmysqlclient # -lvorbisfile -lvorbis -lFLAC++ +LIBS = -lmad -lmysqlclient MILIBS = -lmysqlclient -ltag +ifdef have_vorbisfile +DEFINES += -DHAVE_VORBISFILE +OBJS += vdr_decoder_ogg.o +LIBS += -lvorbisfile -lvorbis +endif +ifdef have_flac +DEFINES += -DHAVE_FLAC +OBJS += vdr_decoder_flac.o +LIBS += -lFLAC++ +endif + ### Targets: all: libvdr-$(PLUGIN).so mugglei diff --git a/i18n.c b/i18n.c index 13b7fea..6ba1065 100644 --- a/i18n.c +++ b/i18n.c @@ -12,6 +12,40 @@ const tI18nPhrase Phrases[] = { + { + "Key %d", + "Schlüsselfeld %d", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Key %d", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Create", + "Neu", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Nouveau", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "Search", "Suchen", @@ -97,6 +131,23 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO }, + { + "Create order", + "Sortierung neu anlegen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Créer un ordre nouveaux", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "Create collection", "Sammlung neu anlegen", diff --git a/mg_actions.c b/mg_actions.c index 48b06f3..42337d9 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -36,33 +36,48 @@ IsEntry(mgActions i) class mgOsdItem : public mgAction, public cOsdItem { public: - eOSState ProcessKey(eKeys key); - protected: - virtual eOSState Process(eKeys key) { return osUnknown; } + eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); } }; + eOSState -mgOsdItem::ProcessKey(eKeys key) +mgAction::ProcessKey(eKeys key) { + if (key!=kNone) + mgDebug(1,"mgAction::ProcessKey(%d)",key); eOSState result = Process(key); if (result != osUnknown) IgnoreNextEvent = true; return result; } +class mgNone: public mgOsdItem +{ public: + void Notify() {}; + bool Enabled(mgActions on) { return false; } + eOSState Process(eKeys key) { return osUnknown; } +}; + //! \brief used for normal data base items class mgEntry : public mgOsdItem { public: void Notify(); bool Enabled(mgActions on) { return IsEntry(on);} - const char *ButtonName() { return ""; } const char *MenuName (const unsigned int idx,const string value); - virtual eOSState Process(eKeys key); + eOSState Process(eKeys key); void Execute(); eOSState Back(); }; +class mgKeyItem : public mgAction, public cMenuEditStraItem +{ + public: + mgKeyItem(const char *Name, int *Value, int NumStrings, const char *const *Strings) : cMenuEditStraItem(Name, Value, NumStrings, Strings) {} + eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); } + eOSState Process(eKeys key); +}; + class mgDoCollEntry : public mgEntry { public: @@ -100,7 +115,6 @@ mgDoCollEntry::Process(eKeys key) eOSState result = osContinue; switch (key) { - case kBlue: case kBack: break; case kOk: @@ -202,9 +216,60 @@ class mgCommand : public mgOsdItem public: bool Enabled(mgActions on); virtual eOSState Process(eKeys key); +}; + +class mgActOrder : public mgOsdItem +{ + public: + const char* MenuName(const unsigned int idx,const string value); + virtual eOSState Process(eKeys key); void Execute(); }; +const char* +mgActOrder::MenuName(const unsigned int idx,const string value) +{ + return strdup(value.c_str()); +} + +eOSState +mgActOrder::Process(eKeys key) +{ + mgMenu *n = osd ()->newmenu; + osd ()->newmenu = NULL; + eOSState result = osContinue; + switch (key) + { + case kBack: + break; + case kOk: + Execute (); + break; + default: + osd ()->newmenu = n; // wrong key: stay in submenu + result = osUnknown; + break; + } + return result; +} + +void +mgActOrder::Execute() +{ + mgSelection *s = osd()->selection(); + mgOrder oldorder = s->getOrder(); + mgContentItem o; + s->select(); + if (s->getNumTracks()==1) + o = s->getTrack(0); + osd()->UseNormalSelection(); // Default for all orders + osd()->setOrder(s,osd()->Current()); + mgSelection *newsel = osd()->selection(); + newsel->selectfrom(oldorder,&o); + osd()->newposition = newsel->getPosition(); + osd()->SaveState(); +} + bool mgCommand::Enabled(mgActions on) { @@ -321,7 +386,6 @@ mgCommand::Process(eKeys key) else parent->TreeYellowAction = Type(); break; - case kBlue: case kBack: break; case kOk: @@ -335,16 +399,11 @@ mgCommand::Process(eKeys key) return result; } -void -mgCommand::Execute() -{ -} class mgExternal : public mgCommand { public: const char *ButtonName(); - const char *MenuName (const unsigned int idx,const string value); void Execute(); bool Enabled(mgActions on) { return true; } private: @@ -385,12 +444,6 @@ mgExternal::ButtonName() return ""; } -const char* -mgExternal::MenuName(const unsigned int idx,const string value) -{ - return strdup(ButtonName()); -} - cCommand * mgExternal::Command() { @@ -426,7 +479,17 @@ mgExternal::Execute() if (!m3u_file.empty ()) { /*char *result = (char *)*/ - command->Execute (m3u_file.c_str ()); + string quoted = "'" + m3u_file + "'"; + char prev[1000]; + if (!getcwd(prev,1000)) + mgError("current path too long"); + if (chdir(the_setup.ToplevelDir)) + mgError("cannnot change to directory %s", + the_setup.ToplevelDir); + command->Execute (quoted.c_str ()); + chdir(prev); + selection()->clearCache(); + osd()->forcerefresh = true; // the ext cmd could change the database /* What to do? Recode cMenuText (not much)? if (result) { @@ -475,17 +538,91 @@ mgChooseOrder::Process(eKeys key) void mgChooseOrder::Execute() { osd ()->newmenu = new mgMenuOrders; + osd ()->newposition = osd()->getCurrentOrder(); + +} + +class mgEditOrder : public mgCommand +{ + public: + bool Enabled(mgActions on) { return true; } + eOSState Process(eKeys key); + void Execute () { osd ()->newmenu = new mgMenuOrder; } + const char *ButtonName() { return tr("Edit"); } +}; + +eOSState +mgEditOrder::Process(eKeys key) +{ + if (key == kOk) + { + Execute(); + return osContinue; + } + else + return mgCommand::Process(key); +} + +class mgCreateOrder : public mgCommand +{ + public: + bool Enabled(mgActions on) { return true; } + void Execute (); + const char *ButtonName() { return tr("Create"); } +}; +void +mgCreateOrder::Execute() +{ + osd()->AddOrder(); + osd()->SaveState(); + osd()->forcerefresh = true; } +class mgDeleteOrder : public mgCommand +{ + public: + bool Enabled(mgActions on) { return true; } + void Execute (); + const char *ButtonName() { return tr("Delete"); } +}; + +void +mgDeleteOrder::Execute() +{ + osd()->DeleteOrder(); + osd()->SaveState(); + osd()->forcerefresh = true; + osd()->newposition = osd()->Current(); +} + +//! \brief show the normal selection list +class mgShowList: public mgOsdItem +{ + public: + bool Enabled(mgActions) { return true; } + const char *ButtonName () { return tr("List"); } + void Execute() { osd()->newmenu=NULL; } +}; + + +//! \brief show the command submenu +class mgShowCommands: public mgOsdItem +{ + public: + bool Enabled(mgActions on) { return true; } + const char *ButtonName () { return tr("Commands"); } + void Execute() { osd()->newmenu = new mgSubmenu; } +}; + + //! \brief toggles between the normal and the collection selection class mgToggleSelection:public mgCommand { public: - bool Enabled(mgActions on = mgActions(0)) { return true; } + bool Enabled(mgActions on) { return true; } void Execute (); const char *ButtonName (); - const char *MenuName (const unsigned int idx,const string value); }; const char * @@ -497,17 +634,6 @@ mgToggleSelection::ButtonName () return tr ("Collections"); } - -const char * -mgToggleSelection::MenuName (const unsigned int idx,const string value) -{ - if (osd ()->UsingCollection) - return strdup(tr ("Search")); - else - return strdup(tr ("Collections")); -} - - void mgToggleSelection::Execute () { @@ -526,7 +652,7 @@ mgToggleSelection::Execute () class mgSetDefaultCollection:public mgCommand { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName () { @@ -567,11 +693,7 @@ class mgSetButton : public mgCommand { public: bool Enabled(mgActions on) { return true; } - const char *ButtonName() - { - return tr("Set"); - } - const char *MenuName(const unsigned int idx,const string value) { return strdup(""); } + const char *ButtonName() { return tr("Set"); } }; @@ -579,19 +701,9 @@ class mgSetButton : public mgCommand class mgInstantPlay : public mgCommand { public: void Execute (); - const char *ButtonName () - { - return tr ("Instant play"); - } - const char *MenuName (const unsigned int idx,const string value); + const char *ButtonName () { return tr ("Instant play"); } }; -const char * -mgInstantPlay::MenuName (const unsigned int idx,const string value) -{ - return strdup(tr("Instant play")); -} - void mgInstantPlay::Execute() { @@ -636,7 +748,8 @@ mgAddAllToCollection::ExecuteMove() char *b; asprintf(&b,tr("'%s' to collection"),selection()->getCurrentValue().c_str()); osd ()->newmenu = new mgTreeAddToCollSelector(string(b)); - osd ()->newposition = osd()->collselection()->getPosition(0); + osd ()->collselection()->leave_all(); + osd ()->newposition = osd()->collselection()->getPosition(); free(b); } @@ -699,7 +812,7 @@ mgAddAllToDefaultCollection::ExecuteSelection (mgSelection *s) class mgAddThisToCollection:public mgAddAllToCollection { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName (); const char *MenuName (const unsigned int idx,const string value); @@ -738,7 +851,7 @@ mgAddThisToCollection::MenuName (const unsigned int idx,const string value) class mgAddThisToDefaultCollection:public mgAddAllToDefaultCollection { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName (); const char *MenuName (const unsigned int idx,const string value); @@ -778,60 +891,6 @@ mgAddThisToDefaultCollection::MenuName (const unsigned int idx,const string valu return b; } -class mgOrderNew : public mgCommand -{ - protected: - virtual void NewOrder() = 0; - public: - bool Enabled(mgActions on = mgActions(0)) { return true; } - void Execute(); -}; - -void -mgOrderNew::Execute() -{ - mgSelection *s = osd()->selection(); - map* oldkeys = s->UsedKeyValues(); - mgContentItem o; - s->select(); - if (s->getNumTracks()==1) - { - o = s->getTrack(0); - } - osd()->UseNormalSelection(); // Default for all orders - NewOrder(); - mgSelection *newsel = osd()->selection(); - newsel->leave(0); - for (unsigned int idx = 0; idx < newsel->ordersize(); idx++) - { - string selval = ""; - mgKeyTypes new_kt = newsel->getKeyType(idx); - if (oldkeys->count(new_kt)>0) - selval=(*oldkeys)[new_kt]; - else if (o.getId()>=0) - selval=o.getKeyValue(new_kt); - if (!selval.empty()) - { - if (idxordersize()-1) - newsel->select(selval); - else - newsel->setPosition(selval); - continue; - } - break; - } - delete oldkeys; - osd()->newposition = newsel->getPosition(newsel->level()); - newsel->clearCache(); -} - -class mgOrderCollItem : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Collection -> Item")); } - void NewOrder() { osd ()->UseCollectionSelection (); } -}; - - //! \brief remove selected items from default collection class mgRemoveAllFromCollection:public mgCommand { @@ -864,7 +923,7 @@ mgRemoveAllFromCollection::MenuName (const unsigned int idx,const string value) class mgClearCollection : public mgCommand { public: - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); void Execute (); const char *ButtonName () { @@ -922,50 +981,45 @@ mgRemoveThisFromCollection::MenuName (const unsigned int idx,const string value) return strdup(tr("Remove from a collection")); } -/*! \brief create collection directly in the collection list - */ -class mgCreateCollection: public mgAction, public cMenuEditStrItem +class mgEditAction : public mgAction +{ + public: + mgEditAction() : mgAction() { memset(m_value,0,30); } + protected: + char m_value[30]; +}; + +class mgCreate : public mgEditAction, public cMenuEditStrItem { public: - mgCreateCollection(); + mgCreate(const char* mn); + virtual bool Enabled(mgActions on)=0; void Notify(); - bool Enabled(mgActions on = mgActions(0)); - eOSState ProcessKey(eKeys key); - void Execute (); - const char *MenuName (const unsigned int idx=0,const string value=""); - private: + eOSState ProcessKey(eKeys key) { return mgAction::ProcessKey(key); } + eOSState Process(eKeys key); + protected: bool Editing(); - char value[30]; }; +mgCreate::mgCreate(const char *mn) : mgEditAction(), cMenuEditStrItem(mn,m_value,30,tr(FileNameChars)) +{ +} bool -mgCreateCollection::Editing() +mgCreate::Editing() { return (strchr(cOsdItem::Text(),'[') && strchr(cOsdItem::Text(),']')); } - void -mgCreateCollection::Notify() +mgCreate::Notify() { if (!Editing()) m->SetHelpKeys(); } -const char* -mgCreateCollection::MenuName(const unsigned int idx,const string value) -{ - return strdup(tr ("Create collection")); -} - -mgCreateCollection::mgCreateCollection() : mgAction(), cMenuEditStrItem(MenuName(),value,30,tr(FileNameChars)) -{ - value[0]=0; -} - eOSState -mgCreateCollection::ProcessKey(eKeys key) +mgCreate::Process(eKeys key) { if (key == kOk) if (Editing()) @@ -978,16 +1032,29 @@ mgCreateCollection::ProcessKey(eKeys key) return osUnknown; } -bool -mgCreateCollection::Enabled(mgActions on) +class mgCreateCollection : public mgCreate +{ + public: + mgCreateCollection(); + bool Enabled(mgActions on); + void Execute (); + const char *MenuName (const unsigned int idx=0,const string value=""); +}; + +mgCreateCollection::mgCreateCollection() : mgCreate(MenuName()) { - return selection()->isCollectionlist(); +} + +const char* +mgCreateCollection::MenuName(const unsigned int idx,const string value) +{ + return strdup(tr ("Create collection")); } void mgCreateCollection::Execute () { - string name = trim(value); + string name = trim(m_value); if (name.empty()) return; bool created = selection ()->CreateCollection (name); if (created) @@ -1006,12 +1073,19 @@ mgCreateCollection::Execute () osd()->Message1 ("Collection '%s' NOT created", name); } +bool +mgCreateCollection::Enabled(mgActions on) +{ + return selection()->isCollectionlist(); +} + + //! \brief delete collection class mgDeleteCollection:public mgCommand { public: void Execute (); - bool Enabled(mgActions on = mgActions(0)); + bool Enabled(mgActions on); const char *ButtonName () { return tr ("Delete"); @@ -1079,69 +1153,11 @@ mgExportTracklist::Execute () osd()->Message1 ("written to %s", m3u_file); } -class mgOrderArtistAlbumTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Album -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyArtist); - selection ()->setKey (1, keyAlbum); - selection ()->setKey (2, keyTrack); - } -}; - - -class mgOrderAlbumTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Album -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyAlbum); - selection ()->setKey (1, keyTrack); - } -}; - - -class mgOrderGenreYearTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Year -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyGenre1); - selection ()->setKey (1, keyYear); - selection ()->setKey (2, keyTrack); - } -}; - - -class mgOrderGenreArtistAlbumTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Genre 1 -> Artist ->Album -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyGenre1); - selection ()->setKey (1, keyArtist); - selection ()->setKey (2, keyAlbum); - selection ()->setKey (3, keyTrack); - } -}; - - -class mgOrderArtistTitle : public mgOrderNew -{ - const char *MenuName(const unsigned int idx,const string value) { return strdup(tr("Artist -> Track")); } - void NewOrder() - { - selection ()->setKey (0, keyArtist); - selection ()->setKey (1, keyTrack); - } -}; - - mgActions mgAction::Type() { const type_info& t = typeid(*this); + if (t == typeid(mgNone)) return actNone; if (t == typeid(mgChooseOrder)) return actChooseOrder; if (t == typeid(mgToggleSelection)) return actToggleSelection; if (t == typeid(mgClearCollection)) return actClearCollection; @@ -1159,13 +1175,13 @@ mgAction::Type() if (t == typeid(mgRemoveThisFromCollection)) return actRemoveThisFromCollection; if (t == typeid(mgEntry)) return actEntry; if (t == typeid(mgSetButton)) return actSetButton; - if (t == typeid(mgOrderCollItem)) return ActOrderCollItem; - if (t == typeid(mgOrderArtistAlbumTitle)) return ActOrderArtistAlbumTitle; - if (t == typeid(mgOrderArtistTitle)) return ActOrderArtistTitle; - if (t == typeid(mgOrderAlbumTitle)) return ActOrderAlbumTitle; - if (t == typeid(mgOrderGenreYearTitle)) return ActOrderGenreYearTitle; - if (t == typeid(mgOrderGenreArtistAlbumTitle)) return ActOrderArtistAlbumTitle; + if (t == typeid(mgShowList)) return actShowList; + if (t == typeid(mgShowCommands)) return actShowCommands; if (t == typeid(mgSetDefaultCollection)) return actSetDefaultCollection; + if (t == typeid(mgActOrder)) return actOrder; + if (t == typeid(mgCreateOrder)) return actCreateOrder; + if (t == typeid(mgDeleteOrder)) return actDeleteOrder; + if (t == typeid(mgEditOrder)) return actEditOrder; if (t == typeid(mgExternal0)) return actExternal0; if (t == typeid(mgExternal1)) return actExternal1; if (t == typeid(mgExternal2)) return actExternal2; @@ -1186,16 +1202,22 @@ mgAction::Type() if (t == typeid(mgExternal17)) return actExternal17; if (t == typeid(mgExternal18)) return actExternal18; if (t == typeid(mgExternal19)) return actExternal19; - mgError("Unknown mgAction %s",t.name()); return mgActions(0); } +mgAction* +actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings) +{ + return new mgKeyItem(Name,Value,NumStrings,Strings); +} + mgAction* actGenerate(const mgActions action) { mgAction * result = NULL; switch (action) { + case actNone: result = new mgNone;break; case actChooseOrder: result = new mgChooseOrder;break; case actToggleSelection: result = new mgToggleSelection;break; case actClearCollection: result = new mgClearCollection;break; @@ -1213,13 +1235,15 @@ actGenerate(const mgActions action) case actRemoveThisFromCollection: result = new mgRemoveThisFromCollection;break; case actEntry: result = new mgEntry;break; case actSetButton: result = new mgSetButton;break; - case ActOrderCollItem: result = new mgOrderCollItem;break; - case ActOrderArtistAlbumTitle: result = new mgOrderArtistAlbumTitle;break; - case ActOrderArtistTitle: result = new mgOrderArtistTitle;break; - case ActOrderAlbumTitle: result = new mgOrderAlbumTitle;break; - case ActOrderGenreYearTitle: result = new mgOrderGenreYearTitle;break; - case ActOrderGenreArtistAlbumTitle: result = new mgOrderGenreArtistAlbumTitle;break; + case actShowList: result = new mgShowList;break; + case actShowCommands: result = new mgShowCommands;break; + case actUnused5: break; case actSetDefaultCollection: result = new mgSetDefaultCollection;break; + case actOrder: result = new mgActOrder;break; + case actUnused6: break; + case actCreateOrder: result = new mgCreateOrder;break; + case actDeleteOrder: result = new mgDeleteOrder;break; + case actEditOrder: result = new mgEditOrder;break; case actExternal0: result = new mgExternal0;break; case actExternal1: result = new mgExternal1;break; case actExternal2: result = new mgExternal2;break; @@ -1258,3 +1282,28 @@ mgAction::collselection() return osd()->collselection(); } +eOSState +mgKeyItem::Process(eKeys key) +{ + mgMenuOrder *menu = dynamic_cast(m); + if (key==kOk) + { + if (menu->ChangeOrder(key)) + return osContinue; + else + { + menu->SaveOrder(); + osd ()->newmenu = NULL; + return osContinue; + } + } else if (key==kBack) + { + osd ()->newmenu = NULL; + return osContinue; + } + if (key==kUp || key==kDown) + if (menu->ChangeOrder(key)) + return osContinue; + return cMenuEditStraItem::ProcessKey(key); +} + diff --git a/mg_actions.h b/mg_actions.h index 3712191..3994b10 100644 --- a/mg_actions.h +++ b/mg_actions.h @@ -27,10 +27,11 @@ class mgMainMenu; /*! \brief defines all actions which can appear in command submenus. * Since these values are saved in muggle.state, new actions should - * always be appended. The order does not matter. Value 0 means undefined. + * always be appended. The order does not matter. actNone should be 0. */ enum mgActions { - actChooseOrder=1, //!< show a menu with all possible orders + actNone, + actChooseOrder, //!< show a menu with all possible orders actToggleSelection, //!< toggle between search and collection view actClearCollection, //!< clear a collection, actCreateCollection, @@ -45,15 +46,17 @@ enum mgActions { actRemoveThisFromCollection, //!< remove selected item from default collection actEntry, //!< used for normal data base items actSetButton, //!< connect a button with an action - ActOrderCollItem, //!< order by collections - ActOrderArtistAlbumTitle, //!< order by Artist/Album/Title - ActOrderArtistTitle, //!< order by Artist/Title - ActOrderAlbumTitle, //!< order by Album/Title - ActOrderGenreYearTitle, //!< order by Genre1/Year/Title - ActOrderGenreArtistAlbumTitle, //!< order by Genre1/Artist/Album/Title + actShowList, + actShowCommands, + actCreateOrder, + actDeleteOrder, + actUnused5, //!< order by Genre1/Artist/Album/Title actAddAllToDefaultCollection, actAddThisToDefaultCollection, actSetDefaultCollection, + actOrder, + actUnused6, + actEditOrder, actExternal0 = 1000, //!< used for external commands, the number is the entry number in the .conf file starting with line 0 actExternal1, //!< used for external commands, the number is the entry number in the .conf file actExternal2, //!< used for external commands, the number is the entry number in the .conf file @@ -88,7 +91,7 @@ class mgAction virtual bool Enabled(mgActions on = mgActions(0)); //! \brief the action to be executed - virtual void Execute () = 0; + virtual void Execute () {} //! \brief handles the kBack key virtual eOSState Back(); @@ -98,14 +101,17 @@ class mgAction */ virtual const char *ButtonName () { - return NULL; + return ""; } /*! \brief the name for a menu entry. If empty, no button will be able * to execute this. The returned C string must be freeable at any time. * \param value a string that can be used for building the menu name. */ - virtual const char *MenuName (const unsigned int idx=0,const string value="") = 0; + virtual const char *MenuName (const unsigned int idx=0,const string value="") + { + return strdup(ButtonName()); + } //! \brief default constructor mgAction (); @@ -149,12 +155,24 @@ class mgAction mgSelection* playselection (); virtual void Notify(); + eOSState ProcessKey(eKeys key); + virtual eOSState Process(eKeys key) { return osUnknown; } private: mgMainMenu *m_osd; }; +class mgActionWithIntValue: public mgAction +{ + public: + mgActionWithIntValue() { intval=0; } + void setValue(int i) { intval = i; } + protected: + int intval; +}; + //! \brief generate an mgAction for action mgAction* actGenerate(const mgActions action); +mgAction* actGenerateKeyItem(const char *Name, int *Value, int NumStrings, const char * const * Strings); #endif diff --git a/mg_db.c b/mg_db.c index 7e4817f..dfc0843 100644 --- a/mg_db.c +++ b/mg_db.c @@ -13,6 +13,9 @@ #include "vdr_setup.h" #include "mg_tools.h" +bool needGenre2; +bool needGenre2_set; + //! \brief adds string n to string s, using a comma to separate them static string comma (string & s, string n); @@ -36,11 +39,21 @@ comma (string & s, string n) static string zerostring; +bool +mgSelection::mgSelStrings::operator==(const mgSelStrings&x) const +{ + bool result = strings.size()==x.strings.size(); + if (result) + for (unsigned int i=0;irefreshValues(); return strings.size(); } @@ -49,7 +62,7 @@ string& mgSelection::mgSelStrings::operator[](unsigned int idx) { if (!m_sel) - mgError("mgSelStrings: m_sel is NULL"); + mgError("mgSelStrings: m_sel is 0"); m_sel->refreshValues(); if (idx>=strings.size()) return zerostring; return strings[idx]; @@ -77,9 +90,16 @@ mgSelection::getCurrentValue() string mgSelection::getKeyValue(const unsigned int level) const { - return order.getKeyValue(level); + return order.getKeyValue(level); } +unsigned int +mgSelection::getKeyIndex(const unsigned int level) const +{ + return valindex(getKeyValue(level)); +} + + mgKeyTypes mgSelection::getKeyType (const unsigned int level) const { @@ -98,26 +118,14 @@ mgSelection::exec_sql(string query) const */ string mgSelection::get_col0 (string query) const { - MYSQL_RES * sql_result = exec_sql (query); - if (!sql_result) - return "NULL"; - MYSQL_ROW row = mysql_fetch_row (sql_result); - string result; - if (row == NULL) - result = "NULL"; - else if (row[0] == NULL) - result = "NULL"; - else - result = row[0]; - mysql_free_result (sql_result); - return result; + return ::get_col0(m_db, query); } unsigned long mgSelection::exec_count (string query) const { - return atol (get_col0 (query).c_str ()); + return ::exec_count(m_db, query); } @@ -132,14 +140,30 @@ mgSelection::sql_string (const string s) const return result; } +string +mgContentItem::getKeyId(mgKeyTypes kt) +{ + if (m_id<0) + return ""; + switch (kt) { + case keyGenres: + case keyGenre1: + case keyGenre2: + case keyGenre3: return m_genre1_id; + default: return getKeyValue(kt); + } +} + string mgContentItem::getKeyValue(mgKeyTypes kt) { if (m_id<0) return ""; switch (kt) { - case keyGenre1: return getGenre1(); - case keyGenre2: return getGenre2(); + case keyGenres: + case keyGenre1: + case keyGenre2: + case keyGenre3: return getGenre(); case keyArtist: return getArtist(); case keyAlbum: return getAlbum(); case keyYear: return string(ltos(getYear())); @@ -154,26 +178,23 @@ mgContentItem * mgSelection::getTrack (unsigned int position) { if (position >= getNumTracks ()) - return NULL; + return 0; return &(m_tracks[position]); } string mgContentItem::getGenre () const { - return m_genre1; -} - - -string mgContentItem::getGenre1 () const -{ - return m_genre1; -} - - -string mgContentItem::getGenre2 () const -{ - return m_genre2; + string result=""; + if (m_genre1!="NULL") + result = m_genre1; + if (m_genre2!="NULL") + { + if (!result.empty()) + result += "/"; + result += m_genre2; + } + return result; } @@ -350,7 +371,7 @@ void mgSelection::ClearCollection (const string Name) { if (!m_db) return; string listid = id(keyCollection,Name); - exec_sql ("DELETE FROM playlistitem WHERE playlist="+listid); + exec_sql ("DELETE FROM playlistitem WHERE playlist="+sql_string(listid)); if (inCollection(Name)) clearCache (); } @@ -371,18 +392,19 @@ string mgSelection::exportM3U () { // open a file for writing - string fn = m_Directory + '/' + ListFilename () + ".m3u"; + string fn = "/tmp/" + ListFilename () + ".m3u"; FILE * listfile = fopen (fn.c_str (), "w"); if (!listfile) return ""; - fprintf (listfile, "#EXTM3U"); + fprintf (listfile, "#EXTM3U\n"); unsigned int tracksize = getNumTracks (); for (unsigned i = 0; i < tracksize; i++) { mgContentItem& t = m_tracks[i]; fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (), t.getTitle ().c_str ()); - fprintf (listfile, "%s", t.getSourceFile ().c_str ()); + fprintf (listfile, "#MUGGLE:%d\n", t.getId()); + fprintf (listfile, "%s\n", t.getSourceFile (false).c_str ()); } fclose (listfile); return fn; @@ -400,45 +422,40 @@ mgSelection::empty() void mgSelection::setPosition (unsigned int position) { - if (m_level < order.size ()) - m_position[m_level] = position; - if (m_level >= order.size ()-1) - setTrackPosition(position); + if (m_level == order.size()) + setTrackPosition(position); + else + m_position = position; } - void mgSelection::setTrackPosition (unsigned int position) { - m_tracks_position = position; + m_tracks_position = position; } - unsigned int -mgSelection::getPosition (unsigned int level) const +mgSelection::getPosition () const { - if (level == order.size ()) - return getTrackPosition(); + if (m_level == order.size()) + return getTrackPosition(); else - return m_position[m_level]; + return m_position; } unsigned int -mgSelection::gotoPosition (unsigned int level) +mgSelection::gotoPosition () { - if (level>order.size()) - mgError("mgSelection::gotoPosition: level %u > order.size %u", - level,order.size()); - if (level == order.size ()) + if (m_level == order.size ()) return gotoTrackPosition(); else { unsigned int valsize = values.size(); if (valsize==0) - m_position[m_level] = 0; - else if (m_position[m_level] >= valsize) - m_position[m_level] = valsize -1; - return m_position[m_level]; + m_position = 0; + else if (m_position >= valsize) + m_position = valsize -1; + return m_position; } } @@ -517,13 +534,18 @@ mgSelection::getCompletedLength () const string mgSelection::getListname () const { - string - result = ""; + list st; for (unsigned int i = 0; i < m_level; i++) - addsep (result, ":", getKeyValue(i)); + st.push_back(order.getKeyValue(i)); + st.unique(); + string result=""; + for (list < string >::iterator it = st.begin (); it != st.end (); ++it) + { + addsep (result, ":", *it); + } if (result.empty ()) if (order.size()>0) - result = string(ktName(order.Key(0)->Type ())); + result = string(ktName(order.getKeyType(0))); return result; } @@ -531,6 +553,20 @@ string mgSelection::ListFilename () { string res = getListname (); // convert char set ? + string::iterator it; + for (it=res.begin();it!=res.end();it++) + { + char& c = *it; + switch (c) + { + case '\'': + case '/': + case '\\': + case ' ': + case ')': + case '(': c = '_';break; + } + } return res; } @@ -557,23 +593,26 @@ mgSelection::tracks () const p.tables.push_back("tracks"); p.tables.push_back("album"); for (unsigned int i = m_level; iParts(true).orders; + p += order.Key(i)->Parts(true); m_current_tracks = p.sql_select(false); m_tracks.clear (); MYSQL_RES *rows = exec_sql (m_current_tracks); if (rows) { MYSQL_ROW row; - while ((row = mysql_fetch_row (rows)) != NULL) - { + while ((row = mysql_fetch_row (rows)) != 0) m_tracks.push_back (mgContentItem (this,row)); - } mysql_free_result (rows); } return m_tracks; } +mgContentItem::mgContentItem () +{ + m_id = -1; +} + mgContentItem::mgContentItem (const mgContentItem* c) { m_id = c->m_id; @@ -581,6 +620,8 @@ mgContentItem::mgContentItem (const mgContentItem* c) m_mp3file = c->m_mp3file; m_artist = c->m_artist; m_albumtitle = c->m_albumtitle; + m_genre1_id = c->m_genre1_id; + m_genre2_id = c->m_genre2_id; m_genre1 = c->m_genre1; m_genre2 = c->m_genre2; m_bitrate = c->m_bitrate; @@ -591,6 +632,55 @@ mgContentItem::mgContentItem (const mgContentItem* c) m_channels = c->m_channels; } +#if VDRVERSNUM >= 10318 +static char *readline(FILE *f) +{ + static char buffer[MAXPARSEBUFFER]; + if (fgets(buffer, sizeof(buffer), f) > 0) { + int l = strlen(buffer) - 1; + if (l >= 0 && buffer[l] == '\n') + buffer[l] = 0; + return buffer; + } + return 0; +} +#endif + +static const char *FINDCMD = "cd '%s' 2>/dev/null && find -follow -name '%s' -print 2>/dev/null"; + +static string +GdFindFile( string tld, string mp3file ) +{ + string result = ""; + char *cmd = 0; + asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() ); + FILE *p = popen( cmd, "r" ); + if (p) + { + char *s; + if( (s = readline(p) ) != 0) + result = tld + s; + pclose(p); + } + + free( cmd ); + + return result; +} + +string +mgContentItem::getSourceFile(bool AbsolutePath) const +{ + const string& tld = the_setup.ToplevelDir; + string result=""; + if (AbsolutePath) result = tld; + if (the_setup.GdCompatibility) + result += GdFindFile(tld,m_mp3file); + else + result += m_mp3file; + return result; +} + mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row) { m_id = atol (row[0]); @@ -605,17 +695,23 @@ mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row) if (row[3]) m_artist = row[3]; else - m_artist = row[3]; + m_artist = "NULL"; if (row[4]) m_albumtitle = row[4]; else m_albumtitle = "NULL"; if (row[5]) - m_genre1 = sel->value(keyGenre1,row[5]); + { + m_genre1_id = row[5]; + m_genre1 = sel->value(keyGenres,row[5]); + } else m_genre1 = "NULL"; if (row[6]) - m_genre2 = sel->value(keyGenre2,row[5]); + { + m_genre2_id = row[6]; + m_genre2 = sel->value(keyGenres,row[6]); + } else m_genre2 = "NULL"; if (row[7]) @@ -648,31 +744,24 @@ void mgSelection::InitSelection() { m_Directory="."; InitDatabase(); m_level = 0; - m_position.reserve (20); + m_position = 0; m_tracks_position = 0; m_trackid = -1; m_shuffle_mode = SM_NONE; m_loop_mode = LM_NONE; clearCache(); values.setOwner(this); + if (!needGenre2_set) + { + needGenre2_set=true; + needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) from tracks")>1; + } } -mgSelection::mgSelection() -{ - setDB(0); - m_Host = ""; - m_User = ""; - m_Password = ""; - InitSelection (); - m_fall_through = false; -} -mgSelection::mgSelection (const string Host, const string User, const string Password, const bool fall_through) +mgSelection::mgSelection (const bool fall_through) { setDB(0); - m_Host = Host; - m_User = User; - m_Password = Password; InitSelection (); m_fall_through = fall_through; } @@ -699,37 +788,36 @@ mgSelection::setDB(MYSQL *db) order.setDB(db); } +void +mgSelection::setOrder(mgOrder* o) +{ + if (o) + { + order = *o; + order.setDB(m_db); + } + else + mgWarning("mgSelection::setOrder(0)"); +} + void mgSelection::InitFrom(mgValmap& nv) { setDB(0); - m_Host = nv.getstr("Host"); - m_User = nv.getstr("User"); - m_Password = nv.getstr("Password"); InitSelection(); m_fall_through = nv.getbool("FallThrough"); m_Directory = nv.getstr("Directory"); - for (unsigned int i = 0; i < 99 ; i++) - { - char *idx; - asprintf(&idx,"Keys.%u.Choice",i); - unsigned int v = nv.getuint(idx); - free(idx); - if (v==0) break; - setKey (i,mgKeyTypes(v) ); - } while (m_level < nv.getuint("Level")) { char *idx; - asprintf(&idx,"Keys.%u.Position",m_level); - unsigned int newpos = nv.getuint(idx); + asprintf(&idx,"order.Keys.%u.Position",m_level); + string newval = nv.getstr(idx); free(idx); - if (!enter (newpos)) - if (!select (newpos)) break; + if (!enter (newval)) + if (!select (newval)) break; } m_trackid = nv.getlong("TrackId"); - // TODO do we really need Position AND TrackPosition in muggle.state? - setPosition(nv.getlong("Position")); + setPosition(nv.getstr("Position")); if (m_level>=order.size()-1) setTrackPosition(nv.getlong("TrackPosition")); setShuffleMode(ShuffleMode(nv.getuint("ShuffleMode"))); @@ -740,18 +828,12 @@ mgSelection::InitFrom(mgValmap& nv) mgSelection::~mgSelection () { if (m_db) - { - mgDebug(3,"%X: closing m_db %X",this,m_db); mysql_close (m_db); - } } void mgSelection::InitFrom(const mgSelection* s) { setDB(0); - m_Host = s->m_Host; - m_User = s->m_User; - m_Password = s->m_Password; InitSelection(); m_fall_through = s->m_fall_through; m_Directory = s->m_Directory; @@ -759,9 +841,7 @@ void mgSelection::InitFrom(const mgSelection* s) map_ids = s->map_ids; order = s->order; m_level = s->m_level; - m_position.reserve (s->m_position.capacity()); - for (unsigned int i = 0; i < s->m_position.capacity(); i++) - m_position[i] = s->m_position[i]; + m_position = s->m_position; m_trackid = s->m_trackid; m_tracks_position = s->m_tracks_position; setShuffleMode (s->getShuffleMode ()); @@ -770,7 +850,7 @@ void mgSelection::InitFrom(const mgSelection* s) const mgSelection& mgSelection::operator=(const mgSelection &s) { - if ((&m_Host)==&(s.m_Host)) { // prevent s = s + if (this==&s) { // prevent s = s return *this; } InitFrom(&s); @@ -785,7 +865,7 @@ mgSelection::ordersize () } unsigned int -mgSelection::valindex (const string val,const bool second_try) +mgSelection::valindex (const string val,const bool second_try) const { for (unsigned int i = 0; i < values.size (); i++) { @@ -794,8 +874,9 @@ mgSelection::valindex (const string val,const bool second_try) } // nochmal mit neuen Werten: clearCache(); + refreshValues(); if (second_try) { - mgWarning("valindex: Gibt es nicht:%s",val.c_str()); + esyslog("valindex: Gibt es nicht:%s",val.c_str()); return 0; } else @@ -803,13 +884,32 @@ mgSelection::valindex (const string val,const bool second_try) } +unsigned int +mgSelection::idindex (const string id,const bool second_try) const +{ + for (unsigned int i = 0; i < m_ids.size (); i++) + if (m_ids[i] == id) + return i; + // nochmal mit neuen Werten: + clearCache(); + refreshValues(); + if (second_try) { + mgWarning("idindex: Gibt es nicht:%s",id.c_str()); + return 0; + } + else + return idindex(id,true); +} + + void mgSelection::refreshValues () const { + assert(this); if (!m_db) return; - mgOrder o1 = order; if (m_current_values.empty()) { + mgOrder o1 = order; mgParts p = order.Parts(m_level); m_current_values = p.sql_select(); values.strings.clear (); @@ -819,16 +919,16 @@ mgSelection::refreshValues () const { unsigned int num_fields = mysql_num_fields(rows); MYSQL_ROW row; - while ((row = mysql_fetch_row (rows)) != NULL) + while ((row = mysql_fetch_row (rows)) != 0) { - string r0 = "NULL"; - if (row[0]) - r0 = row[0]; + if (!row[0]) continue; + string r0 = row[0]; + if (r0=="NULL") // there is a genre NULL! + continue; if (num_fields==2) { - string r1 = "NULL"; - if (row[1]) - r1 = row[1]; + if (!row[1]) continue; + string r1 = row[1]; values.strings.push_back (r0); m_ids.push_back (r1); } @@ -848,66 +948,57 @@ mgSelection::count () const { return values.size (); } - void mgSelection::InitDatabase () { if (m_db) { - mgDebug(3,"%X: InitDatabase closes %X",this,m_db); mysql_close (m_db); setDB(0); } - if (m_Host == "") return; + if (the_setup.DbHost == "") return; setDB(mysql_init (0)); - mgDebug(3,"%X: InitDatabase opens %X",this, m_db); if (!m_db) return; - if (mysql_real_connect (m_db, m_Host.c_str (), m_User.c_str (), m_Password.c_str (), - "GiantDisc", 0, NULL, 0) == NULL) { - mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s", - m_Host.c_str(),m_User.c_str(),m_Password.c_str(),mysql_error(m_db)); - mysql_close (m_db); - setDB(0); - return; - } - return; -} - - -void -mgSelection::setKey (const unsigned int level, const mgKeyTypes kt) -{ - mgKey *newkey = ktGenerate(kt,m_db); - if (level == 0 && kt == keyCollection) + bool success; + if (the_setup.DbSocket != NULL) { - order.clear (); - order += newkey; - order += ktGenerate(keyCollectionItem,m_db); - return; + mgDebug(1,"Using socket %s for connecting to Database %s as user %s.", + the_setup.DbSocket, + the_setup.DbName, + the_setup.DbUser); + mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass); + success = (mysql_real_connect( m_db, + "", + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + 0, + the_setup.DbSocket, 0 ) != 0 ); } - if (level == order.size ()) + else { - order += newkey; + mgDebug(1,"Using TCP-%s for connecting to Database %s as user %s.", + the_setup.DbHost, + the_setup.DbName, + the_setup.DbUser); + mgDebug(3,"DbPassword is: '%s'",the_setup.DbPass); + success = ( mysql_real_connect( m_db, + the_setup.DbHost, + the_setup.DbUser, + the_setup.DbPass, + the_setup.DbName, + the_setup.DbPort, + 0, 0 ) != 0 ); } - else + if (!success) { - if (level >= order.size()) - mgError("mgSelection::setKey(%u,%s): level greater than order.size() %u", - level,ktName(kt),order.size()); - delete order[level]; - order[level] = newkey; + mgWarning("Failed to connect to host '%s' as User '%s', Password '%s': Error: %s", + the_setup.DbHost,the_setup.DbUser,the_setup.DbPass,mysql_error(m_db)); + mysql_close (m_db); + setDB(0); } - - order.clean(); - -// clear values for this and following levels (needed for copy constructor) - for (unsigned int i = level; i < order.size (); i++) - order[i]->set ("",EMPTY); - - if (m_level > level) - m_level = level; - if (m_level == level) setPosition(0); + return; } @@ -921,9 +1012,11 @@ bool mgSelection::enter (unsigned int position) position = gotoPosition(); // reload adjusted position string value = values[position]; string id = m_ids[position]; + mgSelStrings prev; + if (m_fall_through && values.size()<10) + prev=values; while (1) { - mgDebug(3,"enter(level=%u,pos=%u, id=%s)",m_level,position,id.c_str()); if (m_level >= order.size () - 1) return false; order[m_level++]->set (value,id); @@ -931,18 +1024,18 @@ bool mgSelection::enter (unsigned int position) if (m_level >= order.size()) mgError("mgSelection::enter(%u): level greater than order.size() %u", m_level,order.size()); - if (m_position.capacity () == m_position.size ()) - m_position.reserve (m_position.capacity () + 10); - m_position[m_level] = 0; + m_position = 0; + refreshValues(); + if (count()==0) + break; + value = values[0]; + id = m_ids[0]; if (!m_fall_through) break; - if (count () > 1) + if (m_level==order.size()-1) + break; + if (count () > 1 && !(prev==values)) break; - if (count () == 1) - { - value = values[0]; - id = m_ids[0]; - } } return true; } @@ -950,7 +1043,6 @@ bool mgSelection::enter (unsigned int position) bool mgSelection::select (unsigned int position) { - mgDebug(3,"select(%u) on Level %d",position,m_level); if (m_level == order.size () - 1) { if (getNumTracks () <= position) @@ -966,9 +1058,10 @@ bool mgSelection::select (unsigned int position) return enter (position); } - -bool mgSelection::leave () +bool +mgSelection::leave () { + string prevvalue; if (order.empty()) { mgWarning("mgSelection::leave(): order is empty"); @@ -977,29 +1070,103 @@ bool mgSelection::leave () if (m_level == order.size ()) { m_level--; + prevvalue=order.getKeyValue(m_level); + order[m_level]->set("",EMPTY); m_trackid = -1; clearCache(); + setPosition(prevvalue); return true; } + mgSelStrings prev; + if (m_fall_through && values.size()<10) + prev=values; while (1) { if (m_level < 1) return false; - order[m_level]->set ("",EMPTY); - order[--m_level]->set ("",EMPTY); + if (m_levelset ("",EMPTY); + m_level--; + prevvalue=order.getKeyValue(m_level); + if (m_levelset ("",EMPTY); clearCache(); if (!m_fall_through) break; - if (count () > 1) + if (count () > 1 && !(prev==values)) break; } + setPosition(prevvalue); return true; } +void +mgSelection::leave_all () +{ + m_level=0; + for (unsigned int i=0;iset ("",EMPTY); + clearCache(); +} + +void +mgSelection::selectfrom(mgOrder& oldorder,mgContentItem* o) +{ + leave_all(); + string selval; + string selid; + assert(m_level==0); + for (unsigned int idx = 0; idx < ordersize(); idx++) + { + selval = EMPTY; + selid = EMPTY; + mgKeyTypes new_kt = getKeyType(idx); + for (unsigned int i=0;inew_kt + && iskeyGenre(old_kt) + && iskeyGenre(new_kt)) + { + selid = id(new_kt,value(new_kt,oldorder.getKeyId(i))); + selval= value(new_kt,selid); + } + if (selid!=EMPTY) break; + } + if (selid==EMPTY && o && o->getId()>=0) + { + selval = o->getKeyValue(new_kt); + selid = o->getKeyId(new_kt); + } + if (selid==EMPTY) + break; + if (m_levelset (selval, selid); + } + else + { + setPosition(selval); + return; + } + } + if (m_level>0) + { + m_level--; + selval = order.getKeyValue(m_level); + order[m_level]->set("",EMPTY); + setPosition(selval); + order[m_level+1]->set("",EMPTY); + } + assert(m_level& valmap = map_values[kt]; @@ -1035,11 +1202,14 @@ mgSelection::value(mgKey* k) const string mgSelection::id(mgKeyTypes kt, string val) const { - if (kt==keyGenre2) kt = keyGenre1; if (loadvalues (kt)) { map& idmap = map_ids[kt]; - return idmap[val]; + string v = idmap[val]; + if (kt==keyGenre1) v=v.substr(0,1); + if (kt==keyGenre2) v=v.substr(0,2); + if (kt==keyGenre3) v=v.substr(0,3); + return v; } else return val; @@ -1058,12 +1228,12 @@ mgSelection::id(mgKey* k) const } bool -mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const +mgSelection::UsedBefore(mgOrder *o,const mgKeyTypes kt,unsigned int level) const { - if (level>=order.size()) - level = order.size() -1; + if (level>=o->size()) + level = o->size() -1; for (unsigned int lx = 0; lx < level; lx++) - if (order.Key(lx)->Type()==kt) + if (o->getKeyType(lx)==kt) return true; return false; } @@ -1072,16 +1242,16 @@ mgSelection::UsedBefore(const mgKeyTypes kt,unsigned int level) const bool mgSelection::isCollectionlist () const { if (order.size()==0) return false; - return (order.Key(0)->Type() == keyCollection && m_level == 0); + return (order.getKeyType(0) == keyCollection && m_level == 0); } bool mgSelection::inCollection(const string Name) const { if (order.size()==0) return false; - bool result = (order.Key(0)->Type() == keyCollection && m_level == 1); + bool result = (order.getKeyType(0) == keyCollection && m_level == 1); if (result) - if (order.Key(1)->Type() != keyCollectionItem) + if (order.getKeyType(1) != keyCollectionItem) mgError("inCollection: key[1] is not keyCollectionItem"); if (!Name.empty()) result &= (order.getKeyValue(0) == Name); @@ -1089,52 +1259,8 @@ mgSelection::inCollection(const string Name) const } -#if 0 -void -keychoice(mgOrder& o,const unsigned int level) -{ - if (level > o.size ()) - return; - std::cout<<"possible choices:"; - for (mgKeyTypes kt = mgKeyTypes(1); kt <= mgKeyTypesHigh; kt = mgKeyTypes(int(kt)+1)) - { - if (level !=0 && kt == keyCollection) - continue; - if (level !=1 && kt == keyCollectionItem) - continue; - if (level == 1 && o[0]->Type() != keyCollection && kt == keyCollectionItem) - continue; - if (level == 1 && o[0]->Type() == keyCollection && kt != keyCollectionItem) - continue; - if (level >1 && o[0]->Type() == keyCollection) - break; - if (kt == keyDecade && UsedBefore(o,keyYear,level)) - continue; - if (o[0]->Type() == keyCollection) - { - std::cout<<" "<1) - std::cout<<" "<Type())); - free(n); if (i=order.size()-1) + nv.put("TrackPosition",getTrackPosition()); } -map * +map mgSelection::UsedKeyValues() { - map *result = new map; + map result; for (unsigned int idx = 0 ; idx < level() ; idx++) { - (*result)[order.Key(idx)->Type()] = order.getKeyValue(idx); + result[order.getKeyType(idx)] = order.getKeyValue(idx); } if (level() < order.size()-1) { mgKeyTypes ch = order.getKeyType(level()); - (*result)[ch] = getCurrentValue(); + result[ch] = getCurrentValue(); } return result; } @@ -1179,18 +1300,15 @@ mgSelection::UsedKeyValues() bool mgSelection::loadvalues (mgKeyTypes kt) const { + if (map_ids.count(kt)>0) + return true; + map& idmap = map_ids[kt]; mgKey* k = ktGenerate(kt,m_db); if (k->map_idfield().empty()) { delete k; return false; } - map& idmap = map_ids[kt]; - if (!idmap.empty()) - { - delete k; - return true; - } map& valmap = map_values[kt]; char *b; asprintf(&b,"select %s,%s from %s;",k->map_idfield().c_str(),k->map_valuefield().c_str(),k->map_valuetable().c_str()); @@ -1199,14 +1317,82 @@ mgSelection::loadvalues (mgKeyTypes kt) const if (rows) { MYSQL_ROW row; - while ((row = mysql_fetch_row (rows)) != NULL) + while ((row = mysql_fetch_row (rows)) != 0) { if (row[0] && row[1]) + { valmap[row[0]] = row[1]; idmap[row[1]] = row[0]; + } } mysql_free_result (rows); } delete k; return true; } + +static vector keycounts; + +unsigned int +mgSelection::keycount(mgKeyTypes kt) +{ + assert(strlen(m_db->host)); + if (keycounts.size()==0) + { + for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++) + { + keycounts.push_back(-1); + } + } + int& count = keycounts[int(kt-mgKeyTypesLow)]; + if (count==-1) + { + mgKey* k = ktGenerate(kt,m_db); + count = exec_count(k->Parts(true).sql_count()); + delete k; + } + return count; +} + + +vector +mgSelection::choices(mgOrder *o,unsigned int level, unsigned int *current) +{ + vector result; + if (level>o->size()) + { + *current = 0; + return result; + } + for (unsigned int ki=int(mgKeyTypesLow);ki<=int(mgKeyTypesHigh);ki++) + { + mgKeyTypes kt = mgKeyTypes(ki); + if (kt==o->getKeyType(level)) + { + *current = result.size(); + result.push_back(ktName(kt)); + continue; + } + if (UsedBefore(o,kt,level)) + continue; + if (kt==keyDecade && UsedBefore(o,keyYear,level)) + continue; + if (kt==keyGenre1 && UsedBefore(o,keyGenres,level)) + continue; + if (kt==keyGenre2 && UsedBefore(o,keyGenres,level)) + continue; + if (kt==keyGenre3 && UsedBefore(o,keyGenres,level)) + continue; + if (kt==keyGenre1 && UsedBefore(o,keyGenre3,level)) + continue; + if (kt==keyGenre2 && UsedBefore(o,keyGenre3,level)) + continue; + if (kt==keyGenre1 && UsedBefore(o,keyGenre2,level)) + continue; + if (kt==keyCollection || kt==keyCollectionItem) + result.push_back(ktName(kt)); + else if (keycount(kt)>1) + result.push_back(ktName(kt)); + } + return result; +} diff --git a/mg_db.h b/mg_db.h index 2ba02df..5ef26e2 100644 --- a/mg_db.h +++ b/mg_db.h @@ -37,11 +37,10 @@ class mgSelection; class mgContentItem { public: - mgContentItem () - { - } + mgContentItem (); string getKeyValue(mgKeyTypes kt); + string getKeyId(mgKeyTypes kt); //! \brief copy constructor mgContentItem(const mgContentItem* c); @@ -61,10 +60,7 @@ class mgContentItem } //! \brief returns filename - string getSourceFile () const - { - return m_mp3file; - } + string getSourceFile (bool AbsolutePath=true) const; //! \brief returns artist string getArtist () const @@ -75,13 +71,7 @@ class mgContentItem //! \brief returns the name of the album string getAlbum () const; -//! \brief returns the name of genre 1 - string getGenre1 () const; - -//! \brief returns the name of genre 2 - string getGenre2 () const; - -//! \brief returns the name of genre 1 +//! \brief returns the name of genre string getGenre () const; //! \brief returns the bitrate @@ -111,6 +101,8 @@ class mgContentItem string m_mp3file; string m_artist; string m_albumtitle; + string m_genre1_id; + string m_genre2_id; string m_genre1; string m_genre2; string m_bitrate; @@ -140,20 +132,18 @@ class mgSelection void setOwner(mgSelection* sel); public: string& operator[](unsigned int idx); - size_t size(); + bool operator==(const mgSelStrings&x) const; + size_t size() const; }; - public: +//! \brief defines an order to be used + void setOrder(mgOrder *o); + + mgOrder& getOrder() { return order; } + /*! \brief define various ways to play music in random order * \todo Party mode is not implemented, does same as SM_NORMAL */ -/*! \brief defines a field to be used as key for selection - * - * \param level 0 is the top level - * \param kt type of the key field. For possible values see mg_order.h - */ - void setKey (const unsigned int level, const mgKeyTypes kt); - enum ShuffleMode { SM_NONE, //!< \brief play normal sequence @@ -172,20 +162,12 @@ class mgSelection //! \brief escapes special characters string sql_string(const string s) const; -//! \brief the default constructor. Does not start a DB connection. - mgSelection(); - /*! \brief the main constructor - * \param Host where the data base lives. If not localhost, TCP/IP is used. - * \param User if empty, the current user is used. - * \param Password no comment * \param fall_through if TRUE: If enter() returns a choice * containing only one item, that item is automatically entered. * The analog happens with leave() */ - mgSelection (const string Host, const string User = - "", const string Password = "", const bool fall_through = - false); + mgSelection ( const bool fall_through = false); /*! \brief a copy constructor. Does a deep copy. * Some of the data base content will only be retrieved by the @@ -226,13 +208,14 @@ class mgSelection //! \brief return the current value of this key string getKeyValue (const unsigned int level) const; + unsigned int getKeyIndex(const unsigned int level) const; /*! \brief returns the current item from the value() list */ string getCurrentValue(); //! \brief returns a map (new allocated) for all used key fields and their values - map * UsedKeyValues(); + map UsedKeyValues(); //! \brief the number of key fields used for the query unsigned int ordersize (); @@ -240,18 +223,12 @@ class mgSelection //! \brief the number of music items currently selected unsigned int count () const; -//! \brief the current position in the current level - unsigned int gotoPosition () - { - return gotoPosition (m_level); - } - //! \brief the current position - unsigned int getPosition (unsigned int level)const; + unsigned int getPosition ()const; //! \brief go to the current position. If it does not exist, // go to the nearest. - unsigned int gotoPosition (unsigned int level); + unsigned int gotoPosition (); //! \brief the current position in the tracks list @@ -307,6 +284,13 @@ class mgSelection return select (valindex(value)); } + bool selectid (const string id) + { + return select(idindex(id)); + } + + void selectfrom(mgOrder& oldorder,mgContentItem* o); + /*! \brief leave the current level, go one up in the tree. * If fall_through (see constructor) is set to true, and the * level entered by leave() contains only one item, automatically @@ -322,12 +306,7 @@ class mgSelection * goes up further until a level with more than one item is reached. * \return returns false if there is no further upper level */ - bool leave (const unsigned int target_level) - { - while (m_level>target_level) - if (!leave()) return false; - return true; - } + void leave_all (); //! \brief the current level in the tree unsigned int level () const @@ -529,6 +508,8 @@ class mgSelection string id(mgKeyTypes kt, string val) const; string id(mgKey* k, string val) const; string id(mgKey* k) const; + unsigned int keycount(mgKeyTypes kt); + vector choices(mgOrder *o,unsigned int level, unsigned int *current); private: mutable map > map_values; @@ -541,20 +522,17 @@ class mgSelection //! \brief initializes maps for id/value mapping in both direction bool loadvalues (mgKeyTypes kt) const; bool m_fall_through; - vector < unsigned int >m_position; + unsigned int m_position; mutable unsigned int m_tracks_position; ShuffleMode m_shuffle_mode; LoopMode m_loop_mode; MYSQL *m_db; void setDB(MYSQL *db); - string m_Host; - string m_User; - string m_Password; unsigned int m_level; long m_trackid; mgOrder order; - bool UsedBefore (const mgKeyTypes kt, unsigned int level) const; + bool UsedBefore (mgOrder *o,const mgKeyTypes kt, unsigned int level) const; void InitSelection (); void InitDatabase (); /*! \brief returns the SQL command for getting all values. @@ -565,7 +543,8 @@ class mgSelection * entries and the wrong tracks might be played. */ string sql_values (); - unsigned int valindex (const string val,const bool second_try=false); + unsigned int valindex (const string val,const bool second_try=false) const; + unsigned int idindex (const string val,const bool second_try=false) const; string ListFilename (); string m_Directory; void loadgenres (); @@ -579,9 +558,9 @@ class mgSelection * returning only one row. * \param query the SQL query to be executed */ - unsigned long mgSelection::exec_count (string query) const; - + unsigned long exec_count (string query) const; + }; diff --git a/mg_order.c b/mg_order.c index ead9436..46d4d57 100644 --- a/mg_order.c +++ b/mg_order.c @@ -2,6 +2,12 @@ #include "mg_tools.h" #include "i18n.h" + +bool iskeyGenre(mgKeyTypes kt) +{ + return kt>=keyGenre1 && kt <= keyGenres; +} + class mgRefParts : public mgParts { public: mgRefParts(const mgReference& r); @@ -27,16 +33,6 @@ sql_string (MYSQL *db, const string s) return result; } -//! \brief adds n1=n2 to string s, using AND to separate several such items -static string -undequal (string & s, string n1, string op, string n2) -{ - if (n1.compare (n2) || op != "=") - return addsep (s, " AND ", n1 + op + n2); - else - return s; -} - /*! \brief if the SQL command works on only 1 table, remove all table * qualifiers. Example: SELECT tracks.title FROM tracks becomes SELECT title * FROM tracks @@ -83,28 +79,98 @@ exec_sql (MYSQL *db,string query) return mysql_store_result (db); } +string +get_col0(MYSQL *db, string query) +{ + MYSQL_RES * sql_result = exec_sql (db, query); + if (!sql_result) + return "NULL"; + MYSQL_ROW row = mysql_fetch_row (sql_result); + string result; + if (row == NULL) + result = "NULL"; + else if (row[0] == NULL) + result = "NULL"; + else + result = row[0]; + mysql_free_result (sql_result); + return result; +} + +int +exec_count(MYSQL *db, string query) +{ + return atol (get_col0 (db, query).c_str ()); +} + + +string& +addsep (string & s, string sep, string n) +{ + if (!n.empty ()) + { + if (!s.empty ()) + s.append (sep); + s.append (n); + } + return s; +} + + +static string +sql_list (string prefix,strlist v,string sep=",",string postfix="") +{ + string result = ""; + for (list < string >::iterator it = v.begin (); it != v.end (); ++it) + { + addsep (result, sep, *it); + } + if (!result.empty()) + { + result.insert(0," "+prefix+" "); + result += postfix; + } + return result; +} + +//! \brief converts long to string +string +itos (int i) +{ + stringstream s; + s << i; + return s.str (); +} + +//! \brief convert long to string +string +ltos (long l) +{ + stringstream s; + s << l; + return s.str (); +} class mgKeyNormal : public mgKey { public: mgKeyNormal(const mgKeyNormal& k); - mgKeyNormal(const mgKeyTypes kt, string table, string field); + mgKeyNormal(const mgKeyTypes kt, string table, string field); virtual mgParts Parts(bool orderby=false) const; string value() const; string id() const; void set(string value,string id); mgKeyTypes Type() const { return m_kt; } - virtual string expr() const { return field(); } - virtual string field() const { return m_table + "." + m_field; } + virtual string expr() const { return m_table + "." + m_field; } virtual string table() const { return m_table; } protected: - virtual string orderfield() const { return expr(); } + string IdClause(string what,string::size_type start=0,string::size_type len=string::npos) const; void AddIdClause(mgParts &result,string what) const; + string m_id; private: mgKeyTypes m_kt; string m_field; string m_table; string m_value; - string m_id; }; class mgKeyTrack : public mgKeyNormal { @@ -113,24 +179,96 @@ class mgKeyTrack : public mgKeyNormal { mgParts Parts(bool orderby=false) const; }; -class mgKeyGenre1 : public mgKeyNormal { +class mgKeyGenres : public mgKeyNormal { public: - mgKeyGenre1() : mgKeyNormal(keyGenre1,"tracks","genre1") {}; + mgKeyGenres() : mgKeyNormal(keyGenres,"tracks","genre1") {}; + mgKeyGenres(mgKeyTypes kt) : mgKeyNormal(kt,"tracks","genre1") {}; mgParts Parts(bool orderby=false) const; string map_idfield() const { return "id"; } string map_valuefield() const { return "genre"; } string map_valuetable() const { return "genre"; } + string GenreClauses(bool orderby) const; + virtual unsigned int genrelevel() const { return 4; } }; -class mgKeyGenre2 : public mgKeyNormal { +class mgKeyGenre1 : public mgKeyGenres +{ public: - mgKeyGenre2() : mgKeyNormal(keyGenre2,"tracks","genre2") {}; - mgParts Parts(bool orderby=false) const; - string map_idfield() const { return "id"; } - string map_valuefield() const { return "genre"; } - string map_valuetable() const { return "genre"; } + mgKeyGenre1() : mgKeyGenres(keyGenre1) {} + unsigned int genrelevel() const { return 1; } +}; + +class mgKeyGenre2 : public mgKeyGenres +{ + public: + mgKeyGenre2() : mgKeyGenres(keyGenre2) {} + unsigned int genrelevel() const { return 2; } +}; + +class mgKeyGenre3 : public mgKeyGenres +{ + public: + mgKeyGenre3() : mgKeyGenres(keyGenre3) {} + unsigned int genrelevel() const { return 3; } }; +string +mgKeyGenres::GenreClauses(bool orderby) const +{ + strlist g1; + strlist g2; + + if (orderby) + if (genrelevel()==4) + { + g1.push_back("tracks.genre1=genre.id"); + g2.push_back("tracks.genre2=genre.id"); + } + else + { + g1.push_back("substring(tracks.genre1,1,"+ltos(genrelevel())+")=genre.id"); + g2.push_back("substring(tracks.genre2,1,"+ltos(genrelevel())+")=genre.id"); + } + + if (id() != EMPTY) + { + unsigned int len=genrelevel(); + if (len==4) len=0; + g1.push_back(IdClause("tracks.genre1",0,genrelevel())); + g2.push_back(IdClause("tracks.genre2",0,genrelevel())); + } + + extern bool needGenre2; + if (needGenre2) + { + string o1=sql_list("(",g1," AND ",")"); + if (o1.empty()) + return ""; + string o2=sql_list("(",g2," AND ",")"); + return string("(") + o1 + " OR " + o2 + string(")"); + } + else + return sql_list("",g1," AND "); +} + + +mgParts +mgKeyGenres::Parts(bool orderby) const +{ + mgParts result; + result.clauses.push_back(GenreClauses(orderby)); + result.tables.push_back("tracks"); + if (orderby) + { + result.fields.push_back("genre.genre"); + result.fields.push_back("genre.id"); + result.tables.push_back("genre"); + result.orders.push_back("genre.genre"); + } + return result; +} + + class mgKeyLanguage : public mgKeyNormal { public: mgKeyLanguage() : mgKeyNormal(keyLanguage,"tracks","lang") {}; @@ -182,6 +320,11 @@ mgKey::~mgKey() { } +void +mgKey::setdb(MYSQL *db) +{ + m_db = db; +} mgKeyNormal::mgKeyNormal(const mgKeyNormal& k) { @@ -226,34 +369,35 @@ mgKeyNormal::Parts(bool orderby) const if (orderby) { result.fields.push_back(expr()); - result.orders.push_back(orderfield()); + result.orders.push_back(expr()); } return result; } -void -mgKeyNormal::AddIdClause(mgParts &result,string what) const +string +mgKeyNormal::IdClause(string what,string::size_type start,string::size_type len) const { assert(strlen(m_db->host)); - if (id() != EMPTY) + if (len==0) + len=string::npos; + if (id() == "'NULL'") + return what + " is NULL"; + else if (len==string::npos) + return what + "=" + sql_string(m_db,id()); + else { - string op; - string xid; - if (id() == "'NULL'") - { - op = "is"; - xid = "NULL"; - } - else - { - op = "="; - xid = sql_string(m_db,id()); - } - string clause = ""; - result.clauses.push_back(undequal(clause,what,op,xid)); + return "substring("+what + ","+ltos(start+1)+","+ltos(len)+")=" + + sql_string(m_db,id().substr(start,len)); } } +void +mgKeyNormal::AddIdClause(mgParts &result,string what) const +{ + if (id() != EMPTY) + result.clauses.push_back(IdClause(what)); +} + mgParts mgKeyTrack::Parts(bool orderby) const { @@ -270,38 +414,6 @@ mgKeyTrack::Parts(bool orderby) const return result; } -mgParts -mgKeyGenre1::Parts(bool orderby) const -{ - mgParts result; - AddIdClause(result,"tracks.genre1"); - result.tables.push_back("tracks"); - if (orderby) - { - result.fields.push_back("genre.genre"); - result.fields.push_back("tracks.genre1"); - result.tables.push_back("genre"); - result.orders.push_back("genre.genre"); - } - return result; -} - -mgParts -mgKeyGenre2::Parts(bool orderby) const -{ - mgParts result; - AddIdClause(result,"tracks.genre2"); - result.tables.push_back("tracks"); - if (orderby) - { - result.fields.push_back("genre.genre"); - result.fields.push_back("tracks.genre2"); - result.tables.push_back("genre"); - result.orders.push_back("genre.genre"); - } - return result; -} - mgParts mgKeyLanguage::Parts(bool orderby) const { @@ -366,54 +478,6 @@ mgParts::operator+=(mgParts a) return *this; } - -string& -addsep (string & s, string sep, string n) -{ - if (!n.empty ()) - { - if (!s.empty ()) - s.append (sep); - s.append (n); - } - return s; -} - - -static string -sql_list (string prefix,list < string > v,string sep=",",string postfix="") -{ - string result = ""; - for (list < string >::iterator it = v.begin (); it != v.end (); it++) - { - addsep (result, sep, *it); - } - if (!result.empty()) - { - result.insert(0," "+prefix+" "); - result += postfix; - } - return result; -} - -//! \brief converts long to string -string -itos (int i) -{ - stringstream s; - s << i; - return s.str (); -} - -//! \brief convert long to string -string -ltos (long l) -{ - stringstream s; - s << l; - return s.str (); -} - mgRefParts::mgRefParts(const mgReference& r) { tables.push_back(r.t1()); @@ -428,7 +492,7 @@ mgParts::Prepare() tables.unique(); strlist::reverse_iterator it; string prevtable = ""; - for (it = tables.rbegin(); it != tables.rend(); it++) + for (it = tables.rbegin(); it != tables.rend(); ++it) { if (!prevtable.empty()) *this += ref.Connect(prevtable,*it); @@ -475,7 +539,7 @@ mgParts::sql_count() bool mgParts::UsesTracks() { - for (list < string >::iterator it = tables.begin (); it != tables.end (); it++) + for (list < string >::iterator it = tables.begin (); it != tables.end (); ++it) if (*it == "tracks") return true; return false; } @@ -525,6 +589,10 @@ mgReference::mgReference(string t1,string f1,string t2,string f2) mgOrder::mgOrder() { + setDB(0); + setKey (0,keyArtist); + setKey (1,keyAlbum); + setKey (2,keyTrack); } mgKey* @@ -540,10 +608,22 @@ mgOrder::operator[](unsigned int idx) return Keys[idx]; } +bool +operator==(const mgOrder& a, const mgOrder &b) +{ + bool result = a.size()==b.size(); + if (result) + for (unsigned int i=0; iType()==b.Key(i)->Type(); + if (!result) break; + } + return result; +} + const mgOrder& mgOrder::operator=(const mgOrder& from) { - Name = from.Name; Keys.clear(); for (unsigned int i = 0; i < from.size();i++) { @@ -551,7 +631,7 @@ mgOrder::operator=(const mgOrder& from) k->set(from.getKeyValue(i),from.getKeyId(i)); Keys.push_back(k); } - setDB(from.m_db); + if (from.m_db) setDB(from.m_db); return *this; } @@ -562,6 +642,77 @@ mgOrder::operator+=(mgKey* k) { return *this; } +string +mgOrder::Name() +{ + string result=""; + for (unsigned int idx=0;idxType()); + } + return result; +} + +void +mgOrder::setKey (const unsigned int level, const mgKeyTypes kt) +{ + mgKey *newkey = ktGenerate(kt,m_db); + if (level == 0 && kt == keyCollection) + { + clear (); + Keys.push_back(newkey); + Keys.push_back(ktGenerate(keyCollectionItem,m_db)); + return; + } + if (level == size ()) + { + Keys.push_back(newkey); + } + else + { + if (level >= Keys.size()) + mgError("mgOrder::setKey(%u,%s): level greater than size() %u", + level,ktName(kt),Keys.size()); + delete Keys[level]; + Keys[level] = newkey; + } + +// clear values for this and following levels (needed for copy constructor) + for (unsigned int i = level; i < Keys.size (); i++) + Keys[i]->set ("",EMPTY); +} + +mgOrder::mgOrder(mgValmap& nv,char *prefix) +{ + setDB(0); + for (unsigned int i = 0; i < 999 ; i++) + { + char *idx; + asprintf(&idx,"%s.Keys.%u.Type",prefix,i); + unsigned int v = nv.getuint(idx); + free(idx); + if (v==0) break; + setKey (i,mgKeyTypes(v) ); + } +} + +mgOrder::mgOrder(vector kt) +{ + setDB(0); + setKeys(kt); +} + +void +mgOrder::setKeys(vector kt) +{ + clear(); + for (unsigned int i=0;isetdb(db); } @@ -598,7 +749,7 @@ mgKey* mgOrder::find(const mgKeyTypes kt) { keyvector::iterator i; - for (i = Keys.begin () ; i != Keys.end (); i++) + for (i = Keys.begin () ; i != Keys.end (); ++i) { if ((*i)->Type() == kt) return *i; @@ -625,25 +776,47 @@ mgOrder::clean() bool album_found = false; bool tracknb_found = false; bool title_found = false; - for (i = Keys.begin () ; i != Keys.end (); i++) + bool is_unique = false; + for (i = Keys.begin () ; i != Keys.end (); ++i) { mgKeyNormal* k = dynamic_cast(*i); album_found |= (k->Type()==keyAlbum); tracknb_found |= (k->Type()==keyTrack); title_found |= (k->Type()==keyTitle); - if (tracknb_found || (album_found && title_found)) + is_unique = tracknb_found || (album_found && title_found); + if (is_unique) { - for (j = i+1 ; j !=Keys.end(); j++) + for (j = i+1 ; j !=Keys.end(); ++j) delete *j; Keys.erase(i+1,Keys.end ()); break; } - for (j = i+1 ; j != Keys.end(); j++) - if (*i == *j) { + if (k->Type()==keyYear) + { + for (j = i+1 ; j != Keys.end(); ++j) + if ((*j)->Type() == keyDecade) + { + delete *j; + Keys.erase(j); + break; + } + } +cleanagain: + for (j = i+1 ; j != Keys.end(); ++j) + if ((*i)->Type() == (*j)->Type()) + { delete *j; Keys.erase(j); + goto cleanagain; } } + if (!is_unique) + { + if (!album_found) + Keys.push_back(ktGenerate(keyAlbum,m_db)); + if (!title_found) + Keys.push_back(ktGenerate(keyTitle,m_db)); + } } @@ -657,18 +830,37 @@ mgOrder::Parts(unsigned int level,bool orderby) const if (i==Keys.size()) break; mgKeyNormal *k = dynamic_cast(Keys[i]); k->setdb(m_db); + mgKeyTypes kt = k->Type(); + if (iskeyGenre(kt)) + { + for (unsigned int j=i+1;j<=level;j++) + { + if (j>=Keys.size()) + break; + mgKeyNormal *kn = dynamic_cast(Keys[j]); + if (kn) + { + mgKeyTypes knt = kn->Type(); + if (iskeyGenre(knt) + && knt>kt && kn->id()!=EMPTY) + goto next; + } + } + } result += k->Parts(orderby && (i==level)); +next: + continue; } return result; } +//! \brief right now thread locking should not be needed here mgReferences::mgReferences() { push_back(mgReference ("tracks","id","playlistitem","trackid")); push_back(mgReference ("playlist","id","playlistitem","playlist")); push_back(mgReference ("tracks","sourceid","album","cddbid")); - push_back(mgReference ("tracks","genre1","genre","id")); - push_back(mgReference ("tracks","genre2","genre","id")); + push_back(mgReference ("tracks","lang","language","id")); } bool @@ -691,6 +883,8 @@ mgParts mgReferences::ConnectToTracks(string table) const { mgParts result; + if (table=="tracks") + return result; result += FindConnectionBetween(table,"tracks"); if (result.empty()) { @@ -711,6 +905,8 @@ mgReferences::Connect(string table1, string table2) const mgParts result; // same table? if (table1 == table2) return result; + if (table1=="genre") return ConnectToTracks(table2); + if (table2=="genre") return ConnectToTracks(table1); // do not connect aliases. See sql_delete_from_collection if (table1.find(" as ")!=string::npos) return result; if (table2.find(" as ")!=string::npos) return result; @@ -734,8 +930,10 @@ ktGenerate(const mgKeyTypes kt,MYSQL* db) mgKey* result = 0; switch (kt) { + case keyGenres: result = new mgKeyGenres;break; case keyGenre1: result = new mgKeyGenre1;break; case keyGenre2: result = new mgKeyGenre2;break; + case keyGenre3: result = new mgKeyGenre3;break; case keyArtist: result = new mgKeyNormal(kt,"tracks","artist");break; case keyTitle: result = new mgKeyNormal(kt,"tracks","title");break; case keyTrack: result = new mgKeyTrack;break; @@ -757,8 +955,10 @@ ktName(const mgKeyTypes kt) const char * result = ""; switch (kt) { - case keyGenre1: result = "Genre";break; - case keyGenre2: result = "Genre 2";break; + case keyGenres: result = "Genre";break; + case keyGenre1: result = "Genre1";break; + case keyGenre2: result = "Genre2";break; + case keyGenre3: result = "Genre3";break; case keyArtist: result = "Artist";break; case keyTitle: result = "Title";break; case keyTrack: result = "Track";break; @@ -773,3 +973,23 @@ ktName(const mgKeyTypes kt) return tr(result); } +mgKeyTypes +ktValue(const char * name) +{ + for (int kt=int(mgKeyTypesLow);kt +ktNames() +{ + static vector result; + for (unsigned int i = int(mgKeyTypesLow); i <= int(mgKeyTypesHigh); i++) + result.push_back(ktName(mgKeyTypes(i))); + return result; +} + diff --git a/mg_order.h b/mg_order.h index d46f244..71907da 100644 --- a/mg_order.h +++ b/mg_order.h @@ -12,6 +12,8 @@ #include #include +#include "mg_valmap.h" + using namespace std; typedef list strlist; @@ -24,20 +26,26 @@ static const string EMPTY = "XNICHTGESETZTX"; string& addsep (string & s, string sep, string n); enum mgKeyTypes { - keyGenre1 = 1, + keyGenre1=1, // the genre types must have exactly this order! keyGenre2, + keyGenre3, + keyGenres, + keyDecade, + keyYear, keyArtist, + keyAlbum, keyTitle, keyTrack, - keyDecade, - keyCollection, - keyCollectionItem, - keyAlbum, keyLanguage, keyRating, - keyYear, + keyCollection, + keyCollectionItem, }; -const mgKeyTypes mgKeyTypesHigh = keyYear; +const mgKeyTypes mgKeyTypesLow = keyGenre1; +const mgKeyTypes mgKeyTypesHigh = keyCollectionItem; +const unsigned int mgKeyTypesNr = keyCollectionItem; + +bool iskeyGenre(mgKeyTypes kt); class mgParts; @@ -79,7 +87,7 @@ class mgKey { virtual string map_idfield() const { return ""; } virtual string map_valuefield() const { return ""; } virtual string map_valuetable() const { return ""; } - void setdb(MYSQL *db) { m_db = db; } + void setdb(MYSQL *db); protected: MYSQL *m_db; }; @@ -88,9 +96,10 @@ class mgKey { mgKey* ktGenerate(const mgKeyTypes kt,MYSQL *db); -const char * const -ktName(const mgKeyTypes kt); - +const char * const ktName(const mgKeyTypes kt); +mgKeyTypes ktValue(const char * name); +vector < const char*> ktNames(); + typedef vector keyvector; class mgParts { @@ -117,6 +126,8 @@ string sql_string (MYSQL *db, const string s); MYSQL_RES * exec_sql (MYSQL *db,string query); +string get_col0 (MYSQL *db,string query); +int exec_count (MYSQL *db,string query); //! \brief converts long to string string itos (int i); @@ -130,9 +141,10 @@ const unsigned int MaxKeys = 20; class mgOrder { public: mgOrder(); + mgOrder(mgValmap& nv, char *prefix); + mgOrder(vector kt); void setDB(MYSQL *db); mgParts Parts(unsigned int level,bool orderby=true) const; - string Name; const mgOrder& operator=(const mgOrder& from); mgOrder& operator+=(mgKey* k); mgKey*& operator[](unsigned int idx); @@ -146,9 +158,14 @@ public: mgKeyTypes getKeyType(unsigned int idx) const; string getKeyValue(unsigned int idx) const; string getKeyId(unsigned int idx) const; + void setKeys(vector kt); + string Name(); private: MYSQL *m_db; keyvector Keys; + void setKey (const unsigned int level, const mgKeyTypes kt); }; +bool operator==(const mgOrder& a,const mgOrder&b); //! \brief compares only the order, not the current key values + #endif // _MG_SQL_H diff --git a/mg_tools.c b/mg_tools.c index 3a484c5..3b07ba5 100644 --- a/mg_tools.c +++ b/mg_tools.c @@ -84,7 +84,8 @@ mgWarning (const char *fmt, ...) isyslog ("Warning: %s\n", buffer); #endif } - + extern void showmessage(const char*); + showmessage(buffer); va_end (ap); } diff --git a/muggle.c b/muggle.c index c4e1831..179909e 100644 --- a/muggle.c +++ b/muggle.c @@ -21,7 +21,7 @@ #include #include -static const char *VERSION = "0.1.1"; +static const char *VERSION = "0.1.2"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/mugglei.c b/mugglei.c index 2e780df..19a0b3d 100755 --- a/mugglei.c +++ b/mugglei.c @@ -23,6 +23,9 @@ #include #include +#include +#include +#include #include #include "mg_tools.h" @@ -35,6 +38,10 @@ bool import_assorted, delete_mode; #define MAX_QUERY_BUFLEN 2048 static char querybuf[MAX_QUERY_BUFLEN]; +void showmessage(const char *msg) +{ +} + MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { va_list ap; @@ -45,10 +52,6 @@ MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } - -#ifdef VERBOSE - std::cout << querybuf << std::endl; -#endif MYSQL_RES *result = mysql_store_result(db); @@ -67,10 +70,6 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); } -#ifdef VERBOSE - std::cout << querybuf << std::endl; -#endif - va_end(ap); } @@ -142,23 +141,14 @@ time_t get_db_modification_time( long uid ) TagLib::String escape_string( MYSQL *db, TagLib::String s ) { - TagLib::String r; + char *buf = strdup( s.toCString() ); + char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); - if( !s.isNull() && !s.isEmpty() ) - { - char *buf = strdup( s.toCString() ); - char *escbuf = (char *) malloc( 2*strlen( buf ) + 1 ); + mysql_real_escape_string( db, escbuf, s.toCString(), s.size() ); + TagLib::String r = TagLib::String( escbuf ); - mysql_real_escape_string( db, escbuf, s.toCString(), s.size() ); - r = TagLib::String( escbuf ); - - free( escbuf ); - free( buf); - } - else - { - r = s; - } + free( escbuf ); + free( buf); return r; } @@ -204,88 +194,48 @@ TagLib::String find_genre_id( TagLib::String genre ) // read tags from the mp3 file and store them into the corresponding database entry void update_db( long uid, std::string filename ) { - TagLib::String title, album, artist, genre, cddbid; + TagLib::String title, album, artist, genre, cddbid, language; uint trackno, year; // ID3_Tag filetag( filename.c_str() ); TagLib::FileRef f( filename.c_str() ); - if( !f.isNull() ) + if( !f.isNull() && f.tag() ) { -#ifdef VERBOSE - std::cout << "Evaluating " << filename << std::endl; -#endif + // std::cout << "Evaluating " << filename << std::endl; TagLib::Tag *tag = f.tag(); - if( tag ) - { - // obtain tag information - title = tag->title(); - album = tag->album(); - year = tag->year(); - artist = tag->artist(); - trackno = tag->track(); - genre = tag->genre(); - -#ifdef VERBOSE - std::cout << "-- TAG --" << std::endl; - std::cout << "title - '" << tag->title() << "'" << std::endl; - std::cout << "artist - '" << tag->artist() << "'" << std::endl; - std::cout << "album - '" << tag->album() << "'" << std::endl; - std::cout << "year - '" << tag->year() << "'" << std::endl; - std::cout << "comment - '" << tag->comment() << "'" << std::endl; - std::cout << "track - '" << tag->track() << "'" << std::endl; - std::cout << "genre - '" << tag->genre() << "'" << std::endl; -#endif - } - else - { -#ifdef VERBOSE - std::cerr << "No id3 tag found." << std::endl; -#endif - - // use basename - TagLib::String file( filename.c_str() ); - - int pos = title.size(); - while( pos > 0 && title[pos] != '\\' ) - { - pos --; - } - - title = file.substr( pos ); - - // will be associated to an Unassigned album for Unknown - album = ""; - artist = "Unknown"; - - year = 0; - trackno = 0; - } - - if( title.isNull() || title.isEmpty() ) - { -#ifdef VERBOSE - std::cout << "No title tag found." << std::endl; -#endif - // use basename - TagLib::String file( filename.c_str() ); - - int pos = title.size(); - while( pos > 0 && title[pos] != '\\' ) - { - pos --; - } - - title = file.substr( pos ); - } - if( artist.isNull() || artist.isEmpty() ) - { - artist = "Unknown"; -#ifdef VERBOSE - std::cout << "No artist tag found." << std::endl; -#endif - } + // obtain tag information + title = tag->title(); + album = tag->album(); + year = tag->year(); + artist = tag->artist(); + trackno = tag->track(); + genre = tag->genre(); + language = ""; + TagLib::ID3v2::Tag * id3v2tag=0; + if (filename.substr(filename.size()-5)==".flac") + { + TagLib::FLAC::File f(filename.c_str()); + id3v2tag = f.ID3v2Tag(); + if (id3v2tag) + { + TagLib::ID3v2::FrameList l = id3v2tag->frameListMap()["TLAN"]; + if (!l.isEmpty()) + language = l.front()->toString(); + } + } + else if (filename.substr(filename.size()-4)==".mp3") + { + TagLib::MPEG::File f(filename.c_str()); + id3v2tag = f.ID3v2Tag(); + if (id3v2tag) + { + TagLib::ID3v2::FrameList l = id3v2tag->frameListMap()["TLAN"]; + if (!l.isEmpty()) + language = l.front()->toString(); + } + } TagLib::String gid = find_genre_id( genre ); @@ -304,7 +254,7 @@ void update_db( long uid, std::string filename ) // finally update the database // obtain associated album or create - if( album.isNull() || album.isEmpty() ) + if( album == "" ) { // no album found, create default album for artist MYSQL_RES *result = mgSqlReadQuery( db, "SELECT cddbid FROM album WHERE title=\"Unassigned\" AND artist=\"%s\"", artist.toCString() ); MYSQL_ROW row = mysql_fetch_row( result ); @@ -385,20 +335,32 @@ void update_db( long uid, std::string filename ) mgSqlWriteQuery( db, "UPDATE tracks SET artist=\"%s\", title=\"%s\", year=\"%d\"," "sourceid=\"%s\", mp3file=\"%s\", length=%d, bitrate=\"%d\"," - "samplerate=%d, channels=%d, genre1=\"%s\" WHERE id=%d", + "samplerate=%d, channels=%d, genre1=\"%s\", lang=\"%s\" WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.toCString(), filename.c_str(), len, bitrate, - sample, channels, gid.toCString(), uid ); + sample, channels, gid.toCString(), uid, language.toCString() ); } else { // the entry does not exist, create it mgSqlWriteQuery( db, "INSERT INTO tracks " - "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2) VALUES" - "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\")", + "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2,lang) VALUES" + "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\",\"%s\")", artist.toCString(), title.toCString(), year, cddbid.toCString(), - trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString() ); + trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString(), + language.toCString()); +#ifdef VERBOSE + std::cout << "-- TAG --" << std::endl; + std::cout << "title - '" << tag->title() << "'" << std::endl; + std::cout << "artist - '" << tag->artist() << "'" << std::endl; + std::cout << "album - '" << tag->album() << "'" << std::endl; + std::cout << "year - '" << tag->year() << "'" << std::endl; + std::cout << "comment - '" << tag->comment() << "'" << std::endl; + std::cout << "track - '" << tag->track() << "'" << std::endl; + std::cout << "genre - '" << tag->genre() << "'" << std::endl; + std::cout << "language- '" << language << "'" << std::endl; +#endif } } } diff --git a/scripts/languages.txt b/scripts/languages.txt index 68dfdec..a24d25b 100755 --- a/scripts/languages.txt +++ b/scripts/languages.txt @@ -13,6 +13,7 @@ en English eo Esperanto es Spanish fi Finnish +fon Fon fr French hi Hindi hu Hungarian diff --git a/vdr_decoder.c b/vdr_decoder.c index d5e8004..93dc540 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -25,6 +25,8 @@ #include "vdr_decoder.h" #include "vdr_decoder_mp3.h" +extern void showmessage(const char *); + #ifdef HAVE_VORBISFILE #include "vdr_decoder_ogg.h" #endif @@ -60,13 +62,21 @@ mgMediaType mgDecoders::getMediaType (std::string s) { if (!strcmp (p, ".ogg")) { +#ifdef HAVE_VORBISFILE mt = MT_OGG; +#else + mgWarning("Support for vorbis not compiled in, define HAVE_VORBISFILE in Makefile"); +#endif } else { if (!strcmp (p, ".flac")) { +#ifdef HAVE_FLAC mt = MT_FLAC; +#else + mgWarning("Support for flac not compiled in, define HAVE_FLAC in Makefile"); +#endif } } } @@ -79,11 +89,19 @@ mgDecoders::findDecoder (mgContentItem * item) { mgDecoder *decoder = 0; - std::string filename = the_setup.getFilename( item->getSourceFile () ); + std::string filename = item->getSourceFile (); struct stat st; if (stat (filename.c_str (), &st)) { + char *b=0; + int nsize = filename.size(); + if (nsize<30) + asprintf(&b,tr("%s not readable"),filename.c_str()); + else + asprintf(&b,tr("%s..%s not readable"),filename.substr(0,20).c_str(),filename.substr(nsize-20).c_str());; + showmessage(b); + free(b); esyslog ("ERROR: cannot stat %s. Meaning not found, not a valid file, or no access rights", filename.c_str ()); return 0; } diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c index c640205..f18ceab 100644 --- a/vdr_decoder_flac.c +++ b/vdr_decoder_flac.c @@ -34,8 +34,7 @@ mgFlacDecoder::mgFlacDecoder( mgContentItem *item ) { mgLog lg( "mgFlacDecoder::mgFlacDecoder" ); - m_filename = the_setup.getFilename( item->getSourceFile () ); - // m_filename = item->getSourceFile(); + m_filename = item->getSourceFile(); m_pcm = 0; m_reservoir = 0; @@ -100,10 +99,10 @@ bool mgFlacDecoder::clean() if( m_reservoir ) { - delete m_reservoir[0]; - delete m_reservoir[1]; + delete[] m_reservoir[0]; + delete[] m_reservoir[1]; } - delete m_reservoir; + delete[] m_reservoir; // why false? true? return true; diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c index 754ab39..2ad6bc3 100644 --- a/vdr_decoder_mp3.c +++ b/vdr_decoder_mp3.c @@ -60,8 +60,7 @@ mgMP3Decoder::mgMP3Decoder (mgContentItem * item, bool preinit):mgDecoder m_stream = 0; m_isStream = false; - m_filename = the_setup.getFilename( item->getSourceFile () ); - // m_filename = item->getSourceFile (); + m_filename = item->getSourceFile (); if (preinit) { @@ -102,6 +101,7 @@ mgMP3Decoder::init () m_playtime = mad_timer_zero; m_skiptime = mad_timer_zero; m_framenum = m_framemax = 0; + m_frameinfo = 0; m_mute = m_errcount = 0; } diff --git a/vdr_decoder_ogg.c b/vdr_decoder_ogg.c index 24d253d..96c44cf 100644 --- a/vdr_decoder_ogg.c +++ b/vdr_decoder_ogg.c @@ -239,8 +239,7 @@ mgOggFile::stream (short *buffer, int samples) mgOggDecoder::mgOggDecoder (mgContentItem * item):mgDecoder (item) { - // m_filename = item->getSourceFile (); - m_filename = the_setup.getFilename( item->getSourceFile () ); + m_filename = item->getSourceFile (); m_file = new mgOggFile (m_filename); m_pcm = 0; init (); diff --git a/vdr_menu.c b/vdr_menu.c index e3db994..e7e107b 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -45,7 +45,8 @@ mgStatus::OsdCurrentItem(const char* Text) mgAction * a = dynamic_cast(i); if (!a) mgError("mgStatus::OsdCurrentItem expected an mgAction*"); - a->TryNotify(); + if (a) + a->TryNotify(); } void Play(mgSelection *sel,const bool select) { @@ -81,6 +82,27 @@ mgMainMenu::PlayInstant(const bool select) Play(selection(),select); } +void +mgMainMenu::setOrder(mgSelection *sel,unsigned int idx) +{ + mgOrder* o = getOrder(idx); + if (o->size()>0) + { + m_current_order = idx; + sel->setOrder(o); + } + else + mgWarning("mgMainMenu::setOrder: orders[%u] is empty",idx); +} + +mgOrder* mgMainMenu::getOrder(unsigned int idx) +{ + if (idx>=orders.size()) + mgError("mgMainMenu::getOrder(%u): orders.size() is %d", + idx,orders.size()); + return orders[idx]; +} + void mgMainMenu::CollectionChanged(string name) { @@ -151,7 +173,7 @@ mgMenu::GenerateAction(const mgActions action,mgActions on) return result; } -void +eOSState mgMenu::ExecuteAction(const mgActions action,mgActions on) { mgAction *a = GenerateAction (action,on); @@ -159,7 +181,9 @@ mgMenu::ExecuteAction(const mgActions action,mgActions on) { a->Execute (); delete a; + return osContinue; } + return osUnknown; } @@ -178,19 +202,42 @@ PlayerControl () mgMenu::mgMenu () { m_osd = NULL; - TreeRedAction = mgActions(0); - TreeGreenAction = mgActions(0); - TreeYellowAction = mgActions(0); - CollRedAction = mgActions(0); - CollGreenAction = mgActions(0); - CollYellowAction = mgActions(0); + m_parent_index=-1; + TreeRedAction = actNone; + TreeGreenAction = actNone; + TreeYellowAction = actNone; + TreeBlueAction = actNone; + CollRedAction = actNone; + CollGreenAction = actNone; + CollYellowAction = actNone; + CollBlueAction = actNone; } // ----------------------- mgMainMenu ---------------------- -void mgMainMenu::SaveState() +void +mgMainMenu::DumpOrders(mgValmap& nv) +{ + map::iterator it; + for (unsigned int idx=0;idxsize();i++) + { + asprintf(&n,"order%u.Keys.%d.Type",idx,i); + nv.put(n,int(o->Key(i)->Type())); + free(n); + } + } +} + +void +mgMainMenu::SaveState() { char *b; asprintf(&b,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle")); @@ -206,6 +253,8 @@ void mgMainMenu::SaveState() nmain.put("CollRedAction",int(Menus.front()->CollRedAction)); nmain.put("CollGreenAction",int(Menus.front()->CollGreenAction)); nmain.put("CollYellowAction",int(Menus.front()->CollYellowAction)); + nmain.put("CurrentOrder",m_current_order); + DumpOrders(nmain); mgValmap nsel("tree"); m_treesel.DumpState(nsel); mgValmap ncol("collection"); @@ -216,7 +265,7 @@ void mgMainMenu::SaveState() fclose(f); } -mgMainMenu::mgMainMenu ():cOsdMenu ("") +mgMainMenu::mgMainMenu ():cOsdMenu ("",25) { m_Status = new mgStatus(this); m_message = NULL; @@ -230,11 +279,7 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") mgValmap nmain("MainMenu"); // define defaults for values missing in state file: - nsel.put("Keys.0.Choice",keyArtist); - nsel.put("Keys.1.Choice",keyAlbum); - nsel.put("Keys.2.Choice",keyTitle); - ncol.put("Keys.0.Choice",keyCollection); - ncol.put("Keys.1.Choice",keyCollectionItem); + nsel.put("FallThrough",true); nmain.put("DefaultCollection",play_collection); nmain.put("UsingCollection","false"); nmain.put("TreeRedAction",int(actAddThisToCollection)); @@ -257,21 +302,31 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") } // get values from mgValmaps + LoadOrders(nmain); default_collection = nmain.getstr("DefaultCollection"); UsingCollection = nmain.getbool("UsingCollection"); InitMapFromSetup(nsel); - m_treesel.InitFrom (nsel); InitMapFromSetup(ncol); + m_treesel.setOrder(getOrder(m_current_order)); + m_treesel.InitFrom (nsel); + m_treesel.CreateCollection(default_collection); + if (default_collection!=play_collection) + m_treesel.CreateCollection(play_collection); + vector kt; + kt.push_back(keyCollection); + mgOrder o; + o.setKeys(kt); + m_collectionsel.setOrder(&o); m_collectionsel.InitFrom (ncol); + m_playsel.setOrder(&o); m_playsel.InitFrom(ncol); // initialize - m_collectionsel.CreateCollection(default_collection); - m_collectionsel.CreateCollection(play_collection); - m_playsel.setKey(0,keyCollection); - m_playsel.setKey(1,keyCollectionItem); - m_playsel.leave(0); - m_playsel.enter(play_collection); + if (m_playsel.level()!=1) + { + m_playsel.leave_all(); + m_playsel.enter(play_collection); + } UseNormalSelection (); unsigned int posi = selection()->gotoPosition(); LoadExternalCommands(); // before AddMenu() @@ -289,6 +344,57 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("") forcerefresh = false; } +void +mgMainMenu::AddOrder() +{ + orders.push_back(new mgOrder); +} + +void +mgMainMenu::DeleteOrder() +{ + orders.erase(orders.begin()+Current()); +} + +void +mgMainMenu::LoadOrders(mgValmap& nv) +{ + for (unsigned int idx=0;idx<10;idx++) + { + char b[10]; + sprintf(b,"order%u",idx); + mgOrder *o = new mgOrder(nv,b); + if (o->size()==0) + { + delete o; + continue; + } + orders.push_back(o); + } + m_current_order = nv.getuint("CurrentOrder"); + if (m_current_order >= orders.size()) + m_current_order=0; + if (orders.size()>0) return; + + nv.put("order1.Keys.0.Type",keyArtist); + nv.put("order1.Keys.1.Type",keyAlbum); + nv.put("order1.Keys.2.Type",keyTrack); + + nv.put("order2.Keys.0.Type",keyAlbum); + nv.put("order2.Keys.1.Type",keyTrack); + + nv.put("order3.Keys.0.Type",keyGenres); + nv.put("order3.Keys.1.Type",keyArtist); + nv.put("order3.Keys.2.Type",keyAlbum); + nv.put("order3.Keys.3.Type",keyTrack); + + nv.put("order4.Keys.0.Type",keyArtist); + nv.put("order4.Keys.1.Type",keyTrack); + + nv.put("CurrentOrder",0); + LoadOrders(nv); +} + void mgMainMenu::LoadExternalCommands() { @@ -326,11 +432,7 @@ void mgMainMenu::InitMapFromSetup (mgValmap& nv) { // values from setup override saved values - nv["Host"] = the_setup.DbHost; - nv["User"] = the_setup.DbUser; - nv["Password"] = the_setup.DbPass; nv["Directory"] = cPlugin::ConfigDirectory ("muggle"); - nv["ToplevelDir"] = the_setup.ToplevelDir; } void @@ -353,12 +455,28 @@ mgMenu::AddAction (const mgActions action, mgActions on,const bool hotkey) void mgMenu::AddExternalAction(const mgActions action, const char *title) { - mgAction *a = GenerateAction(action,mgActions(0)); + mgAction *a = GenerateAction(action,actNone); if (!a) return; a->SetText(osd()->hk(title)); osd()->AddItem(a); } +void +mgMainMenu::AddOrderActions(mgMenu* m) +{ + map::iterator it; + for (unsigned int idx=0;idxGenerateAction(actOrder,actNone); + assert(a); + a->SetText(hk(o->Name().c_str())); + AddItem(a); + } +} + void mgMenu::AddSelectionItems (mgSelection *sel,mgActions act) { @@ -371,7 +489,7 @@ mgMenu::AddSelectionItems (mgSelection *sel,mgActions act) } if (osd()->ShowingCollections ()) { - mgAction *a = GenerateAction(actCreateCollection,mgActions(0)); + mgAction *a = GenerateAction(actCreateCollection,actNone); if (a) { a->SetText(a->MenuName(),false); @@ -381,20 +499,6 @@ mgMenu::AddSelectionItems (mgSelection *sel,mgActions act) } -const char * -mgMenu::BlueName (mgActions on) -{ - // do not use typeid because we want to catch descendants too - if (dynamic_cast(this)) - return tr ("List"); - else if (osd()->Current()<0) - return tr("Commands"); - else if (dynamic_cast(this)) - return tr("Commands"); - else - return tr ("List"); -} - const char* mgMenu::HKey(const mgActions act,mgActions on) { @@ -411,23 +515,25 @@ mgMenu::HKey(const mgActions act,mgActions on) void mgMenu::SetHelpKeys(mgActions on) { - mgActions r,g,y; + mgActions r,g,y,b; if (osd()->UsingCollection) { r = CollRedAction; g = CollGreenAction; y = CollYellowAction; + b = CollBlueAction; } else { r = TreeRedAction; g = TreeGreenAction; y = TreeYellowAction; + b = TreeBlueAction; } osd()->SetHelpKeys(HKey(r,on), HKey(g,on), HKey(y,on), - BlueName(on)); + HKey(b,on)); } @@ -463,8 +569,7 @@ mgSubmenu::BuildOsd () snprintf(b,99,tr("Commands:%s"),trim(osd()->selection()->getCurrentValue()).c_str()); mgActions on = osd()->CurrentType(); InitOsd (b); - mgMenu *p = osd ()->Parent (); - if (!p) + if (!osd ()->Parent ()) return; AddAction(actInstantPlay,on); AddAction(actAddThisToCollection,on); @@ -502,7 +607,7 @@ mgSubmenu::BuildOsd () mgActions mgMainMenu::CurrentType() { - mgActions result = mgActions(0); + mgActions result = actNone; cOsdItem* c = Get(Current()); if (c) { @@ -518,37 +623,36 @@ eOSState mgMenu::ExecuteButton(eKeys key) { mgActions on = osd()->CurrentType(); - switch (key) - { - case kRed: - if (osd()->UsingCollection) - ExecuteAction (CollRedAction,on); - else - ExecuteAction (TreeRedAction,on); - return osContinue; - case kGreen: - if (osd()->UsingCollection) - ExecuteAction (CollGreenAction,on); - else - ExecuteAction (TreeGreenAction,on); - return osContinue; - case kYellow: - if (osd()->UsingCollection) - ExecuteAction (CollYellowAction,on); - else - ExecuteAction (TreeYellowAction,on); - return osContinue; - case kBlue: - osd()->newmenu = new mgSubmenu; - return osContinue; - default: - break; - } - return osUnknown; + mgActions action = actNone; + if (osd()->UsingCollection) + switch (key) + { + case kRed: action = CollRedAction; break; + case kGreen: action = CollGreenAction; break; + case kYellow: action = CollYellowAction; break; + case kBlue: action = CollBlueAction; break; + default: break; + } + else + switch (key) + { + case kRed: action = TreeRedAction; break; + case kGreen: action = TreeGreenAction; break; + case kYellow: action = TreeYellowAction; break; + case kBlue: action = TreeBlueAction; break; + default: break; + } + return ExecuteAction(action,on); +} + +mgTree::mgTree() +{ + TreeBlueAction = actShowCommands; + CollBlueAction = actShowCommands; } eOSState -mgTree::Process (eKeys key) +mgMenu::Process (eKeys key) { return ExecuteButton(key); } @@ -695,6 +799,7 @@ void mgMainMenu::CloseMenu() { mgMenu* m = Menus.back(); + if (newposition==-1) newposition = m->getParentIndex(); Menus.pop_back (); delete m; } @@ -704,24 +809,32 @@ mgMainMenu::showMessage() { if (m_message) { + showmessage(m_message); + free(m_message); + m_message = NULL; + } +} + +void +showmessage(const char * msg) +{ #if VDRVERSNUM >= 10307 - Skins.Message (mtInfo, m_message,2); + Skins.Message (mtInfo, msg,2); Skins.Flush (); #else - Interface->Status (m_message); + Interface->Status (msg); Interface->Flush (); #endif - free(m_message); - m_message = NULL; - } } - void mgMainMenu::AddMenu (mgMenu * m,unsigned int position) { Menus.push_back (m); m->setosd (this); + m->setParentIndex(Current()); + if (Get(Current())) + m->setParentName(Get(Current())->Text()); m->Display (position); } @@ -732,25 +845,94 @@ mgMenu::setosd(mgMainMenu *osd) m_prevUsingCollection = osd->UsingCollection; } -eOSState -mgSubmenu::Process (eKeys key) +mgSubmenu::mgSubmenu() { - return osUnknown; + TreeBlueAction = actShowList; + CollBlueAction = actShowList; } void mgMenuOrders::BuildOsd () { - InitOsd (tr ("Select an order")); - AddAction(ActOrderCollItem); - AddAction(ActOrderArtistAlbumTitle); - AddAction(ActOrderArtistTitle); - AddAction(ActOrderAlbumTitle); - AddAction(ActOrderGenreYearTitle); - AddAction(ActOrderGenreArtistAlbumTitle); + TreeRedAction = actEditOrder; + TreeGreenAction = actCreateOrder; + TreeYellowAction = actDeleteOrder; + InitOsd (tr ("Select an order")); + osd()->AddOrderActions(this); +} + +mgMenuOrder::mgMenuOrder() +{ + m_order=0; } +mgMenuOrder::~mgMenuOrder() +{ + if (m_order) + delete m_order; +} + +void +mgMenuOrder::BuildOsd () +{ + if (!m_order) + { + m_order = new mgOrder; + *m_order = *(osd()->getOrder(getParentIndex())); + } + InitOsd (m_order->Name().c_str()); + m_keytypes.clear(); + m_keytypes.reserve(mgKeyTypesNr+1); + m_keynames.clear(); + m_keynames.reserve(50); + for (unsigned int i=0;isize();i++) + { + unsigned int kt; + m_keynames.push_back(selection()->choices(m_order,i,&kt)); + m_keytypes.push_back(kt); + char buf[20]; + sprintf(buf,tr("Key %d"),i+1); + mgAction *a = actGenerateKeyItem(buf,(int*)&m_keytypes[i],m_keynames[i].size(),&m_keynames[i][0]); + a->SetMenu(this); + osd()->AddItem(a); + } +} + +bool +mgMenuOrder::ChangeOrder(eKeys key) +{ + vector newtypes; + newtypes.clear(); + for (unsigned int i=0; iforcerefresh = true; + int np = osd()->Current(); + if (key==kUp && np) np--; + if (key==kDown) np++; + osd()->newposition = np; + } + return result; +} + +void +mgMenuOrder::SaveOrder() +{ + *(osd()->getOrder(getParentIndex())) = *m_order; + osd()->SaveState(); +} + + +mgTreeCollSelector::mgTreeCollSelector() +{ + TreeBlueAction = actShowList; + CollBlueAction = actShowList; +} mgTreeCollSelector::~mgTreeCollSelector() { @@ -763,10 +945,10 @@ mgTreeCollSelector::BuildOsd () osd()->UsingCollection = true; mgSelection *coll = osd()->collselection(); InitOsd (m_title.c_str()); - coll->leave(0); + coll->leave_all(); coll->setPosition(osd()->default_collection); AddSelectionItems (coll,coll_action()); - osd()->newposition = coll->gotoPosition(0); + osd()->newposition = coll->gotoPosition(); cOsdItem *c = osd()->Get(osd()->newposition); mgAction *a = dynamic_cast(c); a->IgnoreNextEvent = true; @@ -787,13 +969,12 @@ mgMainMenu::DisplayGoto (unsigned int select) { if (select >= 0) { + if ((int)select>=Count()) + select = Count() -1; SetCurrent (Get (select)); RefreshCurrent (); } Display (); -#if VDRVERSNUM >= 10307 - DisplayMenu()->SetTabs(25); -#endif } diff --git a/vdr_menu.h b/vdr_menu.h index faadb87..ddd9787 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -31,6 +31,8 @@ using namespace std; //! \param select if true, play only what the current position selects void Play(mgSelection *sel,const bool select=false); +void showmessage(const char *msg); + class cCommands; class mgSelection; @@ -68,7 +70,18 @@ class mgMainMenu:public cOsdMenu char *m_message; void showMessage(); void LoadExternalCommands(); + vector orders; + unsigned int m_current_order; + void DumpOrders(mgValmap& nv); + void LoadOrders(mgValmap& nv); public: + void AddOrder(); + void DeleteOrder(); + void AddOrderActions(mgMenu *m); + unsigned int getCurrentOrder() { return m_current_order; } + mgOrder* getOrder(unsigned int idx); + void setOrder(mgSelection *sel, unsigned int idx); + mgSelection *moveselection; mgActions CurrentType(); @@ -243,7 +256,8 @@ class mgMenu // creation. void AddSelectionItems (mgSelection *sel,mgActions act = actEntry); //! \brief the name of the blue button depends of where we are - const char *mgMenu::BlueName (mgActions on); + int m_parent_index; + string m_parent_name; public: /*! sets the correct help keys. * \todo without data from mysql, no key is shown, @@ -254,11 +268,16 @@ class mgMenu mgAction* GenerateAction(const mgActions action,mgActions on); //! \brief executes the wanted action - void ExecuteAction (const mgActions action, const mgActions on); + eOSState ExecuteAction (const mgActions action, const mgActions on); //! \brief sets the pointer to the owning mgMainMenu void setosd (mgMainMenu* osd); + void setParentIndex(int idx) { m_parent_index = idx; } + int getParentIndex() { return m_parent_index; } + void setParentName(string name) { m_parent_name = name; } + string getParentName() { return m_parent_name; } + //! \brief the pointer to the owning mgMainMenu mgMainMenu* osd () { @@ -299,10 +318,7 @@ class mgMenu * value for a new display. If NULL is returned, the caller will display * the previous menu. */ - virtual eOSState Process (eKeys Key) - { - return osUnknown; - } + virtual eOSState Process (eKeys Key); //! \brief the ID of the action defined by the red button. mgActions TreeRedAction; @@ -315,13 +331,17 @@ class mgMenu //! \brief the action defined by the yellow button. mgActions TreeYellowAction; mgActions CollYellowAction; + +//! \brief the action defined by the blue button. + mgActions TreeBlueAction; + mgActions CollBlueAction; }; //! \brief an mgMenu class for navigating through the data base class mgTree:public mgMenu { public: - eOSState Process (eKeys Key); + mgTree(); protected: void BuildOsd (); }; @@ -330,7 +350,7 @@ class mgTree:public mgMenu class mgSubmenu:public mgMenu { public: - eOSState Process (eKeys Key); + mgSubmenu::mgSubmenu(); protected: void BuildOsd (); }; @@ -342,10 +362,27 @@ class mgMenuOrders:public mgMenu void BuildOsd (); }; +class mgMenuOrder : public mgMenu +{ + public: + mgMenuOrder(); + ~mgMenuOrder(); + bool ChangeOrder(eKeys key); + void SaveOrder(); + protected: + void BuildOsd (); + private: + void AddKeyActions(mgMenu *m,mgOrder *o); + mgOrder * m_order; + vector m_keytypes; + vector < vector > m_keynames; +}; + //! \brief an mgMenu class for selecting a collection class mgTreeCollSelector:public mgMenu { public: + mgTreeCollSelector(); ~mgTreeCollSelector(); protected: void BuildOsd (); diff --git a/vdr_player.c b/vdr_player.c index e4dd7ef..cea9e3b 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -323,10 +323,10 @@ mgPCMPlayer::SetPlayMode (ePlayMode mode) { m_playmode_mutex.Lock (); if (mode != m_playmode) - { + { m_playmode = mode; m_playmode_cond.Broadcast (); - } + } m_playmode_mutex.Unlock (); } @@ -336,10 +336,10 @@ mgPCMPlayer::WaitPlayMode (ePlayMode mode, bool inv) { // must be called with m_playmode_mutex LOCKED !!! - while( m_active - && ((!inv && mode != m_playmode) || (inv && mode == m_playmode)) ) + while (m_active + && ((!inv && mode != m_playmode) || (inv && mode == m_playmode))) { - m_playmode_cond.Wait (m_playmode_mutex); + m_playmode_cond.Wait (m_playmode_mutex); } } @@ -412,18 +412,20 @@ mgPCMPlayer::Action (void) if (m_playing) { - if ((m_decoder = mgDecoders::findDecoder (m_playing)) - && m_decoder->start ()) + std::string filename = m_playing->getSourceFile (); + if ((m_decoder = mgDecoders::findDecoder (m_playing)) + && m_decoder->start ()) { levelgood = true; haslevel = false; - scale.Init (); level.Init (); m_state = msDecode; break; } + else + mgWarning("found no decoder for %s",filename.c_str()); } m_state = msEof; } @@ -620,8 +622,8 @@ mgPCMPlayer::Action (void) { if (m_ringbuffer->Available () == 0) { - // m_active = false; - SetPlayMode (pmStopped); + m_active = false; + SetPlayMode (pmStopped); } } break; diff --git a/vdr_setup.c b/vdr_setup.c index 2239191..0334fea 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -24,8 +24,6 @@ mgSetup the_setup; -std::string GdFindFile( std::string mp3file, std::string ToplevelDir ); -char *readline(FILE *f); // --- mgMenuSetup ----------------------------------------------------------- @@ -92,52 +90,3 @@ mgSetup::mgSetup () Only48kHz = 0; ToplevelDir = "/mnt/music/"; } - -std::string -mgSetup::getFilename( std::string basename ) -{ - if( GdCompatibility ) - { - return GdFindFile( basename, std::string( ToplevelDir ) ); - } - else - { - return std::string( ToplevelDir ) + basename; - } -} - -#define FINDCMD "cd '%s' && find -follow -name '%s' 2> /dev/null" - -char *readline(FILE *f) -{ - static char buffer[MAXPARSEBUFFER]; - if (fgets(buffer, sizeof(buffer), f) > 0) { - int l = strlen(buffer) - 1; - if (l >= 0 && buffer[l] == '\n') - buffer[l] = 0; - return buffer; - } - return NULL; -} - -std::string GdFindFile( std::string mp3file, std::string tld ) -{ - std::string fullname = ""; - char *cmd = NULL; - asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() ); - FILE *p = popen( cmd, "r" ); - if (p) - { - char *s; - while( (s = readline(p) ) != NULL) - { - // printf( "Found: %s", s ); - fullname = std::string( tld ) + std::string( s ); - } - pclose(p); - } - - free( cmd ); - - return fullname; -} diff --git a/vdr_setup.h b/vdr_setup.h index b820857..f17aa2a 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -58,7 +58,6 @@ class mgSetup public: mgSetup (void); - std::string getFilename( std::string basename ); }; extern mgSetup the_setup; diff --git a/vdr_sound.c b/vdr_sound.c index 7fc8361..7c4304f 100644 --- a/vdr_sound.c +++ b/vdr_sound.c @@ -343,7 +343,7 @@ cNormalize::cNormalize (void) cNormalize::~cNormalize () { #ifdef USE_FAST_LIMITER - delete table; + delete[] table; #endif } @@ -550,15 +550,14 @@ class cScale inline unsigned long Prng (unsigned long state); inline signed long LinearDither (mad_fixed_t sample, struct dither *dither); public: - void Init (void); + cScale(); void Stats (void); unsigned int ScaleBlock (unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode); }; -void -cScale::Init (void) +cScale::cScale() { #ifdef DEBUG clipped_samples = 0; diff --git a/vdr_stream.c b/vdr_stream.c index 4f79224..872ac4b 100644 --- a/vdr_stream.c +++ b/vdr_stream.c @@ -139,7 +139,7 @@ mgStream::close (void) else { #endif - delete m_buffer; + delete[] m_buffer; m_buffer = 0; #ifdef USE_MMAP } -- cgit v1.2.3 From 1e61edee5d4dcaa185e3212fd30e663c32b7bbf9 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 7 Feb 2005 09:37:48 +0000 Subject: Added documentation about user-editable search orders git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@448 e10066b5-e1e2-0310-b819-94efdf66514b --- README | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README b/README index 0b6fd1a..dd0d00c 100644 --- a/README +++ b/README @@ -37,8 +37,7 @@ parameters are descibed in Section 5. The plugin currently runs on versions 1.3.17- of VDR (including 1.2.6). It also compiles on 1.3.18 but your mileage may vary. In addition, the following pieces of software are required: - - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) - - mySQL client libraries + - mySQL server (tested with 4.0.18) (Debian packages mysql-server, mysql-client) - mySQL client libraries (Debian package libmysqlclient-dev or http://www.mysql.org) - libmad (for mp3 decoding) @@ -308,7 +307,7 @@ removed. - Collections: switch to the collection view -- Select search order: select another search order +- Select an order: select another search order, edit existing ones, or create new ones (see below) - Export tracklist: generate a file X.m3u containing all tracks from the current selection @@ -320,6 +319,20 @@ between the *Music browser* and the *Collection browser*. Thus, if you want to p album, browse to it and press green. Remember that you can redefine commands executed by Red, Green and Yellow by pressing them while displaying the command list. +Muggle comes with a few default browsing orders (like artist / album /track). Since release 0.1.2 +it is possible for the user to change these or create now ones without going into the code. +In the music browser submenu (enter with blue while in the music browser) enter "Select an order". +Existing search orders will be shown. Move the cursor to any of those and press the Red button to edit +it. Each key of the current search order will be shown on a line. Move the cursor to a line and +change the search key using Left/Right buttons. Note, that the number of key depends on what is +currently selected. So keys may appear/vanish as you cycle through the choices. This is intented +and not a bug. Play around with this a while to see, why this is necessary. Press Ok to make your +choices persistent, use back to leave the search order editor without making any changes. + +In addition, you can create new search orders using the Green key and delete orders no longer +needed using Yellow. As an exercise, try to e.g. create orders like "Decade > Genre > Track" or +"Year > Album > Track". + \subsection collections Collection browser The *Collection browser* displays a list of available collections. Browse the list with -- cgit v1.2.3 From ec539d44593f97bc543a2b1df900075d226f8a73 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Mon, 7 Feb 2005 10:31:25 +0000 Subject: Remerge changes regarding end of playlist and better dist archive creation git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@451 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 2 +- vdr_player.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 1dd8773..7b8ba41 100644 --- a/Makefile +++ b/Makefile @@ -111,7 +111,7 @@ dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) - @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @tar czf $(PACKAGE).tgz --exclude=.svn/* -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @echo Distribution package created as $(PACKAGE).tgz diff --git a/vdr_player.c b/vdr_player.c index cea9e3b..292f07c 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -622,8 +622,8 @@ mgPCMPlayer::Action (void) { if (m_ringbuffer->Available () == 0) { - m_active = false; - SetPlayMode (pmStopped); + // m_active = false; + SetPlayMode (pmStopped); } } break; -- cgit v1.2.3 From dc68a2ee19366fc958b9d876845c3aef6a469240 Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 8 Feb 2005 16:40:33 +0000 Subject: UPDATE: swap language/uid git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@460 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mugglei.c b/mugglei.c index 19a0b3d..9cf74b0 100755 --- a/mugglei.c +++ b/mugglei.c @@ -338,7 +338,7 @@ void update_db( long uid, std::string filename ) "samplerate=%d, channels=%d, genre1=\"%s\", lang=\"%s\" WHERE id=%d", artist.toCString(), title.toCString(), year, cddbid.toCString(), filename.c_str(), len, bitrate, - sample, channels, gid.toCString(), uid, language.toCString() ); + sample, channels, gid.toCString(), language.toCString(), uid ); } else { // the entry does not exist, create it -- cgit v1.2.3 From 4e66317bdef5451a50a3bf4bc1b564338bc52788 Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 8 Feb 2005 22:51:17 +0000 Subject: make it compilable with 2.95 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@464 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 2 +- mg_actions.c | 2 ++ mg_db.c | 3 ++- mg_db.h | 4 ---- mg_order.c | 12 ++++++++---- mg_order.h | 4 ---- mg_valmap.h | 2 ++ muggle.h | 3 +++ mugglei.c | 2 ++ vdr_decoder.c | 10 ++++++---- vdr_decoder_flac.c | 15 +++++++++------ vdr_menu.c | 4 ++-- 12 files changed, 37 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 7b8ba41..19b9c80 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ PACKAGE = vdr-$(ARCHIVE) INCLUDES += -I$(VDRDIR) -I$(VDRDIR)/include -I$(DVBDIR)/include \ -I/usr/include/mysql/ -I/usr/include/taglib -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' MIFLAGS += -I/usr/include/taglib -lmysqlclient diff --git a/mg_actions.c b/mg_actions.c index 42337d9..ea35c32 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -9,6 +9,8 @@ * $Id: mg_actions.c 276 2004-12-25 15:52:35Z wr61 $ */ +#include + #include #include #include diff --git a/mg_db.c b/mg_db.c index dfc0843..d6cf8b3 100644 --- a/mg_db.c +++ b/mg_db.c @@ -9,6 +9,7 @@ * */ +#include #include "mg_db.h" #include "vdr_setup.h" #include "mg_tools.h" @@ -403,7 +404,7 @@ string mgSelection::exportM3U () mgContentItem& t = m_tracks[i]; fprintf (listfile, "#EXTINF:%d,%s\n", t.getDuration (), t.getTitle ().c_str ()); - fprintf (listfile, "#MUGGLE:%d\n", t.getId()); + fprintf (listfile, "#MUGGLE:%ld\n", t.getId()); fprintf (listfile, "%s\n", t.getSourceFile (false).c_str ()); } fclose (listfile); diff --git a/mg_db.h b/mg_db.h index 5ef26e2..c0daf7c 100644 --- a/mg_db.h +++ b/mg_db.h @@ -17,10 +17,6 @@ #include #include #include -#include -#include -#include -#include #include using namespace std; diff --git a/mg_order.c b/mg_order.c index 46d4d57..cc93141 100644 --- a/mg_order.c +++ b/mg_order.c @@ -1,6 +1,7 @@ #include "mg_order.h" #include "mg_tools.h" #include "i18n.h" +#include bool iskeyGenre(mgKeyTypes kt) @@ -28,7 +29,7 @@ sql_string (MYSQL *db, const string s) return ""; char *buf = (char *) malloc (s.size () * 2 + 1); mysql_real_escape_string (db, buf, s.c_str (), s.size ()); - string result = "'" + std::string (buf) + "'"; + string result = "'" + string (buf) + "'"; free (buf); return result; } @@ -866,8 +867,11 @@ mgReferences::mgReferences() bool mgReferences::Equal(unsigned int i,string table1, string table2) const { - return (((at(i).t1()==table1) && (at(i).t2()==table2)) - || ((at(i).t1()==table2) && (at(i).t2()==table1))); + const mgReference& r = operator[](i); + string s1 = r.t1(); + string s2 = r.t2(); + return ((s1==table1) && (s2==table2)) + || ((s1==table2) && (s2==table1)); } mgParts @@ -875,7 +879,7 @@ mgReferences::FindConnectionBetween(string table1, string table2) const { for (unsigned int i=0 ; i #include #include -#include -#include #include -#include - #include "mg_valmap.h" using namespace std; diff --git a/mg_valmap.h b/mg_valmap.h index a1da822..ca39139 100644 --- a/mg_valmap.h +++ b/mg_valmap.h @@ -1,5 +1,7 @@ #ifndef _MG_VALMAP_H #define _MG_VALMAP_H + +#include #include #include diff --git a/muggle.h b/muggle.h index 171cdb7..4a05aee 100644 --- a/muggle.h +++ b/muggle.h @@ -32,6 +32,9 @@ #ifndef _MUGGLE_H #define _MUGGLE_H #include +#include +#include +#include #include class mgMainMenu; diff --git a/mugglei.c b/mugglei.c index 9cf74b0..79c68f6 100755 --- a/mugglei.c +++ b/mugglei.c @@ -6,6 +6,8 @@ */ // #define VERBOSE + +#include #include #include diff --git a/vdr_decoder.c b/vdr_decoder.c index 93dc540..2ba1e5b 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -21,6 +21,12 @@ #include #include +#include "mg_db.h" + +#include +#include + + #include "vdr_setup.h" #include "vdr_decoder.h" #include "vdr_decoder_mp3.h" @@ -35,10 +41,6 @@ extern void showmessage(const char *); #include "vdr_decoder_flac.h" #endif -#include "mg_db.h" - -#include -#include // --- mgDecoders --------------------------------------------------------------- diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c index f18ceab..76d15dd 100644 --- a/vdr_decoder_flac.c +++ b/vdr_decoder_flac.c @@ -11,17 +11,20 @@ #ifdef HAVE_FLAC #define DEBUG -#include "vdr_setup.h" -#include "vdr_decoder_flac.h" + +#include +#include +#include #include "mg_tools.h" #include "mg_db.h" +#include "vdr_setup.h" +#include "vdr_decoder_flac.h" + + #include -#include -#include -#include using namespace std; @@ -82,7 +85,7 @@ bool mgFlacDecoder::initialize() m_reservoir[0] = new FLAC__int32[MAX_RES_SIZE]; m_reservoir[1] = new FLAC__int32[MAX_RES_SIZE]; - FLAC::Decoder::File::State d = init(); // TODO: check this + /*FLAC::Decoder::File::State d =*/ init(); // TODO: check this process_until_end_of_metadata(); // basically just skip metadata diff --git a/vdr_menu.c b/vdr_menu.c index e7e107b..5d3d1cd 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -9,6 +9,8 @@ * $Id$ */ +#include + #include #include #include @@ -220,7 +222,6 @@ mgMenu::mgMenu () void mgMainMenu::DumpOrders(mgValmap& nv) { - map::iterator it; for (unsigned int idx=0;idx::iterator it; for (unsigned int idx=0;idx Date: Tue, 8 Feb 2005 23:01:47 +0000 Subject: clear playing collection notifies player thread git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@465 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_actions.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mg_actions.c b/mg_actions.c index ea35c32..13cb2c7 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -950,7 +950,11 @@ void mgClearCollection::Execute() { if (Interface->Confirm(tr("Clear the collection?"))) - selection()->ClearCollection(selection()->getCurrentValue()); + { + string target = selection()->getCurrentValue(); + selection()->ClearCollection(target); + osd()->CollectionChanged(target); + } } //! \brief remove selected items from default collection -- cgit v1.2.3 From 0a66da766a3a28c7c7121a3cc954cbd939241f5a Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 8 Feb 2005 23:02:22 +0000 Subject: correctly init m_db in mgSelection copy constructor git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@466 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mg_db.c b/mg_db.c index d6cf8b3..cba9baf 100644 --- a/mg_db.c +++ b/mg_db.c @@ -841,6 +841,7 @@ void mgSelection::InitFrom(const mgSelection* s) map_values = s->map_values; map_ids = s->map_ids; order = s->order; + order.setDB(m_db); m_level = s->m_level; m_position = s->m_position; m_trackid = s->m_trackid; -- cgit v1.2.3 From 7b0db96df1380ea1eea851a7beda8c4de90a2db2 Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 8 Feb 2005 23:02:47 +0000 Subject: HAVE_FLAC and HAVE_VORBISFILE from Make.config git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@467 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 19b9c80..d2439e5 100644 --- a/Makefile +++ b/Makefile @@ -9,14 +9,10 @@ # PLUGIN = muggle -#if you want ogg / flac support, remove the trailing x in those two -#definitions: -define have_vorbisfile -1 -endef -define have_flac -1 -endef +#if you want ogg / flac support, define HAVE_VORBISFILE and/or HAVE_FLAC +#in $VDRDIR/Make.config like this: +# HAVE_VORBISFILE=1 +# HAVE_FLAC=1 ### The version number of this plugin (taken from the main source file): @@ -66,12 +62,12 @@ OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o LIBS = -lmad -lmysqlclient MILIBS = -lmysqlclient -ltag -ifdef have_vorbisfile +ifdef HAVE_VORBISFILE DEFINES += -DHAVE_VORBISFILE OBJS += vdr_decoder_ogg.o LIBS += -lvorbisfile -lvorbis endif -ifdef have_flac +ifdef HAVE_FLAC DEFINES += -DHAVE_FLAC OBJS += vdr_decoder_flac.o LIBS += -lFLAC++ -- cgit v1.2.3 From 01fb6ca159328f4c6d32098098d50d805975d777 Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 9 Feb 2005 22:39:22 +0000 Subject: fix segfault in mgSelection::enter, fix order in collections git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@469 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 2 ++ mg_order.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mg_db.c b/mg_db.c index cba9baf..aa84ef4 100644 --- a/mg_db.c +++ b/mg_db.c @@ -1012,6 +1012,8 @@ bool mgSelection::enter (unsigned int position) return false; setPosition (position); position = gotoPosition(); // reload adjusted position + if (values.size()==0) + return false; string value = values[position]; string id = m_ids[position]; mgSelStrings prev; diff --git a/mg_order.c b/mg_order.c index cc93141..93463b5 100644 --- a/mg_order.c +++ b/mg_order.c @@ -625,7 +625,7 @@ operator==(const mgOrder& a, const mgOrder &b) const mgOrder& mgOrder::operator=(const mgOrder& from) { - Keys.clear(); + clear(); for (unsigned int i = 0; i < from.size();i++) { mgKey *k = ktGenerate(from.getKeyType(i),m_db); @@ -811,7 +811,8 @@ cleanagain: goto cleanagain; } } - if (!is_unique) + bool IsCollection = size()==0 ? false : Keys[0]->Type()==keyCollection; + if (!IsCollection && !is_unique) { if (!album_found) Keys.push_back(ktGenerate(keyAlbum,m_db)); -- cgit v1.2.3 From a6bcad270fbcd7b8945b764402954966cd451cf7 Mon Sep 17 00:00:00 2001 From: wr61 Date: Wed, 9 Feb 2005 22:46:38 +0000 Subject: mg_readline, and replace one mgWarning by esyslog git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@470 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mg_db.c b/mg_db.c index aa84ef4..4465d0d 100644 --- a/mg_db.c +++ b/mg_db.c @@ -633,8 +633,7 @@ mgContentItem::mgContentItem (const mgContentItem* c) m_channels = c->m_channels; } -#if VDRVERSNUM >= 10318 -static char *readline(FILE *f) +static char *mg_readline(FILE *f) { static char buffer[MAXPARSEBUFFER]; if (fgets(buffer, sizeof(buffer), f) > 0) { @@ -645,7 +644,6 @@ static char *readline(FILE *f) } return 0; } -#endif static const char *FINDCMD = "cd '%s' 2>/dev/null && find -follow -name '%s' -print 2>/dev/null"; @@ -659,7 +657,7 @@ GdFindFile( string tld, string mp3file ) if (p) { char *s; - if( (s = readline(p) ) != 0) + if( (s = mg_readline(p) ) != 0) result = tld + s; pclose(p); } @@ -1007,7 +1005,7 @@ mgSelection::InitDatabase () bool mgSelection::enter (unsigned int position) { if (order.empty()) - mgWarning("mgSelection::enter(%u): order is empty", position); + esyslog("mgSelection::enter(%u): order is empty", position); if (empty()) return false; setPosition (position); -- cgit v1.2.3 From 8c8f0db2c1b58536836d129d542e3646c717192b Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 10 Feb 2005 16:42:54 +0000 Subject: Added database intialization code for embedded mySQL git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@472 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_database.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- mg_database.h | 8 ++++---- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/mg_database.c b/mg_database.c index 1636262..dff7b0f 100644 --- a/mg_database.c +++ b/mg_database.c @@ -14,6 +14,26 @@ static const int MAX_QUERY_BUFLEN = 2048; +static char *db_cmds[] = { + "use GiantDisc;" + "DROP DATABASE IF EXISTS GiantDisc; CREATE DATABASE GiantDisc;", + "grant all privileges on GiantDisc.* to vdr@localhost;", + "drop table if exists album; CREATE TABLE album ( artist varchar(255) default NULL, title varchar(255) default NULL, cddbid varchar(20) NOT NULL default '', coverimg varchar(255) default NULL, covertxt mediumtext, modified date default NULL, genre varchar(10) default NULL, PRIMARY KEY (cddbid), KEY artist (artist(10)), KEY title (title(10)), KEY genre (genre), KEY modified (modified)) TYPE=MyISAM;", + "drop table if exists genre; CREATE TABLE genre (id varchar(10) NOT NULL default '', id3genre smallint(6) default NULL, genre varchar(255) default NULL, freq int(11) default NULL, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists language; CREATE TABLE language (id varchar(4) NOT NULL default '', language varchar(40) default NULL, freq int(11) default NULL, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists musictype; CREATE TABLE musictype (musictype varchar(40) default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists player;CREATE TABLE player ( ipaddr varchar(255) NOT NULL default '', uichannel varchar(255) NOT NULL default '', logtarget int(11) default NULL, cdripper varchar(255) default NULL, mp3encoder varchar(255) default NULL, cdromdev varchar(255) default NULL, cdrwdev varchar(255) default NULL, id int(11) NOT NULL default '0', PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists playerstate;CREATE TABLE playerstate ( playerid int(11) NOT NULL default '0', playertype int(11) NOT NULL default '0', snddevice varchar(255) default NULL, playerapp varchar(255) default NULL, playerparams varchar(255) default NULL, ptlogger varchar(255) default NULL, currtracknb int(11) default NULL, state varchar(4) default NULL, shufflepar varchar(255) default NULL, shufflestat varchar(255) default NULL, pauseframe int(11) default NULL, framesplayed int(11) default NULL, framestotal int(11) default NULL, anchortime bigint(20) default NULL, PRIMARY KEY (playerid,playertype)) TYPE=HEAP;", + "drop table if exists playlist;CREATE TABLE playlist ( title varchar(255) default NULL, author varchar(255) default NULL, note varchar(255) default NULL, created timestamp(8) NOT NULL, id int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists playlistitem;CREATE TABLE playlistitem ( playlist int(11) NOT NULL default '0', tracknumber mediumint(9) NOT NULL default '0', trackid int(11) default NULL, PRIMARY KEY (playlist,tracknumber)) TYPE=MyISAM;", + "drop table if exists playlog;CREATE TABLE playlog ( trackid int(11) default NULL, played date default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists recordingitem;CREATE TABLE recordingitem ( trackid int(11) default NULL, recdate date default NULL, rectime time default NULL, reclength int(11) default NULL, enddate date default NULL, endtime time default NULL, repeat varchar(10) default NULL, initcmd varchar(255) default NULL, parameters varchar(255) default NULL, atqjob int(11) default NULL, id int(11) NOT NULL default '0', PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists source; CREATE TABLE source ( source varchar(40) default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists tracklistitem;CREATE TABLE tracklistitem ( playerid int(11) NOT NULL default '0', listtype smallint(6) NOT NULL default '0', tracknb int(11) NOT NULL default '0', trackid int(11) NOT NULL default '0', PRIMARY KEY (playerid,listtype,tracknb)) TYPE=MyISAM;", + "drop table if exists tracks;CREATE TABLE tracks ( artist varchar(255) default NULL, title varchar(255) default NULL, genre1 varchar(10) default NULL, genre2 varchar(10) default NULL, year smallint(5) unsigned default NULL, lang varchar(4) default NULL, type tinyint(3) unsigned default NULL, rating tinyint(3) unsigned default NULL, length smallint(5) unsigned default NULL, source tinyint(3) unsigned default NULL, sourceid varchar(20) default NULL, tracknb tinyint(3) unsigned default NULL, mp3file varchar(255) default NULL, condition tinyint(3) unsigned default NULL, voladjust smallint(6) default '0', lengthfrm mediumint(9) default '0', startfrm mediumint(9) default '0', bpm smallint(6) default '0', lyrics mediumtext, bitrate varchar(10) default NULL, created date default NULL, modified date default NULL, backup tinyint(3) unsigned default NULL, samplerate int(7) unsigned default NULL, channels tinyint(3) unsigned default NULL, id int(11) NOT NULL auto_increment, PRIMARY KEY (id), KEY title (title(10)), KEY mp3file (mp3file(10)), KEY genre1 (genre1), KEY genre2 (genre2), KEY year (year), KEY lang (lang), KEY type (type), KEY rating (rating), KEY sourceid (sourceid), KEY artist (artist(10))) TYPE=MyISAM;" +}; + + mgDB::mgDB() { } @@ -28,6 +48,36 @@ mgDB::~mgDB() { } +MYSQL_RES * +mgDB::exec_sql(std::string query) +{ + if (query.empty()) + return 0; + mgDebug( 3, "exec_sql(%X,%s)", &m_dbase, query.c_str() ); + if (mysql_query (&m_dbase, (query + ';').c_str ())) + { + mgError("SQL Error in %s: %s",query.c_str(),mysql_error (&m_dbase)); + std::cout << "ERROR in " << query << ":" << mysql_error(&m_dbase) << std::endl; + return 0; + } + return mysql_store_result(&m_dbase); +} + +void mgDB::initialize() +{ + // create database + exec_sql( std::string(db_cmds[1]) ); + exec_sql( std::string(db_cmds[0]) ); + exec_sql( std::string(db_cmds[2]) ); + + // create tables + int len = sizeof( db_cmds ) / sizeof( char* ); + for( int i=3; i < len; i ++ ) + { + exec_sql( std::string( db_cmds[i] ) ); + } +} + MYSQL mgDB::getDBHandle() { @@ -38,7 +88,7 @@ std::string mgDB::escape_string( MYSQL *db, std::string s ) { char *escbuf = (char *) malloc( 2*s.size() + 1 ); - int len = mysql_real_escape_string( db, escbuf, s.c_str(), s.size() ); + mysql_real_escape_string( db, escbuf, s.c_str(), s.size() ); std::string r = std::string( escbuf ); free( escbuf ); diff --git a/mg_database.h b/mg_database.h index d974a33..de159aa 100644 --- a/mg_database.h +++ b/mg_database.h @@ -40,7 +40,7 @@ class mgDB // add constructor for sockets - /*! \brief constructor */ + /*! \brief destructor */ ~mgDB(); /*! @@ -48,10 +48,12 @@ class mgDB */ MYSQL getDBHandle(); + MYSQL_RES *exec_sql (std::string query); + /*! * \brief database initialization */ - // int initialize(); + void initialize(); /*! * \brief helper function to execute read queries @@ -73,8 +75,6 @@ class mgDB * \todo use m_dbase member of this class */ static std::string escape_string( MYSQL *db, std::string s ); - - private: MYSQL m_dbase; -- cgit v1.2.3 From dd1334ae324e6ac56909266ba150cd11aea0d6ea Mon Sep 17 00:00:00 2001 From: LarsAC Date: Thu, 10 Feb 2005 16:44:02 +0000 Subject: Enhanced to cover mySQL embedded library git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@473 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 1 + mugglei.c | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d2439e5..48d42e7 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o LIBS = -lmad -lmysqlclient MILIBS = -lmysqlclient -ltag +# MILIBS = -lmysqld -lpthread -lz -lcrypt -lnsl -lm -lpthread -lrt -lwrap -ltag ifdef HAVE_VORBISFILE DEFINES += -DHAVE_VORBISFILE diff --git a/mugglei.c b/mugglei.c index 79c68f6..a31a601 100755 --- a/mugglei.c +++ b/mugglei.c @@ -34,6 +34,21 @@ MYSQL *db; +static char *server_args[] = +{ + "this_program", /* this string is not used */ + "--datadir=.", + "--key_buffer_size=32M" +}; + +static char *server_groups[] = +{ + "embedded", + "server", + "this_program_SERVER", + (char *)NULL +}; + std::string host, user, pass, dbname, sck; bool import_assorted, delete_mode; @@ -452,8 +467,13 @@ void evaluate_file( std::string filename ) int main( int argc, char *argv[] ) { + if( mysql_server_init(sizeof(server_args) / sizeof(char *), + server_args, server_groups) ) + { + exit(1); + } std::string filename; - + if( argc < 2 ) { // we need at least a filename! std::cout << "mugglei -- import helper for Muggle VDR plugin" << std::endl; @@ -554,6 +574,9 @@ int main( int argc, char *argv[] ) evaluate_file( filename ); } } + + mysql_server_end(); + return 0; } -- cgit v1.2.3 From 4e06beebe60a4bdc3c8c47e826bda74c4a98d023 Mon Sep 17 00:00:00 2001 From: wr61 Date: Fri, 11 Feb 2005 09:40:09 +0000 Subject: fix many memory leaks, add missing copy constructor to mgOrder git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@478 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 9 +++++---- mg_order.c | 33 +++++++++++++++++++++++++-------- mg_order.h | 8 +++++--- muggle.c | 5 +++++ vdr_decoder_flac.c | 1 + vdr_menu.c | 30 +++++++++++++++++------------- vdr_menu.h | 1 + vdr_player.c | 11 +++++++---- 8 files changed, 66 insertions(+), 32 deletions(-) diff --git a/mg_db.c b/mg_db.c index 4465d0d..2a3878c 100644 --- a/mg_db.c +++ b/mg_db.c @@ -648,11 +648,11 @@ static char *mg_readline(FILE *f) static const char *FINDCMD = "cd '%s' 2>/dev/null && find -follow -name '%s' -print 2>/dev/null"; static string -GdFindFile( string tld, string mp3file ) +GdFindFile( const char* tld, string mp3file ) { string result = ""; char *cmd = 0; - asprintf( &cmd, FINDCMD, tld.c_str(), mp3file.c_str() ); + asprintf( &cmd, FINDCMD, tld, mp3file.c_str() ); FILE *p = popen( cmd, "r" ); if (p) { @@ -670,7 +670,7 @@ GdFindFile( string tld, string mp3file ) string mgContentItem::getSourceFile(bool AbsolutePath) const { - const string& tld = the_setup.ToplevelDir; + const char* tld = the_setup.ToplevelDir; string result=""; if (AbsolutePath) result = tld; if (the_setup.GdCompatibility) @@ -906,7 +906,8 @@ void mgSelection::refreshValues () const { assert(this); - if (!m_db) return; + if (!m_db) + return; if (m_current_values.empty()) { mgOrder o1 = order; diff --git a/mg_order.c b/mg_order.c index 93463b5..64cb379 100644 --- a/mg_order.c +++ b/mg_order.c @@ -4,6 +4,8 @@ #include +const char * EMPTY = "XNICHTGESETZTX"; + bool iskeyGenre(mgKeyTypes kt) { return kt>=keyGenre1 && kt <= keyGenres; @@ -596,6 +598,11 @@ mgOrder::mgOrder() setKey (2,keyTrack); } +mgOrder::~mgOrder() +{ + truncate(0); +} + mgKey* mgOrder::Key(unsigned int idx) const { @@ -626,6 +633,18 @@ const mgOrder& mgOrder::operator=(const mgOrder& from) { clear(); + InitFrom(from); + return *this; +} + +mgOrder::mgOrder(const mgOrder &from) +{ + InitFrom(from); +} + +void +mgOrder::InitFrom(const mgOrder &from) +{ for (unsigned int i = 0; i < from.size();i++) { mgKey *k = ktGenerate(from.getKeyType(i),m_db); @@ -633,14 +652,6 @@ mgOrder::operator=(const mgOrder& from) Keys.push_back(k); } if (from.m_db) setDB(from.m_db); - return *this; -} - -mgOrder& -mgOrder::operator+=(mgKey* k) { - k->setdb(m_db); - Keys.push_back(k); - return *this; } string @@ -768,6 +779,12 @@ mgOrder::truncate(unsigned int i) } } +void +mgOrder::clear() +{ + truncate(0); +} + void mgOrder::clean() { diff --git a/mg_order.h b/mg_order.h index 900b498..576f2c4 100644 --- a/mg_order.h +++ b/mg_order.h @@ -16,7 +16,7 @@ typedef list strlist; strlist& operator+=(strlist&a, strlist b); -static const string EMPTY = "XNICHTGESETZTX"; +extern const char * EMPTY; //! \brief adds string n to string s, using string sep to separate them string& addsep (string & s, string sep, string n); @@ -137,17 +137,19 @@ const unsigned int MaxKeys = 20; class mgOrder { public: mgOrder(); + mgOrder(const mgOrder &from); mgOrder(mgValmap& nv, char *prefix); mgOrder(vector kt); + ~mgOrder(); + void InitFrom(const mgOrder &from); void setDB(MYSQL *db); mgParts Parts(unsigned int level,bool orderby=true) const; const mgOrder& operator=(const mgOrder& from); - mgOrder& operator+=(mgKey* k); mgKey*& operator[](unsigned int idx); unsigned int size() const { return Keys.size(); } void truncate(unsigned int i); bool empty() const { return Keys.empty(); } - void clear() { Keys.clear();} + void clear(); void clean(); mgKey* Key(unsigned int idx) const; mgKey* find(const mgKeyTypes kt) ; diff --git a/muggle.c b/muggle.c index 179909e..28b0729 100644 --- a/muggle.c +++ b/muggle.c @@ -64,6 +64,11 @@ mgMuggle::mgMuggle (void) mgMuggle::~mgMuggle () { if (main) main->SaveState(); + free(the_setup.DbHost); + free(the_setup.DbName); + free(the_setup.DbUser); + free(the_setup.DbPass); + free(the_setup.ToplevelDir); } diff --git a/vdr_decoder_flac.c b/vdr_decoder_flac.c index 76d15dd..2861d4e 100644 --- a/vdr_decoder_flac.c +++ b/vdr_decoder_flac.c @@ -106,6 +106,7 @@ bool mgFlacDecoder::clean() delete[] m_reservoir[1]; } delete[] m_reservoir; + m_reservoir = 0; // why false? true? return true; diff --git a/vdr_menu.c b/vdr_menu.c index 5d3d1cd..d37b899 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -269,9 +269,10 @@ mgMainMenu::SaveState() mgMainMenu::mgMainMenu ():cOsdMenu ("",25) { m_Status = new mgStatus(this); - m_message = NULL; - moveselection = NULL; - external_commands = NULL; + m_message = 0; + moveselection = 0; + m_root = 0; + external_commands = 0; queue_playing=false; instant_playing=false; play_collection = tr("play"); @@ -331,14 +332,14 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("",25) UseNormalSelection (); unsigned int posi = selection()->gotoPosition(); LoadExternalCommands(); // before AddMenu() - mgMenu *root = new mgTree; - root->TreeRedAction = mgActions(nmain.getuint("TreeRedAction")); - root->TreeGreenAction = mgActions(nmain.getuint("TreeGreenAction")); - root->TreeYellowAction = mgActions(nmain.getuint("TreeYellowAction")); - root->CollRedAction = mgActions(nmain.getuint("CollRedAction")); - root->CollGreenAction = mgActions(nmain.getuint("CollGreenAction")); - root->CollYellowAction = mgActions(nmain.getuint("CollYellowAction")); - AddMenu (root,posi); + m_root = new mgTree; + m_root->TreeRedAction = mgActions(nmain.getuint("TreeRedAction")); + m_root->TreeGreenAction = mgActions(nmain.getuint("TreeGreenAction")); + m_root->TreeYellowAction = mgActions(nmain.getuint("TreeYellowAction")); + m_root->CollRedAction = mgActions(nmain.getuint("CollRedAction")); + m_root->CollGreenAction = mgActions(nmain.getuint("CollGreenAction")); + m_root->CollYellowAction = mgActions(nmain.getuint("CollYellowAction")); + AddMenu (m_root,posi); //SetCurrent (Get (posi)); @@ -425,8 +426,11 @@ mgMainMenu::LoadExternalCommands() mgMainMenu::~mgMainMenu() { delete m_Status; - if (moveselection) - delete moveselection; + delete moveselection; + delete m_root; + delete external_commands; + for (unsigned int i=0;igetCurrentTrack (); if (newcurr) { - if (m_current) delete m_current; + delete m_current; m_current = new mgContentItem(newcurr); } Play (); @@ -271,7 +272,7 @@ mgPCMPlayer::Activate (bool on) Start (); m_started = true; - if (m_current) delete m_current; + delete m_current; m_current = 0; m_playmode_mutex.Lock (); @@ -602,6 +603,7 @@ mgPCMPlayer::Action (void) if (m_decoder) { // who deletes decoder? m_decoder->stop (); + delete m_decoder; m_decoder = 0; } @@ -704,6 +706,7 @@ mgPCMPlayer::Action (void) if (m_decoder) { // who deletes decoder? m_decoder->stop (); + delete m_decoder; m_decoder = 0; } @@ -768,7 +771,7 @@ bool mgPCMPlayer::SkipFile (int step) { newcurr = m_playlist->getCurrentTrack (); if (newcurr) { - if (m_current) delete m_current; + delete m_current; m_current = new mgContentItem(newcurr); } } @@ -866,7 +869,7 @@ mgPCMPlayer::Goto (int index, bool still) { Lock (); StopPlay (); - if (m_current) delete m_current; + delete m_current; m_current = new mgContentItem(next); Play (); Unlock (); -- cgit v1.2.3 From 8f03ced96dbea76010525ca596faa1d86bd4f56d Mon Sep 17 00:00:00 2001 From: LarsAC Date: Fri, 11 Feb 2005 11:44:10 +0000 Subject: Ignore case for filename extensions git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@479 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_decoder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vdr_decoder.c b/vdr_decoder.c index 2ba1e5b..62e6a6d 100644 --- a/vdr_decoder.c +++ b/vdr_decoder.c @@ -56,13 +56,13 @@ mgMediaType mgDecoders::getMediaType (std::string s) while (p >= f && *p != '.') --p; - if (!strcmp (p, ".mp3")) + if (!strcasecmp (p, ".mp3")) { mt = MT_MP3; } else { - if (!strcmp (p, ".ogg")) + if (!strcasecmp (p, ".ogg")) { #ifdef HAVE_VORBISFILE mt = MT_OGG; @@ -72,7 +72,7 @@ mgMediaType mgDecoders::getMediaType (std::string s) } else { - if (!strcmp (p, ".flac")) + if (!strcasecmp (p, ".flac")) { #ifdef HAVE_FLAC mt = MT_FLAC; -- cgit v1.2.3 From 0497728d48f4f1adf2d84ed46f289669dcd12676 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sat, 12 Feb 2005 10:00:55 +0000 Subject: corrected error in GD compatibility function git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@480 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mg_db.c b/mg_db.c index 2a3878c..99fc27b 100644 --- a/mg_db.c +++ b/mg_db.c @@ -658,7 +658,7 @@ GdFindFile( const char* tld, string mp3file ) { char *s; if( (s = mg_readline(p) ) != 0) - result = tld + s; + result = string(tld) + string(s); pclose(p); } -- cgit v1.2.3 From a5bdba18cb2c8f760c99d012267bd9278ce74e09 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sat, 12 Feb 2005 10:04:58 +0000 Subject: corrected bug in GdFindFile git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@481 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mg_db.c b/mg_db.c index 99fc27b..8249647 100644 --- a/mg_db.c +++ b/mg_db.c @@ -658,7 +658,7 @@ GdFindFile( const char* tld, string mp3file ) { char *s; if( (s = mg_readline(p) ) != 0) - result = string(tld) + string(s); + result = string(s); pclose(p); } -- cgit v1.2.3 From 27d2d81ca6b5a28e56cd73e4d921d7e2f6e6a754 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sat, 12 Feb 2005 13:09:27 +0000 Subject: since player stays silent at EOF, extending playlist did not start playling new tracks git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@483 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index a7fb727..11a432c 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -302,6 +302,11 @@ mgPCMPlayer::ReloadPlaylist() { Lock (); m_playlist->clearCache(); + if (!m_playing) + { + SkipFile(1); + Play (); + } Unlock (); } -- cgit v1.2.3 From cd427cfd84f57481479bb9863e3b2ae4c2112163 Mon Sep 17 00:00:00 2001 From: LarsAC Date: Sun, 13 Feb 2005 23:02:06 +0000 Subject: Further stuff for using mySQL embedded git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@484 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/mugglei.c b/mugglei.c index a31a601..c3e07f5 100755 --- a/mugglei.c +++ b/mugglei.c @@ -49,8 +49,29 @@ static char *server_groups[] = (char *)NULL }; +char *db_cmds[] = +{ + "DROP DATABASE IF EXISTS GiantDisc; CREATE DATABASE GiantDisc;", + "grant all privileges on GiantDisc.* to vdr@localhost;", + "use GiantDisc;", + "drop table if exists album; CREATE TABLE album ( artist varchar(255) default NULL, title varchar(255) default NULL, cddbid varchar(20) NOT NULL default '', coverimg varchar(255) default NULL, covertxt mediumtext, modified date default NULL, genre varchar(10) default NULL, PRIMARY KEY (cddbid), KEY artist (artist(10)), KEY title (title(10)), KEY genre (genre), KEY modified (modified)) TYPE=MyISAM;", + "drop table if exists genre; CREATE TABLE genre (id varchar(10) NOT NULL default '', id3genre smallint(6) default NULL, genre varchar(255) default NULL, freq int(11) default NULL, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists language; CREATE TABLE language (id varchar(4) NOT NULL default '', language varchar(40) default NULL, freq int(11) default NULL, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists musictype; CREATE TABLE musictype (musictype varchar(40) default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists player;CREATE TABLE player ( ipaddr varchar(255) NOT NULL default '', uichannel varchar(255) NOT NULL default '', logtarget int(11) default NULL, cdripper varchar(255) default NULL, mp3encoder varchar(255) default NULL, cdromdev varchar(255) default NULL, cdrwdev varchar(255) default NULL, id int(11) NOT NULL default '0', PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists playerstate;CREATE TABLE playerstate ( playerid int(11) NOT NULL default '0', playertype int(11) NOT NULL default '0', snddevice varchar(255) default NULL, playerapp varchar(255) default NULL, playerparams varchar(255) default NULL, ptlogger varchar(255) default NULL, currtracknb int(11) default NULL, state varchar(4) default NULL, shufflepar varchar(255) default NULL, shufflestat varchar(255) default NULL, pauseframe int(11) default NULL, framesplayed int(11) default NULL, framestotal int(11) default NULL, anchortime bigint(20) default NULL, PRIMARY KEY (playerid,playertype)) TYPE=HEAP;", + "drop table if exists playlist;CREATE TABLE playlist ( title varchar(255) default NULL, author varchar(255) default NULL, note varchar(255) default NULL, created timestamp(8) NOT NULL, id int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists playlistitem;CREATE TABLE playlistitem ( playlist int(11) NOT NULL default '0', tracknumber mediumint(9) NOT NULL default '0', trackid int(11) default NULL, PRIMARY KEY (playlist,tracknumber)) TYPE=MyISAM;", + "drop table if exists playlog;CREATE TABLE playlog ( trackid int(11) default NULL, played date default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists recordingitem;CREATE TABLE recordingitem ( trackid int(11) default NULL, recdate date default NULL, rectime time default NULL, reclength int(11) default NULL, enddate date default NULL, endtime time default NULL, repeat varchar(10) default NULL, initcmd varchar(255) default NULL, parameters varchar(255) default NULL, atqjob int(11) default NULL, id int(11) NOT NULL default '0', PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists source; CREATE TABLE source ( source varchar(40) default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", + "drop table if exists tracklistitem;CREATE TABLE tracklistitem ( playerid int(11) NOT NULL default '0', listtype smallint(6) NOT NULL default '0', tracknb int(11) NOT NULL default '0', trackid int(11) NOT NULL default '0', PRIMARY KEY (playerid,listtype,tracknb)) TYPE=MyISAM;", + "drop table if exists tracks;CREATE TABLE tracks ( artist varchar(255) default NULL, title varchar(255) default NULL, genre1 varchar(10) default NULL, genre2 varchar(10) default NULL, year smallint(5) unsigned default NULL, lang varchar(4) default NULL, type tinyint(3) unsigned default NULL, rating tinyint(3) unsigned default NULL, length smallint(5) unsigned default NULL, source tinyint(3) unsigned default NULL, sourceid varchar(20) default NULL, tracknb tinyint(3) unsigned default NULL, mp3file varchar(255) default NULL, condition tinyint(3) unsigned default NULL, voladjust smallint(6) default '0', lengthfrm mediumint(9) default '0', startfrm mediumint(9) default '0', bpm smallint(6) default '0', lyrics mediumtext, bitrate varchar(10) default NULL, created date default NULL, modified date default NULL, backup tinyint(3) unsigned default NULL, samplerate int(7) unsigned default NULL, channels tinyint(3) unsigned default NULL, id int(11) NOT NULL auto_increment, PRIMARY KEY (id), KEY title (title(10)), KEY mp3file (mp3file(10)), KEY genre1 (genre1), KEY genre2 (genre2), KEY year (year), KEY lang (lang), KEY type (type), KEY rating (rating), KEY sourceid (sourceid), KEY artist (artist(10))) TYPE=MyISAM;" +}; + + std::string host, user, pass, dbname, sck; -bool import_assorted, delete_mode; +bool import_assorted, delete_mode, create_mode; #define MAX_QUERY_BUFLEN 2048 static char querybuf[MAX_QUERY_BUFLEN]; @@ -124,6 +145,32 @@ int init_database() return 0; } +MYSQL_RES * +exec_sql( std::string query ) +{ + if( query.empty() ) + return 0; + mgDebug( 3, "exec_sql(%X,%s)", db, query.c_str() ); + if (mysql_query (db, (query + ';').c_str ())) + { + mgError("SQL Error in %s: %s",query.c_str(),mysql_error (db)); + std::cout << "ERROR in " << query << ":" << mysql_error(db) << std::endl; + return 0; + } + return mysql_store_result(db); +} + +int create_database() +{ + // create database and tables + int len = sizeof( db_cmds ) / sizeof( char* ); + for( int i=0; i < len; i ++ ) + { + exec_sql( std::string( db_cmds[i] ) ); + } + return 0; +} + time_t get_fs_modification_time( std::string filename ) { struct stat *buf = (struct stat*) malloc( sizeof( struct stat ) ); @@ -489,6 +536,7 @@ int main( int argc, char *argv[] ) std::cout << " -f - name of music file to import or update" << std::endl; std::cout << " -a - import track as if it was on an assorted album" << std::endl; std::cout << " -z - scan all database entries and delete entries for files not found" << std::endl; + std::cout << " -c - create a new database entry deleting existing entries" << std::endl; exit( 1 ); } @@ -501,6 +549,7 @@ int main( int argc, char *argv[] ) sck = ""; import_assorted = false; delete_mode = false; + create_mode = false; filename = ""; // parse command line options @@ -549,6 +598,10 @@ int main( int argc, char *argv[] ) { delete_mode = true; } break; + case 'c': + { + create_mode = true; + } break; } } @@ -569,6 +622,10 @@ int main( int argc, char *argv[] ) { update_tags( -1 ); } + else if( create_mode ) + { + create_database(); + } if( filename.size() ) { evaluate_file( filename ); -- cgit v1.2.3 From 52743b40f3185c9c855d2b93d4839a425866394f Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 15 Feb 2005 09:17:26 +0000 Subject: warn once if muggle.state not writable git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@486 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_menu.c | 10 +++++++++- vdr_menu.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/vdr_menu.c b/vdr_menu.c index d37b899..605b675 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -243,8 +243,15 @@ mgMainMenu::SaveState() char *b; asprintf(&b,"%s/muggle.state",cPlugin::ConfigDirectory ("muggle")); FILE *f = fopen(b,"w"); + if (!f) + { + if (!m_save_warned) + mgWarning("Cannot write %s",b); + m_save_warned=true; + free(b); + return; + } free(b); - if (!f) return; mgValmap nmain("MainMenu"); nmain.put("DefaultCollection",default_collection); nmain.put("UsingCollection",UsingCollection); @@ -275,6 +282,7 @@ mgMainMenu::mgMainMenu ():cOsdMenu ("",25) external_commands = 0; queue_playing=false; instant_playing=false; + m_save_warned=false; play_collection = tr("play"); mgValmap nsel("tree"); mgValmap ncol("collection"); diff --git a/vdr_menu.h b/vdr_menu.h index 6cabe29..17df308 100644 --- a/vdr_menu.h +++ b/vdr_menu.h @@ -75,6 +75,7 @@ class mgMainMenu:public cOsdMenu void DumpOrders(mgValmap& nv); void LoadOrders(mgValmap& nv); mgMenu *m_root; + bool m_save_warned; public: void AddOrder(); void DeleteOrder(); -- cgit v1.2.3 From 4e6dd27b393de80b5da48a589d662b41c3e47c89 Mon Sep 17 00:00:00 2001 From: wr61 Date: Tue, 15 Feb 2005 10:08:37 +0000 Subject: respect initial shuffle/loop mode from setup git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@489 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_db.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mg_db.c b/mg_db.c index 8249647..57edc0e 100644 --- a/mg_db.c +++ b/mg_db.c @@ -746,8 +746,8 @@ void mgSelection::InitSelection() { m_position = 0; m_tracks_position = 0; m_trackid = -1; - m_shuffle_mode = SM_NONE; - m_loop_mode = LM_NONE; + m_shuffle_mode = ShuffleMode(the_setup.InitShuffleMode); + m_loop_mode = LoopMode(the_setup.InitLoopMode); clearCache(); values.setOwner(this); if (!needGenre2_set) @@ -819,8 +819,6 @@ mgSelection::InitFrom(mgValmap& nv) setPosition(nv.getstr("Position")); if (m_level>=order.size()-1) setTrackPosition(nv.getlong("TrackPosition")); - setShuffleMode(ShuffleMode(nv.getuint("ShuffleMode"))); - setLoopMode(LoopMode(nv.getuint("LoopMode"))); } @@ -1265,8 +1263,6 @@ mgSelection::inCollection(const string Name) const void mgSelection::DumpState(mgValmap& nv) const { nv.put("FallThrough",m_fall_through); - nv.put("ShuffleMode",int(m_shuffle_mode)); - nv.put("LoopMode",int(m_loop_mode)); nv.put("Directory",m_Directory); nv.put("Level",int(m_level)); for (unsigned int i=0;i Date: Sun, 20 Feb 2005 12:15:09 +0000 Subject: sort by folder, show track counts, use shuttle/loop from setup, separate mg_setup from vdr_setup, better error reporting in mugglei, escape cddbid in mugglei git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@494 e10066b5-e1e2-0310-b819-94efdf66514b --- Makefile | 2 +- i18n.c | 57 ++++++++++++++++++++++++++++++++++++++--- mg_db.c | 77 +++++++++++++++++++++++++++++++------------------------ mg_db.h | 18 ++++++++----- mg_order.c | 56 +++++++++++++++++++++++++++++++++++++++- mg_order.h | 5 ++++ mg_setup.c | 34 +++++++++++++++++++++++++ mg_setup.h | 61 ++++++++++++++++++++++++++++++++++++++++++++ mugglei.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- vdr_menu.c | 28 ++++++++++---------- vdr_setup.c | 17 ------------- vdr_setup.h | 47 +++------------------------------- 12 files changed, 364 insertions(+), 123 deletions(-) create mode 100644 mg_setup.c create mode 100644 mg_setup.h diff --git a/Makefile b/Makefile index 48d42e7..2f5ac30 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,7 @@ MIFLAGS += -I/usr/include/taglib -lmysqlclient OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_order.o mg_db.o mg_actions.o vdr_menu.o mg_tools.o \ vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \ - vdr_setup.o + vdr_setup.o mg_setup.o LIBS = -lmad -lmysqlclient MILIBS = -lmysqlclient -ltag diff --git a/i18n.c b/i18n.c index 6ba1065..c9e1240 100644 --- a/i18n.c +++ b/i18n.c @@ -999,13 +999,64 @@ const tI18nPhrase Phrases[] = "", // TODO }, { - "Genre 1", - "Genre 1", + "Folder1", + "Ordner1", "", // TODO "", // TODO "", // TODO "", // TODO - "Genre 1", // TODO + "Folder1", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Folder2", + "Ordner2", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Folder2", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Folder3", + "Ordner3", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Folder3", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { + "Folder4", + "Ordner4", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Folder4", // TODO "", // TODO "", // TODO "", // TODO diff --git a/mg_db.c b/mg_db.c index 57edc0e..0797465 100644 --- a/mg_db.c +++ b/mg_db.c @@ -740,8 +740,8 @@ mgContentItem::mgContentItem (const mgSelection* sel,const MYSQL_ROW row) }; void mgSelection::InitSelection() { + setDB(0); m_Directory="."; - InitDatabase(); m_level = 0; m_position = 0; m_tracks_position = 0; @@ -750,18 +750,13 @@ void mgSelection::InitSelection() { m_loop_mode = LoopMode(the_setup.InitLoopMode); clearCache(); values.setOwner(this); - if (!needGenre2_set) - { - needGenre2_set=true; - needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) from tracks")>1; - } } mgSelection::mgSelection (const bool fall_through) { - setDB(0); InitSelection (); + Connect(); m_fall_through = fall_through; } @@ -802,8 +797,8 @@ mgSelection::setOrder(mgOrder* o) void mgSelection::InitFrom(mgValmap& nv) { - setDB(0); InitSelection(); + Connect(); m_fall_through = nv.getbool("FallThrough"); m_Directory = nv.getstr("Directory"); while (m_level < nv.getuint("Level")) @@ -830,7 +825,6 @@ mgSelection::~mgSelection () void mgSelection::InitFrom(const mgSelection* s) { - setDB(0); InitSelection(); m_fall_through = s->m_fall_through; m_Directory = s->m_Directory; @@ -844,18 +838,9 @@ void mgSelection::InitFrom(const mgSelection* s) m_tracks_position = s->m_tracks_position; setShuffleMode (s->getShuffleMode ()); setLoopMode (s->getLoopMode ()); + Connect(); } -const mgSelection& mgSelection::operator=(const mgSelection &s) -{ - if (this==&s) { // prevent s = s - return *this; - } - InitFrom(&s); - return *this; -} - - unsigned int mgSelection::ordersize () { @@ -948,7 +933,7 @@ mgSelection::count () const return values.size (); } void -mgSelection::InitDatabase () +mgSelection::Connect () { if (m_db) { @@ -997,6 +982,11 @@ mgSelection::InitDatabase () mysql_close (m_db); setDB(0); } + if (!needGenre2_set && m_db) + { + needGenre2_set=true; + needGenre2=exec_count("SELECT COUNT(DISTINCT genre2) from tracks")>1; + } return; } @@ -1347,7 +1337,10 @@ mgSelection::keycount(mgKeyTypes kt) if (count==-1) { mgKey* k = ktGenerate(kt,m_db); - count = exec_count(k->Parts(true).sql_count()); + if (k->Enabled()) + count = exec_count(k->Parts(true).sql_count()); + else + count = 0; delete k; } return count; @@ -1376,18 +1369,36 @@ mgSelection::choices(mgOrder *o,unsigned int level, unsigned int *current) continue; if (kt==keyDecade && UsedBefore(o,keyYear,level)) continue; - if (kt==keyGenre1 && UsedBefore(o,keyGenres,level)) - continue; - if (kt==keyGenre2 && UsedBefore(o,keyGenres,level)) - continue; - if (kt==keyGenre3 && UsedBefore(o,keyGenres,level)) - continue; - if (kt==keyGenre1 && UsedBefore(o,keyGenre3,level)) - continue; - if (kt==keyGenre2 && UsedBefore(o,keyGenre3,level)) - continue; - if (kt==keyGenre1 && UsedBefore(o,keyGenre2,level)) - continue; + if (kt==keyGenre1) + { + if (UsedBefore(o,keyGenre2,level)) continue; + if (UsedBefore(o,keyGenre3,level)) continue; + if (UsedBefore(o,keyGenres,level)) continue; + } + if (kt==keyGenre2) + { + if (UsedBefore(o,keyGenre3,level)) continue; + if (UsedBefore(o,keyGenres,level)) continue; + } + if (kt==keyGenre3) + { + if (UsedBefore(o,keyGenres,level)) continue; + } + if (kt==keyFolder1) + { + if (UsedBefore(o,keyFolder2,level)) continue; + if (UsedBefore(o,keyFolder3,level)) continue; + if (UsedBefore(o,keyFolder4,level)) continue; + } + if (kt==keyFolder2) + { + if (UsedBefore(o,keyFolder3,level)) continue; + if (UsedBefore(o,keyFolder4,level)) continue; + } + if (kt==keyFolder3) + { + if (UsedBefore(o,keyFolder4,level)) continue; + } if (kt==keyCollection || kt==keyCollectionItem) result.push_back(ktName(kt)); else if (keycount(kt)>1) diff --git a/mg_db.h b/mg_db.h index c0daf7c..28fc5a1 100644 --- a/mg_db.h +++ b/mg_db.h @@ -178,12 +178,6 @@ class mgSelection */ mgSelection (const mgSelection* s); -/*! \brief the assignment operator. Does a deep copy. - * Some of the data base content will only be retrieved by the - * new mgSelection as needed, so some data base - * overhead is involved - */ - const mgSelection& operator=(const mgSelection& s); //! \brief initializes from a map. void InitFrom(mgValmap& nv); @@ -397,6 +391,16 @@ class mgSelection */ string exportM3U (); + /*! import/export tags like + * \par path can be a file or a directory. If directory, + * sync all files within but by default non recursive + * \par recursive recurse into all directories beneath path + * \par assorted see mugglei -h + * \par delete_missing if the file does not exist, delete the + * data base entry. If the file is unreadable, do not delete. + */ + void Sync(string path, bool recursive=false,bool assorted=false,bool delete_missing=false); + /*! \brief go to a position in the current level. If we are at the * most detailled level this also sets the track position since * they are identical. @@ -530,7 +534,7 @@ class mgSelection mgOrder order; bool UsedBefore (mgOrder *o,const mgKeyTypes kt, unsigned int level) const; void InitSelection (); - void InitDatabase (); + void Connect (); /*! \brief returns the SQL command for getting all values. * For the leaf level, all values are returned. For upper * levels, every distinct value is returned only once. diff --git a/mg_order.c b/mg_order.c index 64cb379..479e2f3 100644 --- a/mg_order.c +++ b/mg_order.c @@ -169,9 +169,9 @@ class mgKeyNormal : public mgKey { string IdClause(string what,string::size_type start=0,string::size_type len=string::npos) const; void AddIdClause(mgParts &result,string what) const; string m_id; + string m_field; private: mgKeyTypes m_kt; - string m_field; string m_table; string m_value; }; @@ -182,6 +182,52 @@ class mgKeyTrack : public mgKeyNormal { mgParts Parts(bool orderby=false) const; }; +class mgKeyFolder : public mgKeyNormal { + public: + mgKeyFolder(mgKeyTypes kt,const char *fname) + : mgKeyNormal(kt,"tracks",fname) { m_enabled=-1;}; + bool Enabled(); + private: + int m_enabled; +}; + +class mgKeyFolder1 : public mgKeyFolder { + public: + mgKeyFolder1() : mgKeyFolder(keyFolder1,"folder1") {}; +}; +class mgKeyFolder2 : public mgKeyFolder { + public: + mgKeyFolder2() : mgKeyFolder(keyFolder2,"folder2") {}; +}; +class mgKeyFolder3 : public mgKeyFolder { + public: + mgKeyFolder3() : mgKeyFolder(keyFolder3,"folder3") {}; +}; +class mgKeyFolder4 : public mgKeyFolder { + public: + mgKeyFolder4() : mgKeyFolder(keyFolder4,"folder4") {}; +}; + +bool +mgKeyFolder::Enabled() +{ + if (m_enabled<0) + { + if (!m_db) + return false; + char *b; + asprintf(&b,"DESCRIBE tracks %s",m_field.c_str()); + MYSQL_RES * rows = exec_sql (m_db, b); + free(b); + if (rows) + { + m_enabled = mysql_num_rows(rows); + mysql_free_result (rows); + } + } + return (m_enabled==1); +} + class mgKeyGenres : public mgKeyNormal { public: mgKeyGenres() : mgKeyNormal(keyGenres,"tracks","genre1") {}; @@ -956,6 +1002,10 @@ ktGenerate(const mgKeyTypes kt,MYSQL* db) case keyGenre1: result = new mgKeyGenre1;break; case keyGenre2: result = new mgKeyGenre2;break; case keyGenre3: result = new mgKeyGenre3;break; + case keyFolder1:result = new mgKeyFolder1;break; + case keyFolder2:result = new mgKeyFolder2;break; + case keyFolder3:result = new mgKeyFolder3;break; + case keyFolder4:result = new mgKeyFolder4;break; case keyArtist: result = new mgKeyNormal(kt,"tracks","artist");break; case keyTitle: result = new mgKeyNormal(kt,"tracks","title");break; case keyTrack: result = new mgKeyTrack;break; @@ -981,6 +1031,10 @@ ktName(const mgKeyTypes kt) case keyGenre1: result = "Genre1";break; case keyGenre2: result = "Genre2";break; case keyGenre3: result = "Genre3";break; + case keyFolder1: result = "Folder1";break; + case keyFolder2: result = "Folder2";break; + case keyFolder3: result = "Folder3";break; + case keyFolder4: result = "Folder4";break; case keyArtist: result = "Artist";break; case keyTitle: result = "Title";break; case keyTrack: result = "Track";break; diff --git a/mg_order.h b/mg_order.h index 576f2c4..e2dcbd7 100644 --- a/mg_order.h +++ b/mg_order.h @@ -34,6 +34,10 @@ enum mgKeyTypes { keyTrack, keyLanguage, keyRating, + keyFolder1, + keyFolder2, + keyFolder3, + keyFolder4, keyCollection, keyCollectionItem, }; @@ -84,6 +88,7 @@ class mgKey { virtual string map_valuefield() const { return ""; } virtual string map_valuetable() const { return ""; } void setdb(MYSQL *db); + virtual bool Enabled() { return true; } protected: MYSQL *m_db; }; diff --git a/mg_setup.c b/mg_setup.c new file mode 100644 index 0000000..41a673e --- /dev/null +++ b/mg_setup.c @@ -0,0 +1,34 @@ +/*! + * \file vdr_setup.c + * \brief A setup class for a VDR media plugin (muggle) + * + * \version $Revision: 1.3 $ + * \date $Date: 2005-01-24 15:45:30 +0100 (Mon, 24 Jan 2005) $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: wr61 $ + * + * $Id: vdr_setup.c 399 2005-01-24 14:45:30Z wr61 $ + * + * Partially adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + + +#include "mg_setup.h" + +mgSetup the_setup; + + +mgSetup::mgSetup () +{ + InitLoopMode = 0; + InitShuffleMode = 0; + AudioMode = 1; + DisplayMode = 3; + BackgrMode = 1; + TargetLevel = DEFAULT_TARGET_LEVEL; + LimiterLevel = DEFAULT_LIMITER_LEVEL; + Only48kHz = 0; + ToplevelDir = "/mnt/music/"; +} diff --git a/mg_setup.h b/mg_setup.h new file mode 100644 index 0000000..7bd18ac --- /dev/null +++ b/mg_setup.h @@ -0,0 +1,61 @@ +/*! + * \file vdr_setup.h + * \brief A setup class for a VDR media plugin (muggle) + * + * \version $Revision: 1.2 $ + * \date $Date: 2005-01-24 15:45:30 +0100 (Mon, 24 Jan 2005) $ + * \author Ralf Klueber, Lars von Wedel, Andreas Kellner + * \author Responsible author: $Author: wr61 $ + * + * $Id: vdr_setup.h 399 2005-01-24 14:45:30Z wr61 $ + * + * Adapted from + * MP3/MPlayer plugin to VDR (C++) + * (C) 2001,2002 Stefan Huelswitt + */ + +#ifndef ___SETUP_MG_H +#define ___SETUP_MG_H + +#define MAX_STRING_LEN 128 + +#define DEFAULT_TARGET_LEVEL 25 +#define MAX_TARGET_LEVEL 50 +#define DEFAULT_LIMITER_LEVEL 70 +#define MIN_LIMITER_LEVEL 25 + +/*! + * \brief storage for setup data + */ +class mgSetup +{ + public: + int InitLoopMode; + int InitShuffleMode; + int AudioMode; + int DisplayMode; + int BackgrMode; + int MenuMode; + int TargetLevel; + int LimiterLevel; + int Only48kHz; + + char *DbHost; + char *DbSocket; + char *DbName; + char *DbUser; + char *DbPass; + int DbPort; + bool GdCompatibility; + char *ToplevelDir; + + char PathPrefix[MAX_STRING_LEN]; + + public: + mgSetup (void); + +}; + +extern mgSetup the_setup; + +#endif diff --git a/mugglei.c b/mugglei.c index c3e07f5..40ed785 100755 --- a/mugglei.c +++ b/mugglei.c @@ -69,6 +69,7 @@ char *db_cmds[] = "drop table if exists tracks;CREATE TABLE tracks ( artist varchar(255) default NULL, title varchar(255) default NULL, genre1 varchar(10) default NULL, genre2 varchar(10) default NULL, year smallint(5) unsigned default NULL, lang varchar(4) default NULL, type tinyint(3) unsigned default NULL, rating tinyint(3) unsigned default NULL, length smallint(5) unsigned default NULL, source tinyint(3) unsigned default NULL, sourceid varchar(20) default NULL, tracknb tinyint(3) unsigned default NULL, mp3file varchar(255) default NULL, condition tinyint(3) unsigned default NULL, voladjust smallint(6) default '0', lengthfrm mediumint(9) default '0', startfrm mediumint(9) default '0', bpm smallint(6) default '0', lyrics mediumtext, bitrate varchar(10) default NULL, created date default NULL, modified date default NULL, backup tinyint(3) unsigned default NULL, samplerate int(7) unsigned default NULL, channels tinyint(3) unsigned default NULL, id int(11) NOT NULL auto_increment, PRIMARY KEY (id), KEY title (title(10)), KEY mp3file (mp3file(10)), KEY genre1 (genre1), KEY genre2 (genre2), KEY year (year), KEY lang (lang), KEY type (type), KEY rating (rating), KEY sourceid (sourceid), KEY artist (artist(10))) TYPE=MyISAM;" }; +bool folderfields; std::string host, user, pass, dbname, sck; bool import_assorted, delete_mode, create_mode; @@ -80,6 +81,28 @@ void showmessage(const char *msg) { } +void +init_folderfields() +{ + folderfields=false; + mysql_query(db,"DESCRIBE tracks folder1"); + MYSQL_RES *rows = mysql_store_result(db); + if (rows) + { + folderfields = mysql_num_rows(rows)>0; + mysql_free_result(rows); + if (!folderfields) + { + folderfields = !mysql_query(db, + "alter table tracks add column folder1 varchar(255)," + "add column folder2 varchar(255)," + "add column folder3 varchar(255)," + "add column folder4 varchar(255)"); + + } + } +} + MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) { va_list ap; @@ -88,7 +111,7 @@ MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...) if( mysql_query(db, querybuf) ) { - mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); + mgError( "SQL error in MUGGLE:\n%s: %s\n", querybuf,mysql_error(db) ); } MYSQL_RES *result = mysql_store_result(db); @@ -105,7 +128,7 @@ void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...) if( mysql_query(db, querybuf) ) { - mgError( "SQL error in MUGGLE:\n%s\n", querybuf ); + mgError( "SQL error in MUGGLE:\n%s %s\n", querybuf,mysql_error(db) ); } va_end(ap); @@ -342,7 +365,7 @@ void update_db( long uid, std::string filename ) } else { // use first album found as source id for the track - cddbid = row[0]; + cddbid = escape_string(db,row[0]); } } else @@ -389,7 +412,7 @@ void update_db( long uid, std::string filename ) } else { // use first album found as source id for the track - cddbid = row[0]; + cddbid = escape_string(db,row[0]); } } @@ -406,7 +429,58 @@ void update_db( long uid, std::string filename ) } else { // the entry does not exist, create it - mgSqlWriteQuery( db, + if (folderfields) + { + char *path = strdup(filename.c_str()); + char *folder1=""; + char *folder2=""; + char *folder3=""; + char *folder4=""; + char *p=path; + char *slash; + slash=strchr(p,'/'); + if (slash) + { + folder1=p; + *slash=0; + p=slash+1; + slash=strchr(p,'/'); + if (slash) + { + folder2=p; + *slash=0; + p=slash+1; + slash=strchr(p,'/'); + if (slash) + { + folder3=p; + *slash=0; + p=slash+1; + slash=strchr(p,'/'); + if (slash) + { + folder4=p; + *slash=0; + } + } + } + } + TagLib::String f1 = escape_string( db, folder1 ); + TagLib::String f2 = escape_string( db, folder2 ); + TagLib::String f3 = escape_string( db, folder3 ); + TagLib::String f4 = escape_string( db, folder4 ); + mgSqlWriteQuery( db, + "INSERT INTO tracks " + "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2,lang,folder1,folder2,folder3,folder4) VALUES" + "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\",\"%s\"," + "\"%s\",\"%s\",\"%s\",\"%s\")", + artist.toCString(), title.toCString(), year, cddbid.toCString(), + trackno, filename.c_str(), len, bitrate, sample, channels, gid.toCString(), + language.toCString(),f1.toCString(),f2.toCString(),f3.toCString(),f4.toCString()); + free(path); + } + else + mgSqlWriteQuery( db, "INSERT INTO tracks " "(artist, title, year,sourceid,tracknb,mp3file,length,bitrate,samplerate,channels,genre1,genre2,lang) VALUES" "(\"%s\", \"%s\", %d, \"%s\", %d, \"%s\", %d, \"%d\", %d, %d, \"%s\",\"\",\"%s\")", @@ -618,6 +692,7 @@ int main( int argc, char *argv[] ) if( 0 == init_database() ) { + init_folderfields(); if( delete_mode ) { update_tags( -1 ); diff --git a/vdr_menu.c b/vdr_menu.c index 605b675..d2bb163 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -363,13 +363,15 @@ mgMainMenu::AddOrder() void mgMainMenu::DeleteOrder() { + mgOrder *o = orders[Current()]; + delete o; orders.erase(orders.begin()+Current()); } void mgMainMenu::LoadOrders(mgValmap& nv) { - for (unsigned int idx=0;idx<10;idx++) + for (unsigned int idx=0;idx<1000;idx++) { char b[10]; sprintf(b,"order%u",idx); @@ -377,7 +379,7 @@ mgMainMenu::LoadOrders(mgValmap& nv) if (o->size()==0) { delete o; - continue; + break; } orders.push_back(o); } @@ -386,20 +388,20 @@ mgMainMenu::LoadOrders(mgValmap& nv) m_current_order=0; if (orders.size()>0) return; - nv.put("order1.Keys.0.Type",keyArtist); - nv.put("order1.Keys.1.Type",keyAlbum); - nv.put("order1.Keys.2.Type",keyTrack); + nv.put("order0.Keys.0.Type",keyArtist); + nv.put("order0.Keys.1.Type",keyAlbum); + nv.put("order0.Keys.2.Type",keyTrack); - nv.put("order2.Keys.0.Type",keyAlbum); - nv.put("order2.Keys.1.Type",keyTrack); + nv.put("order1.Keys.0.Type",keyAlbum); + nv.put("order1.Keys.1.Type",keyTrack); - nv.put("order3.Keys.0.Type",keyGenres); - nv.put("order3.Keys.1.Type",keyArtist); - nv.put("order3.Keys.2.Type",keyAlbum); - nv.put("order3.Keys.3.Type",keyTrack); + nv.put("order2.Keys.0.Type",keyGenres); + nv.put("order2.Keys.1.Type",keyArtist); + nv.put("order2.Keys.2.Type",keyAlbum); + nv.put("order2.Keys.3.Type",keyTrack); - nv.put("order4.Keys.0.Type",keyArtist); - nv.put("order4.Keys.1.Type",keyTrack); + nv.put("order3.Keys.0.Type",keyArtist); + nv.put("order3.Keys.1.Type",keyTrack); nv.put("CurrentOrder",0); LoadOrders(nv); diff --git a/vdr_setup.c b/vdr_setup.c index 0334fea..95d7df2 100644 --- a/vdr_setup.c +++ b/vdr_setup.c @@ -22,8 +22,6 @@ #include "vdr_setup.h" #include "i18n.h" -mgSetup the_setup; - // --- mgMenuSetup ----------------------------------------------------------- @@ -75,18 +73,3 @@ mgMenuSetup::Store (void) SetupStore ("Only48kHz", the_setup.Only48kHz); } - -// --- mgSetup --------------------------------------------------------------- - -mgSetup::mgSetup () -{ - InitLoopMode = 0; - InitShuffleMode = 0; - AudioMode = 1; - DisplayMode = 3; - BackgrMode = 1; - TargetLevel = DEFAULT_TARGET_LEVEL; - LimiterLevel = DEFAULT_LIMITER_LEVEL; - Only48kHz = 0; - ToplevelDir = "/mnt/music/"; -} diff --git a/vdr_setup.h b/vdr_setup.h index f17aa2a..354534f 100644 --- a/vdr_setup.h +++ b/vdr_setup.h @@ -14,53 +14,14 @@ * (C) 2001,2002 Stefan Huelswitt */ -#ifndef ___SETUP_MG_H -#define ___SETUP_MG_H +#ifndef ___VDR_SETUP_MG_H +#define ___VDR_SETUP_MG_H // #include #include #include -#define MAX_STRING_LEN 128 - -#define DEFAULT_TARGET_LEVEL 25 -#define MAX_TARGET_LEVEL 50 -#define DEFAULT_LIMITER_LEVEL 70 -#define MIN_LIMITER_LEVEL 25 - -/*! - * \brief storage for setup data - */ -class mgSetup -{ - public: - int InitLoopMode; - int InitShuffleMode; - int AudioMode; - int DisplayMode; - int BackgrMode; - int MenuMode; - int TargetLevel; - int LimiterLevel; - int Only48kHz; - - char *DbHost; - char *DbSocket; - char *DbName; - char *DbUser; - char *DbPass; - int DbPort; - bool GdCompatibility; - char *ToplevelDir; - - char PathPrefix[MAX_STRING_LEN]; - - public: - mgSetup (void); - -}; - -extern mgSetup the_setup; +#include "mg_setup.h" /*! * \brief allow user to modify setup on OSD @@ -74,4 +35,4 @@ class mgMenuSetup:public cMenuSetupPage public: mgMenuSetup (); }; -#endif //___SETUP_MP3_H +#endif -- cgit v1.2.3 From 1f5bb22fa45cff069f7e0071f7a910daaa7d303a Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 20 Feb 2005 12:20:48 +0000 Subject: more of the same git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@495 e10066b5-e1e2-0310-b819-94efdf66514b --- mg_actions.c | 20 +++++++++++++++----- mg_db.c | 44 +++++++++++++++++++++++++++++++++++++------- mg_db.h | 12 ++++++------ mg_order.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- mg_order.h | 4 ++++ vdr_menu.c | 4 +--- 6 files changed, 110 insertions(+), 25 deletions(-) diff --git a/mg_actions.c b/mg_actions.c index 13cb2c7..1f80325 100644 --- a/mg_actions.c +++ b/mg_actions.c @@ -138,6 +138,8 @@ mgDoCollEntry::getTarget() result.erase(0,5); else result.erase(0,3); + string::size_type lparen = result.find(" ["); + result.erase(lparen,string::npos); return result; } @@ -159,7 +161,8 @@ void mgRemoveCollEntry::Execute() { string target = getTarget(); - osd()->Message1 ("Removed %s entries",itos (osd()->moveselection->RemoveFromCollection (target))); + int removed = osd()->moveselection->RemoveFromCollection (target); + osd()->Message1 ("Removed %s entries",ltos(removed)); osd()->CollectionChanged(target); } @@ -306,21 +309,28 @@ mgEntry::Notify() mgAction::Notify(); // only after selection is updated } + const char * mgEntry::MenuName(const unsigned int idx,const string value) { char *result; + char ct[20]; + ct[0]=0; + unsigned int selcount = selection()->valcount(value); + if (selection()->level()ordersize()-1 || selcount>1) + sprintf(ct," [%u]",selcount); + // when changing this, also change mgDoCollEntry::getTarget() if (selection()->isCollectionlist()) { if (value == osd()->default_collection) - asprintf(&result,"-> %s",value.c_str()); + asprintf(&result,"-> %s%s",value.c_str(),ct); else - asprintf(&result," %s",value.c_str()); + asprintf(&result," %s%s",value.c_str(),ct); } else if (selection()->inCollection()) - asprintf(&result,"%4d %s",idx,value.c_str()); + asprintf(&result,"%4d %s%s",idx,value.c_str(),ct); else - result = strdup(value.c_str()); + asprintf(&result,"%s%s",value.c_str(),ct); return result; } diff --git a/mg_db.c b/mg_db.c index 0797465..7971b7a 100644 --- a/mg_db.c +++ b/mg_db.c @@ -249,8 +249,22 @@ int mgContentItem::getChannels () const mgSelection::ShuffleMode mgSelection::toggleShuffleMode () { - m_shuffle_mode = (m_shuffle_mode == SM_PARTY) ? SM_NONE : ShuffleMode (m_shuffle_mode + 1); + setShuffleMode((m_shuffle_mode == SM_PARTY) ? SM_NONE : ShuffleMode (m_shuffle_mode + 1)); + Shuffle(); + return getShuffleMode(); +} + +void +mgSelection::setShuffleMode (mgSelection::ShuffleMode mode) +{ + m_shuffle_mode = mode; +} + +void +mgSelection::Shuffle() const +{ unsigned int tracksize = getNumTracks(); + if (tracksize==0) return; switch (m_shuffle_mode) { case SM_NONE: @@ -299,7 +313,6 @@ mgSelection::ShuffleMode mgSelection::toggleShuffleMode () - add the file to the end of the list */ } - return m_shuffle_mode; } @@ -430,7 +443,7 @@ mgSelection::setPosition (unsigned int position) } void -mgSelection::setTrackPosition (unsigned int position) +mgSelection::setTrackPosition (unsigned int position) const { m_tracks_position = position; } @@ -605,6 +618,8 @@ mgSelection::tracks () const m_tracks.push_back (mgContentItem (this,row)); mysql_free_result (rows); } + if (m_shuffle_mode!=SM_NONE) + Shuffle(); return m_tracks; } @@ -746,8 +761,14 @@ void mgSelection::InitSelection() { m_position = 0; m_tracks_position = 0; m_trackid = -1; - m_shuffle_mode = ShuffleMode(the_setup.InitShuffleMode); - m_loop_mode = LoopMode(the_setup.InitLoopMode); + if (the_setup.InitShuffleMode) + m_shuffle_mode = SM_NORMAL; + else + m_shuffle_mode = SM_NONE; + if (the_setup.InitLoopMode) + m_loop_mode = LM_FULL; + else + m_loop_mode = LM_NONE; clearCache(); values.setOwner(this); } @@ -836,9 +857,9 @@ void mgSelection::InitFrom(const mgSelection* s) m_position = s->m_position; m_trackid = s->m_trackid; m_tracks_position = s->m_tracks_position; + Connect(); setShuffleMode (s->getShuffleMode ()); setLoopMode (s->getLoopMode ()); - Connect(); } unsigned int @@ -866,6 +887,12 @@ mgSelection::valindex (const string val,const bool second_try) const return valindex(val,true); } +unsigned int +mgSelection::valcount (string value) +{ + assert(m_counts.size()==values.size()); + return m_counts[valindex(value)]; +} unsigned int mgSelection::idindex (const string id,const bool second_try) const @@ -898,6 +925,7 @@ mgSelection::refreshValues () const m_current_values = p.sql_select(); values.strings.clear (); m_ids.clear (); + m_counts.clear(); MYSQL_RES *rows = exec_sql (m_current_values); if (rows) { @@ -909,17 +937,19 @@ mgSelection::refreshValues () const string r0 = row[0]; if (r0=="NULL") // there is a genre NULL! continue; - if (num_fields==2) + if (num_fields==3) { if (!row[1]) continue; string r1 = row[1]; values.strings.push_back (r0); m_ids.push_back (r1); + m_counts.push_back(atol(row[2])); } else { values.strings.push_back (value(order.Key(m_level),r0)); m_ids.push_back (r0); + m_counts.push_back(atol(row[1])); } } mysql_free_result (rows); diff --git a/mg_db.h b/mg_db.h index 28fc5a1..fd61422 100644 --- a/mg_db.h +++ b/mg_db.h @@ -345,10 +345,7 @@ class mgSelection } //! \brief sets the current shuffle mode - void setShuffleMode (const ShuffleMode shuffle_mode) - { - m_shuffle_mode = shuffle_mode; - } + void setShuffleMode (const ShuffleMode shuffle_mode); //! \brief returns the current loop mode LoopMode getLoopMode () const @@ -423,7 +420,7 @@ class mgSelection * last existing position * \return only if no position exists, false will be returned */ - void setTrackPosition (unsigned int position); + void setTrackPosition (unsigned int position) const; /*! \brief skip some tracks in the track list * \return false if new position does not exist @@ -459,7 +456,7 @@ class mgSelection * instead, only count the tracks. If the size differs from * m_tracks.size(), invalidate m_tracks */ - unsigned int getNumTracks () + unsigned int getNumTracks () const { return tracks ().size (); } @@ -510,6 +507,7 @@ class mgSelection string id(mgKey* k) const; unsigned int keycount(mgKeyTypes kt); vector choices(mgOrder *o,unsigned int level, unsigned int *current); + unsigned int valcount (string val); private: mutable map > map_values; @@ -519,12 +517,14 @@ class mgSelection //! \brief be careful when accessing this, see mgSelection::tracks() mutable vector < mgContentItem > m_tracks; mutable strvector m_ids; + mutable vector < unsigned int > m_counts; //! \brief initializes maps for id/value mapping in both direction bool loadvalues (mgKeyTypes kt) const; bool m_fall_through; unsigned int m_position; mutable unsigned int m_tracks_position; ShuffleMode m_shuffle_mode; + void Shuffle() const; LoopMode m_loop_mode; MYSQL *m_db; void setDB(MYSQL *db); diff --git a/mg_order.c b/mg_order.c index 479e2f3..ced9df6 100644 --- a/mg_order.c +++ b/mg_order.c @@ -176,12 +176,31 @@ class mgKeyNormal : public mgKey { string m_value; }; +class mgKeyDate : public mgKeyNormal { + public: + mgKeyDate(mgKeyTypes kt,string table, string field) : mgKeyNormal(kt,table,field) {} +}; + class mgKeyTrack : public mgKeyNormal { public: mgKeyTrack() : mgKeyNormal(keyTrack,"tracks","tracknb") {}; mgParts Parts(bool orderby=false) const; }; +class mgKeyAlbum : public mgKeyNormal { + public: + mgKeyAlbum() : mgKeyNormal(keyAlbum,"album","title") {}; + mgParts Parts(bool orderby=false) const; +}; + +mgParts +mgKeyAlbum::Parts(bool orderby) const +{ + mgParts result = mgKeyNormal::Parts(orderby); + result.tables.push_back("tracks"); + return result; +} + class mgKeyFolder : public mgKeyNormal { public: mgKeyFolder(mgKeyTypes kt,const char *fname) @@ -402,6 +421,7 @@ mgKeyNormal::set(string value, string id) mgParts::mgParts() { + m_sql_select=""; } mgParts::~mgParts() @@ -557,16 +577,24 @@ mgParts::Prepare() string mgParts::sql_select(bool distinct) { + if (!m_sql_select.empty()) + return m_sql_select; Prepare(); - string result = ""; + string result; if (distinct) - result += sql_list("SELECT DISTINCT",fields); + { + fields.push_back("COUNT(*)"); + result = sql_list("SELECT",fields); + fields.pop_back(); + } else - result += sql_list("SELECT",fields); + result = sql_list("SELECT",fields); if (result.empty()) return result; result += sql_list("FROM",tables); result += sql_list("WHERE",clauses," AND "); + if (distinct) + result += sql_list("GROUP BY",fields); result += sql_list("ORDER BY",orders); optimize(result); return result; @@ -890,6 +918,17 @@ mgOrder::Parts(unsigned int level,bool orderby) const { assert(strlen(m_db->host)); mgParts result; + mgKeyNormal *k0 = dynamic_cast(Keys[0]); + mgKeyTypes kt0 = k0->Type(); + if (level==0 && kt0==keyCollection) + { + // sql command contributed by jarny + result.m_sql_select = string("select playlist.title,playlist.id, " + "count(*) * (playlistitem.playlist is not null) from playlist " + "left join playlistitem on playlist.id = playlistitem.playlist " + "group by playlist.title"); + return result; + } for (unsigned int i=0;i<=level;i++) { if (i==Keys.size()) break; @@ -1010,7 +1049,9 @@ ktGenerate(const mgKeyTypes kt,MYSQL* db) case keyTitle: result = new mgKeyNormal(kt,"tracks","title");break; case keyTrack: result = new mgKeyTrack;break; case keyDecade: result = new mgKeyDecade;break; - case keyAlbum: result = new mgKeyNormal(kt,"album","title");break; + case keyAlbum: result = new mgKeyAlbum;break; + case keyCreated: result = new mgKeyDate(kt,"tracks","created");break; + case keyModified: result = new mgKeyDate(kt,"tracks","modified");break; case keyCollection: result = new mgKeyCollection;break; case keyCollectionItem: result = new mgKeyCollectionItem;break; case keyLanguage: result = new mgKeyLanguage;break; @@ -1040,6 +1081,8 @@ ktName(const mgKeyTypes kt) case keyTrack: result = "Track";break; case keyDecade: result = "Decade";break; case keyAlbum: result = "Album";break; + case keyCreated: result = "Created";break; + case keyModified: result = "Modified";break; case keyCollection: result = "Collection";break; case keyCollectionItem: result = "Collection item";break; case keyLanguage: result = "Language";break; diff --git a/mg_order.h b/mg_order.h index e2dcbd7..8b2fe38 100644 --- a/mg_order.h +++ b/mg_order.h @@ -38,6 +38,8 @@ enum mgKeyTypes { keyFolder2, keyFolder3, keyFolder4, + keyCreated, + keyModified, keyCollection, keyCollectionItem, }; @@ -110,6 +112,7 @@ public: strlist fields; strlist tables; strlist clauses; + strlist groupby; strlist orders; mgParts& operator+=(mgParts a); void Prepare(); @@ -118,6 +121,7 @@ public: string sql_delete_from_collection(string pid); string sql_update(strlist new_values); bool empty() const { return tables.size()==0;} + string m_sql_select; private: bool UsesTracks(); mgReferences ref; diff --git a/vdr_menu.c b/vdr_menu.c index d2bb163..50eef84 100644 --- a/vdr_menu.c +++ b/vdr_menu.c @@ -120,10 +120,8 @@ mgMainMenu::CollectionChanged(string name) else PlayQueue(); } - if (CollectionEntered(name)) - { + if (CollectionEntered(name) || selection()->isCollectionlist()) selection()->clearCache(); - } } bool -- cgit v1.2.3 From b07385bcd8e1622714be8c17923190eed101303e Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 20 Feb 2005 12:25:05 +0000 Subject: use all and correct ISO 639-2/B (bibliographic) language codes as used in id3v2 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@496 e10066b5-e1e2-0310-b819-94efdf66514b --- scripts/COPYRIGHT | 16 ++ scripts/languages.txt | 510 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 481 insertions(+), 45 deletions(-) create mode 100644 scripts/COPYRIGHT diff --git a/scripts/COPYRIGHT b/scripts/COPYRIGHT new file mode 100644 index 0000000..9b0e8cb --- /dev/null +++ b/scripts/COPYRIGHT @@ -0,0 +1,16 @@ +# the content of languages.txt is taken from the file +# iso_639.tab which contains this copyright: + + +## iso-639.tab +## +## Copyright (C) 2004 Alastair McKinstry +## Released under the GNU License; see file COPYING for details +## +## Last update: 2004-03-29 +## +## This file gives a list of all languages in the ISO-639 +## standard, and is used to provide translations (via gettext) +## +## Status: ISO 639-2:1998 + additions and changes until 2003-03-05 +## Source: http://lcweb.loc.gov/standards/iso639-2/englangn.html diff --git a/scripts/languages.txt b/scripts/languages.txt index a24d25b..d623f6c 100755 --- a/scripts/languages.txt +++ b/scripts/languages.txt @@ -1,46 +1,466 @@ -- Instrumental -CHde Swiss German -af Afrikaans -ar Arabic -bg Bulgarian -bn Bengali; Bangla -bo Tibetan -cs Czech -da Danish -de German -el Greek -en English -eo Esperanto -es Spanish -fi Finnish +aar Afar +abk Abkhazian +ace Achinese +ach Acoli +ada Adangme +ady Adyghe; Adygei +afa Afro-Asiatic (Other) +afh Afrihili +afr Afrikaans +aka Akan +akk Akkadian +alb Albanian +ale Aleut +alg Algonquian languages +amh Amharic +ang English, Old (ca.450-1100) +apa Apache languages +ara Arabic +arc Aramaic +arg Aragonese +arm Armenian +arn Araucanian +arp Arapaho +art Artificial (Other) +arw Arawak +asm Assamese +ast Asturian; Bable +ath Athapascan language +aus Australian languages +ava Avaric +ave Avestan +awa Awadhi +aym Aymara +aze Azerbaijani +bad Banda +bai Bamileke languages +bak Bashkir +bal Baluchi +bam Bambara +ban Balinese +baq Basque +bas Basa +bat Baltic (Other) +bej Beja +bel Belarusian +bem Bemba +ben Bengali +ber Berber (Other) +bho Bhojpuri +bih Bihari +bik Bikol +bin Bini +bis Bislama +bla Siksika +bnt Bantu (Other) +bos Bosnian +bra Braj +bre Breton +btk Batak (Indonesia) +bua Buriat +bug Buginese +bul Bulgarian +bur Burmese +byn Blin; Bilin +cad Caddo +cai Central American Indian (Other) +car Carib +cat Catalan +cau Caucasian (Other) +ceb Cebuano +cel Celtic (Other) +cha Chamorro +chb Chibcha +che Chechen +chg Chagatai +chi Chinese +chk Chukese +chm Mari +chn Chinook jargon +cho Choctaw +chp Chipewyan +chr Cherokee +chu Church Slavic +chv Chuvash +chy Cheyenne +cmc Chamic languages +cop Coptic +cor Cornish +cos Corsican +cpe English-based (Other) +cpf French-based (Other) +cpp Portuguese-based (Other) +cre Cree +crh Crimean Turkish; Crimean Tatar +crp Creoles and pidgins (Other) +csb Kashubian +cus Cushitic (Other) +cus Portuguese-based (Other) +cze Czech +dak Dakota +dan Danish +dar Dargwa +dsb Lower Sorbian +del Delaware +den Slave (Athapascan) +dgr Dogrib +din Dinka +div Divehi +doi Dogri +dra Dravidian (Other) +dua Duala +dum Dutch, Middle (ca. 1050-1350) +dut Dutch +dyu Dyula +dzo Dzongkha +efi Efik +egy Egyptian (Ancient) +eka Ekajuk +elx Elamite +eng English +enm English, Middle (1100-1500) +epo Esperanto +est Estonian +ewe Ewe +ewo Ewondo +fan Fang +fao Faroese +fat Fanti +fij Fijian +fin Finnish +fiu Finno-Ugrian (Other) fon Fon -fr French -hi Hindi -hu Hungarian -is Icelandic -it Italian -iw Hebrew -ja Japanese -ku Kurdish -la Latin -lt Lithuanian -lv Latvian, Lettish -nl Dutch -no Norwegian -pl Polish -pt Portuguese -rm Rhaeto-Romance -ro Romanian -ru Russian -sh Serbo-Croatian -sk Slovak -sl Slovenian -sq Albanian -sr Serbian -sv Swedish -ta Tamil -th Thai -tr Turkish -vi Vietnamese -zh Chinese -zu African +fre French +frm French, Middle (ca.1400-1600) +fro French, Old (842-ca.1400) +fry Frisian +ful Fulah +fur Friulian +gaa Ga +gay Gayo +gba Gbaya +gem Germanic (Other) +geo Georgian +ger German +gez Geez +gil Gilbertese +gla Gaelic; Scottish +gle Irish +glg Gallegan +glv Manx +gmh German, Middle High (ca.1050-1500) +goh German, Old High (ca.750-1050) +gon Gondi +gor Gorontalo +got Gothic +grb Grebo +grc Greek, Ancient (to 1453) +gre Greek, Modern (1453-) +grn Guarani +guj Gujarati +gwi Gwichin +hai Haida +hat Haitian; Haitian Creole +hau Hausa +haw Hawaiian +heb Hebrew +her Herero +hil Hiligaynon +him Himachali +hin Hindi +hit Hittite +hmn Hmong +hmo Hiri +hsb Upper Sorbian +hun Hungarian +hup Hupa +iba Iban +ibo Igbo +ice Icelandic +ido Ido +iii Sichuan Yi +ijo Ijo +iku Inuktitut +ile Interlingue +ilo Iloko +ina Interlingua +inc Indic (Other) +ind Indonesian +ine Indo-European (Other) +inh Ingush +ipk Inupiaq +ira Iranian (Other) +iro Iroquoian languages +ita Italian +jav Javanese +jbo Lojban +jpn Japanese +jpr Judeo-Persian +jrb Judeo-Arabic +kaa Kara-Kalpak +kab Kabyle +kac Kachin +kal Greenlandic (Kalaallisut) +kam Kamba +kan Kannada +kar Karen +kas Kashmiri +kau Kanuri +kaw Kawi +kaz Kazakh +kbd Kabardian +kha Khazi +khi Khoisan (Other) +khm Khmer +kho Khotanese +kik Kikuyu +kin Kinyarwanda +kir Kirghiz +kmb Kimbundu +kok Konkani +kom Komi +kon Kongo +kor Korean +kos Kosraean +kpe Kpelle +krc Karachay-Balkar +kro Kru +kru Kurukh +kua Kuanyama +kum Kumyk +kur Kurdish +kut Kutenai +lad Ladino +lah Lahnda +lam Lamba +lao Lao +lat Latin +lav Latvian +lez Lezghian +lim Limburgian +lin Lingala +lit Lithuanian +lol Mongo +loz Lozi +ltz Luxembourgish +lua Luba-Lulua +lub Luba-Katanga +lug Ganda +lui Luiseno +lun Lunda +luo Luo (Kenya and Tanzania) +lus Lushai +mac Macedonian +mad Madurese +mag Magahi +mah Marshallese +mai Maithili +mak Makasar +mal Malayalam +man Mandingo +mao Maori +map Austronesian (Other) +mar Marathi +mas Masai +mal Malay +mdf Moksha +mdr Mandar +men Mende +mga Irish, Middle (900-1200) +mic Micmac +min Minangkabau +mis Miscellaneous languages +mkh Mon-Khmer (Other) +mlg Malagasy +mlt Maltese +mnc Manchu +mno Manobo languages +moh Mohawk +mol Moldavian +mon Mongolian +mos Mossi +mul Multiple languages +mun Munda languages +mus Creek +mwr Marwari +myn Mayan languages +myv Erzya +nah Nahuatl +nai North American Indian (Other) +nap Neapolitan +nau Nauru +nav Navaho +nbl Ndebele, South +nde Ndebele, North +ndo Ndonga +nds German, Low +nep Nepali +new Newari +nia Nias +nic Niger-Kordofanian (Other) +niu Niuean +nno Norwegian Nynorsk +nob Bøkmal, Norwegian +nog Nogai +non Norse, Old +nor Norwegian +nso Sotho, Northern +nub Nubian languages +nwc Classical Newari; Old Newari +nya Chewa; Chichewa; Nyanja +nym Nyankole +nyo Nyoro +nzi Nzima +oci Occitan (post 1500) +oji Ojibwa +ori Oriya +orm Oromo +osa Osage +oss Ossetian +ota Turkish, Ottoman (1500-1928) +oto Otomian languages +paa Papuan (Other) +pag Pangasinan +pal Pahlavi +pam Pampanga +pan Punjabi +pap Papiamento +pau Palauan +peo Persian, Old (ca.600-400 B.C.) +per Persian +phi Philippine (Other) +phn Phoenician +pli Pali +pol Polish +por Portuguese +pon Pohnpeian +pra Prakrit languages +pro Proveçal, Old (to 1500) +pus Pushto +que Quechua +raj Rajasthani +rap Rapanui +rar Rarotongan +roa Romance (Other) +roh Raeto-Romance +rom Romany +rum Romanian +run Rundi +rus Russian +sad Sandawe +sag Sango +sah Yakut +sai South American Indian (Other) +sal Salishan languages +sam Samaritan Aramaic +san Sanskrit +sas Sasak +sat Santali +scc Serbian +sco Scots +scr Croatian +sel Selkup +sem Semitic (Other) +sga Irish, Old (to 900) +sgn Sign languages +shn Shan +sid Sidamo +sin Sinhalese +sio Siouan languages +sit Sino-Tibetan (Other) +sla Slavic (Other) +slo Slovak +slv Slovenian +sma Southern Sami +sme Northern Sami +smi Sami languages (Other) +smj Lule Sami +smn Inari Sami +smo Samoan +sms Skolt Sami +sna Shona +snd Sindhi +snk Soninke +sog Sogdian +som Somali +son Songhai +sot Sotho, Southern +spa Spanish (Castilian) +srd Sardinian +srr Serer +ssa Nilo-Saharan (Other) +ssw Swati +suk Sukuma +sun Sundanese +sus Susu +sux Sumerian +swa Swahili +swe Swedish +syr Syriac +tah Tahitian +tai Tai (Other) +tam Tamil +tso Tsonga +tat Tatar +tel Telugu +tem Timne +ter Tereno +tet Tetum +tgk Tajik +tgl Tagalog +tha Thai +tib Tibetan +tig Tigre +tir Tigrinya +tiv Tiv +tkl Tokelau +tlh Klingon; tlhIngan-Hol +tli Tlinglit +tmh Tamashek +tog Tonga (Nyasa) +ton Tonga (Tonga Islands) +tpi Tok Pisin +tsi Tsimshian +tsn Tswana +tuk Turkmen +tum Tumbuka +tup Tupi languages +tur Turkish +tut Altaic (Other) +tvl Tuvalu +twi Twi +tyv Tuvinian +udm Udmurt +uga Ugaritic +uig Uighur +ukr Ukrainian +umb Umbundu +und Undetermined +urd Urdu +uzb Uzbek +vai Vai +ven Venda +vie Vietnamese +vol Volapuk +vot Votic +wak Wakashan languages +wal Walamo +war Waray +was Washo +wel Welsh +wen Sorbian languages +wln Walloon +wol Wolof +xal Kalmyk +xho Xhosa +yao Yao +yap Yapese +yid Yiddish +yor Yoruba +ypk Yupik languages +zap Zapotec +zen Zenaga +zha Chuang; Zhuang +znd Zande +zul Zulu +zun Zuni -- cgit v1.2.3 From 3125b4c69099d3b035573860ce6a64563052e999 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 20 Feb 2005 12:27:06 +0000 Subject: avoid infinite loop git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@497 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index 11a432c..38dfec5 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -431,7 +431,14 @@ mgPCMPlayer::Action (void) break; } else + { mgWarning("found no decoder for %s",filename.c_str()); + m_state=msStop; // if loop mode is on and no decoder + // for any track is found, we would + // otherwise get into an endless loop + // not stoppable with the remote. + break; + } } m_state = msEof; } -- cgit v1.2.3 From fc37b9a84e47c0f158547836eb74031f5813715f Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 20 Feb 2005 12:32:50 +0000 Subject: remove order names git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@499 e10066b5-e1e2-0310-b819-94efdf66514b --- i18n.c | 136 ----------------------------------------------------------------- 1 file changed, 136 deletions(-) diff --git a/i18n.c b/i18n.c index c9e1240..4e8dc76 100644 --- a/i18n.c +++ b/i18n.c @@ -267,91 +267,6 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO }, - { - "Album -> Track", - "Titel nach Album", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Titre après albume", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, - { - "Genre -> Year -> Track", - "Titel nach Genre und Jahr", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Genre -> Année -> Titre", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, - { - "Artist -> Track", - "Titel nach Interpret", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "interprète -> titre", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, - { - "Genre -> Artist -> Album -> Track", - "Album nach Genre und Interpret", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Genre -> interprète -> album -> Titre", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, - { - "Artist -> Album -> Track", - "Album nach Interpret", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "interprète -> album -> Titre", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, { "Collection", "Sammlung", @@ -1117,57 +1032,6 @@ const tI18nPhrase Phrases[] = "", // TODO "", // TODO }, - { - "Title -> Album -> Track", - "Titel -> Album -> Track", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Titre -> Album -> Pièce", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, - { - "Collection -> Item", - "Sammlung - Stück", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Collectin -> Pièce", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, - { - "Genre -> Decade -> Artist -> Album -> Track", - "Genre -> Dekade -> Interpret -> Album -> Track", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "Genre -> Décade -> Interprète -> Album -> Pièce", - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - "", // TODO - }, { "play", "spielen", -- cgit v1.2.3 From 643a54aeb6bd059c8c573d33939ac50274dc91d2 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 20 Feb 2005 12:42:49 +0000 Subject: reformat SQL strings, add new folder1..folder4 fields git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@500 e10066b5-e1e2-0310-b819-94efdf66514b --- mugglei.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 149 insertions(+), 13 deletions(-) diff --git a/mugglei.c b/mugglei.c index 40ed785..6feecf3 100755 --- a/mugglei.c +++ b/mugglei.c @@ -54,19 +54,155 @@ char *db_cmds[] = "DROP DATABASE IF EXISTS GiantDisc; CREATE DATABASE GiantDisc;", "grant all privileges on GiantDisc.* to vdr@localhost;", "use GiantDisc;", - "drop table if exists album; CREATE TABLE album ( artist varchar(255) default NULL, title varchar(255) default NULL, cddbid varchar(20) NOT NULL default '', coverimg varchar(255) default NULL, covertxt mediumtext, modified date default NULL, genre varchar(10) default NULL, PRIMARY KEY (cddbid), KEY artist (artist(10)), KEY title (title(10)), KEY genre (genre), KEY modified (modified)) TYPE=MyISAM;", - "drop table if exists genre; CREATE TABLE genre (id varchar(10) NOT NULL default '', id3genre smallint(6) default NULL, genre varchar(255) default NULL, freq int(11) default NULL, PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists language; CREATE TABLE language (id varchar(4) NOT NULL default '', language varchar(40) default NULL, freq int(11) default NULL, PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists musictype; CREATE TABLE musictype (musictype varchar(40) default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists player;CREATE TABLE player ( ipaddr varchar(255) NOT NULL default '', uichannel varchar(255) NOT NULL default '', logtarget int(11) default NULL, cdripper varchar(255) default NULL, mp3encoder varchar(255) default NULL, cdromdev varchar(255) default NULL, cdrwdev varchar(255) default NULL, id int(11) NOT NULL default '0', PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists playerstate;CREATE TABLE playerstate ( playerid int(11) NOT NULL default '0', playertype int(11) NOT NULL default '0', snddevice varchar(255) default NULL, playerapp varchar(255) default NULL, playerparams varchar(255) default NULL, ptlogger varchar(255) default NULL, currtracknb int(11) default NULL, state varchar(4) default NULL, shufflepar varchar(255) default NULL, shufflestat varchar(255) default NULL, pauseframe int(11) default NULL, framesplayed int(11) default NULL, framestotal int(11) default NULL, anchortime bigint(20) default NULL, PRIMARY KEY (playerid,playertype)) TYPE=HEAP;", - "drop table if exists playlist;CREATE TABLE playlist ( title varchar(255) default NULL, author varchar(255) default NULL, note varchar(255) default NULL, created timestamp(8) NOT NULL, id int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists playlistitem;CREATE TABLE playlistitem ( playlist int(11) NOT NULL default '0', tracknumber mediumint(9) NOT NULL default '0', trackid int(11) default NULL, PRIMARY KEY (playlist,tracknumber)) TYPE=MyISAM;", - "drop table if exists playlog;CREATE TABLE playlog ( trackid int(11) default NULL, played date default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists recordingitem;CREATE TABLE recordingitem ( trackid int(11) default NULL, recdate date default NULL, rectime time default NULL, reclength int(11) default NULL, enddate date default NULL, endtime time default NULL, repeat varchar(10) default NULL, initcmd varchar(255) default NULL, parameters varchar(255) default NULL, atqjob int(11) default NULL, id int(11) NOT NULL default '0', PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists source; CREATE TABLE source ( source varchar(40) default NULL, id tinyint(3) unsigned NOT NULL auto_increment, PRIMARY KEY (id)) TYPE=MyISAM;", - "drop table if exists tracklistitem;CREATE TABLE tracklistitem ( playerid int(11) NOT NULL default '0', listtype smallint(6) NOT NULL default '0', tracknb int(11) NOT NULL default '0', trackid int(11) NOT NULL default '0', PRIMARY KEY (playerid,listtype,tracknb)) TYPE=MyISAM;", - "drop table if exists tracks;CREATE TABLE tracks ( artist varchar(255) default NULL, title varchar(255) default NULL, genre1 varchar(10) default NULL, genre2 varchar(10) default NULL, year smallint(5) unsigned default NULL, lang varchar(4) default NULL, type tinyint(3) unsigned default NULL, rating tinyint(3) unsigned default NULL, length smallint(5) unsigned default NULL, source tinyint(3) unsigned default NULL, sourceid varchar(20) default NULL, tracknb tinyint(3) unsigned default NULL, mp3file varchar(255) default NULL, condition tinyint(3) unsigned default NULL, voladjust smallint(6) default '0', lengthfrm mediumint(9) default '0', startfrm mediumint(9) default '0', bpm smallint(6) default '0', lyrics mediumtext, bitrate varchar(10) default NULL, created date default NULL, modified date default NULL, backup tinyint(3) unsigned default NULL, samplerate int(7) unsigned default NULL, channels tinyint(3) unsigned default NULL, id int(11) NOT NULL auto_increment, PRIMARY KEY (id), KEY title (title(10)), KEY mp3file (mp3file(10)), KEY genre1 (genre1), KEY genre2 (genre2), KEY year (year), KEY lang (lang), KEY type (type), KEY rating (rating), KEY sourceid (sourceid), KEY artist (artist(10))) TYPE=MyISAM;" + "drop table if exists album; CREATE TABLE album ( " + "artist varchar(255) default NULL, " + "title varchar(255) default NULL, " + "cddbid varchar(20) NOT NULL default '', " + "coverimg varchar(255) default NULL, " + "covertxt mediumtext, " + "modified date default NULL, " + "genre varchar(10) default NULL, " + "PRIMARY KEY (cddbid), " + "KEY artist (artist(10)), " + "KEY title (title(10)), " + "KEY genre (genre), " + "KEY modified (modified)) " + "TYPE=MyISAM;", + "drop table if exists genre; CREATE TABLE genre (" + "id varchar(10) NOT NULL default '', " + "id3genre smallint(6) default NULL, " + "genre varchar(255) default NULL, " + "freq int(11) default NULL, " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists language; CREATE TABLE language (" + "id varchar(4) NOT NULL default '', " + "language varchar(40) default NULL, " + "freq int(11) default NULL, " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists musictype; CREATE TABLE musictype (" + "musictype varchar(40) default NULL, " + "id tinyint(3) unsigned NOT NULL auto_increment, " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists player;CREATE TABLE player ( " + "ipaddr varchar(255) NOT NULL default '', " + "uichannel varchar(255) NOT NULL default '', " + "logtarget int(11) default NULL, " + "cdripper varchar(255) default NULL, " + "mp3encoder varchar(255) default NULL, " + "cdromdev varchar(255) default NULL, " + "cdrwdev varchar(255) default NULL, " + "id int(11) NOT NULL default '0', " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists playerstate;CREATE TABLE playerstate ( " + "playerid int(11) NOT NULL default '0', " + "playertype int(11) NOT NULL default '0', " + "snddevice varchar(255) default NULL, " + "playerapp varchar(255) default NULL, " + "playerparams varchar(255) default NULL, " + "ptlogger varchar(255) default NULL, " + "currtracknb int(11) default NULL, " + "state varchar(4) default NULL, " + "shufflepar varchar(255) default NULL, " + "shufflestat varchar(255) default NULL, " + "pauseframe int(11) default NULL, " + "framesplayed int(11) default NULL, " + "framestotal int(11) default NULL, " + "anchortime bigint(20) default NULL, " + "PRIMARY KEY (playerid,playertype)) " + "TYPE=HEAP;", + "drop table if exists playlist;CREATE TABLE playlist ( " + "title varchar(255) default NULL, " + "author varchar(255) default NULL, " + "note varchar(255) default NULL, " + "created timestamp(8) NOT NULL, " + "id int(10) unsigned NOT NULL auto_increment, " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists playlistitem;CREATE TABLE playlistitem ( " + "playlist int(11) NOT NULL default '0', " + "tracknumber mediumint(9) NOT NULL default '0', " + "trackid int(11) default NULL, " + "PRIMARY KEY (playlist,tracknumber)) " + "TYPE=MyISAM;", + "drop table if exists playlog;CREATE TABLE playlog ( " + "trackid int(11) default NULL, " + "played date default NULL, " + "id tinyint(3) unsigned NOT NULL auto_increment, " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists recordingitem;CREATE TABLE recordingitem ( " + "trackid int(11) default NULL, " + "recdate date default NULL, " + "rectime time default NULL, " + "reclength int(11) default NULL, " + "enddate date default NULL, " + "endtime time default NULL, " + "repeat varchar(10) default NULL, " + "initcmd varchar(255) default NULL, " + "parameters varchar(255) default NULL, " + "atqjob int(11) default NULL, " + "id int(11) NOT NULL default '0', " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists source; CREATE TABLE source ( " + "source varchar(40) default NULL, " + "id tinyint(3) unsigned NOT NULL auto_increment, " + "PRIMARY KEY (id)) " + "TYPE=MyISAM;", + "drop table if exists tracklistitem;CREATE TABLE tracklistitem ( " + "playerid int(11) NOT NULL default '0', " + "listtype smallint(6) NOT NULL default '0', " + "tracknb int(11) NOT NULL default '0', " + "trackid int(11) NOT NULL default '0', " + "PRIMARY KEY (playerid,listtype,tracknb)) " + "TYPE=MyISAM;", + "drop table if exists tracks;CREATE TABLE tracks ( " + "artist varchar(255) default NULL, " + "title varchar(255) default NULL, " + "genre1 varchar(10) default NULL, " + "genre2 varchar(10) default NULL, " + "year smallint(5) unsigned default NULL, " + "lang varchar(4) default NULL, " + "type tinyint(3) unsigned default NULL, " + "rating tinyint(3) unsigned default NULL, " + "length smallint(5) unsigned default NULL, " + "source tinyint(3) unsigned default NULL, " + "sourceid varchar(20) default NULL, " + "tracknb tinyint(3) unsigned default NULL, " + "mp3file varchar(255) default NULL, " + "condition tinyint(3) unsigned default NULL, " + "voladjust smallint(6) default '0', " + "lengthfrm mediumint(9) default '0', " + "startfrm mediumint(9) default '0', " + "bpm smallint(6) default '0', " + "lyrics mediumtext, " + "bitrate varchar(10) default NULL, " + "created date default NULL, " + "modified date default NULL, " + "backup tinyint(3) unsigned default NULL, " + "samplerate int(7) unsigned default NULL, " + "channels tinyint(3) unsigned default NULL, " + "id int(11) NOT NULL auto_increment, " + "folder1 varchar(255), " + "folder2 varchar(255), " + "folder3 varchar(255), " + "folder4 varchar(255), " + "PRIMARY KEY (id), " + "KEY title (title(10)), " + "KEY mp3file (mp3file(10)), " + "KEY genre1 (genre1), " + "KEY genre2 (genre2), " + "KEY year (year), " + "KEY lang (lang), " + "KEY type (type), " + "KEY rating (rating), " + "KEY sourceid (sourceid), " + "KEY artist (artist(10))) " + "TYPE=MyISAM;" }; bool folderfields; -- cgit v1.2.3 From aaa6565adc37f851d19c7558ba150fd194142ca7 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 20 Feb 2005 12:50:32 +0000 Subject: bump version to 0.1.3 git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@501 e10066b5-e1e2-0310-b819-94efdf66514b --- muggle.c | 2 +- muggle.doxygen | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/muggle.c b/muggle.c index 28b0729..46fda32 100644 --- a/muggle.c +++ b/muggle.c @@ -21,7 +21,7 @@ #include #include -static const char *VERSION = "0.1.2"; +static const char *VERSION = "0.1.3"; static const char *DESCRIPTION = "Media juggle plugin for VDR"; static const char *MAINMENUENTRY = "Muggle"; diff --git a/muggle.doxygen b/muggle.doxygen index 2b401c4..3a1ee60 100644 --- a/muggle.doxygen +++ b/muggle.doxygen @@ -23,7 +23,7 @@ PROJECT_NAME = Muggle media plugin # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.1.1 +PROJECT_NUMBER = 0.1.3 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. -- cgit v1.2.3 From c87cae66c3cbf92ce6cbf9ced5bc2016c5d6b406 Mon Sep 17 00:00:00 2001 From: wr61 Date: Sun, 20 Feb 2005 18:36:51 +0000 Subject: track info shows file name git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk/muggle-plugin@502 e10066b5-e1e2-0310-b819-94efdf66514b --- vdr_player.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vdr_player.c b/vdr_player.c index 38dfec5..bf0a504 100644 --- a/vdr_player.c +++ b/vdr_player.c @@ -1378,6 +1378,14 @@ mgPlayerControl::ShowContents () m_menu->SetItem (buf, 6, false, false); free (buf); } + if (num_items > 6) + { + string sf = player->getCurrent ()->getSourceFile (); + char *p = strrchr(sf.c_str(),'/'); + asprintf (&buf, "File name:\t%s", p+1); + m_menu->SetItem (buf, 7, false, false); + free (buf); + } } else { -- cgit v1.2.3 -- cgit v1.2.3