2023-05-14 Fred Gleason <fredg@paravelsystems.com>

* Added a 'FEEDS.CDN_PURGE_PLUGIN_PATH' field to the database.
	* Incremented the database version to 369.
	* Added ' RDFeed::cdnPurgePluginPath()' and
	'RDFeed::setCdnPurgePluginPath()' methods.
	* Removed the RD_MODULES_DIR #define from 'lib/rd.h'.
	* Changed the RD_PYPAD_SCRIPT_DIR #define in 'lib/rd.h' to be
	a static value of "/usr/lib/rivendell/pypad".
	* Added a 'CDN Purge Plug-In' control to the 'Feed' dialog in
	rdadmin(1).

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2023-05-14 08:56:49 -04:00
parent 7716315bd9
commit ff63adcf3c
21 changed files with 972 additions and 118 deletions

View File

@ -24075,3 +24075,13 @@
* Modified rdcastmanager(1) so that the 'Title' and 'Description'
field of the 'Editing Item' dialog default to empty after posting
a new item when AutoPost is disabled.
2023-05-14 Fred Gleason <fredg@paravelsystems.com>
* Added a 'FEEDS.CDN_PURGE_PLUGIN_PATH' field to the database.
* Incremented the database version to 369.
* Added ' RDFeed::cdnPurgePluginPath()' and
'RDFeed::setCdnPurgePluginPath()' methods.
* Removed the RD_MODULES_DIR #define from 'lib/rd.h'.
* Changed the RD_PYPAD_SCRIPT_DIR #define in 'lib/rd.h' to be
a static value of "/usr/lib/rivendell/pypad".
* Added a 'CDN Purge Plug-In' control to the 'Feed' dialog in
rdadmin(1).

View File

@ -2,7 +2,7 @@
##
## Makefile.am for rivendell/apis
##
## (C) Copyright 2018-2022 Fred Gleason <fredg@paravelsystems.com>
## (C) Copyright 2018-2023 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
@ -20,7 +20,8 @@
##
## Use automake to process this into a Makefile.in
SUBDIRS = pypad\
SUBDIRS = cdn\
pypad\
rivwebcapi\
rivwebpyapi

38
apis/cdn/Makefile.am Normal file
View File

@ -0,0 +1,38 @@
## automake.am
##
## Automake.am for Rivendell CDN interface
##
## (C) Copyright 2023 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of
## the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public
## License along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
## Use automake to process this into a Makefile.in
SUBDIRS = scripts
CLEANFILES = *~\
*.idb\
*ilk\
*.obj\
*.pdb\
*.qm\
moc_*
MAINTAINERCLEANFILES = *~\
*.tar.gz\
aclocal.m4\
configure\
Makefile.in\
moc_*

View File

@ -0,0 +1,48 @@
## Makefile.am
##
## Makefile.am for Rivendell apis/cdn/scripts
##
## (C) Copyright 2023 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of
## the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public
## License along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
## Use automake to process this into a Makefile.in
install-exec-am:
mkdir -p $(DESTDIR)/usr/lib/rivendell/cdn
cp aka_purge.sh $(DESTDIR)/usr/lib/rivendell/cdn/
cp akamai_purge.cdn $(DESTDIR)/usr/lib/rivendell/cdn/
uninstall-local:
rm -f $(DESTDIR)/usr/lib/rivendell/cdn/aka_purge.sh
rm -f $(DESTDIR)/usr/lib/rivendell/cdn/akamai_purge.cdn
EXTRA_DIST = aka_purge.sh\
akamai_purge.cdn
CLEANFILES = *~\
*.idb\
*ilk\
*.obj\
*.pdb\
*.qm\
moc_*
MAINTAINERCLEANFILES = *~\
*.tar.gz\
aclocal.m4\
configure\
Makefile.in\
moc_*

573
apis/cdn/scripts/aka_purge.sh Executable file
View File

