Merge commit 'fd7425a2cf312b787b52843a142e22eb965314d2' into fix-android-backAction

This commit is contained in:
KryptoPX 2022-11-29 08:53:53 +01:00
commit 0273c2129f
39 changed files with 599 additions and 269 deletions

View File

@ -1,3 +1,3 @@
{
"version": "0.31.1"
"version": "0.33.0"
}

View File

@ -1,13 +0,0 @@
name: Create Changelogs
on:
push:
branches: [ master ]
jobs:
create_changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Create Changelogs
uses: heineiuo/create-changelogs@v0.2.8

View File

@ -1,18 +0,0 @@
name: Trello Issue List
on:
issues:
types: [opened]
env:
TRELLO_KEY: ${{ secrets.TRELLO_KEY }}
TRELLO_TOKEN: ${{ secrets.TRELLO_TOKEN }}
jobs:
issue_send:
name: Send Issue to Trello
runs-on: ubuntu-latest
steps:
- name: Runs trello manage
uses: sisodiya2421/trello-manage@master
with:
repo-name: Comty Development
trello-username: ${{ secrets.TRELLO_USERNAME }}

View File

@ -1,17 +0,0 @@
name: Validate code
on:
push:
branches:
- '**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm ci
- run: npm test

View File

@ -22,5 +22,5 @@
"devDependencies": {
"concurrently": "^7.5.0"
},
"version": "0.31.1"
"version": "0.33.0"
}

View File

@ -19,7 +19,11 @@
"language": "en",
"sidebarKeys": [
"home",
"tv",
"music",
"events",
"groups",
"marketplace",
"groups"
"dev"
]
}

View File

@ -14,10 +14,31 @@
"reachable": true
},
{
"id": "saved",
"path": "/saved",
"title": "Saved",
"icon": "Archive",
"id": "events",
"path": "/events",
"title": "Events",
"icon": "MdLocalActivity",
"reachable": true
},
{
"id": "tv",
"path": "/tv",
"title": "Tv",
"icon": "Tv",
"reachable": true
},
{
"id": "music",
"path": "/music",
"title": "Music",
"icon": "MdMusicVideo",
"reachable": true
},
{
"id": "groups",
"path": "/groups",
"title": "Groups",
"icon": "Users",
"reachable": true
},
{
@ -28,23 +49,10 @@
"reachable": true
},
{
"id": "streams",
"path": "/streams",
"title": "Streams",
"icon": "Tv",
"reachable": true
},
{
"id": "streaming_control",
"path": "/streaming_control",
"title": "Streaming Control",
"icon": "Video"
},
{
"id": "groups",
"path": "/groups",
"title": "Groups",
"icon": "Users",
"id": "dev",
"path": "/dev",
"title": "Development",
"icon": "MdOutlineCode",
"reachable": true
}
]

View File

