Fix trailing whitespaces in files
This commit is contained in:
parent
1af089219f
commit
d8195f0fd8
88
CREDITS
88
CREDITS
|
@ -92,7 +92,7 @@ Martin Konecny (martin.konecny@sourcefabric.org)
|
|||
|
||||
James Moon (james.moon@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
||||
Denise Rigato (denise.rigato@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
@ -124,7 +124,7 @@ Martin Konecny (martin.konecny@sourcefabric.org)
|
|||
|
||||
James Moon (james.moon@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
||||
Denise Rigato (denise.rigato@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
@ -147,7 +147,7 @@ Naomi Aro (naomi.aro@sourcefabric.org)
|
|||
|
||||
James Moon (james.moon@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
||||
Denise Rigato (denise.rigato@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
@ -198,7 +198,7 @@ Naomi Aro (naomi.aro@sourcefabric.org)
|
|||
|
||||
James Moon (james.moon@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
||||
Denise Rigato (denise.rigato@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
@ -267,7 +267,7 @@ Martin Konecny (martin.konecny@sourcefabric.org)
|
|||
|
||||
James Moon (james.moon@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
||||
Yuchen Wang (yuchen.wang@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
|
||||
|
@ -350,10 +350,10 @@ Version 1.6.1
|
|||
-------------
|
||||
Same as previous version.
|
||||
|
||||
Version 1.6.0
|
||||
Version 1.6.0
|
||||
-------------
|
||||
This version marks a major change to the project, completely replacing the
|
||||
custom audio player with liquidsoap, dropping the custom desktop GUI, and
|
||||
custom audio player with liquidsoap, dropping the custom desktop GUI, and
|
||||
completely rewriting the web interface. The project has also been renamed
|
||||
from "Campcaster" to "Airtime" for this release.
|
||||
|
||||
|
@ -361,11 +361,11 @@ Paul Baranowski (paul.baranowski@sourcefabric.org)
|
|||
Role: Project Lead / Software Developer
|
||||
Highlights:
|
||||
- Integration and development of liquidsoap scheduler
|
||||
- Separation of playlists from the scheduler
|
||||
- Separation of playlists from the scheduler
|
||||
|
||||
Naomi Aro (naomi.aro@sourcefabric.org)
|
||||
Role: Software Developer
|
||||
Highlights:
|
||||
Highlights:
|
||||
- New User Interface
|
||||
- Conversion to Propel DB backend
|
||||
|
||||
|
@ -397,44 +397,44 @@ generated radio station based in Basel, Switzerland powered by Campcaster. We ar
|
|||
very grateful for their contributions, and specifically to Thomas Gilgen, Dirk Claes,
|
||||
Rigzen Latshang and Fabiano Sidler.
|
||||
|
||||
Douglas Arellanes
|
||||
Douglas Arellanes
|
||||
- Tester and user feedback
|
||||
|
||||
Robin Gareus
|
||||
Robin Gareus
|
||||
- Packaging
|
||||
|
||||
Ferenc Gerlits
|
||||
Ferenc Gerlits
|
||||
- Studio GUI
|
||||
|
||||
Sebastian Göbel
|
||||
Sebastian Göbel
|
||||
- Web interface, storage server
|
||||
|
||||
Nebojsa Grujic
|
||||
Nebojsa Grujic
|
||||
- Scheduler, XML-RPC interface, Gstreamer plugins
|
||||
|
||||
Tomáš Hlava
|
||||
Tomáš Hlava
|
||||
- Bug fixes
|
||||
|
||||
Sava Tatić
|
||||
Sava Tatić
|
||||
- Manager
|
||||
|
||||
|
||||
Version 1.3.0 - "Dakar"
|
||||
-----------------------
|
||||
|
||||
Douglas Arellanes
|
||||
Douglas Arellanes
|
||||
- Tester and user feedback
|
||||
|
||||
Ferenc Gerlits
|
||||
Ferenc Gerlits
|
||||
- Studio GUI, scheduler, packaging
|
||||
|
||||
Sebastian Göbel
|
||||
Sebastian Göbel
|
||||
- Web interface
|
||||
|
||||
Tomáš Hlava
|
||||
Tomáš Hlava
|
||||
- Bug fixes
|
||||
|
||||
Sava Tatić
|
||||
Sava Tatić
|
||||
- Manager
|
||||
|
||||
|
||||
|
@ -442,19 +442,19 @@ Version 1.2.0 - "Kotor"
|
|||
-----------------------
|
||||
In alphabetical order:
|
||||
|
||||
Douglas Arellanes
|
||||
Douglas Arellanes
|
||||
- Tester and user feedback
|
||||
Paul Baranowski
|
||||
Paul Baranowski
|
||||
- Project manager, HTML UI, storage server
|
||||
Ferenc Gerlits
|
||||
Ferenc Gerlits
|
||||
- Studio GUI, scheduler, packaging
|
||||
Tomáš Hlava
|
||||
Tomáš Hlava
|
||||
- Bug fixes
|
||||
Robert Klajn
|
||||
- Superuser feedback
|
||||
Mark Kretschmann
|
||||
Robert Klajn
|
||||
- Superuser feedback
|
||||
Mark Kretschmann
|
||||
- Audio player
|
||||
Sava Tatić
|
||||
Sava Tatić
|
||||
- Manager
|
||||
|
||||
|
||||
|
@ -462,40 +462,40 @@ Version 1.1.X - "Freetown"
|
|||
--------------------------
|
||||
In alphabetical order:
|
||||
|
||||
Douglas Arellanes
|
||||
Douglas Arellanes
|
||||
- Tester and user feedback
|
||||
Paul Baranowski
|
||||
Paul Baranowski
|
||||
- Project manager, HTML UI, storage server, scheduler
|
||||
János Csikós
|
||||
János Csikós
|
||||
- HTML UI
|
||||
Ferenc Gerlits
|
||||
Ferenc Gerlits
|
||||
- Studio GUI, scheduler, packaging
|
||||
Tomáš Hlava
|
||||
Tomáš Hlava
|
||||
- Storage server, network hub
|
||||
Mark Kretschmann
|
||||
Mark Kretschmann
|
||||
- Audio player
|
||||
Ákos Maróy
|
||||
Ákos Maróy
|
||||
- Architecture design, scheduler, audio player
|
||||
Sava Tatić
|
||||
- Manager
|
||||
|
||||
Version 1.0
|
||||
-----------
|
||||
The original Campcaster (LiveSupport) concept was drafted by Micz Flor. It was
|
||||
fully developed by Robert Klajn, Douglas Arellanes, Ákos Maróy, and Sava Tatić.
|
||||
The user interface has been designed by Charles Truett, based on the initial work
|
||||
done by a team of his then-fellow Parsons School of Design students Turi McKinley,
|
||||
Catalin Lazia and Sangita Shah. The team was led by then-head of the school's
|
||||
The original Campcaster (LiveSupport) concept was drafted by Micz Flor. It was
|
||||
fully developed by Robert Klajn, Douglas Arellanes, Ákos Maróy, and Sava Tatić.
|
||||
The user interface has been designed by Charles Truett, based on the initial work
|
||||
done by a team of his then-fellow Parsons School of Design students Turi McKinley,
|
||||
Catalin Lazia and Sangita Shah. The team was led by then-head of the school's
|
||||
Department of Digital Design Colleen Macklin, assisted by Kunal Jain.
|
||||
|
||||
In alphabetical order:
|
||||
Douglas Arellanes
|
||||
Michael Aschauer
|
||||
Micz Flor
|
||||
Michael Aschauer
|
||||
Micz Flor
|
||||
Ferenc Gerlits
|
||||
Sebastian Göbel
|
||||
Tomáš Hlava
|
||||
Nadine Kokot
|
||||
Ákos Maróy
|
||||
Ákos Maróy
|
||||
Sava Tatić
|
||||
Charles Truett
|
||||
|
|
78
changelog
78
changelog
|
@ -49,14 +49,14 @@
|
|||
* Much faster library import (Silan analyzer runs in background)
|
||||
* Fixed zombie process sometimes being created
|
||||
* Other
|
||||
* Upgrade to Mutagen (tag reader) 1.21
|
||||
* Upgrade to Mutagen (tag reader) 1.21
|
||||
|
||||
2.3.0 - Jan 21st, 2013
|
||||
* New features
|
||||
* Localization (Chinese, Czech, English, French, German, Italian, Korean,
|
||||
Portuguese, Russian, Spanish)
|
||||
* User management page for non-admin users
|
||||
* Listener statistics (Icecast/Shoutcast)
|
||||
* Listener statistics (Icecast/Shoutcast)
|
||||
* Airtime no longer requires Apache document root
|
||||
* Replay Gain offset in real-time
|
||||
* Enable/disable replay gain
|
||||
|
@ -113,7 +113,7 @@
|
|||
* Playlist Builder should remember your position instead of resetting to the first page every time an operation was performed
|
||||
* If Master or Live input source is disconnected, Airtime will no longer automatically switch off that source. This should allow the source to
|
||||
reconnect and continue playback.
|
||||
|
||||
|
||||
* Bug fixes
|
||||
* Fixed playout engine sometimes not receiving new schedule which could result in dead air
|
||||
* Fixed script timeout which caused Apache to become unresponsive
|
||||
|
@ -174,7 +174,7 @@
|
|||
* Fixed Airtime could stop automatically recording after 2 hours if the web interface isn't used.
|
||||
* Fixed upgrading from 1.8.2 when the stor directory was a symlink would cause filenames to not be preserved.
|
||||
* Fixed Day View in the Now Playing tab showed some items on incorrect days.
|
||||
* Fixed problems with having an equal '=' sign as an icecast password
|
||||
* Fixed problems with having an equal '=' sign as an icecast password
|
||||
* Other
|
||||
* Various optimizations to make Airtime feel snappier in the browser. Various views should
|
||||
load much quicker.
|
||||
|
@ -295,11 +295,11 @@
|
|||
- Fixed pypo hanging if web server is unavailable
|
||||
- Fixed items that were being dragged and dropped in the Playlist Builder
|
||||
being obscured by other UI elements.
|
||||
|
||||
|
||||
1.9.3 - August 26th, 2011
|
||||
* Improvements
|
||||
- It is now possible to upgrade your system while a show is playing.
|
||||
Playout will be temporarily interrupted for about 5-10 seconds and then
|
||||
Playout will be temporarily interrupted for about 5-10 seconds and then
|
||||
playout will resume. Previously playout would not resume until the next
|
||||
scheduled show.
|
||||
* Fixes
|
||||
|
@ -324,7 +324,7 @@
|
|||
- Prevent users from doing a manual install of Airtime if they already have
|
||||
the Debian package version installed
|
||||
* Changes
|
||||
- Support Settings moved to a separate page accessible by Admin user only.
|
||||
- Support Settings moved to a separate page accessible by Admin user only.
|
||||
|
||||
1.9.0 - August 9, 2011
|
||||
|
||||
|
@ -333,20 +333,20 @@ The cool stuff:
|
|||
- Human-readable file structure. The directory structure and file names on
|
||||
disk are now human-readable. This means you can easily find files using
|
||||
your file browser on your server.
|
||||
- Magic file synchronization. Edits to your files are automatically
|
||||
- Magic file synchronization. Edits to your files are automatically
|
||||
noticed by Airtime. If you edit any files on disk, such as trimming the
|
||||
length of a track, Airtime will automatically notice this and adjust the
|
||||
playlist lengths and shows for that audio file.
|
||||
- Auto-import and multiple-directory support. You can set any number of
|
||||
directories to be watched by Airtime. Any new files you add to watched
|
||||
- Auto-import and multiple-directory support. You can set any number of
|
||||
directories to be watched by Airtime. Any new files you add to watched
|
||||
directories will be automatically imported into Airtime, and any deleted
|
||||
files will be automatically removed.
|
||||
- The "airtime-import" command line tool can now set watched directories
|
||||
- The "airtime-import" command line tool can now set watched directories
|
||||
and change the storage directory.
|
||||
- Graceful recovery from reboot. If the playout engine starts up and
|
||||
detects that a show should be playing at the current time, it will skip
|
||||
to the right point in the track and start playing. Previously, Airtime
|
||||
would not play anything until the next show started. This also fixes a
|
||||
to the right point in the track and start playing. Previously, Airtime
|
||||
would not play anything until the next show started. This also fixes a
|
||||
problem where the metadata on the stream was lost when a file had
|
||||
cue-in/out values set. Thanks to the Liquidsoap developers for
|
||||
implementing the ability to do all of this!
|
||||
|
@ -354,28 +354,28 @@ The cool stuff:
|
|||
- A new "Program Manager" role. A program manager can create shows but
|
||||
can't change the preferences or modify users.
|
||||
- No more rebooting after install! Airtime now uses standard SystemV initd
|
||||
scripts instead of non-standard daemontools. This also makes for a much
|
||||
scripts instead of non-standard daemontools. This also makes for a much
|
||||
faster install.
|
||||
- Frontend widgets are much easier to use and their theme can be modified
|
||||
- Frontend widgets are much easier to use and their theme can be modified
|
||||
with CSS (Click here for more info and installation instructions).
|
||||
- Improved installation - only one command to install on Ubuntu!
|
||||
|
||||
* Improvements:
|
||||
- Cumulative time shown on playlists. The Playlist Builder now shows the
|
||||
- Cumulative time shown on playlists. The Playlist Builder now shows the
|
||||
total time since the beginning of the playlist for each song.
|
||||
- "End Time" instead of "Duration". In the Add/Edit Show dialog, we
|
||||
- "End Time" instead of "Duration". In the Add/Edit Show dialog, we
|
||||
replaced the "Duration" field with "End Time". Users reported that this
|
||||
was a much more intuitive way to schedule the show. Duration is still
|
||||
shown as a read-only field.
|
||||
- Feedback & promotion system. Airtime now includes a way to send feedback
|
||||
and promote your site on the Sourcefabric web page. This will greatly
|
||||
enhance our ability to understand who is using the software, which in
|
||||
turn will allow us to make appropriate features and receive grant
|
||||
and promote your site on the Sourcefabric web page. This will greatly
|
||||
enhance our ability to understand who is using the software, which in
|
||||
turn will allow us to make appropriate features and receive grant
|
||||
funding.
|
||||
- The show recorder can now instantly cancel a show thanks to the use of
|
||||
RabbitMQ.
|
||||
- Only admins have the ability to delete files now.
|
||||
- The playout engine now runs with a higher priority. This should help
|
||||
- The playout engine now runs with a higher priority. This should help
|
||||
prevent any problems with audio skipping.
|
||||
- Airtime has been contained. It is now easier to run other apps on the
|
||||
same system with Airtime because it no longer messes with the system-wide
|
||||
|
@ -386,12 +386,12 @@ The cool stuff:
|
|||
page( above the search box).
|
||||
|
||||
* Bug fixes:
|
||||
- Fixed bug where you couldn't import a file with a name longer than 255
|
||||
- Fixed bug where you couldn't import a file with a name longer than 255
|
||||
characters.
|
||||
- Fixed bug where searching an audio archive of 15K+ files was slow.
|
||||
- Fixed bug where upgrading from more than one version back
|
||||
(e.g. 1.8.0 -> 1.9.0) did not work.
|
||||
- Fixed bug where the wrong file length was reported for very large CBR
|
||||
- Fixed bug where the wrong file length was reported for very large CBR
|
||||
mp3 files (thanks to mutagen developers for the patch!)
|
||||
|
||||
1.8.2 - June 8, 2011
|
||||
|
@ -456,30 +456,30 @@ Highlights:
|
|||
|
||||
1.8.0 - April 19, 2011
|
||||
* The biggest feature of this release is the ability to edit shows. You can
|
||||
change everything from ‘Name’, ‘Description’, and ‘URL’, to repeat and
|
||||
change everything from ‘Name’, ‘Description’, and ‘URL’, to repeat and
|
||||
rebroadcast days. Show instances will be dynamically created or removed as
|
||||
needed. Radio stations will be pleased to know they can now have up to
|
||||
needed. Radio stations will be pleased to know they can now have up to
|
||||
ten rebroadcast shows too.
|
||||
* Airtime’s calendar now looks, feels and performs better than ever. Loading
|
||||
a station schedule is now five to eight times faster. In our tests of 1.7,
|
||||
if the month calendar had shows scheduled for every hour of every day, it
|
||||
a station schedule is now five to eight times faster. In our tests of 1.7,
|
||||
if the month calendar had shows scheduled for every hour of every day, it
|
||||
used to take 16 seconds to load. Now in 1.8 it takes two seconds.
|
||||
* It is possible to have up to ten rebroadcast shows now, in 1.7 it was only
|
||||
up to five.
|
||||
* Airtime’s new installation script has two options for increased install
|
||||
flexibility: --preserve to keep your existing config files, or --overwrite
|
||||
to replace your existing config files with new ones. Uninstall no longer
|
||||
* Airtime’s new installation script has two options for increased install
|
||||
flexibility: --preserve to keep your existing config files, or --overwrite
|
||||
to replace your existing config files with new ones. Uninstall no longer
|
||||
removes Airtime config files or the music storage directory.
|
||||
* New improved look & feel of the calendar (thanks to the "FullCalendar"
|
||||
* New improved look & feel of the calendar (thanks to the "FullCalendar"
|
||||
jQuery project).
|
||||
* Installation now puts files in standard locations in the Linux file
|
||||
* Installation now puts files in standard locations in the Linux file
|
||||
hierarchy, which prepares the project to be accepted into Ubuntu and Debian.
|
||||
Also because of our wish to be part of those projects, the default output
|
||||
stream type is now OGG instead of MP3 -- due to MP3 licensing issues.
|
||||
Also because of our wish to be part of those projects, the default output
|
||||
stream type is now OGG instead of MP3 -- due to MP3 licensing issues.
|
||||
This configuration can be changed in "/etc/airtime/liquidsoap.conf".
|
||||
* You now have the ability to start and stop pypo and the show recorder from
|
||||
the command line with the commands "airtime-pypo-start",
|
||||
"airtime-pypo-stop", "airtime-show-recorder-start", and
|
||||
* You now have the ability to start and stop pypo and the show recorder from
|
||||
the command line with the commands "airtime-pypo-start",
|
||||
"airtime-pypo-stop", "airtime-show-recorder-start", and
|
||||
"airtime-show-recorder-stop".
|
||||
* Bug fixes:
|
||||
- CC-2192 Schedule sent to pypo is not sorted by start time.
|
||||
|
@ -520,7 +520,7 @@ Highlights:
|
|||
* Bug fixes:
|
||||
- CC-2082 OGG stream dies after every song when using MPlayer
|
||||
- CC-1894 Warn users about time zone differences or clock drift problems on
|
||||
the server
|
||||
the server
|
||||
- CC-2058 Utilities are not in the system $PATH
|
||||
- CC-2051 Unable to change user password
|
||||
- CC-2030 Icon needed for Cue In/Out
|
||||
|
@ -531,7 +531,7 @@ Bug fixes:
|
|||
* CC-1973 Liquidsoap crashes after multi-day playout
|
||||
* CC-1970 API key fix (Security fix) - Each time you run the install scripts,
|
||||
a new API key is now generated.
|
||||
* CC-1992 Editing metadata goes blank on 'submit'
|
||||
* CC-1992 Editing metadata goes blank on 'submit'
|
||||
* CC-1993 ui start time and song time unsynchronized
|
||||
|
||||
1.6.0 - Feb 14, 2011
|
||||
|
|
|
@ -7,5 +7,5 @@ To update the Airtime translations:
|
|||
- Commit the updated files.
|
||||
- Push to GitHub.
|
||||
- Transifex will then pick up the updated files in about 24 hours, and they'll be available for translation there.
|
||||
- After translators have updated strings, they'll be automatically downloaded and committed to our git repo by
|
||||
- After translators have updated strings, they'll be automatically downloaded and committed to our git repo by
|
||||
a script running here at Sourcefabric (contact Andrey).
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<?PHP
|
||||
|
||||
/*
|
||||
/*
|
||||
|
||||
|
||||
The purpose of this script is to take a file from cc_files table, and insert it into
|
||||
the schedule table. DB columns at the time of writing are
|
||||
|
||||
|
||||
starts | ends | file_id | clip_length | fade_in | fade_out | cue_in | cue_out | media_item_played | instance_id
|
||||
|
||||
|
||||
an example of data in this row is:
|
||||
"9" | "2012-02-29 17:10:00" | "2012-02-29 17:15:05.037166" | 1 | "00:05:05.037166" | "00:00:00" | "00:00:00" | "00:00:00" | "00:05:05.037166" | FALSE | 5
|
||||
|
||||
|
@ -19,25 +19,25 @@ function query($conn, $query){
|
|||
echo "Error executing query $query.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getFileFromCcFiles($conn){
|
||||
$query = "SELECT * from cc_files LIMIT 2";
|
||||
|
||||
|
||||
$result = query($conn, $query);
|
||||
|
||||
|
||||
$files = array();
|
||||
while ($row = pg_fetch_array($result)) {
|
||||
$files[] = $row;
|
||||
}
|
||||
|
||||
|
||||
if (count($files) == 0){
|
||||
echo "Library is empty. Could not choose random file.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ function insertIntoCcShow($conn){
|
|||
while ($row = pg_fetch_array($result)) {
|
||||
$show_id = $row["currval"];
|
||||
}
|
||||
|
||||
|
||||
return $show_id;
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ function insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $files){
|
|||
* Column values:
|
||||
* starts | ends | show_id | record | rebroadcast | instance_id | file_id | time_filled | last_scheduled | modified_instance
|
||||
* */
|
||||
|
||||
|
||||
$nowDateTime = new DateTime("now", new DateTimeZone("UTC"));
|
||||
|
||||
$now = $nowDateTime->format("Y-m-d H:i:s");
|
||||
|
@ -79,7 +79,7 @@ function insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $files){
|
|||
$values = "('$starts', '$ends', $show_id, 0, 0, NULL, NULL, TIMESTAMP '$ends' - TIMESTAMP '$starts', '$now', 'f')";
|
||||
$query = "INSERT INTO cc_show_instances $columns values $values ";
|
||||
echo $query.PHP_EOL;
|
||||
|
||||
|
||||
$result = query($conn, $query);
|
||||
|
||||
$query = "SELECT currval('cc_show_instances_id_seq');";
|
||||
|
@ -92,7 +92,7 @@ function insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $files){
|
|||
while ($row = pg_fetch_array($result)) {
|
||||
$show_instance_id = $row["currval"];
|
||||
}
|
||||
|
||||
|
||||
return $show_instance_id;
|
||||
}
|
||||
|
||||
|
@ -102,9 +102,9 @@ function insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $files){
|
|||
*/
|
||||
function insertIntoCcSchedule($conn, $files, $show_instance_id, $p_starts, $p_ends){
|
||||
$columns = "(starts, ends, file_id, clip_length, fade_in, fade_out, cue_in, cue_out, media_item_played, instance_id)";
|
||||
|
||||
|
||||
$starts = $p_starts;
|
||||
|
||||
|
||||
foreach($files as $file){
|
||||
|
||||
$endsDateTime = new DateTime($starts, new DateTimeZone("UTC"));
|
||||
|
@ -115,9 +115,9 @@ function insertIntoCcSchedule($conn, $files, $show_instance_id, $p_starts, $p_en
|
|||
$values = "('$starts', '$ends', $file[id], '$file[length]', '00:00:00', '00:00:00', '00:00:00', '$file[length]', 'f', $show_instance_id)";
|
||||
$query = "INSERT INTO cc_schedule $columns VALUES $values";
|
||||
echo $query.PHP_EOL;
|
||||
|
||||
|
||||
$starts = $ends;
|
||||
$result = query($conn, $query);
|
||||
$result = query($conn, $query);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ function getEndTime($startDateTime, $p_files){
|
|||
foreach ($p_files as $file){
|
||||
$startDateTime->add(getDateInterval($file['length']));
|
||||
}
|
||||
|
||||
|
||||
return $startDateTime;
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ function rabbitMqNotify(){
|
|||
echo "Contacting $url".PHP_EOL;
|
||||
$ch = curl_init($url);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
curl_close($ch);
|
||||
}
|
||||
|
||||
$conn = pg_connect("host=localhost port=5432 dbname=airtime user=airtime password=airtime");
|
||||
|
@ -152,9 +152,9 @@ if (!$conn) {
|
|||
}
|
||||
|
||||
if (count($argv) > 1){
|
||||
if ($argv[1] == "--clean"){
|
||||
if ($argv[1] == "--clean"){
|
||||
$tables = array("cc_schedule", "cc_show_instances", "cc_show");
|
||||
|
||||
|
||||
foreach($tables as $table){
|
||||
$query = "DELETE FROM $table";
|
||||
echo $query.PHP_EOL;
|
||||
|
@ -162,9 +162,9 @@ if (count($argv) > 1){
|
|||
}
|
||||
rabbitMqNotify();
|
||||
exit(0);
|
||||
} else {
|
||||
} else {
|
||||
$str = <<<EOD
|
||||
This script schedules a file to play 30 seconds in the future. It
|
||||
This script schedules a file to play 30 seconds in the future. It
|
||||
modifies the database tables cc_schedule, cc_show_instances and cc_show.
|
||||
You can clean up these tables using the --clean option.
|
||||
EOD;
|
||||
|
@ -178,7 +178,7 @@ $startDateTime = new DateTime("now + 30sec", new DateTimeZone("UTC"));
|
|||
$starts = $startDateTime->format("Y-m-d H:i:s");
|
||||
//$ends = $endDateTime->format("Y-m-d H:i:s");
|
||||
|
||||
$files = getFileFromCcFiles($conn);
|
||||
$files = getFileFromCcFiles($conn);
|
||||
$show_id = insertIntoCcShow($conn);
|
||||
|
||||
$endDateTime = getEndTime(clone $startDateTime, $files);
|
||||
|
|
|
@ -30,7 +30,7 @@ class AirtimeMediaMonitorBootstrap:
|
|||
config = ConfigObj("/etc/airtime/airtime.conf")
|
||||
self.api_client = apc.api_client_factory(config)
|
||||
|
||||
"""
|
||||
"""
|
||||
try:
|
||||
logging.config.fileConfig("logging.cfg")
|
||||
except Exception, e:
|
||||
|
|
|
@ -90,7 +90,7 @@ fi
|
|||
rm -rf liquidsoap-full
|
||||
git clone https://github.com/savonet/liquidsoap-full
|
||||
cd liquidsoap-full
|
||||
git checkout master
|
||||
git checkout master
|
||||
make init
|
||||
make update
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ build_env () {
|
|||
echo "Please use -u to assign sudo username before build environments."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "build_env $1"
|
||||
#exec > >(tee ./liquidsoap_compile_logs/build_env_$1.log)
|
||||
os=`echo $1 | awk '/(debian)/'`
|
||||
|
@ -40,7 +40,7 @@ build_env () {
|
|||
useradd tmp
|
||||
echo "User tmp is created."
|
||||
fi
|
||||
|
||||
|
||||
apt-get update
|
||||
apt-get --force-yes -y install debootstrap dchroot
|
||||
echo [$1] > /etc/schroot/chroot.d/$1.conf
|
||||
|
@ -87,7 +87,7 @@ compile_liq () {
|
|||
else
|
||||
mv ./liquidsoap-compile_logs/compile_liq_$1.log ./liquidsoap-compile_logs/fail_to_compile_liq_$1.log
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
os_versions=("ubuntu_lucid_32" "ubuntu_lucid_64" "ubuntu_precise_32" "ubuntu_precise_64" "ubuntu_quantal_32" "ubuntu_quantal_64" "ubuntu_raring_32" "ubuntu_raring_64" "debian_squeeze_32" "debian_squeeze_64" "debian_wheezy_32" "debian_wheezy_64")
|
||||
|
||||
|
@ -147,7 +147,7 @@ do
|
|||
compile_liq ${os_versions[$i]} | tee ./liquidsoap-compile_logs/compile_liq_${os_versions[$i]}.log
|
||||
flag=0
|
||||
fi
|
||||
done
|
||||
done
|
||||
if [ $flag = 1 ];then
|
||||
echo "Unsupported Platform from:"
|
||||
for k in "${os_versions[@]}"
|
||||
|
|
|
@ -74,7 +74,7 @@ tar -czf $target_file \
|
|||
--exclude dev_tools \
|
||||
--exclude vendor/phing \
|
||||
--exclude vendor/simplepie/simplepie/tests \
|
||||
libretime-${suffix}
|
||||
libretime-${suffix}
|
||||
echo " Done"
|
||||
popd
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[merge "pofile"]
|
||||
name = Gettext merge driver
|
||||
driver = git merge-po %O %A %B
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# https://gist.github.com/mezis/1605647
|
||||
# https://gist.github.com/mezis/1605647
|
||||
# by Julien Letessier (mezis)
|
||||
#
|
||||
# Custom Git merge driver - merges PO files using msgcat(1)
|
||||
|
@ -8,15 +8,15 @@
|
|||
# - Install gettext
|
||||
#
|
||||
# - Place this script in your PATH
|
||||
#
|
||||
#
|
||||
# - Add this to your .git/config :
|
||||
#
|
||||
# [merge "pofile"]
|
||||
# name = Gettext merge driver
|
||||
# driver = git merge-po %O %A %B
|
||||
#
|
||||
#
|
||||
# - Add this to .gitattributes :
|
||||
#
|
||||
#
|
||||
# *.po merge=pofile
|
||||
# *.pot merge=pofile
|
||||
#
|
||||
|
|
|
@ -5,7 +5,7 @@ if [[ $EUID -ne 0 ]]; then
|
|||
fi
|
||||
|
||||
usage () {
|
||||
echo "Use --enable <user> or --disable flag. Enable is to set up environment"
|
||||
echo "Use --enable <user> or --disable flag. Enable is to set up environment"
|
||||
echo "for specified user. --disable is to reset it back to pypo user"
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ elif [ "$1" = "--disable" ]; then
|
|||
echo "Changing ownership to user $1"
|
||||
chmod 644 /etc/airtime/airtime.conf
|
||||
chown -Rv $user:$user /var/tmp/airtime/pypo/
|
||||
chmod -v a+r /etc/airtime/api_client.cfg
|
||||
chmod -v a+r /etc/airtime/api_client.cfg
|
||||
|
||||
|
||||
/etc/init.d/airtime-playout stop-liquidsoap
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
|
||||
|
||||
|
||||
<!-- Custom fonts, icons for this template -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" rel="stylesheet" type="text/css">
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Merriweather:400,300,300italic,400italic,700,700italic,900,900italic' rel='stylesheet' type='text/css'>
|
||||
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="/css/creative.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<title>{{ site.title }} - {{ page.title }}</title>
|
|
@ -1,6 +1,6 @@
|
|||
<nav class="navbar navbar-expand-lg navbar-light fixed-top navbar-shrink" id="mainNav">
|
||||
<div class="container">
|
||||
<a class="navbar-brand js-scroll-trigger" href="/index">
|
||||
<a class="navbar-brand js-scroll-trigger" href="/index">
|
||||
{{ site.title }} </a>
|
||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
<script>
|
||||
// Set a variable for our button element.
|
||||
const scrollToTopButton = document.getElementById('js-top');
|
||||
|
||||
|
||||
const scrollFunc = () => {
|
||||
// Get the current scroll value
|
||||
let y = window.scrollY;
|
||||
|
||||
|
||||
// If the scroll value is greater than the window height, let's add a class to the scroll-to-top button to show it!
|
||||
if (y > 0) {
|
||||
scrollToTopButton.className = "top-link show";
|
||||
|
@ -24,7 +24,7 @@
|
|||
const scrollToTop = () => {
|
||||
// Let's set a variable for the number of pixels we are from the top of the document.
|
||||
const c = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
|
||||
|
||||
// If that number is greater than 0, we'll scroll back to 0, or the top of the document.
|
||||
// We'll also animate that scroll with requestAnimationFrame:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
|
||||
|
@ -41,4 +41,3 @@
|
|||
scrollToTop();
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
|
||||
<!-- Promo Section -->
|
||||
<section class="bg-primary">
|
||||
<div class="container">
|
||||
<div class="d-flex mh-25rem pt-11 py-6">
|
||||
<div class="align-self-center">
|
||||
<h1 class="text-white font-weight-light mb-3">{{ page.title }}</h1>
|
||||
|
||||
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb breadcrumb-light">
|
||||
<li class="breadcrumb-item"><a href="/">Home</a></li>
|
||||
|
@ -20,7 +20,7 @@ layout: default
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- SVG BG -->
|
||||
<svg class="position-absolute bottom-0 left-0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1920 323" enable-background="new 0 0 1920 323" xml:space="preserve">
|
||||
<polygon fill="#ffffff" style="fill-opacity: .05;" points="-0.5,322.5 -0.5,121.5 658.3,212.3 "></polygon>
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
{% include head.html %}
|
||||
|
||||
|
||||
</head>
|
||||
<body id="page-top">
|
||||
|
||||
<!-- Navigation -->
|
||||
{% include navbar.html %}
|
||||
|
||||
|
||||
<!-- Scroll to Top link -->
|
||||
<a class="top-link hide" href="" id="js-top">
|
||||
<svg class="bi bi-arrow-up-circle-fill" width="4em" height="4em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
|
@ -16,9 +16,9 @@
|
|||
</svg>
|
||||
<span class="screen-reader-text">Back to top</span>
|
||||
</a>
|
||||
|
||||
|
||||
{{content}}
|
||||
|
||||
|
||||
{% include footer.html %}
|
||||
|
||||
{% include scripts.html %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
|
||||
<style>
|
||||
header.masthead {
|
||||
padding-top: 10rem;
|
||||
|
@ -27,7 +27,7 @@ layout: default
|
|||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<!-- Features Block -->
|
||||
<section id="features">
|
||||
<div class="container">
|
||||
|
@ -150,7 +150,7 @@ layout: default
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- "Our Biggest Fans" Section -->
|
||||
<section class="" id="biggest-fans">
|
||||
<div class="container text-center">
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
/*!
|
||||
* Start Bootstrap - Libretime v1.0.0 ()
|
||||
* Copyright 2013-2018
|
||||
* Copyright 2013-2018
|
||||
* Licensed under MIT (https://github.com/BlackrockDigital/libretime-website/blob/master/LICENSE)
|
||||
*/
|
||||
body,html{
|
||||
width:100%;height:100%
|
||||
}
|
||||
|
||||
|
||||
body{
|
||||
font-family:Merriweather,'Helvetica Neue',Arial,sans-serif;
|
||||
padding-top: 57px;
|
||||
}
|
||||
|
||||
|
||||
hr{
|
||||
max-width:auto;border-width:2px;border-color:#f05f40
|
||||
}
|
||||
|
||||
|
||||
hr.light{
|
||||
border-color:#fff
|
||||
}
|
||||
|
||||
|
||||
img{
|
||||
width:100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
|
||||
a{
|
||||
color:#f05f40;
|
||||
-webkit-transition:all .2s;
|
||||
transition:all .2s;
|
||||
}
|
||||
|
||||
|
||||
a:hover{
|
||||
color:#f05f40
|
||||
}
|
||||
|
||||
|
||||
h1,h2,h3,h4,h5,h6{
|
||||
font-family:'Open Sans','Helvetica Neue',Arial,sans-serif;
|
||||
}
|
||||
|
@ -58,34 +58,34 @@ th{
|
|||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
tr, td{
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
border: 2px solid #f05f40
|
||||
}
|
||||
|
||||
/* Theme Colors */
|
||||
/* Theme Colors */
|
||||
.bg-primary{
|
||||
background-color:#f05f40!important
|
||||
}
|
||||
|
||||
|
||||
.bg-dark{
|
||||
background-color:#212529!important
|
||||
}
|
||||
|
||||
|
||||
.footer-dark{
|
||||
background-color:#212529!important;
|
||||
padding:2rem 0
|
||||
}
|
||||
|
||||
|
||||
.text-faded{
|
||||
color:rgba(255,255,255,.9)
|
||||
}
|
||||
|
||||
|
||||
div.text-footer{
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Changed padding */
|
||||
section{padding:2rem 0}
|
||||
|
@ -98,7 +98,7 @@ img::-moz-selection{color:#fff;background:0 0}
|
|||
|
||||
#mainNav{
|
||||
border-bottom:1px solid rgba(33,37,41,.1);background-color:#fff;font-family:'Open Sans','Helvetica Neue',Arial,sans-serif;-webkit-transition:all .2s;transition:all .2s
|
||||
}
|
||||
}
|
||||
|
||||
#mainNav .navbar-brand{
|
||||
font-weight:700;
|
||||
|
@ -106,7 +106,7 @@ img::-moz-selection{color:#fff;background:0 0}
|
|||
color:#f05f40;
|
||||
font-family:'Open Sans','Helvetica Neue',Arial,sans-serif
|
||||
}
|
||||
|
||||
|
||||
#mainNav .navbar-brand:focus,
|
||||
#mainNav .navbar-brand:hover{color:#f05f40}
|
||||
#mainNav .navbar-nav>li.nav-item>a.nav-link,
|
||||
|
@ -123,7 +123,7 @@ img::-moz-selection{color:#fff;background:0 0}
|
|||
@media (min-width:992px){
|
||||
#mainNav{border-color:transparent;background-color:transparent
|
||||
}
|
||||
|
||||
|
||||
#mainNav .navbar-brand{color:rgba(255,255,255,.7)}
|
||||
#mainNav .navbar-brand:focus,#mainNav .navbar-brand:hover{color:#fff}
|
||||
#mainNav .navbar-nav>li.nav-item>a.nav-link{padding:.5rem 1rem}
|
||||
|
@ -204,7 +204,7 @@ header.masthead p{font-weight:300}
|
|||
.btn-primary:active,.btn-primary:focus{
|
||||
-webkit-box-shadow:0 0 0 .2rem rgba(240,95,64,.5)!important;box-shadow:0 0 0 .2rem rgba(240,95,64,.5)!important
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-full-width{border:1px solid #888;margin:5px;width:100%}
|
||||
|
||||
/* Blockquotes */
|
||||
|
@ -215,12 +215,12 @@ font-size: .9rem;
|
|||
margin: 10px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
|
||||
blockquote p {
|
||||
margin: 0;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
|
||||
blockquote .small {
|
||||
display: block;
|
||||
font-size: 80%;
|
||||
|
@ -250,7 +250,7 @@ padding: 10px 20px;
|
|||
bottom: 0;
|
||||
right: 0;
|
||||
display: inline-flex;
|
||||
|
||||
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -266,7 +266,7 @@ padding: 10px 20px;
|
|||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.hide {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
|
@ -283,12 +283,12 @@ padding: 10px 20px;
|
|||
overflow: hidden;
|
||||
word-wrap: normal !important;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
|
||||
|
||||
&:focus {
|
||||
display: block;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
z-index: 100000;
|
||||
z-index: 100000;
|
||||
clip-path: none;
|
||||
background-color: #eee;
|
||||
padding: 15px 23px 14px;
|
||||
|
|
|
@ -139,4 +139,4 @@ title: Docs
|
|||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ title: Search
|
|||
<div class="d-flex mh-25rem pt-11 py-6">
|
||||
<div class="align-self-center">
|
||||
<h1 class="text-white font-weight-light mb-3">{{ page.title }}</h1>
|
||||
|
||||
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb breadcrumb-light">
|
||||
<li class="breadcrumb-item"><a href="/">Home</a></li>
|
||||
|
|
|
@ -22,7 +22,7 @@ echo "...Done"
|
|||
set -e
|
||||
|
||||
###
|
||||
# ! NOTE: If you run into errors resolving the archives when running apt-get update,
|
||||
# ! NOTE: If you run into errors resolving the archives when running apt-get update,
|
||||
# clear your /var/cache/lxc directory and retry.
|
||||
###
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ patch -f /var/lib/pgsql/data/pg_hba.conf << EOD
|
|||
--- pg_hba.conf.orig 2020-12-19 13:10:46.828960307 +0000
|
||||
+++ pg_hba.conf 2020-12-19 13:11:37.356290128 +0000
|
||||
@@ -78,10 +78,11 @@
|
||||
|
||||
|
||||
# "local" is for Unix domain socket connections only
|
||||
local all all peer
|
||||
+local all all md5
|
||||
|
@ -153,7 +153,7 @@ echo 'date.timezone=Europe/Zurich' >> /etc/php.d/timezone.ini
|
|||
systemctl restart httpd
|
||||
|
||||
# icecast needs to be available to everyone
|
||||
sed -i -e 's@<bind-address>127.0.0.1</bind-address>@<bind-address>0.0.0.0</bind-address>@' /etc/icecast.xml
|
||||
sed -i -e 's@<bind-address>127.0.0.1</bind-address>@<bind-address>0.0.0.0</bind-address>@' /etc/icecast.xml
|
||||
systemctl enable --now icecast
|
||||
|
||||
# let em use alsa
|
||||
|
|
|
@ -21,9 +21,9 @@ endobj
|
|||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/AIMetaData 9 0 R /AIPrivateData1 10 0 R /AIPrivateData2 8 0 R /CreatorVersion 15
|
||||
/ContainerVersion 9
|
||||
/RoundtripVersion 15
|
||||
/AIMetaData 9 0 R /AIPrivateData1 10 0 R /AIPrivateData2 8 0 R /CreatorVersion 15
|
||||
/ContainerVersion 9
|
||||
/RoundtripVersion 15
|
||||
/NumBlock 2 >>
|
||||
endobj
|
||||
9 0 obj
|
||||
|
@ -43,7 +43,7 @@ stream
|
|||
%AI3_TileBox:0 0 246 -68
|
||||
%AI3_TemplateBox:123 -34 123 -34
|
||||
%AI3_DocumentPreview: None
|
||||
%AI5_ArtSize: 246 68
|
||||
%AI5_ArtSize: 246 68
|
||||
%AI5_NumLayers: 1
|
||||
%AI5_FileFormat 2.0
|
||||
%AI9_ColorModel: 2
|
||||
|
|
|
@ -19,36 +19,36 @@ QUEUE = "airtime-uploads"
|
|||
|
||||
""" A message listener class that waits for messages from Airtime through RabbitMQ
|
||||
notifying us about new uploads.
|
||||
|
||||
|
||||
This is probably the most important class in this application. It connects
|
||||
to RabbitMQ (or an AMQP server) and listens for messages that notify us
|
||||
when a user uploads a new file to Airtime, either through the web interface
|
||||
or via FTP (on Airtime Pro). When we get a notification, we spawn a child
|
||||
process that extracts the uploaded audio file's metadata and moves it into
|
||||
Airtime's music library directory. Lastly, the extracted metadata is
|
||||
Airtime's music library directory. Lastly, the extracted metadata is
|
||||
reported back to the Airtime web application.
|
||||
|
||||
|
||||
There's a couple of Very Important technical details and constraints that you
|
||||
need to know if you're going to work on this code:
|
||||
|
||||
1) airtime_analyzer is designed so it doesn't have to run on the same
|
||||
|
||||
1) airtime_analyzer is designed so it doesn't have to run on the same
|
||||
computer as the web server. It just needs access to your Airtime library
|
||||
folder (stor).
|
||||
folder (stor).
|
||||
2) airtime_analyzer is multi-tenant - One process can be used for many
|
||||
Airtime instances. It's designed NOT to know about whether it's running
|
||||
in a single tenant or multi-tenant environment. All the information it
|
||||
in a single tenant or multi-tenant environment. All the information it
|
||||
needs to import a file into an Airtime instance is passed in via those
|
||||
RabbitMQ messages.
|
||||
3) We're using a "topic exchange" for the new upload notification RabbitMQ
|
||||
messages. This means if we run several airtime_analyzer processes on
|
||||
messages. This means if we run several airtime_analyzer processes on
|
||||
different computers, RabbitMQ will do round-robin dispatching of the
|
||||
file notification. This is cheap, easy load balancing and
|
||||
redundancy for us. You can even run multiple airtime_analyzer processes
|
||||
on one machine if you want.
|
||||
4) We run the actual work (metadata analysis and file moving) in a separate
|
||||
child process so that if it crashes, we can stop RabbitMQ from resending
|
||||
the file notification message to another airtime_analyzer process (NACK),
|
||||
which would otherwise cause cascading failure. We also do this so that we
|
||||
the file notification message to another airtime_analyzer process (NACK),
|
||||
which would otherwise cause cascading failure. We also do this so that we
|
||||
can report the problem file to the Airtime web interface ("import failed").
|
||||
|
||||
So that is a quick overview of the design constraints for this application, and
|
||||
|
@ -164,10 +164,10 @@ class MessageListener:
|
|||
api_key = ""
|
||||
file_prefix = ""
|
||||
|
||||
""" Spin up a worker process. We use the multiprocessing module and multiprocessing.Queue
|
||||
""" Spin up a worker process. We use the multiprocessing module and multiprocessing.Queue
|
||||
to pass objects between the processes so that if the analyzer process crashes, it does not
|
||||
take down the rest of the daemon and we NACK that message so that it doesn't get
|
||||
propagated to other airtime_analyzer daemons (eg. running on other servers).
|
||||
take down the rest of the daemon and we NACK that message so that it doesn't get
|
||||
propagated to other airtime_analyzer daemons (eg. running on other servers).
|
||||
We avoid cascading failure this way.
|
||||
"""
|
||||
try:
|
||||
|
@ -208,8 +208,8 @@ class MessageListener:
|
|||
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
""" If ANY exception happens while processing a file, we're going to NACK to the
|
||||
messaging server and tell it to remove the message from the queue.
|
||||
""" If ANY exception happens while processing a file, we're going to NACK to the
|
||||
messaging server and tell it to remove the message from the queue.
|
||||
(NACK is a negative acknowledgement. We could use ACK instead, but this might come
|
||||
in handy in the future.)
|
||||
Exceptions in this context are unexpected, unhandled errors. We try to recover
|
||||
|
|
|
@ -216,7 +216,7 @@ class StatusReporter:
|
|||
# r = requests.Request(method='PUT', url=callback_url, data=put_payload,
|
||||
# auth=requests.auth.HTTPBasicAuth(api_key, ''))
|
||||
"""
|
||||
r = requests.Request(method='PUT', url=callback_url, data=put_payload,
|
||||
r = requests.Request(method='PUT', url=callback_url, data=put_payload,
|
||||
auth=requests.auth.HTTPBasicAuth(api_key, ''))
|
||||
|
||||
StatusReporter._send_http_request(r)
|
||||
|
@ -235,11 +235,11 @@ class StatusReporter:
|
|||
StatusReporter._ipc_queue.put(r.prepare())
|
||||
"""
|
||||
|
||||
"""
|
||||
"""
|
||||
# Encode the audio metadata as json and post it back to the callback_url
|
||||
put_payload = json.dumps(audio_metadata)
|
||||
logging.debug("sending http put with payload: " + put_payload)
|
||||
r = requests.put(callback_url, data=put_payload,
|
||||
r = requests.put(callback_url, data=put_payload,
|
||||
auth=requests.auth.HTTPBasicAuth(api_key, ''),
|
||||
timeout=StatusReporter._HTTP_REQUEST_TIMEOUT)
|
||||
logging.debug("HTTP request returned status: " + str(r.status_code))
|
||||
|
@ -266,7 +266,7 @@ class StatusReporter:
|
|||
put_payload = json.dumps(audio_metadata)
|
||||
# logging.debug("sending http put with payload: " + put_payload)
|
||||
"""
|
||||
r = requests.put(callback_url, data=put_payload,
|
||||
r = requests.put(callback_url, data=put_payload,
|
||||
auth=requests.auth.HTTPBasicAuth(api_key, ''),
|
||||
timeout=StatusReporter._HTTP_REQUEST_TIMEOUT)
|
||||
"""
|
||||
|
|
|
@ -16,27 +16,27 @@ post_file() {
|
|||
mv "${file_path}" "${stripped_file_path}"
|
||||
file_path="${stripped_file_path}"
|
||||
filename="${file_path##*/}"
|
||||
|
||||
|
||||
airtime_conf_path=/etc/airtime/airtime.conf
|
||||
|
||||
|
||||
|
||||
#instance_path will look like 1/1384, for example
|
||||
http_path=$(grep base_url ${airtime_conf_path} | awk '{print $3;}' )
|
||||
http_port=$(grep base_port ${airtime_conf_path} | awk '{print $3;}' )
|
||||
|
||||
http_port=$(grep base_port ${airtime_conf_path} | awk '{print $3;}' )
|
||||
|
||||
#post request url - http://bananas.airtime.pro/rest/media, for example
|
||||
url=http://
|
||||
url+=$http_path
|
||||
url+=:
|
||||
url+=$http_port
|
||||
url+=/rest/media
|
||||
|
||||
|
||||
api_key=$(grep api_key ${airtime_conf_path} | awk '{print $3;}' )
|
||||
|
||||
|
||||
api_key=$(grep api_key ${airtime_conf_path} | awk '{print $3;}' )
|
||||
|
||||
# -f is needed to make curl fail if there's an HTTP error code
|
||||
# -L is needed to follow redirects! (just in case)
|
||||
until curl -fL --max-time 30 $url -u $api_key":" -X POST -F "file=@${file_path}"
|
||||
until curl -fL --max-time 30 $url -u $api_key":" -X POST -F "file=@${file_path}"
|
||||
do
|
||||
retry_count=$[$retry_count+1]
|
||||
if [ $retry_count -ge $max_retry ]; then
|
||||
|
|
|
@ -21,8 +21,8 @@ if ($argc <= 1)
|
|||
exit();
|
||||
}
|
||||
|
||||
$message = $argv[1];
|
||||
|
||||
$message = $argv[1];
|
||||
|
||||
$connection = new AMQPConnection(HOST, PORT, USER, PASS, VHOST);
|
||||
if (!isset($connection))
|
||||
{
|
||||
|
@ -31,10 +31,10 @@ if (!isset($connection))
|
|||
}
|
||||
|
||||
$channel = $connection->channel();
|
||||
|
||||
|
||||
// declare/create the queue
|
||||
$channel->queue_declare($queue, false, true, false, false);
|
||||
|
||||
|
||||
// declare/create the exchange as a topic exchange.
|
||||
$channel->exchange_declare($exchange, $exchangeType, false, true, false);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
post_file() {
|
||||
file_path=${1}
|
||||
filename="${file_path##*/}"
|
||||
|
||||
|
||||
#kill process after 30 minutes (360*5=30 minutes)
|
||||
max_retry=10
|
||||
retry_count=0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
ignore(output_stereo(%fdkaac(bitrate = 24, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source))
|
||||
elsif bitrate == 32 then
|
||||
ignore(output_stereo(%fdkaac(bitrate = 32, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source))
|
||||
|
|
|
@ -16,7 +16,7 @@ def notify_stream(m)
|
|||
#escaping the apostrophe, and then starting a new string right after it. This is why we use 3 apostrophes.
|
||||
json_str = string.replace(pattern="'",(fun (s) -> "'\''"), json_str)
|
||||
command = "timeout --signal=KILL 45 pyponotify --webstream='#{json_str}' --media-id=#{!current_dyn_id} &"
|
||||
|
||||
|
||||
if !current_dyn_id != "-1" then
|
||||
log(command)
|
||||
system(command)
|
||||
|
@ -24,7 +24,7 @@ def notify_stream(m)
|
|||
end
|
||||
|
||||
# A function applied to each metadata chunk
|
||||
def append_title(m) =
|
||||
def append_title(m) =
|
||||
log("Using stream_format #{!stream_metadata_type}")
|
||||
|
||||
if list.mem_assoc("mapped", m) then
|
||||
|
@ -82,9 +82,9 @@ end
|
|||
|
||||
|
||||
# Define a transition that fades out the
|
||||
# old source, adds a single, and then
|
||||
# old source, adds a single, and then
|
||||
# plays the new source
|
||||
def to_live(old,new) =
|
||||
def to_live(old,new) =
|
||||
# Fade out old source
|
||||
old = fade.final(old)
|
||||
# Compose this in sequence with
|
||||
|
@ -233,7 +233,7 @@ def clear_queue(s)
|
|||
end
|
||||
|
||||
def set_dynamic_source_id(id) =
|
||||
current_dyn_id := id
|
||||
current_dyn_id := id
|
||||
string_of(!current_dyn_id)
|
||||
end
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ end
|
|||
# cue cut fix for liquidsoap <1.2.2
|
||||
#
|
||||
# This was most likely broken on 1.1.1 (debian) as well.
|
||||
#
|
||||
#
|
||||
# adapted from https://github.com/savonet/liquidsoap/issues/390#issuecomment-277562081
|
||||
#
|
||||
def fix_cue_in(~cue_in_metadata='liq_cue_in', m) =
|
||||
|
@ -80,9 +80,9 @@ def create_source()
|
|||
sources := list.append([l], !sources)
|
||||
server.register(namespace="queues",
|
||||
"s#{!source_id}_skip",
|
||||
fun (s) -> begin log("queues.s#{!source_id}_skip")
|
||||
clear_queue(l)
|
||||
"Done"
|
||||
fun (s) -> begin log("queues.s#{!source_id}_skip")
|
||||
clear_queue(l)
|
||||
"Done"
|
||||
end)
|
||||
source_id := !source_id + 1
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%mp3(bitrate = 24, stereo = true), !source))
|
||||
else
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
source := add(normalize=false, [amplify(0.00001, noise()), !source])
|
||||
end
|
||||
|
||||
if bitrate == 24 or bitrate == 32 or bitrate == 48 then
|
||||
if bitrate == 24 or bitrate == 32 or bitrate == 48 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%vorbis(quality=-0.1, channels = 2), !source))
|
||||
else
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%opus(bitrate = 24, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source))
|
||||
else
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
ignore(output_stereo(%fdkaac(bitrate = 24, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source))
|
||||
elsif bitrate == 32 then
|
||||
ignore(output_stereo(%fdkaac(bitrate = 32, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source))
|
||||
|
|
|
@ -16,7 +16,7 @@ def notify_stream(m)
|
|||
#escaping the apostrophe, and then starting a new string right after it. This is why we use 3 apostrophes.
|
||||
json_str = string.replace(pattern="'",(fun (s) -> "'\''"), json_str)
|
||||
command = "timeout --signal=KILL 45 pyponotify --webstream='#{json_str}' --media-id=#{!current_dyn_id} &"
|
||||
|
||||
|
||||
if !current_dyn_id != "-1" then
|
||||
log(command)
|
||||
system(command)
|
||||
|
@ -24,7 +24,7 @@ def notify_stream(m)
|
|||
end
|
||||
|
||||
# A function applied to each metadata chunk
|
||||
def append_title(m) =
|
||||
def append_title(m) =
|
||||
log("Using stream_format #{!stream_metadata_type}")
|
||||
|
||||
if list.mem_assoc("mapped", m) then
|
||||
|
@ -82,9 +82,9 @@ end
|
|||
|
||||
|
||||
# Define a transition that fades out the
|
||||
# old source, adds a single, and then
|
||||
# old source, adds a single, and then
|
||||
# plays the new source
|
||||
def to_live(old,new) =
|
||||
def to_live(old,new) =
|
||||
# Fade out old source
|
||||
old = fade.final(old)
|
||||
# Compose this in sequence with
|
||||
|
@ -233,7 +233,7 @@ def clear_queue(s)
|
|||
end
|
||||
|
||||
def set_dynamic_source_id(id) =
|
||||
current_dyn_id := id
|
||||
current_dyn_id := id
|
||||
string_of(!current_dyn_id)
|
||||
end
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ end
|
|||
# cue cut fix for liquidsoap <1.2.2
|
||||
#
|
||||
# This was most likely broken on 1.1.1 (debian) as well.
|
||||
#
|
||||
#
|
||||
# adapted from https://github.com/savonet/liquidsoap/issues/390#issuecomment-277562081
|
||||
#
|
||||
def fix_cue_in(~cue_in_metadata='liq_cue_in', m) =
|
||||
|
@ -80,9 +80,9 @@ def create_source()
|
|||
sources := list.append([l], !sources)
|
||||
server.register(namespace="queues",
|
||||
"s#{!source_id}_skip",
|
||||
fun (s) -> begin log("queues.s#{!source_id}_skip")
|
||||
clear_queue(l)
|
||||
"Done"
|
||||
fun (s) -> begin log("queues.s#{!source_id}_skip")
|
||||
clear_queue(l)
|
||||
"Done"
|
||||
end)
|
||||
source_id := !source_id + 1
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%mp3(bitrate = 24, stereo = true), !source))
|
||||
else
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
source := add(normalize=false, [amplify(0.00001, noise()), !source])
|
||||
end
|
||||
|
||||
if bitrate == 24 or bitrate == 32 or bitrate == 48 then
|
||||
if bitrate == 24 or bitrate == 32 or bitrate == 48 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%vorbis(quality=-0.1, channels = 2), !source))
|
||||
else
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%opus(bitrate = 24, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source))
|
||||
else
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
ignore(output_stereo(%fdkaac(bitrate = 24, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source))
|
||||
elsif bitrate == 32 then
|
||||
ignore(output_stereo(%fdkaac(bitrate = 32, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%mp3(bitrate = 24, stereo = true), !source))
|
||||
else
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
source := add(normalize=false, [amplify(0.00001, noise()), !source])
|
||||
end
|
||||
|
||||
if bitrate == 24 or bitrate == 32 or bitrate == 48 then
|
||||
if bitrate == 24 or bitrate == 32 or bitrate == 48 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%vorbis(quality=-0.1, channels = 2), !source))
|
||||
else
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if bitrate == 24 then
|
||||
if bitrate == 24 then
|
||||
if stereo then
|
||||
ignore(output_stereo(%opus(bitrate = 24, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source))
|
||||
else
|
||||
|
|
|
@ -41,7 +41,7 @@ function exitIfNotRoot()
|
|||
function printUsage($userMsg = "")
|
||||
{
|
||||
global $opts;
|
||||
|
||||
|
||||
$msg = $opts->getUsageMessage();
|
||||
if (strlen($userMsg)>0)
|
||||
echo $userMsg;
|
||||
|
@ -64,11 +64,11 @@ function viewSpecificLog($key){
|
|||
} else printUsage();
|
||||
}
|
||||
|
||||
function dumpAllLogs(){
|
||||
function dumpAllLogs(){
|
||||
$dateStr = gmdate("Y-m-d-H-i-s");
|
||||
|
||||
$dir = getcwd();
|
||||
|
||||
|
||||
$filename = "$dir/airtime-log-all-$dateStr.tgz";
|
||||
echo "Creating Airtime logs tgz file at $filename";
|
||||
$command = "tar cfz $filename /var/log/airtime 2>/dev/null";
|
||||
|
@ -82,7 +82,7 @@ function dumpSpecificLog($key){
|
|||
$dateStr = gmdate("Y-m-d-H-i-s");
|
||||
|
||||
$dir = getcwd();
|
||||
|
||||
|
||||
$filename = "$dir/airtime-log-$key-$dateStr.tgz";
|
||||
echo "Creating Airtime logs tgz file at $filename";
|
||||
$dir = dirname($log_files[$key]);
|
||||
|
@ -104,7 +104,7 @@ function tailSpecificLog($key){
|
|||
if (isKeyValid($key)){
|
||||
echo "Tail $key log";
|
||||
pcntl_exec(exec("which tail"), array("-F", $log_files[$key]));
|
||||
pcntl_wait($status);
|
||||
pcntl_wait($status);
|
||||
} else printUsage();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# database and set appropriate tags to each MP3 file.
|
||||
|
||||
# Notes:
|
||||
# 1 - Rivendell store files in .wav format, airtime uses .mp3 format
|
||||
# 1 - Rivendell store files in .wav format, airtime uses .mp3 format
|
||||
# 2 - WAV does not have Meta-tag support so all meta-tags need to be fetched from Rivendell database.
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue