Add option to allow share to be downloaded

This commit is contained in:
Deluan 2023-03-10 23:01:03 -05:00 committed by Deluan Quintão
parent a22eef39f7
commit a7d3e6e1f1
13 changed files with 70 additions and 7 deletions

View File

@ -59,6 +59,7 @@ type configOptions struct {
EnableStarRating bool
EnableUserEditing bool
EnableSharing bool
DefaultDownloadableShare bool
DefaultTheme string
DefaultLanguage string
DefaultUIVolume int
@ -306,6 +307,7 @@ func init() {
viper.SetDefault("devautologinusername", "")
viper.SetDefault("devactivitypanel", true)
viper.SetDefault("enablesharing", false)
viper.SetDefault("defaultdownloadableshare", false)
viper.SetDefault("devenablebufferedscrobble", true)
viper.SetDefault("devsidebarplaylists", true)
viper.SetDefault("devshowartistpage", true)

View File

@ -125,7 +125,7 @@ func (r *shareRepositoryWrapper) Save(entity interface{}) (string, error) {
}
func (r *shareRepositoryWrapper) Update(id string, entity interface{}, _ ...string) error {
cols := []string{"description"}
cols := []string{"description", "downloadable"}
// TODO Better handling of Share expiration
if !entity.(*model.Share).ExpiresAt.IsZero() {

View File

@ -45,7 +45,7 @@ var _ = Describe("Share", func() {
entity := &model.Share{}
err := repo.Update("id", entity)
Expect(err).ToNot(HaveOccurred())
Expect(mockedRepo.(*tests.MockShareRepo).Cols).To(ConsistOf("description"))
Expect(mockedRepo.(*tests.MockShareRepo).Cols).To(ConsistOf("description", "downloadable"))
})
})
})

View File

@ -0,0 +1,23 @@
package migrations
import (
"database/sql"
"github.com/pressly/goose"
)
func init() {
goose.AddMigration(upAddDownloadToShare, downAddDownloadToShare)
}
func upAddDownloadToShare(tx *sql.Tx) error {
_, err := tx.Exec(`
alter table share
add downloadable bool not null default false;
`)
return err
}
func downAddDownloadToShare(tx *sql.Tx) error {
return nil
}

View File

@ -12,6 +12,7 @@ type Share struct {
UserID string `structs:"user_id" json:"userId,omitempty" orm:"column(user_id)"`
Username string `structs:"-" json:"username,omitempty" orm:"-"`
Description string `structs:"description" json:"description,omitempty"`
Downloadable bool `structs:"downloadable" json:"downloadable"`
ExpiresAt time.Time `structs:"expires_at" json:"expiresAt,omitempty"`
LastVisitedAt time.Time `structs:"last_visited_at" json:"lastVisitedAt,omitempty"`
ResourceIDs string `structs:"resource_ids" json:"resourceIds,omitempty" orm:"column(resource_ids)"`

View File

@ -183,6 +183,7 @@
"username": "Compartilhado por",
"url": "Link",
"description": "Descrição",
"downloadable": "Permitir Baixar?",
"contents": "Conteúdo",
"expiresAt": "Dt. Expiração",
"lastVisitedAt": "Última visita",
@ -452,4 +453,4 @@
"current_song": "Vai para música atual"
}
}
}
}

View File

@ -58,6 +58,7 @@ func serveIndex(ds model.DataStore, fs fs.FS, shareInfo *model.Share) http.Handl
"devActivityPanel": conf.Server.DevActivityPanel,
"enableUserEditing": conf.Server.EnableUserEditing,
"enableSharing": conf.Server.EnableSharing,
"defaultDownloadableShare": conf.Server.DefaultDownloadableShare,
"devSidebarPlaylists": conf.Server.DevSidebarPlaylists,
"lastFMEnabled": conf.Server.LastFM.Enabled,
"lastFMApiKey": conf.Server.LastFM.ApiKey,

View File