@ -0,0 +1,573 @@
#!/bin/bash
## ----------------------------------------------------------------------------
#
# SCRIPT NAME: aka_purge.sh
#
# DESCRIPTION: Purge the Akamai cache via CCU REST API v3.
#
# REVISION: v0.9
# DATE: May 2019
# AUTHORS: Luca, Genasci <luca.genasci@rsi.ch>
# Marco, De March <mdemarch@akamai.com>
#
# CONTRIBUTORS: Raffaele, Rusconi <raffaele.rusconi@prada.com>
#
# TESTED: Tested in:
# GNU bash, version 4.4.12(3)-release (x86_64-unknown-cygwin)
# GNU bash, version 5.0.7(1)-release (x86_64-pc-linux-gnu)
# GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
# GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
#
## ----------------------------------------------------------------------------
## Definition if functions ----------------------------------------------------
function aa_set {
local key="${1}"
local val="${2}"
shift 2
declare -a arr=( ${@} )
local t_key
local idx=0
for el in "${arr[@]}"
do
t_key="${el%%:*}"
[ "${key}" == "${t_key}" ] && { arr[idx]="${key}:${val}"; echo "${arr[@]}"; return 0; }
let idx++
done
arr[idx]="${key}:${val}"
echo "${arr[@]}"
return 0
}
function aa_get {
local key="${1}"
shift 1
declare -a arr=( ${@} )
for el in "${arr[@]}"
do
t_key="${el%%:*}"
[ "${key}" == "${t_key}" ] && { echo "${el#*:}"; return 0; }
done
return 1
}
function _get_property {
[ "$#" -lt 2 ] && return 1
local RC=$1
local PROP=$2
local value=$(cat ${RC} | sed "/^\s*#/d;s/\s*#[^\"']*$//" | grep ${PROP} | tail -n 1 | cut -d '=' -f2- )
if [ -z "${value}" ]; then
return 1
else
echo ${value}
return 0
fi
}
function get_properties {
local file="${1}"
declare -a aka_props
local tmp
tmp=$( _get_property "${file}" client_secret )
[ -z "${tmp}" ] && { >&2 echo "ERROR: Please, set variable client_secret in file ${file}!!!"; exit 1; }
aka_props=( $( aa_set client_secret ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" client_token )
[ -z "${tmp}" ] && { >&2 echo "ERROR: Please, set variable client_token in file ${file}!!!"; exit 1; }
aka_props=( $( aa_set client_token ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" access_token )
[ -z "${tmp}" ] && { >&2 echo "ERROR: Please, set variable access_token in file ${file}!!!"; exit 1; }
aka_props=( $( aa_set access_token ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" host )
[ -z "${tmp}" ] && { >&2 echo "ERROR: Please, set variable host in file ${file}!!!"; exit 1; }
aka_props=( $( aa_set host ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" network )
[ -z "${tmp}" ] && aka_props=( $( aa_set network staging "${aka_props[@]}" ) ) || aka_props=( $( aa_set network ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" action )
[ -z "${tmp}" ] && aka_props=( $( aa_set action invalidate "${aka_props[@]}" ) ) || aka_props=( $( aa_set action ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" type )
[ -z "${tmp}" ] && aka_props=( $( aa_set type url "${aka_props[@]}" ) ) || aka_props=( $( aa_set type ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" n_retries )
[ -z "${tmp}" ] && aka_props=( $( aa_set n_retries 3 "${aka_props[@]}" ) ) || aka_props=( $( aa_set n_retries ${tmp} "${aka_props[@]}" ) )
tmp=$( _get_property "${file}" t_retry )
[ -z "${tmp}" ] && aka_props=( $( aa_set t_retry 60 "${aka_props[@]}" ) ) || aka_props=( $( aa_set t_retry ${tmp} "${aka_props[@]}" ) )
echo "${aka_props[@]}"
return 0
}
function mk_nonce {
local s=$1
if [ -z ${s} ]; then s=$( date -u +'%Y%m%dT%H:%M:%S%z' ); fi
if [ $(uname) == 'Darwin' ]; then
echo -n "${s}" | md5 -r | cut -d ' ' -f1 | sed 's/.\{4\}/&-/g' | sed 's/.$//'
else
echo -n "${s}" | md5sum | cut -d ' ' -f1 | sed 's/.\{4\}/&-/g' | sed 's/.$//'
fi
}
function base64_hmac_sha256 {
[ "$#" -lt 2 ] && return 1
local key=$1
local value=$2
echo -ne "${value}"| openssl sha256 -binary -hmac "${key}" | openssl base64
}
function base64_sha256 {
[ "$#" -lt 1 ] && return 1
local value=$1
echo -ne "${value}" | openssl dgst -binary -sha256 | openssl base64
}
function mk_auth_header {
[ "$#" -lt 3 ] && return 1
local timestamp=${1}
local nonce=${2}
shift 2
declare -a aka_props=( ${@} )
# EG1-HMAC-SHA256
echo -n "client_token="$(aa_get client_token "${aka_props[@]}")";access_token="$( aa_get access_token "${aka_props[@]}" )";timestamp=${timestamp};nonce=${nonce};"
}
function sign_data {
[ "$#" -lt 2 ] && return 1
local key=${1}
shift
declare -a data_to_sign=( ${@} )
local method=$( aa_get method "${data_to_sign[@]}" )
local scheme=$( aa_get scheme "${data_to_sign[@]}" )
local host=$( aa_get host "${data_to_sign[@]}" )
local request_uri=$( aa_get request_uri "${data_to_sign[@]}" )
local hash_content=$( aa_get hash_content "${data_to_sign[@]}" )
local auth_header=$( aa_get auth_header "${data_to_sign[@]}" )
local data="${method}\t${scheme}\t${host}\t${request_uri}\t\t${hash_content}\tEG1-HMAC-SHA256 ${auth_header}"
base64_hmac_sha256 "${key}" "${data}"
}
function mk_body {
local type="${1}"
local objects="${2}"
local domain="${3}"
local arr_objects
local objs
IFS=',' read -r -a arr_objects <<< "${objects}"
for i in ${!arr_objects[@]}
do
local tmp=$( echo ${arr_objects[i]} | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's,/,\\/,g' )
if [ "${type}" == "cpcode" ]; then
objs="${objs},${tmp}"
else
objs="${objs},\"${tmp}\""
fi
done
objs=$( echo "${objs}" | sed 's/^,//' )
if [ "${type}" == "url" ]; then
echo "{\"hostname\":\"${domain}\",\"objects\":[${objs}]}"
else
echo "{\"objects\":[${objs}]}"
fi
}
function get_objects {
local file="${1}"
local domain="${2}"
local type="${3}"
local objs
case ${type} in
url )
cat "${file}" | grep -E '^(http(s)?://'${domain}')?/' | sed -E 's|^http(s)?://'${domain}'||' | paste -sd ','
;;
cpcode )
cat "${file}" | sed -E '/^[0-9]+$/!d' | paste -sd ','
;;
tag )
cat "${file}" | paste -sd ','
;;
* ) return 1 ;;
esac
return 0
}
function separate_objects {
local objs="${1}"
local limit=49000
local bytes buffer
local b_len=0
declare -a arr
bytes=$( echo "${objs}" | wc -c )
[ ${bytes} -le ${limit} ] && { echo ${objs}; return 0; }
while true
do
buffer=${objs:${b_len}:${limit}}
[ -z ${buffer} ] && break
buffer=${buffer%,*}
arr+=( ${buffer} )
b_len=$(( ${b_len} + ${#buffer} + 1 ))
echo ${b_len}
done
echo "${arr[@]}"
return 0
}
function countdown(){
local cn=${1}
local lb=${2}
date1=$((`date +%s` + ${cn}));
while [ "$date1" -ge `date +%s` ]; do
echo -ne "${lb}: $(date -u --date @$(($date1 - `date +%s`)) +%H:%M:%S)\r";
sleep 0.1
done
}
function getopt_type {
local type="BSD"
getopt -T > /dev/null
[ $? -eq 4 ] && type="GNU"
echo "${type}"
}
## Options of script ----------------------------------------------------------
P_NAME=${0##*\/}
GETOPT_TYPE=$( getopt_type )
WARNING=
[ "${GETOPT_TYPE}" != "GNU" ] && {
WARNING+=$'\n'$'\e[93m';
WARNING+="WARNING"$'\n';
WARNING+=" The version of your getopt is not GNU."$'\n';
WARNING+=" If you want use the long options use the brew command to install gnu-getopt."$'\n';
WARNING+=" ${P_NAME} work fine with short options."$'\n';
WARNING+=$'\e[0m';
}
CONF=/usr/share/rivendell/keys/edgegrid.conf
declare -a AKA_PROPS=$( get_properties "${CONF}" )
usage() {
cat << EOF
usage: ${P_NAME} [OPTIONS] -o <obj1[,obj2,obj3,...]>
Purge the Akamai cache via CCU REST API v3.
REMARKS
Please create a config file in /usr/share/rivendell/keys
${WARNING}
PARAMETERS:
-o, --objects List of objects to purge (with comma separated values)
-i, --input-file Input file that contain a list of objects.
The separator value in the file mode is the carriage
return
Type | Comment
--------------------------------------------------------
url | The addresses considered, depend of the domain
| (see the option -d). If an address not have a
| domain it's included in the purge procedure.
--------------------------------------------------------
cpcode | List of the cpcodes. The items must be numbers
| otherwise will be excluded.
--------------------------------------------------------
tag | List of a tags
OPTIONS:
-t, --type Type of objects
Possible values: url, cpcode or tag
Default: ${TYPE}
-d, --domain Domain site (es. your.domain.com).
To use with type=url
-a, --action The desired action to manage the cache
Possible values: invalidate or delete
Default: ${ACTION}
-n, --network The network on which you want to invalidate or delete content
Possible values: staging or production
Default: ${NETWORK}
-r, --show-quests Show the requests
-h, --help Show this message
-v, --version Show version
CONFIG FILE:
In the config file the following values have to been declared:
client_secret = <your client secret>
client_token = <your client token>
access_token = <your access token>
host = <your akamai host>
There is the possibility to set the default values:
network = <staging | production>
action = <invalidate | delete>
type = <url | cpcode | tag>
If no values are declared the default ones are:
network = staging
action = invalidate
type = url
For manage an error:
n_retries = <number of retries> (default 3)
t_retry = <time of next retry> (default 60)
EOF
}
[ "${GETOPT_TYPE}" == "GNU" ] && ARGS=$( getopt -o "d:t:o:i:a:n:rhv" -l "domain:,type:,objects:,input-file:,action:,network:,show-quests,help,version" -n "$0" -- "$@" ) || ARGS=$( getopt "d:t:o:i:a:n:rhv" "$@" )
eval set -- "$ARGS"
VERSION=0.9
NETWORK=$( aa_get network "${AKA_PROPS[@]}" )
ACTION=$( aa_get action "${AKA_PROPS[@]}" )
TYPE=$( aa_get type "${AKA_PROPS[@]}" )
N_RETRIES=$( aa_get n_retries "${AKA_PROPS[@]}" )
T_RETRY=$( aa_get t_retry "${AKA_PROPS[@]}" )
S_QUEST=0
while true; do
case "$1" in
-d | --domain )
DOMAIN=${2}
shift 2
;;
-t | --type )
TYPE=${2}
shift 2
;;
-o | --objects )
OBJECTS=${2}
shift 2
;;
-i | --input-file )
IFILE=${2}
shift 2
;;
-a | --action )
ACTION=${2}
shift 2
;;
-n | --network )
NETWORK=${2}
shift 2
;;
-r | --show-quests )
S_QUEST=1
shift
;;
-h | --help )
usage
exit 0
;;
-v | --version )
echo $0 version: $VERSION
exit 0
;;
-- )
shift
break
;;
* )
>&2 echo "Internal error!"
exit 1
;;
esac
done
## Main -----------------------------------------------------------------------
#[ -z "${TYPE}" ] && { usage; exit 1; }
#[ -z "${OBJECTS}" ] && [ ! -f "${IFILE}" ] && { usage; exit 1; }
#[ "${TYPE}" == "url" ] && [ -z "${DOMAIN}" ] && { usage; exit 1; }
[ "${TYPE}" != "url" ] && [ ! -z "${DOMAIN}" ] && { echo "WARNING: type is different of url then -d,--domain will be ignored ..."; }
[ "${TYPE}" != "url" ] && [ "${TYPE}" != "cpcode" ] && [ "${TYPE}" != "tag" ] && { >&2 echo "ERROR: Possible value of OBJECT TYPE is url, cpcode or tag"; exit 1; }
[ "${NETWORK}" != "staging" ] && [ "${NETWORK}" != "production" ] && { >&2 echo "ERROR: Possible value of NETWORK is staging or production"; exit 1; }
[ "${ACTION}" != "invalidate" ] && [ "${ACTION}" != "delete" ] && { >&2 echo "ERROR: Possible value of ACTION is invalidate or delete"; exit 1; }
[ -z "${OBJECTS}" ] && OBJECTS=$( get_objects "${IFILE}" ${DOMAIN} ${TYPE} )
ARR_OBJS=( $( separate_objects ${OBJECTS} ) )
NREQ="${#ARR_OBJS[@]}"
#echo Number of total requests: ${NREQ}
#echo
TMP_RETRIES=${N_RETRIES}
while [ ${#ARR_OBJS[@]} -gt 0 ]
do
objs="${ARR_OBJS[0]}" # get first element
BODY=$( mk_body "${TYPE}" "${objs}" "${DOMAIN}" )
[ $(echo -ne "${BODY}" | wc -c ) -gt 131072 ] && { >&2 echo "ERROR: The body size is greater than 131072!!!" exit 1; }
TIMESTAMP=$( date -u +'%Y%m%dT%H:%M:%S%z' )
NONCE=$( mk_nonce ${TIMESTAMP} )
SIGN_KEY=$( base64_hmac_sha256 $( aa_get client_secret "${AKA_PROPS[@]}" ) ${TIMESTAMP} )
AUTH_HEADER=$( mk_auth_header ${TIMESTAMP} ${NONCE} "${AKA_PROPS[@]}" )
declare -a DATA_TO_SIGN
DATA_TO_SIGN=( $(aa_set method POST "${DATA_TO_SIGN[@]}" ) )
DATA_TO_SIGN=( $(aa_set scheme https "${DATA_TO_SIGN[@]}" ) )
DATA_TO_SIGN=( $(aa_set host $( aa_get host "${AKA_PROPS[@]}" ) "${DATA_TO_SIGN[@]}" ) )
DATA_TO_SIGN=( $(aa_set request_uri "/ccu/v3/${ACTION}/${TYPE}/${NETWORK}" "${DATA_TO_SIGN[@]}" ) )
DATA_TO_SIGN=( $(aa_set hash_content $( base64_sha256 "${BODY}" ) "${DATA_TO_SIGN[@]}" ) )
DATA_TO_SIGN=( $(aa_set auth_header ${AUTH_HEADER} "${DATA_TO_SIGN[@]}" ) )
SIGNED_DATA=$( sign_data "${SIGN_KEY}" "${DATA_TO_SIGN[@]}" )
SIGNED_AUTH_HEADER="Authorization: EG1-HMAC-SHA256 ${AUTH_HEADER}signature=${SIGNED_DATA}"
H_JSON="Content-Type: application/json"
if [ ${TMP_RETRIES} -le 1 ]; then
ARR_OBJS=( "${ARR_OBJS[@]:1}" )
TMP_RETRIES=${N_RETRIES};
echo "ERROR: This request as aborted, max number of retries exceeded.";
echo
continue;
fi
# if [ ${TMP_RETRIES} -eq ${N_RETRIES} ]; then
# echo "Number of remaining requests: ${#ARR_OBJS[@]}"
# echo
# fi
REQ_CURL=$( echo curl -s -w \"\\n%{http_code}\\n\" \
-H \"Expect:\" \
-H \"User-Agent:${P_NAME} v${VERSION}\" \
-H \"Accept:${H_JSON}\" \
-H \"${H_JSON}\" \
-H \"${SIGNED_AUTH_HEADER}\" \
-X POST -d \'${BODY}\' \"$( aa_get scheme "${DATA_TO_SIGN[@]}" )://$( aa_get host "${DATA_TO_SIGN[@]}" )$( aa_get request_uri "${DATA_TO_SIGN[@]}" )\" )
[ ${S_QUEST} -eq 1 ] && JSON_RES=$( echo "${REQ_CURL}" | bash -x ) || JSON_RES=$( echo "${REQ_CURL}" | bash -x 2> /dev/null )
HTTP_CODE=$( echo "${JSON_RES}" | tail -1 )
case "${HTTP_CODE}" in
"201" )
ARR_OBJS=( "${ARR_OBJS[@]:1}" ) # pop first element
;;
"429" )
TMP_RETRIES=$(( TMP_RETRIES - 1 ))
echo
countdown ${T_RETRY} "ERROR: Akamai purge rate limit (code ${HTTP_CODE}) -> (${TMP_RETRIES}) Retry request in"
echo
echo
continue
;;
"507" )
TMP_RETRIES=$(( TMP_RETRIES - 1 ))
echo
countdown ${T_RETRY} "ERROR: Akamai purge queue limit (code ${HTTP_CODE}) -> (${TMP_RETRIES}) Retry request in"
echo
echo
continue
;;
"400" )
echo
echo "ABORT: Akamai bad request (code ${HTTP_CODE}) -> Check your parameters"
echo
echo
exit 1
;;
"401" )
echo
echo "ABORT: Akamai credential problem (code ${HTTP_CODE}) -> Check credentials used to connect to the edge server"
echo
echo
exit 1
;;
"403" )
echo
echo "ABORT: Akamai authorization token problem (code ${HTTP_CODE}) -> Check the authorizations for the client and the property configurations being purged"
echo
echo
exit 1
;;
"413" )
echo
echo "ABORT: Akamai request entity too large problem (code ${HTTP_CODE}) -> AHHHH Problem with script function separate_objects"
echo
echo
exit 1
;;
* )
echo
echo "ABORT: Akamai unknow problem (code ${HTTP_CODE})"
echo
echo
exit 1
;;
esac
JSON_RESP=$( echo "${JSON_RES}" | sed '$ d' )
which jq > /dev/null 2>&1
# if [ ${?} -ne 0 ]; then
# echo
# echo "INFO: For better formatting to the output, install jq tool (https://stedolan.github.io/jq/)"
# echo
# echo "${JSON_RESP}"
# else
# echo
# echo "${JSON_RESP}" | jq .
# fi
done
exit 0

