From 189825135a62e692768d825eef185e6b3e82fa1f Mon Sep 17 00:00:00 2001 From: samuelmaddock Date: Sat, 7 Nov 2015 19:18:53 -0500 Subject: [PATCH] Added queue locking. --- lua/mediaplayer/cl_requests.lua | 11 +++++++ lua/mediaplayer/players/base/cl_init.lua | 3 ++ lua/mediaplayer/players/base/init.lua | 24 ++++++++++++++ lua/mediaplayer/players/base/sh_snapshot.lua | 4 ++- lua/mediaplayer/players/base/shared.lua | 8 +++++ lua/mediaplayer/sv_requests.lua | 11 +++++++ lua/mp_menu/cl_init.lua | 1 + lua/mp_menu/icons.lua | 2 ++ lua/mp_menu/queue.lua | 20 +++++++++-- lua/mp_menu/sidebar.lua | 4 +++ lua/mp_menu/sidebar_tabs.lua | 2 ++ lua/mp_menu/volume_control.lua | 31 ++++++++++++++++++ .../mediaplayer/ui/spritesheet2015-10-7.png | Bin 22794 -> 22950 bytes 13 files changed, 118 insertions(+), 3 deletions(-) diff --git a/lua/mediaplayer/cl_requests.lua b/lua/mediaplayer/cl_requests.lua index 97667c0..878b14e 100644 --- a/lua/mediaplayer/cl_requests.lua +++ b/lua/mediaplayer/cl_requests.lua @@ -186,3 +186,14 @@ function MediaPlayer.RequestShuffle( mp ) net.SendToServer() end + +function MediaPlayer.RequestLock( mp ) + + local mpId = GetMediaPlayerId( mp ) + if not mpId then return end + + net.Start( "MEDIAPLAYER.RequestLock" ) + net.WriteString( mpId ) + net.SendToServer() + +end diff --git a/lua/mediaplayer/players/base/cl_init.lua b/lua/mediaplayer/players/base/cl_init.lua index 690d4d3..b224bf9 100644 --- a/lua/mediaplayer/players/base/cl_init.lua +++ b/lua/mediaplayer/players/base/cl_init.lua @@ -50,6 +50,9 @@ local function OnMediaUpdate( len ) local queueShuffle = net.ReadBool() mp:SetQueueShuffle( queueShuffle ) + local queueLocked = net.ReadBool() + mp:SetQueueLocked( queueLocked ) + -- Read extended update information mp:NetReadUpdate() diff --git a/lua/mediaplayer/players/base/init.lua b/lua/mediaplayer/players/base/init.lua index 8dad072..9f11b78 100644 --- a/lua/mediaplayer/players/base/init.lua +++ b/lua/mediaplayer/players/base/init.lua @@ -240,6 +240,10 @@ function MEDIAPLAYER:CanPlayerRequestMedia( ply, media ) return false, msg end + if self:GetQueueLocked() and not self:IsPlayerPrivileged(ply) then + return false, "The requested media couldn't be added as the queue is locked." + end + return true end @@ -502,6 +506,22 @@ function MEDIAPLAYER:RequestShuffle( ply ) end +function MEDIAPLAYER:RequestLock( ply ) + + if not ( IsValid(ply) and self:HasListener(ply) ) then + return + end + + if not self:IsPlayerPrivileged(ply) then + self:NotifyPlayer(ply, "You don't have permission to do that.") + return + end + + self:SetQueueLocked( not self:GetQueueLocked() ) + self:BroadcastUpdate() + +end + --[[--------------------------------------------------------- Media Player Updates @@ -531,9 +551,13 @@ function MEDIAPLAYER:BroadcastUpdate( ply ) net.WriteString( self.Name ) -- media player type net.WriteEntity( self:GetOwner() ) self.net.WritePlayerState( self:GetPlayerState() ) + net.WriteBool( self:GetQueueRepeat() ) net.WriteBool( self:GetQueueShuffle() ) + net.WriteBool( self:GetQueueLocked() ) + self:NetWriteUpdate( pl ) -- mp type-specific info + net.WriteUInt( #self._Queue, self:GetQueueLimit(true) ) for _, media in ipairs(self._Queue) do self.net.WriteMedia(media) diff --git a/lua/mediaplayer/players/base/sh_snapshot.lua b/lua/mediaplayer/players/base/sh_snapshot.lua index e635a20..8e56763 100644 --- a/lua/mediaplayer/players/base/sh_snapshot.lua +++ b/lua/mediaplayer/players/base/sh_snapshot.lua @@ -7,7 +7,8 @@ function MEDIAPLAYER:GetSnapshot() currentTime = media and media:CurrentTime(), queue = queue, queueRepeat = self:GetQueueRepeat(), - queueShuffle = self:GetQueueShuffle() + queueShuffle = self:GetQueueShuffle(), + queueLocked = self:GetQueueLocked() } end @@ -16,6 +17,7 @@ function MEDIAPLAYER:RestoreSnapshot( snapshot ) self:SetQueueRepeat( snapshot.queueRepeat ) self:SetQueueShuffle( snapshot.queueShuffle ) + self:SetQueueLocked( snapshot.queueLocked ) if snapshot.media then -- restore currently playing media from where it left off diff --git a/lua/mediaplayer/players/base/shared.lua b/lua/mediaplayer/players/base/shared.lua index a31b688..cf0aece 100644 --- a/lua/mediaplayer/players/base/shared.lua +++ b/lua/mediaplayer/players/base/shared.lua @@ -315,6 +315,14 @@ function MEDIAPLAYER:SetQueueShuffle( shouldShuffle ) end end +function MEDIAPLAYER:GetQueueLocked() + return self._QueueLocked +end + +function MEDIAPLAYER:SetQueueLocked( locked ) + self._QueueLocked = locked +end + --- -- Called when the queue is updated; emits a change event. -- diff --git a/lua/mediaplayer/sv_requests.lua b/lua/mediaplayer/sv_requests.lua index eff2979..3916b07 100644 --- a/lua/mediaplayer/sv_requests.lua +++ b/lua/mediaplayer/sv_requests.lua @@ -7,6 +7,7 @@ util.AddNetworkString( "MEDIAPLAYER.RequestSeek" ) util.AddNetworkString( "MEDIAPLAYER.RequestRemove" ) util.AddNetworkString( "MEDIAPLAYER.RequestRepeat" ) util.AddNetworkString( "MEDIAPLAYER.RequestShuffle" ) +util.AddNetworkString( "MEDIAPLAYER.RequestLock" ) local REQUEST_DELAY = 0.2 @@ -148,3 +149,13 @@ net.Receive( "MEDIAPLAYER.RequestShuffle", RequestWrapper(function(mp, ply) mp:RequestShuffle( ply ) end) ) + +net.Receive( "MEDIAPLAYER.RequestLock", RequestWrapper(function(mp, ply) + + if MediaPlayer.DEBUG then + print("MEDIAPLAYER.RequestLock:", mp:GetId(), ply) + end + + mp:RequestLock( ply ) + +end) ) diff --git a/lua/mp_menu/cl_init.lua b/lua/mp_menu/cl_init.lua index 9c7aa6e..fe89213 100644 --- a/lua/mp_menu/cl_init.lua +++ b/lua/mp_menu/cl_init.lua @@ -18,6 +18,7 @@ MP.EVENTS.UI = { REMOVE_MEDIA = "mp.events.ui.removeMedia", SKIP_MEDIA = "mp.events.ui.skipMedia", VOTE_MEDIA = "mp.events.ui.voteMedia", + TOGGLE_LOCK = "mp.events.ui.toggleLock", TOGGLE_PAUSE = "mp.events.ui.togglePause", TOGGLE_REPEAT = "mp.events.ui.toggleRepeat", TOGGLE_SHUFFLE = "mp.events.ui.toggleShuffle", diff --git a/lua/mp_menu/icons.lua b/lua/mp_menu/icons.lua index ae5c47d..9b9f593 100644 --- a/lua/mp_menu/icons.lua +++ b/lua/mp_menu/icons.lua @@ -36,6 +36,8 @@ spritesheet.Register { mpIcon( "mp-repeat", 4, 2, 18, 18 ), mpIcon( "mp-shuffle", 0, 3, 16, 16 ), mpIcon( "mp-replay", 1, 3, 13, 16 ), + mpIcon( "mp-lock", 2, 3, 12, 16 ), + mpIcon( "mp-lock-open", 3, 3, 12, 16 ), mpIcon( "mp-play", 3, 4, 19, 25 ), mpIcon( "mp-pause", 4, 4, 22, 24 ), diff --git a/lua/mp_menu/queue.lua b/lua/mp_menu/queue.lua index b9fa928..639560f 100644 --- a/lua/mp_menu/queue.lua +++ b/lua/mp_menu/queue.lua @@ -75,8 +75,13 @@ derma.DefineControl( "MP.QueueHeader", "", QUEUE_HEADER, "Panel" ) local ADD_VIDEO_BTN = {} -ADD_VIDEO_BTN.Color = Color( 232, 78, 64 ) -ADD_VIDEO_BTN.HoverColor = Color( 252, 98, 84 ) +local AddEnabledColor = Color( 232, 78, 64 ) +local AddEnabledHoverColor = Color( 252, 98, 84 ) + +local AddDisabledColor = Color( 140, 140, 140 ) + +ADD_VIDEO_BTN.Color = AddEnabledColor +ADD_VIDEO_BTN.HoverColor = AddEnabledHoverColor function ADD_VIDEO_BTN:Init() @@ -94,6 +99,17 @@ function ADD_VIDEO_BTN:Init() end +function ADD_VIDEO_BTN:SetLocked( locked ) + + if locked and not hook.Run( MP.EVENTS.UI.PRIVILEGED_PLAYER ) then + self:SetDisabled( true ) + self.Color = AddDisabledColor + self.HoverColor = AddDisabledColor + self:SetIcon( "mp-lock" ) + end + +end + function ADD_VIDEO_BTN:Paint( w, h ) local col diff --git a/lua/mp_menu/sidebar.lua b/lua/mp_menu/sidebar.lua index c52c2ee..c27d2d3 100644 --- a/lua/mp_menu/sidebar.lua +++ b/lua/mp_menu/sidebar.lua @@ -106,6 +106,10 @@ function SidebarPresenter:SetupEvents() MediaPlayer.Skip( mp ) end ) + self:RegisterHook( MP.EVENTS.UI.TOGGLE_LOCK, function() + MediaPlayer.RequestLock( mp ) + end ) + self:RegisterHook( MP.EVENTS.UI.TOGGLE_PAUSE, function() MediaPlayer.Pause( mp ) end ) diff --git a/lua/mp_menu/sidebar_tabs.lua b/lua/mp_menu/sidebar_tabs.lua index e56bcfd..6327205 100644 --- a/lua/mp_menu/sidebar_tabs.lua +++ b/lua/mp_menu/sidebar_tabs.lua @@ -179,6 +179,8 @@ function CURRENTLY_PLAYING_TAB:OnMediaPlayerChanged( mp ) self:SetMediaPlayerId( mp:GetId() ) + self.QueuePanel.Header.AddVidBtn:SetLocked( mp:GetQueueLocked() ) + if not self.MediaChangedHandle then -- set current media self.PlaybackPanel:OnMediaChanged( mp:GetMedia() ) diff --git a/lua/mp_menu/volume_control.lua b/lua/mp_menu/volume_control.lua index fd5fb91..bba1d0f 100644 --- a/lua/mp_menu/volume_control.lua +++ b/lua/mp_menu/volume_control.lua @@ -28,6 +28,8 @@ function PANEL:Init() self:AddButton( self.RepeatBtn ) self.ShuffleBtn = vgui.Create( "MP.ShuffleButton" ) self:AddButton( self.ShuffleBtn ) + self.LockBtn = vgui.Create( "MP.LockButton" ) + self:AddButton( self.LockBtn ) end self:OnVolumeChanged( MediaPlayer.Volume() ) @@ -54,6 +56,7 @@ function PANEL:OnMediaPlayerChanged( mp ) if hook.Run( MP.EVENTS.UI.PRIVILEGED_PLAYER ) then self.RepeatBtn:SetEnabled( mp:GetQueueRepeat() ) self.ShuffleBtn:SetEnabled( mp:GetQueueShuffle() ) + self.LockBtn:SetEnabled( mp:GetQueueLocked() ) end end @@ -213,3 +216,31 @@ function SHUFFLE_BTN:DoClick() end derma.DefineControl( "MP.ShuffleButton", "", SHUFFLE_BTN, "MP.SidebarToggleButton" ) + + +local LOCK_BTN = {} + +function LOCK_BTN:Init() + self.BaseClass.Init( self ) + self:SetIcon( "mp-lock-open" ) + self:SetTooltip( "Toggle Queue Lock" ) +end + +function LOCK_BTN:DoClick() + self.BaseClass.DoClick( self ) + + hook.Run( MP.EVENTS.UI.TOGGLE_LOCK ) + self:UpdateIcon() +end + +function LOCK_BTN:SetEnabled( bEnabled ) + self.BaseClass.SetEnabled( self, bEnabled ) + self:UpdateIcon() +end + +function LOCK_BTN:UpdateIcon() + local icon = self:GetEnabled() and "mp-lock" or "mp-lock-open" + self:SetIcon( icon ) +end + +derma.DefineControl( "MP.LockButton", "", LOCK_BTN, "MP.SidebarToggleButton" ) diff --git a/materials/mediaplayer/ui/spritesheet2015-10-7.png b/materials/mediaplayer/ui/spritesheet2015-10-7.png index 79a2884c59f312e8788658455a03b39680545d70..19be63c700131f087c4baf4f86db3a8b909a9b0d 100644 GIT binary patch delta 3795 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

b$0scq*>q8yT7!ryM-9Z81KU2 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