diff --git a/.gitignore b/.gitignore index d44a1ce7..d946a238 100644 --- a/.gitignore +++ b/.gitignore @@ -35,18 +35,26 @@ configure depcomp docs/apis/*.html docs/apis/*.pdf +docs/dtds/superfeed.html +docs/dtds/superfeed.pdf docs/opsguide/*.html docs/opsguide/*.pdf +docs/opsguide/blueball.png docs/opsguide/chain.png docs/opsguide/frontspiece.xml +docs/opsguide/greenball.png +docs/opsguide/greencheckmark.png docs/opsguide/mic16.png docs/opsguide/music.png docs/opsguide/notemarker.png docs/opsguide/play.png +docs/opsguide/redball.png +docs/opsguide/redx.png docs/opsguide/rml5.png docs/opsguide/track_cart.png docs/opsguide/traffic.png docs/opsguide/trashcan-32x32.png +docs/opsguide/whiteball.png docs/rivwebcapi/*.7 docs/rivwebcapi/*.html docs/rivwebcapi/*.pdf @@ -93,6 +101,7 @@ rdrepld/rdrepld rdrepld-suse rdselect/rdselect rdservice/rdservice +rdrssd/rdrssd rdvairplayd/rdvairplayd ripcd/ripcd rivendell @@ -109,7 +118,9 @@ tests/cmdline_parser_test tests/datedecode_test tests/dateparse_test tests/db_charset_test +tests/delete_test tests/download_test +tests/feed_image_test tests/getpids_test tests/log_unlink_test tests/mcast_recv_test @@ -161,7 +172,6 @@ utils/rdselect_helper/rdprofile.cpp utils/rdselect_helper/rdprofile.h utils/rdselect_helper/rdselect_helper utils/sas_shim/sas_shim -web/rdfeed/rdfeed.xml xdg/rdalsaconfig-root-consolehelper xdg/rddbconfig-root-consolehelper xdg/rivendell-opsguide-html.desktop diff --git a/ChangeLog b/ChangeLog index acd3e78d..655ca959 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19534,6 +19534,15 @@ * Cleaned up a compiler warning in 'lib/rdcae.cpp'. 2020-02-07 Fred Gleason * Incremented the package version to 3.2.1int1. +2020-02-07 Fred Gleason + * Removed Q3Support dependencies from the 'Editing Feed' dialog + in rdadmin(1). +2020-02-07 Fred Gleason + * Changed the default character encoding for podcasts from + 'ISO-8859-1' to 'UTF-8'. +2020-02-10 Fred Gleason + * Cleaned up UI alignment in the 'Rivendell Feed List' dialog in + rdadmin(1). 2020-02-14 Fred Gleason * Fixed a regression in the BroadcastTools SS16.4 switcher driver that could cause deadlocks and intermittent operation. @@ -19668,6 +19677,8 @@ 2020-02-21 Fred Gleason * Added a syslog message for each CIC update in the 'pypad_xds.py' script. +2020-02-23 Fred Gleason + * Implemented superfeed in 'rdfeed.xml'. 2020-02-23 Fred Gleason * Added '-lexpat' to the '--libs' output for the 'rivwebcapi' pkg-config profile. @@ -19733,8 +19744,22 @@ * Fixed a bug in rddbmgr(8) that threw a SQL error when attempting to modify a DB containing tables set to use character set UTF-8 to schema 297. +2020-03-10 Fred Gleason + * Added a '%FEED_URL%' variable to the channel and item data + resolver for RSS feeds. + * Added an '' tag to the XML templates for RSS feeds. +2020-03-10 Fred Gleason + * Fixed a typo in the default channel XML template for RSS feeds. +2020-03-10 Fred Gleason + * Cleaned up formatting of the RSS feeds in rdadmin(1)'s 'List Feeds' + dialog. +2020-03-10 Fred Gleason + * Removed the 'Source URL' and 'Source Text' controls from the + 'Edit Cast' dialog in rdcastmanager(1). 2020-03-12 Fred Gleason - * Incremented the package version to v3.2.1int4. + * Fixed a bug where '' tags in RSS Superfeeds were rendered + with an incorrect 'url=' attribute. + * Added indentation formatting on RSS XML feeds. 2020-03-12 Fred Gleason * Fixed a race in ripcd(8) that caused arguments to the 'Run Shell Command' ['RN'] macro to be corrupted. @@ -19743,8 +19768,6 @@ that caused the Transition Type dropdown to be greyed-out when the 'IMPORT' controls were set to use the built-in music scheduler. -2020-03-16 Fred Gleason - * Incremented the package version to v3.3.0. 2020-03-18 Fred Gleason * Fixed a regression in rdairplay(1) that caused two events to be started by a single spacebar tap if a SoundPanel event had been @@ -19836,8 +19859,147 @@ 2020-05-12 Fred Gleason * Fixed a regression that broke the ability to scroll through Sound Panel panels by means of the mouse wheel. -2020-05-19 Fred Gleason - * Incremented the package version to 3.4.0. +2020-05-12 Fred Gleason + * Rebased DB schema changes 315-320 from the 'rss' branch + to be 318-323. +2020-05-12 Fred Gleason + * Added a 'FEEDS.IS_SUPERFEED' field to the database. + * Added a 'SUPERFEED_MAPS' table to the database. + * Incremented the database version to 318. + * Added a 'Superfeed' column to the feed list in the 'Rivendell + Feed List' dialog in rdadmin(1). + * Added an 'Is Superfeed' dropdown to the 'Feed' dialog in + rdadmin(1). + * Added a 'Select Member Feeds' button to the 'Feed' dialog in + rdadmin(1). + * Added an 'RSS Superfeed' selection dialog to rdadmin(1). +2020-05-12 Fred Gleason + * Added an 'RDFeed::create()' method. + * Added a 'Base Feed On' dropdown to the 'Add RSS Feed' dialog + in rdadmin(1). +2020-05-12 Fred Gleason + * Added a check to the 'Add RSS Feed' dialog in rdadmin(1) to + prevent creation of feeds with a blank name. +2020-05-12 Fred Gleason + * Added a 'FEEDS.AUDIENCE_METRICS' field to the database. + * Incremented the database version to 319. + * Added a 'Collect Audience Metrics' checkbox to the 'Feed' + dialog in rdadmin(1). + * Added an 'RDTransfer' base class. + * Rebased 'RDDelete' to 'RDTransfer'. + * Added 'sftp' support to 'RDDelete'. + * Added 'file' support to 'RDDelete'. + * Refactored RSS generation to occur in 'RDFeed'. + * Added a 'delete_test' test harness. +2020-05-12 Fred Gleason + * Rebased 'RDUpload' to 'RDTransfer'. +2020-05-12 Fred Gleason + * Rebased 'RDDownload' to 'RDTransfer'. +2020-05-12 Fred Gleason + * Updated the URL scheme check code in the 'Edit Feed' dialog + in rdadmin(1). +2020-05-12 Fred Gleason + * Added a 'USERS.EMAIL_ADDRESS' field to the database. + * Incremented the database version to 320. + * Added 'RDUser::emailAddress()', 'RDUser::setEmaiAddress()' and + 'RDUser::emailContact()' methods. + * Added 'RDUser::emailIsValid()' and 'RDUser::emailContact()' + static methods. + * Added logic to rdcastmanager(1) to insert the author's e-mail + contact automatically when posting a new episode. +2020-05-12 Fred Gleason + * Tweaked controls to be disabled for superfeeds in the 'Edit Feed' + dialog in rdadmin(1). +2020-05-12 Fred Gleason + * Updated the maximum length of RSS entries to match + the current DB schema in the 'Edit Feed' dialog in rdadmin(1). + * Added a 'FEEDS.CHANNEL_EDITOR' field to the database. + * Incremented the database version to 321. + * Added 'RDFeed::channelEditor()' and + 'RDFeed::setChannelEditor()' methods. + * Added an 'Editor' control to the 'Channel' controls in the + 'Edit Feed' dialog in rdadmin(1). +2020-05-12 Fred Gleason + * Added an 'RSS_SCHEMAS' table to the database. + * Incremented the database version to 322. + * Added an 'RSS Schema' dropdown to the 'Edit Feed' dialog in + rdadmin(1). +2020-05-12 Fred Gleason + * Added a 'FEED_IMAGES' table to the database. + * Incremented the database version to 323. + * Added 'feed_image_test' in 'tests/'. +2020-05-17 Fred Gleason + * Added a 'FEEDS.CHANNEL_IMAGE_ID' field to the database. + * Added a 'FEEDS.DEFAULT_ITEM_IMAGE_ID' field to the database. + * Added a 'PODCASTS.ITEM_IMAGE_ID' field to the database. + * Incremented the database version to 324. + * Added an 'Image Manager' dialog to rdadmin(1). + * Added an 'Image Viewer' dialog on rdadmin(1). +2020-05-17 Fred Gleason + * Dropped the 'RSS_SCHEMAS' table from the database. + * Incremented the database version to 325. + * Moved RSS template definitions to 'lib/rdfeed.h'. +2020-05-17 Fred Gleason + * Updated interlocking logic in the 'Edit Feed' dialog in + rdadmin(1) to handle image assignments correctly. +2020-05-18 Fred Gleason + * Added 'RDRssSchemas' class. +2020-05-18 Fred Gleason + * Added code to remove remote imaging when deleting an RSS feed. +2020-05-20 Fred Gleason + * Added 'FEEDS.CHANNEL_AUTHOR', 'FEEDS.CHANNEL_OWNER_NAME', + 'FEEDS.CHANNEL_OWNER_EMAIL', 'FEEDS.CHANNEL_EXPLICIT' and + 'PODCASTS.ITEM_EXPLICIT' fields to the database. + * Incremented the database version to 326. + * Added 'RDFeed::channelAuthor()', 'RDFeed::setChannelAuthor()', + 'RDFeed::channelOwnerName()', 'RDFeed::setChannelOwnerName()', + 'RDFeed::channelOwnerEmail(), 'RDFeed::setChannelOwnerEmail()', + 'RDFeed::channelExplicit()', 'RDFeed::setChannelExplicit()', + 'RDPodcast::itemExplicit()' and 'RDPodcast::setItemExplicit()' + methods. + * Added 'Author', Owner Name' and 'Owner E-Mail controls to the + 'Edit Feed' dialog box in rdadmin(1). + * Added a 'Channel contains explicit content' checkbox to the + 'Edit Feed' dialog box in rdadmin(1). +2020-05-20 Fred Gleason + * Added 'RDPodcast::itemExplicit()', 'RDPodcast::setItemExplicit()', + 'RDPodcast::itemImageId()', and 'RDPodcast::setItemImageId()' + methods. + * Added a 'Post contains explicit content' checkbox to the 'Edit + Podcast' dialog in rdcastmanager(1). + * Added an 'Image' control to the 'Edit Podcast dialog in + rdcastmanager(1). +2020-05-21 Fred Gleason + * Populated the 'Apple' RSS schema. +2020-05-21 Fred Gleason + * Fixed a bug in the 'Edit Feed' dialog in rdadmin(1) that allowed + superfeed setups to be saved with no member subfeeds. + * Fixed a bug in the 'Edit Feed' dialog in rdadmin(1) that allowed + a per-item default image to be configured for superfeeds. +2020-05-21 Fred Gleason + * Added a file-type field to the image attributes line in rdadmin(1). +2020-05-21 Fred Gleason + * Added image size and type data to the 'View Image' dialog in + rdadmin(1). +2020-05-21 Fred Gleason + * Added a check to verify correct size of imported images in + 'RDFeed::importImageFile()'. +2020-05-21 Fred Gleason + * Fixed bugs in the podcasting system that caused corrupt URLs + to be generated for GUID and image tags for items contained in + a superfeed. + * Commented out file posting code in the web version of + RDCastManager. + * Disabled the 'Post from Cart/Cut', 'Post from File', 'Edit' and + 'Delete' buttons when showing a superfeed in the 'Podcast List' + dialog of rdcastmanager(1). +2020-05-21 Fred Gleason + * Added a 'URL' field to the 'View Image' dialog in rdadmin(1). +2020-05-21 Fred Gleason + * Added '' tags to the 'Apple' RSS schema. + * Added '' tags to the 'Apple' RSS schema. +2020-06-04 Fred Gleason + * Added an 'iTunes + Superfeed' RSS schema. 2020-05-22 Fred Gleason * Added 'RDListView::NumericSort' to the 'RDListView::SortType' enumeration. @@ -19912,14 +20074,77 @@ 2020-06-23 Fred Gleason * Applied the RD_MAX_CART_NUMBER define in 'rdairplay/local_macros.cpp'. +2020-06-30 Fred Gleason + * Added a 'RDFeed::publicUrl()' static method. + * Added a 'Public URL' column in the 'List Feeds' dialog in + rdadmin(1). + * Removed the 'Superfeed' and 'Description' columns from the + main window of rdcastmanager(1). + * Added a 'Public URL' column and a 'Copy URL to Clipboard' + button to the main window of rdcastmanager(1). +2020-06-30 Fred Gleason + * Removed the 'exemplar' parameter from the 'RDFeed::create()' + method. + * Removed the 'Base Feed On' dropdown from the 'Add Feed' + dialog in rdadmin(1). +2020-07-02 Fred Gleason + * Fixed a bug in rdadmin(1) that caused the 'Public URL' column in + the 'List Feeds' dialog to display incorrect data after editing + a feed. +2020-07-02 Fred Gleason + * Added a 'FEEDS.CHANNEL_SUB_CATEGORY' field to the database. + * Incremented the database version to 327. + * Added 'RDFeed::channelSubCategory()' and + 'RDFeed::setChannelSubCategory()' methods. + * Added a 'RDRssCategoryBox' widget. + * Updated the 'Edit Feed' dialog in rdadmin(1) to use the + 'RDRssCategoryBox' widget. +2020-07-02 Fred Gleason + * Added support for iTunes subcategories to the podcast subsystem. +2020-07-02 Fred Gleason + * Added a 'RDRssSchemas::supportsItemCategories()' method. +2020-07-02 Fred Gleason + * Added a 'RDApplication::rssSchemas()' method. + * Removed the 'RDFeed::rssSchemas()' method. +2020-07-03 Fred Gleason + * Moved the relocated the 'Comment URL' control in the + 'Editing Podcast' dialog in rdcastmanager(1). + * Made the 'Category' control conditional in the 'Editing Podcast' + dialog in rdcastmanager(1). +2020-07-03 Fred Gleason + * Dropped the 'FEEDS.AUDIENCE_METRICS' field from the database. + * Dropped the 'FEEDS.KEEP_METADATA' field from the database. + * Dropped the 'FEEDS.MEDIA_LINK_MODE' field from the database. + * Dropped the 'FEEDS.REDIRECT_PATH' field from the database. + * Dropped the 'CAST_DOWNLOADS' table from the database. + * Incremented the database version to 328. + * Removed support for dynamically generated RSS feed metadata. +2020-07-07 Fred Gleason + * Added 'Copy to Clipboard' buttons for the XML template editors + in the 'Edit Feed' dialog in rdadmin(1). 2020-07-07 Fred Gleason * Modified 'RDWaveFile' to work around known fencepost errors in WAV files generated by "Sonic Studio soundBlade" by "PME Mastering, Inc." 2020-07-07 Fred Gleason * Incremented the package version to 3.4.0int1. +<<<<<<< HEAD +2020-07-08 Fred Gleason + * Modified the layout of the 'Editing PodCast' dialog in + rdcastmanager(1) to more consistent with the event editing + dialogs in rdcatch(1). 2020-07-21 Fred Gleason * Incremented the package version to 3.4.1. +2020-08-02 Fred Gleason + * Added a 'Post from Log' button to the 'Podcast List' dialog + in rdcastmanager(1). +2020-08-04 Fred Gleason + * Fixed a fencepost bug in the 'RDRenderer::renderToFile()' method + with the 'last_line' parameter. +2020-08-04 Fred Gleason + * Added an 'RDLogIcons' class. +2020-08-04 Fred Gleason + * Added a 'Log Render Options' dialog to rdcastmanager(1). 2020-08-05 Fred Gleason * Added an 'RDDataPacer' class. * Modified the GVC7000 switcher driver to insert 50 mS pauses @@ -19933,9 +20158,56 @@ 2020-08-05 Patrick Linstruth * Fixed regression with "Scheduler Code:" label overlapping "Group" combobox in rdlibrary(1). -2020-06-23 Fred Gleason - * Applied the RD_MAX_CART_NUMBER define in - 'rdairplay/local_macros.cpp'. +2020-08-07 Fred Gleason + * Added a 'SYSTEM.RSS_PROCESSOR_STATION' field to the database. + * Added a 'PODCASTS.EXPIRATION_DATETIME' field to the database. + * Dropped the 'PODCASTS.SHELF_LIFE' field from the database. + * Incremented the database version to 329. + * Modified convention to use local system time for all podcast + components. + * Added 'RDPodcast::expirationDateTime()' and + 'RDPodcast::setExpirationDateTime()' methods. + * Removed 'RDPodcast::shelfLife()' and 'RDPodcast::setShelfLife()' + methods. + * Added an rdrssd(8) daemon. + * Removed the rdpurgecasts(8) utility. + * Added 'RDSystem::rssProcessorStation()' and + 'RDSystem::setRssProcessorStation()' methods. + * Added a 'Process RSS Updates' control to the 'System-Wide Settings' + dialog in rdadmin(1). +2020-08-08 Fred Gleason + * Changed 'Cast' to 'Item' in rdcastmanager(1). +2020-08-08 Fred Gleason + * Modified rdcastmanager(1) to apply the cart title as the default + item title when posting from a cart/cut. + * Modified rdcastmanager(1) to apply the file metadata or filename + as the default item title when posting from a file. +2020-08-08 Fred Gleason + * Added a 'PODCASTS.ORIGIN_LOGIN_NAME' field to the database. + * Added a 'PODCASTS.ORIGIN_STATION' field to the database. + * Incremented the database version to 330. + * Changed the 'Origin' column to 'Start' in the 'Podcast Item List' + dialog in rdcastmanager(1). + * Added a 'Posted By' column to the 'Podcast Item List' dialog in + rdcastmanager(1). +2020-08-08 Fred Gleason + * Implemented multicast notifications for RSS feed items. +2020-08-09 Fred Gleason + * Fixed a regression in rdcastmanager(1) that caused no items to + appear in superfeeds. +2020-08-09 Fred Gleason + * Modified the 'Podcast Item List' dialog in rdcastmanager(1) to + show a blue icon for items with Air Dates in the future. +2020-08-09 Fred Gleason + * Fixed a bug in the 'Podcast Item List' dialog in rdcastmanager(1) + That caused incorrect data to appear in the 'Posted By' column. +2020-08-09 Fred Gleason + * Removed the 'Description' column from the 'Podcast Item List' + dialog in rdcastmanager(1). +2020-08-09 Fred Gleason + * Fixed a bug in the 'Log Render Options' dialog in rdcastmanager(1) + that could cause the start time control to be disabled even with + the Virtual Start Time dropdown set to 'As Specified'. 2020-08-10 Brian McGlynn * Allow RN Macro to run when Rivendell daemons running as non-root 'ripcd/local_macros.cpp'. @@ -19946,6 +20218,171 @@ service with a name containing whitespace. * Added whitespace to the set of banned characters for service names in the 'Add Service' dialog in rdadmin(1). +2020-08-11 Fred Gleason + * Fixed a bug in rdcastmanager(1) that caused a segfault when + attempting to post a log with no events selected. +2020-08-11 Fred Gleason + * Dropped the 'ENCODERS', 'ENCODER_BITRATES', 'ENCODER_BITRATES' + and 'ENCODER_CHANNELS' tables from the database. + * Incremented the database version to 331. + * Removed the 'RDSettings::customCommandLine()', + 'RDSettings::setCustomCommandLine()' and + ' RDSettings::resolvedCustomCommandLine()' methods. +2020-08-11 Fred Gleason + * Added 'RDRssSchemas::itemLinksSupported()' and + 'RDRssSchemas::itemCommentsSupported()' methods. + * Modified the 'Editing Item' dialog in rdcastmanager(1) to + conditionally display the 'Link' and 'Comments' controls. +2020-08-11 Fred Gleason + * Added a 'FEEDS.CHANNEL_AUTHOR_IS_DEFAULT' field to the database. + * Incremented the database version to 332. + * Added 'RDFeed::channelAuthorIsDefault()' and + 'RDFeed::setChannelAuthorIsDefault()' methods. + * Added a 'Use as default Item Author' checkbox to the 'Edit Feed' + dialog in rdadmin(1). +2020-08-12 Fred Gleason + * Added 'RDFontEngine::bigLabelFont()' and + 'RDFontEngine::bigLabelFontMetrics()' methods. +2020-08-12 Fred Gleason + * Added a feed name label to the 'Podcast Item List' in + rdcastmanager(1). +2020-08-13 Fred Gleason + * Documented the 'RDFontEngine::bigLabelFont()' method. +2020-08-13 Fred Gleason + * Changed the name of the 'List Log' dialog in rdcastmanager(1) + to 'List Log Events'. +2020-08-13 Fred Gleason + * Changed the word 'podcast' to 'item' in the item deletion + confirmation in the 'Podcast Item List' dialog in rdcastmanager(1). +2020-08-13 Fred Gleason + * Titled the audio selector dialog in rdcastmanager(1) + 'Select Audio File'. +2020-08-13 Fred Gleason + * Removed the 'Unly Show Unexpired Items' checkbox from the + 'Podcast Feed Item List' dialog in rdcastmanager(1). +2020-08-13 Fred Gleason + * Added an 'RDCastManager' chapter to the Operations Guide. +2020-08-19 Fred Gleason + * Added a 'STATIONS.SSH_IDENTITY_FILE' field to the database. + * Added a 'RECORDINGS.URL_USE_ID_FILE' field to the database. + * Added a 'FEEDS.PURGE_USE_ID_FILE' field to the database. + * Incremented the database version to 333. + * Added 'RDStation::sshIdentityFile()' and + 'RDStation::setSshIdentityFile()' methods. + * Added 'RDRecording::urlUseIdFile()' and + 'RDRecording::setUrlUseIdFile()' methods. + * Added 'RDFeed::purgeUseIdFile()' and 'RDFeed::setPurgeUseIdFile()' + methods. + * Added a 'SSH Ident. File' control to the 'Host' dialog in + rdadmin(1). + * Added an 'Authenticate with local identity file' checkbox to + the 'Feed' dialog in rdadmin(1). +2020-08-19 Fred Gleason + * Changed the database field 'FEEDS.PURGE_PASSWORD' from + 'varchar(64)' to 'text'. + * Changed the contents of the 'FEEDS.PURGE_PASSWORD' database + field from plaintext to Base64 encoding. + * Incremented the database version to 334. +2020-08-19 Fred Gleason + * Fixed compiler warnings in 'lib/rdsettings.cpp' and + 'lib/rdstation.cpp'. +2020-08-19 David Klann + * Added support for FTPS protocol for uploading, downloading, and + Feed management. +2020-08-28 Fred Gleason + * Added sanity checks for Air and Expiration datetimes to the + 'Editing Item' dialog in rdcastmanager(1). +2020-08-28 Fred Gleason + * Added an 'RDNotification::FeedType' value to the + 'RDNotification::Type' enumeration. +2020-09-17 Fred Gleason + * Added an 'RDUser::feedAuthorized()' method. + * Added a 'SavePodcast' method to the Web API. + * Added a 'GetPodcast' method to the Web API. + * Added a 'DeletePodcast' method to the Web API. +2020-09-21 Fred Gleason + * Added a 'PODCASTS.SHA1_HASH' field to the database. + * Incremented the database version to 335. + * Added 'RDPodcast::sha1Hash()' and 'RDPodcast::setSha1Hash()' + methods. + * Implemented audio relinking for podcast media files. +2020-09-21 Fred Gleason + * Added a 'SHA1' column to the 'Podcast Item List' dialog in + rdcastmanager(1). +2020-09-21 Fred Gleason + * Added a 'PostPodcast' method to the Web API. + * Added a 'RemovePodcast' method to the Web API. + * Added a 'PostRss' method to the Web API. + * Added a 'RemoveRss' method to the Web API. +2020-09-21 Fred Gleason + * Modified the type of the 'FEED_IMAGES.DATA' database field + to 'longblob'. + * Incremented the database version to 336. +2020-09-22 Fred Gleason + * Added a 'PostImage' method to the Web API. + * Added a 'RemoveImage' method to the Web API. +2020-09-22 Fred Gleason + * Added 'FEEDS.CHANNEL_IMAGE_ID_IDX', 'FEEDS.DEFAULT_ITEM_IMAGE_ID_IDX' + and 'PODCASTS.ITEM_IMAGE_ID_IDX' indexes to the database. + * Incremented the database version to 337. + * Added a sanity check to the 'Image Manager' dialog in + rdadmin(1) to prevent deletion of in-use images. +2020-09-22 Fred Gleason + * Renamed the 'RDFeed::deleteImages()' method to + 'RDFeed::removeAllImages()'. +2020-09-22 Fred Gleason + * Implemented the 'Repost' button on the 'Rivendell Feed List' + dialog in rdadmin(1). + * Added an 'Unpost' button to the 'Rivendell Feed List' dialog + in rdadmin(1). +2020-09-22 Fred Gleason + * Replaced the feed summary status icon with the feed image in the + feed list in rdcastmanager(1). + * Moved the post status icon to a new 'Status' column in the item + list on the 'Podcast Item List' dialog in rdcastmanager(1). + * Added the item icon to the start of each entry in the item list + on the 'Podcast Item List' dialog in rdcastmanager(1). +2020-09-22 Fred Gleason + * Added the feed image to the start of each entry in the feed list + on the 'Rivendell Feed List' dialog in rdadmin(1). +2020-09-22 Fred Gleason + * Fixed bugs in rdcastmanager(1) that caused incorrect feed and + item icons to be displayed in lists. +2020-09-22 Fred Gleason + * Updated screenshots in the 'RDCastManager' chapter of the + Operations Guide. +2020-09-23 Fred Gleason + * Added 'RD_PostImage()' and 'RD_RemoveImage()' calls to the + rivwebcapi API. +2020-09-23 Fred Gleason + * Added 'RD_PostRss()' and 'RD_RemoveRss()' calls to the + rivwebcapi API. +2020-09-23 Fred Gleason + * Added 'RD_PostPodcast()', 'RDRemovePodcast()' and + 'RD_SavePodcast()' calls to the rivwebcapi API. +2020-09-23 Fred Gleason + * Added a 'RD_DeletePodcast()' call to the rivwebcapi API. +2020-09-23 Fred Gleason + * Fixed a regression in rdxport.cgi(1) that caused segfaults when + processing the 'PostRss' for feeds enclosed by one or more + superfeeds. +2020-09-23 Fred Gleason + * Fixed a bug in rdcastmanager(1) that made it possible to edit + underlying posts when viewing a superfeed. +2020-09-29 Fred Gleason + * Added debugging messages to syslog for the podcasting subsystem. +2020-09-29 Fred Gleason + * Added the name of the Rivendell library component to 'Unknown Error' + messages. +2020-09-29 Fred Gleason + * Fixed a bug in 'RDFeed::postCut()' that could throw an intermittent + 'Unknown Error' message. +2020-09-30 Fred Gleason + * Changed the 'FEEDS.CHANNEL_LANGUAGE' database field from + 'varchar(5)' to' 'varchar(8)'. + * Incremented the database version to 338. + * Increased the 5 character length limitation of the + 'Channel Language' field for podcasts to 8. 2020-10-01 Fred Gleason * Fixed a bug in the 'Test Import' dialog in rdadmin(1) that caused the sort of events in the 'Imported Events' list to be incorrect. @@ -19958,5 +20395,8 @@ caused the Services List to be initialized with all services upon creation, even when one or more services had already be added to the list by the user. -2020-10-06 Patrick Linstruth +2020-10-07 Fred Gleason + * Changed the predicted next release version from '4.0' to '3.5'. + in rddbmgr(8). +2020-10-07 Patrick Linstruth * Added '--scheduler-code=' switch to rdexport(1). diff --git a/Makefile.am b/Makefile.am index dd4e2be5..58b98e60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,6 +52,7 @@ SUBDIRS = icons\ rdrepld\ rdpadd\ rdpadengined\ + rdrssd\ rdselect\ rdservice\ rdvairplayd\ diff --git a/apis/rivwebcapi/rivwebcapi/Makefile.am b/apis/rivwebcapi/rivwebcapi/Makefile.am index ac7f8b3c..2bc30141 100644 --- a/apis/rivwebcapi/rivwebcapi/Makefile.am +++ b/apis/rivwebcapi/rivwebcapi/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am ## -## (C) Copyright 2015-2018 Fred Gleason +## (C) Copyright 2015-2020 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 @@ -31,6 +31,7 @@ dist_librivwebcapi_la_SOURCES = rd_addcart.c rd_addcart.h \ rd_createticket.c rd_createticket.h \ rd_deleteaudio.c rd_deleteaudio.h \ rd_deletelog.c rd_deletelog.h \ + rd_deletepodcast.c rd_deletepodcast.h \ rd_editcart.c rd_editcart.h \ rd_editcut.c rd_editcut.h \ rd_export.c rd_export.h \ @@ -52,9 +53,16 @@ dist_librivwebcapi_la_SOURCES = rd_addcart.c rd_addcart.h \ rd_listservices.c rd_listservices.h \ rd_listschedcodes.c rd_listschedcodes.h \ rd_listsystemsettings.c rd_listsystemsettings.h \ + rd_postimage.c rd_postimage.h \ + rd_postpodcast.c rd_postpodcast.h \ + rd_postrss.c rd_postrss.h \ rd_removecart.c rd_removecart.h \ rd_removecut.c rd_removecut.h \ + rd_removeimage.c rd_removeimage.h \ + rd_removepodcast.c rd_removepodcast.h \ + rd_removerss.c rd_removerss.h \ rd_savelog.c rd_savelog.h \ + rd_savepodcast.c rd_savepodcast.h \ rd_trimaudio.c rd_trimaudio.h \ rd_unassignschedcode.c rd_unassignschedcode.h @@ -74,6 +82,7 @@ include_HEADERS = rd_addcart.h\ rd_cut.h\ rd_deleteaudio.h\ rd_deletelog.h\ + rd_deletepodcast.h\ rd_editcart.h\ rd_editcut.h\ rd_export.h\ @@ -96,10 +105,17 @@ include_HEADERS = rd_addcart.h\ rd_listschedcodes.h\ rd_listservices.h\ rd_listsystemsettings.h \ + rd_postimage.h \ + rd_postpodcast.h \ + rd_postrss.h \ rd_savelog.h\ + rd_savepodcast.h\ rd_schedcodes.h\ rd_removecart.h\ rd_removecut.h\ + rd_removeimage.h\ + rd_removepodcast.h\ + rd_removerss.h\ rd_trimaudio.h\ rd_unassignschedcode.h diff --git a/apis/rivwebcapi/rivwebcapi/rd_deletepodcast.c b/apis/rivwebcapi/rivwebcapi/rd_deletepodcast.c new file mode 100644 index 00000000..1c5a8af1 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_deletepodcast.c @@ -0,0 +1,197 @@ +/* rd_deletepodcast.c + * + * Implementation of the Remove RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_deletepodcast.h" +#include "rd_common.h" +#include "rd_getuseragent.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __DeletePodcastElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __DeletePodcastElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __DeletePodcastElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __DeletePodcastCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + +int RD_DeletePodcast( const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + char id_buffer[21]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__DeletePodcastElementStart, + __DeletePodcastElementEnd); + XML_SetCharacterDataHandler(parser,__DeletePodcastElementData); + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "39", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + snprintf(id_buffer,21,"%u",cast_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__DeletePodcastCallback); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + // curl_easy_setopt(curl,CURLOPT_VERBOSE,1); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + curl_formfree(first); + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + + if (response_code > 199 && response_code < 300) { //Success + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_deletepodcast Call Returned Error: %s\n",xml_data.strbuf); + #endif + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_deletepodcast.h b/apis/rivwebcapi/rivwebcapi/rd_deletepodcast.h new file mode 100644 index 00000000..6c3d069d --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_deletepodcast.h @@ -0,0 +1,39 @@ +/* rd_deletepodcast.h + * + * Header for the Delete RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_DELETEPODCAST_H +#define RD_DELETEPODCAST_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_DeletePodcast(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_DELETEPODCAST_H diff --git a/apis/rivwebcapi/rivwebcapi/rd_postimage.c b/apis/rivwebcapi/rivwebcapi/rd_postimage.c new file mode 100644 index 00000000..dd972138 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_postimage.c @@ -0,0 +1,197 @@ +/* rd_postimage.c + * + * Implementation of the Post Image Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_postimage.h" +#include "rd_common.h" +#include "rd_getuseragent.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __PostImageElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __PostImageElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __PostImageElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __PostImageCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + +int RD_PostImage( const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned img_id, + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + char id_buffer[21]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__PostImageElementStart, + __PostImageElementEnd); + XML_SetCharacterDataHandler(parser,__PostImageElementData); + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "44", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + snprintf(id_buffer,21,"%u",img_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__PostImageCallback); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + // curl_easy_setopt(curl,CURLOPT_VERBOSE,1); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + curl_formfree(first); + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + + if (response_code > 199 && response_code < 300) { //Success + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_postimage Call Returned Error: %s\n",xml_data.strbuf); + #endif + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_postimage.h b/apis/rivwebcapi/rivwebcapi/rd_postimage.h new file mode 100644 index 00000000..821fb456 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_postimage.h @@ -0,0 +1,39 @@ +/* rd_postimage.h + * + * Header for the Post Image Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_POSTIMAGE_H +#define RD_POSTIMAGE_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_PostImage(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned img_id, + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_POSTIMAGE_H diff --git a/apis/rivwebcapi/rivwebcapi/rd_postpodcast.c b/apis/rivwebcapi/rivwebcapi/rd_postpodcast.c new file mode 100644 index 00000000..0576319a --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_postpodcast.c @@ -0,0 +1,197 @@ +/* rd_podpodcast.c + * + * Implementation of the Post RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_postpodcast.h" +#include "rd_common.h" +#include "rd_getuseragent.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __PostPodcastElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __PostPodcastElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __PostPodcastElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __PostPodcastCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + +int RD_PostPodcast( const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + char id_buffer[21]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__PostPodcastElementStart, + __PostPodcastElementEnd); + XML_SetCharacterDataHandler(parser,__PostPodcastElementData); + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "40", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + snprintf(id_buffer,21,"%u",cast_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__PostPodcastCallback); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + // curl_easy_setopt(curl,CURLOPT_VERBOSE,1); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + curl_formfree(first); + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + + if (response_code > 199 && response_code < 300) { //Success + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_postpodcast Call Returned Error: %s\n",xml_data.strbuf); + #endif + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_postpodcast.h b/apis/rivwebcapi/rivwebcapi/rd_postpodcast.h new file mode 100644 index 00000000..bc440402 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_postpodcast.h @@ -0,0 +1,39 @@ +/* rd_podpodcast.h + * + * Header for the Post RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_PODPODCAST_H +#define RD_PODPODCAST_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_PostPodcast(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_PODPODCAST_H diff --git a/apis/rivwebcapi/rivwebcapi/rd_postrss.c b/apis/rivwebcapi/rivwebcapi/rd_postrss.c new file mode 100644 index 00000000..be38c507 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_postrss.c @@ -0,0 +1,197 @@ +/* rd_postrss.c + * + * Implementation of the Post Image Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_postrss.h" +#include "rd_common.h" +#include "rd_getuseragent.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __PostRssElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __PostRssElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __PostRssElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __PostRssCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + +int RD_PostRss( const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned feed_id, + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + char id_buffer[21]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__PostRssElementStart, + __PostRssElementEnd); + XML_SetCharacterDataHandler(parser,__PostRssElementData); + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "42", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + snprintf(id_buffer,21,"%u",feed_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__PostRssCallback); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + // curl_easy_setopt(curl,CURLOPT_VERBOSE,1); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + curl_formfree(first); + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + + if (response_code > 199 && response_code < 300) { //Success + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_postrss Call Returned Error: %s\n",xml_data.strbuf); + #endif + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_postrss.h b/apis/rivwebcapi/rivwebcapi/rd_postrss.h new file mode 100644 index 00000000..9d3a9bd9 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_postrss.h @@ -0,0 +1,39 @@ +/* rd_postrss.h + * + * Header for the Post RSS XML Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_POSTRSS_H +#define RD_POSTRSS_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_PostRss(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned feed_id, + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_POSTRSS_H diff --git a/apis/rivwebcapi/rivwebcapi/rd_removeimage.c b/apis/rivwebcapi/rivwebcapi/rd_removeimage.c new file mode 100644 index 00000000..ff9b56e2 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_removeimage.c @@ -0,0 +1,197 @@ +/* rd_removeimage.c + * + * Implementation of the Remove Image Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_removeimage.h" +#include "rd_common.h" +#include "rd_getuseragent.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __RemoveImageElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __RemoveImageElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __RemoveImageElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __RemoveImageCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + +int RD_RemoveImage( const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned img_id, + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + char id_buffer[21]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__RemoveImageElementStart, + __RemoveImageElementEnd); + XML_SetCharacterDataHandler(parser,__RemoveImageElementData); + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "45", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + snprintf(id_buffer,21,"%u",img_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__RemoveImageCallback); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + // curl_easy_setopt(curl,CURLOPT_VERBOSE,1); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + curl_formfree(first); + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + + if (response_code > 199 && response_code < 300) { //Success + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_removeimage Call Returned Error: %s\n",xml_data.strbuf); + #endif + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_removeimage.h b/apis/rivwebcapi/rivwebcapi/rd_removeimage.h new file mode 100644 index 00000000..d8b59f69 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_removeimage.h @@ -0,0 +1,39 @@ +/* rd_removeimage.h + * + * Header for the Remove Image Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_REMOVEIMAGE_H +#define RD_REMOVEIMAGE_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_RemoveImage(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned img_id, + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_REMOVEIMAGE_H diff --git a/apis/rivwebcapi/rivwebcapi/rd_removepodcast.c b/apis/rivwebcapi/rivwebcapi/rd_removepodcast.c new file mode 100644 index 00000000..d7121968 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_removepodcast.c @@ -0,0 +1,197 @@ +/* rd_removepodcast.c + * + * Implementation of the Remove RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_removepodcast.h" +#include "rd_common.h" +#include "rd_getuseragent.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __RemovePodcastElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __RemovePodcastElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __RemovePodcastElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __RemovePodcastCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + +int RD_RemovePodcast( const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + char id_buffer[21]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__RemovePodcastElementStart, + __RemovePodcastElementEnd); + XML_SetCharacterDataHandler(parser,__RemovePodcastElementData); + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "41", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + snprintf(id_buffer,21,"%u",cast_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__RemovePodcastCallback); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + // curl_easy_setopt(curl,CURLOPT_VERBOSE,1); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + curl_formfree(first); + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + + if (response_code > 199 && response_code < 300) { //Success + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_removepodcast Call Returned Error: %s\n",xml_data.strbuf); + #endif + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_removepodcast.h b/apis/rivwebcapi/rivwebcapi/rd_removepodcast.h new file mode 100644 index 00000000..171b69ef --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_removepodcast.h @@ -0,0 +1,39 @@ +/* rd_removepodcast.h + * + * Header for the Remove RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_REMOVEPODCAST_H +#define RD_REMOVEPODCAST_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_RemovePodcast(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_REMOVEPODCAST_H diff --git a/apis/rivwebcapi/rivwebcapi/rd_removerss.c b/apis/rivwebcapi/rivwebcapi/rd_removerss.c new file mode 100644 index 00000000..3fddf62f --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_removerss.c @@ -0,0 +1,197 @@ +/* rd_removerss.c + * + * Implementation of the Remove RSS XML Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_removerss.h" +#include "rd_common.h" +#include "rd_getuseragent.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __RemoveRssElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __RemoveRssElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __RemoveRssElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __RemoveRssCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + +int RD_RemoveRss( const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned feed_id, + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + char id_buffer[21]; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__RemoveRssElementStart, + __RemoveRssElementEnd); + XML_SetCharacterDataHandler(parser,__RemoveRssElementData); + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "43", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + snprintf(id_buffer,21,"%u",feed_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__RemoveRssCallback); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + // curl_easy_setopt(curl,CURLOPT_VERBOSE,1); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + curl_formfree(first); + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + + if (response_code > 199 && response_code < 300) { //Success + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_removerss Call Returned Error: %s\n",xml_data.strbuf); + #endif + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_removerss.h b/apis/rivwebcapi/rivwebcapi/rd_removerss.h new file mode 100644 index 00000000..ee4bb05d --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_removerss.h @@ -0,0 +1,39 @@ +/* rd_removerss.h + * + * Header for the Remove RSS XML Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_REMOVERSS_H +#define RD_REMOVERSS_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_RemoveRss(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned feed_id, + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_REMOVERSS_H diff --git a/apis/rivwebcapi/rivwebcapi/rd_savepodcast.c b/apis/rivwebcapi/rivwebcapi/rd_savepodcast.c new file mode 100644 index 00000000..46525705 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_savepodcast.c @@ -0,0 +1,217 @@ +/* rd_import.c + * + * Implementation of the save RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 + +#include "rd_common.h" +#include "rd_getuseragent.h" +#include "rd_savepodcast.h" + +struct xml_data { + char elem_name[256]; + char strbuf[1024]; +}; + +static void XMLCALL __SavePodcastElementStart(void *data, const char *el, + const char **attr) +{ + struct xml_data *xml_data=(struct xml_data *)data; + strlcpy(xml_data->elem_name,el,256); + memset(xml_data->strbuf,0,1024); +} + +static void XMLCALL __SavePodcastElementData(void *data,const XML_Char *s, + int len) +{ + struct xml_data *xml_data=(struct xml_data *)data; + + memcpy(xml_data->strbuf+strlen(xml_data->strbuf),s,len); +} + +static void XMLCALL __SavePodcastElementEnd(void *data, const char *el) +{ + struct xml_data *xml_data=(struct xml_data *)data; + +} + + +size_t __SavePodcastCallback(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + XML_Parser p=(XML_Parser)userdata; + + XML_Parse(p,ptr,size*nmemb,0); + + return size*nmemb; +} + + + +int RD_SavePodcast(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char filename[], + const char user_agent[]) +{ + char post[1500]; + char url[1500]; + CURL *curl=NULL; + XML_Parser parser; + struct xml_data xml_data; + long response_code; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + int i; + char id_buffer[21]; + long userlen = strlen(username); + long passwdlen = strlen(passwd); + char errbuf[CURL_ERROR_SIZE]; + CURLcode res; + char user_agent_string[255]; + + if((curl=curl_easy_init())==NULL) { + curl_easy_cleanup(curl); + return -1; + } + +// +// Generate POST Data +// + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "COMMAND", + CURLFORM_COPYCONTENTS, + "38", + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "LOGIN_NAME", + CURLFORM_COPYCONTENTS, + username, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "PASSWORD", + CURLFORM_COPYCONTENTS, + passwd, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "TICKET", + CURLFORM_COPYCONTENTS, + ticket, + CURLFORM_END); + + sprintf(id_buffer,"%u",cast_id); + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "ID", + CURLFORM_COPYCONTENTS, + id_buffer, + CURLFORM_END); + + curl_formadd(&first, + &last, + CURLFORM_PTRNAME, + "FILENAME", + CURLFORM_FILE, + filename, + CURLFORM_END); + + /* + * Setup the CURL call + */ + memset(&xml_data,0,sizeof(xml_data)); + parser=XML_ParserCreate(NULL); + XML_SetUserData(parser,&xml_data); + XML_SetElementHandler(parser,__SavePodcastElementStart, + __SavePodcastElementEnd); + XML_SetCharacterDataHandler(parser,__SavePodcastElementData); + + //curl_easy_setopt(curl, CURLOPT_WRITEDATA, stderr); Debug try + curl_easy_setopt(curl,CURLOPT_WRITEDATA,parser); + curl_easy_setopt(curl,CURLOPT_TIMEOUT,1200); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,__SavePodcastCallback); + + snprintf(url,1500,"http://%s/rd-bin/rdxport.cgi",hostname); + + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_VERBOSE,0); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + + // Check if User Agent Present otherwise set to default + if (strlen(user_agent)> 0){ + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent); + } + else + { + strcpy(user_agent_string, RD_GetUserAgent()); + strcat(user_agent_string,VERSION); + curl_easy_setopt(curl, CURLOPT_USERAGENT,user_agent_string); + } + + res = curl_easy_perform(curl); + + if(res != CURLE_OK) { + #ifdef RIVC_DEBUG_OUT + size_t len = strlen(errbuf); + fprintf(stderr, "\nlibcurl error: (%d)", res); + if (len) + fprintf(stderr, "%s%s", errbuf, + ((errbuf[len-1] != '\n') ? "\n" : "")); + else + fprintf(stderr, "%s\n", curl_easy_strerror(res)); + #endif + + curl_easy_cleanup(curl); + return -1; + } +/* The response OK - so figure out if we got what we wanted.. */ + + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_formfree(first); + curl_easy_cleanup(curl); + if (response_code > 199 && response_code < 300) { + return 0; + } + else { + #ifdef RIVC_DEBUG_OUT + fprintf(stderr," rd_savepodcast Call Returned Error: %s\n",xml_data.strbuf); + #endif + + return (int)response_code; + } +} diff --git a/apis/rivwebcapi/rivwebcapi/rd_savepodcast.h b/apis/rivwebcapi/rivwebcapi/rd_savepodcast.h new file mode 100644 index 00000000..4301fd29 --- /dev/null +++ b/apis/rivwebcapi/rivwebcapi/rd_savepodcast.h @@ -0,0 +1,40 @@ +/* rd_savepodcast.h + * + * Header for the Save RSS item audio Rivendell Access Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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. + */ + +#ifndef RD_SAVEPODCAST_H +#define RD_SAVEPODCAST_H + +#include + +_MYRIVLIB_INIT_DECL + +int RD_SavePodcast(const char hostname[], + const char username[], + const char passwd[], + const char ticket[], + const unsigned cast_id, + const char filename[], + const char user_agent[]); + +_MYRIVLIB_FINI_DECL + + +#endif // RD_SAVEPODCAST_H diff --git a/apis/rivwebcapi/tests/Makefile.am b/apis/rivwebcapi/tests/Makefile.am index 2318107b..76a3525d 100644 --- a/apis/rivwebcapi/tests/Makefile.am +++ b/apis/rivwebcapi/tests/Makefile.am @@ -1,7 +1,7 @@ ## automake.am ## ## (C) Copyright 2015 Todd Baker -## (C) Copyright 2018 Fred Gleason +## (C) Copyright 2020 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 @@ -31,6 +31,7 @@ noinst_PROGRAMS = addcart_test \ createticket_test \ deleteaudio_test \ deletelog_test \ + deletepodcast_test \ editcart_test \ editcut_test \ exportcart_test \ @@ -52,9 +53,16 @@ noinst_PROGRAMS = addcart_test \ listschedcodes_test \ listservices_test \ listsystemsettings_test \ + postimage_test \ + postpodcast_test \ + postrss_test \ removecart_test \ removecut_test \ + removeimage_test \ + removepodcast_test \ + removerss_test \ savelog_test \ + savepodcast_test \ trimaudio_test \ unassignschedcode_test @@ -88,6 +96,9 @@ deleteaudio_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm dist_deletelog_test_SOURCES = deletelog_test.c common.c common.h deletelog_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm +dist_deletepodcast_test_SOURCES = deletepodcast_test.c common.c common.h +deletepodcast_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + dist_editcart_test_SOURCES = editcart_test.c editcart_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm @@ -151,15 +162,36 @@ listservices_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm dist_listsystemsettings_test_SOURCES = listsystemsettings_test.c listsystemsettings_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm +dist_postimage_test_SOURCES = postimage_test.c +postimage_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + +dist_postpodcast_test_SOURCES = postpodcast_test.c +postpodcast_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + +dist_postrss_test_SOURCES = postrss_test.c +postrss_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + dist_removecart_test_SOURCES = removecart_test.c removecart_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm dist_removecut_test_SOURCES = removecut_test.c removecut_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm +dist_removeimage_test_SOURCES = removeimage_test.c +removeimage_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + +dist_removepodcast_test_SOURCES = removepodcast_test.c +removepodcast_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + +dist_removerss_test_SOURCES = removerss_test.c +removerss_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + dist_savelog_test_SOURCES = savelog_test.c common.c common.h savelog_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm +dist_savepodcast_test_SOURCES = savepodcast_test.c common.c common.h +savepodcast_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm + dist_trimaudio_test_SOURCES = trimaudio_test.c trimaudio_test_LDADD = -lrivwebcapi -lexpat -lcurl -lm diff --git a/apis/rivwebcapi/tests/deletepodcast_test.c b/apis/rivwebcapi/tests/deletepodcast_test.c new file mode 100644 index 00000000..910d7dbc --- /dev/null +++ b/apis/rivwebcapi/tests/deletepodcast_test.c @@ -0,0 +1,205 @@ +/* deletepodcast_test.c + * + * Test the delete podcast library. + * + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + + char buf[BUFSIZ]; + char *p; + long int cast_id=0; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the ID number of the RSS podcast item that you want to delete ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + cast_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + + // Add the User Agent and Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result=RD_DeletePodcast( host, + user, + passwd, + ticket, + (unsigned)cast_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No such podcast exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized \n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Podcast item: %ld was successfully deleted!\n",cast_id); + printf("\n"); + +// Add test of create_ticket function + + int i; + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + // + // Call the function + // + result=RD_DeletePodcast(host, + user, + passwd, + ticket, + (unsigned)cast_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No such podcast exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Podcast item: %ld was successfully deleted!\n",cast_id); + printf("\n"); + + exit(0); +} diff --git a/apis/rivwebcapi/tests/postimage_test.c b/apis/rivwebcapi/tests/postimage_test.c new file mode 100644 index 00000000..a22107bb --- /dev/null +++ b/apis/rivwebcapi/tests/postimage_test.c @@ -0,0 +1,205 @@ +/* postimage_test.c + * + * Test the post RSS image library. + * + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + + char buf[BUFSIZ]; + char *p; + long int img_id=0; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the ID number of the RSS image that you want to Post ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + img_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + + // Add the User Agent and Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result=RD_PostImage( host, + user, + passwd, + ticket, + (unsigned)img_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Such Image Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized \n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Image: %ld was successfully posted!\n",img_id); + printf("\n"); + +// Add test of create_ticket function + + int i; + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + // + // Call the function + // + result=RD_PostImage( host, + user, + passwd, + ticket, + (unsigned)img_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Image Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Image: %ld was successfully deleted!\n",img_id); + printf("\n"); + + exit(0); +} diff --git a/apis/rivwebcapi/tests/postpodcast_test.c b/apis/rivwebcapi/tests/postpodcast_test.c new file mode 100644 index 00000000..a05d4513 --- /dev/null +++ b/apis/rivwebcapi/tests/postpodcast_test.c @@ -0,0 +1,205 @@ +/* postpodcast_test.c + * + * Test the post RSS item audio library. + * + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + + char buf[BUFSIZ]; + char *p; + long int cast_id=0; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the ID number of the podcast item that you want to Post ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + cast_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + + // Add the User Agent and Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result=RD_PostPodcast(host, + user, + passwd, + ticket, + (unsigned)cast_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No such podcast item exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized \n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Podcast: %ld was successfully posted!\n",cast_id); + printf("\n"); + +// Add test of create_ticket function + + int i; + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + // + // Call the function + // + result=RD_PostPodcast( host, + user, + passwd, + ticket, + (unsigned)cast_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No such podcast item exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Podcast item: %ld was successfully deleted!\n",cast_id); + printf("\n"); + + exit(0); +} diff --git a/apis/rivwebcapi/tests/postrss_test.c b/apis/rivwebcapi/tests/postrss_test.c new file mode 100644 index 00000000..c140c1e1 --- /dev/null +++ b/apis/rivwebcapi/tests/postrss_test.c @@ -0,0 +1,205 @@ +/* postrss_test.c + * + * Test the post RSS XML library. + * + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + + char buf[BUFSIZ]; + char *p; + long int feed_id=0; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the ID number of the RSS feed that you want to Post ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + feed_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + + // Add the User Agent and Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result=RD_PostRss( host, + user, + passwd, + ticket, + (unsigned)feed_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Such Feed Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized \n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Feed: %ld was successfully posted!\n",feed_id); + printf("\n"); + +// Add test of create_ticket function + + int i; + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + // + // Call the function + // + result=RD_PostRss( host, + user, + passwd, + ticket, + (unsigned)feed_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Feed Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Feed XML: %ld was successfully deleted!\n",feed_id); + printf("\n"); + + exit(0); +} diff --git a/apis/rivwebcapi/tests/removeimage_test.c b/apis/rivwebcapi/tests/removeimage_test.c new file mode 100644 index 00000000..2cb67b21 --- /dev/null +++ b/apis/rivwebcapi/tests/removeimage_test.c @@ -0,0 +1,205 @@ +/* removeimage_test.c + * + * Test the post RSS image library. + * + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + + char buf[BUFSIZ]; + char *p; + long int img_id=0; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the ID number of the RSS image that you want to Post ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + img_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + + // Add the User Agent and Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result=RD_RemoveImage( host, + user, + passwd, + ticket, + (unsigned)img_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Such Image Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized \n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Image: %ld was successfully posted!\n",img_id); + printf("\n"); + +// Add test of create_ticket function + + int i; + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + // + // Call the function + // + result=RD_RemoveImage( host, + user, + passwd, + ticket, + (unsigned)img_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Image Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Image: %ld was successfully deleted!\n",img_id); + printf("\n"); + + exit(0); +} diff --git a/apis/rivwebcapi/tests/removepodcast_test.c b/apis/rivwebcapi/tests/removepodcast_test.c new file mode 100644 index 00000000..b21d2f5f --- /dev/null +++ b/apis/rivwebcapi/tests/removepodcast_test.c @@ -0,0 +1,205 @@ +/* removepodcast_test.c + * + * Test the remove podcast library. + * + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + + char buf[BUFSIZ]; + char *p; + long int cast_id=0; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the ID number of the RSS podcast item that you want to remove ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + cast_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + + // Add the User Agent and Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result=RD_RemovePodcast( host, + user, + passwd, + ticket, + (unsigned)cast_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Such Image Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized \n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Podcast item: %ld was successfully removed!\n",cast_id); + printf("\n"); + +// Add test of create_ticket function + + int i; + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + // + // Call the function + // + result=RD_RemovePodcast(host, + user, + passwd, + ticket, + (unsigned)cast_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Image Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Podcast item: %ld was successfully removed!\n",cast_id); + printf("\n"); + + exit(0); +} diff --git a/apis/rivwebcapi/tests/removerss_test.c b/apis/rivwebcapi/tests/removerss_test.c new file mode 100644 index 00000000..6eecd284 --- /dev/null +++ b/apis/rivwebcapi/tests/removerss_test.c @@ -0,0 +1,204 @@ +/* removerss_test.c + * + * Test the remove RSS XML library. + * + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + char buf[BUFSIZ]; + char *p; + long int feed_id=0; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the ID number of the RSS feed that you want to remove ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + feed_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + + // Add the User Agent and Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result=RD_RemoveRss( host, + user, + passwd, + ticket, + (unsigned)feed_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Such Feed Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized \n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Feed: %ld was successfully removed!\n",feed_id); + printf("\n"); + +// Add test of create_ticket function + + int i; + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + // + // Call the function + // + result=RD_RemoveRss( host, + user, + passwd, + ticket, + (unsigned)feed_id, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong!\n"); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: No Feed Exists! \n"); + break; + case 401: + fprintf(stderr, "ERROR: Unauthorized\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + } + exit(256); + } + + // + // List the Results + // + printf(" Feed: %ld was successfully removed!\n",feed_id); + printf("\n"); + + exit(0); +} diff --git a/apis/rivwebcapi/tests/savepodcast_test.c b/apis/rivwebcapi/tests/savepodcast_test.c new file mode 100644 index 00000000..b43c8afe --- /dev/null +++ b/apis/rivwebcapi/tests/savepodcast_test.c @@ -0,0 +1,210 @@ +/* import_test.c + * + * Test the Save RSS item audio API Library + * + * (C) Copyright 2015 Todd Baker + * (C) Copyright 2020 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 +#include +#include +#include + +int main(int argc,char *argv[]) +{ + + int i; + char buf[BUFSIZ]; + char *p; + long int cast_id=0; + char filename[BUFSIZ]; + char *host; + char *user; + char *passwd; + char ticket[41]=""; + char user_agent[255]={0}; + + /* Get the Rivendell Host, User and Password if set in env */ + if (getenv("RIVHOST")!=NULL) { + host = getenv("RIVHOST"); + } + else { + host="localhost"; + } + + if (getenv("RIVUSER")!=NULL) { + user = getenv("RIVUSER"); + } + else { + user="USER"; + } + + if (getenv("RIVPASS")!=NULL) { + passwd = getenv("RIVPASS"); + } + else { + passwd = ""; + } + + printf("Please enter the Podcast ID Number that you want to save to ==> "); + if (fgets(buf,sizeof(buf),stdin) != NULL) + { + cast_id = strtol(buf, &p,10); + + if ( (buf[0] != '\n') && + ((*p != '\n') && (*p != '\0'))) + { + fprintf(stderr," Illegal Characters detected! Exiting.\n"); + exit(0); + } + } + printf("Please enter the File Name that you want to save ==> "); + if (fgets(filename,sizeof(filename),stdin) != NULL) + { + if((0xFF&filename[strlen(filename)-1])<32) { + filename[strlen(filename)-1]=0; + } + } + + // Add the Rivendell-C-API Version + strcat(user_agent,RD_GetUserAgent()); + strcat(user_agent,RD_GetVersion()); + strcat(user_agent," (Test Suite)"); + + // + // Call the function + // + int result= RD_SavePodcast(host, + user, + passwd, + ticket, + (unsigned)cast_id, + filename, + user_agent); + if(result<0) { + fprintf(stderr,"Something went wrong! Result Code = %d\n",result); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: no such podcast item \n"); + break; + + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + break; + } + exit(256); + } + + // + // List the results + // + printf(" Podcast item: %ld was successfully saved!\n",cast_id); + + +// Add test of create_ticket function + + struct rd_ticketinfo *myticket=0; + unsigned numrecs=0; + + result = RD_CreateTicket( &myticket, + host, + user, + passwd, + user_agent, + &numrecs); + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 403: + fprintf(stderr," ERROR: Invalid User Information During Create Ticket\n"); + break; + default: + fprintf(stderr, "Unknown Error occurred ==> %d\n",result); + } + exit(256); + } + + // We got a ticket created - use it and do the call again + // + // List the Results + // + for(i=0;itkt_expiration_datetime.tm_year); + printf("Ticket Expire month value = %d\n",myticket->tkt_expiration_datetime.tm_mon); + printf("Ticket Expire day value = %d\n",myticket->tkt_expiration_datetime.tm_mday); + printf("Ticket Expire wday value = %d\n",myticket->tkt_expiration_datetime.tm_wday); + printf("Ticket Expire hour value = %d\n",myticket->tkt_expiration_datetime.tm_hour); + printf("Ticket Expire min value = %d\n",myticket->tkt_expiration_datetime.tm_min); + printf("Ticket Expire sec value = %d\n",myticket->tkt_expiration_datetime.tm_sec); + printf("Ticket Expire isdst value = %d\n",myticket->tkt_expiration_datetime.tm_isdst); + printf("\n"); + + } + + user=""; + passwd=""; + strcpy( ticket,myticket->ticket); + fprintf(stderr, "Ticket was copied - = %s\n",ticket); + + result=RD_SavePodcast(host, + user, + passwd, + ticket, + (unsigned)cast_id, + filename, + user_agent); + + if(result<0) { + fprintf(stderr,"Something went wrong! Result Code = %d\n",result); + exit(256); + } + + if ((result< 200 || result > 299) && + (result != 0)) + { + switch(result) { + case 404: + fprintf(stderr,"ERROR: no such podcast item \n"); + break; + + default: + fprintf(stderr, "Unknown Error occurred ==> %d",result); + break; + } + exit(256); + } + + // + // List the results + // + printf(" Podcast item: %ld was successfully saved!\n",cast_id); + + exit(0); +} diff --git a/conf/rd-bin.conf.in b/conf/rd-bin.conf.in index 5a686a74..6e0cc041 100644 --- a/conf/rd-bin.conf.in +++ b/conf/rd-bin.conf.in @@ -1,8 +1,8 @@ -# rdfeed.conf +# rd-bin.conf # # This is the Apache Web Server configuration for Rivendell. # -# (C) Copyright 2007,2010,2016 Fred Gleason +# (C) Copyright 2007-2020 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 @@ -35,15 +35,9 @@ SetHandler default-handler - - SetHandler cgi-script - SetHandler cgi-script - - SetHandler cgi-script - ScriptAlias /rd-bin/ "@libexecdir@/" TimeOut 1200 diff --git a/configure.ac b/configure.ac index d0c7aa49..8dde9523 100644 --- a/configure.ac +++ b/configure.ac @@ -487,6 +487,25 @@ ln -s ../../icons/traffic.png docs/opsguide/traffic.png rm -f docs/opsguide/trashcan-32x32.png ln -s ../../icons/trashcan-32x32.png docs/opsguide/trashcan-32x32.png +rm -f docs/opsguide/blueball.png +ln -s ../../icons/blueball.png docs/opsguide/blueball.png + +rm -f docs/opsguide/greenball.png +ln -s ../../icons/greenball.png docs/opsguide/greenball.png + +rm -f docs/opsguide/redball.png +ln -s ../../icons/redball.png docs/opsguide/redball.png + +rm -f docs/opsguide/whiteball.png +ln -s ../../icons/whiteball.png docs/opsguide/whiteball.png + +rm -f docs/opsguide/greencheckmark.png +ln -s ../../icons/greencheckmark.png docs/opsguide/greencheckmark.png + +rm -f docs/opsguide/redx.png +ln -s ../../icons/redx.png docs/opsguide/redx.png + + AC_CONFIG_FILES([rivendell.spec \ Makefile \ conf/rd-bin.conf \ @@ -507,7 +526,6 @@ AC_CONFIG_FILES([rivendell.spec \ rdhpi/Makefile \ cae/Makefile \ web/Makefile \ - web/rdfeed/Makefile \ web/rdcastmanager/Makefile \ web/rdxport/Makefile \ web/tests/Makefile \ @@ -515,6 +533,7 @@ AC_CONFIG_FILES([rivendell.spec \ conf/Makefile \ docs/Makefile \ docs/apis/Makefile \ + docs/dtds/Makefile \ docs/examples/Makefile \ docs/manpages/Makefile \ docs/misc/Makefile \ @@ -544,6 +563,7 @@ AC_CONFIG_FILES([rivendell.spec \ rdrepld/Makefile \ rdpadd/Makefile \ rdpadengined/Makefile \ + rdrssd/Makefile \ rdselect/Makefile \ rdservice/Makefile \ rdvairplayd/Makefile \ @@ -571,7 +591,6 @@ AC_CONFIG_FILES([rivendell.spec \ utils/rdmarkerset/Makefile \ utils/rdmetadata/Makefile \ utils/rdpopup/Makefile \ - utils/rdpurgecasts/Makefile \ utils/rdrender/Makefile \ utils/rdselect_helper/Makefile \ utils/rdsoftkeys/Makefile \ diff --git a/docs/Makefile.am b/docs/Makefile.am index bfa12a3d..4cd1f4b1 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -2,7 +2,7 @@ ## ## docs/automake.am for Rivendell ## -## (C) Copyright 2002-2017 Fred Gleason +## (C) Copyright 2002-2020 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 @@ -20,7 +20,7 @@ ## Use automake to process this into a Makefile.in if DOCBOOK_AM - DOCBOOK_AM_OPT = stylesheets apis manpages opsguide rivwebcapi + DOCBOOK_AM_OPT = stylesheets apis manpages opsguide rivwebcapi dtds endif SUBDIRS = $(DOCBOOK_AM_OPT) examples\ diff --git a/docs/apis/Makefile.am b/docs/apis/Makefile.am index 66039837..2d848303 100644 --- a/docs/apis/Makefile.am +++ b/docs/apis/Makefile.am @@ -36,10 +36,12 @@ all-local: cae.html\ catchd.html\ catchd.pdf\ fonts.html\ + fonts.rdcastmanager.list_carts.png\ fonts.pdf\ fonts.rdadmin.configure_rdairplay_screenshot.png\ fonts.rdairplay_screenshot.png\ fonts.rdcartslots_screenshot.png\ + fonts.rdcastmanager.list_carts.png\ fonts.rdcastmanager.uploading_audio_screenshot.png\ fonts.rdlibrary.edit_audio_screenshot.png\ notification.html\ @@ -64,6 +66,8 @@ EXTRA_DIST = cae.html\ fonts.rdairplay_screenshot.xcf\ fonts.rdcartslots_screenshot.png\ fonts.rdcartslots_screenshot.xcf\ + fonts.rdcastmanager.list_carts.png\ + fonts.rdcastmanager.list_carts.xcf\ fonts.rdcastmanager.uploading_audio_screenshot.png\ fonts.rdcastmanager.uploading_audio_screenshot.xcf\ fonts.rdlibrary.edit_audio_screenshot.png\ diff --git a/docs/apis/fonts.rdcastmanager.list_carts.png b/docs/apis/fonts.rdcastmanager.list_carts.png new file mode 100644 index 00000000..223a3b35 Binary files /dev/null and b/docs/apis/fonts.rdcastmanager.list_carts.png differ diff --git a/docs/apis/fonts.rdcastmanager.list_carts.xcf b/docs/apis/fonts.rdcastmanager.list_carts.xcf new file mode 100644 index 00000000..96d9fe42 Binary files /dev/null and b/docs/apis/fonts.rdcastmanager.list_carts.xcf differ diff --git a/docs/apis/fonts.xml b/docs/apis/fonts.xml index 6aa9ebcb..bc755946 100644 --- a/docs/apis/fonts.xml +++ b/docs/apis/fonts.xml @@ -90,6 +90,16 @@ Label Fonts + + bigLabelFont() + + + Font for use in QLabel widgets used for + labeling the major UI element in a dialog, such as a + QListView. + + + labelFont() @@ -274,6 +284,21 @@ + + RDCastManager - List Carts + + + + + + + + Example of bigLabelFont() + + + + + diff --git a/docs/apis/web_api.xml b/docs/apis/web_api.xml index 3970ee76..06a611b3 100644 --- a/docs/apis/web_api.xml +++ b/docs/apis/web_api.xml @@ -831,6 +831,69 @@ + + DeletePodcast + Delete posted podcast audio from the audio store + + Command Code: RDXPORT_COMMAND_DELETE_PODCAST + + + Required User Permissions: Delete Podcast, Feed Permission + or Administer System + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + DeletePodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 39 + + + Mandatory + + + + + ID + + + ID of podcast item (integer) + + + Mandatory + + + + +
+
+ EditCart @@ -1717,6 +1780,69 @@ + + GetPodcast + Get posted podcast audio + + Command Code: RDXPORT_COMMAND_GET_PODCAST, Feed Permissions + or Administer System + + + Required User Permissions: Edit Podcast, Feed Permission + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + GetPodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 37 + + + Mandatory + + + + + ID + + + ID of podcast item (integer) + + + Mandatory + + + + +
+
+ Import Import audio data into the audio store @@ -2726,6 +2852,189 @@ + + PostImage + Upload a podcast image to the remote archive + + Command Code: RDXPORT_COMMAND_POST_IMAGE + + + Required User Permissions: Administer System + + + PostRss Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 44 + + + Mandatory + + + + + ID + + + ID of image (integer) + + + Mandatory + + + + +
+
+ + + PostPodcast + Upload podcast audio from the audio store to the remote archive + + Command Code: RDXPORT_COMMAND_POST_PODCAST + + + Required User Permissions: Add Podcast, Feed Permission + or Administer System + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + PostPodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 40 + + + Mandatory + + + + + ID + + + ID of podcast item (integer) + + + Mandatory + + + + +
+
+ + + PostRss + Upload podcast RSS XML to the remote archive + + Command Code: RDXPORT_COMMAND_POST_RSS + + + Required User Permissions: Edit Podcast, Feed Permission + or Administer System + + + A 404 error will be returned if the + requested feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + PostRss Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 42 + + + Mandatory + + + + + ID + + + ID of podcast feed (integer) + + + Mandatory + + + + +
+
+ Rehash Generate a SHA-1 hash for a cut and write it to the database @@ -2916,6 +3225,189 @@ + + RemoveImage + Remove a podcast image from the remote archive + + Command Code: RDXPORT_COMMAND_REMOVE_IMAGE + + + Required User Permissions: Administer System + + + PostRss Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 45 + + + Mandatory + + + + + ID + + + ID of image (integer) + + + Mandatory + + + + +
+
+ + + RemovePodcast + Delete podcast audio from the remote archive + + Command Code: RDXPORT_COMMAND_REMOVE_PODCAST + + + Required User Permissions: Delete Podcast, Feed Permission + or Administer System + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + RemovePodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 41 + + + Mandatory + + + + + ID + + + ID of podcast item (integer) + + + Mandatory + + + + +
+
+ + + RemoveRss + Remove podcast RSS XML from the remote archive + + Command Code: RDXPORT_COMMAND_REMOVE_RSS + + + Required User Permissions: Delete Podcast, Feed Permission + or Administer System + + + A 404 error will be returned if the + requested feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + RemoveRss Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 43 + + + Mandatory + + + + + ID + + + ID of podcast feed (integer) + + + Mandatory + + + + +
+
+ SaveFile @@ -3526,6 +4018,83 @@ + + SavePodcast + Save posted podcast audio + + Command Code: RDXPORT_COMMAND_SAVE_PODCAST + + + Required User Permissions: Create Podcast, Feed Permission + or Administer System + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + NOTE: The method must be called with 'multipart/form-data' encoding. + + + SavePodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 38 + + + Mandatory + + + + + ID + + + ID of podcast (integer) + + + Mandatory + + + + + FILENAME + + + Binary file data + + + Mandatory + + + + +
+
+ SaveString diff --git a/docs/dtds/Makefile.am b/docs/dtds/Makefile.am new file mode 100644 index 00000000..7722a2da --- /dev/null +++ b/docs/dtds/Makefile.am @@ -0,0 +1,45 @@ +## Makefile.am +## +## docs/dtds/Makefile.am for Rivendell +## +## (C) Copyright 2020 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. +## +## Use automake to process this into a Makefile.in + +## +## Build Dependencies +## +%.html: %.xml + xsltproc -o $@ $(DOCBOOK_STYLESHEETS)/xhtml/docbook.xsl $< +%.pdf: %.xml + xsltproc $(DOCBOOK_STYLESHEETS)/fo/docbook.xsl $< | fop - -pdf $@ + +all-local: superfeed.html\ + superfeed.pdf + +EXTRA_DIST = superfeed.html\ + superfeed.pdf\ + superfeed.xml + +CLEANFILES = *~ +MAINTAINERCLEANFILES = *~\ + *.1\ + *.8\ + *.html\ + *.pdf\ + aclocal.m4\ + configure\ + Makefile.in diff --git a/docs/dtds/superfeed.xml b/docs/dtds/superfeed.xml new file mode 100644 index 00000000..742de870 --- /dev/null +++ b/docs/dtds/superfeed.xml @@ -0,0 +1,188 @@ + +
+ + The Rivendell "superfeed" Namespace + Version 0.1 + v0.1 + + + Fred + Gleason + fredg@paravelsystems.com + + + + v0.1 + + 2020Fred 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 Superfeed RSS podcasting functionality. + + + + Namespace Declaration + + The namespace declaration is: + + + xmlns:superfeed="http://www.rivendellaudio.org/dtds/superfeed-0.1.dtd" + + + + Fields + + The namespace adds the following fields: + + + + + <channelTitle> + + + + The value of the <title> field from the + <channel> section of the underlying elemental + feed. + + + + + + <channelDescription> + + + + The value of the <description> field from the + <channel> section of the underlying elemental + feed. + + + + + + + + Example + + The following example posits the existence of two elemental feeds, + AIRPLAY, + CARTSLTS and + LIBRARY; each containing a single + post. A superfeed, called RIVENDEL + and consisting of those three elemental fields would look like this: + + +<?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"> + <channel> + <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> + <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> + <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/docs/opsguide/Makefile.am b/docs/opsguide/Makefile.am index 74de1ddd..49084138 100644 --- a/docs/opsguide/Makefile.am +++ b/docs/opsguide/Makefile.am @@ -37,6 +37,7 @@ OPSGUIDE_HTML = appendix.filepath_wildcards.html\ chapter.rdlogedit.html\ chapter.rdlogin.html\ chapter.rdlogmanager.html\ + chapter.rdcastmanager.html\ chapter.rml.html\ chapter.utilities.html\ chapter.voicetracking.html\ @@ -102,6 +103,9 @@ OPSGUIDE_HTML = appendix.filepath_wildcards.html\ sect.rdcartslots.operation_in_breakaway_mode.html\ sect.rdcartslots.operation_in_cart_deck_mode.html\ sect.rdcartslots.setting_the_slot_options.html\ + sect.rdcastmanager.deleting_an_item.html\ + sect.rdcastmanager.editing_an_items_metadata.html\ + sect.rdcastmanager.posting_creating_a_new_item.html\ sect.rdcatch.adding_new_events.html\ sect.rdcatch_automating_macro_execution.html\ sect.rdcatch.automating_playouts.html\ @@ -217,6 +221,15 @@ OPSGUIDE_IMAGES = rdadmin.audio_resource_information_dialog.png\ rdairplay.soundpanel_widget.png\ rdairplay.wallclock_widget.png\ rdairplay.where_to.png\ + rdcastmanager.delete_item_dialog.png\ + rdcastmanager.editing_item_dialog.png\ + rdcastmanager.list_log_events_dialog.png\ + rdcastmanager.list_log_select_select_audio_file_dialog.png\ + rdcastmanager.log_render_options_dialog.png\ + rdcastmanager.podcast_item_list_dialog.png\ + rdcastmanager.rdcastmanager_screenshot.png\ + rdcastmanager.select_cut_dialog.png\ + rdcastmanager.select_log_dialog.png\ rdcartslots.rdcartslots.edit_slot_options_dialog.png\ rdcartslots.rdcartslots_screenshot.png\ rdcartslots.rivendell_services_dialog.png\ @@ -274,15 +287,21 @@ OPSGUIDE_IMAGES = rdadmin.audio_resource_information_dialog.png\ voicetracking.voice_tracker_dialog_track_selected.png\ webget.webget_screen.png -OPSGUIDE_ICONS = chain.png\ +OPSGUIDE_ICONS = blueball.png\ + chain.png\ + greenball.png\ + greencheckmark.png\ mic16.png\ music.png\ notemarker.png\ play.png\ + redball.png\ + redx.png\ rml5.png\ track_cart.png\ traffic.png\ - trashcan-32x32.png + trashcan-32x32.png\ + whiteball.png OPSGUIDE_DEPS = $(OPSGUIDE_IMAGES) \ $(OPSGUIDE_ICONS) \ @@ -297,6 +316,7 @@ OPSGUIDE_DEPS = $(OPSGUIDE_IMAGES) \ rdadmin.xml\ rdairplay.xml\ rdcartslots.xml\ + rdcastmanager.xml\ rdcatch.xml\ rddbconfig.xml\ rdlibrary.xml\ @@ -345,14 +365,20 @@ CLEANFILES = *~\ *.pdf DISTCLEANFILES = chain.png \ + blueball.png\ + greenball.png\ + greencheckmark.png\ mic16.png \ music.png \ notemarker.png \ play.png \ + redball.png\ + redx.png\ rml5.png \ track_cart.png \ traffic.png \ - trashcan-32x32.png + trashcan-32x32.png\ + whiteball.png MAINTAINERCLEANFILES = *~\ *.1\ diff --git a/docs/opsguide/opsguide.xml b/docs/opsguide/opsguide.xml index 3e8afeb1..06936452 100644 --- a/docs/opsguide/opsguide.xml +++ b/docs/opsguide/opsguide.xml @@ -12,6 +12,7 @@ + @@ -39,6 +40,7 @@ &rdcartslots; &rdlogmanager; &voicetracking; + &rdcastmanager; &webget; diff --git a/docs/opsguide/rdcastmanager.delete_item_dialog.png b/docs/opsguide/rdcastmanager.delete_item_dialog.png new file mode 100644 index 00000000..32922ebb Binary files /dev/null and b/docs/opsguide/rdcastmanager.delete_item_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.editing_item_dialog.png b/docs/opsguide/rdcastmanager.editing_item_dialog.png new file mode 100644 index 00000000..1170b6dd Binary files /dev/null and b/docs/opsguide/rdcastmanager.editing_item_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.list_log_events_dialog.png b/docs/opsguide/rdcastmanager.list_log_events_dialog.png new file mode 100644 index 00000000..bc7e6eda Binary files /dev/null and b/docs/opsguide/rdcastmanager.list_log_events_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.list_log_select_select_audio_file_dialog.png b/docs/opsguide/rdcastmanager.list_log_select_select_audio_file_dialog.png new file mode 100644 index 00000000..152ac675 Binary files /dev/null and b/docs/opsguide/rdcastmanager.list_log_select_select_audio_file_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.log_render_options_dialog.png b/docs/opsguide/rdcastmanager.log_render_options_dialog.png new file mode 100644 index 00000000..2200a5ae Binary files /dev/null and b/docs/opsguide/rdcastmanager.log_render_options_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.podcast_item_list_dialog.png b/docs/opsguide/rdcastmanager.podcast_item_list_dialog.png new file mode 100644 index 00000000..8a482478 Binary files /dev/null and b/docs/opsguide/rdcastmanager.podcast_item_list_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.rdcastmanager_screenshot.png b/docs/opsguide/rdcastmanager.rdcastmanager_screenshot.png new file mode 100644 index 00000000..19245e9e Binary files /dev/null and b/docs/opsguide/rdcastmanager.rdcastmanager_screenshot.png differ diff --git a/docs/opsguide/rdcastmanager.select_cut_dialog.png b/docs/opsguide/rdcastmanager.select_cut_dialog.png new file mode 100644 index 00000000..290ac419 Binary files /dev/null and b/docs/opsguide/rdcastmanager.select_cut_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.select_log_dialog.png b/docs/opsguide/rdcastmanager.select_log_dialog.png new file mode 100644 index 00000000..aedbc97b Binary files /dev/null and b/docs/opsguide/rdcastmanager.select_log_dialog.png differ diff --git a/docs/opsguide/rdcastmanager.xml b/docs/opsguide/rdcastmanager.xml new file mode 100644 index 00000000..db2b464a --- /dev/null +++ b/docs/opsguide/rdcastmanager.xml @@ -0,0 +1,420 @@ + + Managing Podcasts with RDCastManager + + Overview + + RDCastManager is a Rivendell module designed for posting items and + managing item metadata for audio podcasts managed by Rivendell. + + + + + + + + The RDCastManager Main Window + + + + + When started, RDCastManager will display the list of available podcast + feeds for the currently logged in user. Each line begins with an icon, + showing the assigned channel image for the repective feed. + + + To see the list of items posted + to a particular feed, select the desired feed on the list and then touch + the View Feed button to open the + Podcast Item List dialog. + + + + + + + + The Podcast Item List Dialog + + + + + Each posted item has a status, indicated by the + color of the icon in the Status column. + + + + RDCastManager Item States + + + + + + + Status + + + Meaning + + + + + + + + + + + + + + Item is active and visible to the audience. + + + + + + + + + + + + Item is inactive and not visible to the audience. + + + + + + + + + + + + Item is active, but currently embargoed and not visible to + the audience. + + + + +
+
+ + + Editing an Item's Metadata + + To view (and possibly modify) an item's metadata, select the item + on the list and then touch the Edit + button to open the Editing Item + dialog. + + + + + + + + The Editing Item Dialog + + + + + The following controls are available: + + + + + Item Active + + + When ticked, this makes the item visible to the audience + (but see Air Date/Time below). + + + Unticking this is a good way to put an item "on hold" + without permanently deleting it from the feed. + + + + + Posted By + + + This is a "read-only" field that provides details on + the provenance of the item. + + + + + Title + + + The title of the item. + + + + + Author E-Mail + + + The e-mail address (and optionally, full name) of the + author/originator of the item. + + + + + Description + + + Text describing the content of the item. + + + The accompanying + Item contains explicit content + checkbox should be ticked if the item contains content that + may not be suitable for under-age listeners. + + + + + Image + + + The icon that will associated with this item when viewed + by the audience. + + + + + Air Date/Time + + + The date/time that the item was/will be published. If this is + set to a value in the future, the item will be + embargoed --i.e. hidden from the audience-- + until this date/time. + + + + + Expires + + + If this dropdown is set to Yes, then + the item will be automatically deleted from the feed at the + specified date/time. If set to No, + the item will never be deleted automatically. + + + Deleting an item cannot be undone! + + + + + + + To save any changes made to the item's metadata and close the dialog + touch the OK. To close the dialog + while abandoning any pending changes, touch + Cancel. + + + + + Deleting an Item + + To delete an item and permanently remove it from the feed, select + the desired item and touch the Delete + button. The system will prompt for confirmation: + + + + + + + + The Delete Item Dialog + + + + + then touch the Yes button to complete + the operation. + + + Deletion of an item is permanent; it cannot be undone! + + + If it is desired to temporarily hide an item, simply untick the + Item Active checkbox in the item's + Editing Item dialog. + + + + + Posting: Creating a New Item + + "Posting" is the action by which a new item is added to + a podcast feed. RDCastManager is capable of posting to a feed from + three different sources: a Rivendell cart/cut, a Rivendell log, and + directly from an audio file. + + + + Posting from a Rivendell Cart/Cut + + To post the contents of a Rivendell cart/cut, touch the + Post from Cart/Cut button to + open the Select Cut dialog: + + + + + + + + The Select Cut Dialog + + + + + Select the desired cart/cut, then touch the + OK button. RDCastManager will + upload the audio and then open the + Editing Item dialog to allow + the new item's metadata to be set. + + + + + Posting from a Rivendell Log + + To post one or more events from a Rivendell log, touch the + Post from Log button to + open the Select Log dialog: + + + + + + + + The Select Log Dialog + + + + + Select the log to post, then touch the + OK button to bring up the + Log Render Options dialog: + + + + + + + + The Select Log Dialog + + + + + The following controls are available in this dialog: + + + + + Virtual Start Time + + + Set the virtual start time to be used when rendering the + log. This can be useful for simulating a "real" + log play-out so that dayparting of cuts works as expected. + By default, the wall time when the + OK is touched will be used. + + + + + + At STOP transition + + + Set the action to take during log rendering if a STOP + transition is encountered. If + Treat as PLAY (the default) + is selected, rendering will continue as if a PLAY transition + were found. If Stop Rendering + is selected, rendering of the log will be terminated at that + point. + + + + + + Selected Log Events + + + By default, all of the events in the log will be rendered + (subject to possible truncation by a STOP transition as + specified by the setting of + At STOP Transition). To + select a subset of events to be rendered, touch the + Select button bring up + the List Log dialog: + + + + + + + + The List Log Events Dialog + + + + + Select the events to be rendered and then touch the + OK button to return to the + Log Render Options dialog. + + + + + + + After the desired rendering options set, touch the + OK button. RDCastManager will + upload the audio and then open the + Editing Item dialog to allow + the new item's metadata to be set. + + + + + Posting an Audio File + + To post the contents of an audio file, touch the + Post from File button to bring up + the Select Audio File dialog. + + + + + + + + The Select Audio File Dialog + + + + + Select the file to be posted and then touch the + OK button. RDCastManager will + upload the audio and then open the + Editing Item dialog to allow + the new item's metadata to be set. + + + +
diff --git a/docs/rivwebcapi/Makefile.am b/docs/rivwebcapi/Makefile.am index 8ca0eafe..79038f23 100644 --- a/docs/rivwebcapi/Makefile.am +++ b/docs/rivwebcapi/Makefile.am @@ -2,7 +2,7 @@ ## ## docs/rivwebcapi/Makefile.am for Rivendell C API ## -## (C) Copyright 2015-2018 Fred Gleason +## (C) Copyright 2015-2020 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 @@ -64,6 +64,9 @@ all-local: rd_addcart.html\ rd_deletelog.html\ rd_deletelog.pdf\ rd_deletelog.xml\ + rd_deletepodcast.html\ + rd_deletepodcast.pdf\ + rd_deletepodcast.xml\ rd_editcart.html\ rd_editcart.pdf\ rd_editcart.xml\ @@ -127,15 +130,37 @@ all-local: rd_addcart.html\ rd_listsystemsettings.html\ rd_listsystemsettings.pdf\ rd_listsystemsettings.xml\ + rd_postimage.html\ + rd_postimage.pdf\ + rd_postimage.xml\ + rd_postpodcast.html\ + rd_postpodcast.pdf\ + rd_postpodcast.xml\ + rd_postrss.html\ + rd_postrss.pdf\ + rd_postrss.xml\ rd_removecart.html\ rd_removecart.pdf\ rd_removecart.xml\ rd_removecut.html\ rd_removecut.pdf\ rd_removecut.xml\ + rd_removeimage.html\ + rd_removeimage.pdf\ + rd_removeimage.xml\ + rd_removepodcast.html\ + rd_removepodcast.pdf\ + rd_removepodcast.xml\ + rd_removerss.html\ + rd_removerss.pdf\ + rd_removerss.xml\ + rd_removecut.xml\ rd_savelog.html\ rd_savelog.pdf\ rd_savelog.xml\ + rd_savepodcast.html\ + rd_savepodcast.pdf\ + rd_savepodcast.xml\ rd_trimaudio.html\ rd_trimaudio.pdf\ rd_trimaudio.xml\ @@ -153,6 +178,7 @@ man_MANS = rd_addcart.7\ rd_createticket.7\ rd_deleteaudio.7\ rd_deletelog.7\ + rd_deletepodcast.7\ rd_editcart.7\ rd_editcut.7\ rd_exportpeaks.7\ @@ -174,9 +200,16 @@ man_MANS = rd_addcart.7\ rd_listschedcodes.7\ rd_listservices.7\ rd_listsystemsettings.7\ + rd_postimage.7\ + rd_postpodcast.7\ + rd_postrss.7\ rd_removecart.7\ rd_removecut.7\ + rd_removeimage.7\ + rd_removepodcast.7\ + rd_removerss.7\ rd_savelog.7\ + rd_savepodcast.7\ rd_trimaudio.7\ rd_unassignschedcode.7 @@ -222,6 +255,10 @@ EXTRA_DIST = rd_addcart.7\ rd_deletelog.html\ rd_deletelog.pdf\ rd_deletelog.xml\ + rd_deletepodcast.7\ + rd_deletepodcast.html\ + rd_deletepodcast.pdf\ + rd_deletepodcast.xml\ rd_editcart.7\ rd_editcart.html\ rd_editcart.pdf\ @@ -307,6 +344,18 @@ EXTRA_DIST = rd_addcart.7\ rd_listsystemsettings.html\ rd_listsystemsettings.pdf\ rd_listsystemsettings.xml\ + rd_postimage.7\ + rd_postimage.html\ + rd_postimage.pdf\ + rd_postimage.xml\ + rd_postpodcast.7\ + rd_postpodcast.html\ + rd_postpodcast.pdf\ + rd_postpodcast.xml\ + rd_postrss.7\ + rd_postrss.html\ + rd_postrss.pdf\ + rd_postrss.xml\ rd_removecart.7\ rd_removecart.html\ rd_removecart.pdf\ @@ -315,10 +364,26 @@ EXTRA_DIST = rd_addcart.7\ rd_removecut.html\ rd_removecut.pdf\ rd_removecut.xml\ + rd_removeimage.7\ + rd_removeimage.html\ + rd_removeimage.pdf\ + rd_removeimage.xml\ + rd_removepodcast.7\ + rd_removepodcast.html\ + rd_removepodcast.pdf\ + rd_removepodcast.xml\ + rd_removerss.7\ + rd_removerss.html\ + rd_removerss.pdf\ + rd_removerss.xml\ rd_savelog.7\ rd_savelog.html\ rd_savelog.pdf\ rd_savelog.xml\ + rd_savepodcast.7\ + rd_savepodcast.html\ + rd_savepodcast.pdf\ + rd_savepodcast.xml\ rd_trimaudio.7\ rd_trimaudio.html\ rd_trimaudio.pdf\ diff --git a/docs/rivwebcapi/rd_deletepodcast.xml b/docs/rivwebcapi/rd_deletepodcast.xml new file mode 100644 index 00000000..66299a33 --- /dev/null +++ b/docs/rivwebcapi/rd_deletepodcast.xml @@ -0,0 +1,188 @@ + + + + + RD_DeletePodcast + 7 + September 2020 + Rivendell C Library Manual + + + rd_deletepodcast + Rivendell Delete RSS item audio C Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_deletepodcast.h> + + int RD_DeletePodcast + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned cast_id + const char user_agent[] + + + + + + Description + + RD_DeletePodcast is the function to use + to delete RSS item audio from audio store. + + + RD_DeletePodcast function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory When NO Ticket Provided + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + cast_id + + + unsigned integer + + + Podcast ID + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+ +
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Podcast ID + + + 403 User Authentification Error. + + + 404 Unauthorized or No Such Podcast + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/rivwebcapi/rd_postimage.xml b/docs/rivwebcapi/rd_postimage.xml new file mode 100644 index 00000000..7287b9fe --- /dev/null +++ b/docs/rivwebcapi/rd_postimage.xml @@ -0,0 +1,189 @@ + + + + + RD_PostImage + 7 + September 2020 + Rivendell C Library Manual + + + rd_postimage + Rivendell Post RSS Image C Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_postimage.h> + + int RD_PostImage + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned img_id + const char user_agent[] + + + + + + Description + + RD_PostImage is the function to use + to post an RSS image from the Rivendell database to the remote RSS + repository. + + + RD_PostImage function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory When NO Ticket Provided + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + img_id + + + unsigned integer + + + Image ID + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+ +
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Image ID + + + 403 User Authentification Error. + + + 404 Unauthorized or No Such Image + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/rivwebcapi/rd_postpodcast.xml b/docs/rivwebcapi/rd_postpodcast.xml new file mode 100644 index 00000000..38b2075e --- /dev/null +++ b/docs/rivwebcapi/rd_postpodcast.xml @@ -0,0 +1,188 @@ + + + + + RD_PostPodcast + 7 + September 2020 + Rivendell C Library Manual + + + rd_postpodcast + Rivendell Post RSS item audio Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_postpodcast.h> + + int RD_PostPodcast + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned cast_id + const char user_agent[] + + + + + + Description + + RD_PostPodcast is the function to use + to post RSS audio from the audio store to the remote RSS repository. + + + RD_PostPodcast function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory When NO Ticket Provided + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + cast_id + + + unsigned integer + + + Podcast ID + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+ +
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Cast ID + + + 403 User Authentification Error. + + + 404 Unauthorized or No Such Podcast + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/rivwebcapi/rd_postrss.xml b/docs/rivwebcapi/rd_postrss.xml new file mode 100644 index 00000000..664b6bfd --- /dev/null +++ b/docs/rivwebcapi/rd_postrss.xml @@ -0,0 +1,188 @@ + + + + + RD_PostRss + 7 + September 2020 + Rivendell C Library Manual + + + rd_postrss + Rivendell Post RSS XML Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_postrss.h> + + int RD_PostRss + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned feed_id + const char user_agent[] + + + + + + Description + + RD_PostRss is the function to use + to post the current RSS XML to the remote RSS repository. + + + RD_PostRss function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory When NO Ticket Provided + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + feed_id + + + unsigned integer + + + Feed ID + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+ +
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Feed ID + + + 403 User Authentification Error. + + + 404 Unauthorized or No Such Feed + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/rivwebcapi/rd_removeimage.xml b/docs/rivwebcapi/rd_removeimage.xml new file mode 100644 index 00000000..a6c982b4 --- /dev/null +++ b/docs/rivwebcapi/rd_removeimage.xml @@ -0,0 +1,188 @@ + + + + + RD_RemoveImage + 7 + September 2020 + Rivendell C Library Manual + + + rd_removeimage + Rivendell Remove RSS Image C Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_removeimage.h> + + int RD_RemoveImage + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned img_id + const char user_agent[] + + + + + + Description + + RD_RemoveImage is the function to use + to remve an RSS image from the remote RSS repository. + + + RD_RemoveImage function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory When NO Ticket Provided + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + img_id + + + unsigned integer + + + Image ID + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+ +
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Image ID + + + 403 User Authentification Error. + + + 404 Unauthorized or No Such Image + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/rivwebcapi/rd_removepodcast.xml b/docs/rivwebcapi/rd_removepodcast.xml new file mode 100644 index 00000000..6af40778 --- /dev/null +++ b/docs/rivwebcapi/rd_removepodcast.xml @@ -0,0 +1,188 @@ + + + + + RD_RemovePodcast + 7 + September 2020 + Rivendell C Library Manual + + + rd_removepodcast + Rivendell Remove RSS item audio C Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_removepodcast.h> + + int RD_RemovePodcast + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned cast_id + const char user_agent[] + + + + + + Description + + RD_RemovePodcast is the function to use + to remove RSS item audio from the remote RSS repository. + + + RD_RemovePodcast function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory When NO Ticket Provided + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + cast_id + + + unsigned integer + + + Podcast ID + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+ +
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Podcast ID + + + 403 User Authentification Error. + + + 404 Unauthorized or No Such Podcast + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/rivwebcapi/rd_removerss.xml b/docs/rivwebcapi/rd_removerss.xml new file mode 100644 index 00000000..dc88dbee --- /dev/null +++ b/docs/rivwebcapi/rd_removerss.xml @@ -0,0 +1,188 @@ + + + + + RD_RemoveRss + 7 + September 2020 + Rivendell C Library Manual + + + rd_removerss + Rivendell Remove RSS XML C Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_removerss.h> + + int RD_RemoveRss + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned feed_id + const char user_agent[] + + + + + + Description + + RD_RemoveRss is the function to use + to remove the RSS XML from the remote RSS repository. + + + RD_RemoveRss function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory When NO Ticket Provided + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + feed_id + + + unsigned integer + + + Feed ID + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+ +
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Feed ID + + + 403 User Authentification Error. + + + 404 Unauthorized or No Such Feed + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/rivwebcapi/rd_savepodcast.xml b/docs/rivwebcapi/rd_savepodcast.xml new file mode 100644 index 00000000..c53a26f6 --- /dev/null +++ b/docs/rivwebcapi/rd_savepodcast.xml @@ -0,0 +1,205 @@ + + + + + RD_SavePodcast + 7 + October 2020 + Rivendell C Library Manual + + + rd_savepodcast + Rivendell Save Podcast C Library Function + + + + + Fred + Gleason + fredg@paravelsystems.com + + Rivendell C Library Author + + + + + + + #include <rivwebcapi/rd_savepodcast.h> + + int RD_SavePodcast + const char hostname[] + const char username[] + const char passwd[] + const char ticket[] + const unsigned cast_id + const char filename[] + const char user_agent[] + + + + + + Description + + RD_SavePodcast is the function to use + to copy finished RSS item audio to the Rivendell audio store. + + + RD_SavePodcast function call fields + + + + + + + + + FIELD NAME + + + FIELD TYPE + + + MEANING + + + REMARKS + + + + + + + hostname + + + Character Array + + + Name Of Rivendell DB Host + + + Mandatory When NO Ticket Provided + + + + + username + + + Character Array + + + Rivendell User Name + + + Mandatory + + + + + passwd + + + Character Array + + + Rivendell User Password + + + Mandatory When NO Ticket Provided + + + + + ticket + + + Character Array + + + Rivendell Authentification Ticket + + + Mandatory When NO User/Password Pair Provided. + + + + + cast_id + + + unsigned integer + + + ID of podcast item + + + Mandatory + + + + + filename + + + character array + + + File Name for Source Audio + + + Mandatory + + + + + user_agent + + + Character Array + + + User Agent Value put into HTTP request + + + Optional (default is Rivendell-C-API/x.x.x) + + + + +
+
+ RETURN VALUE + + On success, zero is returned. + + + If a server error occurs a -1 is returned. + If a client error occurs a specific error number is returned. + + + ERRORS + + 400 Missing Podcast ID + + + 400 Missing filename + + + 403 User Authentification Error. + + + 404 Unauthorized or no such podcast + + + nnn Unknown Error Occurred. + + + +
diff --git a/docs/tables/Makefile.am b/docs/tables/Makefile.am index a4d60aa6..87920c8d 100644 --- a/docs/tables/Makefile.am +++ b/docs/tables/Makefile.am @@ -2,7 +2,7 @@ ## ## docs/tables/automake.am for Rivendell ## -## (C) Copyright 2002-2018 Fred Gleason +## (C) Copyright 2002-2020 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 @@ -28,7 +28,6 @@ EXTRA_DIST = audio_cards.txt\ aux_metadata.txt\ cart.txt\ cartslots.txt\ - cast_downloads.txt\ clipboard.txt\ clock_lines.txt\ clock_perms.txt\ @@ -41,15 +40,12 @@ EXTRA_DIST = audio_cards.txt\ dropbox_paths.txt\ dropbox_sched_codes.txt\ elr_lines.txt\ - encoder_bitrates.txt\ - encoder_channels.txt\ - encoder_samplerates.txt\ - encoders.txt\ event_lines.txt\ event_perms.txt\ events.txt\ extended_panel_names.txt\ extended_panels.txt\ + feed_images.txt\ feed_perms.txt\ feeds.txt\ gpio_events.txt\ @@ -99,6 +95,7 @@ EXTRA_DIST = audio_cards.txt\ stack_lines.txt\ stack_sched_codes.txt\ stations.txt\ + superfeed_maps.txt\ switcher_nodes.txt\ system.txt\ triggers.txt\ diff --git a/docs/tables/cast_downloads.txt b/docs/tables/cast_downloads.txt deleted file mode 100644 index c6016e22..00000000 --- a/docs/tables/cast_downloads.txt +++ /dev/null @@ -1,12 +0,0 @@ - CAST_DOWNLOADS Table Layout for Rivendell - -The CAST_DOWNLOADS table holds data concerning downloads of podcast episodes. - - -FIELD NAME TYPE REMARKS ------------------------------------------------------------------------------- -ID int(10) unsigned Primary key, Auto Increment -FEED_KEY_NAME varchar(8) From FEEDS.KEY_NAME -CAST_ID int(10) unsigned From PODCASTS.ID -ACCESS_DATE date -ACCESS_COUNT int(10) unsigned diff --git a/docs/tables/encoder_bitrates.txt b/docs/tables/encoder_bitrates.txt deleted file mode 100644 index eb836f70..00000000 --- a/docs/tables/encoder_bitrates.txt +++ /dev/null @@ -1,11 +0,0 @@ - ENCODER_BITRATES Table Layout for Rivendell - -The ENCODER_BITRATES table holds data concerning the allowed bit rates -in bits/sec for each custom encoder format on the system. -Following is the layout of a record in the ENCODER_BITRATES table: - -FIELD NAME TYPE REMARKS -------------------------------------------------------------------- -ID int(11) -ENCODER_ID int(11) From ENCODERS.ID -BITRATE int(11) diff --git a/docs/tables/encoder_channels.txt b/docs/tables/encoder_channels.txt deleted file mode 100644 index 215acf24..00000000 --- a/docs/tables/encoder_channels.txt +++ /dev/null @@ -1,11 +0,0 @@ - ENCODER_CHANNELS Table Layout for Rivendell - -The ENCODER_CHANNELS table holds data concerning the allowed channel -count for each custom encoder format on the system. -Following is the layout of a record in the ENCODER_CHANNELS table: - -FIELD NAME TYPE REMARKS -------------------------------------------------------------------- -ID int(11) -ENCODER_ID int(11) From ENCODERS.ID -CHANNELS int(11) diff --git a/docs/tables/encoder_samplerates.txt b/docs/tables/encoder_samplerates.txt deleted file mode 100644 index 4594dd2b..00000000 --- a/docs/tables/encoder_samplerates.txt +++ /dev/null @@ -1,11 +0,0 @@ - ENCODER_SAMPLERATES Table Layout for Rivendell - -The ENCODER_SAMPLERATES table holds data concerning the allowed -sample rates in samples/sec for each custom encoder format on the system. -Following is the layout of a record in the ENCODER_SAMPLERATES table: - -FIELD NAME TYPE REMARKS -------------------------------------------------------------------- -ID int(11) -ENCODER_ID int(11) From ENCODERS.ID -SAMPLERATE int(11) diff --git a/docs/tables/encoders.txt b/docs/tables/encoders.txt deleted file mode 100644 index 2a314446..00000000 --- a/docs/tables/encoders.txt +++ /dev/null @@ -1,12 +0,0 @@ - ENCODERS Table Layout for Rivendell - -The EVENTS table holds data concerning each custom encoder format -on the system. - -FIELD NAME TYPE REMARKS -------------------------------------------------------------------- -ID int(11) -NAME varchar(32) -STATION_NAME varchar(64) From STATIONS.NAME -COMMAND_LINE varchar(255) -DEFAULT_EXTENSION varchar(16) diff --git a/docs/tables/feed_images.txt b/docs/tables/feed_images.txt new file mode 100644 index 00000000..ede19257 --- /dev/null +++ b/docs/tables/feed_images.txt @@ -0,0 +1,16 @@ + FEED_IMAGES Table Layout for Rivendell + +The FEED_IMAGES table holds binary images used by podcast feeds. + + +FIELD NAME TYPE REMARKS +--------------------------------------------------------------- +ID int(10) unsigned Primary key, auto increment +FEED_ID int(10) unsigned From FEEDS.ID +FEED_KEY_NAME varchar(8) From FEEDS.KEY_NAME +WIDTH int(11) signed Pixels +HEIGHT int(11) signed Pixels +DEPTH int(11) signed Bits/pixel +DESCRIPTION varchar(191) +FILE_EXTENSION varchar(10) +DATA longblob diff --git a/docs/tables/feeds.txt b/docs/tables/feeds.txt index 92694e6c..7afb56fe 100644 --- a/docs/tables/feeds.txt +++ b/docs/tables/feeds.txt @@ -2,37 +2,46 @@ The FEEDS table holds data concerning which defines RSS feeds. -FIELD NAME TYPE REMARKS +FIELD NAME TYPE REMARKS --------------------------------------------------------------- -ID int(10) unsigned Primary key, auto increment -KEY_NAME varchar(8) Unique -CHANNEL_TITLE varchar(191) -CHANNEL_DESCRIPTION text -CHANNEL_CATEGORY varchar(64) -CHANNEL_LINK varchar(191) -CHANNEL_COPYRIGHT varchar(64) -CHANNEL_WEBMASTER varchar(64) -CHANNEL_LANGUAGE varchar(5) -BASE_URL varchar(191) -BASE_PREAMBLE varchar(191) -PURGE_URL varchar(191) -PURGE_USERNAME varchar(64) -PURGE_PASSWORD varchar(64) -HEADER_XML text -CHANNEL_XML text -ITEM_XML text -CAST_ORDER enum('N','Y') -MAX_SHELF_LIFE int(11) -LAST_BUILD_DATETIME datetime -ORIGIN_DATETIME datetime -ENABLE_AUTOPOST enum('N','Y') -KEEP_METADATA enum('N','Y') -UPLOAD_FORMAT int(11) -UPLOAD_CHANNELS int(11) -UPLOAD_SAMPRATE int(11) -UPLOAD_BITRATE int(11) -UPLOAD_QUALITY int(11) -UPLOAD_EXTENSION varchar(16) -NORMALIZE_LEVEL int(11) -REDIRECT_PATH varchar(191) -MEDIA_LINK_MODE int(11) +ID int(10) unsigned Primary key, auto increment +KEY_NAME varchar(8) Unique +IS_SUPERFEED enum('N','Y') +CHANNEL_TITLE varchar(191) +CHANNEL_DESCRIPTION text +CHANNEL_CATEGORY varchar(64) +CHANNEL_SUB_CATEGORY varchar(64) +CHANNEL_LINK varchar(191) +CHANNEL_COPYRIGHT varchar(64) +CHANNEL_EDITOR varchar(64) +CHANNEL_AUTHOR varchar(64) +CHANNEL_AUTHOR_IS_DEFAULT enum('N','Y') +CHANNEL_OWNER_NAME varchar(64) +CHANNEL_OWNER_EMAIL varchar(64) +CHANNEL_WEBMASTER varchar(64) +CHANNEL_LANGUAGE varchar(8) +CHANNEL_EXPLICIT enum('N','Y') +CHANNEL_IMAGE_ID int(11) From FEED_IMAGES.ID +BASE_URL varchar(191) +BASE_PREAMBLE varchar(191) +PURGE_URL varchar(191) +PURGE_USERNAME varchar(64) +PURGE_PASSWORD text Base64 encoded +PURGE_USE_ID_FILE enum('N','Y') +RSS_SCHEMA int(10) unsigned 0=Custom, 1=RSS-2.0.2 +HEADER_XML text +CHANNEL_XML text +ITEM_XML text +CAST_ORDER enum('N','Y') +MAX_SHELF_LIFE int(11) +LAST_BUILD_DATETIME datetime +ORIGIN_DATETIME datetime +ENABLE_AUTOPOST enum('N','Y') +DEFAULT_ITEM_IMAGE_ID int(11) From FEED_IMAGES.ID +UPLOAD_FORMAT int(11) +UPLOAD_CHANNELS int(11) +UPLOAD_SAMPRATE int(11) +UPLOAD_BITRATE int(11) +UPLOAD_QUALITY int(11) +UPLOAD_EXTENSION varchar(16) +NORMALIZE_LEVEL int(11) diff --git a/docs/tables/podcasts.txt b/docs/tables/podcasts.txt index 9bcf3fe7..6cbddc19 100644 --- a/docs/tables/podcasts.txt +++ b/docs/tables/podcasts.txt @@ -15,9 +15,14 @@ ITEM_COMMENTS varchar(191) ITEM_AUTHOR varchar(191) ITEM_SOURCE_TEXT varchar(64) ITEM_SOURCE_URL varchar(191) +ITEM_EXPLICIT enum('N','Y') +ITEM_IMAGE_ID int(11) From FEED_IMAGES.ID AUDIO_FILENAME varchar(191) AUDIO_LENGTH int(10) unsigned AUDIO_TIME int(10) unsigned -SHELF_LIFE int(11) +SHA1_HASH varchar(40) +ORIGIN_LOGIN_NAME varchar(191) From USERS.LOGIN_NAME +ORIGIN_STATION varchar(64) From STATIONS.NAME ORIGIN_DATETIME datetime EFFECTIVE_DATETIME datetime +EXPIRATION_DATETIME datetime diff --git a/docs/tables/recordings.txt b/docs/tables/recordings.txt index 5441d300..4719af7e 100644 --- a/docs/tables/recordings.txt +++ b/docs/tables/recordings.txt @@ -60,5 +60,6 @@ ONE_SHOT enum ('N','Y') URL varchar(255) URL_USERNAME varchar(64) URL_PASSWORD varchar(64) +URL_USE_ID_FILE enum('N','Y') ENABLE_METADATA enum('N','Y') FEED_ID int From FEEDS.ID diff --git a/docs/tables/stations.txt b/docs/tables/stations.txt index 8109cabf..96c147a7 100644 --- a/docs/tables/stations.txt +++ b/docs/tables/stations.txt @@ -20,6 +20,7 @@ STARTUP_CART int(10) unsigned EDITOR_PATH varchar(191) Audio editor --e.g. Audacity REPORT_EDITOR_PATH varchar(191) Text Editor --e.g. GEdit BROWSER_PATH varchar(191) Web Browser --e.g. Firefox +SSH_IDENTITY_FILE text FILTER_MODE int(11) 0=Synchronous, 1=Asynchronous START_JACK enum('Y','N') JACK_SERVER_NAME varchar(64) diff --git a/docs/tables/superfeed_maps.txt b/docs/tables/superfeed_maps.txt new file mode 100644 index 00000000..e3b74a89 --- /dev/null +++ b/docs/tables/superfeed_maps.txt @@ -0,0 +1,12 @@ + SUPERFEED_MAPS Table Layout for Rivendell + +The SUPERFEED_MAPS table contains the mappings of actual RSS feeds with super +feeds. + +FIELD NAME TYPE REMARKS +------------------------------------------------------------------ +ID int(10) unsigned Primary key, auto increment +FEED_ID int(10) unsigned From FEEDS.ID +MEMBER_FEED_ID int(10) unsigned From FEEDS.ID +KEY_NAME varchar(8) From FEEDS.KEY_NAME +MEMBER_KEY_NAME varchar(8) From FEEDS.KEY_NAME diff --git a/docs/tables/system.txt b/docs/tables/system.txt index 14341f30..3c5560f7 100644 --- a/docs/tables/system.txt +++ b/docs/tables/system.txt @@ -2,14 +2,15 @@ The SYSTEM table contains system-wide settings. -FIELD NAME TYPE REMARKS +FIELD NAME TYPE REMARKS --------------------------------------------------------------- -ID int(11) Auto increment, primary key -SAMPLE_RATE int(10) unsigned -DUP_CART_TITLE enum('N','Y') -FIX_DUP_CART_TITLES enum('N','Y') -MAX_POST_LENGTH int(10) unsigned -ISCI_XREFERENCE_PATH varchar(191) -TEMP_CART_GROUP varchar(10) From 'GROUPS.NAME' -SHOW_USER_LIST enum('N','Y') -NOTIFICATION_ADDRESS varchar(15) +ID int(11) Auto increment, primary key +SAMPLE_RATE int(10) unsigned +DUP_CART_TITLE enum('N','Y') +FIX_DUP_CART_TITLES enum('N','Y') +MAX_POST_LENGTH int(10) unsigned +ISCI_XREFERENCE_PATH varchar(191) +TEMP_CART_GROUP varchar(10) From 'GROUPS.NAME' +SHOW_USER_LIST enum('N','Y') +NOTIFICATION_ADDRESS varchar(15) +RSS_PROCESSOR_STATION varchar(64) diff --git a/docs/tables/users.txt b/docs/tables/users.txt index 0925484e..f06b3c3e 100644 --- a/docs/tables/users.txt +++ b/docs/tables/users.txt @@ -5,10 +5,11 @@ on the system. FIELD NAME TYPE REMARKS -------------------------------------------------------------------------- -LOGIN_NAME varchar(255) Primary key -FULL_NAME varchar(255) Indexed +LOGIN_NAME varchar(191) Primary key +FULL_NAME varchar(191) Indexed +EMAIL_ADDRESS varchar(191) PHONE_NUMBER varchar(20) -DESCRIPTION varchar(255) +DESCRIPTION varchar(191) PASSWORD varchar(32) Not-NULL, Hashed WEBAPI_AUTH_TIMEOUT int(11) signed Seconds ENABLE_WEB enum('N','Y') diff --git a/icons/Makefile.am b/icons/Makefile.am index c484ca7b..6c36ce95 100644 --- a/icons/Makefile.am +++ b/icons/Makefile.am @@ -24,6 +24,7 @@ install-exec-am: mkdir -p $(DESTDIR)@libexecdir@ cp greencheckmark.png $(DESTDIR)@libexecdir@ cp redx.png $(DESTDIR)@libexecdir@ + cp blueball.png $(DESTDIR)@libexecdir@ cp greenball.png $(DESTDIR)@libexecdir@ cp redball.png $(DESTDIR)@libexecdir@ cp whiteball.png $(DESTDIR)@libexecdir@ @@ -188,6 +189,7 @@ install-exec-am: uninstall-local: rm -f $(DESTDIR)@libexecdir@/greencheckmark.png rm -f $(DESTDIR)@libexecdir@/redx.png + rm -f $(DESTDIR)@libexecdir@/blueball.png rm -f $(DESTDIR)@libexecdir@/greenball.png rm -f $(DESTDIR)@libexecdir@/redball.png rm -f $(DESTDIR)@libexecdir@/whiteball.png @@ -266,6 +268,7 @@ uninstall-local: EXTRA_DIST = admin.xpm\ + blueball.png\ blueball.xpm\ chain.png\ chain.xpm\ @@ -463,6 +466,9 @@ EXTRA_DIST = admin.xpm\ rml3.xpm\ rml5.png\ rml5.xpm\ + rss.png\ + rss.xcf\ + rss.xpm\ split.xpm\ switch.xpm\ switch2.xpm\ diff --git a/icons/blueball.png b/icons/blueball.png new file mode 100644 index 00000000..698c3809 Binary files /dev/null and b/icons/blueball.png differ diff --git a/icons/rss.png b/icons/rss.png new file mode 100644 index 00000000..dbee8840 Binary files /dev/null and b/icons/rss.png differ diff --git a/icons/rss.xcf b/icons/rss.xcf new file mode 100644 index 00000000..54dcc5b7 Binary files /dev/null and b/icons/rss.xcf differ diff --git a/icons/rss.xpm b/icons/rss.xpm new file mode 100644 index 00000000..3cd38bf1 --- /dev/null +++ b/icons/rss.xpm @@ -0,0 +1,115 @@ +/* XPM */ +const static char * rss_xpm[] = { +"32 32 80 1", +" c None", +". c #FEFEFF", +"+ c #F6F6FF", +"@ c #F7F7FF", +"# c #F5F5FF", +"$ c #F4F4FF", +"% c #F3F3FF", +"& c #F2F2FF", +"* c #0000F9", +"= c #0101F9", +"- c #0202F9", +"; c #E3E3FF", +"> c #FAFAFF", +", c #FFFFFF", +"' c #1B1BF9", +") c #0202F8", +"! c #1616F9", +"~ c #5353FA", +"{ c #E2E2FD", +"] c #A3A3FC", +"^ c #F6F6FE", +"/ c #5656FA", +"( c #4C4CFA", +"_ c #9898FC", +": c #2626F9", +"< c #F9F9FF", +"[ c #7272FB", +"} c #4747FA", +"| c #FCFCFE", +"1 c #DDDDFD", +"2 c #8B8BFB", +"3 c #9292FB", +"4 c #0F0FF8", +"5 c #F5F5FE", +"6 c #F1F1FE", +"7 c #0B0BF8", +"8 c #9C9CFC", +"9 c #6363FA", +"0 c #3434F9", +"a c #DFDFFD", +"b c #5252FA", +"c c #1515F8", +"d c #0404F8", +"e c #1010F8", +"f c #2D2DF9", +"g c #DCDCFD", +"h c #F8F8FF", +"i c #3232F9", +"j c #C2C2FD", +"k c #CBCBFD", +"l c #5D5DFA", +"m c #D2D2FD", +"n c #EBEBFE", +"o c #0707F8", +"p c #F4F4FE", +"q c #0303F8", +"r c #1F1FF9", +"s c #7070FB", +"t c #B4B4FC", +"u c #F7F7FE", +"v c #8080FB", +"w c #1D1DF9", +"x c #D3D3FD", +"y c #CECEFD", +"z c #7979FB", +"A c #2F2FF9", +"B c #5959FA", +"C c #F0F0FE", +"D c #4242FA", +"E c #B1B1FC", +"F c #E8E8FD", +"G c #5454FA", +"H c #8F8FFB", +"I c #4949FA", +"J c #0A0AF8", +"K c #0808F8", +"L c #2525F9", +"M c #8282FB", +"N c #FDFDFE", +"O c #EBEBFF", +".+@@@++++#####$$$$%%%%%&&%%%%%%&", +"$*======================------*;", +">*,,,,,,,,,,,,,,,,,,,,,,,,,,,,*$", +">*,,,,,,,,,,,,,,,,,,,,,,,,,,,,*$", +">*,,'**)!~{,,,,,,,,,,,,,,,,,,,*$", +">*,,'*]^/*(,,,,,,,,,,,,,,,,,,,*$", +">*,,'*],_*:,,,,,,,,,,,,,,,,,,,*$", +"<*,,'*]^/*[,,,,,,,,,,,,,,,,,,,*$", +"<*,,'****}|,,,,,,,,,,,,,,,,,,,*$", +"<*,,'*]1'*2,,,,,,,,,,,,,,,,,,,*$", +"<*,,'*],3*45,,,,,,,,,,,,,,,,,,*$", +"<*,,'*],67*8,,,,,,,,,,,,,,,,,,*$", +"<*,,'*],,9*0,abcdefg,,,,,,,,,,*$", +"h*,,,,,,,,,,,i)j5klm,,,,,,,,,,*$", +"h*,,,,,,,,,,n*o6,,,,,,,,,,,,,,*$", +"h*,,,,,,,,,,pq*rstu,,,,,,,,,,,*$", +"h*,,,,,,,,,,,v****wx,,,,,,,,,,*%", +"h*,,,,,,,,,,,,yzA**B,,,,,,,,,,*$", +"h*,,,,,,,,,,,,,,,[*(,,,,,,,,,,*$", +"h*,,,,,,,,,,CDEFCG*H,abcdefg,,*$", +"@*,,,,,,,,,,^IrJKLMN,i)j5klm,,*$", +"@*,,,,,,,,,,,,,,,,,,n*o6,,,,,,*#", +"@*,,,,,,,,,,,,,,,,,,pq*rstu,,,*#", +"@*,,,,,,,,,,,,,,,,,,,v****wx,,*#", +"@*,,,,,,,,,,,,,,,,,,,,yzA**B,,*#", +"@*,,,,,,,,,,,,,,,,,,,,,,,[*(,,*#", +"+*,,,,,,,,,,,,,,,,,,CDEFCG*H,,*#", +"+*,,,,,,,,,,,,,,,,,,^IrJKLMN,,*#", +"+*,,,,,,,,,,,,,,,,,,,,,,,,,,,,*#", +"+*,,,,,,,,,,,,,,,,,,,,,,,,,,,,*#", +"+******************************O", +",#####+++++################$$###"}; diff --git a/lib/Makefile.am b/lib/Makefile.am index 289624b0..ba4aaa07 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -128,7 +128,6 @@ dist_librd_la_SOURCES = dbversion.h\ rdeventimportlist.cpp rdeventimportlist.h\ rdexport_settings_dialog.cpp rdexport_settings_dialog.h\ rdfeed.cpp rdfeed.h\ - rdfeedlog.cpp rdfeedlog.h\ rdfontengine.cpp rdfontengine.h\ rdformpost.cpp rdformpost.h\ rdflacdecode.cpp rdflacdecode.h\ @@ -145,6 +144,8 @@ dist_librd_la_SOURCES = dbversion.h\ rdhotkeys.cpp rdhotkeys.h\ rdhotkeylist.cpp rdhotkeylist.h\ rdidvalidator.cpp rdidvalidator.h\ + rdimagepickerbox.cpp rdimagepickerbox.h\ + rdimagepickermodel.cpp rdimagepickermodel.h\ rdimport_audio.cpp rdimport_audio.h\ rdinstancelock.cpp rdinstancelock.h\ rd.h\ @@ -162,6 +163,7 @@ dist_librd_la_SOURCES = dbversion.h\ rdlivewiresource.cpp rdlivewiresource.h\ rdlog.cpp rdlog.h\ rdlog_event.cpp rdlog_event.h\ + rdlog_icons.cpp rdlog_icons.h\ rdlog_line.cpp rdlog_line.h\ rdlogedit_conf.cpp rdlogedit_conf.h\ rdlogfilter.cpp rdlogfilter.h\ @@ -199,6 +201,8 @@ dist_librd_la_SOURCES = dbversion.h\ rdreport.cpp rdreport.h\ rdringbuffer.cpp rdringbuffer.h\ rdripc.cpp rdripc.h\ + rdrssschemas.cpp rdrssschemas.h\ + rdrsscategorybox.cpp rdrsscategorybox.h\ rdschedcartlist.cpp rdschedcartlist.h\ rdschedcode.cpp rdschedcode.h\ rdschedcodes_dialog.cpp rdschedcodes_dialog.h\ @@ -227,6 +231,7 @@ dist_librd_la_SOURCES = dbversion.h\ rdtimeengine.cpp rdtimeengine.h\ rdtimeevent.cpp rdtimeevent.h\ rdtransportbutton.cpp rdtransportbutton.h\ + rdtransfer.cpp rdtransfer.h\ rdtrimaudio.cpp rdtrimaudio.h\ rdtty.cpp rdtty.h\ rdttydevice.cpp rdttydevice.h\ @@ -293,6 +298,8 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\ moc_rdgpioselector.cpp\ moc_rdhotkeys.cpp\ moc_rdhotkeylist.cpp\ + moc_rdimagepickerbox.cpp\ + moc_rdimagepickermodel.cpp\ moc_rdimport_audio.cpp\ moc_rdkernelgpio.cpp\ moc_rdlineedit.cpp\ @@ -321,6 +328,7 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\ moc_rdrenderer.cpp\ moc_rdripc.cpp\ moc_rdschedcodes_dialog.cpp\ + moc_rdrsscategorybox.cpp\ moc_rdsegmeter.cpp\ moc_rdsimpleplayer.cpp\ moc_rdslider.cpp\ @@ -332,6 +340,7 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\ moc_rdsvc.cpp\ moc_rdtimeedit.cpp\ moc_rdtimeengine.cpp\ + moc_rdtransfer.cpp\ moc_rdtransportbutton.cpp\ moc_rdtrimaudio.cpp\ moc_rdttydevice.cpp\ diff --git a/lib/dbversion.h b/lib/dbversion.h index cf8bf983..66ee685f 100644 --- a/lib/dbversion.h +++ b/lib/dbversion.h @@ -24,7 +24,7 @@ /* * Current Database Version */ -#define RD_VERSION_DATABASE 317 +#define RD_VERSION_DATABASE 338 #endif // DBVERSION_H diff --git a/lib/lib.pro b/lib/lib.pro index 7ef0fe46..e259cb54 100644 --- a/lib/lib.pro +++ b/lib/lib.pro @@ -104,6 +104,8 @@ SOURCES += rdgroup.cpp SOURCES += rdgroup_list.cpp SOURCES += rdhash.cpp SOURCES += rdidvalidator.cpp +SOURCES += rdimagepickerbox.cpp +SOURCES += rdimagepickermodel.cpp SOURCES += rdimport_audio.cpp SOURCES += rdkernelgpio.cpp SOURCES += rdlibrary_conf.cpp @@ -115,6 +117,7 @@ SOURCES += rdlistview.cpp SOURCES += rdlistviewitem.cpp SOURCES += rdlog.cpp SOURCES += rdlog_event.cpp +SOURCES += rdlog_icons.cpp SOURCES += rdlog_line.cpp SOURCES += rdlogedit_conf.cpp SOURCES += rdlogfilter.cpp @@ -141,6 +144,8 @@ SOURCES += rdrehash.cpp SOURCES += rdrenderer.cpp SOURCES += rdreport.cpp SOURCES += rdripc.cpp +SOURCES += rdrssschemas.cpp +SOURCES += rdrsscategorybox.cpp SOURCES += rdschedcode.cpp SOURCES += rdsegmeter.cpp SOURCES += rdsettings.cpp @@ -242,6 +247,8 @@ HEADERS += rdgroup_list.h HEADERS += rdgroup.h HEADERS += rdhash.h HEADERS += rdidvalidator.h +HEADERS += rdimagepickerbox.h +HEADERS += rdimagepickermodel.h HEADERS += rdimport_audio.h HEADERS += rdkernelgpio.h HEADERS += rdlibrary_conf.h @@ -253,6 +260,7 @@ HEADERS += rdlistview.h HEADERS += rdlistviewitem.h HEADERS += rdlog.h HEADERS += rdlog_event.h +HEADERS += rdlog_icons.h HEADERS += rdlog_line.h HEADERS += rdlogedit_conf.h HEADERS += rdlogfilter.h @@ -280,6 +288,8 @@ HEADERS += rdrehash.h HEADERS += rdrenderer.h HEADERS += rdreport.h HEADERS += rdripc.h +HEADERS += rdrssschemas.h +HEADERS += rdrsscategorybox.h HEADERS += rdschedcode.h HEADERS += rdsegmeter.h HEADERS += rdsettings.h diff --git a/lib/librd_cs.ts b/lib/librd_cs.ts index 71d8f0fe..23a3e2c7 100644 --- a/lib/librd_cs.ts +++ b/lib/librd_cs.ts @@ -809,6 +809,34 @@ Results Report + + MARKER + + + + TRACK + + + + LOG CHAIN + + + + LINK + + + + [cart not found] + + + + [music link] + + + + [traffic link] + + whitespace is not permitted in service names @@ -2015,6 +2043,13 @@ pro dobu zprávy. PIN: + + RDImagePickerModel + + Image + + + RDImportAudio diff --git a/lib/librd_de.ts b/lib/librd_de.ts index 1661b0b5..8a2200d5 100644 --- a/lib/librd_de.ts +++ b/lib/librd_de.ts @@ -805,6 +805,34 @@ Results Report + + MARKER + + + + TRACK + + + + LOG CHAIN + + + + LINK + + + + [cart not found] + + + + [music link] + + + + [traffic link] + + whitespace is not permitted in service names @@ -2006,6 +2034,13 @@ figure for the report period. PIN: + + RDImagePickerModel + + Image + + + RDImportAudio diff --git a/lib/librd_es.ts b/lib/librd_es.ts index 6c61edf3..59cecc69 100644 --- a/lib/librd_es.ts +++ b/lib/librd_es.ts @@ -805,6 +805,34 @@ Results Report + + MARKER + + + + TRACK + + + + LOG CHAIN + + + + LINK + + + + [cart not found] + + + + [music link] + + + + [traffic link] + + whitespace is not permitted in service names @@ -2004,6 +2032,13 @@ para el período a reportar. Pin: + + RDImagePickerModel + + Image + + + RDImportAudio diff --git a/lib/librd_fr.ts b/lib/librd_fr.ts index 14aab15a..5c4004fa 100644 --- a/lib/librd_fr.ts +++ b/lib/librd_fr.ts @@ -775,6 +775,34 @@ Results Report + + MARKER + + + + TRACK + + + + LOG CHAIN + + + + LINK + + + + [cart not found] + + + + [music link] + + + + [traffic link] + + whitespace is not permitted in service names @@ -1761,6 +1789,13 @@ figure for the report period. + + RDImagePickerModel + + Image + + + RDImportAudio diff --git a/lib/librd_nb.ts b/lib/librd_nb.ts index f53a9dcd..2a1fc45f 100644 --- a/lib/librd_nb.ts +++ b/lib/librd_nb.ts @@ -805,6 +805,34 @@ Results Report + + MARKER + + + + TRACK + + + + LOG CHAIN + + + + LINK + + + + [cart not found] + + + + [music link] + + + + [traffic link] + + whitespace is not permitted in service names @@ -2004,6 +2032,13 @@ for rapportperioden. Pin-nummer: + + RDImagePickerModel + + Image + + + RDImportAudio diff --git a/lib/librd_nn.ts b/lib/librd_nn.ts index f53a9dcd..2a1fc45f 100644 --- a/lib/librd_nn.ts +++ b/lib/librd_nn.ts @@ -805,6 +805,34 @@ Results Report + + MARKER + + + + TRACK + + + + LOG CHAIN + + + + LINK + + + + [cart not found] + + + + [music link] + + + + [traffic link] + + whitespace is not permitted in service names @@ -2004,6 +2032,13 @@ for rapportperioden. Pin-nummer: + + RDImagePickerModel + + Image + + + RDImportAudio diff --git a/lib/librd_pt_BR.ts b/lib/librd_pt_BR.ts index 87d5290a..3e892adf 100644 --- a/lib/librd_pt_BR.ts +++ b/lib/librd_pt_BR.ts @@ -805,6 +805,34 @@ Results Report + + MARKER + + + + TRACK + + + + LOG CHAIN + + + + LINK + + + + [cart not found] + + + + [music link] + + + + [traffic link] + + whitespace is not permitted in service names @@ -2010,6 +2038,13 @@ para o período do relatório Pin: + + RDImagePickerModel + + Image + + + RDImportAudio diff --git a/lib/rd.h b/lib/rd.h index 2b4602f0..ee5af605 100644 --- a/lib/rd.h +++ b/lib/rd.h @@ -448,6 +448,11 @@ */ #define RD_IMAGE_FILE_FILTER "Image Files (*.png *.bmp *.xbm *.xpm *.pbm *.pgm *.ppm *.jpg *.mng *.gif *.PNG *.BMP *.XBM *.XPM *.PBM *.PGM *.PPM *.JPG *.MNG *.GIF)\nAll Files (*.*)" +/* + * Podcast Image File Filter for QFileDialog + */ +#define RD_PODCAST_IMAGE_FILE_FILTER "Image Files (*.png *.jpg *.PNG *.JPG *.jpeg *.JPEG)\nAll Files (*.*)" + /* * Loadable Module Filter for QFileDialog */ @@ -605,4 +610,9 @@ */ #define RD_DEFAULT_SERVICE_TIMEOUT 30 +/* + * File Extension for RSS XML Feed Files + */ +#define RD_RSS_XML_FILE_EXTENSION "rss" + #endif // RD_H diff --git a/lib/rdapplication.cpp b/lib/rdapplication.cpp index e5898b22..e5a39e23 100644 --- a/lib/rdapplication.cpp +++ b/lib/rdapplication.cpp @@ -191,6 +191,7 @@ bool RDApplication::open(QString *err_msg,RDApplication::ErrorType *err_type, // app_station=new RDStation(app_config->stationName()); app_system=new RDSystem(); + app_schemas=new RDRssSchemas(); app_library_conf=new RDLibraryConf(app_config->stationName()); app_logedit_conf=new RDLogeditConf(app_config->stationName()); app_airplay_conf=new RDAirPlayConf(app_config->stationName(),"RDAIRPLAY"); @@ -262,6 +263,12 @@ RDRipc *RDApplication::ripc() } +RDRssSchemas *RDApplication::rssSchemas() +{ + return app_schemas; +} + + RDStation *RDApplication::station() { return app_station; diff --git a/lib/rdapplication.h b/lib/rdapplication.h index b37817d9..51772713 100644 --- a/lib/rdapplication.h +++ b/lib/rdapplication.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -58,10 +59,10 @@ class RDApplication : public QObject RDLogeditConf *logeditConf(); RDAirPlayConf *panelConf(); RDRipc *ripc(); + RDRssSchemas *rssSchemas(); RDStation *station(); RDSystem *system(); RDUser *user(); - // void log(RDConfig::LogPriority prio,const QString &msg); bool dropTable(const QString &tbl_name); void addTempFile(const QString &pathname); void syslog(int priority,const char *fmt,...) const; @@ -83,6 +84,7 @@ class RDApplication : public QObject RDLibraryConf *app_library_conf; RDLogeditConf *app_logedit_conf; RDRipc *app_ripc; + RDRssSchemas *app_schemas; RDStation *app_station; RDSystem *app_system; RDUser *app_user; diff --git a/lib/rdaudioconvert.cpp b/lib/rdaudioconvert.cpp index 59812e4f..53bdb246 100644 --- a/lib/rdaudioconvert.cpp +++ b/lib/rdaudioconvert.cpp @@ -231,7 +231,7 @@ bool RDAudioConvert::settingsValid(RDSettings *settings) QString RDAudioConvert::errorText(RDAudioConvert::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDAudioConvert Error [%u]",err); switch(err) { case RDAudioConvert::ErrorOk: diff --git a/lib/rdaudioinfo.cpp b/lib/rdaudioinfo.cpp index d983dee5..462111a8 100644 --- a/lib/rdaudioinfo.cpp +++ b/lib/rdaudioinfo.cpp @@ -213,7 +213,7 @@ RDAudioInfo::ErrorCode RDAudioInfo::runInfo(const QString &username, QString RDAudioInfo::errorText(RDAudioInfo::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDAudioInfo Error [%u]",err); switch(err) { case RDAudioInfo::ErrorOk: diff --git a/lib/rdaudiostore.cpp b/lib/rdaudiostore.cpp index d60e2443..6df8df71 100644 --- a/lib/rdaudiostore.cpp +++ b/lib/rdaudiostore.cpp @@ -161,7 +161,7 @@ RDAudioStore::ErrorCode RDAudioStore::runStore(const QString &username, QString RDAudioStore::errorText(RDAudioStore::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDAudioStore Error [%u]",err); switch(err) { case RDAudioStore::ErrorOk: diff --git a/lib/rdcastsearch.cpp b/lib/rdcastsearch.cpp index 647c3aa6..ae6fac2f 100644 --- a/lib/rdcastsearch.cpp +++ b/lib/rdcastsearch.cpp @@ -24,8 +24,7 @@ #include #include -QString RDCastSearchString(const QString &filter,bool unexp_only, - bool active_only) +QString RDCastSearchString(const QString &filter,bool active_only) { QString ret; if(!filter.stripWhiteSpace().isEmpty()) { @@ -39,31 +38,46 @@ QString RDCastSearchString(const QString &filter,bool unexp_only, "(PODCASTS.ITEM_SOURCE_TEXT like \"%"+fil+"%\")||"+ "(PODCASTS.ITEM_SOURCE_URL like \"%"+fil+"%\"))"; } - if(unexp_only) { - ret+=QString().sprintf("&&(STATUS!=%d)",RDPodcast::StatusExpired); - } if(active_only) { ret+=QString().sprintf("&&(STATUS=%d)",RDPodcast::StatusActive); } return ret; } -QString RDCastSearch(int feed_id,const QString &filter,bool unexp_only, + +QString RDCastSearch(const QString &keyname,bool is_super,const QString &filter, bool active_only) { - QString ret=QString().sprintf("where (FEED_ID=%d)",feed_id); - ret+=RDCastSearchString(filter,unexp_only,active_only); - - return ret; -} - - -QString RDCastSearch(const QString &keyname,const QString &filter, - bool unexp_only,bool active_only) -{ - QString ret=QString("where (KEY_NAME=\"")+ + QString sql; + RDSqlQuery *q; + QString ret=QString("where (.KEY_NAME=\"")+ RDEscapeString(keyname)+"\")"; - ret+=RDCastSearchString(filter,unexp_only,active_only); + + if(is_super) { + ret="where "; + sql=QString("select ")+ + "MEMBER_FEED_ID "+ // 00 + "from SUPERFEED_MAPS where "+ + "KEY_NAME=\""+RDEscapeString(keyname)+"\""; + q=new RDSqlQuery(sql); + while(q->next()) { + ret+=QString().sprintf("PODCASTS.FEED_ID=%u || ",q->value(0).toUInt()); + } + delete q; + ret=ret.left(ret.length()-3); + } + else { + sql=QString("select ")+ + "ID "+ // 00 + "from FEEDS where "+ + "KEY_NAME=\""+RDEscapeString(keyname)+"\""; + q=new RDSqlQuery(sql); + if(q->first()) { + ret=QString().sprintf("where PODCASTS.FEED_ID=%u ",q->value(0).toUInt()); + } + delete q; + } + ret+=RDCastSearchString(filter,active_only); return ret; } diff --git a/lib/rdcastsearch.h b/lib/rdcastsearch.h index d6adcd2d..b21dbbd1 100644 --- a/lib/rdcastsearch.h +++ b/lib/rdcastsearch.h @@ -2,7 +2,7 @@ // // SQL search clause for RDCastManager // -// (C) Copyright 2009,2016 Fred Gleason +// (C) Copyright 2009-2020 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,10 +21,8 @@ #ifndef RDCASTSEARCH_H #define RDCASTSEARCH_H -QString RDCastSearch(int feed_id,const QString &filter,bool unexp_only, +QString RDCastSearch(const QString &keyname,bool is_super,const QString &filter, bool active_only); -QString RDCastSearch(const QString &keyname,const QString &filter, - bool unexp_only,bool active_only); #endif // RDCASTSEARCH_H diff --git a/lib/rdcdripper.cpp b/lib/rdcdripper.cpp index 444c5b5a..1b369917 100644 --- a/lib/rdcdripper.cpp +++ b/lib/rdcdripper.cpp @@ -152,7 +152,7 @@ RDCdRipper::ErrorCode RDCdRipper::rip(int first_track,int last_track) QString RDCdRipper::errorText(RDCdRipper::ErrorCode err) { - QString ret="Unknown Error"; + QString ret=QString().sprintf("Unknown RDCdRipper Error [%d]",err); switch(err) { case RDCdRipper::ErrorOk: diff --git a/lib/rddelete.cpp b/lib/rddelete.cpp index e9bcbd13..8d392ba5 100644 --- a/lib/rddelete.cpp +++ b/lib/rddelete.cpp @@ -68,10 +68,22 @@ int DeleteErrorCallback(CURL *curl,curl_infotype type,char *msg,size_t size, } -RDDelete::RDDelete(RDConfig *config,QObject *parent) - : QObject(parent) +RDDelete::RDDelete(RDConfig *c,QObject *parent) + : RDTransfer(c,parent) { - conv_config=config; +} + + +QStringList RDDelete::supportedSchemes() const +{ + QStringList schemes; + + schemes.push_back("file"); + schemes.push_back("ftp"); + schemes.push_back("sftp"); + schemes.push_back("ftps"); + + return schemes; } @@ -83,6 +95,8 @@ void RDDelete::setTargetUrl(const QString &url) RDDelete::ErrorCode RDDelete::runDelete(const QString &username, const QString &password, + const QString &id_filename, + bool use_id_filename, bool log_debug) { CURL *curl=NULL; @@ -90,44 +104,76 @@ RDDelete::ErrorCode RDDelete::runDelete(const QString &username, CURLcode err; RDDelete::ErrorCode ret=RDDelete::ErrorOk; QString currentdir; - char urlstr[1024]; - char userpwd[256]; + QString filename; QString xml=""; + char userpwd[256]; + + if(!urlIsSupported(conv_target_url)) { + return RDDelete::ErrorUnsupportedUrlScheme; + } + + if(conv_target_url.scheme().toLower()=="file") { + unlink(conv_target_url.path().toUtf8().constData()); + return RDDelete::ErrorOk; + } if((curl=curl_easy_init())==NULL) { rda->syslog(LOG_ERR,"unable to initialize curl library\n"); return RDDelete::ErrorInternal; } - strncpy(urlstr,(const char *)(conv_target_url.protocol()+"://"+ - conv_target_url.host()+"/").utf8(),1024); - curl_easy_setopt(curl,CURLOPT_URL,urlstr); - strncpy(userpwd,(QString(username)+":"+password).utf8(),256); - curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); + + // + // Authentication + // + if((conv_target_url.scheme().toLower()=="sftp")&& + (!id_filename.isEmpty())&&use_id_filename) { + curl_easy_setopt(curl,CURLOPT_USERNAME,username.toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE, + id_filename.toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_KEYPASSWD,password.toUtf8().constData()); + } + else { + strncpy(userpwd,(username+":"+password).utf8(),256); + curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); + } + + curl_easy_setopt(curl,CURLOPT_URL,conv_target_url.toEncoded().constData()); curl_easy_setopt(curl,CURLOPT_HTTPAUTH,CURLAUTH_ANY); curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,DeleteWriteCallback); curl_easy_setopt(curl,CURLOPT_WRITEDATA,&xml); curl_easy_setopt(curl,CURLOPT_USERAGENT, - (const char *)conv_config->userAgent().utf8()); + config()->userAgent().toUtf8().constData()); if(log_debug) { curl_easy_setopt(curl,CURLOPT_VERBOSE,1); curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,DeleteErrorCallback); } - currentdir=""; - if(!conv_target_url.dirPath().right(conv_target_url.dirPath().length()-1). - isEmpty()) { - currentdir=conv_target_url.dirPath(). - right(conv_target_url.dirPath().length()-1)+"/"; + + if(conv_target_url.scheme().toLower()=="ftp"||conv_target_url.scheme().toLower()=="ftps") { + QStringList f0=conv_target_url.path().split("/",QString::SkipEmptyParts); + filename=f0.last(); + f0.removeLast(); + if(f0.size()>0) { + currentdir=f0.join("/")+"/"; + } + if(!currentdir.isEmpty()) { + cmds=curl_slist_append(cmds,(QString("cwd ")+currentdir).toUtf8()); + } + cmds=curl_slist_append(cmds,(QString("dele ")+filename).toUtf8()); } - if(!currentdir.isEmpty()) { - cmds=curl_slist_append(cmds,QString().sprintf("cwd %s", - (const char *)currentdir)); + + if(conv_target_url.scheme().toLower()=="sftp") { + cmds= + curl_slist_append(cmds,(QString("rm ")+conv_target_url.path()).toUtf8()); } - cmds=curl_slist_append(cmds, QString().sprintf("dele %s", - (const char *)conv_target_url.fileName())); + curl_easy_setopt(curl,CURLOPT_QUOTE,cmds); + switch((err=curl_easy_perform(curl))) { case CURLE_OK: - case 21: // CURLE_QUOTE_ERROR -- In case the file is already gone + case CURLE_REMOTE_ACCESS_DENIED: // Sometimes we get this even when + // successful (?!) + case CURLE_QUOTE_ERROR: // In case the file is already gone + case CURLE_REMOTE_FILE_NOT_FOUND: ret=RDDelete::ErrorOk; break; @@ -151,10 +197,6 @@ RDDelete::ErrorCode RDDelete::runDelete(const QString &username, ret=RDDelete::ErrorRemoteConnection; break; - case 9: // CURLE_REMOTE_ACCESS_DENIED: - ret=RDDelete::ErrorRemoteAccess; - break; - default: ret=RDDelete::ErrorUnknown; printf("CURL error: %d\n",err); @@ -165,14 +207,14 @@ RDDelete::ErrorCode RDDelete::runDelete(const QString &username, } curl_slist_free_all(cmds); curl_easy_cleanup(curl); - + return ret; } QString RDDelete::errorText(RDDelete::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDDelete Error [%u]",err); switch(err) { case RDDelete::ErrorOk: @@ -220,7 +262,10 @@ QString RDDelete::errorText(RDDelete::ErrorCode err) break; case RDDelete::ErrorUnknown: - ret=tr("Unknown Error"); + break; + + case RDDelete::ErrorUnsupportedUrlScheme: + ret=tr("Unsupported URL Scheme"); break; } return ret; diff --git a/lib/rddelete.h b/lib/rddelete.h index 244656e6..314b010f 100644 --- a/lib/rddelete.h +++ b/lib/rddelete.h @@ -2,7 +2,7 @@ // // Delete a Remote File // -// (C) Copyright 2011,2016 Fred Gleason +// (C) Copyright 2011-2020 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 @@ -22,11 +22,12 @@ #define RDDELETE_H #include -#include +#include #include +#include -class RDDelete : public QObject +class RDDelete : public RDTransfer { Q_OBJECT; public: @@ -35,17 +36,20 @@ class RDDelete : public QObject ErrorInternal=5,ErrorRemoteServer=6,ErrorUrlInvalid=7, ErrorUnspecified=8,ErrorInvalidUser=9, ErrorInvalidLogin=11,ErrorRemoteAccess=12, - ErrorRemoteConnection=13,ErrorUnknown=14}; + ErrorRemoteConnection=13,ErrorUnknown=14, + ErrorUnsupportedUrlScheme=15}; RDDelete(RDConfig *config,QObject *parent=0); + QStringList supportedSchemes() const; void setTargetUrl(const QString &url); RDDelete::ErrorCode runDelete(const QString &username, const QString &password, + const QString &id_filename, + bool use_id_filename, bool log_debug); static QString errorText(RDDelete::ErrorCode err); private: - Q3Url conv_target_url; - RDConfig *conv_config; + QUrl conv_target_url; }; diff --git a/lib/rddownload.cpp b/lib/rddownload.cpp index 3b425e23..e51b7483 100644 --- a/lib/rddownload.cpp +++ b/lib/rddownload.cpp @@ -69,14 +69,28 @@ int DownloadErrorCallback(CURL *curl,curl_infotype type,char *msg,size_t size, } -RDDownload::RDDownload(RDConfig *config,QObject *parent) - : QObject(parent) +RDDownload::RDDownload(RDConfig *c,QObject *parent) + : RDTransfer(c,parent) { - conv_config=config; conv_aborting=false; } +QStringList RDDownload::supportedSchemes() const +{ + QStringList schemes; + + schemes.push_back("file"); + schemes.push_back("ftp"); + schemes.push_back("ftps"); + schemes.push_back("http"); + schemes.push_back("https"); + schemes.push_back("sftp"); + + return schemes; +} + + void RDDownload::setSourceUrl(const QString &url) { conv_src_url=url; @@ -99,6 +113,8 @@ int RDDownload::totalSteps() const RDDownload::ErrorCode RDDownload::runDownload(const QString &username, const QString &password, + const QString &id_filename, + bool use_id_filename, bool log_debug) { CURL *curl=NULL; @@ -107,9 +123,12 @@ RDDownload::ErrorCode RDDownload::runDownload(const QString &username, long response_code=0; RDDownload::ErrorCode ret=RDDownload::ErrorOk; RDSystemUser *user=NULL; - // char url[1024]; char userpwd[256]; + if(!urlIsSupported(conv_src_url)) { + return RDDownload::ErrorUnsupportedProtocol; + } + // // Validate User for file: transfers // @@ -140,17 +159,30 @@ RDDownload::ErrorCode RDDownload::runDownload(const QString &username, // url.replace("#","%23"); - curl_easy_setopt(curl,CURLOPT_URL,(const char *)url); + // + // Authentication + // + if((conv_src_url.scheme().toLower()=="sftp")&& + (!id_filename.isEmpty())&&use_id_filename) { + curl_easy_setopt(curl,CURLOPT_USERNAME,username.toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE, + id_filename.toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_KEYPASSWD,password.toUtf8().constData()); + } + else { + strncpy(userpwd,(username+":"+password).utf8(),256); + curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); + } + + curl_easy_setopt(curl,CURLOPT_URL,url.constData()); curl_easy_setopt(curl,CURLOPT_WRITEDATA,f); - strncpy(userpwd,(username+":"+password).utf8(),256); - curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1); curl_easy_setopt(curl,CURLOPT_PROGRESSFUNCTION,DownloadProgressCallback); curl_easy_setopt(curl,CURLOPT_PROGRESSDATA,this); curl_easy_setopt(curl,CURLOPT_NOPROGRESS,0); curl_easy_setopt(curl,CURLOPT_USERAGENT, - (const char *)conv_config->userAgent().utf8()); + config()->userAgent().toUtf8().constData()); if(log_debug) { curl_easy_setopt(curl,CURLOPT_VERBOSE,1); curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,DownloadErrorCallback); @@ -224,7 +256,7 @@ bool RDDownload::aborting() const QString RDDownload::errorText(RDDownload::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDDownload Error [%u]",err); switch(err) { case RDDownload::ErrorOk: diff --git a/lib/rddownload.h b/lib/rddownload.h index 8db7533d..b70c5f05 100644 --- a/lib/rddownload.h +++ b/lib/rddownload.h @@ -21,12 +21,12 @@ #ifndef RDDOWNLOAD_H #define RDDOWNLOAD_H -#include #include #include +#include -class RDDownload : public QObject +class RDDownload : public RDTransfer { Q_OBJECT; public: @@ -37,11 +37,14 @@ class RDDownload : public QObject ErrorInvalidLogin=11,ErrorRemoteAccess=12, ErrorRemoteConnection=13}; RDDownload(RDConfig *config,QObject *parent=0); + QStringList supportedSchemes() const; void setSourceUrl(const QString &url); void setDestinationFile(const QString &filename); int totalSteps() const; RDDownload::ErrorCode runDownload(const QString &username, const QString &password, + const QString &id_filename, + bool use_id_filename, bool log_debug); bool aborting() const; static QString errorText(RDDownload::ErrorCode err); @@ -60,7 +63,6 @@ class RDDownload : public QObject QString conv_dst_filename; bool conv_aborting; uint conv_dst_size; - RDConfig *conv_config; }; diff --git a/lib/rdescape_string.cpp b/lib/rdescape_string.cpp index 655922cf..32aa9afd 100644 --- a/lib/rdescape_string.cpp +++ b/lib/rdescape_string.cpp @@ -2,7 +2,7 @@ // // Escape non-valid characters in a string. // -// (C) Copyright 2002-2005,2016-2017 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -113,3 +113,21 @@ QString RDEscapeShellString(QString str) { return "\""+str.replace("$","\\$")+"\""; } + + +QString RDEscapeBlob(const QByteArray &data) +{ + return RDEscapeBlob(data.constData(),data.length()); +} + + +QString RDEscapeBlob(const char *data,size_t len) +{ + QString ret="x'"; + + for(unsigned i=0;i -#include - #ifndef RDESCAPE_STRING_H #define RDESCAPE_STRING_H +#include +#include +#include + QString RDCheckDateTime(const QTime &time, const QString &format); QString RDCheckDateTime(const QDateTime &datetime, const QString &format); QString RDCheckDateTime(const QDate &date, const QString &format); QString RDEscapeString(const QString &str); QString RDEscapeShellString(QString str); +QString RDEscapeBlob(const QByteArray &data); +QString RDEscapeBlob(const char *data,size_t len); #endif // RDESCAPE_STRING_H diff --git a/lib/rdfeed.cpp b/lib/rdfeed.cpp index 6ea56492..5e52797c 100644 --- a/lib/rdfeed.cpp +++ b/lib/rdfeed.cpp @@ -2,7 +2,7 @@ // // Abstract a Rivendell RSS Feed // -// (C) Copyright 2002-2018 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -18,25 +18,35 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // +#include #include -#include -#include -#include +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include "rdapplication.h" +#include "rdaudioconvert.h" +#include "rdaudioexport.h" +#include "rdcart.h" +#include "rdcut.h" +#include "rdconf.h" +#include "rddb.h" +#include "rddelete.h" +#include "rdescape_string.h" +#include "rdfeed.h" +#include "rdlibrary_conf.h" +#include "rdlog.h" +#include "rdlog_event.h" +#include "rdpodcast.h" +#include "rdrenderer.h" +#include "rdtempdirectory.h" +#include "rdupload.h" +#include "rdwavefile.h" +#include "rdxport_interface.h" RDFeed::RDFeed(const QString &keyname,RDConfig *config,QObject *parent) : QObject(parent) @@ -54,6 +64,13 @@ RDFeed::RDFeed(const QString &keyname,RDConfig *config,QObject *parent) feed_id=q->value(0).toUInt(); } delete q; + + // + // Get the CGI Hostname + // + if(getenv("SERVER_NAME")!=NULL) { + feed_cgi_hostname=getenv("SERVER_NAME"); + } } @@ -77,7 +94,59 @@ RDFeed::RDFeed(unsigned id,RDConfig *config,QObject *parent) bool RDFeed::exists() const { - return RDDoesRowExist("FEEDS","NAME",feed_keyname); + return RDDoesRowExist("FEEDS","KEY_NAME",feed_keyname); +} + + +bool RDFeed::isSuperfeed() const +{ + return RDBool(RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"IS_SUPERFEED"). + toString()); + +} + + +void RDFeed::setIsSuperfeed(bool state) const +{ + SetRow("IS_SUPERFEED",RDYesNo(state)); +} + + +QStringList RDFeed::subfeedNames() const +{ + QString sql; + RDSqlQuery *q=NULL; + QStringList ret; + + sql=QString("select ")+ + "MEMBER_KEY_NAME "+ // 00 + "from SUPERFEED_MAPS where "+ + "KEY_NAME=\""+RDEscapeString(keyName())+"\""; + q=new RDSqlQuery(sql); + while(q->next()) { + ret.push_back(q->value(0).toString()); + } + delete q; + + return ret; +} + + +QStringList RDFeed::isSubfeedOf() const +{ + QStringList ret; + + QString sql=QString("select ")+ + "KEY_NAME "+ // 00 + "from SUPERFEED_MAPS where "+ + "MEMBER_KEY_NAME=\""+RDEscapeString(keyName())+"\""; + RDSqlQuery *q=new RDSqlQuery(sql); + while(q->next()) { + ret.push_back(q->value(0).toString()); + } + delete q; + + return ret; } @@ -132,6 +201,19 @@ void RDFeed::setChannelCategory(const QString &str) const } +QString RDFeed::channelSubCategory() const +{ + return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_SUB_CATEGORY"). + toString(); +} + + +void RDFeed::setChannelSubCategory(const QString &str) const +{ + SetRow("CHANNEL_SUB_CATEGORY",str); +} + + QString RDFeed::channelLink() const { return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_LINK"). @@ -172,6 +254,71 @@ void RDFeed::setChannelWebmaster(const QString &str) const } +QString RDFeed::channelEditor() const +{ + return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_EDITOR"). + toString(); +} + + +void RDFeed::setChannelEditor(const QString &str) const +{ + SetRow("CHANNEL_EDITOR",str); +} + + +QString RDFeed::channelAuthor() const +{ + return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_AUTHOR"). + toString(); +} + + +void RDFeed::setChannelAuthor(const QString &str) const +{ + SetRow("CHANNEL_AUTHOR",str); +} + + +bool RDFeed::channelAuthorIsDefault() const +{ + return RDBool(RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname, + "CHANNEL_AUTHOR_IS_DEFAULT").toString()); +} + + +void RDFeed::setChannelAuthorIsDefault(bool state) const +{ + SetRow("CHANNEL_AUTHOR_IS_DEFAULT",RDYesNo(state)); +} + + +QString RDFeed::channelOwnerName() const +{ + return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_OWNER_NAME"). + toString(); +} + + +void RDFeed::setChannelOwnerName(const QString &str) const +{ + SetRow("CHANNEL_OWNER_NAME",str); +} + + +QString RDFeed::channelOwnerEmail() const +{ + return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_OWNER_EMAIL"). + toString(); +} + + +void RDFeed::setChannelOwnerEmail(const QString &str) const +{ + SetRow("CHANNEL_OWNER_EMAIL",str); +} + + QString RDFeed::channelLanguage() const { return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_LANGUAGE"). @@ -185,13 +332,66 @@ void RDFeed::setChannelLanguage(const QString &str) } -QString RDFeed::baseUrl() const +bool RDFeed::channelExplicit() const { - return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"BASE_URL"). - toString(); + return RDBool(RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname, + "CHANNEL_EXPLICIT").toString()); } +void RDFeed::setChannelExplicit(bool state) const +{ + SetRow("CHANNEL_EXPLICIT",RDYesNo(state)); +} + + +int RDFeed::channelImageId() const +{ + return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_IMAGE_ID"). + toInt(); +} + + +void RDFeed::setChannelImageId(int img_id) const +{ + SetRow("CHANNEL_IMAGE_ID",img_id); +} + + +int RDFeed::defaultItemImageId() const +{ + return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"DEFAULT_ITEM_IMAGE_ID"). + toInt(); +} + + +void RDFeed::setDefaultItemImageId(int img_id) const +{ + SetRow("DEFAULT_ITEM_IMAGE_ID",img_id); +} + + +QString RDFeed::baseUrl(const QString &subfeed_key_name) const +{ + QString key_name=subfeed_key_name; + if(subfeed_key_name.isEmpty()) { + key_name=feed_keyname; + } + return RDGetSqlValue("FEEDS","KEY_NAME",key_name,"BASE_URL").toString(); +} + + +QString RDFeed::baseUrl(int subfeed_feed_id) const +{ + int id=subfeed_feed_id; + if(subfeed_feed_id<0) { + id=feed_id; + } + return RDGetSqlValue("FEEDS","ID",id,"BASE_URL").toString(); +} + + + void RDFeed::setBaseUrl(const QString &str) const { SetRow("BASE_URL",str); @@ -239,14 +439,40 @@ void RDFeed::setPurgeUsername(const QString &str) const QString RDFeed::purgePassword() const { - return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"PURGE_PASSWORD"). - toString(); + return QString(QByteArray::fromBase64(RDGetSqlValue("FEEDS","KEY_NAME", + feed_keyname,"PURGE_PASSWORD").toString().toUtf8())); } void RDFeed::setPurgePassword(const QString &str) const { - SetRow("PURGE_PASSWORD",str); + SetRow("PURGE_PASSWORD",QString(str.toUtf8().toBase64())); +} + + +bool RDFeed::purgeUseIdFile() const +{ + return RDBool(RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname, + "PURGE_USE_ID_FILE").toString()); +} + + +void RDFeed::setPurgeUseIdFile(bool state) const +{ + SetRow("PURGE_USE_ID_FILE",RDYesNo(state)); +} + + +RDRssSchemas::RssSchema RDFeed::rssSchema() const +{ + return (RDRssSchemas::RssSchema)RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname, + "RSS_SCHEMA").toUInt(); +} + + +void RDFeed::setRssSchema(RDRssSchemas::RssSchema schema) const +{ + SetRow("RSS_SCHEMA",(unsigned)schema); } @@ -289,6 +515,12 @@ void RDFeed::setItemXml(const QString &str) } +QString RDFeed::feedUrl() const +{ + return purgeUrl()+"/"+keyName()+"."+RD_RSS_XML_FILE_EXTENSION; +} + + bool RDFeed::castOrder() const { return RDBool(RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname, @@ -353,19 +585,6 @@ void RDFeed::setEnableAutopost(bool state) const } -bool RDFeed::keepMetadata() const -{ - return RDBool(RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname, - "KEEP_METADATA").toString()); -} - - -void RDFeed::setKeepMetadata(bool state) -{ - SetRow("KEEP_METADATA",RDYesNo(state)); -} - - RDSettings::Format RDFeed::uploadFormat() const { return (RDSettings::Format)RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname, @@ -470,77 +689,540 @@ void RDFeed::setNormalizeLevel(int lvl) const } -QString RDFeed::redirectPath() const +QByteArray RDFeed::imageData(int img_id) const { - return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"REDIRECT_PATH"). - toString(); + return RDGetSqlValue("FEED_IMAGES","ID",img_id,"DATA").toByteArray(); } -void RDFeed::setRedirectPath(const QString &str) +int RDFeed::importImageFile(const QString &pathname,QString *err_msg, + QString desc) const { - SetRow("REDIRECT_PATH",str); -} + bool ok=false; + QString sql; + int ret; + QSize min=rda->rssSchemas()->minimumImageSize(rssSchema()); + QSize max=rda->rssSchemas()->maximumImageSize(rssSchema()); + *err_msg="OK"; - -RDFeed::MediaLinkMode RDFeed::mediaLinkMode() const -{ - return (RDFeed::MediaLinkMode)RDGetSqlValue("FEEDS","KEY_NAME", - feed_keyname,"MEDIA_LINK_MODE"). - toUInt(); -} - - -void RDFeed::setMediaLinkMode(RDFeed::MediaLinkMode mode) const -{ - SetRow("MEDIA_LINK_MODE",(unsigned)mode); -} - - -QString RDFeed::audioUrl(RDFeed::MediaLinkMode mode, - const QString &cgi_hostname,unsigned cast_id) -{ - Q3Url url(baseUrl()); - QString ret; - RDPodcast *cast; - - switch(mode) { - case RDFeed::LinkNone: - ret=""; - break; - - case RDFeed::LinkDirect: - cast=new RDPodcast(feed_config,cast_id); - ret=baseUrl()+"/"+cast->audioFilename(); - delete cast; - break; - - case RDFeed::LinkCounted: - ret=QString("http://")+basePreamble()+cgi_hostname+ - "/rd-bin/rdfeed."+uploadExtension()+"?"+keyName()+ - QString().sprintf("&cast_id=%d",cast_id); - break; + // + // Load the image + // + QFile file(pathname); + if(!file.open(QIODevice::ReadOnly)) { + *err_msg=QString("Unable to open image file [")+ + QString(strerror(errno))+"]"; + return -1; } + QByteArray data=file.readAll(); + file.close(); + + // + // Validate the image + // + QImage *img=new QImage(); + if(!img->loadFromData(data)) { + *err_msg="Invalid image file!"; + return -1; + } + if((!min.isNull())&& + ((img->width()height()width()>max.width())||(img->height()>max.height()))) { + *err_msg= + QString().sprintf("Image is too large - %dx%d or smaller required", + max.width(),max.height()); + return -1; + } + + // + // Fix up the Description + // + if(desc.isEmpty()) { + desc=tr("Imported from")+" "+pathname; + } + + // + // FIXME: Upload to remote file store here... + // + + // + // Write it to the DB + // + QStringList f0=pathname.split(".",QString::SkipEmptyParts); + sql=QString("insert into FEED_IMAGES set ")+ + QString().sprintf("FEED_ID=%u,",id())+ + "FEED_KEY_NAME=\""+RDEscapeString(keyName())+"\","+ + QString().sprintf("WIDTH=%d,",img->width())+ + QString().sprintf("HEIGHT=%d,",img->height())+ + QString().sprintf("DEPTH=%d,",img->depth())+ + "DESCRIPTION=\""+RDEscapeString(desc)+"\","+ + "FILE_EXTENSION=\""+RDEscapeString(f0.last().toLower())+"\","+ + "DATA="+RDEscapeBlob(data); + ret=RDSqlQuery::run(sql,&ok).toInt(); + if(!ok) { + *err_msg="Unable to write to database"; + return -1; + } + return ret; } -unsigned RDFeed::postCut(RDUser *user,RDStation *station, - const QString &cutname,Error *err,bool log_debug, - RDConfig *config) +bool RDFeed::deleteImage(int img_id,QString *err_msg) { + QString sql; + RDSqlQuery *q=NULL; + + *err_msg="OK"; + + removeImage(img_id); + + sql=QString("delete from FEED_IMAGES where ")+ + QString().sprintf("ID=%d",img_id); + if(!RDSqlQuery::apply(sql,err_msg)) { + *err_msg=QString("database error: ")+*err_msg; + delete q; + return false; + } + delete q; + + return true; +} + + +bool RDFeed::postPodcast(unsigned cast_id) const +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_POST_PODCAST), + 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, + (const char *)QString().sprintf("%u",cast_id), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + 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)) { + return false; + } + + return true; +} + + +QString RDFeed::audioUrl(unsigned cast_id) +{ + RDPodcast *cast=new RDPodcast(feed_config,cast_id); + QUrl url(baseUrl(cast->feedId())); + QString ret; + + ret=url.toString()+"/"+cast->audioFilename(); + delete cast; + + return ret; +} + + +QString RDFeed::imageUrl(int img_id) const +{ + QString ret; + + QString sql=QString("select ")+ + "FEED_ID,"+ // 00 + "FILE_EXTENSION "+ // 01 + "from FEED_IMAGES where "+ + QString().sprintf("ID=%d",img_id); + RDSqlQuery *q=new RDSqlQuery(sql); + if(q->first()) { + ret=baseUrl(q->value(0).toUInt())+"/"+ + RDFeed::imageFilename(id(),img_id,q->value(1).toString()); + } + delete q; + + return ret; +} + + +bool RDFeed::postXml() +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_POST_RSS), + 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, + (const char *)QString().sprintf("%u",feed_id), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + 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)) { + return false; + } + + return true; +} + + +bool RDFeed::postXmlConditional(const QString &caption,QWidget *widget) +{ + if(!postXml()) { + QMessageBox::warning(widget,caption+" - "+tr("Error"), + tr("XML data upload failed!")); + return false; + } + return true; +} + + +bool RDFeed::removeRss() +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_REMOVE_RSS), + 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, + (const char *)QString().sprintf("%u",feed_id), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + 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)) { + return false; + } + + return true; +} + + +bool RDFeed::postImage(int img_id) const +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_POST_IMAGE), + 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, + (const char *)QString().sprintf("%u",img_id), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + 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)) { + return false; + } + + return true; +} + + +bool RDFeed::removeImage(int img_id) const +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_REMOVE_IMAGE), + 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, + (const char *)QString().sprintf("%u",img_id), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + 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)) { + return false; + } + + return true; +} + + +void RDFeed::removeAllImages() +{ + QString sql; + RDSqlQuery *q=NULL; + + sql=QString("select ")+ + "ID "+ // 00 + "from FEED_IMAGES where "+ + QString().sprintf("FEED_ID=%u",feed_id); + q=new RDSqlQuery(sql); + while(q->next()) { + removeImage(q->value(0).toUInt()); + } + delete q; +} + + +unsigned RDFeed::postCut(const QString &cutname,Error *err) +{ + QString err_msg; QString tmpfile; QString destfile; - QString sql; - RDSqlQuery *q; RDPodcast *cast=NULL; - RDUpload *upload=NULL; - RDUpload::ErrorCode upload_err; RDAudioConvert::ErrorCode audio_conv_err; RDAudioExport::ErrorCode export_err; + emit postProgressRangeChanged(0,5); emit postProgressChanged(0); - emit postProgressChanged(1); // // Export Cut @@ -564,7 +1246,10 @@ unsigned RDFeed::postCut(RDUser *user,RDStation *station, settings->setBitRate(uploadBitRate()); settings->setNormalizationLevel(normalizeLevel()/100); conv->setDestinationSettings(settings); - switch((export_err=conv->runExport(user->name(),user->password(),&audio_conv_err))) { + emit postProgressChanged(1); + switch((export_err=conv-> + runExport(rda->user()->name(),rda->user()->password(), + &audio_conv_err))) { case RDAudioExport::ErrorOk: break; @@ -573,6 +1258,7 @@ unsigned RDFeed::postCut(RDUser *user,RDStation *station, delete conv; *err=RDFeed::ErrorUnsupportedType; unlink(tmpfile); + emit postProgressChanged(5); return 0; case RDAudioExport::ErrorNoSource: @@ -587,70 +1273,65 @@ unsigned RDFeed::postCut(RDUser *user,RDStation *station, delete conv; *err=RDFeed::ErrorGeneral; unlink(tmpfile); + emit postProgressChanged(5); return 0; } delete settings; delete conv; + postProgressChanged(2); // - // Upload + // Save to Audio Store // - emit postProgressChanged(2); QFile file(tmpfile); int length=file.size(); unsigned cast_id=CreateCast(&destfile,length,cut->length()); - delete cut; cast=new RDPodcast(feed_config,cast_id); - upload=new RDUpload(this); - upload->setSourceFile(tmpfile); - upload->setDestinationUrl(purgeUrl()+"/"+cast->audioFilename()); - switch((upload_err=upload->runUpload(purgeUsername(),purgePassword(), - log_debug))) { - case RDUpload::ErrorOk: - *err=RDFeed::ErrorOk; - break; - - default: - emit postProgressChanged(totalPostSteps()); - *err=RDFeed::ErrorUploadFailed; - sql=QString().sprintf("delete from PODCASTS where ID=%u",cast_id); - q=new RDSqlQuery(sql); - delete q; - delete upload; - delete cast; - *err=RDFeed::ErrorUploadFailed; - unlink(tmpfile); - return 0; - } - emit postProgressChanged(3); + SavePodcast(cast_id,tmpfile); unlink(tmpfile); - delete upload; - delete cast; - emit postProgressChanged(totalPostSteps()); + // + // Upload to remote archive + // + postPodcast(cast_id); + postProgressChanged(3); + + // + // Set default cast parameters + // + RDCart *cart=new RDCart(RDCut::cartNumber(cutname)); + cast->setItemTitle(cart->title()); + cast->setItemImageId(defaultItemImageId()); + delete cart; + delete cut; + delete cast; + emit postProgressChanged(4); + + // + // Update posted XML + // + postXml(); + emit postProgressChanged(5); + *err=RDFeed::ErrorOk; return cast_id; } -unsigned RDFeed::postFile(RDStation *station,const QString &srcfile,Error *err, - bool log_debug,RDConfig *config) +unsigned RDFeed::postFile(const QString &srcfile,Error *err) { - QString sql; - RDSqlQuery *q; + QString err_msg; QString cmd; QString tmpfile; QString tmpfile2; QString destfile; int time_length=0; - RDUpload *upload=NULL; - RDUpload::ErrorCode upload_err; RDWaveFile *wave=NULL; + RDWaveData wavedata; unsigned audio_time=0; + emit postProgressRangeChanged(0,6); emit postProgressChanged(0); - emit postProgressChanged(1); - qApp->processEvents(); // // Convert Cut @@ -666,10 +1347,12 @@ unsigned RDFeed::postFile(RDStation *station,const QString &srcfile,Error *err, settings->setBitRate(uploadBitRate()); settings->setNormalizationLevel(normalizeLevel()/100); conv->setDestinationSettings(settings); + emit postProgressChanged(1); + switch(conv->convert()) { case RDAudioConvert::ErrorOk: - wave=new RDWaveFile(tmpfile); - if(wave->openWave()) { + wave=new RDWaveFile(srcfile); + if(wave->openWave(&wavedata)) { audio_time=wave->getExtTimeLength(); } delete wave; @@ -677,11 +1360,11 @@ unsigned RDFeed::postFile(RDStation *station,const QString &srcfile,Error *err, case RDAudioConvert::ErrorInvalidSettings: case RDAudioConvert::ErrorFormatNotSupported: - emit postProgressChanged(totalPostSteps()); delete settings; delete conv; *err=RDFeed::ErrorUnsupportedType; unlink(tmpfile); + emit postProgressChanged(6); return 0; case RDAudioConvert::ErrorNoSource: @@ -693,71 +1376,373 @@ unsigned RDFeed::postFile(RDStation *station,const QString &srcfile,Error *err, case RDAudioConvert::ErrorInvalidSpeed: case RDAudioConvert::ErrorFormatError: case RDAudioConvert::ErrorNoSpace: - emit postProgressChanged(totalPostSteps()); delete settings; delete conv; *err=RDFeed::ErrorGeneral; unlink(tmpfile); + emit postProgressChanged(6); return 0; } delete settings; delete conv; + emit postProgressChanged(2); // - // Upload + // Save to Audio Store // - emit postProgressChanged(2); - emit postProgressChanged(3); - qApp->processEvents(); QFile file(tmpfile); int length=file.size(); - unsigned cast_id=CreateCast(&destfile,length,time_length); RDPodcast *cast=new RDPodcast(feed_config,cast_id); - upload=new RDUpload(this); - upload->setSourceFile(tmpfile); - upload->setDestinationUrl(purgeUrl()+"/"+cast->audioFilename()); - switch((upload_err=upload->runUpload(purgeUsername(),purgePassword(), - log_debug))) { - case RDUpload::ErrorOk: - sql=QString().sprintf("update PODCASTS set AUDIO_TIME=%u where ID=%u", - audio_time,cast_id); - q=new RDSqlQuery(sql); - delete q; - break; - - default: - emit postProgressChanged(totalPostSteps()); - *err=RDFeed::ErrorUploadFailed; - sql=QString().sprintf("delete from PODCASTS where ID=%u",cast_id); - q=new RDSqlQuery(sql); - delete q; - delete upload; - delete cast; - *err=RDFeed::ErrorUploadFailed; - unlink(tmpfile); - return 0; - } - delete upload; - delete cast; + SavePodcast(cast_id,tmpfile); unlink(QString(tmpfile)+".wav"); unlink(tmpfile); - emit postProgressChanged(totalPostSteps()); + emit postProgressChanged(3); + // + // Upload to remote archive + // + postPodcast(cast_id); + postProgressChanged(4); + + // + // Set default cast parameters + // + if(wavedata.metadataFound()&&(!wavedata.title().isEmpty())) { + cast->setItemTitle(wavedata.title()); + } + else { + cast->setItemTitle(srcfile.split("/").last()); + } + cast->setAudioTime(audio_time); + cast->setItemImageId(defaultItemImageId()); + delete cast; + emit postProgressChanged(5); + + // + // + // Update posted XML + // + postXml(); + emit postProgressChanged(6); *err=RDFeed::ErrorOk; + return cast_id; } -int RDFeed::totalPostSteps() const +unsigned RDFeed::postLog(const QString &logname,const QTime &start_time, + bool stop_at_stop,int start_line,int end_line, + RDFeed::Error *err) { - return RDFEED_TOTAL_POST_STEPS; + QString tmpfile; + QString destfile; + QString err_msg; + RDRenderer *renderer=NULL; + RDSettings *settings=NULL; + RDLogEvent *log_event=NULL; + + feed_render_start_line=start_line; + feed_render_end_line=end_line; + + emit postProgressRangeChanged(0,4+(end_line-start_line)); + emit postProgressChanged(0); + + // + // Open Log + // + log_event=new RDLogEvent(logname); + log_event->load(); + if(!log_event->exists()) { + *err=RDFeed::ErrorNoLog; + delete log_event; + return 0; + } + + // + // Render Log + // + tmpfile=GetTempFilename(); + + settings=new RDSettings(); + settings->setFormat(uploadFormat()); + settings->setChannels(uploadChannels()); + settings->setSampleRate(uploadSampleRate()); + settings->setBitRate(uploadBitRate()); + settings->setNormalizationLevel(normalizeLevel()/100); + renderer=new RDRenderer(this); + connect(renderer,SIGNAL(progressMessageSent(const QString &)), + this,SLOT(renderMessage(const QString &))); + connect(renderer,SIGNAL(lineStarted(int,int)), + this,SLOT(renderLineStartedData(int,int))); + + if(!renderer->renderToFile(tmpfile,log_event,settings,start_time,stop_at_stop, + &err_msg,start_line,end_line)) { + *err=RDFeed::ErrorRenderError; + delete renderer; + delete settings; + delete log_event; + unlink(tmpfile); + return 0; + } + delete renderer; + emit postProgressChanged(1+(end_line-start_line)); + + // + // Save to Audio Store + // + QFile f(tmpfile); + unsigned cast_id= + CreateCast(&destfile,f.size(),log_event->length(0,log_event->size())); + RDPodcast *cast=new RDPodcast(feed_config,cast_id); + SavePodcast(cast_id,tmpfile); + unlink(tmpfile); + emit postProgressChanged(2+(end_line-start_line)); + + // + // Save to remote archive + // + postPodcast(cast_id); + emit postProgressChanged(3+(end_line-start_line)); + + // + // Set default cast parameters + // + RDLog *log=new RDLog(logname); + if(log->description().isEmpty()) { + cast->setItemTitle(logname+" "+tr("log")); + } + else { + cast->setItemTitle(log->description()); + } + cast->setItemImageId(defaultItemImageId()); + cast->setAudioTime(log_event->length(start_line,1+end_line)); + delete log; + + postXml(); + emit postProgressChanged(4+(end_line-start_line)); + *err=RDFeed::ErrorOk; + + delete cast; + delete settings; + delete log_event; + unlink(tmpfile); + + return cast_id; +} + + +QString RDFeed::rssXml(QString *err_msg,const QDateTime &now,bool *ok) +{ + QString ret; + + QString sql; + RDSqlQuery *chan_q; + RDSqlQuery *item_q; + RDSqlQuery *q; + + if(ok!=NULL) { + *ok=false; + } + sql=QString("select ")+ + "FEEDS.CHANNEL_TITLE,"+ // 00 + "FEEDS.CHANNEL_DESCRIPTION,"+ // 01 + "FEEDS.CHANNEL_CATEGORY,"+ // 02 + "FEEDS.CHANNEL_SUB_CATEGORY,"+ // 03 + "FEEDS.CHANNEL_LINK,"+ // 04 + "FEEDS.CHANNEL_COPYRIGHT,"+ // 05 + "FEEDS.CHANNEL_EDITOR,"+ // 06 + "FEEDS.CHANNEL_AUTHOR,"+ // 07 + "FEEDS.CHANNEL_OWNER_NAME,"+ // 08 + "FEEDS.CHANNEL_OWNER_EMAIL,"+ // 09 + "FEEDS.CHANNEL_WEBMASTER,"+ // 10 + "FEEDS.CHANNEL_LANGUAGE,"+ // 11 + "FEEDS.CHANNEL_EXPLICIT,"+ // 12 + "FEEDS.ORIGIN_DATETIME,"+ // 13 + "FEEDS.HEADER_XML,"+ // 14 + "FEEDS.CHANNEL_XML,"+ // 15 + "FEEDS.ITEM_XML,"+ // 16 + "FEEDS.BASE_URL,"+ // 17 + "FEEDS.ID,"+ // 18 + "FEEDS.UPLOAD_EXTENSION,"+ // 19 + "FEEDS.CAST_ORDER,"+ // 20 + "FEEDS.BASE_PREAMBLE,"+ // 21 + "FEEDS.IS_SUPERFEED,"+ // 22 + "FEED_IMAGES.ID,"+ // 23 + "FEED_IMAGES.WIDTH,"+ // 24 + "FEED_IMAGES.HEIGHT,"+ // 25 + "FEED_IMAGES.DESCRIPTION,"+ // 26 + "FEED_IMAGES.FILE_EXTENSION "+ // 27 + "from FEEDS "; + sql+="left join FEED_IMAGES "; + sql+="on FEEDS.CHANNEL_IMAGE_ID=FEED_IMAGES.ID "; + sql+="where "; + sql+="FEEDS.KEY_NAME=\""+RDEscapeString(keyName())+"\""; + chan_q=new RDSqlQuery(sql); + if(!chan_q->first()) { + *err_msg="no feed matches the supplied key name"; + return QString(); + } + + // + // Load the XML Templates + // + QString header_template=rda->rssSchemas()->headerTemplate(rssSchema()); + QString channel_template=rda->rssSchemas()->channelTemplate(rssSchema()); + QString item_template=rda->rssSchemas()->itemTemplate(rssSchema()); + if(rssSchema()==RDRssSchemas::CustomSchema) { + header_template=chan_q->value(14).toString(); + channel_template=chan_q->value(15).toString(); + item_template=chan_q->value(16).toString(); + } + + // + // Render Header XML + // + ret+=header_template+"\r\n"; + + // + // Render Channel XML + // + ret+=" \n"; + ret+=ResolveChannelWildcards(channel_template,chan_q,now)+"\r\n"; + + // + // Render Item XML + // + QString where; + if(chan_q->value(22).toString()=="Y") { // Is a Superfeed + sql=QString("select ")+ + "MEMBER_FEED_ID "+ // 00 + "from SUPERFEED_MAPS where "+ + QString().sprintf("FEED_ID=%d",chan_q->value(18).toUInt()); + q=new RDSqlQuery(sql); + while(q->next()) { + where+=QString().sprintf("(PODCASTS.FEED_ID=%u) || ",q->value(0).toUInt()); + } + delete q; + where=("("+where.left(where.length()-4)+") && "); + } + else { + where=QString().sprintf("(PODCASTS.FEED_ID=%u)&&",chan_q->value(18).toUInt()); + } + sql=QString("select ")+ + "PODCASTS.FEED_ID,"+ // 00 + "PODCASTS.ITEM_TITLE,"+ // 01 + "PODCASTS.ITEM_DESCRIPTION,"+ // 02 + "PODCASTS.ITEM_CATEGORY,"+ // 03 + "PODCASTS.ITEM_LINK,"+ // 04 + "PODCASTS.ITEM_AUTHOR,"+ // 05 + "PODCASTS.ITEM_SOURCE_TEXT,"+ // 06 + "PODCASTS.ITEM_SOURCE_URL,"+ // 07 + "PODCASTS.ITEM_COMMENTS,"+ // 08 + "PODCASTS.ITEM_EXPLICIT,"+ // 09 + "PODCASTS.AUDIO_FILENAME,"+ // 10 + "PODCASTS.AUDIO_LENGTH,"+ // 11 + "PODCASTS.AUDIO_TIME,"+ // 12 + "PODCASTS.EFFECTIVE_DATETIME,"+ // 13 + "PODCASTS.ID,"+ // 14 + "FEEDS.BASE_URL,"+ // 15 + "FEEDS.CHANNEL_TITLE,"+ // 16 + "FEEDS.CHANNEL_DESCRIPTION,"+ // 17 + "FEED_IMAGES.ID,"+ // 18 + "FEED_IMAGES.WIDTH,"+ // 19 + "FEED_IMAGES.HEIGHT,"+ // 20 + "FEED_IMAGES.DESCRIPTION,"+ // 21 + "FEED_IMAGES.FILE_EXTENSION "+ // 22 + "from PODCASTS left join FEEDS "+ + "on PODCASTS.FEED_ID=FEEDS.ID "+ + "left join FEED_IMAGES "+ + "on PODCASTS.ITEM_IMAGE_ID=FEED_IMAGES.ID where "+ + where+ + QString().sprintf("(PODCASTS.STATUS=%d) && ",RDPodcast::StatusActive)+ + "(PODCASTS.EFFECTIVE_DATETIME<=now()) && "+ + "((PODCASTS.EXPIRATION_DATETIME is null)||"+ + "(PODCASTS.EXPIRATION_DATETIME>now())) "+ + "order by PODCASTS.ORIGIN_DATETIME"; + if(chan_q->value(20).toString()=="N") { + sql+=" desc"; + } + // printf("item_sql: %s\n",sql.toUtf8().constData()); + item_q=new RDSqlQuery(sql); + while(item_q->next()) { + ret+=" \r\n"; + ret+=ResolveItemWildcards(item_template,item_q,chan_q); + ret+="\r\n"; + ret+=" \r\n"; + } + delete item_q; + + ret+=" \r\n"; + ret+="\r\n"; + delete chan_q; + + if(ok!=NULL) { + *ok=true; + } + + return ret; +} + + +unsigned RDFeed::create(const QString &keyname,bool enable_users, + QString *err_msg) +{ + QString sql; + RDSqlQuery *q; + RDSqlQuery *q1; + unsigned feed_id=0; + + // + // Sanity Checks + // + sql=QString("select KEY_NAME from FEEDS where ")+ + "KEY_NAME=\""+RDEscapeString(keyname)+"\""; + q=new RDSqlQuery(sql); + if(q->first()) { + *err_msg=tr("A feed with that key name already exists!"); + delete q; + return 0; + } + delete q; + + // + // Create Feed + // + sql=QString("insert into FEEDS set ")+ + "KEY_NAME=\""+RDEscapeString(keyname)+"\","+ + "ORIGIN_DATETIME=now(),"+ + "HEADER_XML=\"\","+ + "CHANNEL_XML=\"\","+ + "ITEM_XML=\"\""; + q=new RDSqlQuery(sql); + feed_id=q->lastInsertId().toUInt(); + delete q; + + // + // Create Default Feed Perms + // + if(enable_users) { + sql=QString("select LOGIN_NAME from USERS where ")+ + "(ADMIN_USERS_PRIV='N')&&(ADMIN_CONFIG_PRIV='N')"; + q=new RDSqlQuery(sql); + while(q->next()) { + sql=QString("insert into FEED_PERMS set ")+ + "USER_NAME=\""+RDEscapeString(q->value(0).toString())+"\","+ + "KEY_NAME=\""+RDEscapeString(keyname)+"\""; + q1=new RDSqlQuery(sql); + delete q1; + } + delete q; + } + + return feed_id; } QString RDFeed::errorString(RDFeed::Error err) { - QString ret="Unknown Error"; + QString ret=QString().sprintf("Unknown RDFeed Error [%d]",err); switch(err) { case RDFeed::ErrorOk: @@ -783,11 +1768,141 @@ QString RDFeed::errorString(RDFeed::Error err) case RDFeed::ErrorGeneral: ret="General Error"; break; + + case RDFeed::ErrorNoLog: + ret="No such log"; + break; + + case RDFeed::ErrorRenderError: + ret="Log rendering error"; + break; } return ret; } +QString RDFeed::imageFilename(int feed_id,int img_id,const QString &ext) +{ + return QString().sprintf("img%06d_%06d.",feed_id,img_id)+ext; +} + + +QString RDFeed::publicUrl(const QString &base_url,const QString &keyname) +{ + return base_url+"/"+keyname+"."+RD_RSS_XML_FILE_EXTENSION; +} + + +QString RDFeed::itunesCategoryXml(const QString &category, + const QString &sub_category,int padding) +{ + QString pad_str=""; + + for(int i=0;i"; + } + return QString("\n"+ + pad_str+" \n"+ + pad_str+""; +} + + +void RDFeed::renderMessage(const QString &msg) +{ + fprintf(stderr,"RENDERER: %s\n",msg.toUtf8().constData()); +} + + +void RDFeed::renderLineStartedData(int lineno,int total_lines) +{ + if((lineno>=feed_render_start_line)&&(lineno<=feed_render_end_line)) { + emit postProgressChanged(1+(lineno-feed_render_start_line)); + } +} + + +bool RDFeed::SavePodcast(unsigned cast_id,const QString &src_filename) const +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + // We have to use multipart here because we have a file to send. + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_SAVE_PODCAST), + 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, + (const char *)QString().sprintf("%u",cast_id), + CURLFORM_END); + curl_formadd(&first,&last,CURLFORM_PTRNAME,"FILENAME", + CURLFORM_FILE,src_filename.toUtf8().constData(), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + 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)) { + return false; + } + + return true; +} + + unsigned RDFeed::CreateCast(QString *filename,int bytes,int msecs) const { QString sql; @@ -795,15 +1910,28 @@ unsigned RDFeed::CreateCast(QString *filename,int bytes,int msecs) const RDSqlQuery *q1; unsigned cast_id=0; - sql=QString().sprintf("select CHANNEL_TITLE,CHANNEL_DESCRIPTION,\ - CHANNEL_CATEGORY,CHANNEL_LINK,MAX_SHELF_LIFE,\ - UPLOAD_FORMAT,UPLOAD_EXTENSION from FEEDS \ - where ID=%u",feed_id); + sql=QString("select ")+ + "CHANNEL_TITLE,"+ // 00 + "CHANNEL_DESCRIPTION,"+ // 01 + "CHANNEL_CATEGORY,"+ // 02 + "CHANNEL_LINK,"+ // 03 + "MAX_SHELF_LIFE,"+ // 04 + "UPLOAD_FORMAT,"+ // 05 + "UPLOAD_EXTENSION,"+ // 06 + "ENABLE_AUTOPOST,"+ // 07 + "CHANNEL_AUTHOR,"+ // 08 + "CHANNEL_AUTHOR_IS_DEFAULT "+ // 09 + "from FEEDS where "+ + QString().sprintf("ID=%u",feed_id); q=new RDSqlQuery(sql); if(!q->first()) { delete q; return 0; } + QString item_author=rda->user()->emailContact(); + if(q->value(9).toString()=="Y") { + item_author=q->value(8).toString(); + } // // Create Entry @@ -814,9 +1942,25 @@ unsigned RDFeed::CreateCast(QString *filename,int bytes,int msecs) const "ITEM_DESCRIPTION=\""+RDEscapeString(q->value(1).toString())+"\","+ "ITEM_CATEGORY=\""+RDEscapeString(q->value(2).toString())+"\","+ "ITEM_LINK=\""+RDEscapeString(q->value(3).toString())+"\","+ - QString().sprintf("SHELF_LIFE=%d,",q->value(4).toInt())+ - "EFFECTIVE_DATETIME=UTC_TIMESTAMP(),"+ - "ORIGIN_DATETIME=UTC_TIMESTAMP()"; + "ITEM_AUTHOR=\""+RDEscapeString(item_author)+"\","+ + "EFFECTIVE_DATETIME=now(),"+ + "ORIGIN_LOGIN_NAME=\""+RDEscapeString(rda->user()->name())+"\","+ + "ORIGIN_STATION=\""+RDEscapeString(rda->station()->name())+"\","+ + "ORIGIN_DATETIME=now(),"; + if(RDBool(q->value(7).toString())) { + sql+=QString().sprintf("STATUS=%d,",RDPodcast::StatusActive); + } + else { + sql+=QString().sprintf("STATUS=%d,",RDPodcast::StatusPending); + } + if(q->value(4).toInt()==0) { + sql+="EXPIRATION_DATETIME=NULL"; + } + else { + sql+="EXPIRATION_DATETIME=\""+ + QDateTime::currentDateTime().addDays(q->value(4).toInt()). + toString("yyyy-MM-dd hh:mm:ss")+"\""; + } q1=new RDSqlQuery(sql); delete q1; @@ -847,6 +1991,102 @@ unsigned RDFeed::CreateCast(QString *filename,int bytes,int msecs) const } +QString RDFeed::ResolveChannelWildcards(const QString &tmplt,RDSqlQuery *chan_q, + const QDateTime &build_datetime) +{ + QString ret=" "+tmplt; + + ret.replace("\n","\r\n "); + ret.replace("%TITLE%",RDXmlEscape(chan_q->value(0).toString())); + ret.replace("%DESCRIPTION%",RDXmlEscape(chan_q->value(1).toString())); + ret.replace("%CATEGORY%",RDXmlEscape(chan_q->value(2).toString())); + ret.replace("%SUB_CATEGORY%",RDXmlEscape(chan_q->value(3).toString())); + ret.replace("%ITUNES_CATEGORY%", + RDFeed::itunesCategoryXml(chan_q->value(2).toString(), + chan_q->value(3).toString(),4)); + ret.replace("%LINK%",RDXmlEscape(chan_q->value(4).toString())); + ret.replace("%COPYRIGHT%",RDXmlEscape(chan_q->value(5).toString())); + ret.replace("%EDITOR%",RDXmlEscape(chan_q->value(6).toString())); + ret.replace("%AUTHOR%",RDXmlEscape(chan_q->value(7).toString())); + ret.replace("%OWNER_NAME%",RDXmlEscape(chan_q->value(8).toString())); + ret.replace("%OWNER_EMAIL%",RDXmlEscape(chan_q->value(9).toString())); + ret.replace("%WEBMASTER%",RDXmlEscape(chan_q->value(10).toString())); + ret.replace("%LANGUAGE%",RDXmlEscape(chan_q->value(11).toString())); + QString explicit_str="false"; + if(chan_q->value(12).toString()=="Y") { + explicit_str="true"; + } + ret.replace("%EXPLICIT%",RDXmlEscape(explicit_str)); + ret.replace("%BUILD_DATE%",RDLocalToUtc(build_datetime). + toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT"); + ret.replace("%PUBLISH_DATE%",RDLocalToUtc(chan_q->value(13).toDateTime()). + toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT"); + ret.replace("%GENERATOR%",QString("Rivendell ")+VERSION); + ret.replace("%FEED_URL%",RDXmlEscape(chan_q->value(17).toString())+"/"+ + RDXmlEscape(keyName()+"."+RD_RSS_XML_FILE_EXTENSION)); + ret.replace("%IMAGE_URL%",chan_q->value(17).toString()+"/"+ + RDFeed::imageFilename(id(),chan_q->value(23).toInt(), + chan_q->value(27).toString())); + ret.replace("%IMAGE_WIDTH%", + QString().sprintf("%d",chan_q->value(24).toInt())); + ret.replace("%IMAGE_HEIGHT%", + QString().sprintf("%d",chan_q->value(24).toInt())); + ret.replace("%IMAGE_DESCRIPTION%",chan_q->value(26).toString()); + + return ret; +} + + +QString RDFeed::ResolveItemWildcards(const QString &tmplt,RDSqlQuery *item_q, + RDSqlQuery *chan_q) +{ + QString ret=" "+tmplt; + + ret.replace("\n","\r\n "); + + ret.replace("%ITEM_CHANNEL_TITLE%",RDXmlEscape(item_q->value(16).toString())); + ret.replace("%ITEM_CHANNEL_DESCRIPTION%", + RDXmlEscape(item_q->value(17).toString())); + ret.replace("%ITEM_TITLE%",RDXmlEscape(item_q->value(1).toString())); + ret.replace("%ITEM_DESCRIPTION%", + RDXmlEscape(item_q->value(2).toString())); + ret.replace("%ITEM_CATEGORY%", + RDXmlEscape(item_q->value(3).toString())); + ret.replace("%ITEM_LINK%",RDXmlEscape(item_q->value(4).toString())); + ret.replace("%ITEM_AUTHOR%",RDXmlEscape(item_q->value(5).toString())); + ret.replace("%ITEM_SOURCE_TEXT%", + RDXmlEscape(chan_q->value(0).toString())); + ret.replace("%ITEM_SOURCE_URL%", + RDXmlEscape(item_q->value(15).toString()+"/"+keyName())); + ret.replace("%ITEM_COMMENTS%", + RDXmlEscape(item_q->value(8).toString())); + QString explicit_str="false"; + if(item_q->value(9).toString()=="Y") { + explicit_str="true"; + } + ret.replace("%ITEM_EXPLICIT%",explicit_str); + ret.replace("%ITEM_AUDIO_URL%", + RDXmlEscape(audioUrl(item_q->value(14).toUInt()))); + ret.replace("%ITEM_AUDIO_LENGTH%",item_q->value(11).toString()); + ret.replace("%ITEM_AUDIO_TIME%", + RDGetTimeLength(item_q->value(12).toInt(),false,false)); + ret.replace("%ITEM_AUDIO_SECONDS%", + QString().sprintf("%d",item_q->value(12).toInt()/1000)); + ret.replace("%ITEM_PUBLISH_DATE%", + RDLocalToUtc(item_q->value(13).toDateTime()). + toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT"); + ret.replace("%ITEM_GUID%",RDPodcast::guid(item_q->value(15).toString(), + item_q->value(10).toString(), + item_q->value(0).toUInt(), + item_q->value(14).toUInt())); + ret.replace("%ITEM_IMAGE_URL%",item_q->value(15).toString()+"/"+ + RDFeed::imageFilename(item_q->value(0).toInt(), + item_q->value(18).toInt(), + item_q->value(22).toString())); + return ret; +} + + QString RDFeed::GetTempFilename() const { char tempname[PATH_MAX]; diff --git a/lib/rdfeed.h b/lib/rdfeed.h index 38f9c84b..f701a8cb 100644 --- a/lib/rdfeed.h +++ b/lib/rdfeed.h @@ -2,7 +2,7 @@ // // Abstract a Rivendell RSS Feed // -// (C) Copyright 2002-2007,2016 Fred Gleason +// (C) Copyright 2002-2020 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,13 +21,15 @@ #ifndef RDFEED_H #define RDFEED_H -#include #include +#include #include -#include -#include +#include #include +#include +#include +#include #define RDFEED_TOTAL_POST_STEPS 4 @@ -36,28 +38,51 @@ class RDFeed : public QObject Q_OBJECT; public: enum Error {ErrorOk=0,ErrorNoFile=1,ErrorCannotOpenFile=2, - ErrorUnsupportedType=3,ErrorUploadFailed=4,ErrorGeneral=5}; - enum MediaLinkMode {LinkNone=0,LinkDirect=1,LinkCounted=2}; + ErrorUnsupportedType=3,ErrorUploadFailed=4,ErrorGeneral=5, + ErrorNoLog=6,ErrorRenderError=7}; RDFeed(const QString &keyname,RDConfig *config,QObject *parent=0); RDFeed(unsigned id,RDConfig *config,QObject *parent=0); QString keyName() const; unsigned id() const; bool exists() const; + bool isSuperfeed() const; + void setIsSuperfeed(bool state) const; + QStringList subfeedNames() const; + QStringList isSubfeedOf() const; QString channelTitle() const; void setChannelTitle(const QString &str) const; QString channelDescription() const; void setChannelDescription(const QString &str) const; QString channelCategory() const; void setChannelCategory(const QString &str) const; + QString channelSubCategory() const; + void setChannelSubCategory(const QString &str) const; QString channelLink() const; void setChannelLink(const QString &str) const; QString channelCopyright() const; void setChannelCopyright(const QString &str) const; + QString channelEditor() const; + void setChannelEditor(const QString &str) const; + QString channelAuthor() const; + void setChannelAuthor(const QString &str) const; + bool channelAuthorIsDefault() const; + void setChannelAuthorIsDefault(bool state) const; + QString channelOwnerName() const; + void setChannelOwnerName(const QString &str) const; + QString channelOwnerEmail() const; + void setChannelOwnerEmail(const QString &str) const; QString channelWebmaster() const; void setChannelWebmaster(const QString &str) const; QString channelLanguage() const; void setChannelLanguage(const QString &str); - QString baseUrl() const; + bool channelExplicit() const; + void setChannelExplicit(bool state) const; + int channelImageId() const; + void setChannelImageId(int img_id) const; + int defaultItemImageId() const; + void setDefaultItemImageId(int img_id) const; + QString baseUrl(const QString &subfeed_key_name) const; + QString baseUrl(int subfeed_feed_id) const; void setBaseUrl(const QString &str) const; QString basePreamble() const; void setBasePreamble(const QString &str) const; @@ -67,12 +92,17 @@ class RDFeed : public QObject void setPurgeUsername(const QString &str) const; QString purgePassword() const; void setPurgePassword(const QString &str) const; + bool purgeUseIdFile() const; + void setPurgeUseIdFile(bool state) const; + RDRssSchemas::RssSchema rssSchema() const; + void setRssSchema(RDRssSchemas::RssSchema schema) const; QString headerXml() const; void setHeaderXml(const QString &str); QString channelXml() const; void setChannelXml(const QString &str); QString itemXml() const; void setItemXml(const QString &str); + QString feedUrl() const; bool castOrder() const; void setCastOrder(bool state) const; int maxShelfLife() const; @@ -83,8 +113,6 @@ class RDFeed : public QObject void setOriginDateTime(const QDateTime &datetime) const; bool enableAutopost() const; void setEnableAutopost(bool state) const; - bool keepMetadata() const; - void setKeepMetadata(bool state); RDSettings::Format uploadFormat() const; void setUploadFormat(RDSettings::Format fmt) const; int uploadChannels() const; @@ -101,33 +129,61 @@ class RDFeed : public QObject void setUploadMimetype(const QString &str); int normalizeLevel() const; void setNormalizeLevel(int lvl) const; - QString redirectPath() const; - void setRedirectPath(const QString &str); - RDFeed::MediaLinkMode mediaLinkMode() const; - void setMediaLinkMode(RDFeed::MediaLinkMode mode) const; - QString audioUrl(RDFeed::MediaLinkMode mode,const QString &cgi_hostname, - unsigned cast_id); - unsigned postCut(RDUser *user,RDStation *station, - const QString &cutname,Error *err,bool log_debug, - RDConfig *config); - unsigned postFile(RDStation *station,const QString &srcfile,Error *err, - bool log_debug,RDConfig *config); - int totalPostSteps() const; + QByteArray imageData(int img_id) const; + int importImageFile(const QString &pathname,QString *err_msg, + QString desc="") const; + bool deleteImage(int img_id,QString *err_msg); + bool postPodcast(unsigned cast_id) const; + QString audioUrl(unsigned cast_id); + QString imageUrl(int img_id) const; + bool postXml(); + bool postXmlConditional(const QString &caption,QWidget *widget); + bool removeRss(); + bool postImage(int img_id) const; + bool removeImage(int img_id) const; + void removeAllImages(); + unsigned postCut(const QString &cutname,Error *err); + unsigned postFile(const QString &srcfile,Error *err); + unsigned postLog(const QString &logname,const QTime &start_time, + bool stop_at_stop,int start_line,int end_line,Error *err); + QString rssXml(QString *err_msg,const QDateTime &now,bool *ok=NULL); + static unsigned create(const QString &keyname,bool enable_users, + QString *err_msg); static QString errorString(RDFeed::Error err); + 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); signals: void postProgressChanged(int step); + void postProgressRangeChanged(int min,int max); + + private slots: + void renderMessage(const QString &msg); + void renderLineStartedData(int lineno,int total_lines); private: + bool SavePodcast(unsigned cast_id,const QString &src_filename) const; unsigned CreateCast(QString *filename,int bytes,int msecs) const; + QString ResolveChannelWildcards(const QString &tmplt,RDSqlQuery *chan_q, + const QDateTime &build_datetime); + QString ResolveItemWildcards(const QString &tmplt,RDSqlQuery *item_q, + RDSqlQuery *chan_q); QString GetTempFilename() const; + void LoadSchemas(); void SetRow(const QString ¶m,int value) const; void SetRow(const QString ¶m,const QString &value) const; void SetRow(const QString ¶m,const QDateTime &value, const QString &format) const; QString feed_keyname; unsigned feed_id; + QString feed_cgi_hostname; RDConfig *feed_config; + QByteArray feed_xml; + int feed_xml_ptr; + int feed_render_start_line; + int feed_render_end_line; }; diff --git a/lib/rdfeedlog.cpp b/lib/rdfeedlog.cpp deleted file mode 100644 index 6ff354c9..00000000 --- a/lib/rdfeedlog.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// rdfeedlog.cpp -// -// Functions for manipulating RSS feed log tables. -// -// (C) Copyright 2007,2016-2018 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 -#include - -#include "rdfeedlog.h" - -void RDDeleteFeedLog(QString keyname) -{ - QString sql=QString("delete from CAST_DOWNLOADS where ")+ - "FEED_KEY_NAME=\""+RDEscapeString(keyname)+"\""; - RDSqlQuery::apply(sql); -} - - -void RDDeleteCastCount(QString keyname,unsigned cast_id) -{ - QString sql; - - sql=QString("delete from CAST_DOWNLOADS where ")+ - "FEED_KEY_NAME=\""+RDEscapeString(keyname)+"\" && "+ - QString().sprintf("CAST_ID=%u",cast_id); - RDSqlQuery::apply(sql); -} - - -void RDDeleteCastCount(unsigned feed_id,unsigned cast_id) -{ - QString sql; - RDSqlQuery *q; - - sql=QString().sprintf("select KEY_NAME from FEEDS where ID=%u",feed_id); - q=new RDSqlQuery(sql); - if(q->first()) { - RDDeleteCastCount(q->value(0).toString(),cast_id); - } - delete q; -} - - -void RDIncrementFeedCount(QString keyname) -{ - RDIncrementCastCount(keyname,0); -} - - -void RDIncrementCastCount(QString keyname,unsigned cast_id) -{ - QString sql; - RDSqlQuery *q; - QDate now=QDate::currentDate(); - - sql=QString("select ACCESS_COUNT from CAST_DOWNLOADS where ")+ - "FEED_KEY_NAME=\""+RDEscapeString(keyname)+"\" && "+ - QString().sprintf("(CAST_ID=%u)&&",cast_id)+ - "(ACCESS_DATE=\""+RDEscapeString(now.toString("yyyy-MM-dd"))+"\")"; - q=new RDSqlQuery(sql); - if(q->first()) { - sql=QString("update CAST_DOWNLOADS set ")+ - QString().sprintf("ACCESS_COUNT=%u where ",q->value(0).toUInt()+1)+ - "FEED_KEY_NAME=\""+RDEscapeString(keyname)+"\" && "+ - QString().sprintf("(CAST_ID=%u)&&",cast_id)+ - "(ACCESS_DATE=\""+RDEscapeString(now.toString("yyyy-MM-dd"))+"\")"; - } - else { - sql=QString("insert into CAST_DOWNLOADS set ")+ - "FEED_KEY_NAME=\""+RDEscapeString(keyname)+"\","+ - QString().sprintf("CAST_ID=%u,",cast_id)+ - "ACCESS_DATE=\""+RDEscapeString(now.toString("yyyy-MM-dd"))+"\","+ - "ACCESS_COUNT=1"; - } - RDSqlQuery::apply(sql); - delete q; -} diff --git a/lib/rdfontengine.cpp b/lib/rdfontengine.cpp index 2af39031..9ddc5f4b 100644 --- a/lib/rdfontengine.cpp +++ b/lib/rdfontengine.cpp @@ -54,6 +54,7 @@ RDFontEngine::~RDFontEngine() delete font_big_button_font_metrics; delete font_sub_button_font_metrics; delete font_section_label_font_metrics; + delete font_big_label_font_metrics; delete font_label_font_metrics; delete font_sub_label_font_metrics; delete font_progress_font_metrics; @@ -124,6 +125,18 @@ QFontMetrics *RDFontEngine::sectionLabelFontMetrics() const } +QFont RDFontEngine::bigLabelFont() const +{ + return font_big_label_font; +} + + +QFontMetrics *RDFontEngine::bigLabelFontMetrics() const +{ + return font_big_label_font_metrics; +} + + QFont RDFontEngine::labelFont() const { return font_label_font; @@ -264,6 +277,10 @@ void RDFontEngine::MakeFonts(const QFont &default_font) font_section_label_font.setPixelSize(label_size+2); font_section_label_font_metrics=new QFontMetrics(font_section_label_font); + font_big_label_font=QFont(family,label_size+4,QFont::Bold); + font_big_label_font.setPixelSize(label_size+4); + font_big_label_font_metrics=new QFontMetrics(font_label_font); + font_label_font=QFont(family,label_size,QFont::Bold); font_label_font.setPixelSize(label_size); font_label_font_metrics=new QFontMetrics(font_label_font); diff --git a/lib/rdfontengine.h b/lib/rdfontengine.h index 00fba2ed..308ae4aa 100644 --- a/lib/rdfontengine.h +++ b/lib/rdfontengine.h @@ -42,6 +42,8 @@ class RDFontEngine QFontMetrics *subButtonFontMetrics() const; QFont sectionLabelFont() const; QFontMetrics *sectionLabelFontMetrics() const; + QFont bigLabelFont() const; + QFontMetrics *bigLabelFontMetrics() const; QFont labelFont() const; QFontMetrics *labelFontMetrics() const; QFont subLabelFont() const; @@ -69,6 +71,8 @@ class RDFontEngine QFontMetrics *font_sub_button_font_metrics; QFont font_section_label_font; QFontMetrics *font_section_label_font_metrics; + QFont font_big_label_font; + QFontMetrics *font_big_label_font_metrics; QFont font_label_font; QFontMetrics *font_label_font_metrics; QFont font_sub_label_font; diff --git a/lib/rdimagepickerbox.cpp b/lib/rdimagepickerbox.cpp new file mode 100644 index 00000000..40caeb31 --- /dev/null +++ b/lib/rdimagepickerbox.cpp @@ -0,0 +1,81 @@ +// rdimagepickerbox.cpp +// +// ComboBox for selecting images +// +// (C) Copyright 2020 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 "rdimagepickerbox.h" + +RDImagePickerBox::RDImagePickerBox(const QString &tbl_name, + const QString &cat_id_col, + const QString &img_id_col,QWidget *parent) + : QComboBox(parent) +{ + c_model=new RDImagePickerModel(tbl_name,cat_id_col,img_id_col,this); + setModel(c_model); + + setCurrentIndex(0); +} + + +RDImagePickerBox::~RDImagePickerBox() +{ + delete c_model; +} + + +int RDImagePickerBox::currentImageId() const +{ + if(currentIndex()<0) { + return -1; + } + return c_model->imageId(currentIndex()); +} + + +void RDImagePickerBox::setCurrentImageId(int img_id) +{ + setCurrentIndex(c_model->imageRowOfId(img_id)); +} + + +void RDImagePickerBox::refresh() +{ + int current_id=currentImageId(); + + c_model->refresh(); + setCurrentImageId(current_id); +} + + +void RDImagePickerBox::setCategoryId(int id) +{ + c_model->setCategoryId(id); + setCurrentIndex(0); +} + + +void RDImagePickerBox::resizeEvent(QResizeEvent *e) +{ + int index=currentIndex(); + + QSize img_size(size().height()-4,size().height()-4); + c_model->rescaleImages(img_size); + setIconSize(img_size); + + setCurrentIndex(index); +} diff --git a/lib/rdimagepickerbox.h b/lib/rdimagepickerbox.h new file mode 100644 index 00000000..0b6f78a8 --- /dev/null +++ b/lib/rdimagepickerbox.h @@ -0,0 +1,50 @@ +// rdimagepickerbox.h +// +// ComboBox for selecting images +// +// (C) Copyright 2020 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. +// + +#ifndef RDIMAGEPICKERBOX_H +#define RDIMAGEPICKERBOX_H + +#include + +#include + +class RDImagePickerBox : public QComboBox +{ + Q_OBJECT; + public: + RDImagePickerBox(const QString &tbl_name,const QString &cat_id_col, + const QString &img_id_col,QWidget *parent=0); + ~RDImagePickerBox(); + int currentImageId() const; + void refresh(); + + public slots: + void setCurrentImageId(int img_id); + void setCategoryId(int id); + + protected: + void resizeEvent(QResizeEvent *e); + + private: + RDImagePickerModel *c_model; +}; + + +#endif // RDIMAGEPICKERBOX_H diff --git a/lib/rdimagepickermodel.cpp b/lib/rdimagepickermodel.cpp new file mode 100644 index 00000000..d85183b4 --- /dev/null +++ b/lib/rdimagepickermodel.cpp @@ -0,0 +1,209 @@ +// rdimagepickermodel.cpp +// +// One-dimensional model for picking images +// +// (C) Copyright 2020 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 "rddb.h" +#include "rdimagepickermodel.h" + +RDImagePickerModel::RDImagePickerModel(const QString &tbl_name, + const QString &cat_id_col, + const QString &img_id_col, + QObject *parent) + : QAbstractListModel(parent) +{ + c_table_name=tbl_name; + c_category_column=cat_id_col; + c_image_column=img_id_col; + c_category_id=-1;; + c_image_size=QSize(100,100); +} + + +RDImagePickerModel::~RDImagePickerModel() +{ + for(int i=0;ifirst()) { + c_descriptions[row]=q->value(0).toString()+"\n"+ + +"["+q->value(1).toString().toUpper()+", "+ + QString().sprintf("%dx%d]",q->value(2).toInt(),q->value(3).toInt()); + emit dataChanged(createIndex(row,0),createIndex(row,0)); + } + delete q; +} + + +void RDImagePickerModel::refresh() +{ + LoadRows(c_category_id,c_image_size); +} + + +int RDImagePickerModel::rowCount(const QModelIndex &parent) const +{ + return c_images.size(); +} + + +QVariant RDImagePickerModel::data(const QModelIndex &index,int role) const +{ + if(index.column()==0) { + if(role==Qt::DisplayRole) { + return QVariant(c_descriptions.at(index.row())); + } + if(role==Qt::DecorationRole) { + if(c_images.at(index.row())!=NULL) { + return QVariant(*(c_images.at(index.row()))); + } + } + if(role==Qt::SizeHintRole) { + return QVariant(QSize(200,50)); + } + } + + return QVariant(); +} + + +QVariant RDImagePickerModel::headerData(int section,Qt::Orientation orient, + int role) +{ + if((orient==Qt::Horizontal)&&(section==0)) { + if(role==Qt::DisplayRole) { + return QVariant(tr("Image")); + } + } + + return QVariant(); +} + + +void RDImagePickerModel::setCategoryId(int id) +{ + if(id!=c_category_id) { + LoadRows(id,c_image_size); + c_category_id=id; + } +} + + +void RDImagePickerModel::LoadRows(int cat_id,const QSize &img_size) +{ + QString sql; + RDSqlQuery *q=NULL; + QImage img; + + // + // Clear stale data + // + if(c_images.size()>0) { + beginRemoveRows(QModelIndex(),0,c_images.size()-1); + for(int i=0;isize()>0) { + beginInsertRows(QModelIndex(),0,q->size()-1); + while(q->next()) { + c_image_ids.push_back(q->value(0).toUInt()); + c_descriptions. + push_back(q->value(1).toString()+"\n"+ + "["+q->value(2).toString().toUpper()+", "+ + QString().sprintf("%dx%d]", + q->value(3).toInt(),q->value(4).toInt())); + img.loadFromData(q->value(5).toByteArray()); + c_images.push_back(new QPixmap(QPixmap::fromImage(img.scaled(img_size, + Qt::KeepAspectRatio,Qt::SmoothTransformation)))); + } + endInsertRows(); + } + delete q; +} diff --git a/lib/rdimagepickermodel.h b/lib/rdimagepickermodel.h new file mode 100644 index 00000000..137fe154 --- /dev/null +++ b/lib/rdimagepickermodel.h @@ -0,0 +1,64 @@ +// rdimagepickermodel.h +// +// One-dimensional model for picking images +// +// (C) Copyright 2020 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. +// + +#ifndef RDIMAGEPICKERMODEL_H +#define RDIMAGEPICKERMODEL_H + +#include +#include +#include +#include + +class RDImagePickerModel : public QAbstractListModel +{ + Q_OBJECT; + public: + RDImagePickerModel(const QString &tbl_name,const QString &cat_id_col, + const QString &img_id_col,QObject *parent=0); + ~RDImagePickerModel(); + int categoryId() const; + int imageId(int row) const; + int imageRowOfId(int img_id) const; + void rescaleImages(const QSize &size); + void update(int row); + void refresh(); + int rowCount(const QModelIndex &parent=QModelIndex()) const; + QVariant data(const QModelIndex &index,int role=Qt::DisplayRole) const; + QVariant headerData(int section,Qt::Orientation orient, + int role=Qt::DisplayRole); + + public slots: + void setCategoryId(int id); + + + private: + void LoadRows(int cat_id,const QSize &img_size); + QString c_table_name; + QString c_category_column; + QString c_image_column; + int c_category_id; + QSize c_image_size; + QList c_images; + QStringList c_descriptions; + QList c_image_ids; +}; + + +#endif // RDIMAGEPICKERMODEL_H diff --git a/lib/rdlist_logs.cpp b/lib/rdlist_logs.cpp index a3206db7..75913345 100644 --- a/lib/rdlist_logs.cpp +++ b/lib/rdlist_logs.cpp @@ -119,13 +119,13 @@ void RDListLogs::okButtonData() return; } *list_logname=item->text(0); - done(0); + done(true); } void RDListLogs::cancelButtonData() { - done(1); + done(false); } diff --git a/lib/rdlog_icons.cpp b/lib/rdlog_icons.cpp new file mode 100644 index 00000000..efd63e8c --- /dev/null +++ b/lib/rdlog_icons.cpp @@ -0,0 +1,57 @@ +// rdlog_icons.cpp +// +// Icons for Rivendell log events. +// +// (C) Copyright 2020 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 "rdlog_icons.h" + +// +// Icons +// +#include "../icons/play.xpm" +#include "../icons/rml5.xpm" +#include "../icons/chain.xpm" +#include "../icons/track_cart.xpm" +#include "../icons/notemarker.xpm" +#include "../icons/music.xpm" +#include "../icons/mic16.xpm" +#include "../icons/traffic.xpm" + +RDLogIcons::RDLogIcons() +{ + // + // Create Icons + // + log_type_icons[RDLogLine::Cart]=QPixmap(play_xpm); + log_type_icons[RDLogLine::Macro]=QPixmap(rml5_xpm); + log_type_icons[RDLogLine::Marker]=QPixmap(notemarker_xpm); + log_type_icons[RDLogLine::Chain]=QPixmap(chain_xpm); + log_type_icons[RDLogLine::Track]=QPixmap(mic16_xpm); + log_type_icons[RDLogLine::MusicLink]=QPixmap(music_xpm); + log_type_icons[RDLogLine::TrafficLink]=QPixmap(traffic_xpm); + log_track_cart_icon=QPixmap(track_cart_xpm); +} + + +QPixmap RDLogIcons::typeIcon(RDLogLine::Type type,RDLogLine::Source src) const +{ + if((type==RDLogLine::Cart)&&(src==RDLogLine::Tracker)) { + return log_track_cart_icon; + } + return log_type_icons.value(type); +} diff --git a/lib/rdfeedlog.h b/lib/rdlog_icons.h similarity index 57% rename from lib/rdfeedlog.h rename to lib/rdlog_icons.h index 56c0337f..78840142 100644 --- a/lib/rdfeedlog.h +++ b/lib/rdlog_icons.h @@ -1,8 +1,8 @@ -// rdfeedlog.h +// rdlog_icons.h // -// Functions for manipulating RSS feed log tables. +// Icons for Rivendell log events. // -// (C) Copyright 2007,2016-2018 Fred Gleason +// (C) Copyright 2020 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 @@ -18,16 +18,25 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#ifndef RDFEEDLOG_H -#define RDFEEDLOG_H +#ifndef RDLOG_ICONS_H +#define RDLOG_ICONS_H -#include +#include +#include -void RDDeleteFeedLog(QString keyname); -void RDDeleteCastCount(QString keyname,unsigned cast_id); -void RDDeleteCastCount(unsigned feed_id,unsigned cast_id); -void RDIncrementFeedCount(QString keyname); -void RDIncrementCastCount(QString keyname,unsigned cast_id); +#include + +class RDLogIcons +{ + public: + RDLogIcons(); + QPixmap typeIcon(RDLogLine::Type type, + RDLogLine::Source src=RDLogLine::Manual) const; + + private: + QMap log_type_icons; + QPixmap log_track_cart_icon; +}; -#endif // RDFEEDLOG_H +#endif // RDLOG_ICONS diff --git a/lib/rdlog_line.cpp b/lib/rdlog_line.cpp index c6b2b00c..7939b5dc 100644 --- a/lib/rdlog_line.cpp +++ b/lib/rdlog_line.cpp @@ -66,15 +66,12 @@ RDLogLine::RDLogLine(unsigned cartnum) delete q; } + void RDLogLine::clearModified(void) { modified = false; } -bool RDLogLine::hasBeenModified(void) -{ - return modified; -} void RDLogLine::clear() { @@ -230,6 +227,12 @@ void RDLogLine::setId(int id) } +bool RDLogLine::hasBeenModified(void) +{ + return modified; +} + + RDLogLine::Status RDLogLine::status() const { return log_status; @@ -349,6 +352,43 @@ void RDLogLine::setCartNumber(unsigned cart) } +QString RDLogLine::cartNumberText() const +{ + QString ret=QObject::tr("UNKNOWN"); + + switch(type()) { + case RDLogLine::Cart: + case RDLogLine::Macro: + ret=QString().sprintf("%06u",cartNumber()); + break; + + case RDLogLine::Marker: + ret=QObject::tr("MARKER"); + break; + + case RDLogLine::Track: + ret=QObject::tr("TRACK"); + break; + + case RDLogLine::Chain: + ret=QObject::tr("LOG CHAIN"); + break; + + case RDLogLine::MusicLink: + case RDLogLine::TrafficLink: + ret=QObject::tr("LINK"); + break; + + case RDLogLine::OpenBracket: + case RDLogLine::CloseBracket: + case RDLogLine::UnknownType: + break; + } + + return ret; +} + + QTime RDLogLine::startTime(RDLogLine::StartTimeType type) const { return log_start_time[type]; @@ -364,6 +404,24 @@ void RDLogLine::setStartTime(RDLogLine::StartTimeType type,QTime time) } +QString RDLogLine::startTimeText() const +{ + QString ret(""); + + if(timeType()==RDLogLine::Hard) { + ret="T"; + } + else { + if(!startTime(RDLogLine::Logged).isValid()) { + return QString(" "); + } + } + ret+=startTime(RDLogLine::Logged).toString("hh:mm:ss.zzz").left(10); + + return ret; +} + + int RDLogLine::graceTime() const { return log_grace_time; @@ -755,6 +813,48 @@ void RDLogLine::setTitle(const QString &title) } +QString RDLogLine::titleText() const +{ + QString ret; + + switch(type()) { + case RDLogLine::Cart: + case RDLogLine::Macro: + if(title().isEmpty()) { + ret=QObject::tr("[cart not found]"); + } + else { + ret=title(); + } + break; + + case RDLogLine::Chain: + ret=markerComment(); + break; + + case RDLogLine::Track: + case RDLogLine::Marker: + ret=RDTruncateAfterWord(markerComment(),5,true); + break; + + case RDLogLine::MusicLink: + ret=QObject::tr("[music link]"); + break; + + case RDLogLine::TrafficLink: + ret=QObject::tr("[traffic link]"); + break; + + case RDLogLine::OpenBracket: + case RDLogLine::CloseBracket: + case RDLogLine::UnknownType: + break; + } + + return ret; +} + + QString RDLogLine::artist() const { return log_artist; @@ -995,6 +1095,31 @@ void RDLogLine::setForcedLength(unsigned len) } +QString RDLogLine::forcedLengthText() const +{ + QString ret=""; + + switch(type()) { + case RDLogLine::Cart: + case RDLogLine::Macro: + ret=RDGetTimeLength(forcedLength(),false,false); + break; + + case RDLogLine::Marker: + case RDLogLine::OpenBracket: + case RDLogLine::CloseBracket: + case RDLogLine::Chain: + case RDLogLine::Track: + case RDLogLine::MusicLink: + case RDLogLine::TrafficLink: + case RDLogLine::UnknownType: + break; + } + + return ret; +} + + unsigned RDLogLine::averageSegueLength() const { return log_average_segue_length; diff --git a/lib/rdlog_line.h b/lib/rdlog_line.h index 29c83d03..cfdf498a 100644 --- a/lib/rdlog_line.h +++ b/lib/rdlog_line.h @@ -21,9 +21,10 @@ #ifndef RDLOG_LINE_H #define RDLOG_LINE_H -#include -#include #include +#include +#include +#include #include #include @@ -31,8 +32,6 @@ class RDLogLine { public: - bool hasBeenModified(void); - enum StartTimeType {Imported=0,Logged=1,Predicted=2,Actual=3,Initial=4}; enum TimeType {Relative=0,Hard=1,NoTime=255}; enum TransType {Play=0,Segue=1,Stop=2,NoTrans=255}; @@ -56,6 +55,7 @@ class RDLogLine void clearModified(void); int id() const; void setId(int id); + bool hasBeenModified(void); RDLogLine::Status status() const; void setStatus(RDLogLine::Status stat); RDLogLine::State state() const; @@ -74,8 +74,10 @@ class RDLogLine void setSource(RDLogLine::Source src); unsigned cartNumber() const; void setCartNumber(unsigned cart); + QString cartNumberText() const; QTime startTime(RDLogLine::StartTimeType type) const; void setStartTime(RDLogLine::StartTimeType type,QTime time); + QString startTimeText() const; int graceTime() const; void setGraceTime(int time); RDLogLine::TimeType timeType() const; @@ -128,6 +130,7 @@ class RDLogLine void setGroupColor(const QColor &color); QString title() const; void setTitle(const QString &title); + QString titleText() const; QString artist() const; void setArtist(const QString &artist); QString publisher() const; @@ -168,6 +171,7 @@ class RDLogLine void setUsageCode(RDCart::UsageCode code); unsigned forcedLength() const; void setForcedLength(unsigned len); + QString forcedLengthText() const; unsigned averageSegueLength() const; void setAverageSegueLength(unsigned len); unsigned cutQuantity() const; @@ -385,4 +389,4 @@ class RDLogLine }; -#endif +#endif // RDLOG_LINE diff --git a/lib/rdnotification.cpp b/lib/rdnotification.cpp index 46ed9d9b..60c3e7d1 100644 --- a/lib/rdnotification.cpp +++ b/lib/rdnotification.cpp @@ -2,7 +2,7 @@ // // A container class for a Rivendell Notification message. // -// (C) Copyright 2018-2019 Fred Gleason +// (C) Copyright 2018-2020 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 @@ -115,6 +115,14 @@ bool RDNotification::read(const QString &str) notify_id=QVariant(args[3].toUInt()); break; + case RDNotification::FeedItemType: + notify_id=QVariant(args[3].toUInt()); + break; + + case RDNotification::FeedType: + notify_id=QVariant(args[3]); + break; + case RDNotification::NullType: case RDNotification::LastType: break; @@ -166,6 +174,14 @@ QString RDNotification::write() const ret+=QString().sprintf("%u",notify_id.toUInt()); break; + case RDNotification::FeedItemType: + ret+=QString().sprintf("%u",notify_id.toUInt()); + break; + + case RDNotification::FeedType: + ret+=notify_id.toString(); + break; + case RDNotification::NullType: case RDNotification::LastType: break; @@ -199,6 +215,14 @@ QString RDNotification::typeString(RDNotification::Type type) ret="CATCH_EVENT"; break; + case RDNotification::FeedItemType: + ret="FEED_ITEM"; + break; + + case RDNotification::FeedType: + ret="FEED"; + break; + case RDNotification::NullType: case RDNotification::LastType: break; diff --git a/lib/rdnotification.h b/lib/rdnotification.h index 3d0f02f5..0ffe6d47 100644 --- a/lib/rdnotification.h +++ b/lib/rdnotification.h @@ -2,7 +2,7 @@ // // A container class for a Rivendell Notification message. // -// (C) Copyright 2018-2019 Fred Gleason +// (C) Copyright 2018-2020 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 @@ -28,7 +28,7 @@ class RDNotification { public: enum Type {NullType=0,CartType=1,LogType=2,PypadType=3,DropboxType=4, - CatchEventType=5,LastType=6}; + CatchEventType=5,FeedItemType=6,FeedType=7,LastType=8}; enum Action {NoAction=0,AddAction=1,DeleteAction=2,ModifyAction=3, LastAction=4}; RDNotification(Type type,Action action,const QVariant &id); diff --git a/lib/rdpeaksexport.cpp b/lib/rdpeaksexport.cpp index 3412f197..3bb3536d 100644 --- a/lib/rdpeaksexport.cpp +++ b/lib/rdpeaksexport.cpp @@ -186,7 +186,7 @@ RDPeaksExport::ErrorCode RDPeaksExport::runExport(const QString &username, QString RDPeaksExport::errorText(RDPeaksExport::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDPeaksExport Error [%u]",err); switch(err) { case RDPeaksExport::ErrorOk: diff --git a/lib/rdpodcast.cpp b/lib/rdpodcast.cpp index 14c9244b..04905fa0 100644 --- a/lib/rdpodcast.cpp +++ b/lib/rdpodcast.cpp @@ -2,7 +2,7 @@ // // Abstract a Rivendell Podcast // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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,11 +25,13 @@ #include #include "rdapplication.h" -#include "rddb.h" -#include "rdpodcast.h" #include "rdconf.h" +#include "rddb.h" +#include "rddelete.h" #include "rdescape_string.h" +#include "rdpodcast.h" #include "rdurl.h" +#include "rdxport_interface.h" // // CURL Callbacks @@ -129,6 +131,31 @@ void RDPodcast::setItemDescription(const QString &str) const } +bool RDPodcast::itemExplicit() const +{ + return RDBool(RDGetSqlValue("PODCASTS","ID",podcast_id, + "ITEM_EXPLICIT").toString()); +} + + +void RDPodcast::setItemExplicit(bool state) const +{ + SetRow("ITEM_EXPLICIT",RDYesNo(state)); +} + + +int RDPodcast::itemImageId() const +{ + return RDGetSqlValue("PODCASTS","ID",podcast_id,"ITEM_IMAGE_ID").toInt(); +} + + +void RDPodcast::setItemImageId(int img_id) const +{ + SetRow("ITEM_IMAGE_ID",img_id); +} + + QString RDPodcast::itemCategory() const { return RDGetSqlValue("PODCASTS","ID",podcast_id,"ITEM_CATEGORY"). @@ -207,6 +234,31 @@ void RDPodcast::setItemSourceUrl(const QString &str) const } +QString RDPodcast::originLoginName() const +{ + return RDGetSqlValue("PODCASTS","ID",podcast_id,"ORIGIN_LOGIN_NAME"). + toString(); +} + + +void RDPodcast::setOriginLoginName(const QString &str) const +{ + SetRow("ORIGIN_LOGIN_NAME",str); +} + + +QString RDPodcast::originStation() const +{ + return RDGetSqlValue("PODCASTS","ID",podcast_id,"ORIGIN_STATION").toString(); +} + + +void RDPodcast::setOriginStation(const QString &str) const +{ + SetRow("ORIGIN_STATION",str); +} + + QDateTime RDPodcast::originDateTime() const { return RDGetSqlValue("PODCASTS","ID",podcast_id, @@ -266,22 +318,34 @@ int RDPodcast::audioTime() const } +QString RDPodcast::sha1Hash() const +{ + return RDGetSqlValue("PODCASTS","ID",podcast_id,"SHA1_HASH").toString(); +} + + +void RDPodcast::setSha1Hash(const QString &str) const +{ + SetRow("SHA1_HASH",str); +} + + void RDPodcast::setAudioTime(int msecs) const { SetRow("AUDIO_TIME",msecs); } -unsigned RDPodcast::shelfLife() const +QDateTime RDPodcast::expirationDateTime() const { - return RDGetSqlValue("PODCASTS","ID",podcast_id,"SHELF_LIFE"). - toUInt(); + return RDGetSqlValue("PODCASTS","ID",podcast_id, + "EXPIRATION_DATETIME").toDateTime(); } -void RDPodcast::setShelfLife(unsigned days) const +void RDPodcast::setExpirationDateTime(const QDateTime &dt) const { - SetRow("SHELF_LIFE",days); + SetRow("EXPIRATION_DATETIME",dt,"yyyy-MM-dd hh:mm:ss"); } @@ -298,81 +362,83 @@ void RDPodcast::setStatus(RDPodcast::Status status) } -bool RDPodcast::removeAudio(RDFeed *feed,QString *err_text,bool log_debug) const +bool RDPodcast::dropAudio(RDFeed *feed,QString *err_text,bool log_debug) const { - CURL *curl=NULL; - struct curl_slist *cmds=NULL; - CURLcode err; - QUrl *url; - bool ret=true; - QString currentdir; - char urlstr[1024]; - char userpwd[256]; - - if((curl=curl_easy_init())==NULL) { - rda->syslog(LOG_ERR,"unable to initialize curl library\n"); + if(!removePodcast()) { return false; } - url=new QUrl(feed->purgeUrl()); - strncpy(urlstr,(const char *)(url->protocol()+"://"+url->host()+"/").utf8(), - 1024); - curl_easy_setopt(curl,CURLOPT_URL,urlstr); - strncpy(userpwd,(feed->purgeUsername()+":"+feed->purgePassword()).utf8(),256); - curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); - curl_easy_setopt(curl,CURLOPT_HTTPAUTH,CURLAUTH_ANY); - curl_easy_setopt(curl,CURLOPT_USERAGENT, - (const char *)podcast_config->userAgent().utf8()); - if(log_debug) { - curl_easy_setopt(curl,CURLOPT_VERBOSE,1); - curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,PodcastErrorCallback); - } - if(url->scheme()=="ftp") { - currentdir=""; - if(!url->dirPath().right(url->dirPath().length()-1).isEmpty()) { - currentdir=url->dirPath().right(url->dirPath().length()-1)+"/"; - } - if(!url->fileName().isEmpty()) { - currentdir+=url->fileName()+"/"; - } - if(!currentdir.isEmpty()) { - cmds=curl_slist_append(cmds,QString().sprintf("cwd %s", - (const char *)currentdir)); - } - cmds=curl_slist_append(cmds, QString().sprintf("dele %s", - (const char *)audioFilename())); - } - if(url->scheme()=="sftp") { - cmds=curl_slist_append(cmds,("rm "+url->path()+"/"+audioFilename()).toUtf8()); - } - if(cmds==NULL) { - if(err_text!=NULL) { - *err_text="\""+url->scheme()+"\" scheme does not support remote deletion"; - delete url; - curl_easy_cleanup(curl); - return false; - } - } - curl_easy_setopt(curl,CURLOPT_QUOTE,cmds); - switch((err=curl_easy_perform(curl))) { - case CURLE_OK: -#ifdef CURLE_QUOTE_ERROR - case CURLE_QUOTE_ERROR: // In case the file is already gone -#endif // CURLE_QUOTE_ERROR - ret=true; - break; + return DeletePodcast(podcast_id); +} - default: - ret=false; - break; + +bool RDPodcast::removePodcast() const +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_REMOVE_PODCAST), + 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, + (const char *)QString().sprintf("%u",podcast_id), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; } - if(err_text!=NULL) { - *err_text=curl_easy_strerror(err); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + return false; } - curl_slist_free_all(cmds); + + // + // Clean up + // + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); curl_easy_cleanup(curl); - delete url; - - return ret; + curl_formfree(first); + + // + // Process the results + // + if((response_code<200)||(response_code>299)) { + return false; + } + + return true; } @@ -390,41 +456,120 @@ QString RDPodcast::guid(const QString &full_url,unsigned feed_id, } +bool RDPodcast::DeletePodcast(unsigned cast_id) const +{ + long response_code; + CURL *curl=NULL; + CURLcode curl_err; + struct curl_httppost *first=NULL; + struct curl_httppost *last=NULL; + + // + // Generate POST Data + // + curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND", + CURLFORM_COPYCONTENTS, + (const char *)QString().sprintf("%u",RDXPORT_COMMAND_DELETE_PODCAST), + 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, + (const char *)QString().sprintf("%u",cast_id), + CURLFORM_END); + + // + // Set up the transfer + // + if((curl=curl_easy_init())==NULL) { + curl_formfree(first); + return false; + } + curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout); + curl_easy_setopt(curl,CURLOPT_HTTPPOST,first); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent()); + 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) { + curl_easy_cleanup(curl); + curl_formfree(first); + 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)) { + return false; + } + + return true; +} + + void RDPodcast::SetRow(const QString ¶m,int value) const { - RDSqlQuery *q; QString sql; sql=QString("update PODCASTS set ")+ param+QString().sprintf("=%d where ",value)+ QString().sprintf("ID=%u",podcast_id); - q=new RDSqlQuery(sql); - delete q; + RDSqlQuery::apply(sql); } void RDPodcast::SetRow(const QString ¶m,const QString &value) const { - RDSqlQuery *q; QString sql; - sql=QString("update PODCASTS set ")+ - param+"=\""+RDEscapeString(value)+"\" where "+ - QString().sprintf("ID=%u",podcast_id); - q=new RDSqlQuery(sql); - delete q; + if(value.isNull()) { + sql=QString("update PODCASTS set ")+ + param+"=NULL where "+ + QString().sprintf("ID=%u",podcast_id); + } + else { + sql=QString("update PODCASTS set ")+ + param+"=\""+RDEscapeString(value)+"\" where "+ + QString().sprintf("ID=%u",podcast_id); + } + RDSqlQuery::apply(sql); } void RDPodcast::SetRow(const QString ¶m,const QDateTime &value, const QString &format) const { - RDSqlQuery *q; QString sql; - sql=QString("update PODCASTS set ")+ - param+"="+RDCheckDateTime(value, format)+" where "+ - QString().sprintf("ID=%u",podcast_id); - q=new RDSqlQuery(sql); - delete q; + if(value.isNull()) { + sql=QString("update PODCASTS set ")+ + param+"=NULL"+" where "+ + QString().sprintf("ID=%u",podcast_id); + } + else { + sql=QString("update PODCASTS set ")+ + param+"="+RDCheckDateTime(value, format)+" where "+ + QString().sprintf("ID=%u",podcast_id); + } + RDSqlQuery::apply(sql); } diff --git a/lib/rdpodcast.h b/lib/rdpodcast.h index 33e89fe7..e43cf91a 100644 --- a/lib/rdpodcast.h +++ b/lib/rdpodcast.h @@ -2,7 +2,7 @@ // // Abstract a Rivendell Podcast Entry // -// (C) Copyright 2002-2007,2016-2017 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -18,8 +18,6 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#include - #include #include @@ -40,6 +38,10 @@ class RDPodcast void setItemTitle(const QString &str) const; QString itemDescription() const; void setItemDescription(const QString &str) const; + bool itemExplicit() const; + void setItemExplicit(bool state) const; + int itemImageId() const; + void setItemImageId(int img_id) const; QString itemCategory() const; void setItemCategory(const QString &str) const; QString itemLink() const; @@ -52,6 +54,10 @@ class RDPodcast void setItemSourceText(const QString &str) const; QString itemSourceUrl() const; void setItemSourceUrl(const QString &str) const; + QString originLoginName() const; + void setOriginLoginName(const QString &str) const; + QString originStation() const; + void setOriginStation(const QString &str) const; QDateTime originDateTime() const; void setOriginDateTime(const QDateTime &datetime) const; QDateTime effectiveDateTime() const; @@ -62,19 +68,21 @@ class RDPodcast void setAudioLength(int len) const; int audioTime() const; void setAudioTime(int msecs) const; - unsigned shelfLife() const; - void setShelfLife(unsigned days) const; + QString sha1Hash() const; + void setSha1Hash(const QString &str=QString()) const; + QDateTime expirationDateTime() const; + void setExpirationDateTime(const QDateTime &dt) const; RDPodcast::Status status() const; void setStatus(RDPodcast::Status status); - //QString audioUploadCommand(const QString &srcfile) const; - //QString audioPurgeCommand() const; - bool removeAudio(RDFeed *feed,QString *err_text,bool log_debug) const; + bool dropAudio(RDFeed *feed,QString *err_text,bool log_debug) const; + bool removePodcast() const; static QString guid(const QString &url,const QString &filename, unsigned feed_id,unsigned cast_id); static QString guid(const QString &full_url, unsigned feed_id,unsigned cast_id); private: + bool DeletePodcast(unsigned cast_id) const; void SetRow(const QString ¶m,int value) const; void SetRow(const QString ¶m,const QString &value) const; void SetRow(const QString ¶m,const QDateTime &datetime,const QString &value) const; diff --git a/lib/rdrecording.cpp b/lib/rdrecording.cpp index 8e0f7d34..84780c76 100644 --- a/lib/rdrecording.cpp +++ b/lib/rdrecording.cpp @@ -2,7 +2,7 @@ // // Abstract a Rivendell Netcatch Recording. // -// (C) Copyright 2002-2004,2016 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -616,6 +616,18 @@ void RDRecording::setUrlPassword(QString passwd) const } +bool RDRecording::urlUseIdFile() const +{ + return RDBool(GetStringValue("URL_PASSWORD")); +} + + +void RDRecording::setUrlUseIdFile(bool state) const +{ + SetRow("URL_USE_ID_FILE",RDYesNo(state)); +} + + bool RDRecording::enableMetadata() const { return GetBoolValue("ENABLE_METADATA"); diff --git a/lib/rdrecording.h b/lib/rdrecording.h index 3dcb9a67..17077ad9 100644 --- a/lib/rdrecording.h +++ b/lib/rdrecording.h @@ -2,7 +2,7 @@ // // Abstract a Rivendell RDCatch Event // -// (C) Copyright 2002-2004,2016 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -131,6 +131,8 @@ class RDRecording void setUrlUsername(QString name) const; QString urlPassword() const; void setUrlPassword(QString passwd) const; + bool urlUseIdFile() const; + void setUrlUseIdFile(bool state) const; bool enableMetadata() const; void setEnableMetadata(bool state) const; int feedId() const; @@ -156,4 +158,4 @@ class RDRecording }; -#endif +#endif // RDRECORDING_H diff --git a/lib/rdrehash.cpp b/lib/rdrehash.cpp index 611daa16..f7c7c816 100644 --- a/lib/rdrehash.cpp +++ b/lib/rdrehash.cpp @@ -160,7 +160,7 @@ RDRehash::ErrorCode RDRehash::runRehash(const QString &username, QString RDRehash::errorText(RDRehash::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDRehash Error [%u]",err); switch(err) { case RDRehash::ErrorOk: diff --git a/lib/rdrenderer.cpp b/lib/rdrenderer.cpp index 533fe5fe..d64b459e 100644 --- a/lib/rdrenderer.cpp +++ b/lib/rdrenderer.cpp @@ -402,12 +402,19 @@ bool RDRenderer::Render(const QString &outfile,RDLogEvent *log,RDSettings *s, const QTime &first_time,const QTime &last_time) { float *pcm=NULL; - QTime current_time=start_time; QString temp_output_filename; + QTime current_time; render_warnings.clear(); render_abort=false; + if(start_time.isNull()) { + current_time=QTime::currentTime(); + } + else { + current_time=start_time; + } + // // Open Output File // @@ -483,7 +490,7 @@ bool RDRenderer::Render(const QString &outfile,RDLogEvent *log,RDSettings *s, } emit lineStarted(i,log->size()+render_total_passes-1); if(((first_line==-1)||(first_line<=(int)i))&& - ((last_line==-1)||(last_line>(int)i))) { + ((last_line==-1)||(last_line>=(int)i))) { if(lls.at(i)->transType()==RDLogLine::Stop) { ProgressMessage(current_time,i,tr("STOP")+" ",lls.at(i)->summary()); render_warnings. diff --git a/lib/rdripc.cpp b/lib/rdripc.cpp index 5cd039a5..3be58866 100644 --- a/lib/rdripc.cpp +++ b/lib/rdripc.cpp @@ -139,6 +139,15 @@ void RDRipc::sendGpoCart(int matrix) } +void RDRipc::sendNotification(RDNotification::Type type, + RDNotification::Action action,const QVariant &id) +{ + RDNotification *notify=new RDNotification(type,action,id); + sendNotification(*notify); + delete notify; +} + + void RDRipc::sendNotification(const RDNotification ¬ify) { SendCommand("ON "+notify.write()+"!"); diff --git a/lib/rdripc.h b/lib/rdripc.h index ca53add4..c3324f48 100644 --- a/lib/rdripc.h +++ b/lib/rdripc.h @@ -55,6 +55,8 @@ class RDRipc : public QObject void sendGpoMask(int matrix); void sendGpiCart(int matrix); void sendGpoCart(int matrix); + void sendNotification(RDNotification::Type type, + RDNotification::Action action,const QVariant &id); void sendNotification(const RDNotification ¬ify); void sendOnairFlag(); void sendRml(RDMacro *macro); diff --git a/lib/rdrsscategorybox.cpp b/lib/rdrsscategorybox.cpp new file mode 100644 index 00000000..77b2bea1 --- /dev/null +++ b/lib/rdrsscategorybox.cpp @@ -0,0 +1,160 @@ +// rdrsscategorybox.cpp +// +// A Combo Box widget for RSS categories. +// +// (C) Copyright 2020 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 "rdapplication.h" +#include "rdrsscategorybox.h" + +RDRssCategoryBox::RDRssCategoryBox(QWidget *parent) + : RDWidget(parent) +{ + c_schema=RDRssSchemas::CustomSchema; + + // + // Category + // + c_box=new QComboBox(this); + connect(c_box,SIGNAL(activated(const QString &)), + this,SLOT(boxActivatedData(const QString &))); + c_edit=new QLineEdit(this); + c_edit->setMaxLength(64); + + // + // Seperator + // + c_seperator_label=new QLabel(":",this); + c_seperator_label->setFont(labelFont()); + c_seperator_label->setAlignment(Qt::AlignCenter|Qt::AlignVCenter); + + // + // Subcategory + // + c_sub_box=new QComboBox(this); + c_sub_edit=new QLineEdit(this); + c_sub_edit->setMaxLength(64); + connect(c_sub_box,SIGNAL(activated(const QString &)), + c_sub_edit,SLOT(setText(const QString &))); +} + + +RDRssCategoryBox::~RDRssCategoryBox() +{ + delete c_box; + delete c_sub_box; + delete c_seperator_label; + delete c_edit; + delete c_sub_edit; +} + + +RDRssSchemas::RssSchema RDRssCategoryBox::schema() const +{ + return c_schema; +} + + +void RDRssCategoryBox::setSchema(RDRssSchemas::RssSchema schema) +{ + if(schema!=c_schema) { + RefreshCategories(schema,c_edit->text(),c_sub_edit->text()); + c_schema=schema; + } +} + + +QString RDRssCategoryBox::category() const +{ + return c_edit->text(); +} + + +QString RDRssCategoryBox::subCategory() const +{ + return c_sub_edit->text(); +} + + +void RDRssCategoryBox::setCategory(const QString &category, + const QString &sub_category) +{ + c_edit->setText(category); + c_sub_edit->setText(sub_category); + RefreshCategories(c_schema,category,sub_category); +} + + +void RDRssCategoryBox::boxActivatedData(const QString str) +{ + c_edit->setText(str); + RefreshSubcategories(c_schema,str,c_sub_edit->text()); +} + + +void RDRssCategoryBox::resizeEvent(QResizeEvent *e) +{ + int w=e->size().width(); + int h=e->size().height(); + + c_box->setGeometry(0,0,w/2-3,h); + c_edit->setGeometry(0,0,w/2-3,h); + c_seperator_label->setGeometry(w/2-2,0,5,h); + c_sub_box->setGeometry(w/2+3,0,w/2-2,h); + c_sub_edit->setGeometry(w/2+3,0,w/2-2,h); +} + + +void RDRssCategoryBox::RefreshCategories(RDRssSchemas::RssSchema schema, + const QString &category, + const QString &sub_category) +{ + QStringList categories=rda->rssSchemas()->categories(schema); + c_edit->setVisible(categories.size()==0); + c_sub_edit->setVisible(categories.size()==0); + c_box->setVisible(categories.size()>0); + c_sub_box->setVisible(categories.size()>0); + if(categories.size()>0) { // Update categories list + c_box->clear(); + for(int i=0;iinsertItem(c_box->count(),categories.at(i)); + if(category==categories.at(i)) { + c_box->setCurrentIndex(i); + } + } + c_edit->setText(c_box->currentText()); + RefreshSubcategories(schema,c_edit->text(),sub_category); + } +} + + +void RDRssCategoryBox::RefreshSubcategories(RDRssSchemas::RssSchema schema, + const QString &category, + const QString &sub_category) +{ + QStringList subcategories=rda->rssSchemas()->subCategories(schema,category); + + c_sub_box->clear(); + for(int i=0;iinsertItem(c_sub_box->count(),subcategories.at(i)); + if(subcategories.at(i)==sub_category) { + c_sub_box->setCurrentIndex(i); + } + } + c_sub_edit->setText(c_sub_box->currentText()); + c_sub_box->setDisabled(c_sub_box->count()==0); +} diff --git a/lib/rdrsscategorybox.h b/lib/rdrsscategorybox.h new file mode 100644 index 00000000..cfd16598 --- /dev/null +++ b/lib/rdrsscategorybox.h @@ -0,0 +1,66 @@ +// rdrsscategorybox.h +// +// A Combo Box widget for RSS categories. +// +// (C) Copyright 2020 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. +// + +#ifndef RDRSSCATEGORYBOX_H +#define RDRSSCATEGORYBOX_H + +#include +#include +#include +#include + +#include + +class RDRssCategoryBox : public RDWidget +{ + Q_OBJECT + public: + RDRssCategoryBox(QWidget *parent=0); + ~RDRssCategoryBox(); + RDRssSchemas::RssSchema schema() const; + void setSchema(RDRssSchemas::RssSchema schema); + QString category() const; + QString subCategory() const; + + public slots: + void setCategory(const QString &category,const QString &sub_category); + + private slots: + void boxActivatedData(const QString str); + + protected: + void resizeEvent(QResizeEvent *e); + + private: + void RefreshCategories(RDRssSchemas::RssSchema schema,const QString &category, + const QString &sub_category); + void RefreshSubcategories(RDRssSchemas::RssSchema schema, + const QString &category, + const QString &sub_category); + RDRssSchemas::RssSchema c_schema; + QComboBox *c_box; + QComboBox *c_sub_box; + QLabel *c_seperator_label; + QLineEdit *c_edit; + QLineEdit *c_sub_edit; +}; + + +#endif // RDRSSCATEGORYBOX_H diff --git a/lib/rdrssschemas.cpp b/lib/rdrssschemas.cpp new file mode 100644 index 00000000..c2627bdc --- /dev/null +++ b/lib/rdrssschemas.cpp @@ -0,0 +1,407 @@ +// rdrssschemas.cpp +// +// RSS schema definitions for Rivendell +// +// (C) Copyright 2020 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 "rdrssschemas.h" + +RDRssSchemas::RDRssSchemas() +{ + // + // Names + // + c_names.push_back("Custom"); // CustomSchema + c_names.push_back("RSS 2.0.2"); // Rss202Schema + c_names.push_back("Apple iTunes"); // AppleSchema + c_names.push_back("Apple iTunes + Superfeed"); // AppleSuperfeedSchema + + + // + // Minimum Images Sizes + // + c_minimum_image_sizes.push_back(QSize(0,0)); // CustomSchema + c_minimum_image_sizes.push_back(QSize(88,31)); // Rss202Schema + c_minimum_image_sizes.push_back(QSize(1400,1400)); // AppleSchema + c_minimum_image_sizes.push_back(QSize(1400,1400)); // AppleSuperfeedSchema + + + // + // Maximum Image Sizes + // + c_maximum_image_sizes.push_back(QSize(0,0)); // CustomSchema + c_maximum_image_sizes.push_back(QSize(144,400)); // Rss202Schema + c_maximum_image_sizes.push_back(QSize(3000,3000)); // AppleSchema + c_maximum_image_sizes.push_back(QSize(3000,3000)); // AppleSuperfeedSchema + + // + // Header Templates + // + // CustomSchema + c_header_templates.push_back(""); + + // Rss202Schema + c_header_templates.push_back("\n"); + + // AppleSchema + c_header_templates.push_back("\n"); + + // AppleSuperfeedSchema + c_header_templates.push_back("\n"); + + // + // Channel Templates + // + // CustomSchema + c_channel_templates.push_back(""); + + // Rs202Schema + c_channel_templates.push_back("%TITLE%\n%DESCRIPTION%\n%CATEGORY%\n%LINK%\n%LANGUAGE%\n%COPYRIGHT%\n%BUILD_DATE%\n%PUBLISH_DATE%\n%EDITOR%\n%WEBMASTER%\n%GENERATOR%\n\n %IMAGE_URL%\n %TITLE%\n %LINK%\n %IMAGE_WIDTH%\n %IMAGE_HEIGHT%\n %IMAGE_DESCRIPTION%\n\n"); + + // AppleSchema + c_channel_templates.push_back("%TITLE%\n%DESCRIPTION%\n%DESCRIPTION%\n%CATEGORY%\n%LINK%\n%LANGUAGE%\n%COPYRIGHT%\n%BUILD_DATE%\n%PUBLISH_DATE%\n%EDITOR%\n%WEBMASTER%\n%GENERATOR%\n\n %IMAGE_URL%\n %TITLE%\n %LINK%\n %IMAGE_WIDTH%\n %IMAGE_HEIGHT%\n %IMAGE_DESCRIPTION%\n\n\n%AUTHOR%\nepisodic\n\n %OWNER_NAME%\n %OWNER_EMAIL%\n\n\n%ITUNES_CATEGORY%\n%EXPLICIT%"); + + // AppleSuperfeedSchema + c_channel_templates.push_back("%TITLE%\n%DESCRIPTION%\n%DESCRIPTION%\n%CATEGORY%\n%LINK%\n%LANGUAGE%\n%COPYRIGHT%\n%BUILD_DATE%\n%PUBLISH_DATE%\n%EDITOR%\n%WEBMASTER%\n%GENERATOR%\n\n %IMAGE_URL%\n %TITLE%\n %LINK%\n %IMAGE_WIDTH%\n %IMAGE_HEIGHT%\n %IMAGE_DESCRIPTION%\n\n\n%AUTHOR%\nepisodic\n\n %OWNER_NAME%\n %OWNER_EMAIL%\n\n\n%ITUNES_CATEGORY%\n%EXPLICIT%"); + + + // + // Item Image Support + // + // CustomSchema + c_supports_item_images.push_back(true); + + // Rss202Schema + c_supports_item_images.push_back(false); + + // AppleSchema + c_supports_item_images.push_back(true); + + // AppleSuperfeedSchema + c_supports_item_images.push_back(true); + + + // + // Item Category Support + // + // CustomSchema + c_supports_item_categories.push_back(true); + + // Rss202Schema + c_supports_item_categories.push_back(true); + + // AppleSchema + c_supports_item_categories.push_back(false); + + // AppleSuperfeedSchema + c_supports_item_categories.push_back(false); + + + // + // Item Link Support + // + // CustomSchema + c_supports_item_links.push_back(true); + + // Rss202Schema + c_supports_item_links.push_back(true); + + // AppleSchema + c_supports_item_links.push_back(false); + + // AppleSuperfeedSchema + c_supports_item_links.push_back(false); + + + // + // Item Comments Support + // + // CustomSchema + c_supports_item_comments.push_back(true); + + // Rss202Schema + c_supports_item_comments.push_back(true); + + // AppleSchema + c_supports_item_comments.push_back(false); + + // AppleSuperfeedSchema + c_supports_item_comments.push_back(false); + + + // + // Item Templates + // + // CustomSchema + c_item_templates.push_back(""); + + // Rss202Schema + c_item_templates.push_back("%ITEM_TITLE%\n%ITEM_LINK%\n%ITEM_GUID%\n%ITEM_DESCRIPTION%\n%ITEM_AUTHOR%\n%ITEM_COMMENTS%\n%ITEM_SOURCE_TEXT%\n\n%ITEM_CATEGORY%\n%ITEM_PUBLISH_DATE%"); + + // AppleSchema + c_item_templates.push_back("%ITEM_TITLE%\n%ITEM_TITLE%\n%ITEM_LINK%\n%ITEM_GUID%\n%ITEM_DESCRIPTION%\n%ITEM_DESCRIPTION%\n%ITEM_AUTHOR%\n%ITEM_AUTHOR%\n%ITEM_COMMENTS%\n%ITEM_SOURCE_TEXT%\n\n%ITEM_CATEGORY%\n%ITEM_PUBLISH_DATE%\n%ITEM_AUDIO_SECONDS%\n\n%ITEM_EXPLICIT%"); + + // AppleSuperfeedSchema + c_item_templates.push_back("%ITEM_CHANNEL_TITLE%\n%ITEM_CHANNEL_DESCRIPTION%\n%ITEM_TITLE%\n%ITEM_TITLE%\n%ITEM_LINK%\n%ITEM_GUID%\n%ITEM_DESCRIPTION%\n%ITEM_DESCRIPTION%\n%ITEM_AUTHOR%\n%ITEM_AUTHOR%\n%ITEM_COMMENTS%\n%ITEM_SOURCE_TEXT%\n\n%ITEM_CATEGORY%\n%ITEM_PUBLISH_DATE%\n%ITEM_AUDIO_SECONDS%\n\n%ITEM_EXPLICIT%"); + + // + // Categories + // + QMap empty_sub_category; + + c_categories.push_back(QStringList()); // CustomSchema + c_sub_categories.push_back(empty_sub_category); + + c_categories.push_back(QStringList()); // Rss202Schema + c_sub_categories.push_back(empty_sub_category); + + QStringList apple_categories; + QMap apple_sub_categories; + + // + // Apple categories are from + // https://help.apple.com/itc/podcasts_connect/#/itc9267a2f12 + // + apple_categories.push_back("Arts"); + apple_sub_categories["Arts"]=QStringList(); + apple_sub_categories["Arts"].push_back("Books"); + apple_sub_categories["Arts"].push_back("Design"); + apple_sub_categories["Arts"].push_back("Fashion & Beauty"); + apple_sub_categories["Arts"].push_back("Food"); + apple_sub_categories["Arts"].push_back("Performing Arts"); + apple_sub_categories["Arts"].push_back("Visual Arts"); + + apple_categories.push_back("Business"); + apple_sub_categories["Business"]=QStringList(); + apple_sub_categories["Business"].push_back("Careers"); + apple_sub_categories["Business"].push_back("Entrepreneurship"); + apple_sub_categories["Business"].push_back("Investing"); + apple_sub_categories["Business"].push_back("Management"); + apple_sub_categories["Business"].push_back("Marketing"); + apple_sub_categories["Business"].push_back("Non-Profit"); + + apple_categories.push_back("Comedy"); + apple_sub_categories["Comedy"]=QStringList(); + apple_sub_categories["Comedy"].push_back("Comedy Interviews"); + apple_sub_categories["Comedy"].push_back("Improv"); + apple_sub_categories["Comedy"].push_back("Stand-Up"); + + apple_categories.push_back("Education"); + apple_sub_categories["Education"]=QStringList(); + apple_sub_categories["Education"].push_back("Courses"); + apple_sub_categories["Education"].push_back("How To"); + apple_sub_categories["Education"].push_back("Language Learning"); + apple_sub_categories["Education"].push_back("Self-Improvement"); + + apple_categories.push_back("Fiction"); + apple_sub_categories["Fiction"]=QStringList(); + apple_sub_categories["Fiction"].push_back("Comedy Fiction"); + apple_sub_categories["Fiction"].push_back("Drama"); + apple_sub_categories["Fiction"].push_back("Science Fiction"); + + apple_categories.push_back("Government"); + apple_sub_categories["Government"]=QStringList(); + + apple_categories.push_back("History"); + apple_sub_categories["History"]=QStringList(); + + apple_categories.push_back("Health & Fitness"); + apple_sub_categories["Health & Fitness"]=QStringList(); + apple_sub_categories["Health & Fitness"].push_back("Alternative Health"); + apple_sub_categories["Health & Fitness"].push_back("Fitness"); + apple_sub_categories["Health & Fitness"].push_back("Medicine"); + apple_sub_categories["Health & Fitness"].push_back("Mental Health"); + apple_sub_categories["Health & Fitness"].push_back("Nutrition"); + apple_sub_categories["Health & Fitness"].push_back("Sexuality"); + + apple_categories.push_back("Kids & Family"); + apple_sub_categories["Kids & Family"]=QStringList(); + apple_sub_categories["Kids & Family"].push_back("Education for Kids"); + apple_sub_categories["Kids & Family"].push_back("Parenting"); + apple_sub_categories["Kids & Family"].push_back("Pets & Animals"); + apple_sub_categories["Kids & Family"].push_back("Stories for Kids"); + + apple_categories.push_back("Leisure"); + apple_sub_categories["Leisure"]=QStringList(); + apple_sub_categories["Leisure"].push_back("Animation & Manga"); + apple_sub_categories["Leisure"].push_back("Automotive"); + apple_sub_categories["Leisure"].push_back("Aviation"); + apple_sub_categories["Leisure"].push_back("Crafts"); + apple_sub_categories["Leisure"].push_back("Games"); + apple_sub_categories["Leisure"].push_back("Hobbies"); + apple_sub_categories["Leisure"].push_back("Home & Garden"); + apple_sub_categories["Leisure"].push_back("Video Games"); + + apple_categories.push_back("Music"); + apple_sub_categories["Music"]=QStringList(); + apple_sub_categories["Music"].push_back("Music Commentary"); + apple_sub_categories["Music"].push_back("Music History"); + apple_sub_categories["Music"].push_back("Music Interviews"); + + apple_categories.push_back("News"); + apple_sub_categories["News"]=QStringList(); + apple_sub_categories["News"].push_back("Business News"); + apple_sub_categories["News"].push_back("Daily News"); + apple_sub_categories["News"].push_back("Entertainment News"); + apple_sub_categories["News"].push_back("News Commentary"); + apple_sub_categories["News"].push_back("Politics"); + apple_sub_categories["News"].push_back("Sports News"); + apple_sub_categories["News"].push_back("Tech News"); + + apple_categories.push_back("Religion & Spirituality"); + apple_sub_categories["Religion & Spirituality"]=QStringList(); + apple_sub_categories["Religion & Spirituality"].push_back("Buddhism"); + apple_sub_categories["Religion & Spirituality"].push_back("Christianity"); + apple_sub_categories["Religion & Spirituality"].push_back("Hinduism"); + apple_sub_categories["Religion & Spirituality"].push_back("Islam"); + apple_sub_categories["Religion & Spirituality"].push_back("Judaism"); + apple_sub_categories["Religion & Spirituality"].push_back("Religion"); + apple_sub_categories["Religion & Spirituality"].push_back("Spirituality"); + + apple_categories.push_back("Science"); + apple_sub_categories["Science"]=QStringList(); + apple_sub_categories["Science"].push_back("Astronomy"); + apple_sub_categories["Science"].push_back("Chemistry"); + apple_sub_categories["Science"].push_back("Earth Sciences"); + apple_sub_categories["Science"].push_back("Life Sciences"); + apple_sub_categories["Science"].push_back("Mathematics"); + apple_sub_categories["Science"].push_back("Natural Sciences"); + apple_sub_categories["Science"].push_back("Nature"); + apple_sub_categories["Science"].push_back("Physics"); + apple_sub_categories["Science"].push_back("Social Sciences"); + + apple_categories.push_back("Society & Culture"); + apple_sub_categories["Society & Culture"]=QStringList(); + apple_sub_categories["Society & Culture"].push_back("Documentary"); + apple_sub_categories["Society & Culture"].push_back("Personal Journals"); + apple_sub_categories["Society & Culture"].push_back("Philosophy"); + apple_sub_categories["Society & Culture"].push_back("Places & Travel"); + apple_sub_categories["Society & Culture"].push_back("Relationships"); + + apple_categories.push_back("Sports"); + apple_sub_categories["Sports"]=QStringList(); + apple_sub_categories["Sports"].push_back("Baseball"); + apple_sub_categories["Sports"].push_back("Basketball"); + apple_sub_categories["Sports"].push_back("Cricket"); + apple_sub_categories["Sports"].push_back("Fantasy Sports"); + apple_sub_categories["Sports"].push_back("Football"); + apple_sub_categories["Sports"].push_back("Golf"); + apple_sub_categories["Sports"].push_back("Hockey"); + apple_sub_categories["Sports"].push_back("Rugby"); + apple_sub_categories["Sports"].push_back("Running"); + apple_sub_categories["Sports"].push_back("Soccer"); + apple_sub_categories["Sports"].push_back("Swimming"); + apple_sub_categories["Sports"].push_back("Tennis"); + apple_sub_categories["Sports"].push_back("Volleyball"); + apple_sub_categories["Sports"].push_back("Wilderness"); + apple_sub_categories["Sports"].push_back("Wrestling"); + + apple_categories.push_back("Technology"); + apple_sub_categories["Technology"]=QStringList(); + + apple_categories.push_back("True Crime"); + apple_sub_categories["True Crime"]=QStringList(); + + apple_categories.push_back("TV & Film"); + apple_sub_categories["TV & Film"]=QStringList(); + apple_sub_categories["TV & Film"].push_back("After Shows"); + apple_sub_categories["TV & Film"].push_back("Film History"); + apple_sub_categories["TV & Film"].push_back("Film Interviews"); + apple_sub_categories["TV & Film"].push_back("Film Reviews"); + apple_sub_categories["TV & Film"].push_back("TV Reviews"); + + c_categories.push_back(apple_categories); // AppleSchema + c_sub_categories.push_back(apple_sub_categories); + + c_categories.push_back(apple_categories); // AppleSuperfeedSchema + c_sub_categories.push_back(apple_sub_categories); +} + + +QString RDRssSchemas::name(RssSchema schema) const +{ + return c_names.at(schema); +} + + +QSize RDRssSchemas::minimumImageSize(RssSchema schema) const +{ + return c_minimum_image_sizes.at(schema); +} + + +QSize RDRssSchemas::maximumImageSize(RssSchema schema) const +{ + return c_maximum_image_sizes.at(schema); +} + + +QString RDRssSchemas::headerTemplate(RssSchema schema) const +{ + return c_header_templates.at(schema); +} + + +QString RDRssSchemas::channelTemplate(RssSchema schema) const +{ + return c_channel_templates.at(schema); +} + + +QString RDRssSchemas::itemTemplate(RssSchema schema) const +{ + return c_item_templates.at(schema); +} + + +bool RDRssSchemas::supportsItemImages(RssSchema schema) const +{ + return c_supports_item_images.at(schema); +} + + +bool RDRssSchemas::supportsItemCategories(RssSchema schema) const +{ + return c_supports_item_categories.at(schema); +} + + +bool RDRssSchemas::supportsItemLinks(RssSchema schema) const +{ + return c_supports_item_links.at(schema); +} + + +bool RDRssSchemas::supportsItemComments(RssSchema schema) const +{ + return c_supports_item_comments.at(schema); +} + + +QStringList RDRssSchemas::categories(RDRssSchemas::RssSchema schema) const +{ + return c_categories.at((int)schema); +} + + +QStringList RDRssSchemas::subCategories(RssSchema schema, + const QString &category) const +{ + return c_sub_categories.at((int)schema).value(category); +} diff --git a/lib/rdrssschemas.h b/lib/rdrssschemas.h new file mode 100644 index 00000000..b9b383ca --- /dev/null +++ b/lib/rdrssschemas.h @@ -0,0 +1,65 @@ +// rdrssschemas.h +// +// RSS schema definitions for Rivendell +// +// (C) Copyright 2020 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. +// + +#ifndef RDRSSSCHEMAS_H +#define RDRSSSCHEMAS_H + +#include +#include +#include +#include +#include + +class RDRssSchemas +{ + public: + enum RssSchema {CustomSchema=0,Rss202Schema=1,AppleSchema=2, + AppleSuperfeedSchema=3,LastSchema=4}; + RDRssSchemas(); + QString name(RssSchema schema) const; + QSize minimumImageSize(RssSchema schema) const; + QSize maximumImageSize(RssSchema schema) const; + QString headerTemplate(RssSchema schema) const; + QString channelTemplate(RssSchema schema) const; + QString itemTemplate(RssSchema schema) const; + bool supportsItemImages(RssSchema schema) const; + bool supportsItemCategories(RssSchema schema) const; + bool supportsItemLinks(RssSchema schema) const; + bool supportsItemComments(RssSchema schema) const; + QStringList categories(RssSchema schema) const; + QStringList subCategories(RssSchema schema,const QString &category) const; + + private: + QStringList c_names; + QList c_minimum_image_sizes; + QList c_maximum_image_sizes; + QStringList c_header_templates; + QStringList c_channel_templates; + QStringList c_item_templates; + QList c_supports_item_images; + QList c_supports_item_categories; + QList c_supports_item_links; + QList c_supports_item_comments; + QList c_categories; + QList > c_sub_categories; +}; + + +#endif // RDRSSSCHEMAS_H diff --git a/lib/rdsettings.cpp b/lib/rdsettings.cpp index ab386920..f871b8f5 100644 --- a/lib/rdsettings.cpp +++ b/lib/rdsettings.cpp @@ -2,7 +2,7 @@ // // Audio Format Settings // -// (C) Copyright 2002-2015 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -138,8 +138,6 @@ void RDSettings::setAutotrimLevel(int level) QString RDSettings::description() { - QString sql; - RDSqlQuery *q; QString desc; QString sr=QString().sprintf("%d S/sec",set_sample_rate); switch(set_format) { @@ -147,6 +145,10 @@ QString RDSettings::description() desc="PCM16, "; break; + case RDSettings::Pcm24: + desc="PCM24, "; + break; + case RDSettings::MpegL1: desc="MPEG L1, "; if(set_bit_rate==0) { @@ -158,6 +160,7 @@ QString RDSettings::description() break; case RDSettings::MpegL2: + case RDSettings::MpegL2Wav: desc="MPEG L2, "; if(set_bit_rate==0) { desc+=QString().sprintf("Qual %d, ",set_quality); @@ -184,24 +187,6 @@ QString RDSettings::description() case RDSettings::OggVorbis: desc=QString().sprintf("OggVorbis, Qual %d, ",set_quality); break; - - default: // Custom format - if(set_format_name.isEmpty()) { - sql=QString().sprintf("select NAME from ENCODERS where ID=%d", - set_format); - q=new RDSqlQuery(sql); - if(q->first()) { - set_format_name=q->value(0).toString(); - } - else { - set_format_name="Unknown"; - } - delete q; - } - desc=set_format_name+" "; - if(set_bit_rate>0) { - desc+=" "+QString().sprintf("%d kbit/sec, ",set_bit_rate/1000); - } } if(set_sample_rate>0) { desc+=QString().sprintf("%d samp/sec, ",set_sample_rate); @@ -223,42 +208,6 @@ QString RDSettings::description() } -QString RDSettings::customCommandLine() const -{ - return set_custom_command_line; -} - - -void RDSettings::setCustomCommandLine(const QString &str) -{ - set_custom_command_line=str; -} - - -QString RDSettings::resolvedCustomCommandLine(const QString &destfile) -{ - if(set_custom_command_line.isEmpty()) { - QString sql; - RDSqlQuery *q; - sql=QString().sprintf("select COMMAND_LINE from ENCODERS where ID=%d", - set_format); - q=new RDSqlQuery(sql); - if(q->first()) { - set_custom_command_line=q->value(0).toString(); - } - delete q; - } - QString ret=set_custom_command_line; - - ret.replace("%f",destfile); - ret.replace("%c",QString().sprintf("%u",set_channels)); - ret.replace("%r",QString().sprintf("%u",set_sample_rate)); - ret.replace("%b",QString().sprintf("%u",set_bit_rate)); - - return ret; -} - - QString RDSettings::pathName(const QString &stationname,QString pathname, RDSettings::Format fmt) { @@ -336,23 +285,7 @@ QString RDSettings::defaultExtension(const QString &stationname, return QString("ogg"); } - // - // Custom Format - // - QString sql; - RDSqlQuery *q; - QString ret; - - sql=QString("select DEFAULT_EXTENSION from ENCODERS where ")+ - QString().sprintf("ID=%d)&&",fmt)+ - "(STATION_NAME=\""+RDEscapeString(stationname)+"\")"; - q=new RDSqlQuery(sql); - if(q->first()) { - ret=q->value(0).toString(); - } - delete q; - - return ret; + return QString("dat"); } @@ -394,5 +327,5 @@ void RDSettings::clear() set_quality=0; set_normalization_level=0; set_autotrim_level=0; - set_custom_command_line=""; + // set_custom_command_line=""; } diff --git a/lib/rdsettings.h b/lib/rdsettings.h index aedb68c4..22d839aa 100644 --- a/lib/rdsettings.h +++ b/lib/rdsettings.h @@ -2,7 +2,7 @@ // // Audio Format Settings // -// (C) Copyright 2002-2015 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -48,9 +48,6 @@ class RDSettings int autotrimLevel() const; void setAutotrimLevel(int level); QString description(); - QString customCommandLine() const; - void setCustomCommandLine(const QString &str); - QString resolvedCustomCommandLine(const QString &destfile); static QString pathName(const QString &stationname,QString pathname, RDSettings::Format fmt); static QString defaultExtension(RDSettings::Format fmt); @@ -70,9 +67,7 @@ class RDSettings unsigned set_quality; int set_normalization_level; int set_autotrim_level; - QString set_custom_command_line; }; -#endif - +#endif // RDSETTINGS_H diff --git a/lib/rdstation.cpp b/lib/rdstation.cpp index c6eb241c..4c1666ee 100644 --- a/lib/rdstation.cpp +++ b/lib/rdstation.cpp @@ -283,6 +283,19 @@ void RDStation::setBrowserPath(const QString &cmd) const } +QString RDStation::sshIdentityFile() const +{ + return RDGetSqlValue("STATIONS","NAME",station_name,"SSH_IDENTITY_FILE"). + toString(); +} + + +void RDStation::setSshIdentityFile(const QString &str) const +{ + SetRow("SSH_IDENTITY_FILE",str); +} + + RDStation::FilterMode RDStation::filterMode() const { return (RDStation::FilterMode)RDGetSqlValue("STATIONS","NAME",station_name, @@ -1888,7 +1901,6 @@ void RDStation::remove(const QString &name) { QString sql; RDSqlQuery *q; - RDSqlQuery *q1; sql=QString("delete from DECKS where ")+ "STATION_NAME=\""+RDEscapeString(name)+"\""; @@ -1978,28 +1990,6 @@ void RDStation::remove(const QString &name) "OWNER=\""+RDEscapeString(name)+"\")"; q=new RDSqlQuery(sql); delete q; - sql=QString("select ID from ENCODERS where ")+ - "STATION_NAME=\""+RDEscapeString(name)+"\""; - q=new RDSqlQuery(sql); - while(q->next()) { - sql=QString().sprintf("delete from ENCODER_CHANNELS where ENCODER_ID=%d", - q->value(0).toInt()); - q1=new RDSqlQuery(sql); - delete q1; - sql=QString().sprintf("delete from ENCODER_SAMPLERATES where ENCODER_ID=%d", - q->value(0).toInt()); - q1=new RDSqlQuery(sql); - delete q1; - sql=QString().sprintf("delete from ENCODER_BITRATES where ENCODER_ID=%d", - q->value(0).toInt()); - q1=new RDSqlQuery(sql); - delete q1; - } - delete q; - sql=QString("delete from ENCODERS where ")+ - "STATION_NAME=\""+RDEscapeString(name)+"\""; - q=new RDSqlQuery(sql); - delete q; sql=QString().sprintf("delete from RDHOTKEYS where ")+ "STATION_NAME=\""+RDEscapeString(name)+"\""; q=new RDSqlQuery(sql); diff --git a/lib/rdstation.h b/lib/rdstation.h index 056bc473..94dc15e3 100644 --- a/lib/rdstation.h +++ b/lib/rdstation.h @@ -68,6 +68,8 @@ class RDStation void setReportEditorPath(const QString &cmd); QString browserPath() const; void setBrowserPath(const QString &cmd) const; + QString sshIdentityFile() const; + void setSshIdentityFile(const QString &str) const; RDStation::FilterMode filterMode() const; void setFilterMode(RDStation::FilterMode mode) const; bool startJack() const; diff --git a/lib/rdsystem.cpp b/lib/rdsystem.cpp index 8aa2d8cc..e0c1148e 100644 --- a/lib/rdsystem.cpp +++ b/lib/rdsystem.cpp @@ -187,6 +187,18 @@ void RDSystem::setNotificationAddress(const QHostAddress &addr) } +QString RDSystem::rssProcessorStation() const +{ + return GetValue("RSS_PROCESSOR_STATION").toString(); +} + + +void RDSystem::setRssProcessorStation(const QString &str) const +{ + SetRow("RSS_PROCESSOR_STATION",str); +} + + QString RDSystem::xml() const { QString xml="\n"; @@ -218,24 +230,25 @@ QVariant RDSystem::GetValue(const QString &field) const void RDSystem::SetRow(const QString ¶m,QString value) const { - RDSqlQuery *q; QString sql; - // value.replace("\\","\\\\"); // Needed to preserve Windows pathnames - sql=QString("update SYSTEM set ")+ - param+"=\""+RDEscapeString(value)+"\""; - q=new RDSqlQuery(sql); - delete q; + if(value.isNull()) { + sql=QString("update SYSTEM set ")+ + param+"=NULL"; + } + else { + sql=QString("update SYSTEM set ")+ + param+"=\""+RDEscapeString(value)+"\""; + } + RDSqlQuery::apply(sql); } void RDSystem::SetRow(const QString ¶m,int value) const { - RDSqlQuery *q; QString sql; sql=QString("update SYSTEM set ")+ param+QString().sprintf("=%d",value); - q=new RDSqlQuery(sql); - delete q; + RDSqlQuery::apply(sql); } diff --git a/lib/rdsystem.h b/lib/rdsystem.h index e49aa7f7..98429cf1 100644 --- a/lib/rdsystem.h +++ b/lib/rdsystem.h @@ -44,6 +44,8 @@ class RDSystem void setShowUserList(bool state) const; QHostAddress notificationAddress() const; void setNotificationAddress(const QHostAddress &addr); + QString rssProcessorStation() const; + void setRssProcessorStation(const QString &str=QString()) const; QString xml() const; private: diff --git a/lib/rdtransfer.cpp b/lib/rdtransfer.cpp new file mode 100644 index 00000000..4d2694bf --- /dev/null +++ b/lib/rdtransfer.cpp @@ -0,0 +1,48 @@ +// rdtransfer.cpp +// +// Abstract base class for Rivendell remote data tranfer methods +// +// (C) Copyright 2020 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 "rdtransfer.h" + +RDTransfer::RDTransfer(RDConfig *c,QObject *parent) + : QObject(parent) +{ + xfer_config=c; +} + + +bool RDTransfer::urlIsSupported(const QString &url) +{ + return urlIsSupported(QUrl(url)); +} + + +bool RDTransfer::urlIsSupported(const QUrl &url) +{ + if(url.isRelative()||(!url.isValid())) { + return false; + } + return supportedSchemes().contains(url.scheme()); +} + + +RDConfig *RDTransfer::config() const +{ + return xfer_config; +} diff --git a/lib/rdtransfer.h b/lib/rdtransfer.h new file mode 100644 index 00000000..69600c00 --- /dev/null +++ b/lib/rdtransfer.h @@ -0,0 +1,47 @@ +// rdtransfer.h +// +// Abstract base class for Rivendell remote data tranfer methods +// +// (C) Copyright 2020 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. +// + +#ifndef RDTRANSFER_H +#define RDTRANSFER_H + +#include +#include +#include + +#include + +class RDTransfer : public QObject +{ + Q_OBJECT; + public: + RDTransfer(RDConfig *c,QObject *parent=0); + virtual QStringList supportedSchemes() const=0; + bool urlIsSupported(const QString &url); + bool urlIsSupported(const QUrl &url); + + protected: + RDConfig *config() const; + + private: + RDConfig *xfer_config; +}; + + +#endif // RDTRANSFER_H diff --git a/lib/rdtrimaudio.cpp b/lib/rdtrimaudio.cpp index 6caac37d..31b3638c 100644 --- a/lib/rdtrimaudio.cpp +++ b/lib/rdtrimaudio.cpp @@ -191,7 +191,7 @@ RDTrimAudio::ErrorCode RDTrimAudio::runTrim(const QString &username, QString RDTrimAudio::errorText(RDTrimAudio::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDTrimAudio Error [%u]",err); switch(err) { case RDTrimAudio::ErrorOk: diff --git a/lib/rdupload.cpp b/lib/rdupload.cpp index 3a635e94..4aa8a5cb 100644 --- a/lib/rdupload.cpp +++ b/lib/rdupload.cpp @@ -71,13 +71,26 @@ int UploadErrorCallback(CURL *curl,curl_infotype type,char *msg,size_t size, } -RDUpload::RDUpload(QObject *parent) - : QObject(parent) +RDUpload::RDUpload(RDConfig *c,QObject *parent) + : RDTransfer(c,parent) { conv_aborting=false; } +QStringList RDUpload::supportedSchemes() const +{ + QStringList schemes; + + schemes.push_back("file"); + schemes.push_back("ftp"); + schemes.push_back("sftp"); + schemes.push_back("ftps"); + + return schemes; +} + + void RDUpload::setSourceFile(const QString &filename) { conv_src_filename=filename; @@ -100,6 +113,8 @@ int RDUpload::totalSteps() const RDUpload::ErrorCode RDUpload::runUpload(const QString &username, const QString &password, + const QString &id_filename, + bool use_id_filename, bool log_debug) { CURL *curl=NULL; @@ -109,6 +124,10 @@ RDUpload::ErrorCode RDUpload::runUpload(const QString &username, RDSystemUser *user=NULL; char userpwd[256]; + if(!urlIsSupported(conv_dst_url)) { + return RDUpload::ErrorUnsupportedProtocol; + } + // // Validate User for file: transfers // @@ -139,12 +158,25 @@ RDUpload::ErrorCode RDUpload::runUpload(const QString &username, // url.replace("#","%23"); + // + // Authentication + // + if((conv_dst_url.scheme().toLower()=="sftp")&& + (!id_filename.isEmpty())&&use_id_filename) { + curl_easy_setopt(curl,CURLOPT_USERNAME,username.toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE, + id_filename.toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_KEYPASSWD,password.toUtf8().constData()); + } + else { + strncpy(userpwd,(username+":"+password).utf8(),256); + curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); + } + curl_easy_setopt(curl,CURLOPT_URL,(const char *)url); curl_easy_setopt(curl,CURLOPT_UPLOAD,1); curl_easy_setopt(curl,CURLOPT_READDATA,f); curl_easy_setopt(curl,CURLOPT_INFILESIZE,(long)conv_src_size); - strncpy(userpwd,(username+":"+password).utf8(),256); - curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd); curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); curl_easy_setopt(curl,CURLOPT_PROGRESSFUNCTION,UploadProgressCallback); curl_easy_setopt(curl,CURLOPT_PROGRESSDATA,this); @@ -219,7 +251,7 @@ bool RDUpload::aborting() const QString RDUpload::errorText(RDUpload::ErrorCode err) { - QString ret=QString().sprintf("Unknown Error [%u]",err); + QString ret=QString().sprintf("Unknown RDUpload Error [%u]",err); switch(err) { case RDUpload::ErrorOk: diff --git a/lib/rdupload.h b/lib/rdupload.h index babba14c..3b41cc3c 100644 --- a/lib/rdupload.h +++ b/lib/rdupload.h @@ -21,10 +21,11 @@ #ifndef RDUPLOAD_H #define RDUPLOAD_H -#include #include -class RDUpload : public QObject +#include + +class RDUpload : public RDTransfer { Q_OBJECT; public: @@ -34,12 +35,15 @@ class RDUpload : public QObject ErrorUnspecified=8,ErrorInvalidUser=9,ErrorAborted=10, ErrorInvalidLogin=11,ErrorRemoteAccess=12, ErrorRemoteConnection=13}; - RDUpload(QObject *parent=0); + RDUpload(RDConfig *c,QObject *parent=0); + QStringList supportedSchemes() const; void setSourceFile(const QString &filename); void setDestinationUrl(const QString &url); int totalSteps() const; RDUpload::ErrorCode runUpload(const QString &username, const QString &password, + const QString &id_filename, + bool use_id_filename, bool log_debug); bool aborting() const; static QString errorText(RDUpload::ErrorCode err); diff --git a/lib/rduser.cpp b/lib/rduser.cpp index b448823c..cc87f977 100644 --- a/lib/rduser.cpp +++ b/lib/rduser.cpp @@ -162,6 +162,38 @@ void RDUser::setFullName(const QString &name) const } +QString RDUser::emailAddress() const +{ + return RDGetSqlValue("USERS","LOGIN_NAME",user_name,"EMAIL_ADDRESS"). + toString(); +} + + +void RDUser::setEmailAddress(const QString &str) const +{ + SetRow("EMAIL_ADDRESS",str); +} + + +QString RDUser::emailContact() const +{ + QString ret; + + QString sql=QString("select ")+ + "EMAIL_ADDRESS,"+ // 00 + "FULL_NAME "+ // 01 + "from USERS where "+ + "LOGIN_NAME=\""+RDEscapeString(user_name)+"\""; + RDSqlQuery *q=new RDSqlQuery(sql); + if(q->first()) { + ret=RDUser::emailContact(q->value(0).toString(),q->value(1).toString()); + } + delete q; + + return ret; +} + + QString RDUser::description() const { return RDGetSqlValue("USERS","LOGIN_NAME",user_name,"DESCRIPTION").toString(); @@ -512,6 +544,22 @@ bool RDUser::cartAuthorized(unsigned cartnum) const } +bool RDUser::feedAuthorized(const QString &keyname) +{ + QString sql; + RDSqlQuery *q; + bool ret=false; + + sql=QString("select ID from FEED_PERMS where ")+ + "(USER_NAME=\""+RDEscapeString(user_name)+"\")&&"+ + "(KEY_NAME=\""+RDEscapeString(keyname)+"\")"; + q=new RDSqlQuery(sql); + ret=q->first(); + delete q; + return ret; +} + + QString RDUser::serviceCheckDefault(QString serv) const { bool match_flag = false; @@ -559,6 +607,36 @@ QStringList RDUser::services() const } +bool RDUser::emailIsValid(const QString &addr) +{ + QStringList f0=addr.split("@",QString::KeepEmptyParts); + + if(f0.size()!=2) { + return false; + } + QStringList f1=f0.last().split("."); + if(f1.size()<2) { + return false; + } + return true; +} + + +QString RDUser::emailContact(const QString &addr,const QString &fullname) +{ + QString ret; + + if(RDUser::emailIsValid(addr)) { + ret=addr; + if(!fullname.isEmpty()) { + ret+=" ("+fullname+")"; + } + } + + return ret; +} + + void RDUser::SetRow(const QString ¶m,const QString &value) const { RDSqlQuery *q; diff --git a/lib/rduser.h b/lib/rduser.h index 12fa6552..99d95c92 100644 --- a/lib/rduser.h +++ b/lib/rduser.h @@ -18,8 +18,6 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#include - #ifndef RDUSER_H #define RDUSER_H @@ -43,6 +41,9 @@ class RDUser void setPamService(const QString &str) const; QString fullName() const; void setFullName(const QString &name) const; + QString emailAddress() const; + void setEmailAddress(const QString &str) const; + QString emailContact() const; QString description() const; void setDescription(const QString &desc) const; QString phone() const; @@ -92,23 +93,11 @@ class RDUser bool groupAuthorized(const QString &group_name); QStringList groups() const; bool cartAuthorized(unsigned cartnum) const; - - /** Check a default service to ensure it is valid for the current user. - * - * @param serv QString with the proposed default service, presumably gotten - * from RDAirPlayConf::defaultSvc() - * @return QString with serv if it was valid, otherwise an empty QString. - */ + bool feedAuthorized(const QString &keyname); QString serviceCheckDefault(QString serv) const; - - /** Calculate the services associated with a user, based on the user's group - * membership and the relationship of groups to services. - * - * Note: admin users, those who pass adminConfig(), can see all services. - * - * @return QStringList with all the services associated with the user. - */ QStringList services() const; + static bool emailIsValid(const QString &addr); + static QString emailContact(const QString &addr,const QString &fullname); private: void SetRow(const QString ¶m,const QString &value) const; diff --git a/lib/rdxport_interface.h b/lib/rdxport_interface.h index 051b04a2..a6a14152 100644 --- a/lib/rdxport_interface.h +++ b/lib/rdxport_interface.h @@ -2,7 +2,7 @@ * * Public Interface for the RDXport Service * - * (C) Copyright 2010 Fred Gleason + * (C) Copyright 2010-2020 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,6 +57,15 @@ #define RDXPORT_COMMAND_LOCKLOG 34 #define RDXPORT_COMMAND_SAVESTRING 35 #define RDXPORT_COMMAND_SAVEFILE 36 +#define RDXPORT_COMMAND_GET_PODCAST 37 +#define RDXPORT_COMMAND_SAVE_PODCAST 38 +#define RDXPORT_COMMAND_DELETE_PODCAST 39 +#define RDXPORT_COMMAND_POST_PODCAST 40 +#define RDXPORT_COMMAND_REMOVE_PODCAST 41 +#define RDXPORT_COMMAND_POST_RSS 42 +#define RDXPORT_COMMAND_REMOVE_RSS 43 +#define RDXPORT_COMMAND_POST_IMAGE 44 +#define RDXPORT_COMMAND_REMOVE_IMAGE 45 #endif // RDXPORT_INTERFACE_H diff --git a/rdadmin/Makefile.am b/rdadmin/Makefile.am index dc57e3af..acfca9a8 100644 --- a/rdadmin/Makefile.am +++ b/rdadmin/Makefile.am @@ -2,7 +2,7 @@ ## ## Automake.am for rivendell/rdadmin ## -## (C) Copyright 2002-2018 Fred Gleason +## (C) Copyright 2002-2020 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 @@ -72,6 +72,7 @@ dist_rdadmin_SOURCES = add_feed.cpp add_feed.h\ edit_group.cpp edit_group.h\ edit_hostvar.cpp edit_hostvar.h\ edit_hotkeys.cpp edit_hotkeys.h\ + edit_image.cpp edit_image.h\ edit_jack.cpp edit_jack.h\ edit_jack_client.cpp edit_jack_client.h\ edit_livewiregpio.cpp edit_livewiregpio.h\ @@ -88,6 +89,7 @@ dist_rdadmin_SOURCES = add_feed.cpp add_feed.h\ edit_schedcodes.cpp edit_schedcodes.h\ edit_settings.cpp edit_settings.h\ edit_station.cpp edit_station.h\ + edit_superfeed.cpp edit_superfeed.h\ edit_svc.cpp edit_svc.h\ edit_svc_perms.cpp edit_svc_perms.h\ edit_ttys.cpp edit_ttys.h\ @@ -106,6 +108,7 @@ dist_rdadmin_SOURCES = add_feed.cpp add_feed.h\ list_gpis.cpp list_gpis.h\ list_groups.cpp list_groups.h\ list_hostvars.cpp list_hostvars.h\ + list_images.cpp list_images.h\ list_livewiregpios.cpp list_livewiregpios.h\ list_matrices.cpp list_matrices.h\ list_nodes.cpp list_nodes.h\ @@ -152,6 +155,7 @@ nodist_rdadmin_SOURCES = global_credits.c\ moc_edit_group.cpp\ moc_edit_hostvar.cpp\ moc_edit_hotkeys.cpp\ + moc_edit_image.cpp\ moc_edit_jack.cpp\ moc_edit_jack_client.cpp\ moc_edit_livewiregpio.cpp\ @@ -168,6 +172,7 @@ nodist_rdadmin_SOURCES = global_credits.c\ moc_edit_schedcodes.cpp\ moc_edit_settings.cpp\ moc_edit_station.cpp\ + moc_edit_superfeed.cpp\ moc_edit_svc.cpp\ moc_edit_svc_perms.cpp\ moc_edit_ttys.cpp\ @@ -185,6 +190,7 @@ nodist_rdadmin_SOURCES = global_credits.c\ moc_list_gpis.cpp\ moc_list_groups.cpp\ moc_list_hostvars.cpp\ + moc_list_images.cpp\ moc_list_livewiregpios.cpp\ moc_list_matrices.cpp\ moc_list_nodes.cpp\ diff --git a/rdadmin/add_feed.cpp b/rdadmin/add_feed.cpp index 7085973f..57048594 100644 --- a/rdadmin/add_feed.cpp +++ b/rdadmin/add_feed.cpp @@ -2,7 +2,7 @@ // // Add a Rivendell RSS Feed // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -20,15 +20,12 @@ #include #include -#include -#include #include #include #include #include #include -#include #include #include "add_feed.h" @@ -38,67 +35,61 @@ AddFeed::AddFeed(unsigned *id,QString *keyname,QWidget *parent) : RDDialog(parent) { - setModal(true); - feed_keyname=keyname; feed_id=id; // // Fix the Window Size // - setMinimumWidth(sizeHint().width()); - setMaximumWidth(sizeHint().width()); - setMinimumHeight(sizeHint().height()); - setMaximumHeight(sizeHint().height()); + setMinimumSize(sizeHint()); + setMaximumSize(sizeHint()); setWindowTitle("RDADmin - "+tr("Add RSS Feed")); - // - // Enable Users Checkbox - // - feed_users_box=new QCheckBox(this); - feed_users_box->setGeometry(40,40,15,15); - feed_users_box->setChecked(true); - QLabel *label=new QLabel(feed_users_box,tr("Enable Feed for All Users"),this); - label->setGeometry(60,38,sizeHint().width()-60,19); - label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - // // Text Validator // - RDTextValidator *validator=new RDTextValidator(this,"validator"); + RDTextValidator *validator=new RDTextValidator(this); + validator->addBannedChar(' '); // // Feed Name // feed_keyname_edit=new QLineEdit(this); - feed_keyname_edit->setGeometry(145,11,sizeHint().width()-150,19); feed_keyname_edit->setMaxLength(8); feed_keyname_edit->setValidator(validator); - label=new QLabel(feed_keyname_edit,tr("&New Feed Name:"),this); - label->setGeometry(10,11,130,19); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + connect(feed_keyname_edit,SIGNAL(textChanged(const QString &)), + this,SLOT(keynameChangedData(const QString &))); + feed_keyname_label=new QLabel(feed_keyname_edit,tr("&New Feed Name:"),this); + feed_keyname_label->setFont(labelFont()); + feed_keyname_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + + // + // Enable Users Checkbox + // + feed_users_box=new QCheckBox(this); + feed_users_box->setChecked(true); + feed_users_label= + new QLabel(feed_users_box,tr("Enable Feed for All Users"),this); + feed_users_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Ok Button // - QPushButton *ok_button=new QPushButton(this); - ok_button->setGeometry(sizeHint().width()-180,sizeHint().height()-60,80,50); - ok_button->setDefault(true); - ok_button->setFont(buttonFont()); - ok_button->setText(tr("&OK")); - connect(ok_button,SIGNAL(clicked()),this,SLOT(okData())); + feed_ok_button=new QPushButton(this); + feed_ok_button->setDefault(true); + feed_ok_button->setFont(buttonFont()); + feed_ok_button->setText(tr("&OK")); + feed_ok_button->setDisabled(true); + connect(feed_ok_button,SIGNAL(clicked()),this,SLOT(okData())); // // Cancel Button // - QPushButton *cancel_button=new QPushButton(this); - cancel_button->setGeometry(sizeHint().width()-90,sizeHint().height()-60, - 80,50); - cancel_button->setFont(buttonFont()); - cancel_button->setText(tr("&Cancel")); - connect(cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); + feed_cancel_button=new QPushButton(this); + feed_cancel_button->setFont(buttonFont()); + feed_cancel_button->setText(tr("&Cancel")); + connect(feed_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); } @@ -110,7 +101,7 @@ AddFeed::~AddFeed() QSize AddFeed::sizeHint() const { - return QSize(250,124); + return QSize(290,123); } @@ -120,60 +111,25 @@ QSizePolicy AddFeed::sizePolicy() const } +void AddFeed::keynameChangedData(const QString &str) +{ + feed_ok_button->setDisabled(str.isEmpty()); +} + + void AddFeed::okData() { - QString sql; - RDSqlQuery *q; - RDSqlQuery *q1; + QString err_msg; - sql=QString("select KEY_NAME from FEEDS where ")+ - "KEY_NAME=\""+RDEscapeString(feed_keyname_edit->text())+"\""; - q=new RDSqlQuery(sql); - if(q->first()) { - QMessageBox::warning(this,tr("Add Feed Error"), - tr("A feed with that key name already exists!")); - delete q; - return; + *feed_id=RDFeed::create(feed_keyname_edit->text(),feed_users_box->isChecked(), + &err_msg); + if(*feed_id!=0) { + *feed_keyname=feed_keyname_edit->text(); + done(0); } - delete q; - - // - // Create Default Feed Perms - // - if(feed_users_box->isChecked()) { - sql=QString("select LOGIN_NAME from USERS where ")+ - "(ADMIN_USERS_PRIV='N')&&(ADMIN_CONFIG_PRIV='N')"; - q=new RDSqlQuery(sql); - while(q->next()) { - sql=QString("insert into FEED_PERMS set ")+ - "USER_NAME=\""+RDEscapeString(q->value(0).toString())+"\","+ - "KEY_NAME=\""+RDEscapeString(feed_keyname_edit->text())+"\""; - q1=new RDSqlQuery(sql); - delete q1; - } - delete q; + else { + QMessageBox::warning(this,"RDAdmin - "+tr("Add Feed Error"),err_msg); } - - // - // Create Feed - // - sql=QString("insert into FEEDS set ")+ - "KEY_NAME=\""+RDEscapeString(feed_keyname_edit->text())+"\","+ - "ORIGIN_DATETIME=now(),"+ - "HEADER_XML=\""+RDEscapeString(DEFAULT_HEADER_XML)+"\","+ - "CHANNEL_XML=\""+RDEscapeString(DEFAULT_CHANNEL_XML)+"\","+ - "ITEM_XML=\""+RDEscapeString(DEFAULT_ITEM_XML)+"\""; - q=new RDSqlQuery(sql); - delete q; - sql=QString("select ID from FEEDS where ")+ - "KEY_NAME=\""+RDEscapeString(feed_keyname_edit->text())+"\""; - q=new RDSqlQuery(sql); - if(q->first()) { - *feed_id=q->value(0).toUInt(); - } - delete q; - *feed_keyname=feed_keyname_edit->text(); - done(0); } @@ -181,3 +137,16 @@ void AddFeed::cancelData() { done(-1); } + + +void AddFeed::resizeEvent(QResizeEvent *e) +{ + feed_keyname_label->setGeometry(10,11,120,19); + feed_keyname_edit->setGeometry(135,11,sizeHint().width()-140,19); + + feed_users_box->setGeometry(40,35,15,15); + feed_users_label->setGeometry(60,33,sizeHint().width()-60,19); + + feed_ok_button->setGeometry(size().width()-180,size().height()-60,80,50); + feed_cancel_button->setGeometry(size().width()-90,size().height()-60,80,50); +} diff --git a/rdadmin/add_feed.h b/rdadmin/add_feed.h index cfc10ece..01982546 100644 --- a/rdadmin/add_feed.h +++ b/rdadmin/add_feed.h @@ -2,7 +2,7 @@ // // Add a Rivendell Feed // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -22,33 +22,37 @@ #define ADD_FEED_H #include +#include #include +#include +#include #include -// -// Default XML Templates -// -#define DEFAULT_HEADER_XML "\n" -#define DEFAULT_CHANNEL_XML "%TITLE%\n%DESCRIPTION%\n%CATEGORY%\n%LINK%\n%LANGUAGE%\n%COPYRIGHT%\n%BUILD_DATE%\n%PUBLISH_DATE%\n%WEBMASTER%\n%GENERATOR%" -#define DEFAULT_ITEM_XML "%ITEM_TITLE%\n%ITEM_LINK%\n%ITEM_GUID%\n%ITEM_DESCRIPTION%\n%ITEM_AUTHOR%\n%ITEM_COMMENTS%\n%ITEM_SOURCE_TEXT%\n\n%ITEM_CATEGORY%\n%ITEM_PUBLISH_DATE%" - class AddFeed : public RDDialog { Q_OBJECT - public: - AddFeed(unsigned *id,QString *keyname,QWidget *parent=0); - ~AddFeed(); - QSize sizeHint() const; - QSizePolicy sizePolicy() const; + public: + AddFeed(unsigned *id,QString *keyname,QWidget *parent=0); + ~AddFeed(); + QSize sizeHint() const; + QSizePolicy sizePolicy() const; - private slots: - void okData(); - void cancelData(); + private slots: + void keynameChangedData(const QString &str); + void okData(); + void cancelData(); + + protected: + void resizeEvent(QResizeEvent *e); private: - QCheckBox *feed_users_box; + QLabel *feed_keyname_label; QLineEdit *feed_keyname_edit; + QCheckBox *feed_users_box; + QLabel *feed_users_label; + QPushButton *feed_ok_button; + QPushButton *feed_cancel_button; QString *feed_keyname; unsigned *feed_id; }; diff --git a/rdadmin/edit_feed.cpp b/rdadmin/edit_feed.cpp index f5310394..5f78c65b 100644 --- a/rdadmin/edit_feed.cpp +++ b/rdadmin/edit_feed.cpp @@ -2,7 +2,7 @@ // // Edit a Rivendell Feed // -// (C) Copyright 2002-2018 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -20,90 +20,99 @@ #include -#include -#include +#include +#include +#include #include #include #include -#include -#include +#include +#include #include +#include #include +#include #include "edit_feed.h" +#include "edit_superfeed.h" #include "globals.h" EditFeed::EditFeed(const QString &feed,QWidget *parent) : RDDialog(parent) { - setModal(true); - // // Fix the Window Size // - setMinimumWidth(sizeHint().width()); - setMaximumWidth(sizeHint().width()); - setMinimumHeight(sizeHint().height()); - setMaximumHeight(sizeHint().height()); + setMinimumSize(sizeHint()); + setMaximumSize(sizeHint()); feed_feed=new RDFeed(feed,rda->config(),this); + feed_image_model=new RDImagePickerModel("FEED_IMAGES","FEED_ID","ID",this); + feed_image_model->setCategoryId(feed_feed->id()); setWindowTitle("RDAdmin - "+tr("Feed: ")+feed); // - // Feed Name + // Dialogs // - feed_keyname_edit=new QLineEdit(this); - feed_keyname_edit->setGeometry(115,11,100,19); - feed_keyname_edit->setMaxLength(8); - feed_keyname_edit->setReadOnly(true); - QLabel *feed_keyname_label=new QLabel(feed_keyname_edit,tr("Key Name:"),this); - feed_keyname_label->setGeometry(10,11,100,19); - feed_keyname_label->setFont(labelFont()); - feed_keyname_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_images_dialog=new ListImages(feed_image_model,this); + + // + // Superfeed Settings + // + feed_is_superfeed_box=new QComboBox(this); + feed_is_superfeed_box->insertItem(0,tr("No")); + feed_is_superfeed_box->insertItem(1,tr("Yes")); + connect(feed_is_superfeed_box,SIGNAL(activated(int)), + this,SLOT(superfeedActivatedData(int))); + feed_is_superfeed_label=new QLabel(tr("Is Superfeed")+":",this); + feed_is_superfeed_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_is_superfeed_label->setFont(labelFont()); + feed_is_superfeed_button=new QPushButton(tr("Select Member\nFeeds"),this); + feed_is_superfeed_button->setFont(buttonFont()); + connect(feed_is_superfeed_button,SIGNAL(clicked()), + this,SLOT(selectSubfeedsData())); + + // + // Image Management Button + // + feed_list_images_button=new QPushButton(tr("Manage")+"\n"+tr("Images"),this); + feed_list_images_button->setFont(buttonFont()); + connect(feed_list_images_button,SIGNAL(clicked()), + this,SLOT(listImagesData())); // // Channel Section // feed_channel_section_groupbox=new QGroupBox(tr("Channel Values"),this); feed_channel_section_groupbox->setFont(labelFont()); - feed_channel_section_groupbox->setGeometry(10,45,sizeHint().width()/2-10,227); // // Channel Title // feed_channel_title_edit=new QLineEdit(this); - feed_channel_title_edit->setGeometry(115,60,375,19); - feed_channel_title_edit->setMaxLength(255); + feed_channel_title_edit->setMaxLength(191); feed_channel_title_label= new QLabel(feed_channel_title_edit,tr("Title:"),this); - feed_channel_title_label->setGeometry(20,60,90,19); feed_channel_title_label->setFont(labelFont()); feed_channel_title_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Channel Category // - feed_channel_category_edit=new QLineEdit(this); - feed_channel_category_edit->setGeometry(115,82,375,19); - feed_channel_category_edit->setMaxLength(64); - feed_channel_category_label= - new QLabel(feed_channel_category_edit,tr("Category:"),this); - feed_channel_category_label->setGeometry(20,82,90,19); + feed_channel_category_box=new RDRssCategoryBox(this); + feed_channel_category_label=new QLabel(tr("Category:"),this); feed_channel_category_label->setFont(labelFont()); - feed_channel_category_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_channel_category_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Channel Link // feed_channel_link_edit=new QLineEdit(this); - feed_channel_link_edit->setGeometry(115,104,375,19); - feed_channel_link_edit->setMaxLength(255); + feed_channel_link_edit->setMaxLength(191); feed_channel_link_label= new QLabel(feed_channel_link_edit,tr("Link:"),this); - feed_channel_link_label->setGeometry(20,104,90,19); feed_channel_link_label->setFont(labelFont()); feed_channel_link_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -111,24 +120,71 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Channel Copyright // feed_channel_copyright_edit=new QLineEdit(this); - feed_channel_copyright_edit->setGeometry(115,126,375,19); feed_channel_copyright_edit->setMaxLength(64); feed_channel_copyright_label= new QLabel(feed_channel_copyright_edit,tr("Copyright:"),this); - feed_channel_copyright_label->setGeometry(20,126,90,19); feed_channel_copyright_label->setFont(labelFont()); feed_channel_copyright_label-> setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // + // Channel Editor + // + feed_channel_editor_edit=new QLineEdit(this); + feed_channel_editor_edit->setMaxLength(64); + feed_channel_editor_label= + new QLabel(feed_channel_editor_edit,tr("Editor:"),this); + feed_channel_editor_label->setFont(labelFont()); + feed_channel_editor_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter); + + // + // Channel Author + // + feed_channel_author_edit=new QLineEdit(this); + feed_channel_author_edit->setMaxLength(64); + feed_channel_author_label= + new QLabel(feed_channel_author_edit,tr("Author:"),this); + feed_channel_author_label->setFont(labelFont()); + feed_channel_author_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter); + + feed_channel_author_is_default_check=new QCheckBox(this); + feed_channel_author_is_default_label= + new QLabel(tr("Use as default Item Author"),this); + feed_channel_author_is_default_label->setFont(labelFont()); + feed_channel_author_is_default_label-> + setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + // + // Channel Owner Name + // + feed_channel_owner_name_edit=new QLineEdit(this); + feed_channel_owner_name_edit->setMaxLength(64); + feed_channel_owner_name_label= + new QLabel(feed_channel_owner_name_edit,tr("Owner Name:"),this); + feed_channel_owner_name_label->setFont(labelFont()); + feed_channel_owner_name_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter); + + // + // Channel Owner Email + // + feed_channel_owner_email_edit=new QLineEdit(this); + feed_channel_owner_email_edit->setMaxLength(64); + feed_channel_owner_email_label= + new QLabel(feed_channel_owner_email_edit,tr("Owner E-Mail:"),this); + feed_channel_owner_email_label->setFont(labelFont()); + feed_channel_owner_email_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // // Channel Webmaster // feed_channel_webmaster_edit=new QLineEdit(this); - feed_channel_webmaster_edit->setGeometry(115,148,375,19); feed_channel_webmaster_edit->setMaxLength(64); feed_channel_webmaster_label= new QLabel(feed_channel_webmaster_edit,tr("Webmaster:"),this); - feed_channel_webmaster_label->setGeometry(20,148,90,19); feed_channel_webmaster_label->setFont(labelFont()); feed_channel_webmaster_label-> setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -137,23 +193,39 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Channel Language // feed_channel_language_edit=new QLineEdit(this); - feed_channel_language_edit-> - setGeometry(115,170,60,19); - feed_channel_language_edit->setMaxLength(5); + feed_channel_language_edit->setMaxLength(8); feed_channel_language_label= new QLabel(feed_channel_language_edit,tr("Language:"),this); - feed_channel_language_label->setGeometry(20,170,90,19); feed_channel_language_label->setFont(labelFont()); feed_channel_language_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // + // Channel is Explicit + // + feed_channel_explicit_check=new QCheckBox(this); + feed_channel_explicit_label=new QLabel(feed_channel_explicit_check, + tr("Channel contains explicit content"),this); + feed_channel_explicit_label->setFont(labelFont()); + feed_channel_explicit_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + // + // Channel Image + // + feed_channel_image_box= + new RDImagePickerBox("FEED_IMAGES","FEED_ID","ID",this); + feed_channel_image_box->setCategoryId(feed_feed->id()); + feed_channel_image_label= + new QLabel(feed_channel_image_box,tr("Image")+":",this); + feed_channel_image_label->setFont(labelFont()); + feed_channel_image_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // // Channel Description // - feed_channel_description_edit=new Q3TextEdit(this); - feed_channel_description_edit->setGeometry(115,192,375,76); + feed_channel_description_edit=new QTextEdit(this); + feed_channel_description_edit->setAcceptRichText(false); feed_channel_description_label= new QLabel(feed_channel_description_edit,tr("Description:"),this); - feed_channel_description_label->setGeometry(20,192,90,19); feed_channel_description_label->setFont(labelFont()); feed_channel_description_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -161,13 +233,11 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Purge Audio URL // feed_purge_url_edit=new QLineEdit(this); - feed_purge_url_edit->setGeometry(155,280,335,19); - feed_purge_url_edit->setMaxLength(255); + feed_purge_url_edit->setMaxLength(191); connect(feed_purge_url_edit,SIGNAL(textChanged(const QString &)), this,SLOT(purgeUrlChangedData(const QString &))); feed_purge_url_label= - new QLabel(feed_purge_url_edit,tr("Audio Upload URL:"),this); - feed_purge_url_label->setGeometry(20,280,130,19); + new QLabel(feed_purge_url_edit,tr("Upload URL")+":",this); feed_purge_url_label->setFont(labelFont()); feed_purge_url_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -175,13 +245,11 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Purge Username // feed_purge_username_edit=new QLineEdit(this); - feed_purge_username_edit->setGeometry(225,302,95,19); feed_purge_username_edit->setMaxLength(64); connect(feed_purge_username_edit,SIGNAL(textChanged(const QString &)), - this,SLOT(purgeUsernameChangedData(const QString &))); + this,SLOT(lineeditChangedData(const QString &))); feed_purge_username_label= new QLabel(feed_purge_username_edit,tr("Username:"),this); - feed_purge_username_label->setGeometry(40,302,180,19); feed_purge_username_label->setFont(labelFont()); feed_purge_username_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -189,27 +257,32 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Purge Password // feed_purge_password_edit=new QLineEdit(this); - feed_purge_password_edit->setGeometry(395,302,95,19); feed_purge_password_edit->setMaxLength(64); feed_purge_password_edit->setEchoMode(QLineEdit::Password); feed_purge_password_label= new QLabel(feed_purge_password_edit,tr("Password:"),this); - feed_purge_password_label->setGeometry(320,302,70,19); feed_purge_password_label->setFont(labelFont()); feed_purge_password_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // + // Purge Use Id File + // + feed_purge_use_id_file_check=new QCheckBox(this); + feed_purge_use_id_file_label= + new QLabel(feed_purge_use_id_file_check, + tr("Authenticate with local identity file"),this); + feed_purge_use_id_file_label->setFont(labelFont()); + feed_purge_use_id_file_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + // // Audio Format // feed_format_edit=new QLineEdit(this); - feed_format_edit->setGeometry(155,324,285,20); feed_format_edit->setReadOnly(true); - feed_format_label=new QLabel(feed_format_edit,tr("Upload Format:"),this); - feed_format_label->setGeometry(5,324,145,20); + feed_format_label=new QLabel(feed_format_edit,tr("Audio Format:"),this); feed_format_label->setFont(labelFont()); feed_format_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); feed_format_button=new QPushButton(this); - feed_format_button->setGeometry(450,324,40,24); feed_format_button->setFont(subButtonFont()); feed_format_button->setText(tr("S&et")); connect(feed_format_button,SIGNAL(clicked()),this,SLOT(setFormatData())); @@ -217,29 +290,24 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // // Normalize Check Box // - feed_normalize_box=new QCheckBox(this); - feed_normalize_box->setGeometry(155,348,15,15); - feed_normalize_box->setChecked(true); + feed_normalize_check=new QCheckBox(this); + feed_normalize_check->setChecked(true); feed_normalize_check_label= - new QLabel(feed_normalize_box,tr("Normalize"),this); - feed_normalize_check_label->setGeometry(175,346,83,20); + new QLabel(feed_normalize_check,tr("Normalize"),this); feed_normalize_check_label->setFont(labelFont()); feed_normalize_check_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - connect(feed_normalize_box,SIGNAL(toggled(bool)), - this,SLOT(normalizeCheckData(bool))); + connect(feed_normalize_check,SIGNAL(toggled(bool)), + this,SLOT(checkboxToggledData(bool))); // // Normalize Level // feed_normalize_spin=new QSpinBox(this); - feed_normalize_spin->setGeometry(295,346,40,20); feed_normalize_spin->setRange(-30,-1); feed_normalize_label=new QLabel(feed_normalize_spin,tr("Level:"),this); - feed_normalize_label->setGeometry(245,346,45,20); feed_normalize_label->setFont(labelFont()); feed_normalize_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); feed_normalize_unit_label=new QLabel(tr("dBFS"),this); - feed_normalize_unit_label->setGeometry(340,346,40,20); feed_normalize_unit_label->setFont(labelFont()); feed_normalize_unit_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); @@ -247,45 +315,30 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Base Audio URL // feed_base_url_edit=new QLineEdit(this); - feed_base_url_edit->setGeometry(155,368,335,19); - feed_base_url_edit->setMaxLength(255); + feed_base_url_edit->setMaxLength(191); feed_base_url_label= - new QLabel(feed_base_url_edit,tr("Audio Download URL:"),this); - feed_base_url_label->setGeometry(20,368,130,19); + new QLabel(feed_base_url_edit,tr("Download URL")+":",this); feed_base_url_label->setFont(labelFont()); feed_base_url_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // - // Keep Expired Metadata Checkbox + // AutoPost // - feed_keep_metadata_box=new QCheckBox(this); - feed_keep_metadata_box->setGeometry(155,390,15,15); - feed_keep_metadata_label= - new QLabel(feed_keep_metadata_box,tr("Keep Expired Metadata"),this); - feed_keep_metadata_label->setGeometry(175,390,180,19); - feed_keep_metadata_label->setFont(labelFont()); - feed_keep_metadata_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - - // - // AutoPost Checkbox - // - feed_autopost_box=new QCheckBox(this); - feed_autopost_box->setGeometry(365,390,15,15); + feed_autopost_box=new QComboBox(this); + feed_autopost_box->insertItem(feed_autopost_box->count(),tr("No")); + feed_autopost_box->insertItem(feed_autopost_box->count(),tr("Yes")); feed_autopost_label= - new QLabel(feed_autopost_box,tr("Enable AutoPost"),this); - feed_autopost_label->setGeometry(385,390,200,19); + new QLabel(feed_autopost_box,tr("Enable AutoPost")+":",this); feed_autopost_label->setFont(labelFont()); - feed_autopost_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + feed_autopost_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Enclosure Preamble // feed_base_preamble_edit=new QLineEdit(this); - feed_base_preamble_edit->setGeometry(155,412,335,19); - feed_base_preamble_edit->setMaxLength(255); + feed_base_preamble_edit->setMaxLength(191); feed_base_preamble_label= new QLabel(feed_base_preamble_edit,tr("Enclosure Preamble:"),this); - feed_base_preamble_label->setGeometry(20,412,130,19); feed_base_preamble_label->setFont(labelFont()); feed_base_preamble_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -293,11 +346,9 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Audio File Extension // feed_extension_edit=new QLineEdit(this); - feed_extension_edit->setGeometry(155,434,70,19); feed_extension_edit->setMaxLength(16); feed_extension_label= new QLabel(feed_extension_edit,tr("Audio Extension:"),this); - feed_extension_label->setGeometry(20,434,130,19); feed_extension_label->setFont(labelFont()); feed_extension_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -305,140 +356,150 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) // Maximum Shelf Life // feed_max_shelf_life_spin=new QSpinBox(this); - feed_max_shelf_life_spin->setGeometry(155,456,60,19); feed_max_shelf_life_spin->setRange(0,365); - feed_max_shelf_life_spin->setSpecialValueText(tr("None")); + feed_max_shelf_life_spin->setSpecialValueText(tr("Unlimited")); feed_max_shelf_life_label= - new QLabel(feed_max_shelf_life_spin,tr("Maximum Shelf Life:"),this); - feed_max_shelf_life_label->setGeometry(20,456,130,19); + new QLabel(feed_max_shelf_life_spin,tr("Default Shelf Life")+":",this); feed_max_shelf_life_label->setFont(labelFont()); - feed_max_shelf_life_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_max_shelf_life_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); feed_max_shelf_life_unit_label= new QLabel(feed_max_shelf_life_spin,tr("days"),this); - feed_max_shelf_life_unit_label->setGeometry(220,456,50,19); feed_max_shelf_life_unit_label->setFont(labelFont()); - feed_max_shelf_life_unit_label-> - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + feed_max_shelf_life_unit_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Episode Order // feed_castorder_box=new QComboBox(this); - feed_castorder_box->setGeometry(155,478,100,19); feed_castorder_box->insertItem(tr("Descending")); feed_castorder_box->insertItem(tr("Ascending")); feed_castorder_label= new QLabel(feed_castorder_box,tr("Episode Sort Order:"),this); - feed_castorder_label->setGeometry(20,478,130,19); feed_castorder_label->setFont(labelFont()); feed_castorder_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // - // Media Link Mode + // Default Item Image // - feed_media_link_mode_box=new QComboBox(this); - feed_media_link_mode_box->setGeometry(155,500,100,19); - feed_media_link_mode_box->insertItem(tr("None")); - feed_media_link_mode_box->insertItem(tr("Direct")); - feed_media_link_mode_box->insertItem(tr("Counted")); - feed_media_link_mode_label= - new QLabel(feed_media_link_mode_box,tr("Media Link Mode:"),this); - feed_media_link_mode_label->setGeometry(20,500,130,19); - feed_media_link_mode_label->setFont(labelFont()); - feed_media_link_mode_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_item_image_box=new RDImagePickerBox("FEED_IMAGES","FEED_ID","ID",this); + feed_item_image_box->setCategoryId(feed_feed->id()); + feed_item_image_label= + new QLabel(feed_item_image_box,tr("Default Item Image")+":",this); + feed_item_image_label->setFont(labelFont()); + feed_item_image_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // - // Feed Redirection + // RSS Schema // - feed_redirect_check=new QCheckBox(this); - feed_redirect_check->setGeometry(20,532,15,15); - QLabel *label= - new QLabel(feed_redirect_check,tr("Enable Feed Redirection"),this); - label->setGeometry(40,532,200,19); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - - feed_redirect_edit=new QLineEdit(this); - feed_redirect_edit->setGeometry(85,552,405,20); - feed_redirect_label=new QLabel(feed_redirect_edit,tr("URL:"),this); - feed_redirect_label->setGeometry(40,552,40,19); - feed_redirect_label->setFont(labelFont()); - feed_redirect_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_rss_schema_box=new QComboBox(this); + for(int i=0;i + insertItem(feed_rss_schema_box->count(), + rda->rssSchemas()->name((RDRssSchemas::RssSchema)i),i); + } + connect(feed_rss_schema_box,SIGNAL(activated(int)), + this,SLOT(schemaActivatedData(int))); + feed_rss_schema_label=new QLabel(tr("RSS Schema")+":",this); + feed_rss_schema_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_rss_schema_label->setFont(labelFont()); // // Header XML // - feed_header_xml_edit=new Q3TextEdit(this); - feed_header_xml_edit->setGeometry(615,10,365,76); + feed_header_xml_edit=new QTextEdit(this); + feed_header_xml_edit->setAcceptRichText(false); feed_header_xml_label=new QLabel(feed_header_xml_edit,tr("Header XML:"),this); - feed_header_xml_label->setGeometry(520,10,90,19); feed_header_xml_label->setFont(labelFont()); feed_header_xml_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_header_xml_button=new QPushButton(tr("Copy to\nClipboard"),this); + feed_header_xml_button->setFont(subButtonFont()); + connect(feed_header_xml_button,SIGNAL(clicked()), + this,SLOT(copyHeaderXmlData())); // // Channel XML // - feed_channel_xml_edit=new Q3TextEdit(this); - feed_channel_xml_edit->setGeometry(615,88,365,176); + feed_channel_xml_edit=new QTextEdit(this); + feed_channel_xml_edit->setAcceptRichText(false); feed_channel_xml_label= new QLabel(feed_channel_xml_edit,tr("Channel XML:"),this); - feed_channel_xml_label->setGeometry(520,88,90,19); feed_channel_xml_label->setFont(labelFont()); feed_channel_xml_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_channel_xml_button=new QPushButton(tr("Copy to\nClipboard"),this); + feed_channel_xml_button->setFont(subButtonFont()); + connect(feed_channel_xml_button,SIGNAL(clicked()), + this,SLOT(copyChannelXmlData())); // // Item XML // - feed_item_xml_edit=new Q3TextEdit(this); - feed_item_xml_edit->setGeometry(615,270,365,250); + feed_item_xml_edit=new QTextEdit(this); + feed_item_xml_edit->setAcceptRichText(false); feed_item_xml_label=new QLabel(feed_item_xml_edit,tr("Item XML:"),this); - feed_item_xml_label->setGeometry(520,270,90,19); feed_item_xml_label->setFont(labelFont()); feed_item_xml_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + feed_item_xml_button=new QPushButton(tr("Copy to\nClipboard"),this); + feed_item_xml_button->setFont(subButtonFont()); + connect(feed_item_xml_button,SIGNAL(clicked()), + this,SLOT(copyItemXmlData())); // // Ok Button // - QPushButton *ok_button=new QPushButton(this); - ok_button->setGeometry(sizeHint().width()-180,sizeHint().height()-60,80,50); - ok_button->setDefault(true); - ok_button->setFont(buttonFont()); - ok_button->setText(tr("&OK")); - connect(ok_button,SIGNAL(clicked()),this,SLOT(okData())); + feed_ok_button=new QPushButton(this); + feed_ok_button->setDefault(true); + feed_ok_button->setFont(buttonFont()); + feed_ok_button->setText(tr("&OK")); + connect(feed_ok_button,SIGNAL(clicked()),this,SLOT(okData())); // // Cancel Button // - QPushButton *cancel_button=new QPushButton(this); - cancel_button-> - setGeometry(sizeHint().width()-90,sizeHint().height()-60,80,50); - cancel_button->setFont(buttonFont()); - cancel_button->setText(tr("&Cancel")); - connect(cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); + feed_cancel_button=new QPushButton(this); + feed_cancel_button->setFont(buttonFont()); + feed_cancel_button->setText(tr("&Cancel")); + connect(feed_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); // // Populate Values // - feed_keyname_edit->setText(feed_feed->keyName()); + feed_is_superfeed_box->setCurrentIndex(feed_feed->isSuperfeed()); feed_channel_title_edit->setText(feed_feed->channelTitle()); - feed_channel_category_edit->setText(feed_feed->channelCategory()); + feed_channel_category_box->setSchema(feed_feed->rssSchema()); + feed_channel_category_box-> + setCategory(feed_feed->channelCategory(),feed_feed->channelSubCategory()); feed_channel_link_edit->setText(feed_feed->channelLink()); feed_channel_copyright_edit->setText(feed_feed->channelCopyright()); + feed_channel_editor_edit->setText(feed_feed->channelEditor()); + feed_channel_author_edit->setText(feed_feed->channelAuthor()); + feed_channel_author_is_default_check-> + setChecked(feed_feed->channelAuthorIsDefault()); + feed_channel_owner_name_edit->setText(feed_feed->channelOwnerName()); + feed_channel_owner_email_edit->setText(feed_feed->channelOwnerEmail()); feed_channel_webmaster_edit->setText(feed_feed->channelWebmaster()); - feed_channel_description_edit->setText(feed_feed->channelDescription()); + feed_channel_description_edit->setPlainText(feed_feed->channelDescription()); + feed_channel_image_box->setCurrentImageId(feed_feed->channelImageId()); feed_channel_language_edit->setText(feed_feed->channelLanguage()); - feed_base_url_edit->setText(feed_feed->baseUrl()); + feed_channel_explicit_check->setChecked(feed_feed->channelExplicit()); + feed_base_url_edit->setText(feed_feed->baseUrl("")); feed_base_preamble_edit->setText(feed_feed->basePreamble()); feed_purge_url_edit->setText(feed_feed->purgeUrl()); feed_purge_username_edit->setText(feed_feed->purgeUsername()); feed_purge_password_edit->setText(feed_feed->purgePassword()); - feed_header_xml_edit->setText(feed_feed->headerXml()); - feed_channel_xml_edit->setText(feed_feed->channelXml()); - feed_item_xml_edit->setText(feed_feed->itemXml()); + feed_purge_use_id_file_check->setChecked(feed_feed->purgeUseIdFile()); + purgeUrlChangedData(feed_purge_url_edit->text()); + RDRssSchemas::RssSchema schema=feed_feed->rssSchema(); + for(int i=0;icount();i++) { + if(feed_rss_schema_box->itemData(i).toInt()==schema) { + feed_rss_schema_box->setCurrentItem(i); + continue; + } + } + feed_header_xml_edit->setPlainText(feed_feed->headerXml()); + feed_channel_xml_edit->setPlainText(feed_feed->channelXml()); + feed_item_xml_edit->setPlainText(feed_feed->itemXml()); feed_max_shelf_life_spin->setValue(feed_feed->maxShelfLife()); - feed_autopost_box->setChecked(feed_feed->enableAutopost()); - feed_keep_metadata_box->setChecked(feed_feed->keepMetadata()); + feed_autopost_box->setCurrentIndex(feed_feed->enableAutopost()); feed_settings.setFormat(feed_feed->uploadFormat()); feed_settings.setChannels(feed_feed->uploadChannels()); feed_settings.setSampleRate(feed_feed->uploadSampleRate()); @@ -447,27 +508,29 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent) feed_extension_edit->setText(feed_feed->uploadExtension()); feed_format_edit->setText(feed_settings.description()); if(feed_feed->normalizeLevel()>0) { - feed_normalize_box->setChecked(false); + feed_normalize_check->setChecked(false); } else { - feed_normalize_box->setChecked(true); + feed_normalize_check->setChecked(true); feed_normalize_spin->setValue(feed_feed->normalizeLevel()/1000); } feed_castorder_box->setCurrentItem(feed_feed->castOrder()); - feed_media_link_mode_box->setCurrentItem((int)feed_feed->mediaLinkMode()); - feed_redirect_edit->setText(feed_feed->redirectPath()); - feed_redirect_check->setChecked(!feed_redirect_edit->text().isEmpty()); - normalizeCheckData(feed_normalize_box->isChecked()); + feed_item_image_box->setCurrentImageId(feed_feed->defaultItemImageId()); - RedirectChanged(feed_redirect_check->isChecked()); - connect(feed_redirect_check,SIGNAL(toggled(bool)), - this,SLOT(redirectToggledData(bool))); + UpdateControlState(); +} + + +EditFeed::~EditFeed() +{ + delete feed_image_model; + delete feed_feed; } QSize EditFeed::sizeHint() const { - return QSize(1000,591); + return QSize(1000,710); } @@ -477,31 +540,54 @@ QSizePolicy EditFeed::sizePolicy() const } -void EditFeed::purgeUrlChangedData(const QString &str) + +void EditFeed::superfeedActivatedData(int n) { - Q3Url url(str); - QString protocol=url.protocol(); - if(((protocol=="ftp")||(protocol=="sftp"))&& - (!feed_redirect_check->isChecked())) { - feed_purge_username_label->setEnabled(true); - feed_purge_username_edit->setEnabled(true); - } - else { - feed_purge_username_label->setDisabled(true); - feed_purge_username_edit->setDisabled(true); - } - purgeUsernameChangedData(feed_purge_username_edit->text()); + UpdateControlState(); } -void EditFeed::purgeUsernameChangedData(const QString &username) +void EditFeed::schemaActivatedData(int n) { - feed_purge_password_label-> - setDisabled(username.isEmpty()||feed_purge_url_edit->text().isEmpty()|| - feed_redirect_check->isChecked()); - feed_purge_password_edit-> - setDisabled(username.isEmpty()||feed_purge_url_edit->text().isEmpty()|| - feed_redirect_check->isChecked()); + feed_channel_category_box->setSchema((RDRssSchemas::RssSchema)n); + + UpdateControlState(); +} + + +void EditFeed::checkboxToggledData(bool state) +{ + UpdateControlState(); +} + + +void EditFeed::purgeUrlChangedData(const QString &str) +{ + QUrl url(str); + + if((url.scheme().toLower()=="sftp")&& + (!rda->station()->sshIdentityFile().isEmpty())) { + feed_purge_use_id_file_check->setEnabled(true); + feed_purge_use_id_file_label->setEnabled(true); + } + else { + feed_purge_use_id_file_check->setDisabled(true); + feed_purge_use_id_file_label->setDisabled(true); + } +} + + +void EditFeed::lineeditChangedData(const QString &str) +{ + UpdateControlState(); +} + + +void EditFeed::selectSubfeedsData() +{ + EditSuperfeed *d=new EditSuperfeed(feed_feed,this); + d->exec(); + delete d; } @@ -515,72 +601,128 @@ void EditFeed::setFormatData() } -void EditFeed::normalizeCheckData(bool state) +void EditFeed::listImagesData() { - feed_normalize_label->setEnabled(state); - feed_normalize_spin->setEnabled(state); - feed_normalize_unit_label->setEnabled(state); + feed_images_dialog->exec(feed_feed); + feed_channel_image_box->refresh(); + feed_item_image_box->refresh(); } -void EditFeed::redirectToggledData(bool state) + +void EditFeed::copyHeaderXmlData() { - if(state) { - switch(QMessageBox::warning(this,tr("Edit Feed - Redirect"), - tr("Enabling feed redirection will cause clients subscribed to\nthis feed to be PERMANENTLY redirected to the\nspecified URL.\n\nDo you still want to enable redireciton?"),QMessageBox::Yes,QMessageBox::No)) { - case QMessageBox::Yes: - break; - - default: - feed_redirect_check->setChecked(false); - return; - } + RDRssSchemas::RssSchema schema= + (RDRssSchemas::RssSchema)feed_rss_schema_box->currentIndex(); + + if(schema==RDRssSchemas::CustomSchema) { + QApplication::clipboard()->setText(feed_header_xml_edit->text()); + } + else { + QApplication::clipboard()-> + setText(rda->rssSchemas()->headerTemplate(schema)); + } +} + + +void EditFeed::copyChannelXmlData() +{ + RDRssSchemas::RssSchema schema= + (RDRssSchemas::RssSchema)feed_rss_schema_box->currentIndex(); + + if(schema==RDRssSchemas::CustomSchema) { + QApplication::clipboard()->setText(feed_channel_xml_edit->text()); + } + else { + QApplication::clipboard()-> + setText(rda->rssSchemas()->channelTemplate(schema)); + } +} + + +void EditFeed::copyItemXmlData() +{ + RDRssSchemas::RssSchema schema= + (RDRssSchemas::RssSchema)feed_rss_schema_box->currentIndex(); + + if(schema==RDRssSchemas::CustomSchema) { + QApplication::clipboard()->setText(feed_item_xml_edit->text()); + } + else { + QApplication::clipboard()-> + setText(rda->rssSchemas()->itemTemplate(schema)); } - RedirectChanged(state); } void EditFeed::okData() { + if(feed_is_superfeed_box->currentItem()&& + feed_feed->subfeedNames().size()==0) { + QMessageBox::warning(this,"RDAdmin - "+tr("Error"), + tr("Superfeed must have at least one subfeed assigned!")); + return; + } + RDDelete *d=new RDDelete(rda->config(),this); + RDUpload *u=new RDUpload(rda->config(),this); + if((!d->urlIsSupported(feed_purge_url_edit->text()))|| + (!u->urlIsSupported(feed_purge_url_edit->text()))) { + QMessageBox::warning(this,"RDAdmin - "+tr("Error"), + tr("Audio Upload URL has unsupported scheme!")); + delete d; + delete u; + return; + } + delete d; + delete u; + feed_feed->setIsSuperfeed(feed_is_superfeed_box->currentItem()); feed_feed->setChannelTitle(feed_channel_title_edit->text()); - feed_feed->setChannelCategory(feed_channel_category_edit->text()); + feed_feed->setChannelCategory(feed_channel_category_box->category()); + feed_feed->setChannelSubCategory(feed_channel_category_box->subCategory()); feed_feed->setChannelLink(feed_channel_link_edit->text()); feed_feed->setChannelCopyright(feed_channel_copyright_edit->text()); + feed_feed->setChannelEditor(feed_channel_editor_edit->text()); + feed_feed->setChannelAuthor(feed_channel_author_edit->text()); + feed_feed->setChannelAuthorIsDefault(feed_channel_author_is_default_check-> + isChecked()); + feed_feed->setChannelOwnerName(feed_channel_owner_name_edit->text()); + feed_feed->setChannelOwnerEmail(feed_channel_owner_email_edit->text()); feed_feed->setChannelWebmaster(feed_channel_webmaster_edit->text()); feed_feed->setChannelDescription(feed_channel_description_edit->text()); feed_feed->setChannelLanguage(feed_channel_language_edit->text()); + feed_feed->setChannelExplicit(feed_channel_explicit_check->isChecked()); + feed_feed->setChannelImageId(feed_channel_image_box->currentImageId()); + feed_feed->setDefaultItemImageId(feed_item_image_box->currentImageId()); feed_feed->setBaseUrl(feed_base_url_edit->text()); feed_feed->setBasePreamble(feed_base_preamble_edit->text()); feed_feed->setPurgeUrl(feed_purge_url_edit->text()); feed_feed->setPurgeUsername(feed_purge_username_edit->text()); feed_feed->setPurgePassword(feed_purge_password_edit->text()); + feed_feed->setPurgeUseIdFile(feed_purge_use_id_file_check->isChecked()&& + feed_purge_use_id_file_check->isEnabled()); + feed_feed-> + setRssSchema((RDRssSchemas::RssSchema)feed_rss_schema_box-> + itemData(feed_rss_schema_box->currentIndex()).toUInt()); feed_feed->setHeaderXml(feed_header_xml_edit->text()); feed_feed->setChannelXml(feed_channel_xml_edit->text()); feed_feed->setItemXml(feed_item_xml_edit->text()); feed_feed->setMaxShelfLife(feed_max_shelf_life_spin->value()); - feed_feed->setLastBuildDateTime(QDateTime(QDate::currentDate(), - QTime::currentTime())); - feed_feed->setEnableAutopost(feed_autopost_box->isChecked()); - feed_feed->setKeepMetadata(feed_keep_metadata_box->isChecked()); + feed_feed->setEnableAutopost(feed_autopost_box->currentIndex()); feed_feed->setUploadFormat(feed_settings.format()); feed_feed->setUploadChannels(feed_settings.channels()); feed_feed->setUploadSampleRate(feed_settings.sampleRate()); feed_feed->setUploadBitRate(feed_settings.bitRate()); feed_feed->setUploadQuality(feed_settings.quality()); feed_feed->setUploadExtension(feed_extension_edit->text()); - if(feed_normalize_box->isChecked()) { + if(feed_normalize_check->isChecked()) { feed_feed->setNormalizeLevel(feed_normalize_spin->value()*1000); } else { feed_feed->setNormalizeLevel(1); } feed_feed->setCastOrder(feed_castorder_box->currentItem()); - feed_feed->setMediaLinkMode((RDFeed::MediaLinkMode)feed_media_link_mode_box-> - currentItem()); - if(feed_redirect_check->isChecked()) { - feed_feed->setRedirectPath(feed_redirect_edit->text()); - } - else { - feed_feed->setRedirectPath(""); + + if(!feed_feed->postXmlConditional("RDAdmin",this)) { + return; } done(0); @@ -593,62 +735,177 @@ void EditFeed::cancelData() } -void EditFeed::RedirectChanged(bool state) +void EditFeed::resizeEvent(QResizeEvent *e) { - feed_redirect_label->setEnabled(state); - feed_redirect_edit->setEnabled(state); - feed_channel_title_edit->setDisabled(state); - feed_channel_description_edit->setDisabled(state); - feed_channel_category_edit->setDisabled(state); - feed_channel_link_edit->setDisabled(state); - feed_channel_copyright_edit->setDisabled(state); - feed_channel_webmaster_edit->setDisabled(state); - feed_channel_language_edit->setDisabled(state); - feed_base_url_edit->setDisabled(state); - feed_purge_url_edit->setDisabled(state); - feed_purge_username_label->setDisabled(state); - feed_purge_username_edit->setDisabled(state); - feed_purge_password_label->setDisabled(state); - feed_purge_password_edit->setDisabled(state); - feed_header_xml_edit->setDisabled(state); - feed_channel_xml_edit->setDisabled(state); - feed_item_xml_edit->setDisabled(state); - feed_max_shelf_life_spin->setDisabled(state); - feed_autopost_box->setDisabled(state); - feed_keep_metadata_box->setDisabled(state); - feed_format_edit->setDisabled(state); - feed_normalize_box->setDisabled(state); - feed_extension_edit->setDisabled(state); - feed_castorder_box->setDisabled(state); - feed_format_button->setDisabled(state); - feed_channel_title_label->setDisabled(state); - feed_channel_category_label->setDisabled(state); - feed_channel_link_label->setDisabled(state); - feed_channel_copyright_label->setDisabled(state); - feed_channel_webmaster_label->setDisabled(state); - feed_channel_language_label->setDisabled(state); - feed_channel_description_label->setDisabled(state); - feed_base_url_label->setDisabled(state); - feed_base_preamble_label->setDisabled(state); - feed_purge_url_label->setDisabled(state); - feed_max_shelf_life_label->setDisabled(state); - feed_max_shelf_life_unit_label->setDisabled(state); - feed_autopost_label->setDisabled(state); - feed_keep_metadata_label->setDisabled(state); - feed_format_label->setDisabled(state); - feed_normalize_check_label->setDisabled(state); - feed_normalize_unit_label->setDisabled(state); - feed_castorder_label->setDisabled(state); - feed_extension_label->setDisabled(state); - feed_channel_section_groupbox->setDisabled(state); - feed_header_xml_label->setDisabled(state); - feed_channel_xml_label->setDisabled(state); - feed_item_xml_label->setDisabled(state); - feed_normalize_label->setDisabled(state||(!feed_normalize_box->isChecked())); - feed_normalize_spin->setDisabled(state||(!feed_normalize_box->isChecked())); - feed_normalize_unit_label-> - setDisabled(state||(!feed_normalize_box->isChecked())); - feed_media_link_mode_box->setDisabled(state); - feed_media_link_mode_label->setDisabled(state); - purgeUrlChangedData(feed_purge_url_edit->text()); + feed_image_model->rescaleImages(QSize(36,36)); + + // + // Left-hand Side + // + feed_is_superfeed_box->setGeometry(115,2,60,19); + feed_is_superfeed_label->setGeometry(10,2,100,19); + feed_is_superfeed_button->setGeometry(185,2,140,38); + + feed_list_images_button->setGeometry(345,2,100,38); + + feed_channel_section_groupbox->setGeometry(10,41,sizeHint().width()/2-10,377); + + feed_channel_title_edit->setGeometry(115,56,375,19); + feed_channel_title_label->setGeometry(20,56,90,19); + + feed_channel_category_box->setGeometry(115,78,375,19); + feed_channel_category_label->setGeometry(20,78,90,19); + + feed_channel_link_edit->setGeometry(115,100,375,19); + feed_channel_link_label->setGeometry(20,100,90,19); + + feed_channel_copyright_edit->setGeometry(115,122,375,19); + feed_channel_copyright_label->setGeometry(20,122,90,19); + + feed_channel_editor_edit->setGeometry(115,144,375,19); + feed_channel_editor_label->setGeometry(20,144,90,19); + + feed_channel_author_edit->setGeometry(115,166,375,19); + feed_channel_author_label->setGeometry(20,166,90,19); + + feed_channel_author_is_default_check->setGeometry(120,186,15,15); + feed_channel_author_is_default_label->setGeometry(140,185,260,19); + + feed_channel_explicit_check->setGeometry(205,187,15,15); + feed_channel_explicit_label->setGeometry(225,186,260,19); + + feed_channel_owner_name_edit->setGeometry(115,209,375,19); + feed_channel_owner_name_label->setGeometry(20,209,90,19); + + feed_channel_owner_email_edit->setGeometry(115,231,375,19); + feed_channel_owner_email_label->setGeometry(20,231,90,19); + + feed_channel_webmaster_edit->setGeometry(115,253,375,19); + feed_channel_webmaster_label->setGeometry(20,253,90,19); + + feed_channel_language_edit->setGeometry(115,275,60,19); + feed_channel_language_label->setGeometry(20,275,90,19); + + feed_channel_explicit_check->setGeometry(205,277,15,15); + feed_channel_explicit_label->setGeometry(225,276,260,19); + + feed_channel_description_edit->setGeometry(115,297,375,76); + feed_channel_description_label->setGeometry(20,297,90,19); + + feed_channel_image_box->setGeometry(115,375,375,38); + feed_channel_image_box->setIconSize(QSize(36,36)); + feed_channel_image_label->setGeometry(20,375,90,19); + + feed_purge_url_edit->setGeometry(155,425,335,19); + feed_purge_url_label->setGeometry(20,425,130,19); + feed_purge_username_edit->setGeometry(225,445,95,19); + feed_purge_username_label->setGeometry(40,445,180,19); + feed_purge_password_edit->setGeometry(395,445,95,19); + feed_purge_password_label->setGeometry(320,445,70,19); + feed_purge_use_id_file_check->setGeometry(160,466,15,15); + feed_purge_use_id_file_label->setGeometry(180,464,300,19); + + feed_format_edit->setGeometry(155,485,285,20); + feed_format_label->setGeometry(5,485,145,20); + feed_format_button->setGeometry(450,483,40,24); + + feed_normalize_check->setGeometry(160,510,15,15); + feed_normalize_check_label->setGeometry(180,507,83,20); + feed_normalize_spin->setGeometry(295,507,40,20); + feed_normalize_label->setGeometry(245,507,45,20); + feed_normalize_unit_label->setGeometry(340,507,40,20); + + feed_extension_edit->setGeometry(155,529,70,19); + feed_extension_label->setGeometry(20,529,130,19); + + feed_base_url_edit->setGeometry(155,551,335,19); + feed_base_url_label->setGeometry(5,551,145,19); + + feed_autopost_box->setGeometry(155,573,60,19); + feed_autopost_label->setGeometry(5,573,145,19); + + feed_max_shelf_life_spin->setGeometry(155,595,90,19); + feed_max_shelf_life_label->setGeometry(20,595,130,19); + feed_max_shelf_life_unit_label->setGeometry(250,595,50,19); + + feed_item_image_box->setGeometry(155,617,335,38); + feed_item_image_box->setIconSize(QSize(36,36)); + feed_item_image_label->setGeometry(20,617,130,19); + + feed_base_preamble_edit->setGeometry(155,658,335,19); + feed_base_preamble_label->setGeometry(20,658,130,19); + + feed_castorder_box->setGeometry(155,682,100,19); + feed_castorder_label->setGeometry(20,682,130,19); + + // + // Right-hand Side + // + feed_rss_schema_label->setGeometry(520,10,90,19); + feed_rss_schema_box->setGeometry(615,10,200,19); + + feed_header_xml_label->setGeometry(520,32,90,19); + feed_header_xml_edit->setGeometry(615,32,size().width()-625,76); + feed_header_xml_button->setGeometry(540,54,70,30); + + feed_channel_xml_label->setGeometry(520,110,90,19); + feed_channel_xml_edit->setGeometry(615,110,size().width()-625,256); + feed_channel_xml_button->setGeometry(540,132,70,30); + + feed_item_xml_label->setGeometry(520,368,90,19); + feed_item_xml_edit->setGeometry(615,368,size().width()-625,240); + feed_item_xml_button->setGeometry(540,390,70,30); + + feed_ok_button->setGeometry(size().width()-180,size().height()-60,80,50); + feed_cancel_button->setGeometry(size().width()-90,size().height()-60,80,50); +} + + +void EditFeed::UpdateControlState() +{ + bool superfeed=feed_is_superfeed_box->currentIndex(); + bool custom_schema= + feed_rss_schema_box->itemData(feed_rss_schema_box->currentIndex()).toInt()== + RDRssSchemas::CustomSchema; + bool item_image=rda->rssSchemas()-> + supportsItemImages((RDRssSchemas::RssSchema)feed_rss_schema_box-> + itemData(feed_rss_schema_box->currentIndex()).toInt()); + + feed_is_superfeed_button->setEnabled(superfeed); + + feed_max_shelf_life_spin->setDisabled(superfeed); + feed_autopost_box->setDisabled(superfeed); + feed_autopost_label->setDisabled(superfeed); + feed_format_edit->setDisabled(superfeed); + feed_normalize_check->setDisabled(superfeed); + feed_extension_edit->setDisabled(superfeed); + feed_format_button->setDisabled(superfeed); + feed_max_shelf_life_label->setDisabled(superfeed); + feed_max_shelf_life_unit_label->setDisabled(superfeed); + feed_format_label->setDisabled(superfeed); + feed_normalize_check_label->setDisabled(superfeed); + feed_normalize_unit_label->setDisabled(superfeed); + feed_extension_label->setDisabled(superfeed); + + feed_normalize_label-> + setDisabled(superfeed||(!feed_normalize_check->isChecked())); + feed_normalize_spin-> + setDisabled(superfeed||(!feed_normalize_check->isChecked())); + feed_normalize_unit_label-> + setDisabled(superfeed||(!feed_normalize_check->isChecked())); + + feed_item_image_label->setDisabled(item_image&&(superfeed)); + feed_item_image_box->setDisabled(item_image&&(superfeed)); + + feed_header_xml_edit->setEnabled(custom_schema); + feed_channel_xml_edit->setEnabled(custom_schema); + feed_item_xml_edit->setDisabled(superfeed||(!custom_schema)); + feed_item_xml_button->setDisabled(superfeed); + + feed_purge_password_label-> + setDisabled(feed_purge_username_edit->text().isEmpty()|| + feed_purge_url_edit->text().isEmpty()); + feed_purge_password_edit-> + setDisabled(feed_purge_username_edit->text().isEmpty()|| + feed_purge_url_edit->text().isEmpty()); } diff --git a/rdadmin/edit_feed.h b/rdadmin/edit_feed.h index fa34560f..94d24b61 100644 --- a/rdadmin/edit_feed.h +++ b/rdadmin/edit_feed.h @@ -2,7 +2,7 @@ // // Edit a Rivendell Feed // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -28,42 +28,73 @@ #include #include #include - -#include +#include #include #include +#include #include +#include #include +#include "list_images.h" + class EditFeed : public RDDialog { Q_OBJECT public: EditFeed(const QString &feed,QWidget *parent=0); + ~EditFeed(); QSize sizeHint() const; QSizePolicy sizePolicy() const; private slots: + void superfeedActivatedData(int n); + void schemaActivatedData(int n); + void checkboxToggledData(bool state); void purgeUrlChangedData(const QString &str); - void purgeUsernameChangedData(const QString &username); + void lineeditChangedData(const QString &str); + void selectSubfeedsData(); void setFormatData(); - void normalizeCheckData(bool state); - void redirectToggledData(bool state); + void listImagesData(); + void copyHeaderXmlData(); + void copyChannelXmlData(); + void copyItemXmlData(); void okData(); void cancelData(); + + protected: + void resizeEvent(QResizeEvent *e); private: - void RedirectChanged(bool state); + void UpdateControlState(); RDFeed *feed_feed; - QLineEdit *feed_keyname_edit; + RDImagePickerModel *feed_image_model; + QLabel *feed_is_superfeed_label; + QPushButton *feed_is_superfeed_button; + QPushButton *feed_list_images_button; + QComboBox *feed_is_superfeed_box; QLineEdit *feed_channel_title_edit; - Q3TextEdit *feed_channel_description_edit; - QLineEdit *feed_channel_category_edit; + QTextEdit *feed_channel_description_edit; + QLabel *feed_channel_category_label; + RDRssCategoryBox *feed_channel_category_box; QLineEdit *feed_channel_link_edit; QLineEdit *feed_channel_copyright_edit; + QLabel *feed_channel_editor_label; + QLineEdit *feed_channel_editor_edit; + QLabel *feed_channel_author_label; + QLineEdit *feed_channel_author_edit; + QCheckBox *feed_channel_author_is_default_check; + QLabel *feed_channel_author_is_default_label; + QLabel *feed_channel_owner_name_label; + QLineEdit *feed_channel_owner_name_edit; + QLabel *feed_channel_owner_email_label; + QLineEdit *feed_channel_owner_email_edit; + QLabel *feed_channel_webmaster_label; QLineEdit *feed_channel_webmaster_edit; QLineEdit *feed_channel_language_edit; + QCheckBox *feed_channel_explicit_check; + QLabel *feed_channel_explicit_label; QLineEdit *feed_base_url_edit; QLineEdit *feed_base_preamble_edit; QLineEdit *feed_purge_url_edit; @@ -71,48 +102,53 @@ class EditFeed : public RDDialog QLineEdit *feed_purge_username_edit; QLabel *feed_purge_password_label; QLineEdit *feed_purge_password_edit; - Q3TextEdit *feed_header_xml_edit; - Q3TextEdit *feed_channel_xml_edit; - Q3TextEdit *feed_item_xml_edit; + QCheckBox *feed_purge_use_id_file_check; + QLabel *feed_purge_use_id_file_label; + QLabel *feed_rss_schema_label; + QComboBox *feed_rss_schema_box; + QLabel *feed_header_xml_label; + QTextEdit *feed_header_xml_edit; + QPushButton *feed_header_xml_button; + QLabel *feed_channel_xml_label; + QTextEdit *feed_channel_xml_edit; + QPushButton *feed_channel_xml_button; + QLabel *feed_item_xml_label; + QTextEdit *feed_item_xml_edit; + QPushButton *feed_item_xml_button; QSpinBox *feed_max_shelf_life_spin; - QCheckBox *feed_autopost_box; - QCheckBox *feed_keep_metadata_box; + QLabel *feed_autopost_label; + QComboBox *feed_autopost_box; RDSettings feed_settings; + ListImages *feed_images_dialog; QLineEdit *feed_format_edit; - QCheckBox *feed_normalize_box; + QCheckBox *feed_normalize_check; QLabel *feed_normalize_label; QSpinBox *feed_normalize_spin; QLineEdit *feed_extension_edit; QComboBox *feed_castorder_box; - QComboBox *feed_media_link_mode_box; - QCheckBox *feed_redirect_check; - QLabel *feed_redirect_label; - QLineEdit *feed_redirect_edit; QPushButton *feed_format_button; QGroupBox *feed_channel_section_groupbox; QLabel *feed_channel_title_label; - QLabel *feed_channel_category_label; QLabel *feed_channel_link_label; QLabel *feed_channel_copyright_label; - QLabel *feed_channel_webmaster_label; QLabel *feed_channel_language_label; QLabel *feed_channel_description_label; + QLabel *feed_channel_image_label; + RDImagePickerBox *feed_channel_image_box; QLabel *feed_base_url_label; QLabel *feed_base_preamble_label; QLabel *feed_purge_url_label; QLabel *feed_max_shelf_life_label; QLabel *feed_max_shelf_life_unit_label; - QLabel *feed_autopost_label; - QLabel *feed_keep_metadata_label; QLabel *feed_format_label; QLabel *feed_normalize_check_label; QLabel *feed_normalize_unit_label; QLabel *feed_castorder_label; - QLabel *feed_media_link_mode_label; QLabel *feed_extension_label; - QLabel *feed_header_xml_label; - QLabel *feed_channel_xml_label; - QLabel *feed_item_xml_label; + QLabel *feed_item_image_label; + RDImagePickerBox *feed_item_image_box; + QPushButton *feed_ok_button; + QPushButton *feed_cancel_button; }; diff --git a/rdadmin/edit_image.cpp b/rdadmin/edit_image.cpp new file mode 100644 index 00000000..093f3027 --- /dev/null +++ b/rdadmin/edit_image.cpp @@ -0,0 +1,221 @@ +// edit_image.cpp +// +// View a pixmap image and modify its metadata +// +// (C) Copyright 2020 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 +#include + +#include "edit_image.h" + +EditImage::EditImage(QWidget *parent) + : RDDialog(parent) +{ + setWindowTitle("RDAdmin - "+tr("Image Viewer")); + setMinimumWidth(200); + setMinimumHeight(150); + + c_image_label=new QLabel(this); + + c_description_label=new QLabel(tr("Description")+":",this); + c_description_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + c_description_label->setFont(labelFont()); + c_description_edit=new QLineEdit(this); + c_description_edit->setMaxLength(191); + + c_url_label=new QLabel(tr("URL")+":",this); + c_url_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + c_url_label->setFont(labelFont()); + c_url_edit=new QLineEdit(this); + c_url_edit->setReadOnly(true); + + c_size_label=new QLabel(tr("Native Size")+":",this); + c_size_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + c_size_label->setFont(labelFont()); + c_size_value_label=new QLabel(this); + c_size_value_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + c_extension_label=new QLabel(tr("Type")+":",this); + c_extension_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + c_extension_label->setFont(labelFont()); + c_extension_value_label=new QLabel(this); + c_extension_value_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + c_ok_button=new QPushButton(tr("OK"),this); + c_ok_button->setFont(buttonFont()); + connect(c_ok_button,SIGNAL(clicked()),this,SLOT(okData())); + + c_cancel_button=new QPushButton(tr("Cancel"),this); + c_cancel_button->setFont(buttonFont()); + connect(c_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); + + setMinimumSize(sizeHint()); +} + + +EditImage::~EditImage() +{ + delete c_image_label; + delete c_description_label; + delete c_description_edit; + delete c_url_label; + delete c_url_edit; + delete c_size_label; + delete c_size_value_label; + delete c_extension_label; + delete c_extension_value_label; + delete c_ok_button; + delete c_cancel_button; +} + + +QSize EditImage::sizeHint() const +{ + return QSize(600,722); +} + + +int EditImage::exec(int img_id) +{ + QString sql; + RDSqlQuery *q=NULL; + + c_image_id=img_id; + + sql=QString("select ")+ + "FEED_IMAGES.DESCRIPTION,"+ // 00 + "FEED_IMAGES.FILE_EXTENSION,"+ // 01 + "FEED_IMAGES.WIDTH,"+ // 02 + "FEED_IMAGES.HEIGHT,"+ // 03 + "FEED_IMAGES.DEPTH,"+ // 04 + "FEED_IMAGES.DATA,"+ // 05 + "FEED_IMAGES.FEED_ID,"+ // 06 + "FEEDS.BASE_URL "+ // 07 + "from FEED_IMAGES left join FEEDS "+ + "on FEED_IMAGES.FEED_ID=FEEDS.ID where "+ + QString().sprintf("FEED_IMAGES.ID=%d",img_id); + q=new RDSqlQuery(sql); + if(q->first()) { + c_description_edit->setText(q->value(0).toString()); + c_url_edit->setText(q->value(7).toString()+"/"+ + RDFeed::imageFilename(q->value(6).toInt(),img_id, + q->value(1).toString())); + c_extension_value_label->setText(q->value(1).toString().toUpper()); + c_size_value_label-> + setText(QString().sprintf("%dx%d",q->value(2).toInt(), + q->value(3).toInt())); + c_image=QImage(); + c_image.loadFromData(q->value(5).toByteArray()); + + QSize fsize=FittedSize(c_image.size()); + c_image_label->setPixmap(QPixmap::fromImage(c_image. + scaled(fsize, + Qt::KeepAspectRatio))); + // resize(EDIT_IMAGE_WIDTH_OFFSET+fsize.width(), + // EDIT_IMAGE_HEIGHT_OFFSET+fsize.height()); + resize(sizeHint()); + } + delete q; + + return QDialog::exec(); +} + + +void EditImage::okData() +{ + QString sql=QString("update FEED_IMAGES set ")+ + "DESCRIPTION=\""+RDEscapeString(c_description_edit->text())+"\" "+ + QString().sprintf("where ID=%d",c_image_id); + RDSqlQuery::apply(sql); + + done(true); +} + + +void EditImage::cancelData() +{ + done(false); +} + + +void EditImage::closeEvent(QCloseEvent *e) +{ + cancelData(); +} + + +void EditImage::resizeEvent(QResizeEvent *e) +{ + int w=size().width(); + int h=size().height(); + + c_image_label-> + setGeometry(10,2,w-EDIT_IMAGE_WIDTH_OFFSET,h-EDIT_IMAGE_HEIGHT_OFFSET); + c_image_label->setPixmap(QPixmap::fromImage(c_image. + scaled(c_image_label->size(),Qt::KeepAspectRatio))); + + c_description_label->setGeometry(10,h-109,120,20); + c_description_edit->setGeometry(135,h-109,w-145,20); + + c_url_label->setGeometry(10,h-87,120,20); + c_url_edit->setGeometry(135,h-87,w-145,20); + + c_size_label-> + setGeometry(140,h-65,80,20); + c_size_value_label->setGeometry(225,h-65,80,20); + + c_extension_label->setGeometry(300,h-65,50,20); + c_extension_value_label->setGeometry(355,h-65,100,20); + + c_ok_button->setGeometry(w-180,h-60,80,50); + c_cancel_button->setGeometry(w-90,h-60,80,50); +} + + +QSize EditImage::FittedSize(const QSize &img_size) const +{ + QSize max_size=MaxFriendlyImageSize(); + + if((img_size.width()<=max_size.width())&& + (img_size.height()<=max_size.height())) { + return img_size; + } + QSize ret(img_size.boundedTo(max_size)); + if(ret.height()==max_size.height()) { + ret.setWidth(img_size.width()*ret.height()/img_size.height()); + } + else { + ret.setHeight(img_size.height()*ret.width()/img_size.width()); + } + + return ret; +} + + +QSize EditImage::MaxFriendlyImageSize() const +{ + QDesktopWidget *dt=QApplication::desktop(); + QSize dsize(dt->screenGeometry(dt->screenNumber(this)).size()); + + return QSize(dsize.width()-EDIT_IMAGE_WIDTH_OFFSET, + dsize.height()-EDIT_IMAGE_HEIGHT_OFFSET-100); +} diff --git a/rdadmin/edit_image.h b/rdadmin/edit_image.h new file mode 100644 index 00000000..57711ce5 --- /dev/null +++ b/rdadmin/edit_image.h @@ -0,0 +1,72 @@ +// edit_image.h +// +// View a pixmap image and modify its metadata +// +// (C) Copyright 2020 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. +// + +#ifndef EDIT_IMAGE_H +#define EDIT_IMAGE_H + +#include +#include +#include +#include + +#include + +#define EDIT_IMAGE_WIDTH_OFFSET 20 +#define EDIT_IMAGE_HEIGHT_OFFSET 95 + +class EditImage : public RDDialog +{ + Q_OBJECT + public: + EditImage(QWidget *parent=0); + ~EditImage(); + QSize sizeHint() const; + + public slots: + int exec(int img_id); + + private slots: + void okData(); + void cancelData(); + + protected: + void closeEvent(QCloseEvent *e); + void resizeEvent(QResizeEvent *e); + + private: + QSize FittedSize(const QSize &img_size) const; + QSize MaxFriendlyImageSize() const; + QLabel *c_image_label; + QLabel *c_description_label; + QLineEdit *c_description_edit; + QLabel *c_url_label; + QLineEdit *c_url_edit; + QLabel *c_size_label; + QLabel *c_size_value_label; + QLabel *c_extension_label; + QLabel *c_extension_value_label; + QPushButton *c_ok_button; + QPushButton *c_cancel_button; + QImage c_image; + int c_image_id; +}; + + +#endif // EDIT_IMAGE_H diff --git a/rdadmin/edit_rdairplay.cpp b/rdadmin/edit_rdairplay.cpp index 07193100..508eb8b1 100644 --- a/rdadmin/edit_rdairplay.cpp +++ b/rdadmin/edit_rdairplay.cpp @@ -1168,7 +1168,7 @@ void EditRDAirPlay::selectData() QString logname=air_startlog_edit->text(); RDListLogs *ll=new RDListLogs(&logname,RDLogFilter::NoFilter,this); - if(ll->exec()==0) { + if(ll->exec()) { air_startlog_edit->setText(logname); } delete ll; diff --git a/rdadmin/edit_settings.cpp b/rdadmin/edit_settings.cpp index 6c0c9ef4..32a4bc44 100644 --- a/rdadmin/edit_settings.cpp +++ b/rdadmin/edit_settings.cpp @@ -18,7 +18,6 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // - #include #include #include @@ -34,8 +33,6 @@ EditSettings::EditSettings(QWidget *parent) : RDDialog(parent) { - setModal(true); - QString sql; RDSqlQuery *q; @@ -45,7 +42,7 @@ EditSettings::EditSettings(QWidget *parent) // Fix the Window Size // setMinimumSize(sizeHint()); - setMaximumSize(sizeHint()); + setMaximumHeight(sizeHint().height()); edit_system=new RDSystem(); @@ -57,37 +54,35 @@ EditSettings::EditSettings(QWidget *parent) // System Sample Rate // edit_sample_rate_box=new QComboBox(this); - edit_sample_rate_box->setGeometry(250,10,70,20); edit_sample_rate_box->insertItem("32000"); edit_sample_rate_box->insertItem("44100"); edit_sample_rate_box->insertItem("48000"); - QLabel *label=new QLabel(edit_sample_rate_box,tr("System Sample Rate:"),this); - label->setGeometry(10,10,235,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); - label=new QLabel(tr("samples/second"),this); - label->setGeometry(325,10,sizeHint().width()-285,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_sample_rate_label= + new QLabel(edit_sample_rate_box,tr("System Sample Rate:"),this); + edit_sample_rate_label->setFont(labelFont()); + edit_sample_rate_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_sample_rate_unit_label=new QLabel(tr("samples/second"),this); + edit_sample_rate_unit_label->setGeometry(325,10,sizeHint().width()-285,20); + edit_sample_rate_unit_label->setFont(labelFont()); + edit_sample_rate_unit_label-> + setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); // // Allow Duplicate Cart Titles Box // edit_duplicate_carts_box=new QCheckBox(this); - edit_duplicate_carts_box->setGeometry(20,32,15,15); connect(edit_duplicate_carts_box,SIGNAL(toggled(bool)), this,SLOT(duplicatesCheckedData(bool))); - label= + edit_duplicate_label= new QLabel(edit_duplicate_carts_box,tr("Allow Duplicate Cart Titles"),this); - label->setGeometry(40,30,sizeHint().width()-50,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_duplicate_label->setFont(labelFont()); + edit_duplicate_label-> + setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); edit_fix_duplicate_carts_box=new QCheckBox(this); - edit_fix_duplicate_carts_box->setGeometry(30,52,15,15); edit_fix_duplicate_carts_label=new QLabel(edit_fix_duplicate_carts_box, tr("Auto-Correct Duplicate Cart Titles"),this); - edit_fix_duplicate_carts_label->setGeometry(50,50,sizeHint().width()-60,20); edit_fix_duplicate_carts_label->setFont(labelFont()); edit_fix_duplicate_carts_label-> setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); @@ -96,77 +91,93 @@ EditSettings::EditSettings(QWidget *parent) // ISCI Cross Reference Path // edit_isci_path_edit=new QLineEdit(this); - edit_isci_path_edit->setGeometry(250,76,sizeHint().width()-260,20); - label=new QLabel(edit_isci_path_edit,tr("ISCI Cross Reference Path:"),this); - label->setGeometry(10,76,235,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_isci_path_label= + new QLabel(edit_isci_path_edit,tr("ISCI Cross Reference Path:"),this); + edit_isci_path_label->setFont(labelFont()); + edit_isci_path_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); // // Notification Address // edit_notification_address_edit=new QLineEdit(this); - edit_notification_address_edit->setGeometry(250,98,150,20); - label=new QLabel(edit_notification_address_edit,tr("Multicast Address for Notifications"),this); - label->setGeometry(10,98,235,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_notification_address_label= + new QLabel(edit_notification_address_edit, + tr("Multicast Address for Notifications"),this); + edit_notification_address_label->setFont(labelFont()); + edit_notification_address_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); // // Maximum POST Size // edit_maxpost_spin=new QSpinBox(this); - edit_maxpost_spin->setGeometry(250,120,60,20); edit_maxpost_spin->setRange(1,1000); - label=new QLabel(edit_maxpost_spin,tr("Maximum Remote Post Length:"),this); - label->setGeometry(10,120,235,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); - label=new QLabel(tr("Mbytes"),this); - label->setGeometry(315,120,60,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_maxpost_label= + new QLabel(edit_maxpost_spin,tr("Maximum Remote Post Length:"),this); + edit_maxpost_label->setFont(labelFont()); + edit_maxpost_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_maxpost_unit_label=new QLabel(tr("Mbytes"),this); + edit_maxpost_unit_label->setFont(labelFont()); + edit_maxpost_unit_label-> + setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); // // Temporary Cart Group // edit_temp_cart_group_box=new QComboBox(this); - edit_temp_cart_group_box->setGeometry(250,141,100,20); sql="select NAME from GROUPS order by NAME"; q=new RDSqlQuery(sql); while(q->next()) { edit_temp_cart_group_box->insertItem(q->value(0).toString()); } delete q; - label=new QLabel(edit_temp_cart_group_box,tr("Temporary Cart Group:"),this); - label->setGeometry(10,141,235,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_temp_cart_group_label= + new QLabel(edit_temp_cart_group_box,tr("Temporary Cart Group:"),this); + edit_temp_cart_group_label->setFont(labelFont()); + edit_temp_cart_group_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); // // Show User List // edit_show_user_list_box=new QCheckBox(this); - edit_show_user_list_box->setGeometry(20,165,15,15); connect(edit_show_user_list_box,SIGNAL(toggled(bool)), this,SLOT(duplicatesCheckedData(bool))); - label= + edit_show_user_list_label= new QLabel(edit_show_user_list_box,tr("Show User List in RDLogin"),this); - label->setGeometry(40,163,sizeHint().width()-50,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); + edit_show_user_list_label->setFont(labelFont()); + edit_show_user_list_label-> + setAlignment(Qt::AlignLeft|Qt::AlignVCenter|Qt::TextShowMnemonic); + + // + // RSS Processor Host + // + edit_rss_processor_box=new QComboBox(this); + sql=QString("select NAME from STATIONS order by NAME"); + q=new RDSqlQuery(sql); + edit_rss_processor_box->insertItem(0,tr("[none]")); + while(q->next()) { + edit_rss_processor_box->insertItem(edit_rss_processor_box->count(), + q->value(0).toString()); + } + delete q; + edit_rss_processor_label= + new QLabel(edit_rss_processor_box,tr("Process RSS Updates On")+":",this); + edit_rss_processor_label->setFont(labelFont()); + edit_rss_processor_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter|Qt::TextShowMnemonic); // // Duplicate List (initially hidden) // - edit_duplicate_label=new QLabel(this); - edit_duplicate_label->setText(tr("The following duplicate titles must be corrected before \"Allow Duplicate Values\" can be turned off.")); - edit_duplicate_label->setWordWrap(true); - edit_duplicate_label->setGeometry(15,186,sizeHint().width()-30,50); - edit_duplicate_label->setFont(subLabelFont()); - edit_duplicate_label->hide(); + edit_duplicate_hidden_label=new QLabel(this); + edit_duplicate_hidden_label->setText(tr("The following duplicate titles must be corrected before \"Allow Duplicate Values\" can be turned off.")); + edit_duplicate_hidden_label->setWordWrap(true); + edit_duplicate_hidden_label->setFont(subLabelFont()); + edit_duplicate_hidden_label->hide(); edit_duplicate_list=new Q3ListView(this); - edit_duplicate_list->setGeometry(10,234,sizeHint().width()-20,215); edit_duplicate_list->setItemMargin(5); edit_duplicate_list->setAllColumnsShowFocus(true); edit_duplicate_list->addColumn(tr("Cart")); @@ -175,8 +186,6 @@ EditSettings::EditSettings(QWidget *parent) edit_duplicate_list->setColumnAlignment(1,Qt::AlignLeft); edit_duplicate_list->hide(); edit_save_button=new QPushButton(this); - edit_save_button-> - setGeometry(sizeHint().width()-85,454,70,25); edit_save_button->setFont(buttonFont()); edit_save_button->setText(tr("&Save List")); connect(edit_save_button,SIGNAL(clicked()),this,SLOT(saveData())); @@ -186,8 +195,6 @@ EditSettings::EditSettings(QWidget *parent) // Ok Button // edit_ok_button=new QPushButton(this); - edit_ok_button->setGeometry(sizeHint().width()-180,sizeHint().height()-60, - 80,50); edit_ok_button->setFont(buttonFont()); edit_ok_button->setText(tr("&OK")); connect(edit_ok_button,SIGNAL(clicked()),this,SLOT(okData())); @@ -196,8 +203,6 @@ EditSettings::EditSettings(QWidget *parent) // Cancel Button // edit_cancel_button=new QPushButton(this); - edit_cancel_button->setGeometry(sizeHint().width()-90,sizeHint().height()-60, - 80,50); edit_cancel_button->setFont(buttonFont()); edit_cancel_button->setText(tr("&Cancel")); connect(edit_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); @@ -212,6 +217,16 @@ EditSettings::EditSettings(QWidget *parent) setText(edit_system->notificationAddress().toString()); edit_show_user_list_box->setChecked(edit_system->showUserList()); + + + QString station=edit_system->rssProcessorStation(); + for(int i=0;icount();i++) { + if(edit_rss_processor_box->text(i)==station) { + edit_rss_processor_box->setCurrentIndex(i); + } + } + + for(int i=0;icount();i++) { if(edit_sample_rate_box->text(i).toUInt()==edit_system->sampleRate()) { edit_sample_rate_box->setCurrentItem(i); @@ -236,7 +251,7 @@ EditSettings::~EditSettings() QSize EditSettings::sizeHint() const { - return QSize(500,262+y_pos); + return QSize(500,284+y_pos); } @@ -350,7 +365,7 @@ void EditSettings::okData() setMinimumHeight(sizeHint().height()); setMaximumHeight(sizeHint().height()); edit_duplicate_carts_box->setChecked(true); - edit_duplicate_label->show(); + edit_duplicate_hidden_label->show(); edit_duplicate_list->show(); edit_save_button->show(); edit_duplicate_list->clear(); @@ -402,6 +417,12 @@ void EditSettings::okData() setNotificationAddress(QHostAddress(edit_notification_address_edit->text())); edit_system->setTempCartGroup(edit_temp_cart_group_box->currentText()); edit_system->setShowUserList(edit_show_user_list_box->isChecked()); + if(edit_rss_processor_box->currentIndex()==0) { + edit_system->setRssProcessorStation(); + } + else { + edit_system->setRssProcessorStation(edit_rss_processor_box->currentText()); + } done(0); } @@ -433,3 +454,41 @@ void EditSettings::BuildDuplicatesList(std::map *dups) } delete q; } + + +void EditSettings::resizeEvent(QResizeEvent *e) +{ + edit_sample_rate_box->setGeometry(250,10,70,20); + edit_sample_rate_label->setGeometry(10,10,235,20); + + edit_duplicate_carts_box->setGeometry(20,32,15,15); + edit_duplicate_label->setGeometry(40,30,size().width()-50,20); + edit_fix_duplicate_carts_box->setGeometry(30,52,15,15); + edit_fix_duplicate_carts_label->setGeometry(50,50,size().width()-60,20); + + edit_show_user_list_box->setGeometry(20,74,15,15); + edit_show_user_list_label->setGeometry(40,72,size().width()-50,20); + + edit_isci_path_edit->setGeometry(250,98,size().width()-260,20); + edit_isci_path_label->setGeometry(10,98,235,20); + + edit_notification_address_edit->setGeometry(250,120,150,20); + edit_notification_address_label->setGeometry(10,120,235,20); + + edit_maxpost_spin->setGeometry(250,142,60,20); + edit_maxpost_label->setGeometry(10,142,235,20); + edit_maxpost_unit_label->setGeometry(315,142,60,20); + + edit_temp_cart_group_box->setGeometry(250,163,100,20); + edit_temp_cart_group_label->setGeometry(10,163,235,20); + + edit_rss_processor_label->setGeometry(10,185,235,20); + edit_rss_processor_box->setGeometry(250,185,200,20); + + edit_duplicate_hidden_label->setGeometry(15,186,size().width()-30,50); + edit_duplicate_list->setGeometry(10,234,size().width()-20,215); + edit_save_button->setGeometry(size().width()-85,454,70,25); + + edit_ok_button->setGeometry(size().width()-180,size().height()-60,80,50); + edit_cancel_button->setGeometry(size().width()-90,size().height()-60,80,50); +} diff --git a/rdadmin/edit_settings.h b/rdadmin/edit_settings.h index ad530f13..3dbc7090 100644 --- a/rdadmin/edit_settings.h +++ b/rdadmin/edit_settings.h @@ -2,7 +2,7 @@ // // Edit Rivendell System-wide Settings. // -// (C) Copyright 2009-2019 Fred Gleason +// (C) Copyright 2009-2020 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 @@ -51,24 +51,39 @@ class EditSettings : public RDDialog void okData(); void cancelData(); + protected: + void resizeEvent(QResizeEvent *e); + private: - QComboBox *edit_sample_rate_box; - QCheckBox *edit_duplicate_carts_box; - QLabel *edit_duplicate_label; - QCheckBox *edit_fix_duplicate_carts_box; - QLabel *edit_fix_duplicate_carts_label; - QSpinBox *edit_maxpost_spin; - QLineEdit *edit_isci_path_edit; - QComboBox *edit_temp_cart_group_box; - QCheckBox *edit_show_user_list_box; - Q3ListView *edit_duplicate_list; - QLineEdit *edit_notification_address_edit; - QPushButton *edit_settings_button; - QPushButton *edit_save_button; - QPushButton *edit_ok_button; - QPushButton *edit_cancel_button; - RDSystem *edit_system; - int y_pos; + QLabel *edit_sample_rate_label; + QComboBox *edit_sample_rate_box; + QLabel *edit_sample_rate_unit_label; + QLabel *edit_duplicate_label; + QLabel *edit_duplicate_hidden_label; + QCheckBox *edit_duplicate_carts_box; + QCheckBox *edit_fix_duplicate_carts_box; + QLabel *edit_fix_duplicate_carts_label; + QLabel *edit_maxpost_label; + QSpinBox *edit_maxpost_spin; + QLabel *edit_maxpost_unit_label; + QLabel *edit_isci_path_label; + QLineEdit *edit_isci_path_edit; + QLabel *edit_temp_cart_group_label; + QComboBox *edit_temp_cart_group_box; + QComboBox *edit_rss_processor_station_box; + QLabel *edit_show_user_list_label; + QCheckBox *edit_show_user_list_box; + Q3ListView *edit_duplicate_list; + QLabel *edit_notification_address_label; + QLineEdit *edit_notification_address_edit; + QLabel *edit_rss_processor_label; + QComboBox *edit_rss_processor_box; + QPushButton *edit_settings_button; + QPushButton *edit_save_button; + QPushButton *edit_ok_button; + QPushButton *edit_cancel_button; + RDSystem *edit_system; + int y_pos; }; diff --git a/rdadmin/edit_station.cpp b/rdadmin/edit_station.cpp index e7536004..964f994b 100644 --- a/rdadmin/edit_station.cpp +++ b/rdadmin/edit_station.cpp @@ -158,6 +158,17 @@ EditStation::EditStation(QString sname,QWidget *parent) station_web_browser_label->setFont(labelFont()); station_web_browser_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // + // ssh(1) Identity File + // + station_ssh_identity_file_edit=new QLineEdit(this); + station_ssh_identity_file_edit->setMaxLength(191); + station_ssh_identity_file_label= + new QLabel(station_ssh_identity_file_edit,tr("SSH Ident. File")+":",this); + station_ssh_identity_file_label->setFont(labelFont()); + station_ssh_identity_file_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // // Station Time Offset // @@ -474,6 +485,7 @@ EditStation::EditStation(QString sname,QWidget *parent) station_audio_editor_edit->setText(station_station->editorPath()); station_report_editor_edit->setText(station_station->reportEditorPath()); station_web_browser_edit->setText(station_station->browserPath()); + station_ssh_identity_file_edit->setText(station_station->sshIdentityFile()); station_timeoffset_box->setValue(station_station->timeOffset()); unsigned cartnum=station_station->startupCart(); if(cartnum>0) { @@ -708,6 +720,7 @@ void EditStation::okData() setEditorPath(station_audio_editor_edit->text()); station_station->setReportEditorPath(station_report_editor_edit->text()); station_station->setBrowserPath(station_web_browser_edit->text()); + station_station->setSshIdentityFile(station_ssh_identity_file_edit->text()); station_station->setTimeOffset(station_timeoffset_box->value()); cartnum=station_startup_cart_edit->text().toUInt(&ok); if(ok&&(cartnum<=999999)) { @@ -896,102 +909,105 @@ void EditStation::stopCartClickedData() void EditStation::resizeEvent(QResizeEvent *e) { - station_name_edit->setGeometry(115,11,size().width()-120,19); - station_name_label->setGeometry(10,11,100,19); + station_name_edit->setGeometry(115,2,size().width()-120,19); + station_name_label->setGeometry(10,2,100,19); - station_short_name_edit->setGeometry(115,32,size().width()-120,19); - station_short_name_label->setGeometry(10,32,100,19); + station_short_name_edit->setGeometry(115,23,size().width()-120,19); + station_short_name_label->setGeometry(10,23,100,19); - station_description_edit->setGeometry(115,53,size().width()-120,19); - station_description_label->setGeometry(10,53,100,19); + station_description_edit->setGeometry(115,44,size().width()-120,19); + station_description_label->setGeometry(10,44,100,19); - station_default_name_edit->setGeometry(115,74,160,19); - station_default_name_label->setGeometry(10,74,100,19); + station_default_name_edit->setGeometry(115,65,160,19); + station_default_name_label->setGeometry(10,65,100,19); - station_address_edit->setGeometry(115,95,120,19); - station_address_label->setGeometry(10,95,100,19); + station_address_edit->setGeometry(115,86,120,19); + station_address_label->setGeometry(10,86,100,19); - station_audio_editor_edit->setGeometry(115,116,size().width()-120,19); - station_audio_editor_label->setGeometry(10,116,100,19); + station_audio_editor_edit->setGeometry(115,107,size().width()-120,19); + station_audio_editor_label->setGeometry(10,107,100,19); - station_report_editor_edit->setGeometry(115,137,size().width()-120,19); - station_report_editor_label->setGeometry(10,137,100,19); + station_report_editor_edit->setGeometry(115,128,size().width()-120,19); + station_report_editor_label->setGeometry(10,128,100,19); - station_web_browser_edit->setGeometry(115,158,size().width()-120,19); - station_web_browser_label->setGeometry(10,158,100,19); + station_web_browser_edit->setGeometry(115,149,size().width()-120,19); + station_web_browser_label->setGeometry(10,149,100,19); - station_timeoffset_box->setGeometry(115,179,80,19); - station_timeoffset_label->setGeometry(10,179,100,19); + station_ssh_identity_file_edit->setGeometry(115,170,size().width()-120,19); + station_ssh_identity_file_label->setGeometry(10,170,100,19); - station_startup_cart_edit->setGeometry(115,200,60,19); - station_startup_cart_label->setGeometry(10,200,100,19); - station_startup_select_button->setGeometry(180,199,50,22); + station_timeoffset_box->setGeometry(115,191,80,19); + station_timeoffset_label->setGeometry(10,191,100,19); - station_cue_sel->setGeometry(90,221,110,117); - station_cue_sel_label->setGeometry(10,221,100,19); + station_startup_cart_edit->setGeometry(115,212,60,19); + station_startup_cart_label->setGeometry(10,212,100,19); + station_startup_select_button->setGeometry(180,211,50,22); - station_start_cart_edit->setGeometry(290,221,60,20); - station_start_cart_label->setGeometry(205,221,80,20); - station_start_cart_button->setGeometry(355,220,50,22); + station_cue_sel->setGeometry(90,243,110,117); + station_cue_sel_label->setGeometry(10,243,100,19); - station_stop_cart_edit->setGeometry(290,243,60,20); - station_stop_cart_label->setGeometry(205,243,80,20); - station_stop_cart_button->setGeometry(355,242,50,22); + station_start_cart_edit->setGeometry(290,243,60,20); + station_start_cart_label->setGeometry(205,243,80,20); + station_start_cart_button->setGeometry(355,242,50,22); - station_heartbeat_box->setGeometry(10,268,15,15); - station_heartbeat_label->setGeometry(30,266,150,20); + station_stop_cart_edit->setGeometry(290,264,60,20); + station_stop_cart_label->setGeometry(205,264,80,20); + station_stop_cart_button->setGeometry(355,263,50,22); - station_filter_box->setGeometry(210,268,15,15); - station_filter_label->setGeometry(230,266,150,20); + station_heartbeat_box->setGeometry(10,290,15,15); + station_heartbeat_label->setGeometry(30,285,150,20); - station_hbcart_edit->setGeometry(65,290,60,19); - station_hbcart_label->setGeometry(10,290,50,19); - station_hbcart_button->setGeometry(140,287,60,26); + station_filter_box->setGeometry(210,290,15,15); + station_filter_label->setGeometry(230,285,150,20); - station_hbinterval_spin->setGeometry(275,290,45,19); - station_hbinterval_label->setGeometry(220,290,50,19); - station_hbinterval_unit->setGeometry(325,290,100,19); + station_hbcart_edit->setGeometry(65,310,60,19); + station_hbcart_label->setGeometry(10,310,50,19); + station_hbcart_button->setGeometry(140,307,60,26); - station_maint_box->setGeometry(10,317,15,15); - station_maint_label->setGeometry(30,315,size().width()-40,20); + station_hbinterval_spin->setGeometry(275,310,45,19); + station_hbinterval_label->setGeometry(220,310,50,19); + station_hbinterval_unit->setGeometry(325,310,100,19); - station_dragdrop_box->setGeometry(10,338,15,15); - station_dragdrop_label->setGeometry(30,335,size().width()-40,20); + station_maint_box->setGeometry(10,335,15,15); + station_maint_label->setGeometry(30,333,size().width()-40,20); - station_panel_enforce_box->setGeometry(25,356,15,15); - station_panel_enforce_label->setGeometry(45,356,size().width()-55,20); + station_dragdrop_box->setGeometry(10,356,15,15); + station_dragdrop_label->setGeometry(30,353,size().width()-40,20); - station_systemservices_groupbox->setGeometry(10,381,size().width()-20,60); + station_panel_enforce_box->setGeometry(25,374,15,15); + station_panel_enforce_label->setGeometry(45,374,size().width()-55,20); - station_http_station_box->setGeometry(145,396,size().width()-165,19); - station_http_station_label->setGeometry(11,396,130,19); + station_systemservices_groupbox->setGeometry(10,395,size().width()-20,60); - station_cae_station_box->setGeometry(145,417,size().width()-165,19); - station_cae_station_label->setGeometry(11,417,130,19); + station_http_station_box->setGeometry(145,410,size().width()-165,19); + station_http_station_label->setGeometry(11,408,130,19); - station_rdlibrary_button->setGeometry(30,455,80,50); + station_cae_station_box->setGeometry(145,431,size().width()-165,19); + station_cae_station_label->setGeometry(11,431,130,19); - station_rdcatch_button->setGeometry(120,455,80,50); + station_rdlibrary_button->setGeometry(30,461,80,50); - station_rdairplay_button->setGeometry(210,455,80,50); + station_rdcatch_button->setGeometry(120,461,80,50); - station_rdpanel_button->setGeometry(300,455,80,50); + station_rdairplay_button->setGeometry(210,461,80,50); - station_rdlogedit_button->setGeometry(30,513,80,50); + station_rdpanel_button->setGeometry(300,461,80,50); - station_rdcartslots_button->setGeometry(120,513,80,50); + station_rdlogedit_button->setGeometry(30,519,80,50); - station_dropboxes_button->setGeometry(210,513,80,50); + station_rdcartslots_button->setGeometry(120,519,80,50); - station_switchers_button->setGeometry(300,513,80,50); + station_dropboxes_button->setGeometry(210,519,80,50); - station_hostvars_button->setGeometry(30,575,80,50); + station_switchers_button->setGeometry(300,519,80,50); - station_audioports_button->setGeometry(120,575,80,50); + station_hostvars_button->setGeometry(30,577,80,50); - station_ttys_button->setGeometry(210,575,80,50); + station_audioports_button->setGeometry(120,577,80,50); - station_adapters_button->setGeometry(300,575,80,50); + station_ttys_button->setGeometry(210,577,80,50); + + station_adapters_button->setGeometry(300,577,80,50); station_jack_button->setGeometry(120,635,80,50); diff --git a/rdadmin/edit_station.h b/rdadmin/edit_station.h index 137d2b80..5b7ac952 100644 --- a/rdadmin/edit_station.h +++ b/rdadmin/edit_station.h @@ -94,6 +94,8 @@ class EditStation : public RDDialog QLineEdit *station_report_editor_edit; QLabel *station_web_browser_label; QLineEdit *station_web_browser_edit; + QLabel *station_ssh_identity_file_label; + QLineEdit *station_ssh_identity_file_edit; QLabel *station_timeoffset_label; QSpinBox *station_timeoffset_box; QLabel *station_startup_cart_label; diff --git a/rdadmin/edit_superfeed.cpp b/rdadmin/edit_superfeed.cpp new file mode 100644 index 00000000..71a74bda --- /dev/null +++ b/rdadmin/edit_superfeed.cpp @@ -0,0 +1,172 @@ +// edit_superfeed.cpp +// +// Edit Rivendell Superfeed +// +// (C) Copyright 2002-2020 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 + +#include "edit_superfeed.h" + +EditSuperfeed::EditSuperfeed(RDFeed *feed,QWidget *parent) + : RDDialog(parent) +{ + QString sql; + RDSqlQuery *q; + + feed_feed=feed; + + // + // Fix the Window Size + // + setMinimumSize(sizeHint()); + setMaximumSize(sizeHint()); + + setWindowTitle("RDAdmin - "+tr("RSS Superfeed")+": "+feed_feed->keyName()); + + // + // Feed Selector + // + feed_host_sel=new RDListSelector(this); + feed_host_sel->sourceSetLabel(tr("Available Feeds")); + feed_host_sel->destSetLabel(tr("Member Feeds")); + feed_host_sel->setGeometry(10,10,380,130); + + // + // Ok Button + // + QPushButton *ok_button=new QPushButton(this); + ok_button->setGeometry(sizeHint().width()-180,sizeHint().height()-60,80,50); + ok_button->setDefault(true); + ok_button->setFont(buttonFont()); + ok_button->setText(tr("&OK")); + connect(ok_button,SIGNAL(clicked()),this,SLOT(okData())); + + // + // Cancel Button + // + QPushButton *cancel_button=new QPushButton(this); + cancel_button->setGeometry(sizeHint().width()-90,sizeHint().height()-60, + 80,50); + cancel_button->setFont(buttonFont()); + cancel_button->setText(tr("&Cancel")); + connect(cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); + + // + // Populate Fields + // + sql=QString("select ")+ + "MEMBER_KEY_NAME "+ // 00 + "from SUPERFEED_MAPS where "+ + "KEY_NAME=\""+RDEscapeString(feed_feed->keyName())+"\""; + q=new RDSqlQuery(sql); + while(q->next()) { + feed_host_sel->destInsertItem(q->value(0).toString()); + } + delete q; + + sql=QString("select KEY_NAME from FEEDS where ")+ + "IS_SUPERFEED='N'"; + q=new RDSqlQuery(sql); + while(q->next()) { + if(feed_host_sel->destFindItem(q->value(0).toString())==0) { + feed_host_sel->sourceInsertItem(q->value(0).toString()); + } + } + delete q; +} + + +EditSuperfeed::~EditSuperfeed() +{ +} + + +QSize EditSuperfeed::sizeHint() const +{ + return QSize(400,212); +} + + +QSizePolicy EditSuperfeed::sizePolicy() const +{ + return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); +} + + +void EditSuperfeed::okData() +{ + RDSqlQuery *q; + QString sql; + + // + // Feed ID Map + // + QMap feed_ids; + sql=QString("select KEY_NAME,ID from FEEDS"); + q=new RDSqlQuery(sql); + while(q->next()) { + feed_ids[q->value(0).toString()]=q->value(1).toUInt(); + } + delete q; + + // + // Add New Groups + // + for(unsigned i=0;idestCount();i++) { + sql=QString("select ")+ + "MEMBER_KEY_NAME " // 00 + "from SUPERFEED_MAPS where "+ + "KEY_NAME=\""+RDEscapeString(feed_feed->keyName())+"\" && " + "MEMBER_KEY_NAME=\""+RDEscapeString(feed_host_sel->destText(i))+"\""; + q=new RDSqlQuery(sql); + if(q->size()==0) { + delete q; + sql=QString("insert into SUPERFEED_MAPS set ")+ + "KEY_NAME=\""+RDEscapeString(feed_feed->keyName())+"\","+ + "MEMBER_KEY_NAME=\""+RDEscapeString(feed_host_sel->destText(i))+"\","+ + QString().sprintf("FEED_ID=%u,",feed_ids.value(feed_feed->keyName()))+ + QString().sprintf("MEMBER_FEED_ID=%u",feed_ids.value(feed_host_sel->destText(i))); + q=new RDSqlQuery(sql); + } + delete q; + } + + // + // Delete Old Groups + // + sql=QString("delete from SUPERFEED_MAPS where ")+ + "KEY_NAME=\""+RDEscapeString(feed_feed->keyName())+"\""; + for(unsigned i=0;idestCount();i++) { + sql+=QString(" && MEMBER_KEY_NAME<>\"")+ + RDEscapeString(feed_host_sel->destText(i))+"\""; + } + q=new RDSqlQuery(sql); + delete q; + done(0); +} + + +void EditSuperfeed::cancelData() +{ + done(1); +} diff --git a/rdcastmanager/pick_report_dates.h b/rdadmin/edit_superfeed.h similarity index 50% rename from rdcastmanager/pick_report_dates.h rename to rdadmin/edit_superfeed.h index 55569ccc..c7e4c14a 100644 --- a/rdcastmanager/pick_report_dates.h +++ b/rdadmin/edit_superfeed.h @@ -1,8 +1,8 @@ -// pick_report_date.h +// edit_superfeed.h // -// Select a Set of Dates for a Rivendell Podcast Report +// Edit Rivendell Superfeed // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -18,38 +18,31 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#ifndef PICK_REPORT_DATE_H -#define PICK_REPORT_DATE_H - -#include +#ifndef EDIT_SUPERFEED_H +#define EDIT_SUPERFEED_H #include +#include -class PickReportDates : public RDDialog +#include + +class EditSuperfeed : public RDDialog { Q_OBJECT public: - PickReportDates(unsigned feed_id,unsigned cast_id,QWidget *parent=0); - ~PickReportDates(); + EditSuperfeed(RDFeed *feed,QWidget *parent=0); + ~EditSuperfeed(); QSize sizeHint() const; QSizePolicy sizePolicy() const; private slots: - void selectStartDateData(); - void selectEndDateData(); - void generateData(); - void closeData(); + void okData(); + void cancelData(); private: - void GenerateSubscriptionReport(const QString &keyname,QString *rpt); - void GenerateEpisodeReport(const QString &keyname,unsigned cast_id, - QString *rpt); - QDateEdit *edit_startdate_edit; - QDateEdit *edit_enddate_edit; - QString edit_keyname; - unsigned edit_feed_id; - unsigned edit_cast_id; + RDListSelector *feed_host_sel; + RDFeed *feed_feed; }; -#endif // PICK_REPORT_DATES_H +#endif // EDIT_SUPERFEED_H diff --git a/rdadmin/edit_user.cpp b/rdadmin/edit_user.cpp index b56f316d..340c091f 100644 --- a/rdadmin/edit_user.cpp +++ b/rdadmin/edit_user.cpp @@ -2,7 +2,7 @@ // // Edit a Rivendell User // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -32,8 +32,6 @@ EditUser::EditUser(const QString &user,QWidget *parent) : RDDialog(parent) { - setModal(true); - // // Fix the Window Size // @@ -53,7 +51,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) // user_name_edit=new QLineEdit(this); user_name_edit->setGeometry(130,11,sizeHint().width()-140,19); - user_name_edit->setMaxLength(255); + user_name_edit->setMaxLength(191); user_name_edit->setValidator(validator); QLabel *user_name_label=new QLabel(user_name_edit,tr("&User Name:"),this); user_name_label->setGeometry(5,11,120,19); @@ -65,7 +63,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) // user_full_name_edit=new QLineEdit(this); user_full_name_edit->setGeometry(130,32,sizeHint().width()-140,19); - user_full_name_edit->setMaxLength(255); + user_full_name_edit->setMaxLength(191); user_full_name_edit->setValidator(validator); QLabel *user_full_name_label= new QLabel(user_full_name_edit,tr("&Full Name:"),this); @@ -78,7 +76,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) // user_description_edit=new QLineEdit(this); user_description_edit->setGeometry(130,53,sizeHint().width()-140,19); - user_description_edit->setMaxLength(255); + user_description_edit->setMaxLength(191); user_description_edit->setValidator(validator); QLabel *user_description_label= new QLabel(user_description_edit,tr("&Description:"),this); @@ -86,15 +84,28 @@ EditUser::EditUser(const QString &user,QWidget *parent) user_description_label->setFont(labelFont()); user_description_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // + // Email Address + // + user_email_address_edit=new QLineEdit(this); + user_email_address_edit->setGeometry(130,74,sizeHint().width()-140,19); + user_email_address_edit->setMaxLength(191); + user_email_address_edit->setValidator(validator); + QLabel *user_email_address_label= + new QLabel(user_email_address_edit,tr("E-Mail Address")+":",this); + user_email_address_label->setGeometry(5,74,120,19); + user_email_address_label->setFont(labelFont()); + user_email_address_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // // User Phone // user_phone_edit=new QLineEdit(this); - user_phone_edit->setGeometry(130,75,sizeHint().width()-140,19); + user_phone_edit->setGeometry(130,95,sizeHint().width()-140,19); user_phone_edit->setMaxLength(20); user_phone_edit->setValidator(validator); QLabel *user_phone_label=new QLabel(user_phone_edit,tr("&Phone:"),this); - user_phone_label->setGeometry(10,75,115,19); + user_phone_label->setGeometry(10,95,115,19); user_phone_label->setFont(labelFont()); user_phone_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -102,12 +113,12 @@ EditUser::EditUser(const QString &user,QWidget *parent) // Local Authentication // user_localauth_check=new QCheckBox(this); - user_localauth_check->setGeometry(20,97,15,15); + user_localauth_check->setGeometry(20,118,15,15); connect(user_localauth_check,SIGNAL(toggled(bool)), this,SLOT(localAuthToggledData(bool))); user_localauth_label=new QLabel(user_localauth_check, tr("Authenticate This User Locally"),this); - user_localauth_label->setGeometry(40,95,200,19); + user_localauth_label->setGeometry(40,116,200,19); user_localauth_label->setFont(labelFont()); user_localauth_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); @@ -115,11 +126,11 @@ EditUser::EditUser(const QString &user,QWidget *parent) // PAM Service // user_pamservice_edit=new QLineEdit(this); - user_pamservice_edit->setGeometry(130,119,140,19); + user_pamservice_edit->setGeometry(130,140,140,19); user_pamservice_edit->setMaxLength(32); user_pamservice_label= new QLabel(user_pamservice_edit,tr("PAM Service")+":",this); - user_pamservice_label->setGeometry(10,119,115,19); + user_pamservice_label->setGeometry(10,140,115,19); user_pamservice_label->setFont(labelFont()); user_pamservice_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -127,7 +138,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) // Change Password Button // user_password_button=new QPushButton(this); - user_password_button->setGeometry(sizeHint().width()-90,97,80,50); + user_password_button->setGeometry(sizeHint().width()-90,118,80,50); user_password_button->setFont(buttonFont()); user_password_button->setText(tr("Change\n&Password")); connect(user_password_button,SIGNAL(clicked()),this,SLOT(passwordData())); @@ -136,20 +147,20 @@ EditUser::EditUser(const QString &user,QWidget *parent) // WebAPI Authorization Timeout // user_webapi_auth_spin=new QSpinBox(this); - user_webapi_auth_spin->setGeometry(130,141,80,19); + user_webapi_auth_spin->setGeometry(130,162,80,19); user_webapi_auth_spin->setRange(0,86400); user_webapi_auth_spin->setSpecialValueText(tr("Disabled")); QLabel *user_webapi_auth_label= new QLabel(user_webapi_auth_spin,tr("WebAPI Timeout:"),this); - user_webapi_auth_label->setGeometry(10,141,115,19); + user_webapi_auth_label->setGeometry(10,162,115,19); user_webapi_auth_label->setFont(labelFont()); user_webapi_auth_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Administrative Group Priviledges // - user_admin_group=new Q3ButtonGroup(tr("Administrative Rights"),this); - user_admin_group->setGeometry(10,170,355,45); + user_admin_group=new QGroupBox(tr("Administrative Rights"),this); + user_admin_group->setGeometry(10,191,355,45); user_admin_group->setFont(labelFont()); user_admin_config_button=new QCheckBox(user_admin_group); @@ -167,8 +178,8 @@ EditUser::EditUser(const QString &user,QWidget *parent) // // Production Group Priviledges // - user_prod_group=new Q3ButtonGroup(tr("Production Rights"),this); - user_prod_group->setGeometry(10,225,355,106); + user_prod_group=new QGroupBox(tr("Production Rights"),this); + user_prod_group->setGeometry(10,246,355,106); user_prod_group->setFont(labelFont()); user_create_carts_button=new QCheckBox(user_prod_group); @@ -233,8 +244,8 @@ EditUser::EditUser(const QString &user,QWidget *parent) // // Traffic Group Priviledges // - user_traffic_group=new Q3ButtonGroup(tr("Traffic Rights"),this); - user_traffic_group->setGeometry(10,341,355,66); + user_traffic_group=new QGroupBox(tr("Traffic Rights"),this); + user_traffic_group->setGeometry(10,362,355,66); user_traffic_group->setFont(labelFont()); user_create_log_button=new QCheckBox(user_traffic_group); @@ -274,8 +285,8 @@ EditUser::EditUser(const QString &user,QWidget *parent) // // OnAir Group Priviledges // - user_onair_group=new Q3ButtonGroup(tr("OnAir Rights"),this); - user_onair_group->setGeometry(10,417,355,85); + user_onair_group=new QGroupBox(tr("OnAir Rights"),this); + user_onair_group->setGeometry(10,438,355,85); user_onair_group->setFont(labelFont()); user_playout_log_button=new QCheckBox(user_onair_group); @@ -324,8 +335,8 @@ EditUser::EditUser(const QString &user,QWidget *parent) // // Podcast Group Priviledges // - user_podcast_group=new Q3ButtonGroup(tr("Podcasting Rights"),this); - user_podcast_group->setGeometry(10,512,355,66); + user_podcast_group=new QGroupBox(tr("Podcasting Rights"),this); + user_podcast_group->setGeometry(10,533,355,66); user_podcast_group->setFont(labelFont()); user_add_podcast_button=new QCheckBox(user_podcast_group); @@ -366,7 +377,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) // Group Permissions Button // user_assign_perms_button=new QPushButton(this); - user_assign_perms_button->setGeometry(10,582,sizeHint().width()/3-20,50); + user_assign_perms_button->setGeometry(10,603,sizeHint().width()/3-20,50); user_assign_perms_button->setFont(buttonFont()); user_assign_perms_button->setText(tr("Group\nPermissions")); connect(user_assign_perms_button,SIGNAL(clicked()),this,SLOT(groupsData())); @@ -375,7 +386,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) // Services Permissions Button // user_assign_svcs_button=new QPushButton(this); - user_assign_svcs_button->setGeometry(sizeHint().width()/3+10,582,sizeHint().width()/3-20,50); + user_assign_svcs_button->setGeometry(sizeHint().width()/3+10,603,sizeHint().width()/3-20,50); user_assign_svcs_button->setFont(buttonFont()); user_assign_svcs_button->setText(tr("Service\nPermissions")); connect(user_assign_svcs_button,SIGNAL(clicked()),this,SLOT(servicesData())); @@ -385,7 +396,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) // user_assign_feeds_button=new QPushButton(this); user_assign_feeds_button-> - setGeometry(2*sizeHint().width()/3+10,582,sizeHint().width()/3-20,50); + setGeometry(2*sizeHint().width()/3+10,603,sizeHint().width()/3-20,50); user_assign_feeds_button->setFont(buttonFont()); user_assign_feeds_button->setText(tr("Podcast Feed\nPermissions")); connect(user_assign_feeds_button,SIGNAL(clicked()),this,SLOT(feedsData())); @@ -417,6 +428,7 @@ EditUser::EditUser(const QString &user,QWidget *parent) user_name_edit->setReadOnly(true); user_full_name_edit->setText(user_user->fullName()); user_description_edit->setText(user_user->description()); + user_email_address_edit->setText(user_user->emailAddress()); user_phone_edit->setText(user_user->phone()); user_localauth_check->setChecked(user_user->localAuthentication()); user_pamservice_edit->setText(user_user->pamService()); @@ -467,6 +479,7 @@ EditUser::~EditUser() delete user_name_edit; delete user_full_name_edit; delete user_description_edit; + delete user_email_address_edit; delete user_phone_edit; delete user_admin_group; delete user_prod_group; @@ -477,7 +490,7 @@ EditUser::~EditUser() QSize EditUser::sizeHint() const { - return QSize(375,702); + return QSize(375,723); } @@ -582,6 +595,7 @@ void EditUser::okData() { user_user->setFullName(user_full_name_edit->text()); user_user->setDescription(user_description_edit->text()); + user_user->setEmailAddress(user_email_address_edit->text()); user_user->setPhone(user_phone_edit->text()); user_user->setLocalAuthentication(user_localauth_check->isChecked()); user_user->setPamService(user_pamservice_edit->text()); diff --git a/rdadmin/edit_user.h b/rdadmin/edit_user.h index b7572549..0d587288 100644 --- a/rdadmin/edit_user.h +++ b/rdadmin/edit_user.h @@ -2,7 +2,7 @@ // // Edit a Rivendell User // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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,9 +21,8 @@ #ifndef EDIT_USER_H #define EDIT_USER_H -#include - #include +#include #include #include #include @@ -54,6 +53,7 @@ class EditUser : public RDDialog private: QLineEdit *user_name_edit; QLineEdit *user_full_name_edit; + QLineEdit *user_email_address_edit; QLineEdit *user_description_edit; QCheckBox *user_localauth_check; QLabel *user_localauth_label; @@ -64,11 +64,11 @@ class EditUser : public RDDialog QSpinBox *user_webapi_auth_spin; QCheckBox *user_web_box; QLabel *user_web_label; - Q3ButtonGroup *user_admin_group; - Q3ButtonGroup *user_prod_group; - Q3ButtonGroup *user_traffic_group; - Q3ButtonGroup *user_onair_group; - Q3ButtonGroup *user_podcast_group; + QGroupBox *user_admin_group; + QGroupBox *user_prod_group; + QGroupBox *user_traffic_group; + QGroupBox *user_onair_group; + QGroupBox *user_podcast_group; QCheckBox *user_admin_config_button; QCheckBox *user_create_carts_button; QCheckBox *user_delete_carts_button; diff --git a/rdadmin/list_feeds.cpp b/rdadmin/list_feeds.cpp index 3fb98685..89271baa 100644 --- a/rdadmin/list_feeds.cpp +++ b/rdadmin/list_feeds.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -39,19 +38,26 @@ #include "globals.h" #include "list_feeds.h" +// +// Icons +// +#include "../icons/rdcastmanager-32x32.xpm" + ListFeeds::ListFeeds(QWidget *parent) : RDDialog(parent) { - setModal(true); - // // Fix the Window Size // - setMinimumWidth(sizeHint().width()); - setMinimumHeight(sizeHint().height()); + setMinimumSize(sizeHint()); setWindowTitle("RDAdmin - "+tr("Rivendell Feed List")); + // + // Create Icons + // + list_rdcastmanager_32x32_map=new QPixmap(rdcastmanager_32x32_xpm); + // // Add Button // @@ -76,6 +82,22 @@ ListFeeds::ListFeeds(QWidget *parent) list_delete_button->setText(tr("&Delete")); connect(list_delete_button,SIGNAL(clicked()),this,SLOT(deleteData())); + // + // Repost Button + // + list_repost_button=new QPushButton(this); + list_repost_button->setFont(buttonFont()); + list_repost_button->setText(tr("&Repost")); + connect(list_repost_button,SIGNAL(clicked()),this,SLOT(repostData())); + + // + // Unpost Button + // + list_unpost_button=new QPushButton(this); + list_unpost_button->setFont(buttonFont()); + list_unpost_button->setText(tr("&Unpost")); + connect(list_unpost_button,SIGNAL(clicked()),this,SLOT(unpostData())); + // // Close Button // @@ -90,19 +112,25 @@ ListFeeds::ListFeeds(QWidget *parent) // list_feeds_view=new RDListView(this); list_feeds_view->setAllColumnsShowFocus(true); + list_feeds_view->setItemMargin(5); + + list_feeds_view->addColumn(" "); + list_feeds_view->setColumnAlignment(0,Qt::AlignCenter|Qt::AlignVCenter); + list_feeds_view->addColumn(tr("Key")); - list_feeds_view->setColumnAlignment(0,Qt::AlignCenter); + list_feeds_view->setColumnAlignment(1,Qt::AlignCenter|Qt::AlignVCenter); list_feeds_view->addColumn(tr("Title")); - list_feeds_view->setColumnAlignment(1,Qt::AlignLeft); + list_feeds_view->setColumnAlignment(2,Qt::AlignLeft); + list_feeds_view->addColumn(tr("Public URL")); + list_feeds_view->setColumnAlignment(3,Qt::AlignLeft); + list_feeds_view->addColumn(tr("Superfeed")); + list_feeds_view->setColumnAlignment(4,Qt::AlignCenter|Qt::AlignVCenter); list_feeds_view->addColumn(tr("AutoPost")); - list_feeds_view->setColumnAlignment(2,Qt::AlignCenter); - list_feeds_view->addColumn(tr("Keep Metadata")); - list_feeds_view->setColumnAlignment(3,Qt::AlignCenter); + list_feeds_view->setColumnAlignment(5,Qt::AlignCenter|Qt::AlignVCenter); list_feeds_view->addColumn(tr("Creation Date")); - list_feeds_view->setColumnAlignment(4,Qt::AlignCenter); - QLabel *list_box_label=new QLabel(list_feeds_view,tr("&Feeds:"),this); - list_box_label->setFont(labelFont()); - list_box_label->setGeometry(14,11,85,19); + list_feeds_view->setColumnAlignment(6,Qt::AlignCenter|Qt::AlignVCenter); + list_box_label=new QLabel(list_feeds_view,tr("Podcast Feeds"),this); + list_box_label->setFont(bigLabelFont()); connect(list_feeds_view, SIGNAL(doubleClicked(Q3ListViewItem *,const QPoint &,int)), this, @@ -119,7 +147,7 @@ ListFeeds::~ListFeeds() QSize ListFeeds::sizeHint() const { - return QSize(400,280); + return QSize(800,450); } @@ -134,7 +162,6 @@ void ListFeeds::addData() QString feed; unsigned id; QString sql; - RDSqlQuery *q; AddFeed *add_feed=new AddFeed(&id,&feed,this); if(add_feed->exec()<0) { @@ -148,20 +175,23 @@ void ListFeeds::addData() if(edit_feed->exec()<0) { sql=QString("delete from FEED_PERMS where ")+ "KEY_NAME=\""+RDEscapeString(feed)+"\""; - q=new RDSqlQuery(sql); - delete q; + RDSqlQuery::apply(sql); + + sql=QString("delete from FEED_IMAGES where ")+ + "FEED_KEY_NAME=\""+RDEscapeString(feed)+"\""; + RDSqlQuery::apply(sql); + sql=QString("delete from FEEDS where ")+ "KEY_NAME=\""+RDEscapeString(feed)+"\""; - q=new RDSqlQuery(sql); - delete q; - RDDeleteFeedLog(feed); + RDSqlQuery::apply(sql); + delete edit_feed; return; } delete edit_feed; RDListViewItem *item=new RDListViewItem(list_feeds_view); item->setId(id); - item->setText(0,feed); + item->setText(1,feed); RefreshItem(item); item->setSelected(true); list_feeds_view->setCurrentItem(item); @@ -175,7 +205,7 @@ void ListFeeds::editData() if(item==NULL) { return; } - EditFeed *edit_feed=new EditFeed(item->text(0),this); + EditFeed *edit_feed=new EditFeed(item->text(1),this); edit_feed->exec(); delete edit_feed; RefreshItem(item); @@ -196,7 +226,7 @@ void ListFeeds::deleteData() RDFeed *feed; QString errs; - QString feedname=item->text(0); + QString feedname=item->text(1); if(feedname.isEmpty()) { return; } @@ -220,9 +250,9 @@ void ListFeeds::deleteData() RDPodcast *cast; sql=QString().sprintf("select ID from PODCASTS where FEED_ID=%d",item->id()); q=new RDSqlQuery(sql); - QProgressDialog *pd=new QProgressDialog(tr("Deleting Audio..."),tr("Cancel"), + QProgressDialog *pd=new QProgressDialog(tr("Deleting remote audio..."),"", 0,q->size()+1,this); - pd->setWindowTitle("RDAdmin - "+tr("Deleting")); + pd->setWindowTitle("RDAdmin"); pd->setValue(0); qApp->processEvents(); sleep(1); @@ -230,30 +260,46 @@ void ListFeeds::deleteData() pd->setValue(pd->value()+1); qApp->processEvents(); cast=new RDPodcast(rda->config(),q->value(0).toUInt()); - cast->removeAudio(feed,&errs,rda->config()->logXloadDebugData()); + cast->dropAudio(feed,&errs,rda->config()->logXloadDebugData()); delete cast; } delete q; + // + // Delete Remote XML + // + if(!feed->removeRss()) { + QMessageBox::warning(this,"RDAdmin - "+tr("Warning"), + tr("Failed to delete remote feed XML.")); + } + // // Delete Cast Entries // sql=QString().sprintf("delete from PODCASTS where FEED_ID=%d",item->id()); - q=new RDSqlQuery(sql); - delete q; + RDSqlQuery::apply(sql); + + // + // Delete Images + // + feed->removeAllImages(); + sql=QString("delete from FEED_IMAGES where ")+ + QString().sprintf("FEED_ID=%d",feed->id()); + RDSqlQuery::apply(sql); // // Delete Feed // sql=QString("delete from FEED_PERMS where ")+ "KEY_NAME=\""+RDEscapeString(feedname)+"\""; - q=new RDSqlQuery(sql); - delete q; + RDSqlQuery::apply(sql); + sql=QString("delete from SUPERFEED_MAPS where ")+ + "KEY_NAME=\""+RDEscapeString(feedname)+"\" || "+ + "MEMBER_KEY_NAME=\""+RDEscapeString(feedname)+"\""; + RDSqlQuery::apply(sql); sql=QString("delete from FEEDS where ")+ "KEY_NAME=\""+RDEscapeString(feedname)+"\""; - q=new RDSqlQuery(sql); - delete q; - RDDeleteFeedLog(feedname); + RDSqlQuery::apply(sql); item->setSelected(false); pd->reset(); @@ -271,6 +317,165 @@ void ListFeeds::doubleClickedData(Q3ListViewItem *item,const QPoint &pt, } +void ListFeeds::repostData() +{ + QString sql; + RDSqlQuery *q=NULL; + RDFeed *feed=NULL; + int count; + + RDListViewItem *item=(RDListViewItem *)list_feeds_view->selectedItem(); + if(item==NULL) { + return; + } + QString keyname=item->text(1); + if(keyname.isEmpty()) { + return; + } + + if(QMessageBox::question(this,"RDAdmin - "+tr("Feed Repost"), + tr("This operation will repost all XML, image and")+"\n"+ + tr("audio data to the remote archive, and thus may")+"\n"+ + tr("take signficant time to complete.")+"\n\n"+ + tr("Continue?"),QMessageBox::Yes,QMessageBox::No)!= + QMessageBox::Yes) { + return; + } + + feed=new RDFeed(keyname,rda->config(),this); + QProgressDialog *pd=new QProgressDialog(this); + pd->setCancelButton(NULL); + + // + // Post Images + // + sql=QString("select ")+ + "ID "+ // 00 + "from FEED_IMAGES where "+ + QString().sprintf("FEED_ID=%u",feed->id()); + q=new RDSqlQuery(sql); + pd->setLabelText(tr("Posting images...")); + pd->setRange(0,q->size()); + count=0; + pd->setValue(0); + while(q->next()) { + feed->postImage(q->value(0).toUInt()); + pd->setValue(++count); + } + delete q; + + // + // Post Item Data + // + sql=QString("select ")+ + "ID "+ // 00 + "from PODCASTS where "+ + QString().sprintf("FEED_ID=%u",feed->id()); + q=new RDSqlQuery(sql); + pd->setLabelText(tr("Posting item data...")); + pd->setRange(0,q->size()); + count=0; + pd->setValue(0); + while(q->next()) { + feed->postPodcast(q->value(0).toUInt()); + pd->setValue(++count); + } + delete q; + + // + // Post RSS XML + // + pd->setLabelText(tr("Posting RSS XML data...")); + pd->setRange(0,1); + pd->setValue(0); + feed->postXml(); + pd->setValue(1); + + delete pd; +} + + +void ListFeeds::unpostData() +{ + QString sql; + RDSqlQuery *q=NULL; + RDFeed *feed=NULL; + int count; + + RDListViewItem *item=(RDListViewItem *)list_feeds_view->selectedItem(); + if(item==NULL) { + return; + } + QString keyname=item->text(1); + if(keyname.isEmpty()) { + return; + } + + if(QMessageBox::question(this,"RDAdmin - "+tr("Feed Repost"), + tr("This operation will unpost (remove) all XML, image and")+"\n"+ + tr("audio data from the remote archive, and thus may")+"\n"+ + tr("take signficant time to complete.")+"\n\n"+ + tr("Continue?"),QMessageBox::Yes,QMessageBox::No)!= + QMessageBox::Yes) { + return; + } + + feed=new RDFeed(keyname,rda->config(),this); + QProgressDialog *pd=new QProgressDialog(this); + pd->setCancelButton(NULL); + + // + // Remove RSS XML + // + pd->setLabelText(tr("Unposting RSS XML data...")); + pd->setRange(0,1); + pd->setValue(0); + feed->removeRss(); + pd->setValue(1); + + // + // Remove Item Data + // + sql=QString("select ")+ + "ID "+ // 00 + "from PODCASTS where "+ + QString().sprintf("FEED_ID=%u",feed->id()); + q=new RDSqlQuery(sql); + pd->setLabelText(tr("Unposting item data...")); + pd->setRange(0,q->size()); + count=0; + pd->setValue(0); + while(q->next()) { + RDPodcast *cast=new RDPodcast(rda->config(),q->value(0).toUInt()); + cast->removePodcast(); + delete cast; + pd->setValue(++count); + + } + delete q; + + // + // Remove Images + // + sql=QString("select ")+ + "ID "+ // 00 + "from FEED_IMAGES where "+ + QString().sprintf("FEED_ID=%u",feed->id()); + q=new RDSqlQuery(sql); + pd->setLabelText(tr("Unposting images...")); + pd->setRange(0,q->size()); + count=0; + pd->setValue(0); + while(q->next()) { + feed->removeImage(q->value(0).toUInt()); + pd->setValue(++count); + } + delete q; + + delete pd; +} + + void ListFeeds::closeData() { done(0); @@ -282,7 +487,10 @@ void ListFeeds::resizeEvent(QResizeEvent *e) list_add_button->setGeometry(size().width()-90,30,80,50); list_edit_button->setGeometry(size().width()-90,90,80,50); list_delete_button->setGeometry(size().width()-90,150,80,50); + list_repost_button->setGeometry(size().width()-90,240,80,50); + list_unpost_button->setGeometry(size().width()-90,300,80,50); list_close_button->setGeometry(size().width()-90,size().height()-60,80,50); + list_box_label->setGeometry(14,11,size().width()-28,19); list_feeds_view->setGeometry(10,30,size().width()-120,size().height()-40); } @@ -294,17 +502,35 @@ void ListFeeds::RefreshList() RDListViewItem *item; list_feeds_view->clear(); - q=new - RDSqlQuery("select ID,KEY_NAME,CHANNEL_TITLE,ENABLE_AUTOPOST,\ - KEEP_METADATA,ORIGIN_DATETIME from FEEDS"); + sql=QString("select ")+ + "FEEDS.ID,"+ // 00 + "FEEDS.KEY_NAME,"+ // 01 + "FEEDS.CHANNEL_TITLE,"+ // 02 + "FEEDS.IS_SUPERFEED,"+ // 03 + "FEEDS.ENABLE_AUTOPOST,"+ // 04 + "FEEDS.ORIGIN_DATETIME,"+ // 05 + "FEEDS.BASE_URL,"+ // 06 + "FEED_IMAGES.DATA "+ // 07 + "from FEEDS left join FEED_IMAGES "+ + "on FEEDS.CHANNEL_IMAGE_ID=FEED_IMAGES.ID"; + q=new RDSqlQuery(sql); while (q->next()) { item=new RDListViewItem(list_feeds_view); item->setId(q->value(0).toInt()); - item->setText(0,q->value(1).toString()); - item->setText(1,q->value(2).toString()); - item->setText(2,q->value(3).toString()); - item->setText(3,q->value(4).toString()); - item->setText(4,q->value(5).toDateTime().toString("MM/dd/yyyy")); + if(q->value(7).isNull()) { + item->setPixmap(0,*list_rdcastmanager_32x32_map); + } + else { + QImage img=QImage::fromData(q->value(7).toByteArray()); + item->setPixmap(0,QPixmap::fromImage(img.scaled(32,32))); + } + item->setText(1,q->value(1).toString()); + item->setText(2,q->value(2).toString()); + item->setText(3,RDFeed::publicUrl(q->value(6).toString(), + q->value(1).toString())); + item->setText(4,q->value(3).toString()); + item->setText(5,q->value(4).toString()); + item->setText(6,q->value(5).toDateTime().toString("MM/dd/yyyy")); } delete q; } @@ -315,16 +541,33 @@ void ListFeeds::RefreshItem(RDListViewItem *item) QString sql; RDSqlQuery *q; - sql=QString().sprintf("select KEY_NAME,CHANNEL_TITLE,ENABLE_AUTOPOST,\ - KEEP_METADATA,ORIGIN_DATETIME from FEEDS \ - where ID=%d",item->id()); + sql=QString("select ")+ + "FEEDS.KEY_NAME,"+ // 00 + "FEEDS.CHANNEL_TITLE,"+ // 01 + "FEEDS.IS_SUPERFEED,"+ // 02 + "FEEDS.ENABLE_AUTOPOST,"+ // 03 + "FEEDS.ORIGIN_DATETIME,"+ // 04 + "FEEDS.BASE_URL,"+ // 05 + "FEED_IMAGES.DATA "+ // 06 + "from FEEDS left join FEED_IMAGES "+ + "on FEEDS.CHANNEL_IMAGE_ID=FEED_IMAGES.ID where "+ + QString().sprintf("FEEDS.ID=%d",item->id()); q=new RDSqlQuery(sql); if(q->next()) { - item->setText(0,q->value(0).toString()); - item->setText(1,q->value(1).toString()); - item->setText(2,q->value(2).toString()); - item->setText(3,q->value(3).toString()); - item->setText(4,q->value(4).toDateTime().toString("MM/dd/yyyy")); + if(q->value(6).isNull()) { + item->setPixmap(0,*list_rdcastmanager_32x32_map); + } + else { + QImage img=QImage::fromData(q->value(6).toByteArray()); + item->setPixmap(0,QPixmap::fromImage(img.scaled(32,32))); + } + item->setText(1,q->value(0).toString()); + item->setText(2,q->value(1).toString()); + item->setText(3,RDFeed::publicUrl(q->value(5).toString(), + q->value(0).toString())); + item->setText(4,q->value(2).toString()); + item->setText(5,q->value(3).toString()); + item->setText(6,q->value(4).toDateTime().toString("MM/dd/yyyy")); } delete q; } diff --git a/rdadmin/list_feeds.h b/rdadmin/list_feeds.h index c3cb6b30..bad36ce5 100644 --- a/rdadmin/list_feeds.h +++ b/rdadmin/list_feeds.h @@ -2,7 +2,7 @@ // // List Rivendell Feeds // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -40,6 +40,8 @@ class ListFeeds : public RDDialog void editData(); void deleteData(); void doubleClickedData(Q3ListViewItem *item,const QPoint &pt,int col); + void repostData(); + void unpostData(); void closeData(); protected: @@ -48,14 +50,18 @@ class ListFeeds : public RDDialog private: void RefreshList(); void RefreshItem(RDListViewItem *item); + QPixmap *list_rdcastmanager_32x32_map; + QLabel *list_box_label; RDListView *list_feeds_view; QPushButton *list_add_button; QPushButton *list_edit_button; QPushButton *list_delete_button; + QPushButton *list_repost_button; + QPushButton *list_unpost_button; QPushButton *list_close_button; }; -#endif +#endif // LIST_FEEDS_H diff --git a/rdadmin/list_images.cpp b/rdadmin/list_images.cpp new file mode 100644 index 00000000..e86416f6 --- /dev/null +++ b/rdadmin/list_images.cpp @@ -0,0 +1,293 @@ +// list_images.cpp +// +// Manage a collection of pixmap images +// +// (C) Copyright 2020 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 +#include + +#include "list_images.h" + +ListImages::ListImages(RDImagePickerModel *model,QWidget *parent) +: RDDialog(parent) +{ + list_model=model; + + setWindowTitle("RDAdmin - "+tr("Image Manager")); + + // + // Dialogs + // + list_edit_image_dialog=new EditImage(this); + if(getenv("HOME")!=NULL) { + list_file_dir=getenv("HOME"); + } + else { + list_file_dir="/"; + } + + list_view=new QListView(this); + connect(list_view,SIGNAL(clicked(const QModelIndex &)), + this,SLOT(clickedData(const QModelIndex &))); + connect(list_view,SIGNAL(doubleClicked(const QModelIndex &)), + this,SLOT(doubleClickedData(const QModelIndex &))); + list_view->setModel(list_model); + + list_add_button=new QPushButton(tr("Add"),this); + list_add_button->setFont(buttonFont()); + connect(list_add_button,SIGNAL(clicked()),this,SLOT(addData())); + + list_view_button=new QPushButton(tr("View"),this); + list_view_button->setFont(buttonFont()); + connect(list_view_button,SIGNAL(clicked()),this,SLOT(viewData())); + + list_delete_button=new QPushButton(tr("Delete"),this); + list_delete_button->setFont(buttonFont()); + connect(list_delete_button,SIGNAL(clicked()),this,SLOT(deleteData())); + + list_close_button=new QPushButton(tr("Close"),this); + list_close_button->setFont(buttonFont()); + connect(list_close_button,SIGNAL(clicked()),this,SLOT(closeData())); +} + + +ListImages::~ListImages() +{ + delete list_close_button; + delete list_delete_button; + delete list_view_button; + delete list_add_button; + delete list_view; + delete list_edit_image_dialog; +} + + +QSize ListImages::sizeHint() const +{ + return QSize(400,300); +} + + +int ListImages::exec(RDFeed *feed) +{ + list_feed=feed; + list_model->setCategoryId(feed->id()); + + return QDialog::exec(); +} + + +void ListImages::addData() +{ + // RDUpload::ErrorCode err_code; + QStringList f0; + int img_id=-1; + QString err_msg=""; + QString filename= + QFileDialog::getOpenFileName(this, + "RDAdmin - "+tr("Open Podcast Image File"), + list_file_dir,RD_PODCAST_IMAGE_FILE_FILTER); + if(!filename.isNull()) { + // + // Import the image + // + if((img_id=list_feed->importImageFile(filename,&err_msg))<0) { + QMessageBox::warning(this,"RDAdmin - "+tr("Import Error"), + tr("Image import failed.")+"\n"+ + "["+err_msg+"]."); + return; + } + + // + // Upload the image + // + f0=filename.split(".",QString::SkipEmptyParts); + if(!list_feed->postImage(img_id)) { + QMessageBox::warning(this,"RDAdmin - "+tr("Upload Error"), + tr("Image upload failed!")+"\n"+ + "["+err_msg+"]."); + list_feed->deleteImage(img_id,&err_msg); + return; + } + + // + // Open dialog for setting the metadata + // + if(!list_edit_image_dialog->exec(img_id)) { + list_feed->deleteImage(img_id,&err_msg); + return; + } + + list_model->refresh(); + + // + // Save import path + // + f0=filename.split("/",QString::SkipEmptyParts); + f0.removeLast(); + list_file_dir=f0.join("/"); + } +} + + +void ListImages::viewData() +{ + int row; + + if((row=SelectedRow())>=0) { + if(list_edit_image_dialog->exec(list_model->imageId(row))) { + list_model->update(row); + } + } +} + + +void ListImages::deleteData() +{ + int row; + QString err_msg=""; + QString sql; + RDSqlQuery *q=NULL; + int channel_ids=0; + int channel_default_ids=0; + int item_ids=0; + + if((row=SelectedRow())>=0) { + sql=QString("select ")+ + "ID "+ + "from FEEDS where "+ + QString().sprintf("CHANNEL_IMAGE_ID=%d",list_model->imageId(row)); + q=new RDSqlQuery(sql); + channel_ids=q->size(); + delete q; + + sql=QString("select ")+ + "ID "+ + "from FEEDS where "+ + QString().sprintf("DEFAULT_ITEM_IMAGE_ID=%d",list_model->imageId(row)); + q=new RDSqlQuery(sql); + channel_default_ids=q->size(); + delete q; + + sql=QString("select ")+ + "ID "+ + "from PODCASTS where "+ + QString().sprintf("ITEM_IMAGE_ID=%d",list_model->imageId(row)); + q=new RDSqlQuery(sql); + item_ids=q->size(); + delete q; + + if((channel_ids>0)||(channel_default_ids>0)||(item_ids>0)) { + QString msg=tr("Image is in use as")+" "; + if(channel_ids>0) { + msg+=tr("a channel image")+", "; + } + if(channel_default_ids>0) { + msg+=tr("a default item image")+", "; + } + if(item_ids>0) { + msg+=tr("an item image")+", "; + } + msg=msg.left(msg.length()-2)+"."; + QMessageBox::warning(this,"RDAdmin - "+tr("Image in Use"),msg); + return; + } + + sql=QString("select ID from FEED_IMAGES where ")+ + QString().sprintf("ID=%d",list_model->imageId(row)); + q=new RDSqlQuery(sql); + if(q->first()) { + if((row=SelectedRow())>=0) { + if(QMessageBox::question(this,"RDAdmin - "+tr("Delete Podcast Image"), + tr("Are you sure you want to delete this image?"), + QMessageBox::Yes,QMessageBox::No)!= + QMessageBox::Yes) { + return; + } + if(list_feed->deleteImage(list_model->imageId(row),&err_msg)) { + list_model->refresh(); + } + else { + QMessageBox::warning(this,"RDAdmin - "+tr("Error"), + tr("Image deletion failed!")+"\n"+ + "["+err_msg+"]."); + } + } + } + } +} + + +void ListImages::clickedData(const QModelIndex &index) +{ + list_view_button-> + setDisabled(list_view->model()->data(index,Qt::DecorationRole).isNull()); + list_delete_button-> + setDisabled(list_view->model()->data(index,Qt::DecorationRole).isNull()); +} + + +void ListImages::doubleClickedData(const QModelIndex &index) +{ + viewData(); +} + + +void ListImages::closeData() +{ + done(true); +} + + +void ListImages::closeEvent(QCloseEvent *e) +{ + closeData(); +} + + +void ListImages::resizeEvent(QResizeEvent *e) +{ + int w=size().width(); + int h=size().height(); + + list_model->rescaleImages(QSize(40,40)); + + list_view->setGeometry(10,2,w-20,h-70); + + list_add_button->setGeometry(15,h-60,60,37); + list_view_button->setGeometry(95,h-60,60,37); + list_delete_button->setGeometry(175,h-60,60,37); + + list_close_button->setGeometry(w-90,h-60,80,50); +} + + +int ListImages::SelectedRow() const +{ + QModelIndexList indexes=list_view->selectionModel()->selectedIndexes(); + + if(indexes.size()>0) { + return indexes.at(0).row(); + } + + return -1; +} diff --git a/rdadmin/list_images.h b/rdadmin/list_images.h new file mode 100644 index 00000000..6057cd17 --- /dev/null +++ b/rdadmin/list_images.h @@ -0,0 +1,71 @@ +// list_images.h +// +// Manage a collection of pixmap images +// +// (C) Copyright 2020 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. +// + +#ifndef LIST_IMAGES_H +#define LIST_IMAGES_H + +#include +#include + +#include +#include +#include +#include + +#include "edit_image.h" + +class ListImages : public RDDialog +{ + Q_OBJECT + public: + ListImages(RDImagePickerModel *model,QWidget *parent=0); + ~ListImages(); + QSize sizeHint() const; + + public slots: + int exec(RDFeed *feed); + + private slots: + void addData(); + void viewData(); + void deleteData(); + void clickedData(const QModelIndex &index); + void doubleClickedData(const QModelIndex &index); + void closeData(); + + protected: + void closeEvent(QCloseEvent *e); + void resizeEvent(QResizeEvent *e); + + private: + int SelectedRow() const; + EditImage *list_edit_image_dialog; + QListView *list_view; + RDImagePickerModel *list_model; + RDFeed *list_feed; + QPushButton *list_add_button; + QPushButton *list_view_button; + QPushButton *list_delete_button; + QPushButton *list_close_button; + QString list_file_dir; +}; + + +#endif // LIST_IMAGES_H diff --git a/rdadmin/rdadmin.pro b/rdadmin/rdadmin.pro index b26bee1c..57ebff32 100644 --- a/rdadmin/rdadmin.pro +++ b/rdadmin/rdadmin.pro @@ -41,6 +41,7 @@ x11 { SOURCES += edit_group.cpp SOURCES += edit_hostvar.cpp SOURCES += edit_hotkeys.cpp + SOURCES += edit_image.cpp SOURCES += edit_jack.cpp SOURCES += edit_jack_client.cpp SOURCES += edit_livewiregpio.cpp @@ -68,6 +69,7 @@ x11 { SOURCES += list_gpis.cpp SOURCES += list_groups.cpp SOURCES += list_hostvars.cpp + SOURCES += list_images.cpp SOURCES += list_livewiregpios.cpp SOURCES += list_matrices.cpp SOURCES += list_pypads.cpp @@ -109,6 +111,7 @@ x11 { HEADERS += edit_group.h HEADERS += edit_hostvar.h HEADERS += edit_hotkeys.h + HEADERS += edit_image.h HEADERS += edit_jack.h HEADERS += edit_jack_client.h HEADERS += edit_livewiregpio.h @@ -136,6 +139,7 @@ x11 { HEADERS += list_gpis.h HEADERS += list_groups.h HEADERS += list_hostvars.h + HEADERS += list_images.h HEADERS += list_livewiregpios.h HEADERS += list_matrices.h HEADERS += list_pypads.h diff --git a/rdadmin/rdadmin_cs.ts b/rdadmin/rdadmin_cs.ts index cc6894ce..22142afa 100644 --- a/rdadmin/rdadmin_cs.ts +++ b/rdadmin/rdadmin_cs.ts @@ -236,11 +236,11 @@ a záloha původní databáze uložena v Add Feed Error - Chyba pÅ™i pÅ™idávání přívodu + Chyba pÅ™i pÅ™idávání přívodu A feed with that key name already exists! - Přívod s tímto názvem klíÄe již existuje! + Přívod s tímto názvem klíÄe již existuje! Add RSS Feed @@ -1437,7 +1437,7 @@ files, causing any whose files remain to be imported again. Key Name: - Název klíÄe: + Název klíÄe: CHANNEL VALUES @@ -1473,7 +1473,7 @@ files, causing any whose files remain to be imported again. Audio Upload URL: - Adresa (URL) nahrání zvuku: + Adresa (URL) nahrání zvuku: Username: @@ -1485,7 +1485,7 @@ files, causing any whose files remain to be imported again. Upload Format: - Formát nahrání: + Formát nahrání: S&et @@ -1505,11 +1505,11 @@ files, causing any whose files remain to be imported again. Audio Download URL: - Adresa (URL) stažení zvuku: + Adresa (URL) stažení zvuku: Keep Expired Metadata - Zachovat proÅ¡lá popisná data + Zachovat proÅ¡lá popisná data Enable AutoPost @@ -1525,11 +1525,11 @@ files, causing any whose files remain to be imported again. None - Žádná + Žádná Maximum Shelf Life: - NejvÄ›tší Äas pÅ™ed vyprÅ¡ením: + NejvÄ›tší Äas pÅ™ed vyprÅ¡ením: days @@ -1549,23 +1549,23 @@ files, causing any whose files remain to be imported again. Direct - Přímé + Přímé Counted - PoÄítané + PoÄítané Media Link Mode: - Režim odkazu na media: + Režim odkazu na media: Enable Feed Redirection - Povolit pÅ™esmÄ›rování přívodu + Povolit pÅ™esmÄ›rování přívodu URL: - Adresa (URL): + Adresa (URL): Header XML: @@ -1595,7 +1595,7 @@ pole popisných dat Edit Feed - Redirect - Upravit přívod - PÅ™esmÄ›rovat + Upravit přívod - PÅ™esmÄ›rovat Enabling feed redirection will cause clients subscribed to @@ -1603,7 +1603,7 @@ this feed to be PERMANENTLY redirected to the specified URL. Do you still want to enable redireciton? - Povolení pÅ™esmÄ›rování přívodu způsobí, že klienti pÅ™ihlášení k tomuto + Povolení pÅ™esmÄ›rování přívodu způsobí, že klienti pÅ™ihlášení k tomuto přívodu budou TRVALE pÅ™esmÄ›rováni na zadanou adresu. Pořád jeÅ¡tÄ› chcete povolit pÅ™esmÄ›rování? @@ -1612,6 +1612,112 @@ Pořád jeÅ¡tÄ› chcete povolit pÅ™esmÄ›rování? Channel Values + + No + Ne + + + Yes + Ano + + + Is Superfeed + + + + Error + + + + Select Member +Feeds + + + + Audio Upload URL has unsupported scheme! + + + + Audio Format: + + + + Upload URL + + + + Download URL + + + + Editor: + + + + RSS Schema + + + + Default Item Image + + + + Image + + + + Manage + + + + Images + + + + Author: + + + + Owner Name: + + + + Owner E-Mail: + + + + Channel contains explicit content + + + + Superfeed must have at least one subfeed assigned! + + + + [none] + [žádný] + + + Default Shelf Life + + + + Unlimited + + + + Copy to +Clipboard + + + + Use as default Item Author + + + + Authenticate with local identity file + + EditFeedPerms @@ -1886,6 +1992,37 @@ Stále jeÅ¡tÄ› chcete uložit? + + EditImage + + Description + + + + OK + + + + Cancel + ZruÅ¡it + + + Image Viewer + + + + Native Size + + + + Type + + + + URL + + + EditJack @@ -3688,6 +3825,14 @@ PÅ™epsat? Progress + + [none] + [žádný] + + + Process RSS Updates On + + EditStation @@ -3944,6 +4089,10 @@ Instances Web Browser + + SSH Ident. File + + EditSvc @@ -4502,6 +4651,10 @@ Permissions Allow &WebGet Login + + E-Mail Address + + EditUserPerms @@ -5028,7 +5181,7 @@ Stále jeÅ¡tÄ› jej chcete smazat? Keep Metadata - Zachovat popisná data + Zachovat popisná data Creation Date @@ -5036,7 +5189,7 @@ Stále jeÅ¡tÄ› jej chcete smazat? &Feeds: - &Přívod: + &Přívod: Are you sure you want to delete feed @@ -5048,15 +5201,99 @@ Stále jeÅ¡tÄ› jej chcete smazat? Deleting Audio... - Mazání zvuku... + Mazání zvuku... Cancel - ZruÅ¡it + ZruÅ¡it Deleting - Mazání + Mazání + + + Superfeed + + + + Failed to delete remote feed XML. + + + + Warning + Varování + + + Deleting remote audio... + + + + Public URL + + + + &Repost + &VyvÄ›sit znovu + + + Feed Repost + + + + This operation will repost all XML, image and + + + + audio data to the remote archive, and thus may + + + + Continue? + + + + take signficant time to complete. + + + + Posting images... + + + + Posting item data... + + + + &Unpost + + + + Posting RSS XML data... + + + + Unposting RSS XML data... + + + + audio data from the remote archive, and thus may + + + + This operation will unpost (remove) all XML, image and + + + + Unposting item data... + + + + Unposting images... + + + + Podcast Feeds + @@ -5254,6 +5491,85 @@ Stále jeÅ¡tÄ› jej chcete smazat? + + ListImages + + Add + PÅ™idat + + + View + + + + Delete + Smazat + + + Close + + + + Image Manager + + + + Open Podcast Image File + + + + Import Error + Chyba pÅ™i zavedení + + + Image import failed. + + + + Delete Podcast Image + + + + Are you sure you want to delete this image? + + + + Error + + + + Image deletion failed! + + + + Upload Error + + + + Image upload failed! + + + + Image is in use as + + + + a channel image + + + + a default item image + + + + an item image + + + + Image in Use + + + ListLiveWireGpios diff --git a/rdadmin/rdadmin_de.ts b/rdadmin/rdadmin_de.ts index 2d80272d..248c9207 100644 --- a/rdadmin/rdadmin_de.ts +++ b/rdadmin/rdadmin_de.ts @@ -168,18 +168,14 @@ worden. Aktuelle Version &Cancel Abbre&chen - - Add Feed Error - - - - A feed with that key name already exists! - - Add RSS Feed + + Add Feed Error + + AddGroup @@ -1347,10 +1343,6 @@ files, causing any whose files remain to be imported again. Feed: Feed: - - Key Name: - - Title: Titel: @@ -1379,10 +1371,6 @@ files, causing any whose files remain to be imported again. Description: Beschreibung: - - Audio Upload URL: - - Username: Benutzername: @@ -1391,10 +1379,6 @@ files, causing any whose files remain to be imported again. Password: Passwort: - - Upload Format: - - S&et @@ -1411,14 +1395,6 @@ files, causing any whose files remain to be imported again. dBFS - - Audio Download URL: - - - - Keep Expired Metadata - - Enable AutoPost @@ -1431,14 +1407,6 @@ files, causing any whose files remain to be imported again. Audio Extension: - - None - - - - Maximum Shelf Life: - - days @@ -1455,26 +1423,6 @@ files, causing any whose files remain to be imported again. Episode Sort Order: - - Direct - - - - Counted - - - - Media Link Mode: - - - - Enable Feed Redirection - - - - URL: - - Header XML: @@ -1495,22 +1443,116 @@ files, causing any whose files remain to be imported again. &Cancel Abbre&chen - - Edit Feed - Redirect - - - - Enabling feed redirection will cause clients subscribed to -this feed to be PERMANENTLY redirected to the -specified URL. - -Do you still want to enable redireciton? - - Channel Values + + No + Nein + + + Yes + Ja + + + Is Superfeed + + + + Error + + + + Select Member +Feeds + + + + Audio Upload URL has unsupported scheme! + + + + Audio Format: + + + + Upload URL + + + + Download URL + + + + Editor: + + + + RSS Schema + + + + Default Item Image + + + + Image + + + + Manage + + + + Images + + + + Author: + + + + Owner Name: + + + + Owner E-Mail: + + + + Channel contains explicit content + + + + Superfeed must have at least one subfeed assigned! + + + + [none] + [keine] + + + Default Shelf Life + + + + Unlimited + + + + Copy to +Clipboard + + + + Use as default Item Author + + + + Authenticate with local identity file + + EditFeedPerms @@ -1784,6 +1826,37 @@ Do you still want to save? + + EditImage + + Description + + + + OK + + + + Cancel + + + + Image Viewer + + + + Native Size + + + + Type + + + + URL + + + EditJack @@ -3564,6 +3637,14 @@ Overwrite? Progress + + [none] + [keine] + + + Process RSS Updates On + + EditStation @@ -3817,6 +3898,10 @@ Instances Web Browser + + SSH Ident. File + + EditSvc @@ -4375,6 +4460,10 @@ Permissions Allow &WebGet Login + + E-Mail Address + + EditUserPerms @@ -4872,18 +4961,10 @@ Wollen Sie ihn immernoch löschen? AutoPost - - Keep Metadata - - Creation Date - - &Feeds: - - Are you sure you want to delete feed @@ -4893,15 +4974,87 @@ Wollen Sie ihn immernoch löschen? - Deleting Audio... + Superfeed - Cancel + Failed to delete remote feed XML. - Deleting + Warning + + + + Deleting remote audio... + + + + Public URL + + + + &Repost + + + + Feed Repost + + + + This operation will repost all XML, image and + + + + audio data to the remote archive, and thus may + + + + Continue? + + + + take signficant time to complete. + + + + Posting images... + + + + Posting item data... + + + + &Unpost + + + + Posting RSS XML data... + + + + Unposting RSS XML data... + + + + audio data from the remote archive, and thus may + + + + This operation will unpost (remove) all XML, image and + + + + Unposting item data... + + + + Unposting images... + + + + Podcast Feeds @@ -5100,6 +5253,85 @@ Generieren + + ListImages + + Add + Hinzufügen + + + View + + + + Delete + Löschen + + + Close + + + + Image Manager + + + + Open Podcast Image File + + + + Import Error + Importfehler + + + Image import failed. + + + + Delete Podcast Image + + + + Are you sure you want to delete this image? + + + + Error + + + + Image deletion failed! + + + + Upload Error + + + + Image upload failed! + + + + Image is in use as + + + + a channel image + + + + a default item image + + + + an item image + + + + Image in Use + + + ListLiveWireGpios diff --git a/rdadmin/rdadmin_es.ts b/rdadmin/rdadmin_es.ts index 2c8e492e..12923ed4 100644 --- a/rdadmin/rdadmin_es.ts +++ b/rdadmin/rdadmin_es.ts @@ -245,11 +245,11 @@ y un respaldo de la base de datos se guardó en Add Feed Error - Error añadiendo feed + Error añadiendo feed A feed with that key name already exists! - ¡Un feed con ese nombre ya existe! + ¡Un feed con ese nombre ya existe! Add RSS Feed @@ -1439,7 +1439,7 @@ files, causing any whose files remain to be imported again. Key Name: - Nombre: + Nombre: Title: @@ -1471,7 +1471,7 @@ files, causing any whose files remain to be imported again. Audio Upload URL: - URL subida audio: + URL subida audio: Username: @@ -1483,7 +1483,7 @@ files, causing any whose files remain to be imported again. Upload Format: - Formato de subida: + Formato de subida: S&et @@ -1503,11 +1503,11 @@ files, causing any whose files remain to be imported again. Audio Download URL: - URL descarga audio: + URL descarga audio: Keep Expired Metadata - Mantener metadatos vencidos + Mantener metadatos vencidos Enable AutoPost @@ -1523,11 +1523,11 @@ files, causing any whose files remain to be imported again. None - Ninguno + Ninguno Maximum Shelf Life: - Vida útil máxima: + Vida útil máxima: days @@ -1547,23 +1547,23 @@ files, causing any whose files remain to be imported again. Direct - Directo + Directo Counted - Contado + Contado Media Link Mode: - Modo de los Enlaces: + Modo de los Enlaces: Enable Feed Redirection - Activar redirec. de feed + Activar redirec. de feed URL: - URL: + URL: Header XML: @@ -1593,7 +1593,7 @@ metadatos auxiliares Edit Feed - Redirect - Edtar feed - Redireccionar + Edtar feed - Redireccionar Enabling feed redirection will cause clients subscribed to @@ -1601,7 +1601,7 @@ this feed to be PERMANENTLY redirected to the specified URL. Do you still want to enable redireciton? - Activar el redireccionamiento de feeds causará que los clientes + Activar el redireccionamiento de feeds causará que los clientes suscritos a este feed sean redireccionados PERMANENTEMENTE al URL especificado. @@ -1615,6 +1615,108 @@ al URL especificado. Channel Values + + No + No + + + Yes + Sí + + + Is Superfeed + + + + Error + + + + Select Member +Feeds + + + + Audio Upload URL has unsupported scheme! + + + + Audio Format: + + + + Upload URL + + + + Download URL + + + + Editor: + + + + RSS Schema + + + + Default Item Image + + + + Image + + + + Manage + + + + Images + + + + Author: + + + + Owner Name: + + + + Owner E-Mail: + + + + Channel contains explicit content + + + + Superfeed must have at least one subfeed assigned! + + + + Default Shelf Life + + + + Unlimited + + + + Copy to +Clipboard + + + + Use as default Item Author + + + + Authenticate with local identity file + + EditFeedPerms @@ -1888,6 +1990,37 @@ Do you still want to save? + + EditImage + + Description + + + + OK + + + + Cancel + Cancelar + + + Image Viewer + + + + Native Size + + + + Type + + + + URL + + + EditJack @@ -3699,6 +3832,14 @@ Overwrite? Progress + + [none] + + + + Process RSS Updates On + + EditStation @@ -3953,6 +4094,10 @@ Instances Web Browser + + SSH Ident. File + + EditSvc @@ -4459,6 +4604,10 @@ Permissions Allow &WebGet Login + + E-Mail Address + + EditUserPerms @@ -4995,7 +5144,7 @@ Do you still want to delete it? Keep Metadata - Mantener metadatos + Mantener metadatos Creation Date @@ -5003,7 +5152,7 @@ Do you still want to delete it? &Feeds: - &Feeds: + &Feeds: Are you sure you want to delete feed @@ -5015,15 +5164,99 @@ Do you still want to delete it? Deleting Audio... - Eliminando Audio... + Eliminando Audio... Cancel - Cancelar + Cancelar Deleting - Borrando + Borrando + + + Superfeed + + + + Failed to delete remote feed XML. + + + + Warning + Advertencia + + + Deleting remote audio... + + + + Public URL + + + + &Repost + &Republicar + + + Feed Repost + + + + This operation will repost all XML, image and + + + + audio data to the remote archive, and thus may + + + + Continue? + + + + take signficant time to complete. + + + + Posting images... + + + + Posting item data... + + + + &Unpost + + + + Posting RSS XML data... + + + + Unposting RSS XML data... + + + + audio data from the remote archive, and thus may + + + + This operation will unpost (remove) all XML, image and + + + + Unposting item data... + + + + Unposting images... + + + + Podcast Feeds + @@ -5221,6 +5454,85 @@ Do you still want to delete it? + + ListImages + + Add + Añadir + + + View + + + + Delete + Borrar + + + Close + + + + Image Manager + + + + Open Podcast Image File + + + + Import Error + Error de importación + + + Image import failed. + + + + Delete Podcast Image + + + + Are you sure you want to delete this image? + + + + Error + + + + Image deletion failed! + + + + Upload Error + + + + Image upload failed! + + + + Image is in use as + + + + a channel image + + + + a default item image + + + + an item image + + + + Image in Use + + + ListLiveWireGpios diff --git a/rdadmin/rdadmin_fr.ts b/rdadmin/rdadmin_fr.ts index c0453584..4e917bb8 100644 --- a/rdadmin/rdadmin_fr.ts +++ b/rdadmin/rdadmin_fr.ts @@ -19,18 +19,14 @@ &Cancel - - Add Feed Error - - - - A feed with that key name already exists! - - Add RSS Feed + + Add Feed Error + + AddGroup @@ -1019,10 +1015,6 @@ files, causing any whose files remain to be imported again. Feed: - - Key Name: - - Title: @@ -1051,10 +1043,6 @@ files, causing any whose files remain to be imported again. Description: - - Audio Upload URL: - - Username: @@ -1063,10 +1051,6 @@ files, causing any whose files remain to be imported again. Password: - - Upload Format: - - S&et @@ -1083,14 +1067,6 @@ files, causing any whose files remain to be imported again. dBFS - - Audio Download URL: - - - - Keep Expired Metadata - - Enable AutoPost @@ -1103,14 +1079,6 @@ files, causing any whose files remain to be imported again. Audio Extension: - - None - - - - Maximum Shelf Life: - - days @@ -1127,26 +1095,6 @@ files, causing any whose files remain to be imported again. Episode Sort Order: - - Direct - - - - Counted - - - - Media Link Mode: - - - - Enable Feed Redirection - - - - URL: - - Header XML: @@ -1167,22 +1115,112 @@ files, causing any whose files remain to be imported again. &Cancel - - Edit Feed - Redirect - - - - Enabling feed redirection will cause clients subscribed to -this feed to be PERMANENTLY redirected to the -specified URL. - -Do you still want to enable redireciton? - - Channel Values + + No + + + + Yes + + + + Is Superfeed + + + + Error + + + + Select Member +Feeds + + + + Audio Upload URL has unsupported scheme! + + + + Audio Format: + + + + Upload URL + + + + Download URL + + + + Editor: + + + + RSS Schema + + + + Default Item Image + + + + Image + + + + Manage + + + + Images + + + + Author: + + + + Owner Name: + + + + Owner E-Mail: + + + + Channel contains explicit content + + + + Superfeed must have at least one subfeed assigned! + + + + Default Shelf Life + + + + Unlimited + + + + Copy to +Clipboard + + + + Use as default Item Author + + + + Authenticate with local identity file + + EditFeedPerms @@ -1454,6 +1492,37 @@ Do you still want to save? + + EditImage + + Description + + + + OK + + + + Cancel + + + + Image Viewer + + + + Native Size + + + + Type + + + + URL + + + EditJack @@ -2978,6 +3047,14 @@ Overwrite? Progress + + [none] + + + + Process RSS Updates On + + EditStation @@ -3195,6 +3272,10 @@ Instances Web Browser + + SSH Ident. File + + EditSvc @@ -3644,6 +3725,10 @@ Permissions Allow &WebGet Login + + E-Mail Address + + EditUserPerms @@ -3995,18 +4080,10 @@ Permissions AutoPost - - Keep Metadata - - Creation Date - - &Feeds: - - Are you sure you want to delete feed @@ -4016,15 +4093,87 @@ Permissions - Deleting Audio... + Superfeed - Cancel + Failed to delete remote feed XML. - Deleting + Warning + + + + Deleting remote audio... + + + + Public URL + + + + &Repost + + + + Feed Repost + + + + This operation will repost all XML, image and + + + + audio data to the remote archive, and thus may + + + + Continue? + + + + take signficant time to complete. + + + + Posting images... + + + + Posting item data... + + + + &Unpost + + + + Posting RSS XML data... + + + + Unposting RSS XML data... + + + + audio data from the remote archive, and thus may + + + + This operation will unpost (remove) all XML, image and + + + + Unposting item data... + + + + Unposting images... + + + + Podcast Feeds @@ -4222,6 +4371,85 @@ Permissions + + ListImages + + Add + + + + View + + + + Delete + + + + Close + + + + Image Manager + + + + Open Podcast Image File + + + + Import Error + + + + Image import failed. + + + + Delete Podcast Image + + + + Are you sure you want to delete this image? + + + + Error + + + + Image deletion failed! + + + + Upload Error + + + + Image upload failed! + + + + Image is in use as + + + + a channel image + + + + a default item image + + + + an item image + + + + Image in Use + + + ListLiveWireGpios diff --git a/rdadmin/rdadmin_nb.ts b/rdadmin/rdadmin_nb.ts index 6f97b9e9..a431c967 100644 --- a/rdadmin/rdadmin_nb.ts +++ b/rdadmin/rdadmin_nb.ts @@ -158,18 +158,14 @@ oppdatert til versjon &Cancel &Avbryt - - Add Feed Error - - - - A feed with that key name already exists! - - Add RSS Feed + + Add Feed Error + + AddGroup @@ -1309,10 +1305,6 @@ files, causing any whose files remain to be imported again. Feed: Straum: - - Key Name: - - Title: Tittel: @@ -1341,10 +1333,6 @@ files, causing any whose files remain to be imported again. Description: Skildring: - - Audio Upload URL: - - Username: Brukarnamn: @@ -1353,10 +1341,6 @@ files, causing any whose files remain to be imported again. Password: Passord: - - Upload Format: - - S&et @@ -1373,14 +1357,6 @@ files, causing any whose files remain to be imported again. dBFS - - Audio Download URL: - - - - Keep Expired Metadata - - Enable AutoPost @@ -1395,11 +1371,7 @@ files, causing any whose files remain to be imported again. None - Ingen - - - Maximum Shelf Life: - + Ingen days @@ -1417,26 +1389,6 @@ files, causing any whose files remain to be imported again. Episode Sort Order: - - Direct - - - - Counted - - - - Media Link Mode: - - - - Enable Feed Redirection - - - - URL: - - Header XML: @@ -1457,22 +1409,116 @@ files, causing any whose files remain to be imported again. &Cancel &Avbryt - - Edit Feed - Redirect - - - - Enabling feed redirection will cause clients subscribed to -this feed to be PERMANENTLY redirected to the -specified URL. - -Do you still want to enable redireciton? - - Channel Values + + No + Nei + + + Yes + Ja + + + Is Superfeed + + + + Error + + + + Select Member +Feeds + + + + Audio Upload URL has unsupported scheme! + + + + Audio Format: + + + + Upload URL + + + + Download URL + + + + Editor: + + + + RSS Schema + + + + Default Item Image + + + + Image + + + + Manage + + + + Images + + + + Author: + + + + Owner Name: + + + + Owner E-Mail: + + + + Channel contains explicit content + + + + Superfeed must have at least one subfeed assigned! + + + + [none] + [ingen] + + + Default Shelf Life + + + + Unlimited + + + + Copy to +Clipboard + + + + Use as default Item Author + + + + Authenticate with local identity file + + EditFeedPerms @@ -1751,6 +1797,37 @@ Vil du framleis lagra? + + EditImage + + Description + + + + OK + + + + Cancel + + + + Image Viewer + + + + Native Size + + + + Type + + + + URL + + + EditJack @@ -3502,6 +3579,14 @@ Overwrite? Progress + + [none] + [ingen] + + + Process RSS Updates On + + EditStation @@ -3734,6 +3819,10 @@ Instances Web Browser + + SSH Ident. File + + EditSvc @@ -4276,6 +4365,10 @@ Permissions Allow &WebGet Login + + E-Mail Address + + EditUserPerms @@ -4729,18 +4822,10 @@ Klikk pÃ¥ "Lisens"-knappen for fleire opplysningar. AutoPost - - Keep Metadata - - Creation Date - - &Feeds: - - Are you sure you want to delete feed @@ -4750,15 +4835,87 @@ Klikk pÃ¥ "Lisens"-knappen for fleire opplysningar. - Deleting Audio... + Superfeed - Cancel + Failed to delete remote feed XML. - Deleting + Warning + + + + Deleting remote audio... + + + + Public URL + + + + &Repost + + + + Feed Repost + + + + This operation will repost all XML, image and + + + + audio data to the remote archive, and thus may + + + + Continue? + + + + take signficant time to complete. + + + + Posting images... + + + + Posting item data... + + + + &Unpost + + + + Posting RSS XML data... + + + + Unposting RSS XML data... + + + + audio data from the remote archive, and thus may + + + + This operation will unpost (remove) all XML, image and + + + + Unposting item data... + + + + Unposting images... + + + + Podcast Feeds @@ -4965,6 +5122,85 @@ Klikk pÃ¥ "Lisens"-knappen for fleire opplysningar. + + ListImages + + Add + + + + View + + + + Delete + + + + Close + + + + Image Manager + + + + Open Podcast Image File + + + + Import Error + Importfeil + + + Image import failed. + + + + Delete Podcast Image + + + + Are you sure you want to delete this image? + + + + Error + + + + Image deletion failed! + + + + Upload Error + + + + Image upload failed! + + + + Image is in use as + + + + a channel image + + + + a default item image + + + + an item image + + + + Image in Use + + + ListLiveWireGpios diff --git a/rdadmin/rdadmin_nn.ts b/rdadmin/rdadmin_nn.ts index 6f97b9e9..a431c967 100644 --- a/rdadmin/rdadmin_nn.ts +++ b/rdadmin/rdadmin_nn.ts @@ -158,18 +158,14 @@ oppdatert til versjon &Cancel &Avbryt - - Add Feed Error - - - - A feed with that key name already exists! - - Add RSS Feed + + Add Feed Error + + AddGroup @@ -1309,10 +1305,6 @@ files, causing any whose files remain to be imported again. Feed: Straum: - - Key Name: - - Title: Tittel: @@ -1341,10 +1333,6 @@ files, causing any whose files remain to be imported again. Description: Skildring: - - Audio Upload URL: - - Username: Brukarnamn: @@ -1353,10 +1341,6 @@ files, causing any whose files remain to be imported again. Password: Passord: - - Upload Format: - - S&et @@ -1373,14 +1357,6 @@ files, causing any whose files remain to be imported again. dBFS - - Audio Download URL: - - - - Keep Expired Metadata - - Enable AutoPost @@ -1395,11 +1371,7 @@ files, causing any whose files remain to be imported again. None - Ingen - - - Maximum Shelf Life: - + Ingen days @@ -1417,26 +1389,6 @@ files, causing any whose files remain to be imported again. Episode Sort Order: - - Direct - - - - Counted - - - - Media Link Mode: - - - - Enable Feed Redirection - - - - URL: - - Header XML: @@ -1457,22 +1409,116 @@ files, causing any whose files remain to be imported again. &Cancel &Avbryt - - Edit Feed - Redirect - - - - Enabling feed redirection will cause clients subscribed to -this feed to be PERMANENTLY redirected to the -specified URL. - -Do you still want to enable redireciton? - - Channel Values + + No + Nei + + + Yes + Ja + + + Is Superfeed + + + + Error + + + + Select Member +Feeds + + + + Audio Upload URL has unsupported scheme! + + + + Audio Format: + + + + Upload URL + + + + Download URL + + + + Editor: + + + + RSS Schema + + + + Default Item Image + + + + Image + + + + Manage + + + + Images + + + + Author: + + + + Owner Name: + + + + Owner E-Mail: + + + + Channel contains explicit content + + + + Superfeed must have at least one subfeed assigned! + + + + [none] + [ingen] + + + Default Shelf Life + + + + Unlimited + + + + Copy to +Clipboard + + + + Use as default Item Author + + + + Authenticate with local identity file + + EditFeedPerms @@ -1751,6 +1797,37 @@ Vil du framleis lagra? + + EditImage + + Description + + + + OK + + + + Cancel + + + + Image Viewer + + + + Native Size + + + + Type + + + + URL + + + EditJack @@ -3502,6 +3579,14 @@ Overwrite? Progress + + [none] + [ingen] + + + Process RSS Updates On + + EditStation @@ -3734,6 +3819,10 @@ Instances Web Browser + + SSH Ident. File + + EditSvc @@ -4276,6 +4365,10 @@ Permissions Allow &WebGet Login + + E-Mail Address + + EditUserPerms @@ -4729,18 +4822,10 @@ Klikk pÃ¥ "Lisens"-knappen for fleire opplysningar. AutoPost - - Keep Metadata - - Creation Date - - &Feeds: - - Are you sure you want to delete feed @@ -4750,15 +4835,87 @@ Klikk pÃ¥ "Lisens"-knappen for fleire opplysningar. - Deleting Audio... + Superfeed - Cancel + Failed to delete remote feed XML. - Deleting + Warning + + + + Deleting remote audio... + + + + Public URL + + + + &Repost + + + + Feed Repost + + + + This operation will repost all XML, image and + + + + audio data to the remote archive, and thus may + + + + Continue? + + + + take signficant time to complete. + + + + Posting images... + + + + Posting item data... + + + + &Unpost + + + + Posting RSS XML data... + + + + Unposting RSS XML data... + + + + audio data from the remote archive, and thus may + + + + This operation will unpost (remove) all XML, image and + + + + Unposting item data... + + + + Unposting images... + + + + Podcast Feeds @@ -4965,6 +5122,85 @@ Klikk pÃ¥ "Lisens"-knappen for fleire opplysningar. + + ListImages + + Add + + + + View + + + + Delete + + + + Close + + + + Image Manager + + + + Open Podcast Image File + + + + Import Error + Importfeil + + + Image import failed. + + + + Delete Podcast Image + + + + Are you sure you want to delete this image? + + + + Error + + + + Image deletion failed! + + + + Upload Error + + + + Image upload failed! + + + + Image is in use as + + + + a channel image + + + + a default item image + + + + an item image + + + + Image in Use + + + ListLiveWireGpios diff --git a/rdadmin/rdadmin_pt_BR.ts b/rdadmin/rdadmin_pt_BR.ts index 739af685..27ccf20a 100644 --- a/rdadmin/rdadmin_pt_BR.ts +++ b/rdadmin/rdadmin_pt_BR.ts @@ -151,18 +151,14 @@ atualizada para a Versão &Cancel &Cancelar - - Add Feed Error - - - - A feed with that key name already exists! - - Add RSS Feed + + Add Feed Error + + AddGroup @@ -1319,10 +1315,6 @@ files, causing any whose files remain to be imported again. Feed: Feed: - - Key Name: - - Title: Título: @@ -1351,10 +1343,6 @@ files, causing any whose files remain to be imported again. Description: Descrição: - - Audio Upload URL: - - Username: Usuário: @@ -1363,10 +1351,6 @@ files, causing any whose files remain to be imported again. Password: Senha: - - Upload Format: - - S&et @@ -1383,14 +1367,6 @@ files, causing any whose files remain to be imported again. dBFS - - Audio Download URL: - - - - Keep Expired Metadata - - Enable AutoPost @@ -1405,11 +1381,7 @@ files, causing any whose files remain to be imported again. None - Nenhum - - - Maximum Shelf Life: - + Nenhum days @@ -1427,26 +1399,6 @@ files, causing any whose files remain to be imported again. Episode Sort Order: - - Direct - - - - Counted - - - - Media Link Mode: - - - - Enable Feed Redirection - - - - URL: - - Header XML: @@ -1467,22 +1419,116 @@ files, causing any whose files remain to be imported again. &Cancel &Cancelar - - Edit Feed - Redirect - - - - Enabling feed redirection will cause clients subscribed to -this feed to be PERMANENTLY redirected to the -specified URL. - -Do you still want to enable redireciton? - - Channel Values + + No + Não + + + Yes + Sim + + + Is Superfeed + + + + Error + + + + Select Member +Feeds + + + + Audio Upload URL has unsupported scheme! + + + + Audio Format: + + + + Upload URL + + + + Download URL + + + + Editor: + + + + RSS Schema + + + + Default Item Image + + + + Image + + + + Manage + + + + Images + + + + Author: + + + + Owner Name: + + + + Owner E-Mail: + + + + Channel contains explicit content + + + + Superfeed must have at least one subfeed assigned! + + + + [none] + [Nenhum] + + + Default Shelf Life + + + + Unlimited + + + + Copy to +Clipboard + + + + Use as default Item Author + + + + Authenticate with local identity file + + EditFeedPerms @@ -1757,6 +1803,37 @@ Você ainda quer salvar? + + EditImage + + Description + + + + OK + + + + Cancel + + + + Image Viewer + + + + Native Size + + + + Type + + + + URL + + + EditJack @@ -3551,6 +3628,14 @@ Overwrite? Progress + + [none] + [Nenhum] + + + Process RSS Updates On + + EditStation @@ -3805,6 +3890,10 @@ Instances Web Browser + + SSH Ident. File + + EditSvc @@ -4355,6 +4444,10 @@ Permissions Allow &WebGet Login + + E-Mail Address + + EditUserPerms @@ -4852,18 +4945,10 @@ Você ainda quer Deletar? AutoPost - - Keep Metadata - - Creation Date - - &Feeds: - - Are you sure you want to delete feed @@ -4873,15 +4958,87 @@ Você ainda quer Deletar? - Deleting Audio... + Superfeed - Cancel + Failed to delete remote feed XML. - Deleting + Warning + + + + Deleting remote audio... + + + + Public URL + + + + &Repost + + + + Feed Repost + + + + This operation will repost all XML, image and + + + + audio data to the remote archive, and thus may + + + + Continue? + + + + take signficant time to complete. + + + + Posting images... + + + + Posting item data... + + + + &Unpost + + + + Posting RSS XML data... + + + + Unposting RSS XML data... + + + + audio data from the remote archive, and thus may + + + + This operation will unpost (remove) all XML, image and + + + + Unposting item data... + + + + Unposting images... + + + + Podcast Feeds @@ -5080,6 +5237,85 @@ Você ainda quer Deletar? + + ListImages + + Add + Adicionar + + + View + + + + Delete + Deletar + + + Close + + + + Image Manager + + + + Open Podcast Image File + + + + Import Error + Erro ao Importar + + + Image import failed. + + + + Delete Podcast Image + + + + Are you sure you want to delete this image? + + + + Error + + + + Image deletion failed! + + + + Upload Error + + + + Image upload failed! + + + + Image is in use as + + + + a channel image + + + + a default item image + + + + an item image + + + + Image in Use + + + ListLiveWireGpios diff --git a/rdcastmanager/Makefile.am b/rdcastmanager/Makefile.am index ef9cda8b..a921d7fe 100644 --- a/rdcastmanager/Makefile.am +++ b/rdcastmanager/Makefile.am @@ -45,13 +45,17 @@ bin_PROGRAMS = rdcastmanager dist_rdcastmanager_SOURCES = edit_cast.cpp edit_cast.h\ globals.h\ list_casts.cpp list_casts.h\ - pick_report_dates.cpp pick_report_dates.h\ - rdcastmanager.cpp rdcastmanager.h + logdialog.cpp logdialog.h\ + logmodel.cpp logmodel.h\ + rdcastmanager.cpp rdcastmanager.h\ + render_dialog.cpp render_dialog.h nodist_rdcastmanager_SOURCES = moc_edit_cast.cpp\ moc_list_casts.cpp\ - moc_pick_report_dates.cpp\ - moc_rdcastmanager.cpp + moc_logdialog.cpp\ + moc_logmodel.cpp\ + moc_rdcastmanager.cpp\ + moc_render_dialog.cpp rdcastmanager_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support diff --git a/rdcastmanager/edit_cast.cpp b/rdcastmanager/edit_cast.cpp index 40c4589e..f73bac4a 100644 --- a/rdcastmanager/edit_cast.cpp +++ b/rdcastmanager/edit_cast.cpp @@ -2,7 +2,7 @@ // // Edit a Rivendell Cast // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -18,54 +18,47 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#include +#include #include #include #include "edit_cast.h" #include "globals.h" -#include "pick_report_dates.h" EditCast::EditCast(unsigned cast_id,QWidget *parent) : RDDialog(parent) { - int ypos=0; - cast_cast=new RDPodcast(rda->config(),cast_id); cast_feed=new RDFeed(cast_cast->feedId(),rda->config()); + cast_schema=cast_feed->rssSchema(); cast_status=cast_cast->status(); - setWindowTitle("RDCastManager - "+tr("Editing PodCast")); // - // Item Media Link + // Active Checkbox // - cast_item_medialink_edit=new QLineEdit(this); - cast_item_medialink_edit->setGeometry(135,10,sizeHint().width()-145,20); - cast_item_medialink_edit->setReadOnly(true); - QLabel *cast_item_medialink_label= - new QLabel(cast_item_medialink_edit,tr("Media Link:"),this); - cast_item_medialink_label->setGeometry(20,10,110,20); - cast_item_medialink_label->setFont(labelFont()); - cast_item_medialink_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - if(cast_feed->mediaLinkMode()==RDFeed::LinkNone) { - cast_item_medialink_edit->hide(); - cast_item_medialink_label->hide(); - ypos=10; - } - else { - ypos=42; - } + cast_active_check=new QCheckBox(this); + cast_active_label= + new QLabel(cast_active_check,tr("Item Active"),this); + cast_active_label->setFont(labelFont()); + cast_active_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + // + // Item Origin + // + cast_item_origin_label=new QLabel(tr("Posted By")+":",this); + cast_item_origin_label->setFont(labelFont()); + cast_item_origin_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + cast_item_origin_edit=new QLineEdit(this); + cast_item_origin_edit->setReadOnly(true); // // Item Title // cast_item_title_edit=new QLineEdit(this); - cast_item_title_edit->setGeometry(135,ypos,sizeHint().width()-145,20); cast_item_title_edit->setMaxLength(255); - QLabel *cast_item_title_label= + cast_item_title_label= new QLabel(cast_item_title_edit,tr("Title:"),this); - cast_item_title_label->setGeometry(20,ypos,110,20); cast_item_title_label->setFont(labelFont()); cast_item_title_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -73,11 +66,9 @@ EditCast::EditCast(unsigned cast_id,QWidget *parent) // Item Author // cast_item_author_edit=new QLineEdit(this); - cast_item_author_edit->setGeometry(135,ypos+22,sizeHint().width()-145,20); cast_item_author_edit->setMaxLength(255); - QLabel *cast_item_author_label= + cast_item_author_label= new QLabel(cast_item_author_edit,tr("Author E-Mail:"),this); - cast_item_author_label->setGeometry(20,ypos+22,110,20); cast_item_author_label->setFont(labelFont()); cast_item_author_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -85,11 +76,9 @@ EditCast::EditCast(unsigned cast_id,QWidget *parent) // Item Category // cast_item_category_edit=new QLineEdit(this); - cast_item_category_edit->setGeometry(135,ypos+44,sizeHint().width()-145,20); cast_item_category_edit->setMaxLength(64); - QLabel *cast_item_category_label= + cast_item_category_label= new QLabel(cast_item_category_edit,tr("Category:"),this); - cast_item_category_label->setGeometry(20,ypos+44,110,20); cast_item_category_label->setFont(labelFont()); cast_item_category_label-> setAlignment(Qt::AlignRight|Qt::AlignVCenter); @@ -98,246 +87,187 @@ EditCast::EditCast(unsigned cast_id,QWidget *parent) // Item Link // cast_item_link_edit=new QLineEdit(this); - cast_item_link_edit->setGeometry(135,ypos+66,sizeHint().width()-145,20); cast_item_link_edit->setMaxLength(255); - QLabel *cast_item_link_label= + cast_item_link_edit-> + setVisible(rda->rssSchemas()->supportsItemLinks(cast_schema)); + cast_item_link_label= new QLabel(cast_item_link_edit,tr("Link URL:"),this); - cast_item_link_label->setGeometry(20,ypos+66,110,20); cast_item_link_label->setFont(labelFont()); cast_item_link_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - - // - // Item Description - // - cast_item_description_edit=new QTextEdit(this); - cast_item_description_edit-> - setGeometry(135,ypos+88,sizeHint().width()-145,76); - QLabel *cast_item_description_label= - new QLabel(cast_item_description_edit,tr("Description:"),this); - cast_item_description_label->setGeometry(20,ypos+88,110,20); - cast_item_description_label->setFont(labelFont()); - cast_item_description_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); - - // - // Item Source Text - // - cast_item_sourcetext_edit=new QLineEdit(this); - cast_item_sourcetext_edit-> - setGeometry(135,ypos+169,sizeHint().width()-145,20); - cast_item_sourcetext_edit->setMaxLength(64); - QLabel *cast_item_sourcetext_label= - new QLabel(cast_item_sourcetext_edit,tr("Source Text:"),this); - cast_item_sourcetext_label->setGeometry(20,ypos+169,110,20); - cast_item_sourcetext_label->setFont(labelFont()); - cast_item_sourcetext_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - - // - // Item Source URL - // - cast_item_sourceurl_edit=new QLineEdit(this); - cast_item_sourceurl_edit->setGeometry(135,ypos+191,sizeHint().width()-145,20); - cast_item_sourceurl_edit->setMaxLength(64); - QLabel *cast_item_sourceurl_label= - new QLabel(cast_item_sourceurl_edit,tr("Source URL:"),this); - cast_item_sourceurl_label->setGeometry(20,ypos+191,110,20); - cast_item_sourceurl_label->setFont(labelFont()); - cast_item_sourceurl_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + cast_item_link_label-> + setVisible(rda->rssSchemas()->supportsItemLinks(cast_schema)); + // // Item Comments // cast_item_comments_edit=new QLineEdit(this); - cast_item_comments_edit->setGeometry(135,ypos+213,sizeHint().width()-145,20); cast_item_comments_edit->setMaxLength(64); - QLabel *cast_item_comments_label= + cast_item_comments_edit-> + setVisible(rda->rssSchemas()->supportsItemComments(cast_schema)); + cast_item_comments_label= new QLabel(cast_item_comments_edit,tr("Comments URL:"),this); - cast_item_comments_label->setGeometry(10,ypos+213,120,20); cast_item_comments_label->setFont(labelFont()); cast_item_comments_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + cast_item_comments_label-> + setVisible(rda->rssSchemas()->supportsItemComments(cast_schema)); - cast_ypos=233+ypos; + // + // Item Description + // + cast_item_description_edit=new QTextEdit(this); + cast_item_description_label= + new QLabel(cast_item_description_edit,tr("Description:"),this); + cast_item_description_label->setFont(labelFont()); + cast_item_description_label-> + setAlignment(Qt::AlignRight|Qt::AlignVCenter); + + // + // Item Explicit + // + cast_item_explicit_check=new QCheckBox(this); + cast_item_explicit_label=new QLabel(cast_item_explicit_check, + tr("Item contains explicit content"),this); + cast_item_explicit_label->setFont(labelFont()); + cast_item_explicit_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + cast_item_image_box=new RDImagePickerBox("FEED_IMAGES","FEED_ID","ID",this); + cast_item_image_label=new QLabel(cast_item_image_box,tr("Image")+":",this); + cast_item_image_label->setFont(labelFont()); + cast_item_image_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Effective DateTime // cast_item_effective_edit=new QDateTimeEdit(this); - cast_item_effective_edit-> - setGeometry(135,cast_ypos,165,20); - QLabel *label=new QLabel(cast_item_effective_edit,tr("Air Date/Time:"),this); - label->setGeometry(20,cast_ypos,110,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - QPushButton *button=new QPushButton(this); - button->setGeometry(310,cast_ypos,50,20); - button->setFont(subButtonFont()); - button->setText(tr("&Select")); - connect(button,SIGNAL(clicked()),this,SLOT(effectiveSelectData())); - cast_ypos+=22; - - // - // Item Origin - // - cast_item_origin_edit=new QLineEdit(this); - cast_item_origin_edit->setReadOnly(true); - cast_item_origin_edit->setGeometry(135,cast_ypos,165,20); - cast_item_origin_edit->setMaxLength(64); - QLabel *cast_item_origin_label= - new QLabel(cast_item_origin_edit,tr("Posted At:"),this); - cast_item_origin_label->setGeometry(20,cast_ypos,110,20); - cast_item_origin_label->setFont(labelFont()); - cast_item_origin_label-> + cast_item_effective_edit->setDisplayFormat("MM/dd/yyyy hh:mm:ss"); + cast_item_effective_label= + new QLabel(cast_item_effective_edit,tr("Air Date/Time:"),this); + cast_item_effective_label->setFont(labelFont()); + cast_item_effective_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + cast_item_effective_label-> setAlignment(Qt::AlignRight|Qt::AlignVCenter); - cast_ypos+=22; + cast_item_effective_button=new QPushButton(this); + cast_item_effective_button->setFont(subButtonFont()); + cast_item_effective_button->setText(tr("&Select Date")); + connect(cast_item_effective_button,SIGNAL(clicked()), + this,SLOT(effectiveSelectData())); // // Item Expiration // cast_item_expiration_box=new QComboBox(this); - cast_item_expiration_box->setGeometry(135,cast_ypos,50,20); cast_item_expiration_box->insertItem(tr("No")); cast_item_expiration_box->insertItem(tr("Yes")); connect(cast_item_expiration_box,SIGNAL(activated(int)), this,SLOT(expirationSelectedData(int))); - label=new QLabel(cast_item_expiration_box,tr("Cast Expires:"),this); - label->setGeometry(20,cast_ypos,110,20); - label->setFont(labelFont()); - label-> + cast_item_expiration_box_label= + new QLabel(cast_item_expiration_box,tr("Item Expires")+":",this); + cast_item_expiration_box_label->setFont(labelFont()); + cast_item_expiration_box_label-> setAlignment(Qt::AlignRight|Qt::AlignVCenter); - cast_ypos+=22; cast_item_expiration_box->setEnabled(cast_status!=RDPodcast::StatusExpired); - label->setEnabled(cast_status!=RDPodcast::StatusExpired); + cast_item_expiration_box_label-> + setEnabled(cast_status!=RDPodcast::StatusExpired); - cast_item_expiration_edit=new QDateEdit(this); - cast_item_expiration_edit->setGeometry(135,cast_ypos,95,20); + cast_item_expiration_edit=new QDateTimeEdit(this); + cast_item_expiration_edit->setDisplayFormat("MM/dd/yyyy hh:mm:ss"); cast_item_expiration_label= - new QLabel(cast_item_expiration_edit,tr("Expires On:"),this); - cast_item_expiration_label->setGeometry(20,cast_ypos,110,20); + new QLabel(cast_item_expiration_edit,tr("at"),this); cast_item_expiration_label->setFont(labelFont()); - cast_item_expiration_label-> - setAlignment(Qt::AlignRight|Qt::AlignVCenter); + cast_item_expiration_label->setAlignment(Qt::AlignCenter); cast_item_expiration_button=new QPushButton(this); - cast_item_expiration_button->setGeometry(240,cast_ypos,50,20); cast_item_expiration_button->setFont(subButtonFont()); - cast_item_expiration_button->setText(tr("&Select")); + cast_item_expiration_button->setText(tr("&Select Date")); connect(cast_item_expiration_button,SIGNAL(clicked()), this,SLOT(expirationSelectData())); - cast_ypos+=27; cast_item_expiration_edit->setEnabled(cast_status!=RDPodcast::StatusExpired); cast_item_expiration_label-> setEnabled(cast_status!=RDPodcast::StatusExpired); cast_item_expiration_button-> setEnabled(cast_status!=RDPodcast::StatusExpired); - // - // Cast Status - // - cast_item_status_group=new QButtonGroup(this); - cast_item_status_group->setExclusive(true); - // cast_item_status_group->hide(); - - QRadioButton *rbutton=new QRadioButton(this); - rbutton->setGeometry(140,cast_ypos,15,15); - cast_item_status_group->addButton(rbutton,0); - label=new QLabel(rbutton,tr("Hold"),this); - label->setFont(subButtonFont()); - label->setGeometry(160,cast_ypos,30,15); - label->setAlignment(Qt::AlignVCenter|Qt::AlignLeft); - rbutton->setChecked(true); - label->setEnabled(cast_status!=RDPodcast::StatusExpired); - rbutton->setEnabled(cast_status!=RDPodcast::StatusExpired); - - rbutton=new QRadioButton(this); - rbutton->setGeometry(210,cast_ypos,15,15); - cast_item_status_group->addButton(rbutton,1); - label=new QLabel(rbutton,tr("Active"),this); - label->setFont(subLabelFont()); - label->setGeometry(230,cast_ypos,80,15); - label->setAlignment(Qt::AlignVCenter|Qt::AlignLeft); - label->setEnabled(cast_status!=RDPodcast::StatusExpired); - label=new QLabel(tr("Posting Status:"),this); - label->setGeometry(20,cast_ypos-1,110,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - label->setEnabled(cast_status!=RDPodcast::StatusExpired); - rbutton->setEnabled(cast_status!=RDPodcast::StatusExpired); - - // - // Report Button - // - button=new QPushButton(this); - button->setGeometry(10,sizeHint().height()-60,80,50); - button->setFont(buttonFont()); - button->setText(tr("Episode\n&Report")); - connect(button,SIGNAL(clicked()),this,SLOT(reportData())); - // // Ok Button // - button=new QPushButton(this); - button->setGeometry(sizeHint().width()-180,sizeHint().height()-60,80,50); - button->setDefault(true); - button->setFont(buttonFont()); - button->setText(tr("&OK")); - connect(button,SIGNAL(clicked()),this,SLOT(okData())); + cast_ok_button=new QPushButton(this); + cast_ok_button->setDefault(true); + cast_ok_button->setFont(buttonFont()); + cast_ok_button->setText(tr("&OK")); + connect(cast_ok_button,SIGNAL(clicked()),this,SLOT(okData())); // // Cancel Button // - button=new QPushButton(this); - button->setGeometry(sizeHint().width()-90,sizeHint().height()-60, - 80,50); - button->setFont(buttonFont()); - button->setText(tr("&Cancel")); - connect(button,SIGNAL(clicked()),this,SLOT(cancelData())); + cast_cancel_button=new QPushButton(this); + cast_cancel_button->setFont(buttonFont()); + cast_cancel_button->setText(tr("&Cancel")); + connect(cast_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); + //******** END *********** // // Populate Values // - cast_item_medialink_edit-> - setText(cast_feed->audioUrl(cast_feed->mediaLinkMode(), - "[web-hostname]",cast_cast->id())); + setWindowTitle("RDCastManager - "+tr("Editing Item")+ + +" [Cast ID: "+QString().sprintf("%u",cast_cast->id())+"]"); cast_item_title_edit->setText(cast_cast->itemTitle()); cast_item_author_edit->setText(cast_cast->itemAuthor()); - cast_item_origin_edit->setText(RDUtcToLocal(cast_cast->originDateTime()). - toString("MM/dd/yyyy - hh:mm:ss")); + if(cast_cast->originLoginName().isEmpty()) { + cast_item_origin_edit-> + setText(tr("unknown")+" "+tr("at")+" "+ + cast_cast->originDateTime().toString("MM/dd/yyyy - hh:mm:ss")); + } + else { + cast_item_origin_edit-> + setText(cast_cast->originLoginName()+" "+tr("on")+" "+ + cast_cast->originStation()+" "+tr("at")+" "+ + cast_cast->originDateTime().toString("MM/dd/yyyy - hh:mm:ss")); + } + cast_item_category_edit->setText(cast_cast->itemCategory()); + cast_item_category_label-> + setVisible(rda->rssSchemas()-> + supportsItemCategories(cast_feed->rssSchema())); + cast_item_category_edit-> + setVisible(rda->rssSchemas()-> + supportsItemCategories(cast_feed->rssSchema())); cast_item_link_edit->setText(cast_cast->itemLink()); - cast_item_sourcetext_edit->setText(cast_cast->itemSourceText()); - cast_item_sourceurl_edit->setText(cast_cast->itemSourceUrl()); cast_item_description_edit->setText(cast_cast->itemDescription()); + cast_item_explicit_check->setChecked(cast_cast->itemExplicit()); + cast_item_image_box->setCategoryId(cast_feed->id()); + cast_item_image_box->setCurrentImageId(cast_cast->itemImageId()); cast_item_comments_edit->setText(cast_cast->itemComments()); - cast_item_effective_edit-> - setDateTime(RDUtcToLocal(cast_cast->effectiveDateTime())); - if(cast_cast->shelfLife()>0) { + cast_item_effective_edit->setDateTime(cast_cast->effectiveDateTime()); + if(!cast_cast->expirationDateTime().isNull()) { cast_item_expiration_box->setCurrentItem(1); } + cast_item_expiration_edit->setDateTime(cast_cast->expirationDateTime()); cast_item_expiration_edit-> - setDate(RDUtcToLocal(cast_cast->originDateTime()).date(). - addDays(cast_cast->shelfLife())); - expirationSelectedData(cast_item_expiration_box->currentItem()); + setEnabled(cast_item_expiration_box->currentItem()); + cast_item_expiration_button-> + setEnabled(cast_item_expiration_box->currentItem()); + cast_item_expiration_label-> + setEnabled(cast_item_expiration_box->currentItem()); + switch(cast_status) { case RDPodcast::StatusActive: - cast_item_status_group->button(1)->setChecked(true); + cast_active_check->setChecked(true); break; case RDPodcast::StatusPending: - cast_item_status_group->button(0)->setChecked(true); + cast_active_check->setChecked(false); break; case RDPodcast::StatusExpired: - cast_item_status_group->button(0)->setDisabled(true); - cast_item_status_group->button(1)->setDisabled(true); + cast_active_check->setDisabled(true); + cast_active_label->setDisabled(true); break; } // // Fix the Window Size // - setMinimumWidth(sizeHint().width()); - setMaximumWidth(sizeHint().width()); - setMinimumHeight(sizeHint().height()); - setMaximumHeight(sizeHint().height()); + setMinimumSize(sizeHint()); } @@ -350,7 +280,7 @@ EditCast::~EditCast() QSize EditCast::sizeHint() const { - return QSize(640,cast_ypos+92); + return QSize(640,430); } @@ -360,28 +290,29 @@ QSizePolicy EditCast::sizePolicy() const } +void EditCast::effectiveSelectData() +{ + QDate current_date=QDate::currentDate(); + QDate date=cast_item_effective_edit->date(); + + RDDateDialog *dd= + new RDDateDialog(current_date.year(),current_date.year()+10,this); + if(dd->exec(&date)==0) { + cast_item_effective_edit->setDate(date); + } + delete dd; +} + + void EditCast::expirationSelectedData(int state) { state=state&&(cast_status!=RDPodcast::StatusExpired); cast_item_expiration_edit->setEnabled(state); cast_item_expiration_button->setEnabled(state); cast_item_expiration_label->setEnabled(state); -} - - -void EditCast::effectiveSelectData() -{ - QDate current_date=QDate::currentDate(); - QDateTime datetime=cast_item_effective_edit->dateTime(); - QDate date=datetime.date(); - - RDDateDialog *dd= - new RDDateDialog(current_date.year()-5,current_date.year()+5,this); - if(dd->exec(&date)==0) { - datetime.setDate(date); - cast_item_effective_edit->setDateTime(datetime); + if(state) { + cast_item_expiration_edit->setDate(QDate::currentDate().addDays(1)); } - delete dd; } @@ -389,9 +320,9 @@ void EditCast::expirationSelectData() { QDate current_date=QDate::currentDate(); QDate date=cast_item_expiration_edit->date(); - + RDDateDialog *dd= - new RDDateDialog(current_date.year(),current_date.year()+10,this); + new RDDateDialog(1970,current_date.year()+10,this); if(dd->exec(&date)==0) { cast_item_expiration_edit->setDate(date); } @@ -399,52 +330,60 @@ void EditCast::expirationSelectData() } -void EditCast::reportData() -{ - PickReportDates *rd=new PickReportDates(cast_cast->feedId(),cast_cast->id()); - rd->exec(); - delete rd; -} - - void EditCast::okData() { + QString err_msg; + + // + // Sanity Checks + // + if(cast_item_expiration_box->currentItem()) { + if(cast_item_effective_edit->dateTime()> + cast_item_expiration_edit->dateTime()) { + QMessageBox::warning(this,"RDCastManager - "+tr("Error"), + tr("Item expiration cannot be prior to Air Date/Time!")); + return; + } + if(cast_item_expiration_edit->dateTime()setItemTitle(cast_item_title_edit->text()); cast_cast->setItemAuthor(cast_item_author_edit->text()); cast_cast->setItemCategory(cast_item_category_edit->text()); cast_cast->setItemLink(cast_item_link_edit->text()); - cast_cast->setItemSourceText(cast_item_sourcetext_edit->text()); - cast_cast->setItemSourceUrl(cast_item_sourceurl_edit->text()); cast_cast->setItemDescription(cast_item_description_edit->text()); + cast_cast->setItemExplicit(cast_item_explicit_check->isChecked()); + cast_cast->setItemImageId(cast_item_image_box->currentImageId()); cast_cast->setItemComments(cast_item_comments_edit->text()); - cast_cast-> - setEffectiveDateTime(RDLocalToUtc(cast_item_effective_edit->dateTime())); - if(cast_item_status_group->button(0)->isEnabled()) { + cast_cast->setEffectiveDateTime(cast_item_effective_edit->dateTime()); + if(cast_active_check->isEnabled()) { if(cast_item_expiration_box->currentItem()) { - int shelf_life=RDUtcToLocal(cast_cast->originDateTime()).date(). + int shelf_life=cast_cast->originDateTime().date(). daysTo(cast_item_expiration_edit->date()); if(shelf_life<1) { shelf_life=1; } - cast_cast->setShelfLife(shelf_life); + cast_cast->setExpirationDateTime(cast_item_expiration_edit->dateTime()); } else { - cast_cast->setShelfLife(0); + cast_cast->setExpirationDateTime(QDateTime()); } - switch(cast_item_status_group->checkedId()) { - case 0: - cast_cast->setStatus(RDPodcast::StatusPending); - break; - - case 1: - cast_cast->setStatus(RDPodcast::StatusActive); - break; + if(cast_active_check->isChecked()) { + cast_cast->setStatus(RDPodcast::StatusActive); + } + else { + cast_cast->setStatus(RDPodcast::StatusPending); } } - cast_feed-> - setLastBuildDateTime(RDLocalToUtc(QDateTime(QDate::currentDate(), - QTime::currentTime()))); + if(!cast_feed->postXmlConditional("RDCastManager",this)) { + return; + } + done(0); } @@ -453,3 +392,110 @@ void EditCast::cancelData() { done(-1); } + + +void EditCast::resizeEvent(QResizeEvent *e) +{ + int ypos=0; + int w=size().width(); + int h=size().height(); + + ypos=2; + + // + // Posting Status + // + cast_active_check->setGeometry(10,ypos+2,15,15); + cast_active_label->setGeometry(30,ypos,300,20); + + // + // Posted At + // + cast_item_origin_label->setGeometry(w-445,ypos,110,20); + cast_item_origin_edit->setGeometry(w-330,ypos,320,20); + ypos+=27; + + // + // Title + // + cast_item_title_edit->setGeometry(135,ypos,size().width()-145,20); + cast_item_title_label->setGeometry(20,ypos,110,20); + ypos+=22; + + // + // Author E-Mail + // + cast_item_author_edit->setGeometry(135,ypos,size().width()-145,20); + cast_item_author_label->setGeometry(20,ypos,110,20); + ypos+=22; + + // + // Category + // + if(rda->rssSchemas()->supportsItemCategories(cast_feed->rssSchema())) { + cast_item_category_edit->setGeometry(135,ypos,size().width()-145,20); + cast_item_category_label->setGeometry(20,ypos,110,20); + ypos+=22; + } + + // + // Link URL + // + if(rda->rssSchemas()->supportsItemLinks(cast_schema)) { + cast_item_link_edit->setGeometry(135,ypos,size().width()-145,20); + cast_item_link_label->setGeometry(20,ypos,110,20); + ypos+=22; + } + + // + // Comments URL + // + if(rda->rssSchemas()->supportsItemComments(cast_schema)) { + cast_item_comments_edit->setGeometry(135,ypos,size().width()-145,20); + cast_item_comments_label->setGeometry(10,ypos,120,20); + ypos+=22; + } + + // + // Description + // + cast_item_description_label->setGeometry(20,ypos,110,20); + cast_item_description_edit-> + setGeometry(135,ypos,size().width()-145,h-ypos-221); + + // + // Explicit Content + // + cast_item_explicit_check->setGeometry(140,h-216,15,15); + cast_item_explicit_label->setGeometry(160,h-219,size().width()-145,20); + + // + // Image + // + cast_item_image_label->setGeometry(20,h-197,110,20); + cast_item_image_box->setIconSize(QSize(36,36)); + cast_item_image_box->setGeometry(135,h-197,300,38); + + // + // Air Date/Time + // + cast_item_effective_label->setGeometry(20,h-154,110,20); + cast_item_effective_edit->setGeometry(135,h-154,150,20); + cast_item_effective_button->setGeometry(295,h-156,75,24); + + // + // Cast Expiration + // + cast_item_expiration_box_label->setGeometry(20,h-126,110,20); + cast_item_expiration_box->setGeometry(135,h-126,50,20); + + cast_item_expiration_label->setGeometry(190,h-126,20,20); + cast_item_expiration_edit->setGeometry(215,h-126,150,20); + cast_item_expiration_button->setGeometry(375,h-128,75,24); + + // + // Buttons + // + cast_ok_button->setGeometry(size().width()-180,size().height()-60,80,50); + cast_cancel_button->setGeometry(size().width()-90,size().height()-60,80,50); +} diff --git a/rdcastmanager/edit_cast.h b/rdcastmanager/edit_cast.h index e293ac1a..0527030e 100644 --- a/rdcastmanager/edit_cast.h +++ b/rdcastmanager/edit_cast.h @@ -2,7 +2,7 @@ // // Edit a Rivendell Cast // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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,17 +21,19 @@ #ifndef EDIT_CAST_H #define EDIT_CAST_H - #include +#include #include #include #include #include #include +#include #include #include #include +#include #include class EditCast : public RDDialog @@ -44,33 +46,49 @@ class EditCast : public RDDialog QSizePolicy sizePolicy() const; private slots: - void expirationSelectedData(int state); void effectiveSelectData(); + void expirationSelectedData(int state); void expirationSelectData(); - void reportData(); void okData(); void cancelData(); + + protected: + void resizeEvent(QResizeEvent *e); private: RDFeed *cast_feed; RDPodcast *cast_cast; - QLineEdit *cast_item_medialink_edit; - QLineEdit *cast_item_title_edit; - QLineEdit *cast_item_author_edit; + RDRssSchemas::RssSchema cast_schema; + QLabel *cast_item_origin_label; QLineEdit *cast_item_origin_edit; - QTextEdit *cast_item_description_edit; + QLabel *cast_item_title_label; + QLineEdit *cast_item_title_edit; + QLabel *cast_item_author_label; + QLineEdit *cast_item_author_edit; + QLabel *cast_item_category_label; QLineEdit *cast_item_category_edit; - QLineEdit *cast_item_comments_edit; - QLineEdit *cast_item_sourcetext_edit; - QLineEdit *cast_item_sourceurl_edit; + QLabel *cast_item_link_label; QLineEdit *cast_item_link_edit; + QLabel *cast_item_description_label; + QTextEdit *cast_item_description_edit; + QCheckBox *cast_item_explicit_check; + QLabel *cast_item_explicit_label; + QLabel *cast_item_image_label; + RDImagePickerBox *cast_item_image_box; + QLabel *cast_item_comments_label; + QLineEdit *cast_item_comments_edit; + QLabel *cast_item_expiration_box_label; QComboBox *cast_item_expiration_box; QLabel *cast_item_expiration_label; + QCheckBox *cast_active_check; + QLabel *cast_active_label; QPushButton *cast_item_expiration_button; - QDateEdit *cast_item_expiration_edit; + QDateTimeEdit *cast_item_expiration_edit; + QLabel *cast_item_effective_label; QDateTimeEdit *cast_item_effective_edit; - QButtonGroup *cast_item_status_group; - int cast_ypos; + QPushButton *cast_item_effective_button; + QPushButton *cast_ok_button; + QPushButton *cast_cancel_button; RDPodcast::Status cast_status; }; diff --git a/rdcastmanager/list_casts.cpp b/rdcastmanager/list_casts.cpp index 5fa7ae23..4d5563c5 100644 --- a/rdcastmanager/list_casts.cpp +++ b/rdcastmanager/list_casts.cpp @@ -2,7 +2,7 @@ // // List Rivendell Casts // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -26,40 +26,50 @@ #include #include #include -#include +#include +#include #include "edit_cast.h" #include "globals.h" #include "list_casts.h" -#include "pick_report_dates.h" // // Icons // -#include "../icons/redball.xpm" +#include "../icons/blueball.xpm" #include "../icons/greenball.xpm" +#include "../icons/redball.xpm" #include "../icons/whiteball.xpm" +#include "../icons/rdcastmanager-32x32.xpm" -ListCasts::ListCasts(unsigned feed_id,QWidget *parent) +ListCasts::ListCasts(unsigned feed_id,bool is_super,QWidget *parent) : RDDialog(parent) { - setModal(true); - list_feed_id=feed_id; + list_is_superfeed=is_super; + + setWindowTitle("RDCastManager - "+tr("Podcast Item List")+ + " [Feed ID: "+QString().sprintf("%u",feed_id)+"]"); // // Fix the Window Size // setMinimumSize(sizeHint()); - setWindowTitle("RDCastManager - "+tr("Podcast List")); - // // Create Icons // + list_blueball_map=new QPixmap(blueball_xpm); list_greenball_map=new QPixmap(greenball_xpm); list_redball_map=new QPixmap(redball_xpm); list_whiteball_map=new QPixmap(whiteball_xpm); + list_rdcastmanager_32x32_map=new QPixmap(rdcastmanager_32x32_xpm); + + // + // Notifications + // + connect(rda->ripc(),SIGNAL(notificationReceived(RDNotification *)), + this,SLOT(notificationReceivedData(RDNotification *))); // // The Feed @@ -67,14 +77,19 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) list_feed=new RDFeed(feed_id,rda->config(),this); // - // Progress Dialog + // Dialogs // + list_render_dialog=new RenderDialog(this); + list_progress_dialog= - new QProgressDialog(tr("Uploading Audio..."),tr("Cancel"),0,list_feed->totalPostSteps(),this); + new QProgressDialog(tr("Uploading Audio..."),tr("Cancel"),0,1,this); list_progress_dialog->setWindowTitle("RDCastManager - "+tr("Progress")); + list_progress_dialog->setCancelButton(NULL); list_progress_dialog->setMinimumDuration(0); connect(list_feed,SIGNAL(postProgressChanged(int)), this,SLOT(postProgressChangedData(int))); + connect(list_feed,SIGNAL(postProgressRangeChanged(int,int)), + list_progress_dialog,SLOT(setRange(int,int))); // // Filter @@ -87,23 +102,12 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) connect(list_filter_edit,SIGNAL(textChanged(const QString &)), this,SLOT(filterChangedData(const QString &))); - // - // Unexpired Check Box - // - list_unexpired_check=new QCheckBox(this); - list_unexpired_label= - new QLabel(list_unexpired_check,tr("Only Show Unexpired Casts"),this); - list_unexpired_label->setFont(labelFont()); - list_unexpired_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - connect(list_unexpired_check,SIGNAL(toggled(bool)), - this,SLOT(notexpiredToggledData(bool))); - // // Active Check Box // list_active_check=new QCheckBox(this); list_active_label= - new QLabel(list_active_check,tr("Only Show Active Casts"),this); + new QLabel(list_active_check,tr("Only Show Active Items"),this); list_active_label->setFont(labelFont()); list_active_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); connect(list_active_check,SIGNAL(toggled(bool)), @@ -112,6 +116,10 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) // // Group List // + list_casts_label= + new QLabel(list_feed->channelTitle(),this); + list_casts_label->setFont(bigLabelFont()); + list_casts_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); list_casts_view=new RDListView(this); list_casts_view->setAllColumnsShowFocus(true); list_casts_view->setItemMargin(5); @@ -119,22 +127,30 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) list_casts_view->setColumnAlignment(0,Qt::AlignCenter); list_casts_view->addColumn(tr("Title")); list_casts_view->setColumnAlignment(1,Qt::AlignLeft); - list_casts_view->addColumn(tr("Origin")); - list_casts_view->setColumnAlignment(2,Qt::AlignLeft); - list_casts_view->addColumn(tr("Expires")); - list_casts_view->setColumnAlignment(3,Qt::AlignCenter); + + list_casts_view->addColumn(tr("Status")); + list_casts_view->setColumnAlignment(2,Qt::AlignCenter); + + list_casts_view->addColumn(tr("Start")); + list_casts_view->setColumnAlignment(3,Qt::AlignLeft); + list_casts_view->addColumn(tr("Expiration")); + list_casts_view->setColumnAlignment(4,Qt::AlignCenter); list_casts_view->addColumn(tr("Length")); - list_casts_view->setColumnAlignment(4,Qt::AlignRight); - list_casts_view->addColumn(tr("Description")); - list_casts_view->setColumnAlignment(5,Qt::AlignLeft); + list_casts_view->setColumnAlignment(5,Qt::AlignRight); + list_casts_view->addColumn(tr("Feed")); + list_casts_view->setColumnAlignment(6,Qt::AlignLeft); list_casts_view->addColumn(tr("Category")); - list_casts_view->setColumnAlignment(6,Qt::AlignCenter); - list_casts_view->addColumn(tr("Link")); list_casts_view->setColumnAlignment(7,Qt::AlignCenter); - connect(list_casts_view, - SIGNAL(doubleClicked(Q3ListViewItem *,const QPoint &,int)), - this, - SLOT(doubleClickedData(Q3ListViewItem *,const QPoint &,int))); + list_casts_view->addColumn(tr("Posted By")); + list_casts_view->setColumnAlignment(8,Qt::AlignLeft); + list_casts_view->addColumn(tr("SHA1")); + list_casts_view->setColumnAlignment(9,Qt::AlignLeft); + if(!list_feed->isSuperfeed()) { + connect(list_casts_view, + SIGNAL(doubleClicked(Q3ListViewItem *,const QPoint &,int)), + this, + SLOT(doubleClickedData(Q3ListViewItem *,const QPoint &,int))); + } // // Post Cart Button @@ -142,6 +158,7 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) list_cart_button=new QPushButton(this); list_cart_button->setFont(buttonFont()); list_cart_button->setText(tr("Post From\nCar&t/Cut")); + list_cart_button->setDisabled(list_feed->isSuperfeed()); connect(list_cart_button,SIGNAL(clicked()),this,SLOT(addCartData())); // @@ -150,8 +167,18 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) list_file_button=new QPushButton(this); list_file_button->setFont(buttonFont()); list_file_button->setText(tr("Post From\n&File")); + list_file_button->setDisabled(list_feed->isSuperfeed()); connect(list_file_button,SIGNAL(clicked()),this,SLOT(addFileData())); + // + // Post Log Button + // + list_log_button=new QPushButton(this); + list_log_button->setFont(buttonFont()); + list_log_button->setText(tr("Post From\nLog")); + list_log_button->setDisabled(list_feed->isSuperfeed()); + connect(list_log_button,SIGNAL(clicked()),this,SLOT(addLogData())); + // // Edit Button // @@ -168,14 +195,6 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) list_delete_button->setText(tr("&Delete")); connect(list_delete_button,SIGNAL(clicked()),this,SLOT(deleteData())); - // - // Report Button - // - list_report_button=new QPushButton(this); - list_report_button->setFont(buttonFont()); - list_report_button->setText(tr("Subscription\n&Report")); - connect(list_report_button,SIGNAL(clicked()),this,SLOT(reportData())); - // // Close Button // @@ -186,14 +205,16 @@ ListCasts::ListCasts(unsigned feed_id,QWidget *parent) connect(list_close_button,SIGNAL(clicked()),this,SLOT(closeData())); RefreshList(); - GetEncoderId(); + connect(rda->ripc(),SIGNAL(userChanged()),this,SLOT(userChangedData())); + userChangedData(); } ListCasts::~ListCasts() { + delete list_render_dialog; delete list_progress_dialog; delete list_feed; } @@ -223,10 +244,10 @@ void ListCasts::addCartData() } delete cd; RDFeed::Error err; - unsigned cast_id=list_feed->postCut(rda->user(),rda->station(),cutname,&err, - rda->config()->logXloadDebugData(),rda->config()); + unsigned cast_id=list_feed->postCut(cutname,&err); if(err!=RDFeed::ErrorOk) { - QMessageBox::warning(this,tr("Posting Error"),RDFeed::errorString(err)); + QMessageBox::warning(this,"RDCastManager - "+tr("Posting Error"), + RDFeed::errorString(err)); return; } EditCast *edit_cast=new EditCast(cast_id,this); @@ -237,21 +258,29 @@ void ListCasts::addCartData() list_casts_view->setSelected(item,true); list_casts_view->ensureItemVisible(item); delete edit_cast; + + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction, + list_feed->keyName()); + rda->ripc()->sendNotification(RDNotification::FeedItemType, + RDNotification::AddAction,cast_id); } void ListCasts::addFileData() { QString srcfile= - QFileDialog::getOpenFileName(this,"RDCastManager","",RD_AUDIO_FILE_FILTER); + QFileDialog::getOpenFileName(this,"RDCastManager - "+ + tr("Select Audio File"),"", + RD_AUDIO_FILE_FILTER); if(srcfile.isNull()) { return; } RDFeed::Error err; - unsigned cast_id=list_feed->postFile(rda->station(),srcfile,&err, - rda->config()->logXloadDebugData(),rda->config()); + unsigned cast_id=list_feed->postFile(srcfile,&err); if(err!=RDFeed::ErrorOk) { - QMessageBox::warning(this,tr("Posting Error"),RDFeed::errorString(err)); + QMessageBox::warning(this,"RDCastManager - "+tr("Posting Error"), + RDFeed::errorString(err)); return; } EditCast *edit_cast=new EditCast(cast_id,this); @@ -262,6 +291,62 @@ void ListCasts::addFileData() list_casts_view->setSelected(item,true); list_casts_view->ensureItemVisible(item); delete edit_cast; + + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction, + list_feed->keyName()); + rda->ripc()->sendNotification(RDNotification::FeedItemType, + RDNotification::AddAction,cast_id); +} + + +void ListCasts::addLogData() +{ + QString logname; + RDFeed::Error err=RDFeed::ErrorOk; + unsigned cast_id=0; + + RDListLogs *d=new RDListLogs(&logname,RDLogFilter::UserFilter,this); + if(d->exec()) { + RDLogEvent *log=new RDLogEvent(logname); + log->load(); + QTime start_time; + bool ignore_stops=true; + int start_line=0; + int end_line=log->size()-1; + + if(list_render_dialog->exec(log,&start_time,&ignore_stops, + &start_line,&end_line)) { + if((cast_id=list_feed->postLog(logname,start_time,ignore_stops, + start_line,end_line,&err))!=0) { + EditCast *cast=new EditCast(cast_id,this); + cast->exec(); + RDListViewItem *item=new RDListViewItem(list_casts_view); + item->setId(cast_id); + RefreshItem(item); + list_casts_view->setSelected(item,true); + list_casts_view->ensureItemVisible(item); + delete cast; + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction, + list_feed->keyName()); + rda->ripc()->sendNotification(RDNotification::FeedItemType, + RDNotification::AddAction,cast_id); + } + else { + QMessageBox::warning(this,"RDCastManager - "+tr("Posting Error"), + RDFeed::errorString(err)); + delete d; + delete log; + return; + } + + delete log; + } + else { // Render dialog was canceled! + } + } + delete d; } @@ -274,6 +359,12 @@ void ListCasts::editData() EditCast *edit_cast=new EditCast(item->id(),this); if(edit_cast->exec()==0) { RefreshItem(item); + + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction, + list_feed->keyName()); + rda->ripc()->sendNotification(RDNotification::FeedItemType, + RDNotification::ModifyAction,item->id()); } delete edit_cast; } @@ -289,25 +380,27 @@ void ListCasts::deleteData() if(item==NULL) { return; } - if(QMessageBox::question(this,tr("Delete Podcast"), - tr("Are you sure you want to delete this podcast?"), + unsigned cast_id=item->id(); + if(QMessageBox::question(this,"RDCastManager - "+tr("Delete Item"), + tr("Are you sure you want to delete this item?"), QMessageBox::Yes,QMessageBox::No)== QMessageBox::No) { return; } QProgressDialog *pd= - new QProgressDialog(tr("Deleting Podcast..."),"Cancel",0,2,this); - pd->setCaption(tr("Progress")); + new QProgressDialog(tr("Deleting Item..."),"Cancel",0,2,this); + pd->setWindowTitle(tr("Progress")); + pd->setCancelButton(NULL); pd->setMinimumDuration(0); pd->setValue(0); qApp->processEvents(); sleep(1); qApp->processEvents(); RDPodcast *cast=new RDPodcast(rda->config(),item->id()); - if(!cast->removeAudio(list_feed,&err_text,rda->config()->logXloadDebugData())) { - if(QMessageBox::warning(this,tr("Remote Error"), - tr("Unable to delete remote audio!\n")+ + if(!cast->dropAudio(list_feed,&err_text,rda->config()->logXloadDebugData())) { + if(QMessageBox::warning(this,"RDCastManager - "+tr("Remote Error"), + tr("Unable to drop remote audio!\n")+ tr("The server said: \"")+err_text+"\".\n\n"+ tr("Continue deleting cast?"), QMessageBox::Yes,QMessageBox::No)==QMessageBox::No) { @@ -326,21 +419,21 @@ void ListCasts::deleteData() q=new RDSqlQuery(sql); delete q; - RDDeleteCastCount(list_feed_id,item->id()); + if(!list_feed->postXml()) { + QMessageBox::warning(this,"RDCastManager - "+tr("Remote Error"), + tr("Unable to update remote XML data!")); + } pd->reset(); delete pd; delete cast; delete item; -} - - -void ListCasts::reportData() -{ - PickReportDates *rd=new PickReportDates(list_feed_id,0); - rd->exec(); - delete rd; + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction, + list_feed->keyName()); + rda->ripc()->sendNotification(RDNotification::FeedItemType, + RDNotification::DeleteAction,cast_id); } @@ -353,10 +446,14 @@ void ListCasts::doubleClickedData(Q3ListViewItem *item,const QPoint &pt, void ListCasts::userChangedData() { - list_cart_button->setEnabled(rda->user()->addPodcast()&&(list_encoder_id>=0)); - list_file_button->setEnabled(rda->user()->addPodcast()&&(list_encoder_id>=0)); - list_edit_button->setEnabled(rda->user()->editPodcast()); - list_delete_button->setEnabled(rda->user()->deletePodcast()); + bool is_superfeed=list_feed->isSuperfeed(); + + list_cart_button->setEnabled(rda->user()->addPodcast()&&(!is_superfeed)); + list_file_button->setEnabled(rda->user()->addPodcast()&&(!is_superfeed)); + list_log_button->setEnabled(rda->user()->addPodcast()&&(!is_superfeed)); + list_edit_button->setEnabled(rda->user()->editPodcast()&&(!is_superfeed)); + list_delete_button-> + setEnabled(rda->user()->deletePodcast()&&(!is_superfeed)); } @@ -398,20 +495,70 @@ void ListCasts::resizeEvent(QResizeEvent *e) { list_filter_label->setGeometry(10,10,40,20); list_filter_edit->setGeometry(55,10,size().width()-65,20); - list_unexpired_check->setGeometry(55,35,15,15); - list_unexpired_label->setGeometry(75,33,200,20); - list_active_check->setGeometry(300,35,15,15); - list_active_label->setGeometry(320,33,200,20); - list_casts_view->setGeometry(10,54,size().width()-20,size().height()-124); + list_active_check->setGeometry(60,35,15,15); + list_active_label->setGeometry(80,33,200,20); + list_casts_label->setGeometry(15,57,size().width()-25,20); + list_casts_view->setGeometry(10,76,size().width()-20,size().height()-146); list_cart_button->setGeometry(10,size().height()-60,80,50); list_file_button->setGeometry(100,size().height()-60,80,50); - list_edit_button->setGeometry(190,size().height()-60,80,50); - list_delete_button->setGeometry(280,size().height()-60,80,50); - list_report_button->setGeometry(400,size().height()-60,110,50); + list_log_button->setGeometry(190,size().height()-60,80,50); + list_edit_button->setGeometry(300,size().height()-60,80,50); + list_delete_button->setGeometry(390,size().height()-60,80,50); list_close_button->setGeometry(size().width()-90,size().height()-60,80,50); } +void ListCasts::notificationReceivedData(RDNotification *notify) +{ + unsigned cast_id=0; + RDListViewItem *item=NULL; + RDPodcast *cast=NULL; + + if(notify->type()==RDNotification::FeedItemType) { + cast_id=notify->id().toUInt(); + switch(notify->action()) { + case RDNotification::AddAction: + cast=new RDPodcast(rda->config(),cast_id); + if(cast->keyName()==list_feed->keyName()) { + item=new RDListViewItem(list_casts_view); + item->setId(cast_id); + RefreshItem(item); + delete cast; + return; + } + delete cast; + break; + + case RDNotification::DeleteAction: + item=(RDListViewItem *)list_casts_view->firstChild(); + while(item!=NULL) { + if(item->id()==(int)cast_id) { + delete item; + return; + } + item=(RDListViewItem *)item->nextSibling(); + } + break; + + case RDNotification::ModifyAction: + item=(RDListViewItem *)list_casts_view->firstChild(); + while(item!=NULL) { + if(item->id()==(int)cast_id) { + RefreshItem(item); + } + item=(RDListViewItem *)item->nextSibling(); + } + break; + + case RDNotification::LastAction: + case RDNotification::NoAction: + break; + + } + } +} + + void ListCasts::RefreshList() { QString sql; @@ -420,8 +567,8 @@ void ListCasts::RefreshList() list_casts_view->clear(); sql=QString("select ID from PODCASTS ")+ - RDCastSearch(list_feed_id,list_filter_edit->text(), - list_unexpired_check->isChecked(), + RDCastSearch(list_feed->keyName(),list_is_superfeed, + list_filter_edit->text(), list_active_check->isChecked())+ " order by ORIGIN_DATETIME"; q=new RDSqlQuery(sql); @@ -439,66 +586,77 @@ void ListCasts::RefreshItem(RDListViewItem *item) QString sql; RDSqlQuery *q; - sql=QString().sprintf("select STATUS,ITEM_TITLE,ORIGIN_DATETIME,SHELF_LIFE,\ - AUDIO_TIME,ITEM_DESCRIPTION,ITEM_CATEGORY,ITEM_LINK \ - from PODCASTS where ID=%d",item->id()); + sql=QString("select ")+ + "PODCASTS.STATUS,"+ // 00 + "PODCASTS.ITEM_TITLE,"+ // 01 + "PODCASTS.EFFECTIVE_DATETIME,"+ // 02 + "PODCASTS.EXPIRATION_DATETIME,"+ // 03 + "PODCASTS.AUDIO_TIME,"+ // 04 + "PODCASTS.ITEM_DESCRIPTION,"+ // 05 + "FEEDS.KEY_NAME,"+ // 06 + "PODCASTS.ITEM_CATEGORY,"+ // 07 + "PODCASTS.ORIGIN_LOGIN_NAME,"+ // 08 + "PODCASTS.ORIGIN_STATION,"+ // 09 + "PODCASTS.ORIGIN_DATETIME,"+ // 10 + "PODCASTS.SHA1_HASH,"+ // 11 + "FEED_IMAGES.DATA "+ // 12 + "from PODCASTS left join FEEDS "+ + "on PODCASTS.FEED_ID=FEEDS.ID left join FEED_IMAGES "+ + "on PODCASTS.ITEM_IMAGE_ID=FEED_IMAGES.ID where "+ + QString().sprintf("PODCASTS.ID=%d",item->id()); q=new RDSqlQuery(sql); if(q->first()) { + if(q->value(12).isNull()) { + item->setPixmap(0,*list_rdcastmanager_32x32_map); + } + else { + QImage img=QImage::fromData(q->value(12).toByteArray()); + item->setPixmap(0,QPixmap::fromImage(img.scaled(32,32))); + } + item->setText(1,q->value(1).toString()); switch((RDPodcast::Status)q->value(0).toUInt()) { case RDPodcast::StatusActive: - item->setPixmap(0,*list_greenball_map); + if(q->value(2).toDateTime()<=QDateTime::currentDateTime()) { + item->setPixmap(2,*list_greenball_map); + } + else { + item->setPixmap(2,*list_blueball_map); + } break; case RDPodcast::StatusPending: - item->setPixmap(0,*list_redball_map); + item->setPixmap(2,*list_redball_map); break; case RDPodcast::StatusExpired: - item->setPixmap(0,*list_whiteball_map); + item->setPixmap(2,*list_whiteball_map); break; } - item->setText(1,q->value(1).toString()); - item->setText(2,RDUtcToLocal(q->value(2).toDateTime()). - toString("MM/dd/yyyy hh:mm:ss")); - if(q->value(3).toInt()==0) { - item->setText(3,tr("Never")); + item->setText(3,q->value(2).toDateTime().toString("MM/dd/yyyy hh:mm:ss")); + if(q->value(3).isNull()) { + item->setText(4,tr("Never")); } else { - item->setText(3,RDUtcToLocal(q->value(2).toDateTime()). - addDays(q->value(3).toInt()).toString("MM/dd/yyyy")); + item->setText(4,q->value(3).toDateTime().toString("MM/dd/yyyy hh:mm:ss")); } - item->setText(4,RDGetTimeLength(q->value(4).toInt(),false,false)); - item->setText(5,q->value(5).toString()); + item->setText(5,RDGetTimeLength(q->value(4).toInt(),false,false)); + item->setText(6,q->value(6).toString()); item->setText(7,q->value(7).toString()); - } - delete q; -} - - -void ListCasts::GetEncoderId() -{ - QString sql; - RDSqlQuery *q; - - list_encoder_id=-1; - RDFeed *feed=new RDFeed(list_feed_id,rda->config()); - int format=feed->uploadFormat(); - delete feed; - if((format>0)&&(format<100)) { // Built-in format - list_encoder_id=format; - return; - } - sql=QString().sprintf("select NAME from ENCODERS where ID=%d",format); - q=new RDSqlQuery(sql); - if(q->first()) { - sql=QString("select ID from ENCODERS where ")+ - "(NAME=\""+RDEscapeString(q->value(0).toString())+"\")&&"+ - "(STATION_NAME=\""+RDEscapeString(rda->station()->name())+"\")"; - delete q; - q=new RDSqlQuery(sql); - if(q->first()) { - list_encoder_id=q->value(0).toInt(); + if(q->value(8).isNull()) { + item->setText(8,tr("unknown")+" "+tr("at")+" "+ + q->value(10).toDateTime().toString("MM/dd/yyyy hh:mm:ss")); + } + else { + item->setText(8,q->value(8).toString()+" "+tr("on")+" "+ + q->value(9).toString()+" "+tr("at")+" "+ + q->value(10).toDateTime().toString("MM/dd/yyyy hh:mm:ss")); + } + if(q->value(11).toString().isEmpty()) { + item->setText(9,tr("[none]")); + } + else { + item->setText(9,q->value(11).toString()); } } delete q; diff --git a/rdcastmanager/list_casts.h b/rdcastmanager/list_casts.h index b6646c17..f7659924 100644 --- a/rdcastmanager/list_casts.h +++ b/rdcastmanager/list_casts.h @@ -2,7 +2,7 @@ // // List Rivendell Casts // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -28,11 +28,13 @@ #include #include +#include "render_dialog.h" + class ListCasts : public RDDialog { Q_OBJECT public: - ListCasts(unsigned feed_id,QWidget *parent=0); + ListCasts(unsigned feed_id,bool is_super,QWidget *parent=0); ~ListCasts(); QSize sizeHint() const; QSizePolicy sizePolicy() const; @@ -40,9 +42,9 @@ class ListCasts : public RDDialog private slots: void addCartData(); void addFileData(); + void addLogData(); void editData(); void deleteData(); - void reportData(); void doubleClickedData(Q3ListViewItem *item,const QPoint &pt,int col); void userChangedData(); void filterChangedData(const QString &str); @@ -50,6 +52,7 @@ class ListCasts : public RDDialog void activeToggledData(bool state); void postProgressChangedData(int step); void closeData(); + void notificationReceivedData(RDNotification *notify); protected: void resizeEvent(QResizeEvent *e); @@ -58,26 +61,28 @@ class ListCasts : public RDDialog void RefreshList(); void RefreshItem(RDListViewItem *item); void GetEncoderId(); + QLabel *list_casts_label; RDListView *list_casts_view; QPushButton *list_cart_button; QPushButton *list_file_button; + QPushButton *list_log_button; QPushButton *list_edit_button; QPushButton *list_delete_button; - QPushButton *list_report_button; QPushButton *list_close_button; - QPixmap *list_redball_map; + QPixmap *list_blueball_map; QPixmap *list_greenball_map; + QPixmap *list_redball_map; QPixmap *list_whiteball_map; + QPixmap *list_rdcastmanager_32x32_map; unsigned list_feed_id; - int list_encoder_id; QLabel *list_filter_label; QLineEdit *list_filter_edit; - QLabel *list_unexpired_label; - QCheckBox *list_unexpired_check; QLabel *list_active_label; QCheckBox *list_active_check; QProgressDialog *list_progress_dialog; + RenderDialog *list_render_dialog; RDFeed *list_feed; + bool list_is_superfeed; }; diff --git a/rdcastmanager/logdialog.cpp b/rdcastmanager/logdialog.cpp new file mode 100644 index 00000000..15867354 --- /dev/null +++ b/rdcastmanager/logdialog.cpp @@ -0,0 +1,119 @@ +// logdialog.cpp +// +// Real-only lister dialogs for Rivendell logs +// +// (C) Copyright 2020 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 + +#include "logdialog.h" + +LogDialog::LogDialog(QWidget *parent) + : RDDialog(parent) +{ + setWindowTitle("RDCastManager - "+tr("List Log Events")); + + d_log_view=new QTableView(this); + d_log_view->setSelectionBehavior(QAbstractItemView::SelectRows); + d_log_view->setSelectionMode(QAbstractItemView::ContiguousSelection); + d_log_view->setShowGrid(false); + d_log_view->setSortingEnabled(false); + d_log_view->setWordWrap(false); + d_log_model=new LogModel(this); + d_log_model->setFont(font()); + d_log_view->setModel(d_log_model); + + d_ok_button=new QPushButton(tr("OK"),this); + d_ok_button->setFont(buttonFont()); + connect(d_ok_button,SIGNAL(clicked()),this,SLOT(okData())); + + d_cancel_button=new QPushButton(tr("Cancel"),this); + d_cancel_button->setFont(buttonFont()); + connect(d_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); +} + + +LogDialog::~LogDialog() +{ +} + + +QSize LogDialog::sizeHint() const +{ + return QSize(640,480); +} + + +int LogDialog::exec(RDLogEvent *log,int *start_line,int *end_line) +{ + QItemSelection item_sel; + QItemSelectionModel *sel=NULL; + + d_log_model->setLogEvent(log); + d_log_view->resizeColumnsToContents(); + + sel=d_log_view->selectionModel(); + sel->reset(); + item_sel.select(d_log_model->index(*start_line,0), + d_log_model->index(*end_line,d_log_model->columnCount()-1)); + sel->select(item_sel,QItemSelectionModel::SelectCurrent); + + d_log=log; + d_start_line=start_line; + d_end_line=end_line; + + return QDialog::exec(); +} + + +void LogDialog::okData() +{ + QModelIndexList list=d_log_view->selectionModel()->selectedRows(); + if(list.size()==0) { + QMessageBox::information(this,"RDCastManager - "+tr("List Log"), + tr("At least one log event must be selected!")); + return; + } + *d_start_line=list.first().row(); + *d_end_line=list.last().row(); + d_log_model->clearLogEvent(); + + done(true); +} + + +void LogDialog::cancelData() +{ + d_log_model->clearLogEvent(); + + done(false); +} + + +void LogDialog::resizeEvent(QResizeEvent *e) +{ + int w=size().width(); + int h=size().height(); + + d_log_view->setGeometry(10,2,w-20,h-72); + + d_ok_button->setGeometry(w-180,h-60,80,50); + d_cancel_button->setGeometry(w-90,h-60,80,50); +} diff --git a/rdcastmanager/logdialog.h b/rdcastmanager/logdialog.h new file mode 100644 index 00000000..65a56b50 --- /dev/null +++ b/rdcastmanager/logdialog.h @@ -0,0 +1,61 @@ +// logdialog.h +// +// Real-only lister dialogs for Rivendell logs +// +// (C) Copyright 2020 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. +// + +#ifndef LOGDIALOG_H +#define LOGDIALOG_H + +#include +#include + +#include +#include + +#include "logmodel.h" + +class LogDialog : public RDDialog +{ + Q_OBJECT + public: + LogDialog(QWidget *parent=0); + ~LogDialog(); + QSize sizeHint() const; + + public slots: + int exec(RDLogEvent *log,int *start_line,int *end_line); + + private slots: + void okData(); + void cancelData(); + + protected: + void resizeEvent(QResizeEvent *e); + + private: + QTableView *d_log_view; + QPushButton *d_ok_button; + QPushButton *d_cancel_button; + LogModel *d_log_model; + RDLogEvent *d_log; + int *d_start_line; + int *d_end_line; +}; + + +#endif // LIST_CASTS_H diff --git a/rdcastmanager/logmodel.cpp b/rdcastmanager/logmodel.cpp new file mode 100644 index 00000000..f592a790 --- /dev/null +++ b/rdcastmanager/logmodel.cpp @@ -0,0 +1,246 @@ +// logmodel.cpp +// +// Read-only data model for Rivendell logs +// +// (C) Copyright 2020 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 "logmodel.h" + +LogModel::LogModel(QObject *parent) + : QAbstractTableModel(parent) +{ + d_log=NULL; + d_fms=NULL; + d_bold_fms=NULL; + d_log_icons=new RDLogIcons(); + + // + // Column Attributes + // + unsigned left=Qt::AlignLeft|Qt::AlignVCenter; + unsigned center=Qt::AlignCenter; + unsigned right=Qt::AlignRight|Qt::AlignVCenter; + + d_headers.push_back(tr("Start Time")); + d_alignments.push_back(right); + + d_headers.push_back(tr("Trans")); + d_alignments.push_back(center); + + d_headers.push_back(tr("Cart")); + d_alignments.push_back(center); + + d_headers.push_back(tr("Group")); + d_alignments.push_back(center); + + d_headers.push_back(tr("Length")); + d_alignments.push_back(right); + + d_headers.push_back(tr("Title")); + d_alignments.push_back(left); + + d_headers.push_back(tr("Artist")); + d_alignments.push_back(left); + + d_headers.push_back(tr("Client")); + d_alignments.push_back(left); + + d_headers.push_back(tr("Agency")); + d_alignments.push_back(left); + + d_headers.push_back(tr("Label")); + d_alignments.push_back(left); + + d_headers.push_back(tr("Source")); + d_alignments.push_back(left); + + d_headers.push_back(tr("Ext Data")); + d_alignments.push_back(left); + + d_headers.push_back(tr("Line ID")); + d_alignments.push_back(right); + + d_headers.push_back(tr("Count")); + d_alignments.push_back(right); +} + + +LogModel::~LogModel() +{ + if(d_fms!=NULL) { + delete d_fms; + } + if(d_bold_fms!=NULL) { + delete d_bold_fms; + } + delete d_log_icons; +} + + +void LogModel::setLogEvent(RDLogEvent *log) +{ + if(log->size()>0) { + beginInsertRows(QModelIndex(),0,log->size()-1); + endInsertRows(); + } + d_log=log; +} + + +void LogModel::clearLogEvent() +{ + if((d_log!=NULL)&&(d_log->size()>0)) { + beginRemoveRows(QModelIndex(),0,d_log->size()-1); + endRemoveRows(); + } +} + + +void LogModel::setFont(const QFont &font) +{ + d_font=font; + if(d_fms!=NULL) { + delete d_fms; + } + d_fms=new QFontMetrics(d_font); + d_bold_font=font; + d_bold_font.setBold(true); + if(d_bold_fms!=NULL) { + delete d_bold_fms; + } + d_bold_fms=new QFontMetrics(d_bold_font); +} + + +int LogModel::columnCount(const QModelIndex &parent) const +{ + return d_headers.size(); +} + + +int LogModel::rowCount(const QModelIndex &parent) const +{ + if(d_log==NULL) { + return 0; + } + return d_log->size(); +} + + +QVariant LogModel::headerData(int section,Qt::Orientation orient,int role) const +{ + if((orient==Qt::Horizontal)&&(role==Qt::DisplayRole)) { + return d_headers.at(section); + } + return QVariant(); +} + + +QVariant LogModel::data(const QModelIndex &index,int role) const +{ + QString str; + RDLogLine *ll=NULL; + int col=index.column(); + int row=index.row(); + + if((ll=d_log->logLine(row))!=NULL) { + switch((Qt::ItemDataRole)role) { + case Qt::DisplayRole: + switch(index.column()) { + case 0: // Start Time + return ll->startTimeText(); + + case 1: // Transition + return RDLogLine::transText(ll->transType()); + + case 2: // Cart Number + return ll->cartNumberText(); + + case 3: // Group + return ll->groupName(); + + case 4: // Length + return ll->forcedLengthText(); + + case 5: // Title + return ll->titleText(); + + case 6: // Artist + return ll->artist(); + + case 7: // Client + return ll->client(); + + case 8: // Agency + return ll->agency(); + + case 9: // Label + return ll->markerLabel(); + + case 10: // Source + return RDLogLine::sourceText(ll->source()); + + case 11: // Ext Data + return ll->extData(); + + case 12: // Line ID + return QString().sprintf("%d",ll->id()); + + case 13: // Count + return QString().sprintf("%d",row); + } + break; + + case Qt::DecorationRole: + if(col==0) { + return d_log_icons->typeIcon(ll->type(),ll->source()); + } + break; + + case Qt::FontRole: + if(col==3) { + return d_bold_font; + } + return d_font; + + case Qt::TextColorRole: + switch(col) { + case 0: + if(ll->timeType()==RDLogLine::Hard) { + return Qt::blue; + } + break; + + case 3: + return ll->groupColor(); + } + break; + + case Qt::TextAlignmentRole: + return d_alignments.at(col); + + default: + break; + } + } + return QVariant(); +} diff --git a/rdcastmanager/logmodel.h b/rdcastmanager/logmodel.h new file mode 100644 index 00000000..017b72fa --- /dev/null +++ b/rdcastmanager/logmodel.h @@ -0,0 +1,59 @@ +// logmodel.h +// +// Read-only data model for Rivendell logs +// +// (C) Copyright 2020 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. +// + +#ifndef LOGMODEL_H +#define LOGMODEL_H + +#include +#include +#include +#include + +#include +#include + +class LogModel : public QAbstractTableModel +{ + Q_OBJECT + public: + LogModel(QObject *parent=0); + ~LogModel(); + void setLogEvent(RDLogEvent *log); + void clearLogEvent(); + void setFont(const QFont &font); + int columnCount(const QModelIndex &parent=QModelIndex()) const; + int rowCount(const QModelIndex &parent=QModelIndex()) const; + QVariant headerData(int section,Qt::Orientation orient, + int role=Qt::DisplayRole) const; + QVariant data(const QModelIndex &index,int role=Qt::DisplayRole) const; + + private: + RDLogEvent *d_log; + QFont d_font; + QFontMetrics *d_fms; + QFont d_bold_font; + QFontMetrics *d_bold_fms; + QList d_headers; + QList d_alignments; + RDLogIcons *d_log_icons; +}; + + +#endif // LOGMODEL_H diff --git a/rdcastmanager/pick_report_dates.cpp b/rdcastmanager/pick_report_dates.cpp deleted file mode 100644 index 3b0823f4..00000000 --- a/rdcastmanager/pick_report_dates.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// pick_report_date.cpp -// -// Select a Set of Dates for a Rivendell Report -// -// (C) Copyright 2002-2019 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 -#include -#include -#include - -#include "globals.h" -#include "pick_report_dates.h" - -PickReportDates::PickReportDates(unsigned feed_id,unsigned cast_id, - QWidget *parent) - : RDDialog(parent) -{ - QString sql; - RDSqlQuery *q; - QDate yesterday_date=QDate::currentDate().addDays(-1); - - edit_cast_id=feed_id; - edit_cast_id=cast_id; - setWindowTitle("RDCastManager - "+tr("Select Report Dates")); - - sql=QString().sprintf("select KEY_NAME from FEEDS where ID=%d",feed_id); - q=new RDSqlQuery(sql); - if(q->first()) { - edit_keyname=q->value(0).toString(); - } - delete q; - - // - // Fix the Window Size - // - setMaximumSize(sizeHint()); - setMaximumSize(sizeHint()); - - // - // Start Date - // - edit_startdate_edit=new QDateEdit(this); - edit_startdate_edit->setGeometry(150,10,100,20); - edit_startdate_edit->setDisplayFormat("MM/dd/yyyy"); - edit_startdate_edit->setDate(yesterday_date.addMonths(-1)); - QLabel *label=new QLabel(edit_startdate_edit,tr("&Start Date:"),this); - label->setGeometry(75,10,70,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - QPushButton *button=new QPushButton(this); - button->setGeometry(260,7,50,27); - button->setFont(subButtonFont()); - button->setText(tr("&Select")); - connect(button,SIGNAL(clicked()),this,SLOT(selectStartDateData())); - - // - // End Date - // - edit_enddate_edit=new QDateEdit(this); - edit_enddate_edit->setGeometry(150,40,100,20); - edit_enddate_edit->setDisplayFormat("MM/dd/yyyy"); - edit_enddate_edit->setDate(yesterday_date); - label=new QLabel(edit_enddate_edit,tr("&End Date:"),this); - label->setGeometry(75,40,70,20); - label->setFont(labelFont()); - label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - button=new QPushButton(this); - button->setGeometry(260,37,50,27); - button->setFont(subButtonFont()); - button->setText(tr("&Select")); - connect(button,SIGNAL(clicked()),this,SLOT(selectEndDateData())); - - // - // Generate Button - // - button=new QPushButton(this); - button->setGeometry(10,sizeHint().height()-60,80,50); - button->setFont(buttonFont()); - button->setText(tr("&Generate\nReport")); - connect(button,SIGNAL(clicked()),this,SLOT(generateData())); - - // - // Close Button - // - button=new QPushButton(this); - button->setGeometry(sizeHint().width()-90,sizeHint().height()-60,80,50); - button->setDefault(true); - button->setFont(buttonFont()); - button->setText(tr("C&lose")); - connect(button,SIGNAL(clicked()),this,SLOT(closeData())); -} - - -PickReportDates::~PickReportDates() -{ -} - - -QSize PickReportDates::sizeHint() const -{ - return QSize(400,134); -} - - -QSizePolicy PickReportDates::sizePolicy() const -{ - return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); -} - - -void PickReportDates::selectStartDateData() -{ - RDDateDialog *dialog=new RDDateDialog(2002,QDate::currentDate().year(),this); - QDate date=edit_startdate_edit->date(); - if(dialog->exec(&date)<0) { - delete dialog; - return; - } - edit_startdate_edit->setDate(date); - edit_enddate_edit->setDate(date); - delete dialog; -} - - -void PickReportDates::selectEndDateData() -{ - RDDateDialog *dialog=new RDDateDialog(2002,QDate::currentDate().year(),this); - QDate date=edit_enddate_edit->date(); - if(dialog->exec(&date)<0) { - delete dialog; - return; - } - edit_enddate_edit->setDate(date); - delete dialog; -} - - -void PickReportDates::generateData() -{ - QString report; - if(edit_cast_id>0) { - GenerateEpisodeReport(edit_keyname,edit_cast_id,&report); - } - else { - GenerateSubscriptionReport(edit_keyname,&report); - } - RDTextFile(report); -} - - -void PickReportDates::closeData() -{ - done(0); -} - - -void PickReportDates::GenerateSubscriptionReport(const QString &keyname, - QString *rpt) -{ - QString sql; - RDSqlQuery *q; - - RDFeed *feed=new RDFeed(keyname,rda->config(),this); - unsigned total=0; - unsigned rss_total=0; - unsigned audio_total=0; - - // - // Header - // - *rpt=RDReport::center("Rivendell Podcast Subscription Report",76)+"\n"; - *rpt+=RDReport::center(feed->channelTitle(),75)+"\n"; - *rpt+="\n"; - *rpt+=" ----- Downloads -----\n"; - *rpt+=" Date RSS Audio\n"; - *rpt+=" ---------------------------------------------\n"; - - // - // Data Rows - // - sql=QString("select ")+ - "ACCESS_DATE,"+ // 00 - "ACCESS_COUNT,"+ // 01 - "CAST_ID "+ // 02 - "from CAST_DOWNLOADS where "+ - "FEED_KEY_NAME=\""+RDEscapeString(keyname)+"\" && "+ - "(ACCESS_DATE>=\""+RDEscapeString(edit_startdate_edit->date(). - toString("yyyy-MM-dd"))+"\")&&"+ - "(ACCESS_DATE<=\""+RDEscapeString(edit_enddate_edit->date(). - toString("yyyy-MM-dd"))+"\") "+ - "order by ACCESS_DATE,CAST_ID desc"; - q=new RDSqlQuery(sql); - while(q->next()) { - if(q->value(2).toUInt()==0) { - *rpt+=QString().sprintf(" %s %9u %9u\n", - (const char *)q->value(0).toDate(). - toString("MM/dd/yyyy"), - q->value(1).toUInt(),total); - total=0; - rss_total+=q->value(1).toUInt(); - } - else { - total+=q->value(1).toUInt(); - audio_total+=q->value(1).toUInt(); - } - } - delete q; - *rpt+=QString().sprintf(" ------------ ------------\n"); - *rpt+=QString().sprintf(" %9u %9u\n", - rss_total,audio_total); - - delete feed; -} - - -void PickReportDates::GenerateEpisodeReport(const QString &keyname, - unsigned cast_id,QString *rpt) -{ - QString sql; - RDSqlQuery *q; - - RDFeed *feed=new RDFeed(keyname,rda->config(),this); - RDPodcast *cast=new RDPodcast(rda->config(),cast_id); - - // - // Header - // - *rpt=RDReport::center("Rivendell Podcast Episode Report",76)+"\n"; - *rpt+=RDReport::center(feed->channelTitle(),76)+"\n"; - *rpt+=RDReport::center(cast->itemTitle(),76)+"\n"; - *rpt+=QString().sprintf(" Posted on %s at %s\n\n", - (const char *)cast->originDateTime(). - toString("MM/dd/yyyy"), - (const char *)cast->originDateTime(). - toString("hh:mm:ss")); - *rpt+=" Date Downloads\n"; - *rpt+=" --------------------------------\n"; - - // - // Data Rows - // - unsigned total=0; - sql=QString("select ")+ - "ACCESS_DATE,"+ // 00 - "ACCESS_COUNT "+ // 01 - "from CAST_DOWNLOADS where "+ - "FEED_KEY_NAME=\""+RDEscapeString(keyname)+"\" && "+ - "(ACCESS_DATE>=\""+RDEscapeString(edit_startdate_edit->date(). - toString("yyyy-MM-dd"))+"\")&&"+ - "(ACCESS_DATE<=\""+RDEscapeString(edit_enddate_edit->date(). - toString("yyyy-MM-dd"))+"\")&&"+ - QString().sprintf("(CAST_ID=%u) ",cast_id)+ - "order by ACCESS_DATE"; - q=new RDSqlQuery(sql); - while(q->next()) { - *rpt+=QString().sprintf(" %s %9u\n", - (const char *)q->value(0).toDate(). - toString("MM/dd/yyyy"), - q->value(1).toUInt()); - total+=q->value(1).toUInt(); - } - delete q; - - *rpt+=QString(). - sprintf(" ------------\n"); - *rpt+=QString(). - sprintf(" %9u\n",total); - - delete cast; - delete feed; -} diff --git a/rdcastmanager/rdcastmanager.cpp b/rdcastmanager/rdcastmanager.cpp index ec39030f..7a3f28aa 100644 --- a/rdcastmanager/rdcastmanager.cpp +++ b/rdcastmanager/rdcastmanager.cpp @@ -2,7 +2,7 @@ // // A PodCast Management Utility for Rivendell. // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -19,9 +19,11 @@ // #include +#include #include #include +#include #include #include @@ -35,6 +37,7 @@ #include "../icons/rdcastmanager-22x22.xpm" #include "../icons/greencheckmark.xpm" #include "../icons/redx.xpm" +#include "../icons/rdcastmanager-32x32.xpm" // // Global Resources @@ -97,6 +100,13 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) setWindowIcon(*cast_rivendell_map); cast_greencheckmark_map=new QPixmap(greencheckmark_xpm); cast_redx_map=new QPixmap(redx_xpm); + cast_rdcastmanager_32x32_map=new QPixmap(rdcastmanager_32x32_xpm); + + // + // Notifications + // + connect(rda->ripc(),SIGNAL(notificationReceived(RDNotification *)), + this,SLOT(notificationReceivedData(RDNotification *))); // // Feed List @@ -104,29 +114,45 @@ MainWidget::MainWidget(RDConfig *c,QWidget *parent) cast_feed_list=new RDListView(this); cast_feed_list->setAllColumnsShowFocus(true); cast_feed_list->setItemMargin(5); + connect(cast_feed_list,SIGNAL(clicked(Q3ListViewItem *)), + this,SLOT(feedClickedData(Q3ListViewItem *))); connect(cast_feed_list, SIGNAL(doubleClicked(Q3ListViewItem *,const QPoint &,int)), this, SLOT(feedDoubleclickedData(Q3ListViewItem *,const QPoint &,int))); cast_feed_list->addColumn(""); cast_feed_list->setColumnAlignment(0,Qt::AlignCenter); + cast_feed_list->addColumn(tr("Key Name")); - cast_feed_list->setColumnAlignment(1,Qt::AlignHCenter); + cast_feed_list->setColumnAlignment(1,Qt::AlignLeft); + cast_feed_list->addColumn(tr("Feed Name")); cast_feed_list->setColumnAlignment(2,Qt::AlignLeft); - cast_feed_list->addColumn(tr("Description")); - cast_feed_list->setColumnAlignment(3,Qt::AlignLeft); + cast_feed_list->addColumn(tr("Casts")); cast_feed_list->setColumnAlignment(3,Qt::AlignCenter); + cast_feed_list->addColumn(tr("Public URL")); + cast_feed_list->setColumnAlignment(4,Qt::AlignLeft); + // // Open Button // cast_open_button=new QPushButton(this); cast_open_button->setFont(buttonFont()); cast_open_button->setText(tr("&View\nFeed")); + cast_open_button->setDisabled(true); connect(cast_open_button,SIGNAL(clicked()),this,SLOT(openData())); + // + // Copy Button + // + cast_copy_button=new QPushButton(this); + cast_copy_button->setFont(buttonFont()); + cast_copy_button->setText(tr("Copy URL to\nClipboard")); + cast_copy_button->setDisabled(true); + connect(cast_copy_button,SIGNAL(clicked()),this,SLOT(copyData())); + // // Close Button // @@ -163,25 +189,71 @@ void MainWidget::userChangedData() } +void MainWidget::feedClickedData(Q3ListViewItem *item) +{ + cast_open_button->setDisabled(item==NULL); + cast_copy_button->setDisabled(item==NULL); +} + + void MainWidget::openData() { RDListViewItem *item=(RDListViewItem *)cast_feed_list->selectedItem(); if(item==NULL) { return; } - ListCasts *casts=new ListCasts(item->id(),this); + ListCasts *casts= + new ListCasts(item->id(),item->text(3)==tr("[superfeed]"),this); casts->exec(); RefreshItem(item); delete casts; } +void MainWidget::copyData() +{ + RDListViewItem *item=(RDListViewItem *)cast_feed_list->selectedItem(); + if(item==NULL) { + return; + } + QApplication::clipboard()->setText(item->text(4)); +} + + void MainWidget::feedDoubleclickedData(Q3ListViewItem *,const QPoint &,int) { openData(); } +void MainWidget::notificationReceivedData(RDNotification *notify) +{ + QString keyname; + RDListViewItem *item=NULL; + + if(notify->type()==RDNotification::FeedType) { + keyname=notify->id().toString(); + switch(notify->action()) { + case RDNotification::ModifyAction: + item=(RDListViewItem *)cast_feed_list->firstChild(); + while(item!=NULL) { + if(item->text(1)==keyname) { + RefreshItem(item); + } + item=(RDListViewItem *)item->nextSibling(); + } + break; + + case RDNotification::NoAction: + case RDNotification::AddAction: + case RDNotification::DeleteAction: + case RDNotification::LastAction: + break; + } + } +} + + void MainWidget::quitMainWidget() { exit(0); @@ -193,6 +265,7 @@ void MainWidget::resizeEvent(QResizeEvent *e) if(cast_resize) { cast_feed_list->setGeometry(10,10,size().width()-20,size().height()-70); cast_open_button->setGeometry(10,size().height()-55,80,50); + cast_copy_button->setGeometry(120,size().height()-55,100,50); cast_close_button->setGeometry(size().width()-90,size().height()-55,80,50); } } @@ -207,11 +280,14 @@ void MainWidget::RefreshItem(RDListViewItem *item) int total=0; sql=QString("select ")+ - "CHANNEL_TITLE,"+ // 00 - "CHANNEL_DESCRIPTION,"+ // 01 - "ID "+ // 02 - "from FEEDS where "+ - "KEY_NAME=\""+RDEscapeString(item->text(1))+"\""; + "FEEDS.CHANNEL_TITLE,"+ // 00 + "FEEDS.IS_SUPERFEED,"+ // 01 + "FEEDS.ID,"+ // 02 + "FEEDS.BASE_URL,"+ // 03 + "FEED_IMAGES.DATA "+ // 04 + "from FEEDS left join FEED_IMAGES "+ + "on FEEDS.CHANNEL_IMAGE_ID=FEED_IMAGES.ID where "+ + "FEEDS.KEY_NAME=\""+RDEscapeString(item->text(1))+"\""; q=new RDSqlQuery(sql); while(q->next()) { sql=QString().sprintf("select STATUS from PODCASTS where FEED_ID=%u", @@ -230,15 +306,21 @@ void MainWidget::RefreshItem(RDListViewItem *item) } } delete q1; - if(active==total) { - item->setPixmap(0,*cast_greencheckmark_map); + if(q->value(4).isNull()) { + item->setPixmap(0,*cast_rdcastmanager_32x32_map); } else { - item->setPixmap(0,*cast_redx_map); + QImage img=QImage::fromData(q->value(4).toByteArray()); + item->setPixmap(0,QPixmap::fromImage(img.scaled(32,32))); } item->setText(2,q->value(0).toString()); - item->setText(3,q->value(1).toString()); - item->setText(4,QString().sprintf("%d / %d",active,total)); + if(RDBool(q->value(1).toString())) { + item->setText(3,tr("[superfeed]")); + } + else { + item->setText(3,QString().sprintf("%d / %d",active,total)); + } + item->setText(4,RDFeed::publicUrl(q->value(3).toString(),item->text(1))); } delete q; } diff --git a/rdcastmanager/rdcastmanager.h b/rdcastmanager/rdcastmanager.h index 8dab8c46..0b2ddc3c 100644 --- a/rdcastmanager/rdcastmanager.h +++ b/rdcastmanager/rdcastmanager.h @@ -2,7 +2,7 @@ // // A RSS Feed Management Utility for Rivendell. // -// (C) Copyright 2002-2018 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -39,8 +39,11 @@ class MainWidget : public RDWidget private slots: void openData(); + void copyData(); void userChangedData(); + void feedClickedData(Q3ListViewItem *item); void feedDoubleclickedData(Q3ListViewItem *item,const QPoint &pt,int col); + void notificationReceivedData(RDNotification *notify); void quitMainWidget(); protected: @@ -53,7 +56,9 @@ class MainWidget : public RDWidget QPixmap *cast_rivendell_map; QPixmap *cast_greencheckmark_map; QPixmap *cast_redx_map; + QPixmap *cast_rdcastmanager_32x32_map; QPushButton *cast_open_button; + QPushButton *cast_copy_button; QPushButton *cast_close_button; bool cast_resize; }; diff --git a/rdcastmanager/rdcastmanager.pro b/rdcastmanager/rdcastmanager.pro index a3eb3c6f..4fbb51e8 100644 --- a/rdcastmanager/rdcastmanager.pro +++ b/rdcastmanager/rdcastmanager.pro @@ -2,7 +2,7 @@ # # The rdcastmanager/ QMake project file for Rivendell # -# (C) Copyright 2003-2018 Fred Gleason +# (C) Copyright 2003-2020 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 @@ -24,15 +24,21 @@ # purely for the sake of i18n support. # -SOURCES += rdcastmanager.cpp SOURCES += edit_cast.cpp SOURCES += list_casts.cpp +SOURCES += logdialog.cpp +SOURCES += logmodel.cpp SOURCES += pick_report_dates.cpp +SOURCES += rdcastmanager.cpp +SOURCES += render_dialog.cpp -HEADERS += rdcastmanager.h HEADERS += edit_cast.h HEADERS += list_casts.h +HEADERS += logdialog.h +HEADERS += logmodel.h HEADERS += pick_report_dates.h +HEADERS += rdcastmanager.h +HEADERS += render_dialog.h TRANSLATIONS += rdcastmanager_cs.ts TRANSLATIONS += rdcastmanager_de.ts diff --git a/rdcastmanager/rdcastmanager_cs.ts b/rdcastmanager/rdcastmanager_cs.ts index 98fa3682..3be1a20e 100644 --- a/rdcastmanager/rdcastmanager_cs.ts +++ b/rdcastmanager/rdcastmanager_cs.ts @@ -5,7 +5,7 @@ EditCast Editing PodCast - Upravit podcast + Upravit podcast Title: @@ -29,11 +29,11 @@ Source Text: - Zdrojový text: + Zdrojový text: Source URL: - Adresa (URL) zdroje: + Adresa (URL) zdroje: Comments URL: @@ -41,7 +41,7 @@ Posted At: - Vyvěšeno: + Vyvěšeno: No @@ -53,7 +53,7 @@ Cast Expires: - PÅ™estane platit: + PÅ™estane platit: Expires At: @@ -61,24 +61,24 @@ &Select - &Vybrat + &Vybrat Hold - Držet + Držet Active - ÄŒinný + ÄŒinný Posting Status: - Stav vyvěšení: + Stav vyvěšení: Episode &Report - &Zpráva o + &Zpráva o dílu @@ -95,18 +95,70 @@ dílu Media Link: - Multimediální odkaz: + Multimediální odkaz: Expires On: - PÅ™estane platit: + PÅ™estane platit: + + + Image + + + + at + + + + &Select Date + + + + Item Active + + + + Item contains explicit content + + + + Item Expires + + + + Editing Item + + + + unknown + + + + Posted By + + + + on + + + + Error + + + + Item expiration cannot be prior to Air Date/Time! + + + + Item expiration must be in the future! + ListCasts Podcast List - Seznam podcastů + Seznam podcastů Post From @@ -131,7 +183,7 @@ vozí&ku/zábÄ›ru Subscription &Report - Zpráva o + Zpráva o &odbÄ›ru @@ -159,11 +211,11 @@ Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : Origin - Původ + Původ Expires - PÅ™estane platit + PÅ™estane platit Length @@ -171,7 +223,7 @@ Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : Description - Popis + Popis Category @@ -179,7 +231,7 @@ Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : Link - Odkaz + Odkaz Uploading Audio... @@ -207,15 +259,15 @@ Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : Delete Podcast - Smazat podcast + Smazat podcast Are you sure you want to delete this podcast? - Opravdu chcete smazat tento podcast? + Opravdu chcete smazat tento podcast? Deleting Podcast... - Maže se podcast... + Maže se podcast... Internal Error @@ -265,11 +317,11 @@ Podcast trotzdem löschen? Only Show Unexpired Casts - Ukázat jen podcasty, co platí + Ukázat jen podcasty, co platí Only Show Active Casts - Ukázat jen Äinné podcasty + Ukázat jen Äinné podcasty Posting Error @@ -278,7 +330,7 @@ Podcast trotzdem löschen? Unable to delete remote audio! - Nelze smazat soubory se zvukem na jiném serveru! + Nelze smazat soubory se zvukem na jiném serveru! The server said: " @@ -292,6 +344,166 @@ Podcast trotzdem löschen? Cancel + + Feed + + + + Post From +Log + + + + Podcast Item List + + + + Only Show Active Items + + + + Posted By + + + + at + + + + Start + + + + Expiration + + + + unknown + + + + on + + + + Delete Item + + + + Are you sure you want to delete this item? + + + + Deleting Item... + + + + Select Audio File + + + + SHA1 + + + + [none] + + + + Unable to drop remote audio! + + + + + Unable to update remote XML data! + + + + Status + + + + + LogDialog + + OK + + + + Cancel + + + + List Log + + + + At least one log event must be selected! + + + + List Log Events + + + + + LogModel + + Start Time + + + + Trans + + + + Title + Název + + + Artist + + + + Cart + + + + Group + + + + Length + Délka + + + Client + + + + Agency + + + + Label + + + + Source + + + + Ext Data + + + + Line ID + + + + Count + + MainWidget @@ -317,7 +529,7 @@ Podcast trotzdem löschen? Description - Popis + Popis Casts @@ -349,32 +561,87 @@ přívod Unknown command option + + Public URL + + + + Copy URL to +Clipboard + + + + [superfeed] + + PickReportDates - - Select Report Dates - - - - &Start Date: - - &Select - &Vybrat + &Vybrat + + + RenderDialog - &End Date: + [now] - &Generate -Report + As Specified - C&lose + Virtual Start Time + + + + Stop Rendering + + + + Treat as PLAY + + + + At STOP transition + + + + &Cancel + Z&ruÅ¡it + + + Log Render Options + + + + OK + + + + Select + + + + Selected Log Events + + + + Lines + + + + through + + + + All lines + + + + Line diff --git a/rdcastmanager/rdcastmanager_de.ts b/rdcastmanager/rdcastmanager_de.ts index 246de822..abe4fb70 100644 --- a/rdcastmanager/rdcastmanager_de.ts +++ b/rdcastmanager/rdcastmanager_de.ts @@ -5,7 +5,7 @@ EditCast Editing PodCast - Podcast editieren + Podcast editieren Title: @@ -29,11 +29,11 @@ Source Text: - Quelltext: + Quelltext: Source URL: - Quell-URL: + Quell-URL: Comments URL: @@ -41,7 +41,7 @@ Posted At: - Gepostet am: + Gepostet am: No @@ -53,7 +53,7 @@ Cast Expires: - Cast läuft ab: + Cast läuft ab: Expires At: @@ -61,24 +61,24 @@ &Select - &Auswählen + &Auswählen Hold - Hold + Hold Active - Aktiv + Aktiv Posting Status: - Posting Status: + Posting Status: Episode &Report - Episoden- + Episoden- &Report @@ -95,10 +95,58 @@ Media Link: - Medienlink: + Medienlink: - Expires On: + Image + + + + at + + + + &Select Date + + + + Item Active + + + + Item contains explicit content + + + + Item Expires + + + + Editing Item + + + + unknown + + + + Posted By + + + + on + + + + Error + + + + Item expiration cannot be prior to Air Date/Time! + + + + Item expiration must be in the future! @@ -106,7 +154,7 @@ ListCasts Podcast List - Podcastliste + Podcastliste Post From @@ -131,7 +179,7 @@ Car&t/Cut Subscription &Report - Abonnement + Abonnement &Report @@ -148,11 +196,11 @@ Car&t/Cut Origin - Herkunft + Herkunft Expires - Läuf ab + Läuf ab Length @@ -160,7 +208,7 @@ Car&t/Cut Description - Beschreibung + Beschreibung Category @@ -168,7 +216,7 @@ Car&t/Cut Link - Link + Link Uploading Audio... @@ -196,15 +244,15 @@ Car&t/Cut Delete Podcast - Podcast löschen + Podcast löschen Are you sure you want to delete this podcast? - Sind Sie sicher, daß sie diesen Podcast löschen wollen? + Sind Sie sicher, daß sie diesen Podcast löschen wollen? Deleting Podcast... - Lösche Podcast... + Lösche Podcast... Internal Error @@ -254,11 +302,11 @@ Podcast trotzdem löschen? Only Show Unexpired Casts - Zeige nur nichtabgelaufene Casts + Zeige nur nichtabgelaufene Casts Only Show Active Casts - Zeige nur aktive Casts + Zeige nur aktive Casts Posting Error @@ -267,7 +315,7 @@ Podcast trotzdem löschen? Unable to delete remote audio! - Kann die Audiodatei(en) auf dem anderen Server nicht löschen! + Kann die Audiodatei(en) auf dem anderen Server nicht löschen! The server said: " @@ -281,6 +329,166 @@ Podcast trotzdem löschen? Cancel + + Feed + + + + Post From +Log + + + + Podcast Item List + + + + Only Show Active Items + + + + Posted By + + + + at + + + + Start + + + + Expiration + + + + unknown + + + + on + + + + Delete Item + + + + Are you sure you want to delete this item? + + + + Deleting Item... + + + + Select Audio File + + + + SHA1 + + + + [none] + + + + Unable to drop remote audio! + + + + + Unable to update remote XML data! + + + + Status + + + + + LogDialog + + OK + + + + Cancel + + + + List Log + + + + At least one log event must be selected! + + + + List Log Events + + + + + LogModel + + Start Time + + + + Trans + + + + Title + Titel + + + Artist + + + + Cart + + + + Group + + + + Length + Länge + + + Client + + + + Agency + + + + Label + + + + Source + + + + Ext Data + + + + Line ID + + + + Count + + MainWidget @@ -306,7 +514,7 @@ Podcast trotzdem löschen? Description - Beschreibung + Beschreibung Casts @@ -338,32 +546,87 @@ Feed Unknown command option + + Public URL + + + + Copy URL to +Clipboard + + + + [superfeed] + + PickReportDates - - Select Report Dates - - - - &Start Date: - - &Select - &Auswählen + &Auswählen + + + RenderDialog - &End Date: + [now] - &Generate -Report + As Specified - C&lose + Virtual Start Time + + + + Stop Rendering + + + + Treat as PLAY + + + + At STOP transition + + + + &Cancel + Abbre&chen + + + Log Render Options + + + + OK + + + + Select + + + + Selected Log Events + + + + Lines + + + + through + + + + All lines + + + + Line diff --git a/rdcastmanager/rdcastmanager_es.ts b/rdcastmanager/rdcastmanager_es.ts index f66365f2..8814d758 100644 --- a/rdcastmanager/rdcastmanager_es.ts +++ b/rdcastmanager/rdcastmanager_es.ts @@ -5,7 +5,7 @@ EditCast Editing PodCast - Editando PodCast + Editando PodCast Title: @@ -21,7 +21,7 @@ Posted At: - Colocado el: + Colocado el: No @@ -33,23 +33,23 @@ Cast Expires: - Expira: + Expira: &Select - &Elegir + &Elegir Hold - Mantener + Mantener Active - Activo + Activo Posting Status: - Estado del Post: + Estado del Post: &OK @@ -73,16 +73,16 @@ Source Text: - Texto fuente: + Texto fuente: Source URL: - URL fuente: + URL fuente: Episode &Report - &Reporte de + &Reporte de Episodios @@ -91,11 +91,63 @@ Episodios Media Link: - Enlace al medio: + Enlace al medio: Expires On: - Expira el: + Expira el: + + + Image + + + + at + + + + &Select Date + + + + Item Active + + + + Item contains explicit content + + + + Item Expires + + + + Editing Item + + + + unknown + + + + Posted By + + + + on + + + + Error + + + + Item expiration cannot be prior to Air Date/Time! + + + + Item expiration must be in the future! + @@ -118,15 +170,15 @@ Episodios Origin - Origen + Origen Expires - Expira + Expira Description - Descripción + Descripción Category @@ -134,7 +186,7 @@ Episodios Link - Enlace + Enlace Never @@ -142,7 +194,7 @@ Episodios Podcast List - Lista de Podcasts + Lista de Podcasts Post From @@ -175,7 +227,7 @@ Car&t./Audio Subscription &Report - &Reporte de + &Reporte de Suscripción @@ -188,11 +240,11 @@ Suscripción Only Show Unexpired Casts - Sólo Mostrar sin Expirar + Sólo Mostrar sin Expirar Only Show Active Casts - Sólo mostrar activos + Sólo mostrar activos Posting Error @@ -200,15 +252,15 @@ Suscripción Delete Podcast - Borrar Podcast + Borrar Podcast Are you sure you want to delete this podcast? - ¿Está seguro de borrar este Podcast? + ¿Está seguro de borrar este Podcast? Deleting Podcast... - Eliminando Podcast... + Eliminando Podcast... Remote Error @@ -217,7 +269,7 @@ Suscripción Unable to delete remote audio! - ¡No fue posible eliminar audio remoto! + ¡No fue posible eliminar audio remoto! @@ -232,6 +284,166 @@ Suscripción Cancel + + Feed + + + + Post From +Log + + + + Podcast Item List + + + + Only Show Active Items + + + + Posted By + + + + at + + + + Start + + + + Expiration + + + + unknown + + + + on + + + + Delete Item + + + + Are you sure you want to delete this item? + + + + Deleting Item... + + + + Select Audio File + + + + SHA1 + + + + [none] + + + + Unable to drop remote audio! + + + + + Unable to update remote XML data! + + + + Status + + + + + LogDialog + + OK + + + + Cancel + + + + List Log + + + + At least one log event must be selected! + + + + List Log Events + + + + + LogModel + + Start Time + + + + Trans + + + + Title + Título + + + Artist + + + + Cart + + + + Group + + + + Length + Longitud + + + Client + + + + Agency + + + + Label + + + + Source + + + + Ext Data + + + + Line ID + + + + Count + + MainWidget @@ -257,7 +469,7 @@ Suscripción Description - Descripción + Descripción Casts @@ -288,34 +500,110 @@ Feed Unknown command option + + Public URL + + + + Copy URL to +Clipboard + + + + [superfeed] + + PickReportDates Select Report Dates - Elegir Fechas del Reporte + Elegir Fechas del Reporte &Start Date: - Fech. &Inicio: + Fech. &Inicio: &Select - &Elegir + &Elegir &End Date: - Fech. &Fin: + Fech. &Fin: &Generate Report - &Generar + &Generar Reporte C&lose - &Cerrar + &Cerrar + + + + RenderDialog + + [now] + + + + As Specified + + + + Virtual Start Time + + + + Stop Rendering + + + + Treat as PLAY + + + + At STOP transition + + + + &Cancel + &Cancelar + + + Log Render Options + + + + OK + + + + Select + + + + Selected Log Events + + + + Lines + + + + through + + + + All lines + + + + Line + diff --git a/rdcastmanager/rdcastmanager_fr.ts b/rdcastmanager/rdcastmanager_fr.ts index a28dbd94..2c1f1a5b 100644 --- a/rdcastmanager/rdcastmanager_fr.ts +++ b/rdcastmanager/rdcastmanager_fr.ts @@ -3,10 +3,6 @@ EditCast - - Editing PodCast - - Title: @@ -27,22 +23,10 @@ Description: - - Source Text: - - - - Source URL: - - Comments URL: - - Posted At: - - No @@ -51,31 +35,6 @@ Yes - - Cast Expires: - - - - &Select - - - - Hold - - - - Active - - - - Posting Status: - - - - Episode -&Report - - &OK @@ -89,20 +48,60 @@ - Media Link: + Image - Expires On: + at + + + + &Select Date + + + + Item Active + + + + Item contains explicit content + + + + Item Expires + + + + Editing Item + + + + unknown + + + + Posted By + + + + on + + + + Error + + + + Item expiration cannot be prior to Air Date/Time! + + + + Item expiration must be in the future! ListCasts - - Podcast List - - Post From Car&t/Cut @@ -121,11 +120,6 @@ Car&t/Cut &Delete - - Subscription -&Report - - &Close @@ -138,30 +132,14 @@ Car&t/Cut Title - - Origin - - - - Expires - - Length - - Description - - Category - - Link - - Uploading Audio... @@ -182,39 +160,14 @@ Car&t/Cut Filter: - - Only Show Unexpired Casts - - - - Only Show Active Casts - - Posting Error - - Delete Podcast - - - - Are you sure you want to delete this podcast? - - - - Deleting Podcast... - - Remote Error - - Unable to delete remote audio! - - - The server said: " @@ -227,6 +180,166 @@ Car&t/Cut Cancel + + Feed + + + + Post From +Log + + + + Podcast Item List + + + + Only Show Active Items + + + + Posted By + + + + at + + + + Start + + + + Expiration + + + + unknown + + + + on + + + + Delete Item + + + + Are you sure you want to delete this item? + + + + Deleting Item... + + + + Select Audio File + + + + SHA1 + + + + [none] + + + + Unable to drop remote audio! + + + + + Unable to update remote XML data! + + + + Status + + + + + LogDialog + + OK + + + + Cancel + + + + List Log + + + + At least one log event must be selected! + + + + List Log Events + + + + + LogModel + + Start Time + + + + Trans + + + + Title + + + + Artist + + + + Cart + + + + Group + + + + Length + + + + Client + + + + Agency + + + + Label + + + + Source + + + + Ext Data + + + + Line ID + + + + Count + + MainWidget @@ -242,10 +355,6 @@ Car&t/Cut Feed Name - - Description - - Casts @@ -275,32 +384,80 @@ Feed Unknown command option + + Public URL + + + + Copy URL to +Clipboard + + + + [superfeed] + + - PickReportDates + RenderDialog - Select Report Dates + [now] - &Start Date: + As Specified - &Select + Virtual Start Time - &End Date: + Stop Rendering - &Generate -Report + Treat as PLAY - C&lose + At STOP transition + + + + &Cancel + + + + Log Render Options + + + + OK + + + + Select + + + + Selected Log Events + + + + Lines + + + + through + + + + All lines + + + + Line diff --git a/rdcastmanager/rdcastmanager_nb.ts b/rdcastmanager/rdcastmanager_nb.ts index 891177d3..d1fba85e 100644 --- a/rdcastmanager/rdcastmanager_nb.ts +++ b/rdcastmanager/rdcastmanager_nb.ts @@ -5,7 +5,7 @@ EditCast Editing PodCast - Redigerer podkast + Redigerer podkast Title: @@ -29,11 +29,11 @@ Source Text: - Kjeldetekst: + Kjeldetekst: Source URL: - Kjeldeadresse: + Kjeldeadresse: Comments URL: @@ -41,7 +41,7 @@ Posted At: - Lagt inn: + Lagt inn: No @@ -53,7 +53,7 @@ Cast Expires: - Podkasten gÃ¥r ut: + Podkasten gÃ¥r ut: Expires At: @@ -61,24 +61,24 @@ &Select - &Vel + &Vel Hold - Hald + Hald Active - Aktiv + Aktiv Posting Status: - Postestatus: + Postestatus: Episode &Report - Episode- + Episode- &rapport @@ -89,16 +89,60 @@ &Cancel &Avbryt - - Media Link: - - Air Date/Time: - Expires On: + Image + + + + at + + + + &Select Date + + + + Item Active + + + + Item contains explicit content + + + + Item Expires + + + + Editing Item + + + + unknown + + + + Posted By + + + + on + + + + Error + + + + Item expiration cannot be prior to Air Date/Time! + + + + Item expiration must be in the future! @@ -106,7 +150,7 @@ ListCasts Podcast List - Podkastliste + Podkastliste Post From @@ -131,7 +175,7 @@ Korg/Ku&tt Subscription &Report - Abonnements&rapport + Abonnements&rapport &Close @@ -147,11 +191,11 @@ Korg/Ku&tt Origin - Opphav + Opphav Expires - GÃ¥r ut + GÃ¥r ut Length @@ -159,7 +203,7 @@ Korg/Ku&tt Description - Skildring + Skildring Category @@ -167,7 +211,7 @@ Korg/Ku&tt Link - Lenkje + Lenkje &Casts: @@ -199,15 +243,15 @@ Korg/Ku&tt Delete Podcast - Slett podkast + Slett podkast Are you sure you want to delete this podcast? - Er du sikker pÃ¥ at du vil sletta denne podkasten? + Er du sikker pÃ¥ at du vil sletta denne podkasten? Deleting Podcast... - Slettar podkast... + Slettar podkast... Internal Error @@ -251,23 +295,10 @@ Vil du halda fram med Ã¥ sletta podkasten? Filter: - - Only Show Unexpired Casts - - - - Only Show Active Casts - - Posting Error - - Unable to delete remote audio! - - - The server said: " @@ -280,6 +311,166 @@ Vil du halda fram med Ã¥ sletta podkasten? Cancel + + Feed + + + + Post From +Log + + + + Podcast Item List + + + + Only Show Active Items + + + + Posted By + + + + at + + + + Start + + + + Expiration + + + + unknown + + + + on + + + + Delete Item + + + + Are you sure you want to delete this item? + + + + Deleting Item... + + + + Select Audio File + + + + SHA1 + + + + [none] + + + + Unable to drop remote audio! + + + + + Unable to update remote XML data! + + + + Status + + + + + LogDialog + + OK + + + + Cancel + + + + List Log + + + + At least one log event must be selected! + + + + List Log Events + + + + + LogModel + + Start Time + + + + Trans + + + + Title + Tittel + + + Artist + + + + Cart + + + + Group + + + + Length + Lengd + + + Client + + + + Agency + + + + Label + + + + Source + + + + Ext Data + + + + Line ID + + + + Count + + MainWidget @@ -305,7 +496,7 @@ Vil du halda fram med Ã¥ sletta podkasten? Description - Skildring + Skildring Casts @@ -337,32 +528,87 @@ straum Unknown command option + + Public URL + + + + Copy URL to +Clipboard + + + + [superfeed] + + PickReportDates - - Select Report Dates - - - - &Start Date: - - &Select - &Vel + &Vel + + + RenderDialog - &End Date: + [now] - &Generate -Report + As Specified - C&lose + Virtual Start Time + + + + Stop Rendering + + + + Treat as PLAY + + + + At STOP transition + + + + &Cancel + &Avbryt + + + Log Render Options + + + + OK + + + + Select + + + + Selected Log Events + + + + Lines + + + + through + + + + All lines + + + + Line diff --git a/rdcastmanager/rdcastmanager_nn.ts b/rdcastmanager/rdcastmanager_nn.ts index 891177d3..d1fba85e 100644 --- a/rdcastmanager/rdcastmanager_nn.ts +++ b/rdcastmanager/rdcastmanager_nn.ts @@ -5,7 +5,7 @@ EditCast Editing PodCast - Redigerer podkast + Redigerer podkast Title: @@ -29,11 +29,11 @@ Source Text: - Kjeldetekst: + Kjeldetekst: Source URL: - Kjeldeadresse: + Kjeldeadresse: Comments URL: @@ -41,7 +41,7 @@ Posted At: - Lagt inn: + Lagt inn: No @@ -53,7 +53,7 @@ Cast Expires: - Podkasten gÃ¥r ut: + Podkasten gÃ¥r ut: Expires At: @@ -61,24 +61,24 @@ &Select - &Vel + &Vel Hold - Hald + Hald Active - Aktiv + Aktiv Posting Status: - Postestatus: + Postestatus: Episode &Report - Episode- + Episode- &rapport @@ -89,16 +89,60 @@ &Cancel &Avbryt - - Media Link: - - Air Date/Time: - Expires On: + Image + + + + at + + + + &Select Date + + + + Item Active + + + + Item contains explicit content + + + + Item Expires + + + + Editing Item + + + + unknown + + + + Posted By + + + + on + + + + Error + + + + Item expiration cannot be prior to Air Date/Time! + + + + Item expiration must be in the future! @@ -106,7 +150,7 @@ ListCasts Podcast List - Podkastliste + Podkastliste Post From @@ -131,7 +175,7 @@ Korg/Ku&tt Subscription &Report - Abonnements&rapport + Abonnements&rapport &Close @@ -147,11 +191,11 @@ Korg/Ku&tt Origin - Opphav + Opphav Expires - GÃ¥r ut + GÃ¥r ut Length @@ -159,7 +203,7 @@ Korg/Ku&tt Description - Skildring + Skildring Category @@ -167,7 +211,7 @@ Korg/Ku&tt Link - Lenkje + Lenkje &Casts: @@ -199,15 +243,15 @@ Korg/Ku&tt Delete Podcast - Slett podkast + Slett podkast Are you sure you want to delete this podcast? - Er du sikker pÃ¥ at du vil sletta denne podkasten? + Er du sikker pÃ¥ at du vil sletta denne podkasten? Deleting Podcast... - Slettar podkast... + Slettar podkast... Internal Error @@ -251,23 +295,10 @@ Vil du halda fram med Ã¥ sletta podkasten? Filter: - - Only Show Unexpired Casts - - - - Only Show Active Casts - - Posting Error - - Unable to delete remote audio! - - - The server said: " @@ -280,6 +311,166 @@ Vil du halda fram med Ã¥ sletta podkasten? Cancel + + Feed + + + + Post From +Log + + + + Podcast Item List + + + + Only Show Active Items + + + + Posted By + + + + at + + + + Start + + + + Expiration + + + + unknown + + + + on + + + + Delete Item + + + + Are you sure you want to delete this item? + + + + Deleting Item... + + + + Select Audio File + + + + SHA1 + + + + [none] + + + + Unable to drop remote audio! + + + + + Unable to update remote XML data! + + + + Status + + + + + LogDialog + + OK + + + + Cancel + + + + List Log + + + + At least one log event must be selected! + + + + List Log Events + + + + + LogModel + + Start Time + + + + Trans + + + + Title + Tittel + + + Artist + + + + Cart + + + + Group + + + + Length + Lengd + + + Client + + + + Agency + + + + Label + + + + Source + + + + Ext Data + + + + Line ID + + + + Count + + MainWidget @@ -305,7 +496,7 @@ Vil du halda fram med Ã¥ sletta podkasten? Description - Skildring + Skildring Casts @@ -337,32 +528,87 @@ straum Unknown command option + + Public URL + + + + Copy URL to +Clipboard + + + + [superfeed] + + PickReportDates - - Select Report Dates - - - - &Start Date: - - &Select - &Vel + &Vel + + + RenderDialog - &End Date: + [now] - &Generate -Report + As Specified - C&lose + Virtual Start Time + + + + Stop Rendering + + + + Treat as PLAY + + + + At STOP transition + + + + &Cancel + &Avbryt + + + Log Render Options + + + + OK + + + + Select + + + + Selected Log Events + + + + Lines + + + + through + + + + All lines + + + + Line diff --git a/rdcastmanager/rdcastmanager_pt_BR.ts b/rdcastmanager/rdcastmanager_pt_BR.ts index 32b85f69..41765759 100644 --- a/rdcastmanager/rdcastmanager_pt_BR.ts +++ b/rdcastmanager/rdcastmanager_pt_BR.ts @@ -5,11 +5,11 @@ EditCast Editing PodCast - Editando Podcast + Editando Podcast Media Link: - Link de Mídia: + Link de Mídia: Title: @@ -33,11 +33,11 @@ Source Text: - Texto Fonte: + Texto Fonte: Source URL: - Fonte URL: + Fonte URL: Comments URL: @@ -49,11 +49,11 @@ &Select - &Selecionar + &Selecionar Posted At: - Postado em: + Postado em: No @@ -65,7 +65,7 @@ Cast Expires: - Cast Expira: + Cast Expira: Expires At: @@ -73,20 +73,20 @@ Hold - Espere + Espere Active - Ativo + Ativo Posting Status: - Publicação: + Publicação: Episode &Report - &Relatório do + &Relatório do Episódio @@ -98,7 +98,55 @@ Episódio &Cancelar - Expires On: + Image + + + + at + + + + &Select Date + + + + Item Active + + + + Item contains explicit content + + + + Item Expires + + + + Editing Item + + + + unknown + + + + Posted By + + + + on + + + + Error + + + + Item expiration cannot be prior to Air Date/Time! + + + + Item expiration must be in the future! @@ -106,7 +154,7 @@ Episódio ListCasts Podcast List - Lista de POdcast + Lista de POdcast Uploading Audio... @@ -122,11 +170,11 @@ Episódio Only Show Unexpired Casts - Mostrar Casts não expirados + Mostrar Casts não expirados Only Show Active Casts - Mostrar Casts Ativos + Mostrar Casts Ativos @@ -139,11 +187,11 @@ Episódio Origin - Origem + Origem Expires - Expira + Expira Length @@ -151,7 +199,7 @@ Episódio Description - Descrição + Descrição Category @@ -159,7 +207,7 @@ Episódio Link - Link + Link Post From @@ -184,7 +232,7 @@ Car&t/Cut Subscription &Report - &Relatório de + &Relatório de Assinaturas @@ -197,15 +245,15 @@ Assinaturas Delete Podcast - Deletar PodCast + Deletar PodCast Are you sure you want to delete this podcast? - Tem certeza que você quer deletar este podcast? + Tem certeza que você quer deletar este podcast? Deleting Podcast... - Deletando Podcast... + Deletando Podcast... Internal Error @@ -229,11 +277,6 @@ Continuar deletando cast? Never Nunca - - Unable to delete remote audio! - - - The server said: " @@ -246,6 +289,166 @@ Continuar deletando cast? Cancel + + Feed + + + + Post From +Log + + + + Podcast Item List + + + + Only Show Active Items + + + + Posted By + + + + at + + + + Start + + + + Expiration + + + + unknown + + + + on + + + + Delete Item + + + + Are you sure you want to delete this item? + + + + Deleting Item... + + + + Select Audio File + + + + SHA1 + + + + [none] + + + + Unable to drop remote audio! + + + + + Unable to update remote XML data! + + + + Status + + + + + LogDialog + + OK + + + + Cancel + + + + List Log + + + + At least one log event must be selected! + + + + List Log Events + + + + + LogModel + + Start Time + + + + Trans + + + + Title + Título + + + Artist + + + + Cart + + + + Group + + + + Length + Duração + + + Client + + + + Agency + + + + Label + + + + Source + + + + Ext Data + + + + Line ID + + + + Count + + MainWidget @@ -271,7 +474,7 @@ Continuar deletando cast? Description - Descrição + Descrição Casts @@ -303,32 +506,87 @@ Feed Unknown command option + + Public URL + + + + Copy URL to +Clipboard + + + + [superfeed] + + PickReportDates - - Select Report Dates - - - - &Start Date: - - &Select - &Selecionar + &Selecionar + + + RenderDialog - &End Date: + [now] - &Generate -Report + As Specified - C&lose + Virtual Start Time + + + + Stop Rendering + + + + Treat as PLAY + + + + At STOP transition + + + + &Cancel + &Cancelar + + + Log Render Options + + + + OK + + + + Select + + + + Selected Log Events + + + + Lines + + + + through + + + + All lines + + + + Line diff --git a/rdcastmanager/render_dialog.cpp b/rdcastmanager/render_dialog.cpp new file mode 100644 index 00000000..b9492272 --- /dev/null +++ b/rdcastmanager/render_dialog.cpp @@ -0,0 +1,224 @@ +// render_dialog.cpp +// +// Render Log Dialog for Rivendell. +// +// (C) Copyright 2020 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 + +#include + +#include "render_dialog.h" + +RenderDialog::RenderDialog(QWidget *parent) + : RDDialog(parent) +{ + d_start_line=NULL; + d_end_line=NULL; + + setWindowTitle("RDCastManager - "+tr("Log Render Options")); + + // + // Fix the Window Size + // + setMinimumSize(sizeHint()); + setMaximumSize(sizeHint()); + + // + // Dialogs + // + d_log_dialog=new LogDialog(this); + + // + // Start Time + // + d_start_time_box=new QComboBox(this); + connect(d_start_time_box,SIGNAL(activated(int)), + this,SLOT(startTimeActivatedData(int))); + d_start_time_box->insertItem(tr("[now]")); + d_start_time_box->insertItem(tr("As Specified")); + d_start_time_label=new QLabel(tr("Virtual Start Time")+":",this); + d_start_time_label->setFont(labelFont()); + d_start_time_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + d_start_time_edit=new QTimeEdit(this); + d_start_time_edit->setDisplayFormat("hh:mm:ss"); + d_start_time_edit->setDisabled(true); + + // + // Ignore STOP + // + d_ignorestop_box=new QComboBox(this); + d_ignorestop_box->insertItem(0,tr("Stop Rendering")); + d_ignorestop_box->insertItem(1,tr("Treat as PLAY")); + d_ignorestop_label=new QLabel(tr("At STOP transition")+":",this); + d_ignorestop_label->setFont(labelFont()); + d_ignorestop_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + + // + // Select Log Events + // + d_select_label_label=new QLabel(tr("Selected Log Events"),this); + d_select_label_label->setFont(labelFont()); + d_select_label_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + d_select_label=new QLabel(this); + d_select_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + d_select_label->setFrameStyle(QFrame::Plain); + d_select_label->setFrameShape(QFrame::Box); + d_select_label->setLineWidth(1); + d_select_label->setMidLineWidth(1); + d_select_button=new QPushButton(tr("Select"),this); + d_select_button->setFont(subButtonFont()); + connect(d_select_button,SIGNAL(clicked()),this,SLOT(selectData())); + + // + // OK Button + // + d_ok_button=new QPushButton(tr("OK"),this); + d_ok_button->setGeometry(sizeHint().width()-90,sizeHint().height()-60,80,50); + d_ok_button->setFont(buttonFont()); + d_ok_button->setDefault(true); + connect(d_ok_button,SIGNAL(clicked()),this,SLOT(okData())); + + // + // Cancel Button + // + d_cancel_button=new QPushButton(tr("&Cancel"),this); + d_cancel_button-> + setGeometry(sizeHint().width()-90,sizeHint().height()-60,80,50); + d_cancel_button->setFont(buttonFont()); + connect(d_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); +} + + +RenderDialog::~RenderDialog() +{ + delete d_log_dialog; +} + + +QSize RenderDialog::sizeHint() const +{ + return QSize(500,145); +} + + +int RenderDialog::exec(RDLogEvent *log,QTime *start_time, + bool *ignore_stops,int *start_line,int *end_line) +{ + d_log=log; + d_start_time=start_time; + d_start_time_edit->setDisabled(start_time->isNull()); + d_start_time_box->setCurrentIndex(0); + d_ignore_stops=ignore_stops; + if(*ignore_stops) { + d_ignorestop_box->setCurrentIndex(1); + } + else { + d_ignorestop_box->setCurrentIndex(0); + } + d_start_line=start_line; + d_modified_start_line=*start_line; + d_modified_end_line=*end_line; + d_end_line=end_line; + UpdateLogEventsSelection(); + + return QDialog::exec(); +} + + +void RenderDialog::startTimeActivatedData(int n) +{ + d_start_time_edit->setEnabled(n==1); +} + + +void RenderDialog::selectData() +{ + if(d_log_dialog->exec(d_log,&d_modified_start_line,&d_modified_end_line)) { + UpdateLogEventsSelection(); + } +} + + +void RenderDialog::okData() +{ + if(d_start_time_box->currentIndex()==0) { + *d_start_time=QTime(); + } + else { + *d_start_time=d_start_time_edit->time(); + } + *d_ignore_stops=d_ignorestop_box->currentIndex(); + *d_start_line=d_modified_start_line; + *d_end_line=d_modified_end_line; + + done(true); +} + + +void RenderDialog::cancelData() +{ + done(false); +} + + +void RenderDialog::closeEvent(QCloseEvent *e) +{ + cancelData(); +} + + +void RenderDialog::resizeEvent(QResizeEvent *e) +{ + d_start_time_label->setGeometry(10,2,135,20); + d_start_time_box->setGeometry(150,2,160,20); + d_start_time_edit->setGeometry(315,2,80,20); + + d_ignorestop_label->setGeometry(10,24,135,20); + d_ignorestop_box->setGeometry(150,24,160,20); + + d_select_label_label->setGeometry(10,46,135,20); + d_select_label->setGeometry(150,46,160,20); + d_select_button->setGeometry(315,43,70,24); + + d_ok_button->setGeometry(size().width()-180,size().height()-60,80,50); + d_cancel_button->setGeometry(size().width()-90,size().height()-60,80,50); +} + + +void RenderDialog::UpdateLogEventsSelection() +{ + if((d_modified_start_line==0)&&(d_modified_end_line==(d_log->size()-1))) { + d_select_label->setText(tr("All lines")); + } + else { + if(d_modified_start_line==d_modified_end_line) { + d_select_label->setText(tr("Line")+ + QString().sprintf(" %d",d_modified_start_line)); + } + else { + d_select_label->setText(tr("Lines")+ + QString().sprintf(" %d ",d_modified_start_line)+ + tr("through")+ + QString().sprintf(" %d",d_modified_end_line)); + } + } +} diff --git a/rdcastmanager/render_dialog.h b/rdcastmanager/render_dialog.h new file mode 100644 index 00000000..7e28c7da --- /dev/null +++ b/rdcastmanager/render_dialog.h @@ -0,0 +1,87 @@ +// render_dialog.h +// +// Log Rendering Dialog for rdcastmanager(1) +// +// (C) Copyright 2020 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. +// + +#ifndef RENDER_DIALOG_H +#define RENDER_DIALOG_H + +#include +#include + +#include +#include +#include + +#include "logdialog.h" + +// +// Widget Settings +// +#define IMPORT_BAR_INTERVAL 500 +#define IMPORT_TEMP_BASENAME "rdlib" + +class RenderDialog : public RDDialog +{ + Q_OBJECT + public: + RenderDialog(QWidget *parent=0); + ~RenderDialog(); + QSize sizeHint() const; + + public slots: + int exec(RDLogEvent *log,QTime *start_time,bool *ignore_stops, + int *start_line,int *end_line); + + private slots: + void startTimeActivatedData(int n); + void selectData(); + void okData(); + void cancelData(); + + protected: + void closeEvent(QCloseEvent *e); + void resizeEvent(QResizeEvent *e); + + private: + void UpdateLogEventsSelection(); + QLabel *d_start_time_label; + QComboBox *d_start_time_box; + QTimeEdit *d_start_time_edit; + QLabel *d_ignorestop_label; + QComboBox *d_ignorestop_box; + QLabel *d_select_label_label; + QLabel *d_select_label; + QPushButton *d_select_button; + QPushButton *d_ok_button; + QPushButton *d_cancel_button; + + LogDialog *d_log_dialog; + + int d_modified_start_line; + int d_modified_end_line; + + QTime *d_start_time; + bool *d_ignore_stops; + int *d_start_line; + int *d_end_line; + RDLogEvent *d_log; +}; + + +#endif // RDRENDER_DIALOG_H diff --git a/rdcatch/edit_download.cpp b/rdcatch/edit_download.cpp index d13cf825..20622e73 100644 --- a/rdcatch/edit_download.cpp +++ b/rdcatch/edit_download.cpp @@ -433,7 +433,7 @@ void EditDownload::urlChangedData(const QString &str) { QUrl url(str); QString protocol=url.protocol(); - if((protocol=="ftp")||(protocol=="http")||(protocol=="file")|| + if((protocol=="ftp")||(protocol=="ftps")||(protocol=="http")||(protocol=="file")|| (protocol=="scp")||(protocol=="sftp")) { edit_username_label->setEnabled(true); edit_username_edit->setEnabled(true); @@ -502,7 +502,7 @@ void EditDownload::okData() } QUrl url(edit_url_edit->text()); QString protocol=url.protocol(); - if((protocol!="ftp")&&(protocol!="http")&&(protocol!="https")&& + if((protocol!="ftp")&&(protocol!="ftps")&&(protocol!="http")&&(protocol!="https")&& (protocol!="file")&&(protocol!="scp")&&(protocol!="sftp")) { QMessageBox::warning(this, tr("Invalid URL"),tr("Unsupported URL protocol!")); diff --git a/rdcatch/edit_upload.cpp b/rdcatch/edit_upload.cpp index e1711013..d4a20172 100644 --- a/rdcatch/edit_upload.cpp +++ b/rdcatch/edit_upload.cpp @@ -449,7 +449,7 @@ void EditUpload::urlChangedData(const QString &str) { QUrl url(str); QString protocol=url.protocol().lower(); - if((protocol=="ftp")||(protocol=="file")|| + if((protocol=="ftp")||(protocol=="ftps")||(protocol=="file")|| (protocol=="scp")||(protocol=="sftp")) { edit_username_label->setEnabled(true); edit_username_edit->setEnabled(true); @@ -527,7 +527,7 @@ void EditUpload::okData() } QUrl url(edit_url_edit->text()); QString protocol=url.protocol(); - if((protocol!="ftp")&&(protocol!="file")&& + if((protocol!="ftp")&&(protocol!="ftps")&&(protocol!="file")&& (protocol!="scp")&&(protocol!="sftp")) { QMessageBox::warning(this, tr("Invalid URL"),tr("Unsupported URL protocol!")); diff --git a/rdcatchd/batch.cpp b/rdcatchd/batch.cpp index 309e4acf..c43d7fed 100644 --- a/rdcatchd/batch.cpp +++ b/rdcatchd/batch.cpp @@ -2,7 +2,7 @@ // // Batch Routines for the Rivendell netcatcher daemon // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-2020 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 @@ -212,7 +212,10 @@ void MainObject::RunDownload(CatchEvent *evt) url_username=RD_ANON_FTP_USERNAME; url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION; } - switch((conv_err=conv->runDownload(url_username,url_password, + // + // FIXME: Finish implementing public key support! + // + switch((conv_err=conv->runDownload(url_username,url_password,"",false, rda->config()->logXloadDebugData()))) { case RDDownload::ErrorOk: rda->syslog(LOG_INFO,"finished download of %s to %s, id=%d", @@ -314,7 +317,7 @@ void MainObject::RunUpload(CatchEvent *evt) (const char *)evt->tempName().toUtf8(), (const char *)evt->resolvedUrl().toUtf8(), evt->id()); - RDUpload *conv=new RDUpload(this); + RDUpload *conv=new RDUpload(rda->config(),this); conv->setSourceFile(evt->tempName()); conv->setDestinationUrl(evt->resolvedUrl()); QString url_username=evt->urlUsername(); @@ -324,7 +327,10 @@ void MainObject::RunUpload(CatchEvent *evt) url_username=RD_ANON_FTP_USERNAME; url_password=QString(RD_ANON_FTP_PASSWORD)+"-"+VERSION; } - switch((conv_err=conv->runUpload(url_username,url_password, + // + // FIXME: Finish implementing ssh(1) identity keys! + // + switch((conv_err=conv->runUpload(url_username,url_password,"",false, rda->config()->logXloadDebugData()))) { case RDUpload::ErrorOk: catch_connect->setExitCode(evt->id(),RDRecording::Ok,tr("Ok")); diff --git a/rdhpi/rdhpi_cs.ts b/rdhpi/rdhpi_cs.ts index 64832049..dd4b5746 100644 --- a/rdhpi/rdhpi_cs.ts +++ b/rdhpi/rdhpi_cs.ts @@ -21,7 +21,11 @@ Unknown Error: - Neznámá chyba: + Neznámá chyba: + + + Unknown RDHpiPlayStream Error: + @@ -44,7 +48,11 @@ Unknown Error: - Neznámá chyba: + Neznámá chyba: + + + Unknown RDHpiRecordStream Error: + diff --git a/rdhpi/rdhpi_de.ts b/rdhpi/rdhpi_de.ts index cfdd965d..217609d3 100644 --- a/rdhpi/rdhpi_de.ts +++ b/rdhpi/rdhpi_de.ts @@ -21,7 +21,11 @@ Unknown Error: - Unbekannter Fehler: + Unbekannter Fehler: + + + Unknown RDHpiPlayStream Error: + @@ -44,7 +48,11 @@ Unknown Error: - Unbekannter Fehler: + Unbekannter Fehler: + + + Unknown RDHpiRecordStream Error: + diff --git a/rdhpi/rdhpi_es.ts b/rdhpi/rdhpi_es.ts index a2efc5f7..c474b187 100644 --- a/rdhpi/rdhpi_es.ts +++ b/rdhpi/rdhpi_es.ts @@ -20,7 +20,7 @@ - Unknown Error: + Unknown RDHpiPlayStream Error: @@ -43,7 +43,7 @@ - Unknown Error: + Unknown RDHpiRecordStream Error: diff --git a/rdhpi/rdhpi_fr.ts b/rdhpi/rdhpi_fr.ts index a2efc5f7..c474b187 100644 --- a/rdhpi/rdhpi_fr.ts +++ b/rdhpi/rdhpi_fr.ts @@ -20,7 +20,7 @@ - Unknown Error: + Unknown RDHpiPlayStream Error: @@ -43,7 +43,7 @@ - Unknown Error: + Unknown RDHpiRecordStream Error: diff --git a/rdhpi/rdhpi_nb.ts b/rdhpi/rdhpi_nb.ts index b95eb1bc..ecd5e249 100644 --- a/rdhpi/rdhpi_nb.ts +++ b/rdhpi/rdhpi_nb.ts @@ -21,7 +21,11 @@ Unknown Error: - Ukjend feil: + Ukjend feil: + + + Unknown RDHpiPlayStream Error: + @@ -44,7 +48,11 @@ Unknown Error: - Ukjend feil: + Ukjend feil: + + + Unknown RDHpiRecordStream Error: + diff --git a/rdhpi/rdhpi_nn.ts b/rdhpi/rdhpi_nn.ts index b95eb1bc..ecd5e249 100644 --- a/rdhpi/rdhpi_nn.ts +++ b/rdhpi/rdhpi_nn.ts @@ -21,7 +21,11 @@ Unknown Error: - Ukjend feil: + Ukjend feil: + + + Unknown RDHpiPlayStream Error: + @@ -44,7 +48,11 @@ Unknown Error: - Ukjend feil: + Ukjend feil: + + + Unknown RDHpiRecordStream Error: + diff --git a/rdhpi/rdhpi_pt_BR.ts b/rdhpi/rdhpi_pt_BR.ts index 22e48ec0..c8a72c21 100644 --- a/rdhpi/rdhpi_pt_BR.ts +++ b/rdhpi/rdhpi_pt_BR.ts @@ -21,7 +21,11 @@ Unknown Error: - Erro Desconhecido: + Erro Desconhecido: + + + Unknown RDHpiPlayStream Error: + @@ -44,7 +48,11 @@ Unknown Error: - Erro Desconhecido: + Erro Desconhecido: + + + Unknown RDHpiRecordStream Error: + diff --git a/rdhpi/rdhpiplaystream.cpp b/rdhpi/rdhpiplaystream.cpp index d9ecbb0a..1b826a88 100644 --- a/rdhpi/rdhpiplaystream.cpp +++ b/rdhpi/rdhpiplaystream.cpp @@ -159,7 +159,7 @@ QString RDHPIPlayStream::errorString(RDHPIPlayStream::Error err) break; default: - str=QString(tr("Unknown Error:")); + str=QString(tr("Unknown RDHpiPlayStream Error:")); return QString().sprintf("%s %d\n",(const char *)str,err); break; } diff --git a/rdhpi/rdhpirecordstream.cpp b/rdhpi/rdhpirecordstream.cpp index 986baf1c..9939bc21 100644 --- a/rdhpi/rdhpirecordstream.cpp +++ b/rdhpi/rdhpirecordstream.cpp @@ -134,7 +134,7 @@ QString RDHPIRecordStream::errorString(RDHPIRecordStream::Error err) break; default: - str=QString(tr("Unknown Error:")); + str=QString(tr("Unknown RDHpiRecordStream Error:")); return QString().sprintf("%s %d\n",(const char *)str,err); break; } diff --git a/rdlogedit/edit_chain.cpp b/rdlogedit/edit_chain.cpp index dcbd91ed..588f17bf 100644 --- a/rdlogedit/edit_chain.cpp +++ b/rdlogedit/edit_chain.cpp @@ -93,7 +93,7 @@ void EditChain::selectLogData() RDListLogs *d= new RDListLogs(&logname,RDLogFilter::UserFilter,this); - if(d->exec()!=0) { + if(!d->exec()) { delete d; return; } diff --git a/rdrepld/citadelxds.cpp b/rdrepld/citadelxds.cpp index 4f0165ff..cfc28f3e 100644 --- a/rdrepld/citadelxds.cpp +++ b/rdrepld/citadelxds.cpp @@ -2,7 +2,7 @@ // // Replicator implementation for the Citadel XDS Portal // -// (C) Copyright 2010-2019 Fred Gleason +// (C) Copyright 2010-2020 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 @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -364,11 +366,14 @@ bool CitadelXds::PostCut(const QString &cutname,const QString &filename) // // Upload File // - RDUpload *upload=new RDUpload(); + RDUpload *upload=new RDUpload(rda->config()); upload->setSourceFile(tempfile); upload->setDestinationUrl(config()->url()+"/"+filename); + // + // FIXME: Finish implementing ssh(1) id keys! + // switch(upload_err=upload->runUpload(config()->urlUsername(), - config()->urlPassword(), + config()->urlPassword(),"",false, rda->config()->logXloadDebugData())) { case RDUpload::ErrorOk: break; @@ -418,8 +423,11 @@ void CitadelXds::PurgeCuts() Q3Url url(path+q->value(1).toString()); conv=new RDDelete(rda->config()); conv->setTargetUrl(url); + // + // FIXME: Finish implementing ssh(1) key support! + // if((conv_err=conv->runDelete(config()->urlUsername(), - config()->urlPassword(), + config()->urlPassword(),"",false, rda->config()->logXloadDebugData()))== RDDelete::ErrorOk) { sql=QString().sprintf("delete from REPL_CART_STATE where ID=%d", diff --git a/utils/rdpurgecasts/Makefile.am b/rdrssd/Makefile.am similarity index 70% rename from utils/rdpurgecasts/Makefile.am rename to rdrssd/Makefile.am index 287fd565..2e0453a0 100644 --- a/utils/rdpurgecasts/Makefile.am +++ b/rdrssd/Makefile.am @@ -1,6 +1,8 @@ ## Makefile.am ## -## (C) Copyright 2007-2020 Fred Gleason +## Rivendell RSS Processor Service +## +## (C) Copyright 2020 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 @@ -15,30 +17,32 @@ ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## +## ## Use automake to process this into a Makefile.in -AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -I$(top_srcdir)/lib @QT4_CFLAGS@ @MUSICBRAINZ_CFLAGS@ -DQT3_SUPPORT -I/usr/include/Qt3Support -LIBS = -L$(top_srcdir)/lib +AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -I$(top_srcdir)/lib @QT4_CFLAGS@ @MUSICBRAINZ_CFLAGS@ -I/usr/include/Qt3Support +LIBS = -L$(top_srcdir)/lib -L$(top_srcdir)/rdhpi MOC = @QT_MOC@ # The dependency for qt's Meta Object Compiler (moc) moc_%.cpp: %.h $(MOC) $< -o $@ -bin_PROGRAMS = rdpurgecasts -dist_rdpurgecasts_SOURCES = rdpurgecasts.cpp rdpurgecasts.h +sbin_PROGRAMS = rdrssd -rdpurgecasts_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support +dist_rdrssd_SOURCES = rdrssd.cpp rdrssd.h + +nodist_rdrssd_SOURCES = moc_rdrssd.cpp + +rdrssd_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support CLEANFILES = *~\ *.idb\ *ilk\ *.obj\ *.pdb\ - *.qm\ moc_* MAINTAINERCLEANFILES = *~\ - Makefile.in\ - moc_* + Makefile.in diff --git a/rdrssd/rdrssd.cpp b/rdrssd/rdrssd.cpp new file mode 100644 index 00000000..36049675 --- /dev/null +++ b/rdrssd/rdrssd.cpp @@ -0,0 +1,204 @@ +// rdrssd.cpp +// +// Rivendell RSS Processor Service +// +// (C) Copyright 2020 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 +#include +#include +#include + +#include "rdrssd.h" + +MainObject::MainObject(QObject *parent) + : QObject(parent) +{ + QString err_msg; + bool ok=false; + RDApplication::ErrorType err_type=RDApplication::ErrorOk; + + d_process_interval=RDRSSD_DEFAULT_PROCESS_INTERVAL; + + // + // Open the Database + // + rda=new RDApplication("rdrssd","rdrssd",RDRSSD_USAGE,this); + if(!rda->open(&err_msg,&err_type,false)) { + fprintf(stderr,"rdrssd: %s\n",(const char *)err_msg); + exit(1); + } + + // + // Shed root permissions + // + if(getuid()==0) { + if(setgid(rda->config()->pypadGid())!=0) { + rda->syslog(LOG_ERR,"unable to set GID to %d [%s], exiting", + rda->config()->pypadGid(),strerror(errno)); + exit(1); + } + if(setuid(rda->config()->pypadUid())!=0) { + rda->syslog(LOG_ERR,"unable to set UID to %d [%s], exiting", + rda->config()->pypadUid(),strerror(errno)); + exit(1); + } + } + + // + // Read Command Options + // + for(unsigned i=0;icmdSwitch()->keys();i++) { + if(rda->cmdSwitch()->key(i)=="--process-interval") { + d_process_interval=1000*rda->cmdSwitch()->value(i).toInt(&ok); + if((!ok)||(d_process_interval<=0)) { + fprintf(stderr, + "rdrssd: invalid value specified for --process-interval\n"); + exit(1); + } + rda->cmdSwitch()->setProcessed(i,true); + } + if(!rda->cmdSwitch()->processed(i)) { + rda->syslog(LOG_ERR,"unknown command option \"%s\"", + (const char *)rda->cmdSwitch()->key(i).toUtf8()); + exit(2); + } + } + + // + // Connect to ripcd(8) + // + rda->ripc()-> + connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password()); + + // + // Scan Timer + // + d_timer=new QTimer(this); + d_timer->setSingleShot(true); + connect(d_timer,SIGNAL(timeout()),this,SLOT(timeoutData())); + d_timer->start(0); + + rda->syslog(LOG_DEBUG,"started"); +} + + +void MainObject::timeoutData() +{ + QString sql; + RDSqlQuery *q=NULL; + + sql=QString("select ")+ + "KEY_NAME "+ // 00 + "from FEEDS where "+ + "IS_SUPERFEED='N'"; + q=new RDSqlQuery(sql); + while(q->next()) { + ProcessFeed(q->value(0).toString()); + } + delete q; + + d_timer->start(d_process_interval); +} + + +void MainObject::ProcessFeed(const QString &key_name) +{ + QString sql; + RDSqlQuery *q=NULL; + QDateTime now=QDateTime::currentDateTime(); + QString now_str="\""+now.toString("yyyy-MM-dd hh:mm:ss")+"\""; + QString err_msg; + RDFeed *feed=new RDFeed(key_name,rda->config(),this); + + // + // Update Posted XML + // + sql=QString("select ")+ + "PODCASTS.ID,"+ // 00 + "PODCASTS.EXPIRATION_DATETIME "+ // 01 + "from PODCASTS left join FEEDS "+ + "on PODCASTS.FEED_ID=FEEDS.ID where "+ + "(FEEDS.KEY_NAME=\""+RDEscapeString(key_name)+"\") && "+ + "((FEEDS.LAST_BUILD_DATETIMEnext()) { + bool deleted=false; + if(q->value(1).toDateTime()config(),q->value(0).toUInt()); + if(!cast->dropAudio(feed,&err_msg,false)) { + rda->syslog(LOG_WARNING, + "audio purge failed for cast %u [%s] on feed \"%s\" [%s]", + q->value(0).toUInt(), + cast->itemTitle().toUtf8().constData(), + feed->keyName().toUtf8().constData(), + err_msg.toUtf8().constData()); + } + sql=QString("delete from PODCASTS where ")+ + QString().sprintf("ID=%u",q->value(0).toUInt()); + RDSqlQuery::apply(sql); + rda->syslog(LOG_INFO,"purged cast %u [%s] from feed \"%s\"", + q->value(0).toUInt(),cast->itemTitle().toUtf8().constData(), + feed->keyName().toUtf8().constData()); + delete cast; + + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction,feed->keyName()); + rda->ripc()->sendNotification(RDNotification::FeedItemType, + RDNotification::DeleteAction, + q->value(0).toUInt()); + deleted=true; + } + if(feed->postXml()) { + rda->syslog(LOG_DEBUG, + "repost of XML for feed \"%s\" triggered by cast id %u", + key_name.toUtf8().constData(),q->value(0).toUInt()); + if(!deleted) { + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction, + feed->keyName()); + rda->ripc()->sendNotification(RDNotification::FeedType, + RDNotification::ModifyAction, + feed->keyName()); + } + } + else { + rda->syslog(LOG_WARNING,"repost of XML for feed \"%s\" failed"); + } + } + delete q; + + delete feed; +} + + +int main(int argv,char *argc[]) +{ + QCoreApplication a(argv,argc); + + new MainObject(); + + return a.exec(); +} diff --git a/utils/rdpurgecasts/rdpurgecasts.h b/rdrssd/rdrssd.h similarity index 63% rename from utils/rdpurgecasts/rdpurgecasts.h rename to rdrssd/rdrssd.h index a35d46a3..b603fa14 100644 --- a/utils/rdpurgecasts/rdpurgecasts.h +++ b/rdrssd/rdrssd.h @@ -1,8 +1,8 @@ -// rdpurgecasts.h +// rdrssd.h // -// A Utility to Purge Expired Podcasts. +// Rivendell RSS Processor Service // -// (C) Copyright 2002-2007,2016-2018 Fred Gleason +// (C) Copyright 2020 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 @@ -18,22 +18,29 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#ifndef RDPURGECASTS_H -#define RDPURGECASTS_H +#ifndef RDRSSD_H +#define RDRSSD_H -#include +#include +#include -#define RDPURGECASTS_USAGE "[--help] [--verbose]\n\nPurge expired podcasts.\n" +#define RDRSSD_DEFAULT_PROCESS_INTERVAL 60000 +#define RDRSSD_USAGE "[--process-interval=]\n\n" class MainObject : public QObject { + Q_OBJECT public: MainObject(QObject *parent=0); + private slots: + void timeoutData(); + private: - void PurgeCast(unsigned id); - bool purge_verbose; + void ProcessFeed(const QString &key_name); + int d_process_interval; + QTimer *d_timer; }; -#endif // RDPURGECASTS_H +#endif // RDRSSD_H diff --git a/rdservice/maint_routines.cpp b/rdservice/maint_routines.cpp index d2c0ca11..26e28f01 100644 --- a/rdservice/maint_routines.cpp +++ b/rdservice/maint_routines.cpp @@ -2,7 +2,7 @@ // // Rivendell Maintenance Routines // -// (C) Copyright 2008-2019 Fred Gleason +// (C) Copyright 2008-2020 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 @@ -83,11 +83,6 @@ void MainObject::RunSystemMaintRoutine() args.push_back("--system"); RunEphemeralProcess(RDSERVICE_SYSTEMMAINT_ID, QString(RD_PREFIX)+"/bin/rdmaint",args); - - args.clear(); - RunEphemeralProcess(RDSERVICE_PURGECASTS_ID, - QString(RD_PREFIX)+"/bin/rdpurgecasts",args); - rda->syslog(LOG_INFO,"ran system-wide maintenance routines"); } diff --git a/rdservice/rdservice.h b/rdservice/rdservice.h index 7727447d..03ffbf95 100644 --- a/rdservice/rdservice.h +++ b/rdservice/rdservice.h @@ -2,7 +2,7 @@ // // Rivendell Services Manager // -// (C) Copyright 2018 Fred Gleason +// (C) Copyright 2018-2020 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 @@ -34,9 +34,9 @@ #define RDSERVICE_RDPADENGINED_ID 4 #define RDSERVICE_RDVAIRPLAYD_ID 5 #define RDSERVICE_RDREPLD_ID 6 -#define RDSERVICE_LOCALMAINT_ID 7 -#define RDSERVICE_SYSTEMMAINT_ID 8 -#define RDSERVICE_PURGECASTS_ID 9 +#define RDSERVICE_RDRSSD_ID 7 +#define RDSERVICE_LOCALMAINT_ID 8 +#define RDSERVICE_SYSTEMMAINT_ID 9 #define RDSERVICE_LAST_ID 10 #define RDSERVICE_FIRST_DROPBOX_ID 100 @@ -46,7 +46,8 @@ class MainObject : public QObject public: enum StartupTarget {TargetCaed=0,TargetRipcd=1,TargetRdcatchd=2, TargetRdpadd=3,TargetRdpadengined=4, - TargetRdvairplayd=5,TargetRdrepld=6,TargetAll=7}; + TargetRdvairplayd=5,TargetRdrepld=6, + TargetRdrssd=7,TargetAll=8}; MainObject(QObject *parent=0); private slots: diff --git a/rdservice/startup.cpp b/rdservice/startup.cpp index dd9ecca7..911977eb 100644 --- a/rdservice/startup.cpp +++ b/rdservice/startup.cpp @@ -40,6 +40,7 @@ bool MainObject::Startup(QString *err_msg) // // Kill Stale Programs // + KillProgram("rdrssd"); KillProgram("rdrepld"); KillProgram("rdvairplayd"); KillProgram("rdpadengined"); @@ -180,6 +181,31 @@ bool MainObject::Startup(QString *err_msg) return true; } + // + // rdrssd(8) + // + sql=QString("select RSS_PROCESSOR_STATION from SYSTEM"); + q=new RDSqlQuery(sql); + if(q->first()) { + if(q->value(0).toString().toLower()==rda->station()->name().toLower()) { + svc_processes[RDSERVICE_RDRSSD_ID]= + new RDProcess(RDSERVICE_RDRSSD_ID,this); + args.clear(); + svc_processes[RDSERVICE_RDRSSD_ID]-> + start(QString(RD_PREFIX)+"/sbin/rdrssd",args); + if(!svc_processes[RDSERVICE_RDRSSD_ID]->process()->waitForStarted(-1)) { + *err_msg=tr("unable to start rdrssd(8)")+": "+ + svc_processes[RDSERVICE_RDRSSD_ID]->errorText(); + return false; + } + } + if(svc_startup_target==MainObject::TargetRdrssd) { + fprintf(stderr,"Startup target rdrssd(8) reached\n"); + return true; + } + } + delete q; + if(!StartDropboxes(err_msg)) { return false; } @@ -352,6 +378,9 @@ QString MainObject::TargetCommandString(MainObject::StartupTarget target) const case MainObject::TargetRdrepld: return QString("--end-startup-after-rdrepld"); + case MainObject::TargetRdrssd: + return QString("--end-startup-after-rdrssd"); + case MainObject::TargetAll: break; } diff --git a/rivendell.spec.in b/rivendell.spec.in index baa0d486..37477450 100644 --- a/rivendell.spec.in +++ b/rivendell.spec.in @@ -1,7 +1,7 @@ ## rivendell.spec.in ## ## The Rivendell Radio Automation System -## Copyright (C) 2002-2019 Fred Gleason +## Copyright (C) 2002-2020 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of version 2 of the GNU General Public License as @@ -147,6 +147,9 @@ cp docs/misc/ando_interface.odt $RPM_BUILD_ROOT/@DOC_PATH@/misc/ mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@ mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@/apis cp docs/apis/*.pdf $RPM_BUILD_ROOT/@DOC_PATH@/apis/ +mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@/dtds +cp docs/dtds/*.html $RPM_BUILD_ROOT/@DOC_PATH@/dtds/ +cp docs/dtds/*.pdf $RPM_BUILD_ROOT/@DOC_PATH@/dtds/ mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@/tables cp docs/tables/*.txt $RPM_BUILD_ROOT/@DOC_PATH@/tables/ mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@/scripts @@ -264,7 +267,6 @@ rm -rf $RPM_BUILD_ROOT @LOCAL_PREFIX@/bin/rdexport @LOCAL_PREFIX@/bin/rdimport @LOCAL_PREFIX@/bin/rdmetadata -@LOCAL_PREFIX@/bin/rdpurgecasts @LOCAL_PREFIX@/bin/rdmaint @LOCAL_PREFIX@/bin/rdcollect @LOCAL_PREFIX@/bin/rdconvert @@ -375,9 +377,7 @@ rm -rf $RPM_BUILD_ROOT @libexecdir@/*.gif @libexecdir@/*.html @libexecdir@/*.js -@libexecdir@/*.mp3 @libexecdir@/*.png -@libexecdir@/*.xml @libexecdir@/rdcastmanager.cgi %attr(6755,root,root) @libexecdir@/rdxport.cgi %attr(6755,root,root) @libexecdir@/webget.cgi @@ -416,6 +416,7 @@ rm -rf $RPM_BUILD_ROOT @LOCAL_PREFIX@/sbin/rdrepld @LOCAL_PREFIX@/sbin/rdpadd @LOCAL_PREFIX@/sbin/rdpadengined +@LOCAL_PREFIX@/sbin/rdrssd @LOCAL_PREFIX@/sbin/rdmarkerset @LOCAL_PREFIX@/sbin/rdcleandirs @LOCAL_PREFIX@/sbin/rddbmgr @@ -468,6 +469,7 @@ rm -rf $RPM_BUILD_ROOT @LOCAL_PREFIX@/share/man/man7/rd_createticket.7.gz @LOCAL_PREFIX@/share/man/man7/rd_deleteaudio.7.gz @LOCAL_PREFIX@/share/man/man7/rd_deletelog.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_deletepodcast.7.gz @LOCAL_PREFIX@/share/man/man7/rd_editcart.7.gz @LOCAL_PREFIX@/share/man/man7/rd_editcut.7.gz @LOCAL_PREFIX@/share/man/man7/rd_export.7.gz @@ -489,9 +491,16 @@ rm -rf $RPM_BUILD_ROOT @LOCAL_PREFIX@/share/man/man7/rd_listschedcodes.7.gz @LOCAL_PREFIX@/share/man/man7/rd_listservices.7.gz @LOCAL_PREFIX@/share/man/man7/rd_listsystemsettings.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_postimage.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_postpodcast.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_postrss.7.gz @LOCAL_PREFIX@/share/man/man7/rd_removecart.7.gz @LOCAL_PREFIX@/share/man/man7/rd_removecut.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_removeimage.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_removepodcast.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_removerss.7.gz @LOCAL_PREFIX@/share/man/man7/rd_savelog.7.gz +@LOCAL_PREFIX@/share/man/man7/rd_savepodcast.7.gz @LOCAL_PREFIX@/share/man/man7/rd_trimaudio.7.gz @LOCAL_PREFIX@/share/man/man7/rd_unassignschedcode.7.gz diff --git a/tests/Makefile.am b/tests/Makefile.am index 41621ad0..e8f08290 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,7 +35,9 @@ noinst_PROGRAMS = audio_convert_test\ datedecode_test\ dateparse_test\ db_charset_test\ + delete_test\ download_test\ + feed_image_test\ getpids_test\ log_unlink_test\ mcast_recv_test\ @@ -78,9 +80,15 @@ dateparse_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -l dist_db_charset_test_SOURCES = db_charset_test.cpp db_charset_test.h db_charset_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support +dist_delete_test_SOURCES = delete_test.cpp delete_test.h +delete_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support + dist_download_test_SOURCES = download_test.cpp download_test.h download_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support +dist_feed_image_test_SOURCES = feed_image_test.cpp feed_image_test.h +feed_image_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support + dist_getpids_test_SOURCES = getpids_test.cpp getpids_test.h getpids_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support diff --git a/tests/delete_test.cpp b/tests/delete_test.cpp new file mode 100644 index 00000000..98f4fad7 --- /dev/null +++ b/tests/delete_test.cpp @@ -0,0 +1,122 @@ +// delete_test.cpp +// +// Test Rivendell file deletion routines. +// +// (C) Copyright 2010-2020 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 +#include + +#include "delete_test.h" + +MainObject::MainObject(QObject *parent) + :QObject(parent) +{ + QString err_msg; + + username=""; + password=""; + RDDelete::ErrorCode conv_err; + use_identity_file=false; + + // + // Open the Database + // + rda=new RDApplication("delete_test","delete_test",DELETE_TEST_USAGE,this); + if(!rda->open(&err_msg)) { + fprintf(stderr,"delete_test: %s\n",(const char *)err_msg); + exit(1); + } + + // + // Read Command Options + // + for(unsigned i=0;icmdSwitch()->keys();i++) { + if(rda->cmdSwitch()->key(i)=="--username") { + username=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--password") { + password=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--ssh-identity-filename") { + ssh_identity_filename=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--use-identity-file") { + use_identity_file=true; + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--target-url") { + target_url=QUrl(rda->cmdSwitch()->value(i)); + if(target_url.isRelative()) { + fprintf(stderr,"delete_test: URL's must be fully qualified\n"); + exit(1); + } + if(!target_url.isValid()) { + fprintf(stderr,"delete_test: invalid URL\n"); + exit(1); + } + rda->cmdSwitch()->setProcessed(i,true); + } + if(!rda->cmdSwitch()->processed(i)) { + fprintf(stderr,"delete_test: unknown command option \"%s\"\n", + (const char *)rda->cmdSwitch()->key(i)); + exit(2); + } + } + + // + // Sanity Checks + // + if(target_url.isEmpty()) { + fprintf(stderr,"delete_test: missing --target-url\n"); + exit(256); + } + + // + // Run the Test + // + RDDelete *conv=new RDDelete(rda->config(),this); + if(!conv->urlIsSupported(target_url)) { + fprintf(stderr,"delete_test: unsupported URL scheme\n"); + exit(1); + } + conv->setTargetUrl(target_url); + printf("Deleting...\n"); + conv_err=conv-> + runDelete(username,password,ssh_identity_filename,use_identity_file, + rda->config()->logXloadDebugData()); + printf("Result: %s\n",(const char *)RDDelete::errorText(conv_err)); + delete conv; + + exit(0); +} + + +int main(int argc,char *argv[]) +{ + QApplication a(argc,argv,false); + new MainObject(); + return a.exec(); +} diff --git a/web/rdfeed/rdfeed_script.h b/tests/delete_test.h similarity index 57% rename from web/rdfeed/rdfeed_script.h rename to tests/delete_test.h index 287e8705..3f472390 100644 --- a/web/rdfeed/rdfeed_script.h +++ b/tests/delete_test.h @@ -1,8 +1,8 @@ -// rdfeed_script.h +// delete_test.h // -// An RSS Feed Generator for Rivendell. +// Test Rivendell remote file deletion routines. // -// (C) Copyright 2002-2018 Fred Gleason +// (C) Copyright 2010-2020 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 @@ -18,14 +18,15 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // -#ifndef RDFEED_SCRIPT_H -#define RDFEED_SCRIPT_H +#ifndef DELETE_TEST_H +#define DELETE_TEST_H + +#include #include +#include -#include - -#define RDFEED_XML_USAGE "\n" +#define DELETE_TEST_USAGE "[options]\n\nTest the Rivendell deletion routines\n\nOptions are:\n--username=\n\n--password=\n\n--ssh-identity-key=\n\n--use-identity-file=y|n\n\n--target-url=\n\n" class MainObject : public QObject { @@ -33,14 +34,12 @@ class MainObject : public QObject MainObject(QObject *parent=0); private: - void ServeRss(const char *keyname,bool count); - void ServeLink(const char *keyname,int cast_id,bool count); - QString ResolveChannelWildcards(RDSqlQuery *chan_q); - QString ResolveItemWildcards(const QString &keyname, - RDSqlQuery *item_q,RDSqlQuery *chan_q); - bool ShouldCount(const QString &hdr); - void Redirect(const QString &url); + QString username; + QString password; + QUrl target_url; + QString ssh_identity_filename; + bool use_identity_file; }; -#endif // RDFEED_SCRIPT_H +#endif // DELETE_TEST_H diff --git a/tests/download_test.cpp b/tests/download_test.cpp index 61b94bf3..d06e259a 100644 --- a/tests/download_test.cpp +++ b/tests/download_test.cpp @@ -37,6 +37,7 @@ MainObject::MainObject(QObject *parent) username=""; password=""; RDDownload::ErrorCode conv_err; + use_identity_file=false; // // Open the Database @@ -59,12 +60,20 @@ MainObject::MainObject(QObject *parent) password=rda->cmdSwitch()->value(i); rda->cmdSwitch()->setProcessed(i,true); } + if(rda->cmdSwitch()->key(i)=="--ssh-identity-filename") { + ssh_identity_filename=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--use-identity-file") { + use_identity_file=true; + rda->cmdSwitch()->setProcessed(i,true); + } if(rda->cmdSwitch()->key(i)=="--source-url") { source_url=rda->cmdSwitch()->value(i); rda->cmdSwitch()->setProcessed(i,true); } - if(rda->cmdSwitch()->key(i)=="--destination-filename") { - destination_filename=rda->cmdSwitch()->value(i); + if(rda->cmdSwitch()->key(i)=="--destination-file") { + destination_file=rda->cmdSwitch()->value(i); rda->cmdSwitch()->setProcessed(i,true); } if(!rda->cmdSwitch()->processed(i)) { @@ -81,7 +90,7 @@ MainObject::MainObject(QObject *parent) fprintf(stderr,"download_test: missing source-url\n"); exit(256); } - if(destination_filename.isEmpty()) { + if(destination_file.isEmpty()) { fprintf(stderr,"download_test: missing destination-filename\n"); exit(256); } @@ -91,10 +100,11 @@ MainObject::MainObject(QObject *parent) // RDDownload *conv=new RDDownload(rda->config(),this); conv->setSourceUrl(source_url); - conv->setDestinationFile(destination_filename); + conv->setDestinationFile(destination_file); printf("Downloading...\n"); conv_err=conv-> - runDownload(username,password,rda->config()->logXloadDebugData()); + runDownload(username,password,ssh_identity_filename,use_identity_file, + rda->config()->logXloadDebugData()); printf("Result: %s\n",(const char *)RDDownload::errorText(conv_err)); delete conv; diff --git a/tests/download_test.h b/tests/download_test.h index 5b98e225..357691bf 100644 --- a/tests/download_test.h +++ b/tests/download_test.h @@ -25,7 +25,7 @@ #include -#define DOWNLOAD_TEST_USAGE "[options]\n\nTest the Rivendell download routines\n\nOptions are:\n--username=\n\n--password=\n\n--source-url=\n\n--destination-file=\n\n" +#define DOWNLOAD_TEST_USAGE "[options]\n\nTest the Rivendell download routines\n\nOptions are:\n--username=\n\n--password=\n\n--ssh-identity-key=\n\n--use-identity-file=y|n\n\n--source-url=\n\n--destination-file=\n\n" class MainObject : public QObject { @@ -36,7 +36,9 @@ class MainObject : public QObject QString username; QString password; QString source_url; - QString destination_filename; + QString destination_file; + QString ssh_identity_filename; + bool use_identity_file; }; diff --git a/tests/feed_image_test.cpp b/tests/feed_image_test.cpp new file mode 100644 index 00000000..0be7d9b1 --- /dev/null +++ b/tests/feed_image_test.cpp @@ -0,0 +1,290 @@ +// feed_image_test.cpp +// +// Test Rivendell image storage +// +// (C) Copyright 2010-2020 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 +#include + +#include +#include +#include + +#include +#include + +#include "feed_image_test.h" + +MainObject::MainObject(QObject *parent) + :QObject(parent) +{ + QString err_msg; + QString key_name; + bool ok=false; + MainObject::Command command=MainObject::None; + test_image_id=-1; + + // + // Open the Database + // + rda=new RDApplication("feed_image_test","feed_image_test",FEED_IMAGE_TEST_USAGE,this); + if(!rda->open(&err_msg)) { + fprintf(stderr,"feed_image_test: %s\n",(const char *)err_msg); + exit(1); + } + + // + // Read Command Options + // + for(unsigned i=0;icmdSwitch()->keys();i++) { + if(rda->cmdSwitch()->key(i)=="--feed") { + key_name=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--filename") { + test_filename=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--description") { + test_description=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--image-id") { + test_image_id=rda->cmdSwitch()->value(i).toInt(&ok); + if((!ok)||(test_image_id<0)) { + fprintf(stderr,"feed_image_test: invalid --image-id\n"); + exit(1); + } + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--list") { + if(command!=MainObject::None) { + fprintf(stderr, + "feed_image_test: --list, --pop and --push are mutually exclusive\n"); + exit(1); + } + command=MainObject::List; + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--pop") { + if(command!=MainObject::None) { + fprintf(stderr, + "feed_image_test: --list, --pop and --push are mutually exclusive\n"); + exit(1); + } + command=MainObject::Pop; + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--push") { + if(command!=MainObject::None) { + fprintf(stderr, + "feed_image_test: --list, --pop and --push are mutually exclusive\n"); + exit(1); + } + command=MainObject::Push; + rda->cmdSwitch()->setProcessed(i,true); + } + if(!rda->cmdSwitch()->processed(i)) { + fprintf(stderr,"feed_image_test: unknown command option \"%s\"\n", + (const char *)rda->cmdSwitch()->key(i)); + exit(2); + } + } + + // + // Sanity Checks + // + if(command==MainObject::None) { + fprintf(stderr,"feed_image_test: you must specify --list, --pop or --push\n"); + exit(1); + } + + + + + // + // Get the feed data + // + + // + // Dispatch + // + switch(command) { + case MainObject::List: + if(key_name.isEmpty()) { + fprintf(stderr,"feed_image_test: you must specify --feed=\n"); + exit(1); + } + test_feed=new RDFeed(key_name,rda->config(),this); + if(!test_feed->exists()) { + fprintf(stderr,"feed_image_test: no such feed\n"); + exit(1); + } + RunList(); + break; + + case MainObject::Pop: + if(test_image_id<0) { + fprintf(stderr,"feed_image_test: you must specify --image-id\n"); + exit(1); + } + if(test_filename.isEmpty()) { + fprintf(stderr, + "feed_image_test: you must specify --filename=\n"); + exit(1); + } + RunPop(); + break; + + case MainObject::Push: + if(key_name.isEmpty()) { + fprintf(stderr,"feed_image_test: you must specify --feed=\n"); + exit(1); + } + test_feed=new RDFeed(key_name,rda->config(),this); + if(!test_feed->exists()) { + fprintf(stderr,"feed_image_test: no such feed\n"); + exit(1); + } + if(test_filename.isEmpty()) { + fprintf(stderr, + "feed_image_test: you must specify --filename=\n"); + exit(1); + } + if(test_description.isEmpty()) { + fprintf(stderr, + "feed_image_test: you must specify --description=\n"); + exit(1); + } + RunPush(); + break; + + case MainObject::None: + break; + } + + exit(0); +} + + +void MainObject::RunList() +{ + QString sql; + RDSqlQuery *q=NULL; + + sql=QString("select ")+ + "ID,"+ // 00 + "WIDTH,"+ // 01 + "HEIGHT,"+ // 02 + "DEPTH,"+ // 03 + "DESCRIPTION "+ // 04 + "from FEED_IMAGES where "+ + "FEED_KEY_NAME=\""+RDEscapeString(test_feed->keyName())+"\""; + q=new RDSqlQuery(sql); + while(q->next()) { + printf("ID: %u\n",q->value(0).toUInt()); + printf(" Description: %s\n",q->value(4).toString().toUtf8().constData()); + printf(" Dimensions: %dx%dx%d\n",q->value(1).toInt(),q->value(2).toInt(), + q->value(3).toInt()); + printf("\n"); + } + delete q; +} + + +void MainObject::RunPush() +{ + QString sql; + + // + // Load the image + // + QFile file(test_filename); + if(!file.open(QIODevice::ReadOnly)) { + fprintf(stderr,"feed_image_test: unable to open image file [%s]\n", + strerror(errno)); + exit(1); + } + QByteArray data=file.readAll(); + file.close(); + + // + // Validate the image + // + QImage *img=new QImage(); + if(!img->loadFromData(data)) { + fprintf(stderr,"feed_image_test: invalid image file\n"); + exit(1); + } + printf("Image is %dx%dx%d\n",img->width(),img->height(),img->depth()); + + // + // Write it to the DB + // + sql=QString("insert into FEED_IMAGES set ")+ + QString().sprintf("FEED_ID=%u,",test_feed->id())+ + "FEED_KEY_NAME=\""+RDEscapeString(test_feed->keyName())+"\","+ + QString().sprintf("WIDTH=%d,",img->width())+ + QString().sprintf("HEIGHT=%d,",img->height())+ + QString().sprintf("DEPTH=%d,",img->depth())+ + "DESCRIPTION=\""+RDEscapeString(test_description)+"\","+ + "DATA="+RDEscapeBlob(data); + RDSqlQuery::apply(sql); +} + + +void MainObject::RunPop() +{ + QString sql; + RDSqlQuery *q=NULL; + QByteArray data; + FILE *f=NULL; + + sql=QString("select DATA from FEED_IMAGES where ")+ + QString().sprintf("ID=%u",test_image_id); + q=new RDSqlQuery(sql); + if(q->first()) { + if((f=fopen(test_filename.toUtf8(),"w"))==NULL) { + fprintf(stderr,"feed_image_test: unable to open \"%s\" [%s]\n", + test_filename.toUtf8().constData(),strerror(errno)); + exit(1); + } + data=q->value(0).toByteArray(); + if(fwrite(data.constData(),1,data.size(),f)<0) { + fprintf(stderr,"feed_image_test: unable to write to \"%s\" [%s]\n", + test_filename.toUtf8().constData(),strerror(errno)); + exit(1); + } + } + else { + fprintf(stderr,"feed_image_test: no such image\n"); + exit(1); + } + delete q; +} + + +int main(int argc,char *argv[]) +{ + QApplication a(argc,argv,false); + new MainObject(); + return a.exec(); +} diff --git a/tests/feed_image_test.h b/tests/feed_image_test.h new file mode 100644 index 00000000..24fec331 --- /dev/null +++ b/tests/feed_image_test.h @@ -0,0 +1,48 @@ +// feed_image_test.h +// +// Test Rivendell image storage. +// +// (C) Copyright 2010-2020 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. +// + +#ifndef FEED_IMAGE_TEST_H +#define FEED_IMAGE_TEST_H + +#include + +#include + +#define FEED_IMAGE_TEST_USAGE "[options]\n\nTest the Rivendell binary image routines\n\nOptions are:\n--push | --pop | --list\n\n--description=\n\n--name=\n\n--feed=\n\n--image-id=\n\n" + +class MainObject : public QObject +{ + public: + enum Command {None=0,List=1,Push=2,Pop=3}; + MainObject(QObject *parent=0); + + private: + void RunList(); + void RunPush(); + void RunPop(); + QString test_filename; + QString test_description; + QString test_name; + RDFeed *test_feed; + int test_image_id; +}; + + +#endif // FEED_IMAGE_TEST_H diff --git a/tests/upload_test.cpp b/tests/upload_test.cpp index afb5bfb0..20a504d5 100644 --- a/tests/upload_test.cpp +++ b/tests/upload_test.cpp @@ -2,7 +2,7 @@ // // Test Rivendell file uploading. // -// (C) Copyright 2010,2016 Fred Gleason +// (C) Copyright 2010-2020 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 @@ -37,6 +37,7 @@ MainObject::MainObject(QObject *parent) username=""; password=""; RDUpload::ErrorCode conv_err; + use_identity_file=false; // // Open the Database @@ -59,6 +60,14 @@ MainObject::MainObject(QObject *parent) password=rda->cmdSwitch()->value(i); rda->cmdSwitch()->setProcessed(i,true); } + if(rda->cmdSwitch()->key(i)=="--ssh-identity-filename") { + ssh_identity_filename=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--use-identity-file") { + use_identity_file=true; + rda->cmdSwitch()->setProcessed(i,true); + } if(rda->cmdSwitch()->key(i)=="--source-file") { source_filename=rda->cmdSwitch()->value(i); rda->cmdSwitch()->setProcessed(i,true); @@ -74,33 +83,6 @@ MainObject::MainObject(QObject *parent) } } - /* - // - // Read Command Options - // - RDCmdSwitch *cmd= - new RDCmdSwitch(qApp->argc(),qApp->argv(),"upload_test", - UPLOAD_TEST_USAGE); - for(unsigned i=0;ikeys();i++) { - if(cmd->key(i)=="--username") { - username=cmd->value(i); - cmd->setProcessed(i,true); - } - if(cmd->key(i)=="--password") { - password=cmd->value(i); - cmd->setProcessed(i,true); - } - if(cmd->key(i)=="--source-file") { - source_filename=cmd->value(i); - cmd->setProcessed(i,true); - } - if(cmd->key(i)=="--destination-url") { - destination_url=cmd->value(i); - cmd->setProcessed(i,true); - } - } - */ - // // Sanity Checks // @@ -112,34 +94,17 @@ MainObject::MainObject(QObject *parent) fprintf(stderr,"upload_test: missing destination-url\n"); exit(256); } - /* - // - // Read Configuration - // - rdconfig=new RDConfig(); - rdconfig->load(); - rdconfig->setModuleName("upload_test"); - // - // Open Database - // - QString err (tr("upload_test: ")); - QSqlDatabase *db=RDInitDb(&schema,&err); - if(!db) { - fprintf(stderr,err.ascii()); - delete cmd; - exit(256); - } - */ // // Run the Test // - RDUpload *conv=new RDUpload(this); + RDUpload *conv=new RDUpload(rda->config(),this); conv->setSourceFile(source_filename); conv->setDestinationUrl(destination_url); printf("Uploading...\n"); conv_err=conv-> - runUpload(username,password,rda->config()->logXloadDebugData()); + runUpload(username,password,ssh_identity_filename,use_identity_file, + rda->config()->logXloadDebugData()); printf("Result: %s\n",(const char *)RDUpload::errorText(conv_err)); delete conv; diff --git a/tests/upload_test.h b/tests/upload_test.h index cab339a3..d2ee99d5 100644 --- a/tests/upload_test.h +++ b/tests/upload_test.h @@ -25,7 +25,7 @@ #include -#define UPLOAD_TEST_USAGE "[options]\n\nTest the Rivendell upload routines\n\nOptions are:\n--username=\n\n--password=\n\n--source-file=\n\n--destination-url=\n\n" +#define UPLOAD_TEST_USAGE "[options]\n\nTest the Rivendell upload routines\n\nOptions are:\n--username=\n\n--password=\n\n--ssh-identity-key=\n\n--use-identity-file=y|n\n\n--source-file=\n\n--destination-url=\n\n" class MainObject : public QObject { @@ -37,6 +37,8 @@ class MainObject : public QObject QString password; QString source_filename; QString destination_url; + QString ssh_identity_filename; + bool use_identity_file; }; diff --git a/utils/Makefile.am b/utils/Makefile.am index 2e0f42ca..4e5fe881 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -42,7 +42,6 @@ SUBDIRS = $(ALSACONFIG_RD_OPT)\ rdmarkerset\ rdmetadata\ rdpopup\ - rdpurgecasts\ rdrender\ rdselect_helper\ rdsoftkeys\ diff --git a/utils/rddbmgr/check.cpp b/utils/rddbmgr/check.cpp index bed3c601..90bfc4c5 100644 --- a/utils/rddbmgr/check.cpp +++ b/utils/rddbmgr/check.cpp @@ -389,60 +389,42 @@ void MainObject::RelinkAudio(const QString &srcdir) const QString hash=RDSha1Hash(filename); QString firstdest; bool delete_source=true; + + // + // Check against audio cuts + // sql=QString("select CUTS.CUT_NAME,CART.TITLE from ")+ "CUTS left join CART "+ "on CUTS.CART_NUMBER=CART.NUMBER where "+ "CUTS.SHA1_HASH=\""+RDEscapeString(hash)+"\""; q=new RDSqlQuery(sql); while(q->next()) { - printf(" Recovering %06u/%03d [%s]...", - RDCut::cartNumber(q->value(0).toString()), - RDCut::cutNumber(q->value(0).toString()), - (const char *)q->value(1).toString()); - fflush(stdout); - if(db_relink_audio_move) { - unlink(RDCut::pathName(q->value(0).toString())); - if(link(filename,RDCut::pathName(q->value(0).toString()))<0) { - if(errno==EXDEV) { - if(firstdest.isEmpty()) { - if(CopyFile(RDCut::pathName(q->value(0).toString()),filename)) { - firstdest=RDCut::pathName(q->value(0).toString()); - } - else { - fprintf(stderr,"unable to copy file \"%s\"\n", - (const char *)filename); - delete_source=false; - } - } - else { - unlink(RDCut::pathName(q->value(0).toString())); - link(firstdest,RDCut::pathName(q->value(0).toString())); - } - } - else { - fprintf(stderr,"unable to move file \"%s\" [%s]\n", - (const char *)filename,strerror(errno)); - delete_source=false; - } - } - } - else { - if(firstdest.isEmpty()) { - if(CopyFile(RDCut::pathName(q->value(0).toString()),filename)) { - firstdest=RDCut::pathName(q->value(0).toString()); - } - else { - fprintf(stderr,"unable to copy file \"%s\"\n", - (const char *)filename); - } - } - else { - unlink(RDCut::pathName(q->value(0).toString())); - link(firstdest,RDCut::pathName(q->value(0).toString())); - } - } - printf(" done.\n"); + RelinkCut(filename,q->value(0).toString(),q->value(1).toString(), + &firstdest,&delete_source); } + + // + // Check against RSS posts + // + sql=QString("select ")+ + "FEEDS.KEY_NAME,"+ // 00 + "PODCASTS.ID,"+ // 01 + "PODCASTS.ITEM_TITLE,"+ // 02 + "PODCASTS.AUDIO_FILENAME "+ // 03 + "from PODCASTS left join FEEDS "+ + "on FEEDS.ID=PODCASTS.FEED_ID where "+ + "PODCASTS.SHA1_HASH=\""+RDEscapeString(hash)+"\""; + q=new RDSqlQuery(sql); + while(q->next()) { + RelinkCast(filename,q->value(0).toString(),q->value(1).toUInt(), + q->value(2).toString(),q->value(3).toString(), + &firstdest,&delete_source); + } + delete q; + + // + // (Perhaps) delete the source file + // if(db_relink_audio_move&&delete_source) { unlink(filename); } @@ -450,6 +432,125 @@ void MainObject::RelinkAudio(const QString &srcdir) const } +void MainObject::RelinkCut(const QString &src_filename,const QString &cutname, + const QString &title, + QString *firstdest,bool *delete_src) const +{ + printf(" Recovering %06u/%03d [%s]...", + RDCut::cartNumber(cutname),RDCut::cutNumber(cutname), + title.toUtf8().constData()); + fflush(stdout); + + if(db_relink_audio_move) { + unlink(RDCut::pathName(cutname)); + if(link(src_filename,RDCut::pathName(cutname))<0) { + if(errno==EXDEV) { // We're crossing filesystems, so do a copy + if(firstdest->isEmpty()) { + if(CopyToAudioStore(RDCut::pathName(cutname),src_filename)) { + *firstdest=RDCut::pathName(cutname); + } + else { + fprintf(stderr,"unable to copy file \"%s\"\n", + (const char *)src_filename); + *delete_src=false; + } + } + else { + unlink(RDCut::pathName(cutname)); + link(*firstdest,RDCut::pathName(cutname)); + } + } + else { + fprintf(stderr,"unable to move file \"%s\" [%s]\n", + (const char *)src_filename,strerror(errno)); + *delete_src=false; + } + } + else { + chown(RDCut::pathName(cutname),db_config->uid(),db_config->gid()); + chmod(RDCut::pathName(cutname),S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + } + } + else { + if(firstdest->isEmpty()) { + if(CopyToAudioStore(RDCut::pathName(cutname),src_filename)) { + *firstdest=RDCut::pathName(cutname); + } + else { + fprintf(stderr,"unable to copy file \"%s\"\n", + (const char *)src_filename); + } + } + else { + unlink(RDCut::pathName(cutname)); + link(*firstdest,RDCut::pathName(cutname)); + } + } + printf(" done.\n"); +} + + +void MainObject::RelinkCast(const QString &src_filename,const QString &keyname, + unsigned cast_id,const QString &title, + const QString &audio_filename, + QString *firstdest,bool *delete_src) const +{ + QString destpath=QString(RD_AUDIO_ROOT)+"/"+audio_filename; + + printf(" Recovering RSS item %s:%u [%s]...", + keyname.toUtf8().constData(),cast_id,title.toUtf8().constData()); + fflush(stdout); + + if(db_relink_audio_move) { + unlink(destpath); + if(link(src_filename,destpath)<0) { + if(errno==EXDEV) { // We're crossing filesystems, so do a copy + if(firstdest->isEmpty()) { + if(CopyToAudioStore(destpath,src_filename)) { + *firstdest=destpath; + } + else { + fprintf(stderr,"unable to copy file \"%s\"\n", + (const char *)src_filename); + *delete_src=false; + } + } + else { + unlink(destpath); + link(*firstdest,destpath); + } + } + else { + fprintf(stderr,"unable to move file \"%s\" [%s]\n", + (const char *)src_filename,strerror(errno)); + *delete_src=false; + } + } + else { + chown(destpath,db_config->uid(),db_config->gid()); + chmod(destpath,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + } + } + else { + if(firstdest->isEmpty()) { + unlink(destpath); + if(CopyToAudioStore(destpath,src_filename)) { + *firstdest=destpath; + } + else { + fprintf(stderr,"unable to copy file \"%s\" [%s]\n", + (const char *)src_filename,strerror(errno)); + } + } + else { + unlink(destpath); + link(*firstdest,destpath); + } + } + printf(" done.\n"); +} + + void MainObject::CheckOrphanedTracks() const { QString sql="select NUMBER,TITLE,OWNER from CART where OWNER!=\"\""; @@ -865,7 +966,8 @@ bool MainObject::UserResponse() const } -bool MainObject::CopyFile(const QString &destfile,const QString &srcfile) const +bool MainObject::CopyToAudioStore(const QString &destfile, + const QString &srcfile) const { int src_fd=-1; struct stat src_stat; @@ -896,10 +998,10 @@ bool MainObject::CopyFile(const QString &destfile,const QString &srcfile) const write(dest_fd,data,n); } free(data); - fchown(dest_fd,150,150); // FIXME: do name lookup! + fchown(dest_fd,db_config->uid(),db_config->gid()); + fchmod(dest_fd,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); close(dest_fd); close(src_fd); - return true; } diff --git a/utils/rddbmgr/rddbmgr.h b/utils/rddbmgr/rddbmgr.h index cd19d473..ef55cbe9 100644 --- a/utils/rddbmgr/rddbmgr.h +++ b/utils/rddbmgr/rddbmgr.h @@ -24,6 +24,7 @@ #include #include +#include #include #define RDDBMGR_USAGE "[options]\n" @@ -49,6 +50,13 @@ class MainObject : public QObject const QString &new_filename,const QString &new_str, QString *err_msg); void RelinkAudio(const QString &srcdir) const; + void RelinkCut(const QString &src_filename,const QString &cutname, + const QString &title, + QString *firstdest,bool *delete_src) const; + void RelinkCast(const QString &src_filename,const QString &keyname, + unsigned cast_id,const QString &title, + const QString &audio_filename, + QString *firstdest,bool *delete_src) const; void CheckOrphanedTracks() const; void CheckCutCounts() const; void CheckPendingCarts() const; @@ -61,7 +69,7 @@ class MainObject : public QObject void RehashCut(const QString &cutnum) const; void SetCutLength(const QString &cutname,int len) const; void RemoveCart(unsigned cartnum); - bool CopyFile(const QString &destfile,const QString &srcfile) const; + bool CopyToAudioStore(const QString &destfile,const QString &srcfile) const; bool UserResponse() const; // diff --git a/utils/rddbmgr/revertschema.cpp b/utils/rddbmgr/revertschema.cpp index f626c958..cc0a11b4 100644 --- a/utils/rddbmgr/revertschema.cpp +++ b/utils/rddbmgr/revertschema.cpp @@ -40,6 +40,392 @@ bool MainObject::RevertSchema(int cur_schema,int set_schema,QString *err_msg) // NEW SCHEMA REVERSIONS GO HERE... + // + // Revert 338 + // + if((cur_schema==338)&&(set_schemanext()) { + QString password(QByteArray::fromBase64(q->value(1).toString().toUtf8())); + sql=QString("update FEEDS set ")+ + "PURGE_PASSWORD=\""+RDEscapeString(password)+"\" where "+ + "KEY_NAME=\""+RDEscapeString(q->value(0).toString())+"\""; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + } + delete q; + sql=QString("alter table FEEDS modify column ")+ + "PURGE_PASSWORD varchar(64)"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(--cur_schema); + } + + + // + // Revert 333 + // + if((cur_schema==333)&&(set_schemanext()) { + if(q->value(2).isNull()) { + sql=QString("update PODCASTS set ")+ + "SHELF_LIFE=0 where "+ + QString().sprintf("ID=%u",q->value(0).toUInt()); + } + else { + sql=QString("update PODCASTS set ")+ + QString().sprintf("SHELF_LIFE=%d where ", + q->value(1).toDateTime(). + daysTo(q->value(2).toDateTime()))+ + QString().sprintf("ID=%u",q->value(0).toUInt()); + } + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + } + DropColumn("PODCASTS","EXPIRATION_DATETIME"); + DropColumn("SYSTEM","RSS_PROCESSOR_STATION"); + + WriteSchemaVersion(--cur_schema); + } + + // + // Revert 328 + // + if((cur_schema==328)&&(set_schemanext()) { + sql=QString("select ")+ + "HEADER_XML,"+ // 00 + "CHANNEL_XML,"+ // 01 + "ITEM_XML "+ // 02 + "from RSS_SCHEMAS where "+ + QString().sprintf("ID=%u",q->value(1).toUInt()); + q1=new RDSqlQuery(sql); + if(q1->first()) { + QString("update FEEDS set ")+ + "HEADER_XML=\""+RDEscapeString(q1->value(0).toString())+"\","+ + "CHANNEL_XML=\""+RDEscapeString(q1->value(1).toString())+"\","+ + "ITEM_XML=\""+RDEscapeString(q1->value(2).toString())+"\" "+ + "where "+ + QString().sprintf("ID=%u",q->value(0).toUInt()); + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + } + delete q1; + } + delete q; + DropColumn("FEEDS","RSS_SCHEMA"); + DropTable("RSS_SCHEMAS"); + + WriteSchemaVersion(--cur_schema); + } + + // + // Revert 321 + // + if((cur_schema==321)&&(set_schema Rivendell version map // -// (C) Copyright 2018 Fred Gleason +// (C) Copyright 2018-2020 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 @@ -161,6 +161,7 @@ void MainObject::InitializeSchemaMap() { global_version_map["3.2"]=311; global_version_map["3.3"]=314; global_version_map["3.4"]=317; + global_version_map["3.5"]=338; } diff --git a/utils/rddbmgr/updateschema.cpp b/utils/rddbmgr/updateschema.cpp index b30bad71..b0b60f75 100644 --- a/utils/rddbmgr/updateschema.cpp +++ b/utils/rddbmgr/updateschema.cpp @@ -2,7 +2,7 @@ // // Update Rivendell DB schema. // -// (C) Copyright 2018-2019 Fred Gleason +// (C) Copyright 2018-2020 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 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "rddbmgr.h" @@ -9916,6 +9917,12 @@ bool MainObject::UpdateSchema(int cur_schema,int set_schema,QString *err_msg) WriteSchemaVersion(++cur_schema); } + if((cur_schema<316)&&(set_schema>cur_schema)) { + DropColumn("EVENTS","PROPERTIES"); + + WriteSchemaVersion(++cur_schema); + } + if((cur_schema<317)&&(set_schema>cur_schema)) { sql=QString("create index STACK_LINES_ID_IDX on ")+ "STACK_SCHED_CODES(STACK_LINES_ID)"; @@ -9926,7 +9933,369 @@ bool MainObject::UpdateSchema(int cur_schema,int set_schema,QString *err_msg) WriteSchemaVersion(++cur_schema); } + if((cur_schema<318)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS add column ")+ + "IS_SUPERFEED enum('N','Y') default 'N' after KEY_NAME"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table FEEDS add index IS_SUPERFEED_IDX(IS_SUPERFEED)"); + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("create table SUPERFEED_MAPS (")+ + "ID int unsigned primary key auto_increment,"+ + "FEED_ID int unsigned not null,"+ + "MEMBER_FEED_ID int unsigned not null,"+ + "KEY_NAME varchar(8) not null,"+ + "MEMBER_KEY_NAME varchar(8) not null,"+ + "index FEED_ID_IDX(FEED_ID),"+ + "index MEMBER_FEED_ID_IDX(MEMBER_FEED_ID),"+ + "index KEY_NAME_IDX(KEY_NAME),"+ + "index MEMBER_KEY_NAME_IDX(MEMBER_KEY_NAME))"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<319)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS add column ")+ + "AUDIENCE_METRICS enum('N','Y') default 'N' after IS_SUPERFEED"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("update FEEDS set AUDIENCE_METRICS='Y'"); + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + /// + if((cur_schema<320)&&(set_schema>cur_schema)) { + sql=QString("alter table USERS add column ")+ + "EMAIL_ADDRESS varchar(191) after FULL_NAME"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<321)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS add column ")+ + "CHANNEL_EDITOR varchar(64) after CHANNEL_COPYRIGHT"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<322)&&(set_schema>cur_schema)) { + sql=QString("create table RSS_SCHEMAS (")+ + "ID int unsigned primary key,"+ + "NAME varchar(64) unique not null,"+ + "HEADER_XML text,"+ + "CHANNEL_XML text,"+ + "ITEM_XML text,"+ + "index NAME_IDX(NAME))"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + sql=QString("alter table FEEDS add column ")+ + "RSS_SCHEMA int unsigned not null default 0 after PURGE_PASSWORD"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<323)&&(set_schema>cur_schema)) { + sql=QString("create table FEED_IMAGES (")+ + "ID int unsigned primary key auto_increment,"+ + "FEED_ID int unsigned not null,"+ + "FEED_KEY_NAME varchar(8) not null,"+ + "WIDTH int not null,"+\ + "HEIGHT int not null,"+ + "DEPTH int not null,"+ + "DESCRIPTION varchar(191) not null,"+ + "FILE_EXTENSION varchar(10) not null,"+ + "DATA mediumblob not null,"+ + "index FEED_ID_IDX (FEED_ID),"+ + "index FEED_KEY_NAME_IDX (FEED_KEY_NAME))"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<324)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS add column CHANNEL_IMAGE_ID ")+ + "int not null default -1 after CHANNEL_LANGUAGE"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table FEEDS add column DEFAULT_ITEM_IMAGE_ID ")+ + "int not null default -1 after KEEP_METADATA"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table PODCASTS add column ITEM_IMAGE_ID ")+ + "int not null default -1 after ITEM_SOURCE_URL"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<325)&&(set_schema>cur_schema)) { + DropTable("RSS_SCHEMAS"); + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<326)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS add column CHANNEL_AUTHOR varchar(64) ")+ + "after CHANNEL_EDITOR"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table FEEDS add column CHANNEL_OWNER_NAME varchar(64) ")+ + "after CHANNEL_AUTHOR"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table FEEDS add column ")+ + "CHANNEL_OWNER_EMAIL varchar(64) after CHANNEL_OWNER_NAME"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table FEEDS add column CHANNEL_EXPLICIT enum('N','Y') ")+ + "not null default 'N' after CHANNEL_LANGUAGE"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table PODCASTS add column ITEM_EXPLICIT enum('N','Y') ")+ + "not null default 'N' after ITEM_SOURCE_URL"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<327)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS ")+ + "add column CHANNEL_SUB_CATEGORY varchar(64) "+ + "after CHANNEL_CATEGORY"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<328)&&(set_schema>cur_schema)) { + DropColumn("FEEDS","AUDIENCE_METRICS"); + DropColumn("FEEDS","KEEP_METADATA"); + DropColumn("FEEDS","MEDIA_LINK_MODE"); + DropColumn("FEEDS","REDIRECT_PATH"); + DropTable("CAST_DOWNLOADS"); + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<329)&&(set_schema>cur_schema)) { + sql=QString("alter table SYSTEM ")+ + "add column RSS_PROCESSOR_STATION varchar(64) "+ + "after NOTIFICATION_ADDRESS"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("select NAME from STATIONS where SYSTEM_MAINT='Y'"); + q=new RDSqlQuery(sql); + if(q->first()) { + sql=QString("update SYSTEM set ")+ + "RSS_PROCESSOR_STATION=\""+RDEscapeString(q->value(0).toString())+"\""; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + } + delete q; + + sql=QString("alter table PODCASTS ")+ + "add column EXPIRATION_DATETIME datetime after EFFECTIVE_DATETIME"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("select ")+ + "ID,"+ // 00 + "ORIGIN_DATETIME,"+ // 01 + "SHELF_LIFE "+ // 02 + "from PODCASTS where "+ + "SHELF_LIFE>0"; + q=new RDSqlQuery(sql); + while(q->next()) { + sql=QString("update PODCASTS set ")+ + "EXPIRATION_DATETIME=\""+ + q->value(1).toDateTime().addDays(q->value(2).toInt()). + toString("yyyy-MM-dd hh:mm:ss")+"\" where "+ + QString().sprintf("ID=%u",q->value(0).toUInt()); + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + } + delete q; + + DropColumn("PODCASTS","SHELF_LIFE"); + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<330)&&(set_schema>cur_schema)) { + sql=QString("alter table PODCASTS ")+ + "add column ORIGIN_LOGIN_NAME varchar(191) after AUDIO_TIME"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table PODCASTS ")+ + "add column ORIGIN_STATION varchar(64) after ORIGIN_LOGIN_NAME"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<331)&&(set_schema>cur_schema)) { + DropTable("ENCODER_SAMPLERATES"); + DropTable("ENCODER_BITRATES"); + DropTable("ENCODER_CHANNELS"); + DropTable("ENCODERS"); + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<332)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS ")+ + "add column CHANNEL_AUTHOR_IS_DEFAULT enum('N','Y') default 'N' "+ + "after CHANNEL_AUTHOR"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<333)&&(set_schema>cur_schema)) { + sql=QString("alter table STATIONS add ")+ + "SSH_IDENTITY_FILE text after BROWSER_PATH"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + sql=QString("alter table RECORDINGS ")+ + "add column URL_USE_ID_FILE enum('N','Y') default 'N' after URL_PASSWORD"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + sql=QString("alter table FEEDS add ")+ + "column PURGE_USE_ID_FILE enum('N','Y') default 'N' after PURGE_PASSWORD"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<334)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS modify column ")+ + "PURGE_PASSWORD text"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("select ")+ + "KEY_NAME,"+ // 00 + "PURGE_PASSWORD "+ // 01 + "from FEEDS"; + q=new RDSqlQuery(sql); + while(q->next()) { + sql=QString("update FEEDS set ")+ + "PURGE_PASSWORD=\""+ + RDEscapeString(q->value(1).toString().toUtf8().toBase64())+"\" where "+ + "KEY_NAME=\""+RDEscapeString(q->value(0).toString())+"\""; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + } + delete q; + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<335)&&(set_schema>cur_schema)) { + sql=QString("alter table PODCASTS add column ")+ + "SHA1_HASH varchar(40) after AUDIO_TIME"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table PODCASTS add index SHA1_HASH_IDX(SHA1_HASH)"); + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<336)&&(set_schema>cur_schema)) { + sql=QString("alter table FEED_IMAGES modify column ")+ + "DATA longblob not null"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<337)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS ")+ + "add index CHANNEL_IMAGE_ID_IDX(CHANNEL_IMAGE_ID)"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table FEEDS ")+ + "add index DEFAULT_ITEM_IMAGE_ID_IDX(DEFAULT_ITEM_IMAGE_ID)"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + sql=QString("alter table PODCASTS ")+ + "add index ITEM_IMAGE_ID_IDX(ITEM_IMAGE_ID)"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + + if((cur_schema<338)&&(set_schema>cur_schema)) { + sql=QString("alter table FEEDS ")+ + "modify column CHANNEL_LANGUAGE varchar(8) default 'en-us'"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } // NEW SCHEMA UPDATES GO HERE... diff --git a/utils/rdpurgecasts/rdpurgecasts.cpp b/utils/rdpurgecasts/rdpurgecasts.cpp deleted file mode 100644 index 9b31d082..00000000 --- a/utils/rdpurgecasts/rdpurgecasts.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// rdpurgecasts.cpp -// -// A Utility to Purge Expired Podcasts. -// -// (C) Copyright 2007,2016-2018 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 -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rdpurgecasts.h" - -MainObject::MainObject(QObject *parent) - :QObject(parent) -{ - QString sql; - RDSqlQuery *q; - QString err_msg; - - // - // Initialize Data Structures - // - purge_verbose=false; - - // - // Open the Database - // - rda=new RDApplication("rdpurgecasts","rdpurgecasts",RDPURGECASTS_USAGE,this); - if(!rda->open(&err_msg)) { - fprintf(stderr,"rdpurgecasts: %s\n",(const char *)err_msg); - exit(1); - } - - // - // Read Command Options - // - if(rda->cmdSwitch()->keys()>2) { - fprintf(stderr,"\n"); - fprintf(stderr,"%s",RDPURGECASTS_USAGE); - fprintf(stderr,"\n"); - exit(256); - } - for(unsigned i=0;icmdSwitch()->keys();i++) { - if(rda->cmdSwitch()->key(i)=="--verbose") { - purge_verbose=true; - rda->cmdSwitch()->setProcessed(i,true); - } - - if(!rda->cmdSwitch()->processed(i)) { - fprintf(stderr,"rdpurgecasts: unknown command option \"%s\"\n", - (const char *)rda->cmdSwitch()->key(i)); - exit(2); - } - } - - // - // Scan Podcasts - // - QDateTime current_datetime= - QDateTime(QDate::currentDate(),QTime::currentTime()); - sql=QString().sprintf("select ID,ORIGIN_DATETIME,SHELF_LIFE from PODCASTS \ - where (SHELF_LIFE>0)&&(STATUS=%u)", - RDPodcast::StatusActive); - q=new RDSqlQuery(sql); - while(q->next()) { - if(q->value(1).toDateTime().addDays(q->value(2).toInt())< - current_datetime) { - PurgeCast(q->value(0).toUInt()); - } - } - delete q; - - exit(0); -} - - -void MainObject::PurgeCast(unsigned id) -{ - QString sql; - RDSqlQuery *q; - RDSqlQuery *q1; - QString cmd; - QDateTime current_datetime= - QDateTime(QDate::currentDate(),QTime::currentTime()); - RDFeed *feed=NULL; - RDPodcast *cast=NULL; - QString errs; - - sql=QString("select ")+ - "FEEDS.ID,"+ // 00 - "FEEDS.KEEP_METADATA,"+ // 01 - "FEEDS.KEY_NAME "+ // 02 - "from PODCASTS left join FEEDS "+ - "on(PODCASTS.FEED_ID=FEEDS.ID) where "+ - QString().sprintf("PODCASTS.ID=%u",id); - q=new RDSqlQuery(sql); - while(q->next()) { - feed=new RDFeed(q->value(0).toUInt(),rda->config()); - cast=new RDPodcast(rda->config(),id); - cast->removeAudio(feed,&errs,rda->config()->logXloadDebugData()); - if(purge_verbose) { - printf("purging cast: ID=%d,cmd=\"%s\"\n",id,(const char *)cmd); - } - delete cast; - delete feed; - if(RDBool(q->value(1).toString())) { - sql=QString("update PODCASTS set ")+ - QString().sprintf("STATUS=%u where ",RDPodcast::StatusExpired)+ - QString().sprintf("ID=%u",id); - q1=new RDSqlQuery(sql); - delete q1; - } - else { - sql=QString("delete from CAST_DOWNLOADS where ")+ - "FEED_KEY_NAME=\""+RDEscapeString(q->value(2).toString())+"\" && "+ - QString().sprintf("CAST_ID=%d",id); - q1=new RDSqlQuery(sql); - delete q1; - - sql=QString("delete from PODCASTS where ")+ - QString().sprintf("ID=%d",id); - q1=new RDSqlQuery(sql); - delete q1; - } - sql=QString("update FEEDS set ")+ - "LAST_BUILD_DATETIME=\""+ - RDEscapeString(current_datetime.toString("yyyy-MM-dd hh:mm:ss"))+ - "\" where "+ - QString().sprintf("ID=%u",q->value(0).toUInt()); - q1=new RDSqlQuery(sql); - delete q1; - - } - delete q; -} - - -int main(int argc,char *argv[]) -{ - QApplication a(argc,argv,false); - new MainObject(); - return a.exec(); -} diff --git a/web/Makefile.am b/web/Makefile.am index 1a6de85f..1a29eef3 100644 --- a/web/Makefile.am +++ b/web/Makefile.am @@ -2,7 +2,7 @@ ## ## Automake.am for rivendell/web ## -## (C) Copyright 2002-2007,2016 Fred Gleason +## (C) Copyright 2002-2020 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as @@ -21,7 +21,6 @@ ## Use automake to process this into a Makefile.in SUBDIRS = rdcastmanager\ - rdfeed\ rdxport\ tests\ webget diff --git a/web/rdcastmanager/rdcastmanager.cpp b/web/rdcastmanager/rdcastmanager.cpp index 1bf94df3..021f0f49 100644 --- a/web/rdcastmanager/rdcastmanager.cpp +++ b/web/rdcastmanager/rdcastmanager.cpp @@ -551,6 +551,7 @@ void MainObject::ServeListCasts() line_colors[0]=RD_WEB_LINE_COLOR1; line_colors[1]=RD_WEB_LINE_COLOR2; int current_color=0; + /* sql=QString("select ")+ "ID,"+ // 00 "STATUS,"+ // 01 @@ -560,8 +561,9 @@ void MainObject::ServeListCasts() "ITEM_CATEGORY,"+ // 05 "AUDIO_TIME "+ // 06 "from PODCASTS "+ - RDCastSearch(cast_feed_id,filter,unexp_only,active_only)+ + RDCastSearch(cast_key_name,false,filter,unexp_only,active_only)+ " order by ORIGIN_DATETIME desc"; + */ q=new RDSqlQuery(sql); while(q->next()) { printf("\n"); @@ -813,6 +815,7 @@ void MainObject::ServeEditCast(int cast_id) // // Media Link // + /* if(feed->mediaLinkMode()!=RDFeed::LinkNone) { printf("\n"); printf("Media Link:\n", @@ -828,7 +831,7 @@ void MainObject::ServeEditCast(int cast_id) printf(" \n"); printf("\n"); } - + */ // // Cast Data // @@ -1434,7 +1437,7 @@ void MainObject::DeleteCast() RDFeed *feed=new RDFeed(cast_feed_id,rda->config()); RDPodcast *cast=new RDPodcast(rda->config(),cast_cast_id); - cast->removeAudio(feed,&errs,rda->config()->logXloadDebugData()); + cast->dropAudio(feed,&errs,rda->config()->logXloadDebugData()); delete cast; delete feed; @@ -1569,6 +1572,7 @@ void MainObject::ServeSubscriptionReport() void MainObject::PostEpisode() { + /* QString media_file; GetContext(); @@ -1609,7 +1613,7 @@ void MainObject::PostEpisode() Exit(0); } ServeEditCast(cast_id); - + */ Exit(0); } diff --git a/web/rdfeed/Makefile.am b/web/rdfeed/Makefile.am deleted file mode 100644 index 6253879c..00000000 --- a/web/rdfeed/Makefile.am +++ /dev/null @@ -1,48 +0,0 @@ -## Makefile.am -## -## (C) Copyright 2002-2020 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. -## -## Use automake to process this into a Makefile.in - -AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -I$(top_srcdir)/lib @QT4_CFLAGS@ @MUSICBRAINZ_CFLAGS@ -DQT3_SUPPORT -I/usr/include/Qt3Support -LIBS = -L$(top_srcdir)/lib -MOC = @QT_MOC@ - -install-exec-local: - if test ! -f $(DESTDIR)@libexecdir@/rdfeed.mp3 ; then ln -s @libexecdir@/rdfeed.xml $(DESTDIR)@libexecdir@/rdfeed.mp3 ; fi - -uninstall-local: - rm -f $(DESTDIR)@libexecdir@/rdfeed.mp3 - -libexec_PROGRAMS = rdfeed.xml - -dist_rdfeed_xml_SOURCES = rdfeed_script.cpp rdfeed_script.h - -rdfeed_xml_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ @MUSICBRAINZ_LIBS@ -lQt3Support - -EXTRA_DIST = rdfeed.pro - -CLEANFILES = *~\ - *.idb\ - *ilk\ - *.obj\ - *.pdb\ - *.qm\ - moc_* - -MAINTAINERCLEANFILES = *~\ - Makefile.in\ - moc_* diff --git a/web/rdfeed/rdfeed.pro b/web/rdfeed/rdfeed.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/web/rdfeed/rdfeed_script.cpp b/web/rdfeed/rdfeed_script.cpp deleted file mode 100644 index 44ea1b8d..00000000 --- a/web/rdfeed/rdfeed_script.cpp +++ /dev/null @@ -1,379 +0,0 @@ -// rdfeed_script.cpp -// -// An RSS Feed Generator for Rivendell. -// -// (C) Copyright 2002-2007,2016-2018 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 - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rdfeed_script.h" - -char server_name[PATH_MAX]; - -MainObject::MainObject(QObject *parent) - :QObject(parent) -{ - QString err_msg; - - char keyname[10]; - int cast_id=-1; - bool count; - - // - // Validate Feed Key Name - // - if(getenv("QUERY_STRING")==NULL) { - printf("Content-type: text/html\n"); - printf("Status: 400\n"); - printf("\n"); - printf("rdfeed: missing feed key name\n"); - exit(0); - } - int arg=0; - while((getenv("QUERY_STRING")[arg]!=0)&& - (getenv("QUERY_STRING")[arg]!='&')&&(arg<9)) { - keyname[arg]=getenv("QUERY_STRING")[arg]; - arg++; - } - if(arg==9) { - printf("Content-type: text/html\n"); - printf("Status: 400\n"); - printf("\n"); - printf("rdfeed: invalid feed key name\n"); - exit(0); - } - keyname[arg]=0; - RDGetPostInt(getenv("QUERY_STRING")+arg+1,"cast_id",&cast_id); - - // - // Get the Server Name - // - if(getenv("SERVER_NAME")==NULL) { - printf("Content-type: text/html\n"); - printf("Status: 500\n"); - printf("\n"); - printf("rdfeed: missing SERVER_NAME\n"); - exit(0); - } - strncpy(server_name,getenv("SERVER_NAME"),PATH_MAX); - - // - // Determine Range - // - if(getenv("HTTP_RANGE")!=NULL) { - count=ShouldCount(getenv("HTTP_RANGE")); - } - else { - count=true; - } - - // - // Open the Database - // - rda=new RDApplication("rdfeed.xml","rdfeed.xml",RDFEED_XML_USAGE,this); - if(!rda->open(&err_msg)) { - printf("Content-type: text/html\n"); - printf("Status: 500\n"); - printf("\n"); - printf("rdfeed.xml: %s\n",(const char *)err_msg.utf8()); - exit(0); - } - - /* - printf("Content-type: text/html\n\n"); - QString sql; - RDSqlQuery *q; - sql=QString("show variables like '%character_set%'"); - q=new RDSqlQuery(sql); - while(q->next()) { - printf("%s: %s
\n",(const char *)q->value(0).toString(), - (const char *)q->value(1).toString()); - } - delete q; - sql=QString("show variables like '%collation%'"); - q=new RDSqlQuery(sql); - while(q->next()) { - printf("%s: %s
\n",(const char *)q->value(0).toString(), - (const char *)q->value(1).toString()); - } - delete q; - exit(0); - */ - - if(cast_id<0) { - ServeRss(keyname,count); - } - ServeLink(keyname,cast_id,count); -} - - -void MainObject::ServeRss(const char *keyname,bool count) -{ - QString sql; - RDSqlQuery *q; - RDSqlQuery *q1; - - sql=QString("select ")+ - "CHANNEL_TITLE,"+ // 00 - "CHANNEL_DESCRIPTION,"+ // 01 - "CHANNEL_CATEGORY,"+ // 02 - "CHANNEL_LINK,"+ // 03 - "CHANNEL_COPYRIGHT,"+ // 04 - "CHANNEL_WEBMASTER,"+ // 05 - "CHANNEL_LANGUAGE,"+ // 06 - "LAST_BUILD_DATETIME,"+ // 07 - "ORIGIN_DATETIME,"+ // 08 - "HEADER_XML,"+ // 09 - "CHANNEL_XML,"+ // 10 - "ITEM_XML,"+ // 11 - "BASE_URL,"+ // 12 - "ID,"+ // 13 - "UPLOAD_EXTENSION,"+ // 14 - "CAST_ORDER,"+ // 15 - "REDIRECT_PATH,"+ // 16 - "BASE_PREAMBLE "+ // 17 - "from FEEDS where "+ - "KEY_NAME=\""+RDEscapeString(keyname)+"\""; - q=new RDSqlQuery(sql); - if(!q->first()) { - printf("Content-type: text/html\n\n"); - printf("rdfeed: no feed matches the supplied key name\n"); - exit(0); - } - - // - // Log the Access - // - if(count) { - RDIncrementFeedCount(keyname); - } - - // - // Redirect if necessary - // - if(!q->value(16).toString().isEmpty()) { - Redirect(q->value(16).toString()); - delete q; - exit(0); - } - - // - // Generate CGI Header - // - printf("Content-type: application/rss+xml; charset=UTF-8\n\n"); - - // - // Render Header XML - // - printf("%s\n",(const char *)q->value(9).toString().utf8()); - - // - // Render Channel XML - // - printf("\n"); - printf("%s\n",(const char *)ResolveChannelWildcards(q).utf8()); - - // - // Render Item XML - // - sql=QString("select ")+ - "ITEM_TITLE,"+ // 00 - "ITEM_DESCRIPTION,"+ // 01 - "ITEM_CATEGORY,"+ // 02 - "ITEM_LINK,"+ // 03 - "ITEM_AUTHOR,"+ // 04 - "ITEM_SOURCE_TEXT,"+ // 05 - "ITEM_SOURCE_URL,"+ // 06 - "ITEM_COMMENTS,"+ // 07 - "AUDIO_FILENAME,"+ // 08 - "AUDIO_LENGTH,"+ // 09 - "AUDIO_TIME,"+ // 10 - "EFFECTIVE_DATETIME,"+ // 11 - "ID "+ // 12 - "from PODCASTS where "+ - QString().sprintf("(FEED_ID=%d)&&",q->value(13).toUInt())+ - QString().sprintf("(STATUS=%d) ",RDPodcast::StatusActive)+ - "order by ORIGIN_DATETIME"; - if(q->value(15).toString()=="N") { - sql+=" desc"; - } - q1=new RDSqlQuery(sql); - while(q1->next()) { - printf("\n"); - printf("%s\n",(const char *)ResolveItemWildcards(keyname,q1,q).utf8()); - printf("\n"); - } - delete q1; - - printf("\n"); - printf("\n"); - delete q; - - exit(0); -} - - -void MainObject::ServeLink(const char *keyname,int cast_id,bool count) -{ - QString sql; - RDSqlQuery *q; - - sql=QString("select ")+ - "FEEDS.BASE_URL,"+ // 00 - "PODCASTS.AUDIO_FILENAME "+ // 01 - "from FEEDS left join PODCASTS "+ - "on FEEDS.ID=PODCASTS.FEED_ID where "+ - "(FEEDS.KEY_NAME=\""+RDEscapeString(keyname)+"\")&&"+ - QString().sprintf("(PODCASTS.ID=%d)",cast_id); - q=new RDSqlQuery(sql); - if(!q->first()) { - delete q; - RDCgiError("Unable to retrieve cast record!"); - } - if(count) { - RDIncrementCastCount(keyname,cast_id); - } - printf("Content-type: audio/x-mpeg\n"); - printf("Location: %s/%s\n\n",(const char *)q->value(0).toString(), - (const char *)q->value(1).toString().utf8()); - delete q; - - exit(0); -} - - -QString MainObject::ResolveChannelWildcards(RDSqlQuery *chan_q) -{ - QString ret=chan_q->value(10).toString(); - // ret.replace("%TITLE%",chan_q->value(0).toString()); - ret.replace("%TITLE%",RDXmlEscape(chan_q->value(0).toString())); - ret.replace("%DESCRIPTION%",RDXmlEscape(chan_q->value(1).toString())); - ret.replace("%CATEGORY%",RDXmlEscape(chan_q->value(2).toString())); - ret.replace("%LINK%",RDXmlEscape(chan_q->value(3).toString())); - ret.replace("%COPYRIGHT%",RDXmlEscape(chan_q->value(4).toString())); - ret.replace("%WEBMASTER%",RDXmlEscape(chan_q->value(5).toString())); - ret.replace("%LANGUAGE%",RDXmlEscape(chan_q->value(6).toString())); - ret.replace("%BUILD_DATE%",chan_q->value(7).toDateTime(). - toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT"); - ret.replace("%PUBLISH_DATE%",chan_q->value(8).toDateTime(). - toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT"); - ret.replace("%GENERATOR%",QString("Rivendell ")+VERSION); - - return ret; -} - - -QString MainObject::ResolveItemWildcards(const QString &keyname, - RDSqlQuery *item_q,RDSqlQuery *chan_q) -{ - RDFeed *feed=new RDFeed(keyname,rda->config()); - QString ret=chan_q->value(11).toString(); - ret.replace("%ITEM_TITLE%",RDXmlEscape(item_q->value(0).toString())); - ret.replace("%ITEM_DESCRIPTION%", - RDXmlEscape(item_q->value(1).toString())); - ret.replace("%ITEM_CATEGORY%", - RDXmlEscape(item_q->value(2).toString())); - ret.replace("%ITEM_LINK%",RDXmlEscape(item_q->value(3).toString())); - ret.replace("%ITEM_AUTHOR%",RDXmlEscape(item_q->value(4).toString())); - ret.replace("%ITEM_SOURCE_TEXT%", - RDXmlEscape(item_q->value(5).toString())); - ret.replace("%ITEM_SOURCE_URL%", - RDXmlEscape(item_q->value(6).toString())); - ret.replace("%ITEM_COMMENTS%", - RDXmlEscape(item_q->value(7).toString())); - ret.replace("%ITEM_AUDIO_URL%", - (const char *)RDXmlEscape(feed-> - audioUrl(RDFeed::LinkCounted,server_name, - item_q->value(12).toUInt()))); - ret.replace("%ITEM_AUDIO_LENGTH%",item_q->value(9).toString()); - ret.replace("%ITEM_AUDIO_TIME%", - RDGetTimeLength(item_q->value(10).toInt(),false,false)); - ret.replace("%ITEM_PUBLISH_DATE%",item_q->value(11).toDateTime(). - toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT"); - ret.replace("%ITEM_GUID%",RDPodcast::guid(chan_q->value(12).toString(), - item_q->value(8).toString(), - chan_q->value(11).toUInt(), - item_q->value(12).toUInt())); - delete feed; - return ret; -} - - -bool MainObject::ShouldCount(const QString &hdr) -{ - bool ret=false; - QStringList lines=hdr.split("\n"); - int n; - QString str; - - for(int i=0;i0) { - if(lines[i].left(n).lower()=="bytes") { - str=lines[i].right(lines[i].length()-n-1).stripWhiteSpace(); - n=str.find("-"); - if(n==0) { - ret=true; - } - if(n>0) { - if(str.left(n)=="0") { - ret=true; - } - } - } - } - } - - return ret; -} - - -void MainObject::Redirect(const QString &url) -{ - printf("Status: 301 Moved Permanently\n"); - printf("Location: %s\n",(const char *)url.utf8()); - printf("Content-type: text/html\n"); - printf("\n"); - printf("The feed has been relocated to %s.\n",(const char *)url.utf8()); -} - - -int main(int argc,char *argv[]) -{ - QApplication a(argc,argv,false); - new MainObject(); - return a.exec(); -} diff --git a/web/rdxport/Makefile.am b/web/rdxport/Makefile.am index 586aec3d..442d6818 100644 --- a/web/rdxport/Makefile.am +++ b/web/rdxport/Makefile.am @@ -40,6 +40,7 @@ dist_rdxport_cgi_SOURCES = audioinfo.cpp\ exportpeaks.cpp\ import.cpp\ logs.cpp\ + podcasts.cpp\ rdxport.cpp rdxport.h\ rehash.cpp\ tests.cpp\ diff --git a/web/rdxport/podcasts.cpp b/web/rdxport/podcasts.cpp new file mode 100644 index 00000000..816328d1 --- /dev/null +++ b/web/rdxport/podcasts.cpp @@ -0,0 +1,720 @@ +// podcasts.cpp +// +// Rivendell web service portal -- Podcast services +// +// (C) Copyright 2010-2020 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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rdxport.h" + +size_t __PostRss_Readfunction_Callback(char *buffer,size_t size,size_t nitems, + void *userdata) +{ + Xport *xport=(Xport *)userdata; + + int curlsize=size*nitems; + int segsize=xport->xport_curl_data.size()-xport->xport_curl_data_ptr; + if(segsizexport_curl_data.mid(xport->xport_curl_data_ptr,curlsize).constData(), + curlsize); + xport->xport_curl_data_ptr+=curlsize; + return curlsize; +} + + +void Xport::SavePodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + RDFeed *feed=NULL; + QString filename; + QString msg="OK"; + + if(!xport_post->getValue("ID",&cast_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + + if(!xport_post->getValue("FILENAME",&filename)) { + XmlExit("Missing FILENAME",400,"podcasts.cpp",LINE_NUMBER); + } + if(!xport_post->isFile("FILENAME")) { + XmlExit("Missing file data",400,"podcasts.cpp",LINE_NUMBER); + } + + cast=new RDPodcast(rda->config(),cast_id); + if(!cast->exists()) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=cast->keyName(); + if(((!rda->user()->addPodcast())|| + (!rda->user()->feedAuthorized(keyname)))&& + (!rda->user()->adminConfig())) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + feed=new RDFeed(keyname,rda->config(),this); + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + + if(!RDCopy(filename,destpath)) { + delete feed; + delete cast; + XmlExit("Internal server error [copy failed]",500,"podcasts.cpp", + LINE_NUMBER); + } + if(chmod(destpath.toUtf8(),S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)!=0) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + unlink(destpath.toUtf8()); + delete feed; + delete cast; + XmlExit(err_msg.toUtf8(),500,"podcasts.cpp",LINE_NUMBER); + } + cast->setSha1Hash(RDSha1Hash(destpath)); + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + rda->syslog(LOG_DEBUG,"saved podcast \"%s\"",destpath.toUtf8().constData()); + + delete feed; + delete cast; + + Exit(0); +} + + +void Xport::GetPodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + QString msg="OK"; + int fd=-1; + struct stat st; + ssize_t n=0; + char *data=NULL; + + if(!xport_post->getValue("ID",&cast_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + + cast=new RDPodcast(rda->config(),cast_id); + if(!cast->exists()) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=cast->keyName(); + if(((!rda->user()->addPodcast())|| + (!rda->user()->feedAuthorized(keyname)))&& + (!rda->user()->adminConfig())) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + + if((fd=open(destpath.toUtf8(),O_RDONLY))<0) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + delete cast; + XmlExit(err_msg.toUtf8(),500,"podcasts.cpp",LINE_NUMBER); + } + memset(&st,0,sizeof(st)); + if(fstat(fd,&st)!=0) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + delete cast; + XmlExit(err_msg.toUtf8(),500,"podcasts.cpp",LINE_NUMBER); + } + + printf("Content-type: audio/x-mpeg\n"); + printf("Content-length: %ld\n",st.st_size); + printf("\n"); + fflush(stdout); + data=new char[st.st_blksize]; + n=read(fd,data,st.st_blksize); + while(n>0) { + write(1,data,n); + n=read(fd,data,st.st_blksize); + } + delete data; + close(fd); + + rda->syslog(LOG_DEBUG,"served podcast \"%s\"",destpath.toUtf8().constData()); + + delete cast; + + Exit(0); +} + + +void Xport::DeletePodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + QString msg="OK"; + + if(!xport_post->getValue("ID",&cast_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + + cast=new RDPodcast(rda->config(),cast_id); + if(!cast->exists()) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=cast->keyName(); + if(((!rda->user()->deletePodcast())|| + (!rda->user()->feedAuthorized(keyname)))&& + (!rda->user()->adminConfig())) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + + if(unlink(destpath.toUtf8())!=0) { + if(errno!=ENOENT) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + delete cast; + XmlExit(err_msg.toUtf8(),500,"podcasts.cpp",LINE_NUMBER); + } + } + cast->setSha1Hash(); + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + rda->syslog(LOG_DEBUG,"deleted podcast \"%s\"",destpath.toUtf8().constData()); + + delete cast; + + Exit(0); +} + + +void Xport::PostPodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + RDFeed *feed=NULL; + QString msg="OK"; + RDUpload::ErrorCode upload_err; + + if(!xport_post->getValue("ID",&cast_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + + cast=new RDPodcast(rda->config(),cast_id); + if(!cast->exists()) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=cast->keyName(); + if(((!rda->user()->addPodcast())|| + (!rda->user()->feedAuthorized(keyname)))&& + (!rda->user()->adminConfig())) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + feed=new RDFeed(keyname,rda->config(),this); + + RDUpload *upload=new RDUpload(rda->config(),this); + upload->setSourceFile(destpath); + QString desturl=feed->purgeUrl()+"/"+cast->audioFilename(); + upload->setDestinationUrl(desturl); + if((upload_err=upload-> + runUpload(feed->purgeUsername(),feed->purgePassword(), + rda->station()->sshIdentityFile(),feed->purgeUseIdFile(), + rda->config()->logXloadDebugData()))!=RDUpload::ErrorOk) { + delete upload; + delete feed; + delete cast; + XmlExit(QString("Upload to \"")+desturl+"\" failed ["+ + RDUpload::errorText(upload_err)+"]",500,"podcasts.cpp",LINE_NUMBER); + } + delete upload; + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + rda->syslog(LOG_DEBUG, + "posted podcast audio \"%s\"",destpath.toUtf8().constData()); + + delete feed; + delete cast; + + Exit(0); +} + + +void Xport::RemovePodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + RDFeed *feed=NULL; + QString msg="OK"; + RDDelete::ErrorCode del_err; + + if(!xport_post->getValue("ID",&cast_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + + cast=new RDPodcast(rda->config(),cast_id); + if(!cast->exists()) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=cast->keyName(); + if(((!rda->user()->deletePodcast())|| + (!rda->user()->feedAuthorized(keyname)))&& + (!rda->user()->adminConfig())) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + feed=new RDFeed(keyname,rda->config(),this); + + RDDelete *del=new RDDelete(rda->config(),this); + QString desturl=feed->purgeUrl()+"/"+cast->audioFilename(); + del->setTargetUrl(desturl); + if((del_err=del-> + runDelete(feed->purgeUsername(),feed->purgePassword(), + rda->station()->sshIdentityFile(),feed->purgeUseIdFile(), + rda->config()->logXloadDebugData()))!=RDDelete::ErrorOk) { + delete del; + delete feed; + delete cast; + XmlExit(QString("Deletion of \"")+desturl+"\" failed ["+ + RDDelete::errorText(del_err)+"]",500,"podcasts.cpp",LINE_NUMBER); + } + delete del; + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + rda->syslog(LOG_DEBUG, + "delete podcast audio \"%s\"",destpath.toUtf8().constData()); + + delete feed; + delete cast; + + Exit(0); +} + + +bool Xport::PostRssElemental(RDFeed *feed,const QDateTime &now,QString *err_msg) +{ + CURL *curl=NULL; + CURLcode curl_err; + char errstr[CURL_ERROR_SIZE]; + bool ret=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_ptr=0; + + // + // Authentication Parameters + // + if((QUrl(feed->feedUrl()).scheme().toLower()=="sftp")&& + (!rda->station()->sshIdentityFile().isEmpty())&&feed->purgeUseIdFile()) { + curl_easy_setopt(curl,CURLOPT_USERNAME, + feed->purgeUsername().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE, + rda->station()->sshIdentityFile().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_KEYPASSWD, + feed->purgePassword().toUtf8().constData()); + *err_msg+="using ssh key at \""+rda->station()->sshIdentityFile()+"\" "; + } + else { + curl_easy_setopt(curl,CURLOPT_USERNAME, + feed->purgeUsername().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_PASSWORD, + feed->purgePassword().toUtf8().constData()); + } + + // + // Transfer Parameters + // + curl_easy_setopt(curl,CURLOPT_URL,feed->feedUrl().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_UPLOAD,1); + curl_easy_setopt(curl,CURLOPT_READFUNCTION, __PostRss_Readfunction_Callback); + curl_easy_setopt(curl,CURLOPT_READDATA,this); + + curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent().utf8()); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errstr); + + // + // Execute it + // + switch((curl_err=curl_easy_perform(curl))) { + case CURLE_OK: + case CURLE_PARTIAL_FILE: + feed->setLastBuildDateTime(now); + ret=true; + break; + + default: + *err_msg+=errstr; + ret=false; + break; + } + curl_easy_cleanup(curl); + + rda->syslog(LOG_DEBUG, + "posted RSS XML to \"%s\"",feed->feedUrl().toUtf8().constData()); + + return ret; +} + + +void Xport::PostRss() +{ + int feed_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDFeed *feed=NULL; + QString msg="OK"; + bool ret=false; + + QDateTime now=QDateTime::currentDateTime(); + + if(!xport_post->getValue("ID",&feed_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + feed=new RDFeed(feed_id,rda->config(),this); + if(!feed->exists()) { + XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=feed->keyName(); + + if(((!rda->user()->editPodcast())|| + (!rda->user()->feedAuthorized(keyname)))&& + (!rda->user()->adminConfig())) { + delete feed; + XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER); + } + + ret=PostRssElemental(feed,now,&err_msg); + delete feed; + + // + // Update Enclosing Superfeeds + // + QStringList superfeeds=feed->isSubfeedOf(); + for(int i=0;iconfig(),this); + if(!PostRssElemental(feed,now,&err_msg)) { + err_msg+="\nRepost of XML failed"; + } + delete feed; + } + + if(!ret) { + XmlExit(err_msg,500,"podcasts.cpp",LINE_NUMBER); + } + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + Exit(0); +} + + +void Xport::RemoveRss() +{ + int feed_id=0; + RDFeed *feed=NULL; + QString keyname; + QString destpath; + QString err_msg; + // RDPodcast *cast=NULL; + QString msg="OK"; + RDDelete::ErrorCode del_err; + + if(!xport_post->getValue("ID",&feed_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + + feed=new RDFeed(feed_id,rda->config(),this); + if(!feed->exists()) { + XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=feed->keyName(); + if(((!rda->user()->deletePodcast())|| + (!rda->user()->feedAuthorized(keyname)))&& + (!rda->user()->adminConfig())) { + + delete feed; + XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER); + } + + RDDelete *del=new RDDelete(rda->config(),this); + QString desturl=feed->feedUrl(); + del->setTargetUrl(desturl); + if((del_err=del-> + runDelete(feed->purgeUsername(),feed->purgePassword(), + rda->station()->sshIdentityFile(),feed->purgeUseIdFile(), + rda->config()->logXloadDebugData()))!=RDDelete::ErrorOk) { + delete del; + delete feed; + XmlExit(QString("Deletion of \"")+desturl+"\" failed ["+ + RDDelete::errorText(del_err)+"]",500,"podcasts.cpp",LINE_NUMBER); + } + delete del; + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + rda->syslog(LOG_DEBUG, + "deleted podcast RSS \"%s\"",destpath.toUtf8().constData()); + + delete feed; + + Exit(0); +} + + +void Xport::PostImage() +{ + int img_id=0; + QString keyname; + QString desturl; + QString err_msg; + unsigned feed_id=0; + RDFeed *feed=NULL; + QString file_ext; + bool ret=false; + QString sql; + RDSqlQuery *q=NULL; + CURL *curl=NULL; + CURLcode curl_err; + char errstr[CURL_ERROR_SIZE]; + QDateTime now=QDateTime::currentDateTime(); + + if(!xport_post->getValue("ID",&img_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + sql=QString("select ")+ + "FEED_ID,"+ // 00 + "DATA,"+ // 01 + "FILE_EXTENSION "+ // 02 + "from FEED_IMAGES where "+ + QString().sprintf("ID=%d",img_id); + q=new RDSqlQuery(sql); + if(q->first()) { + feed_id=q->value(0).toUInt(); + xport_curl_data=q->value(1).toByteArray(); + xport_curl_data_ptr=0; + file_ext=q->value(2).toString(); + } + delete q; + if(feed_id==0) { + XmlExit("invalid image ID",400,"podcasts.cpp",LINE_NUMBER); + } + feed=new RDFeed(feed_id,rda->config(),this); + if(!feed->exists()) { + XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=feed->keyName(); + + if(!rda->user()->adminConfig()) { + delete feed; + XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER); + } + desturl=feed->purgeUrl()+"/"+RDFeed::imageFilename(feed_id,img_id,file_ext); + if((curl=curl_easy_init())==NULL) { + XmlExit("unable to get CURL handle",500,"podcasts.cpp",LINE_NUMBER); + } + + // + // Authentication Parameters + // + if((QUrl(feed->feedUrl()).scheme().toLower()=="sftp")&& + (!rda->station()->sshIdentityFile().isEmpty())&&feed->purgeUseIdFile()) { + curl_easy_setopt(curl,CURLOPT_USERNAME, + feed->purgeUsername().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE, + rda->station()->sshIdentityFile().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_KEYPASSWD, + feed->purgePassword().toUtf8().constData()); + } + else { + curl_easy_setopt(curl,CURLOPT_USERNAME, + feed->purgeUsername().toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_PASSWORD, + feed->purgePassword().toUtf8().constData()); + } + + // + // Transfer Parameters + // + curl_easy_setopt(curl,CURLOPT_URL,desturl.toUtf8().constData()); + curl_easy_setopt(curl,CURLOPT_UPLOAD,1); + curl_easy_setopt(curl,CURLOPT_READFUNCTION, __PostRss_Readfunction_Callback); + curl_easy_setopt(curl,CURLOPT_READDATA,this); + curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); + curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(curl,CURLOPT_USERAGENT, + (const char *)rda->config()->userAgent().utf8()); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errstr); + + // + // Execute it + // + switch((curl_err=curl_easy_perform(curl))) { + case CURLE_OK: + case CURLE_PARTIAL_FILE: + feed->setLastBuildDateTime(now); + ret=true; + break; + + default: + err_msg=errstr; + ret=false; + break; + } + curl_easy_cleanup(curl); + + if(!ret) { + XmlExit(err_msg,500,"podcasts.cpp",LINE_NUMBER); + } + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + rda->syslog(LOG_DEBUG, + "posted image \"%s\"",desturl.toUtf8().constData()); + + Exit(0); +} + + +void Xport::RemoveImage() +{ + int img_id=0; + QString keyname; + unsigned feed_id=0; + RDFeed *feed=NULL; + QString desturl; + QString file_ext; + QString sql; + RDSqlQuery *q=NULL; + QDateTime now=QDateTime::currentDateTime(); + RDDelete::ErrorCode del_err; + + if(!xport_post->getValue("ID",&img_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + sql=QString("select ")+ + "FEED_ID,"+ // 00 + "FILE_EXTENSION "+ // 01 + "from FEED_IMAGES where "+ + QString().sprintf("ID=%d",img_id); + q=new RDSqlQuery(sql); + if(q->first()) { + feed_id=q->value(0).toUInt(); + file_ext=q->value(1).toString(); + } + delete q; + if(feed_id==0) { + XmlExit("invalid image ID",400,"podcasts.cpp",LINE_NUMBER); + } + feed=new RDFeed(feed_id,rda->config(),this); + if(!feed->exists()) { + XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER); + } + keyname=feed->keyName(); + if(!rda->user()->adminConfig()) { + delete feed; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + feed=new RDFeed(keyname,rda->config(),this); + desturl=feed->purgeUrl()+"/"+RDFeed::imageFilename(feed_id,img_id,file_ext); + + RDDelete *del=new RDDelete(rda->config(),this); + del->setTargetUrl(desturl); + if((del_err=del-> + runDelete(feed->purgeUsername(),feed->purgePassword(), + rda->station()->sshIdentityFile(),feed->purgeUseIdFile(), + rda->config()->logXloadDebugData()))!=RDDelete::ErrorOk) { + delete del; + delete feed; + XmlExit(QString("Deletion of image \"")+desturl+"\" failed ["+ + RDDelete::errorText(del_err)+"]",500,"podcasts.cpp",LINE_NUMBER); + } + delete del; + + printf("Content-type: text/html; charset: UTF-8\n"); + printf("Status: 200\n\n"); + printf("OK\n"); + + rda->syslog(LOG_DEBUG, + "deleted image \"%s\"",desturl.toUtf8().constData()); + + delete feed; + + Exit(0); +} + diff --git a/web/rdxport/rdxport.cpp b/web/rdxport/rdxport.cpp index bb5f6aaa..e7acad6a 100644 --- a/web/rdxport/rdxport.cpp +++ b/web/rdxport/rdxport.cpp @@ -2,7 +2,7 @@ // // Rivendell web service portal // -// (C) Copyright 2010-2019 Fred Gleason +// (C) Copyright 2010-2020 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 @@ -154,147 +154,228 @@ void Xport::ripcConnectedData(bool state) // Read Command Variable and Dispatch // int command=xport_post->value("COMMAND").toInt(); + switch(command) { case RDXPORT_COMMAND_EXPORT: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_EXPORT"); Export(); break; case RDXPORT_COMMAND_IMPORT: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_IMPORT"); Import(); break; case RDXPORT_COMMAND_DELETEAUDIO: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_DELETEAUDIO"); DeleteAudio(); break; case RDXPORT_COMMAND_LISTGROUPS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTGROUPS"); ListGroups(); break; case RDXPORT_COMMAND_LISTGROUP: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTGROUP"); ListGroup(); break; case RDXPORT_COMMAND_ADDCART: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_ADDCART"); AddCart(); break; case RDXPORT_COMMAND_LISTCARTS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTCARTS"); ListCarts(); break; case RDXPORT_COMMAND_LISTCART: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTCART"); ListCart(); break; case RDXPORT_COMMAND_EDITCART: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_EDITCART"); EditCart(); break; case RDXPORT_COMMAND_REMOVECART: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_REMOVECART"); RemoveCart(); break; case RDXPORT_COMMAND_ADDCUT: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_ADDCUT"); AddCut(); break; case RDXPORT_COMMAND_LISTCUTS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTCUTS"); ListCuts(); break; case RDXPORT_COMMAND_LISTCUT: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTCUT"); ListCut(); break; case RDXPORT_COMMAND_EDITCUT: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_EDITCUT"); EditCut(); break; case RDXPORT_COMMAND_REMOVECUT: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_REMOVECUT"); RemoveCut(); break; case RDXPORT_COMMAND_EXPORT_PEAKS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_EXPORT_PEAKS"); ExportPeaks(); break; case RDXPORT_COMMAND_TRIMAUDIO: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_TRIMAUDIO"); TrimAudio(); break; case RDXPORT_COMMAND_COPYAUDIO: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_COPYAUDIO"); CopyAudio(); break; case RDXPORT_COMMAND_AUDIOINFO: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_AUDIOINFO"); AudioInfo(); break; case RDXPORT_COMMAND_AUDIOSTORE: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_AUDIOSTORE"); AudioStore(); break; case RDXPORT_COMMAND_ADDLOG: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_ADDLOG"); AddLog(); break; case RDXPORT_COMMAND_DELETELOG: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_DELETELOG"); DeleteLog(); break; case RDXPORT_COMMAND_LISTLOGS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTLOGS"); ListLogs(); break; case RDXPORT_COMMAND_LISTLOG: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTLOG"); ListLog(); break; case RDXPORT_COMMAND_SAVELOG: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_SAVELOG"); SaveLog(); break; case RDXPORT_COMMAND_LISTSCHEDCODES: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTSCHEDCODES"); ListSchedCodes(); break; case RDXPORT_COMMAND_ASSIGNSCHEDCODE: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_ASSIGNSCHEDCODE"); AssignSchedCode(); break; case RDXPORT_COMMAND_UNASSIGNSCHEDCODE: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_UNASSIGNSCHEDCODE"); UnassignSchedCode(); break; case RDXPORT_COMMAND_LISTCARTSCHEDCODES: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTCARTSCHEDCODES"); ListCartSchedCodes(); break; case RDXPORT_COMMAND_LISTSERVICES: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTSERVICES"); ListServices(); break; case RDXPORT_COMMAND_LISTSYSTEMSETTINGS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LISTSYSTEMSETTINGS"); ListSystemSettings(); break; case RDXPORT_COMMAND_LOCKLOG: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_LOCKLOG"); LockLog(); break; case RDXPORT_COMMAND_REHASH: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_REHASH"); Rehash(); break; case RDXPORT_COMMAND_SAVESTRING: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_SAVESTRING"); SaveString(); break; case RDXPORT_COMMAND_SAVEFILE: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_SAVEFILE"); SaveFile(); break; + case RDXPORT_COMMAND_SAVE_PODCAST: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_SAVE_PODCAST"); + SavePodcast(); + break; + + case RDXPORT_COMMAND_GET_PODCAST: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_GET_PODCAST"); + GetPodcast(); + break; + + case RDXPORT_COMMAND_DELETE_PODCAST: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_DELETE_PODCAST"); + DeletePodcast(); + break; + + case RDXPORT_COMMAND_POST_PODCAST: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_POST_PODCAST"); + PostPodcast(); + break; + + case RDXPORT_COMMAND_REMOVE_PODCAST: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_REMOVE_PODCAST"); + RemovePodcast(); + break; + + case RDXPORT_COMMAND_POST_RSS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_POST_RSS"); + PostRss(); + break; + + case RDXPORT_COMMAND_REMOVE_RSS: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_REMOVE_RSS"); + RemoveRss(); + break; + + case RDXPORT_COMMAND_POST_IMAGE: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_POST_IMAGE"); + PostImage(); + break; + + case RDXPORT_COMMAND_REMOVE_IMAGE: + rda->syslog(LOG_DEBUG,"processing RDXPORT_COMMAND_REMOVE_IMAGE"); + RemoveImage(); + break; + default: printf("Content-type: text/html\n\n"); printf("rdxport: missing/invalid command\n"); diff --git a/web/rdxport/rdxport.h b/web/rdxport/rdxport.h index bc8ccbd1..445b855d 100644 --- a/web/rdxport/rdxport.h +++ b/web/rdxport/rdxport.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -82,6 +83,16 @@ class Xport : public QObject void ListCartSchedCodes(); void ListServices(); void ListSystemSettings(); + void SavePodcast(); + void GetPodcast(); + void DeletePodcast(); + void PostPodcast(); + void RemovePodcast(); + bool PostRssElemental(RDFeed *feed,const QDateTime &now,QString *err_msg); + void PostRss(); + void RemoveRss(); + void PostImage(); + void RemoveImage(); void LockLog(); QString LogLockXml(bool result,const QString &log_name,const QString &guid, const QString &username,const QString &stationname, @@ -97,6 +108,10 @@ class Xport : public QObject RDFormPost *xport_post; QString xport_remote_hostname; QHostAddress xport_remote_address; + QByteArray xport_curl_data; + int xport_curl_data_ptr; + friend size_t __PostRss_Readfunction_Callback(char *buffer,size_t size, + size_t nitems,void *userdata); }; diff --git a/web/tests/Makefile.am b/web/tests/Makefile.am index 61a8372f..ca934bfe 100644 --- a/web/tests/Makefile.am +++ b/web/tests/Makefile.am @@ -2,7 +2,7 @@ ## ## Automake.am for rivendell/web/tests ## -## (C) Copyright 2010,2013-2015 Fred Gleason +## (C) Copyright 2010-2020 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 @@ -31,13 +31,14 @@ install-exec-am: cp createticket.html $(DESTDIR)@libexecdir@ cp delete_audio.html $(DESTDIR)@libexecdir@ cp deletelog.html $(DESTDIR)@libexecdir@ + cp deletepodcast.html $(DESTDIR)@libexecdir@ cp editcart.html $(DESTDIR)@libexecdir@ cp editcut.js $(DESTDIR)@libexecdir@ cp editcut.html $(DESTDIR)@libexecdir@ cp editcart.js $(DESTDIR)@libexecdir@ cp export.html $(DESTDIR)@libexecdir@ cp exportpeaks.html $(DESTDIR)@libexecdir@ - cp import.html $(DESTDIR)@libexecdir@ + cp getpodcast.html $(DESTDIR)@libexecdir@ cp import.html $(DESTDIR)@libexecdir@ cp listcart.html $(DESTDIR)@libexecdir@ cp listcarts.html $(DESTDIR)@libexecdir@ @@ -52,11 +53,18 @@ install-exec-am: cp listservices.html $(DESTDIR)@libexecdir@ cp listsystemsettings.html $(DESTDIR)@libexecdir@ cp locklog.html $(DESTDIR)@libexecdir@ + cp postimage.html $(DESTDIR)@libexecdir@ + cp postpodcast.html $(DESTDIR)@libexecdir@ + cp postrss.html $(DESTDIR)@libexecdir@ cp rehash.html $(DESTDIR)@libexecdir@ cp removecart.html $(DESTDIR)@libexecdir@ cp removecut.html $(DESTDIR)@libexecdir@ + cp removeimage.html $(DESTDIR)@libexecdir@ + cp removepodcast.html $(DESTDIR)@libexecdir@ + cp removerss.html $(DESTDIR)@libexecdir@ cp savefile.html $(DESTDIR)@libexecdir@ cp savelog.html $(DESTDIR)@libexecdir@ + cp savepodcast.html $(DESTDIR)@libexecdir@ cp savestring.html $(DESTDIR)@libexecdir@ cp trimaudio.html $(DESTDIR)@libexecdir@ cp unassignschedcode.html $(DESTDIR)@libexecdir@ @@ -73,12 +81,14 @@ uninstall-local: rm -f $(DESTDIR)@libexecdir@/createticket.html rm -f $(DESTDIR)@libexecdir@/delete_audio.html rm -f $(DESTDIR)@libexecdir@/deletelog.html + rm -f $(DESTDIR)@libexecdir@/deletepodcast.html rm -f $(DESTDIR)@libexecdir@/editcart.html rm -f $(DESTDIR)@libexecdir@/editcart.js rm -f $(DESTDIR)@libexecdir@/editcut.html rm -f $(DESTDIR)@libexecdir@/editcut.js rm -f $(DESTDIR)@libexecdir@/export.html rm -f $(DESTDIR)@libexecdir@/exportpeaks.html + rm -f $(DESTDIR)@libexecdir@/getpodcast.html rm -f $(DESTDIR)@libexecdir@/import.html rm -f $(DESTDIR)@libexecdir@/listcart.html rm -f $(DESTDIR)@libexecdir@/listcarts.html @@ -93,11 +103,18 @@ uninstall-local: rm -f $(DESTDIR)@libexecdir@/listservices.html rm -f $(DESTDIR)@libexecdir@/listsystemsettings.html rm -f $(DESTDIR)@libexecdir@/locklog.html + rm -f $(DESTDIR)@libexecdir@/postimage.html + rm -f $(DESTDIR)@libexecdir@/postpodcast.html + rm -f $(DESTDIR)@libexecdir@/postrss.html rm -f $(DESTDIR)@libexecdir@/rehash.html rm -f $(DESTDIR)@libexecdir@/removecart.html rm -f $(DESTDIR)@libexecdir@/removecut.html + rm -f $(DESTDIR)@libexecdir@/removeimage.html + rm -f $(DESTDIR)@libexecdir@/removepodcast.html + rm -f $(DESTDIR)@libexecdir@/removepodrss.html rm -f $(DESTDIR)@libexecdir@/savefile.html rm -f $(DESTDIR)@libexecdir@/savelog.html + rm -f $(DESTDIR)@libexecdir@/savepodcast.html rm -f $(DESTDIR)@libexecdir@/savestring.html rm -f $(DESTDIR)@libexecdir@/trimaudio.html rm -f $(DESTDIR)@libexecdir@/unassignschedcode.html @@ -113,12 +130,14 @@ EXTRA_DIST = addcart.html\ createticket.html\ delete_audio.html\ deletelog.html\ + deletepodcast.html\ editcart.html\ editcart.js\ editcut.html\ editcut.js\ export.html\ exportpeaks.html\ + getpodcast.html\ import.html\ listcart.html\ listcarts.html\ @@ -133,11 +152,18 @@ EXTRA_DIST = addcart.html\ listservices.html\ listsystemsettings.html\ locklog.html\ + postimage.html\ + postpodcast.html\ + postrss.html\ rehash.html\ removecart.html\ removecut.html\ + removeimage.html\ + removepodcast.html\ + removerss.html\ savefile.html\ savelog.html\ + savepodcast.html\ savestring.html\ trimaudio.html\ unassignschedcode.html\ diff --git a/web/tests/deletepodcast.html b/web/tests/deletepodcast.html new file mode 100644 index 00000000..2f7ecb52 --- /dev/null +++ b/web/tests/deletepodcast.html @@ -0,0 +1,33 @@ + + +Rivendell DELETEPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/getpodcast.html b/web/tests/getpodcast.html new file mode 100644 index 00000000..fc64032d --- /dev/null +++ b/web/tests/getpodcast.html @@ -0,0 +1,33 @@ + + +Rivendell GETPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/postimage.html b/web/tests/postimage.html new file mode 100644 index 00000000..578e7f6b --- /dev/null +++ b/web/tests/postimage.html @@ -0,0 +1,33 @@ + + +Rivendell POSTIMAGE Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/postpodcast.html b/web/tests/postpodcast.html new file mode 100644 index 00000000..8119f34a --- /dev/null +++ b/web/tests/postpodcast.html @@ -0,0 +1,33 @@ + + +Rivendell POSTPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/postrss.html b/web/tests/postrss.html new file mode 100644 index 00000000..7f4060da --- /dev/null +++ b/web/tests/postrss.html @@ -0,0 +1,33 @@ + + +Rivendell POSTRSS Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/removeimage.html b/web/tests/removeimage.html new file mode 100644 index 00000000..6529aa29 --- /dev/null +++ b/web/tests/removeimage.html @@ -0,0 +1,33 @@ + + +Rivendell REMOVEIMAGE Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/removepodcast.html b/web/tests/removepodcast.html new file mode 100644 index 00000000..6b6447e3 --- /dev/null +++ b/web/tests/removepodcast.html @@ -0,0 +1,33 @@ + + +Rivendell REMOVEPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/removerss.html b/web/tests/removerss.html new file mode 100644 index 00000000..eb672e0a --- /dev/null +++ b/web/tests/removerss.html @@ -0,0 +1,33 @@ + + +Rivendell REMOVERSS Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/savepodcast.html b/web/tests/savepodcast.html new file mode 100644 index 00000000..6c827b04 --- /dev/null +++ b/web/tests/savepodcast.html @@ -0,0 +1,36 @@ + + +Rivendel SAVEPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
FILENAME:
 
+
+ +