Merge branch 'main' of repo.anhonesthost.net:HPR/hpr-tools
This commit is contained in:
commit
7e925621f4
14
feed_watcher/db_regen
Executable file
14
feed_watcher/db_regen
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Make a new empty database using the schema file
|
||||
#
|
||||
BASEDIR="$HOME/HPR/feed_watcher"
|
||||
DB="$BASEDIR/feedWatcher.db"
|
||||
SCHEMA="$BASEDIR/feedWatcher_schema.sql"
|
||||
|
||||
if [[ -e $DB ]]; then
|
||||
rm -f "$DB"
|
||||
fi
|
||||
|
||||
sqlite3 "$DB" < "$SCHEMA"
|
2195
feed_watcher/feedWatcher
Executable file
2195
feed_watcher/feedWatcher
Executable file
File diff suppressed because it is too large
Load Diff
6
feed_watcher/feedWatcher.cfg
Normal file
6
feed_watcher/feedWatcher.cfg
Normal file
@ -0,0 +1,6 @@
|
||||
<database>
|
||||
type = SQLite
|
||||
file = feedWatcher.db
|
||||
user =
|
||||
password =
|
||||
</database>
|
BIN
feed_watcher/feedWatcher.db
Normal file
BIN
feed_watcher/feedWatcher.db
Normal file
Binary file not shown.
708
feed_watcher/feedWatcher.html
Normal file
708
feed_watcher/feedWatcher.html
Normal file
@ -0,0 +1,708 @@
|
||||
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
/* prefixed by https://autoprefixer.github.io (PostCSS: v7.0.26, autoprefixer: v9.7.3) */
|
||||
html {
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
font-family: "FreeSans", sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: white;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: stretch;
|
||||
-ms-flex-align: stretch;
|
||||
align-items: stretch;
|
||||
-ms-flex-line-pack: center;
|
||||
align-content: center;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
max-width: 50%;
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
body > * {
|
||||
padding: 1em 1em;
|
||||
}
|
||||
main {
|
||||
background-color: #efbe0032;
|
||||
background-color: hsla(47.7,100%,46.9%, 0.2);
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
main * {
|
||||
opacity: 100%;
|
||||
}
|
||||
header, footer {
|
||||
-webkit-box-flex: 0;
|
||||
-ms-flex: 0 1 auto;
|
||||
flex: 0 1 auto;
|
||||
background-color: #efbe00;
|
||||
background-color: hsla(47.7,100%,46.9%, 1);
|
||||
}
|
||||
footer {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
header {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
flex-basis: 50%;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: start;
|
||||
-ms-flex-pack: start;
|
||||
justify-content: start;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
header > * {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
header img {
|
||||
max-height: 128px;
|
||||
min-height: 128px;
|
||||
}
|
||||
header h1 {
|
||||
padding: 0 0.5em 0 0.15em;
|
||||
-webkit-box-flex: 2;
|
||||
-ms-flex-positive: 2;
|
||||
flex-grow: 2;
|
||||
color: white;
|
||||
font-size: 3rem;
|
||||
text-shadow: -1px 1px 0 #000,
|
||||
1px 1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px -1px 0 #000;
|
||||
|
||||
}
|
||||
header h1 span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
header aside {
|
||||
border: solid 0.5em black;
|
||||
padding: 1em;
|
||||
background-color: #FCF2CD;
|
||||
background-color: hsla(47.2, 88.7%, 89.6%, 1);
|
||||
-ms-flex-negative: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
dt {
|
||||
font-weight: 600;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
dd {
|
||||
margin-top: 0.15em;
|
||||
font-weight: 400;
|
||||
}
|
||||
@media only screen and (max-width: 1270px){
|
||||
html {
|
||||
border-left: solid 0.5em black;
|
||||
border-right: solid 0.5em black;
|
||||
}
|
||||
body {
|
||||
max-width:none;
|
||||
}
|
||||
header {
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
header h1 {
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex-positive: 1;
|
||||
flex-grow: 1;
|
||||
max-width: 67%;
|
||||
}
|
||||
header aside {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 600px){
|
||||
body > * {
|
||||
padding: 0.5em;
|
||||
}
|
||||
header > * {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
padding-right: 0;
|
||||
font-size: 2rem;
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex-positive: 1;
|
||||
flex-grow: 1;
|
||||
max-width: 70%;
|
||||
}
|
||||
header img {
|
||||
max-height: 96px;
|
||||
min-height: 96px;
|
||||
}
|
||||
header aside {
|
||||
font-size: 0.95rem;
|
||||
padding: 0.5em;
|
||||
}
|
||||
footer img {
|
||||
max-height: 32px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-height: 780px) and (orientation: landscape) {
|
||||
html {
|
||||
height:auto;
|
||||
display:initial;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="./logo.svg" alt="[logo]">
|
||||
<h1><span>Free Culture</span> Podcasts</h1>
|
||||
<aside>
|
||||
The finest selection of Free Culture Podcasts spanning
|
||||
the genres of Discussion, Drama, Education, Music, and beyond.
|
||||
</aside>
|
||||
</header>
|
||||
<main>
|
||||
|
||||
|
||||
<dl>
|
||||
|
||||
|
||||
<dt><a href="https://www.adminadminpodcast.co.uk">Admin Admin Podcast</a> (<a href="http://feeds.feedburner.com/TheAdminAdminPodcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A Podcast about servers and Networking</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://twit.tv/shows/all-about-android">All About Android (MP3)</a> (<a href="http://feeds.twit.tv/aaa.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>All About Android delivers everything you want to know about Android each week--the biggest news, freshest hardware, best apps and geekiest how-tos--with Android enthusiasts Jason Howell, Florence Ion, Ron Richards, and a variety of special guests along the way. Records live every Tuesday at 8:00pm Eastern / 5:00pm Pacific / 01:00 (Wed) UTC.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://aiit.se/radio/">All In IT Radio (ogg)</a> (<a href="http://feeds.aiit.se/allinit-radio-ogg">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Join us as we talk about everything related to Information Technology, and some other random stuff as well.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://www.amateurlogic.tv">AmateurLogic.TV</a> (<a href="http://amateurlogic.tv/transmitter/feeds/ipod.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A monthly podcast covering Ham Radio equipment, events and personalities.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://leukemensen.nl/angrynerds/">Angry Nerds</a> (<a href="https://leukemensen.nl/angrynerds/feed.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Heb je al gehoord over die laatste grote hack? Een panel van kritische deskundigen bespreken privacy- en security gerelateerde onderwerpen uit het nieuws in gewone mensentaal.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://cchits.net/daily">CCHits.net</a> (<a href="https://cchits.net/daily/rss">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>CCHits.net is designed to provide a Chart for Creative Commons Music, in a way that is easily able to be integrated into other music shows that play Creative Commons Music. CCHits.net has a daily exposure podcast, playing one new track every day, a weekly podcast, playing the last week of tracks played on the podcast, plus the top rated three tracks from the previous week. There is also a monthly podcast which features the top rated tracks over the whole system.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://ccjam.otherside.network">CCJam</a> (<a href="https://ccjam.otherside.network/feed/podcast/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Community music podcast – turn up the volume!!!</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://www.castofwonders.org">Cast of Wonders</a> (<a href="http://www.castofwonders.org/feed/podcast/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>The Young Adult Speculative Fiction Podcast</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://category5.tv/">Category5 Technology TV (MP3 Audio)</a> (<a href="https://rss.cat5.tv/audio.rss">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Weekly live digital TV show with Robbie Ferguson focused on topics of interest to tech minds. Ask your questions and get live answers. Recipient of 2014 and 2017 Top 100 Tech Podcasters award.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://destinationlinux.org/blog/">Destination Linux</a> (<a href="http://destinationlinux.org/feed/mp3/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A podcast made by people who love running Linux.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://distrohoppersdigest.blogspot.com/">Distrohoppers' Digest</a> (<a href="http://feeds.feedburner.com/blogspot/jejQf">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>We are two Blokes who love Linux and trying out new stuff, we thought it would be interesting to share our experience of trying new Linux and BSD distributions and how we found it trying to live with them as our daily driver for up to a Month at a time, by recording a podcast about how we got on.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://edictzero.wordpress.com">Edict Zero – FIS</a> (<a href="https://edictzero.wordpress.com/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Home of the Science Fiction Audio Drama series</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://wikipediapodden.se">English – Wikipediapodden</a> (<a href="http://wikipediapodden.se/tag/english/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>En podcast om Wikipedia på svenska</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://escapepod.org">Escape Pod</a> (<a href="http://escapepod.org/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>The Original Science Fiction Podcast</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://twit.tv/shows/floss-weekly">FLOSS Weekly (MP3)</a> (<a href="http://feeds.twit.tv/floss.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>We're not talking dentistry here; FLOSS all about Free Libre Open Source Software. Join host Randal Schwartz and his rotating panel of co-hosts every Wednesday as they talk with the most interesting and important people in the Open Source and Free Software community. Records live every Wednesday at 12:30pm Eastern / 9:30am Pacific / 17:30 UTC.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://faif.us/cast/">Free as in Freedom</a> (<a href="http://faif.us/feeds/cast-ogg/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A bi-weekly discussion of legal, policy, and other issues in the open source and software freedom community (including occasional interviews) from Brooklyn, New York, USA. Presented by Karen Sandler and Bradley M. Kuhn.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://www.gnuworldorder.info">GNU World Order Linux Cast</a> (<a href="https://gnuworldorder.info/ogg.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>GNU, Linux, coffee, and subversion.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://geekspeak.org/">Geek Speak with Lyle Troxell</a> (<a href="https://geekspeak.org/episodes/rss.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A weekly talk show about technology, science, and human creativity that excites, educates, and fosters curiosity. Discussions touch upon how technology affects society and how we react to that change. Hosts are passionate about explaining complex concepts in simple, easy to digest, chunks. We bridge the gaps between Geeks and the rest of humanity.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://frontrowcrew.com/geeknights/monday/">GeekNights Mondays: Science Technology Computing</a> (<a href="http://feeds.feedburner.com/GNSciTech">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>GeekNights Mondays is the weekly sci/tech segment of GeekNights, featuring science, technology, computing, and more. We talk Linux, Windows, gadgets, you name it.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://goinglinux.com">Going Linux</a> (<a href="http://goinglinux.com/oggpodcast.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Once you become aware that there is a dependable, secure, capable, and modern computer system that rivals all others in popularity and actual use, you will want to try the Linux operating system on your computer. Perhaps you've been using a member of the Unix/Linux family - Linux, Android, ChromeOS, BSD or even OSX - for quite a while. If so, you are likely looking for new ways to optimize your technology for the way you work. Going Linux is for computer users who just want to use Linux to get things done. Are you new to Linux, upgrading from Windows to Linux, or just thinking about moving to Linux? This audio podcast provides you with practical, day-to-day advice on how to use Linux and its applications. Our goal is to help make the Linux experience easy for you.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://hackerpublicradio.org/about.php">Hacker Public Radio</a> (<a href="http://hackerpublicradio.org/hpr_ogg_rss.php">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Hacker Public Radio is an podcast that releases shows every weekday Monday through Friday. Our shows are produced by the community (you) and can be on any topic that are of interest to hackers and hobbyists.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://knightwise.com">Knightwise.com Audio Feed.</a> (<a href="http://feeds.feedburner.com/knightcastpodcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>The cross platform podcast that makes technology work for you and not the other way around. The place to go for all geeks who slide between Mac, iOS, Android, Linux and Windows offering an essential mix of hacks, tips, howto's and tweaks spiced up with a dash of geek culture. Also check out our Mediafeed that has both our audio and video episodes.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://latenightlinux.com">Late Night Linux (Ogg)</a> (<a href="http://latenightlinux.com/feed/ogg">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd><em>No description</em></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://librelounge.org">Libre Lounge</a> (<a href="https://librelounge.org/rss-feed.rss">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A casual podcast about user freedom</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://podnutz.com">Linux For The Rest Of Us - Podnutz</a> (<a href="http://feeds.feedburner.com/linuxfortherestofus">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Linux For The Rest Of Us - Podnutz</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://linuxlads.com/feed_ogg.rss">Linux Lads</a> (<a href="https://linuxlads.com/feed_ogg.rss">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Chat about Linux and all things connected</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://setbit.org/lt.html">Linux Trivia Podcast</a> (<a href="http://setbit.org/lt-ogg.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Verbal's Linux Trivia Podcast</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://lhspodcast.info/category/podcast-ogg/">Linux in the Ham Shack (OGG Feed)</a> (<a href="https://lhspodcast.info/category/podcast-ogg/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Linux in the Ham Shack Podcast in OGG Format</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://linuxgamecast.com">LinuxGameCast</a> (<a href="https://linuxgamecast.com/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Linux fueled mayhem & madness with a side of news, reviews, and whatever the Hell-Elks™ we come up with</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://linuxlugcast.com">Linuxlugcast</a> (<a href="http://feeds.feedburner.com/linuxlugcast-ogg">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Linuxlugcast</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://makerscorner.tech">Makers Corner, with Nate and Yannick</a> (<a href="https://makerscorner.tech/feed/podcast/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A tech oriented DIY podcast, from the Other Side Podcast Network</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://downloads.cavalcadeaudio.com/stardrifter-novels/01-motherload/">Motherload</a> (<a href="http://downloads.cavalcadeaudio.com/stardrifter-novels/01-motherload/feed.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A remote corner of a bleak system. A broken-down gunboat, stuck in space. An incompetent captain and a misfit crew. A pirate ship, a silent target, and a whole bunch of secrets. So how's YOUR day going?</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://mintcast.org">OGG – mintCast</a> (<a href="https://mintcast.org/category/ogg/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Just another WordPress site</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://openmetalcast.com">Open Metalcast</a> (<a href="http://feeds.feedburner.com/OpenMetalcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Music that will rip your face off and give a copy to your friends</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://opensourcesecuritypodcast.com">Open Source Security Podcast</a> (<a href="https://opensourcesecuritypodcast.libsyn.com/rss">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A security podcast geared towards those looking to better understand security topics of the day. Hosted by Kurt Seifried and Josh Bressers covering a wide range of topics including IoT, application security, operational security, cloud, devops, and security news of the day. There is a special open source twist to the discussion often giving a unique perspective on any given topic.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://petecogle.co.uk/blog/category/pc-podcast/">PC Podcast. A Music Podcast with Pete Cogle, since January 2006</a> (<a href="http://feeds.feedburner.com/pcpodcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Guaranteed to widen your musical perspectives, Pete Cogle plays tracks from all over the world in all possible genres. Like it, or hate it, it's a change from the same old shit on clearchannel radio.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://podcastle.org">PodCastle</a> (<a href="http://podcastle.org/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>The Fantasy Fiction Podcast</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://craphound.com">Podcast – Cory Doctorow's craphound.com</a> (<a href="http://feeds.feedburner.com/doctorow_podcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Cory Doctorow's Literary Works</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://pseudopod.org">PseudoPod</a> (<a href="http://pseudopod.org/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>The Sound of Horror</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://www.radiotux.de/">RadioTux</a> (<a href="http://radiotux.de/podcast/rss/radiotux-all.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Linux, Open Source und Netzkultur</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://ratholeradio.org">RatholeRadio.org (Ogg Version)</a> (<a href="http://feeds.feedburner.com/RatholeRadio-ogg">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Music, Waffling, Live Performances & Laughs</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://twit.tv/shows/security-now">Security Now (MP3)</a> (<a href="http://feeds.twit.tv/sn.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Steve Gibson, the man who coined the term spyware and created the first anti-spyware program, creator of Spinrite and ShieldsUP, discusses the hot topics in security today with Leo Laporte. Records live every Tuesday at 4:30pm Eastern / 1:30pm Pacific / 21:30 UTC.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://www.skepticule.co.uk/">Skepticule</a> (<a href="http://www.skepticule.co.uk/feeds/posts/default?alt=rss">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Spanking the bottom of ignorance since 2009</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://smlr.us">Sunday Morning Linux Review - OGG Feed</a> (<a href="http://feeds.feedburner.com/SundayMorningLinuxReview-OggFeed">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Sunday Morning Linux Review – OGG Feed for Freedom Lovers!</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://teaearlgreyhot.org">Tea, Earl Grey, Hot !</a> (<a href="https://teaearlgreyhot.org/feed/podcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>An unofficial Star Trek fan podcast from the Other Side Podcast Network</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://www.thebinarytimes.net">The Binary Times Audiocast - ogg</a> (<a href="https://www.thebinarytimes.net/rss-ogg.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Linux and open source tips, tricks and discussion. Free software, hardware and modern culture. The Binary Times Audiocast is created by Mark and Wayne, two chaps who just like using linux and open source software and want to spread the word. Linux is free and open source and it is an excellent choice of operating system for our ever changing times. This audiocast is released fortnightly.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://thebugcast.org">The Bugcast - Ogg Feed</a> (<a href="http://thebugcast.org/feed/ogg/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Award-winning music and chat from South Yorkshire in the UK</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://thecommandline.net/">The Command Line Podcast</a> (<a href="https://thecommandline.net/files/cmdln_mp3.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>A podcast by a self-described hacker, curmudgeon and hacktivist about the practice and profession of programming drawing on over a decade of professional experience and a lifetime spent hacking, the intersection of politics and society with technology and anything else clever, elegant or funny that catches my mind as a die hard technology geek.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://petecogle.co.uk/blog">The Dub Zone</a> (<a href="http://feeds.feedburner.com/TheDubZone">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>An 8-track mix of the best dub reggae, every month. Curated by Pete Cogle, since May 2007.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://duffercast.org">The Duffercast in Ogg Vorbis</a> (<a href="http://duffercast.org/feed/podcast/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Some auld duffers providing world wide wisdom</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://fullcirclemagazine.org/">The Full Circle Weekly News</a> (<a href="https://fullcirclemagazine.org/feed/podcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>From the independent magazine for the Ubuntu Linux community. The Full Circle Weekly News is a short podcast with just the news. No chit-chat. No time wasting. Just the latest FOSS/Linux/Ubuntu news.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://thejakattack.com/">The JaK Attack! Podcast</a> (<a href="http://feeds.feedburner.com/TheJakAttack">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Where Chic meets geek! The JaK Attack! is hoted by Jon Watson and Kelly Penguin Girl. We talk about tech, art, and we do it just a little too loud.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://www.jodcast.net/">The Jodcast (high bandwidth)</a> (<a href="http://www.jodcast.net/rss-high.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Monthly astronomy news, interviews and questions. Created by astronomers.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://www.tllts.org">The Linux Link Tech Show Ogg-Vorbis Feed</a> (<a href="http://feeds.feedburner.com/TheLinuxLinkTechShowOgg-vorbisFeed">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>The Linux Link Tech Show</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://twit.tv/shows/this-week-in-computer-hardware">This Week in Computer Hardware (MP3)</a> (<a href="http://feeds.twit.tv/twich.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>If you obsess about the details inside computers then on This Week in Computer Hardware, you'll find out the latest in motherboards, CPUs, GPUs, storage, RAM, power supplies, input devices, and monitors. Hosts Patrick Norton of TekThing and Sebastian Peak of PC Perspective bring you the newest hardware, talk benchmarks, and even dive into the not-yet-released products on the horizon. Records live every Thursday at 3:30pm Eastern / 12:30pm Pacific / 20:30 UTC.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://twit.tv/shows/this-week-in-google">This Week in Google (MP3)</a> (<a href="http://feeds.twit.tv/twig.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Leo Laporte, Jeff Jarvis, Stacey Higginbotham, Ant Pruitt, and their guests talk about the latest Google and cloud computing news. Records live every Wednesday at 4:00pm Eastern / 1:00pm Pacific / 21:00 UTC.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://www.asm.org/twim/">This Week in Microbiology</a> (<a href="http://feeds.feedburner.com/twim">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>This Week in Microbiology is a podcast about unseen life on Earth hosted by Vincent Racaniello and friends. Following in the path of his successful shows 'This Week in Virology' (TWiV) and 'This Week in Parasitism' (TWiP), Racaniello and guests produce an informal yet informative conversation about microbes which is accessible to everyone, no matter what their science background.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://twit.tv/shows/this-week-in-tech">This Week in Tech (MP3)</a> (<a href="http://feeds.twit.tv/twit.xml">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Your first podcast of the week is the last word in tech. Join the top tech pundits in a roundtable discussion of the latest trends in high tech. Records live every Sunday at 5:15pm Eastern / 2:15pm Pacific / 22:15 UTC.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://tuxjam.otherside.network">TuxJam OGG</a> (<a href="https://tuxjam.otherside.network/?feed=podcast">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>TuxJam is a family friendly show that blends Creative Commons music and Open Source goodness. This is an OGG feed.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://ubuntupodcast.org">Ubuntu Podcast</a> (<a href="http://ubuntupodcast.org/feed/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>Upbeat and family-friendly show including news, discussion, interviews and reviews from the Ubuntu, Linux and Open Source world.</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="http://wikipediapodden.se/prenumerera/">Wikipediapodden</a> (<a href="http://wikipediapodden.se/feed/podcast/">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>En podcast om Wikipedia på svenska</dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dt><a href="https://urandom-podcast.info/">urandom podcast</a> (<a href="http://feeds.feedburner.com/urandom-podcast/ogg">feed</a>)</dt>
|
||||
|
||||
|
||||
<dd>urandom: your unlimited source of medium quality randomness</dd>
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
<p>and many <a href="https://gpodder.net/directory/">more</a> ...</p>
|
||||
</main>
|
||||
<footer>
|
||||
<img src="./cc.svg" alt="[Creative Commons]">
|
||||
<img src="./attribution.svg" alt="[Attribution]">
|
||||
<img src="./share_alike.svg" alt="[Share Alike]">
|
||||
<img src="./remix.svg" alt="[Remix]">
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
2127
feed_watcher/feedWatcher.json
Normal file
2127
feed_watcher/feedWatcher.json
Normal file
File diff suppressed because it is too large
Load Diff
290
feed_watcher/feedWatcher.mkd
Normal file
290
feed_watcher/feedWatcher.mkd
Normal file
@ -0,0 +1,290 @@
|
||||
# Free Culture Podcasts
|
||||
|
||||
### The finest selection of Free Culture Podcasts spanning the genres of Discussion, Drama, Education, Music, and beyond.
|
||||
|
||||
- **Admin Admin Podcast**
|
||||
- Website: https://www.adminadminpodcast.co.uk
|
||||
- Feed: http://feeds.feedburner.com/TheAdminAdminPodcast
|
||||
- Licence:
|
||||
|
||||
- **All About Android (MP3)**
|
||||
- Website: https://twit.tv/shows/all-about-android
|
||||
- Feed: http://feeds.twit.tv/aaa.xml
|
||||
- Licence: This work is licensed under a Creative Commons License - Attribution-NonCommercial-NoDerivatives 4.0 International - http://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
|
||||
- **All In IT Radio (ogg)**
|
||||
- Website: http://aiit.se/radio/
|
||||
- Feed: http://feeds.aiit.se/allinit-radio-ogg
|
||||
- Licence: Creative Commons Attribution-NoDerivs 3.0 Unported License
|
||||
|
||||
- **AmateurLogic.TV**
|
||||
- Website: http://www.amateurlogic.tv
|
||||
- Feed: http://amateurlogic.tv/transmitter/feeds/ipod.xml
|
||||
- Licence: Creative Commons
|
||||
|
||||
- **Angry Nerds**
|
||||
- Website: https://leukemensen.nl/angrynerds/
|
||||
- Feed: https://leukemensen.nl/angrynerds/feed.xml
|
||||
- Licence: leukemensen.nl
|
||||
|
||||
- **CCHits.net**
|
||||
- Website: https://cchits.net/daily
|
||||
- Feed: https://cchits.net/daily/rss
|
||||
- Licence: The content created by this site is generated by a script which is licensed under the Affero General Public License version 3 (AGPL3). The generated content is released under a Creative Commons By-Attribution License.
|
||||
|
||||
- **CCJam**
|
||||
- Website: https://ccjam.otherside.network
|
||||
- Feed: https://ccjam.otherside.network/feed/podcast/
|
||||
- Licence:
|
||||
|
||||
- **Cast of Wonders**
|
||||
- Website: http://www.castofwonders.org
|
||||
- Feed: http://www.castofwonders.org/feed/podcast/
|
||||
- Licence: Copyright © 2011-2019
|
||||
|
||||
- **Category5 Technology TV (MP3 Audio)**
|
||||
- Website: https://category5.tv/
|
||||
- Feed: https://rss.cat5.tv/audio.rss
|
||||
- Licence: This work is licensed under a Creative Commons License - Attribution-NonCommercial-ShareAlike - https://creativecommons.org/licenses/by-nc-sa/3.0/
|
||||
|
||||
- **Destination Linux**
|
||||
- Website: https://destinationlinux.org/blog/
|
||||
- Feed: http://destinationlinux.org/feed/mp3/
|
||||
- Licence:
|
||||
|
||||
- **Distrohoppers' Digest**
|
||||
- Website: https://distrohoppersdigest.blogspot.com/
|
||||
- Feed: http://feeds.feedburner.com/blogspot/jejQf
|
||||
- Licence:
|
||||
|
||||
- **Edict Zero – FIS**
|
||||
- Website: https://edictzero.wordpress.com
|
||||
- Feed: https://edictzero.wordpress.com/feed/
|
||||
- Licence:
|
||||
|
||||
- **Escape Pod**
|
||||
- Website: http://escapepod.org
|
||||
- Feed: http://escapepod.org/feed/
|
||||
- Licence:
|
||||
|
||||
- **FLOSS Weekly (MP3)**
|
||||
- Website: https://twit.tv/shows/floss-weekly
|
||||
- Feed: http://feeds.twit.tv/floss.xml
|
||||
- Licence: This work is licensed under a Creative Commons License - Attribution-NonCommercial-NoDerivatives 4.0 International - http://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
|
||||
- **Free as in Freedom**
|
||||
- Website: http://faif.us/cast/
|
||||
- Feed: http://faif.us/feeds/cast-ogg/
|
||||
- Licence: 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2019, Free as in Freedom. Licensed under a Creative Commons Attribution-Share Alike 3.0 USA License.
|
||||
|
||||
- **GNU World Order Linux Cast**
|
||||
- Website: http://www.gnuworldorder.info
|
||||
- Feed: https://gnuworldorder.info/ogg.xml
|
||||
- Licence:
|
||||
|
||||
- **Geek Speak with Lyle Troxell**
|
||||
- Website: https://geekspeak.org/
|
||||
- Feed: https://geekspeak.org/episodes/rss.xml
|
||||
- Licence: Creative Commons Attribution 3.0 United States License
|
||||
|
||||
- **GeekNights Mondays: Science Technology Computing**
|
||||
- Website: http://frontrowcrew.com/geeknights/monday/
|
||||
- Feed: http://feeds.feedburner.com/GNSciTech
|
||||
- Licence: Creative Commons
|
||||
|
||||
- **Going Linux**
|
||||
- Website: https://goinglinux.com
|
||||
- Feed: http://goinglinux.com/oggpodcast.xml
|
||||
- Licence: Creative Commons Attribution 4.0 International License.
|
||||
|
||||
- **Hacker Public Radio**
|
||||
- Website: http://hackerpublicradio.org/about.php
|
||||
- Feed: http://hackerpublicradio.org/hpr_ogg_rss.php
|
||||
- Licence: Creative Commons Attribution-ShareAlike 3.0 License
|
||||
|
||||
- **Knightwise.com Audio Feed.**
|
||||
- Website: https://knightwise.com
|
||||
- Feed: http://feeds.feedburner.com/knightcastpodcast
|
||||
- Licence: Creative commons apply ! Non commercial re-use is allowed.
|
||||
|
||||
- **Late Night Linux (Ogg)**
|
||||
- Website: https://latenightlinux.com
|
||||
- Feed: http://latenightlinux.com/feed/ogg
|
||||
- Licence:
|
||||
|
||||
- **Libre Lounge**
|
||||
- Website: https://librelounge.org
|
||||
- Feed: https://librelounge.org/rss-feed.rss
|
||||
- Licence: Licensed under CC BY-SA 4.0 International
|
||||
|
||||
- **Linux For The Rest Of Us - Podnutz**
|
||||
- Website: http://podnutz.com
|
||||
- Feed: http://feeds.feedburner.com/linuxfortherestofus
|
||||
- Licence: Copyright 2017 Podnutz.com
|
||||
|
||||
- **Linux Lads**
|
||||
- Website: https://linuxlads.com/feed_ogg.rss
|
||||
- Feed: https://linuxlads.com/feed_ogg.rss
|
||||
- Licence: Released under the Creative Commons Attribution-Share Alike 3.0 Unported Licence
|
||||
|
||||
- **Linux Trivia Podcast**
|
||||
- Website: http://setbit.org/lt.html
|
||||
- Feed: http://setbit.org/lt-ogg.xml
|
||||
- Licence: Creative Commons License Some Rights Reserved
|
||||
|
||||
- **Linux in the Ham Shack (OGG Feed)**
|
||||
- Website: https://lhspodcast.info/category/podcast-ogg/
|
||||
- Feed: https://lhspodcast.info/category/podcast-ogg/feed/
|
||||
- Licence: Attribution-NonCommercial-NoDerivatives 4.0 International
|
||||
|
||||
- **LinuxGameCast**
|
||||
- Website: https://linuxgamecast.com
|
||||
- Feed: https://linuxgamecast.com/feed/
|
||||
- Licence:
|
||||
|
||||
- **Makers Corner, with Nate and Yannick**
|
||||
- Website: https://makerscorner.tech
|
||||
- Feed: https://makerscorner.tech/feed/podcast/
|
||||
- Licence: Unless otherwise stated, this podcast is released under a Creative Commons, By Attribution, Share Alike license.
|
||||
|
||||
- **Motherload**
|
||||
- Website: http://downloads.cavalcadeaudio.com/stardrifter-novels/01-motherload/
|
||||
- Feed: http://downloads.cavalcadeaudio.com/stardrifter-novels/01-motherload/feed.xml
|
||||
- Licence: Motherload (Stardrifter Book 01) written and read by David Collins-Rivera © 2012 David Collins-Rivera Creative Commons Attribution Share-Alike Unported 3.0 https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
- **OGG – mintCast**
|
||||
- Website: https://mintcast.org
|
||||
- Feed: https://mintcast.org/category/ogg/feed/
|
||||
- Licence:
|
||||
|
||||
- **Open Metalcast**
|
||||
- Website: http://openmetalcast.com
|
||||
- Feed: http://feeds.feedburner.com/OpenMetalcast
|
||||
- Licence: (c) 2010-2018 Craig Maloney. Some Rights Reserved.
|
||||
|
||||
- **PC Podcast. A Music Podcast with Pete Cogle, since January 2006**
|
||||
- Website: http://petecogle.co.uk/blog/category/pc-podcast/
|
||||
- Feed: http://feeds.feedburner.com/pcpodcast
|
||||
- Licence: PCP
|
||||
|
||||
- **PodCastle**
|
||||
- Website: http://podcastle.org
|
||||
- Feed: http://podcastle.org/feed/
|
||||
- Licence:
|
||||
|
||||
- **Podcast – Cory Doctorow's craphound.com**
|
||||
- Website: https://craphound.com
|
||||
- Feed: http://feeds.feedburner.com/doctorow_podcast
|
||||
- Licence: Creative Commons by-nc-sa http://creativecommons.org/licenses/by-nc-sa/2.5/
|
||||
|
||||
- **PseudoPod**
|
||||
- Website: http://pseudopod.org
|
||||
- Feed: http://pseudopod.org/feed/
|
||||
- Licence:
|
||||
|
||||
- **RadioTux**
|
||||
- Website: https://www.radiotux.de/
|
||||
- Feed: http://radiotux.de/podcast/rss/radiotux-all.xml
|
||||
- Licence: © Creative Commons - BY-SA 3.0 Unported
|
||||
|
||||
- **RatholeRadio.org (Ogg Version)**
|
||||
- Website: http://ratholeradio.org
|
||||
- Feed: http://feeds.feedburner.com/RatholeRadio-ogg
|
||||
- Licence: Creative Commons BY-SA Licensed
|
||||
|
||||
- **Security Now (MP3)**
|
||||
- Website: https://twit.tv/shows/security-now
|
||||
- Feed: http://feeds.twit.tv/sn.xml
|
||||
- Licence: This work is licensed under a Creative Commons License - Attribution-NonCommercial-NoDerivatives 4.0 International - http://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
|
||||
- **Skepticule**
|
||||
- Website: http://www.skepticule.co.uk/
|
||||
- Feed: http://www.skepticule.co.uk/feeds/posts/default?alt=rss
|
||||
- Licence: Creative Commons Attribution-NonCommercial-No Derivative Works 2.0 UK: England & Wales
|
||||
|
||||
- **Sunday Morning Linux Review - OGG Feed**
|
||||
- Website: https://smlr.us
|
||||
- Feed: http://feeds.feedburner.com/SundayMorningLinuxReview-OggFeed
|
||||
- Licence: This content is published under the Attribution-Noncommercial-Share Alike 3.0 Unported license.
|
||||
|
||||
- **Tea, Earl Grey, Hot !**
|
||||
- Website: https://teaearlgreyhot.org
|
||||
- Feed: https://teaearlgreyhot.org/feed/podcast
|
||||
- Licence: Unless otherwise stated, this podcast is released under a Creative Commons, By Attribution, Share Alike license.
|
||||
|
||||
- **The Binary Times Audiocast - ogg**
|
||||
- Website: https://www.thebinarytimes.net
|
||||
- Feed: https://www.thebinarytimes.net/rss-ogg.xml
|
||||
- Licence: Unless otherwise stated, this podcast is released under a Creative Commons, By Attribution, Share Alike license.
|
||||
|
||||
- **The Bugcast - Ogg Feed**
|
||||
- Website: https://thebugcast.org
|
||||
- Feed: http://thebugcast.org/feed/ogg/
|
||||
- Licence:
|
||||
|
||||
- **The Command Line Podcast**
|
||||
- Website: http://thecommandline.net/
|
||||
- Feed: https://thecommandline.net/files/cmdln_mp3.xml
|
||||
- Licence: 2005-2014, http://creativecommons.org/licenses/by-sa/3.0/us
|
||||
|
||||
- **The Dub Zone**
|
||||
- Website: http://petecogle.co.uk/blog
|
||||
- Feed: http://feeds.feedburner.com/TheDubZone
|
||||
- Licence: Creative Commons
|
||||
|
||||
- **The Duffercast in Ogg Vorbis**
|
||||
- Website: https://duffercast.org
|
||||
- Feed: http://duffercast.org/feed/podcast/
|
||||
- Licence:
|
||||
|
||||
- **The Full Circle Weekly News**
|
||||
- Website: https://fullcirclemagazine.org/
|
||||
- Feed: https://fullcirclemagazine.org/feed/podcast
|
||||
- Licence: © 2016 Full Circle Magazine
|
||||
|
||||
- **The JaK Attack! Podcast**
|
||||
- Website: http://thejakattack.com/
|
||||
- Feed: http://feeds.feedburner.com/TheJakAttack
|
||||
- Licence: © 2018 The JaK Attack! Podcast
|
||||
|
||||
- **The Linux Link Tech Show Ogg-Vorbis Feed**
|
||||
- Website: http://www.tllts.org
|
||||
- Feed: http://feeds.feedburner.com/TheLinuxLinkTechShowOgg-vorbisFeed
|
||||
- Licence: TLLTS is licensed under Creative Commons License for Non-Commercial Use
|
||||
|
||||
- **This Week in Computer Hardware (MP3)**
|
||||
- Website: https://twit.tv/shows/this-week-in-computer-hardware
|
||||
- Feed: http://feeds.twit.tv/twich.xml
|
||||
- Licence: This work is licensed under a Creative Commons License - Attribution-NonCommercial-NoDerivatives 4.0 International - http://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
|
||||
- **This Week in Google (MP3)**
|
||||
- Website: https://twit.tv/shows/this-week-in-google
|
||||
- Feed: http://feeds.twit.tv/twig.xml
|
||||
- Licence: This work is licensed under a Creative Commons License - Attribution-NonCommercial-NoDerivatives 4.0 International - http://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
|
||||
- **This Week in Microbiology**
|
||||
- Website: http://www.asm.org/twim/
|
||||
- Feed: http://feeds.feedburner.com/twim
|
||||
- Licence: Creative Commons Attribution - Noncommercial
|
||||
|
||||
- **This Week in Tech (MP3)**
|
||||
- Website: https://twit.tv/shows/this-week-in-tech
|
||||
- Feed: http://feeds.twit.tv/twit.xml
|
||||
- Licence: This work is licensed under a Creative Commons License - Attribution-NonCommercial-NoDerivatives 4.0 International - http://creativecommons.org/licenses/by-nc-nd/4.0/
|
||||
|
||||
- **TuxJam OGG**
|
||||
- Website: https://tuxjam.otherside.network
|
||||
- Feed: https://tuxjam.otherside.network/?feed=podcast
|
||||
- Licence: Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||
|
||||
- **Ubuntu Podcast**
|
||||
- Website: http://ubuntupodcast.org
|
||||
- Feed: http://ubuntupodcast.org/feed/
|
||||
- Licence:
|
||||
|
||||
- **urandom podcast**
|
||||
- Website: https://urandom-podcast.info/
|
||||
- Feed: http://feeds.feedburner.com/urandom-podcast/ogg
|
||||
- Licence:
|
||||
|
||||
|
80
feed_watcher/feedWatcher.opml
Normal file
80
feed_watcher/feedWatcher.opml
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<opml version="1.1">
|
||||
<head>
|
||||
<title>Free Culture Podcasts</title>
|
||||
<dateCreated>2020-02-14 22:34:08</dateCreated>
|
||||
<dateModified>2020-02-14 22:34:08</dateModified>
|
||||
<ownerName></ownerName>
|
||||
<ownerEmail></ownerEmail>
|
||||
<expansionState></expansionState>
|
||||
<vertScrollState></vertScrollState>
|
||||
<windowTop></windowTop>
|
||||
<windowLeft></windowLeft>
|
||||
<windowBottom></windowBottom>
|
||||
<windowRight></windowRight>
|
||||
</head>
|
||||
<body>
|
||||
<outline description="A Podcast about servers and Networking" htmlUrl="https://www.adminadminpodcast.co.uk" text="Admin Admin Podcast" title="Admin Admin Podcast" xmlUrl="http://feeds.feedburner.com/TheAdminAdminPodcast" />
|
||||
<outline description="All About Android delivers everything you want to know about Android each week--the biggest news, freshest hardware, best apps and geekiest how-tos--with Android enthusiasts Jason Howell, Florence Ion, Ron Richards, and a variety of special guests along the way. Records live every Tuesday at 8:00pm Eastern / 5:00pm Pacific / 01:00 (Wed) UTC." htmlUrl="https://twit.tv/shows/all-about-android" text="All About Android (MP3)" title="All About Android (MP3)" xmlUrl="http://feeds.twit.tv/aaa.xml" />
|
||||
<outline description="Join us as we talk about everything related to Information Technology, and some other random stuff as well." htmlUrl="http://aiit.se/radio/" text="All In IT Radio (ogg)" title="All In IT Radio (ogg)" xmlUrl="http://feeds.aiit.se/allinit-radio-ogg" />
|
||||
<outline description="A monthly podcast covering Ham Radio equipment, events and personalities." htmlUrl="http://www.amateurlogic.tv" text="AmateurLogic.TV" title="AmateurLogic.TV" xmlUrl="http://amateurlogic.tv/transmitter/feeds/ipod.xml" />
|
||||
<outline description="Heb je al gehoord over die laatste grote hack? Een panel van kritische deskundigen bespreken privacy- en security gerelateerde onderwerpen uit het nieuws in gewone mensentaal." htmlUrl="https://leukemensen.nl/angrynerds/" text="Angry Nerds" title="Angry Nerds" xmlUrl="https://leukemensen.nl/angrynerds/feed.xml" />
|
||||
<outline description="CCHits.net is designed to provide a Chart for Creative Commons Music, in a way that is easily able to be integrated into other music shows that play Creative Commons Music. CCHits.net has a daily exposure podcast, playing one new track every day, a weekly podcast, playing the last week of tracks played on the podcast, plus the top rated three tracks from the previous week. There is also a monthly podcast which features the top rated tracks over the whole system." htmlUrl="https://cchits.net/daily" text="CCHits.net" title="CCHits.net" xmlUrl="https://cchits.net/daily/rss" />
|
||||
<outline description="Community music podcast – turn up the volume!!!" htmlUrl="https://ccjam.otherside.network" text="CCJam" title="CCJam" xmlUrl="https://ccjam.otherside.network/feed/podcast/" />
|
||||
<outline description="The Young Adult Speculative Fiction Podcast" htmlUrl="http://www.castofwonders.org" text="Cast of Wonders" title="Cast of Wonders" xmlUrl="http://www.castofwonders.org/feed/podcast/" />
|
||||
<outline description="Weekly live digital TV show with Robbie Ferguson focused on topics of interest to tech minds. Ask your questions and get live answers. Recipient of 2014 and 2017 Top 100 Tech Podcasters award." htmlUrl="https://category5.tv/" text="Category5 Technology TV (MP3 Audio)" title="Category5 Technology TV (MP3 Audio)" xmlUrl="https://rss.cat5.tv/audio.rss" />
|
||||
<outline description="A podcast made by people who love running Linux." htmlUrl="https://destinationlinux.org/blog/" text="Destination Linux" title="Destination Linux" xmlUrl="http://destinationlinux.org/feed/mp3/" />
|
||||
<outline description="We are two Blokes who love Linux and trying out new stuff, we thought it would be interesting to share our experience of trying new Linux and BSD distributions and how we found it trying to live with them as our daily driver for up to a Month at a time, by recording a podcast about how we got on." htmlUrl="https://distrohoppersdigest.blogspot.com/" text="Distrohoppers' Digest" title="Distrohoppers' Digest" xmlUrl="http://feeds.feedburner.com/blogspot/jejQf" />
|
||||
<outline description="Home of the Science Fiction Audio Drama series" htmlUrl="https://edictzero.wordpress.com" text="Edict Zero – FIS" title="Edict Zero – FIS" xmlUrl="https://edictzero.wordpress.com/feed/" />
|
||||
<outline description="En podcast om Wikipedia på svenska" htmlUrl="http://wikipediapodden.se" text="English – Wikipediapodden" title="English – Wikipediapodden" xmlUrl="http://wikipediapodden.se/tag/english/feed/" />
|
||||
<outline description="The Original Science Fiction Podcast" htmlUrl="http://escapepod.org" text="Escape Pod" title="Escape Pod" xmlUrl="http://escapepod.org/feed/" />
|
||||
<outline description="We're not talking dentistry here; FLOSS all about Free Libre Open Source Software. Join host Randal Schwartz and his rotating panel of co-hosts every Wednesday as they talk with the most interesting and important people in the Open Source and Free Software community. Records live every Wednesday at 12:30pm Eastern / 9:30am Pacific / 17:30 UTC." htmlUrl="https://twit.tv/shows/floss-weekly" text="FLOSS Weekly (MP3)" title="FLOSS Weekly (MP3)" xmlUrl="http://feeds.twit.tv/floss.xml" />
|
||||
<outline description="A bi-weekly discussion of legal, policy, and other issues in the open source and software freedom community (including occasional interviews) from Brooklyn, New York, USA. Presented by Karen Sandler and Bradley M. Kuhn." htmlUrl="http://faif.us/cast/" text="Free as in Freedom" title="Free as in Freedom" xmlUrl="http://faif.us/feeds/cast-ogg/" />
|
||||
<outline description="GNU, Linux, coffee, and subversion." htmlUrl="http://www.gnuworldorder.info" text="GNU World Order Linux Cast" title="GNU World Order Linux Cast" xmlUrl="https://gnuworldorder.info/ogg.xml" />
|
||||
<outline description="A weekly talk show about technology, science, and human creativity that excites, educates, and fosters curiosity. Discussions touch upon how technology affects society and how we react to that change. Hosts are passionate about explaining complex concepts in simple, easy to digest, chunks. We bridge the gaps between Geeks and the rest of humanity." htmlUrl="https://geekspeak.org/" text="Geek Speak with Lyle Troxell" title="Geek Speak with Lyle Troxell" xmlUrl="https://geekspeak.org/episodes/rss.xml" />
|
||||
<outline description="GeekNights Mondays is the weekly sci/tech segment of GeekNights, featuring science, technology, computing, and more. We talk Linux, Windows, gadgets, you name it." htmlUrl="http://frontrowcrew.com/geeknights/monday/" text="GeekNights Mondays: Science Technology Computing" title="GeekNights Mondays: Science Technology Computing" xmlUrl="http://feeds.feedburner.com/GNSciTech" />
|
||||
<outline description="Once you become aware that there is a dependable, secure, capable, and modern computer system that rivals all others in popularity and actual use, you will want to try the Linux operating system on your computer. Perhaps you've been using a member of the Unix/Linux family - Linux, Android, ChromeOS, BSD or even OSX - for quite a while. If so, you are likely looking for new ways to optimize your technology for the way you work. Going Linux is for computer users who just want to use Linux to get things done. Are you new to Linux, upgrading from Windows to Linux, or just thinking about moving to Linux? This audio podcast provides you with practical, day-to-day advice on how to use Linux and its applications. Our goal is to help make the Linux experience easy for you." htmlUrl="https://goinglinux.com" text="Going Linux" title="Going Linux" xmlUrl="http://goinglinux.com/oggpodcast.xml" />
|
||||
<outline description="Hacker Public Radio is an podcast that releases shows every weekday Monday through Friday. Our shows are produced by the community (you) and can be on any topic that are of interest to hackers and hobbyists." htmlUrl="http://hackerpublicradio.org/about.php" text="Hacker Public Radio" title="Hacker Public Radio" xmlUrl="http://hackerpublicradio.org/hpr_ogg_rss.php" />
|
||||
<outline description="The cross platform podcast that makes technology work for you and not the other way around. The place to go for all geeks who slide between Mac, iOS, Android, Linux and Windows offering an essential mix of hacks, tips, howto's and tweaks spiced up with a dash of geek culture. Also check out our Mediafeed that has both our audio and video episodes." htmlUrl="https://knightwise.com" text="Knightwise.com Audio Feed." title="Knightwise.com Audio Feed." xmlUrl="http://feeds.feedburner.com/knightcastpodcast" />
|
||||
<outline description="" htmlUrl="https://latenightlinux.com" text="Late Night Linux (Ogg)" title="Late Night Linux (Ogg)" xmlUrl="http://latenightlinux.com/feed/ogg" />
|
||||
<outline description="A casual podcast about user freedom" htmlUrl="https://librelounge.org" text="Libre Lounge" title="Libre Lounge" xmlUrl="https://librelounge.org/rss-feed.rss" />
|
||||
<outline description="Linux For The Rest Of Us - Podnutz" htmlUrl="http://podnutz.com" text="Linux For The Rest Of Us - Podnutz" title="Linux For The Rest Of Us - Podnutz" xmlUrl="http://feeds.feedburner.com/linuxfortherestofus" />
|
||||
<outline description="Chat about Linux and all things connected" htmlUrl="https://linuxlads.com/feed_ogg.rss" text="Linux Lads" title="Linux Lads" xmlUrl="https://linuxlads.com/feed_ogg.rss" />
|
||||
<outline description="Verbal's Linux Trivia Podcast" htmlUrl="http://setbit.org/lt.html" text="Linux Trivia Podcast" title="Linux Trivia Podcast" xmlUrl="http://setbit.org/lt-ogg.xml" />
|
||||
<outline description="Linux in the Ham Shack Podcast in OGG Format" htmlUrl="https://lhspodcast.info/category/podcast-ogg/" text="Linux in the Ham Shack (OGG Feed)" title="Linux in the Ham Shack (OGG Feed)" xmlUrl="https://lhspodcast.info/category/podcast-ogg/feed/" />
|
||||
<outline description="Linux fueled mayhem & madness with a side of news, reviews, and whatever the Hell-Elks™ we come up with" htmlUrl="https://linuxgamecast.com" text="LinuxGameCast" title="LinuxGameCast" xmlUrl="https://linuxgamecast.com/feed/" />
|
||||
<outline description="Linuxlugcast" htmlUrl="http://linuxlugcast.com" text="Linuxlugcast" title="Linuxlugcast" xmlUrl="http://feeds.feedburner.com/linuxlugcast-ogg" />
|
||||
<outline description="A tech oriented DIY podcast, from the Other Side Podcast Network" htmlUrl="https://makerscorner.tech" text="Makers Corner, with Nate and Yannick" title="Makers Corner, with Nate and Yannick" xmlUrl="https://makerscorner.tech/feed/podcast/" />
|
||||
<outline description="A remote corner of a bleak system. A broken-down gunboat, stuck in space. An incompetent captain and a misfit crew. A pirate ship, a silent target, and a whole bunch of secrets. So how's YOUR day going?" htmlUrl="http://downloads.cavalcadeaudio.com/stardrifter-novels/01-motherload/" text="Motherload" title="Motherload" xmlUrl="http://downloads.cavalcadeaudio.com/stardrifter-novels/01-motherload/feed.xml" />
|
||||
<outline description="Just another WordPress site" htmlUrl="https://mintcast.org" text="OGG – mintCast" title="OGG – mintCast" xmlUrl="https://mintcast.org/category/ogg/feed/" />
|
||||
<outline description="Music that will rip your face off and give a copy to your friends" htmlUrl="http://openmetalcast.com" text="Open Metalcast" title="Open Metalcast" xmlUrl="http://feeds.feedburner.com/OpenMetalcast" />
|
||||
<outline description="A security podcast geared towards those looking to better understand security topics of the day. Hosted by Kurt Seifried and Josh Bressers covering a wide range of topics including IoT, application security, operational security, cloud, devops, and security news of the day. There is a special open source twist to the discussion often giving a unique perspective on any given topic." htmlUrl="http://opensourcesecuritypodcast.com" text="Open Source Security Podcast" title="Open Source Security Podcast" xmlUrl="https://opensourcesecuritypodcast.libsyn.com/rss" />
|
||||
<outline description="Guaranteed to widen your musical perspectives, Pete Cogle plays tracks from all over the world in all possible genres. Like it, or hate it, it's a change from the same old shit on clearchannel radio." htmlUrl="http://petecogle.co.uk/blog/category/pc-podcast/" text="PC Podcast. A Music Podcast with Pete Cogle, since January 2006" title="PC Podcast. A Music Podcast with Pete Cogle, since January 2006" xmlUrl="http://feeds.feedburner.com/pcpodcast" />
|
||||
<outline description="The Fantasy Fiction Podcast" htmlUrl="http://podcastle.org" text="PodCastle" title="PodCastle" xmlUrl="http://podcastle.org/feed/" />
|
||||
<outline description="Cory Doctorow's Literary Works" htmlUrl="https://craphound.com" text="Podcast – Cory Doctorow's craphound.com" title="Podcast – Cory Doctorow's craphound.com" xmlUrl="http://feeds.feedburner.com/doctorow_podcast" />
|
||||
<outline description="The Sound of Horror" htmlUrl="http://pseudopod.org" text="PseudoPod" title="PseudoPod" xmlUrl="http://pseudopod.org/feed/" />
|
||||
<outline description="Linux, Open Source und Netzkultur" htmlUrl="https://www.radiotux.de/" text="RadioTux" title="RadioTux" xmlUrl="http://radiotux.de/podcast/rss/radiotux-all.xml" />
|
||||
<outline description="Music, Waffling, Live Performances & Laughs" htmlUrl="http://ratholeradio.org" text="RatholeRadio.org (Ogg Version)" title="RatholeRadio.org (Ogg Version)" xmlUrl="http://feeds.feedburner.com/RatholeRadio-ogg" />
|
||||
<outline description="Steve Gibson, the man who coined the term spyware and created the first anti-spyware program, creator of Spinrite and ShieldsUP, discusses the hot topics in security today with Leo Laporte. Records live every Tuesday at 4:30pm Eastern / 1:30pm Pacific / 21:30 UTC." htmlUrl="https://twit.tv/shows/security-now" text="Security Now (MP3)" title="Security Now (MP3)" xmlUrl="http://feeds.twit.tv/sn.xml" />
|
||||
<outline description="Spanking the bottom of ignorance since 2009" htmlUrl="http://www.skepticule.co.uk/" text="Skepticule" title="Skepticule" xmlUrl="http://www.skepticule.co.uk/feeds/posts/default?alt=rss" />
|
||||
<outline description="Sunday Morning Linux Review – OGG Feed for Freedom Lovers!" htmlUrl="https://smlr.us" text="Sunday Morning Linux Review - OGG Feed" title="Sunday Morning Linux Review - OGG Feed" xmlUrl="http://feeds.feedburner.com/SundayMorningLinuxReview-OggFeed" />
|
||||
<outline description="An unofficial Star Trek fan podcast from the Other Side Podcast Network" htmlUrl="https://teaearlgreyhot.org" text="Tea, Earl Grey, Hot !" title="Tea, Earl Grey, Hot !" xmlUrl="https://teaearlgreyhot.org/feed/podcast" />
|
||||
<outline description="Linux and open source tips, tricks and discussion. Free software, hardware and modern culture. The Binary Times Audiocast is created by Mark and Wayne, two chaps who just like using linux and open source software and want to spread the word. Linux is free and open source and it is an excellent choice of operating system for our ever changing times. This audiocast is released fortnightly." htmlUrl="https://www.thebinarytimes.net" text="The Binary Times Audiocast - ogg" title="The Binary Times Audiocast - ogg" xmlUrl="https://www.thebinarytimes.net/rss-ogg.xml" />
|
||||
<outline description="Award-winning music and chat from South Yorkshire in the UK" htmlUrl="https://thebugcast.org" text="The Bugcast - Ogg Feed" title="The Bugcast - Ogg Feed" xmlUrl="http://thebugcast.org/feed/ogg/" />
|
||||
<outline description="A podcast by a self-described hacker, curmudgeon and hacktivist about the practice and profession of programming drawing on over a decade of professional experience and a lifetime spent hacking, the intersection of politics and society with technology and anything else clever, elegant or funny that catches my mind as a die hard technology geek." htmlUrl="http://thecommandline.net/" text="The Command Line Podcast" title="The Command Line Podcast" xmlUrl="https://thecommandline.net/files/cmdln_mp3.xml" />
|
||||
<outline description="An 8-track mix of the best dub reggae, every month. Curated by Pete Cogle, since May 2007." htmlUrl="http://petecogle.co.uk/blog" text="The Dub Zone" title="The Dub Zone" xmlUrl="http://feeds.feedburner.com/TheDubZone" />
|
||||
<outline description="Some auld duffers providing world wide wisdom" htmlUrl="https://duffercast.org" text="The Duffercast in Ogg Vorbis" title="The Duffercast in Ogg Vorbis" xmlUrl="http://duffercast.org/feed/podcast/" />
|
||||
<outline description="From the independent magazine for the Ubuntu Linux community. The Full Circle Weekly News is a short podcast with just the news. No chit-chat. No time wasting. Just the latest FOSS/Linux/Ubuntu news." htmlUrl="https://fullcirclemagazine.org/" text="The Full Circle Weekly News" title="The Full Circle Weekly News" xmlUrl="https://fullcirclemagazine.org/feed/podcast" />
|
||||
<outline description="Where Chic meets geek! The JaK Attack! is hoted by Jon Watson and Kelly Penguin Girl. We talk about tech, art, and we do it just a little too loud." htmlUrl="http://thejakattack.com/" text="The JaK Attack! Podcast" title="The JaK Attack! Podcast" xmlUrl="http://feeds.feedburner.com/TheJakAttack" />
|
||||
<outline description="Monthly astronomy news, interviews and questions. Created by astronomers." htmlUrl="http://www.jodcast.net/" text="The Jodcast (high bandwidth)" title="The Jodcast (high bandwidth)" xmlUrl="http://www.jodcast.net/rss-high.xml" />
|
||||
<outline description="The Linux Link Tech Show" htmlUrl="http://www.tllts.org" text="The Linux Link Tech Show Ogg-Vorbis Feed" title="The Linux Link Tech Show Ogg-Vorbis Feed" xmlUrl="http://feeds.feedburner.com/TheLinuxLinkTechShowOgg-vorbisFeed" />
|
||||
<outline description="If you obsess about the details inside computers then on This Week in Computer Hardware, you'll find out the latest in motherboards, CPUs, GPUs, storage, RAM, power supplies, input devices, and monitors. Hosts Patrick Norton of TekThing and Sebastian Peak of PC Perspective bring you the newest hardware, talk benchmarks, and even dive into the not-yet-released products on the horizon. Records live every Thursday at 3:30pm Eastern / 12:30pm Pacific / 20:30 UTC." htmlUrl="https://twit.tv/shows/this-week-in-computer-hardware" text="This Week in Computer Hardware (MP3)" title="This Week in Computer Hardware (MP3)" xmlUrl="http://feeds.twit.tv/twich.xml" />
|
||||
<outline description="Leo Laporte, Jeff Jarvis, Stacey Higginbotham, Ant Pruitt, and their guests talk about the latest Google and cloud computing news. Records live every Wednesday at 4:00pm Eastern / 1:00pm Pacific / 21:00 UTC." htmlUrl="https://twit.tv/shows/this-week-in-google" text="This Week in Google (MP3)" title="This Week in Google (MP3)" xmlUrl="http://feeds.twit.tv/twig.xml" />
|
||||
<outline description="This Week in Microbiology is a podcast about unseen life on Earth hosted by Vincent Racaniello and friends. Following in the path of his successful shows 'This Week in Virology' (TWiV) and 'This Week in Parasitism' (TWiP), Racaniello and guests produce an informal yet informative conversation about microbes which is accessible to everyone, no matter what their science background." htmlUrl="http://www.asm.org/twim/" text="This Week in Microbiology" title="This Week in Microbiology" xmlUrl="http://feeds.feedburner.com/twim" />
|
||||
<outline description="Your first podcast of the week is the last word in tech. Join the top tech pundits in a roundtable discussion of the latest trends in high tech. Records live every Sunday at 5:15pm Eastern / 2:15pm Pacific / 22:15 UTC." htmlUrl="https://twit.tv/shows/this-week-in-tech" text="This Week in Tech (MP3)" title="This Week in Tech (MP3)" xmlUrl="http://feeds.twit.tv/twit.xml" />
|
||||
<outline description="TuxJam is a family friendly show that blends Creative Commons music and Open Source goodness. This is an OGG feed." htmlUrl="https://tuxjam.otherside.network" text="TuxJam OGG" title="TuxJam OGG" xmlUrl="https://tuxjam.otherside.network/?feed=podcast" />
|
||||
<outline description="Upbeat and family-friendly show including news, discussion, interviews and reviews from the Ubuntu, Linux and Open Source world." htmlUrl="http://ubuntupodcast.org" text="Ubuntu Podcast" title="Ubuntu Podcast" xmlUrl="http://ubuntupodcast.org/feed/" />
|
||||
<outline description="En podcast om Wikipedia på svenska" htmlUrl="http://wikipediapodden.se/prenumerera/" text="Wikipediapodden" title="Wikipediapodden" xmlUrl="http://wikipediapodden.se/feed/podcast/" />
|
||||
<outline description="urandom: your unlimited source of medium quality randomness" htmlUrl="https://urandom-podcast.info/" text="urandom podcast" title="urandom podcast" xmlUrl="http://feeds.feedburner.com/urandom-podcast/ogg" />
|
||||
</body>
|
||||
</opml>
|
BIN
feed_watcher/feedWatcher.pdf
Normal file
BIN
feed_watcher/feedWatcher.pdf
Normal file
Binary file not shown.
1
feed_watcher/feedWatcher.tpl
Symbolic link
1
feed_watcher/feedWatcher.tpl
Symbolic link
@ -0,0 +1 @@
|
||||
feedWatcher_2.tpl
|
50
feed_watcher/feedWatcher_1.tpl
Normal file
50
feed_watcher/feedWatcher_1.tpl
Normal file
@ -0,0 +1,50 @@
|
||||
[%# feedWatcher_1.tpl 2020-01-23 -%]
|
||||
[%# Generates very primitive standalone HTML %]
|
||||
[% USE dumper(indent=0, pad="<br/>") %]
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="pandoc">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<meta name="author" content="Dave Morriss">
|
||||
<title>Free Culture Podcasts</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link rel="stylesheet" href="http://hackerpublicradio.org/css/hpr.css">
|
||||
</head>
|
||||
|
||||
<body id="home">
|
||||
<div id="container" class="shadow">
|
||||
|
||||
[% IF feeds.size > 0 -%]
|
||||
[% i = 0 -%]
|
||||
<ul>
|
||||
[% WHILE i < feeds.size -%]
|
||||
[% IF feeds.$i.urls_title.length > 0 -%]
|
||||
<li><a href="[% feeds.$i.urls_url %]">[% feeds.$i.urls_title %]</a><br/>
|
||||
[% ELSE -%]
|
||||
<li><a href="[% feeds.$i.urls_url %]">[% feeds.$i.urls_url %]</a><br/>
|
||||
[% END -%]
|
||||
<em>Description:</em> [% feeds.$i.urls_description %]<br/>
|
||||
[% IF feeds.$i.urls_image.defined -%]
|
||||
<em>Logo:</em> <img src="[% feeds.$i.urls_image %]" height="100" width="100" alt="Logo"><br/>
|
||||
[% END -%]
|
||||
<em>Copyright:</em> [% feeds.$i.urls_copyright %]<br/>
|
||||
<em>Latest episode:</em> [% feeds.$i.latest_ep %]<br/>
|
||||
<hr>
|
||||
</li>
|
||||
[% i = i + 1 -%]
|
||||
[% END -%]
|
||||
</ul>
|
||||
[% END -%]
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
[%#
|
||||
# vim: syntax=tt2:ts=8:sw=4:ai:et:tw=78:fo=tcrqn21
|
||||
-%]
|
226
feed_watcher/feedWatcher_2.tpl
Normal file
226
feed_watcher/feedWatcher_2.tpl
Normal file
@ -0,0 +1,226 @@
|
||||
[% TAGS html %]
|
||||
<!--# feedWatcher_2.tpl 2020-01-26 -->
|
||||
<!--# Web page design courtesy of Roan Horning -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
/* prefixed by https://autoprefixer.github.io (PostCSS: v7.0.26, autoprefixer: v9.7.3) */
|
||||
html {
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
font-family: "FreeSans", sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: white;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: stretch;
|
||||
-ms-flex-align: stretch;
|
||||
align-items: stretch;
|
||||
-ms-flex-line-pack: center;
|
||||
align-content: center;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
max-width: 50%;
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
body > * {
|
||||
padding: 1em 1em;
|
||||
}
|
||||
main {
|
||||
background-color: #efbe0032;
|
||||
background-color: hsla(47.7,100%,46.9%, 0.2);
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
main * {
|
||||
opacity: 100%;
|
||||
}
|
||||
header, footer {
|
||||
-webkit-box-flex: 0;
|
||||
-ms-flex: 0 1 auto;
|
||||
flex: 0 1 auto;
|
||||
background-color: #efbe00;
|
||||
background-color: hsla(47.7,100%,46.9%, 1);
|
||||
}
|
||||
footer {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
header {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
flex-basis: 50%;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: start;
|
||||
-ms-flex-pack: start;
|
||||
justify-content: start;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
header > * {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
header img {
|
||||
max-height: 128px;
|
||||
min-height: 128px;
|
||||
}
|
||||
header h1 {
|
||||
padding: 0 0.5em 0 0.15em;
|
||||
-webkit-box-flex: 2;
|
||||
-ms-flex-positive: 2;
|
||||
flex-grow: 2;
|
||||
color: white;
|
||||
font-size: 3rem;
|
||||
text-shadow: -1px 1px 0 #000,
|
||||
1px 1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px -1px 0 #000;
|
||||
|
||||
}
|
||||
header h1 span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
header aside {
|
||||
border: solid 0.5em black;
|
||||
padding: 1em;
|
||||
background-color: #FCF2CD;
|
||||
background-color: hsla(47.2, 88.7%, 89.6%, 1);
|
||||
-ms-flex-negative: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
dt {
|
||||
font-weight: 600;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
dd {
|
||||
margin-top: 0.15em;
|
||||
font-weight: 400;
|
||||
}
|
||||
@media only screen and (max-width: 1270px){
|
||||
html {
|
||||
border-left: solid 0.5em black;
|
||||
border-right: solid 0.5em black;
|
||||
}
|
||||
body {
|
||||
max-width:none;
|
||||
}
|
||||
header {
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
header h1 {
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex-positive: 1;
|
||||
flex-grow: 1;
|
||||
max-width: 67%;
|
||||
}
|
||||
header aside {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 600px){
|
||||
body > * {
|
||||
padding: 0.5em;
|
||||
}
|
||||
header > * {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
padding-right: 0;
|
||||
font-size: 2rem;
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex-positive: 1;
|
||||
flex-grow: 1;
|
||||
max-width: 70%;
|
||||
}
|
||||
header img {
|
||||
max-height: 96px;
|
||||
min-height: 96px;
|
||||
}
|
||||
header aside {
|
||||
font-size: 0.95rem;
|
||||
padding: 0.5em;
|
||||
}
|
||||
footer img {
|
||||
max-height: 32px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-height: 780px) and (orientation: landscape) {
|
||||
html {
|
||||
height:auto;
|
||||
display:initial;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="./logo.svg" alt="[logo]">
|
||||
<h1><span>Free Culture</span> Podcasts</h1>
|
||||
<aside>
|
||||
The finest selection of Free Culture Podcasts spanning
|
||||
the genres of Discussion, Drama, Education, Music, and beyond.
|
||||
</aside>
|
||||
</header>
|
||||
<main>
|
||||
<!-- IF feeds.size > 0 -->
|
||||
<!-- i = 0 -->
|
||||
<dl>
|
||||
<!-- WHILE i < feeds.size -->
|
||||
<!-- IF feeds.$i.urls_title.length > 0 -->
|
||||
<dt><a href="<!-- feeds.$i.urls_link -->"><!-- feeds.$i.urls_title FILTER html_entity --></a> (<a href="<!-- feeds.$i.urls_url -->">feed</a>)</dt>
|
||||
<!-- ELSE -->
|
||||
<dt><a href="<!-- feeds.$i.urls_link -->"><em>No title</em></a> (<a href="<!-- feeds.$i.urls_url -->">feed</a>)</dt>
|
||||
<!-- END -->
|
||||
<!-- IF feeds.$i.urls_description.length > 0 -->
|
||||
<dd><!-- feeds.$i.urls_description FILTER html_entity --></dd>
|
||||
<!-- ELSE -->
|
||||
<dd><em>No description</em></dd>
|
||||
<!-- END -->
|
||||
<!-- i = i + 1 -->
|
||||
<!-- END -->
|
||||
</dl>
|
||||
<!-- END -->
|
||||
<p>and many <a href="https://gpodder.net/directory/">more</a> ...</p>
|
||||
</main>
|
||||
<footer>
|
||||
<img src="./cc.svg" alt="[Creative Commons]">
|
||||
<img src="./attribution.svg" alt="[Attribution]">
|
||||
<img src="./share_alike.svg" alt="[Share Alike]">
|
||||
<img src="./remix.svg" alt="[Remix]">
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
<!--#
|
||||
# vim: syntax=html:ts=8:sw=4:ai:et:tw=78:fo=tcrqn21
|
||||
-->
|
21
feed_watcher/feedWatcher_3.tpl
Normal file
21
feed_watcher/feedWatcher_3.tpl
Normal file
@ -0,0 +1,21 @@
|
||||
[%# feedWatcher_3.tpl 2020-01-24 -%]
|
||||
[%# Generates Markdown as a list with a sublist per feed -%]
|
||||
# Free Culture Podcasts
|
||||
|
||||
### The finest selection of Free Culture Podcasts spanning the genres of Discussion, Drama, Education, Music, and beyond.
|
||||
|
||||
[% IF feeds.size > 0 -%]
|
||||
[% i = 0 -%]
|
||||
[% WHILE i < feeds.size -%]
|
||||
- **[% feeds.$i.urls_title %]**
|
||||
- Website: [% feeds.$i.urls_link %]
|
||||
- Feed: [% feeds.$i.urls_url %]
|
||||
- Licence: [% feeds.$i.urls_copyright %]
|
||||
|
||||
[% i = i + 1 -%]
|
||||
[% END -%]
|
||||
[% END -%]
|
||||
|
||||
[%#
|
||||
# vim: syntax=tt2:ts=8:sw=4:ai:et:tw=78:fo=tcrqn21
|
||||
-%]
|
16
feed_watcher/feedWatcher_4.tpl
Normal file
16
feed_watcher/feedWatcher_4.tpl
Normal file
@ -0,0 +1,16 @@
|
||||
[%# feedWatcher_4.tpl 2020-01-25 -%]
|
||||
[%# Generates simple lines each containing title and description per feed -%]
|
||||
[% i = 0 -%]
|
||||
[% WHILE i < feeds.size -%]
|
||||
[% IF feeds.$i.urls_description.length > 0 -%]
|
||||
[% desc = feeds.$i.urls_description -%]
|
||||
[% ELSE -%]
|
||||
[% desc = "[No description]" -%]
|
||||
[% END -%]
|
||||
[% feeds.$i.urls_title %]: [% desc %]
|
||||
[% i = i + 1 -%]
|
||||
[% END -%]
|
||||
[%#
|
||||
# vim: syntax=tt2:ts=8:sw=4:ai:et:tw=78:fo=tcrqn21
|
||||
-%]
|
||||
|
149
feed_watcher/feedWatcher_schema.sql
Normal file
149
feed_watcher/feedWatcher_schema.sql
Normal file
@ -0,0 +1,149 @@
|
||||
--
|
||||
-- feedWatcher_schema.sql - version 0.0.9
|
||||
--
|
||||
-- Renamed 'feedWatcher.sql' => `feedWatcher_schema.sql' on 2020-01-18
|
||||
--
|
||||
|
||||
/*
|
||||
* Table 'urls'
|
||||
* ------------
|
||||
*
|
||||
* The main table containing host, http and feed information
|
||||
*/
|
||||
|
||||
DROP TABLE IF EXISTS episodes;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id integer PRIMARY KEY,
|
||||
url varchar(1024) NOT NULL,
|
||||
dns text,
|
||||
host_up boolean DEFAULT 0,
|
||||
http_status varchar(80),
|
||||
content_type varchar(40),
|
||||
urltype varchar(40),
|
||||
feedformat varchar(20),
|
||||
title varchar(1024),
|
||||
description text,
|
||||
author varchar(80),
|
||||
modified timestamp,
|
||||
link varchar(1024),
|
||||
image varchar(1024),
|
||||
copyright varchar(80),
|
||||
generator varchar(80),
|
||||
language varchar(40),
|
||||
last_update timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
--
|
||||
-- Ensure the 'url' column is unique
|
||||
--
|
||||
|
||||
DROP INDEX IF EXISTS url_index;
|
||||
|
||||
CREATE UNIQUE INDEX url_index ON urls ( url );
|
||||
|
||||
--
|
||||
-- Trigger to update the 'last_update' field every time the row changes
|
||||
--
|
||||
|
||||
DROP TRIGGER IF EXISTS urls_last_updated;
|
||||
|
||||
CREATE TRIGGER urls_last_updated AFTER UPDATE ON urls
|
||||
BEGIN
|
||||
UPDATE urls SET last_update = datetime('now');
|
||||
END;
|
||||
|
||||
/*
|
||||
* Table 'episodes'
|
||||
* ----------------
|
||||
*
|
||||
* Has a foreign key relationship with table 'urls'. If the matching row in
|
||||
* 'urls' is deleted the deletion cascades to this table (ON DELETE CASCADE).
|
||||
* If the primary key in 'urls' is updated the relationship prevents this
|
||||
* change (ON UPDATE RESTRICT).
|
||||
*/
|
||||
|
||||
DROP TABLE IF EXISTS episodes;
|
||||
|
||||
CREATE TABLE episodes (
|
||||
id integer PRIMARY KEY,
|
||||
urls_id integer NOT NULL
|
||||
REFERENCES urls (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE RESTRICT,
|
||||
link varchar(1024),
|
||||
enclosure varchar(256),
|
||||
title varchar(256),
|
||||
author varchar(256),
|
||||
category text,
|
||||
source varchar(128),
|
||||
ep_id varchar(256),
|
||||
issued timestamp,
|
||||
modified timestamp,
|
||||
byte_length bigint,
|
||||
mime_type varchar(64),
|
||||
last_update timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
--
|
||||
-- Ensure the 'enclosure' column is unique (per feed)
|
||||
--
|
||||
|
||||
DROP INDEX IF EXISTS enclosure_index;
|
||||
|
||||
CREATE UNIQUE INDEX enclosure_index ON episodes ( urls_id, enclosure );
|
||||
|
||||
--
|
||||
-- Trigger to update the 'last_update' field every time the row changes
|
||||
--
|
||||
|
||||
DROP TRIGGER IF EXISTS episodes_last_updated;
|
||||
|
||||
CREATE TRIGGER episodes_last_updated AFTER UPDATE ON episodes
|
||||
BEGIN
|
||||
UPDATE episodes SET last_update = datetime('now');
|
||||
END;
|
||||
|
||||
--
|
||||
-- View to simplify joining the two tables.
|
||||
--
|
||||
|
||||
DROP VIEW IF EXISTS all_episodes;
|
||||
|
||||
CREATE VIEW all_episodes AS
|
||||
SELECT
|
||||
urls.id as urls_id,
|
||||
urls.url as urls_url,
|
||||
urls.dns as urls_dns,
|
||||
urls.host_up as urls_host_up,
|
||||
urls.http_status as urls_http_status,
|
||||
urls.content_type as urls_content_type,
|
||||
urls.urltype as urls_urltype,
|
||||
urls.feedformat as urls_feedformat,
|
||||
urls.title as urls_title,
|
||||
urls.description as urls_description,
|
||||
urls.author as urls_author,
|
||||
urls.modified as urls_modified,
|
||||
urls.link as urls_link,
|
||||
urls.image as urls_image,
|
||||
urls.copyright as urls_copyright,
|
||||
urls.generator as urls_generator,
|
||||
urls.language as urls_language,
|
||||
urls.last_update as urls_last_update,
|
||||
ep.id as ep_id,
|
||||
ep.urls_id as ep_urls_id,
|
||||
ep.link as ep_link,
|
||||
ep.enclosure as ep_enclosure,
|
||||
ep.title as ep_title,
|
||||
ep.author as ep_author,
|
||||
ep.category as ep_category,
|
||||
ep.source as ep_source,
|
||||
ep.ep_id as ep_ep_id,
|
||||
ep.issued as ep_issued,
|
||||
ep.modified as ep_modified,
|
||||
ep.byte_length as ep_byte_length,
|
||||
ep.mime_type as ep_mime_type,
|
||||
ep.last_update as ep_last_update
|
||||
FROM urls LEFT JOIN episodes ep ON urls.id = ep.urls_id;
|
||||
|
||||
-- vim: syntax=sql:ts=8:ai:tw=78:et:fo=tcrqn21:comments+=b\:--
|
30
workflow/duration.bash
Executable file
30
workflow/duration.bash
Executable file
@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
# Copyright Ken Fallon - Released into the public domain. http://creativecommons.org/publicdomain/ ]
|
||||
|
||||
|
||||
if [ -f "${1}" ]
|
||||
then
|
||||
mediainfo --Output=XML --Full "${1}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | sed 's/0$//g'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
find ./ -type f | grep -E -v '/sponsor-anhonesthost.com-hpr15.flac|/outro.flac|/intro.flac|/sponsor-archive.org.flac' | while read mediafile
|
||||
do
|
||||
if [ "$( file "${mediafile}" | grep -c audio )" == "0" ]
|
||||
then
|
||||
continue
|
||||
fi
|
||||
duration=$( mediainfo --full --Output=XML "${mediafile}" | xmlstarlet sel -T -t -m "_:MediaInfo/_:media/_:track[@type='Audio']/_:Duration[1]" -v "." -n - | sed 's/0$//g')
|
||||
if [ "${duration}" != "" ]
|
||||
then
|
||||
echo "${mediafile}: ${duration}"
|
||||
continue
|
||||
fi
|
||||
duration=$( /bin/date -ud "1970-01-01 $( ffprobe -i "${mediafile}" 2>&1| awk -F ': |, ' '/Duration:/ { print $2 }' )" +%s )
|
||||
if [ "${duration}" != 0 ]
|
||||
then
|
||||
echo "${mediafile}: ${duration}"
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
641
workflow/fix_tags
Executable file
641
workflow/fix_tags
Executable file
@ -0,0 +1,641 @@
|
||||
#!/usr/bin/perl
|
||||
#===============================================================================
|
||||
#
|
||||
# FILE: fix_tags
|
||||
#
|
||||
# USAGE: ./fix_tags [-help] [-album=ALBUMSTRING] [-artist=ARTISTSTRING]
|
||||
# [-comment=COMMENTSTRING] [-genre=GENRESTRING]
|
||||
# [-title=TITLESTRING] [-track=TRACKNUMBER] [-year=YEAR]
|
||||
# [-filter TAGNAME=FILTERNAME] audio_file ...
|
||||
#
|
||||
# DESCRIPTION: A tool for reporting and altering tags in audio files of
|
||||
# a variety of formats
|
||||
#
|
||||
# OPTIONS: ---
|
||||
# REQUIREMENTS: ---
|
||||
# BUGS: ---
|
||||
# NOTES: ---
|
||||
# AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
|
||||
# LICENCE: Copyright (c) year 2011, 2012, 2013, 2014, Dave Morriss
|
||||
# VERSION: 1.3.4
|
||||
# CREATED: 2011-12-12 22:00:34
|
||||
# REVISION: 2014-06-25 15:10:17
|
||||
#
|
||||
#===============================================================================
|
||||
|
||||
use Modern::Perl '2011';
|
||||
|
||||
use Getopt::Long;
|
||||
use Pod::Usage;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
use File::stat;
|
||||
use Date::Manip::Delta;
|
||||
use Date::Manip::TZ;
|
||||
use Audio::TagLib;
|
||||
|
||||
use TryCatch;
|
||||
|
||||
use HTML::Restrict;
|
||||
|
||||
#
|
||||
# Version number (manually incremented)
|
||||
#
|
||||
our $VERSION = '1.3.4';
|
||||
|
||||
#
|
||||
# Script name
|
||||
#
|
||||
( my $PROG = $0 ) =~ s|.*/||mx;
|
||||
|
||||
#
|
||||
# Declarations
|
||||
#
|
||||
my ( $sb, $fyear, $ref, $tag, $aprop, $changed );
|
||||
my ( %tags, @errors );
|
||||
|
||||
#
|
||||
# The Audio::TagLib methods to call for each tag manipulated by the script.
|
||||
# The number after the method name is 1 if the value being set is a string,
|
||||
# and zero otherwise.
|
||||
#
|
||||
my %tagmethods = (
|
||||
album => [ 'setAlbum', 1 ],
|
||||
artist => [ 'setArtist', 1 ],
|
||||
comment => [ 'setComment', 1 ],
|
||||
genre => [ 'setGenre', 1 ],
|
||||
title => [ 'setTitle', 1 ],
|
||||
track => [ 'setTrack', 0 ],
|
||||
year => [ 'setYear', 0 ],
|
||||
);
|
||||
|
||||
#
|
||||
# Internal routines to invoke to perform filtering tasks
|
||||
#
|
||||
my %filtermethods = (
|
||||
clean => \&clean_string,
|
||||
underscore => \&replace_underscores,
|
||||
HTML => \&remove_HTML,
|
||||
);
|
||||
|
||||
#
|
||||
# Ensure STDOUT and STDERR are in UTF8 mode
|
||||
#
|
||||
binmode( STDOUT, ":encoding(utf8)" );
|
||||
binmode( STDERR, ":encoding(utf8)" );
|
||||
|
||||
#
|
||||
# Options and arguments
|
||||
#
|
||||
my ( %options, %filter );
|
||||
Options( \%options, \%filter );
|
||||
|
||||
#
|
||||
# Default help
|
||||
#
|
||||
pod2usage( -msg => "Version $VERSION\n", -exitval => 1 )
|
||||
if ( $options{'help'} );
|
||||
|
||||
#
|
||||
# Collect options
|
||||
#
|
||||
my $album = $options{album};
|
||||
my $artist = $options{artist};
|
||||
my $comment = $options{comment};
|
||||
my $genre = $options{genre};
|
||||
my $title = $options{title};
|
||||
my $track = $options{track};
|
||||
my $year = $options{year};
|
||||
|
||||
#
|
||||
# Check the filter options
|
||||
#
|
||||
unless ( check_filters( \%filter, \@errors ) ) {
|
||||
print STDERR join( "\n", @errors ), "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
my @files = @ARGV;
|
||||
|
||||
pod2usage(
|
||||
-msg => "Missing arguments\n\nVersion $VERSION\n",
|
||||
-exitval => 1
|
||||
) unless @files;
|
||||
|
||||
foreach my $file (@files) {
|
||||
unless ( -e $file ) {
|
||||
warn "$file does not exist\n";
|
||||
next;
|
||||
}
|
||||
|
||||
#
|
||||
# Report the file name
|
||||
#
|
||||
print "$file\n";
|
||||
|
||||
#
|
||||
# If the file is empty report it and skip it
|
||||
#
|
||||
if ( -z $file ) {
|
||||
warn "File $file is empty\n";
|
||||
next;
|
||||
}
|
||||
|
||||
$sb = stat($file);
|
||||
$fyear = ( localtime( $sb->mtime ) )[5] + 1900;
|
||||
|
||||
#
|
||||
# Catch errors if someone tries to use this tool on a file that
|
||||
# Audio::TagLib doesn't know about
|
||||
#
|
||||
try {
|
||||
$ref = Audio::TagLib::FileRef->new($file);
|
||||
$tag = $ref->tag();
|
||||
$aprop = $ref->audioProperties();
|
||||
|
||||
%tags = (
|
||||
title => $tag->title()->toCString(),
|
||||
artist => $tag->artist()->toCString(),
|
||||
album => $tag->album()->toCString(),
|
||||
comment => $tag->comment()->toCString(),
|
||||
genre => $tag->genre()->toCString(),
|
||||
year => $tag->year(),
|
||||
track => $tag->track(),
|
||||
length => sprintf( "%s (%d sec)",
|
||||
interval( $aprop->length() ),
|
||||
$aprop->length() ),
|
||||
);
|
||||
}
|
||||
|
||||
#
|
||||
# We choked on something nasty
|
||||
#
|
||||
catch {
|
||||
warn "File $file apparently does not contain tags\n";
|
||||
next;
|
||||
}
|
||||
|
||||
#
|
||||
# Report current tags
|
||||
#
|
||||
for my $key ( sort( keys(%tags) ) ) {
|
||||
printf "%-10s: %s\n", $key, $tags{$key};
|
||||
}
|
||||
|
||||
$changed = 0;
|
||||
|
||||
#
|
||||
# Change album, artist name, comment, genre, track number or year if
|
||||
# requested
|
||||
#
|
||||
$changed
|
||||
+= changeTag( $tag, 'album', $tags{album}, $album, 'setAlbum', 1 );
|
||||
$changed
|
||||
+= changeTag( $tag, 'artist', $tags{artist}, $artist, 'setArtist',
|
||||
1 );
|
||||
$changed
|
||||
+= changeTag( $tag, 'comment', $tags{comment}, $comment,
|
||||
'setComment', 1 );
|
||||
$changed
|
||||
+= changeTag( $tag, 'genre', $tags{genre}, $genre, 'setGenre', 1 );
|
||||
$changed
|
||||
+= changeTag( $tag, 'title', $tags{title}, $title, 'setTitle', 1 );
|
||||
$changed
|
||||
+= changeTag( $tag, 'track', $tags{track}, $track, 'setTrack', 0 );
|
||||
$changed += changeTag( $tag, 'year', $tags{year}, $year, 'setYear', 0 );
|
||||
|
||||
#
|
||||
# Do some filtering
|
||||
#
|
||||
$changed += apply_filters( $tag, \%tags, \%tagmethods, \%filter,
|
||||
\%filtermethods );
|
||||
|
||||
#
|
||||
# Update if there are changes
|
||||
#
|
||||
if ($changed) {
|
||||
$ref->save();
|
||||
}
|
||||
|
||||
}
|
||||
continue {
|
||||
print "\n";
|
||||
}
|
||||
|
||||
exit;
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: changeTag
|
||||
# PURPOSE: Changes a tag to a new value if appropriate
|
||||
# PARAMETERS: $tag Tag object
|
||||
# $tagname Name of tag
|
||||
# $oldValue Current value of tag
|
||||
# $newValue New value of tag or undefined
|
||||
# $setFunc String containing the name of the 'set'
|
||||
# function
|
||||
# $isString True if the value being set is a string
|
||||
# RETURNS: 1 if a change has been made, 0 otherwise
|
||||
# DESCRIPTION: Checks that $newValue is defined (it can be an empty string)
|
||||
# and that the new value differs from the old one, returning if
|
||||
# not. The $isString value defaults to zero. Ensures that a null
|
||||
# $newValue is replaced by a zero if the tag is numeric. Reports
|
||||
# what change has been requested then makes the change.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub changeTag {
|
||||
my ( $tag, $tagname, $oldValue, $newValue, $setFunc, $isString ) = @_;
|
||||
|
||||
return 0 unless defined($newValue);
|
||||
return 0 if $oldValue eq $newValue;
|
||||
|
||||
$isString = 0 unless defined($isString);
|
||||
|
||||
$newValue = 0 if ( $newValue eq '' && !$isString );
|
||||
|
||||
print "Changing $tagname to '$newValue'\n";
|
||||
$tag->$setFunc(
|
||||
( $isString
|
||||
? Audio::TagLib::String->new($newValue)
|
||||
: $newValue
|
||||
)
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: check_filters
|
||||
# PURPOSE: Check that the filter hash contains valid settings
|
||||
# PARAMETERS: $filter Hashref containing the filter options
|
||||
# $errors Arrayref to contain error messages
|
||||
# RETURNS: 1 (true) if all is well, otherwise 0 (false)
|
||||
# DESCRIPTION: The check returns true if there are no filters. It returns
|
||||
# false if there are any unknown tag names or any unknown filter
|
||||
# names.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: The knowledge of what is a vlaid tag name or filter name is
|
||||
# within this function, which is not ideal for maintenance.
|
||||
# SEE ALSO: N/A
|
||||
#===============================================================================
|
||||
sub check_filters {
|
||||
my ( $filters, $errors ) = @_;
|
||||
|
||||
my @filterable_tags = (qw{ album artist comment genre title });
|
||||
my @valid_filters = (qw{clean underscore html});
|
||||
my @unknown;
|
||||
|
||||
#
|
||||
# Nothing is wrong if there are no filters
|
||||
#
|
||||
return 1 unless defined($filters);
|
||||
|
||||
#
|
||||
# Are any tag names unknown?
|
||||
#
|
||||
my %filterable = map { $_ => 1 } @filterable_tags;
|
||||
@unknown = grep { !defined( $filterable{$_} ) }
|
||||
map { lc($_) } keys( %{$filters} );
|
||||
if (@unknown) {
|
||||
push( @{$errors}, "Error: Invalid filter(s)" )
|
||||
unless defined($errors);
|
||||
push( @{$errors}, "Unknown tag names: " . join( ", ", @unknown ) );
|
||||
}
|
||||
|
||||
#
|
||||
# Are any filter names unknown?
|
||||
#
|
||||
my %valid = map { $_ => 1 } @valid_filters;
|
||||
my @names = map { @{ $filters->{$_} } } keys( %{$filters} );
|
||||
@unknown = grep { !defined( $valid{$_} ) } map { lc($_) } @names;
|
||||
if (@unknown) {
|
||||
push( @{$errors}, "Error: Invalid filter(s)" )
|
||||
unless defined($errors);
|
||||
push( @{$errors}, "Unknown filter names: " . join( ", ", @unknown ) );
|
||||
}
|
||||
|
||||
#
|
||||
# All tests passed if no errors
|
||||
#
|
||||
return scalar( @{$errors} ) == 0;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: apply_filters
|
||||
# PURPOSE: Looks for requested filters and the tags they are to be
|
||||
# applied to and performs the necessary filtering
|
||||
# PARAMETERS: $tag Tag object
|
||||
# $tags Hashref containing the converted tags from the
|
||||
# current file
|
||||
# $tagmethods Hashref containing the Audio::TagLib method
|
||||
# names per tag
|
||||
# $filter Hashref containing filter options
|
||||
# $filtermethods Hashref containing filter names and the
|
||||
# routines that handle them
|
||||
# RETURNS: Number of changes made
|
||||
# DESCRIPTION: The $filter hash contains tag names as keys (lower- or
|
||||
# upper-case). The value is an array of filter names (lower- or
|
||||
# upper-case). We loop through the tag names looking for filter
|
||||
# names and applying the filters we find.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: The sorting should be case-insensitive.
|
||||
# SEE ALSO: N/A
|
||||
#===============================================================================
|
||||
sub apply_filters {
|
||||
my ( $tag, $tags, $tagmethods, $filter, $filtermethods ) = @_;
|
||||
|
||||
my $lc_t;
|
||||
my $newtag;
|
||||
my $changes = 0;
|
||||
|
||||
#
|
||||
# Loop through the tags we are to filter in sorted order
|
||||
#
|
||||
for my $t ( sort( keys( %{$filter} ) ) ) {
|
||||
|
||||
#
|
||||
# We need a lowercase key to access the tag
|
||||
#
|
||||
$lc_t = lc($t);
|
||||
|
||||
#
|
||||
# Loop through all available methods and apply them if requested
|
||||
#
|
||||
for my $f ( sort( keys( %{$filtermethods} ) ) ) {
|
||||
if ( grep( /^$f$/i, @{ $filter->{$t} } ) ) {
|
||||
$newtag = &{ $filtermethods->{$f} }( $tags->{$lc_t} );
|
||||
$changes += changeTag( $tag, $lc_t, $tags->{$lc_t},
|
||||
$newtag, @{ $tagmethods->{$lc_t} } );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: interval
|
||||
# PURPOSE: Convert a 'MM:SS' string into an acceptable PostgreSQL
|
||||
# interval where the minutes portion may exceed 60.
|
||||
# PARAMETERS:
|
||||
# RETURNS: The interval string in the format 'HH:MM:SS'
|
||||
# DESCRIPTION:
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub interval {
|
||||
my ($time) = @_;
|
||||
|
||||
return undef unless $time;
|
||||
|
||||
my $date = new Date::Manip::Delta;
|
||||
unless ( $date->parse($time) ) {
|
||||
return $date->printf("%02hv:%02mv:%02sv");
|
||||
}
|
||||
else {
|
||||
warn "Invalid time $time\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: clean_string
|
||||
# PURPOSE: Clean a string of non-printables, newlines, multiple spaces
|
||||
# PARAMETERS: $str The string to process
|
||||
# RETURNS: The processed string
|
||||
# DESCRIPTION: Removes leading and trailing spaces. Removes all non-printable
|
||||
# characters. Removes all CR/LF sequences. Replaces multiple
|
||||
# spaces with a single space.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub clean_string {
|
||||
my ($str) = @_;
|
||||
|
||||
$str =~ s/(^\s+|\s+$)//g;
|
||||
$str =~ tr/[[:graph:]]//c;
|
||||
$str =~ tr/\x0A\x0D/ /s;
|
||||
$str =~ tr/ \t/ /s;
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: replace_underscores
|
||||
# PURPOSE: Replaces underscores in a string by spaces
|
||||
# PARAMETERS: $str The string to process
|
||||
# RETURNS: The processed string
|
||||
# DESCRIPTION:
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO: N/A
|
||||
#===============================================================================
|
||||
sub replace_underscores {
|
||||
my ($str) = @_;
|
||||
|
||||
$str =~ s/_/ /g;
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: remove_HTML
|
||||
# PURPOSE: Clean a string of HTML tags
|
||||
# PARAMETERS: $str The string to process
|
||||
# RETURNS: The processed string
|
||||
# DESCRIPTION:
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO: N/A
|
||||
#===============================================================================
|
||||
sub remove_HTML {
|
||||
my ($str) = @_;
|
||||
|
||||
my $hr = HTML::Restrict->new();
|
||||
my $processed = $hr->process($str);
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: Options
|
||||
# PURPOSE: Processes command-line options
|
||||
# PARAMETERS: $optref Hash reference to hold the options
|
||||
# RETURNS: Undef
|
||||
# DESCRIPTION:
|
||||
# THROWS: no exceptions
|
||||
# COMMENTS: none
|
||||
# SEE ALSO: n/a
|
||||
#===============================================================================
|
||||
sub Options {
|
||||
my ( $optref, $filter ) = @_;
|
||||
|
||||
my @options = (
|
||||
"help", "album:s", "artist:s", "comment:s",
|
||||
"genre:s", "title:s", "track:s", "year:s",
|
||||
);
|
||||
|
||||
#
|
||||
# Implement '-filter=TAGNAME=FILTERNAME' (from the Getopt::Long manpage)
|
||||
#
|
||||
my %opthash
|
||||
= ( "filter=s%" => sub { push( @{ $filter->{ $_[1] } }, $_[2] ) }, );
|
||||
|
||||
if ( !GetOptions( $optref, @options, %opthash ) ) {
|
||||
pod2usage( -msg => "Version $VERSION\n", -exitval => 1 );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
# Application Documentation
|
||||
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#{{{
|
||||
|
||||
=head1 NAME
|
||||
|
||||
fix_tags - manipulate ID3 tags
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
This documentation refers to I<fix_tags> version 1.3.4
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
fix_tags [ -help ] [-album=ALBUMSTRING] [-artist=ARTISTSTRING]
|
||||
[-comment=COMMENTSTRING] [-genre=GENRESTRING] [-title=TITLESTRING]
|
||||
[-track=TRACKNUMBER] [-year=YEAR] [-filter TAGNAME=FILTERNAME] audio_file ...
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 8
|
||||
|
||||
=item B<-help>
|
||||
|
||||
Prints a brief help message describing the usage of the program, and then exits.
|
||||
|
||||
=item B<-album=ALBUMSTRING>
|
||||
|
||||
Sets the album tag to the string defined by the option. Use B<-album=> to
|
||||
clear the tag.
|
||||
|
||||
=item B<-artist=ARTISTSTRING>
|
||||
|
||||
Sets the artist tag to the string defined by the option. Use B<-artist=> to
|
||||
clear the tag.
|
||||
|
||||
=item B<-comment=COMMENTSTRING>
|
||||
|
||||
Sets the comment tag to the string defined by the option. Use B<-comment=> to
|
||||
clear the tag.
|
||||
|
||||
=item B<-genre=GENRESTRING>
|
||||
|
||||
Sets the genre tag to the string defined by the option. Use B<-genre=> to
|
||||
clear the tag.
|
||||
|
||||
=item B<-title=TITLESTRING>
|
||||
|
||||
Sets the title tag to the string defined by the option. Use B<-title=> to
|
||||
clear the tag.
|
||||
|
||||
=item B<-track=TRACKNUMBER>
|
||||
|
||||
Sets the track tag to the number defined by the option. Use B<-track=> to
|
||||
set the tag to zero.
|
||||
|
||||
=item B<-year=YEAR>
|
||||
|
||||
Sets the year tag to the number defined by the option. Use B<-year=> to
|
||||
set the tag to zero.
|
||||
|
||||
=item B<-filter TAGNAME=FILTERNAME> or B<-filter=TAGNAME=FILTERNAME>
|
||||
|
||||
This option provides an interface to the filtering capability of the script.
|
||||
Here B<TAGNAME> denotes the full name of one of the text tags (album, artist,
|
||||
comment, genre or title - not track or year), and B<FILTERNAME> is one of the
|
||||
built-in filters:
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<clean>
|
||||
|
||||
The tag string has leading and trailing spaces removed. Multiple internal
|
||||
spaces and tabs are replaced by a single space. CR/LF sequences are removed,
|
||||
as are all non-graphic characters.
|
||||
|
||||
=item B<underscore>
|
||||
|
||||
All underscores in the tag string are replaced by spaces. No space compression
|
||||
takes place.
|
||||
|
||||
=item B<HTML>
|
||||
|
||||
The tag string is fed through the I<HTML::Restrict> module and all HTML tags
|
||||
are removed.
|
||||
|
||||
=back
|
||||
|
||||
Neither the B<TAGNAME> nor the B<FILTERNAME> parts of the option may be
|
||||
abbreviated, but neither is case-sensitive.
|
||||
|
||||
Multiple filters may be specified by repeating the complete option sequence.
|
||||
For example:
|
||||
|
||||
fix_tags -filter comment=clean -fil comment=underscore FILE
|
||||
|
||||
The script processes the tags specified in the B<-filter> option in alphabetic
|
||||
order, and for a given tag it also processes the filters in alphabetic order.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This script manipulates ID3 tags (or their equivalents) in FLAC, MP3, OGG, SPX
|
||||
and WAV files.
|
||||
|
||||
=head1 DEPENDENCIES
|
||||
|
||||
Audio::TagLib
|
||||
Data::Dumper
|
||||
Date::Manip::Delta
|
||||
Date::Manip::TZ
|
||||
File::stat
|
||||
Getopt::Long
|
||||
HTML::Restrict
|
||||
Pod::Usage
|
||||
TryCatch
|
||||
|
||||
|
||||
=head1 BUGS AND LIMITATIONS
|
||||
|
||||
There are no known bugs in this module.
|
||||
Please report problems to Dave Morriss (Dave.Morriss@gmail.com)
|
||||
Patches are welcome.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Dave Morriss (Dave.Morriss@gmail.com) 2011, 2012, 2013, 2014
|
||||
|
||||
=head1 LICENCE AND COPYRIGHT
|
||||
|
||||
Copyright (c) Dave Morriss (Dave.Morriss@gmail.com). All rights reserved.
|
||||
|
||||
This program is free software. You can redistribute it and/or modify it under
|
||||
the same terms as perl itself.
|
||||
|
||||
=cut
|
||||
|
||||
#}}}
|
||||
|
||||
# [zo to open fold, zc to close]
|
||||
|
||||
# vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker
|
BIN
workflow/hpr-logo.jpg
Normal file
BIN
workflow/hpr-logo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
268
workflow/hprid
Executable file
268
workflow/hprid
Executable file
@ -0,0 +1,268 @@
|
||||
#!/bin/bash
|
||||
# hprid
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# script to prepare audio files for HPR shows
|
||||
#
|
||||
# input: mp3 or ogg file
|
||||
# result: mp3, ogg in 44100 Hz, spx files 16000Hz with intro and outro
|
||||
# provides 3 interactive checks for audio quality, intro and outro
|
||||
#
|
||||
################################################################################
|
||||
# This file is part of the HPR Tool set
|
||||
#
|
||||
# HPR Tool set is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# HPR Tool set 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with HPR Tool set. If not, see <http://www.gnu.org/licenses/>.
|
||||
# http://www.gnu.org/licenses/agpl-3.0.html
|
||||
# #########################################################################
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# PREREQUISITS
|
||||
# - current folder has to be writable
|
||||
# - there should NOT be a temp.ogg or temp.mp3 file
|
||||
# - intro.mp3 and outro.mp3 have to be present
|
||||
# - IMPORTANT: sox compiled with mp3 support
|
||||
# see http://a0u.xanga.com/700438974/howto-soc-installation
|
||||
#
|
||||
#
|
||||
# IMPORTANT
|
||||
# Backup the files before feeding them to this script, no guarantees here
|
||||
# Handling of .wav not yet tested, but it should work
|
||||
#
|
||||
# code.cruncher, May 2011
|
||||
#
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# TODO
|
||||
#
|
||||
# test handling of wav files
|
||||
# add play final files or open them in specific player(s)
|
||||
# add handling of ID3 tags
|
||||
# create html interface for standardized info gathering
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
#============================================================
|
||||
# Check input
|
||||
usage="usage: $(basename $0 ) [ -i ] [ -o ] <fname>, -i to add intro and -o outro, fname is a file with audio for HPR"
|
||||
|
||||
CHANNELS="1"
|
||||
ADDINTRO="n"
|
||||
ADDOUTRO="n"
|
||||
|
||||
|
||||
while getopts "io" opt; do
|
||||
case $opt in
|
||||
i )
|
||||
ADDINTRO="y"
|
||||
;;
|
||||
o )
|
||||
ADDOUTRO="y"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
# if not ${mediafile} return usage
|
||||
if [ $# -lt 1 ]; then
|
||||
echo $usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mediafile=${1}
|
||||
|
||||
# test if file exists
|
||||
if [ ! -f "intro.flac" ]; then
|
||||
echo "sorry, file \"intro.flac\" does not exist"
|
||||
echo "To download it run the command:"
|
||||
echo " wget http://hackerpublicradio.org/media/theme-music/intro.flac"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "outro.flac" ]; then
|
||||
echo "sorry, file \"outro.flac\" does not exist"
|
||||
echo "To download it run the command:"
|
||||
echo " wget http://hackerpublicradio.org/media/theme-music/outro.flac"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for mediafile in "$@"
|
||||
do
|
||||
echo $var
|
||||
|
||||
if [ ! -f "${mediafile}" ]; then
|
||||
echo "sorry, file \"${mediafile}\" does not exist"
|
||||
continue
|
||||
fi
|
||||
|
||||
# test if file exists
|
||||
if [ ! -r "${mediafile}" ]; then
|
||||
echo "sorry, file \"${mediafile}\" is not readable"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ $(ffprobe "${mediafile}" 2>&1 | grep "Audio:" | wc -l ) -eq 0 ]; then
|
||||
echo "sorry, file \"${mediafile}\" has no audio track"
|
||||
continue
|
||||
fi
|
||||
|
||||
# extract file name and extension
|
||||
fname=${mediafile%.*}
|
||||
ext=${mediafile/*./}
|
||||
|
||||
#Make a backup
|
||||
# mediafilebackup=${mediafile}_$(md5sum ${mediafile} | cut -c -32 )_orig.${ext}
|
||||
# cp -v ${mediafile} ${mediafilebackup}
|
||||
#
|
||||
# if [[ ! -e ${mediafilebackup} ]]; then
|
||||
# echo "Backup not made: ${mediafilebackup}"
|
||||
# exit
|
||||
# fi
|
||||
|
||||
# check audio quality
|
||||
dur=7 # playtime of sample in seconds
|
||||
go=1 # variable to repeat playing of sample
|
||||
from=180 # start sample at 3 minutes in
|
||||
|
||||
#============================================================
|
||||
# Question Time
|
||||
|
||||
# # # while [ $go -ne 0 ]
|
||||
# # # do
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "1/4 AUDIO TEST: check audio quality: ... playing $dur seconds ..."
|
||||
# # # play "${mediafile}" trim $from $dur
|
||||
# # # ((from+=180)) # next sample will be 3 minutes later
|
||||
# # # read -s -n1 -p "sound quality ok?[y,n] ... or play another sample[a] ... [y,n,a]"
|
||||
# # # echo
|
||||
# # # case "$REPLY" in
|
||||
# # # n) echo "aborting ... get better quality sound file ... good bye!"; exit 0;;
|
||||
# # # y) go=0;;
|
||||
# # # esac
|
||||
# # # done
|
||||
# # #
|
||||
# # # # Check for intro
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "2/4 ADDINTRO TEST: Is the intro playing? "
|
||||
# # # play "${mediafile}" trim 1 5 # play 5 seconds at beginning of file
|
||||
# # # read -s -n1 -p "Is there a intro? [y, n]" -i "n"; echo
|
||||
# # # if [ "$REPLY" = 'y' ]; then
|
||||
# # # echo "Will add the intro"
|
||||
# # # ADDINTRO="y"
|
||||
# # # fi
|
||||
# # #
|
||||
# # # # Check for outro
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "3/4 ADDOUTRO TEST: Is the outro playing? "
|
||||
# # # len=$(eval "soxi -D \"${mediafile}\"")
|
||||
# # # len=$(echo "scale=0; $len - 50" | bc)
|
||||
# # # play "${mediafile}" trim $len 5
|
||||
# # # read -s -n1 -p "Is there a outro ? [y, n]" -i "n"; echo
|
||||
# # # if [ "$REPLY" = 'y' ]; then
|
||||
# # # echo "Will add the outro"
|
||||
# # # ADDOUTRO="y"
|
||||
# # # fi
|
||||
# # #
|
||||
# # #
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "4/4 STEREO TEST: Should this be mono or stereo [m,s] ? "
|
||||
# # # CHANNELS="1"
|
||||
# # # read -s -n1 -p "intro ok? [m, s]" -i "m" ; echo
|
||||
# # # if [ "$REPLY" = 's' ]; then
|
||||
# # # echo "Will convert to stereo"
|
||||
# # # CHANNELS="2"
|
||||
# # # fi
|
||||
|
||||
#============================================================
|
||||
# Preprocess the source file
|
||||
|
||||
echo "Convert from ${mediafile} to known wav format ${fname}_tmp.wav"
|
||||
ffmpeg -i ${mediafile} -ar 44100 -ac $CHANNELS ${fname}_tmp.wav > ${fname}_tmp.log 2>&1
|
||||
|
||||
# echo "Normalising the audio"
|
||||
# normalize -a 0.5 ${fname}_tmp.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
# TODO Compressor !
|
||||
|
||||
# TODO add a little speed up
|
||||
|
||||
# TODO little overlap in fade in of intro
|
||||
|
||||
# echo "Truncating the silence"
|
||||
# sox ${fname}_tmp.wav ${fname}_sox.wav silence -l 1 0.1 1.6% -1 0.6 1.6% >> ${fname}_tmp.log 2>&1
|
||||
cp -v ${fname}_tmp.wav ${fname}_sox.wav
|
||||
|
||||
echo "Add the intro if it is missing and add it to the temp pcm file"
|
||||
if [ "$ADDINTRO" = 'y' ]; then
|
||||
ffmpeg -i intro.flac -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - > ${fname}_tmp.pcm 2>> ${fname}_tmp.log
|
||||
fi
|
||||
|
||||
echo "convert the uploaded episode and add it to the temp pcm file"
|
||||
ffmpeg -i ${fname}_sox.wav -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp.pcm 2>> ${fname}_tmp.log
|
||||
|
||||
echo "Add the outro if it is missing and add it to the temp pcm file"
|
||||
if [ "$ADDOUTRO" = 'y' ]; then
|
||||
ffmpeg -i outro.flac -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp.pcm 2>> ${fname}_tmp.log
|
||||
fi
|
||||
|
||||
echo "Convert the pcm file to a know wav format"
|
||||
ffmpeg -f s16le -ar 44100 -ac 1 -acodec pcm_s16le -i ${fname}_tmp.pcm ${fname}_mez.wav 2>> ${fname}_tmp.log
|
||||
|
||||
# echo "Get an image of the converted audio"
|
||||
# sox ${fname}_mez.wav -n spectrogram -x 800 -y 100 -o ${fname}_mez.png
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "File information"
|
||||
ffprobe ${fname}_mez.wav 2>&1 | grep Audio:
|
||||
mediainfo ${fname}_mez.wav
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
# display ${fname}_mez.png &
|
||||
# read -s -n1 -p "Spectrogram check: Everything look ok [y,n] ? " -i "y" ; echo
|
||||
# if [ "$REPLY" = 'n' ]; then
|
||||
# echo "Something went w rong. Aborting."
|
||||
# exit
|
||||
# fi
|
||||
|
||||
# echo "--------------------------------------------------------------------------------"
|
||||
# vlc ${fname}_mez.wav >> ${fname}_tmp.log 2>&1 &
|
||||
# read -s -n1 -p "VLC check: Everything look ok [y,n] ? " -i "y" ; echo
|
||||
# if [ "$REPLY" = 'n' ]; then
|
||||
# echo "Something went wrong. Aborting."
|
||||
# exit
|
||||
# fi
|
||||
|
||||
echo "Remove temp files"
|
||||
rm -v ${fname}_tmp.wav ${fname}_sox.wav ${fname}_tmp.pcm ${fname}_tmp.log
|
||||
|
||||
echo "Convert to mp3" # TODO and add tags"
|
||||
sox -S ${fname}_mez.wav ${fname}_mez.mp3
|
||||
|
||||
echo "Convert to ogg" # TODO and add tags"
|
||||
sox -S ${fname}_mez.wav ${fname}_mez.ogg
|
||||
|
||||
echo "Convert to spx" # TODO and add tags"
|
||||
sox -S ${fname}_mez.wav -c 1 -r 16000 -t wav - | speexenc - ${fname}_mez.spx
|
||||
|
||||
echo "Changing the file dates to the time of upload"
|
||||
touch -r ${mediafile} ${fname}*
|
||||
done
|
268
workflow/hprid.sh
Executable file
268
workflow/hprid.sh
Executable file
@ -0,0 +1,268 @@
|
||||
#!/bin/bash
|
||||
# hprid
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# script to prepare audio files for HPR shows
|
||||
#
|
||||
# input: mp3 or ogg file
|
||||
# result: mp3, ogg in 44100 Hz, spx files 16000Hz with intro and outro
|
||||
# provides 3 interactive checks for audio quality, intro and outro
|
||||
#
|
||||
################################################################################
|
||||
# This file is part of the HPR Tool set
|
||||
#
|
||||
# HPR Tool set is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# HPR Tool set 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with HPR Tool set. If not, see <http://www.gnu.org/licenses/>.
|
||||
# http://www.gnu.org/licenses/agpl-3.0.html
|
||||
# #########################################################################
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# PREREQUISITS
|
||||
# - current folder has to be writable
|
||||
# - there should NOT be a temp.ogg or temp.mp3 file
|
||||
# - intro.mp3 and outro.mp3 have to be present
|
||||
# - IMPORTANT: sox compiled with mp3 support
|
||||
# see http://a0u.xanga.com/700438974/howto-soc-installation
|
||||
#
|
||||
#
|
||||
# IMPORTANT
|
||||
# Backup the files before feeding them to this script, no guarantees here
|
||||
# Handling of .wav not yet tested, but it should work
|
||||
#
|
||||
# code.cruncher, May 2011
|
||||
#
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# TODO
|
||||
#
|
||||
# test handling of wav files
|
||||
# add play final files or open them in specific player(s)
|
||||
# add handling of ID3 tags
|
||||
# create html interface for standardized info gathering
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
#============================================================
|
||||
# Check input
|
||||
usage="usage: $(basename $0 ) [ -i ] [ -o ] <fname>, -i to add intro and -o outro, fname is a file with audio for HPR"
|
||||
|
||||
CHANNELS="1"
|
||||
ADDINTRO="n"
|
||||
ADDOUTRO="n"
|
||||
|
||||
|
||||
while getopts "io" opt; do
|
||||
case $opt in
|
||||
i )
|
||||
ADDINTRO="y"
|
||||
;;
|
||||
o )
|
||||
ADDOUTRO="y"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
# if not ${mediafile} return usage
|
||||
if [ $# -lt 1 ]; then
|
||||
echo $usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mediafile=${1}
|
||||
|
||||
# test if file exists
|
||||
if [ ! -f "intro.flac" ]; then
|
||||
echo "sorry, file \"intro.flac\" does not exist"
|
||||
echo "To download it run the command:"
|
||||
echo " wget http://hackerpublicradio.org/media/theme-music/intro.flac"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "outro.flac" ]; then
|
||||
echo "sorry, file \"outro.flac\" does not exist"
|
||||
echo "To download it run the command:"
|
||||
echo " wget http://hackerpublicradio.org/media/theme-music/outro.flac"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for mediafile in "$@"
|
||||
do
|
||||
echo $var
|
||||
|
||||
if [ ! -f "${mediafile}" ]; then
|
||||
echo "sorry, file \"${mediafile}\" does not exist"
|
||||
continue
|
||||
fi
|
||||
|
||||
# test if file exists
|
||||
if [ ! -r "${mediafile}" ]; then
|
||||
echo "sorry, file \"${mediafile}\" is not readable"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ $(ffprobe "${mediafile}" 2>&1 | grep "Audio:" | wc -l ) -eq 0 ]; then
|
||||
echo "sorry, file \"${mediafile}\" has no audio track"
|
||||
continue
|
||||
fi
|
||||
|
||||
# extract file name and extension
|
||||
fname=${mediafile%.*}
|
||||
ext=${mediafile/*./}
|
||||
|
||||
#Make a backup
|
||||
# mediafilebackup=${mediafile}_$(md5sum ${mediafile} | cut -c -32 )_orig.${ext}
|
||||
# cp -v ${mediafile} ${mediafilebackup}
|
||||
#
|
||||
# if [[ ! -e ${mediafilebackup} ]]; then
|
||||
# echo "Backup not made: ${mediafilebackup}"
|
||||
# exit
|
||||
# fi
|
||||
|
||||
# check audio quality
|
||||
dur=7 # playtime of sample in seconds
|
||||
go=1 # variable to repeat playing of sample
|
||||
from=180 # start sample at 3 minutes in
|
||||
|
||||
#============================================================
|
||||
# Question Time
|
||||
|
||||
# # # while [ $go -ne 0 ]
|
||||
# # # do
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "1/4 AUDIO TEST: check audio quality: ... playing $dur seconds ..."
|
||||
# # # play "${mediafile}" trim $from $dur
|
||||
# # # ((from+=180)) # next sample will be 3 minutes later
|
||||
# # # read -s -n1 -p "sound quality ok?[y,n] ... or play another sample[a] ... [y,n,a]"
|
||||
# # # echo
|
||||
# # # case "$REPLY" in
|
||||
# # # n) echo "aborting ... get better quality sound file ... good bye!"; exit 0;;
|
||||
# # # y) go=0;;
|
||||
# # # esac
|
||||
# # # done
|
||||
# # #
|
||||
# # # # Check for intro
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "2/4 ADDINTRO TEST: Is the intro playing? "
|
||||
# # # play "${mediafile}" trim 1 5 # play 5 seconds at beginning of file
|
||||
# # # read -s -n1 -p "Is there a intro? [y, n]" -i "n"; echo
|
||||
# # # if [ "$REPLY" = 'y' ]; then
|
||||
# # # echo "Will add the intro"
|
||||
# # # ADDINTRO="y"
|
||||
# # # fi
|
||||
# # #
|
||||
# # # # Check for outro
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "3/4 ADDOUTRO TEST: Is the outro playing? "
|
||||
# # # len=$(eval "soxi -D \"${mediafile}\"")
|
||||
# # # len=$(echo "scale=0; $len - 50" | bc)
|
||||
# # # play "${mediafile}" trim $len 5
|
||||
# # # read -s -n1 -p "Is there a outro ? [y, n]" -i "n"; echo
|
||||
# # # if [ "$REPLY" = 'y' ]; then
|
||||
# # # echo "Will add the outro"
|
||||
# # # ADDOUTRO="y"
|
||||
# # # fi
|
||||
# # #
|
||||
# # #
|
||||
# # # echo
|
||||
# # # echo "--------------------------------------------------------------------------------"
|
||||
# # # echo "4/4 STEREO TEST: Should this be mono or stereo [m,s] ? "
|
||||
# # # CHANNELS="1"
|
||||
# # # read -s -n1 -p "intro ok? [m, s]" -i "m" ; echo
|
||||
# # # if [ "$REPLY" = 's' ]; then
|
||||
# # # echo "Will convert to stereo"
|
||||
# # # CHANNELS="2"
|
||||
# # # fi
|
||||
|
||||
#============================================================
|
||||
# Preprocess the source file
|
||||
|
||||
echo "Convert from ${mediafile} to known wav format ${fname}_tmp.wav"
|
||||
ffmpeg -i ${mediafile} -ar 44100 -ac $CHANNELS ${fname}_tmp.wav > ${fname}_tmp.log 2>&1
|
||||
|
||||
# echo "Normalising the audio"
|
||||
# normalize -a 0.5 ${fname}_tmp.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
# TODO Compressor !
|
||||
|
||||
# TODO add a little speed up
|
||||
|
||||
# TODO little overlap in fade in of intro
|
||||
|
||||
# echo "Truncating the silence"
|
||||
# sox ${fname}_tmp.wav ${fname}_sox.wav silence -l 1 0.1 1.6% -1 0.6 1.6% >> ${fname}_tmp.log 2>&1
|
||||
cp -v ${fname}_tmp.wav ${fname}_sox.wav
|
||||
|
||||
echo "Add the intro if it is missing and add it to the temp pcm file"
|
||||
if [ "$ADDINTRO" = 'y' ]; then
|
||||
ffmpeg -i intro.flac -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - > ${fname}_tmp.pcm 2>> ${fname}_tmp.log
|
||||
fi
|
||||
|
||||
echo "convert the uploaded episode and add it to the temp pcm file"
|
||||
ffmpeg -i ${fname}_sox.wav -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp.pcm 2>> ${fname}_tmp.log
|
||||
|
||||
echo "Add the outro if it is missing and add it to the temp pcm file"
|
||||
if [ "$ADDOUTRO" = 'y' ]; then
|
||||
ffmpeg -i outro.flac -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp.pcm 2>> ${fname}_tmp.log
|
||||
fi
|
||||
|
||||
echo "Convert the pcm file to a know wav format"
|
||||
ffmpeg -f s16le -ar 44100 -ac 1 -acodec pcm_s16le -i ${fname}_tmp.pcm ${fname}_mez.wav 2>> ${fname}_tmp.log
|
||||
|
||||
# echo "Get an image of the converted audio"
|
||||
# sox ${fname}_mez.wav -n spectrogram -x 800 -y 100 -o ${fname}_mez.png
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "File information"
|
||||
ffprobe ${fname}_mez.wav 2>&1 | grep Audio:
|
||||
mediainfo ${fname}_mez.wav
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
# display ${fname}_mez.png &
|
||||
# read -s -n1 -p "Spectrogram check: Everything look ok [y,n] ? " -i "y" ; echo
|
||||
# if [ "$REPLY" = 'n' ]; then
|
||||
# echo "Something went w rong. Aborting."
|
||||
# exit
|
||||
# fi
|
||||
|
||||
# echo "--------------------------------------------------------------------------------"
|
||||
# vlc ${fname}_mez.wav >> ${fname}_tmp.log 2>&1 &
|
||||
# read -s -n1 -p "VLC check: Everything look ok [y,n] ? " -i "y" ; echo
|
||||
# if [ "$REPLY" = 'n' ]; then
|
||||
# echo "Something went wrong. Aborting."
|
||||
# exit
|
||||
# fi
|
||||
|
||||
echo "Remove temp files"
|
||||
rm -v ${fname}_tmp.wav ${fname}_sox.wav ${fname}_tmp.pcm ${fname}_tmp.log
|
||||
|
||||
echo "Convert to mp3" # TODO and add tags"
|
||||
sox -S ${fname}_mez.wav ${fname}_mez.mp3
|
||||
|
||||
echo "Convert to ogg" # TODO and add tags"
|
||||
sox -S ${fname}_mez.wav ${fname}_mez.ogg
|
||||
|
||||
echo "Convert to spx" # TODO and add tags"
|
||||
sox -S ${fname}_mez.wav -c 1 -r 16000 -t wav - | speexenc - ${fname}_mez.spx
|
||||
|
||||
echo "Changing the file dates to the time of upload"
|
||||
touch -r ${mediafile} ${fname}*
|
||||
done
|
422
workflow/hprtranscode-archive.bash
Normal file
422
workflow/hprtranscode-archive.bash
Normal file
@ -0,0 +1,422 @@
|
||||
#!/bin/bash
|
||||
# Copyright Ken Fallon - Released into the public domain. http://creativecommons.org/publicdomain/
|
||||
#============================================================
|
||||
# Check input
|
||||
usage="usage: $(basename $0 ) [ -i ] [ -o ] <fname>, -i to add intro and -o outro, fname is a file with audio for HPR"
|
||||
|
||||
# Argument = -t test -r server -p password -v
|
||||
|
||||
echoerr()
|
||||
{
|
||||
echo "$@" 1>&2;
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
cat << EOF
|
||||
usage: $0 [options] {media file to encode} {episode number}
|
||||
|
||||
OPTIONS:
|
||||
-h Show this message
|
||||
-p add a promo
|
||||
-i add the intro
|
||||
-s Automatically generate the summary
|
||||
-o add the outro
|
||||
-b add the intro and outro ( and summary default)
|
||||
-2 encode to 2 channels
|
||||
-n no audio normalization
|
||||
-x eXclude sponsor mention
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
TEMP_DIR="/var/tmp/"
|
||||
CHANNELS="1"
|
||||
FIXAUDIO="1"
|
||||
ADDSUMMARY="n"
|
||||
ADDINTRO="n"
|
||||
ADDPROMO="n"
|
||||
ADDOUTRO="n"
|
||||
ADDSPONSOR="y"
|
||||
ARTIST="EMPTY"
|
||||
TITLE="EMPTY"
|
||||
YEAR="EMPTY"
|
||||
SLOT="EMPTY"
|
||||
basedir="/var/IA"
|
||||
intro="${basedir}/intro.flac"
|
||||
promo="${basedir}/promo.flac"
|
||||
outro="${basedir}/outro.flac"
|
||||
anhonesthost="${basedir}/sponsor-anhonesthost.com-hpr15.flac"
|
||||
internetarchive="${basedir}/sponsor-archive.org.flac"
|
||||
|
||||
while getopts "hipsob2nx" OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
h)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
s)
|
||||
ADDSUMMARY="y"
|
||||
;;
|
||||
i)
|
||||
ADDINTRO="y"
|
||||
;;
|
||||
p)
|
||||
ADDPROMO="y"
|
||||
;;
|
||||
o)
|
||||
ADDOUTRO="y"
|
||||
;;
|
||||
b)
|
||||
ADDINTRO="y"
|
||||
ADDOUTRO="y"
|
||||
;;
|
||||
2)
|
||||
CHANNELS="2"
|
||||
;;
|
||||
n)
|
||||
FIXAUDIO="0"
|
||||
;;
|
||||
x)
|
||||
ADDSPONSOR="n"
|
||||
;;
|
||||
?)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echoerr "Please enter the source file and episode number"
|
||||
exit
|
||||
fi
|
||||
|
||||
mediafile=${1}
|
||||
ep_num=${2}
|
||||
|
||||
ep_num=$(echo $ep_num | sed 's/hpr//g')
|
||||
re='^[0-9]+$'
|
||||
if ! [[ $ep_num =~ $re ]] ; then
|
||||
echoerr "error: episode \"${ep_num}\" is not a number"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${anhonesthost}" ]; then
|
||||
echoerr "sorry, file \"${anhonesthost}\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${internetarchive}" ]; then
|
||||
echoerr "sorry, file \"${internetarchive}\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${intro}" ]; then
|
||||
echoerr "sorry, file \"intro.flac\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$ADDPROMO" = 'y' ]; then
|
||||
if [ ! -f "${promo}" ]; then
|
||||
echoerr "sorry, file \"promo.flac\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "${outro}" ]; then
|
||||
echoerr "sorry, file \"outro.flac\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${mediafile}" ]; then
|
||||
echoerr "sorry, media file \"${mediafile}\" does not exist"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -r "${mediafile}" ]; then
|
||||
echoerr "sorry, media file \"${mediafile}\" is not readable"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ $(ffprobe "${mediafile}" 2>&1 | grep "Audio:" | wc -l ) -eq 0 ]; then
|
||||
echoerr "sorry, media file \"${mediafile}\" has no audio track"
|
||||
exit
|
||||
fi
|
||||
|
||||
# extract file name and extension
|
||||
media_dir=$(dirname ${mediafile})
|
||||
fname=${mediafile%.*}
|
||||
ext=${mediafile/*./}
|
||||
|
||||
if [ "$ADDSUMMARY" = 'n' ]; then
|
||||
if [ ! -e "${media_dir}/summary.wav" ]
|
||||
then
|
||||
echoerr "ERROR: Can not find the summary file \"${media_dir}/summary.wav\""
|
||||
wget -O- --timeout=10 --tries=1 --quiet http://hackerpublicradio.org/say.php?id=${ep_num} | grep HPR_summary
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -e hpr${ep_num}.wav ]] || [[ -e hpr${ep_num}.mp3 ]] || [[ -e hpr${ep_num}.ogg ]] || [[ -e hpr${ep_num}.spx ]] || [[ -e hpr${ep_num}_summary.wav ]] || [[ -e ${fname}_mez_norm.wav ]] || [[ -e ${fname}_mez.wav ]] || [[ -e ${fname}_sox_norm.wav ]] || [[ -e ${fname}_sox.wav ]] || [[ -e ${fname}_tmp_hh.pcm ]] || [[ -e ${fname}_tmp_ia.pcm ]] || [[ -e ${fname}_tmp.log ]]
|
||||
then
|
||||
echoerr "Files for this episode already exist."
|
||||
ls -1 hpr${ep_num}* ${fname}_* 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Geting metadata for hpr${ep_num}"
|
||||
|
||||
while read -r line
|
||||
do
|
||||
field=$(echo $line | awk -F ':' '{print $1}')
|
||||
case $field in
|
||||
"HPR_summary")
|
||||
HPR_summary=$(echo $line | grep "HPR_summary: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_album")
|
||||
HPR_album=$(echo $line | grep "HPR_album: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_artist")
|
||||
HPR_artist=$(echo $line | grep "HPR_artist: " | cut -c 13- )
|
||||
continue
|
||||
;;
|
||||
"HPR_comment")
|
||||
HPR_comment=$(echo $line | grep "HPR_comment: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_genre")
|
||||
HPR_genre=$(echo $line | grep "HPR_genre: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_title")
|
||||
HPR_title=$(echo $line | grep "HPR_title: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_track")
|
||||
HPR_track=$(echo $line | grep "HPR_track: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_year")
|
||||
HPR_year=$(echo $line | grep "HPR_year: " | cut -c 11- )
|
||||
continue
|
||||
;;
|
||||
"HPR_duration")
|
||||
HPR_duration=$(echo $line | grep "HPR_duration: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_explicit")
|
||||
HPR_explicit=$(echo $line | grep "HPR_explicit: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_license")
|
||||
HPR_license=$(echo $line | grep "HPR_license: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
done < <( wget --timeout=10 --tries=1 --quiet http://hackerpublicradio.org/say.php?id=${ep_num} -O - )
|
||||
|
||||
if [[ -z "$ADDINTRO" || -z "$ADDPROMO" || -z "$ADDSUMMARY" || -z "$ADDOUTRO" || -z "$HPR_album" || -z "$HPR_artist" || -z "$HPR_comment" || -z "$HPR_genre" || -z "$HPR_title" || -z "$HPR_track" || -z "$HPR_year" || -z "$HPR_summary" || -z "$HPR_duration" || -z "$HPR_explicit" || -z "$HPR_license" ]]
|
||||
then
|
||||
echoerr "Could not find information on ${ep_num}. Has the show been posted ?"
|
||||
exit;
|
||||
fi
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Add intro : $ADDINTRO"
|
||||
echo "Add promo : $ADDPROMO"
|
||||
echo "Add Summary : $ADDSUMMARY"
|
||||
echo "Add outro : $ADDOUTRO"
|
||||
echo "album : $HPR_album"
|
||||
echo "artist : $HPR_artist"
|
||||
echo "comment : $HPR_comment"
|
||||
echo "genre : $HPR_genre"
|
||||
echo "title : $HPR_title"
|
||||
echo "track : $HPR_track"
|
||||
echo "year : $HPR_year"
|
||||
echo "summary : $HPR_summary"
|
||||
echo "duration : $HPR_duration"
|
||||
echo "explicit : $HPR_explicit"
|
||||
echo "license : $HPR_license"
|
||||
|
||||
if [[ $HPR_duration == "0" ]]
|
||||
then
|
||||
echoerr "The duration is set to 0. Please update the show with the correct time."
|
||||
exit;
|
||||
fi
|
||||
|
||||
#============================================================
|
||||
# Preproc`s the source file
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Prepare mezzanine file"
|
||||
ffmpeg -i ${mediafile} -ar 44100 -ac $CHANNELS ${fname}_sox.wav > ${fname}_tmp.log 2>&1
|
||||
|
||||
if [ "$FIXAUDIO" = "1" ];then
|
||||
echo "normalizing audio"
|
||||
sox --temp "${TEMP_DIR}" --norm ${fname}_sox.wav ${fname}_sox_norm.wav
|
||||
mv -v ${fname}_sox_norm.wav ${fname}_sox.wav >> ${fname}_tmp.log 2>&1
|
||||
# normalize -a 0.5 ${fname}_sox.wav >> ${fname}_tmp.log 2>&1
|
||||
fi
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Add HPR Branding"
|
||||
|
||||
if [ "$ADDSUMMARY" = 'y' ]; then
|
||||
echo "Creating the summary"
|
||||
#echo "$HPR_summary" - | espeak -w hpr${ep_num}_summary.wav
|
||||
echo "$HPR_summary" - | text2wave - -o hpr${ep_num}_summary.wav #festival --tts
|
||||
#echo "${HPR_summary}" | gtts-cli - --output hpr${ep_num}_summary.mp3
|
||||
#ffmpeg -i hpr${ep_num}_summary.mp3 hpr${ep_num}_summary.wav
|
||||
#rm -v hpr${ep_num}_summary.mp3
|
||||
else
|
||||
echo "Copying the supplied summary"
|
||||
if [ ! -e "${media_dir}/summary.wav" ]
|
||||
then
|
||||
echoerr "ERROR: Can not find the summary file \"${media_dir}/summary.wav\""
|
||||
exit 1
|
||||
fi
|
||||
cp -v "${media_dir}/summary.wav" hpr${ep_num}_summary.wav
|
||||
fi
|
||||
|
||||
ffmpeg -i hpr${ep_num}_summary.wav -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_hh.pcm 2>> ${fname}_tmp.log
|
||||
ffmpeg -i hpr${ep_num}_summary.wav -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_ia.pcm 2>> ${fname}_tmp.log
|
||||
rm hpr${ep_num}_summary.wav
|
||||
|
||||
if [ "$ADDSPONSOR" = 'y' ]; then
|
||||
echo "Adding the sponsor"
|
||||
ffmpeg -i "$anhonesthost" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_hh.pcm 2>> ${fname}_tmp.log
|
||||
ffmpeg -i "$internetarchive" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_ia.pcm 2>> ${fname}_tmp.log
|
||||
else
|
||||
echo "NOT Adding the sponsor"
|
||||
fi
|
||||
|
||||
if [ "$ADDPROMO" = 'y' ]; then
|
||||
echo "Adding the promo"
|
||||
ffmpeg -i "$promo" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_hh.pcm 2>> ${fname}_tmp.log
|
||||
ffmpeg -i "$promo" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_ia.pcm 2>> ${fname}_tmp.log
|
||||
fi
|
||||
|
||||
if [ "$ADDINTRO" = 'y' ]; then
|
||||
echo "Adding the intro"
|
||||
ffmpeg -i "$intro" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_hh.pcm 2>> ${fname}_tmp.log
|
||||
ffmpeg -i "$intro" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_ia.pcm 2>> ${fname}_tmp.log
|
||||
fi
|
||||
|
||||
echo "convert the uploaded episode and add it to the temp pcm file"
|
||||
ffmpeg -i ${fname}_sox.wav -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_hh.pcm 2>> ${fname}_tmp.log
|
||||
ffmpeg -i ${fname}_sox.wav -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_ia.pcm 2>> ${fname}_tmp.log
|
||||
|
||||
if [ "$ADDOUTRO" = 'y' ]; then
|
||||
echo "Adding the outro"
|
||||
ffmpeg -i "$outro" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_hh.pcm 2>> ${fname}_tmp.log
|
||||
ffmpeg -i "$outro" -ar 44100 -ac $CHANNELS -acodec pcm_s16le -f s16le - >> ${fname}_tmp_ia.pcm 2>> ${fname}_tmp.log
|
||||
fi
|
||||
|
||||
echo "Convert the pcm file to a know wav format"
|
||||
ffmpeg -f s16le -ar 44100 -ac $CHANNELS -acodec pcm_s16le -i ${fname}_tmp_hh.pcm ${fname}_mez.wav 2>> ${fname}_tmp.log
|
||||
ffmpeg -f s16le -ar 44100 -ac $CHANNELS -acodec pcm_s16le -i ${fname}_tmp_ia.pcm hpr${ep_num}.wav 2>> ${fname}_tmp.log
|
||||
|
||||
echo "Normalizing the wav files"
|
||||
sox --temp "${TEMP_DIR}" --norm ${fname}_mez.wav ${fname}_mez_norm.wav
|
||||
mv -v ${fname}_mez_norm.wav ${fname}_mez.wav >> ${fname}_tmp.log 2>&1
|
||||
# normalize -a 0.5 ${fname}_mez.wav >> ${fname}_tmp.log 2>&1
|
||||
sox --temp "${TEMP_DIR}" --norm hpr${ep_num}.wav hpr${ep_num}_norm.wav
|
||||
mv -v hpr${ep_num}_norm.wav /var/IA/uploads/hpr${ep_num}.wav >> ${fname}_tmp.log 2>&1
|
||||
# normalize -a 0.5 hpr${ep_num}.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "File information"
|
||||
ffprobe ${fname}_mez.wav 2>&1 | grep Audio:
|
||||
mediainfo ${fname}_mez.wav
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to opus for IA"
|
||||
opusenc hpr${ep_num}.wav /var/IA/uploads/hpr${ep_num}.opus
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to flac for IA"
|
||||
sox --temp "${TEMP_DIR}" -S hpr${ep_num}.wav /var/IA/uploads/hpr${ep_num}.flac
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to mp3 for HPR"
|
||||
sox --temp "${TEMP_DIR}" -S ${fname}_mez.wav hpr${ep_num}.mp3
|
||||
echo "Convert to mp3 for IA"
|
||||
sox --temp "${TEMP_DIR}" -S hpr${ep_num}.wav /var/IA/uploads/hpr${ep_num}.mp3
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to ogg for HPR"
|
||||
sox --temp "${TEMP_DIR}" -S ${fname}_mez.wav hpr${ep_num}.ogg
|
||||
echo "Convert to ogg for IA"
|
||||
sox --temp "${TEMP_DIR}" -S hpr${ep_num}.wav /var/IA/uploads/hpr${ep_num}.ogg
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to spx for HPR"
|
||||
sox --temp "${TEMP_DIR}" -S ${fname}_mez.wav -c 1 -r 16000 -t wav - | speexenc - hpr${ep_num}.spx
|
||||
echo "Convert to spx for IA"
|
||||
sox --temp "${TEMP_DIR}" -S hpr${ep_num}.wav -c 1 -r 16000 -t wav - | speexenc - /var/IA/uploads/hpr${ep_num}.spx
|
||||
|
||||
|
||||
if [[ ! -s /var/IA/uploads/hpr${ep_num}.wav ]] || [[ ! -s /var/IA/uploads/hpr${ep_num}.mp3 ]] || [[ ! -s /var/IA/uploads/hpr${ep_num}.ogg ]] || [[ ! -s /var/IA/uploads/hpr${ep_num}.spx ]] || [[ ! -s hpr${ep_num}.mp3 ]] || [[ ! -s hpr${ep_num}.ogg ]] || [[ ! -s hpr${ep_num}.spx ]]
|
||||
then
|
||||
echoerr "ERROR: Something went wrong encoding the files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fix_tags -album="$HPR_album" -artist="$HPR_artist" -comment="${HPR_comment} The license is ${HPR_license}" -genre="$HPR_genre" -title="$HPR_title" -track="$HPR_track" -year="$HPR_year" hpr${ep_num}* 2>> ${fname}_tmp.log 1>&2
|
||||
fix_tags -album="$HPR_album" -artist="$HPR_artist" -comment="$HPR_comment" -genre="$HPR_genre" -title="$HPR_title" -track="$HPR_track" -year="$HPR_year" /var/IA/uploads/hpr${ep_num}* 2>> ${fname}_tmp.log 1>&2
|
||||
fix_tags hpr${ep_num}*
|
||||
fix_tags /var/IA/uploads/hpr${ep_num}*
|
||||
|
||||
#echo "Changing the file dates to the time of upload"
|
||||
touch -r ${mediafile} hpr${ep_num}*
|
||||
touch -r ${mediafile} /var/IA/uploads/hpr${ep_num}*
|
||||
|
||||
|
||||
rsync -ave ssh --partial --progress --ignore-existing hpr${ep_num}.mp3 hpr${ep_num}.ogg hpr${ep_num}.spx hpr:www/eps/
|
||||
|
||||
firefox http://hackerpublicradio.org/local/hpr${ep_num}.mp3
|
||||
firefox http://hackerpublicradio.org/local/hpr${ep_num}.ogg
|
||||
firefox file:///var/IA/uploads/hpr${ep_num}.mp3
|
||||
firefox file:///var/IA/uploads/hpr${ep_num}.ogg
|
||||
|
||||
echo "Source: $( mediainfo --Output=XML --Full "${mediafile}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' )"
|
||||
mediainfo --Output=XML --Full hpr${ep_num}* /var/IA/uploads/hpr${ep_num}* | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' | sort | uniq -c
|
||||
|
||||
read -p "Remove files for \"${fname}\" (y|N) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
mediafilename=$(basename "${mediafile}")
|
||||
mediaextension="${mediafilename##*.}"
|
||||
|
||||
ssh hpr -t "mkdir /home/hpr/www/eps/hpr${ep_num}" >/dev/null 2>&1
|
||||
rsync -ave ssh --partial --progress --ignore-existing "${mediafile}" hpr:www/eps/hpr${ep_num}/hpr${ep_num}_source.${mediaextension}
|
||||
ssh hpr -t "ls -al /home/hpr/www/eps/hpr${ep_num}*"
|
||||
cp -v "${mediafile}" "/var/IA/uploads/hpr${ep_num}_source.${mediaextension}"
|
||||
|
||||
#echo "Remove temp files"
|
||||
rm -v ${fname}_sox.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav
|
||||
mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
|
||||
#rsync -ave ssh --partial --progress /var/IA/uploads/ hpr:/home/hpr/upload/processed/
|
||||
rsync -ave ssh --partial --progress /var/IA/uploads/ borg:/data/IA/uploads/
|
||||
find /var/IA/done/ -empty -delete
|
||||
else
|
||||
echo "skipping...."
|
||||
echo "cp -v \"${mediafile}\" \"/var/IA/uploads/hpr${ep_num}_source.${mediaextension}\""
|
||||
echo "rm -v ${fname}_sox.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav"
|
||||
echo "mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "rsync -ave ssh --partial --progress /var/IA/uploads/ borg:/data/IA/uploads/"
|
||||
fi
|
238
workflow/hprtranscode-just-transcode.bash
Executable file
238
workflow/hprtranscode-just-transcode.bash
Executable file
@ -0,0 +1,238 @@
|
||||
#!/bin/bash
|
||||
# Copyright Ken Fallon - Released into the public domain. http://creativecommons.org/publicdomain/
|
||||
#============================================================
|
||||
|
||||
echoerr()
|
||||
{
|
||||
echo "$@" 1>&2;
|
||||
}
|
||||
|
||||
TEMP_DIR="/var/tmp/"
|
||||
CHANNELS="1"
|
||||
FIXAUDIO="1"
|
||||
ARTIST="EMPTY"
|
||||
TITLE="EMPTY"
|
||||
YEAR="EMPTY"
|
||||
SLOT="EMPTY"
|
||||
basedir="/var/IA"
|
||||
upload_dir="${basedir}/uploads"
|
||||
intro="${basedir}/short.flac"
|
||||
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echoerr "Please enter the source file and episode number"
|
||||
exit
|
||||
fi
|
||||
|
||||
mediafile=${1}
|
||||
ep_num=${2}
|
||||
|
||||
ep_num=$(echo $ep_num | sed 's/hpr//g')
|
||||
re='^[0-9]+$'
|
||||
if ! [[ $ep_num =~ $re ]] ; then
|
||||
echoerr "error: episode \"${ep_num}\" is not a number"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${mediafile}" ]; then
|
||||
echoerr "sorry, media file \"${mediafile}\" does not exist"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -r "${mediafile}" ]; then
|
||||
echoerr "sorry, media file \"${mediafile}\" is not readable"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ $(ffprobe "${mediafile}" 2>&1 | grep "Audio:" | wc -l ) -eq 0 ]; then
|
||||
echoerr "sorry, media file \"${mediafile}\" has no audio track"
|
||||
exit
|
||||
fi
|
||||
|
||||
# extract file name and extension
|
||||
media_dir=$(dirname ${mediafile})
|
||||
fname=${mediafile%.*}
|
||||
ext=${mediafile/*./}
|
||||
|
||||
if [[ -e hpr${ep_num}.wav ]] || [[ -e hpr${ep_num}.mp3 ]] || [[ -e hpr${ep_num}.ogg ]] || [[ -e hpr${ep_num}.spx ]] || [[ -e hpr${ep_num}_summary.wav ]] || [[ -e hpr${ep_num}.wav ]] || [[ -e ${fname}_mez.wav ]] || [[ -e ${fname}_sox_norm.wav ]] || [[ -e ${fname}_mezzanine.wav ]] || [[ -e ${fname}_tmp.pcm ]] || [[ -e ${fname}_tmp.log ]]
|
||||
then
|
||||
echoerr "Files for this episode already exist."
|
||||
ls -1 hpr${ep_num}* ${fname}_* 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Geting metadata for hpr${ep_num}"
|
||||
|
||||
while read -r line
|
||||
do
|
||||
field=$(echo $line | awk -F ':' '{print $1}')
|
||||
case $field in
|
||||
"HPR_summary")
|
||||
HPR_summary=$(echo $line | grep "HPR_summary: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_album")
|
||||
HPR_album=$(echo $line | grep "HPR_album: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_artist")
|
||||
HPR_artist=$(echo $line | grep "HPR_artist: " | cut -c 13- )
|
||||
continue
|
||||
;;
|
||||
"HPR_comment")
|
||||
HPR_comment=$(echo $line | grep "HPR_comment: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_genre")
|
||||
HPR_genre=$(echo $line | grep "HPR_genre: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_title")
|
||||
HPR_title=$(echo $line | grep "HPR_title: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_track")
|
||||
HPR_track=$(echo $line | grep "HPR_track: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_year")
|
||||
HPR_year=$(echo $line | grep "HPR_year: " | cut -c 11- )
|
||||
continue
|
||||
;;
|
||||
"HPR_duration")
|
||||
HPR_duration=$(echo $line | grep "HPR_duration: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_explicit")
|
||||
HPR_explicit=$(echo $line | grep "HPR_explicit: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_license")
|
||||
HPR_license=$(echo $line | grep "HPR_license: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
done < <( wget --timeout=10 --tries=1 --quiet http://hackerpublicradio.org/say.php?id=${ep_num} -O - )
|
||||
|
||||
if [[ -z "$HPR_album" || -z "$HPR_artist" || -z "$HPR_comment" || -z "$HPR_genre" || -z "$HPR_title" || -z "$HPR_track" || -z "$HPR_year" || -z "$HPR_summary" || -z "$HPR_duration" || -z "$HPR_explicit" || -z "$HPR_license" ]]
|
||||
then
|
||||
echoerr "Could not find information on ${ep_num}. Has the show been posted ?"
|
||||
exit;
|
||||
fi
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "album : $HPR_album"
|
||||
echo "artist : $HPR_artist"
|
||||
echo "comment : $HPR_comment"
|
||||
echo "genre : $HPR_genre"
|
||||
echo "title : $HPR_title"
|
||||
echo "track : $HPR_track"
|
||||
echo "year : $HPR_year"
|
||||
echo "summary : $HPR_summary"
|
||||
echo "duration : $HPR_duration"
|
||||
echo "explicit : $HPR_explicit"
|
||||
echo "license : $HPR_license"
|
||||
|
||||
if [[ $HPR_duration == "0" ]]
|
||||
then
|
||||
echoerr "The duration is set to 0. Please update the show with the correct time."
|
||||
exit;
|
||||
fi
|
||||
|
||||
#============================================================
|
||||
# Preproc`s the source file
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Prepare mezzanine file"
|
||||
ffmpeg -i ${mediafile} -ar 44100 -ac $CHANNELS hpr${ep_num}.wav > ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "Normalizing the wav files"
|
||||
sox --temp "${TEMP_DIR}" --norm hpr${ep_num}.wav hpr${ep_num}_norm.wav
|
||||
mv -v hpr${ep_num}_norm.wav ${upload_dir}/hpr${ep_num}.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "File information"
|
||||
ffprobe ${upload_dir}/hpr${ep_num}.wav 2>&1 | grep Audio:
|
||||
mediainfo ${upload_dir}/hpr${ep_num}.wav
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to opus"
|
||||
opusenc ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.opus 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to flac"
|
||||
sox --temp "${TEMP_DIR}" -S ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.flac 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to mp3"
|
||||
sox --temp "${TEMP_DIR}" -S ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.mp3 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to ogg"
|
||||
sox --temp "${TEMP_DIR}" -S ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.ogg 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Convert to spx"
|
||||
sox --temp "${TEMP_DIR}" -S ${upload_dir}/hpr${ep_num}.wav -c 1 -r 16000 -t wav - | speexenc - ${upload_dir}/hpr${ep_num}.spx 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
if [[ ! -s ${upload_dir}/hpr${ep_num}.wav ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.opus ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.flac ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.mp3 ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.ogg ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.spx ]]
|
||||
then
|
||||
echoerr "ERROR: Something went wrong encoding the files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fix_tags -album="$HPR_album" -artist="$HPR_artist" -comment="${HPR_comment} The license is ${HPR_license}" -genre="$HPR_genre" -title="$HPR_title" -track="$HPR_track" -year="$HPR_year" ${upload_dir}/hpr${ep_num}* 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
fix_tags ${upload_dir}/hpr${ep_num}*
|
||||
|
||||
#echo "Changing the file dates to the time of upload"
|
||||
touch -r ${mediafile} hpr${ep_num}*
|
||||
touch -r ${mediafile} ${upload_dir}/hpr${ep_num}*
|
||||
|
||||
ls -al hpr${ep_num}* ${upload_dir}/hpr${ep_num}*
|
||||
|
||||
rsync -ave ssh --partial --progress --ignore-existing ${upload_dir}/hpr${ep_num}.mp3 ${upload_dir}/hpr${ep_num}.ogg ${upload_dir}/hpr${ep_num}.spx hpr:www/eps/
|
||||
|
||||
firefox http://hackerpublicradio.org/local/hpr${ep_num}.mp3
|
||||
firefox http://hackerpublicradio.org/local/hpr${ep_num}.ogg
|
||||
firefox file://${upload_dir}/hpr${ep_num}.mp3
|
||||
firefox file://${upload_dir}/hpr${ep_num}.ogg
|
||||
mpv "http://hackerpublicradio.org/local/hpr${ep_num}.spx" "http://hackerpublicradio.org/local/hpr${ep_num}.ogg" "http://hackerpublicradio.org/local/hpr${ep_num}.mp3" "file://${upload_dir}/hpr${ep_num}.spx" "file://${upload_dir}/hpr${ep_num}.opus" "file://${upload_dir}/hpr${ep_num}.ogg" "file://${upload_dir}/hpr${ep_num}.mp3" "file://${upload_dir}/hpr${ep_num}.flac"
|
||||
|
||||
echo "Source: $( mediainfo --Output=XML --Full "${mediafile}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' )"
|
||||
mediainfo --Output=XML --Full hpr${ep_num}* ${upload_dir}/hpr${ep_num}* | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' | sort | uniq -c
|
||||
|
||||
read -p "Remove files for \"${fname}\" (y|N) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
mediafilename=$(basename "${mediafile}")
|
||||
mediaextension="${mediafilename##*.}"
|
||||
|
||||
ssh hpr -t "mkdir /home/hpr/www/eps/hpr${ep_num}" >/dev/null 2>&1
|
||||
rsync -ave ssh --partial --progress --ignore-existing "${mediafile}" hpr:www/eps/hpr${ep_num}/hpr${ep_num}_source.${mediaextension}
|
||||
ssh hpr -t "ls -al /home/hpr/www/eps/hpr${ep_num}*"
|
||||
cp -v "${mediafile}" "${upload_dir}/hpr${ep_num}_source.${mediaextension}"
|
||||
|
||||
#echo "Remove temp files"
|
||||
rm -v ${fname}_mezzanine.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav
|
||||
mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/rss-future.php" -O - | xmlstarlet val --err -
|
||||
|
||||
#rsync -ave ssh --partial --progress ${upload_dir}/ hpr:/home/hpr/upload/processed/
|
||||
rsync -ave ssh --partial --progress ${upload_dir}/ borg:/data/IA/uploads/
|
||||
echo "$( ssh borg -t "ls -al /data/IA/uploads/hpr${ep_num}*" ; ls -al ${upload_dir}/hpr${ep_num}* )" | grep "hpr${ep_num}" | awk '{print $5, $NF}' | sort
|
||||
find /var/IA/done/ -empty -delete
|
||||
else
|
||||
echo "skipping...."
|
||||
echo "cp -v \"${mediafile}\" \"${upload_dir}/hpr${ep_num}_source.${mediaextension}\""
|
||||
echo "rm -v ${fname}_mezzanine.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav"
|
||||
echo "mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "rsync -ave ssh --partial --progress ${upload_dir}/ borg:/data/IA/uploads/"
|
||||
fi
|
603
workflow/hprtranscode-simple.bash
Executable file
603
workflow/hprtranscode-simple.bash
Executable file
@ -0,0 +1,603 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright Ken Fallon - Released into the public domain. http://creativecommons.org/publicdomain/
|
||||
#============================================================
|
||||
TEMP_DIR="/var/tmp/"
|
||||
CHANNELS="1"
|
||||
FIXAUDIO="1"
|
||||
ARTIST="EMPTY"
|
||||
TITLE="EMPTY"
|
||||
YEAR="EMPTY"
|
||||
SLOT="EMPTY"
|
||||
basedir="/var/IA"
|
||||
upload_dir="${basedir}/uploads"
|
||||
theme="${basedir}/theme.wav"
|
||||
outro="${basedir}/2022-03-07-outro.wav"
|
||||
ttsserver="http://localhost:5500"
|
||||
processing_dir="/home/ken/tmp/hpr/processing"
|
||||
git_image_dir="/home/ken/sourcecode/hpr/HPR_Public_Code/www/images/hosts"
|
||||
acceptable_duration_difference="2" # Seconds
|
||||
thedate=$(/usr/bin/date -u +%Y-%m-%d_%H-%M-%SZ_%A)
|
||||
echo "Processing the next HPR Show in the queue"
|
||||
|
||||
|
||||
if [ $( curl -s -o /dev/null -w "%{http_code}" -X 'GET' "${ttsserver}/api/voices" -H 'accept: */*' ) != "200" ]
|
||||
then
|
||||
echo "Please start the tts-server \"podman run -it -p 5500:5500 synesthesiam/opentts:en\""
|
||||
exit
|
||||
fi
|
||||
|
||||
function abs_diff {
|
||||
echo $(($1 >= $2 ? $1 - $2 : $2 - $1))
|
||||
}
|
||||
|
||||
function create_tts_summary {
|
||||
HPR_summary="$( cat "hpr${ep_num}_summary.txt" )"
|
||||
echo "INFO: Converting text \"${HPR_summary}\" to speech."
|
||||
curl -X 'GET' -G --data-urlencode "voice=coqui-tts:en_ljspeech" --data-urlencode "text=${HPR_summary}" --data-urlencode "vocoder=high" --data-urlencode "denoiserStrength=0.03" --data-urlencode "cache=false" ${ttsserver}/api/tts -H 'accept: */*' --output ~hpr${ep_num}_summary.wav
|
||||
}
|
||||
|
||||
###################
|
||||
# Locate media directory
|
||||
#
|
||||
#
|
||||
show_type=""
|
||||
if [[ -f "${1}" && -n "${2}" ]]
|
||||
then
|
||||
show_type="local"
|
||||
mediafile="${1}"
|
||||
media_dir="$( dirname "${mediafile}" )"
|
||||
ep_num="${2}"
|
||||
echo "The duration is \"$( \date -ud "1970-01-01 $( ffprobe -i "${mediafile}" 2>&1| awk -F ': |, ' '/Duration:/ { print $2 }' )" +%s )\"."
|
||||
else
|
||||
show_type="remote"
|
||||
response=$( curl --silent --netrc-file ${HOME}/.netrc "https://hub.hackerpublicradio.org/cms/status.php" | \
|
||||
grep 'SHOW_POSTED' | \
|
||||
head -1 | \
|
||||
sed 's/,/ /g' )
|
||||
|
||||
if [ -z "${response}" ]
|
||||
then
|
||||
echo "INFO: There appear to be no more shows with the status \"SHOW_POSTED\"."
|
||||
echo "Getting a list of all the reservations."
|
||||
curl --silent --netrc-file ${HOME}/.netrc "https://hub.hackerpublicradio.org/cms/status.php"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
timestamp_epoc="$( echo ${response} | awk '{print $1}' )"
|
||||
ep_num="$( echo ${response} | awk '{print $2}' )"
|
||||
ep_date="$( echo ${response} | awk '{print $3}' )"
|
||||
key="$( echo ${response} | awk '{print $4}' )"
|
||||
status="$( echo ${response} | awk '{print $5}' )"
|
||||
email="$( echo ${response} | awk '{print $6}' )"
|
||||
|
||||
#source_dir="hpr:/home/hpr/upload/${timestamp_epoc}_${ep_num}_${ep_date}_${key}"
|
||||
dest_dir="${timestamp_epoc}_${ep_num}_${ep_date}_${key}"
|
||||
media_dir="${processing_dir}/${timestamp_epoc}_${ep_num}_${ep_date}_${key}"
|
||||
fi
|
||||
|
||||
if [ -z "${show_type}" ]
|
||||
then
|
||||
echo "sorry, variable \"show_type\" is not set."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "${media_dir}" ]
|
||||
then
|
||||
echo "sorry, variable \"media_dir\" is not set."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -d "${media_dir}" ]
|
||||
then
|
||||
echo "sorry, directory \"media_dir: ${media_dir}\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo detox -v "${media_dir}/"
|
||||
detox -vr "${media_dir}/"
|
||||
|
||||
if [[ "$( find "${media_dir}" \( -iname "hpr${ep_num}.wav" -o -iname "hpr${ep_num}.mp3" -o -iname "hpr${ep_num}.ogg" -o -iname "hpr${ep_num}.spx" -o -iname "hpr${ep_num}_summary.txt" -o -iname "hpr${ep_num}_summary.wav" -o -iname "hpr${ep_num}.wav" -o -iname "*_mez.wav" -o -iname "*_sox_norm.wav" -o -iname "*_mezzanine.wav" -o -iname "*_tmp.pcm" -o -iname "hpr${ep_num}_intro.wav" -o -iname "~hpr${ep_num}_summary.wav" -o -iname "~~hpr${ep_num}_summary.wav" -o -iname "*_tmp.log" -o -iname "silence.wav" -o -iname "hpr${ep_num}_norm.wav" \) | wc -l )" -ne 0 ]]
|
||||
then
|
||||
echo "Files for this episode already exist."
|
||||
find "${media_dir}" \( -iname "hpr${ep_num}.wav" -o -iname "hpr${ep_num}.mp3" -o -iname "hpr${ep_num}.ogg" -o -iname "hpr${ep_num}.spx" -o -iname "hpr${ep_num}_summary.txt" -o -iname "hpr${ep_num}_summary.wav" -o -iname "hpr${ep_num}.wav" -o -iname "*_mez.wav" -o -iname "*_sox_norm.wav" -o -iname "*_mezzanine.wav" -o -iname "*_tmp.pcm" -o -iname "hpr${ep_num}_intro.wav" -o -iname "~hpr${ep_num}_summary.wav" -o -iname "~~hpr${ep_num}_summary.wav" -o -iname "*_tmp.log" -o -iname "silence.wav" -o -iname "hpr${ep_num}_norm.wav" \)
|
||||
read -p "Shall I remove them ? (N|y) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ ! $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping...."
|
||||
exit
|
||||
else
|
||||
echo "Removing old files ...."
|
||||
find "${media_dir}" \( -iname "hpr${ep_num}.wav" -o -iname "hpr${ep_num}.mp3" -o -iname "hpr${ep_num}.ogg" -o -iname "hpr${ep_num}.spx" -o -iname "hpr${ep_num}_summary.txt" -o -iname "hpr${ep_num}_summary.wav" -o -iname "hpr${ep_num}.wav" -o -iname "*_mez.wav" -o -iname "*_sox_norm.wav" -o -iname "*_mezzanine.wav" -o -iname "*_tmp.pcm" -o -iname "hpr${ep_num}_intro.wav" -o -iname "~hpr${ep_num}_summary.wav" -o -iname "~~hpr${ep_num}_summary.wav" -o -iname "*_tmp.log" -o -iname "silence.wav" -o -iname "hpr${ep_num}_norm.wav" \) -delete -print
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
#####################################################
|
||||
# Process media
|
||||
|
||||
media=$( find "${media_dir}" -type f -exec file {} \; | grep -Ei 'audio|mpeg|video|MP4' | awk -F ': ' '{print $1}' )
|
||||
if [ -z "${media}" ]
|
||||
then
|
||||
echo "ERROR: Can't find any media in \"${media_dir}/\""
|
||||
find "${media_dir}/" -type f
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mediafile=""
|
||||
|
||||
echo "Found more than one media file. Please select which one to use ?"
|
||||
if [ "$( echo "${media}" | wc -l )" -ne 1 ]
|
||||
then
|
||||
select this_media in $( echo "${media}" )
|
||||
do
|
||||
echo "INFO: You selected \"${this_media}\"."
|
||||
ls -al "${this_media}"
|
||||
mediafile="${this_media}"
|
||||
break
|
||||
done
|
||||
else
|
||||
echo "INFO: Selecting media as \"${media}\"."
|
||||
mediafile="${media}"
|
||||
fi
|
||||
|
||||
# extract file name and extension
|
||||
fname="$( basename "${mediafile%.*}" )"
|
||||
ext="${mediafile/*./}"
|
||||
|
||||
cd "${media_dir}/"
|
||||
pwd
|
||||
|
||||
echo "INFO: Processing hpr${ep_num} from ${email}"
|
||||
echo "INFO: Working directory is \"${media_dir}/\""
|
||||
echo ""
|
||||
|
||||
|
||||
if [ -z "${mediafile}" ]
|
||||
then
|
||||
echo "sorry, variable \"mediafile\" is not set."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "${fname}" ]
|
||||
then
|
||||
echo "sorry, variable \"fname\" is not set."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "${ext}" ]
|
||||
then
|
||||
echo "sorry, variable \"ext\" is not set."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -f "${mediafile}" ]
|
||||
then
|
||||
echo "sorry, media file \"${mediafile}\" does not exist"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "File information for \"${mediafile}\"" | tee -a ${fname}_tmp.log
|
||||
ffprobe ${mediafile} 2>&1 | grep Audio:
|
||||
mediainfo ${mediafile}
|
||||
audio2image.bash ${mediafile}
|
||||
xdg-open ${mediafile%.*}.png >/dev/null 2>&1 &
|
||||
unset REPLY
|
||||
until [[ $REPLY =~ ^[yYnN]$ ]]
|
||||
do
|
||||
read -p "Source Waveform look ok ? (N|y) ? " -n 1 -r
|
||||
echo ""
|
||||
done
|
||||
|
||||
if [[ ! $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping.... $REPLY"
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
re='^[0-9]+$'
|
||||
if ! [[ $ep_num =~ $re ]]
|
||||
then
|
||||
echo "error: episode \"${ep_num}\" is not a number"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${outro}" ]
|
||||
then
|
||||
echo "sorry, file \"${outro}\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${theme}" ]
|
||||
then
|
||||
echo "sorry, file \"${theme}\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -r "${mediafile}" ]
|
||||
then
|
||||
echo "sorry, media file \"${mediafile}\" is not readable"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ $(ffprobe "${mediafile}" 2>&1 | grep "Audio:" | wc -l ) -eq 0 ]
|
||||
then
|
||||
echo "sorry, media file \"${mediafile}\" has no audio track"
|
||||
exit
|
||||
fi
|
||||
|
||||
xdg-open "https://hackerpublicradio.org/eps/hpr${ep_num}/index.html" >/dev/null 2>&1 &
|
||||
|
||||
mpv -vo=null "${mediafile}"
|
||||
|
||||
unset REPLY
|
||||
until [[ $REPLY =~ ^[yYnN]$ ]]
|
||||
do
|
||||
read -p "Is the audio ok (n|Y) ? " -n 1 -r
|
||||
echo ""
|
||||
done
|
||||
|
||||
if [[ ! $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping.... $REPLY"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Geting metadata for hpr${ep_num}"
|
||||
|
||||
while read -r line
|
||||
do
|
||||
field=$(echo $line | awk -F ':' '{print $1}')
|
||||
case $field in
|
||||
"HPR_summary")
|
||||
HPR_summary=$(echo $line | grep "HPR_summary: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_album")
|
||||
HPR_album=$(echo $line | grep "HPR_album: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_artist")
|
||||
HPR_artist=$(echo $line | grep "HPR_artist: " | cut -c 13- )
|
||||
continue
|
||||
;;
|
||||
"HPR_comment")
|
||||
HPR_comment=$(echo $line | grep "HPR_comment: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_genre")
|
||||
HPR_genre=$(echo $line | grep "HPR_genre: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_title")
|
||||
HPR_title=$(echo $line | grep "HPR_title: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_track")
|
||||
HPR_track=$(echo $line | grep "HPR_track: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_year")
|
||||
HPR_year=$(echo $line | grep "HPR_year: " | cut -c 11- )
|
||||
continue
|
||||
;;
|
||||
"HPR_duration")
|
||||
HPR_duration=$(echo $line | grep "HPR_duration: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_explicit")
|
||||
HPR_explicit=$(echo $line | grep "HPR_explicit: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_license")
|
||||
HPR_license=$(echo $line | grep "HPR_license: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
done < <( curl --silent --netrc-file ${HOME}/.netrc "https://hub.hackerpublicradio.org/cms/say.php?id=${ep_num}" )
|
||||
|
||||
if [[ -z "$HPR_album" || -z "$HPR_artist" || -z "$HPR_comment" || -z "$HPR_genre" || -z "$HPR_title" || -z "$HPR_track" || -z "$HPR_year" || -z "$HPR_summary" || -z "$HPR_duration" || -z "$HPR_explicit" || -z "$HPR_license" ]]
|
||||
then
|
||||
echo "Could not find information on ${ep_num}. Has the show been posted ?"
|
||||
exit;
|
||||
fi
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "album : $HPR_album"
|
||||
echo "artist : $HPR_artist"
|
||||
echo "comment : $HPR_comment"
|
||||
echo "genre : $HPR_genre"
|
||||
echo "title : $HPR_title"
|
||||
echo "track : $HPR_track"
|
||||
echo "year : $HPR_year"
|
||||
echo "summary : $HPR_summary"
|
||||
echo "duration : $HPR_duration"
|
||||
echo "explicit : $HPR_explicit"
|
||||
echo "license : $HPR_license"
|
||||
echo "media_dir : ${media_dir}"
|
||||
echo "mediafile : ${mediafile}"
|
||||
echo "fname : ${fname}"
|
||||
echo "ext : ${ext}"
|
||||
|
||||
if [[ $HPR_duration == "0" ]]
|
||||
then
|
||||
echo "The duration is set to 0. Please update the show with the correct time."
|
||||
exit;
|
||||
fi
|
||||
|
||||
#============================================================
|
||||
# Preproc`s the source file
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Prepare mezzanine file" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${mediafile} -ar 44100 -ac $CHANNELS ${fname}_mezzanine.wav > ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Add HPR Branding" | tee -a ${fname}_tmp.log
|
||||
|
||||
echo "Creating the summary" | tee -a ${fname}_tmp.log
|
||||
#echo "$HPR_summary" - | text2wave -F 44100 - -o hpr${ep_num}_summary.wav #festival --tts
|
||||
#echo "$HPR_summary" - | espeak -w ~hpr${ep_num}_summary.wav
|
||||
|
||||
echo "$HPR_summary" > "hpr${ep_num}_summary.txt"
|
||||
|
||||
create_tts_summary_ok="not-ok"
|
||||
|
||||
while [ "${create_tts_summary_ok}" != "OK" ]
|
||||
do
|
||||
create_tts_summary
|
||||
|
||||
xdg-open "hpr${ep_num}_summary.txt" 2>&1 &
|
||||
mpv --speed=1.8 ~hpr${ep_num}_summary.wav
|
||||
|
||||
read -p "Is the text to speech correct (y|N) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
create_tts_summary_ok="OK"
|
||||
else
|
||||
echo "WARN: Please correct the text and try again."
|
||||
inotifywait --event modify "hpr${ep_num}_summary.txt"
|
||||
fi
|
||||
done
|
||||
echo "INFO: TTS complete."
|
||||
|
||||
ffmpeg -y -i ~hpr${ep_num}_summary.wav -ar 44100 ~~hpr${ep_num}_summary.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
sox -V2 -n -r 44100 -c 1 silence.wav trim 0.0 6.0 >> ${fname}_tmp.log 2>&1
|
||||
|
||||
sox -V2 silence.wav ~~hpr${ep_num}_summary.wav hpr${ep_num}_summary.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "Adding the theme" | tee -a ${fname}_tmp.log
|
||||
sox -V2 -m "hpr${ep_num}_summary.wav" "${theme}" "hpr${ep_num}_intro.wav" >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "Creating the sandwitch: \"hpr${ep_num}_intro.wav\" \"${fname}_mezzanine.wav\" \"${outro}\" \"hpr${ep_num}.wav\" " | tee -a ${fname}_tmp.log
|
||||
|
||||
sox -V2 "hpr${ep_num}_intro.wav" "${fname}_mezzanine.wav" "${outro}" "hpr${ep_num}.wav" >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "Normalizing the wav files" | tee -a ${fname}_tmp.log
|
||||
#sox --temp "${TEMP_DIR}" --norm hpr${ep_num}.wav hpr${ep_num}_norm.wav
|
||||
ffmpeg -y -i hpr${ep_num}.wav -af loudnorm=I=-16:LRA=11:TP=-1.5 hpr${ep_num}_norm.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
mv -v hpr${ep_num}_norm.wav ${upload_dir}/hpr${ep_num}.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "File information" | tee -a ${fname}_tmp.log
|
||||
ffprobe ${upload_dir}/hpr${ep_num}.wav 2>&1 | grep Audio:
|
||||
mediainfo ${upload_dir}/hpr${ep_num}.wav
|
||||
audio2image.bash ${upload_dir}/hpr${ep_num}.wav
|
||||
xdg-open ${upload_dir}/hpr${ep_num}.png >/dev/null 2>&1 &
|
||||
read -p "Processed Waveform look ok ? (N|y) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ ! $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping...."
|
||||
exit
|
||||
fi
|
||||
rm -v ${upload_dir}/hpr${ep_num}.png
|
||||
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Geting transcript of hpr${ep_num}"
|
||||
|
||||
whisper --model tiny --language en --output_dir "${media_dir}" "${upload_dir}/hpr${ep_num}.wav" | tee -a ${fname}_tmp.log
|
||||
|
||||
ls -al "${media_dir}/hpr${ep_num}".*
|
||||
xdg-open "${media_dir}/hpr${ep_num}".txt 2>&1 &
|
||||
echo mpv --no-audio-display --audio-channels=stereo --speed="3.5" "${media_dir}/hpr${ep_num}".txt
|
||||
unset REPLY
|
||||
until [[ $REPLY =~ ^[yYnN]$ ]]
|
||||
do
|
||||
read -p "Processed transcript look ok ? (N|y) ? " -n 1 -r
|
||||
echo ""
|
||||
done
|
||||
|
||||
if [[ ! $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping.... $REPLY"
|
||||
exit
|
||||
fi
|
||||
|
||||
mkdir "${upload_dir}/hpr${ep_num}" >/dev/null 2>&1
|
||||
|
||||
cp -v "${media_dir}/hpr${ep_num}".vtt "${upload_dir}/hpr${ep_num}/hpr${ep_num}.vtt" | tee -a ${fname}_tmp.log
|
||||
cp -v "${media_dir}/hpr${ep_num}".srt "${upload_dir}/hpr${ep_num}/hpr${ep_num}.srt" | tee -a ${fname}_tmp.log
|
||||
cp -v "${media_dir}/hpr${ep_num}".txt "${upload_dir}/hpr${ep_num}/hpr${ep_num}.txt" | tee -a ${fname}_tmp.log
|
||||
cp -v "${media_dir}/hpr${ep_num}".tsv "${upload_dir}/hpr${ep_num}/hpr${ep_num}.tsv" | tee -a ${fname}_tmp.log
|
||||
cp -v "${media_dir}/hpr${ep_num}".json "${upload_dir}/hpr${ep_num}/hpr${ep_num}.json" | tee -a ${fname}_tmp.log
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to opus" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.opus 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to flac" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.flac 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to mp3" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.mp3 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to ogg" | tee -a ${fname}_tmp.log
|
||||
#ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.ogg 2>> ${fname}_tmp.log 1>&2
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav -acodec libopus -f ogg ${upload_dir}/hpr${ep_num}.ogg 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to spx" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.spx 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
### End conversion
|
||||
|
||||
intro_duration="$( mediainfo --Output=XML --Full "hpr${ep_num}_intro.wav" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' )"
|
||||
outro_duration="$( mediainfo --Output=XML --Full "${outro}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' )"
|
||||
source_duration="$( mediainfo --Output=XML --Full "${mediafile}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' )"
|
||||
expected_duration=$(( ${intro_duration} + ${HPR_duration} + ${outro_duration} ))
|
||||
|
||||
echo "Intro(${intro_duration}) + Show(${HPR_duration}) + Outro(${outro_duration}) = ${expected_duration}"
|
||||
|
||||
media_error="0"
|
||||
|
||||
for check_file in \
|
||||
${upload_dir}/hpr${ep_num}.wav \
|
||||
${upload_dir}/hpr${ep_num}.opus \
|
||||
${upload_dir}/hpr${ep_num}.flac \
|
||||
${upload_dir}/hpr${ep_num}.mp3 \
|
||||
${upload_dir}/hpr${ep_num}.spx \
|
||||
${upload_dir}/hpr${ep_num}.ogg
|
||||
do
|
||||
# ${upload_dir}/hpr${ep_num}.spx
|
||||
echo "INFO: Processing the file \"${check_file}\""
|
||||
if [[ ! -s "${check_file}" ]]
|
||||
then
|
||||
echo "ERROR: Something went wrong encoding of the file \"${check_file}\""
|
||||
exit
|
||||
else
|
||||
mediainfo --Output=XML --Full "${check_file}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}'
|
||||
this_duration=$( mediainfo --Output=XML --Full "${check_file}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' )
|
||||
if [[ $(abs_diff "${this_duration}" "${expected_duration}" ) -le "${acceptable_duration_difference}" ]]
|
||||
then
|
||||
echo "INFO: The file \"${check_file}\" duration of ${this_duration} () is close enough to ${expected_duration}"
|
||||
else
|
||||
echo "ERROR: The file \"${check_file}\" actual duration of ${this_duration} is not close enough to posted duration of ${expected_duration}."
|
||||
echo " Fix or update the posted duration to ${source_duration}."
|
||||
media_error="1"
|
||||
fi
|
||||
#${expected_duration}
|
||||
fi
|
||||
done
|
||||
echo "Source: ${source_duration}"
|
||||
|
||||
if [[ "${media_error}" -eq "1" ]]
|
||||
then
|
||||
echo "ERROR: Media is not encoded correctly"
|
||||
exit
|
||||
else
|
||||
echo "INFO: Media duration is correct"
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! -s ${upload_dir}/hpr${ep_num}.wav ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.opus ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.flac ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.mp3 ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.ogg ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.spx ]]
|
||||
then
|
||||
echo "ERROR: Something went wrong encoding the files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Fixing Tags" | tee -a ${fname}_tmp.log
|
||||
|
||||
fix_tags -album="$HPR_album" -artist="$HPR_artist" -comment="${HPR_comment} The license is ${HPR_license}" -genre="$HPR_genre" -title="$HPR_title" -track="$HPR_track" -year="$HPR_year" ${upload_dir}/hpr${ep_num}* 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
fix_tags ${upload_dir}/hpr${ep_num}*
|
||||
|
||||
#echo "Changing the file dates to the time of upload"
|
||||
touch -r ${mediafile} hpr${ep_num}*
|
||||
touch -r ${mediafile} ${upload_dir}/hpr${ep_num}*
|
||||
|
||||
ls -al hpr${ep_num}* ${upload_dir}/hpr${ep_num}* | tee -a ${fname}_tmp.log
|
||||
|
||||
# # echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
# # echo "Getting info for the asset table" | tee -a ${fname}_tmp.log
|
||||
# #
|
||||
# # echo "INSERT INTO assets (episode_id,filename,extension,size,sha1sum,mime_type,file_type) VALUES"
|
||||
# #
|
||||
# # for asset_file in \
|
||||
# # ${upload_dir}/hpr${ep_num}.wav \
|
||||
# # ${upload_dir}/hpr${ep_num}.opus \
|
||||
# # ${upload_dir}/hpr${ep_num}.flac \
|
||||
# # ${upload_dir}/hpr${ep_num}.mp3 \
|
||||
# # ${upload_dir}/hpr${ep_num}.spx \
|
||||
# # ${upload_dir}/hpr${ep_num}.ogg
|
||||
# # do
|
||||
# # size="$( ls -al "${asset_file}" | awk '{print $5}' )"
|
||||
# # sha1sum="$( sha1sum "${asset_file}" | awk '{print $1}' )"
|
||||
# # mime_type=$( file --dereference --brief --mime "${asset_file}" )
|
||||
# # file_type=$( file --dereference --brief "${asset_file}" )
|
||||
# # echo "(${ep_num},'${filename}','${extension}','${size}','${sha1sum}','${mime_type}','${file_type}'),"
|
||||
# # done
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Transferring files to Servers" | tee -a ${fname}_tmp.log
|
||||
|
||||
|
||||
#rsync -ave ssh --partial --progress --ignore-existing ${upload_dir}/hpr${ep_num}.mp3 ${upload_dir}/hpr${ep_num}.ogg ${upload_dir}/hpr${ep_num}.spx hpr:www/eps/
|
||||
|
||||
#firefox "https://hackerpublicradio.org/local/hpr${ep_num}.mp3" >/dev/null 2>&1 &
|
||||
#firefox "https://hackerpublicradio.org/local/hpr${ep_num}.ogg" >/dev/null 2>&1 &
|
||||
firefox "file://${upload_dir}/hpr${ep_num}.mp3" >/dev/null 2>&1 &
|
||||
firefox "file://${upload_dir}/hpr${ep_num}.ogg" >/dev/null 2>&1 &
|
||||
#firefox "https://hackerpublicradio.org/eps.php?id=${ep_num}" >/dev/null 2>&1 &
|
||||
|
||||
#mpv "http://hackerpublicradio.org/local/hpr${ep_num}.spx" "http://hackerpublicradio.org/local/hpr${ep_num}.ogg" "http://hackerpublicradio.org/local/hpr${ep_num}.mp3" "file://${upload_dir}/hpr${ep_num}.spx" "file://${upload_dir}/hpr${ep_num}.opus" "file://${upload_dir}/hpr${ep_num}.ogg" "file://${upload_dir}/hpr${ep_num}.mp3" "file://${upload_dir}/hpr${ep_num}.flac"
|
||||
mpv "file://${upload_dir}/hpr${ep_num}.spx" "file://${upload_dir}/hpr${ep_num}.opus" "file://${upload_dir}/hpr${ep_num}.ogg" "file://${upload_dir}/hpr${ep_num}.mp3" "file://${upload_dir}/hpr${ep_num}.flac"
|
||||
|
||||
read -p "Remove files for \"${fname}\" (y|N) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
mediafilename=$(basename "${mediafile}")
|
||||
mediaextension="${mediafilename##*.}" # "
|
||||
# ssh hpr -t "mkdir /home/hpr/www/eps/hpr${ep_num}" >/dev/null 2>&1
|
||||
# rsync -ave ssh --partial --progress --ignore-existing "${mediafile}" hpr:www/eps/hpr${ep_num}/hpr${ep_num}_source.${mediaextension} | tee -a ${fname}_tmp.log
|
||||
#
|
||||
# rsync -ave ssh --partial --progress --ignore-existing "${media_dir}/hpr${ep_num}.wav".vtt hpr:www/eps/hpr${ep_num}/hpr${ep_num}.vtt | tee -a ${fname}_tmp.log
|
||||
# rsync -ave ssh --partial --progress --ignore-existing "${media_dir}/hpr${ep_num}.wav".srt hpr:www/eps/hpr${ep_num}/hpr${ep_num}.srt | tee -a ${fname}_tmp.log
|
||||
# rsync -ave ssh --partial --progress --ignore-existing "${media_dir}/hpr${ep_num}.wav".txt hpr:www/eps/hpr${ep_num}/hpr${ep_num}.txt | tee -a ${fname}_tmp.log
|
||||
#
|
||||
# ssh hpr -t "ls -al /home/hpr/www/eps/hpr${ep_num}*"
|
||||
# cp -v "${mediafile}" "${upload_dir}/hpr${ep_num}_source.${mediaextension}"
|
||||
|
||||
#echo "Remove temp files"
|
||||
rm -v ~hpr${ep_num}_summary.wav ~~hpr${ep_num}_summary.wav silence.wav
|
||||
rm -v ${fname}_mezzanine.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav
|
||||
#mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/
|
||||
|
||||
# wget --timeout=0 -q "http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
# wget --timeout=0 -q "http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
# wget --timeout=0 -q "http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
# wget --timeout=0 -q "http://hackerpublicradio.org/rss-future.php" -O - | xmlstarlet val --err -
|
||||
|
||||
echo "INFO: rsync -ave ssh --partial --progress ${upload_dir}/hpr${ep_num}* borg:/data/IA/uploads/"
|
||||
rsync -ave ssh --partial --progress ${upload_dir}/hpr${ep_num}* borg:/data/IA/uploads/
|
||||
echo "$( ssh borg -t "ls -al /data/IA/uploads/hpr${ep_num}*" ; ls -al ${upload_dir}/hpr${ep_num}* )" | grep "hpr${ep_num}" | awk '{print $5, $NF}' | sort
|
||||
|
||||
if [ ${show_type} == "remote" ]
|
||||
then
|
||||
echo "INFO: Setting the status"
|
||||
# SHOW_SUBMITTED → METADATA_PROCESSED → SHOW_POSTED → MEDIA_TRANSCODED → UPLOADED_TO_IA → UPLOADED_TO_RSYNC_NET
|
||||
curl --netrc-file ${HOME}/.netrc "https://hub.hackerpublicradio.org/cms/status.php?ep_num=${ep_num}&status=MEDIA_TRANSCODED"
|
||||
curl --silent --netrc-file ${HOME}/.netrc "https://hub.hackerpublicradio.org/cms/status.php"
|
||||
fi
|
||||
else
|
||||
echo "skipping...."
|
||||
echo "cp -v \"${mediafile}\" \"${upload_dir}/hpr${ep_num}_source.${mediaextension}\""
|
||||
echo "rm -v ${fname}_mezzanine.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav"
|
||||
#echo "mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "rsync -ave ssh --partial --progress ${upload_dir}/ borg:/data/IA/uploads/"
|
||||
fi
|
397
workflow/hprtranscode-simple.bash.2022-07-31
Normal file
397
workflow/hprtranscode-simple.bash.2022-07-31
Normal file
@ -0,0 +1,397 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright Ken Fallon - Released into the public domain. http://creativecommons.org/publicdomain/
|
||||
#============================================================
|
||||
TEMP_DIR="/var/tmp/"
|
||||
CHANNELS="1"
|
||||
FIXAUDIO="1"
|
||||
ARTIST="EMPTY"
|
||||
TITLE="EMPTY"
|
||||
YEAR="EMPTY"
|
||||
SLOT="EMPTY"
|
||||
basedir="/var/IA"
|
||||
upload_dir="${basedir}/uploads"
|
||||
theme="${basedir}/theme.wav"
|
||||
outro="${basedir}/2022-03-07-outro.wav"
|
||||
ttsserver="http://localhost:5500"
|
||||
processing_dir="/home/ken/tmp/hpr/processing"
|
||||
git_image_dir="/home/ken/sourcecode/hpr/HPR_Public_Code/www/images/hosts"
|
||||
thedate=$(/usr/bin/date -u +%Y-%m-%d_%H-%M-%SZ_%A)
|
||||
echo "Processing the next HPR Show in the queue"
|
||||
|
||||
|
||||
if [ $( curl -s -o /dev/null -w "%{http_code}" -X 'GET' "${ttsserver}/api/voices" -H 'accept: */*' ) != "200" ]
|
||||
then
|
||||
echo "Please start the tts-server \"podman run -it -p 5500:5500 synesthesiam/opentts:en\""
|
||||
exit
|
||||
fi
|
||||
|
||||
###################
|
||||
# Get the show
|
||||
#
|
||||
#
|
||||
if [[ -f "${1}" && -n "${2}" ]]
|
||||
then
|
||||
mediafile="${1}"
|
||||
ep_num="${2}"
|
||||
echo "The duration is \"$( \date -ud "1970-01-01 $( ffprobe -i "${mediafile}" 2>&1| awk -F ': |, ' '/Duration:/ { print $2 }' )" +%s )\"."
|
||||
else
|
||||
response=$( curl --silent --netrc-file ${HOME}/.netrc "https://hackerpublicradio.org/cms/status.php" | \
|
||||
grep 'SHOW_POSTED' | \
|
||||
head -1 | \
|
||||
sed 's/,/ /g' )
|
||||
|
||||
if [ -z "${response}" ]
|
||||
then
|
||||
echo "INFO: There appear to be no more shows with the status \"SHOW_POSTED\"."
|
||||
echo "Getting a list of all the reservations."
|
||||
curl --silent --netrc-file ${HOME}/.netrc "https://hackerpublicradio.org/cms/status.php"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
timestamp_epoc="$( echo ${response} | awk '{print $1}' )"
|
||||
ep_num="$( echo ${response} | awk '{print $2}' )"
|
||||
ep_date="$( echo ${response} | awk '{print $3}' )"
|
||||
key="$( echo ${response} | awk '{print $4}' )"
|
||||
status="$( echo ${response} | awk '{print $5}' )"
|
||||
email="$( echo ${response} | awk '{print $6}' )"
|
||||
|
||||
#source_dir="hpr:/home/hpr/upload/${timestamp_epoc}_${ep_num}_${ep_date}_${key}"
|
||||
dest_dir="${timestamp_epoc}_${ep_num}_${ep_date}_${key}"
|
||||
echo detox -v "${processing_dir}/${dest_dir}/"
|
||||
detox -vr "${processing_dir}/${dest_dir}/"
|
||||
cd "${processing_dir}/${dest_dir}/"
|
||||
pwd
|
||||
|
||||
echo "INFO: Processing hpr${ep_num} from ${email}"
|
||||
echo "INFO: Working directory is \"${processing_dir}/${dest_dir}/\""
|
||||
echo ""
|
||||
|
||||
media=$( find "${processing_dir}/${timestamp_epoc}_${ep_num}_${ep_date}_${key}" -type f -exec file {} \; | grep -Ei 'audio|mpeg|video|MP4' | awk -F ': ' '{print $1}' )
|
||||
if [ -z "${media}" ]
|
||||
then
|
||||
echo "ERROR: Can't find any media in \"${processing_dir}/${dest_dir}/\""
|
||||
find "${processing_dir}/${dest_dir}/" -type f
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mediafile=""
|
||||
|
||||
if [ "$( echo "${media}" | wc -l )" -ne 1 ]
|
||||
then
|
||||
select this_media in $( echo "${media}" )
|
||||
do
|
||||
echo "INFO: You selected \"${this_media}\"."
|
||||
ls -al "${this_media}"
|
||||
mediafile="${this_media}"
|
||||
break
|
||||
done
|
||||
else
|
||||
echo "INFO: Selecting media as \"${media}\"."
|
||||
mediafile="${media}"
|
||||
fi
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "File information for \"${mediafile}\"" | tee -a ${fname}_tmp.log
|
||||
ffprobe ${mediafile} 2>&1 | grep Audio:
|
||||
mediainfo ${mediafile}
|
||||
audio2image.bash ${mediafile}
|
||||
xdg-open ${mediafile%.*}.png >/dev/null 2>&1 &
|
||||
read -p "Source Waveform look ok ? (N|y) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ ! $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping...."
|
||||
exit
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
re='^[0-9]+$'
|
||||
if ! [[ $ep_num =~ $re ]]
|
||||
then
|
||||
echo "error: episode \"${ep_num}\" is not a number"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${outro}" ]
|
||||
then
|
||||
echo "sorry, file \"${outro}\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${theme}" ]
|
||||
then
|
||||
echo "sorry, file \"${theme}\" does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${mediafile}" ]
|
||||
then
|
||||
echo "sorry, media file \"${mediafile}\" does not exist"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -r "${mediafile}" ]
|
||||
then
|
||||
echo "sorry, media file \"${mediafile}\" is not readable"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ $(ffprobe "${mediafile}" 2>&1 | grep "Audio:" | wc -l ) -eq 0 ]
|
||||
then
|
||||
echo "sorry, media file \"${mediafile}\" has no audio track"
|
||||
exit
|
||||
fi
|
||||
|
||||
mpv -vo=null "${mediafile}"
|
||||
read -p "Is there any problems with the audio (N|y) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping...."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# extract file name and extension
|
||||
media_dir=$(dirname ${mediafile})
|
||||
fname=${mediafile%.*}
|
||||
ext=${mediafile/*./}
|
||||
|
||||
if [[ -e hpr${ep_num}.wav ]] || [[ -e hpr${ep_num}.mp3 ]] || [[ -e hpr${ep_num}.ogg ]] || [[ -e hpr${ep_num}.spx ]] || [[ -e hpr${ep_num}_summary.wav ]] || [[ -e hpr${ep_num}.wav ]] || [[ -e ${fname}_mez.wav ]] || [[ -e ${fname}_sox_norm.wav ]] || [[ -e ${fname}_mezzanine.wav ]] || [[ -e ${fname}_tmp.pcm ]] || [[ -e ${fname}_tmp.log ]] || [[ -e hpr${ep_num}_intro.wav ]] || [[ -e ~hpr${ep_num}_summary.wav ]] || [[ -e ~~hpr${ep_num}_summary.wav ]]
|
||||
then
|
||||
echo "Files for this episode already exist."
|
||||
ls -1 *hpr${ep_num}* ${fname}_* 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Geting metadata for hpr${ep_num}"
|
||||
|
||||
while read -r line
|
||||
do
|
||||
field=$(echo $line | awk -F ':' '{print $1}')
|
||||
case $field in
|
||||
"HPR_summary")
|
||||
HPR_summary=$(echo $line | grep "HPR_summary: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_album")
|
||||
HPR_album=$(echo $line | grep "HPR_album: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_artist")
|
||||
HPR_artist=$(echo $line | grep "HPR_artist: " | cut -c 13- )
|
||||
continue
|
||||
;;
|
||||
"HPR_comment")
|
||||
HPR_comment=$(echo $line | grep "HPR_comment: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
"HPR_genre")
|
||||
HPR_genre=$(echo $line | grep "HPR_genre: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_title")
|
||||
HPR_title=$(echo $line | grep "HPR_title: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_track")
|
||||
HPR_track=$(echo $line | grep "HPR_track: " | cut -c 12- )
|
||||
continue
|
||||
;;
|
||||
"HPR_year")
|
||||
HPR_year=$(echo $line | grep "HPR_year: " | cut -c 11- )
|
||||
continue
|
||||
;;
|
||||
"HPR_duration")
|
||||
HPR_duration=$(echo $line | grep "HPR_duration: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_explicit")
|
||||
HPR_explicit=$(echo $line | grep "HPR_explicit: " | cut -c 15- )
|
||||
continue
|
||||
;;
|
||||
"HPR_license")
|
||||
HPR_license=$(echo $line | grep "HPR_license: " | cut -c 14- )
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
done < <( wget --timeout=10 --tries=1 --quiet http://hackerpublicradio.org/say.php?id=${ep_num} -O - )
|
||||
|
||||
if [[ -z "$HPR_album" || -z "$HPR_artist" || -z "$HPR_comment" || -z "$HPR_genre" || -z "$HPR_title" || -z "$HPR_track" || -z "$HPR_year" || -z "$HPR_summary" || -z "$HPR_duration" || -z "$HPR_explicit" || -z "$HPR_license" ]]
|
||||
then
|
||||
echo "Could not find information on ${ep_num}. Has the show been posted ?"
|
||||
exit;
|
||||
fi
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "album : $HPR_album"
|
||||
echo "artist : $HPR_artist"
|
||||
echo "comment : $HPR_comment"
|
||||
echo "genre : $HPR_genre"
|
||||
echo "title : $HPR_title"
|
||||
echo "track : $HPR_track"
|
||||
echo "year : $HPR_year"
|
||||
echo "summary : $HPR_summary"
|
||||
echo "duration : $HPR_duration"
|
||||
echo "explicit : $HPR_explicit"
|
||||
echo "license : $HPR_license"
|
||||
|
||||
if [[ $HPR_duration == "0" ]]
|
||||
then
|
||||
echo "The duration is set to 0. Please update the show with the correct time."
|
||||
exit;
|
||||
fi
|
||||
|
||||
#============================================================
|
||||
# Preproc`s the source file
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Prepare mezzanine file" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -i ${mediafile} -ar 44100 -ac $CHANNELS ${fname}_mezzanine.wav > ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Add HPR Branding" | tee -a ${fname}_tmp.log
|
||||
|
||||
echo "Creating the summary" | tee -a ${fname}_tmp.log
|
||||
#echo "$HPR_summary" - | text2wave -F 44100 - -o hpr${ep_num}_summary.wav #festival --tts
|
||||
#echo "$HPR_summary" - | espeak -w ~hpr${ep_num}_summary.wav
|
||||
echo "$HPR_summary" - | curl -X 'GET' -G --data-urlencode "voice=coqui-tts:en_ljspeech" --data-urlencode "text=${HPR_summary}" --data-urlencode "vocoder=high" --data-urlencode "denoiserStrength=0.03" --data-urlencode "cache=false" ${ttsserver}/api/tts -H 'accept: */*' --output ~hpr${ep_num}_summary.wav
|
||||
|
||||
ffmpeg -i ~hpr${ep_num}_summary.wav -ar 44100 ~~hpr${ep_num}_summary.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
sox -n -r 44100 -c 1 silence.wav trim 0.0 6.0 >> ${fname}_tmp.log 2>&1
|
||||
|
||||
sox silence.wav ~~hpr${ep_num}_summary.wav hpr${ep_num}_summary.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "Adding the theme" | tee -a ${fname}_tmp.log
|
||||
sox -m "hpr${ep_num}_summary.wav" "${theme}" "hpr${ep_num}_intro.wav" >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "Creating the sandwitch" | tee -a ${fname}_tmp.log
|
||||
|
||||
sox "hpr${ep_num}_intro.wav" "${fname}_mezzanine.wav" "${outro}" "hpr${ep_num}.wav" >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "Normalizing the wav files" | tee -a ${fname}_tmp.log
|
||||
#sox --temp "${TEMP_DIR}" --norm hpr${ep_num}.wav hpr${ep_num}_norm.wav
|
||||
ffmpeg -i hpr${ep_num}.wav -af loudnorm=I=-16:LRA=11:TP=-1.5 hpr${ep_num}_norm.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
mv -v hpr${ep_num}_norm.wav ${upload_dir}/hpr${ep_num}.wav >> ${fname}_tmp.log 2>&1
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "File information" | tee -a ${fname}_tmp.log
|
||||
ffprobe ${upload_dir}/hpr${ep_num}.wav 2>&1 | grep Audio:
|
||||
mediainfo ${upload_dir}/hpr${ep_num}.wav
|
||||
audio2image.bash ${upload_dir}/hpr${ep_num}.wav
|
||||
xdg-open ${upload_dir}/hpr${ep_num}.png >/dev/null 2>&1 &
|
||||
read -p "Processed Waveform look ok ? (N|y) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ ! $REPLY =~ ^[yY]$ ]]
|
||||
then
|
||||
echo "skipping...."
|
||||
exit
|
||||
fi
|
||||
rm -v ${upload_dir}/hpr${ep_num}.png
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to opus" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.opus 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to flac" | tee -a ${fname}_tmp.log
|
||||
sox --temp "${TEMP_DIR}" -S ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.flac 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to mp3" | tee -a ${fname}_tmp.log
|
||||
sox --temp "${TEMP_DIR}" -S ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.mp3 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to ogg" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.ogg 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Convert to spx" | tee -a ${fname}_tmp.log
|
||||
ffmpeg -y -i ${upload_dir}/hpr${ep_num}.wav ${upload_dir}/hpr${ep_num}.spx 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
for check_file in \
|
||||
${upload_dir}/hpr${ep_num}.wav \
|
||||
${upload_dir}/hpr${ep_num}.opus \
|
||||
${upload_dir}/hpr${ep_num}.flac \
|
||||
${upload_dir}/hpr${ep_num}.mp3 \
|
||||
${upload_dir}/hpr${ep_num}.ogg \
|
||||
${upload_dir}/hpr${ep_num}.spx
|
||||
do
|
||||
if [[ ! -s "${check_file}" ]]
|
||||
then
|
||||
echo "ERROR: Something went wrong encoding of the file \"${check_file}\""
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -s ${upload_dir}/hpr${ep_num}.wav ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.opus ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.flac ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.mp3 ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.ogg ]] || [[ ! -s ${upload_dir}/hpr${ep_num}.spx ]]
|
||||
then
|
||||
echo "ERROR: Something went wrong encoding the files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Fixing Tags" | tee -a ${fname}_tmp.log
|
||||
|
||||
fix_tags -album="$HPR_album" -artist="$HPR_artist" -comment="${HPR_comment} The license is ${HPR_license}" -genre="$HPR_genre" -title="$HPR_title" -track="$HPR_track" -year="$HPR_year" ${upload_dir}/hpr${ep_num}* 2>> ${fname}_tmp.log 1>&2
|
||||
|
||||
fix_tags ${upload_dir}/hpr${ep_num}*
|
||||
|
||||
#echo "Changing the file dates to the time of upload"
|
||||
touch -r ${mediafile} hpr${ep_num}*
|
||||
touch -r ${mediafile} ${upload_dir}/hpr${ep_num}*
|
||||
|
||||
ls -al hpr${ep_num}* ${upload_dir}/hpr${ep_num}* | tee -a ${fname}_tmp.log
|
||||
|
||||
|
||||
echo "--------------------------------------------------------------------------------" | tee -a ${fname}_tmp.log
|
||||
echo "Transferring files to Servers" | tee -a ${fname}_tmp.log
|
||||
|
||||
|
||||
rsync -ave ssh --partial --progress --ignore-existing ${upload_dir}/hpr${ep_num}.mp3 ${upload_dir}/hpr${ep_num}.ogg ${upload_dir}/hpr${ep_num}.spx hpr:www/eps/
|
||||
|
||||
firefox "https://hackerpublicradio.org/local/hpr${ep_num}.mp3" >/dev/null 2>&1 &
|
||||
firefox "https://hackerpublicradio.org/local/hpr${ep_num}.ogg" >/dev/null 2>&1 &
|
||||
firefox "file://${upload_dir}/hpr${ep_num}.mp3" >/dev/null 2>&1 &
|
||||
firefox "file://${upload_dir}/hpr${ep_num}.ogg" >/dev/null 2>&1 &
|
||||
firefox "https://hackerpublicradio.org/eps.php?id=${ep_num}" >/dev/null 2>&1 &
|
||||
|
||||
mpv "http://hackerpublicradio.org/local/hpr${ep_num}.spx" "http://hackerpublicradio.org/local/hpr${ep_num}.ogg" "http://hackerpublicradio.org/local/hpr${ep_num}.mp3" "file://${upload_dir}/hpr${ep_num}.spx" "file://${upload_dir}/hpr${ep_num}.opus" "file://${upload_dir}/hpr${ep_num}.ogg" "file://${upload_dir}/hpr${ep_num}.mp3" "file://${upload_dir}/hpr${ep_num}.flac"
|
||||
|
||||
echo "Source: $( mediainfo --Output=XML --Full "${mediafile}" | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' )"
|
||||
mediainfo --Output=XML --Full hpr${ep_num}* ${upload_dir}/hpr${ep_num}* | xmlstarlet sel -T -t -m '/_:MediaInfo/_:media/_:track[@type="Audio"]' -v '_:Duration' -n | awk -F '.' '{print $1}' | sort | uniq -c
|
||||
|
||||
read -p "Remove files for \"${fname}\" (y|N) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
mediafilename=$(basename "${mediafile}")
|
||||
mediaextension="${mediafilename##*.}" # "
|
||||
ssh hpr -t "mkdir /home/hpr/www/eps/hpr${ep_num}" >/dev/null 2>&1
|
||||
rsync -ave ssh --partial --progress --ignore-existing "${mediafile}" hpr:www/eps/hpr${ep_num}/hpr${ep_num}_source.${mediaextension}
|
||||
ssh hpr -t "ls -al /home/hpr/www/eps/hpr${ep_num}*"
|
||||
cp -v "${mediafile}" "${upload_dir}/hpr${ep_num}_source.${mediaextension}"
|
||||
|
||||
#echo "Remove temp files"
|
||||
rm -v ~hpr${ep_num}_summary.wav ~~hpr${ep_num}_summary.wav silence.wav
|
||||
rm -v ${fname}_mezzanine.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav
|
||||
#mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1" -O - | xmlstarlet val --err -
|
||||
wget --timeout=0 -q "http://hackerpublicradio.org/rss-future.php" -O - | xmlstarlet val --err -
|
||||
|
||||
rsync -ave ssh --partial --progress ${upload_dir}/hpr${ep_num}* borg:/data/IA/uploads/
|
||||
echo "$( ssh borg -t "ls -al /data/IA/uploads/hpr${ep_num}*" ; ls -al ${upload_dir}/hpr${ep_num}* )" | grep "hpr${ep_num}" | awk '{print $5, $NF}' | sort
|
||||
# SHOW_SUBMITTED → METADATA_PROCESSED → SHOW_POSTED → MEDIA_TRANSCODED → UPLOADED_TO_IA → UPLOADED_TO_RSYNC_NET
|
||||
curl --netrc-file ${HOME}/.netrc "https://hackerpublicradio.org/cms/status.php?ep_num=${ep_num}&status=MEDIA_TRANSCODED"
|
||||
else
|
||||
echo "skipping...."
|
||||
echo "cp -v \"${mediafile}\" \"${upload_dir}/hpr${ep_num}_source.${mediaextension}\""
|
||||
echo "rm -v ${fname}_mezzanine.wav ${fname}_tmp*.pcm ${fname}_tmp.log ${fname}_mez.wav"
|
||||
#echo "mv -v ${fname}* hpr${ep_num}* *_${ep_num}_* /var/IA/done/"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_ogg_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_mp3_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "wget --timeout=0 -q \"http://hackerpublicradio.org/hpr_spx_rss.php?gomax=1\" -O - | xmlstarlet val --err -"
|
||||
echo "rsync -ave ssh --partial --progress ${upload_dir}/ borg:/data/IA/uploads/"
|
||||
fi
|
501
workflow/rss-2_0.xsd
Normal file
501
workflow/rss-2_0.xsd
Normal file
@ -0,0 +1,501 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
XML Schema for RSS v2.0
|
||||
Copyright (C) 2003-2008 Jorgen Thelin
|
||||
|
||||
Microsoft Public License (Ms-PL)
|
||||
|
||||
This license governs use of the accompanying software.
|
||||
If you use the software, you accept this license.
|
||||
If you do not accept the license, do not use the software.
|
||||
|
||||
1. Definitions
|
||||
|
||||
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
|
||||
|
||||
A "contribution" is the original software, or any additions or changes to the software.
|
||||
|
||||
A "contributor" is any person that distributes its contribution under this license.
|
||||
|
||||
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
||||
|
||||
2. Grant of Rights
|
||||
|
||||
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
||||
|
||||
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
|
||||
|
||||
3. Conditions and Limitations
|
||||
|
||||
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
||||
|
||||
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
|
||||
|
||||
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
|
||||
|
||||
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
|
||||
|
||||
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
|
||||
|
||||
-->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="unqualified"
|
||||
version="2.0.2.16">
|
||||
<xs:annotation>
|
||||
<xs:documentation>XML Schema for RSS v2.0 feed files.</xs:documentation>
|
||||
<xs:documentation>Project home: http://www.codeplex.com/rss2schema/ </xs:documentation>
|
||||
<xs:documentation>Based on the RSS 2.0 specification document at http://cyber.law.harvard.edu/rss/rss.html </xs:documentation>
|
||||
<xs:documentation>Author: Jorgen Thelin</xs:documentation>
|
||||
<xs:documentation>Revision: 16</xs:documentation>
|
||||
<xs:documentation>Date: 01-Nov-2008</xs:documentation>
|
||||
<xs:documentation>Feedback to: http://www.codeplex.com/rss2schema/WorkItem/List.aspx </xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:element name="rss">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="channel" type="RssChannel"/>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="version" type="xs:decimal" use="required" fixed="2.0"/>
|
||||
<xs:anyAttribute namespace="##any"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:complexType name="RssItem">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An item may represent a "story" -- much like a story in a newspaper or magazine; if so its description is a synopsis of the story, and the link points to the full story. An item may also be complete in itself, if so, the description contains the text (entity-encoded HTML is allowed), and the link and title may be omitted.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element name="title" type="xs:string" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The title of the item.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" type="xs:string" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The item synopsis.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="link" type="xs:anyURI" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL of the item.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="author" type="EmailAddress" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Email address of the author of the item.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="category" type="Category" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Includes the item in one or more categories. </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="comments" type="xs:anyURI" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL of a page for comments relating to the item.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="enclosure" type="Enclosure" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes a media object that is attached to the item.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="guid" type="Guid" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>guid or permalink URL for this entry</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="pubDate" type="Rfc822FormatDate" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Indicates when the item was published.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="source" type="Source" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The RSS channel that the item came from.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Extensibility element.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##any"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="RssChannel">
|
||||
<xs:sequence>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element name="title" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the channel. It's how people refer to your service. If you have an HTML website that contains the same information as your RSS file, the title of your channel should be the same as the title of your website.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="link" type="xs:anyURI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL to the HTML website corresponding to the channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Phrase or sentence describing the channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="language" type="xs:language" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The language the channel is written in. This allows aggregators to group all Italian language sites, for example, on a single page. A list of allowable values for this element, as provided by Netscape, is here. You may also use values defined by the W3C.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="copyright" type="xs:string" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Copyright notice for content in the channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="managingEditor" type="EmailAddress" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Email address for person responsible for editorial content.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="webMaster" type="EmailAddress" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Email address for person responsible for technical issues relating to channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="pubDate" type="Rfc822FormatDate" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The publication date for the content in the channel. All date-times in RSS conform to the Date and Time Specification of RFC 822, with the exception that the year may be expressed with two characters or four characters (four preferred).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="lastBuildDate" type="Rfc822FormatDate" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The last time the content of the channel changed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="category" type="Category" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specify one or more categories that the channel belongs to.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="generator" type="xs:string" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A string indicating the program used to generate the channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="docs" type="xs:anyURI" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A URL that points to the documentation for the format used in the RSS file. It's probably a pointer to this page. It's for people who might stumble across an RSS file on a Web server 25 years from now and wonder what it is.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="cloud" type="Cloud" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Allows processes to register with a cloud to be notified of updates to the channel, implementing a lightweight publish-subscribe protocol for RSS feeds.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="ttl" type="xs:nonNegativeInteger" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ttl stands for time to live. It's a number of minutes that indicates how long a channel can be cached before refreshing from the source.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="image" type="Image" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies a GIF, JPEG or PNG image that can be displayed with the channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="rating" type="xs:string" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The PICS rating for the channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="textInput" type="TextInput" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies a text input box that can be displayed with the channel.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="skipHours" type="SkipHoursList" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A hint for aggregators telling them which hours they can skip.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="skipDays" type="SkipDaysList" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A hint for aggregators telling them which days they can skip.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Extensibility element.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:choice>
|
||||
<xs:element name="item" type="RssItem" minOccurs="1" maxOccurs="unbounded">
|
||||
<!--
|
||||
HACK: According to the RSS 2.0 spec, it should strictly be possible to have zero item elements,
|
||||
but this makes the schema non-deterministic with regard to extensibility elements
|
||||
so for the moment we undid bug-fix 10231 and set minOccurs=1 to work around this problem.
|
||||
-->
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Extensibility element.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##any"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="SkipHour">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A time in GMT when aggregators should not request the channel data. The hour beginning at midnight is hour zero.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:nonNegativeInteger">
|
||||
<xs:minInclusive value="0"/>
|
||||
<xs:maxInclusive value="23"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="SkipHoursList">
|
||||
<xs:sequence>
|
||||
<xs:element name="hour" type="SkipHour" minOccurs="0" maxOccurs="24"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="SkipDay">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A day when aggregators should not request the channel data.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Monday"/>
|
||||
<xs:enumeration value="Tuesday"/>
|
||||
<xs:enumeration value="Wednesday"/>
|
||||
<xs:enumeration value="Thursday"/>
|
||||
<xs:enumeration value="Friday"/>
|
||||
<xs:enumeration value="Saturday"/>
|
||||
<xs:enumeration value="Sunday"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="SkipDaysList">
|
||||
<xs:sequence>
|
||||
<xs:element name="day" type="SkipDay" minOccurs="0" maxOccurs="7">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A time in GMT, when aggregators should not request the channel data. The hour beginning at midnight is hour zero.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Category">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="domain" type="xs:string" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Image">
|
||||
<xs:all>
|
||||
<xs:element name="url" type="xs:anyURI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL of the image file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="title" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Describes the image, it's used in the ALT attribute of the HTML <img> tag when the channel is rendered in HTML.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="link" type="xs:anyURI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL of the site, when the channel is rendered, the image is a link to the site. (Note, in practice the image <title> and <link> should have the same value as the channel's <title> and <link>. </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="width" type="ImageWidth" default="88" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The width of the image in pixels.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="height" type="ImageHeight" default="31" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The height of the image in pixels.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" type="xs:string" minOccurs="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Text that is included in the TITLE attribute of the link formed around the image in the HTML rendering.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="ImageHeight">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The height of the image in pixels.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:positiveInteger">
|
||||
<xs:maxInclusive value="400"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="ImageWidth">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The width of the image in pixels.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:positiveInteger">
|
||||
<xs:maxInclusive value="144"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="Cloud">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies a web service that supports the rssCloud interface which can be implemented in HTTP-POST, XML-RPC or SOAP 1.1. Its purpose is to allow processes to register with a cloud to be notified of updates to the channel, implementing a lightweight publish-subscribe protocol for RSS feeds.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="domain" type="xs:string" use="required"/>
|
||||
<xs:attribute name="port" type="xs:positiveInteger" use="required"/>
|
||||
<xs:attribute name="path" type="xs:string" use="required"/>
|
||||
<xs:attribute name="registerProcedure" type="xs:string" use="required"/>
|
||||
<xs:attribute name="protocol" type="CloudProtocol" use="required"/>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="CloudProtocol">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="xml-rpc"/>
|
||||
<xs:enumeration value="http-post"/>
|
||||
<xs:enumeration value="soap"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="TextInput">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The purpose of this element is something of a mystery! You can use it to specify a search engine box. Or to allow a reader to provide feedback. Most aggregators ignore it.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:all>
|
||||
<xs:element name="title" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The label of the Submit button in the text input area.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Explains the text input area.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="name" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the text object in the text input area.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="link" type="xs:anyURI">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL of the CGI script that processes text input requests.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="EmailAddress">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Using the regexp definiton of E-Mail Address by Lucadean from the .NET RegExp Pattern Repository at http://www.3leaf.com/default/NetRegExpRepository.aspx </xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<!-- <xs:pattern value="([a-zA-Z0-9_\-])([a-zA-Z0-9_\-\.]*)@(\[((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}|((([a-zA-Z0-9\-]+)\.)+))([a-zA-Z]{2,}|(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\])"/>-->
|
||||
<xs:pattern value="[A-Za-z0-9_]+([-+.'][A-Za-z0-9_]+)*@[A-Za-z0-9_]+([-.][A-Za-z0-9_]+)*\.[A-Za-z0-9_]+([-.][A-Za-z0-9_]+)*"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Rfc822FormatDate">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A date-time displayed in RFC-822 format.</xs:documentation>
|
||||
<xs:documentation>Using the regexp definiton of rfc-822 date by Sam Ruby at http://www.intertwingly.net/blog/1360.html </xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="(((Mon)|(Tue)|(Wed)|(Thu)|(Fri)|(Sat)|(Sun)), *)?\d\d? +((Jan)|(Feb)|(Mar)|(Apr)|(May)|(Jun)|(Jul)|(Aug)|(Sep)|(Oct)|(Nov)|(Dec)) +\d\d(\d\d)? +\d\d:\d\d(:\d\d)? +(([+\-]?\d\d\d\d)|(UT)|(GMT)|(EST)|(EDT)|(CST)|(CDT)|(MST)|(MDT)|(PST)|(PDT)|\w)"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="Source">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="url" type="xs:anyURI"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Enclosure">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="url" type="xs:anyURI" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL where the enclosure is located</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="length" type="xs:nonNegativeInteger" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Size in bytes</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="type" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>MIME media-type of the enclosure</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Guid">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="isPermaLink" type="xs:boolean" use="optional" default="true"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
|
||||
<!--
|
||||
TODO:
|
||||
- Need to add regexp pattern for MIME media-type value of tEnclosure/type
|
||||
- Need to add regexp pattern for checking contents of guid is a URL when isPermaLink=true"
|
||||
- Need to add some form of constraint to check on an item that one, or other, or both of title and description are present.
|
||||
However, I'm not sure it is possible to represent these constraints in XML Schema language alone.
|
||||
- Need some way to enforce cardinality constraints preventing repeated elements in channels or items
|
||||
- Unfortunately the bug-fix for issue 10231 made this schema non-deterministic with respect to extensibitity elements.
|
||||
We can't tell whether an extension element in tRssChannel is within the choice or after the item elements.
|
||||
Need to reconsider the solution to bug-fix 10231.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Change Log:
|
||||
Date Revision Description
|
||||
31-Mar-2003 1 Initial version released for comment
|
||||
31-Mar-2003 2 Changes based on feedback from Gudge:
|
||||
- Remove targetNamespace="" and use elemenfFormDefault="unqualified" instead
|
||||
- Use namespace="##other" on <any>'s to create a more deterministic data model.
|
||||
- Added missing xs:documentation inside xs:annotation at the schema level.
|
||||
- Use xs:language for ISO Language Codes in <language> element.
|
||||
- Change guid to a single declaration. This loses some of the checking of the
|
||||
URL when the contents of the guid is a permaLink, so we will need to add
|
||||
that back in with a regexp pattern.
|
||||
14-Apr-2003 3 Changes to solve some element ordering problems.
|
||||
- Use xs:all in place of xs:sequence to support flexible ordering of elements.
|
||||
Although the ordering constraints for elements is not clear from the
|
||||
original specification, the custom and practice seems to be that
|
||||
element ordering is freeform.
|
||||
- Use elemenfFormDefault="qualified" for explicit intent.
|
||||
15-Apr-2003 4 Changes to solve some element ordering problems.
|
||||
- Use xs:choice in place of xs:all as previous usage of <all> was invalid.
|
||||
This creates the problem that unsufficient constraints can be applied
|
||||
by the schema - for example, it can't prevent two title elements for an item.
|
||||
- Use elemenfFormDefault="unqualified" for to get the correct behavious
|
||||
when importing and combining schemas.
|
||||
15-Apr-2003 5 Putting the extensibility element inside the repeating choice solves
|
||||
all problems with element ordering.
|
||||
15-Apr-2003 6 - skipHours and skipDays should contain a nested list of values,
|
||||
not just a single value.
|
||||
- Added version attribute to schema definition.
|
||||
- Corrected type of the cloud element
|
||||
25-Apr-2003 7 - Add regexp for RFC-822 date suggested by Sam Ruby
|
||||
- I had to leave the base type of the tRfc822FormatDate type
|
||||
as xs:string due to the problems with using
|
||||
a pattern with xs:dateTime described at
|
||||
http://www.thearchitect.co.uk/weblog/archives/2003/04/000142.html
|
||||
19-Jun-2003 8 - Fixed a bug the Oxygen XML Editor spotted in the regexp for RFC-822 dates
|
||||
23-Jun-2003 9 - Added legal boilerplate license text for LGPL.
|
||||
- Minor formatting changes.
|
||||
24-Jun-2003 10 - Missing types for item/title and item/description - Spotted by Andreas Schwotzer.
|
||||
01-Jan-2008 11 - Copy made available under the Microsoft Public License (MS-PL).
|
||||
25-May-2008 12 - Bug fix 10231 from Ken Gruven - channel can contain zero or more items.
|
||||
06-Sep-2008 13 - Fixed tab-space whitespace issues. Now always use spaces.
|
||||
- Undid the fix for bug-fix 10231 since it made the schema non-deterministic
|
||||
with respect to extensibility eleemnts in tRssChannel - need to reconsider the fix.
|
||||
08-Sep-2008 14 - Removed 't' prefixes from type names to improve class names
|
||||
that get code-generated from the schema.
|
||||
22-Sep-2008 15 - Move type def for rss element in-line for improved compativility with Java 1.6 tools.
|
||||
01-Nov-2008 16 - Added the missing rating element from the spec to RssChannel.
|
||||
-->
|
||||
|
||||
</xs:schema>
|
60
workflow/show2youtube.bash
Executable file
60
workflow/show2youtube.bash
Executable file
@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# http://eddmann.com/posts/uploading-podcast-audio-to-youtube/
|
||||
# http://cutycapt.sourceforge.net/ xvfb-run
|
||||
# https://el-tramo.be/blog/ken-burns-ffmpeg/
|
||||
|
||||
#hpr${ep_num}.wav
|
||||
|
||||
ep_num=2463
|
||||
|
||||
xvfb-run --server-args="-screen 0, 1024x768x24" CutyCapt --url="http://hackerpublicradio.org/eps.php?id=${ep_num}" --out=${ep_num}.png --insecure
|
||||
|
||||
ffmpeg -i input.wav -filter_complex "[0:a]showwaves=s=800x600:mode=line:rate=25,format=yuv420p[v]" -map "[v]" -map 0:a output-showwaves.mp4
|
||||
|
||||
|
||||
# ffmpeg -start_number n -i test_%d.jpg -vcodec mpeg4 test.avi
|
||||
# ffmpeg -loop 1 -r 2 -i image.jpg -i input.mp3 -vf scale=-1:380 -c:v libx264 -preset slow -tune stillimage -crf 18 -c:a copy -shortest -pix_fmt yuv420p -threads 0 output.mkv
|
||||
|
||||
# https://video.stackexchange.com/questions/9644/how-do-i-turn-audio-into-video-that-is-show-the-waveforms-in-a-video
|
||||
|
||||
#this
|
||||
#
|
||||
# rm out.mp4 output.mp4 ; ffmpeg -i 2463.png -filter_complex "pad=w=9600:h=6000:x='(ow-iw)/2':y='(oh-ih)/2',zoompan=x='(iw-0.625*ih)/2':y='(1-on/(25*4))*(ih-ih/zoom)':z='if(eq(on,1),2.56,zoom+0.002)':d=25*4:s=1280x800" -pix_fmt yuv420p -c:v libx264 out.mp4
|
||||
#
|
||||
# rm out.mp4 output.mp4; ffmpeg -i 2463.png -filter_complex "pad=w=9600:h=6000:x='(ow-iw)/2':y='(oh-ih)/2',zoompan=z='zoom+0':d=25*4:s=1280x2048,crop=w=1280:h=800:x='(iw-ow)/2':y='(ih-oh)/2' " -pix_fmt yuv420p -c:v libx264 out.mp4
|
||||
#
|
||||
# ffmpeg -i in.jpg
|
||||
# -filter_complex
|
||||
# "zoompan=z='zoom+0.002':d=25*4:s=1280x800"
|
||||
# -pix_fmt yuv420p -c:v libx264 out.mp4
|
||||
#
|
||||
#
|
||||
#
|
||||
# ffmpeg -i input.flac -filter_complex "[0:a]ahistogram,format=yuv420p[v]" -map "[v]" -map 0:a output.mp4
|
||||
#
|
||||
#
|
||||
# ffmpeg -i input -i background.png -filter_complex "[0:a]showwavespic=s=640x240[fg];[1:v][top]overlay=format=auto" -frames:v 1 output.png
|
||||
|
||||
|
||||
ffmpeg -loop 1 -i background.png -i video1.mp4 -i video2.mp4 -filter_complex \
|
||||
"[1:v]scale=(iw/2)-20:-1[a]; \
|
||||
[2:v]scale=(iw/2)-20:-1[b]; \
|
||||
[0:v][a]overlay=10:(main_h/2)-(overlay_h/2):shortest=1[c]; \
|
||||
[c][b]overlay=main_w-overlay_w-10:(main_h/2)-(overlay_h/2)[video]" \
|
||||
-map "[video]" output.mkv
|
||||
|
||||
|
||||
https://stackoverflow.com/questions/13390714/superimposing-two-videos-onto-a-static-image
|
||||
https://video.stackexchange.com/questions/14519/add-image-under-the-video-with-ffmepg
|
||||
|
||||
|
||||
ffmpeg \
|
||||
-loop 1 -i hprback.png \
|
||||
-i output-showwaves-320x240.mp4 \
|
||||
-filter_complex "overlay=0:0:shortest=1" \
|
||||
out.m4v
|
||||
|
||||
|
||||
# ffmpeg -i input.wav -filter_complex "[0:a]showfreqs=s=320x266:mode=line:fscale=log,format=yuv420p[v]" -map "[v]" -map 0:a output-showfreqs-319x266.mp4
|
||||
# ffmpeg -loop 1 -i hprback.png -i output-showfreqs-319x266.mp4 -filter_complex "overlay=663:143:shortest=1" out1.m4v
|
||||
|
233
workflow/status_lighttower.py
Normal file
233
workflow/status_lighttower.py
Normal file
@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (C) 2017 Ken Fallon ken@fallon.ie
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# UTF-8 Test >> ÇirçösÚáóíéőöÓÁśł <<
|
||||
# 213.46.252.136 gateway
|
||||
|
||||
import sys, getopt, json, os, logging, sys, requests, datetime, re, dateutil.parser, base64, xmltodict, random, psycopg2, time, automationhat
|
||||
|
||||
from pprint import pprint
|
||||
#import traceback
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
#logging.disable(logging.DEBUG)
|
||||
settings = dict()
|
||||
|
||||
def usage(exitcode, message = None):
|
||||
'''
|
||||
prints usage and exits
|
||||
'''
|
||||
if message is not None:
|
||||
logging.error( message )
|
||||
print( os.path.basename(sys.argv[0]) + ' [--help -l <lab> -c <config> ]')
|
||||
sys.exit(exitcode)
|
||||
|
||||
def get_lab_settings(settings):
|
||||
'''
|
||||
Get configuration for the lab, usually from http://172.30.218.244/autotest/settings.json
|
||||
'''
|
||||
if os.path.isfile( settings[ 'config_url' ] ):
|
||||
with open( settings[ 'config_url' ] ) as json_file:
|
||||
info_dict = json.load(json_file)
|
||||
return info_dict
|
||||
else:
|
||||
session = requests.session()
|
||||
resp = session.get(url=settings[ 'config_url' ], headers={'Accept-Encoding': 'gzip'})
|
||||
if resp.status_code == 200:
|
||||
info_dict = json.loads(resp.text)
|
||||
return info_dict
|
||||
return None
|
||||
|
||||
def argumentTest():
|
||||
'''
|
||||
Gets the required paramaters that the program needs in order to run. These can either be provided on the command line, or as environemental variables, or both.
|
||||
|
||||
export LAB="lab4b"
|
||||
export config_url="http://172.30.218.244/autotest/settings.json"
|
||||
export service="purcha#!/usr/bin/env python
|
||||
|
||||
import automationhat
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
|
||||
counter = -1
|
||||
lightPattern = [
|
||||
[1,0,0],
|
||||
[0,1,0],
|
||||
[0,0,1],
|
||||
[0,1,1],
|
||||
[0,1,0],
|
||||
[1,1,0],
|
||||
[1,0,0],
|
||||
[1,1,0],
|
||||
[0,1,0],
|
||||
[0,1,1],
|
||||
[0,0,1],
|
||||
[0,0,0],
|
||||
[1,0,1],
|
||||
[0,1,0],
|
||||
[0,0,0],
|
||||
]
|
||||
|
||||
def increment():
|
||||
global counter
|
||||
counter+=1
|
||||
if counter==len(lightPattern):
|
||||
counter = 0
|
||||
|
||||
def doCurrentCycle():
|
||||
currentCycle = lightPattern[counter]
|
||||
print currentCycle
|
||||
if (currentCycle[0]==1) :
|
||||
automationhat.output.one.on()
|
||||
else:
|
||||
automationhat.output.one.off()
|
||||
|
||||
if (currentCycle[1] == 1):
|
||||
automationhat.output.two.on()
|
||||
else:
|
||||
automationhat.output.two.off()
|
||||
|
||||
if (currentCycle[2] == 1):
|
||||
automationhat.output.three.on()
|
||||
else:
|
||||
automationhat.output.three.off()
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
|
||||
while True:
|
||||
increment()
|
||||
doCurrentCycle()se-service"
|
||||
export customers="customers.json"
|
||||
|
||||
'''
|
||||
lab = str()
|
||||
config_url = str()
|
||||
service = str()
|
||||
settings = dict()
|
||||
|
||||
for evariable in [ 'LAB', 'config_url' ]:
|
||||
if os.getenv( evariable ) is not None:
|
||||
settings[ evariable ] = os.environ[ evariable ]
|
||||
logging.debug( "Setting \"" + evariable + "\" set to \"" + settings[ evariable ] + "\" from an enviroment variable.")
|
||||
|
||||
try:
|
||||
options, remainder = getopt.getopt(sys.argv[1:], 'hl:c:', [ 'help', 'lab=', 'config=' ])
|
||||
|
||||
except getopt.GetoptError:
|
||||
usage(1, "Unrecognized option was found or missing argument." )
|
||||
|
||||
for opt, arg in options:
|
||||
if opt in ('-h', '--help'):
|
||||
usage(2)
|
||||
elif opt in ('-l', '--lab'):
|
||||
logging.debug( "Setting \"LAB\" set to \"" + arg + "\" from the \"" + opt + "\" command line argument.")
|
||||
settings[ 'LAB' ] = arg
|
||||
elif opt in ('-c', '--config'):
|
||||
logging.debug( "Setting \"config_url\" set to \"" + arg + "\" from the \"" + opt + "\" command line argument.")
|
||||
settings[ 'config_url' ] = arg
|
||||
|
||||
for param in [ 'LAB', 'config_url' ]:
|
||||
if not param in settings:
|
||||
usage( 3, "Cant find value for \"" + param +"\"")
|
||||
|
||||
try:
|
||||
settings[ 'config' ] = get_lab_settings( settings )[ settings[ 'LAB' ] ]
|
||||
if 'jenkins' in settings.keys():
|
||||
logging.debug('Updating TraxIS Customer file to include the Purchase Service status.')
|
||||
|
||||
except Exception as e:
|
||||
print('Error: Could not get Lab Configuration for "%s".' % settings[ 'LAB' ] )
|
||||
traceback.print_exc()
|
||||
exit(1)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
#def get_server_instance():
|
||||
#jenkins_url = 'http://172.22.137.160:8080'
|
||||
#server = Jenkins(jenkins_url, username='autotest', password='autotest')
|
||||
#return server
|
||||
|
||||
def get_server_instance(settings):
|
||||
jenkins_url = 'http://%s:%s' % ( settings[ 'config' ][ 'jenkins' ][ 'host' ] , settings[ 'config' ][ 'jenkins' ][ 'port' ] )
|
||||
server = Jenkins(jenkins_url, username = settings[ 'config' ][ 'jenkins' ][ 'user' ], password = settings[ 'config' ][ 'jenkins' ][ 'password' ])
|
||||
return server
|
||||
|
||||
def alloff():
|
||||
automationhat.relay.one.off()
|
||||
automationhat.output.one.off()
|
||||
automationhat.output.two.off()
|
||||
automationhat.output.three.off()
|
||||
|
||||
# Set according to status
|
||||
def setstatus(status):
|
||||
if status != "":
|
||||
alloff()
|
||||
if status == "SUCCESS":
|
||||
automationhat.output.three.on()
|
||||
print( "SUCCESS" )
|
||||
|
||||
if status == "UNSTABLE":
|
||||
automationhat.output.two.on()
|
||||
print( "UNSTABLE" )
|
||||
|
||||
if status == "FAILURE":
|
||||
automationhat.output.one.on()
|
||||
print( "FAILURE" )
|
||||
return
|
||||
|
||||
def get_job_details( settings ):
|
||||
# Refer Example #1 for definition of function 'get_server_instance'
|
||||
server = get_server_instance( settings )
|
||||
settings[ 'teststatus' ] = str()
|
||||
for job in settings[ 'config' ][ 'jenkins' ][ 'jobs' ]:
|
||||
logging.debug( "Checking job %s" % job )
|
||||
job_instance = server.get_job( job )
|
||||
this_status = job_instance.get_last_build().get_status()
|
||||
if not ( settings[ 'teststatus' ] == "FAILURE" or (settings[ 'teststatus' ] == "UNSTABLE" and this_status == "SUCCESS") ):
|
||||
settings[ 'teststatus' ] = this_status
|
||||
return settings
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.debug( "start" )
|
||||
try:
|
||||
while True:
|
||||
alloff()
|
||||
automationhat.relay.one.on()
|
||||
settings = argumentTest()
|
||||
#pprint( settings[ 'config' ][ 'jenkins' ] )
|
||||
#print( "Hello" )
|
||||
#pprint ( get_server_instance( settings ).version )
|
||||
settings = get_job_details( settings )
|
||||
status = settings[ 'teststatus' ]
|
||||
setstatus(status)
|
||||
#print( get_server_instance().version )
|
||||
time.sleep(300)
|
||||
|
||||
except Exception as e:
|
||||
print("Error: " + str(e))
|
||||
traceback.print_exc()
|
||||
exit(1)
|
||||
logging.debug('done')
|
||||
|
||||
'''
|
||||
export LAB="lab4b"
|
||||
export config_url="http://172.30.218.244/autotest/settings.json"
|
||||
'''
|
18
workflow/this_duration.bash
Executable file
18
workflow/this_duration.bash
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# Copyright Ken Fallon - Released into the public domain. http://creativecommons.org/publicdomain/
|
||||
find ${pwd} -type f | while read mediafile
|
||||
do
|
||||
duration=$( mediainfo --full --Output=XML "${mediafile}" | xmlstarlet sel -T -t -m "_:MediaInfo/_:media/_:track[@type='Audio']/_:Duration[1]" -v "." -n - | awk -F '.' '{print $1}' )
|
||||
if [ "${duration}" != "" ]
|
||||
then
|
||||
echo "${duration} ${mediafile}"
|
||||
continue
|
||||
fi
|
||||
duration=$( /bin/date -ud "1970-01-01 $( ffprobe -i "${mediafile}" 2>&1| awk -F ': |, ' '/Duration:/ { print $2 }' )" +%s )
|
||||
if [ "${duration}" != 0 ]
|
||||
then
|
||||
echo "${duration} ${mediafile}"
|
||||
continue
|
||||
fi
|
||||
done
|
||||
find -type f -exec file {} \; | grep -vEi 'audio|mpeg|video'
|
638
workflow/transfer_tags
Executable file
638
workflow/transfer_tags
Executable file
@ -0,0 +1,638 @@
|
||||
#!/usr/bin/perl
|
||||
#===============================================================================
|
||||
#
|
||||
# FILE: transfer_tags
|
||||
#
|
||||
# USAGE: ./transfer_tags masterfile
|
||||
#
|
||||
# DESCRIPTION: Transfer ID3 (or equivalent) tags from a base file to
|
||||
# whichever of FLAC, MP3, OGG, SPX and WAV versions are found.
|
||||
#
|
||||
# OPTIONS: ---
|
||||
# REQUIREMENTS: ---
|
||||
# BUGS: ---
|
||||
# NOTES: ---
|
||||
# AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
|
||||
# VERSION: 1.4.2
|
||||
# CREATED: 31/03/2013 14:18:55
|
||||
# REVISION: 24/05/2013 15:54:36
|
||||
#
|
||||
#===============================================================================
|
||||
|
||||
use 5.010;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Basename;
|
||||
use File::Find::Rule;
|
||||
use Audio::TagLib;
|
||||
|
||||
#
|
||||
# Version number (manually incremented)
|
||||
#
|
||||
our $VERSION = '1.4.2';
|
||||
|
||||
#
|
||||
# Declarations
|
||||
#
|
||||
my ( $ref, $tag, $tags, %mastertags, %slavetags, $changed );
|
||||
my ( $directories, $filename, $suffix );
|
||||
|
||||
#
|
||||
# The extensions the various codings are expected to have (it looks both for
|
||||
# the lower- and upper-case versions, but only the lower-case ones are
|
||||
# required here)
|
||||
#
|
||||
my @exts = qw{ flac mp3 ogg spx wav };
|
||||
|
||||
#
|
||||
# The additional name variants we'll accept
|
||||
#
|
||||
my @variants = qw{ _mez };
|
||||
|
||||
#
|
||||
# Used to test tag conformity. The key is the tagname, the value is a hashref
|
||||
# containing a regex (key 're') for detecting conformity and the correct value
|
||||
# (key 'ok').
|
||||
# To be passed to subroutine 'checkConformity', but kept here for ease of
|
||||
# maintenance.
|
||||
#
|
||||
my %tag_control = (
|
||||
album => { re => qr{^Hacker Public Radio$}, ok => 'Hacker Public Radio' },
|
||||
comment => {
|
||||
re => qr{^http://hackerpublicradio\.org/?},
|
||||
ok => 'http://hackerpublicradio.org'
|
||||
},
|
||||
genre => { re => qr{(?i)^Podcast$}, ok => 'Podcast' },
|
||||
);
|
||||
|
||||
#
|
||||
# The Audio::TagLib methods to call for each tag manipulated by the script.
|
||||
# The number after the method name is 1 if the value being set is a string,
|
||||
# and zero otherwise.
|
||||
#
|
||||
my %tagmethods = (
|
||||
album => [ 'setAlbum', 1 ],
|
||||
artist => [ 'setArtist', 1 ],
|
||||
comment => [ 'setComment', 1 ],
|
||||
genre => [ 'setGenre', 1 ],
|
||||
title => [ 'setTitle', 1 ],
|
||||
track => [ 'setTrack', 0 ],
|
||||
year => [ 'setYear', 0 ],
|
||||
);
|
||||
|
||||
#
|
||||
# Because Audio::TagLib::FileRef does not seem to commit the tag update until
|
||||
# very late (during the DESTROY?) it's very difficult to update file times
|
||||
# _after_ the tags have been written. The solution is to save all of the tag
|
||||
# hashes (which contain the file path and times) and process then in the END{}
|
||||
# block. Dirty, but very Perl-ish.
|
||||
#
|
||||
my @tag_stash;
|
||||
|
||||
#
|
||||
# Script name
|
||||
#
|
||||
( my $PROG = $0 ) =~ s|.*/||mx;
|
||||
|
||||
#
|
||||
# Ensure STDOUT is in UTF8 mode
|
||||
#
|
||||
binmode( STDOUT, ":encoding(utf8)" );
|
||||
|
||||
#
|
||||
# Get the argument, the "master" file
|
||||
#
|
||||
my $masterfile = shift;
|
||||
die "Usage: $PROG masterfilename\n" unless $masterfile;
|
||||
|
||||
#
|
||||
# Check the file exists
|
||||
#
|
||||
die "$masterfile does not exist\n" unless ( -e $masterfile );
|
||||
|
||||
#
|
||||
# Assume there are other versions of the same file with suffixes in the set
|
||||
# { flac, mp3, ogg, spx, wav } so build these names from the master file name.
|
||||
# Start by parsing the filename into its directories, filename and suffix.
|
||||
# Remove the leading dot from the suffix.
|
||||
#
|
||||
($filename,$directories,$suffix) = fileparse($masterfile,qr/\.[^.]*/);
|
||||
$suffix =~ s/^\.//;
|
||||
|
||||
#
|
||||
# Reject the file if it doesn't have an expected suffix (we're not
|
||||
# case-sensitive here)
|
||||
#
|
||||
die "$masterfile does not have a recognised suffix (expecting "
|
||||
. join( ", ", @exts ) . ")\n"
|
||||
unless ( grep {/$suffix/i} @exts );
|
||||
|
||||
#
|
||||
# Use File::Find::Rule to find all the files that match a regular expression.
|
||||
# This is built from the parsed file, with an optional list of variants and
|
||||
# all of the extensions (case insensitively). We then remove the master file
|
||||
# from the list and we're done.
|
||||
#
|
||||
my $re
|
||||
= "^${filename}("
|
||||
. join( '|', @variants ) . ")?\.(?i:"
|
||||
. join( '|', @exts ) . ')$';
|
||||
my @files = grep { !/^$masterfile$/ }
|
||||
File::Find::Rule->file()->name(qr{$re})->in($directories);
|
||||
|
||||
#
|
||||
# Log the file
|
||||
#
|
||||
print "$masterfile\n";
|
||||
|
||||
#
|
||||
# Collect the tags from the master file (& stash them for later too)
|
||||
#
|
||||
( $ref, $tag, $tags ) = collectTags($masterfile);
|
||||
%mastertags = %$tags;
|
||||
push( @tag_stash, $tags );
|
||||
|
||||
#
|
||||
# Report the tags found in the master file
|
||||
#
|
||||
reportTags( \%mastertags );
|
||||
|
||||
#
|
||||
# Check that the master file conforms to the HPR standards, ensuring that any
|
||||
# changes are returned from the routine
|
||||
#
|
||||
( $ref, $tag, $tags ) = checkConformity( $ref, $tag, $tags, \%tag_control );
|
||||
%mastertags = %$tags;
|
||||
print '=' x 80, "\n";
|
||||
|
||||
#
|
||||
# Now process the "slave" files
|
||||
#
|
||||
foreach my $file (@files) {
|
||||
#
|
||||
# Check the "slave" file exists and report it if so
|
||||
#
|
||||
if ( -r $file ) {
|
||||
print "$file\n";
|
||||
}
|
||||
else {
|
||||
warn "$file is not readable\n";
|
||||
next;
|
||||
}
|
||||
|
||||
#
|
||||
# Get the "slave" file's tags (& keep a copy in the stash)
|
||||
#
|
||||
( $ref, $tag, $tags ) = collectTags($file);
|
||||
%slavetags = %$tags;
|
||||
push( @tag_stash, $tags );
|
||||
|
||||
#
|
||||
# Report the tags
|
||||
#
|
||||
reportTags( \%slavetags );
|
||||
|
||||
#
|
||||
# Change the tags to match the "master" file's tags
|
||||
#
|
||||
$changed = 0;
|
||||
for my $t ( sort( grep { !/^_/ } keys(%mastertags) ) ) {
|
||||
$changed += changeTag( $tag, $t, $slavetags{$t}, $mastertags{$t},
|
||||
@{ $tagmethods{$t} } );
|
||||
}
|
||||
print '-' x 80, "\n";
|
||||
|
||||
#
|
||||
# Save any changes
|
||||
#
|
||||
if ($changed) {
|
||||
$ref->save();
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Post-processing of file times
|
||||
#
|
||||
# Process all files we've visited (whether their tags were changed or not)
|
||||
# and force the 'atime' and 'mtime' back to their starting values. This will
|
||||
# be done after the rest of the script runs, when we know that all of the
|
||||
# Audio::TagLib::FileRef objects have been destroyed and have done their lazy
|
||||
# updates.
|
||||
#-------------------------------------------------------------------------------
|
||||
END {
|
||||
for my $t (@tag_stash) {
|
||||
warn "Time restoration failed on $t->{_path}\n"
|
||||
unless restoreTimes($t);
|
||||
}
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: collectTags
|
||||
# PURPOSE: Collects tags from a media file
|
||||
# PARAMETERS: $filepath Path to the file
|
||||
# RETURNS: A list containing an Audio::TagLib::FileRef object, an
|
||||
# Audio::TagLib::Tag object (containing the actual tags) and
|
||||
# a hashref containing the converted tag values (along with
|
||||
# a few other attributes).
|
||||
# DESCRIPTION: Collects the tags and the timestamps from the file. Then the
|
||||
# various tags and other attributes are placed in a hash which
|
||||
# will be returned to the caller. The non-tag keys begin with
|
||||
# '_' to differentiate them.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub collectTags {
|
||||
my ($filepath) = @_;
|
||||
|
||||
my ( $atime, $mtime ) = ( stat($filepath) )[ 8, 9 ];
|
||||
|
||||
my $fileref = Audio::TagLib::FileRef->new($filepath);
|
||||
my $ftag = $fileref->tag();
|
||||
|
||||
my %tags = (
|
||||
album => $ftag->album()->toCString(),
|
||||
artist => $ftag->artist()->toCString(),
|
||||
comment => $ftag->comment()->toCString(),
|
||||
genre => $ftag->genre()->toCString(),
|
||||
title => $ftag->title()->toCString(),
|
||||
track => $ftag->track(),
|
||||
year => $ftag->year(),
|
||||
_path => $filepath,
|
||||
_atime => $atime,
|
||||
_mtime => $mtime,
|
||||
);
|
||||
|
||||
return ( $fileref, $ftag, \%tags );
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: reportTags
|
||||
# PURPOSE: Print the tags in a hash
|
||||
# PARAMETERS: $tags Hashref keyed by tagname and containing tag
|
||||
# contents from a media file
|
||||
# RETURNS: Nothing
|
||||
# DESCRIPTION: Just prints all the "proper" tags held in the hash argument in
|
||||
# alphabetical order of the keys. Note that the "secret" keys,
|
||||
# those begining with '_', are skipped. See 'collectTags' for
|
||||
# what they are.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub reportTags {
|
||||
my ($tags) = @_;
|
||||
|
||||
my @keys = sort( grep { !/^_/ } keys(%$tags) );
|
||||
|
||||
for my $key (@keys) {
|
||||
printf "%-10s: %s\n", $key, $tags->{$key};
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: changeTag
|
||||
# PURPOSE: Changes a tag to a new value if appropriate
|
||||
# PARAMETERS: $tag Audio::TagLib::Tag object
|
||||
# $tagname Name of tag
|
||||
# $oldValue Current value of tag
|
||||
# $newValue New value of tag or undefined
|
||||
# $setFunc String containing the name of the 'set'
|
||||
# function
|
||||
# $isString True if the value being set is a string
|
||||
# RETURNS: 1 if a change has been made, 0 otherwise
|
||||
# DESCRIPTION: Performs some argument checks, returning on a missing new
|
||||
# value, or if the old and new values are the same. The old and
|
||||
# new values may be encoded integers, so we look for this
|
||||
# eventuality. After all of this we know there's a change to be
|
||||
# made and perform the appropriate steps to make it.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub changeTag {
|
||||
my ( $tag, $tagname, $oldValue, $newValue, $setFunc, $isString ) = @_;
|
||||
|
||||
return 0 unless defined($newValue);
|
||||
return 0 if $oldValue eq $newValue;
|
||||
|
||||
$isString = 0 unless defined($isString);
|
||||
|
||||
if ( !$isString ) {
|
||||
return 0 if int($oldValue) == int($newValue);
|
||||
}
|
||||
|
||||
print ">> Changing $tagname to '$newValue'\n";
|
||||
$tag->$setFunc(
|
||||
( $isString
|
||||
? Audio::TagLib::String->new($newValue)
|
||||
: $newValue
|
||||
)
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: restoreTimes
|
||||
# PURPOSE: Restore the original times to a file which has had its tags
|
||||
# changed
|
||||
# PARAMETERS: $tags Hashref keyed by tagname and containing tag
|
||||
# contents (and file attributes) from a media
|
||||
# file. The file details have keys beginning
|
||||
# with '_'.
|
||||
# RETURNS: Number of files changed (see 'utime')
|
||||
# DESCRIPTION: Uses the Perl 'utime' function to change the file's access
|
||||
# time and modification time to whatever is in the hash. These
|
||||
# are expected to be the times the file had when it was first
|
||||
# encountered.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub restoreTimes {
|
||||
my ($tags) = @_;
|
||||
|
||||
return utime( $tags->{_atime}, $tags->{_mtime}, $tags->{_path} );
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: checkConformity
|
||||
# PURPOSE: Check that the master file has conforming tags, fixing them if
|
||||
# not
|
||||
# PARAMETERS: $ref Audio::TagLib::FileRef relating to the master
|
||||
# file
|
||||
# $tag Audio::TagLib::Tag containing the tags of the
|
||||
# master file
|
||||
# $tags Hashref containing the converted tags (and
|
||||
# a few other odds and sods)
|
||||
# $kosher Hashref containing the checking values (see
|
||||
# %tag_control in the main program)
|
||||
# RETURNS: A list containing $ref, $tag and $tags as described above
|
||||
# DESCRIPTION: Implements a number of complex rules. Firstly the 'genre' tag
|
||||
# is expected to contain 'Podcast'. Secondly the 'album' tag
|
||||
# must contain 'Hacker Public Radio'. If it does not then the
|
||||
# value is stored for later then replaced. Finally the
|
||||
# 'comment' tag must begin with 'http://hackerpublicradio.org'.
|
||||
# If it does not its current contents are stored and replaced
|
||||
# with the required URL. However, the comment tag will also
|
||||
# contain the saved album tag (if any) and the saved comment,
|
||||
# and these will be placed at the end.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: This code is ugly and difficult to extend and maintain.
|
||||
# TODO look into ways of improving it!
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub checkConformity {
|
||||
my ( $ref, $tag, $tags, $kosher ) = @_;
|
||||
|
||||
my $changed = 0;
|
||||
my %saved;
|
||||
my ( $t, $commentOK, $newval );
|
||||
|
||||
#
|
||||
# The 'genre' tag
|
||||
#
|
||||
$t = 'genre';
|
||||
unless ( $tags->{$t} =~ /$kosher->{$t}->{re}/ ) {
|
||||
$changed += changeTag(
|
||||
$tag, $t, $tags->{$t},
|
||||
$kosher->{$t}->{ok},
|
||||
@{ $tagmethods{$t} }
|
||||
);
|
||||
$tags->{genre} = $tag->genre()->toCString();
|
||||
}
|
||||
|
||||
#
|
||||
# The 'album' tag. We save this one for adding to the comment
|
||||
#
|
||||
$t = 'album';
|
||||
unless ( $tags->{$t} =~ /$kosher->{$t}->{re}/ ) {
|
||||
( $saved{$t} = $tags->{$t} ) =~ s/(^\s+|\s+$)//g;
|
||||
$changed += changeTag(
|
||||
$tag, $t, $tags->{$t},
|
||||
$kosher->{$t}->{ok},
|
||||
@{ $tagmethods{$t} }
|
||||
);
|
||||
$tags->{album} = $tag->album()->toCString();
|
||||
}
|
||||
|
||||
#
|
||||
# If the 'comment' is non-standard *or* if the 'album' was changed we want
|
||||
# to do stuff here. We make sure the 'comment' is good and append the
|
||||
# original 'album' and 'comment' as appropriate.
|
||||
#
|
||||
$t = 'comment';
|
||||
$commentOK = $tags->{$t} =~ /$kosher->{$t}->{re}/;
|
||||
unless ( !$changed && $commentOK ) {
|
||||
( $saved{$t} = $tags->{$t} ) =~ s/(^\s+|\s+$)//g;
|
||||
|
||||
if ($changed) {
|
||||
if ($commentOK) {
|
||||
# Album had errors, comment is OK
|
||||
$newval = concat( ", ", $saved{comment}, $saved{album} );
|
||||
}
|
||||
else {
|
||||
# Album had errors, comment also
|
||||
$newval = concat( ", ", $kosher->{$t}->{ok},
|
||||
$saved{album}, $saved{comment} );
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Comment had errors, album OK
|
||||
$newval = concat( ", ", $kosher->{$t}->{ok}, $saved{comment} );
|
||||
}
|
||||
|
||||
$changed += changeTag( $tag, $t, $tags->{$t},
|
||||
$newval, @{ $tagmethods{$t} } );
|
||||
$tags->{comment} = $tag->comment()->toCString();
|
||||
}
|
||||
|
||||
#
|
||||
# Save any changes
|
||||
#
|
||||
if ($changed) {
|
||||
$ref->save();
|
||||
}
|
||||
|
||||
#
|
||||
# Return tag-related stuff so the caller can get the benefit
|
||||
#
|
||||
return ( $ref, $tag, $tags );
|
||||
|
||||
}
|
||||
|
||||
#=== FUNCTION ================================================================
|
||||
# NAME: concat
|
||||
# PURPOSE: Reimplementation of join but with any undefined or empty
|
||||
# arguments removed
|
||||
# PARAMETERS: $sep The string to be used to separate elements in
|
||||
# the result
|
||||
# [variable args] Any number of arguments to be joined together
|
||||
# with the separator
|
||||
# RETURNS: The concatenated arguments
|
||||
# DESCRIPTION: Giving 'join' an array that may contain undefined elements will
|
||||
# result in empty results in the output string and error
|
||||
# messages as the undefined elements are processed. Giving it
|
||||
# empty string elements will result in dangling separators in
|
||||
# the output. This routine removes the undefined and empty
|
||||
# elements before joining the rest.
|
||||
# THROWS: No exceptions
|
||||
# COMMENTS: None
|
||||
# SEE ALSO:
|
||||
#===============================================================================
|
||||
sub concat {
|
||||
my $sep = shift;
|
||||
|
||||
my @args = grep { defined($_) && length($_) > 0 } @_;
|
||||
|
||||
return join( $sep, @args );
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
# Application Documentation
|
||||
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#{{{
|
||||
|
||||
=head1 NAME
|
||||
|
||||
transfer_tags - standardise and transfer tags between HPR audio files
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
This documentation refers to I<transfer_tags> version 1.4.2
|
||||
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
transfer_tags masterfile
|
||||
|
||||
|
||||
=head1 REQUIRED ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<masterfile>
|
||||
|
||||
This is the name of the audio file, which contains the definitive tags which
|
||||
are to be copied to all of the other files of the same name but different
|
||||
extensions.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The script transfers ID3 (or equivalent) tags from a base file to whichever of
|
||||
FLAC, MP3, OGG, SPX and WAV versions are found. The tags copied are: B<album>,
|
||||
B<artist>, B<comment>, B<genre>, B<title>, B<track> and B<year>. The target
|
||||
files are determined by taking the name of the B<master file> without its
|
||||
extension and appending all of the remaining extensions in the list. Files
|
||||
with the string "B<_mez>" between the filename and the extension are also
|
||||
included.
|
||||
|
||||
For example: if the B<master file> is called B<hpr1234.flac> and FLAC, MP3,
|
||||
OGG, SPX and WAV versions exist, the tags found in the file B<hpr1234.flac>
|
||||
are copied to B<hpr1234.mp3>, B<hpr1234.ogg>, B<hpr1234.spx> and
|
||||
B<hpr1234.wav>. If B<hpr1234_mez.mp3> or any other variant existed it would
|
||||
also receive a copy of the tags.
|
||||
|
||||
A certain amount of manipulation is performed before the tags are propagated.
|
||||
The changes made conform to certain rules, which are:
|
||||
|
||||
=over 4
|
||||
|
||||
=item .
|
||||
|
||||
The B<genre> tag must contain the string "I<Podcast>".
|
||||
|
||||
=item .
|
||||
|
||||
The B<album> tag must contain the string "I<Hacker Public Radio>". If it does
|
||||
not then the existing value is stored for later and is then replaced.
|
||||
|
||||
=item .
|
||||
|
||||
The B<comment> tag must begin with the string
|
||||
"I<http://hackerpublicradio.org>". If it does not its current contents are
|
||||
stored and replaced with the required URL. However, the comment tag will also
|
||||
contain the saved album tag (if any) and the saved comment (if any), and these
|
||||
will be placed at the end, separated by commas.
|
||||
|
||||
=back
|
||||
|
||||
The script saves the access time and modification time of all of the media
|
||||
files it processes. It then restores these times at the end of its run. This
|
||||
prevents any external processes which depend on these file times from being
|
||||
confused by the tag changes.
|
||||
|
||||
=head1 DIAGNOSTICS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<Usage: transfer_tags masterfile>
|
||||
|
||||
This error is produced if the script is called without the mandatory argument.
|
||||
The error is fatal.
|
||||
|
||||
=item B<... does not exist>
|
||||
|
||||
The master file specified as the argument does not exist. The error is fatal.
|
||||
|
||||
=item B<... does not have a recognised suffix (expecting ...)>
|
||||
|
||||
The master file specified as the argument does not have one of the expected
|
||||
extensions (flac, mp3, ogg, spx, wav). The error is fatal.
|
||||
|
||||
=item B<... is not readable>
|
||||
|
||||
One of the target files was found not to be readable (probably due to file
|
||||
permissions). The script will ignore this file.
|
||||
|
||||
=item B<Time restoration failed on ...>
|
||||
|
||||
The master file or one of the target files could not have its time restored.
|
||||
The script will ignore this file.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 DEPENDENCIES
|
||||
|
||||
Audio::TagLib
|
||||
File::Basename
|
||||
File::Find::Rule
|
||||
|
||||
|
||||
=head1 BUGS AND LIMITATIONS
|
||||
|
||||
There are no known bugs in this module.
|
||||
Please report problems to Dave Morriss (Dave.Morriss@gmail.com).
|
||||
Patches are welcome.
|
||||
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Dave Morriss (Dave.Morriss@gmail.com) 2013
|
||||
|
||||
|
||||
=head1 LICENCE AND COPYRIGHT
|
||||
|
||||
Copyright (c) 2013 Dave Morriss (Dave.Morriss@gmail.com). All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the same terms as Perl itself. See perldoc perlartistic.
|
||||
|
||||
=cut
|
||||
|
||||
#}}}
|
||||
|
||||
# [zo to open fold, zc to close]
|
||||
|
||||
# vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker
|
44
workflow/unpack.bash
Normal file
44
workflow/unpack.bash
Normal file
@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
# Copyright Ken Fallon - Released into the public domain. http://creativecommons.org/publicdomain/
|
||||
intro_clip_end="00:01:30"
|
||||
quality_clip_duration="90"
|
||||
outro_clip="90"
|
||||
#DEBUG="echo "
|
||||
cd /mnt/DUMP/hpr-for-archive.org/todo/
|
||||
for FILEX in *
|
||||
do
|
||||
if [[ "$(file "$FILEX" | grep -i audio | wc -l )" -ne 1 && "$(mediainfo "$FILEX" | grep -i audio | wc -l )" -ne 1 ]]
|
||||
then
|
||||
echo "$FILEX is not an audio file"
|
||||
else
|
||||
echo "Processing $FILEX"
|
||||
fname=${FILEX%.*}
|
||||
ext=${FILEX/*./}
|
||||
# We get the druation from mediainfo in miliseconds and remove the last three digits to convert it to seconds
|
||||
# Then calculate the times of the end clip working back from the end of the file
|
||||
# We then pick a random segment between the intro and outro.
|
||||
# Finally we convert to hour minute second format and the date command is the easiest way to do that
|
||||
duration=$( mediainfo --full "${fname}.${ext}" | grep Duration | egrep -v ".*:.*:|ms|s|mn" | head -1 | awk -F ': ' '{print $2}' )
|
||||
duration="${duration:0:${#duration}-3}"
|
||||
outro_clip_start="$(($duration-$outro_clip))"
|
||||
quality_clip_end="$(($outro_clip_start-$quality_clip_duration))"
|
||||
quality_clip_start=$(shuf -i ${intro_clip_end}-${quality_clip_end} -n 1)
|
||||
quality_clip_end="$(($quality_clip_start+$quality_clip_duration))"
|
||||
duration=$(\date -d@${duration} -u +%H:%M:%S)
|
||||
quality_clip_start=$(\date -d@${quality_clip_start} -u +%H:%M:%S)
|
||||
quality_clip_end=$(\date -d@${quality_clip_end} -u +%H:%M:%S)
|
||||
outro_clip_start=$(\date -d@${outro_clip_start} -u +%H:%M:%S)
|
||||
echo -e "\tExtracting spectrogram"
|
||||
${DEBUG} sox "${fname}.${ext}" -n spectrogram -Y 130 -l -r -t "${fname}.${ext}" -o "${fname}_spectrogram.png"
|
||||
echo -e "\tExtracting intro clip from 0 to ${intro_clip_end}"
|
||||
${DEBUG} ffmpeg -y -ss 0 -t ${intro_clip_end} -i "${fname}.${ext}" "${fname}_intro_clip".wav 2>/dev/null
|
||||
echo -e "\tExtracting quality clip from ${quality_clip_start} to ${quality_clip_end}"
|
||||
${DEBUG} ffmpeg -y -ss ${quality_clip_start} -t ${quality_clip_end} -i "${fname}.${ext}" "${fname}_quality_clip".wav 2>/dev/null
|
||||
echo -e "\tExtracting outro clip from ${outro_clip_start} ${duration}"
|
||||
${DEBUG} ffmpeg -y -ss ${outro_clip_start} -t ${duration} -i "${fname}.${ext}" "${fname}_outro_clip".wav 2>/dev/null
|
||||
echo -e "\tCreating low fidelity version"
|
||||
${DEBUG} ffmpeg -y -i "${fname}.${ext}" temp.wav 2>/dev/null
|
||||
${DEBUG} sox "temp.wav" -c 1 -r 16000 -t wav - 2>/dev/null | speexenc - "${fname}_low_fidelity.spx" 2>/dev/null
|
||||
${DEBUG} rm temp.wav
|
||||
fi
|
||||
done
|
50
workflow/x.bash
Executable file
50
workflow/x.bash
Executable file
@ -0,0 +1,50 @@
|
||||
#cd /home/ken/processing/1659905303_3664_2022-08-18_037b55af6b77191b2610f5cedb5d2bd962f0251720bf0
|
||||
ttsserver="http://localhost:5500"
|
||||
ep_num="3664"
|
||||
|
||||
echo "This is Hacker Public Radio " > "hpr${ep_num}_summary.txt"
|
||||
|
||||
## Jump to encoding
|
||||
|
||||
function create_tts_summary {
|
||||
HPR_summary="$( cat "hpr${ep_num}_summary.txt" )"
|
||||
echo "INFO: Converting text \"${HPR_summary}\" to speech."
|
||||
curl -X 'GET' -G --data-urlencode "voice=coqui-tts:en_ljspeech" --data-urlencode "text=${HPR_summary}" --data-urlencode "vocoder=high" --data-urlencode "denoiserStrength=0.03" --data-urlencode "cache=false" ${ttsserver}/api/tts -H 'accept: */*' --output ~hpr${ep_num}_summary.wav
|
||||
}
|
||||
|
||||
create_tts_summary_ok="not-ok"
|
||||
|
||||
while [ "${create_tts_summary_ok}" != "OK" ]
|
||||
do
|
||||
create_tts_summary
|
||||
|
||||
mpv ~hpr${ep_num}_summary.wav
|
||||
|
||||
read -p "Is the text to speech correct (y|N) ? " -n 1 -r
|
||||
echo # (optional) move to a new line
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
create_tts_summary_ok="OK"
|
||||
else
|
||||
echo "WARN: Please correct the text and try again."
|
||||
xdg-open "hpr${ep_num}_summary.txt" 2>&1 &
|
||||
inotifywait --event modify "hpr${ep_num}_summary.txt"
|
||||
fi
|
||||
done
|
||||
echo "INFO: TTS complete."
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# exit 9999
|
Loading…
Reference in New Issue
Block a user