View File

@ -0,0 +1,38 @@
#!/bin/bash
# cdn_akamai_purge.sh
#
# CDN Plug-in for Akamai FastPurge v3
#
# (C) Copyright 2023 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
TARGET_URL=$1
_url=$TARGET_URL
[[ $_url =~ ^https?://[^/]+ ]] && _url="${BASH_REMATCH[0]}"
DOMAIN=${_url#https://}
echo -n "URL: "
echo $TARGET_URL
echo -n "DOMAIN: "
echo $DOMAIN
aka_purge.sh $DOMAIN -o $URL

View File

@ -2,7 +2,7 @@
##
## Makefile.am for Rivendell pypad/examples
##
## (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
## (C) Copyright 2018-2023 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
@ -21,89 +21,89 @@
## Use automake to process this into a Makefile.in
install-exec-am:
mkdir -p $(DESTDIR)$(libdir)/rivendell/pypad
../../../helpers/install_python.sh pypad_ando.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ando.py
cp pypad_ando.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ando.exemplar
../../../helpers/install_python.sh pypad_filewrite.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_filewrite.py
cp pypad_filewrite.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_filewrite.exemplar
../../../helpers/install_python.sh pypad_httpget.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_httpget.py
cp pypad_httpget.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_httpget.exemplar
../../../helpers/install_python.sh pypad_icecast2.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_icecast2.py
cp pypad_icecast2.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_icecast2.exemplar
../../../helpers/install_python.sh pypad_inno713.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_inno713.py
cp pypad_inno713.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_inno713.exemplar
../../../helpers/install_python.sh pypad_ino713_tcp.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ino713_tcp.py
cp pypad_ino713_tcp.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ino713_tcp.exemplar
../../../helpers/install_python.sh pypad_live365.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_live365.py
cp pypad_live365.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_live365.exemplar
../../../helpers/install_python.sh pypad_liqcomp.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_liqcomp.py
cp pypad_liqcomp.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_liqcomp.exemplar
../../../helpers/install_python.sh pypad_nautel.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_nautel.py
cp pypad_nautel.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_nautel.exemplar
../../../helpers/install_python.sh pypad_serial.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_serial.py
cp pypad_serial.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_serial.exemplar
../../../helpers/install_python.sh pypad_shoutcast1.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_shoutcast1.py
cp pypad_shoutcast1.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_shoutcast1.exemplar
../../../helpers/install_python.sh pypad_spinitron.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spinitron.py
cp pypad_spinitron.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spinitron.exemplar
../../../helpers/install_python.sh pypad_spottrap.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spottrap.py
cp pypad_spottrap.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spottrap.exemplar
../../../helpers/install_python.sh pypad_tunein.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_tunein.py
cp pypad_tunein.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_tunein.exemplar
../../../helpers/install_python.sh pypad_udp.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_udp.py
cp pypad_udp.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_udp.exemplar
../../../helpers/install_python.sh pypad_urlwrite.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_urlwrite.py
cp pypad_urlwrite.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_urlwrite.exemplar
../../../helpers/install_python.sh pypad_walltime.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_walltime.py
cp pypad_walltime.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_walltime.exemplar
../../../helpers/install_python.sh pypad_xcmd.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xcmd.py
cp pypad_xcmd.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xcmd.exemplar
../../../helpers/install_python.sh pypad_xds.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xds.py
cp pypad_xds.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xds.exemplar
../../../helpers/install_python.sh pypad_xmpad.py $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xmpad.py
cp pypad_xmpad.exemplar $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xmpad.exemplar
mkdir -p $(DESTDIR)/usr/lib/rivendell/pypad
../../../helpers/install_python.sh pypad_ando.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ando.py
cp pypad_ando.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ando.exemplar
../../../helpers/install_python.sh pypad_filewrite.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_filewrite.py
cp pypad_filewrite.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_filewrite.exemplar
../../../helpers/install_python.sh pypad_httpget.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_httpget.py
cp pypad_httpget.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_httpget.exemplar
../../../helpers/install_python.sh pypad_icecast2.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_icecast2.py
cp pypad_icecast2.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_icecast2.exemplar
../../../helpers/install_python.sh pypad_inno713.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_inno713.py
cp pypad_inno713.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_inno713.exemplar
../../../helpers/install_python.sh pypad_ino713_tcp.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ino713_tcp.py
cp pypad_ino713_tcp.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ino713_tcp.exemplar
../../../helpers/install_python.sh pypad_live365.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_live365.py
cp pypad_live365.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_live365.exemplar
../../../helpers/install_python.sh pypad_liqcomp.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_liqcomp.py
cp pypad_liqcomp.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_liqcomp.exemplar
../../../helpers/install_python.sh pypad_nautel.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_nautel.py
cp pypad_nautel.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_nautel.exemplar
../../../helpers/install_python.sh pypad_serial.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_serial.py
cp pypad_serial.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_serial.exemplar
../../../helpers/install_python.sh pypad_shoutcast1.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_shoutcast1.py
cp pypad_shoutcast1.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_shoutcast1.exemplar
../../../helpers/install_python.sh pypad_spinitron.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spinitron.py
cp pypad_spinitron.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spinitron.exemplar
../../../helpers/install_python.sh pypad_spottrap.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spottrap.py
cp pypad_spottrap.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spottrap.exemplar
../../../helpers/install_python.sh pypad_tunein.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_tunein.py
cp pypad_tunein.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_tunein.exemplar
../../../helpers/install_python.sh pypad_udp.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_udp.py
cp pypad_udp.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_udp.exemplar
../../../helpers/install_python.sh pypad_urlwrite.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_urlwrite.py
cp pypad_urlwrite.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_urlwrite.exemplar
../../../helpers/install_python.sh pypad_walltime.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_walltime.py
cp pypad_walltime.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_walltime.exemplar
../../../helpers/install_python.sh pypad_xcmd.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xcmd.py
cp pypad_xcmd.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xcmd.exemplar
../../../helpers/install_python.sh pypad_xds.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xds.py
cp pypad_xds.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xds.exemplar
../../../helpers/install_python.sh pypad_xmpad.py $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xmpad.py
cp pypad_xmpad.exemplar $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xmpad.exemplar
uninstall-local:
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ando.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ando.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_filewrite.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_filewrite.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_httpget.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_httpget.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_icecast2.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_icecast2.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_inno713.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_inno713.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ino713_tcp.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_ino713_tcp.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_live365.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_live365.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_liqcomp.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_liqcomp.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_nautel.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_nautel.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_serial.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_serial.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_shoutcast1.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_shoutcast1.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spinitron.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spinitron.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spottrap.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_spottrap.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_tunein.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_tunein.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_udp.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_udp.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_urlwrite.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_urlwrite.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_walltime.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_walltime.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xcmd.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xcmd.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xds.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xds.py
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xmpad.exemplar
rm -f $(DESTDIR)$(libdir)/rivendell/pypad/pypad_xmpad.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ando.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ando.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_filewrite.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_filewrite.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_httpget.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_httpget.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_icecast2.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_icecast2.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_inno713.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_inno713.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ino713_tcp.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_ino713_tcp.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_live365.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_live365.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_liqcomp.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_liqcomp.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_nautel.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_nautel.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_serial.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_serial.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_shoutcast1.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_shoutcast1.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spinitron.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spinitron.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spottrap.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_spottrap.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_tunein.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_tunein.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_udp.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_udp.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_urlwrite.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_urlwrite.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_walltime.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_walltime.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xcmd.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xcmd.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xds.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xds.py
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xmpad.exemplar
rm -f $(DESTDIR)/usr/lib/rivendell/pypad/pypad_xmpad.py
EXTRA_DIST = pypad_ando.exemplar\
pypad_ando.py\

View File

@ -3,7 +3,7 @@ dnl
dnl Autoconf configuration for Rivendell.
dnl Use autoconf to process this into a configure script
dnl
dnl (C) Copyright 2002-2022 Fred Gleason <fredg@paravelsystems.com>
dnl (C) Copyright 2002-2023 Fred Gleason <fredg@paravelsystems.com>
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License version 2 as
@ -621,6 +621,8 @@ AC_CONFIG_FILES([rivendell.spec \
LICENSES/Makefile \
apis/setup.cfg \
apis/Makefile \
apis/cdn/scripts/Makefile \
apis/cdn/Makefile \
apis/pypad/Makefile \
apis/pypad/api/Makefile \
apis/pypad/scripts/Makefile \

View File

@ -159,20 +159,8 @@
Install a copy of the script, along with a copy of the
sample configuration file (if needed) in the Rivendell
PyPAD script directory
<userinput><replaceable>$(libdir)</replaceable>/rivendell/pypad/</userinput>.
<userinput>/usr/lib/rivendell/pypad/</userinput>.
</para>
<note>
In the RHEL/CentOS reference installation, this directory
is <userinput>/usr/lib64/rivendell/pypad/</userinput>.
</note>
<note>
The <userinput><replaceable>$(libdir)</replaceable></userinput>
path component evaluates to
the value used for the <userinput>--libdir</userinput>
switch that was passed to the &quot;configure&quot; script
when Rivendell was built [default value:
<userinput>/usr/local/lib</userinput>].
</note>
</listitem>
</itemizedlist>
<para>

View File

@ -46,3 +46,4 @@ UPLOAD_QUALITY int(11)
UPLOAD_EXTENSION varchar(16)
NORMALIZE_LEVEL int(11)
SHA1_HASH varchar(40)
CDN_PURGE_PLUGIN_PATH text

View File

@ -2,7 +2,7 @@
//
// The Current Database Schema Version for Rivendell
//
// (C) Copyright 2002-2022 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2002-2023 Fred Gleason <fredg@paravelsystems.com>
//
// 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,7 +24,7 @@
/*
* Current Database Version
*/
#define RD_VERSION_DATABASE 368
#define RD_VERSION_DATABASE 369
#endif // DBVERSION_H

View File

@ -725,6 +725,19 @@ void RDFeed::setSha1Hash(const QString &str) const
}
QString RDFeed::cdnPurgePluginPath() const
{
return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CDN_PURGE_PLUGIN_PATH").
toString();
}
void RDFeed::setCdnPurgePluginPath(const QString &str) const
{
SetRow("CDN_PURGE_PLUGIN_PATH",str);
}
QByteArray RDFeed::imageData(int img_id) const
{
return RDGetSqlValue("FEED_IMAGES","ID",img_id,"DATA").toByteArray();

View File

@ -131,6 +131,8 @@ class RDFeed : public QObject
void setNormalizeLevel(int lvl) const;
QString sha1Hash() const;
void setSha1Hash(const QString &str) const;
QString cdnPurgePluginPath() const;
void setCdnPurgePluginPath(const QString &str) const;
QByteArray imageData(int img_id) const;
int importImageFile(const QString &pathname,QString *err_msg,
QString desc="") const;

View File

@ -4,7 +4,7 @@
// Install-Dependent Path Values for Rivendell
//
// (C) Copyright 2008-2021 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2008-2023 Fred Gleason <fredg@paravelsystems.com>
//
// 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,11 +23,6 @@
#ifndef RDPATHS_H
#define RDPATHS_H
/*
* Rivendell Loadable Modules Directory
*/
#define RD_MODULES_DIR "@libdir@/rivendell"
/*
* Default Skins
*/
@ -50,7 +45,13 @@ extern "C" {
* PyPAD
*/
#define RD_PYPAD_PYTHON_PATH "@PYTHON@"
#define RD_PYPAD_SCRIPT_DIR "@libdir@/rivendell/pypad"
//#define RD_PYPAD_SCRIPT_DIR "@libdir@/rivendell/pypad"
#define RD_PYPAD_SCRIPT_DIR "/usr/lib/rivendell/pypad"
/*
* CDN Plug-ins
*/
#define RD_CDN_SCRIPT_DIR "/usr/lib/rivendell/cdn"
#endif // RDPATHS_H

View File

@ -22,11 +22,13 @@
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QMessageBox>
#include <rdapplication.h>
#include <rddelete.h>
#include <rdexport_settings_dialog.h>
#include <rdpaths.h>
#include <rdupload.h>
#include "edit_feed.h"
@ -353,6 +355,23 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
feed_castorder_label->setFont(labelFont());
feed_castorder_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
//
// CDN Purge Plug-In Path
//
feed_cdn_purge_plugin_path_label=new QLabel(tr("CDN Purge Plug-In")+":",this);
feed_cdn_purge_plugin_path_label->setFont(labelFont());
feed_cdn_purge_plugin_path_label->
setAlignment(Qt::AlignRight|Qt::AlignVCenter);
feed_cdn_purge_plugin_path_edit=new QLineEdit(this);
feed_cdn_purge_plugin_path_select_button=new QPushButton(tr("Select"),this);
feed_cdn_purge_plugin_path_select_button->setFont(subButtonFont());
connect(feed_cdn_purge_plugin_path_select_button,SIGNAL(clicked()),
this,SLOT(selectCdnPurgePlugin()));
feed_cdn_purge_plugin_path_clear_button=new QPushButton(tr("Clear"),this);
feed_cdn_purge_plugin_path_clear_button->setFont(subButtonFont());
connect(feed_cdn_purge_plugin_path_clear_button,SIGNAL(clicked()),
this,SLOT(clearCdnPurgePlugin()));
//
// Default Item Image
//
@ -487,6 +506,10 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
feed_normalize_check->setChecked(true);
feed_normalize_spin->setValue(feed_feed->normalizeLevel()/1000);
}
feed_cdn_purge_plugin_path_edit->setText(feed_feed->cdnPurgePluginPath());
if(feed_cdn_purge_plugin_path_edit->text().isEmpty()) {
feed_cdn_purge_plugin_path_edit->setText(tr("[none]"));
}
feed_castorder_box->setCurrentIndex(feed_feed->castOrderIsAscending());
feed_item_image_box->setCurrentImageId(feed_feed->defaultItemImageId());
@ -503,7 +526,7 @@ EditFeed::~EditFeed()
QSize EditFeed::sizeHint() const
{
return QSize(1000,710);
return QSize(1000,734);
}
@ -639,6 +662,33 @@ void EditFeed::copyItemXmlData()
}
void EditFeed::selectCdnPurgePlugin()
{
QString path=feed_cdn_purge_plugin_path_edit->text();
if(path==tr("[none]")) {
path="";
}
path=QFileDialog::getOpenFileName(this,"RDAdmin - "+tr("Select Plug-In"),
RD_CDN_SCRIPT_DIR,
tr("Rivendell CDN Scripts (*.cdn)")+";;"+
tr("All Files (*)"));
if(!path.isNull()) {
if(path.isEmpty()) {
feed_cdn_purge_plugin_path_edit->setText(tr("[none]"));
}
else {
feed_cdn_purge_plugin_path_edit->setText(path);
}
}
}
void EditFeed::clearCdnPurgePlugin()
{
feed_cdn_purge_plugin_path_edit->setText(tr("[none]"));
}
void EditFeed::okData()
{
if(feed_is_superfeed_box->currentIndex()&&
@ -707,6 +757,12 @@ void EditFeed::okData()
else {
feed_feed->setNormalizeLevel(1);
}
if(feed_cdn_purge_plugin_path_edit->text()==tr("[none]")) {
feed_feed->setCdnPurgePluginPath(QString());
}
else {
feed_feed->setCdnPurgePluginPath(feed_cdn_purge_plugin_path_edit->text());
}
feed_feed->setCastOrderIsAscending(feed_castorder_box->currentIndex());
if(!feed_feed->postXmlConditional("RDAdmin",this)) {
@ -816,15 +872,20 @@ void EditFeed::resizeEvent(QResizeEvent *e)
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_cdn_purge_plugin_path_label->setGeometry(5,617,145,19);
feed_cdn_purge_plugin_path_edit->setGeometry(155,617,245,19);
feed_cdn_purge_plugin_path_select_button->setGeometry(405,615,40,24);
feed_cdn_purge_plugin_path_clear_button->setGeometry(450,615,40,24);
feed_base_preamble_edit->setGeometry(155,658,335,19);
feed_base_preamble_label->setGeometry(20,658,130,19);
feed_item_image_box->setGeometry(155,24+617,335,38);
feed_item_image_box->setIconSize(QSize(36,24+36));
feed_item_image_label->setGeometry(20,641,130,19);
feed_castorder_box->setGeometry(155,682,100,19);
feed_castorder_label->setGeometry(20,682,130,19);
feed_base_preamble_edit->setGeometry(155,682,335,19);
feed_base_preamble_label->setGeometry(20,682,130,19);
feed_castorder_box->setGeometry(155,706,100,19);
feed_castorder_label->setGeometry(20,706,130,19);
//
// Right-hand Side

View File

@ -61,6 +61,8 @@ class EditFeed : public RDDialog
void copyHeaderXmlData();
void copyChannelXmlData();
void copyItemXmlData();
void selectCdnPurgePlugin();
void clearCdnPurgePlugin();
void okData();
void cancelData();
@ -136,6 +138,10 @@ class EditFeed : public RDDialog
QLabel *feed_channel_description_label;
QLabel *feed_channel_image_label;
RDImagePickerBox *feed_channel_image_box;
QLabel *feed_cdn_purge_plugin_path_label;
QLineEdit *feed_cdn_purge_plugin_path_edit;
QPushButton *feed_cdn_purge_plugin_path_select_button;
QPushButton *feed_cdn_purge_plugin_path_clear_button;
QLabel *feed_base_url_label;
QLabel *feed_base_preamble_label;
QLabel *feed_purge_url_label;

View File

@ -2,7 +2,7 @@
//
// Revert Rivendell DB schema
//
// (C) Copyright 2018-2022 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2018-2023 Fred Gleason <fredg@paravelsystems.com>
//
// 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
@ -41,6 +41,15 @@ bool MainObject::RevertSchema(int cur_schema,int set_schema,QString *err_msg)
// NEW SCHEMA REVERSIONS GO HERE...
//
// Revert 369
//
if((cur_schema==369)&&(set_schema<cur_schema)) {
DropColumn("FEEDS","CDN_PURGE_PLUGIN_PATH");
WriteSchemaVersion(--cur_schema);
}
//
// Revert 368
//

View File

@ -2,7 +2,7 @@
//
// DB schema version <==> Rivendell version map
//
// (C) Copyright 2018-2022 Fred Gleason <fredg@paravelsystems.com>
// (C) Copyright 2018-2023 Fred Gleason <fredg@paravelsystems.com>
//
// 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
@ -160,7 +160,7 @@ void MainObject::InitializeSchemaMap() {
global_version_map["3.4"]=317;
global_version_map["3.5"]=346;
global_version_map["3.6"]=347;
global_version_map["4.0"]=368;
global_version_map["4.0"]=369;
}

View File

@ -11392,6 +11392,17 @@ bool MainObject::UpdateSchema(int cur_schema,int set_schema,QString *err_msg)
WriteSchemaVersion(++cur_schema);
}
if((cur_schema<369)&&(set_schema>cur_schema)) {
sql=QString("alter table `FEEDS` ")+
"add column `CDN_PURGE_PLUGIN_PATH` text "+
"after `SHA1_HASH`";
if(!RDSqlQuery::apply(sql,err_msg)) {
return false;
}
WriteSchemaVersion(++cur_schema);
}
// NEW SCHEMA UPDATES GO HERE...

View File

@ -26,6 +26,9 @@
#include <curl/curl.h>
#include <QProcess>
#include <QProcessEnvironment>
#include <rdapplication.h>
#include <rdconf.h>
#include <rddelete.h>
@ -575,6 +578,22 @@ bool Xport::PostRssElemental(RDFeed *feed,const QDateTime &now,QString *err_msg)
}
curl_easy_cleanup(curl);
//
// Cache Management
//
rda->syslog(LOG_NOTICE,"CDN1");
if(ret) {
QString cdn_script=feed->cdnPurgePluginPath();
rda->syslog(LOG_NOTICE,"CDN2 script: %s",
cdn_script.toUtf8().constData());
if(!cdn_script.isEmpty()) {
rda->syslog(LOG_NOTICE,"CDN3");
QStringList args;
args.push_back(RDFeed::publicUrl(feed->baseUrl(""),feed->keyName()));
RunCdnScript(cdn_script,args);
}
}
return ret;
}
@ -871,3 +890,35 @@ void Xport::RemoveImage() // Remove podcast image from the remote archive
Exit(0);
}
void Xport::RunCdnScript(const QString &cmd,const QStringList &args)
{
QStringList f0=cmd.split("/",QString::KeepEmptyParts);
f0.removeLast();
QProcess *proc=new QProcess(this);
QProcessEnvironment env=QProcessEnvironment::systemEnvironment();
QString path=env.value("PATH");
env.remove("PATH");
env.insert("PATH",f0.join("/")+":"+path);
proc->setEnvironment(env.toStringList());
proc->start(cmd,args);
proc->waitForFinished();
if(proc->exitStatus()!=QProcess::NormalExit) {
rda->syslog(LOG_WARNING,"cdn script \"%s\" crashed",
(cmd+" "+args.join(" ")).toUtf8().constData());
}
else {
if(proc->exitCode()!=0) {
rda->syslog(LOG_WARNING,"cdn script \"%s\" returned exit code %d [%s]",
(cmd+" "+args.join(" ")).toUtf8().constData(),
proc->exitCode(),
proc->readAllStandardError().constData());
}
else {
rda->syslog(LOG_DEBUG,"ran cdn script \"%s\"",
(cmd+" "+args.join(" ")).toUtf8().constData());
}
}
delete proc;
}

View File

@ -94,6 +94,7 @@ class Xport : public QObject
void RemoveRss();
void PostImage();
void RemoveImage();
void RunCdnScript(const QString &cmd,const QStringList &args);
void LockLog();
QString LogLockXml(bool result,const QString &log_name,const QString &guid,
const QString &username,const QString &stationname,