I'm having a hard time finding any literature or examples on how to write notifications for plasmoids.
My plasmoid can so far display notifications and sound alarms when certain conditions are met but i don't know how to:
add a 'Dismiss' button to the notification to stop playing a continuous alarm, or
get the alarm to stop sounding when the notification is closed.
This is my (abridged) code so far:
PlasmaCore.DataSource {
id: notificationSource
engine: "notifications"
}
SoundEffect {
id: notificationSound
source: plasmoid.file("data", "beep-alarm.wav")
loops: SoundEffect.Infinite
}
function createNotification(text) {
var service = notificationSource.serviceForSource("notification");
var operation = service.operationDescription("createNotification");
operation["appName"] = root.appName;
operation["appIcon"] = plasmoid.configuration.icon;
operation.summary = root.title;
operation["body"] = '<b>' + text + '</b>';
operation["expireTimeout"] = 0;
operation["isPersistent"] = 'isPersistent';
operation["urgency"] = 2;
var tmp = service.startOperationCall(operation);
if (plasmoid.configuration.alarmEnabled) {
notificationSound.play();
}
}
Plasmoid.compactRepresentation: Item {
...
PlasmaComponents.Label {
text: {
if (Number(root.x) >= plasmoid.configuration.y) {
root.createNotification(root.x);
}
root.x
}
}
...
}
Related
I have a django app based on this tutorial that works perfectly. It uses Redis in the Channel layers
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
The problem I have is that my web hosting provider will not allow Redis (unless I pay ££££).
Every example that I can find uses Redis in this role. Is there an alternative I could use?
there are a few options.
you can run your channel layer on a different service to were the main instance runs. AWS ElastiCache or many other redis hosts out there.
There is also a RabbitMQ channel layer but if your hosting provider charges a lot for reddis i expect they will also charge a lot for this ... https://github.com/CJWorkbench/channels_rabbitmq/
It turned out that channels is a non-starter on an affordable web-hosting platform. So I reverted to using Ajax and long polling. My application is based on this Django Tutorial.
models.py
class Message(models.Model):
room_name = models.CharField(null=False, blank=False, max_length=50)
sender = models.CharField(null=False, blank=False, max_length=50, default='Sender username')
datetime = models.DateTimeField(null=True, auto_now_add=True)
type = models.IntegerField(null=True, blank=True)
text = models.CharField(null=False, blank=False, max_length=250, default='message text')
context = models.TextField(null=True, blank=True)
urls.py
urlpatterns = [
path('<str:partner_pk>/check-message', views.CheckMessage.as_view(), name="check-message"),
path('<str:partner_pk>/send-message/<str:chat_text>', views.SendMessage.as_view(), name="send-message"),
]
views.py
class CheckMessage(View):
"""Duo check message."""
def get(self, request, partner_pk):
"""Render the GET request."""
pair, room_name = sort_pair(partner_pk, request.user.pk)
partner = User.objects.get(pk=partner_pk)
profile = get_object_or_404(Profile, user=request.user)
message = Message.objects.filter(room_name=room_name, sender=partner.username).earliest('datetime')
context = {'type': -1}
context = json.loads(message.context)
context['sender'] = message.sender
context['datetime'] = message.datetime
context['message_type'] = message.type
context['text'] = message.text
context['seat'] = profile.seat
message.delete()
return JsonResponse(context, safe=False)
class SendMessage(View):
def get(self, request, partner_pk, chat_text):
message_type = app.MESSAGE_TYPES['chat']
send_message(request, partner_pk, message_type, text=chat_text, context={})
return JsonResponse({}, safe=False)
chat.js
window.setInterval(checkMessage, 3000);
function checkMessage () {
$.ajax(
{
type:"GET",
url: "check-message",
cache: false,
success: function(message) {
processMessage(message);
}
}
)
}
// Take action when a message is received
function processMessage(context) {
switch (context.message_type) {
case 0:
sendMessage(context)
functionOne()
break;
case 1:
sendMessage(context)
functionTwo()
break;
case 2:
sendMessage(context)
functionThree()
break;
}
}
// Send a message to chat
function sendMessage (context) {
if (context.sender != username) {
var messageObject = {
'username': context.sender,
'text': context.text,
};
displayChat(context);
}
}
// Display a chat message in the chat box.
function displayChat(context) {
if (context.text !== '') {
var today = new Date();
var hours = pad(today.getHours(), 2)
var minutes = pad(today.getMinutes(), 2)
var seconds = pad(today.getSeconds(), 2)
var time = hours + ":" + minutes + ":" + seconds;
var chat_log = document.getElementById("chat-log");
chat_log.value += ('('+time+') '+context.sender + ': ' + context.text + '\n');
chat_log.scrollTop = chat_log.scrollHeight;
}
}
//pad string with leading zeros
function pad(num, size) {
var s = num+"";
while (s.length < size) s = "0" + s;
return s;
}
// Call submit chat message if the user presses <return>.
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function (e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
// Submit the chat message if the user clicks on 'Send'.
document.querySelector('#chat-message-submit').onclick = function (e) {
var messageField = document.querySelector('#chat-message-input'), text = messageField.value, chat_log = document.getElementById("chat-log");
context = {sender: username, text: messageField.value}
displayChat(context)
sendChat(messageField.value)
chat_log.scrollTop = chat_log.scrollHeight;
messageField.value = '';
};
// Call the send-chat view
function sendChat(chat_text) {
$.ajax(
{
type:"GET",
url: "send-message/"+chat_text,
cache: false,
}
)
}
I am using xy chart in amcharts as per my requirement.
In that amcharts, whenever on click of a bubble, I need to highlight the bubbles. So I have added listeners to my options as follows:
"listeners": [
{
"event": "clickGraphItem",
"method": function(event) {
var bubbleId = event.item.dataContext.id;
var toolTip = "";
dataProvider.forEach((data) => {
if (bubbleId != "" && bubbleId == data.id) {
if (data.bubbleSize == 10) {
data.bubbleSize = 20;
data.shape = "diamond";
} else {
data.bubbleSize = 10;
data.shape = "round";
}
} else {
data.bubbleSize = 10;
data.shape = "round";
}
});
this.AmCharts.updateChart(this.chart, () => {
// Change whatever properties you want
this.chart.dataProvider = dataProvider;
});
}
}],
But I am getting an error as "Cannot read property 'Amcharts' of undefined". I am not able to resolve the issue. Can anyone help me on this?
Referal Code for Amcharts angular as follows:
https://github.com/amcharts/amcharts3-angular2
I am working on this Kurento application and there is a strange problem i am facing where once I start recording the video and stop the recording, I cant start another recording again. The event goes to the server but nothing seems to be happening! PFB the code:
room.pipeline.create('WebRtcEndpoint', function (error, outgoingMedia) {
if (error) {
console.error('no participant in room');
// no participants in room yet release pipeline
if (Object.keys(room.participants).length == 0) {
room.pipeline.release();
}
return callback(error);
}
outgoingMedia.setMaxVideoRecvBandwidth(256);
userSession.outgoingMedia = outgoingMedia;
// add ice candidate the get sent before endpoint is established
var iceCandidateQueue = userSession.iceCandidateQueue[socket.id];
if (iceCandidateQueue) {
while (iceCandidateQueue.length) {
var message = iceCandidateQueue.shift();
console.error('user : ' + userSession.id + ' collect candidate for outgoing media');
userSession.outgoingMedia.addIceCandidate(message.candidate);
}
}
userSession.outgoingMedia.on('OnIceCandidate', function (event) {
console.log("generate outgoing candidate : " + userSession.id);
var candidate = kurento.register.complexTypes.IceCandidate(event.candidate);
userSession.sendMessage({
id: 'iceCandidate',
sessionId: userSession.id,
candidate: candidate
});
});
// notify other user that new user is joining
var usersInRoom = room.participants;
var data = {
id: 'newParticipantArrived',
new_user_id: userSession.id,
receiveVid: receiveVid
};
// notify existing user
for (var i in usersInRoom) {
usersInRoom[i].sendMessage(data);
}
var existingUserIds = [];
for (var i in room.participants) {
existingUserIds.push({id: usersInRoom[i].id, receiveVid: usersInRoom[i].receiveVid});
}
// send list of current user in the room to current participant
userSession.sendMessage({
id: 'existingParticipants',
data: existingUserIds,
roomName: room.name,
receiveVid: receiveVid
});
// register user to room
room.participants[userSession.id] = userSession;
var recorderParams = {
mediaProfile: 'WEBM',
uri: "file:///tmp/Room_"+room.name+"_file"+userSession.id +".webm"
};
//make recorder endpoint
room.pipeline.create('RecorderEndpoint', recorderParams, function(error, recorderEndpoint){
userSession.outgoingMedia.recorderEndpoint = recorderEndpoint;
outgoingMedia.connect(recorderEndpoint);
});
On the screen when I click the record button, the function on the server is:
function startRecord(socket) {
console.log("in func");
var userSession = userRegistry.getById(socket.id);
if (!userSession) {
return;
}
var room = rooms[userSession.roomName];
if(!room){
return;
}
var usersInRoom = room.participants;
var data = {
id: 'startRecording'
};
for (var i in usersInRoom) {
console.log("in loop");
var user = usersInRoom[i];
// release viewer from this
user.outgoingMedia.recorderEndpoint.record();
// notify all user in the room
user.sendMessage(data);
console.log(user.id);
}}
The thing is, for the very 1st time, it records properly i.e. file created on server and video&audio recorded properly.
When I press stop to stop recording, the intended effect is seen i.e. recording stops.
NOW, when I press record again, a video file is not made. The event reaches the server properly (console.log says so)
Can anyone help me pls?!
Thanks.
I'm making a game where the player must avoid random falling objects. I dont know how to implement pausing. I've been stuck on this for 2 days!
I tried using gotoAndPlay and such, but the objects continue to run in the background. When I resume the game, they're still falling and it seems like the frame resets and loads new random falling objects.
var steps:Number = 10;
var spriteX:Number = 280;
var spriteY:Number = 650;
var alienCounter=1;
var asteroidCounter=1;
var live:Number=3;
var depthLevel=3000;
var score:Number = 0;
var gamePaused;
dropTimer=setInterval(createAlien,2000);
drpTimer2=setInterval(createAsteroid,1000);
//---- functions ----
function checkKeys()
{
if (Key.isDown(Key.RIGHT))
{
spriteX += steps;
}
else if (Key.isDown(Key.LEFT))
{
spriteX -= steps;
}
}
function updateSpaceship()
{
ship._x = spriteX;
ship._y = spriteY;
}
function createAlien()
{
var curr_alien;
curr_alien=attachMovie("alien","alien"+alienCounter,depthLevel);
curr_alien._y=40;
curr_alien._x=Math.random()*510+20;
curr_alien._xscale=curr_alien._yscale=50;
curr_alien.speed=Math.random()*10+3;
alienCounter+=1;
depthLevel+=1;
curr_alien.onEnterFrame=alienMove;
}
function alienMove()
{
if(!gamePaused)
{
this._y+=this.speed;
if(this.hitTest(ship))
{
score += 1;
trace(score);
removeMovieClip(this);
}
}
}
function createAsteroid()
{
var curr_asteroid;
curr_asteroid=attachMovie("asteroid","asteroid"+asteroidCounter,depthLevel);
curr_asteroid._y=40;
curr_asteroid._x=Math.random()*510+20;
curr_asteroid._xscale=curr_asteroid._yscale=50;
curr_asteroid.speed=Math.random()*10+3;
asteroidCounter+=1;
depthLevel+=1;
curr_asteroid.onEnterFrame=asteroidMove;
}
function asteroidMove()
{
if(!gamePaused)
{
this._y+=this.speed;
if(this.hitTest(ship))
{
live -= 1;
trace(live);
removeMovieClip(this);
if(live<=0)
{
gotoAndPlay(5);
}
}
}
}
this.onEnterFrame = function()
{
checkKeys();
updateSpaceship();
if(Key.isDown(80))
{
if(!gamePaused)
{
gamePaused=true;
gotoAndPlay(4);
}
else
{
gamePaused=false;
gotoAndStop(3);
}
}
};
i decided to use a key instead because i cannot find any solutions when trying to use a button. The pause function is not working as i expected, i need to enter key 'p' several times to pause it, but i dont want the frame resets and loads more random objects when i resume it.
try this:
remove your:
this.onEnterFrame = function()
{
checkKeys();
updateSpaceship();
if(Key.getCode() == 80)
{
if(!gamePaused)
{
gamePaused=true;
gotoAndPlay(4);
trace("AAA")
}
else
{
gamePaused=false;
gotoAndStop(3);
trace("BBB")
}
}
};
Add this (pause by pressing the 'P' key on your keyboard):
var keyListener:Object = new Object();
keyListener.onKeyUp = function() {
if(Key.getCode() == 80) {
gamePaused = !gamePaused;
mainLoop();
}
};
Key.addListener(keyListener);
function mainLoop() {
if (gamePaused) {
gotoAndStop(4); // update game actions here
trace("game is paused");
return;
}
gotoAndStop(3); // update game actions here
trace("game is running");
}
Alternatively if you need to pause using a button:
my_pause_Button.onRelease = function() { // your button instance name 'my_pause_Button'
gamePaused = !gamePaused;
mainLoop();
};
Put this in your button function or keydown function:
stage.frameRate = 0.01;
I'm coding a basic video marquee and one of the key requirements is that the videos need to be able to advance while keeping the player in full screen.
Using Video.js (4.1.0) I have been able to get everything work correctly except that I cannot get the captions to change when switching to another video.
Either inserting a "track" tag when the player HTML is first created or adding a track to the 'options' object when the player is initialized are the only ways I can get the player to display the "CC" button and show captions. However, I cannot re-initialize the player while in full screen so changing the track that way will not work.
I have tried addTextTrack and addTextTracks and both show that the tracks have been added - using something like console.log(videoObject.textTracks()) - but the player never shows them or the "CC" button.
Here is my code, any help is greatly appreciated:
;(function(window,undefined) {
// VIDEOS OBJECT
var videos = [
{"volume":"70","title":"TEST 1","url":"test1.mp4","type":"mp4"},
{"volume":"80","title":"TEST 2","url":"test2.mp4","type":"mp4"},
{"volume":"90","title":"TEST 3","url":"test3.mp4","type":"mp4"}
];
// CONSTANTS
var VIDEO_BOX_ID = "jbunow_marquee_video_box", NAV_TEXT_ID = "jbunow_marquee_nav_text", NAV_ARROWS_ID = "jbunow_marquee_nav_arrows", VIDEO_OBJ_ID = "jbunow_marquee_video", NAV_PREV_ID = "jbunow_nav_prev", NAV_NEXT_ID = "jbunow_nav_next";
// GLOBAL VARIABLS
var videoObject;
var currentTrack = 0;
var videoObjectCreated = false;
var controlBarHideTimeout;
jQuery(document).ready(function(){
// CREATE NAV ARROWS AND LISTENERS, THEN START MARQUEE
var navArrowsHtml = "<div id='" + NAV_PREV_ID + "' title='Play Previous Video'></div>";
navArrowsHtml += "<div id='" + NAV_NEXT_ID + "' title='Play Next Video'></div>";
jQuery('#' + NAV_ARROWS_ID).html(navArrowsHtml);
jQuery('#' + NAV_PREV_ID).on('click',function() { ChangeVideo(GetPrevVideo()); });
jQuery('#' + NAV_NEXT_ID).on('click',function() { ChangeVideo(GetNextVideo()); });
ChangeVideo(currentTrack);
});
var ChangeVideo = function(newIndex) {
var videoBox = jQuery('#' + VIDEO_BOX_ID);
if (!videoObjectCreated) {
// LOAD PLAYER HTML
videoBox.html(GetPlayerHtml());
// INITIALIZE VIDEO-JS
videojs(VIDEO_OBJ_ID, {}, function(){
videoObject = this;
// LISTENERS
videoObject.on("ended", function() { ChangeVideo(GetNextVideo()); });
videoObject.on("loadeddata", function () { videoObject.play(); });
videoObjectCreated = true;
PlayVideo(newIndex);
});
} else { PlayVideo(newIndex); }
}
var PlayVideo = function(newIndex) {
// TRY ADDING MULTIPLE TRACKS
videoObject.addTextTracks([{ kind: 'captions', label: 'English2', language: 'en', srclang: 'en', src: 'track2.vtt' }]);
// TRY ADDING HTML
//jQuery('#' + VIDEO_OBJ_ID + ' video').eq(0).append("<track kind='captions' src='track2.vtt' srclang='en' label='English' default />");
// TRY ADDING SINGLE TRACK THEN SHOWING USING RETURNED ID
//var newTrack = videoObject.addTextTrack('captions', 'English2', 'en', { kind: 'captions', label: 'English2', language: 'en', srclang: 'en', src: 'track2.vtt' });
//videoObject.showTextTrack(newTrack.id_, newTrack.kind_);
videoObject.volume(parseFloat(videos[newIndex]["volume"]) / 100); // SET START VOLUME
videoObject.src({ type: "video/" + videos[newIndex]["type"], src: videos[newIndex]["url"] }); // SET NEW SRC
videoObject.load();
videoObject.ready(function () {
videoObject.play();
clearTimeout(controlBarHideTimeout);
controlBarHideTimeout = setTimeout(function() { videoObject.controlBar.fadeOut(); }, 2000);
jQuery('#' + NAV_TEXT_ID).fadeOut(150, function() {
currentTrack = newIndex;
var navHtml = "";
navHtml += "<h1>Now Playing</h1><h2>" + videos[newIndex]["title"] + "</h2>";
if (videos.length > 1) { navHtml += "<h1>Up Next</h1><h2>" + videos[GetNextVideo()]["title"] + "</h2>"; }
jQuery('#' + NAV_TEXT_ID).html(navHtml).fadeIn(250);
});
});
}
var GetPlayerHtml = function() {
var playerHtml = "";
playerHtml += "<video id='" + VIDEO_OBJ_ID + "' class='video-js vjs-default-skin' controls='controls' preload='auto' width='560' height='315'>";
playerHtml += "<source src='' type='video/mp4' />";
//playerHtml += "<track kind='captions' src='track.vtt' srclang='en' label='English' default='default' />";
playerHtml += "</video>";
return playerHtml;
}
var GetNextVideo = function() {
if (currentTrack >= videos.length - 1) { return 0; }
else { return (currentTrack + 1); }
}
var GetPrevVideo = function() {
if (currentTrack <= 0) { return videos.length - 1; }
else { return (currentTrack - 1); }
}
})(window);
The current VideoJS implementation (4.4.2) loads every kind of text tracks (subtitles, captions, chapters) on initialization time of the player itself, so it grabs correctly only those, which are defined between the <video> tags.
EDIT: I meant it does load them when calling addTextTrack, but the player UI will never update after initialization time, and will always show the initialization time text tracks.
One possible workaround is if you destroy the complete videojs player and re-create it on video source change after you have refreshed the content between the <video> tags. So this way you don't update the source via the videojs player, but via dynamically adding the required DOM elements and initializing a new player on them. Probably this solution will cause some UI flashes, and is quite non-optimal for the problem. Here is a link about destroying the videojs player
Second option is to add the dynamic text track handling to the existing code, which is not as hard as it sounds if one knows where to look (I did it for only chapters, but could be similar for other text tracks as well). The code below works with the latest official build 4.4.2. Note that I'm using jQuery for removing the text track elements, so if anyone applies these changes as is, jQuery needs to be loaded before videojs.
Edit the video.dev.js file as follows:
1: Add a clearTextTracks function to the Player
vjs.Player.prototype.clearTextTracks = function() {
var tracks = this.textTracks_ = this.textTracks_ || [];
for (var i = 0; i != tracks.length; ++i)
$(tracks[i].el()).remove();
tracks.splice(0, tracks.length);
this.trigger("textTracksChanged");
};
2: Add the new 'textTracksChanged' event trigger to the end of the existing addTextTrack method
vjs.Player.prototype.addTextTrack = function(kind, label, language, options) {
...
this.trigger("textTracksChanged");
}
3: Handle the new event in the TextTrackButton constructor function
vjs.TextTrackButton = vjs.MenuButton.extend({
/** #constructor */
init: function(player, options) {
vjs.MenuButton.call(this, player, options);
if (this.items.length <= 1) {
this.hide();
}
player.on('textTracksChanged', vjs.bind(this, this.refresh));
}
});
4: Implement the refresh method on the TextTrackButton
// removes and recreates the texttrack menu
vjs.TextTrackButton.prototype.refresh = function () {
this.removeChild(this.menu);
this.menu = this.createMenu();
this.addChild(this.menu);
if (this.items && this.items.length <= this.kind_ == "chapters" ? 0 : 1) {
this.hide();
} else
this.show();
};
Sorry, but for now I cannot link to a real working example, I hope the snippets above will be enough as a starting point to anyone intrested in this.
You can use this code when you update the source to a new video. Just call the clearTextTracks method, and add the new text tracks with the addTextTrack method, and the menus now should update themselves.
Doing the exact same thing (or rather NOT doing the exact same thing)... really need to figure out how to dynamically change / add a caption track.
This works to get it playing via the underlying HTML5, but it does not show the videojs CC button:
document.getElementById("HtmlFiveMediaPlayer_html5_api").innerHTML = '<track label="English Captions" srclang="en" kind="captions" src="http://localhost/media/captiontest/demo_Brian/demo_h264_1.vtt" type="text/vtt" default />';