Skip to content

Commit

Permalink
Refactor Video Queue (#295)
Browse files Browse the repository at this point in the history
* Refactor Video Queue

* Video queue and playlists

* Web app casting

* queue playlist notification

* VideoQueueView work

* restore properties

* changes
  • Loading branch information
iBicha authored Feb 19, 2024
1 parent 46070b9 commit 6e3f496
Show file tree
Hide file tree
Showing 58 changed files with 1,168 additions and 928 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- The queue no longer contains playlists. When a playlist is added to the queue, the entire playlist is loaded.
- It can take a few seconds to load large playlist before it can be added to the queue
- This is done to be more compatible more the lounge, which does not contain "Playlists in the queue" concept.
- Removed `fields` from Invidious requests, as per [https://github.com/iv-org/invidious/pull/4276](https://github.com/iv-org/invidious/pull/4276)
- The `POST /api/queue/` no longer returns the current queue. Instead it returns a 204.

## [0.19.2] - 2024-02-11

Expand Down
51 changes: 19 additions & 32 deletions docs/playlet-web-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ paths:
description: No Content
/api/queue:
get:
summary: Get play queue
summary: Get video queue
description: Get the current videos in the queue.
operationId: getPlayQueue
operationId: getVideoQueue
responses:
"200":
description: OK
Expand All @@ -151,56 +151,43 @@ paths:
schema:
type: object
properties:
index:
type: integer
playlistIndex:
type: integer
items:
type: array
items:
$ref: "#/components/schemas/PlayQueueObject"
$ref: "#/components/schemas/VideoQueueObject"
index:
type: integer
nowPlaying:
$ref: "#/components/schemas/VideoQueueObject"
post:
summary: Add to play queue
summary: Add to video queue
description: Add a video or a playlist to the queue.
operationId: addToPlayQueue
operationId: addToVideoQueue
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/PlayQueueObject"
$ref: "#/components/schemas/VideoQueueObject"
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
index:
type: integer
playlistIndex:
type: integer
items:
type: array
items:
$ref: "#/components/schemas/PlayQueueObject"
"204":
description: No Content
delete:
summary: Clear play queue
description: Clear the play queue.
operationId: clearPlayQueue
summary: Clear video queue
description: Clear the video queue.
operationId: clearVideoQueue
responses:
"204":
description: No Content
/api/queue/play:
post:
summary: Play video or playlist
summary: Play video
description: Play a video or playlist. This adds the video or playlist to the current position in the queue and plays it.
operationId: playPlayQueue
operationId: playVideoQueue
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/PlayQueueObject"
$ref: "#/components/schemas/VideoQueueObject"
responses:
"204":
description: No Content
Expand Down Expand Up @@ -574,7 +561,7 @@ components:
sponsorblock.show_notifications:
type: boolean
# TODO:P1 add the rest of properties
PlayQueueObject:
VideoQueueObject:
type: object
properties:
videoId:
Expand Down
20 changes: 10 additions & 10 deletions playlet-lib/src/components/AppController/AppController.bs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "pkg:/components/Dialog/DialogUtils.bs"
import "pkg:/components/Navigation/LongPress.bs"
import "pkg:/components/PlayQueue/PlayQueueViewUtils.bs"
import "pkg:/components/VideoPlayer/VideoUtils.bs"
import "pkg:/components/VideoQueue/VideoQueueUtils.bs"
import "pkg:/components/VideoQueue/VideoQueueViewUtils.bs"
import "pkg:/source/utils/FocusManagement.bs"
import "pkg:/source/utils/Logging.bs"
import "pkg:/source/utils/StringUtils.bs"
Expand Down Expand Up @@ -86,13 +86,13 @@ function onKeyEvent(key as string, press as boolean) as boolean
end if

if key = OPTIONS_LONG_PRESS_KEY and press
LogInfo("Opening PlayQueueView")
PlayQueueViewUtils.Open(m.top.playQueue, m.top)
LogInfo("Opening VideoQueueView")
VideoQueueViewUtils.Open(m.top.videoQueue, m.top)
return true
end if

if key = "options" and not press
if VideoUtils.ToggleVideoPictureInPicture()
if VideoQueueUtils.ToggleVideoPictureInPicture(m.top.videoQueue)
return true
end if
end if
Expand All @@ -102,26 +102,26 @@ function onKeyEvent(key as string, press as boolean) as boolean
end if

if key = "play"
if VideoUtils.TogglePause()
if VideoQueueUtils.TogglePause(m.top.videoQueue)
return true
end if
end if

if key = "pause"
if VideoUtils.PauseVideo()
if VideoQueueUtils.Pause(m.top.videoQueue)
return true
end if
end if

if key = "playonly"
if VideoUtils.ResumeVideo()
if VideoQueueUtils.Play(m.top.videoQueue)
return true
end if
end if

if key = "back"
if VideoUtils.IsVideoPlayerOpen() and not VideoUtils.IsVideoPlayerFullScreen()
if VideoUtils.ToggleVideoPictureInPicture()
if VideoQueueUtils.IsVideoPlayerOpen(m.top.videoQueue) and not VideoQueueUtils.IsVideoPlayerFullScreen(m.top.videoQueue)
if VideoQueueUtils.ToggleVideoPictureInPicture(m.top.videoQueue)
return true
end if
end if
Expand Down
2 changes: 1 addition & 1 deletion playlet-lib/src/components/AppController/AppController.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<interface>
<field id="root" type="node" />
<field id="stack" type="node" />
<field id="playQueue" type="node" />
<field id="videoQueue" type="node" />
<function name="PushScreen" />
<function name="PopScreen" />
<function name="FocusTopScreen" />
Expand Down
3 changes: 0 additions & 3 deletions playlet-lib/src/components/ContentNode/ChannelContentNode.bs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import "pkg:/source/AsyncTask/Tasks.bs"
import "pkg:/source/utils/ErrorUtils.bs"
import "pkg:/source/utils/Logging.bs"

function Init()
end function

function LoadChannel(invidiousNode as object) as void
if m.contentTask <> invalid
m.contentTask.cancel = true
Expand Down
55 changes: 55 additions & 0 deletions playlet-lib/src/components/ContentNode/PlaylistContentNode.bs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import "pkg:/components/Dialog/DialogUtils.bs"
import "pkg:/components/VideoFeed/FeedLoadState.bs"
import "pkg:/source/AsyncTask/AsyncTask.bs"
import "pkg:/source/AsyncTask/Tasks.bs"
import "pkg:/source/utils/ErrorUtils.bs"
import "pkg:/source/utils/Logging.bs"

function LoadPlaylistPage(invidiousNode as object) as void
LoadPlaylist(invidiousNode, true)
end function

function LoadPlaylistAll(invidiousNode as object) as void
LoadPlaylist(invidiousNode, false)
end function

function LoadPlaylist(invidiousNode as object, singlePage as boolean) as void
if m.contentTask <> invalid
m.contentTask.cancel = true
end if

loadState = m.top.loadState
if loadState = FeedLoadState.Loading or loadState = FeedLoadState.Loaded
return
end if

m.top.loadState = FeedLoadState.Loading
m.contentTask = AsyncTask.Start(Tasks.PlaylistContentTask, {
content: m.top
invidious: invidiousNode
singlePage: singlePage
}, OnPlaylistContentTaskResult)
end function

function OnPlaylistContentTaskResult(output as object) as void
m.contentTask = invalid

if output.cancelled
return
end if

if not output.success or not output.result.success
' output.error for unhandled exception
error = output.error
if error = invalid
' output.result.error for network errors
error = output.result.error
end if
error = ErrorUtils.Format(error)
LogError(error)
playlistId = output.task.input.content.playlistId
message = `Failed to load playlist ${playlistId}\n${error}`
DialogUtils.ShowDialog(message, "Playlist load fail", true)
return
end if
end function
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<component name="PlaylistContentNode" extends="ContentNode">
<interface>
<function name="LoadPlaylistPage" />
<function name="LoadPlaylistAll" />

<field id="type" type="string" value="playlist" />
<field id="loadState" type="string" />
<!-- index in a feed -->
Expand Down
57 changes: 57 additions & 0 deletions playlet-lib/src/components/ContentNode/PlaylistContentTask.bs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import "pkg:/components/Services/Invidious/InvidiousService.bs"
import "pkg:/components/Services/Invidious/InvidiousToContentNode.bs"
import "pkg:/components/VideoFeed/FeedLoadState.bs"

@asynctask
function PlaylistContentTask(input as object) as object
contentNode = input.content
invidiousNode = input.invidious
singlePage = input.singlePage

if m.top.cancel
contentNode.loadState = FeedLoadState.None
return invalid
end if

service = new Invidious.InvidiousService(invidiousNode)
instance = service.GetInstance()

while true
index = contentNode.getChildCount()
response = service.GetPlaylist(contentNode.playlistId, index, m.top.cancellation)

if m.top.cancel
contentNode.loadState = FeedLoadState.None
return invalid
end if

metadata = response.Json()

if not response.IsSuccess() or metadata = invalid
contentNode.loadState = FeedLoadState.Error
return {
success: false
error: response.ErrorMessage()
}
end if

InvidiousContent.ToPlaylistContentNode(contentNode, metadata, instance)
childCount = contentNode.getChildCount()

if metadata.videos.Count() = 0 or childCount >= metadata.videoCount
contentNode.loadState = FeedLoadState.Loaded
return {
success: true
}
else
contentNode.loadState = FeedLoadState.LoadedPage
if singlePage
return {
success: true
}
end if
end if
end while

return invalid
end function
5 changes: 4 additions & 1 deletion playlet-lib/src/components/ContentNode/VideoContentNode.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
<field id="index" type="integer" value="-1" />
<!-- index in a feed -->
<field id="feedSourcesIndex" type="integer" value="-1" />
<!-- index in the queue -->
<field id="queueIndex" type="integer" value="-1" />

<field id="author" type="string" />
<!-- NOTE: "_author" not "author". See PlaylistContentNode.xml for explanation -->
<field id="_author" type="string" />
<field id="authorId" type="string" />
<field id="isUpcoming" type="boolean" />
<field id="lengthSeconds" type="integer" />
Expand Down
2 changes: 1 addition & 1 deletion playlet-lib/src/components/ContextMenu/ContextMenuUtils.bs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace ContextMenuUtils
if item.type = "video"
LogInfo("Opening context menu for video:", item.videoId)
title = item.title
subtitle = item.author
subtitle = item._author
thumbnail = item.thumbnail
else if item.type = "playlist"
LogInfo("Opening context menu for playlist:", item.playlistId)
Expand Down
5 changes: 2 additions & 3 deletions playlet-lib/src/components/EcpArgs.bs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import "pkg:/components/VideoPlayer/VideoUtils.bs"
import "pkg:/source/utils/Logging.bs"
import "pkg:/source/utils/StringUtils.bs"
import "pkg:/source/utils/Types.bs"

function InitEcpArgs()
m.playQueue = m.top.findNode("PlayQueue")
m.videoQueue = m.top.findNode("VideoQueue")
LaunchArgumentsReceived()

m.scene.ObserveField("inputArgs", FuncName(InputArgumentsReceived))
Expand Down Expand Up @@ -46,6 +45,6 @@ function ProcessArguments(args as object) as void
if args.timestamp <> invalid
node.timestamp = args.timestamp
end if
m.playQueue@.Play(node, -1)
m.videoQueue.playVideo = node
end if
end function
5 changes: 1 addition & 4 deletions playlet-lib/src/components/MainScene.transpiled.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
<script type="text/brightscript" uri="pkg:/source/utils/Types.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/StringUtils.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/Logging.brs" />
<script type="text/brightscript" uri="pkg:/components/VideoPlayer/VideoUtils.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/FocusManagement.brs" />
<script type="text/brightscript" uri="pkg:/components/Dialog/DialogUtils.brs" />
<script type="text/brightscript" uri="pkg:/source/bslib.brs" />
<children>
<Logger id="Logger" />
Expand All @@ -39,7 +36,7 @@
</Group>
<VideoContainer id="VideoContainer" />
<Group id="Notifications" />
<PlayQueue id="PlayQueue" />
<VideoQueue id="VideoQueue" />
<ApplicationInfo id="ApplicationInfo" />
<Preferences id="Preferences" />
<Bookmarks id="Bookmarks" />
Expand Down
Loading

0 comments on commit 6e3f496

Please sign in to comment.