@ -245,6 +245,17 @@ var _ = Describe("serveIndex", func() {
Expect(config).To(HaveKeyWithValue("enableSharing", false))
})
It("sets the defaultDownloadableShare", func() {
conf.Server.DefaultDownloadableShare = true
r := httptest.NewRequest("GET", "/index.html", nil)
w := httptest.NewRecorder()
serveIndex(ds, fs, nil)(w, r)
config := extractAppConfig(w.Body.String())
Expect(config).To(HaveKeyWithValue("defaultDownloadableShare", true))
})
It("sets the defaultDownsamplingFormat", func() {
r := httptest.NewRequest("GET", "/index.html", nil)
w := httptest.NewRecorder()

View File

@ -22,6 +22,7 @@ const defaultConfig = {
defaultUIVolume: 100,
enableUserEditing: true,
enableSharing: true,
defaultDownloadableShare: true,
devSidebarPlaylists: true,
lastFMEnabled: true,
lastFMApiKey: '9b94a5515ea66b2da3ec03c12300327e',

View File

@ -8,6 +8,7 @@ import {
import {
SimpleForm,
TextInput,
BooleanInput,
useCreate,
useNotify,
useTranslate,
@ -17,6 +18,7 @@ import { shareUrl } from '../utils'
import { useTranscodingOptions } from './useTranscodingOptions'
import { useDispatch, useSelector } from 'react-redux'
import { closeShareMenu } from '../actions'
import config from '../config'
export const ShareDialog = () => {
const {
@ -30,6 +32,9 @@ export const ShareDialog = () => {
const notify = useNotify()
const translate = useTranslate()
const [description, setDescription] = useState('')
const [downloadable, setDownloadable] = useState(
config.defaultDownloadableShare
)
useEffect(() => {
setDescription('')
}, [ids])
@ -41,6 +46,7 @@ export const ShareDialog = () => {
resourceType: resource,
resourceIds: ids?.join(','),
description,
downloadable,
...(!originalFormat && { format }),
...(!originalFormat && { maxBitRate }),
},
@ -105,12 +111,21 @@ export const ShareDialog = () => {
<DialogContent>
<SimpleForm toolbar={null} variant={'outlined'}>
<TextInput
source="description"
resource={'share'}
source={'description'}
fullWidth
onChange={(event) => {
setDescription(event.target.value)
}}
/>
<BooleanInput
resource={'share'}
source={'downloadable'}
defaultValue={downloadable}
onChange={(value) => {
setDownloadable(value)
}}
/>
<TranscodingOptionsInput
fullWidth
label={translate('message.shareOriginalFormat')}

View File

@ -184,6 +184,7 @@
"username": "Shared By",
"url": "URL",
"description": "Description",
"downloadable": "Allow Downloads?",
"contents": "Contents",
"expiresAt": "Expires",
"lastVisitedAt": "Last Visited",

View File

@ -1,5 +1,6 @@
import {
DateTimeInput,
BooleanInput,
Edit,
NumberField,
SimpleForm,
@ -19,6 +20,7 @@ export const ShareEdit = (props) => {
{url}
</Link>
<TextInput source="description" />
<BooleanInput source="downloadable" />
<DateTimeInput source="expiresAt" />
<TextInput source="contents" disabled />
<TextInput source="format" disabled />

View File

@ -1,6 +1,7 @@
import {
Datagrid,
FunctionField,
BooleanField,
List,
NumberField,
SimpleList,
@ -24,6 +25,7 @@ export const FormatInfo = ({ record, size }) => {
const ShareList = (props) => {
const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs'))
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('lg'))
const translate = useTranslate()
const notify = useNotify()
@ -101,10 +103,13 @@ const ShareList = (props) => {
/>
<TextField source="username" />
<TextField source="description" />
<TextField source="contents" />
<FormatInfo source="format" />
{isDesktop && <TextField source="contents" />}
{isDesktop && <FormatInfo source="format" />}
<BooleanField source="downloadable" />
<NumberField source="visitCount" />
<DateField source="lastVisitedAt" showTime sortByOrder={'DESC'} />
{isDesktop && (
<DateField source="lastVisitedAt" showTime sortByOrder={'DESC'} />
)}
<DateField source="expiresAt" showTime />
</Datagrid>
)}