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