Skip to content

Commit

Permalink
Bookmarks (#153)
Browse files Browse the repository at this point in the history
* Empty bookmarks screen

* Bookmarks object

* Long press support

* Lint fix

* Context menu v1

* A working context menu

* Basic bookmarks in context menu

* Use a root content node for bookmarks

* context menu animation

* small tweak

* move logger around, adjust animation

* Lint fix

* Restore logger fix

* Cache channel info

* Bookmarks v1

* Added comment

* Adjust "popular" feed error handling

* Progress with bookmarks

* Add todo

* add todo

* handle roDateTime logging

* logger adjustment

* Use yaml for api and layout

* rename requestData to feedSource

* Restructure things

* Lint fix

* remove comment

* introduce feedSource id

* small cleanup

* playlist context menu

* Context menu for the channel view

* changelog

---------

Co-authored-by: github-action linter <githubaction@githubaction.com>
  • Loading branch information
iBicha and github-action linter authored Oct 19, 2023
1 parent 620a8a6 commit c740d5e
Show file tree
Hide file tree
Showing 63 changed files with 1,903 additions and 524 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Bookmarks: different items can now be bookmarked, and be found in the `Bookmarks` screen.
Things that can be bookmarked:
- A video
- A channel
- A playlist
- Subscriptions
- Trending
- Popular
- Playlists
- Search results
- Channel tabs (latest videos, live streams, playlists, shorts, podcasts, related channels)
- Context menu: press and hold `OK` to show a context menu to:
- Play/queue a video or a playlist
- Open the channel of a playlist or of a video
- Manage bookmarks
- Local DASH manifest generation
- This adds support for multi languages audio tracks
- Also adds thumbnails/preview in trick play mode
Expand All @@ -17,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Layout in channel view so that the upload time and view count of videos is visible
- A few things in the logger

### Changed

Expand Down
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,13 @@ Additionally, the web server exposes the settings under `/api/preferences`, whic

When Playlet starts, it shows a video feed on the screen. Subscription, Trending videos, and so on.

The layout is defined under [playlet-lib/src/config/default_home_layout.json5](/playlet-lib/src/config/default_home_layout.json5).
The layout is defined under [playlet-lib/src/config/default_home_layout.yaml](/playlet-lib/src/config/default_home_layout.yaml).

This could allow users to define custom layouts, so they can see what they find relevant in the home page. This can include Subscription, Trending, Popular videos, Search per keywords, or Playlists.

Additionally each feed has information on how it is fetched. For now, only Invidious can be data source, but other systems should be configured in the same way.

Invidious API definitions are defined under [playlet-lib/src/config/invidious_video_api.json5](/playlet-lib/src/config/invidious_video_api.json5), and Playlet parses these at runtime and make the right API calls to fetch the data.
Invidious API definitions are defined under [playlet-lib/src/config/invidious_video_api.yaml](/playlet-lib/src/config/invidious_video_api.yaml), and Playlet parses these at runtime and make the right API calls to fetch the data.

Finally, this layout system is what allows both the BrightScript app and the Web app to display the same homepage.

Expand Down
48 changes: 48 additions & 0 deletions docs/models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!-- TODO:P1 document models better -->

# Models

ApiEndpoint:

- id
- url
- queryParams
- pathParams
- cacheSeconds
- paginationType
- authenticated
- responseHandler

FeedSource:

- id
- title
- apiType: string ("Invidious")
- endpoint: ApiEndpoint
- pathParams
- queryParams
- state:
- paginationtype (Runtime field)
- page (Runtime field)
- continuation (Runtime field)
- queryParams.page (Runtime field)
- queryParams.continuation (Runtime field)

Feed/FeedContentNode:

- Title
- feedSources: FeedSource[]
- feedSourceIndex (Runtime field)

RowListLayout

- Feed[]

Bookmarks

- BookmarkGroup[]

BookmarkGroup

- title
- feedSources: FeedSource[]
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<field id="launchArgs" type="assocarray" />
<field id="inputArgs" type="assocarray" alwaysNotify="true" />
<field id="playletLibLoadedUrl" type="assocarray" />
<field id="systemLogEvent" type="assocarray" />
<field id="systemLogEvent" type="assocarray" alwaysNotify="true" />
</interface>
<script type="text/brightscript" uri="pkg:/source/Dialog.bs" />
<script type="text/brightscript" uri="pkg:/source/Manifest.bs" />
Expand Down
4 changes: 3 additions & 1 deletion playlet-app/src/source/Main.bs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ function Main(args as object) as void
msg = wait(0, port)
msgType = type(msg)
if msgType = "roSystemLogEvent"
scene.systemLogEvent = msg.GetInfo()
info = msg.GetInfo()
info.DateTime = info.DateTime.ToISOString()
scene.systemLogEvent = info
else if msgType = "roSGScreenEvent"
if msg.isScreenClosed()
return
Expand Down
62 changes: 39 additions & 23 deletions playlet-lib/src/components/ChannelView/ChannelView.bs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ function Init()
m.thumbnail = m.top.findNode("thumbnail")
m.authorLabel = m.top.findNode("authorLabel")
m.rowList = m.top.FindNode("rowList")
m.rowList.screen = m.top

m.banner.ObserveField("loadStatus", FuncName(OnBannerLoadStatus))
m.author = ""
m.authorId = ""

InitializeTabs()
Expand All @@ -39,7 +41,6 @@ function OnContentSet() as void

' NOTE: "_author" not "author". See PlaylistContentNode.xml for explanation.
m.authorLabel.text = content._author
' TODO:P1 handle case where there's no banner - a big portion of the screen is blank
m.banner.uri = content.banner
if StringUtils.IsNullOrEmpty(content.thumbnail)
m.thumbnail.uri = ""
Expand All @@ -53,13 +54,15 @@ function OnContentSet() as void
content@.LoadChannel(m.invidious)

authorId = ValidString(content.authorId)
if authorId <> m.authorId and IsArray(content.tabs)
author = ValidString(content._author)
if (authorId <> m.authorId or author <> m.author) and IsArray(content.tabs)
m.authorId = authorId
m.rowList.contentData = CreateChannelFeeds(m.authorId, content.tabs)
m.author = author
m.rowList.feeds = CreateChannelFeeds(m.authorId, author, content.tabs)
end if
end function

function CreateChannelFeeds(authorId as string, tabs as object) as object
function CreateChannelFeeds(authorId as string, author as string, tabs as object) as object
if authorId = ""
return invalid
end if
Expand All @@ -68,7 +71,7 @@ function CreateChannelFeeds(authorId as string, tabs as object) as object
for i = 0 to tabs.count() - 1
tabName = tabs[i]
if m.tabs.DoesExist(tabName)
feeds.Push(m.tabs[tabName](authorId))
feeds.Push(m.tabs[tabName](authorId, author))
end if
end for
return feeds
Expand Down Expand Up @@ -119,34 +122,47 @@ end function

function InitializeTabs()
m.tabs = {
videos: function(authorId as string) as object
return CreateChannelFeed("Latest videos", "channel_videos", authorId)
videos: function(authorId as string, author as string) as object
return CreateChannelFeed("Latest videos", "channel_videos", authorId, author)
end function,
shorts: function(authorId as string) as object
return CreateChannelFeed("Shorts", "channel_shorts", authorId)
shorts: function(authorId as string, author as string) as object
return CreateChannelFeed("Shorts", "channel_shorts", authorId, author)
end function,
streams: function(authorId as string) as object
return CreateChannelFeed("Live", "channel_streams", authorId)
streams: function(authorId as string, author as string) as object
return CreateChannelFeed("Live", "channel_streams", authorId, author)
end function,
podcasts: function(authorId as string) as object
return CreateChannelFeed("Podcasts", "channel_podcasts", authorId)
podcasts: function(authorId as string, author as string) as object
return CreateChannelFeed("Podcasts", "channel_podcasts", authorId, author)
end function,
playlists: function(authorId as string) as object
return CreateChannelFeed("Playlists", "channel_playlists", authorId)
playlists: function(authorId as string, author as string) as object
return CreateChannelFeed("Playlists", "channel_playlists", authorId, author)
end function,
channels: function(authorId as string) as object
return CreateChannelFeed("Channels", "channel_channels", authorId)
channels: function(authorId as string, author as string) as object
return CreateChannelFeed("Channels", "channel_channels", authorId, author)
end function
}
end function

function CreateChannelFeed(title as string, endpoint as string, ucid as string) as object
function CreateChannelFeed(title as string, endpoint as string, ucid as string, author as string) as object
return {
title: title,
apiType: "Invidious",
endpoint: endpoint,
pathParams: {
ucid: ucid
}
feedSources: [{
id: `inv_${endpoint}_${ucid}`,
title: `${author} - ${title}`,
apiType: "Invidious",
endpoint: endpoint,
pathParams: {
ucid: ucid
}
}]
}
end function

function GetContextMenuOptionsForItem(video as object) as object
content = m.top.content
if content = invalid or StringUtils.IsNullOrEmpty(content.authorId)
return []
end if

return m.bookmarks@.GetMenuForChannel(content)
end function
3 changes: 2 additions & 1 deletion playlet-lib/src/components/ChannelView/ChannelView.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<component name="ChannelView" extends="Group" includes="AutoBind,Focus">
<component name="ChannelView" extends="Group" includes="AutoBind,Focus,ContextMenuProvider">
<interface>
<field id="content" type="node" onChange="OnContentSet" />
<field id="appController" type="node" bind="/AppController" />
<field id="invidious" type="node" bind="/Invidious" />
<field id="bookmarks" type="node" bind="/Bookmarks" />
</interface>
<children>
<Rectangle
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<component name="BookmarkContentNode" extends="ContentNode">
<interface>
<field id="feedSource" type="assocarray" />
</interface>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<field id="type" type="string" value="channel" />
<field id="loadState" type="string" />
<function name="LoadChannel" />

<!-- index in a feed -->
<field id="feedSourcesIndex" type="integer" value="-1" />
<!-- NOTE: "_author" not "author". See PlaylistContentNode.xml for explanation -->
<field id="_author" type="string" />
<field id="authorId" type="string" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<component name="ContextMenuItemContentNode" extends="ContentNode">
<interface>
<field id="node" type="node" />
<field id="func" type="string" />
<field id="args" type="array" />
</interface>
</component>
6 changes: 2 additions & 4 deletions playlet-lib/src/components/ContentNode/FeedContentNode.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<component name="FeedContentNode" extends="ContentNode">
<interface>
<field id="feed" type="assocarray" />
<field id="feedSources" type="array" />
<field id="feedSourcesIndex" type="integer" />
<field id="loadState" type="string" />
<field id="paginationType" type="string" />
<field id="page" type="integer" value="0" />
<field id="continuation" type="string" />
</interface>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<interface>
<field id="type" type="string" value="playlist" />
<field id="loadState" type="string" />
<!-- index in a feed -->
<field id="feedSourcesIndex" type="integer" value="-1" />
<!--
For some strange reason, the "author" field can only be set once.
If you try to set it again, it will not be updated, with type mismatch warning:
Expand All @@ -22,6 +24,7 @@
we can query it's original owner)
-->
<field id="_author" type="string" />
<field id="authorId" type="string" />
<field id="playlistId" type="string" />
<field id="thumbnail" type="uri" />
<field id="thumbnailBackground" type="uri" />
Expand Down
3 changes: 3 additions & 0 deletions playlet-lib/src/components/ContentNode/VideoContentNode.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
<field id="type" type="string" value="video" />

<field id="author" type="string" />
<field id="authorId" type="string" />
<field id="hlsUrl" type="uri" />
<!-- index in a playlist -->
<field id="index" type="integer" value="-1" />
<!-- index in a feed -->
<field id="feedSourcesIndex" type="integer" value="-1" />
<field id="isUpcoming" type="boolean" />
<field id="lengthSeconds" type="integer" />
<field id="lengthText" type="string" />
Expand Down
Loading

0 comments on commit c740d5e

Please sign in to comment.