@ -1,6 +1,6 @@
{
"name": "comty",
"version": "0.31.1",
"version": "0.33.0",
"license": "LGPL-2.1",
"main": "electron/main",
"author": "RageStudio",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -85,7 +85,10 @@ const steps = [
setValidCharacters(hasValidCharacters(username))
const timer = setTimeout(async () => {
if (!validCharacters) return
if (!validCharacters) {
setLoading(false)
return
}
const request = await app.api.customRequest("main", {
method: "GET",
@ -120,7 +123,7 @@ const steps = [
autoCorrect="off"
autoCapitalize="none"
onPressEnter={submit}
placeholder="@newuser"
placeholder="newuser"
value={username}
onChange={handleUpdate}
status={username.length == 0 ? "default" : loading ? "default" : (isValid() ? "success" : "error")}

View File

@ -27,6 +27,12 @@ export default class Livestream {
return data
}
static async getCategories() {
const request = await Livestream.bridge.get.streamingCategories()
return request
}
static async getStreamInfo(payload) {
let { username } = payload ?? {}

View File

@ -6,36 +6,10 @@ import { Icons, createIconRender } from "components/Icons"
import { HashtagTrendings, FeaturedEventsAnnouncements, ConnectedFriends } from "components"
import FeedBrowser from "./components/feed"
import ExploreBrowser from "./components/explore"
import LivestreamsBrowser from "./components/livestreams"
import SavedPostsBrowser from "./components/savedPosts"
import Tabs from "./tabs"
import "./index.less"
const Tabs = {
"feed": {
title: "Feed",
icon: "Rss",
component: FeedBrowser
},
"explore": {
title: "Explore",
icon: "Search",
component: ExploreBrowser
},
"savedPosts": {
title: "Saved posts",
icon: "Bookmark",
component: SavedPostsBrowser
},
"livestreams": {
title: "Livestreams",
icon: "Tv",
component: LivestreamsBrowser
},
}
export default class Dashboard extends React.Component {
state = {
activeTab: this.props.match.params.type ?? "feed"
@ -80,7 +54,7 @@ export default class Dashboard extends React.Component {
}
render() {
return <div className="dashboard">
return <div className="postingDashboard">
<div></div>
<div

View File

@ -2,40 +2,10 @@ import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import { Icons, createIconRender } from "components/Icons"
import { HashtagTrendings, FeaturedEventsAnnouncements, ConnectedFriends } from "components"
import FeedBrowser from "./components/feed"
import ExploreBrowser from "./components/explore"
import LivestreamsBrowser from "./components/livestreams"
import SavedPostsBrowser from "./components/savedPosts"
import Tabs from "./tabs"
import "./index.less"
const Tabs = {
"feed": {
title: "Feed",
icon: "Rss",
component: FeedBrowser
},
"explore": {
title: "Explore",
icon: "Search",
component: ExploreBrowser
},
"savedPosts": {
title: "Saved posts",
icon: "Bookmark",
component: SavedPostsBrowser
},
"livestreams": {
title: "Livestreams",
icon: "Tv",
component: LivestreamsBrowser
},
}
export default class Dashboard extends React.Component {
state = {
activeTab: this.props.match.params.type ?? "feed"
@ -80,7 +50,7 @@ export default class Dashboard extends React.Component {
}
render() {
return <div className="dashboard">
return <div className="postingDashboard">
<div
ref={this.primaryPanelRef}
className={classnames("panel", "fade-opacity-active")}

View File

@ -0,0 +1,14 @@
import React from "react"
import { Result } from "antd"
import "./index.less"
export default (props) => {
return <div className="trendingsBrowser">
<Result
status="404"
title="Not implemented"
subTitle="Sorry, but this feature is not implemented yet."
/>
</div>
}

View File

@ -0,0 +1 @@
.trendingsBrowser {}

View File

@ -1,7 +1,7 @@
import React from "react"
export default () => {
app.setLocation("home/feed")
app.setLocation("/home/feed")
return <></>
}

View File

@ -1,4 +1,4 @@
.dashboard {
.postingDashboard {
display: grid;
grid-template-columns: 10vw 1fr 0.5fr;

View File

@ -0,0 +1,27 @@
import FeedTab from "./components/feed"
import ExploreTab from "./components/explore"
import TrendingsTab from "./components/trendings"
import SavedPostsTab from "./components/savedPosts"
export default {
"feed": {
title: "Feed",
icon: "Rss",
component: FeedTab
},
"explore": {
title: "Explore",
icon: "Search",
component: ExploreTab
},
"trendings": {
title: "Trendings",
icon: "TrendingUp",
component: TrendingsTab
},
"savedPosts": {
title: "Saved posts",
icon: "Bookmark",
component: SavedPostsTab
}
}

View File

@ -0,0 +1,87 @@
import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import { Icons, createIconRender } from "components/Icons"
import Tabs from "./tabs"
import "./index.less"
export default class TVDashboard extends React.Component {
state = {
activeTab: this.props.match.params.type ?? "feed"
}
primaryPanelRef = React.createRef()
componentDidMount() {
app.eventBus.emit("style.compactMode", false)
}
renderActiveTab() {
const tab = Tabs[this.state.activeTab]
if (!tab) {
return <antd.Result
status="404"
title="404"
subTitle="Sorry, the tab you visited does not exist."
/>
}
return React.createElement(tab.component)
}
handleTabChange = (key) => {
if (this.state.activeTab === key) return
// set to primary panel fade-opacity-leave class
this.primaryPanelRef.current.classList.add("fade-opacity-leave")
setTimeout(() => {
this.setState({ activeTab: key })
// update location
app.history.replace(key)
}, 200)
// remove fade-opacity-leave class after animation
setTimeout(() => {
this.primaryPanelRef.current.classList.remove("fade-opacity-leave")
}, 300)
}
render() {
return <div className="tvDashboard">
<div
ref={this.primaryPanelRef}
className={classnames("panel", "fade-opacity-active")}
>
{this.renderActiveTab()}
</div>
<div className="panel">
<div className="card" id="browserType">
<h2><Icons.Tv /> TV</h2>
<antd.Menu
mode="inline"
selectedKeys={[this.state.activeTab]}
activeKey={this.state.activeTab}
onClick={({ key }) => this.handleTabChange(key)}
>
{Object.keys(Tabs).map((key) => {
const tab = Tabs[key]
return <antd.Menu.Item
key={key}
icon={createIconRender(tab.icon)}
>
{tab.title}
</antd.Menu.Item>
})}
</antd.Menu>
</div>
</div>
</div>
}
}

View File

@ -3,7 +3,7 @@ import * as antd from "antd"
import { Icons } from "components/Icons"
import Livestream from "../../models/livestream"
import Livestream from "../../../../models/livestream"
import "./index.less"
@ -32,6 +32,131 @@ const StreamingKeyView = (props) => {
</div>
}
const LivestreamsCategoriesSelector = (props) => {
const [categories, setCategories] = React.useState([])
const [loading, setLoading] = React.useState(true)
const loadData = async () => {
setLoading(true)
const categories = await Livestream.getCategories().catch((err) => {
console.error(err)
app.message.error("Failed to load categories")
return null
})
console.log(`Loaded categories >`, categories)
setLoading(false)
if (categories) {
setCategories(categories)
}
}
React.useEffect(() => {
loadData()
}, [])
if (loading) {
return <antd.Skeleton active />
}
return <antd.Select
placeholder="Select a category"
defaultValue={props.defaultValue}
onChange={(value) => props.updateStreamInfo("category", value)}
>
{
categories.map((category) => {
return <antd.Select.Option value={category?.key ?? "unknown"}>{category?.label ?? "No category"}</antd.Select.Option>
})
}
</antd.Select>
}
const StreamInfoEditor = (props) => {
const [streamInfo, setStreamInfo] = React.useState(props.defaultStreamInfo ?? {})
const updateStreamInfo = (key, value) => {
setStreamInfo({
...streamInfo,
[key]: value,
})
}
const saveStreamInfo = async () => {
if (typeof props.onSave === "function") {
return await props.onSave(streamInfo)
}
// peform default save
const result = await Livestream.updateLivestreamInfo(streamInfo).catch((err) => {
console.error(err)
app.message.error("Failed to update stream info")
return false
})
if (result) {
app.message.success("Stream info updated")
}
if (typeof props.onSaveComplete === "function") {
await props.onSaveComplete(result)
}
return result
}
return <div className="streamInfoEditor">
<div className="field">
<span>
<Icons.MdTitle />Title
</span>
<div className="value">
<antd.Input
placeholder="Stream title"
value={streamInfo.title}
onChange={(e) => updateStreamInfo("title", e.target.value)}
/>
</div>
</div>
<div className="field">
<span>
<Icons.MdTextFields /> Description
</span>
<div className="value">
<antd.Input
placeholder="Stream description"
value={streamInfo.description}
onChange={(e) => updateStreamInfo("description", e.target.value)}
/>
</div>
</div>
<div className="field">
<span>
<Icons.MdCategory /> Category
</span>
<div className="value">
<LivestreamsCategoriesSelector
defaultValue={streamInfo.category.key}
updateStreamInfo={updateStreamInfo}
/>
</div>
</div>
<antd.Button
type="primary"
onClick={saveStreamInfo}
>
Save
</antd.Button>
</div>
}
export default (props) => {
const [streamInfo, setStreamInfo] = React.useState({})
const [addresses, setAddresses] = React.useState({})
@ -39,6 +164,19 @@ export default (props) => {
const [isConnected, setIsConnected] = React.useState(false)
const [streamingKey, setStreamingKey] = React.useState(null)
const onClickEditInfo = () => {
app.ModalController.open(() => <StreamInfoEditor
defaultStreamInfo={streamInfo}
onSaveComplete={(result) => {
if (result) {
app.ModalController.close()
fetchStreamInfo()
}
}}
/>)
}
const regenerateStreamingKey = async () => {
antd.Modal.confirm({
title: "Regenerate streaming key",
@ -125,15 +263,35 @@ export default (props) => {
</h2>
</div>
<div className="description">
<span>
Description
</span>
<p>
{streamInfo?.description ?? "No description"}
</p>
</div>
<div className="category">
<span>
Category
</span>
<h4>
{streamInfo?.category ?? "No category"}
{streamInfo?.category?.label ?? "No category"}
</h4>
</div>
</div>
<div>
<antd.Button
type="primary"
icon={<Icons.Edit2 />}
onClick={onClickEditInfo}
>
Edit info
</antd.Button>
</div>
</div>
<div className="config">

View File

@ -2,13 +2,15 @@
display: flex;
flex-direction: column;
width: 100%;
transition: all 0.3s ease-in-out;
.header {
display: flex;
flex-direction: row;
height: 20vh;
height: fit-content;
padding: 15px;
@ -21,7 +23,7 @@
.preview {
height: 100%;
max-width: 400px;
width: 300px;
img {
width: 100%;
@ -38,6 +40,7 @@
flex-direction: column;
padding: 20px 0;
width: 100%;
.status {
margin-bottom: 20px;
@ -47,6 +50,7 @@
.config {
display: flex;
flex-direction: column;
padding: 0 40px;
@ -83,7 +87,7 @@
.title {
display: inline-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
@ -114,8 +118,29 @@
div {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
}
.streamInfoEditor {
display: flex;
flex-direction: column;
.field {
display: flex;
flex-direction: column;
margin-bottom: 20px;
.value {
margin-top: 5px;
margin-left: 20px;
.ant-select {
min-width: 200px;
}
}
}
}

View File

@ -1,11 +1,10 @@
import React from "react"
import Livestream from "models/livestream"
import * as antd from "antd"
import { UserPreview } from "components"
import { Icons } from "components/Icons"
import Livestream from "../../../../models/livestream"
import "./index.less"
const LivestreamItem = (props) => {
@ -34,7 +33,7 @@ const LivestreamItem = (props) => {
<h2>{livestream.info?.description ?? "No description"}</h2>
</div>
<div className="livestream_category">
{livestream.info?.catagory ?? "No category"}
{livestream.info?.category?.label ?? "No category"}
</div>
</div>
</div>
@ -71,10 +70,6 @@ export default (props) => {
app.setLocation(`/live/${livestream.username}`)
}
const onClickControlPanel = () => {
app.setLocation("/live_control")
}
const renderList = () => {
if (loading) {
return <antd.Skeleton active />
@ -105,19 +100,10 @@ export default (props) => {
<span>Livestreams</span>
</h1>
</div>
<div className="panel">
<antd.Button
icon={<Icons.Settings />}
onClick={onClickControlPanel}
>
Control Panel
</antd.Button>
</div>
</div>
<div className="livestream_list">
{renderList()}
</div>
</div>
}
}

View File

@ -0,0 +1,14 @@
import React from "react"
import { Result } from "antd"
import "./index.less"
export default (props) => {
return <div className="livestreamsFeed">
<Result
status="404"
title="Not implemented"
subTitle="Sorry, but this feature is not implemented yet."
/>
</div>
}

View File

@ -0,0 +1 @@
.livestreamsFeed {}

View File

@ -0,0 +1,7 @@
import React from "react"
export default () => {
app.setLocation("/tv/feed")
return <></>
}

View File

@ -0,0 +1,47 @@
.tvDashboard {
display: grid;
grid-template-columns: 3fr 1fr;
grid-template-rows: 1fr;
grid-column-gap: 10px;
grid-row-gap: 0px;
width: 100%;
padding-left: 30px;
.panel {
position: sticky;
top: 0;
height: fit-content;
display: flex;
flex-direction: column;
align-items: center;
>div {
margin-bottom: 15px;
}
.card {
background-color: var(--background-color-accent);
border-radius: 12px;
padding: 20px;
min-width: 20vw;
h1,
h2 {
font-family: "Space Grotesk", sans-serif;
}
}
}
.ant-menu {
svg {
margin-right: 0 !important;
}
}
}

View File

@ -0,0 +1,21 @@
import FeedTab from "./components/feed"
import ExploreTab from "./components/explore"
import ControlPanelTab from "./components/controlPanel"
export default {
"feed": {
title: "Feed",
icon: "Rss",
component: FeedTab
},
"explore": {
title: "Explore",
icon: "Search",
component: ExploreTab
},
"controlPanel": {
title: "Control Panel",
icon: "Settings",
component: ControlPanelTab
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@comty/server",
"version": "0.31.1",
"version": "0.33.0",
"main": "dist/index.js",
"scripts": {
"build": "corenode-cli build",

View File

@ -3,19 +3,17 @@ import { nanoid } from "nanoid"
import lodash from "lodash"
import axios from "axios"
import { User, StreamingKey, StreamingInfo } from "../../models"
import { Schematized } from "../../lib"
import { User, StreamingKey, StreamingInfo, StreamingCategory } from "../../models"
const streamingIngestServer = process.env.STREAMING_INGEST_SERVER
const streamingServerAPIAddress = process.env.STREAMING_API_SERVER
const streamingServerAPIProtocol = streamingServerAPIAddress.startsWith("https") ? "https" : "http"
const streamingIngestServer = process.env.STREAMING_INGEST_SERVER ?? ""
const streamingServerAPIAddress = process.env.STREAMING_API_SERVER ?? ""
const streamingServerAPIUri = `${streamingServerAPIProtocol}://${streamingServerAPIAddress.split("://")[1]}`
const streamingServerAPIUri = `${streamingServerAPIAddress.startsWith("https") ? "https" : "http"}://${streamingServerAPIAddress.split("://")[1]}`
const FILTER_KEYS = ["stream"]
export default class StreamingController extends Controller {
streamings = []
methods = {
genereteKey: async (user_id) => {
// this will generate a new key for the user
@ -34,78 +32,61 @@ export default class StreamingController extends Controller {
return streamingKey
},
regenerateStreamingList: async () => {
fetchStreams: async () => {
// fetch all streams from api
let streams = await axios.get(`${streamingServerAPIUri}/api/v1/streams`).catch((err) => {
console.log(err)
let { data } = await axios.get(`${streamingServerAPIUri}/api/v1/streams`).catch((err) => {
console.error(err)
return false
})
if (streams) {
streams = streams.data.streams
let streamings = []
// FIXME: this method is not totally async
streams.forEach((stream) => {
// check if the stream is already in the list
const streamInList = this.streamings.find((s) => s.stream === stream.name)
if (!data) return streamings
if (!streamInList) {
// if not, add it
this.methods.pushToLocalList({
stream: stream.name,
app: stream.app,
}).catch((err) => {
// sorry for you
})
}
streamings = data.streams
streamings = streamings.map(async (stream) => {
stream = await this.methods.generateStreamFromStreamkey(stream.name)
let info = await StreamingInfo.findOne({
user_id: stream.user_id
})
}
},
pushToLocalList: async (payload) => {
const { stream, app } = payload
const username = app.split("/")[1]
const user_id = await User.findOne({ username }).then((user) => user._id)
if (info) {
stream.info = info.toObject()
const streamingKey = await StreamingKey.findOne({
key: stream
stream.info.category = await StreamingCategory.findOne({
key: stream.info.category
})
}
return stream
})
if (!streamingKey) {
throw new Error("Invalid streaming key")
}
streamings = await Promise.all(streamings)
if (username !== streamingKey.username) {
throw new Error("Invalid streaming key for this username")
}
return streamings.map((stream) => {
return lodash.omit(stream, FILTER_KEYS)
})
},
generateStreamFromStreamkey: async (streamKey) => {
// generate a stream from a streamkey
const streamingKey = await StreamingKey.findOne({
key: streamKey
})
if (!streamingKey) return false
const streaming = {
stream,
user_id: user_id.toString(),
user_id: streamingKey.user_id,
username: streamingKey.username,
sources: {
rtmp: `${streamingIngestServer}/live/${username}`,
hls: `${streamingServerAPIAddress}/live/${username}/src.m3u8`,
flv: `${streamingServerAPIAddress}/live/${username}/src.flv`,
rtmp: `${streamingIngestServer}/live/${streamingKey.username}`,
hls: `${streamingServerAPIAddress}/live/${streamingKey.username}/src.m3u8`,
flv: `${streamingServerAPIAddress}/live/${streamingKey.username}/src.flv`,
}
}
this.streamings.push(streaming)
return streaming
},
removeFromLocalList: async (payload) => {
const { stream } = payload
// remove from streamings array
const streaming = this.streamings.find((streaming) => streaming.stream === stream)
if (!streaming) {
throw new Error("Stream not found")
}
this.streamings = this.streamings.filter((streaming) => streaming.stream !== stream)
return streaming
},
handleInfoUpdate: async (payload) => {
@ -147,28 +128,15 @@ export default class StreamingController extends Controller {
}
get = {
"/streaming/categories": async (req, res) => {
const categories = await StreamingCategory.find()
return res.json(categories)
},
"/streams": async (req, res) => {
await this.methods.regenerateStreamingList()
const remoteStreams = await this.methods.fetchStreams()
let data = this.streamings.map((stream) => {
return lodash.omit(stream, FILTER_KEYS)
})
data = data.map(async (stream) => {
let info = await StreamingInfo.findOne({
user_id: stream.user_id
})
if (info) {
stream.info = info.toObject()
}
return stream
})
data = await Promise.all(data)
return res.json(data)
return res.json(remoteStreams)
},
"/stream/info": {
middleware: ["withAuthentication"],
@ -189,11 +157,32 @@ export default class StreamingController extends Controller {
user_id = user_id["_id"].toString()
}
const info = await StreamingInfo.findOne({
let info = await StreamingInfo.findOne({
user_id,
})
return res.json(info)
if (!info) {
info = new StreamingInfo({
user_id,
})
await info.save()
}
const category = await StreamingCategory.findOne({
key: info.category
}).catch((err) => {
console.error(err)
return {}
}) ?? {}
return res.json({
...info.toObject(),
["category"]: {
key: category?.key ?? "unknown",
label: category?.label ?? "Unknown",
}
})
}
},
"/streaming/addresses": {
@ -218,8 +207,10 @@ export default class StreamingController extends Controller {
"/streaming/:username": async (req, res) => {
const { username } = req.params
const streamings = await this.methods.fetchStreams()
// search on this.streamings
const streaming = this.streamings.find((streaming) => streaming.username === username)
const streaming = streamings.find((streaming) => streaming.username === username)
if (streaming) {
return res.json(lodash.omit(streaming, FILTER_KEYS))
@ -257,6 +248,36 @@ export default class StreamingController extends Controller {
},
}
put = {
"/streaming/category": {
middlewares: ["withAuthentication", "onlyAdmin"],
fn: Schematized({
required: ["key", "label"]
}, async (req, res) => {
const { key, label } = req.selection
const existingCategory = await StreamingCategory.findOne({
key
})
if (existingCategory) {
return res.status(400).json({
error: "Category already exists"
})
}
const category = new StreamingCategory({
key,
label,
})
await category.save()
return res.json(category)
})
}
}
post = {
"/streaming/update_info": {
middlewares: ["withAuthentication"],
@ -285,25 +306,22 @@ export default class StreamingController extends Controller {
}
},
"/streaming/publish": async (req, res) => {
const { app, stream, tcUrl } = req.body
const { stream } = req.body
const streaming = await this.methods.generateStreamFromStreamkey(stream).catch((err) => {
console.error(err)
const streaming = await this.methods.pushToLocalList({
app,
stream,
tcUrl
}).catch((err) => {
res.status(500).json({
code: 1,
error: err.message
error: `Cannot generate stream: ${err.message}`,
})
return false
return null
})
if (streaming) {
global.wsInterface.io.emit(`streaming.new`, {
username: streaming.username,
})
global.wsInterface.io.emit(`streaming.new`, streaming)
global.wsInterface.io.emit(`streaming.new.${streaming.username}`, streaming)
return res.json({
code: 0,
@ -314,21 +332,16 @@ export default class StreamingController extends Controller {
"/streaming/unpublish": async (req, res) => {
const { stream } = req.body
const streaming = await this.methods.removeFromLocalList({
stream
}).catch((err) => {
res.status(500).json({
code: 2,
status: err.message
})
const streaming = await this.methods.generateStreamFromStreamkey(stream).catch((err) => {
console.error(err)
return false
return null
})
if (streaming) {
global.wsInterface.io.emit(`streaming.end`, {
username: streaming.username,
})
global.wsInterface.io.emit(`streaming.end`, streaming)
global.wsInterface.io.emit(`streaming.end.${streaming.username}`, streaming)
return res.json({
code: 0,

View File

@ -37,6 +37,7 @@ export const Playlist = mongoose.model("Playlist", schemas.Playlist, "playlists"
// streamings
export const StreamingKey = mongoose.model("StreamingKey", schemas.StreamingKey, "streamingKeys")
export const StreamingInfo = mongoose.model("StreamingInfo", schemas.StreamingInfo, "streamingInfos")
export const StreamingCategory = mongoose.model("StreamingCategory", schemas.StreamingCategory, "streamingCategories")
// others
export const FeaturedWallpaper = mongoose.model("FeaturedWallpaper", schemas.FeaturedWallpaper, "featuredWallpapers")

View File

@ -18,4 +18,5 @@ export { default as FeaturedWallpaper } from "./featuredWallpaper"
export { default as FeaturedEvent } from "./featuredEvent"
export { default as StreamingKey } from "./streamingKey"
export { default as StreamingInfo } from "./streamingInfo"
export { default as StreamingInfo } from "./streamingInfo"
export { default as StreamingCategory } from "./streamingCategory"

View File

@ -0,0 +1,10 @@
export default {
key: {
type: String,
required: true,
},
label: {
type: String,
required: true,
},
}

View File

@ -1,6 +1,6 @@
{
"name": "wrapper",
"version": "0.31.1",
"version": "0.33.0",
"main": "./src/index.js",
"license": "MIT",
"scripts": {