Merge pull request #4 from LibreTime/master

Update
This commit is contained in:
Codenift 2019-11-06 21:26:08 -05:00 committed by GitHub
commit 8c77bfde1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 449 additions and 65 deletions

View File

@ -155,20 +155,8 @@ class FileDataHelper {
} else {
$ext = 'jpg';
}
self::resizeGroup($file, $ext);
if (file_exists($file)) {
self::resizeImage($file, $file . '-32.jpg', $ext, 32, 100);
self::resizeImage($file, $file . '-64.jpg', $ext, 64, 100);
self::resizeImage($file, $file . '-128.jpg', $ext, 128, 100);
self::resizeImage($file, $file . '-256.jpg', $ext, 256, 100);
self::resizeImage($file, $file . '-512.jpg', $ext, 512, 100);
self::imgToDataURI($file . '-32.jpg', $file . '-32');
self::imgToDataURI($file . '-64.jpg', $file . '-64');
self::imgToDataURI($file . '-128.jpg', $file . '-128');
self::imgToDataURI($file . '-256.jpg', $file . '-256');
} else {
Logging::error("The file $file does not exist");
}
} else {
$get_img = '';
}
@ -231,26 +219,120 @@ class FileDataHelper {
} else {
$ext = 'jpg';
}
self::resizeGroup($rfile, $ext);
if (file_exists($rfile)) {
self::resizeImage($rfile, $rfile . '-32.jpg', $ext, 32, 100);
self::resizeImage($rfile, $rfile . '-64.jpg', $ext, 64, 100);
self::resizeImage($rfile, $rfile . '-128.jpg', $ext, 128, 100);
self::resizeImage($rfile, $rfile . '-256.jpg', $ext, 256, 100);
self::resizeImage($rfile, $rfile . '-512.jpg', $ext, 512, 100);
self::imgToDataURI($rfile . '-32.jpg', $rfile . '-32');
self::imgToDataURI($rfile . '-64.jpg', $rfile . '-64');
self::imgToDataURI($rfile . '-128.jpg', $rfile . '-128');
self::imgToDataURI($rfile . '-256.jpg', $rfile . '-256');
} else {
Logging::error("The file $rfile does not exist");
}
} else {
$get_img = "";
}
return $get_img;
}
/**
* Upload artwork
*
* @param string $trackid
* @param string $data
*
* @return string Path to artwork
*/
public static function setArtwork($trackid, $data)
{
$file = Application_Model_StoredFile::RecallById($trackid);
$md = $file->getMetadata();
$storDir = Application_Model_MusicDir::getStorDir();
$fp = $storDir->getDirectory();
$dbAudioPath = $md["MDATA_KEY_FILEPATH"];
$fullpath = $fp . $dbAudioPath;
if ($data == "0") {
$get_img = "";
self::removeArtwork($trackid, $data);
} else {
$base64 = @$data;
$mime = explode(';', $base64)[0];
$audioPath = dirname($fullpath);
$dbPath = dirname($dbAudioPath);
$path_parts = pathinfo($fullpath);
$file = $path_parts['filename'];
//Save Data URI
if (file_put_contents($audioPath . "/" . $file, $base64)) {
$get_img = $dbPath . "/" . $file;
} else {
Logging::error("Could not save Data URI");
}
$rfile = $audioPath . "/" . $file;
if ($mime == "data:image/png") {
$ext = 'png';
} elseif ($mime == "data:image/gif") {
$ext = 'gif';
} elseif ($mime == "data:image/bmp") {
$ext = 'bmp';
} else {
$ext = 'jpg';
}
self::resizeGroup($rfile, $ext);
}
return $get_img;
}
/**
*
* Deletes just the artwork
*/
public static function removeArtwork($trackid)
{
$file = Application_Model_StoredFile::RecallById($trackid);
$md = $file->getMetadata();
$storDir = Application_Model_MusicDir::getStorDir();
$fp = $storDir->getDirectory();
$dbAudioPath = $md["MDATA_KEY_ARTWORK"];
$fullpath = $fp . $dbAudioPath;
if (file_exists($fullpath)) {
foreach (glob("$fullpath*", GLOB_NOSORT) as $filename) {
unlink($filename);
}
} else {
throw new Exception("Could not locate file ".$filepath);
}
return "";
}
/**
* Resize artwork group
*
* @param string $file
* @param string $ext
*/
public static function resizeGroup($file, $ext)
{
if (file_exists($file)) {
self::resizeImage($file, $file . '-32.jpg', $ext, 32, 100);
self::resizeImage($file, $file . '-64.jpg', $ext, 64, 100);
self::resizeImage($file, $file . '-128.jpg', $ext, 128, 100);
self::resizeImage($file, $file . '-256.jpg', $ext, 256, 100);
self::resizeImage($file, $file . '-512.jpg', $ext, 512, 100);
self::imgToDataURI($file . '-32.jpg', $file . '-32');
self::imgToDataURI($file . '-64.jpg', $file . '-64');
self::imgToDataURI($file . '-128.jpg', $file . '-128');
self::imgToDataURI($file . '-256.jpg', $file . '-256');
} else {
Logging::error("The file $file does not exist");
}
}
/**
* Render image
* Used in API to render JPEG

View File

@ -397,6 +397,15 @@ class LibraryController extends Zend_Controller_Action
if ($j["value"] == null || $j["value"] == ''){
$serialized["artwork"] = FileDataHelper::resetArtwork($file_id);
}
} elseif ($j["name"] == "set_artwork") {
if ($j["value"] != null || $j["value"] != ''){
$serialized["artwork"] = FileDataHelper::setArtwork($file_id, $j["value"] );
}
} elseif ($j["name"] == "remove_artwork") {
if ($j["value"] == 1){
$remove_artwork = true;
$serialized["artwork"] = FileDataHelper::removeArtwork($file_id);
}
} else {
$serialized[$j["name"]] = $j["value"];
}

View File

@ -20,15 +20,32 @@ class Application_Form_EditAudioMD extends Zend_Form
// Add artwork hidden field
$artwork = new Zend_Form_Element_Hidden('artwork');
$artwork->class = 'input_text artwork_'. $p_id;
$artwork->setFilters(array('StringTrim'))
->setValidators(array(
new Zend_Validate_StringLength(array('max' => 512))
new Zend_Validate_StringLength(array('max' => 2048))
));
$file_id->addDecorator('HtmlTag', array('tag' => 'div', 'style' => 'display:none'));
$file_id->removeDecorator('Label');
$file_id->setAttrib('class', 'artwork');
$this->addElement($artwork);
// Set artwork hidden field
$set_artwork = new Zend_Form_Element_Hidden('set_artwork');
$set_artwork->class = 'input_text set_artwork_'. $p_id;
$file_id->addDecorator('HtmlTag', array('tag' => 'div', 'style' => 'display:none'));
$file_id->removeDecorator('Label');
$file_id->setAttrib('class', 'set_artwork');
$this->addElement($set_artwork);
// Remove artwork hidden field
$remove_artwork = new Zend_Form_Element_Hidden('remove_artwork');
$remove_artwork->class = 'input_text remove_artwork_'. $p_id;
$file_id->addDecorator('HtmlTag', array('tag' => 'div', 'style' => 'display:none'));
$file_id->removeDecorator('Label');
$file_id->setAttrib('class', 'remove_artwork');
$this->addElement($remove_artwork);
// Add title field
$track_title = new Zend_Form_Element_Text('track_title');
$track_title->class = 'input_text';

View File

@ -104,7 +104,10 @@ SQL;
$utcNow = new DateTime("now", new DateTimeZone("UTC"));
$shows = Application_Model_Show::getPrevCurrentNext($utcNow, $utcTimeEnd, $showsToRetrieve);
$currentShowID = (is_array($shows['currentShow'] && count($shows['currentShow'])>0))?$shows['currentShow']['instance_id']:null;
$currentShowID = null;
if (is_array($shows['currentShow']) && count($shows['currentShow'])>0) {
$currentShowID = $shows['currentShow']['instance_id'];
}
$source = self::_getSource();
$results = Application_Model_Schedule::getPreviousCurrentNextMedia($utcNow, $currentShowID, self::_getSource());

View File

@ -19,7 +19,19 @@
</div>
</div>
<div class="track-edit-left">
<?php echo '<img width="140" height="140" src="' . $get_artwork .'">'; ?>
<div class="artwork-upload" data-id="<?php echo($this->id); ?>">
<div class="artwork-edit">
<input type='file' class="artworkUpload artwork-uploaded-<?php echo($this->id); ?>" id="artworkUpload" data-id="<?php echo($this->id); ?>" accept=".png, .jpg, .jpeg" />
<label for="artworkUpload"></label>
</div>
<div id="artwork-preview" class="artwork-preview">
<div class="artwork-preview-<?php echo($this->id); ?>" id="artworkPreview" style="background-image: url(<?php echo $get_artwork; ?>);">
</div>
</div>
</div>
<div>
<a href="#" class="delete-artwork" data-id="<?php echo($this->id); ?>" style="font-size: 11px;">Remove</a>
</div>
</div>
</div>
<div style="height: 160px;"></div>

View File

@ -35,7 +35,7 @@ INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_pass', 'h
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_admin_user', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_admin_pass', '', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_mount', 'airtime_128', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_url', 'http://airtime.sourcefabric.org', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_url', 'https://libretime.org', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_description', 'LibreTime Radio! Stream #1', 'string');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s1_genre', 'genre', 'string');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -4284,3 +4284,64 @@ li .ui-state-hover {
.track-edit-right {
margin-left: 160px;
}
/* Artwork Upload in Tracks Edit */
.artwork-upload {
position: relative;
max-width: 205px;
}
.artwork-upload .artwork-edit {
position: absolute;
z-index: 1;
top: 0;
}
.artwork-upload .artwork-edit input {
display: none;
}
.artwork-upload .artwork-edit input + label {
display: inline-block;
width: 140px;
height: 140px;
margin: 1px;
border-radius: 1px;
border: 1px solid transparent;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.12);
cursor: pointer;
font-weight: normal;
transition: all 0.2s ease-in-out;
}
.artwork-upload .artwork-edit input + label:hover {
background: rgba(0, 0, 0, 0.40);
border-color: #d6d6d6;
}
.artwork-upload .artwork-edit input + label:after {
color: #757575;
position: absolute;
top: 10px;
left: 0;
right: 0;
text-align: center;
margin: auto;
}
.artwork-upload .artwork-preview {
width: 140px;
height: 140px;
position: relative;
border-radius: 1px;
border: 2px solid #F8F8F8;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.1);
}
.artwork-upload .artwork-preview > div {
width: 100%;
height: 100%;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
/* Drag and Drop Artwork */
body.droppable .artwork-upload .artwork-preview {
border: 2px dashed lightblue;
z-index: 9999;
}

View File

@ -1601,6 +1601,70 @@ var validationTypes = {
};
function readArtworkURL(input, id) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('.artwork-preview-'+id).css('background-image', 'url('+e.target.result +')');
$('.artwork-preview-'+id).hide();
$('.artwork-preview-'+id).fadeIn(500);
$('.set_artwork_'+id).val(function() {
return e.target.result;
});
}
reader.readAsDataURL(input.files[0]);
}
}
// Resample Artwork
var resampleImg = (function (canvas) {
function resampleImg(img, width, height, onresample) {
var load = typeof img == "string",
i = load || img;
if (load) {
i = new Image;
i.onload = onload;
i.onerror = onerror;
}
i._onresample = onresample;
i._width = width;
i._height = height;
load ? (i.src = img) : onload.call(img);
}
function onerror() {
throw ("not found: " + this.src);
}
function onload() {
var img = this,
width = img._width,
height = img._height,
onresample = img._onresample;
var minValue = Math.min(img.height, img.width);
width == null && (width = round(img.width * height / img.height));
height == null && (height = round(img.height * width / img.width));
delete img._onresample;
delete img._width;
delete img._height;
canvas.width = width;
canvas.height = height;
context.drawImage(img,0,0,minValue,minValue,0,0,width,height);
onresample(canvas.toDataURL("image/jpeg"));
}
var context = canvas.getContext("2d"),
round = Math.round;
return resampleImg;
}(this.document.createElement("canvas")));
$(document).ready(function() {
if (window.location.href.indexOf("showbuilder") > -1) {
AIRTIME.library.initPodcastDatatable();
@ -1613,5 +1677,114 @@ $(document).ready(function() {
$(window).resize(function() {
resizeAdvancedSearch();
});
});
// delete artwork
$(document).on('click', '.delete-artwork', function(event) {
event.preventDefault();
event.stopPropagation();
var id = $(this).attr('data-id');
$('.artwork-preview-'+id).css('background-image', 'url('+ baseUrl +'css/images/no-cover.jpg)');
$('.artwork-preview-'+id).hide();
$('.artwork-preview-'+id).fadeIn(500);
$('.artwork_'+id).val(function() {
return "";
});
$('.set_artwork_'+id).val(function() {
return "";
});
$('.remove_artwork_'+id).val(function() {
return 1;
});
});
// image upload by clicking on the artwork container
$(document).on('change', '.artworkUpload', 'input', function(event) {
event.preventDefault();
event.stopPropagation();
var id = $(this).attr('data-id');
readArtworkURL(this, id);
});
// image upload by dragging onto the artwork container
$.event.props.push('dataTransfer');
(function() {
var s;
var Artwork = {
settings: {
body: $("body")
},
init: function() {
s = Artwork.settings;
Artwork.bindUIActions();
},
bindUIActions: function() {
var timer;
s.body.on('dragover', '.artwork-upload', function(event) {
event.preventDefault();
event.stopPropagation();
clearTimeout(timer);
Artwork.showDroppableArea();
return false;
});
s.body.on('dragleave', '.artwork-upload', function(event) {
event.preventDefault();
event.stopPropagation();
timer = setTimeout(function() {
Artwork.hideDroppableArea();
}, 200);
});
s.body.on('drop', '.artwork-upload', function(event) {
event.preventDefault();
event.stopPropagation();
var id = $(this).attr('data-id');
Artwork.handleDrop(event.dataTransfer.files, id);
});
},
showDroppableArea: function() {
s.body.addClass("droppable");
},
hideDroppableArea: function() {
s.body.removeClass("droppable");
},
handleDrop: function(files, id) {
Artwork.hideDroppableArea();
var file = files[0];
if (typeof file !== 'undefined' && file.type.match('image.*')) {
Artwork.resizeImage(file, 512, function(data) {
Artwork.placeImage(data, id);
});
} else {
alert("The file is not an image.");
}
},
resizeImage: function(file, size, callback) {
var fileTracker = new FileReader;
fileTracker.onload = function() {
resampleImg(this.result, size, size, callback);
}
fileTracker.readAsDataURL(file);
fileTracker.onabort = function() {
alert("Upload aborted!");
}
fileTracker.onerror = function() {
alert("File could not be read.");
}
},
placeImage: function(data, id) {
$('.artwork-preview-'+id).css('background-image', 'url('+ data +')');
$('.artwork-preview-'+id).hide();
$('.artwork-preview-'+id).fadeIn(500);
$('.set_artwork_'+id).val(function() {
return data;
});
}
}
Artwork.init();
})();
});

View File

@ -1,5 +1,8 @@
# Installing LibreTime
There two methods of installing LibreTime - [Source](#source) or
[Ubuntu package](#ubuntu-package).
## Source
LibreTime releases can be downloaded [here](https://github.com/LibreTime/libretime/releases).
Recommendations:
@ -18,12 +21,27 @@ sudo ./install
By default, the installer will install and configure all dependencies.
## Ubuntu Package
LibreTime maintains amd64 .deb packages for Ubuntu 16.04 (Xenial) and 18.04
(Bionic). These can be downloaded [here](https://github.com/LibreTime/libretime-debian-packaging/releases).
Issues with installation of these packages should be reported to the
[LibretTime/libretime-debian-packaging](https://github.com/LibreTime/libretime-debian-packaging)
repository.
These are installed by running the following from the command line (the `./` in
front of the libretime package is important):
```
sudo apt install icecast2 ./libretime_<version>_amd64.deb
```
`<version>` is replaced by the version of the package downloaded.
## Alternative OS installations
Installation in Debian 9 and other Linux distributions is possible, but multiple outstanding issues have yet
to be resolved. Installation on Ubuntu 14.04.5 LTS (Trusty Tahr) is also working, but deprecated due to the
Installation in Debian 9 and other Linux distributions is possible, but these
are less tested. Installation on Ubuntu 14.04.5 LTS (Trusty Tahr) is also working, but deprecated due to the
fact that this version will reach its official end of life in April 2019.
Plans are in the works for `.deb` and `.rpm` packages, as well as Docker and AWS images.
Plans are in the works for `.rpm` packages, as well as Docker and AWS images.
Please note that the install script does not take care to ensure that any
packages installed are set up in a secure manner. Please see the chapter on

View File

@ -60,14 +60,14 @@ s3_mount = "airtime_160.mp3"
s4_mount = "airtime_160.mp3"
# Webstream metadata settings
s1_url = "http://airtime.sourcefabric.org"
s2_url = "http://airtime.sourcefabric.org"
s3_url = "http://airtime.sourcefabric.org"
s4_url = "http://airtime.sourcefabric.org"
s1_description = "Airtime Radio! Stream 1"
s2_description = "Airtime Radio! Stream 2"
s3_description = "Airtime Radio! Stream 3"
s4_description = "Airtime Radio! Stream 4"
s1_url = "https://libretime.org"
s2_url = "https://libretime.org"
s3_url = "https://libretime.org"
s4_url = "https://libretime.org"
s1_description = "LibreTime Radio! Stream 1"
s2_description = "LibreTime Radio! Stream 2"
s3_description = "LibreTime Radio! Stream 3"
s4_description = "LibreTime Radio! Stream 4"
s1_genre = "genre"
s2_genre = "genre"
s3_genre = "genre"

View File

@ -7,15 +7,32 @@ import sys
from threading import Thread
import time
# For RabbitMQ
from kombu.connection import BrokerConnection
from kombu.connection import Connection
from kombu.messaging import Exchange, Queue
from kombu.simple import SimpleQueue
from amqp.exceptions import AMQPError
import json
from kombu.mixins import ConsumerMixin
logging.captureWarnings(True)
class RabbitConsumer(ConsumerMixin):
def __init__(self, connection, queues, handler):
self.connection = connection
self.queues = queues
self.handler = handler
def get_consumers(self, Consumer, channel):
return [
Consumer(self.queues, callbacks=[self.on_message], accept=['text/plain']),
]
def on_message(self, body, message):
self.handler.handle_message(message.payload)
message.ack()
class PypoMessageHandler(Thread):
def __init__(self, pq, rq, config):
Thread.__init__(self)
@ -26,22 +43,19 @@ class PypoMessageHandler(Thread):
def init_rabbit_mq(self):
self.logger.info("Initializing RabbitMQ stuff")
simple_queue = None
try:
schedule_exchange = Exchange("airtime-pypo", "direct", durable=True, auto_delete=True)
schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo")
connection = BrokerConnection(self.config["host"],
self.config["user"],
self.config["password"],
self.config["vhost"])
channel = connection.channel()
simple_queue = SimpleQueue(channel, schedule_queue)
with Connection(self.config["host"], \
self.config["user"], \
self.config["password"], \
self.config["vhost"], \
heartbeat = 5) as connection:
rabbit = RabbitConsumer(connection, [schedule_queue], self)
rabbit.run()
except Exception, e:
self.logger.error(e)
return simple_queue
"""
Handle a message from RabbitMQ, put it into our yucky global var.
Hopefully there is a better way to do this.
@ -89,12 +103,7 @@ class PypoMessageHandler(Thread):
def main(self):
try:
with self.init_rabbit_mq() as queue:
while True:
message = queue.get(block=True)
self.handle_message(message.payload)
# ACK the message to take it off the queue
message.ack()
self.init_rabbit_mq()
except Exception, e:
self.logger.error('Exception: %s', e)
self.logger.error("traceback: %s", traceback.format_exc())