diff --git a/ChangeLog b/ChangeLog index ea3ebed3..a9a3b3f1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24056,3 +24056,8 @@ * Added a 'Generate Back Report' item to the right-click menu in the 'Podcast Feeds' list in the 'Rivendell Feed List' dialog in rdadmin(1). +2023-05-06 Fred Gleason + * Refactored the RSS reports in rdadmin(1) to include information on + missing items. + * Modified the feed report in rdcastmanager to include information + on missing items. diff --git a/docs/dtds/Makefile.am b/docs/dtds/Makefile.am index abec1bc4..0dbe38da 100644 --- a/docs/dtds/Makefile.am +++ b/docs/dtds/Makefile.am @@ -2,7 +2,7 @@ ## ## docs/dtds/Makefile.am for Rivendell ## -## (C) Copyright 2020 Fred Gleason +## (C) Copyright 2020-2023 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as @@ -29,15 +29,20 @@ all-local: direct.html\ direct.pdf\ + rivendell.html\ + rivendell.pdf\ superfeed.html\ superfeed.pdf -EXTRA_DIST = superfeed.html\ - superfeed.pdf\ - superfeed.xml\ - direct.html\ +EXTRA_DIST = direct.html\ direct.pdf\ - direct.xml + direct.xml\ + rivendell.html\ + rivendell.pdf\ + rivendell.xml\ + superfeed.html\ + superfeed.pdf\ + superfeed.xml CLEANFILES = *~ MAINTAINERCLEANFILES = *~\ diff --git a/docs/dtds/rivendell.xml b/docs/dtds/rivendell.xml new file mode 100644 index 00000000..6146cab4 --- /dev/null +++ b/docs/dtds/rivendell.xml @@ -0,0 +1,183 @@ + +
+ + The Rivendell "rivendell" Namespace + Version 0.1 + v0.1 + + + Fred + Gleason + fredg@paravelsystems.com + + + + v0.1 + + 2023Fred Gleason + + + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + + + + + + Overview + + The "superfeed" XML namespace adds support for additional + fields used by Rivendell RSS podcasting report generation. It's use is + limited to internal documents generated within Rivendell. + + + + Namespace Declaration + + The namespace declaration is: + + + xmlns:rivendell="http://www.rivendellaudio.org/dtds/rivendell-0.1.dtd" + + + + Fields + + The namespace adds the following fields: + + + + + <item rivendell:id=id> + + + + This attribute is added to the <channel> and <item> + tags. It is a unique unsigned integer ID that associates the object + to a record in the FEEDS and + PODCASTS database tables, + respectively. + + + + + + + <item rivendell:status=class-name> + + + + This attribute is added to the <item> tag. It indicates + a class name to use for this item when rendering reports. + + + + + + + + Example + +<?xml version="1.0" encoding="UTF-8"?> + +<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:superfeed="http://www.rivendellaudio.org/dtds/superfeed-0.1.dtd" xmlns:rivendell="http://www.rivendellaudio.org/dtds/rivendell-0.1.dtd"> + <channel rivendell:id="1234"> + <title>Rivendell Radio Automation</title> + <description>All about the Rivendell Radio Automation System</description> + <itunes:summary>All about the Rivendell Radio Automation System</itunes:summary> + <category>Technology</category> + <link>http://feeds.example.com/rivendel</link> + <language>en-us</language> + <copyright>© 2020 John Q. Author</copyright> + <lastBuildDate>Thu, 4 Jun 2020 12:18:54 GMT</lastBuildDate> + <pubDate>Thu, 21 May 2020 10:43:55 GMT</pubDate> + <managingEditor>editor@example.com</managingEditor> + <webMaster>webmaster@example.com</webMaster> + <generator>Rivendell 3.3.0int1</generator> + <image> + <url>http://feeds.example.com/rivendel/img000022_000038.png</url> + <title>Rivendell Radio Automation</title> + <link>http://feeds.example.com/rivendel</link> + <width>2048</width> + <height>2048</height> + <description>The Rivendell Icon</description> + </image> + <atom:link href="http://feeds.example.com/rivendel/RIVENDEL.rss" rel="self" type="application/rss+xml" /> + <itunes:author>author@example.com</itunes:author> + <itunes:type>episodic</itunes:type> + <itunes:owner> + <itunes:name>John Q. Author</itunes:name> + <itunes:email>fredg@paravelsystems.com</itunes:email> + </itunes:owner> + <itunes:image href="http://feeds.example.com/rivendel/img000022_000038.png" /> + <itunes:category text="Technology" /> + <itunes:explicit>false</itunes:explicit> + <item rivendell:id="4321" rivendell:class="active-rounded-block"> + <superfeed:channelTitle>RDAirPlay</superfeed:channelTitle> + <superfeed:channelDescription>All about RDAirPlay in Rivendell</superfeed:channelDescription> + <title>Thurber Jewelers</title> + <itunes:title>Thurber Jewelers</itunes:title> + <link>http://feeds.example.com/airplay</link> + <guid isPermaLink="false">http://feeds.example.com/airplay/000019_000026.mp3_000019_000026</guid> + <description>All about RDAirPlay in Rivendell</description> + <itunes:summary>All about RDAirPlay in Rivendell</itunes:summary> + <author>someone@example.com (John Q. Author)</author> + <itunes:author>someone@example.com (John Q. Author)</itunes:author> + <comments>http://feeds.example.com/airplay</comments> + <source url="http://feeds.example.com/airplay/RIVENDEL">Rivendell Radio Automation</source> + <enclosure url="http://feeds.example.com/airplay/000019_000026.mp3" length="170496" type="audio/mpeg" /> + <category>Technology</category> + <pubDate>Thu, 21 May 2020 20:31:26 GMT</pubDate> + <itunes:duration>14</itunes:duration> + <itunes:image href="http://feeds.example.com/airplay/img000019_000024.png" /> + <itunes:explicit>false</itunes:explicit> + </item> + <item rivendell:id="4322" rivendell:class="active-rounded-block"> + <superfeed:channelTitle>RDCartSlots</superfeed:channelTitle> + <superfeed:channelDescription>All channel all about RDCartSlots!</superfeed:channelDescription> + <title>Liberty Tax</title> + <itunes:title>Liberty Tax</itunes:title> + <link>http://feeds.example.com</link> + <guid isPermaLink="false">http://feeds.example.com/cartslts/000025_000025.mp3_000025_000025</guid> + <description>The Liberty tax advisors.</description> + <itunes:summary>The Liberty tax advisors.</itunes:summary> + <author>someone@example.com (John Q. Author)</author> + <itunes:author>someone@example.com (John Q. Author)</itunes:author> + <comments>http://feeds.example.com/cartslts</comments> + <source url="http://feeds.example.com/cartslts/RIVENDEL">Rivendell Radio Automation</source> + <enclosure url="http://feeds.example.com/cartslts/000025_000025.mp3" length="369216" type="audio/mpeg" /> + <category>Technology</category> + <pubDate>Thu, 21 May 2020 19:17:26 GMT</pubDate> + <itunes:duration>15</itunes:duration> + <itunes:image href="http://feeds.example.com/cartslts/img000025_000037.png" /> + <itunes:explicit>false</itunes:explicit> + </item> + <item> + <superfeed:channelTitle>RDLibrary</superfeed:channelTitle> + <superfeed:channelDescription>A channel all about RDLibrary</superfeed:channelDescription> + <title>The Peanut Shoppe</title> + <itunes:title>The Peanut Shoppe</itunes:title> + <link>http://feeds.example.com/library</link> + <guid isPermaLink="false">http://feeds.example.com/library/000024_000024.mp3_000024_000024</guid> + <description>A trip to a peanut and candy shop in downtown Wadsworth OH.</description> + <itunes:summary>A trip to a peanut and candy shop in downtown Wadsworth OH.</itunes:summary> + <author>someone@example.com (John Q. Author)</author> + <itunes:author>someone@example.com (John Q. Author)</itunes:author> + <comments>http://feeds.example.com/library</comments> + <source url="http://feeds.example.com/library/RIVENDEL">Rivendell Radio Automation</source> + <enclosure url="http://feeds.example.com/library/000024_000024.mp3" length="196992" type="audio/mpeg" /> + <category>Technology</category> + <pubDate>Thu, 21 May 2020 19:14:53 GMT</pubDate> + <itunes:duration>16</itunes:duration> + <itunes:image href="http://feeds.example.com/library/img000024_000036.png" /> + <itunes:explicit>false</itunes:explicit> + </item> + </channel> +</rss> + + + + +
diff --git a/lib/Makefile.am b/lib/Makefile.am index 1ff30cdd..ce422a30 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -136,7 +136,6 @@ dist_librd_la_SOURCES = dbversion.h\ rdexport_settings_dialog.cpp rdexport_settings_dialog.h\ rdfeed.cpp rdfeed.h\ rdfeedlistmodel.cpp rdfeedlistmodel.h\ - rdfeedlistview.cpp rdfeedlistview.h\ rdfontengine.cpp rdfontengine.h\ rdformpost.cpp rdformpost.h\ rdflacdecode.cpp rdflacdecode.h\ @@ -348,7 +347,6 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\ moc_rdexport_settings_dialog.cpp\ moc_rdfeed.cpp\ moc_rdfeedlistmodel.cpp\ - moc_rdfeedlistview.cpp\ moc_rdframe.cpp\ moc_rdget_ath.cpp\ moc_rdgetpasswd.cpp\ diff --git a/lib/rdfeed.cpp b/lib/rdfeed.cpp index bd917060..f17fbda5 100644 --- a/lib/rdfeed.cpp +++ b/lib/rdfeed.cpp @@ -44,6 +44,7 @@ #include "rdwavefile.h" #include "rdwebresult.h" #include "rdxport_interface.h" +#include "rdxsltengine.h" int __RDFeed_Debug_Callback(CURL *handle,curl_infotype type,char *data, size_t size,void *userptr) @@ -69,6 +70,8 @@ size_t __RDFeed_Download_Callback(char *ptr,size_t size,size_t nmemb, } + + RDFeed::RDFeed(const QString &keyname,RDConfig *config,QObject *parent) : QObject(parent) { @@ -956,90 +959,6 @@ QString RDFeed::imageUrl(int img_id) const } -bool RDFeed::downloadXml(QByteArray *xml,QString *err_msg) -{ - long response_code; - CURL *curl=NULL; - CURLcode curl_err; - char curl_errorbuffer[CURL_ERROR_SIZE]; - struct curl_httppost *first=NULL; - struct curl_httppost *last=NULL; - - // - // Generate POST Data - // - curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", - CURLFORM_COPYCONTENTS, - QString::asprintf("%u",RDXPORT_COMMAND_DOWNLOAD_RSS).toUtf8(). - constData(), - CURLFORM_END); - curl_formadd(&first,&last,CURLFORM_PTRNAME,"LOGIN_NAME", - CURLFORM_COPYCONTENTS,rda->user()->name().toUtf8().constData(), - CURLFORM_END); - curl_formadd(&first,&last,CURLFORM_PTRNAME,"PASSWORD", - CURLFORM_COPYCONTENTS, - rda->user()->password().toUtf8().constData(),CURLFORM_END); - curl_formadd(&first,&last,CURLFORM_PTRNAME,"ID", - CURLFORM_COPYCONTENTS, - QString::asprintf("%u",feed_id).toUtf8().constData(), - CURLFORM_END); - - // - // Set up the transfer - // - if((curl=curl_easy_init())==NULL) { - curl_formfree(first); - return false; - } - QStringList *err_msgs=SetupCurlLogging(curl); - - curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__RDFeed_Download_Callback); - feed_xml.clear(); - curl_easy_setopt(curl,CURLOPT_WRITEDATA,xml); - curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); - curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,curl_errorbuffer); - curl_easy_setopt(curl,CURLOPT_USERAGENT, - rda->config()->userAgent().toUtf8().constData()); - curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); - curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); - curl_easy_setopt(curl,CURLOPT_URL, - rda->station()->webServiceUrl(rda->config()).toUtf8().constData()); - rda->syslog(LOG_DEBUG,"using web service URL: %s", - rda->station()->webServiceUrl(rda->config()).toUtf8().constData()); - - // - // Send it - // - if((curl_err=curl_easy_perform(curl))!=CURLE_OK) { - *err_msg=QString::fromUtf8(curl_errorbuffer); - curl_easy_cleanup(curl); - curl_formfree(first); - ProcessCurlLogging("RDFeed::postPodcast()",err_msgs); - return false; - } - - // - // Clean up - // - curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); - curl_easy_cleanup(curl); - curl_formfree(first); - - // - // Process the results - // - if((response_code<200)||(response_code>299)) { - *err_msg=tr("remote server returned unexpected response code")+ - QString::asprintf(" %ld",response_code); - ProcessCurlLogging("RDFeed::postPodcast()",err_msgs); - return false; - } - delete err_msgs; - - return true; -} - - bool RDFeed::postXml(QString *err_msg) { long response_code; @@ -1714,7 +1633,8 @@ unsigned RDFeed::postLog(const QString &logname,const QTime &start_time, } -QString RDFeed::rssXml(QString *err_msg,const QDateTime &now,bool *ok) +QString RDFeed::rssXml(QString *err_msg,const QDateTime &now,bool *ok, + QList *active_cast_ids) { QString ret; @@ -1791,7 +1711,8 @@ QString RDFeed::rssXml(QString *err_msg,const QDateTime &now,bool *ok) // // Render Channel XML // - ret+=" \n"; + ret+=QString::asprintf(" \n", + chan_q->value(18).toUInt()); ret+=ResolveChannelWildcards(channel_template,chan_q,now)+"\r\n"; // @@ -1852,7 +1773,15 @@ QString RDFeed::rssXml(QString *err_msg,const QDateTime &now,bool *ok) } item_q=new RDSqlQuery(sql); while(item_q->next()) { - ret+=" \r\n"; + if((active_cast_ids==NULL)|| + (active_cast_ids->contains(item_q->value(14).toUInt()))) { + ret+=QString:: + asprintf(" \r\n",item_q->value(14).toUInt()); + } + else { + ret+=QString:: + asprintf(" \r\n",item_q->value(14).toUInt()); + } ret+=ResolveItemWildcards(item_template,item_q,chan_q); ret+="\r\n"; ret+=" \r\n"; @@ -1871,6 +1800,256 @@ QString RDFeed::rssXml(QString *err_msg,const QDateTime &now,bool *ok) } +bool RDFeed::rssFrontXml(QByteArray *xml,QString *err_msg) const +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + char curl_errorbuffer[CURL_ERROR_SIZE]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + QString::asprintf("%u",RDXPORT_COMMAND_DOWNLOAD_RSS).toUtf8(). + constData(), + CURLFORM_END); + curl_formadd(&first,&last,CURLFORM_PTRNAME,"LOGIN_NAME", + CURLFORM_COPYCONTENTS,rda->user()->name().toUtf8().constData(), + CURLFORM_END); + curl_formadd(&first,&last,CURLFORM_PTRNAME,"PASSWORD", + CURLFORM_COPYCONTENTS, + rda->user()->password().toUtf8().constData(),CURLFORM_END); + curl_formadd(&first,&last,CURLFORM_PTRNAME,"ID", + CURLFORM_COPYCONTENTS, + QString::asprintf("%u",feed_id).toUtf8().constData(), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + QStringList *err_msgs=SetupCurlLogging(curl); + + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__RDFeed_Download_Callback); + xml->clear(); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,xml); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,curl_errorbuffer); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + rda->config()->userAgent().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_URL, + rda->station()->webServiceUrl(rda->config()).toUtf8().constData()); + rda->syslog(LOG_DEBUG,"using web service URL: %s", + rda->station()->webServiceUrl(rda->config()).toUtf8().constData()); + + // + // Send it + // + if((curl_err=curl_easy_perform(curl))!=CURLE_OK) { + *err_msg=QString::fromUtf8(curl_errorbuffer); + curl_easy_cleanup(curl); + curl_formfree(first); + ProcessCurlLogging("RDFeed::postPodcast()",err_msgs); + return false; + } + + // + // Clean up + // + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_easy_cleanup(curl); + curl_formfree(first); + + // + // Process the results + // + if((response_code<200)||(response_code>299)) { + *err_msg=tr("remote server returned unexpected response code")+ + QString::asprintf(" %ld",response_code); + ProcessCurlLogging("RDFeed::postPodcast()",err_msgs); + return false; + } + delete err_msgs; + + return true; +} + + +bool RDFeed::rssBackXml(QByteArray *xml,QString *err_msg) const +{ + CURL *curl=NULL; + CURLcode curl_err; + long response_code; + bool ret=false; + + if((curl=curl_easy_init())==NULL) { + *err_msg=tr("Unable to initialize CURL"); + ret=false; + } + else { + QByteArray src_xml; + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__RDFeed_Download_Callback); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,xml); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + rda->config()->userAgent().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_URL, + RDFeed::publicUrl(baseUrl(""),feed_keyname). + toUtf8().constData()); + curl_err=curl_easy_perform(curl); + if((ret=(curl_err==CURLE_OK))) { + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + if((ret=(response_code>=200)||(response_code<300))) { + ret=true; + *err_msg=QObject::tr("Server returned result code")+ + QString::asprintf(" %lu ",response_code)+*xml; + } + } + else { + *err_msg=QObject::tr("Curl error")+" ["+curl_easy_strerror(curl_err)+"]."; + ret=false; + } + } + curl_easy_cleanup(curl); + return ret; +} + + +void RDFeed::activeCasts(QList *cast_ids) +{ + cast_ids->clear(); + + QString sql=QString("select ")+ + "`ID` "+ // 00 + "from `PODCASTS` where "+ + QString::asprintf("`FEED_ID`=%u && ",feed_id)+ + QString::asprintf("`STATUS`=%u ",RDPodcast::StatusActive)+ + "order by `ORIGIN_DATETIME` desc"; + RDSqlQuery *q=new RDSqlQuery(sql); + while(q->next()) { + cast_ids->push_back(q->value(0).toUInt()); + } + delete q; +} + + +bool RDFeed::frontActiveCasts(QList *cast_ids,QString *err_msg) +{ + QByteArray xml; + bool ret=false; + QString text; + bool ok=false; + + cast_ids->clear(); + if(rssFrontXml(&xml,err_msg)) { + RDXsltEngine *xslt= + new RDXsltEngine("/usr/share/rivendell/rss-item-enclosures.xsl",this); + if(xslt->transform(&text,xml,err_msg)) { + QStringList f0=text.split("|",QString::SkipEmptyParts); + for(int i=0;ipush_back(f3.last().toUInt(&ok)); + if(ok) { + ret=ok; + // OK, keep going + } + else { + // Integer conversion failed + *err_msg=QObject::tr("Internal error 1"); + ret=false; + break; + } + } + else { + // f3: Split on '_' failed + *err_msg=QObject::tr("Internal error 2"); + ret=false; + break; + } + } + else { + // f2: Split on '.' failed + *err_msg=QObject::tr("Internal error 3"); + ret=false; + break; + } + } + } + delete xslt; + } + + return ret; +} + + +bool RDFeed::backActiveCasts(QList *cast_ids,QString *err_msg) +{ + QByteArray xml; + bool ret=false; + QString text; + bool ok=false; + + cast_ids->clear(); + if(rssBackXml(&xml,err_msg)) { + RDXsltEngine *xslt= + new RDXsltEngine("/usr/share/rivendell/rss-item-enclosures.xsl", + this); + if(xslt->transform(&text,xml,err_msg)) { + QStringList f0=text.split("|",QString::SkipEmptyParts); + for(int i=0;ipush_back(f3.last().toUInt(&ok)); + if(ok) { + ret=ok; + // OK, keep going + } + else { + // Integer conversion failed + *err_msg=QObject::tr("Internal error 1"); + ret=false; + break; + } + } + else { + // f3: Split on '_' failed + *err_msg=QObject::tr("Internal error 2"); + ret=false; + break; + } + } + else { + // f2: Split on '.' failed + *err_msg=QObject::tr("Internal error 3"); + ret=false; + break; + } + } + } + delete xslt; + } + + return ret; +} + + unsigned RDFeed::create(const QString &keyname,bool enable_users, QString *err_msg) { diff --git a/lib/rdfeed.h b/lib/rdfeed.h index 1cb10939..e447f991 100644 --- a/lib/rdfeed.h +++ b/lib/rdfeed.h @@ -136,7 +136,6 @@ class RDFeed : public QObject bool postPodcast(unsigned cast_id,QString *err_msg); QString audioUrl(unsigned cast_id); QString imageUrl(int img_id) const; - bool downloadXml(QByteArray *xml,QString *err_msg); // WebAPI Call bool postXml(QString *err_msg); // WebAPI Call bool postXmlConditional(const QString &caption,QWidget *widget); bool removeRss(); // WebAPI Call @@ -148,31 +147,19 @@ class RDFeed : public QObject unsigned postLog(const QString &logname,const QTime &start_time, bool stop_at_stop,int start_line,int end_line, QString *err_msg); - QString rssXml(QString *err_msg,const QDateTime &now,bool *ok=NULL); + QString rssXml(QString *err_msg,const QDateTime &now,bool *ok, + QList *active_cast_ids=NULL); + bool rssFrontXml(QByteArray *xml,QString *err_msg) const; // WebAPI Call + bool rssBackXml(QByteArray *xml,QString *err_msg) const;// Public HTTP(S) Call + void activeCasts(QList *cast_ids); + bool frontActiveCasts(QList *cast_ids,QString *err_msg); + bool backActiveCasts(QList *cast_ids,QString *err_msg); static unsigned create(const QString &keyname,bool enable_users, QString *err_msg); static QString imageFilename(int feed_id,int img_id,const QString &ext); static QString publicUrl(const QString &base_url,const QString &keyname); static QString itunesCategoryXml(const QString &category, const QString &sub_category,int padding=0); - /* - static bool generateReport(const QString &feed_url, - const QString &stylesheet_pathname, - const QString &report_filename, - RDTempDirectory *tempdir,QString *err_msg); - */ - /* - static bool generateReport(const QByteArray &src_xml, - const QString &stylesheet_pathname, - const QString &report_filename, - RDTempDirectory *tempdir,QString *err_msg); - */ - /* - static bool generateReport(QString *output, - const QString &src_xml, - const QString &stylesheet_pathname, - QString *err_msg); - */ signals: void postProgressChanged(int step); diff --git a/lib/rdfeedlistview.cpp b/lib/rdfeedlistview.cpp deleted file mode 100644 index 408e6688..00000000 --- a/lib/rdfeedlistview.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// rdfeedlistview.cpp -// -// RDTableView widget for RSS feeds -// -// (C) Copyright 2021 Fred Gleason -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// - -#include -#include -#include - -#include "rdfeed.h" -#include "rdfeedlistmodel.h" -#include "rdfeedlistview.h" -#include "rdtextfile.h" - -RDFeedListView::RDFeedListView(QWidget *parent) - :RDTableView(parent) -{ - d_mouse_row=-1; - - // - // Mouse menu - // - d_mouse_menu=new QMenu(this); - - d_front_report_action=d_mouse_menu-> - addAction(tr("Generate Front Report"),this,SLOT(generateFrontReportData())); - d_front_report_action->setCheckable(false); - - d_back_report_action=d_mouse_menu-> - addAction(tr("Generate Back Report"),this,SLOT(generateBackReportData())); - d_back_report_action->setCheckable(false); - - connect(d_mouse_menu,SIGNAL(aboutToShow()), - this,SLOT(aboutToShowMenuData())); - - // - // XSLT Engine (for feed reports) - // - d_xslt_engine= - new RDXsltEngine("/usr/share/rivendell/rdcastmanager-report.xsl",this); -} - - -RDFeedListView::~RDFeedListView() -{ - delete d_xslt_engine; -} - - -void RDFeedListView::aboutToShowMenuData() -{ - RDFeedListModel *mod=(RDFeedListModel *)model(); - - if((d_mouse_row<0)||(d_mouse_row>=mod->rowCount())) { - d_front_report_action->setEnabled(false); - d_back_report_action->setEnabled(false); - } - else { - d_front_report_action->setEnabled(true); - d_back_report_action->setEnabled(true); - } -} - - -void RDFeedListView::generateFrontReportData() -{ - QString err_msg; - RDFeedListModel *m=(RDFeedListModel *)model(); - QString keyname=m->data(m->index(d_mouse_row,0)).toString(); - RDFeed *feed=new RDFeed(keyname,rda->config(),this); - QByteArray xml; - if(feed->downloadXml(&xml,&err_msg)) { - QString output_filename="report.html"; - if(d_xslt_engine->transformXml(&output_filename,xml,&err_msg)) { - RDWebBrowser("file://"+output_filename); - } - else { - QMessageBox::warning(this,"RDAdmin - "+tr("Error"),err_msg); - } - } - else { - QMessageBox::warning(this,"RDAdmin - "+tr("Error"),err_msg); - } -} - - -void RDFeedListView::generateBackReportData() -{ - QString err_msg; - RDFeedListModel *m=(RDFeedListModel *)model(); - QString url=m->data(m->index(d_mouse_row,6)).toString(); - - QString output_filename="report.html"; - if(d_xslt_engine->transformUrl(&output_filename,url,&err_msg)) { - RDWebBrowser("file://"+output_filename); - } - else { - QMessageBox::warning(this,"RDAdmin - "+tr("Error"),err_msg); - return; - } -} - - -void RDFeedListView::mousePressEvent(QMouseEvent *e) -{ - if(e->button()==Qt::RightButton) { - d_mouse_row=indexAt(e->pos()).row(); - if((d_mouse_row>=0)&&(d_mouse_rowrowCount())) { - d_mouse_menu->popup(e->globalPos()); - } - else { - d_mouse_row=-1; - } - } - QTableView::mousePressEvent(e); -} diff --git a/lib/rdrssschemas.cpp b/lib/rdrssschemas.cpp index 172fbf56..8fc80e33 100644 --- a/lib/rdrssschemas.cpp +++ b/lib/rdrssschemas.cpp @@ -2,7 +2,7 @@ // // RSS schema definitions for Rivendell // -// (C) Copyright 2020-2022 Fred Gleason +// (C) Copyright 2020-2023 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -57,13 +57,13 @@ RDRssSchemas::RDRssSchemas() c_header_templates.push_back(""); // Rss202Schema - c_header_templates.push_back("\n"); + c_header_templates.push_back("\n"); // AppleSchema - c_header_templates.push_back("\n"); + c_header_templates.push_back("\n"); // AppleSuperfeedSchema - c_header_templates.push_back("\n"); + c_header_templates.push_back("\n"); // // Channel Templates diff --git a/rdadmin/Makefile.am b/rdadmin/Makefile.am index faa8687f..82ab774e 100644 --- a/rdadmin/Makefile.am +++ b/rdadmin/Makefile.am @@ -2,7 +2,7 @@ ## ## Automake.am for rivendell/rdadmin ## -## (C) Copyright 2002-2021 Fred Gleason +## (C) Copyright 2002-2023 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as @@ -93,6 +93,7 @@ dist_rdadmin_SOURCES = add_feed.cpp add_feed.h\ edit_user_perms.cpp edit_user_perms.h\ edit_user_service_perms.cpp edit_user_service_perms.h\ edit_vguest_resource.cpp edit_vguest_resource.h\ + feedlistview.cpp feedlistview.h\ globals.h\ help_audios.cpp help_audios.h\ importfields.cpp importfields.h\ @@ -176,6 +177,7 @@ nodist_rdadmin_SOURCES = global_credits.cpp\ moc_edit_user_perms.cpp\ moc_edit_user_service_perms.cpp\ moc_edit_vguest_resource.cpp\ + moc_feedlistview.cpp\ moc_help_audios.cpp\ moc_importfields.cpp\ moc_info_dialog.cpp\ diff --git a/rdadmin/feedlistview.cpp b/rdadmin/feedlistview.cpp new file mode 100644 index 00000000..55fc36b0 --- /dev/null +++ b/rdadmin/feedlistview.cpp @@ -0,0 +1,148 @@ +// feedlistview.cpp +// +// RDTableView widget for RSS feeds +// +// (C) Copyright 2021-2023 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include +#include + +#include "rdfeedlistmodel.h" +#include "rdreport.h" + +#include "feedlistview.h" +#include "rdtextfile.h" + +FeedListView::FeedListView(QWidget *parent) + :RDTableView(parent) +{ + d_mouse_row=-1; + + // + // Mouse menu + // + d_mouse_menu=new QMenu(this); + + d_front_item_report_action=d_mouse_menu-> + addAction(tr("Generate Item Report [Front]"),this,SLOT(generateFrontItemReportData())); + d_front_item_report_action->setCheckable(false); + d_back_item_report_action=d_mouse_menu-> + addAction(tr("Generate Item Report [Back]"),this,SLOT(generateBackItemReportData())); + d_back_item_report_action->setCheckable(false); + + connect(d_mouse_menu,SIGNAL(aboutToShow()), + this,SLOT(aboutToShowMenuData())); +} + + +FeedListView::~FeedListView() +{ + for(int i=0;i=mod->rowCount())) { + d_front_item_report_action->setEnabled(false); + d_back_item_report_action->setEnabled(false); + } + else { + d_front_item_report_action->setEnabled(true); + d_back_item_report_action->setEnabled(true); + } +} + + +void FeedListView::generateFrontItemReportData() +{ + QDateTime now=QDateTime::currentDateTime(); + bool ok=false; + QList front_ids; + QString err_msg; + QString output_filename="report.html"; + RDFeedListModel *m=(RDFeedListModel *)model(); + QString keyname=m->data(m->index(d_mouse_row,0)).toString(); + RDFeed *feed=new RDFeed(keyname,rda->config(),this); + + if(feed->frontActiveCasts(&front_ids,&err_msg)) { + QString xml=feed->rssXml(&err_msg,now,&ok,&front_ids); + d_xslt_engines. + push_back(new RDXsltEngine("/usr/share/rivendell/rss-front-item-report.xsl",this)); + if(d_xslt_engines.back()->transformXml(&output_filename,xml,&err_msg)) { + RDWebBrowser("file://"+output_filename); + } + else { + QMessageBox::warning(this,"RDAdmin - "+tr("Error"),err_msg); + } + } + else { + QMessageBox::warning(this,"RDAdmin - "+tr("Error"), + tr("Error accessing from XML data.")+"\n"+ + "["+err_msg+"]"); + } +} + + +void FeedListView::generateBackItemReportData() +{ + QDateTime now=QDateTime::currentDateTime(); + bool ok=false; + QList back_ids; + QString err_msg; + QString output_filename="report.html"; + RDFeedListModel *m=(RDFeedListModel *)model(); + QString keyname=m->data(m->index(d_mouse_row,0)).toString(); + RDFeed *feed=new RDFeed(keyname,rda->config(),this); + + if(feed->backActiveCasts(&back_ids,&err_msg)) { + QString xml=feed->rssXml(&err_msg,now,&ok,&back_ids); + d_xslt_engines. + push_back(new RDXsltEngine("/usr/share/rivendell/rss-back-item-report.xsl",this)); + if(d_xslt_engines.back()->transformXml(&output_filename,xml,&err_msg)) { + RDWebBrowser("file://"+output_filename); + } + else { + QMessageBox::warning(this,"RDAdmin - "+tr("Error"),err_msg); + } + } + else { + QMessageBox::warning(this,"RDAdmin - "+tr("Error"), + tr("Error accessing from XML data.")+"\n"+ + "["+err_msg+"]"); + } +} + + +void FeedListView::mousePressEvent(QMouseEvent *e) +{ + if(e->button()==Qt::RightButton) { + d_mouse_row=indexAt(e->pos()).row(); + if((d_mouse_row>=0)&&(d_mouse_rowrowCount())) { + d_mouse_menu->popup(e->globalPos()); + } + else { + d_mouse_row=-1; + } + } + QTableView::mousePressEvent(e); +} diff --git a/lib/rdfeedlistview.h b/rdadmin/feedlistview.h similarity index 71% rename from lib/rdfeedlistview.h rename to rdadmin/feedlistview.h index f4f3b55e..581d9f95 100644 --- a/lib/rdfeedlistview.h +++ b/rdadmin/feedlistview.h @@ -1,4 +1,4 @@ -// rdfeedlistview.h +// feedlistview.h // // RDTableView widget for RSS feeds // @@ -18,28 +18,29 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#ifndef RDFEEDLISTVIEW_H -#define RDFEEDLISTVIEW_H +#ifndef FEEDLISTVIEW_H +#define FEEDLISTVIEW_H #include #include #include +#include #include #include -#include "rdxsltengine.h" +#include -class RDFeedListView : public RDTableView +class FeedListView : public RDTableView { Q_OBJECT public: - RDFeedListView(QWidget *parent=0); - ~RDFeedListView(); + FeedListView(QWidget *parent=0); + ~FeedListView(); private slots: void aboutToShowMenuData(); - void generateFrontReportData(); - void generateBackReportData(); + void generateFrontItemReportData(); + void generateBackItemReportData(); protected: void mousePressEvent(QMouseEvent *e); @@ -47,10 +48,10 @@ class RDFeedListView : public RDTableView private: int d_mouse_row; QMenu *d_mouse_menu; - QAction *d_front_report_action; - QAction *d_back_report_action; - RDXsltEngine *d_xslt_engine; + QAction *d_front_item_report_action; + QAction *d_back_item_report_action; + QList d_xslt_engines; }; -#endif // RDFEEDLISTVIEW_H +#endif // FEEDLISTVIEW_H diff --git a/rdadmin/list_feeds.cpp b/rdadmin/list_feeds.cpp index ede08bdf..29bbcccb 100644 --- a/rdadmin/list_feeds.cpp +++ b/rdadmin/list_feeds.cpp @@ -2,7 +2,7 @@ // // List Rivendell Feeds // -// (C) Copyright 2002-2022 Fred Gleason +// (C) Copyright 2002-2023 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -98,7 +98,7 @@ ListFeeds::ListFeeds(QWidget *parent) // // Feed List // - list_feeds_view=new RDFeedListView(this); + list_feeds_view=new FeedListView(this); list_feeds_model=new RDFeedListModel(true,false,this); list_feeds_model->setFont(font()); list_feeds_model->setPalette(palette()); diff --git a/rdadmin/list_feeds.h b/rdadmin/list_feeds.h index d55a2032..4112f683 100644 --- a/rdadmin/list_feeds.h +++ b/rdadmin/list_feeds.h @@ -2,7 +2,7 @@ // // List Rivendell RSS Feeds // -// (C) Copyright 2002-2021 Fred Gleason +// (C) Copyright 2002-2023 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -25,9 +25,10 @@ #include #include -#include #include +#include + class ListFeeds : public RDDialog { Q_OBJECT @@ -52,7 +53,7 @@ class ListFeeds : public RDDialog private: QLabel *list_box_label; - RDFeedListView *list_feeds_view; + FeedListView *list_feeds_view; RDFeedListModel *list_feeds_model; QPushButton *list_add_button; QPushButton *list_edit_button; diff --git a/rdadmin/rdadmin.pro b/rdadmin/rdadmin.pro index ee39245d..48d273f1 100644 --- a/rdadmin/rdadmin.pro +++ b/rdadmin/rdadmin.pro @@ -2,7 +2,9 @@ # # The QMake project file for RDAdmin. # -# (C) Copyright 2003-2021 Fred Gleason +# (C) Copyright 2003-2023 Fred Gleason +# +# NB - This data is used only by Qt's i18n tools! # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -59,6 +61,7 @@ x11 { SOURCES += edit_user.cpp SOURCES += edit_user_perms.cpp SOURCES += edit_vguest_resource.cpp + SOURCES += feedlistview.cpp SOURCES += info_dialog.cpp SOURCES += license.cpp SOURCES += list_dropboxes.cpp @@ -129,6 +132,7 @@ x11 { HEADERS += edit_user.h HEADERS += edit_user_perms.h HEADERS += edit_vguest_resource.h + HEADERS += feedlistview.h HEADERS += info_dialog.h HEADERS += license.h HEADERS += list_dropboxes.h diff --git a/rdcastmanager/rdcastmanager.cpp b/rdcastmanager/rdcastmanager.cpp index c0ed8b2c..7934d50f 100644 --- a/rdcastmanager/rdcastmanager.cpp +++ b/rdcastmanager/rdcastmanager.cpp @@ -93,7 +93,7 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) // XSLT Engine (for feed reports) // cast_xslt_engine= - new RDXsltEngine("/usr/share/rivendell/rdcastmanager-report.xsl",this); + new RDXsltEngine("/usr/share/rivendell/rss-back-item-report.xsl",this); // // Feed List diff --git a/web/rdxport/podcasts.cpp b/web/rdxport/podcasts.cpp index 4246d30a..a29ade2d 100644 --- a/web/rdxport/podcasts.cpp +++ b/web/rdxport/podcasts.cpp @@ -2,7 +2,7 @@ // // Rivendell web service portal -- Podcast services // -// (C) Copyright 2010-2022 Fred Gleason +// (C) Copyright 2010-2023 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -369,11 +369,12 @@ bool Xport::PostRssElemental(RDFeed *feed,const QDateTime &now,QString *err_msg) CURLcode curl_err; char errstr[CURL_ERROR_SIZE]; bool ret=false; + bool ok=false; if((curl=curl_easy_init())==NULL) { XmlExit("unable to get CURL handle",500,"podcasts.cpp",LINE_NUMBER); } - xport_curl_data=feed->rssXml(err_msg,now).toUtf8(); + xport_curl_data=feed->rssXml(err_msg,now,&ok).toUtf8(); xport_curl_data_ptr=0; // diff --git a/web/stylesheets/Makefile.am b/web/stylesheets/Makefile.am index 58b37a98..20fd681c 100644 --- a/web/stylesheets/Makefile.am +++ b/web/stylesheets/Makefile.am @@ -2,7 +2,7 @@ ## ## Makefile.am for rivendell/web/stylesheets ## -## (C) Copyright 2022 Fred Gleason +## (C) Copyright 2022-2023 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as @@ -21,12 +21,18 @@ install-exec-am: mkdir -p $(DESTDIR)/usr/share/rivendell - cp rdcastmanager-report.xsl $(DESTDIR)/usr/share/rivendell/ + cp rss-item-enclosures.xsl $(DESTDIR)/usr/share/rivendell/ + cp rss-front-item-report.xsl $(DESTDIR)/usr/share/rivendell/ + cp rss-back-item-report.xsl $(DESTDIR)/usr/share/rivendell/ uninstall-local: - rm -f $(DESTDIR)/usr/share/rivendell/rdcastmanager-report.xsl + rm -f $(DESTDIR)/usr/share/rivendell/rss-item-enclosures.xsl + rm -f $(DESTDIR)/usr/share/rivendell/rss-front-item-report.xsl + rm -f $(DESTDIR)/usr/share/rivendell/rss-back-item-report.xsl -EXTRA_DIST = rdcastmanager-report.xsl +EXTRA_DIST = rss-front-item-report.xsl\ + rss-back-item-report.xsl\ + rss-item-enclosures.xsl CLEANFILES = *~ MAINTAINERCLEANFILES = *~\ diff --git a/web/stylesheets/rdcastmanager-report.xsl b/web/stylesheets/rss-back-item-report.xsl similarity index 83% rename from web/stylesheets/rdcastmanager-report.xsl rename to web/stylesheets/rss-back-item-report.xsl index db09c5ed..26f142a1 100644 --- a/web/stylesheets/rdcastmanager-report.xsl +++ b/web/stylesheets/rss-back-item-report.xsl @@ -1,7 +1,7 @@ - - + - <xsl:value-of select="/rss/channel/title"/> RSS Feed + + <xsl:value-of select="/rss/channel/title"/> Rivendell Back Item Report + @@ -31,7 +33,7 @@ body { background-color: darkgray; } - .rounded-block { + .active-rounded-block { background-color: white; width: 800px; border-style: solid; @@ -39,6 +41,14 @@ border-radius: 10px; margin-bottom: 5px; } + .missing-rounded-block { + background-color: red; + width: 800px; + border-style: solid; + border-width: 2px; + border-radius: 10px; + margin-bottom: 5px; + } .image { vertical-align: text-top; margin-left: 5px; @@ -70,17 +80,17 @@
- +
-

Rivendell Podcast Report

+

Rivendell Back Item Report

- +
@@ -117,7 +127,10 @@ - +
+ + +

diff --git a/web/stylesheets/rss-front-item-report.xsl b/web/stylesheets/rss-front-item-report.xsl new file mode 100644 index 00000000..a29ffcf4 --- /dev/null +++ b/web/stylesheets/rss-front-item-report.xsl @@ -0,0 +1,163 @@ + + + + + + + + + <xsl:value-of select="/rss/channel/title"/> Rivendell Front Item Report + + + + + + + +
+ + + + +
+

Rivendell Front Item Report

+
+
+
+ + + + + + +
+ + + + + + + + + + + + + +
+

+ + + + + + + + + +

+
+
+
+ + + + + + + + + + + + + + +

+ + + + + + + + + +

+ +

+ +
+
+
+ + +
+
diff --git a/web/stylesheets/rss-item-enclosures.xsl b/web/stylesheets/rss-item-enclosures.xsl new file mode 100644 index 00000000..c59dfea9 --- /dev/null +++ b/web/stylesheets/rss-item-enclosures.xsl @@ -0,0 +1,29 @@ + + + + + + + + | + + +