Moved in workflow
This commit is contained in:
parent
22fae69de5
commit
a4c24296ef
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